@h-ai/reach 0.1.0-alpha.65 → 0.1.0-alpha.67

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.en.md CHANGED
@@ -4,16 +4,13 @@
4
4
 
5
5
  [Framework home](../../README.en.md) · Related modules: [@h-ai/cache](../cache/README.en.md) · [@h-ai/core](../core/README.en.md) · [@h-ai/reldb](../reldb/README.en.md)
6
6
 
7
+ ## Introduction
8
+
7
9
  A notification module supporting multiple providers (email, SMS, API callbacks) at once. Templates bind to providers for scenario-based routing. Includes do-not-disturb (DND) policies and configuration-defined templates.
8
10
 
9
11
  Template sends need only `template`, `to` and `vars`; the provider comes from the template. An explicit `provider` still overrides it. Direct sends require a provider; omitting it returns a failed HaiResult.
10
12
 
11
- ## Dependencies
12
-
13
- - `@h-ai/reldb`: optional persistence for delivery logs and templates; enabled automatically when initialized.
14
- - `@h-ai/cache`: optional distributed locks for DND delay; enabled automatically when initialized.
15
-
16
- ## Supported providers
13
+ ## Core features
17
14
 
18
15
  | Provider | Channel | Description |
19
16
  | ------------ | ------- | -------------------------------------------------- |
@@ -22,9 +19,16 @@ Template sends need only `template`, `to` and `vars`; the provider comes from th
22
19
  | `aliyun-sms` | SMS | Alibaba Cloud SMS through HTTP API, without an SDK |
23
20
  | `api` | General | HTTP API callback (generic webhook) |
24
21
 
25
- ## Quick start
22
+ ## Getting started
23
+
24
+ ### Dependencies
25
+
26
+ - `@h-ai/reldb`: optional persistence for delivery logs and templates; enabled automatically when initialized.
27
+ - `@h-ai/cache`: optional distributed locks for DND delay; enabled automatically when initialized.
26
28
 
27
- ### Installation
29
+ ### Quick start
30
+
31
+ #### Installation
28
32
 
29
33
  ```bash
30
34
  pnpm add @h-ai/reach
@@ -32,61 +36,44 @@ pnpm add @h-ai/reach
32
36
 
33
37
  Install `nodemailer` for email delivery.
34
38
 
35
- ### Initialize multiple providers
39
+ #### Initialize multiple providers
36
40
 
37
41
  ```ts
38
- import { cache } from '@h-ai/cache'
39
42
  import { reach } from '@h-ai/reach'
40
- import { reldb } from '@h-ai/reldb'
41
-
42
- // 1. 初始化可选依赖(按需)
43
- await reldb.init({ type: 'sqlite', database: './data.db' }) // 可选,启用发送日志与模板持久化
44
- await cache.init({ type: 'memory' }) // 可选,启用 DND delay 策略分布式锁
45
-
46
- // 2. 初始化触达模块(自动检测已初始化的 reldb/cache 单例)
47
- await reach.init({
48
- providers: [
49
- { name: 'email', type: 'smtp', host: 'smtp.example.com', from: 'noreply@example.com' },
50
- { name: 'sms', type: 'aliyun-sms', accessKeyId: '...', accessKeySecret: '...', signName: '某某科技' },
51
- { name: 'webhook', type: 'api', url: 'https://api.example.com/notify' },
52
- ],
53
- templates: [
54
- { name: 'welcome_email', provider: 'email', subject: '欢迎 {userName}', body: '亲爱的 {userName},欢迎!' },
55
- { name: 'sms_code', provider: 'sms', body: '验证码: {code},{minutes} 分钟内有效。' },
56
- ],
57
- dnd: { enabled: true, strategy: 'delay', start: '22:00', end: '08:00' },
58
- })
59
-
60
- // 通过 provider 字段选择发送渠道
61
- await reach.send({
62
- to: 'user@example.com',
63
- template: 'welcome_email',
64
- vars: { userName: '张三' },
65
- })
66
43
 
67
- // 发送短信(通过 extra 传递 Provider 特有参数)
68
- await reach.send({
69
- provider: 'sms',
70
- to: '13800138000',
71
- extra: { templateCode: 'SMS_123456' },
72
- vars: { code: '123456' },
44
+ const initialized = await reach.init({
45
+ providers: [{ name: 'local', type: 'console' }],
46
+ templates: [{ name: 'welcome', provider: 'local', subject: 'Welcome', body: 'Hello {userName}' }],
73
47
  })
74
-
75
- // 关闭所有 Provider
76
- await reach.close()
48
+ if (!initialized.success)
49
+ throw new Error(initialized.error.message)
50
+ try {
51
+ const result = await reach.send({
52
+ to: 'user@example.com',
53
+ template: 'welcome',
54
+ vars: { userName: 'Alice' },
55
+ })
56
+ if (!result.success)
57
+ throw new Error(result.error.message)
58
+ }
59
+ finally {
60
+ await reach.close()
61
+ }
77
62
  ```
78
63
 
64
+ This example uses the development Console provider. It records delivery metadata and does not send email or SMS. Configure SMTP, SMS or API providers below for real delivery; initialize optional persistence/shared cache when needed.
65
+
79
66
  `reach.config` returns a **redacted configuration snapshot**. Embedded credentials in `api.url` / `endpoint`, `headers.Authorization`, SMTP `pass`, SMS keys, and other secrets are hidden automatically.
80
67
 
81
- ## Configuration
68
+ ### Configuration
82
69
 
83
- ### Console (development/tests)
70
+ #### Console (development/tests)
84
71
 
85
72
  ```ts
86
73
  const config = { name: 'dev', type: 'console' }
87
74
  ```
88
75
 
89
- ### SMTP
76
+ #### SMTP
90
77
 
91
78
  | Field | Type | Required | Default | Description |
92
79
  | -------- | --------- | -------- | ------- | ----------------------- |
@@ -99,7 +86,7 @@ const config = { name: 'dev', type: 'console' }
99
86
  | `pass` | `string` | — | — | Authentication password |
100
87
  | `from` | `string` | ✅ | — | Sender address |
101
88
 
102
- ### Alibaba Cloud SMS
89
+ #### Alibaba Cloud SMS
103
90
 
104
91
  | Field | Type | Required | Default | Description |
105
92
  | ----------------- | -------- | -------- | ----------------------- | -------------------- |
@@ -110,7 +97,7 @@ const config = { name: 'dev', type: 'console' }
110
97
  | `signName` | `string` | ✅ | — | SMS signature |
111
98
  | `endpoint` | `string` | — | `dysmsapi.aliyuncs.com` | API endpoint |
112
99
 
113
- ### API callbacks
100
+ #### API callbacks
114
101
 
115
102
  | Field | Type | Required | Default | Description |
116
103
  | --------- | ------------------------ | -------- | ------- | ----------------------- |
@@ -123,7 +110,7 @@ const config = { name: 'dev', type: 'console' }
123
110
 
124
111
  API callback addresses are trusted server configuration. Logs contain only the callback origin, not URL credentials, query parameters, recipients, or third-party failure bodies. For tenant/user-supplied addresses, applications must enforce a domain allowlist; enterprise deployments should also block internal metadata access through egress proxies or network policies.
125
112
 
126
- ### DND (do not disturb)
113
+ #### DND (do not disturb)
127
114
 
128
115
  | Field | Type | Required | Default | Description |
129
116
  | ---------- | ---------------------- | -------- | --------- | ------------------------------------- |
@@ -132,7 +119,7 @@ API callback addresses are trusted server configuration. Logs contain only the c
132
119
  | `start` | `string` | — | `00:00` | Start time (HH:mm) |
133
120
  | `end` | `string` | — | `00:00` | End time (HH:mm) |
134
121
 
135
- ## Error handling
122
+ ### Error handling
136
123
 
137
124
  All operations return `HaiResult<T>`. Error codes are defined in `HaiReachError`:
138
125
 
@@ -156,7 +143,7 @@ if (!result.success) {
156
143
  }
157
144
  ```
158
145
 
159
- ## Distributed locks
146
+ ### Distributed locks
160
147
 
161
148
  Across nodes, flushing pending DND-delay messages is protected by `@h-ai/cache` distributed locks, allowing only one node to flush at a time.
162
149
 
@@ -165,7 +152,7 @@ Across nodes, flushing pending DND-delay messages is protected by `@h-ai/cache`
165
152
  - Without initialized `@h-ai/cache`, distributed locking is disabled automatically; single-node operation continues.
166
153
  - A stable process-level owner identifier prevents releasing another owner's lock.
167
154
 
168
- ## Tests
155
+ ## Development and testing
169
156
 
170
157
  ```bash
171
158
  pnpm --filter @h-ai/reach test
package/README.ja.md CHANGED
@@ -4,16 +4,13 @@
4
4
 
5
5
  [フレームワークのホーム](../../README.ja.md) · 関連モジュール:[@h-ai/cache](../cache/README.ja.md) · [@h-ai/core](../core/README.ja.md) · [@h-ai/reldb](../reldb/README.ja.md)
6
6
 
7
+ ## 概要
8
+
7
9
  メール・SMS・API 通知など複数の Provider を同時登録できる通知モジュールです。テンプレートと Provider の対応で用途に応じて配信します。通知抑制(DND)と設定ファイルによるテンプレート定義を備えます。
8
10
 
9
11
  テンプレート送信は `template`、`to`、`vars` のみで利用でき、Provider はテンプレートから選ばれます。明示した `provider` は優先されます。直接送信は Provider が必須で、省略すると失敗 HaiResult を返します。
10
12
 
11
- ## 依存関係
12
-
13
- - `@h-ai/reldb`:送信ログ・テンプレートの永続化(任意)。初期化済みなら自動有効化。
14
- - `@h-ai/cache`:DND delay 用の分散ロック(任意)。初期化済みなら自動有効化。
15
-
16
- ## 対応 Provider
13
+ ## 主な機能
17
14
 
18
15
  | Provider | チャネル | 説明 |
19
16
  | ------------ | -------- | -------------------------------------------------- |
@@ -22,9 +19,16 @@
22
19
  | `aliyun-sms` | SMS | Alibaba Cloud SMS(HTTP API を直接使用、SDK 不要) |
23
20
  | `api` | 汎用 | HTTP API 通知(汎用 webhook) |
24
21
 
25
- ## クイックスタート
22
+ ## 使い始める
23
+
24
+ ### 依存関係
25
+
26
+ - `@h-ai/reldb`:送信ログ・テンプレートの永続化(任意)。初期化済みなら自動有効化。
27
+ - `@h-ai/cache`:DND delay 用の分散ロック(任意)。初期化済みなら自動有効化。
26
28
 
27
- ### インストール
29
+ ### クイックスタート
30
+
31
+ #### インストール
28
32
 
29
33
  ```bash
30
34
  pnpm add @h-ai/reach
@@ -32,61 +36,44 @@ pnpm add @h-ai/reach
32
36
 
33
37
  メール送信には `nodemailer` をインストールしてください。
34
38
 
35
- ### 複数 Provider の初期化
39
+ #### 複数 Provider の初期化
36
40
 
37
41
  ```ts
38
- import { cache } from '@h-ai/cache'
39
42
  import { reach } from '@h-ai/reach'
40
- import { reldb } from '@h-ai/reldb'
41
-
42
- // 1. 初始化可选依赖(按需)
43
- await reldb.init({ type: 'sqlite', database: './data.db' }) // 可选,启用发送日志与模板持久化
44
- await cache.init({ type: 'memory' }) // 可选,启用 DND delay 策略分布式锁
45
-
46
- // 2. 初始化触达模块(自动检测已初始化的 reldb/cache 单例)
47
- await reach.init({
48
- providers: [
49
- { name: 'email', type: 'smtp', host: 'smtp.example.com', from: 'noreply@example.com' },
50
- { name: 'sms', type: 'aliyun-sms', accessKeyId: '...', accessKeySecret: '...', signName: '某某科技' },
51
- { name: 'webhook', type: 'api', url: 'https://api.example.com/notify' },
52
- ],
53
- templates: [
54
- { name: 'welcome_email', provider: 'email', subject: '欢迎 {userName}', body: '亲爱的 {userName},欢迎!' },
55
- { name: 'sms_code', provider: 'sms', body: '验证码: {code},{minutes} 分钟内有效。' },
56
- ],
57
- dnd: { enabled: true, strategy: 'delay', start: '22:00', end: '08:00' },
58
- })
59
-
60
- // 通过 provider 字段选择发送渠道
61
- await reach.send({
62
- to: 'user@example.com',
63
- template: 'welcome_email',
64
- vars: { userName: '张三' },
65
- })
66
43
 
67
- // 发送短信(通过 extra 传递 Provider 特有参数)
68
- await reach.send({
69
- provider: 'sms',
70
- to: '13800138000',
71
- extra: { templateCode: 'SMS_123456' },
72
- vars: { code: '123456' },
44
+ const initialized = await reach.init({
45
+ providers: [{ name: 'local', type: 'console' }],
46
+ templates: [{ name: 'welcome', provider: 'local', subject: 'Welcome', body: 'Hello {userName}' }],
73
47
  })
74
-
75
- // 关闭所有 Provider
76
- await reach.close()
48
+ if (!initialized.success)
49
+ throw new Error(initialized.error.message)
50
+ try {
51
+ const result = await reach.send({
52
+ to: 'user@example.com',
53
+ template: 'welcome',
54
+ vars: { userName: 'Alice' },
55
+ })
56
+ if (!result.success)
57
+ throw new Error(result.error.message)
58
+ }
59
+ finally {
60
+ await reach.close()
61
+ }
77
62
  ```
78
63
 
64
+ この例は開発用 Console Provider で送信メタデータを記録し、実メールや SMS は送りません。実送信には下記の SMTP・SMS・API を設定し、必要に応じて永続化と共有キャッシュを初期化してください。
65
+
79
66
  `reach.config` は**機密情報をマスクした設定スナップショット**を返します。`api.url` / `endpoint` 内の認証情報、`headers.Authorization`、SMTP の `pass`、SMS の鍵などは自動的に非表示になります。
80
67
 
81
- ## 設定
68
+ ### 設定
82
69
 
83
- ### Console(開発・テスト用)
70
+ #### Console(開発・テスト用)
84
71
 
85
72
  ```ts
86
73
  const config = { name: 'dev', type: 'console' }
87
74
  ```
88
75
 
89
- ### SMTP
76
+ #### SMTP
90
77
 
91
78
  | フィールド | 型 | 必須 | 既定値 | 説明 |
92
79
  | ---------- | --------- | ---- | ------ | ----------------------- |
@@ -99,7 +86,7 @@ const config = { name: 'dev', type: 'console' }
99
86
  | `pass` | `string` | — | — | 認証パスワード |
100
87
  | `from` | `string` | ✅ | — | 送信元アドレス |
101
88
 
102
- ### Alibaba Cloud SMS
89
+ #### Alibaba Cloud SMS
103
90
 
104
91
  | フィールド | 型 | 必須 | 既定値 | 説明 |
105
92
  | ----------------- | -------- | ---- | ----------------------- | --------------------- |
@@ -110,7 +97,7 @@ const config = { name: 'dev', type: 'console' }
110
97
  | `signName` | `string` | ✅ | — | SMS 署名 |
111
98
  | `endpoint` | `string` | — | `dysmsapi.aliyuncs.com` | API endpoint |
112
99
 
113
- ### API 通知
100
+ #### API 通知
114
101
 
115
102
  | フィールド | 型 | 必須 | 既定値 | 説明 |
116
103
  | ---------- | ------------------------ | ---- | ------- | ------------------------ |
@@ -123,7 +110,7 @@ const config = { name: 'dev', type: 'console' }
123
110
 
124
111
  API 通知先は信頼できるサーバー設定です。ログには origin のみを記録し、URL 認証情報、クエリ、宛先、外部の失敗応答本文は残しません。テナント・ユーザー入力の URL にはアプリでドメインの allowlist を設け、企業環境では出口プロキシやネットワーク方針でも内部メタデータへの接続を防いでください。
125
112
 
126
- ### DND(通知抑制)
113
+ #### DND(通知抑制)
127
114
 
128
115
  | フィールド | 型 | 必須 | 既定値 | 説明 |
129
116
  | ---------- | ---------------------- | ---- | --------- | --------------------------------------- |
@@ -132,7 +119,7 @@ API 通知先は信頼できるサーバー設定です。ログには origin
132
119
  | `start` | `string` | — | `00:00` | 開始時刻(HH:mm) |
133
120
  | `end` | `string` | — | `00:00` | 終了時刻(HH:mm) |
134
121
 
135
- ## エラー処理
122
+ ### エラー処理
136
123
 
137
124
  すべての操作は `HaiResult<T>` を返します。エラーコードは `HaiReachError` で定義します:
138
125
 
@@ -156,7 +143,7 @@ if (!result.success) {
156
143
  }
157
144
  ```
158
145
 
159
- ## 分散ロック
146
+ ### 分散ロック
160
147
 
161
148
  複数ノードでは DND delay の保留メッセージの flush を `@h-ai/cache` の分散ロックで保護し、同時に 1 ノードのみが実行します。
162
149
 
@@ -165,12 +152,12 @@ if (!result.success) {
165
152
  - `@h-ai/cache` が未初期化なら分散ロックは自動無効化し、単一ノードで動作します。
166
153
  - 安定したプロセス単位の owner 識別子で、ほかの所有者のロックの誤解除を防ぎます。
167
154
 
168
- ## テスト
155
+ ## 開発とテスト
169
156
 
170
157
  ```bash
171
158
  pnpm --filter @h-ai/reach test
172
159
  ```
173
160
 
174
- ## License
161
+ ## ライセンス
175
162
 
176
163
  Apache-2.0
package/README.md CHANGED
@@ -4,16 +4,13 @@
4
4
 
5
5
  [框架首页](../../README.md) · 相关模块:[@h-ai/cache](../cache/README.md) · [@h-ai/core](../core/README.md) · [@h-ai/reldb](../reldb/README.md)
6
6
 
7
+ ## 简介
8
+
7
9
  用户触达模块,支持同时注册多个 Provider(邮件、短信、API 回调等),通过模板绑定 Provider 实现按场景路由发送。内置免打扰(DND)机制,支持通过配置文件定义模板。
8
10
 
9
11
  模板发送只需 `template`、`to` 和 `vars`,Provider 默认来自模板;显式 `provider` 仍可覆盖。无模板的直接发送必须提供 Provider,缺少时返回失败 HaiResult。
10
12
 
11
- ## 依赖
12
-
13
- - `@h-ai/reldb` — 数据库(发送日志与模板持久化),可选;已初始化时自动启用持久化
14
- - `@h-ai/cache` — 缓存(DND delay 策略分布式锁),可选;已初始化时自动启用分布式锁
15
-
16
- ## 支持的 Provider
13
+ ## 核心功能清单
17
14
 
18
15
  | Provider | 渠道 | 说明 |
19
16
  | ------------ | ---- | ------------------------------------------- |
@@ -22,9 +19,16 @@
22
19
  | `aliyun-sms` | 短信 | 阿里云短信服务(直接调用 HTTP API,无 SDK) |
23
20
  | `api` | 通用 | HTTP API 回调(通用 webhook) |
24
21
 
25
- ## 快速开始
22
+ ## 使用入门
23
+
24
+ ### 依赖
25
+
26
+ - `@h-ai/reldb` — 数据库(发送日志与模板持久化),可选;已初始化时自动启用持久化
27
+ - `@h-ai/cache` — 缓存(DND delay 策略分布式锁),可选;已初始化时自动启用分布式锁
26
28
 
27
- ### 安装
29
+ ### 快速开始
30
+
31
+ #### 安装
28
32
 
29
33
  ```bash
30
34
  pnpm add @h-ai/reach
@@ -32,61 +36,44 @@ pnpm add @h-ai/reach
32
36
 
33
37
  邮件发送需安装 `nodemailer`。
34
38
 
35
- ### 多 Provider 初始化
39
+ #### 多 Provider 初始化
36
40
 
37
41
  ```ts
38
- import { cache } from '@h-ai/cache'
39
42
  import { reach } from '@h-ai/reach'
40
- import { reldb } from '@h-ai/reldb'
41
-
42
- // 1. 初始化可选依赖(按需)
43
- await reldb.init({ type: 'sqlite', database: './data.db' }) // 可选,启用发送日志与模板持久化
44
- await cache.init({ type: 'memory' }) // 可选,启用 DND delay 策略分布式锁
45
-
46
- // 2. 初始化触达模块(自动检测已初始化的 reldb/cache 单例)
47
- await reach.init({
48
- providers: [
49
- { name: 'email', type: 'smtp', host: 'smtp.example.com', from: 'noreply@example.com' },
50
- { name: 'sms', type: 'aliyun-sms', accessKeyId: '...', accessKeySecret: '...', signName: '某某科技' },
51
- { name: 'webhook', type: 'api', url: 'https://api.example.com/notify' },
52
- ],
53
- templates: [
54
- { name: 'welcome_email', provider: 'email', subject: '欢迎 {userName}', body: '亲爱的 {userName},欢迎!' },
55
- { name: 'sms_code', provider: 'sms', body: '验证码: {code},{minutes} 分钟内有效。' },
56
- ],
57
- dnd: { enabled: true, strategy: 'delay', start: '22:00', end: '08:00' },
58
- })
59
-
60
- // 通过 provider 字段选择发送渠道
61
- await reach.send({
62
- to: 'user@example.com',
63
- template: 'welcome_email',
64
- vars: { userName: '张三' },
65
- })
66
43
 
67
- // 发送短信(通过 extra 传递 Provider 特有参数)
68
- await reach.send({
69
- provider: 'sms',
70
- to: '13800138000',
71
- extra: { templateCode: 'SMS_123456' },
72
- vars: { code: '123456' },
44
+ const initialized = await reach.init({
45
+ providers: [{ name: 'local', type: 'console' }],
46
+ templates: [{ name: 'welcome', provider: 'local', subject: 'Welcome', body: 'Hello {userName}' }],
73
47
  })
74
-
75
- // 关闭所有 Provider
76
- await reach.close()
48
+ if (!initialized.success)
49
+ throw new Error(initialized.error.message)
50
+ try {
51
+ const result = await reach.send({
52
+ to: 'user@example.com',
53
+ template: 'welcome',
54
+ vars: { userName: 'Alice' },
55
+ })
56
+ if (!result.success)
57
+ throw new Error(result.error.message)
58
+ }
59
+ finally {
60
+ await reach.close()
61
+ }
77
62
  ```
78
63
 
64
+ 以上示例使用开发 Console Provider,只记录发送元数据,不实际发邮件或短信。需要真实投递时,按下方配置 SMTP、短信或 API Provider;数据库持久化和共享缓存按需初始化。
65
+
79
66
  `reach.config` 返回的是**脱敏后的配置快照**:`api.url` / `endpoint` 的内嵌凭证,以及 `headers.Authorization`、SMTP `pass`、短信密钥等敏感值会自动隐藏。
80
67
 
81
- ## 配置
68
+ ### 配置
82
69
 
83
- ### Console(开发/测试)
70
+ #### Console(开发/测试)
84
71
 
85
72
  ```ts
86
73
  const config = { name: 'dev', type: 'console' }
87
74
  ```
88
75
 
89
- ### SMTP
76
+ #### SMTP
90
77
 
91
78
  | 字段 | 类型 | 必填 | 默认值 | 说明 |
92
79
  | -------- | --------- | ---- | ------ | --------------- |
@@ -99,7 +86,7 @@ const config = { name: 'dev', type: 'console' }
99
86
  | `pass` | `string` | — | — | 认证密码 |
100
87
  | `from` | `string` | ✅ | — | 发件人地址 |
101
88
 
102
- ### 阿里云短信
89
+ #### 阿里云短信
103
90
 
104
91
  | 字段 | 类型 | 必填 | 默认值 | 说明 |
105
92
  | ----------------- | -------- | ---- | ----------------------- | ------------------- |
@@ -110,7 +97,7 @@ const config = { name: 'dev', type: 'console' }
110
97
  | `signName` | `string` | ✅ | — | 短信签名 |
111
98
  | `endpoint` | `string` | — | `dysmsapi.aliyuncs.com` | API 端点 |
112
99
 
113
- ### API 回调
100
+ #### API 回调
114
101
 
115
102
  | 字段 | 类型 | 必填 | 默认值 | 说明 |
116
103
  | --------- | ------------------------ | ---- | ------- | ---------------- |
@@ -123,7 +110,7 @@ const config = { name: 'dev', type: 'console' }
123
110
 
124
111
  API 回调地址属于服务端可信配置。日志只记录回调 origin,不记录 URL 凭据、查询参数、收件人或第三方失败响应体。若回调地址来自租户/用户输入,应用层必须配置域名 allowlist;企业部署还应通过出口代理或网络策略阻止访问内网元数据地址。
125
112
 
126
- ### DND(免打扰)
113
+ #### DND(免打扰)
127
114
 
128
115
  | 字段 | 类型 | 必填 | 默认值 | 说明 |
129
116
  | ---------- | ---------------------- | ---- | --------- | --------------------------------------- |
@@ -132,7 +119,7 @@ API 回调地址属于服务端可信配置。日志只记录回调 origin,不
132
119
  | `start` | `string` | — | `00:00` | 开始时间(HH:mm 格式) |
133
120
  | `end` | `string` | — | `00:00` | 结束时间(HH:mm 格式) |
134
121
 
135
- ## 错误处理
122
+ ### 错误处理
136
123
 
137
124
  所有操作返回 `HaiResult<T>`,错误码定义在 `HaiReachError`:
138
125
 
@@ -156,7 +143,7 @@ if (!result.success) {
156
143
  }
157
144
  ```
158
145
 
159
- ## 分布式锁
146
+ ### 分布式锁
160
147
 
161
148
  多节点部署时,DND(delay 策略)的 pending 消息 flush 操作通过 `@h-ai/cache` 分布式锁保护,确保同一时刻只有一个节点执行 flush。
162
149
 
@@ -165,12 +152,12 @@ if (!result.success) {
165
152
  - 若 `@h-ai/cache` 未初始化,分布式锁自动禁用,不影响单节点运行
166
153
  - 使用稳定的进程级 owner 标识,防止误释放他人锁
167
154
 
168
- ## 测试
155
+ ## 开发测试
169
156
 
170
157
  ```bash
171
158
  pnpm --filter @h-ai/reach test
172
159
  ```
173
160
 
174
- ## License
161
+ ## 许可证
175
162
 
176
163
  Apache-2.0
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@h-ai/reach",
3
3
  "type": "module",
4
- "version": "0.1.0-alpha.65",
4
+ "version": "0.1.0-alpha.67",
5
5
  "description": "Hai Framework user reach module for email, SMS, and webhook channels.",
6
6
  "license": "Apache-2.0",
7
7
  "exports": {
@@ -23,9 +23,9 @@
23
23
  "node": "^22.22.2 || ^24.15.0 || >=26.0.0"
24
24
  },
25
25
  "dependencies": {
26
- "@h-ai/cache": "0.1.0-alpha.65",
27
- "@h-ai/core": "0.1.0-alpha.65",
28
- "@h-ai/reldb": "0.1.0-alpha.65",
26
+ "@h-ai/cache": "0.1.0-alpha.67",
27
+ "@h-ai/core": "0.1.0-alpha.67",
28
+ "@h-ai/reldb": "0.1.0-alpha.67",
29
29
  "zod": "^4.6.5"
30
30
  },
31
31
  "optionalDependencies": {