@h-ai/reach 0.1.0-alpha.74 → 0.1.0-alpha.76

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/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../messages/en-US.json","../messages/ja-JP.json","../messages/zh-CN.json","../src/reach-i18n.ts","../src/reach-config.ts","../src/reach-types.ts","../src/providers/reach-provider-aliyun-sms.ts","../src/providers/reach-provider-api.ts","../src/providers/reach-provider-console.ts","../src/providers/reach-provider-smtp.ts","../src/reach-delivery.ts","../src/reach-send-sensitive.ts","../src/reach-send.ts","../src/reach-template.ts","../src/reach-migration.ts","../src/repositories/reach-repository-send-log.ts","../src/repositories/reach-repository-template.ts","../src/reach-main.ts"],"names":["core","logger","toReachError","err","ok","require","templateRegistry","dndConfig","providers","randomUUID","commitResult","TABLE_NAME","BaseReldbCrudRepository","reldb","process"],"mappings":"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","file":"index.js","sourcesContent":["{\n \"$schema\": \"https://inlang.com/schema/inlang-message-format\",\n \"reach_notInitialized\": \"Reach module not initialized, call reach.init() first\",\n \"reach_initFailed\": \"Reach module initialization failed: {error}\",\n \"reach_unsupportedType\": \"Unsupported reach type: {type}\",\n \"reach_sendFailed\": \"Send failed: {error}\",\n \"reach_templateNotFound\": \"Template not found: {template}\",\n \"reach_templateRenderFailed\": \"Template render failed: {error}\",\n \"reach_invalidRecipient\": \"Invalid recipient: {recipient}\",\n \"reach_providerNotFound\": \"Provider not found: {provider}\",\n \"reach_providerRequired\": \"No provider specified, set provider field in message or template\",\n \"reach_dndBlocked\": \"Message discarded by Do Not Disturb period\",\n \"reach_dndPersistenceRequired\": \"DND delay requires an initialized database and a ready queue table\",\n \"reach_dndEnqueueFailed\": \"The message could not be reliably queued; try again later\",\n \"reach_sendRecordSaveFailed\": \"Failed to persist the reach send record\",\n \"reach_invalidStoredInteger\": \"Send record {field} is not a safe non-negative integer\",\n \"reach_claimLost\": \"The valid delivery lease was lost\",\n \"reach_unknownResolutionFailed\": \"Unknown delivery was not found or has already been resolved\",\n \"reach_queueInputInvalid\": \"Invalid queue receipt or manual resolution parameters\",\n \"reach_deliveryUnknown\": \"The external delivery outcome is unknown; manual review is required and automatic redelivery has stopped\",\n \"reach_smsSendFailed\": \"SMS send failed: {error}\",\n \"reach_emailSendFailed\": \"Email send failed: {error}\",\n \"reach_apiSendFailed\": \"API callback send failed: {error}\",\n \"reach_configError\": \"Reach config validation failed: {error}\",\n \"reach_config_nameRequired\": \"Provider name is required\",\n \"reach_config_hostRequired\": \"SMTP host is required\",\n \"reach_config_fromRequired\": \"Sender address (from) is required\",\n \"reach_config_accessKeyIdRequired\": \"AccessKey ID is required\",\n \"reach_config_accessKeySecretRequired\": \"AccessKey Secret is required\",\n \"reach_config_signNameRequired\": \"SMS sign name is required\",\n \"reach_config_urlRequired\": \"Callback URL is required\",\n \"reach_config_dndTimeInvalid\": \"Invalid time format, must be HH:mm (24-hour)\",\n \"reach_config_templateNameRequired\": \"Template name is required\",\n \"reach_config_templateProviderRequired\": \"Template provider is required\",\n \"reach_config_templateBodyRequired\": \"Template body is required\",\n \"reach_config_providersRequired\": \"At least one provider is required\",\n \"reach_templateDbInitFailed\": \"Template database initialization failed: {error}\",\n \"reach_templateSaveFailed\": \"Template save failed: {error}\",\n \"reach_templateDeleteFailed\": \"Template delete failed: {error}\",\n \"reach_queueRetryInvalid\": \"maxRetryDelayMs must be at least retryDelayMs\",\n \"reach_deadRequeueFailed\": \"Dead message changed or does not exist; reload its state before restoring\",\n \"reach_productionProviderRequired\": \"Production requires a delivery provider; console is available only in development or explicit test mode\",\n \"reach_sensitivePolicyRequired\": \"Credential notifications require a database receipt, stable idempotency key and expiry\",\n \"reach_sensitiveDeliveryUnconfirmed\": \"Credential delivery is unconfirmed and will not be retried automatically. Check the receipt or request new credentials.\",\n \"reach_messageExpired\": \"The credentials expired before delivery\",\n \"reach_apiResponseTooLarge\": \"API acknowledgement exceeds the 64 KiB limit\",\n \"reach_apiResponseInvalid\": \"API acknowledgement must be empty or a JSON object with an optional messageId string\",\n \"reach_apiRequestFailed\": \"API request failed or timed out; delivery is unconfirmed\",\n \"reach_apiHttpsRequired\": \"This API policy requires HTTPS and disallows automatic redirects\"\n}\n","{\n \"$schema\": \"https://inlang.com/schema/inlang-message-format\",\n \"reach_notInitialized\": \"通知モジュールが初期化されていません。先にreach.init()を呼び出してください\",\n \"reach_initFailed\": \"通知モジュールの初期化に失敗しました: {error}\",\n \"reach_unsupportedType\": \"未対応の通知の種類です: {type}\",\n \"reach_sendFailed\": \"送信に失敗しました: {error}\",\n \"reach_templateNotFound\": \"テンプレートが見つかりません: {template}\",\n \"reach_templateRenderFailed\": \"テンプレートの描画に失敗しました: {error}\",\n \"reach_invalidRecipient\": \"宛先が無効です: {recipient}\",\n \"reach_providerNotFound\": \"プロバイダーが見つかりません: {provider}\",\n \"reach_providerRequired\": \"プロバイダーが指定されていません。メッセージまたはテンプレートのproviderを設定してください\",\n \"reach_dndBlocked\": \"通知を停止する時間帯のため、メッセージを破棄しました\",\n \"reach_dndPersistenceRequired\": \"通知制限時間帯の配信延期には初期化済みのデータベースと準備済みのキューテーブルが必要です\",\n \"reach_dndEnqueueFailed\": \"メッセージを確実にキューへ登録できませんでした。しばらくしてから再試行してください\",\n \"reach_sendRecordSaveFailed\": \"配信記録の永続化に失敗しました\",\n \"reach_invalidStoredInteger\": \"配信記録の {field} は安全な非負整数ではありません\",\n \"reach_claimLost\": \"有効な配信リースが失われました\",\n \"reach_unknownResolutionFailed\": \"結果不明の配信が見つからないか、既に解決されています\",\n \"reach_queueInputInvalid\": \"キューの受領記録または手動解決のパラメーターが無効です\",\n \"reach_deliveryUnknown\": \"外部への配信結果は不明です。手動確認が必要なため、自動再配信を停止しました\",\n \"reach_smsSendFailed\": \"SMSの送信に失敗しました: {error}\",\n \"reach_emailSendFailed\": \"メールの送信に失敗しました: {error}\",\n \"reach_apiSendFailed\": \"APIコールバックの送信に失敗しました: {error}\",\n \"reach_configError\": \"通知設定の検証に失敗しました: {error}\",\n \"reach_config_nameRequired\": \"プロバイダー名は必須です\",\n \"reach_config_hostRequired\": \"SMTPホストは必須です\",\n \"reach_config_fromRequired\": \"送信者アドレス(from)は必須です\",\n \"reach_config_accessKeyIdRequired\": \"AccessKey IDは必須です\",\n \"reach_config_accessKeySecretRequired\": \"AccessKey Secretは必須です\",\n \"reach_config_signNameRequired\": \"SMS署名は必須です\",\n \"reach_config_urlRequired\": \"コールバックURLは必須です\",\n \"reach_config_dndTimeInvalid\": \"時刻形式が無効です。HH:mm(24時間制)で指定してください\",\n \"reach_config_templateNameRequired\": \"テンプレート名は必須です\",\n \"reach_config_templateProviderRequired\": \"テンプレートのプロバイダーは必須です\",\n \"reach_config_templateBodyRequired\": \"テンプレート本文は必須です\",\n \"reach_config_providersRequired\": \"少なくとも1つのプロバイダーが必要です\",\n \"reach_templateDbInitFailed\": \"テンプレートデータベースの初期化に失敗しました: {error}\",\n \"reach_templateSaveFailed\": \"テンプレートの保存に失敗しました: {error}\",\n \"reach_templateDeleteFailed\": \"テンプレートの削除に失敗しました: {error}\",\n \"reach_queueRetryInvalid\": \"maxRetryDelayMs は retryDelayMs 以上にしてください\",\n \"reach_deadRequeueFailed\": \"配信不能メッセージが変更されたか、存在しません。復元する前に状態を再読み込みしてください\",\n \"reach_productionProviderRequired\": \"本番環境には配信プロバイダーが必要です。console は開発環境または明示的なテストモードでのみ使用できます\",\n \"reach_sensitivePolicyRequired\": \"認証情報の通知にはデータベースの受領記録、固定の冪等キーと有効期限が必要です\",\n \"reach_sensitiveDeliveryUnconfirmed\": \"認証情報の配信を確認できません。自動再送せず、受領記録の確認または認証情報の再発行が必要です。\",\n \"reach_messageExpired\": \"認証情報の期限が切れたため配信しませんでした\",\n \"reach_apiResponseTooLarge\": \"API 応答が 64 KiB の上限を超えています\",\n \"reach_apiResponseInvalid\": \"API 応答は空か、省略可能な messageId 文字列を持つ JSON オブジェクトである必要があります\",\n \"reach_apiRequestFailed\": \"API リクエストが失敗またはタイムアウトしました。配信結果は未確認です\",\n \"reach_apiHttpsRequired\": \"この API ポリシーでは HTTPS が必要で、自動リダイレクトは禁止されています\"\n}\n","{\n \"$schema\": \"https://inlang.com/schema/inlang-message-format\",\n \"reach_notInitialized\": \"触达模块尚未初始化,请先调用 reach.init()\",\n \"reach_initFailed\": \"触达模块初始化失败: {error}\",\n \"reach_unsupportedType\": \"不支持的触达类型: {type}\",\n \"reach_sendFailed\": \"发送失败: {error}\",\n \"reach_templateNotFound\": \"模板未找到: {template}\",\n \"reach_templateRenderFailed\": \"模板渲染失败: {error}\",\n \"reach_invalidRecipient\": \"无效的接收方: {recipient}\",\n \"reach_providerNotFound\": \"Provider 未找到: {provider}\",\n \"reach_providerRequired\": \"未指定 Provider,请在消息或模板中设置 provider 字段\",\n \"reach_dndBlocked\": \"当前处于免打扰时段,消息已丢弃\",\n \"reach_dndPersistenceRequired\": \"启用免打扰延迟发送需要先初始化数据库并确保队列表可用\",\n \"reach_dndEnqueueFailed\": \"消息未能可靠保存到延迟队列,请稍后重试\",\n \"reach_sendRecordSaveFailed\": \"保存触达发送记录失败\",\n \"reach_invalidStoredInteger\": \"发送记录的 {field} 不是安全的非负整数\",\n \"reach_claimLost\": \"消息的有效投递租约已丢失\",\n \"reach_unknownResolutionFailed\": \"未知投递结果不存在或已被其它操作裁定\",\n \"reach_queueInputInvalid\": \"队列回执或人工裁定参数无效\",\n \"reach_deliveryUnknown\": \"外部投递结果未知,需要人工核对,已停止自动重发\",\n \"reach_smsSendFailed\": \"短信发送失败: {error}\",\n \"reach_emailSendFailed\": \"邮件发送失败: {error}\",\n \"reach_apiSendFailed\": \"API 回调发送失败: {error}\",\n \"reach_configError\": \"触达配置校验失败:{error}\",\n \"reach_config_nameRequired\": \"Provider 名称不能为空\",\n \"reach_config_hostRequired\": \"SMTP 服务器地址不能为空\",\n \"reach_config_fromRequired\": \"发件人地址不能为空\",\n \"reach_config_accessKeyIdRequired\": \"AccessKey ID 不能为空\",\n \"reach_config_accessKeySecretRequired\": \"AccessKey Secret 不能为空\",\n \"reach_config_signNameRequired\": \"短信签名不能为空\",\n \"reach_config_urlRequired\": \"回调 URL 不能为空\",\n \"reach_config_dndTimeInvalid\": \"时间格式无效,须为 HH:mm(24 小时制)\",\n \"reach_config_templateNameRequired\": \"模板名称不能为空\",\n \"reach_config_templateProviderRequired\": \"模板绑定的 Provider 不能为空\",\n \"reach_config_templateBodyRequired\": \"模板正文不能为空\",\n \"reach_config_providersRequired\": \"至少需要配置一个 Provider\",\n \"reach_templateDbInitFailed\": \"模板数据库初始化失败: {error}\",\n \"reach_templateSaveFailed\": \"模板保存失败: {error}\",\n \"reach_templateDeleteFailed\": \"模板删除失败: {error}\",\n \"reach_queueRetryInvalid\": \"maxRetryDelayMs 不能小于 retryDelayMs\",\n \"reach_deadRequeueFailed\": \"死信已变化或不存在,请重新查询后恢复\",\n \"reach_productionProviderRequired\": \"生产环境必须配置真实触达服务;console 仅可用于开发或显式测试模式\",\n \"reach_sensitivePolicyRequired\": \"凭据通知需要数据库回执、稳定幂等键和有效期\",\n \"reach_sensitiveDeliveryUnconfirmed\": \"凭据投递尚未确认;不会自动重复发送,请查询回执或申请新凭据\",\n \"reach_messageExpired\": \"凭据已过期,未投递\",\n \"reach_apiResponseTooLarge\": \"API 回执超过 64 KiB 上限\",\n \"reach_apiResponseInvalid\": \"API 回执必须为空或含可选 messageId 字符串的 JSON 对象\",\n \"reach_apiRequestFailed\": \"API 请求失败或超时,投递结果未确认\",\n \"reach_apiHttpsRequired\": \"此 API 策略要求 HTTPS 且禁止自动重定向\"\n}\n","/**\n * @h-ai/reach — i18n\n *\n * 本文件提供触达模块的 i18n 文案访问入口。\n * @module reach-i18n\n */\n\nimport { core } from '@h-ai/core'\nimport messagesEnUS from '../messages/en-US.json'\nimport messagesJaJP from '../messages/ja-JP.json'\nimport messagesZhCN from '../messages/zh-CN.json'\n\n// ─── i18n ───\n\n/**\n * 内部消息键,由中文词典推导;中英词典同步维护。\n *\n * @example 模块内部上下文\n * ```ts\n * const key: ReachMessageKey = 'reach_notInitialized'\n * const message = reachM(key)\n * ```\n */\ntype ReachMessageKey = keyof typeof messagesZhCN\n\n/**\n * 获取当前locale的Reach消息;插值不应包含正文、收件人或凭据。\n * @param key - 已维护的消息键。\n * @param options - locale覆盖与params插值。\n * @returns 本地化消息。\n *\n * @example 模块内部上下文\n * ```ts\n * const message = reachM('reach_notInitialized', { locale: 'en-US' })\n * ```\n */\nexport const reachM = core.i18n.createMessageGetter<ReachMessageKey>({\n 'zh-CN': messagesZhCN,\n 'en-US': messagesEnUS,\n 'ja-JP': messagesJaJP,\n})\n","/**\n * @h-ai/reach — 配置 Schema\n *\n * 本文件定义触达模块的错误码、Zod Schema 和配置类型。\n * @module reach-config\n */\n\nimport { z } from 'zod'\n\nimport { reachM } from './reach-i18n.js'\n\n// ─── 单个 Provider 配置 Schema ───\n\n/**\n * 开发/测试日志适配器配置;不证明外部投递,生产共享状态守卫拒绝console。\n *\n * @example\n * ```ts\n * import { ConsoleProviderConfigSchema } from '@h-ai/reach'\n * const config = ConsoleProviderConfigSchema.parse({ name: 'dev-log', type: 'console' })\n * ```\n */\nexport const ConsoleProviderConfigSchema = z.object({\n /** Provider 唯一名称 */\n name: z.string().min(1, reachM('reach_config_nameRequired')),\n type: z.literal('console'),\n})\n\n/**\n * SMTP配置,默认TLS/465;凭据来自私有环境,SMTP没有接收端持久幂等能力声明。\n *\n * @example\n * ```ts\n * import { SmtpProviderConfigSchema } from '@h-ai/reach'\n * const config = SmtpProviderConfigSchema.parse({ name: 'email', type: 'smtp', host: 'smtp.example.com', from: 'noreply@example.com', user: process.env.HAI_REACH_PROVIDERS_0_USER, pass: process.env.HAI_REACH_PROVIDERS_0_PASS })\n * ```\n */\nexport const SmtpProviderConfigSchema = z.object({\n /** Provider 唯一名称 */\n name: z.string().min(1, reachM('reach_config_nameRequired')),\n type: z.literal('smtp'),\n /** SMTP 服务器地址 */\n host: z.string().min(1, reachM('reach_config_hostRequired')),\n /** SMTP 端口(默认 465) */\n port: z.number().int().min(1).max(65535).default(465),\n /** 是否使用 TLS(默认 true) */\n secure: z.boolean().default(true),\n /** SMTP 认证用户名 */\n user: z.string().optional(),\n /** SMTP 认证密码 */\n pass: z.string().optional(),\n /** 发件人地址 */\n from: z.string().min(1, reachM('reach_config_fromRequired')),\n})\n\n/**\n * 阿里云短信POP API配置;Secret只在服务端私有注入,发送需Provider模板编码等扩展参数。\n *\n * @example\n * ```ts\n * import { AliyunSmsProviderConfigSchema } from '@h-ai/reach'\n * const config = AliyunSmsProviderConfigSchema.parse({ name: 'sms', type: 'aliyun-sms', accessKeyId: process.env.HAI_REACH_PROVIDERS_0_ACCESSKEYID, accessKeySecret: process.env.HAI_REACH_PROVIDERS_0_ACCESSKEYSECRET, signName: process.env.HAI_REACH_PROVIDERS_0_SIGNNAME })\n * ```\n */\nexport const AliyunSmsProviderConfigSchema = z.object({\n /** Provider 唯一名称 */\n name: z.string().min(1, reachM('reach_config_nameRequired')),\n type: z.literal('aliyun-sms'),\n /** 阿里云 AccessKey ID */\n accessKeyId: z.string().min(1, reachM('reach_config_accessKeyIdRequired')),\n /** 阿里云 AccessKey Secret */\n accessKeySecret: z.string().min(1, reachM('reach_config_accessKeySecretRequired')),\n /** 短信签名 */\n signName: z.string().min(1, reachM('reach_config_signNameRequired')),\n /** API 端点(默认 dysmsapi.aliyuncs.com) */\n endpoint: z.string().default('dysmsapi.aliyuncs.com'),\n})\n\n/**\n * HTTP回调配置;idempotent默认false,只有接收端按稳定Idempotency-Key原子持久去重并覆盖旧请求迟到窗口才允许true。\n * 写入该布尔值或发送请求头本身不证明接收端具备此能力。\n *\n * @example\n * ```ts\n * import { ApiProviderConfigSchema } from '@h-ai/reach'\n * const config = ApiProviderConfigSchema.parse({ name: 'webhook', type: 'api', url: 'https://notify.example.com/events', idempotent: false })\n * ```\n */\nexport const ApiProviderConfigSchema = z.object({\n /** Provider 唯一名称 */\n name: z.string().min(1, reachM('reach_config_nameRequired')),\n type: z.literal('api'),\n /** 回调 URL */\n url: z.string()\n .url(reachM('reach_config_urlRequired'))\n .refine(value => ['http:', 'https:'].includes(new URL(value).protocol), {\n message: reachM('reach_config_urlRequired'),\n }),\n /** HTTP 方法(默认 POST) */\n method: z.enum(['POST', 'PUT']).default('POST'),\n /** 自定义请求头 */\n headers: z.record(z.string(), z.string()).optional(),\n /** 请求超时毫秒数(默认 10000) */\n timeout: z.number().int().min(0).default(10000),\n /** HTTP 重定向策略;默认拒绝。敏感发送始终拒绝,即使普通发送显式允许。 */\n redirect: z.enum(['error', 'follow']).default('error'),\n /** 强制 HTTPS;省略时生产敏感发送要求 HTTPS,显式 false 仅用于受信内网传输。 */\n requireHttps: z.boolean().optional(),\n /** 接收端按 Idempotency-Key 原子持久去重,且覆盖所有重试与旧节点迟到请求时才启用;默认 false。 */\n idempotent: z.boolean().default(false),\n})\n\n/**\n * 按type区分内置Provider配置,不把name当协议类型;name用于模板/消息路由。\n *\n * @example\n * ```ts\n * import { ProviderConfigSchema } from '@h-ai/reach'\n * const config = ProviderConfigSchema.parse({ name: 'webhook', type: 'api', url: 'https://notify.example.com/events', idempotent: false })\n * ```\n */\nexport const ProviderConfigSchema = z.discriminatedUnion('type', [\n ConsoleProviderConfigSchema,\n SmtpProviderConfigSchema,\n AliyunSmsProviderConfigSchema,\n ApiProviderConfigSchema,\n])\n\n/**\n * 由ProviderConfigSchema解析后的配置类型,默认值已经填充;与输入类型区分,秘密字段不写日志。\n *\n * @example\n * ```ts\n * import { ProviderConfigSchema, type ProviderConfig } from '@h-ai/reach'\n * const config: ProviderConfig = ProviderConfigSchema.parse({ name: 'webhook', type: 'api', url: 'https://notify.example.com/events', idempotent: false })\n * ```\n */\nexport type ProviderConfig = z.infer<typeof ProviderConfigSchema>\n\n/**\n * 由ConsoleProviderConfigSchema解析后的配置类型,默认值已经填充;与输入类型区分,秘密字段不写日志。\n *\n * @example\n * ```ts\n * import { ConsoleProviderConfigSchema, type ConsoleProviderConfig } from '@h-ai/reach'\n * const config: ConsoleProviderConfig = ConsoleProviderConfigSchema.parse({ name: 'dev-log', type: 'console' })\n * ```\n */\nexport type ConsoleProviderConfig = z.infer<typeof ConsoleProviderConfigSchema>\n\n/**\n * 由SmtpProviderConfigSchema解析后的配置类型,默认值已经填充;与输入类型区分,秘密字段不写日志。\n *\n * @example\n * ```ts\n * import { SmtpProviderConfigSchema, type SmtpProviderConfig } from '@h-ai/reach'\n * const config: SmtpProviderConfig = SmtpProviderConfigSchema.parse({ name: 'email', type: 'smtp', host: 'smtp.example.com', from: 'noreply@example.com', user: process.env.HAI_REACH_PROVIDERS_0_USER, pass: process.env.HAI_REACH_PROVIDERS_0_PASS })\n * ```\n */\nexport type SmtpProviderConfig = z.infer<typeof SmtpProviderConfigSchema>\n\n/**\n * 由AliyunSmsProviderConfigSchema解析后的配置类型,默认值已经填充;与输入类型区分,秘密字段不写日志。\n *\n * @example\n * ```ts\n * import { AliyunSmsProviderConfigSchema, type AliyunSmsProviderConfig } from '@h-ai/reach'\n * const config: AliyunSmsProviderConfig = AliyunSmsProviderConfigSchema.parse({ name: 'sms', type: 'aliyun-sms', accessKeyId: process.env.HAI_REACH_PROVIDERS_0_ACCESSKEYID, accessKeySecret: process.env.HAI_REACH_PROVIDERS_0_ACCESSKEYSECRET, signName: process.env.HAI_REACH_PROVIDERS_0_SIGNNAME })\n * ```\n */\nexport type AliyunSmsProviderConfig = z.infer<typeof AliyunSmsProviderConfigSchema>\n\n/**\n * 由ApiProviderConfigSchema解析后的配置类型,默认值已经填充;与输入类型区分,秘密字段不写日志。\n *\n * @example\n * ```ts\n * import { ApiProviderConfigSchema, type ApiProviderConfig } from '@h-ai/reach'\n * const config: ApiProviderConfig = ApiProviderConfigSchema.parse({ name: 'webhook', type: 'api', url: 'https://notify.example.com/events', idempotent: false })\n * ```\n */\nexport type ApiProviderConfig = z.infer<typeof ApiProviderConfigSchema>\n\n/**\n * 人工处置unknown的结构边界,仅sent/discarded,不提供重发;应用须先校验运维权限并隔离旧worker/核对外部效果。\n *\n * @example\n * ```ts\n * import { ReachUnknownResolutionSchema } from '@h-ai/reach'\n * const config = ReachUnknownResolutionSchema.parse({ status: 'discarded' })\n * ```\n */\nexport const ReachUnknownResolutionSchema = z.object({\n status: z.enum(['sent', 'discarded']),\n messageId: z.string().max(255).optional(),\n})\n\n/**\n * 持久入队回执UUID格式;合法UUID不代表存在,也不授予队列运维权限。\n *\n * @example\n * ```ts\n * import { ReachQueueIdSchema } from '@h-ai/reach'\n * const config = ReachQueueIdSchema.parse('00000000-0000-4000-8000-000000000001')\n * ```\n */\nexport const ReachQueueIdSchema = z.string().uuid()\n\n/**\n * 可靠队列重试策略,入队时随消息持久保存;maxAttempts含首次与崩溃恢复,指数退避有上限。\n *\n * @example\n * ```ts\n * import { ReachQueueConfigSchema } from '@h-ai/reach'\n * const config = ReachQueueConfigSchema.parse({ maxAttempts: 5, retryDelayMs: 60000, maxRetryDelayMs: 3600000 })\n * ```\n */\nexport const ReachQueueConfigSchema = z.object({\n /** 自动领取次数上限(含首次及崩溃恢复,默认 5,范围 1–100)。 */\n maxAttempts: z.number().int().min(1).max(100).default(5),\n /** 首次失败后的退避毫秒数(默认 60000,范围 1000–86400000)。 */\n retryDelayMs: z.number().int().min(1000).max(86_400_000).default(60_000),\n /** 指数退避上限毫秒数,不得小于 retryDelayMs(默认 3600000)。 */\n maxRetryDelayMs: z.number().int().min(1000).max(86_400_000).default(3_600_000),\n}).refine(value => value.maxRetryDelayMs >= value.retryDelayMs, {\n message: reachM('reach_queueRetryInvalid'),\n})\n\n/**\n * 由ReachQueueConfigSchema解析后的配置类型,默认值已经填充;与输入类型区分,秘密字段不写日志。\n *\n * @example\n * ```ts\n * import { ReachQueueConfigSchema, type ReachQueueConfig } from '@h-ai/reach'\n * const config: ReachQueueConfig = ReachQueueConfigSchema.parse({ maxAttempts: 5, retryDelayMs: 60000, maxRetryDelayMs: 3600000 })\n * ```\n */\nexport type ReachQueueConfig = z.infer<typeof ReachQueueConfigSchema>\n\n/**\n * 仅分页列出dead/unknown元数据,限制limit/offset;不返回正文/收件人,不替代服务端运维授权。\n *\n * @example\n * ```ts\n * import { ReachQueueListSchema } from '@h-ai/reach'\n * const config = ReachQueueListSchema.parse({ status: 'dead', limit: 10, offset: 0 })\n * ```\n */\nexport const ReachQueueListSchema = z.object({\n status: z.enum(['dead', 'unknown']),\n limit: z.number().int().min(1).max(100).default(50),\n offset: z.number().int().min(0).max(1_000_000).default(0),\n})\n\n/**\n * 恢复dead所需的最近updatedAt条件输入,阻止过时/并发恢复;unknown禁止此操作。\n *\n * @example\n * ```ts\n * import { ReachDeadRequeueSchema } from '@h-ai/reach'\n * const config = ReachDeadRequeueSchema.parse({ expectedUpdatedAt: 1730000000000 })\n * ```\n */\nexport const ReachDeadRequeueSchema = z.object({\n expectedUpdatedAt: z.number().int().nonnegative().max(Number.MAX_SAFE_INTEGER - 1),\n})\n\n// ─── DND(免打扰)配置 Schema ───\n\n/**\n * 本机时区的HH:mm免打扰配置,支持跨午夜;start=end表示不拦截。\n * discard返回DND_BLOCKED;delay要求数据库队列就绪且pending写入成功才返回deferred回执。\n *\n * @example\n * ```ts\n * import { DndConfigSchema } from '@h-ai/reach'\n * const config = DndConfigSchema.parse({ enabled: true, strategy: 'delay', start: '22:00', end: '08:00' })\n * ```\n */\nexport const DndConfigSchema = z.object({\n /** 是否启用免打扰(默认 false) */\n enabled: z.boolean().default(false),\n /** discard 丢弃;delay 要求数据库队列就绪并确认入队(默认 discard)。 */\n strategy: z.enum(['discard', 'delay']).default('discard'),\n /** 免打扰开始时间(HH:mm 格式) */\n start: z.string().regex(/^([01]\\d|2[0-3]):[0-5]\\d$/, reachM('reach_config_dndTimeInvalid')).default('00:00'),\n /** 免打扰结束时间(HH:mm 格式) */\n end: z.string().regex(/^([01]\\d|2[0-3]):[0-5]\\d$/, reachM('reach_config_dndTimeInvalid')).default('00:00'),\n})\n\n/**\n * 由DndConfigSchema解析后的配置类型,默认值已经填充;与输入类型区分,秘密字段不写日志。\n *\n * @example\n * ```ts\n * import { DndConfigSchema, type DndConfig } from '@h-ai/reach'\n * const config: DndConfig = DndConfigSchema.parse({ enabled: true, strategy: 'delay', start: '22:00', end: '08:00' })\n * ```\n */\nexport type DndConfig = z.infer<typeof DndConfigSchema>\n\n// ─── 模板配置 Schema ───\n\n/**\n * 绑定Provider名称的持久模板配置,{key}仅做文本替换;需要先初始化数据库,不提供HTML消毒。\n *\n * @example\n * ```ts\n * import { TemplateConfigSchema } from '@h-ai/reach'\n * const config = TemplateConfigSchema.parse({ name: 'welcome', provider: 'webhook', subject: 'Welcome {userName}', body: 'Hello {userName}' })\n * ```\n */\nexport const TemplateConfigSchema = z.object({\n /** 模板名称 */\n name: z.string().min(1, reachM('reach_config_templateNameRequired')),\n /** 绑定的 Provider 名称 */\n provider: z.string().min(1, reachM('reach_config_templateProviderRequired')),\n /** 邮件主题模板 */\n subject: z.string().optional(),\n /** 正文模板 */\n body: z.string().min(1, reachM('reach_config_templateBodyRequired')),\n})\n\n/**\n * 由TemplateConfigSchema解析后的配置类型,默认值已经填充;与输入类型区分,秘密字段不写日志。\n *\n * @example\n * ```ts\n * import { TemplateConfigSchema, type TemplateConfig } from '@h-ai/reach'\n * const config: TemplateConfig = TemplateConfigSchema.parse({ name: 'welcome', provider: 'webhook', subject: 'Welcome {userName}', body: 'Hello {userName}' })\n * ```\n */\nexport type TemplateConfig = z.infer<typeof TemplateConfigSchema>\n\n// ─── 模块配置 Schema ───\n\n/**\n * 模块配置Schema,解析Provider/模板/DND及持久重试默认值;Schema校验不证明外部服务可用或接收端幂等。\n *\n * @example\n * ```ts\n * import { ReachConfigSchema } from '@h-ai/reach'\n * const config = ReachConfigSchema.parse({ providers: [{ name: 'webhook', type: 'api', url: 'https://notify.example.com/events', idempotent: false }], templates: [{ name: 'welcome', provider: 'webhook', subject: 'Welcome {userName}', body: 'Hello {userName}' }], dnd: { enabled: true, strategy: 'delay', start: '22:00', end: '08:00' } })\n * ```\n */\nexport const ReachConfigSchema = z.object({\n /** Provider 配置列表 */\n providers: z.array(ProviderConfigSchema).min(1, reachM('reach_config_providersRequired')),\n /** 模板配置(通过配置文件注册) */\n templates: z.array(TemplateConfigSchema).optional(),\n /** 免打扰配置 */\n dnd: DndConfigSchema.optional(),\n /** 可靠队列重试策略;每条消息入队时持久保存。 */\n queue: ReachQueueConfigSchema.prefault({}),\n})\n\n/**\n * 由ReachConfigSchema解析后的配置类型,默认值已经填充;与输入类型区分,秘密字段不写日志。\n *\n * @example\n * ```ts\n * import { ReachConfigSchema, type ReachConfig } from '@h-ai/reach'\n * const config: ReachConfig = ReachConfigSchema.parse({ providers: [{ name: 'webhook', type: 'api', url: 'https://notify.example.com/events', idempotent: false }], templates: [{ name: 'welcome', provider: 'webhook', subject: 'Welcome {userName}', body: 'Hello {userName}' }], dnd: { enabled: true, strategy: 'delay', start: '22:00', end: '08:00' } })\n * ```\n */\nexport type ReachConfig = z.infer<typeof ReachConfigSchema>\n\n/**\n * reach.init接收的配置输入,queue默认值可省略;生产选真实Provider、共享数据库,不能用console假定通知完成。\n *\n * @example\n * ```ts\n * import { ReachConfigSchema, type ReachConfigInput } from '@h-ai/reach'\n * const input: ReachConfigInput = { providers: [{ name: 'webhook', type: 'api', url: 'https://notify.example.com/events', idempotent: false }], templates: [{ name: 'welcome', provider: 'webhook', subject: 'Welcome {userName}', body: 'Hello {userName}' }], dnd: { enabled: true, strategy: 'delay', start: '22:00', end: '08:00' } }\n * const parsed = ReachConfigSchema.parse(input)\n * ```\n */\nexport type ReachConfigInput = z.input<typeof ReachConfigSchema>\n","/**\n * @h-ai/reach — 类型定义\n *\n * 本文件定义触达模块的核心接口和类型。\n * @module reach-types\n */\n\nimport type { ErrorInfo, HaiResult } from '@h-ai/core'\nimport type { ReachConfig, ReachConfigInput } from './reach-config.js'\nimport { core } from '@h-ai/core'\n\n// ─── 错误定义(照 @h-ai/core 范式) ───\n\nconst ReachErrorInfo = {\n SEND_FAILED: '001:500',\n TEMPLATE_NOT_FOUND: '002:404',\n TEMPLATE_RENDER_FAILED: '003:500',\n INVALID_RECIPIENT: '004:400',\n PROVIDER_NOT_FOUND: '005:500',\n DND_BLOCKED: '006:403',\n NOT_INITIALIZED: '010:500',\n UNSUPPORTED_TYPE: '011:400',\n CONFIG_ERROR: '012:500',\n DELIVERY_REJECTED: '020:422',\n MESSAGE_EXPIRED: '021:410',\n} as const satisfies ErrorInfo\n\n/**\n * Reach标准错误集合;DND拒绝、发送失败与配置错误均通过HaiResult判断,不把失败当已入队。\n *\n * @example\n * ```ts\n * import { reach, HaiReachError } from '@h-ai/reach'\n * const result = await reach.send({ provider: 'webhook', to: 'user@example.com', body: 'hello' })\n * if (!result.success && result.error.code === HaiReachError.NOT_INITIALIZED.code) {\n * // 先执行依赖初始化,不能把未初始化失败当作触达成功。\n * const message = result.error.message\n * }\n * ```\n */\nexport const HaiReachError = core.error.buildHaiErrorsDef('reach', ReachErrorInfo)\n\n// ─── 渠道类型 ───\n\n/**\n * 渠道语义标签email/sms/api;消息路由实际使用配置中的Provider名称。\n *\n * @example\n * ```ts\n * import type { ReachChannel } from '@h-ai/reach'\n * const channel: ReachChannel = 'api'\n * ```\n */\nexport type ReachChannel = 'email' | 'sms' | 'api'\n\n// ─── 消息与模板 ───\n\n/**\n * 直接发送必须指定Provider名和内容;模板发送可由持久模板推导路由,vars做文本替换。\n * 队列幂等键固定为持久queueId;直接调用的业务重试由应用提供稳定idempotencyKey并核验接收端契约。\n *\n * @example\n * ```ts\n * import type { ReachMessage } from '@h-ai/reach'\n * const message: ReachMessage = { provider: 'webhook', to: 'user@example.com', template: 'welcome', vars: { userName: 'Alice' } }\n * ```\n */\nexport interface ReachMessage {\n /** 目标 Provider 名称;模板发送可省略,直接发送必须提供。 */\n provider?: string\n /** 接收方(邮箱地址或手机号) */\n to: string\n /** 邮件主题(直接发送时使用) */\n subject?: string\n /** 消息正文(直接发送时使用) */\n body?: string\n /** 模板名称(使用模板发送时指定) */\n template?: string\n /** 模板变量(使用模板发送时传入) */\n vars?: Record<string, string>\n /** Provider 扩展参数(如短信 Provider 的模板编码、签名等) */\n extra?: Record<string, unknown>\n /** API 接收端幂等键;直接发送可指定,队列投递固定使用持久化 queueId。 */\n idempotencyKey?: string\n /** 凭据通知只在内存中投递;正文、变量、收件人不持久化,不进入延迟或重投队列。 */\n sensitive?: boolean\n /** sensitive=true 时必填的凭据截止时间(Unix 毫秒),不延长业务凭据有效期。 */\n expiresAt?: number\n /** 遇到免打扰时交给 Reach 队列,或返回 DND_BLOCKED 让已有业务 outbox 延后;默认 queue。 */\n deferPolicy?: 'queue' | 'reject'\n}\n\n/**\n * 发送或可靠入队结果;deferred=true只证明持久pending写入成功,queueId用于后续查询,不代表已送达。\n *\n * @example\n * ```ts\n * import { reach, type SendResult } from '@h-ai/reach'\n * const { reldb } = await import('@h-ai/reldb')\n * const connected = await reldb.init({ type: 'postgresql', host: 'db', port: 5432, database: 'hai', user: 'postgres', password: process.env.HAI_DB_PASSWORD })\n * if (!connected.success) throw new Error(connected.error.message)\n * try {\n * const initialized = await reach.init({ providers: [{ name: 'webhook', type: 'api', url: 'https://notify.example.com/events', idempotent: false }], templates: [{ name: 'welcome', provider: 'webhook', subject: 'Welcome {userName}', body: 'Hello {userName}' }], dnd: { enabled: true, strategy: 'delay', start: '22:00', end: '08:00' } })\n * if (!initialized.success) throw new Error(initialized.error.message)\n * try {\n * const result = await reach.send({ provider: 'webhook', to: 'user@example.com', body: 'hello' })\n * if (!result.success) throw new Error(result.error.message)\n * const outcome: SendResult = result.data\n * if (outcome.deferred) {\n * const queued = await reach.queue.get(outcome.queueId)\n * if (!queued.success) throw new Error(queued.error.message)\n * }\n * }\n * finally { await reach.close() }\n * }\n * finally {\n * const closed = await reldb.close()\n * if (!closed.success) throw new Error(closed.error.message)\n * }\n * ```\n */\nexport type SendResult = {\n /** 无载荷凭据回执,可通过 queue.get 查询;不代表进入延迟队列。 */\n receiptId?: string\n /** 是否已完成 Provider 发送或可靠入队;具体状态同时检查 deferred。 */\n success: boolean\n /** Provider 返回的消息 ID(如有) */\n messageId?: string\n} & ({\n /** 已可靠入队,尚未投递。 */\n deferred: true\n /** 持久化回执 UUID,对应 hai_reach_send_log.queue_id。 */\n queueId: string\n} | {\n /** Provider 即时发送结果,不含入队回执。 */\n deferred?: false\n queueId?: never\n})\n\n/**\n * 持久队列状态;sending已开始外部效果,非幂等失败/失租约进unknown禁止自动重发;dead停止自动处理但可条件恢复。\n *\n * @example\n * ```ts\n * import type { ReachQueueStatus } from '@h-ai/reach'\n * const status: ReachQueueStatus = 'unknown'\n * ```\n */\nexport type ReachQueueStatus = 'pending' | 'processing' | 'sending' | 'unknown' | 'sent' | 'discarded' | 'dead'\n\n/**\n * 有界失败分类,不保存Provider响应正文、凭据或收件人;用于修复根因和处置死信。\n *\n * @example\n * ```ts\n * import type { ReachQueueFailure } from '@h-ai/reach'\n * const failure: ReachQueueFailure = 'provider_unavailable'\n * ```\n */\nexport type ReachQueueFailure = 'provider_unavailable' | 'invalid_message' | 'permanent_rejection' | 'transient_failure' | 'attempts_exhausted'\n\n/**\n * 不含消息正文/收件人的队列运维快照;时间为安全整数Unix毫秒,计数/策略来自持久消息。\n *\n * @example\n * ```ts\n * import { reach, type ReachQueueEntry } from '@h-ai/reach'\n * const { reldb } = await import('@h-ai/reldb')\n * const connected = await reldb.init({ type: 'postgresql', host: 'db', port: 5432, database: 'hai', user: 'postgres', password: process.env.HAI_DB_PASSWORD })\n * if (!connected.success) throw new Error(connected.error.message)\n * try {\n * const initialized = await reach.init({ providers: [{ name: 'webhook', type: 'api', url: 'https://notify.example.com/events', idempotent: false }], templates: [{ name: 'welcome', provider: 'webhook', subject: 'Welcome {userName}', body: 'Hello {userName}' }], dnd: { enabled: true, strategy: 'delay', start: '22:00', end: '08:00' } })\n * if (!initialized.success) throw new Error(initialized.error.message)\n * try {\n * const listed = await reach.queue.list({ status: 'dead', limit: 10 })\n * if (!listed.success) throw new Error(listed.error.message)\n * const entry: ReachQueueEntry | undefined = listed.data[0]\n * if (entry) { const attempts = entry.attemptCount }\n * }\n * finally { await reach.close() }\n * }\n * finally {\n * const closed = await reldb.close()\n * if (!closed.success) throw new Error(closed.error.message)\n * }\n * ```\n */\nexport interface ReachQueueEntry {\n /** 持久化入队回执。 */\n queueId: string\n /** Provider 配置名称。 */\n provider: string\n /** 当前处理状态。 */\n status: ReachQueueStatus\n /** 最近更新时间(Unix 毫秒)。 */\n updatedAt?: number\n /** 外部 Provider 确认的消息编号。 */\n messageId?: string\n /** 已领取次数;节点崩溃后的重新领取也计数。 */\n attemptCount: number\n /** 此条消息持久保存的自动领取上限。 */\n maxAttempts: number\n /** 下次允许自动领取的 Unix 毫秒时间;终态为 0。 */\n nextAttemptAt: number\n /** 最近失败分类,便于可信运维流程排查与修复。 */\n lastFailure?: ReachQueueFailure\n}\n\n/**\n * 仅列出需人工处理的dead/unknown;分页有界,不提供凭据或替代运维权限。\n *\n * @example\n * ```ts\n * import { reach, type ReachQueueListOptions } from '@h-ai/reach'\n * const { reldb } = await import('@h-ai/reldb')\n * const connected = await reldb.init({ type: 'postgresql', host: 'db', port: 5432, database: 'hai', user: 'postgres', password: process.env.HAI_DB_PASSWORD })\n * if (!connected.success) throw new Error(connected.error.message)\n * try {\n * const initialized = await reach.init({ providers: [{ name: 'webhook', type: 'api', url: 'https://notify.example.com/events', idempotent: false }], templates: [{ name: 'welcome', provider: 'webhook', subject: 'Welcome {userName}', body: 'Hello {userName}' }], dnd: { enabled: true, strategy: 'delay', start: '22:00', end: '08:00' } })\n * if (!initialized.success) throw new Error(initialized.error.message)\n * try {\n * const options: ReachQueueListOptions = { status: 'unknown', limit: 10, offset: 0 }\n * const listed = await reach.queue.list(options)\n * if (!listed.success) throw new Error(listed.error.message)\n * }\n * finally { await reach.close() }\n * }\n * finally {\n * const closed = await reldb.close()\n * if (!closed.success) throw new Error(closed.error.message)\n * }\n * ```\n */\nexport interface ReachQueueListOptions {\n /** 需要人工处理的状态。 */\n status: 'dead' | 'unknown'\n /** 每页 1–100 条,默认 50。 */\n limit?: number\n /** 跳过条数,默认 0,上限 1000000。 */\n offset?: number\n}\n\n/**\n * dead条件恢复输入;expectedUpdatedAt必须来自最近快照,不能用本机当前时间冒充版本。\n * 先修根因并确认旧执行与接收端幂等边界,unknown不能重排。\n *\n * @example\n * ```ts\n * import { reach, type ReachDeadRequeue } from '@h-ai/reach'\n * const { reldb } = await import('@h-ai/reldb')\n * const connected = await reldb.init({ type: 'postgresql', host: 'db', port: 5432, database: 'hai', user: 'postgres', password: process.env.HAI_DB_PASSWORD })\n * if (!connected.success) throw new Error(connected.error.message)\n * try {\n * const initialized = await reach.init({ providers: [{ name: 'webhook', type: 'api', url: 'https://notify.example.com/events', idempotent: false }], templates: [{ name: 'welcome', provider: 'webhook', subject: 'Welcome {userName}', body: 'Hello {userName}' }], dnd: { enabled: true, strategy: 'delay', start: '22:00', end: '08:00' } })\n * if (!initialized.success) throw new Error(initialized.error.message)\n * try {\n * const listed = await reach.queue.list({ status: 'dead', limit: 10 })\n * if (!listed.success) throw new Error(listed.error.message)\n * const entry = listed.data[0]\n * if (entry && entry.updatedAt !== undefined) {\n * const options: ReachDeadRequeue = { expectedUpdatedAt: entry.updatedAt }\n * // 此调用仅在受信运维流程核验完成后执行。\n * const restored = await reach.queue.requeueDead(entry.queueId, options)\n * if (!restored.success) throw new Error(restored.error.message)\n * }\n * }\n * finally { await reach.close() }\n * }\n * finally {\n * const closed = await reldb.close()\n * if (!closed.success) throw new Error(closed.error.message)\n * }\n * ```\n */\nexport interface ReachDeadRequeue {\n /** 必须等于最近查询的更新时间,阻止并发及过时请求。 */\n expectedUpdatedAt: number\n}\n\n/**\n * unknown人工裁定,不提供自动重发;先校验运维权限、停止/隔离旧worker并核对Provider实际记录。\n * discarded只终止后续处理,不能撤销已开始的外部请求或已发送消息。\n *\n * @example\n * ```ts\n * import { reach, type ReachUnknownResolution } from '@h-ai/reach'\n * const { reldb } = await import('@h-ai/reldb')\n * const connected = await reldb.init({ type: 'postgresql', host: 'db', port: 5432, database: 'hai', user: 'postgres', password: process.env.HAI_DB_PASSWORD })\n * if (!connected.success) throw new Error(connected.error.message)\n * try {\n * const initialized = await reach.init({ providers: [{ name: 'webhook', type: 'api', url: 'https://notify.example.com/events', idempotent: false }], templates: [{ name: 'welcome', provider: 'webhook', subject: 'Welcome {userName}', body: 'Hello {userName}' }], dnd: { enabled: true, strategy: 'delay', start: '22:00', end: '08:00' } })\n * if (!initialized.success) throw new Error(initialized.error.message)\n * try {\n * const listed = await reach.queue.list({ status: 'unknown', limit: 10 })\n * if (!listed.success) throw new Error(listed.error.message)\n * const entry = listed.data[0]\n * if (entry) {\n * // 可信运维前置:旧worker已隔离且外部投递记录已核对。\n * const resolution: ReachUnknownResolution = { status: 'discarded' }\n * const resolved = await reach.queue.resolveUnknown(entry.queueId, resolution)\n * if (!resolved.success) throw new Error(resolved.error.message)\n * }\n * }\n * finally { await reach.close() }\n * }\n * finally {\n * const closed = await reldb.close()\n * if (!closed.success) throw new Error(closed.error.message)\n * }\n * ```\n */\nexport interface ReachUnknownResolution {\n /** 经外部核对确认已投递,或终止后续处理。discarded 不撤销已经开始的外部请求。 */\n status: 'sent' | 'discarded'\n /** 确认已投递时,可记录 Provider 消息编号,最多 255 个字符。 */\n messageId?: string\n}\n\n/**\n * 受信服务端队列运维入口,需应用先授权;只查询安全元数据,恢复/裁定均由数据库条件提交。\n *\n * @example\n * ```ts\n * import { reach } from '@h-ai/reach'\n * const { reldb } = await import('@h-ai/reldb')\n * const connected = await reldb.init({ type: 'postgresql', host: 'db', port: 5432, database: 'hai', user: 'postgres', password: process.env.HAI_DB_PASSWORD })\n * if (!connected.success) throw new Error(connected.error.message)\n * try {\n * const initialized = await reach.init({ providers: [{ name: 'webhook', type: 'api', url: 'https://notify.example.com/events', idempotent: false }], templates: [{ name: 'welcome', provider: 'webhook', subject: 'Welcome {userName}', body: 'Hello {userName}' }], dnd: { enabled: true, strategy: 'delay', start: '22:00', end: '08:00' } })\n * if (!initialized.success) throw new Error(initialized.error.message)\n * try {\n * const listed = await reach.queue.list({ status: 'unknown', limit: 10 })\n * if (!listed.success) throw new Error(listed.error.message)\n * const count = listed.data.length\n * }\n * finally { await reach.close() }\n * }\n * finally {\n * const closed = await reldb.close()\n * if (!closed.success) throw new Error(closed.error.message)\n * }\n * ```\n */\nexport interface ReachQueueOperations {\n /**\n * 按回执查询队列状态。\n * @param queueId - 持久UUID回执,不代替运维权限。\n * @returns HaiResult;先检查success,条件提交失败不能伪装成功。\n *\n * @example\n * ```ts\n * import { reach } from '@h-ai/reach'\n * const { reldb } = await import('@h-ai/reldb')\n * const connected = await reldb.init({ type: 'postgresql', host: 'db', port: 5432, database: 'hai', user: 'postgres', password: process.env.HAI_DB_PASSWORD })\n * if (!connected.success) throw new Error(connected.error.message)\n * try {\n * const initialized = await reach.init({ providers: [{ name: 'webhook', type: 'api', url: 'https://notify.example.com/events', idempotent: false }], templates: [{ name: 'welcome', provider: 'webhook', subject: 'Welcome {userName}', body: 'Hello {userName}' }], dnd: { enabled: true, strategy: 'delay', start: '22:00', end: '08:00' } })\n * if (!initialized.success) throw new Error(initialized.error.message)\n * try {\n * const result = await reach.queue.get('00000000-0000-4000-8000-000000000001')\n * if (!result.success) throw new Error(result.error.message)\n * if (result.data) { const status = result.data.status }\n * }\n * finally { await reach.close() }\n * }\n * finally {\n * const closed = await reldb.close()\n * if (!closed.success) throw new Error(closed.error.message)\n * }\n * ```\n */\n get: (queueId: string) => Promise<HaiResult<ReachQueueEntry | null>>\n /**\n * 分页查询死信或未知结果,仅返回运维元数据。\n * @param options - 实际操作输入,按本方法约束校验。\n * @returns HaiResult;先检查success,条件提交失败不能伪装成功。\n *\n * @example\n * ```ts\n * import { reach } from '@h-ai/reach'\n * const { reldb } = await import('@h-ai/reldb')\n * const connected = await reldb.init({ type: 'postgresql', host: 'db', port: 5432, database: 'hai', user: 'postgres', password: process.env.HAI_DB_PASSWORD })\n * if (!connected.success) throw new Error(connected.error.message)\n * try {\n * const initialized = await reach.init({ providers: [{ name: 'webhook', type: 'api', url: 'https://notify.example.com/events', idempotent: false }], templates: [{ name: 'welcome', provider: 'webhook', subject: 'Welcome {userName}', body: 'Hello {userName}' }], dnd: { enabled: true, strategy: 'delay', start: '22:00', end: '08:00' } })\n * if (!initialized.success) throw new Error(initialized.error.message)\n * try {\n * const listed = await reach.queue.list({ status: 'unknown', limit: 10 })\n * if (!listed.success) throw new Error(listed.error.message)\n * const count = listed.data.length\n * }\n * finally { await reach.close() }\n * }\n * finally {\n * const closed = await reldb.close()\n * if (!closed.success) throw new Error(closed.error.message)\n * }\n * ```\n */\n list: (options: ReachQueueListOptions) => Promise<HaiResult<ReachQueueEntry[]>>\n /**\n * 修复根因并确认接收端幂等契约后恢复 dead;重置计数,保留 queueId/策略。unknown 不能重排。\n * @param queueId - 持久UUID回执,不代替运维权限。\n * @param options - 实际操作输入,按本方法约束校验。\n * @returns HaiResult;先检查success,条件提交失败不能伪装成功。\n *\n * @example\n * ```ts\n * import { reach, type ReachDeadRequeue } from '@h-ai/reach'\n * const { reldb } = await import('@h-ai/reldb')\n * const connected = await reldb.init({ type: 'postgresql', host: 'db', port: 5432, database: 'hai', user: 'postgres', password: process.env.HAI_DB_PASSWORD })\n * if (!connected.success) throw new Error(connected.error.message)\n * try {\n * const initialized = await reach.init({ providers: [{ name: 'webhook', type: 'api', url: 'https://notify.example.com/events', idempotent: false }], templates: [{ name: 'welcome', provider: 'webhook', subject: 'Welcome {userName}', body: 'Hello {userName}' }], dnd: { enabled: true, strategy: 'delay', start: '22:00', end: '08:00' } })\n * if (!initialized.success) throw new Error(initialized.error.message)\n * try {\n * const listed = await reach.queue.list({ status: 'dead', limit: 10 })\n * if (!listed.success) throw new Error(listed.error.message)\n * const entry = listed.data[0]\n * if (entry && entry.updatedAt !== undefined) {\n * const options: ReachDeadRequeue = { expectedUpdatedAt: entry.updatedAt }\n * // 此调用仅在受信运维流程核验完成后执行。\n * const restored = await reach.queue.requeueDead(entry.queueId, options)\n * if (!restored.success) throw new Error(restored.error.message)\n * }\n * }\n * finally { await reach.close() }\n * }\n * finally {\n * const closed = await reldb.close()\n * if (!closed.success) throw new Error(closed.error.message)\n * }\n * ```\n */\n requeueDead: (queueId: string, options: ReachDeadRequeue) => Promise<HaiResult<void>>\n /**\n * 停止/隔离旧 worker 并核对外部投递记录后,裁定 unknown;不重新发送。\n * @param queueId - 持久UUID回执,不代替运维权限。\n * @param resolution - 实际操作输入,按本方法约束校验。\n * @returns HaiResult;先检查success,条件提交失败不能伪装成功。\n *\n * @example\n * ```ts\n * import { reach, type ReachUnknownResolution } from '@h-ai/reach'\n * const { reldb } = await import('@h-ai/reldb')\n * const connected = await reldb.init({ type: 'postgresql', host: 'db', port: 5432, database: 'hai', user: 'postgres', password: process.env.HAI_DB_PASSWORD })\n * if (!connected.success) throw new Error(connected.error.message)\n * try {\n * const initialized = await reach.init({ providers: [{ name: 'webhook', type: 'api', url: 'https://notify.example.com/events', idempotent: false }], templates: [{ name: 'welcome', provider: 'webhook', subject: 'Welcome {userName}', body: 'Hello {userName}' }], dnd: { enabled: true, strategy: 'delay', start: '22:00', end: '08:00' } })\n * if (!initialized.success) throw new Error(initialized.error.message)\n * try {\n * const listed = await reach.queue.list({ status: 'unknown', limit: 10 })\n * if (!listed.success) throw new Error(listed.error.message)\n * const entry = listed.data[0]\n * if (entry) {\n * // 可信运维前置:旧worker已隔离且外部投递记录已核对。\n * const resolution: ReachUnknownResolution = { status: 'discarded' }\n * const resolved = await reach.queue.resolveUnknown(entry.queueId, resolution)\n * if (!resolved.success) throw new Error(resolved.error.message)\n * }\n * }\n * finally { await reach.close() }\n * }\n * finally {\n * const closed = await reldb.close()\n * if (!closed.success) throw new Error(closed.error.message)\n * }\n * ```\n */\n resolveUnknown: (queueId: string, resolution: ReachUnknownResolution) => Promise<HaiResult<void>>\n}\n\n/**\n * 持久文本模板,provider是配置名称,subject/body支持{key}替换;未提供的占位符保留原文。\n * 不提供HTML转义或消毒,渲染结果的最终安全上下文由使用方处理。\n *\n * @example\n * ```ts\n * import { reach, type ReachTemplate } from '@h-ai/reach'\n * const { reldb } = await import('@h-ai/reldb')\n * const connected = await reldb.init({ type: 'postgresql', host: 'db', port: 5432, database: 'hai', user: 'postgres', password: process.env.HAI_DB_PASSWORD })\n * if (!connected.success) throw new Error(connected.error.message)\n * try {\n * const initialized = await reach.init({ providers: [{ name: 'webhook', type: 'api', url: 'https://notify.example.com/events', idempotent: false }], templates: [{ name: 'welcome', provider: 'webhook', subject: 'Welcome {userName}', body: 'Hello {userName}' }], dnd: { enabled: true, strategy: 'delay', start: '22:00', end: '08:00' } })\n * if (!initialized.success) throw new Error(initialized.error.message)\n * try {\n * const template: ReachTemplate = { name: 'welcome', provider: 'webhook', subject: 'Welcome {userName}', body: 'Hello {userName}' }\n * const saved = await reach.template.save(template)\n * if (!saved.success) throw new Error(saved.error.message)\n * }\n * finally { await reach.close() }\n * }\n * finally {\n * const closed = await reldb.close()\n * if (!closed.success) throw new Error(closed.error.message)\n * }\n * ```\n */\nexport interface ReachTemplate {\n /** 模板名称(唯一标识) */\n name: string\n /** 绑定的 Provider 名称 */\n provider: string\n /** 邮件主题模板(支持 `{var}` 占位符,仅 email) */\n subject?: string\n /** 消息正文模板(支持 `{var}` 占位符) */\n body: string\n}\n\n/**\n * 渲染后的主题/正文;文本替换不等于HTML安全,可选subject按原模板保留。\n *\n * @example\n * ```ts\n * import { reach, type RenderedTemplate } from '@h-ai/reach'\n * const { reldb } = await import('@h-ai/reldb')\n * const connected = await reldb.init({ type: 'postgresql', host: 'db', port: 5432, database: 'hai', user: 'postgres', password: process.env.HAI_DB_PASSWORD })\n * if (!connected.success) throw new Error(connected.error.message)\n * try {\n * const initialized = await reach.init({ providers: [{ name: 'webhook', type: 'api', url: 'https://notify.example.com/events', idempotent: false }], templates: [{ name: 'welcome', provider: 'webhook', subject: 'Welcome {userName}', body: 'Hello {userName}' }], dnd: { enabled: true, strategy: 'delay', start: '22:00', end: '08:00' } })\n * if (!initialized.success) throw new Error(initialized.error.message)\n * try {\n * const rendered = await reach.template.render('welcome', { userName: 'Alice' })\n * if (!rendered.success) throw new Error(rendered.error.message)\n * const value: RenderedTemplate = rendered.data\n * const body = value.body\n * }\n * finally { await reach.close() }\n * }\n * finally {\n * const closed = await reldb.close()\n * if (!closed.success) throw new Error(closed.error.message)\n * }\n * ```\n */\nexport interface RenderedTemplate {\n /** 渲染后的主题 */\n subject?: string\n /** 渲染后的正文 */\n body: string\n}\n\n// ─── 操作接口 ───\n\n/**\n * 统一发送操作;失败HaiResult不表示已发送,成功结果仍检查内部success与deferred。\n * 即时外部发送成功不因可选日志失败而转失败,防调用方重复发送;延迟入队必须确认持久写入。\n *\n * @example\n * ```ts\n * import { reach } from '@h-ai/reach'\n * const { reldb } = await import('@h-ai/reldb')\n * const connected = await reldb.init({ type: 'postgresql', host: 'db', port: 5432, database: 'hai', user: 'postgres', password: process.env.HAI_DB_PASSWORD })\n * if (!connected.success) throw new Error(connected.error.message)\n * try {\n * const initialized = await reach.init({ providers: [{ name: 'webhook', type: 'api', url: 'https://notify.example.com/events', idempotent: false }], templates: [{ name: 'welcome', provider: 'webhook', subject: 'Welcome {userName}', body: 'Hello {userName}' }], dnd: { enabled: true, strategy: 'delay', start: '22:00', end: '08:00' } })\n * if (!initialized.success) throw new Error(initialized.error.message)\n * try {\n * const result = await reach.send({ provider: 'webhook', to: 'user@example.com', template: 'welcome', vars: { userName: 'Alice' } })\n * if (!result.success) throw new Error(result.error.message)\n * if (!result.data.success) throw new Error('Delivery was not accepted')\n * if (result.data.deferred) {\n * const queueId = result.data.queueId\n * // 持久入队成功,尚未证明接收端已完成外部投递。\n * }\n * else { const providerMessageId = result.data.messageId }\n * }\n * finally { await reach.close() }\n * }\n * finally {\n * const closed = await reldb.close()\n * if (!closed.success) throw new Error(closed.error.message)\n * }\n * ```\n */\nexport interface SendOperations {\n /**\n * 发送触达消息\n *\n * @param message - 触达消息\n * @returns 发送结果\n *\n * @example\n * ```ts\n * import { reach } from '@h-ai/reach'\n * const { reldb } = await import('@h-ai/reldb')\n * const connected = await reldb.init({ type: 'postgresql', host: 'db', port: 5432, database: 'hai', user: 'postgres', password: process.env.HAI_DB_PASSWORD })\n * if (!connected.success) throw new Error(connected.error.message)\n * try {\n * const initialized = await reach.init({ providers: [{ name: 'webhook', type: 'api', url: 'https://notify.example.com/events', idempotent: false }], templates: [{ name: 'welcome', provider: 'webhook', subject: 'Welcome {userName}', body: 'Hello {userName}' }], dnd: { enabled: true, strategy: 'delay', start: '22:00', end: '08:00' } })\n * if (!initialized.success) throw new Error(initialized.error.message)\n * try {\n * const result = await reach.send({ provider: 'webhook', to: 'user@example.com', template: 'welcome', vars: { userName: 'Alice' } })\n * if (!result.success) throw new Error(result.error.message)\n * if (!result.data.success) throw new Error('Delivery was not accepted')\n * if (result.data.deferred) {\n * const queueId = result.data.queueId\n * // 持久入队成功,尚未证明接收端已完成外部投递。\n * }\n * else { const providerMessageId = result.data.messageId }\n * }\n * finally { await reach.close() }\n * }\n * finally {\n * const closed = await reldb.close()\n * if (!closed.success) throw new Error(closed.error.message)\n * }\n * ```\n */\n send: (message: ReachMessage) => Promise<HaiResult<SendResult>>\n}\n\n// ─── 模板注册表接口 ───\n\n/**\n * 数据库模板操作;无数据库时不回退进程模板缓存,resolve失败、list为空,保存/删除返回失败。\n * saveBatch并发逐条upsert,失败时不承诺其它项回滚;需要原子业务批次时由受控事务流程实现。\n *\n * @example\n * ```ts\n * import { reach } from '@h-ai/reach'\n * const { reldb } = await import('@h-ai/reldb')\n * const connected = await reldb.init({ type: 'postgresql', host: 'db', port: 5432, database: 'hai', user: 'postgres', password: process.env.HAI_DB_PASSWORD })\n * if (!connected.success) throw new Error(connected.error.message)\n * try {\n * const initialized = await reach.init({ providers: [{ name: 'webhook', type: 'api', url: 'https://notify.example.com/events', idempotent: false }], templates: [{ name: 'welcome', provider: 'webhook', subject: 'Welcome {userName}', body: 'Hello {userName}' }], dnd: { enabled: true, strategy: 'delay', start: '22:00', end: '08:00' } })\n * if (!initialized.success) throw new Error(initialized.error.message)\n * try {\n * const saved = await reach.template.save({ name: 'welcome', provider: 'webhook', subject: 'Welcome {userName}', body: 'Hello {userName}' })\n * if (!saved.success) throw new Error(saved.error.message)\n * const rendered = await reach.template.render('welcome', { userName: 'Alice' })\n * if (!rendered.success) throw new Error(rendered.error.message)\n * }\n * finally { await reach.close() }\n * }\n * finally {\n * const closed = await reldb.close()\n * if (!closed.success) throw new Error(closed.error.message)\n * }\n * ```\n */\nexport interface ReachTemplateRegistry {\n /**\n * 按名称解析模板\n * @param name - 实际操作输入,按本方法约束校验。\n * @returns HaiResult;先检查success,条件提交失败不能伪装成功。\n *\n * @example\n * ```ts\n * import { reach } from '@h-ai/reach'\n * const { reldb } = await import('@h-ai/reldb')\n * const connected = await reldb.init({ type: 'postgresql', host: 'db', port: 5432, database: 'hai', user: 'postgres', password: process.env.HAI_DB_PASSWORD })\n * if (!connected.success) throw new Error(connected.error.message)\n * try {\n * const initialized = await reach.init({ providers: [{ name: 'webhook', type: 'api', url: 'https://notify.example.com/events', idempotent: false }], templates: [{ name: 'welcome', provider: 'webhook', subject: 'Welcome {userName}', body: 'Hello {userName}' }], dnd: { enabled: true, strategy: 'delay', start: '22:00', end: '08:00' } })\n * if (!initialized.success) throw new Error(initialized.error.message)\n * try {\n * const result = await reach.template.resolve('welcome')\n * if (!result.success) throw new Error(result.error.message)\n * const providerName = result.data.provider\n * }\n * finally { await reach.close() }\n * }\n * finally {\n * const closed = await reldb.close()\n * if (!closed.success) throw new Error(closed.error.message)\n * }\n * ```\n */\n resolve: (name: string) => Promise<HaiResult<ReachTemplate>>\n /**\n * 保存模板到数据库(upsert)\n * @param template - 实际操作输入,按本方法约束校验。\n * @returns HaiResult;先检查success,条件提交失败不能伪装成功。\n *\n * @example\n * ```ts\n * import { reach, type ReachTemplate } from '@h-ai/reach'\n * const { reldb } = await import('@h-ai/reldb')\n * const connected = await reldb.init({ type: 'postgresql', host: 'db', port: 5432, database: 'hai', user: 'postgres', password: process.env.HAI_DB_PASSWORD })\n * if (!connected.success) throw new Error(connected.error.message)\n * try {\n * const initialized = await reach.init({ providers: [{ name: 'webhook', type: 'api', url: 'https://notify.example.com/events', idempotent: false }], templates: [{ name: 'welcome', provider: 'webhook', subject: 'Welcome {userName}', body: 'Hello {userName}' }], dnd: { enabled: true, strategy: 'delay', start: '22:00', end: '08:00' } })\n * if (!initialized.success) throw new Error(initialized.error.message)\n * try {\n * const template: ReachTemplate = { name: 'welcome', provider: 'webhook', subject: 'Welcome {userName}', body: 'Hello {userName}' }\n * const saved = await reach.template.save(template)\n * if (!saved.success) throw new Error(saved.error.message)\n * }\n * finally { await reach.close() }\n * }\n * finally {\n * const closed = await reldb.close()\n * if (!closed.success) throw new Error(closed.error.message)\n * }\n * ```\n */\n save: (template: ReachTemplate) => Promise<HaiResult<void>>\n /**\n * 批量保存模板到数据库\n * @param templates - 实际操作输入,按本方法约束校验。\n * @returns HaiResult;先检查success,条件提交失败不能伪装成功。\n *\n * @example\n * ```ts\n * import { reach } from '@h-ai/reach'\n * const { reldb } = await import('@h-ai/reldb')\n * const connected = await reldb.init({ type: 'postgresql', host: 'db', port: 5432, database: 'hai', user: 'postgres', password: process.env.HAI_DB_PASSWORD })\n * if (!connected.success) throw new Error(connected.error.message)\n * try {\n * const initialized = await reach.init({ providers: [{ name: 'webhook', type: 'api', url: 'https://notify.example.com/events', idempotent: false }], templates: [{ name: 'welcome', provider: 'webhook', subject: 'Welcome {userName}', body: 'Hello {userName}' }], dnd: { enabled: true, strategy: 'delay', start: '22:00', end: '08:00' } })\n * if (!initialized.success) throw new Error(initialized.error.message)\n * try {\n * const result = await reach.template.saveBatch([{ name: 'receipt', provider: 'webhook', body: 'Receipt {orderId}' }, { name: 'reminder', provider: 'webhook', body: 'Reminder {orderId}' }])\n * if (!result.success) throw new Error(result.error.message)\n * // 非原子批次;失败时应核对已保存条目,不假定整体回滚。\n * }\n * finally { await reach.close() }\n * }\n * finally {\n * const closed = await reldb.close()\n * if (!closed.success) throw new Error(closed.error.message)\n * }\n * ```\n */\n saveBatch: (templates: ReachTemplate[]) => Promise<HaiResult<void>>\n /**\n * 从数据库删除模板\n * @param name - 实际操作输入,按本方法约束校验。\n * @returns HaiResult;先检查success,条件提交失败不能伪装成功。\n *\n * @example\n * ```ts\n * import { reach } from '@h-ai/reach'\n * const { reldb } = await import('@h-ai/reldb')\n * const connected = await reldb.init({ type: 'postgresql', host: 'db', port: 5432, database: 'hai', user: 'postgres', password: process.env.HAI_DB_PASSWORD })\n * if (!connected.success) throw new Error(connected.error.message)\n * try {\n * const initialized = await reach.init({ providers: [{ name: 'webhook', type: 'api', url: 'https://notify.example.com/events', idempotent: false }], templates: [{ name: 'welcome', provider: 'webhook', subject: 'Welcome {userName}', body: 'Hello {userName}' }], dnd: { enabled: true, strategy: 'delay', start: '22:00', end: '08:00' } })\n * if (!initialized.success) throw new Error(initialized.error.message)\n * try {\n * const result = await reach.template.remove('welcome')\n * if (!result.success) throw new Error(result.error.message)\n * }\n * finally { await reach.close() }\n * }\n * finally {\n * const closed = await reldb.close()\n * if (!closed.success) throw new Error(closed.error.message)\n * }\n * ```\n */\n remove: (name: string) => Promise<HaiResult<void>>\n /**\n * 列出所有模板\n * @returns HaiResult;先检查success,条件提交失败不能伪装成功。\n *\n * @example\n * ```ts\n * import { reach } from '@h-ai/reach'\n * const { reldb } = await import('@h-ai/reldb')\n * const connected = await reldb.init({ type: 'postgresql', host: 'db', port: 5432, database: 'hai', user: 'postgres', password: process.env.HAI_DB_PASSWORD })\n * if (!connected.success) throw new Error(connected.error.message)\n * try {\n * const initialized = await reach.init({ providers: [{ name: 'webhook', type: 'api', url: 'https://notify.example.com/events', idempotent: false }], templates: [{ name: 'welcome', provider: 'webhook', subject: 'Welcome {userName}', body: 'Hello {userName}' }], dnd: { enabled: true, strategy: 'delay', start: '22:00', end: '08:00' } })\n * if (!initialized.success) throw new Error(initialized.error.message)\n * try {\n * const result = await reach.template.list()\n * if (!result.success) throw new Error(result.error.message)\n * const count = result.data.length\n * }\n * finally { await reach.close() }\n * }\n * finally {\n * const closed = await reldb.close()\n * if (!closed.success) throw new Error(closed.error.message)\n * }\n * ```\n */\n list: () => Promise<HaiResult<ReachTemplate[]>>\n /**\n * 渲染模板(从数据库解析后渲染)\n * @param name - 实际操作输入,按本方法约束校验。\n * @param vars - 实际操作输入,按本方法约束校验。\n * @returns HaiResult;先检查success,条件提交失败不能伪装成功。\n *\n * @example\n * ```ts\n * import { reach, type RenderedTemplate } from '@h-ai/reach'\n * const { reldb } = await import('@h-ai/reldb')\n * const connected = await reldb.init({ type: 'postgresql', host: 'db', port: 5432, database: 'hai', user: 'postgres', password: process.env.HAI_DB_PASSWORD })\n * if (!connected.success) throw new Error(connected.error.message)\n * try {\n * const initialized = await reach.init({ providers: [{ name: 'webhook', type: 'api', url: 'https://notify.example.com/events', idempotent: false }], templates: [{ name: 'welcome', provider: 'webhook', subject: 'Welcome {userName}', body: 'Hello {userName}' }], dnd: { enabled: true, strategy: 'delay', start: '22:00', end: '08:00' } })\n * if (!initialized.success) throw new Error(initialized.error.message)\n * try {\n * const rendered = await reach.template.render('welcome', { userName: 'Alice' })\n * if (!rendered.success) throw new Error(rendered.error.message)\n * const value: RenderedTemplate = rendered.data\n * const body = value.body\n * }\n * finally { await reach.close() }\n * }\n * finally {\n * const closed = await reldb.close()\n * if (!closed.success) throw new Error(closed.error.message)\n * }\n * ```\n */\n render: (name: string, vars: Record<string, string>) => Promise<HaiResult<RenderedTemplate>>\n}\n\n// ─── 函数接口 ───\n\n/**\n * Reach生命周期、模板、发送及受信队列运维能力;先初始化数据库,再检查reach.init结果,反序close。\n * API Provider init只建立适配器配置,不探测远端可用性;实际send结果才能判断本次投递。\n *\n * @example\n * ```ts\n * import { reach } from '@h-ai/reach'\n * const { reldb } = await import('@h-ai/reldb')\n * const connected = await reldb.init({ type: 'postgresql', host: 'db', port: 5432, database: 'hai', user: 'postgres', password: process.env.HAI_DB_PASSWORD })\n * if (!connected.success) throw new Error(connected.error.message)\n * try {\n * const initialized = await reach.init({ providers: [{ name: 'webhook', type: 'api', url: 'https://notify.example.com/events', idempotent: false }], templates: [{ name: 'welcome', provider: 'webhook', subject: 'Welcome {userName}', body: 'Hello {userName}' }], dnd: { enabled: true, strategy: 'delay', start: '22:00', end: '08:00' } })\n * if (!initialized.success) throw new Error(initialized.error.message)\n * try {\n * const result = await reach.send({ provider: 'webhook', to: 'user@example.com', template: 'welcome', vars: { userName: 'Alice' } })\n * if (!result.success) throw new Error(result.error.message)\n * if (!result.data.success) throw new Error('Delivery was not accepted')\n * if (result.data.deferred) {\n * const queueId = result.data.queueId\n * // 持久入队成功,尚未证明接收端已完成外部投递。\n * }\n * else { const providerMessageId = result.data.messageId }\n * }\n * finally { await reach.close() }\n * }\n * finally {\n * const closed = await reldb.close()\n * if (!closed.success) throw new Error(closed.error.message)\n * }\n * ```\n */\nexport interface ReachFunctions extends SendOperations {\n /** 持久化队列查询、死信恢复与未知结果人工裁定。 */\n readonly queue: ReachQueueOperations\n /**\n * 初始化触达模块(注册多个 Provider)\n * @param config - 实际操作输入,按本方法约束校验。\n * @returns HaiResult;先检查success,条件提交失败不能伪装成功。\n *\n * @example\n * ```ts\n * import { reach } from '@h-ai/reach'\n * const { reldb } = await import('@h-ai/reldb')\n * const connected = await reldb.init({ type: 'postgresql', host: 'db', port: 5432, database: 'hai', user: 'postgres', password: process.env.HAI_DB_PASSWORD })\n * if (!connected.success) throw new Error(connected.error.message)\n * try {\n * const initialized = await reach.init({ providers: [{ name: 'webhook', type: 'api', url: 'https://notify.example.com/events', idempotent: false }], templates: [{ name: 'welcome', provider: 'webhook', subject: 'Welcome {userName}', body: 'Hello {userName}' }], dnd: { enabled: true, strategy: 'delay', start: '22:00', end: '08:00' } })\n * if (!initialized.success) throw new Error(initialized.error.message)\n * try {\n * const result = await reach.send({ provider: 'webhook', to: 'user@example.com', template: 'welcome', vars: { userName: 'Alice' } })\n * if (!result.success) throw new Error(result.error.message)\n * if (!result.data.success) throw new Error('Delivery was not accepted')\n * if (result.data.deferred) {\n * const queueId = result.data.queueId\n * // 持久入队成功,尚未证明接收端已完成外部投递。\n * }\n * else { const providerMessageId = result.data.messageId }\n * }\n * finally { await reach.close() }\n * }\n * finally {\n * const closed = await reldb.close()\n * if (!closed.success) throw new Error(closed.error.message)\n * }\n * ```\n */\n init: (config: ReachConfigInput) => Promise<HaiResult<void>>\n /** 当前脱敏后的配置快照(未初始化时为 null) */\n readonly config: ReachConfig | null\n /** 是否已初始化 */\n readonly isInitialized: boolean\n /** 模板注册表 */\n readonly template: ReachTemplateRegistry\n /**\n * 关闭连接并释放资源\n *\n * @example\n * ```ts\n * import { reach } from '@h-ai/reach'\n * const { reldb } = await import('@h-ai/reldb')\n * const connected = await reldb.init({ type: 'postgresql', host: 'db', port: 5432, database: 'hai', user: 'postgres', password: process.env.HAI_DB_PASSWORD })\n * if (!connected.success) throw new Error(connected.error.message)\n * try {\n * const initialized = await reach.init({ providers: [{ name: 'webhook', type: 'api', url: 'https://notify.example.com/events', idempotent: false }], templates: [{ name: 'welcome', provider: 'webhook', subject: 'Welcome {userName}', body: 'Hello {userName}' }], dnd: { enabled: true, strategy: 'delay', start: '22:00', end: '08:00' } })\n * if (!initialized.success) throw new Error(initialized.error.message)\n * try {\n * const result = await reach.send({ provider: 'webhook', to: 'user@example.com', template: 'welcome', vars: { userName: 'Alice' } })\n * if (!result.success) throw new Error(result.error.message)\n * if (!result.data.success) throw new Error('Delivery was not accepted')\n * if (result.data.deferred) {\n * const queueId = result.data.queueId\n * // 持久入队成功,尚未证明接收端已完成外部投递。\n * }\n * else { const providerMessageId = result.data.messageId }\n * }\n * finally { await reach.close() }\n * }\n * finally {\n * const closed = await reldb.close()\n * if (!closed.success) throw new Error(closed.error.message)\n * }\n * ```\n */\n close: () => Promise<void>\n}\n","/**\n * @h-ai/reach — 阿里云短信 Provider\n *\n * 通过阿里云 SMS HTTP API 发送短信的 Provider 实现(无需 SDK)。\n * @module reach-provider-aliyun-sms\n */\n\nimport type { HaiError, HaiResult } from '@h-ai/core'\nimport type { AliyunSmsProviderConfig, ProviderConfig } from '../reach-config.js'\n\nimport type { ReachMessage, SendResult } from '../reach-types.js'\nimport type { ReachProvider } from './reach-provider-types.js'\nimport { createHmac, randomUUID } from 'node:crypto'\nimport { core, err, ok } from '@h-ai/core'\n\nimport { reachM } from '../reach-i18n.js'\nimport { HaiReachError } from '../reach-types.js'\n\nconst logger = core.logger.child({ module: 'reach', scope: 'provider-aliyun-sms' })\nconst ISO_MILLISECONDS_SUFFIX_REGEX = /\\.\\d{3}Z$/\n\n/**\n * 将异常包装为 ReachError\n */\nfunction toReachError(error: unknown): HaiError {\n return {\n code: HaiReachError.SEND_FAILED.code,\n message: reachM('reach_smsSendFailed', {\n params: { error: error instanceof Error ? error.message : String(error) },\n }),\n cause: error,\n }\n}\n\n/**\n * 对字符串做 RFC 3986 编码\n */\nfunction percentEncode(str: string): string {\n return encodeURIComponent(str)\n .replace(/\\+/g, '%20')\n .replace(/\\*/g, '%2A')\n .replace(/~/g, '%7E')\n}\n\n/**\n * 按阿里云POP规范排序/编码并执行HMAC-SHA1;不能替换为框架默认HMAC-SM3。\n * @param params - 不含Signature的请求参数。\n * @param accessKeySecret - 私有Secret,禁止写日志。\n * @param method - 实际HTTP方法。\n * @returns Base64签名,只用于对应请求。\n *\n * @example 模块内部上下文\n * ```ts\n * const secret = process.env.HAI_REACH_PROVIDERS_0_ACCESSKEYSECRET\n * if (!secret) throw new Error('Missing SMS credential')\n * const signature = signRequest({ Action: 'SendSms', Format: 'JSON' }, secret, 'POST')\n * ```\n */\nfunction signRequest(params: Record<string, string>, accessKeySecret: string, method: string): string {\n const sortedKeys = Object.keys(params).sort()\n const canonicalQuery = sortedKeys\n .map(k => `${percentEncode(k)}=${percentEncode(params[k])}`)\n .join('&')\n\n const stringToSign = `${method}&${percentEncode('/')}&${percentEncode(canonicalQuery)}`\n // 阿里云 POP API 规范要求使用 HMAC-SHA1 签名\n const hmac = createHmac('sha1', `${accessKeySecret}&`)\n hmac.update(stringToSign)\n return hmac.digest('base64')\n}\n\n/**\n * 内部阿里云POP短信适配器,使用平台指定的HMAC-SHA1签名,不用框架HMAC-SM3替代。\n * 短信发送无接收端持久幂等声明,已开始后失败不自动接管重发。\n * @returns Provider;私有AccessKey由配置注入,不进入示例常量或日志。\n *\n * @example 模块内部上下文\n * ```ts\n * const provider = createAliyunSmsProvider()\n * const { AliyunSmsProviderConfigSchema } = await import('@h-ai/reach')\n * const config = AliyunSmsProviderConfigSchema.parse({ name: 'sms', type: 'aliyun-sms', accessKeyId: process.env.HAI_REACH_PROVIDERS_0_ACCESSKEYID, accessKeySecret: process.env.HAI_REACH_PROVIDERS_0_ACCESSKEYSECRET, signName: process.env.HAI_REACH_PROVIDERS_0_SIGNNAME })\n * const connected = await provider.connect(config)\n * if (!connected.success) throw new Error(connected.error.message)\n * try { const connectedNow = provider.isConnected(); const retrySafe = provider.supportsIdempotency }\n * finally { await provider.close() }\n * ```\n */\nexport function createAliyunSmsProvider(): ReachProvider {\n let smsConfig: AliyunSmsProviderConfig | null = null\n\n return {\n name: 'aliyun-sms',\n supportsIdempotency: false,\n\n async connect(config: ProviderConfig): Promise<HaiResult<void>> {\n if (config.type !== 'aliyun-sms') {\n return err(\n HaiReachError.CONFIG_ERROR,\n reachM('reach_unsupportedType', { params: { type: config.type } }),\n )\n }\n\n smsConfig = config\n logger.info('Aliyun SMS provider connected', { endpoint: config.endpoint })\n return ok(undefined)\n },\n\n async close(): Promise<void> {\n smsConfig = null\n logger.info('Aliyun SMS provider disconnected')\n },\n\n isConnected(): boolean {\n return smsConfig !== null\n },\n\n async send(message: ReachMessage): Promise<HaiResult<SendResult>> {\n if (!smsConfig) {\n return err(\n HaiReachError.NOT_INITIALIZED,\n reachM('reach_notInitialized'),\n )\n }\n\n const templateCode = message.extra?.templateCode as string | undefined\n logger.debug('Sending SMS', { to: message.to, templateCode })\n\n try {\n const params: Record<string, string> = {\n AccessKeyId: smsConfig.accessKeyId,\n Action: 'SendSms',\n Format: 'JSON',\n PhoneNumbers: message.to,\n SignName: smsConfig.signName,\n SignatureMethod: 'HMAC-SHA1',\n SignatureNonce: randomUUID(),\n SignatureVersion: '1.0',\n TemplateCode: templateCode ?? '',\n Timestamp: new Date().toISOString().replace(ISO_MILLISECONDS_SUFFIX_REGEX, 'Z'),\n Version: '2017-05-25',\n }\n\n if (message.vars) {\n params.TemplateParam = JSON.stringify(message.vars)\n }\n\n params.Signature = signRequest(params, smsConfig.accessKeySecret, 'GET')\n\n const queryString = Object.entries(params)\n .map(([k, v]) => `${percentEncode(k)}=${percentEncode(v)}`)\n .join('&')\n\n const url = `https://${smsConfig.endpoint}/?${queryString}`\n const response = await fetch(url)\n const body = await response.json() as { Code: string, Message: string, BizId?: string }\n\n if (body.Code !== 'OK') {\n logger.warn('SMS send returned non-OK status', { code: body.Code, message: body.Message })\n return err(\n HaiReachError.SEND_FAILED,\n reachM('reach_smsSendFailed', { params: { error: body.Message } }),\n )\n }\n\n logger.info('SMS sent', { to: message.to, bizId: body.BizId })\n return ok({ success: true, messageId: body.BizId })\n }\n catch (error) {\n logger.error('SMS send failed', { to: message.to, error })\n return err(toReachError(error))\n }\n },\n }\n}\n","import type { HaiError, HaiResult } from '@h-ai/core'\n/**\n * @h-ai/reach — API 回调 Provider\n *\n * 通用 HTTP API 回调 Provider,通过 HTTP 请求发送触达消息。\n * @module reach-provider-api\n */\n\nimport type { ApiProviderConfig, ProviderConfig } from '../reach-config.js'\nimport type { ReachMessage, SendResult } from '../reach-types.js'\n\nimport type { ReachProvider } from './reach-provider-types.js'\nimport process from 'node:process'\nimport { core, err, ok } from '@h-ai/core'\n\nimport { reachM } from '../reach-i18n.js'\nimport { HaiReachError } from '../reach-types.js'\n\nconst logger = core.logger.child({ module: 'reach', scope: 'provider-api' })\n\n/**\n * 日志只保留源站,去除URL用户信息、路径和查询业务数据,非法URL返回固定占位文本。\n * @param url - 已配置回调URL。\n * @returns 可用于结构化日志的源站。\n *\n * @example 模块内部上下文\n * ```ts\n * const endpoint = getSafeEndpoint('https://notify.example.com/private/path?trace=example')\n * // endpoint仅为https://notify.example.com。\n * ```\n */\nfunction getSafeEndpoint(url: string): string {\n try {\n return new URL(url).origin\n }\n catch {\n return '[invalid-url]'\n }\n}\n\n/**\n * 将异常包装为 ReachError\n */\nfunction toReachError(): HaiError {\n return {\n code: HaiReachError.SEND_FAILED.code,\n message: reachM('reach_apiSendFailed', {\n params: { error: reachM('reach_apiRequestFailed') },\n }),\n }\n}\n\n/** 成功回执仅包含可选消息编号;正文有界,解析失败不输出第三方内容。 */\nasync function readAcknowledgement(response: Response): Promise<HaiResult<SendResult>> {\n const reader = response.body?.getReader()\n let complete = false\n try {\n let text = ''\n let bytes = 0\n const decoder = new TextDecoder('utf-8', { fatal: true })\n if (reader) {\n while (true) {\n const chunk = await reader.read()\n if (chunk.done)\n break\n bytes += chunk.value.byteLength\n if (bytes > 65536)\n return err(HaiReachError.SEND_FAILED, reachM('reach_apiResponseTooLarge'))\n text += decoder.decode(chunk.value, { stream: true })\n }\n text += decoder.decode()\n }\n complete = true\n if (text.length === 0)\n return ok({ success: true })\n const result: unknown = JSON.parse(text)\n if (!result || typeof result !== 'object' || Array.isArray(result))\n return err(HaiReachError.SEND_FAILED, reachM('reach_apiResponseInvalid'))\n const messageId = 'messageId' in result ? result.messageId : undefined\n if (messageId !== undefined && (typeof messageId !== 'string' || messageId.length > 255))\n return err(HaiReachError.SEND_FAILED, reachM('reach_apiResponseInvalid'))\n return ok({ success: true, ...(messageId === undefined ? {} : { messageId }) })\n }\n catch {\n return err(HaiReachError.SEND_FAILED, reachM('reach_apiResponseInvalid'))\n }\n finally {\n if (!complete)\n await reader?.cancel().catch(() => undefined)\n reader?.releaseLock()\n }\n}\n\n/**\n * 内部HTTP回调适配器工厂,包用户通过reach.init配置Provider;connect不发送网络请求。\n * 只有接收端实际原子持久去重时才支持重试;默认false,稳定queueId放在请求头和JSON。\n * @returns Provider;超时中断不证明远端副作用被撤销。\n *\n * @example 模块内部上下文\n * ```ts\n * const provider = createApiProvider()\n * const { ApiProviderConfigSchema } = await import('@h-ai/reach')\n * const config = ApiProviderConfigSchema.parse({ name: 'webhook', type: 'api', url: 'https://notify.example.com/events', idempotent: false })\n * const connected = await provider.connect(config)\n * if (!connected.success) throw new Error(connected.error.message)\n * try { const connectedNow = provider.isConnected(); const retrySafe = provider.supportsIdempotency }\n * finally { await provider.close() }\n * ```\n */\nexport function createApiProvider(): ReachProvider {\n let apiConfig: ApiProviderConfig | null = null\n\n return {\n name: 'api',\n get supportsIdempotency() {\n return apiConfig?.idempotent === true\n },\n\n async connect(config: ProviderConfig): Promise<HaiResult<void>> {\n if (config.type !== 'api') {\n return err(\n HaiReachError.CONFIG_ERROR,\n reachM('reach_unsupportedType', { params: { type: config.type } }),\n )\n }\n\n apiConfig = config\n if ((config.requireHttps && new URL(config.url).protocol !== 'https:') || (config.requireHttps && config.redirect === 'follow')) {\n apiConfig = null\n return err(HaiReachError.CONFIG_ERROR, reachM('reach_apiHttpsRequired'))\n }\n logger.info('API provider connected', { endpoint: getSafeEndpoint(config.url), method: config.method })\n return ok(undefined)\n },\n\n async close(): Promise<void> {\n apiConfig = null\n logger.info('API provider disconnected')\n },\n\n isConnected(): boolean {\n return apiConfig !== null\n },\n\n async send(message: ReachMessage): Promise<HaiResult<SendResult>> {\n if (!apiConfig) {\n return err(\n HaiReachError.NOT_INITIALIZED,\n reachM('reach_notInitialized'),\n )\n }\n\n logger.debug('Sending via API callback', { endpoint: getSafeEndpoint(apiConfig.url) })\n\n const requireHttps = apiConfig.requireHttps ?? (message.sensitive === true && core.module.requiresSharedState(process.env))\n if (requireHttps && new URL(apiConfig.url).protocol !== 'https:')\n return err(HaiReachError.CONFIG_ERROR, reachM('reach_apiHttpsRequired'))\n\n try {\n const payload = {\n to: message.to,\n subject: message.subject,\n body: message.body,\n template: message.template,\n vars: message.vars,\n extra: message.extra,\n idempotencyKey: message.idempotencyKey,\n expiresAt: message.expiresAt,\n }\n\n const controller = new AbortController()\n const timeoutId = setTimeout(() => controller.abort(), apiConfig.timeout)\n\n try {\n const headers = new Headers({ 'Content-Type': 'application/json', ...apiConfig.headers })\n if (message.idempotencyKey)\n headers.set('Idempotency-Key', message.idempotencyKey)\n const response = await fetch(apiConfig.url, {\n method: apiConfig.method,\n headers,\n body: JSON.stringify(payload),\n signal: controller.signal,\n redirect: message.sensitive || apiConfig.redirect !== 'follow' ? 'manual' : 'follow',\n })\n\n if (!response.ok) {\n await response.body?.cancel()\n logger.warn('API callback returned non-OK status', { status: response.status })\n // 明确的客户端拒绝无需自动重试;超时、过早请求及限流仍属于暂时故障。\n const rejected = (response.status >= 300 && response.status < 400) || (response.status >= 400 && response.status < 500 && ![408, 425, 429].includes(response.status))\n return err(\n rejected ? HaiReachError.DELIVERY_REJECTED : HaiReachError.SEND_FAILED,\n reachM('reach_apiSendFailed', { params: { error: `HTTP ${response.status}` } }),\n )\n }\n\n const result = await readAcknowledgement(response)\n if (result.success)\n logger.info('API callback accepted', { hasMessageId: result.data.messageId !== undefined })\n return result\n }\n finally {\n clearTimeout(timeoutId)\n }\n }\n catch {\n logger.error('API callback send failed', { errorCode: HaiReachError.SEND_FAILED.code })\n return err(toReachError())\n }\n },\n }\n}\n","/**\n * @h-ai/reach — Console Provider\n *\n * 开发/测试用 Provider,仅将触达元数据输出到日志,保护验证码和令牌。\n * @module reach-provider-console\n */\n\nimport type { HaiResult } from '@h-ai/core'\nimport type { ProviderConfig } from '../reach-config.js'\n\nimport type { ReachMessage, SendResult } from '../reach-types.js'\nimport type { ReachProvider } from './reach-provider-types.js'\nimport { core, ok } from '@h-ai/core'\n\nconst logger = core.logger.child({ module: 'reach', scope: 'provider-console' })\n\n/**\n * 内部开发/测试日志适配器,不证明真实邮件/短信/API投递;生产守卫拒绝console。\n * @returns 仅本地调试Provider;supportsIdempotency=true表示测试日志可重试,不代表接收端持久去重。\n *\n * @example 模块内部上下文\n * ```ts\n * const provider = createConsoleProvider()\n * const config = { name: 'dev-log', type: 'console' as const }\n * const connected = await provider.connect(config)\n * if (!connected.success) throw new Error(connected.error.message)\n * try { const connectedNow = provider.isConnected(); const retrySafe = provider.supportsIdempotency }\n * finally { await provider.close() }\n * ```\n */\nexport function createConsoleProvider(): ReachProvider {\n let connected = false\n\n return {\n name: 'console',\n supportsIdempotency: true,\n\n async connect(_config: ProviderConfig): Promise<HaiResult<void>> {\n connected = true\n logger.info('Console provider connected')\n return ok(undefined)\n },\n\n async close(): Promise<void> {\n connected = false\n logger.info('Console provider disconnected')\n },\n\n isConnected(): boolean {\n return connected\n },\n\n async send(message: ReachMessage): Promise<HaiResult<SendResult>> {\n logger.debug('Sending message via console', {\n provider: message.provider,\n to: message.to,\n hasSubject: Boolean(message.subject),\n bodyLength: message.body?.length ?? 0,\n template: message.template,\n variableNames: Object.keys(message.vars ?? {}),\n })\n\n const messageId = `console-${Date.now()}`\n return ok({ success: true, messageId })\n },\n }\n}\n","/**\n * @h-ai/reach — SMTP Email Provider\n *\n * 使用 nodemailer 发送邮件的 Provider 实现。\n * @module reach-provider-smtp\n */\n\nimport type { HaiError, HaiResult } from '@h-ai/core'\nimport type { ProviderConfig, SmtpProviderConfig } from '../reach-config.js'\n\nimport type { ReachMessage, SendResult } from '../reach-types.js'\nimport type { ReachProvider } from './reach-provider-types.js'\nimport { createRequire } from 'node:module'\nimport { core, err, ok } from '@h-ai/core'\n\nimport { reachM } from '../reach-i18n.js'\nimport { HaiReachError } from '../reach-types.js'\n\nconst logger = core.logger.child({ module: 'reach', scope: 'provider-smtp' })\n\n/**\n * 将异常包装为 ReachError\n */\nfunction toReachError(error: unknown): HaiError {\n return {\n code: HaiReachError.SEND_FAILED.code,\n message: reachM('reach_emailSendFailed', {\n params: { error: error instanceof Error ? error.message : String(error) },\n }),\n cause: error,\n }\n}\n\n/**\n * 内部SMTP适配器,惰性加载nodemailer;SMTP无持久接收端幂等保证,已开始发送失败进入unknown。\n * @returns Provider;连接/发送先检查HaiResult,close释放自有传输连接。\n *\n * @example 模块内部上下文\n * ```ts\n * const provider = createSmtpProvider()\n * const { SmtpProviderConfigSchema } = await import('@h-ai/reach')\n * const config = SmtpProviderConfigSchema.parse({ name: 'email', type: 'smtp', host: 'smtp.example.com', from: 'noreply@example.com', user: process.env.HAI_REACH_PROVIDERS_0_USER, pass: process.env.HAI_REACH_PROVIDERS_0_PASS })\n * const connected = await provider.connect(config)\n * if (!connected.success) throw new Error(connected.error.message)\n * try { const connectedNow = provider.isConnected(); const retrySafe = provider.supportsIdempotency }\n * finally { await provider.close() }\n * ```\n */\nexport function createSmtpProvider(): ReachProvider {\n let transporter: unknown = null\n let smtpConfig: SmtpProviderConfig | null = null\n\n return {\n name: 'smtp',\n supportsIdempotency: false,\n\n async connect(config: ProviderConfig): Promise<HaiResult<void>> {\n if (config.type !== 'smtp') {\n return err(\n HaiReachError.CONFIG_ERROR,\n reachM('reach_unsupportedType', { params: { type: config.type } }),\n )\n }\n\n try {\n const require = createRequire(import.meta.url)\n const nodemailer = require('nodemailer')\n\n smtpConfig = config\n transporter = nodemailer.createTransport({\n host: config.host,\n port: config.port,\n secure: config.secure,\n auth: config.user\n ? { user: config.user, pass: config.pass }\n : undefined,\n })\n\n logger.info('SMTP provider connected', { host: config.host, port: config.port })\n return ok(undefined)\n }\n catch (error) {\n logger.error('SMTP connection failed', { error })\n return err(toReachError(error))\n }\n },\n\n async close(): Promise<void> {\n if (transporter && typeof (transporter as Record<string, unknown>).close === 'function') {\n (transporter as { close: () => void }).close()\n }\n transporter = null\n smtpConfig = null\n logger.info('SMTP provider disconnected')\n },\n\n isConnected(): boolean {\n return transporter !== null\n },\n\n async send(message: ReachMessage): Promise<HaiResult<SendResult>> {\n if (!transporter || !smtpConfig) {\n return err(\n HaiReachError.NOT_INITIALIZED,\n reachM('reach_notInitialized'),\n )\n }\n\n logger.debug('Sending email', { to: message.to, subject: message.subject })\n\n try {\n const sendMail = (transporter as { sendMail: (opts: unknown) => Promise<{ messageId: string }> }).sendMail.bind(transporter)\n const info = await sendMail({\n from: smtpConfig.from,\n to: message.to,\n subject: message.subject ?? '',\n html: message.body ?? '',\n })\n\n logger.info('Email sent', { to: message.to, messageId: info.messageId })\n return ok({ success: true, messageId: info.messageId })\n }\n catch (error) {\n logger.error('Email send failed', { to: message.to, error })\n return err(toReachError(error))\n }\n },\n }\n}\n","/**\n * 单条队列投递:发送前持久化门闩、在途续租及未知结果隔离。\n * @module reach-delivery\n */\nimport type { HaiResult } from '@h-ai/core'\nimport type { ReachProvider } from './providers/reach-provider-types.js'\nimport type { ReachMessage, SendResult } from './reach-types.js'\nimport type { SendLogRepository, StoredSendLog } from './repositories/reach-repository-send-log.js'\nimport { core, err } from '@h-ai/core'\nimport { reachM } from './reach-i18n.js'\nimport { HaiReachError } from './reach-types.js'\n\nconst logger = core.logger.child({ module: 'reach', scope: 'delivery' })\nconst DEFAULT_LEASE_MS = 10 * 60 * 1000\n\n/**\n * 内部投递已领取记录;外部调用前原子持久化sending,有效owner条件提交、在途续租。\n * 续租不能撤销副作用:非幂等请求开始后失败/失租约进入unknown;API仅在真实持久去重时以同queueId有限重试。\n * @param row - 当前owner已领取的记录。\n * @param owner - 本次唯一领取凭据。\n * @param provider - 对应已连接Provider。\n * @param repo - 共享数据库仓库。\n * @param leaseMs - 领取期限毫秒数。\n * @returns 已提交或明确失败/unknown;旧owner迟到不能覆盖新状态。\n *\n * @example 模块内部上下文\n * ```ts\n * async function deliverNext(provider: ReachProvider, repository: SendLogRepository) {\n * const owner = 'worker:' + crypto.randomUUID()\n * const claimed = await repository.claimPendingBatch(owner, { limit: 1 })\n * if (!claimed.success) throw new Error(claimed.error.message)\n * const row = claimed.data[0]\n * if (!row) return\n * // 调用方已按row.provider选择匹配的provider;否则应failClaim退避,不能发送到其它通道。\n * const result = await deliverClaimedMessage(row, owner, provider, repository)\n * if (!result.success) throw new Error(result.error.message)\n * }\n * ```\n */\nexport async function deliverClaimedMessage(\n row: StoredSendLog,\n owner: string,\n provider: ReachProvider,\n repo: SendLogRepository,\n leaseMs = DEFAULT_LEASE_MS,\n): Promise<HaiResult<void>> {\n const retrySafe = provider.supportsIdempotency\n if (!row.queueId || (row.retrySafe && !retrySafe)) {\n await repo.markUnknown(row.id, owner)\n return err(HaiReachError.SEND_FAILED, reachM('reach_deliveryUnknown'))\n }\n if (!provider.isConnected()) {\n await repo.failClaim(row, owner, 'provider_unavailable')\n return err(HaiReachError.NOT_INITIALIZED, reachM('reach_notInitialized'))\n }\n\n let message: ReachMessage\n try {\n message = {\n provider: row.provider,\n to: row.toAddr,\n subject: row.subject ?? undefined,\n body: row.body ?? undefined,\n template: row.template ?? undefined,\n vars: row.varsJson ? JSON.parse(row.varsJson) as Record<string, string> : undefined,\n extra: row.extraJson ? JSON.parse(row.extraJson) as Record<string, unknown> : undefined,\n idempotencyKey: row.queueId,\n }\n }\n catch (cause) {\n // 坏持久化载荷不会随重试改善;尚未调用外部服务,直接进入死信。\n await repo.failClaim(row, owner, 'invalid_message', true)\n return err(HaiReachError.SEND_FAILED, reachM('reach_sendRecordSaveFailed'), cause)\n }\n\n const started = await repo.beginDelivery(row.id, owner, retrySafe, leaseMs)\n if (!started.success)\n return started\n\n let stopped = false\n let lost = false\n let renewing: Promise<void> | undefined\n let timer: ReturnType<typeof setTimeout> | undefined\n const renew = async () => {\n try {\n const result = await repo.renewClaim(row.id, owner, leaseMs)\n lost = !result.success\n }\n catch {\n lost = true\n }\n if (!stopped && !lost) {\n timer = setTimeout(() => {\n renewing = renew()\n }, Math.max(1, Math.floor(leaseMs / 3)))\n }\n }\n timer = setTimeout(() => {\n renewing = renew()\n }, Math.max(1, Math.floor(leaseMs / 3)))\n\n let outcome: HaiResult<SendResult>\n try {\n outcome = await provider.send(message)\n }\n catch (cause) {\n outcome = err(HaiReachError.SEND_FAILED, reachM('reach_deliveryUnknown'), cause)\n }\n finally {\n stopped = true\n clearTimeout(timer)\n await renewing\n }\n\n if (lost) {\n if (!retrySafe)\n await repo.markUnknown(row.id, owner)\n return err(HaiReachError.SEND_FAILED, reachM('reach_claimLost'))\n }\n if (outcome.success && outcome.data.success) {\n const marked = await repo.markSent(row.id, owner, outcome.data.messageId)\n if (!marked.success && !retrySafe)\n await repo.markUnknown(row.id, owner)\n return marked\n }\n if (!retrySafe) {\n await repo.markUnknown(row.id, owner)\n logger.warn('Delivery outcome is unknown; automatic redelivery stopped', { queueId: row.queueId, provider: row.provider })\n return err(HaiReachError.SEND_FAILED, reachM('reach_deliveryUnknown'), outcome.success ? undefined : outcome.error)\n }\n // 接收端持久去重时,响应丢失也可以在之后使用同一个 queueId 重试。\n const permanent = !outcome.success && [HaiReachError.DELIVERY_REJECTED.code, HaiReachError.INVALID_RECIPIENT.code].includes(outcome.error.code)\n const released = await repo.failClaim(row, owner, permanent ? 'permanent_rejection' : 'transient_failure', permanent)\n if (!released.success)\n return released\n return err(HaiReachError.SEND_FAILED, reachM('reach_deliveryUnknown'), outcome.success ? undefined : outcome.error)\n}\n","/** 凭据仅驻留当前调用;持久回执占用幂等键,发送不明也不自动重投旧凭据。 */\nimport type { HaiResult } from '@h-ai/core'\nimport type { ReachProvider } from './providers/reach-provider-types.js'\nimport type { ReachMessage, SendResult } from './reach-types.js'\nimport type { SendLogRepository, StoredSendLog } from './repositories/reach-repository-send-log.js'\nimport { createHash } from 'node:crypto'\nimport { core, err, ok } from '@h-ai/core'\nimport { reachM } from './reach-i18n.js'\nimport { HaiReachError } from './reach-types.js'\n\n/**\n * 元数据回执与普通发送队列共用唯一键约束,不保存可重建凭据的载荷或摘要。\n * @example 模块内部上下文\n * ```ts\n * function receipt(row: StoredSendLog): HaiResult<SendResult> {\n * return previousResult(row)\n * }\n * ```\n */\nfunction previousResult(row: StoredSendLog): HaiResult<SendResult> {\n return row.status === 'sent'\n ? ok({ success: true, receiptId: row.queueId ?? undefined })\n : unconfirmed(row.queueId!)\n}\n\nfunction unconfirmed(receiptId: string): HaiResult<SendResult> {\n return { success: false, error: { ...HaiReachError.SEND_FAILED, message: reachM('reach_sensitiveDeliveryUnconfirmed'), ext: { receiptId } } }\n}\n\n/**\n * 只允许一个节点进行凭据投递;失败/崩溃只留下无载荷回执,不恢复发送。\n * @example 模块内部上下文\n * ```ts\n * async function deliver(message: ReachMessage, provider: ReachProvider, repository: SendLogRepository) {\n * const result = await sendSensitive(message, provider, repository, false)\n * if (!result.success) return result\n * return result\n * }\n * ```\n */\nexport async function sendSensitive(message: ReachMessage, provider: ReachProvider, repo: SendLogRepository | null | undefined, blocked: boolean): Promise<HaiResult<SendResult>> {\n if (!repo || !message.idempotencyKey?.trim() || message.idempotencyKey.length > 255 || !Number.isSafeInteger(message.expiresAt))\n return err(HaiReachError.CONFIG_ERROR, reachM('reach_sensitivePolicyRequired'))\n const hash = createHash('sha256').update(JSON.stringify([message.provider, message.to, message.idempotencyKey])).digest('hex')\n // 固定 UUID 保持既有回执查询契约;只散列随机操作键与路由,不散列低熵密码或验证码。\n const queueId = `${hash.slice(0, 8)}-${hash.slice(8, 12)}-5${hash.slice(13, 16)}-a${hash.slice(17, 20)}-${hash.slice(20, 32)}`\n const find = () => repo.findAll({ where: 'queue_id = ?', params: [queueId], limit: 1 })\n const existing = await find()\n if (!existing.success)\n return existing\n if (existing.data[0])\n return previousResult(existing.data[0])\n const now = Date.now()\n if (message.expiresAt! <= now)\n return err(HaiReachError.MESSAGE_EXPIRED, reachM('reach_messageExpired'))\n const created = await repo.create({\n queueId,\n provider: message.provider,\n toAddr: '[REDACTED]',\n subject: null,\n body: null,\n template: null,\n varsJson: null,\n extraJson: null,\n status: blocked ? 'discarded' : 'sending',\n retrySafe: false,\n createdAt: now,\n updatedAt: now,\n lockedUntil: Math.min(message.expiresAt!, now + 600_000),\n nextAttemptAt: 0,\n })\n if (!created.success || created.data.changes !== 1) {\n const previous = await find()\n if (previous.success && previous.data[0])\n return previousResult(previous.data[0])\n return unconfirmed(queueId)\n }\n if (blocked)\n return err(HaiReachError.DND_BLOCKED, reachM('reach_dndBlocked'))\n if (Date.now() >= message.expiresAt!) {\n await repo.settleSensitive(queueId, 'discarded')\n return err(HaiReachError.MESSAGE_EXPIRED, reachM('reach_messageExpired'))\n }\n let delivered: HaiResult<SendResult>\n try {\n // 允许宿主明确启用的开发 Console 输出,但通知内容不进入任何持久采集观察器。\n delivered = await core.context.run({ suppressLogObservers: true }, () => provider.send(message))\n }\n catch {\n delivered = err(HaiReachError.SEND_FAILED, reachM('reach_sensitiveDeliveryUnconfirmed'))\n }\n const success = delivered.success && delivered.data.success && !delivered.data.deferred\n const settled = await repo.settleSensitive(queueId, success ? 'sent' : 'unknown')\n if (!settled.success || !success)\n return unconfirmed(queueId)\n return ok({ success: true, receiptId: queueId })\n}\n","/**\n * @h-ai/reach — 发送逻辑\n *\n * 本文件封装触达模块的发送逻辑,包括: - 消息预处理(模板渲染 + Provider 路由推导) - DND(免打扰)时间段检查与策略处理(discard / delay) - 通过 SendLogRepository 持久化发送记录 - DND 恢复定时任务(flush pending 消息)\n * @module reach-send\n */\n\nimport type { HaiResult } from '@h-ai/core'\nimport type { ReachProvider } from './providers/reach-provider-types.js'\nimport type { DndConfig, ReachQueueConfig } from './reach-config.js'\nimport type { ReachMessage, ReachTemplateRegistry, SendResult } from './reach-types.js'\n\nimport type { SendLogRepository } from './repositories/reach-repository-send-log.js'\nimport { cache } from '@h-ai/cache'\nimport { core, err, ok } from '@h-ai/core'\n\nimport { ReachQueueConfigSchema } from './reach-config.js'\nimport { deliverClaimedMessage } from './reach-delivery.js'\nimport { reachM } from './reach-i18n.js'\nimport { sendSensitive } from './reach-send-sensitive.js'\nimport {\n HaiReachError,\n\n} from './reach-types.js'\n\nconst logger = core.logger.child({ module: 'reach', scope: 'send' })\nconst FLUSH_LOCK_KEY = 'hai:reach:flush-pending'\nconst FLUSH_LOCK_TTL = 300\nconst FLUSH_BATCH_SIZE = 100\nconst FLUSH_CLAIM_LOCK_MS = 10 * 60 * 1000\n\n/** 稳定的节点标识,用于分布式锁 owner(进程级别唯一) */\nconst reachNodeId = `reach:${crypto.randomUUID()}`\n\n// ─── DND(免打扰)检查 ───\n\n/**\n * 把已由DND Schema校验的HH:mm转换为当日分钟,不能直接用于未校验输入。\n * @param time - 已校验的24小时制文本。\n * @returns 0–1439分钟。\n *\n * @example 模块内部上下文\n * ```ts\n * const minutes = parseTimeToMinutes('22:30')\n * ```\n */\nfunction parseTimeToMinutes(time: string): number {\n const [h, m] = time.split(':').map(Number)\n return h * 60 + m\n}\n\n/**\n * 内部按节点本地时区判断半开区间DND,支持跨午夜;start=end不拦截,集群须统一时区/时钟。\n * @param dnd - 已校验免打扰配置。\n * @param now - 当前时间,测试可注入。\n * @returns 是否需要执行DND策略,不意味着已入队。\n *\n * @example 模块内部上下文\n * ```ts\n * const blocked = isDndBlocked({ enabled: true, strategy: 'delay', start: '22:00', end: '08:00' }, new Date(2026, 9, 23, 23, 0))\n * ```\n */\nexport function isDndBlocked(dnd: DndConfig | undefined, now: Date = new Date()): boolean {\n if (!dnd?.enabled) {\n return false\n }\n\n const startMin = parseTimeToMinutes(dnd.start)\n const endMin = parseTimeToMinutes(dnd.end)\n const currentMin = now.getHours() * 60 + now.getMinutes()\n\n if (startMin === endMin) {\n return false\n }\n\n if (startMin < endMin) {\n return currentMin >= startMin && currentMin < endMin\n }\n\n return currentMin >= startMin || currentMin < endMin\n}\n\n/**\n * 内部计算到下一次DND结束的毫秒数,按本地时区、秒精度调度;不是跨节点数据库时钟。\n * @param dnd - 已校验配置。\n * @param now - 当前本地时间。\n * @returns 下一结束点的延迟毫秒数。\n *\n * @example 模块内部上下文\n * ```ts\n * const delay = msUntilDndEnd({ enabled: true, strategy: 'delay', start: '22:00', end: '08:00' }, new Date(2026, 9, 23, 23, 0))\n * ```\n */\nexport function msUntilDndEnd(dnd: DndConfig, now: Date = new Date()): number {\n const endMin = parseTimeToMinutes(dnd.end)\n const currentMin = now.getHours() * 60 + now.getMinutes()\n const currentSec = now.getSeconds()\n\n let diffMin = endMin - currentMin\n if (diffMin <= 0) {\n diffMin += 24 * 60\n }\n return diffMin * 60 * 1000 - currentSec * 1000\n}\n\n// ─── 消息预处理 ───\n\n/**\n * 内部渲染模板并补全路由/主题/正文;消息显式字段优先,渲染失败原样返回。\n * @param message - 原消息,不原地修改。\n * @param templateRegistry - 真实数据库模板注册表。\n * @returns 补全消息或模板失败。\n *\n * @example 模块内部上下文\n * ```ts\n * async function prepare(message: ReachMessage, registry: ReachTemplateRegistry) {\n * const result = await preprocessMessage(message, registry)\n * if (!result.success) throw new Error(result.error.message)\n * return result.data\n * }\n * ```\n */\nexport async function preprocessMessage(\n message: ReachMessage,\n templateRegistry: ReachTemplateRegistry,\n): Promise<HaiResult<ReachMessage>> {\n if (!message.template) {\n return ok(message)\n }\n\n const rendered = await templateRegistry.render(message.template, message.vars ?? {})\n if (!rendered.success) {\n return rendered\n }\n\n const template = await templateRegistry.resolve(message.template)\n\n const processed: ReachMessage = {\n ...message,\n provider: message.provider || (template.success ? template.data.provider : '') || '',\n subject: message.subject ?? rendered.data.subject,\n body: message.body ?? rendered.data.body,\n }\n return ok(processed)\n}\n\n// ─── 发送记录持久化(通过 Repository) ───\n\n/**\n * 等待持久写入并检查实际changes=1,可靠入队不能由内存状态或未等待的Promise代替。\n * @param repo - 真实数据库仓库。\n * @param message - 已完成模板预处理的消息。\n * @param status - pending可靠队列或sent观察日志。\n * @param provider - Provider配置名称。\n * @param messageId - 可选外部消息编号。\n * @param queueId - 延迟入队时预生成的持久UUID回执。\n * @param queueConfig - 随消息保存的重试预算与退避策略。\n * @returns 持久写入HaiResult;调用方按即时/入队语义分别处理失败。\n *\n * @example 模块内部上下文\n * ```ts\n * async function enqueue(repository: SendLogRepository, message: ReachMessage, providerName: string) {\n * const queueId = crypto.randomUUID()\n * const saved = await saveSendRecord(repository, message, 'pending', providerName, undefined, queueId)\n * if (!saved.success) throw new Error(saved.error.message)\n * return queueId\n * }\n * ```\n */\nasync function saveSendRecord(\n repo: SendLogRepository,\n message: ReachMessage,\n status: 'sent' | 'pending',\n provider: string,\n messageId?: string,\n queueId?: string,\n queueConfig?: ReachQueueConfig,\n): Promise<HaiResult<void>> {\n try {\n const inserted = await repo.create({\n queueId: queueId ?? null,\n provider,\n toAddr: message.to,\n subject: message.subject ?? null,\n body: message.body ?? null,\n template: message.template ?? null,\n varsJson: message.vars ? JSON.stringify(message.vars) : null,\n extraJson: message.extra ? JSON.stringify(message.extra) : null,\n status,\n ...queueConfig,\n messageId: messageId ?? null,\n createdAt: Date.now(),\n })\n if (!inserted.success)\n return inserted\n if (inserted.data.changes !== 1)\n return err(HaiReachError.SEND_FAILED, reachM('reach_sendRecordSaveFailed'))\n return ok(undefined)\n }\n catch (cause) {\n return err(HaiReachError.SEND_FAILED, reachM('reach_sendRecordSaveFailed'), cause)\n }\n}\n\n// ─── DND 恢复定时任务 ───\n\n/** 定时器引用 */\nlet dndTimer: ReturnType<typeof setTimeout> | null = null\n\n/** 保存调度器参数,用于循环调度 */\nlet schedulerContext: { dndConfig: DndConfig, providers: Map<string, ReachProvider>, repo: SendLogRepository | null } | null = null\n\n/**\n * 内部启动本机扫描计时器;业务队列状态由共享数据库持有,本机计时器不决定投递所有权。\n * DND外立即扫描遗留消息,之后按分钟有界恢复;仅delay策略启用。\n * @param dndConfig - 已校验DND配置。\n * @param providers - 当前已连接Provider路由表。\n * @param repo - 真实持久队列;null不产生可靠入队能力。\n *\n * @example 模块内部上下文\n * ```ts\n * function start(config: DndConfig, providers: Map<string, ReachProvider>, repository: SendLogRepository) {\n * startDndScheduler(config, providers, repository)\n * // 生命周期结束调用resetSendState,不把停止计时器当作撤销外部投递。\n * }\n * ```\n */\nexport function startDndScheduler(\n dndConfig: DndConfig,\n providers: Map<string, ReachProvider>,\n repo: SendLogRepository | null,\n): void {\n stopDndScheduler()\n\n if (!dndConfig.enabled || dndConfig.strategy !== 'delay') {\n return\n }\n\n schedulerContext = { dndConfig, providers, repo }\n const context = schedulerContext\n\n // 如果当前不在 DND 时段,不需要启动定时器\n if (!isDndBlocked(dndConfig)) {\n // 但仍然尝试 flush 一次(可能上次 DND 结束后有残留 pending)\n flushPendingMessages(providers, repo).catch((error) => {\n logger.warn('Failed to flush pending messages on init', { error })\n }).finally(() => {\n if (schedulerContext === context)\n scheduleDndCheck()\n })\n return\n }\n\n scheduleFlushAtDndEnd()\n}\n\n/**\n * 在 DND 结束时触发 flush 并重新调度\n */\nfunction scheduleFlushAtDndEnd(): void {\n if (!schedulerContext)\n return\n const context = schedulerContext\n const { dndConfig, providers, repo } = context\n\n const delayMs = msUntilDndEnd(dndConfig)\n logger.info('DND scheduler started', { delayMs, dndEnd: dndConfig.end })\n\n dndTimer = setTimeout(() => {\n if (schedulerContext !== context)\n return\n logger.info('DND period ended, flushing pending messages')\n flushPendingMessages(providers, repo)\n .catch((error) => {\n logger.error('Failed to flush pending messages', { error })\n })\n .finally(() => {\n // 重新调度:等待下一个 DND 周期\n if (schedulerContext === context)\n scheduleDndCheck()\n })\n }, delayMs)\n}\n\n/**\n * 定期检查是否进入 DND 时段,进入后切换到 flush 调度\n */\nfunction scheduleDndCheck(): void {\n if (!schedulerContext)\n return\n const context = schedulerContext\n const { dndConfig, providers, repo } = context\n\n // 每分钟检查一次是否进入 DND\n const CHECK_INTERVAL = 60 * 1000\n dndTimer = setTimeout(() => {\n if (schedulerContext !== context)\n return\n if (isDndBlocked(dndConfig)) {\n scheduleFlushAtDndEnd()\n }\n else {\n // DND 时段外持续恢复遗留任务,避免节点启动时租约未过期就要等到下一天。\n flushPendingMessages(providers, repo)\n .catch(error => logger.warn('Failed to recover pending messages', { error }))\n .finally(() => {\n if (schedulerContext === context)\n scheduleDndCheck()\n })\n }\n }, CHECK_INTERVAL)\n}\n\n/**\n * 内部清除当前本机计时器;不清数据库、不停止已开始的外部Provider调用。\n * 完整生命周期关闭通过resetSendState同时废弃调度上下文。\n *\n * @example 模块内部上下文\n * ```ts\n * stopDndScheduler()\n * ```\n */\nexport function stopDndScheduler(): void {\n if (dndTimer !== null) {\n clearTimeout(dndTimer)\n dndTimer = null\n }\n}\n\n/**\n * 内部关闭时清除计时器并废弃旧调度上下文,迟到finally不会再排下一轮;不撤销在途外部请求。\n *\n * @example 模块内部上下文\n * ```ts\n * resetSendState()\n * ```\n */\nexport function resetSendState(): void {\n stopDndScheduler()\n schedulerContext = null\n}\n\n/**\n * 一轮最多100次逐条领取,固定eligibleBefore让退避消息留给下轮,坏消息不在满批循环自旋。\n * 缓存锁只减少扫描;资格由数据库owner/租约/sending门闩决定,外部效果依赖幂等或unknown隔离。\n * @param providers - 当前已连接路由表。\n * @param repo - 持久队列;缺省无可靠待发送队列。\n *\n * @example 模块内部上下文\n * ```ts\n * async function flush(providers: Map<string, ReachProvider>, repository: SendLogRepository) {\n * await flushPendingMessages(providers, repository)\n * // 生命周期调度负责下一轮;不在此函数外无界while重试。\n * }\n * ```\n */\nasync function flushPendingMessages(\n providers: Map<string, ReachProvider>,\n repo: SendLogRepository | null,\n): Promise<void> {\n if (!repo) {\n return\n }\n\n // 分布式锁:防止多节点同时 flush\n // 注意:这里的锁主要用于减少惊群和重复扫描;真正的行级正确性由 repo.claimPendingBatch 的 claim 事务保证。\n let lockOwner: string | null = null\n // flushOwner 是本轮 flush 的短期 lease owner。\n // 同一进程后续再次 flush 会生成新的 owner,避免旧批次在 lease 失效后误释放/误提交新批次的记录。\n const flushOwner = `${reachNodeId}:${crypto.randomUUID()}`\n if (cache.isInitialized) {\n const lockResult = await cache.lock.acquire(FLUSH_LOCK_KEY, { ttl: FLUSH_LOCK_TTL })\n if (!lockResult.success) {\n logger.warn('Failed to acquire flush lock, continuing with row claims only', { error: lockResult.error.message })\n }\n else if (!lockResult.data) {\n logger.info('Skipping flush, another node holds the lock')\n return\n }\n else {\n lockOwner = lockResult.data\n }\n }\n\n try {\n // 固定本轮资格截止点,耗时发送期间到期的重试留到下一轮,避免毒消息重复占用预算。\n const eligibleBefore = Date.now()\n for (let processed = 0; processed < FLUSH_BATCH_SIZE; processed += 1) {\n // 按实际执行逐条领取,避免后排消息排队等待时耗尽租约;单轮处理数量有界。\n const claimResult = await repo.claimPendingBatch(flushOwner, {\n limit: 1,\n lockMs: FLUSH_CLAIM_LOCK_MS,\n eligibleBefore,\n })\n if (!claimResult.success) {\n logger.warn('Failed to claim pending messages', { error: claimResult.error.message })\n return\n }\n\n if (claimResult.data.length === 0) {\n return\n }\n\n logger.debug('Flushing pending messages', { count: claimResult.data.length })\n\n for (const row of claimResult.data) {\n // 缺少 Provider 可由配置修复,但也必须消耗重试预算并退避,不能原地自旋。\n const provider = providers.get(row.provider)\n if (!provider) {\n logger.warn('Provider not found for pending message, releasing claim', { provider: row.provider, id: row.id })\n const releaseResult = await repo.failClaim(row, flushOwner, 'provider_unavailable')\n if (!releaseResult.success) {\n logger.warn('Failed to release pending message claim', { id: row.id, error: releaseResult.error.message })\n }\n continue\n }\n\n const sent = await deliverClaimedMessage(row, flushOwner, provider, repo, FLUSH_CLAIM_LOCK_MS)\n if (!sent.success)\n logger.warn('Pending message delivery did not commit', { id: row.id, code: sent.error.code })\n }\n }\n }\n finally {\n // 释放锁\n if (lockOwner) {\n await cache.lock.release(FLUSH_LOCK_KEY, lockOwner).catch((error: unknown) => {\n logger.warn('Failed to release flush lock', { error })\n })\n }\n }\n}\n\n// ─── 发送入口 ───\n\n/**\n * 内部发送编排:模板/路由、DND、即时投递或持久pending入队。\n * delay仅在数据库写入确认为1条后返回deferred+queueId;即时外部成功不因可选日志故障返回失败,避免重发。\n * @param message - 原始消息。\n * @param providers - 已连接Provider路由。\n * @param templateRegistry - 真实模板注册表。\n * @param dndConfig - 可选DND策略。\n * @param repo - 可选日志仓库;delay必须存在。\n * @param queueConfig - 入队时保存的有界重试策略。\n * @returns 发送/可靠入队结果;不声称接收端业务恰好执行一次。\n *\n * @example 模块内部上下文\n * ```ts\n * async function sendDirect(message: ReachMessage, providers: Map<string, ReachProvider>, registry: ReachTemplateRegistry, repository: SendLogRepository) {\n * const result = await executeSend(message, providers, registry, undefined, repository)\n * if (!result.success) throw new Error(result.error.message)\n * return result.data\n * }\n * ```\n */\nexport async function executeSend(\n message: ReachMessage,\n providers: Map<string, ReachProvider>,\n templateRegistry: ReachTemplateRegistry,\n dndConfig?: DndConfig,\n repo?: SendLogRepository | null,\n queueConfig: ReachQueueConfig = ReachQueueConfigSchema.parse({}),\n): Promise<HaiResult<SendResult>> {\n if (!message.to) {\n return err(\n HaiReachError.INVALID_RECIPIENT,\n reachM('reach_invalidRecipient', { params: { recipient: '' } }),\n )\n }\n\n const preprocessed = await preprocessMessage(message, templateRegistry)\n if (!preprocessed.success) {\n return preprocessed\n }\n\n // 校验 Provider 存在性(在 DND 检查前,确保 delay 也能正确路由)\n const providerName = preprocessed.data.provider\n if (!providerName) {\n return err(\n HaiReachError.PROVIDER_NOT_FOUND,\n reachM('reach_providerRequired'),\n )\n }\n\n if (!providers.has(providerName)) {\n return err(\n HaiReachError.PROVIDER_NOT_FOUND,\n reachM('reach_providerNotFound', { params: { provider: providerName } }),\n )\n }\n\n if (preprocessed.data.sensitive)\n return sendSensitive(preprocessed.data, providers.get(providerName)!, repo, isDndBlocked(dndConfig))\n\n // DND 检查\n if (isDndBlocked(dndConfig)) {\n if (message.deferPolicy === 'reject')\n return err(HaiReachError.DND_BLOCKED, reachM('reach_dndBlocked'))\n const strategy = dndConfig?.strategy ?? 'discard'\n\n if (strategy === 'delay') {\n if (!repo)\n return err(HaiReachError.SEND_FAILED, reachM('reach_dndPersistenceRequired'))\n\n // 预先生成并随消息落盘,避免依赖不同数据库的自增 ID 返回约定。\n const queueId = crypto.randomUUID()\n const saved = await saveSendRecord(repo, preprocessed.data, 'pending', providerName, undefined, queueId, queueConfig)\n if (!saved.success) {\n logger.warn('Failed to persist deferred message', { provider: providerName, code: saved.error.code })\n return err(\n HaiReachError.SEND_FAILED,\n reachM('reach_dndEnqueueFailed'),\n saved.error,\n )\n }\n logger.info('Message persisted for DND delivery', { provider: providerName, queueId })\n return ok({ success: true, deferred: true, queueId })\n }\n\n // discard 策略:直接拒绝\n logger.info('Message blocked by DND (discard strategy)', { provider: providerName, to: message.to })\n return err(\n HaiReachError.DND_BLOCKED,\n reachM('reach_dndBlocked'),\n )\n }\n\n const provider = providers.get(providerName)!\n\n logger.debug('Sending message', {\n provider: providerName,\n to: preprocessed.data.to,\n template: message.template,\n })\n\n const result = await provider.send(preprocessed.data)\n if (!result.success) {\n logger.warn('Message send failed', {\n provider: providerName,\n to: message.to,\n error: result.error.code,\n })\n return result\n }\n\n // 已完成的外部发送不因可选日志失败而返回失败,避免调用方重发;仍记录持久化失败。\n if (repo) {\n void saveSendRecord(repo, preprocessed.data, 'sent', providerName, result.data.messageId).then((saved) => {\n if (!saved.success)\n logger.warn('Failed to persist sent message log', { provider: providerName, code: saved.error.code })\n })\n }\n\n return result\n}\n","/**\n * @h-ai/reach — 模板引擎\n *\n * 本文件提供触达模块的模板定义与渲染功能。\n * 所有模板通过数据库持久化存储,避免多节点部署时的状态不一致。\n * @module reach-template\n */\n\nimport type { HaiResult } from '@h-ai/core'\nimport type { ReachTemplate, ReachTemplateRegistry, RenderedTemplate } from './reach-types.js'\n\nimport type { TemplateRepository } from './repositories/reach-repository-template.js'\nimport { err, ok } from '@h-ai/core'\n\nimport { reachM } from './reach-i18n.js'\nimport { HaiReachError } from './reach-types.js'\n\n// ─── 变量渲染 ───\n\n/**\n * 用自身属性替换{word}占位符,未知变量保留原文;不读取原型链,不做HTML转义。\n * @param template - 模板文本。\n * @param vars - 字符串变量。\n * @returns 渲染文本,最终HTML安全上下文另行处理。\n *\n * @example 模块内部上下文\n * ```ts\n * const text = renderString('Hello {name}, {missing}', { name: 'Alice' })\n * ```\n */\nfunction renderString(template: string, vars: Record<string, string>): string {\n return template.replace(/\\{(\\w+)\\}/g, (match, key: string) => {\n return Object.hasOwn(vars, key) ? vars[key] : match\n })\n}\n\n/**\n * 将持久模板转换为主题/正文,渲染异常返回TEMPLATE_RENDER_FAILED,不改写原模板。\n * @param template - 已解析模板。\n * @param vars - 变量字典。\n * @returns 渲染HaiResult,结果不代表内容已消毒或消息已发送。\n *\n * @example 模块内部上下文\n * ```ts\n * const result = renderTemplate({ name: 'welcome', provider: 'webhook', body: 'Hello {name}' }, { name: 'Alice' })\n * if (!result.success) throw new Error(result.error.message)\n * ```\n */\nfunction renderTemplate(template: ReachTemplate, vars: Record<string, string>): HaiResult<RenderedTemplate> {\n try {\n const rendered: RenderedTemplate = {\n subject: template.subject ? renderString(template.subject, vars) : undefined,\n body: renderString(template.body, vars),\n }\n return ok(rendered)\n }\n catch (error) {\n return err(\n HaiReachError.TEMPLATE_RENDER_FAILED,\n reachM('reach_templateRenderFailed', {\n params: { error: error instanceof Error ? error.message : String(error) },\n }),\n error,\n )\n }\n}\n\n// ─── 无数据库时的错误 ───\n\nfunction noDbError(msgKey: 'reach_templateSaveFailed' | 'reach_templateDeleteFailed'): HaiResult<never> {\n return err(\n HaiReachError.SEND_FAILED,\n reachM(msgKey, { params: { error: 'database not available' } }),\n )\n}\n\n// ─── 模板注册表工厂 ───\n\n/**\n * 内部数据库模板注册表工厂;无repo时明确不保存到内存,写入返回失败、解析找不到模板、列表为空。\n * @param repo - 已初始化的模板数据库仓库。\n * @returns 注册表;批量保存逐条upsert,不承诺事务全有全无。\n *\n * @example 模块内部上下文\n * ```ts\n * const registry = createTemplateRegistry()\n * const result = await registry.save({ name: 'welcome', provider: 'webhook', subject: 'Welcome {userName}', body: 'Hello {userName}' })\n * if (!result.success) { const code = result.error.code }\n * // 无数据库明确失败;应用通过reach.template使用持久注册表。\n * ```\n */\nexport function createTemplateRegistry(repo?: TemplateRepository | null): ReachTemplateRegistry {\n return {\n async resolve(name: string): Promise<HaiResult<ReachTemplate>> {\n if (!repo) {\n return err(\n HaiReachError.TEMPLATE_NOT_FOUND,\n reachM('reach_templateNotFound', { params: { template: name } }),\n )\n }\n\n const dbResult = await repo.findByName(name)\n if (!dbResult.success) {\n return dbResult\n }\n if (dbResult.data) {\n return ok({\n name: dbResult.data.name,\n provider: dbResult.data.provider,\n subject: dbResult.data.subject ?? undefined,\n body: dbResult.data.body,\n })\n }\n\n return err(\n HaiReachError.TEMPLATE_NOT_FOUND,\n reachM('reach_templateNotFound', { params: { template: name } }),\n )\n },\n\n async save(template: ReachTemplate): Promise<HaiResult<void>> {\n if (!repo) {\n return noDbError('reach_templateSaveFailed')\n }\n return repo.upsert(template)\n },\n\n async saveBatch(templates: ReachTemplate[]): Promise<HaiResult<void>> {\n if (!repo) {\n return noDbError('reach_templateSaveFailed')\n }\n const results = await Promise.all(templates.map(t => repo!.upsert(t)))\n const failed = results.find(r => !r.success)\n if (failed) {\n return failed\n }\n return ok(undefined)\n },\n\n async remove(name: string): Promise<HaiResult<void>> {\n if (!repo) {\n return noDbError('reach_templateDeleteFailed')\n }\n return repo.deleteByName(name)\n },\n\n async list(): Promise<HaiResult<ReachTemplate[]>> {\n if (!repo) {\n return ok([])\n }\n return repo.listTemplates()\n },\n\n async render(name: string, vars: Record<string, string>): Promise<HaiResult<RenderedTemplate>> {\n const resolved = await this.resolve(name)\n if (!resolved.success) {\n return resolved\n }\n return renderTemplate(resolved.data, vars)\n },\n }\n}\n","/** 发送账本升级仅处理 Reach 自己的表;旧在途消息隔离,不能猜测发送结果。 */\nimport type { ReldbFunctions, SchemaMigration } from '@h-ai/reldb'\nimport { randomUUID } from 'node:crypto'\nimport { ok } from '@h-ai/core'\nimport { migrateSchema, reldb, schemaColumns } from '@h-ai/reldb'\n\n/**\n * 预检或升级 Reach 队列。旧凭据相关载荷清除,待发凭据丢弃,保留发送状态和历史标识。\n * @example\n * ```ts\n * import { migrateReach } from '@h-ai/reach'\n * const plan = await migrateReach({ dryRun: true })\n * ```\n */\nexport function migrateReach(options: { dryRun?: boolean } = {}, db: ReldbFunctions = reldb) {\n const definitions = { queue_id: db.config?.type === 'mysql' ? 'VARCHAR(255)' : 'TEXT', retry_safe: 'BOOLEAN NOT NULL DEFAULT FALSE', attempt_count: 'BIGINT NOT NULL DEFAULT 0', max_attempts: 'BIGINT NOT NULL DEFAULT 5', next_attempt_at: 'BIGINT NOT NULL DEFAULT 0', retry_delay_ms: 'BIGINT NOT NULL DEFAULT 60000', max_retry_delay_ms: 'BIGINT NOT NULL DEFAULT 3600000', last_failure: 'TEXT' }\n const steps: SchemaMigration[] = [{\n version: 1,\n name: 'durable-delivery-ledger',\n async needed(sql) {\n const found = await schemaColumns(db, 'hai_reach_send_log', sql)\n return found.success ? ok(found.data.length > 0 && Object.keys(definitions).some(name => !found.data.includes(name))) : found\n },\n async apply(sql) {\n const found = await schemaColumns(db, 'hai_reach_send_log', sql)\n if (!found.success)\n return found\n for (const [name, definition] of Object.entries(definitions)) {\n if (found.data.includes(name))\n continue\n const added = await sql.execute(`ALTER TABLE hai_reach_send_log ADD COLUMN ${name} ${definition}`)\n if (!added.success)\n return added\n }\n return ok(undefined)\n },\n }, {\n version: 2,\n name: 'legacy-delivery-isolation',\n async needed(sql) {\n const found = await schemaColumns(db, 'hai_reach_send_log', sql)\n if (!found.success || !found.data.length)\n return found.success ? ok(false) : found\n if (!found.data.includes('queue_id'))\n return ok(true)\n const row = await sql.get('SELECT id FROM hai_reach_send_log WHERE queue_id IS NULL AND status IN (\\'pending\\',\\'processing\\',\\'unknown\\') LIMIT 1')\n return row.success ? ok(Boolean(row.data)) : row\n },\n async apply(sql) {\n const isolated = await sql.execute('UPDATE hai_reach_send_log SET status=\\'unknown\\',processing_owner=NULL,locked_until=NULL WHERE status=\\'processing\\' AND queue_id IS NULL')\n if (!isolated.success)\n return isolated\n while (true) {\n const rows = await sql.query<{ id: number }>('SELECT id FROM hai_reach_send_log WHERE queue_id IS NULL AND status IN (\\'pending\\',\\'unknown\\') ORDER BY id LIMIT 100')\n if (!rows.success)\n return rows\n for (const row of rows.data) {\n const saved = await sql.execute('UPDATE hai_reach_send_log SET queue_id=? WHERE id=? AND queue_id IS NULL', [randomUUID(), row.id])\n if (!saved.success)\n return saved\n }\n if (rows.data.length < 100)\n return ok(undefined)\n }\n },\n }, {\n version: 3,\n name: 'credential-payload-retirement',\n async needed(sql) {\n const found = await schemaColumns(db, 'hai_reach_send_log', sql)\n if (!found.success || !found.data.length)\n return found.success ? ok(false) : found\n // 旧数据无法可靠识别正文中的任意凭据,升级一次性退役旧载荷,元数据和状态保留。\n const ledger = await schemaColumns(db, 'hai_schema_migrations', sql)\n if (!ledger.success)\n return ledger\n const version = ledger.data.length ? await sql.get<{ version: number }>('SELECT version FROM hai_schema_migrations WHERE module=?', ['reach']) : ok(null)\n if (!version.success)\n return version\n if (Number(version.data?.version ?? 0) >= 3)\n return ok(false)\n const row = await sql.get('SELECT id FROM hai_reach_send_log WHERE subject IS NOT NULL OR body IS NOT NULL OR template IS NOT NULL OR vars_json IS NOT NULL OR extra_json IS NOT NULL LIMIT 1')\n return row.success ? ok(Boolean(row.data)) : row\n },\n async apply(sql) {\n return sql.execute('UPDATE hai_reach_send_log SET body=NULL,subject=NULL,vars_json=NULL,extra_json=NULL,template=NULL,status=CASE WHEN status IN (\\'pending\\',\\'processing\\') THEN \\'discarded\\' WHEN status=\\'sending\\' THEN \\'unknown\\' ELSE status END,processing_owner=NULL,locked_until=NULL WHERE subject IS NOT NULL OR body IS NOT NULL OR template IS NOT NULL OR vars_json IS NOT NULL OR extra_json IS NOT NULL')\n },\n }, {\n version: 4,\n name: 'unique-operation-receipts',\n async needed(sql) {\n const columns = await schemaColumns(db, 'hai_reach_send_log', sql)\n if (!columns.success || !columns.data.length)\n return columns.success ? ok(false) : columns\n if (!columns.data.includes('queue_id'))\n return ok(true)\n if (db.config?.type === 'sqlite') {\n const indexes = await sql.query<{ name: string, unique: number }>('PRAGMA index_list(hai_reach_send_log)')\n if (!indexes.success)\n return indexes\n for (const index of indexes.data) {\n if (!index.unique || !/^\\w+$/.test(index.name))\n continue\n const fields = await sql.query<{ name: string }>(`PRAGMA index_info(${index.name})`)\n if (!fields.success)\n return fields\n if (fields.data.length === 1 && fields.data[0]?.name === 'queue_id')\n return ok(false)\n }\n return ok(true)\n }\n const found = db.config?.type === 'mysql'\n ? await sql.get('SELECT index_name FROM information_schema.statistics WHERE table_schema=DATABASE() AND table_name=? AND non_unique=0 GROUP BY index_name HAVING COUNT(*)=1 AND MAX(column_name)=?', ['hai_reach_send_log', 'queue_id'])\n : await sql.get('SELECT i.indexrelid FROM pg_index i JOIN pg_class t ON t.oid=i.indrelid JOIN pg_namespace n ON n.oid=t.relnamespace JOIN pg_attribute a ON a.attrelid=t.oid AND a.attnum=ANY(i.indkey) WHERE t.relname=? AND n.nspname=current_schema() AND i.indisunique AND i.indnatts=1 AND a.attname=?', ['hai_reach_send_log', 'queue_id'])\n return found.success ? ok(!found.data) : found\n },\n async apply(sql) {\n return sql.execute('CREATE UNIQUE INDEX uq_hai_reach_queue_v1 ON hai_reach_send_log(queue_id)')\n },\n }]\n return migrateSchema(db, 'reach', steps, options)\n}\n","/**\n * @h-ai/reach — 发送日志存储实现\n *\n * 基于 @h-ai/reldb 的发送日志存储实现。\n * @module reach-repository-send-log\n */\n\nimport type { HaiResult, PaginatedResult } from '@h-ai/core'\nimport type { CrudPageOptions, CrudQueryOptions, DmlWithTxOperations, ReldbCrudFieldDefinition, ReldbCrudRepository, ReldbFunctions } from '@h-ai/reldb'\nimport type { ReachQueueFailure, ReachQueueStatus, ReachUnknownResolution } from '../reach-types.js'\nimport { err, ok } from '@h-ai/core'\nimport { BaseReldbCrudRepository } from '@h-ai/reldb'\n\nimport { reachM } from '../reach-i18n.js'\nimport { migrateReach } from '../reach-migration.js'\nimport { HaiReachError } from '../reach-types.js'\n\n// ─── 发送日志实体类型 ───\n\n/**\n * 内部发送日志状态别名,与公共队列状态一致;不把unknown自动降级成pending。\n *\n * @example 模块内部上下文\n * ```ts\n * const status: SendLogStatus = 'unknown'\n * ```\n */\nexport type SendLogStatus = ReachQueueStatus\n\n/**\n * 内部有界领取参数;owner唯一,now仅测试注入,eligibleBefore固定本轮资格截止避免毒消息自旋。\n *\n * @example 模块内部上下文\n * ```ts\n * const options: SendLogClaimOptions = { limit: 1, lockMs: 600000, eligibleBefore: Date.now() }\n * ```\n */\nexport interface SendLogClaimOptions {\n /** 单批最多 claim 的记录数 */\n limit?: number\n /** 当前时间(测试时可注入) */\n now?: number\n /** 本轮扫描截止时间;重试后 nextAttemptAt 超出本轮时不再重复领取。 */\n eligibleBefore?: number\n /** claim 锁持续时间(毫秒) */\n lockMs?: number\n}\n\n/**\n * 内部持久日志实体,包含敏感收件人与正文;仅运维安全投影可对外,绝不把整行写日志。\n * 毫秒时间/次数/BIGINT读取严格映射安全整数number,和模板Date时间列不同。\n *\n * @example 模块内部上下文\n * ```ts\n * const { reldb } = await import('@h-ai/reldb')\n * const connected = await reldb.init({ type: 'postgresql', host: 'db', port: 5432, database: 'hai', user: 'postgres', password: process.env.HAI_DB_PASSWORD })\n * if (!connected.success) throw new Error(connected.error.message)\n * try {\n * const created = await createSendLogRepository(reldb)\n * if (!created.success) throw new Error(created.error.message)\n * const repository = created.data\n * const pending = await repository.findPending()\n * if (!pending.success) throw new Error(pending.error.message)\n * const row: StoredSendLog | undefined = pending.data[0]\n * if (row) { const attempts = row.attemptCount }\n * }\n * finally {\n * const closed = await reldb.close()\n * if (!closed.success) throw new Error(closed.error.message)\n * }\n * ```\n */\nexport interface StoredSendLog {\n /** 自增主键;驱动 BIGSERIAL/BIGINT 值读取后校验为安全整数 number。 */\n id: number\n /** 入队回执 UUID;延迟消息创建时写入,可用 queue_id 列追踪持久化状态。 */\n queueId: string | null\n /** Provider 名称 */\n provider: string\n /** 接收方地址 */\n toAddr: string\n /** 邮件主题 */\n subject: string | null\n /** 消息正文 */\n body: string | null\n /** 模板名称 */\n template: string | null\n /** 模板变量 JSON */\n varsJson: string | null\n /** 扩展参数 JSON */\n extraJson: string | null\n /** 发送状态 */\n status: SendLogStatus\n /** 已开始的投递是否可由接收端持久幂等保护;用于崩溃恢复决策。 */\n retrySafe?: boolean\n /** 已领取次数;领取时原子递增,崩溃恢复同样消耗预算。 */\n attemptCount: number\n /** 入队时保存的自动领取次数上限。 */\n maxAttempts: number\n /** 下次可领取时间(Unix 毫秒;BIGINT 安全 number)。 */\n nextAttemptAt: number\n /** 入队时保存的首次退避毫秒数。 */\n retryDelayMs: number\n /** 入队时保存的指数退避上限毫秒数。 */\n maxRetryDelayMs: number\n /** 不含消息正文的最近失败分类。 */\n lastFailure: ReachQueueFailure | null\n /** Provider 返回的消息 ID */\n messageId: string | null\n /** 当前 claim owner(处理中时存在) */\n processingOwner?: string\n /** 当前 claim 过期时间(Unix 时间戳毫秒) */\n lockedUntil?: number\n /** 创建时间(Unix 时间戳毫秒;BIGINT 读取后校验为安全整数 number)。 */\n createdAt: number\n /** 最近更新时间(Unix 时间戳毫秒;BIGINT 读取后校验为安全整数 number)。 */\n updatedAt?: number\n}\n\n// ─── 发送日志存储接口 ───\n\n/**\n * 内部共享数据库队列/日志仓库;逐条条件领取和sending门闩决定资格,进程cache锁仅减少扫描。\n * 非幂等sending过期隔离unknown;有限重试/退避持久化,dead/unknown只走受信条件管理入口。\n *\n * @example 模块内部上下文\n * ```ts\n * const { reldb } = await import('@h-ai/reldb')\n * const connected = await reldb.init({ type: 'postgresql', host: 'db', port: 5432, database: 'hai', user: 'postgres', password: process.env.HAI_DB_PASSWORD })\n * if (!connected.success) throw new Error(connected.error.message)\n * try {\n * const created = await createSendLogRepository(reldb)\n * if (!created.success) throw new Error(created.error.message)\n * const repository = created.data\n * const claimed = await repository.claimPendingBatch('worker:' + crypto.randomUUID(), { limit: 1 })\n * if (!claimed.success) throw new Error(claimed.error.message)\n * const count = claimed.data.length\n * }\n * finally {\n * const closed = await reldb.close()\n * if (!closed.success) throw new Error(closed.error.message)\n * }\n * ```\n */\nexport interface SendLogRepository extends ReldbCrudRepository<StoredSendLog> {\n /** 只结算无载荷的凭据回执;不覆盖已恢复或人工裁定的状态。 */\n settleSensitive: (queueId: string, status: 'sent' | 'unknown' | 'discarded', messageId?: string) => Promise<HaiResult<void>>\n /**\n * 获取所有待发送记录(按创建时间升序)\n * @param tx - 可选受控数据库事务。\n * @returns HaiResult;先检查success,条件提交失败不能伪装成功。\n *\n * @example 模块内部上下文\n * ```ts\n * async function pending(repository: SendLogRepository) {\n * const rows = await repository.findPending()\n * if (!rows.success) throw new Error(rows.error.message)\n * return rows.data\n * // 普通查询没有投递权,发送前必须claim。\n * }\n * ```\n */\n findPending: (tx?: DmlWithTxOperations) => Promise<HaiResult<StoredSendLog[]>>\n\n /**\n * 按创建时间顺序 claim 一批待发送记录。\n * 可接管已过期 processing 和接收端幂等的 sending;非幂等 sending 过期后隔离为 unknown。\n * PostgreSQL / MySQL 优先使用行锁 + `SKIP LOCKED` 降低多节点争抢;\n * SQLite 不支持该能力时退回条件更新(compare-and-set)模式。\n * @param owner - 当前唯一领取凭据,不记录或暴露给客户端。\n * @param options - 实际操作输入,按本方法约束校验。\n * @returns HaiResult;先检查success,条件提交失败不能伪装成功。\n *\n * @example 模块内部上下文\n * ```ts\n * async function claim(repository: SendLogRepository) {\n * const owner = 'worker:' + crypto.randomUUID()\n * const rows = await repository.claimPendingBatch(owner, { limit: 1, lockMs: 600000 })\n * if (!rows.success) throw new Error(rows.error.message)\n * return { owner, rows: rows.data }\n * }\n * ```\n */\n claimPendingBatch: (owner: string, options?: SendLogClaimOptions) => Promise<HaiResult<StoredSendLog[]>>\n\n /**\n * 取消一次领取并按失败策略退避/死信;不立即重排队,不绕过次数上限。\n * 仅有效 owner 可以释放 processing 或可安全重试的 sending;未知外部副作用不能重排队。\n * @param id - 已读取/领取的持久记录主键。\n * @param owner - 当前唯一领取凭据,不记录或暴露给客户端。\n * @param tx - 可选受控数据库事务。\n * @returns HaiResult;先检查success,条件提交失败不能伪装成功。\n *\n * @example 模块内部上下文\n * ```ts\n * async function release(repository: SendLogRepository, row: StoredSendLog, owner: string) {\n * const released = await repository.releaseClaim(row.id, owner)\n * if (!released.success) throw new Error(released.error.message)\n * // 只用于当前有效owner/安全状态;内部持久退避,不立即热循环重领。\n * }\n * ```\n */\n releaseClaim: (id: number, owner: string, tx?: DmlWithTxOperations) => Promise<HaiResult<void>>\n\n /**\n * 当前有效 owner 提交失败;永久失败/超限进入 dead,其余按持久策略退避。\n * @param row - 实际操作输入,按本方法约束校验。\n * @param owner - 当前唯一领取凭据,不记录或暴露给客户端。\n * @param reason - 实际操作输入,按本方法约束校验。\n * @param permanent - 实际操作输入,按本方法约束校验。\n * @returns HaiResult;先检查success,条件提交失败不能伪装成功。\n *\n * @example 模块内部上下文\n * ```ts\n * async function failed(repository: SendLogRepository, row: StoredSendLog, owner: string) {\n * const released = await repository.failClaim(row, owner, 'provider_unavailable')\n * if (!released.success) throw new Error(released.error.message)\n * }\n * ```\n */\n failClaim: (row: StoredSendLog, owner: string, reason: ReachQueueFailure, permanent?: boolean) => Promise<HaiResult<void>>\n /**\n * 根因修复后仅恢复指定版本的 dead,重置次数但保留幂等键和持久策略。\n * @param queueId - 持久UUID回执,不代替运维权限。\n * @param expectedUpdatedAt - 最近读取的更新时间版本,不能使用当前时间替代。\n * @returns HaiResult;先检查success,条件提交失败不能伪装成功。\n *\n * @example 模块内部上下文\n * ```ts\n * async function restore(repository: SendLogRepository, row: StoredSendLog) {\n * if (row.status !== 'dead' || !row.queueId || row.updatedAt === undefined) return\n * // 调用前已完成可信运维授权、根因修复与外部效果核对。\n * const restored = await repository.requeueDead(row.queueId, row.updatedAt)\n * if (!restored.success) throw new Error(restored.error.message)\n * }\n * ```\n */\n requeueDead: (queueId: string, expectedUpdatedAt: number) => Promise<HaiResult<void>>\n\n /**\n * 发送前原子校验有效 owner 并持久化 sending;失败时绝不能调用外部 Provider。\n * @param id - 已读取/领取的持久记录主键。\n * @param owner - 当前唯一领取凭据,不记录或暴露给客户端。\n * @param retrySafe - 真实接收端持久幂等能力声明。\n * @param lockMs - 实际操作输入,按本方法约束校验。\n * @returns HaiResult;先检查success,条件提交失败不能伪装成功。\n *\n * @example 模块内部上下文\n * ```ts\n * async function begin(repository: SendLogRepository, row: StoredSendLog, owner: string, receiverDeduplicates: boolean) {\n * const started = await repository.beginDelivery(row.id, owner, receiverDeduplicates)\n * if (!started.success) return started\n * // 仅此success分支可调用对应Provider;receiverDeduplicates须来自真实能力声明。\n * return started\n * }\n * ```\n */\n beginDelivery: (id: number, owner: string, retrySafe: boolean, lockMs?: number) => Promise<HaiResult<void>>\n /**\n * 仅续租尚未到期且仍由该 owner 持有的记录;过期后不得复活旧 owner。\n * @param id - 已读取/领取的持久记录主键。\n * @param owner - 当前唯一领取凭据,不记录或暴露给客户端。\n * @param lockMs - 实际操作输入,按本方法约束校验。\n * @returns HaiResult;先检查success,条件提交失败不能伪装成功。\n *\n * @example 模块内部上下文\n * ```ts\n * async function renew(repository: SendLogRepository, row: StoredSendLog, owner: string) {\n * const result = await repository.renewClaim(row.id, owner)\n * if (!result.success) return result\n * return result\n * // 续租失败不能恢复旧owner,已开始的外部效果按幂等/unknown边界处置。\n * }\n * ```\n */\n renewClaim: (id: number, owner: string, lockMs?: number) => Promise<HaiResult<void>>\n /**\n * 标记无法确定外部结果的记录,停止自动投递;仅当前 owner 可以提交。\n * @param id - 已读取/领取的持久记录主键。\n * @param owner - 当前唯一领取凭据,不记录或暴露给客户端。\n * @returns HaiResult;先检查success,条件提交失败不能伪装成功。\n *\n * @example 模块内部上下文\n * ```ts\n * async function isolate(repository: SendLogRepository, row: StoredSendLog, owner: string) {\n * const marked = await repository.markUnknown(row.id, owner)\n * if (!marked.success) throw new Error(marked.error.message)\n * }\n * ```\n */\n markUnknown: (id: number, owner: string) => Promise<HaiResult<void>>\n /**\n * 人工核对并隔离旧 worker 后裁定 unknown;条件提交阻止并发覆盖。\n * @param queueId - 持久UUID回执,不代替运维权限。\n * @param resolution - 实际操作输入,按本方法约束校验。\n * @returns HaiResult;先检查success,条件提交失败不能伪装成功。\n *\n * @example 模块内部上下文\n * ```ts\n * async function resolve(repository: SendLogRepository, queueId: string) {\n * // 可信运维已授权、隔离旧worker并核对外部记录,不进行自动重发。\n * const resolved = await repository.resolveUnknown(queueId, { status: 'discarded' })\n * if (!resolved.success) throw new Error(resolved.error.message)\n * }\n * ```\n */\n resolveUnknown: (queueId: string, resolution: ReachUnknownResolution) => Promise<HaiResult<void>>\n\n /**\n * 将记录标记为已发送。\n * 仅持有 sending 有效租约的 owner 可以提交;过期 owner 不能覆盖新节点状态。\n * @param id - 已读取/领取的持久记录主键。\n * @param owner - 当前唯一领取凭据,不记录或暴露给客户端。\n * @param messageId - 实际操作输入,按本方法约束校验。\n * @param tx - 可选受控数据库事务。\n * @returns HaiResult;先检查success,条件提交失败不能伪装成功。\n *\n * @example 模块内部上下文\n * ```ts\n * async function commit(repository: SendLogRepository, row: StoredSendLog, owner: string, messageId: string) {\n * const marked = await repository.markSent(row.id, owner, messageId)\n * if (!marked.success) throw new Error(marked.error.message)\n * // 提交失败不代表外部未发送,不得盲目重新调用Provider。\n * }\n * ```\n */\n markSent: (id: number, owner: string, messageId?: string, tx?: DmlWithTxOperations) => Promise<HaiResult<void>>\n}\n\n// ─── 发送日志存储实现 ───\n\n/** 表名 */\nconst TABLE_NAME = 'hai_reach_send_log'\nconst DEFAULT_CLAIM_BATCH_SIZE = 100\nconst DEFAULT_CLAIM_LOCK_MS = 10 * 60 * 1000\nconst CLAIMABLE_STATUS_WHERE = '(attempt_count < max_attempts AND ((status = ? AND next_attempt_at <= ?) OR (status = ? AND updated_at < ? AND (locked_until IS NULL OR locked_until <= ?)) OR (status = ? AND updated_at < ? AND retry_safe = TRUE AND locked_until <= ?)))'\n\n/**\n * 与固定领取SQL一一对应的参数,仅允许pending、过期processing或幂等sending重领。\n * @param now - 本轮节点Unix毫秒时间。\n * @param eligibleBefore - 固定资格截止,避免本轮新退避消息被立即重复领取。\n * @returns 参数数组,绝不拼接不可信SQL。\n *\n * @example 模块内部上下文\n * ```ts\n * const now = Date.now()\n * const params = claimableParams(now, now)\n * ```\n */\nfunction claimableParams(now: number, eligibleBefore = now): unknown[] {\n return ['pending', eligibleBefore, 'processing', eligibleBefore, now, 'sending', eligibleBefore, now]\n}\n\nfunction buildSendLogError<T>(detail: string, cause?: unknown): HaiResult<T> {\n return err(\n HaiReachError.SEND_FAILED,\n reachM('reach_sendFailed', { params: { error: detail } }),\n cause,\n )\n}\n\n/**\n * 读取边界统一校验BIGINT时间/次数/策略,支持驱动number/bigint/十进制字符串并拒绝精度丢失。\n * @param row - 驱动返回的日志行;不改写原对象。\n * @returns 安全整数number行或失败,不能静默截断精度。\n *\n * @example 模块内部上下文\n * ```ts\n * function normalize(row: StoredSendLog) {\n * const result = normalizeSendLog(row)\n * if (!result.success) throw new Error(result.error.message)\n * return result.data\n * }\n * ```\n */\nfunction normalizeSendLog(row: StoredSendLog): HaiResult<StoredSendLog> {\n const normalized = { ...row }\n for (const field of ['id', 'createdAt', 'updatedAt', 'lockedUntil', 'nextAttemptAt', 'attemptCount', 'maxAttempts', 'retryDelayMs', 'maxRetryDelayMs'] as const) {\n const value: unknown = row[field]\n if (value === undefined && (field === 'updatedAt' || field === 'lockedUntil'))\n continue\n const validType = typeof value === 'number' || typeof value === 'bigint'\n || (typeof value === 'string' && /^\\d+$/.test(value))\n const integer = validType ? Number(value) : Number.NaN\n if (!Number.isSafeInteger(integer) || integer < 0)\n return buildSendLogError(reachM('reach_invalidStoredInteger', { params: { field } }))\n Object.assign(normalized, { [field]: integer })\n }\n return ok(normalized)\n}\n\n/**\n * 批量、分页及claim回读共用同一规范化,任一行无效即失败,不返回混合时间类型。\n * @param rows - 数据库读取行。\n * @returns 规范化数组或原失败。\n *\n * @example 模块内部上下文\n * ```ts\n * function normalizeBatch(rows: StoredSendLog[]) {\n * const result = normalizeSendLogs(rows)\n * if (!result.success) throw new Error(result.error.message)\n * return result.data\n * }\n * ```\n */\nfunction normalizeSendLogs(rows: StoredSendLog[]): HaiResult<StoredSendLog[]> {\n const normalized: StoredSendLog[] = []\n for (const row of rows) {\n const result = normalizeSendLog(row)\n if (!result.success)\n return result\n normalized.push(result.data)\n }\n return ok(normalized)\n}\n\n/** 字段定义 */\nconst SEND_LOG_FIELDS: ReldbCrudFieldDefinition[] = [\n ...[\n { fieldName: 'attemptCount', columnName: 'attempt_count', value: 0 },\n { fieldName: 'maxAttempts', columnName: 'max_attempts', value: 5 },\n { fieldName: 'retryDelayMs', columnName: 'retry_delay_ms', value: 60_000 },\n { fieldName: 'maxRetryDelayMs', columnName: 'max_retry_delay_ms', value: 3_600_000 },\n { fieldName: 'nextAttemptAt', columnName: 'next_attempt_at', value: 0 },\n ].map(({ fieldName, columnName, value }) => ({\n fieldName,\n columnName,\n def: { type: 'BIGINT' as const, notNull: true, defaultValue: value },\n select: true,\n create: true,\n update: true,\n })),\n {\n fieldName: 'lastFailure',\n columnName: 'last_failure',\n def: { type: 'TEXT' as const },\n select: true,\n create: true,\n update: true,\n },\n {\n fieldName: 'retrySafe',\n columnName: 'retry_safe',\n def: { type: 'BOOLEAN' as const, notNull: true, defaultValue: false },\n select: true,\n create: true,\n update: true,\n },\n {\n fieldName: 'queueId',\n columnName: 'queue_id',\n def: { type: 'TEXT' as const, unique: true },\n select: true,\n create: true,\n update: false,\n },\n {\n fieldName: 'id',\n columnName: 'id',\n def: { type: 'INTEGER' as const, primaryKey: true, autoIncrement: true },\n select: true,\n create: false,\n update: false,\n },\n {\n fieldName: 'provider',\n columnName: 'provider',\n def: { type: 'TEXT' as const, notNull: true },\n select: true,\n create: true,\n update: false,\n },\n {\n fieldName: 'toAddr',\n columnName: 'to_addr',\n def: { type: 'TEXT' as const, notNull: true },\n select: true,\n create: true,\n update: false,\n },\n {\n fieldName: 'subject',\n columnName: 'subject',\n def: { type: 'TEXT' as const },\n select: true,\n create: true,\n update: false,\n },\n {\n fieldName: 'body',\n columnName: 'body',\n def: { type: 'TEXT' as const },\n select: true,\n create: true,\n update: false,\n },\n {\n fieldName: 'template',\n columnName: 'template',\n def: { type: 'TEXT' as const },\n select: true,\n create: true,\n update: false,\n },\n {\n fieldName: 'varsJson',\n columnName: 'vars_json',\n def: { type: 'TEXT' as const },\n select: true,\n create: true,\n update: false,\n },\n {\n fieldName: 'extraJson',\n columnName: 'extra_json',\n def: { type: 'TEXT' as const },\n select: true,\n create: true,\n update: false,\n },\n {\n fieldName: 'status',\n columnName: 'status',\n def: { type: 'TEXT' as const, notNull: true },\n select: true,\n create: true,\n update: true,\n },\n {\n fieldName: 'messageId',\n columnName: 'message_id',\n def: { type: 'TEXT' as const },\n select: true,\n create: true,\n update: true,\n },\n {\n fieldName: 'processingOwner',\n columnName: 'processing_owner',\n def: { type: 'TEXT' as const },\n select: true,\n create: true,\n update: true,\n },\n {\n fieldName: 'lockedUntil',\n columnName: 'locked_until',\n def: { type: 'BIGINT' as const },\n select: true,\n create: true,\n update: true,\n },\n {\n fieldName: 'createdAt',\n columnName: 'created_at',\n def: { type: 'BIGINT' as const, notNull: true },\n select: true,\n create: true,\n update: false,\n },\n {\n fieldName: 'updatedAt',\n columnName: 'updated_at',\n def: { type: 'BIGINT' as const, notNull: true },\n select: true,\n create: true,\n update: true,\n },\n]\n\n/** 发送日志存储单例缓存 */\nlet sendLogRepoInstance: SendLogRepository | null = null\nlet sendLogRepoSqlOps: unknown = null\n\n/**\n * 内部关闭时释放仓库包装对象引用;不删除数据库记录,不是跨节点业务状态重置。\n *\n * @example 模块内部上下文\n * ```ts\n * resetSendLogRepoSingleton()\n * ```\n */\nexport function resetSendLogRepoSingleton(): void {\n sendLogRepoInstance = null\n sendLogRepoSqlOps = null\n}\n\n/**\n * 内部创建/复用当前db.sql生命周期的队列仓库,确认表就绪后才返回;缓存只持有包装对象。\n * @param db - 已初始化数据库;生产共享PostgreSQL/MySQL。\n * @returns 仓库HaiResult,建表/连接故障失败,不回退进程队列。\n *\n * @example 模块内部上下文\n * ```ts\n * const { reldb } = await import('@h-ai/reldb')\n * const connected = await reldb.init({ type: 'postgresql', host: 'db', port: 5432, database: 'hai', user: 'postgres', password: process.env.HAI_DB_PASSWORD })\n * if (!connected.success) throw new Error(connected.error.message)\n * try {\n * const created = await createSendLogRepository(reldb)\n * if (!created.success) throw new Error(created.error.message)\n * const repository = created.data\n * const pending = await repository.findPending()\n * if (!pending.success) throw new Error(pending.error.message)\n * const count = pending.data.length\n * }\n * finally {\n * const closed = await reldb.close()\n * if (!closed.success) throw new Error(closed.error.message)\n * }\n * ```\n */\nexport async function createSendLogRepository(db: ReldbFunctions): Promise<HaiResult<SendLogRepository>> {\n if (sendLogRepoInstance && sendLogRepoSqlOps === db.sql)\n return ok(sendLogRepoInstance)\n\n const migration = await migrateReach({}, db)\n if (!migration.success)\n return migration\n const repo = new DbSendLogRepository(db)\n // 触发表创建(BaseReldbCrudRepository 的表创建是异步的)\n const initResult = await repo.count()\n if (!initResult.success) {\n return err(\n HaiReachError.SEND_FAILED,\n reachM('reach_sendFailed', { params: { error: initResult.error.message } }),\n initResult.error,\n )\n }\n sendLogRepoInstance = repo\n sendLogRepoSqlOps = db.sql\n return ok(repo)\n}\n\n/**\n * 基于数据库的发送日志存储实现\n */\nclass DbSendLogRepository extends BaseReldbCrudRepository<StoredSendLog> implements SendLogRepository {\n async settleSensitive(queueId: string, status: 'sent' | 'unknown' | 'discarded', messageId?: string): Promise<HaiResult<void>> {\n const settled = await this.sql().execute(\n `UPDATE ${TABLE_NAME} SET status = ?, message_id = ?, locked_until = NULL, updated_at = ? WHERE queue_id = ? AND status = 'sending' AND body IS NULL AND vars_json IS NULL AND retry_safe = FALSE`,\n [status, messageId ?? null, Date.now(), queueId],\n )\n if (!settled.success)\n return settled\n return settled.data.changes === 1 ? ok(undefined) : buildSendLogError(reachM('reach_sensitiveDeliveryUnconfirmed'))\n }\n\n constructor(db: ReldbFunctions) {\n super(db, {\n table: TABLE_NAME,\n fields: SEND_LOG_FIELDS,\n idColumn: 'id',\n idField: 'id',\n createTableIfNotExists: true,\n })\n }\n\n /** 单条可空查询统一返回经过校验的毫秒 number。 */\n override async findById(id: unknown, tx?: DmlWithTxOperations): Promise<HaiResult<StoredSendLog | null>> {\n const result = await super.findById(id, tx)\n if (!result.success || result.data === null)\n return result\n return normalizeSendLog(result.data)\n }\n\n /** 单条必需查询统一返回经过校验的毫秒 number。 */\n override async getById(id: unknown, tx?: DmlWithTxOperations): Promise<HaiResult<StoredSendLog>> {\n const result = await super.getById(id, tx)\n return result.success ? normalizeSendLog(result.data) : result\n }\n\n /** 普通列表与 claim 回读共用同一 BIGINT 转换边界。 */\n override async findAll(options?: CrudQueryOptions, tx?: DmlWithTxOperations): Promise<HaiResult<StoredSendLog[]>> {\n const result = await super.findAll(options, tx)\n return result.success ? normalizeSendLogs(result.data) : result\n }\n\n /** 分页结果同样拒绝不安全的时间值,不返回经过舍入的毫秒数。 */\n override async findPage(options: CrudPageOptions, tx?: DmlWithTxOperations): Promise<HaiResult<PaginatedResult<StoredSendLog>>> {\n const result = await super.findPage(options, tx)\n if (!result.success)\n return result\n const rows = normalizeSendLogs(result.data.items)\n return rows.success ? ok({ ...result.data, items: rows.data }) : rows\n }\n\n /** 获取所有待发送记录 */\n async findPending(tx?: DmlWithTxOperations): Promise<HaiResult<StoredSendLog[]>> {\n const result = await this.findAll({ where: 'status = ?', params: ['pending'], orderBy: 'created_at ASC' }, tx)\n if (!result.success) {\n return result\n }\n return ok(result.data)\n }\n\n /**\n * 使用数据库行锁 claim 一批记录。\n *\n * PostgreSQL / MySQL 支持 `FOR UPDATE SKIP LOCKED`:\n * - 当前事务锁住即将 claim 的行\n * - 并发事务会直接跳过这些行,而不是重复命中同一批记录\n * - 在 commit 前不会暴露给其它节点重复 claim\n */\n private async claimPendingIdsWithRowLocks(\n tx: DmlWithTxOperations,\n owner: string,\n now: number,\n lockedUntil: number,\n limit: number,\n eligibleBefore: number,\n ): Promise<number[]> {\n const lockedRowsResult = await tx.query(\n `SELECT id\n FROM ${TABLE_NAME}\n WHERE ${CLAIMABLE_STATUS_WHERE}\n ORDER BY created_at ASC\n LIMIT ?\n FOR UPDATE SKIP LOCKED`,\n [...claimableParams(now, eligibleBefore), limit],\n )\n if (!lockedRowsResult.success) {\n throw lockedRowsResult.error\n }\n\n const claimedIds = lockedRowsResult.data.map((row) => {\n const id = typeof row.id === 'number' ? row.id : Number(row.id)\n if (!Number.isSafeInteger(id)) {\n throw new TypeError(`Invalid send log id returned when claiming pending batch: ${String(row.id)}`)\n }\n return id\n })\n if (claimedIds.length === 0) {\n return []\n }\n\n const placeholders = claimedIds.map(() => '?').join(', ')\n const updateResult = await tx.execute(\n `UPDATE ${TABLE_NAME}\n SET status = ?, processing_owner = ?, locked_until = ?, updated_at = ?, attempt_count = attempt_count + 1\n WHERE id IN (${placeholders})`,\n ['processing', owner, lockedUntil, now, ...claimedIds],\n )\n if (!updateResult.success) {\n throw updateResult.error\n }\n\n return claimedIds\n }\n\n /**\n * SQLite 等不支持 `SKIP LOCKED` 的数据库回退到 compare-and-set claim。\n *\n * 流程是:先按顺序读候选,再用带条件的 UPDATE 抢占每一行。\n * 即使多个节点读到了同一候选,只有第一个满足条件的 UPDATE 会成功,\n * 从而避免同一行被两个节点同时 claim。\n */\n private async claimPendingIdsWithConditionalUpdates(\n tx: DmlWithTxOperations,\n owner: string,\n now: number,\n lockedUntil: number,\n limit: number,\n eligibleBefore: number,\n ): Promise<number[]> {\n const candidatesResult = await this.findAll({\n where: CLAIMABLE_STATUS_WHERE,\n params: claimableParams(now, eligibleBefore),\n orderBy: 'created_at ASC',\n limit,\n }, tx)\n if (!candidatesResult.success) {\n throw candidatesResult.error\n }\n\n const claimedIds: number[] = []\n for (const row of candidatesResult.data) {\n const updateResult = await tx.execute(\n `UPDATE ${TABLE_NAME}\n SET status = ?, processing_owner = ?, locked_until = ?, updated_at = ?, attempt_count = attempt_count + 1\n WHERE id = ? AND ${CLAIMABLE_STATUS_WHERE}`,\n ['processing', owner, lockedUntil, now, row.id, ...claimableParams(now, eligibleBefore)],\n )\n if (!updateResult.success) {\n throw updateResult.error\n }\n if (updateResult.data.changes > 0) {\n claimedIds.push(row.id)\n }\n }\n\n return claimedIds\n }\n\n /** 根据已 claim 的 id 回读完整记录,统一返回给 flush 流程。 */\n private async loadClaimedRows(ids: number[], tx: DmlWithTxOperations): Promise<StoredSendLog[]> {\n const placeholders = ids.map(() => '?').join(', ')\n const claimedRowsResult = await this.findAll({\n where: `id IN (${placeholders})`,\n params: ids,\n orderBy: 'created_at ASC',\n }, tx)\n if (!claimedRowsResult.success) {\n throw claimedRowsResult.error\n }\n return claimedRowsResult.data\n }\n\n /** claim 一批待发送记录,避免多节点重复发送 */\n async claimPendingBatch(owner: string, options?: SendLogClaimOptions): Promise<HaiResult<StoredSendLog[]>> {\n const now = options?.now ?? Date.now()\n const limit = options?.limit ?? DEFAULT_CLAIM_BATCH_SIZE\n const lockMs = options?.lockMs ?? DEFAULT_CLAIM_LOCK_MS\n const lockedUntil = now + lockMs\n const eligibleBefore = Math.min(now, options?.eligibleBefore ?? now)\n const dbType = this.db.config?.type\n\n const txResult = await this.db.tx.begin()\n if (!txResult.success) {\n return buildSendLogError(txResult.error.message, txResult.error)\n }\n\n const tx = txResult.data\n\n try {\n // 可能已产生不可去重的外部副作用:租约过期只能转 unknown,不能被新节点自动重发。\n const unknown = await tx.execute(\n `UPDATE ${TABLE_NAME} SET status = ?, processing_owner = NULL, locked_until = NULL, next_attempt_at = 0, updated_at = ?\n WHERE status = ? AND retry_safe = FALSE AND (locked_until IS NULL OR locked_until <= ?)`,\n ['unknown', now, 'sending', now],\n )\n if (!unknown.success)\n throw unknown.error\n // 最后一次领取后崩溃也不能无限接管;有效租约仍须等待到期,非幂等 sending 已先隔离。\n const exhausted = await tx.execute(\n `UPDATE ${TABLE_NAME}\n SET status = ?, last_failure = ?, processing_owner = NULL, locked_until = NULL, next_attempt_at = 0, updated_at = ?\n WHERE attempt_count >= max_attempts\n AND (status = ? OR (status = ? AND (locked_until IS NULL OR locked_until <= ?))\n OR (status = ? AND retry_safe = TRUE AND locked_until <= ?))`,\n ['dead', 'attempts_exhausted', now, 'pending', 'processing', now, 'sending', now],\n )\n if (!exhausted.success)\n throw exhausted.error\n const claimedIds = dbType === 'postgresql' || dbType === 'mysql'\n ? await this.claimPendingIdsWithRowLocks(tx, owner, now, lockedUntil, limit, eligibleBefore)\n : await this.claimPendingIdsWithConditionalUpdates(tx, owner, now, lockedUntil, limit, eligibleBefore)\n\n if (claimedIds.length === 0) {\n const commitResult = await tx.commit()\n if (!commitResult.success) {\n return buildSendLogError(commitResult.error.message, commitResult.error)\n }\n return ok([])\n }\n\n const claimedRows = await this.loadClaimedRows(claimedIds, tx)\n\n const commitResult = await tx.commit()\n if (!commitResult.success) {\n return buildSendLogError(commitResult.error.message, commitResult.error)\n }\n\n return ok(claimedRows)\n }\n catch (error) {\n await tx.rollback()\n return buildSendLogError(error instanceof Error ? error.message : String(error), error)\n }\n }\n\n /** 显式取消未完成的领取;仍按失败退避,避免调用方绕过自动重试预算。 */\n async releaseClaim(id: number, owner: string, tx?: DmlWithTxOperations): Promise<HaiResult<void>> {\n const row = await this.getById(id, tx)\n if (!row.success)\n return row\n return this.failClaim(row.data, owner, 'transient_failure', false, tx)\n }\n\n /** 失败提交与 owner、次数和有效租约同时比较,旧 worker 不能更改新领取的退避策略。 */\n async failClaim(row: StoredSendLog, owner: string, reason: ReachQueueFailure, permanent = false, tx?: DmlWithTxOperations): Promise<HaiResult<void>> {\n const now = Date.now()\n const dead = permanent || row.attemptCount >= row.maxAttempts\n const delay = Math.min(row.maxRetryDelayMs, row.retryDelayMs * 2 ** Math.min(row.attemptCount - 1, 30))\n const result = await this.sql(tx).execute(\n `UPDATE ${TABLE_NAME}\n SET status = ?, last_failure = ?, next_attempt_at = ?, processing_owner = NULL, locked_until = NULL, updated_at = ?\n WHERE id = ? AND processing_owner = ? AND locked_until > ? AND attempt_count = ?\n AND (status = ? OR (status = ? AND retry_safe = TRUE))`,\n [dead ? 'dead' : 'pending', reason, dead ? 0 : now + delay, now, row.id, owner, now, row.attemptCount, 'processing', 'sending'],\n )\n if (!result.success) {\n return buildSendLogError(result.error.message, result.error)\n }\n if (result.data.changes === 0) {\n return buildSendLogError(reachM('reach_claimLost'))\n }\n return ok(undefined)\n }\n\n /** 仅当前 dead 版本可以恢复;保留 queueId,迟到的幂等 API 请求仍使用同一键。 */\n async requeueDead(queueId: string, expectedUpdatedAt: number): Promise<HaiResult<void>> {\n const now = Math.max(Date.now(), expectedUpdatedAt + 1)\n const result = await this.sql().execute(\n `UPDATE ${TABLE_NAME} SET status = ?, attempt_count = 0, next_attempt_at = ?, last_failure = NULL, updated_at = ?\n WHERE queue_id = ? AND status = ? AND updated_at = ?`,\n ['pending', now, now, queueId, 'dead', expectedUpdatedAt],\n )\n return result.success && result.data.changes === 1 ? ok(undefined) : buildSendLogError(reachM('reach_deadRequeueFailed'))\n }\n\n /** 发送前持久化门闩;仅有效租约可从 processing 进入 sending。 */\n async beginDelivery(id: number, owner: string, retrySafe: boolean, lockMs = DEFAULT_CLAIM_LOCK_MS): Promise<HaiResult<void>> {\n const now = Date.now()\n const flag = this.db.config?.type === 'postgresql' ? retrySafe : Number(retrySafe)\n const result = await this.sql().execute(\n `UPDATE ${TABLE_NAME} SET status = ?, retry_safe = ?, locked_until = ?, updated_at = ?\n WHERE id = ? AND status = ? AND processing_owner = ? AND locked_until > ?`,\n ['sending', flag, now + lockMs, now, id, 'processing', owner, now],\n )\n return result.success && result.data.changes === 1 ? ok(undefined) : buildSendLogError(reachM('reach_claimLost'))\n }\n\n /** 续租不接受已经过期的 owner,防止暂停的旧进程恢复后重新取得发送权。 */\n async renewClaim(id: number, owner: string, lockMs = DEFAULT_CLAIM_LOCK_MS): Promise<HaiResult<void>> {\n const now = Date.now()\n const result = await this.sql().execute(\n `UPDATE ${TABLE_NAME} SET locked_until = ?, updated_at = ?\n WHERE id = ? AND processing_owner = ? AND status IN (?, ?) AND locked_until > ?`,\n [now + lockMs, now, id, owner, 'processing', 'sending', now],\n )\n return result.success && result.data.changes === 1 ? ok(undefined) : buildSendLogError(reachM('reach_claimLost'))\n }\n\n /** 未知结果只能停止自动处理,不推断外部副作用已撤销。 */\n async markUnknown(id: number, owner: string): Promise<HaiResult<void>> {\n const result = await this.sql().execute(\n `UPDATE ${TABLE_NAME} SET status = ?, processing_owner = NULL, locked_until = NULL, next_attempt_at = 0, updated_at = ?\n WHERE id = ? AND processing_owner = ? AND status IN (?, ?)`,\n ['unknown', Date.now(), id, owner, 'processing', 'sending'],\n )\n return result.success && result.data.changes === 1 ? ok(undefined) : buildSendLogError(reachM('reach_claimLost'))\n }\n\n /** 人工裁定的条件提交,不提供重新排队或重新发送操作。 */\n async resolveUnknown(queueId: string, resolution: ReachUnknownResolution): Promise<HaiResult<void>> {\n const result = await this.sql().execute(\n `UPDATE ${TABLE_NAME} SET status = ?, message_id = ?, next_attempt_at = 0, updated_at = ? WHERE queue_id = ? AND status = ?`,\n [resolution.status, resolution.messageId ?? null, Date.now(), queueId, 'unknown'],\n )\n return result.success && result.data.changes === 1 ? ok(undefined) : buildSendLogError(reachM('reach_unknownResolutionFailed'))\n }\n\n /** 只有仍持有 sending 有效租约的 owner 可以确认投递。 */\n async markSent(id: number, owner: string, messageId?: string, tx?: DmlWithTxOperations): Promise<HaiResult<void>> {\n const now = Date.now()\n const result = await this.sql(tx).execute(\n `UPDATE ${TABLE_NAME}\n SET status = ?, message_id = ?, processing_owner = NULL, locked_until = NULL, next_attempt_at = 0, updated_at = ?\n WHERE id = ? AND status = ? AND processing_owner = ? AND locked_until > ?`,\n ['sent', messageId ?? null, now, id, 'sending', owner, now],\n )\n if (!result.success) {\n return buildSendLogError(result.error.message, result.error)\n }\n if (result.data.changes === 0) {\n return buildSendLogError(reachM('reach_claimLost'))\n }\n return ok(undefined)\n }\n}\n","/**\n * @h-ai/reach — 模板存储实现\n *\n * 基于 @h-ai/reldb 的模板持久化存储。\n * 模板支持两种来源:配置文件(config)和数据库(db)。\n * @module reach-repository-template\n */\n\nimport type { HaiResult } from '@h-ai/core'\nimport type { DmlWithTxOperations, ReldbCrudFieldDefinition, ReldbCrudRepository, ReldbFunctions } from '@h-ai/reldb'\n\nimport type { ReachTemplate } from '../reach-types.js'\nimport { err, ok } from '@h-ai/core'\nimport { BaseReldbCrudRepository } from '@h-ai/reldb'\n\nimport { reachM } from '../reach-i18n.js'\nimport { HaiReachError } from '../reach-types.js'\n\n// ─── 模板存储实体类型 ───\n\n/**\n * 内部数据库模板行;createdAt/updatedAt由TIMESTAMP字段映射为Date,与发送队列BIGINT毫秒不同。\n *\n * @example 模块内部上下文\n * ```ts\n * const { reldb } = await import('@h-ai/reldb')\n * const connected = await reldb.init({ type: 'postgresql', host: 'db', port: 5432, database: 'hai', user: 'postgres', password: process.env.HAI_DB_PASSWORD })\n * if (!connected.success) throw new Error(connected.error.message)\n * try {\n * const created = await createTemplateRepository(reldb)\n * if (!created.success) throw new Error(created.error.message)\n * const repository = created.data\n * const found = await repository.findByName('welcome')\n * if (!found.success) throw new Error(found.error.message)\n * const row: StoredTemplate | undefined = found.data\n * if (row) { const updatedAt = row.updatedAt }\n * }\n * finally {\n * const closed = await reldb.close()\n * if (!closed.success) throw new Error(closed.error.message)\n * }\n * ```\n */\nexport interface StoredTemplate {\n /** 自增主键 */\n id: number\n /** 模板名称(唯一标识) */\n name: string\n /** 绑定的 Provider 名称 */\n provider: string\n /** 邮件主题模板 */\n subject: string | null\n /** 正文模板 */\n body: string\n /** 创建时间(TIMESTAMP 经仓库映射为 Date)。 */\n createdAt: Date\n /** 更新时间(TIMESTAMP 经仓库映射为 Date)。 */\n updatedAt: Date\n}\n\n// ─── 模板存储接口 ───\n\n/**\n * 内部模板数据库契约,唯一name支持upsert;表配置/数据库持有内容,进程缓存只持有仓库对象。\n *\n * @example 模块内部上下文\n * ```ts\n * const { reldb } = await import('@h-ai/reldb')\n * const connected = await reldb.init({ type: 'postgresql', host: 'db', port: 5432, database: 'hai', user: 'postgres', password: process.env.HAI_DB_PASSWORD })\n * if (!connected.success) throw new Error(connected.error.message)\n * try {\n * const created = await createTemplateRepository(reldb)\n * if (!created.success) throw new Error(created.error.message)\n * const repository = created.data\n * const saved = await repository.upsert({ name: 'welcome', provider: 'webhook', subject: 'Welcome {userName}', body: 'Hello {userName}' })\n * if (!saved.success) throw new Error(saved.error.message)\n * const found = await repository.findByName('welcome')\n * if (!found.success) throw new Error(found.error.message)\n * }\n * finally {\n * const closed = await reldb.close()\n * if (!closed.success) throw new Error(closed.error.message)\n * }\n * ```\n */\nexport interface TemplateRepository extends ReldbCrudRepository<StoredTemplate> {\n /**\n * 按名称查找模板\n * @param name - 实际操作输入,按本方法约束校验。\n * @param tx - 可选受控数据库事务。\n * @returns HaiResult;先检查success,条件提交失败不能伪装成功。\n *\n * @example 模块内部上下文\n * ```ts\n * async function find(repository: TemplateRepository, name: string) {\n * const found = await repository.findByName(name)\n * if (!found.success) throw new Error(found.error.message)\n * return found.data\n * }\n * ```\n */\n findByName: (name: string, tx?: DmlWithTxOperations) => Promise<HaiResult<StoredTemplate | undefined>>\n\n /**\n * 保存模板(存在则更新,不存在则插入)\n * @param template - 实际操作输入,按本方法约束校验。\n * @param tx - 可选受控数据库事务。\n * @returns HaiResult;先检查success,条件提交失败不能伪装成功。\n *\n * @example 模块内部上下文\n * ```ts\n * async function save(repository: TemplateRepository, template: ReachTemplate) {\n * const saved = await repository.upsert(template)\n * if (!saved.success) throw new Error(saved.error.message)\n * }\n * ```\n */\n upsert: (template: ReachTemplate, tx?: DmlWithTxOperations) => Promise<HaiResult<void>>\n\n /**\n * 按名称删除模板\n * @param name - 实际操作输入,按本方法约束校验。\n * @param tx - 可选受控数据库事务。\n * @returns HaiResult;先检查success,条件提交失败不能伪装成功。\n *\n * @example 模块内部上下文\n * ```ts\n * async function remove(repository: TemplateRepository, name: string) {\n * const removed = await repository.deleteByName(name)\n * if (!removed.success) throw new Error(removed.error.message)\n * }\n * ```\n */\n deleteByName: (name: string, tx?: DmlWithTxOperations) => Promise<HaiResult<void>>\n\n /**\n * 获取所有模板(转换为 ReachTemplate 格式)\n * @param tx - 可选受控数据库事务。\n * @returns HaiResult;先检查success,条件提交失败不能伪装成功。\n *\n * @example 模块内部上下文\n * ```ts\n * async function list(repository: TemplateRepository) {\n * const listed = await repository.listTemplates()\n * if (!listed.success) throw new Error(listed.error.message)\n * return listed.data\n * }\n * ```\n */\n listTemplates: (tx?: DmlWithTxOperations) => Promise<HaiResult<ReachTemplate[]>>\n}\n\n// ─── 模板存储实现 ───\n\n/** 表名 */\nconst TABLE_NAME = 'hai_reach_template'\n\n/** 字段定义 */\nconst TEMPLATE_FIELDS: ReldbCrudFieldDefinition[] = [\n {\n fieldName: 'id',\n columnName: 'id',\n def: { type: 'INTEGER' as const, primaryKey: true, autoIncrement: true },\n select: true,\n create: false,\n update: false,\n },\n {\n fieldName: 'name',\n columnName: 'name',\n def: { type: 'TEXT' as const, notNull: true, unique: true },\n select: true,\n create: true,\n update: true,\n },\n {\n fieldName: 'provider',\n columnName: 'provider',\n def: { type: 'TEXT' as const, notNull: true },\n select: true,\n create: true,\n update: true,\n },\n {\n fieldName: 'subject',\n columnName: 'subject',\n def: { type: 'TEXT' as const },\n select: true,\n create: true,\n update: true,\n },\n {\n fieldName: 'body',\n columnName: 'body',\n def: { type: 'TEXT' as const, notNull: true },\n select: true,\n create: true,\n update: true,\n },\n {\n fieldName: 'createdAt',\n columnName: 'created_at',\n def: { type: 'TIMESTAMP' as const, notNull: true },\n select: true,\n create: true,\n update: false,\n },\n {\n fieldName: 'updatedAt',\n columnName: 'updated_at',\n def: { type: 'TIMESTAMP' as const, notNull: true },\n select: true,\n create: true,\n update: true,\n },\n]\n\n/** 模板存储单例缓存 */\nlet templateRepoInstance: TemplateRepository | null = null\nlet templateRepoSqlOps: unknown = null\n\n/**\n * 内部关闭时释放旧db.sql对应的仓库引用,不删除持久模板或重置其它节点。\n *\n * @example 模块内部上下文\n * ```ts\n * resetTemplateRepoSingleton()\n * ```\n */\nexport function resetTemplateRepoSingleton(): void {\n templateRepoInstance = null\n templateRepoSqlOps = null\n}\n\n/**\n * 内部创建/复用当前数据库生命周期的模板仓库,并实际确认表可访问;连接故障返回失败。\n * @param db - 已初始化共享数据库。\n * @returns 模板仓库HaiResult;不使用进程模板内容缓存。\n *\n * @example 模块内部上下文\n * ```ts\n * const { reldb } = await import('@h-ai/reldb')\n * const connected = await reldb.init({ type: 'postgresql', host: 'db', port: 5432, database: 'hai', user: 'postgres', password: process.env.HAI_DB_PASSWORD })\n * if (!connected.success) throw new Error(connected.error.message)\n * try {\n * const created = await createTemplateRepository(reldb)\n * if (!created.success) throw new Error(created.error.message)\n * const repository = created.data\n * const listed = await repository.listTemplates()\n * if (!listed.success) throw new Error(listed.error.message)\n * const count = listed.data.length\n * }\n * finally {\n * const closed = await reldb.close()\n * if (!closed.success) throw new Error(closed.error.message)\n * }\n * ```\n */\nexport async function createTemplateRepository(db: ReldbFunctions): Promise<HaiResult<TemplateRepository>> {\n if (templateRepoInstance && templateRepoSqlOps === db.sql)\n return ok(templateRepoInstance)\n\n const repo = new DbTemplateRepository(db)\n // 触发表创建(BaseReldbCrudRepository 的表创建是异步的)\n const initResult = await repo.count()\n if (!initResult.success) {\n return err(\n HaiReachError.SEND_FAILED,\n reachM('reach_templateDbInitFailed', { params: { error: initResult.error.message } }),\n initResult.error,\n )\n }\n templateRepoInstance = repo\n templateRepoSqlOps = db.sql\n return ok(repo)\n}\n\n/**\n * 将 StoredTemplate 转换为 ReachTemplate\n */\nfunction toReachTemplate(stored: StoredTemplate): ReachTemplate {\n return {\n name: stored.name,\n provider: stored.provider,\n subject: stored.subject ?? undefined,\n body: stored.body,\n }\n}\n\n/**\n * 基于数据库的模板存储实现\n */\nclass DbTemplateRepository extends BaseReldbCrudRepository<StoredTemplate> implements TemplateRepository {\n constructor(db: ReldbFunctions) {\n super(db, {\n table: TABLE_NAME,\n fields: TEMPLATE_FIELDS,\n idColumn: 'id',\n idField: 'id',\n createTableIfNotExists: true,\n })\n }\n\n /** 按名称查找模板 */\n async findByName(name: string, tx?: DmlWithTxOperations): Promise<HaiResult<StoredTemplate | undefined>> {\n const result = await this.findAll({ where: 'name = ?', params: [name], limit: 1 }, tx)\n if (!result.success) {\n return result\n }\n return ok(result.data[0])\n }\n\n /** 保存模板(存在则更新,不存在则插入) */\n async upsert(template: ReachTemplate, tx?: DmlWithTxOperations): Promise<HaiResult<void>> {\n const existing = await this.findByName(template.name, tx)\n if (!existing.success) {\n return existing\n }\n\n const now = Date.now()\n\n if (existing.data) {\n // 更新\n const updateResult = await this.updateById(\n existing.data.id,\n {\n provider: template.provider,\n subject: template.subject ?? null,\n body: template.body,\n updatedAt: now,\n },\n tx,\n )\n if (!updateResult.success) {\n return updateResult\n }\n }\n else {\n // 插入\n const createResult = await this.create(\n {\n name: template.name,\n provider: template.provider,\n subject: template.subject ?? null,\n body: template.body,\n createdAt: now,\n updatedAt: now,\n },\n tx,\n )\n if (!createResult.success) {\n return createResult\n }\n }\n return ok(undefined)\n }\n\n /** 按名称删除模板 */\n async deleteByName(name: string, tx?: DmlWithTxOperations): Promise<HaiResult<void>> {\n const existing = await this.findByName(name, tx)\n if (!existing.success) {\n return existing\n }\n if (!existing.data) {\n return ok(undefined)\n }\n const result = await this.deleteById(existing.data.id, tx)\n if (!result.success) {\n return result\n }\n return ok(undefined)\n }\n\n /** 获取所有模板(转换为 ReachTemplate 格式) */\n async listTemplates(tx?: DmlWithTxOperations): Promise<HaiResult<ReachTemplate[]>> {\n const result = await this.findAll({ orderBy: 'name ASC' }, tx)\n if (!result.success) {\n return result\n }\n return ok(result.data.map(toReachTemplate))\n }\n}\n","/**\n * @h-ai/reach — 触达服务主入口\n *\n * 本文件提供统一的 `reach` 对象,聚合模块生命周期管理。\n * @module reach-main\n */\n\nimport type { HaiResult } from '@h-ai/core'\nimport type { ReldbFunctions } from '@h-ai/reldb'\nimport type { ReachProvider } from './providers/reach-provider-types.js'\nimport type { DndConfig, ProviderConfig, ReachConfig, ReachConfigInput } from './reach-config.js'\nimport type { ReachFunctions, ReachMessage, ReachQueueEntry, ReachTemplateRegistry, SendResult } from './reach-types.js'\nimport type { SendLogRepository, StoredSendLog } from './repositories/reach-repository-send-log.js'\nimport type { TemplateRepository } from './repositories/reach-repository-template.js'\nimport process from 'node:process'\n\nimport { core, err, ok } from '@h-ai/core'\nimport { reldb } from '@h-ai/reldb'\n\nimport { createAliyunSmsProvider } from './providers/reach-provider-aliyun-sms.js'\nimport { createApiProvider } from './providers/reach-provider-api.js'\nimport { createConsoleProvider } from './providers/reach-provider-console.js'\nimport { createSmtpProvider } from './providers/reach-provider-smtp.js'\nimport { ReachConfigSchema, ReachDeadRequeueSchema, ReachQueueIdSchema, ReachQueueListSchema, ReachUnknownResolutionSchema } from './reach-config.js'\nimport { reachM } from './reach-i18n.js'\nimport { executeSend, resetSendState, startDndScheduler } from './reach-send.js'\nimport { createTemplateRegistry } from './reach-template.js'\nimport {\n HaiReachError,\n\n} from './reach-types.js'\nimport { createSendLogRepository, resetSendLogRepoSingleton } from './repositories/reach-repository-send-log.js'\nimport { createTemplateRepository, resetTemplateRepoSingleton } from './repositories/reach-repository-template.js'\n\nconst logger = core.logger.child({ module: 'reach', scope: 'main' })\n\n// ─── 内部状态 ───\n\n/** 已注册的 Provider 实例(name → Provider) */\nlet providers: Map<string, ReachProvider> = new Map()\n\n/** 当前配置(未初始化时为 null) */\nlet currentConfig: ReachConfig | null = null\n\n/** DND 配置 */\nlet dndConfig: DndConfig | undefined\n\n/** 模板注册表(始终可用) */\nlet templateRegistry: ReachTemplateRegistry = createTemplateRegistry()\n\n/** 发送日志存储(db 可用时初始化) */\nlet sendLogRepo: SendLogRepository | null = null\n\n/** 模板存储(db 可用时初始化) */\nlet templateRepo: TemplateRepository | null = null\n\n/** 初始化进行中标志(防止并发调用) */\nlet initInProgress = false\n\n// ─── Provider 工厂 ───\n\n/**\n * 根据配置创建对应的 Provider\n */\nfunction createProvider(config: ProviderConfig): ReachProvider {\n switch (config.type) {\n case 'console':\n return createConsoleProvider()\n case 'smtp':\n return createSmtpProvider()\n case 'aliyun-sms':\n return createAliyunSmsProvider()\n case 'api':\n return createApiProvider()\n }\n}\n\n// ─── DB 动态引用(可选依赖) ───\n\n/**\n * 尝试获取 db 实例(可选依赖,不可用时返回 null)\n */\nasync function tryGetDb(): Promise<ReldbFunctions | null> {\n // 与 Repository 的公开静态导入一致,避免 SSR 打包后动态导入得到另一份未初始化单例。\n return reldb.isInitialized ? reldb : null\n}\n\n/**\n * 尝试初始化发送日志存储\n */\nasync function tryInitSendLogRepo(): Promise<SendLogRepository | null> {\n try {\n const db = await tryGetDb()\n if (!db) {\n return null\n }\n const result = await createSendLogRepository(db)\n if (!result.success) {\n logger.debug('Send log repository not initialized', { error: result.error.message })\n return null\n }\n return result.data\n }\n catch (error) {\n logger.debug('Send log repository not initialized', { error: error instanceof Error ? error.message : String(error) })\n return null\n }\n}\n\n/**\n * 尝试初始化模板存储\n */\nasync function tryInitTemplateRepo(): Promise<TemplateRepository | null> {\n try {\n const db = await tryGetDb()\n if (!db) {\n return null\n }\n const result = await createTemplateRepository(db)\n if (!result.success) {\n logger.debug('Template repository not initialized', { error: result.error.message })\n return null\n }\n return result.data\n }\n catch (error) {\n logger.debug('Template repository not initialized', { error: error instanceof Error ? error.message : String(error) })\n return null\n }\n}\n\n// ─── 未初始化工具集 ───\n\nconst notInitialized = core.module.createNotInitializedKit(\n HaiReachError.NOT_INITIALIZED,\n () => reachM('reach_notInitialized'),\n)\n\n// ─── 关闭逻辑 ───\n\n/**\n * 关闭所有 Provider 并释放资源(内部实现)\n */\nasync function doClose(): Promise<void> {\n if (providers.size === 0 && currentConfig === null) {\n logger.info('Reach module already closed, skipping')\n return\n }\n\n logger.info('Closing reach module')\n\n for (const [name, provider] of providers.entries()) {\n try {\n await provider.close()\n }\n catch (error) {\n logger.error('Provider close failed', { name, error })\n }\n }\n\n providers = new Map()\n currentConfig = null\n dndConfig = undefined\n templateRegistry = createTemplateRegistry()\n sendLogRepo = null\n templateRepo = null\n resetSendLogRepoSingleton()\n resetTemplateRepoSingleton()\n resetSendState()\n\n logger.info('Reach module closed')\n}\n\n// ─── 初始化逻辑 ───\n\n/**\n * 实际的初始化逻辑(由 init 包裹并发防护后调用)\n */\nasync function doInit(config: ReachConfigInput): Promise<HaiResult<void>> {\n if (providers.size > 0) {\n logger.warn('Reach module is already initialized, reinitializing')\n await doClose()\n }\n\n logger.info('Initializing reach module')\n\n const parseResult = ReachConfigSchema.safeParse(config)\n if (!parseResult.success) {\n logger.error('Reach config validation failed', { error: parseResult.error.message })\n return err(\n HaiReachError.CONFIG_ERROR,\n reachM('reach_configError', { params: { error: parseResult.error.message } }),\n parseResult.error,\n )\n }\n const parsed = parseResult.data\n // Console只证明本机日志调用;生产环境不能把它当作真实触达成功。\n if (core.module.requiresSharedState(process.env) && parsed.providers.some(provider => provider.type === 'console'))\n return err(HaiReachError.CONFIG_ERROR, reachM('reach_productionProviderRequired'))\n\n try {\n // delay 队列是可靠发送前置;连接任何 Provider、发布模块状态前先确认表已就绪。\n let requiredQueue: SendLogRepository | null = null\n if (parsed.dnd?.enabled && parsed.dnd.strategy === 'delay') {\n if (!reldb.isInitialized)\n return err(HaiReachError.CONFIG_ERROR, reachM('reach_dndPersistenceRequired'))\n const queue = await createSendLogRepository(reldb)\n if (!queue.success)\n return err(HaiReachError.CONFIG_ERROR, reachM('reach_dndPersistenceRequired'), queue.error)\n requiredQueue = queue.data\n }\n\n const newProviders = new Map<string, ReachProvider>()\n\n for (const providerConfig of parsed.providers) {\n const provider = createProvider(providerConfig)\n const connectResult = await provider.connect(providerConfig)\n if (!connectResult.success) {\n logger.error('Provider initialization failed', {\n name: providerConfig.name,\n type: providerConfig.type,\n code: connectResult.error.code,\n message: connectResult.error.message,\n })\n logger.info('Rolling back connected providers', { count: newProviders.size })\n for (const [name, p] of newProviders.entries()) {\n try {\n await p.close()\n }\n catch (rollbackError) {\n logger.error('Provider rollback close failed', { name, error: rollbackError })\n }\n }\n return connectResult\n }\n newProviders.set(providerConfig.name, provider)\n }\n\n providers = newProviders\n currentConfig = parsed\n dndConfig = parsed.dnd\n\n // 尝试初始化模板存储\n templateRepo = await tryInitTemplateRepo()\n\n // 创建模板注册表\n templateRegistry = createTemplateRegistry(templateRepo)\n\n // 配置文件中定义的模板写入数据库\n if (parsed.templates && templateRepo) {\n const saveResult = await templateRegistry.saveBatch(parsed.templates)\n if (!saveResult.success) {\n logger.warn('Failed to save config templates to database', { error: saveResult.error.message })\n }\n }\n\n // delay 使用已验证的必需队列;即时发送的普通日志仍是可选能力。\n sendLogRepo = requiredQueue ?? await tryInitSendLogRepo()\n\n // 启动 DND 恢复定时器\n if (dndConfig) {\n startDndScheduler(dndConfig, providers, sendLogRepo)\n }\n\n const providerNames = parsed.providers.map(p => p.name)\n logger.info('Reach module initialized', { providers: providerNames })\n return ok(undefined)\n }\n catch (error) {\n logger.error('Reach module initialization failed', { error })\n return err(\n HaiReachError.CONFIG_ERROR,\n reachM('reach_initFailed', {\n params: { error: error instanceof Error ? error.message : String(error) },\n }),\n error,\n )\n }\n}\n\n// ─── 触达服务对象 ───\n\n/**\n * 把敏感日志实体投影成运维元数据,排除收件人、正文、vars/extra和owner凭据。\n * @param row - 已确认queueId非空且字段规范化的队列行。\n * @returns 对外可查询的元数据;不能据此替代运维授权。\n *\n * @example 模块内部上下文\n * ```ts\n * function safeEntry(row: StoredSendLog): ReachQueueEntry | null {\n * if (!row.queueId) return null\n * return queueEntry(row)\n * }\n * ```\n */\nfunction queueEntry(row: StoredSendLog): ReachQueueEntry {\n return {\n queueId: row.queueId!,\n provider: row.provider,\n status: row.status,\n updatedAt: row.updatedAt,\n messageId: row.messageId ?? undefined,\n attemptCount: row.attemptCount,\n maxAttempts: row.maxAttempts,\n nextAttemptAt: row.nextAttemptAt,\n lastFailure: row.lastFailure ?? undefined,\n }\n}\n\n/**\n * 邮件/短信/API触达统一入口;先检查init与send的HaiResult,成功后还区分deferred和即时Provider结果。\n * 模板与delay队列使用已初始化数据库,生产共享后端;close停止本地调度/关闭Provider,不撤销已发外部请求。\n *\n * @example\n * ```ts\n * import { reach } from '@h-ai/reach'\n * const { reldb } = await import('@h-ai/reldb')\n * const connected = await reldb.init({ type: 'postgresql', host: 'db', port: 5432, database: 'hai', user: 'postgres', password: process.env.HAI_DB_PASSWORD })\n * if (!connected.success) throw new Error(connected.error.message)\n * try {\n * const initialized = await reach.init({ providers: [{ name: 'webhook', type: 'api', url: 'https://notify.example.com/events', idempotent: false }], templates: [{ name: 'welcome', provider: 'webhook', subject: 'Welcome {userName}', body: 'Hello {userName}' }], dnd: { enabled: true, strategy: 'delay', start: '22:00', end: '08:00' } })\n * if (!initialized.success) throw new Error(initialized.error.message)\n * try {\n * const result = await reach.send({ provider: 'webhook', to: 'user@example.com', template: 'welcome', vars: { userName: 'Alice' } })\n * if (!result.success) throw new Error(result.error.message)\n * if (!result.data.success) throw new Error('Delivery was not accepted')\n * if (result.data.deferred) {\n * const queueId = result.data.queueId\n * // 持久入队成功,尚未证明接收端已完成外部投递。\n * }\n * else { const providerMessageId = result.data.messageId }\n * }\n * finally { await reach.close() }\n * }\n * finally {\n * const closed = await reldb.close()\n * if (!closed.success) throw new Error(closed.error.message)\n * }\n * ```\n */\nexport const reach: ReachFunctions = {\n queue: {\n async get(queueId) {\n if (providers.size === 0)\n return notInitialized.result()\n if (!sendLogRepo)\n return err(HaiReachError.CONFIG_ERROR, reachM('reach_dndPersistenceRequired'))\n if (!ReachQueueIdSchema.safeParse(queueId).success)\n return err(HaiReachError.CONFIG_ERROR, reachM('reach_queueInputInvalid'))\n const rows = await sendLogRepo.findAll({ where: 'queue_id = ?', params: [queueId], limit: 1 })\n if (!rows.success)\n return rows\n const row = rows.data[0]\n return ok(row ? queueEntry(row) : null)\n },\n async list(options) {\n if (providers.size === 0)\n return notInitialized.result()\n if (!sendLogRepo)\n return err(HaiReachError.CONFIG_ERROR, reachM('reach_dndPersistenceRequired'))\n const parsed = ReachQueueListSchema.safeParse(options)\n if (!parsed.success)\n return err(HaiReachError.CONFIG_ERROR, reachM('reach_queueInputInvalid'))\n const rows = await sendLogRepo.findAll({\n where: 'status = ? AND queue_id IS NOT NULL',\n params: [parsed.data.status],\n orderBy: 'created_at ASC, id ASC',\n limit: parsed.data.limit,\n offset: parsed.data.offset,\n })\n return rows.success ? ok(rows.data.map(queueEntry)) : rows\n },\n async requeueDead(queueId, options) {\n if (providers.size === 0)\n return notInitialized.result()\n if (!sendLogRepo)\n return err(HaiReachError.CONFIG_ERROR, reachM('reach_dndPersistenceRequired'))\n const parsed = ReachDeadRequeueSchema.safeParse(options)\n if (!ReachQueueIdSchema.safeParse(queueId).success || !parsed.success)\n return err(HaiReachError.CONFIG_ERROR, reachM('reach_queueInputInvalid'))\n return sendLogRepo.requeueDead(queueId, parsed.data.expectedUpdatedAt)\n },\n async resolveUnknown(queueId, resolution) {\n if (providers.size === 0)\n return notInitialized.result()\n if (!sendLogRepo)\n return err(HaiReachError.CONFIG_ERROR, reachM('reach_dndPersistenceRequired'))\n const parsed = ReachUnknownResolutionSchema.safeParse(resolution)\n if (!ReachQueueIdSchema.safeParse(queueId).success || !parsed.success)\n return err(HaiReachError.CONFIG_ERROR, reachM('reach_queueInputInvalid'))\n return sendLogRepo.resolveUnknown(queueId, parsed.data)\n },\n },\n async init(config: ReachConfigInput): Promise<HaiResult<void>> {\n if (initInProgress) {\n logger.warn('Reach module init already in progress, skipping')\n return err(\n HaiReachError.CONFIG_ERROR,\n reachM('reach_configError', { params: { error: 'init already in progress' } }),\n )\n }\n\n initInProgress = true\n try {\n return await doInit(config)\n }\n finally {\n initInProgress = false\n }\n },\n\n async send(message: ReachMessage): Promise<HaiResult<SendResult>> {\n if (providers.size === 0) {\n return notInitialized.result()\n }\n\n const send = () => executeSend(message, providers, templateRegistry, dndConfig, sendLogRepo, currentConfig!.queue)\n return message.sensitive ? core.context.run({ suppressLogObservers: true }, send) : send()\n },\n\n get template(): ReachTemplateRegistry {\n return templateRegistry\n },\n\n get config(): ReachConfig | null {\n return currentConfig ? core.sanitize.sanitizeSensitiveFields(currentConfig) : null\n },\n\n get isInitialized(): boolean {\n return providers.size > 0\n },\n\n async close(): Promise<void> {\n await doClose()\n },\n}\n"]}
1
+ {"version":3,"sources":["../messages/en-US.json","../messages/ja-JP.json","../messages/zh-CN.json","../src/reach-i18n.ts","../src/reach-config.ts","../src/reach-types.ts","../src/providers/reach-provider-aliyun-sms.ts","../src/providers/reach-provider-api.ts","../src/providers/reach-provider-console.ts","../src/providers/reach-provider-smtp.ts","../src/reach-delivery.ts","../src/reach-send-sensitive.ts","../src/reach-send.ts","../src/reach-template.ts","../src/reach-migration.ts","../src/repositories/reach-repository-send-log.ts","../src/repositories/reach-repository-template.ts","../src/reach-main.ts"],"names":["core","logger","toReachError","err","ok","require","templateRegistry","dndConfig","providers","randomUUID","commitResult","TABLE_NAME","BaseReldbCrudRepository","reldb","process"],"mappings":"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","file":"index.js","sourcesContent":["{\n \"$schema\": \"https://inlang.com/schema/inlang-message-format\",\n \"reach_notInitialized\": \"Reach module not initialized, call reach.init() first\",\n \"reach_initFailed\": \"Reach module initialization failed: {error}\",\n \"reach_unsupportedType\": \"Unsupported reach type: {type}\",\n \"reach_sendFailed\": \"Send failed: {error}\",\n \"reach_templateNotFound\": \"Template not found: {template}\",\n \"reach_templateRenderFailed\": \"Template render failed: {error}\",\n \"reach_invalidRecipient\": \"Invalid recipient: {recipient}\",\n \"reach_providerNotFound\": \"Provider not found: {provider}\",\n \"reach_providerRequired\": \"No provider specified, set provider field in message or template\",\n \"reach_dndBlocked\": \"Message discarded by Do Not Disturb period\",\n \"reach_dndPersistenceRequired\": \"DND delay requires an initialized database and a ready queue table\",\n \"reach_dndEnqueueFailed\": \"The message could not be reliably queued; try again later\",\n \"reach_sendRecordSaveFailed\": \"Failed to persist the reach send record\",\n \"reach_invalidStoredInteger\": \"Send record {field} is not a safe non-negative integer\",\n \"reach_claimLost\": \"The valid delivery lease was lost\",\n \"reach_unknownResolutionFailed\": \"Unknown delivery was not found or has already been resolved\",\n \"reach_queueInputInvalid\": \"Invalid queue receipt or manual resolution parameters\",\n \"reach_deliveryUnknown\": \"The external delivery outcome is unknown; manual review is required and automatic redelivery has stopped\",\n \"reach_smsSendFailed\": \"SMS send failed: {error}\",\n \"reach_emailSendFailed\": \"Email send failed: {error}\",\n \"reach_apiSendFailed\": \"API callback send failed: {error}\",\n \"reach_configError\": \"Reach config validation failed: {error}\",\n \"reach_config_nameRequired\": \"Provider name is required\",\n \"reach_config_hostRequired\": \"SMTP host is required\",\n \"reach_config_fromRequired\": \"Sender address (from) is required\",\n \"reach_config_accessKeyIdRequired\": \"AccessKey ID is required\",\n \"reach_config_accessKeySecretRequired\": \"AccessKey Secret is required\",\n \"reach_config_signNameRequired\": \"SMS sign name is required\",\n \"reach_config_urlRequired\": \"Callback URL is required\",\n \"reach_config_dndTimeInvalid\": \"Invalid time format, must be HH:mm (24-hour)\",\n \"reach_config_templateNameRequired\": \"Template name is required\",\n \"reach_config_templateProviderRequired\": \"Template provider is required\",\n \"reach_config_templateBodyRequired\": \"Template body is required\",\n \"reach_config_providersRequired\": \"At least one provider is required\",\n \"reach_templateDbInitFailed\": \"Template database initialization failed: {error}\",\n \"reach_templateSaveFailed\": \"Template save failed: {error}\",\n \"reach_templateDeleteFailed\": \"Template delete failed: {error}\",\n \"reach_queueRetryInvalid\": \"maxRetryDelayMs must be at least retryDelayMs\",\n \"reach_deadRequeueFailed\": \"Dead message changed or does not exist; reload its state before restoring\",\n \"reach_productionProviderRequired\": \"Production requires a delivery provider; console is available only in development or explicit test mode\",\n \"reach_sensitivePolicyRequired\": \"Credential notifications require a database receipt, stable idempotency key and expiry\",\n \"reach_sensitiveDeliveryUnconfirmed\": \"Credential delivery is unconfirmed and will not be retried automatically. Check the receipt or request new credentials.\",\n \"reach_messageExpired\": \"The credentials expired before delivery\",\n \"reach_apiResponseTooLarge\": \"API acknowledgement exceeds the 64 KiB limit\",\n \"reach_apiResponseInvalid\": \"API acknowledgement must be empty or a JSON object with an optional messageId string\",\n \"reach_apiRequestFailed\": \"API request failed or timed out; delivery is unconfirmed\",\n \"reach_apiHttpsRequired\": \"This API policy requires HTTPS and disallows automatic redirects\"\n}\n","{\n \"$schema\": \"https://inlang.com/schema/inlang-message-format\",\n \"reach_notInitialized\": \"通知モジュールが初期化されていません。先にreach.init()を呼び出してください\",\n \"reach_initFailed\": \"通知モジュールの初期化に失敗しました: {error}\",\n \"reach_unsupportedType\": \"未対応の通知の種類です: {type}\",\n \"reach_sendFailed\": \"送信に失敗しました: {error}\",\n \"reach_templateNotFound\": \"テンプレートが見つかりません: {template}\",\n \"reach_templateRenderFailed\": \"テンプレートの描画に失敗しました: {error}\",\n \"reach_invalidRecipient\": \"宛先が無効です: {recipient}\",\n \"reach_providerNotFound\": \"プロバイダーが見つかりません: {provider}\",\n \"reach_providerRequired\": \"プロバイダーが指定されていません。メッセージまたはテンプレートのproviderを設定してください\",\n \"reach_dndBlocked\": \"通知を停止する時間帯のため、メッセージを破棄しました\",\n \"reach_dndPersistenceRequired\": \"通知制限時間帯の配信延期には初期化済みのデータベースと準備済みのキューテーブルが必要です\",\n \"reach_dndEnqueueFailed\": \"メッセージを確実にキューへ登録できませんでした。しばらくしてから再試行してください\",\n \"reach_sendRecordSaveFailed\": \"配信記録の永続化に失敗しました\",\n \"reach_invalidStoredInteger\": \"配信記録の {field} は安全な非負整数ではありません\",\n \"reach_claimLost\": \"有効な配信リースが失われました\",\n \"reach_unknownResolutionFailed\": \"結果不明の配信が見つからないか、既に解決されています\",\n \"reach_queueInputInvalid\": \"キューの受領記録または手動解決のパラメーターが無効です\",\n \"reach_deliveryUnknown\": \"外部への配信結果は不明です。手動確認が必要なため、自動再配信を停止しました\",\n \"reach_smsSendFailed\": \"SMSの送信に失敗しました: {error}\",\n \"reach_emailSendFailed\": \"メールの送信に失敗しました: {error}\",\n \"reach_apiSendFailed\": \"APIコールバックの送信に失敗しました: {error}\",\n \"reach_configError\": \"通知設定の検証に失敗しました: {error}\",\n \"reach_config_nameRequired\": \"プロバイダー名は必須です\",\n \"reach_config_hostRequired\": \"SMTPホストは必須です\",\n \"reach_config_fromRequired\": \"送信者アドレス(from)は必須です\",\n \"reach_config_accessKeyIdRequired\": \"AccessKey IDは必須です\",\n \"reach_config_accessKeySecretRequired\": \"AccessKey Secretは必須です\",\n \"reach_config_signNameRequired\": \"SMS署名は必須です\",\n \"reach_config_urlRequired\": \"コールバックURLは必須です\",\n \"reach_config_dndTimeInvalid\": \"時刻形式が無効です。HH:mm(24時間制)で指定してください\",\n \"reach_config_templateNameRequired\": \"テンプレート名は必須です\",\n \"reach_config_templateProviderRequired\": \"テンプレートのプロバイダーは必須です\",\n \"reach_config_templateBodyRequired\": \"テンプレート本文は必須です\",\n \"reach_config_providersRequired\": \"少なくとも1つのプロバイダーが必要です\",\n \"reach_templateDbInitFailed\": \"テンプレートデータベースの初期化に失敗しました: {error}\",\n \"reach_templateSaveFailed\": \"テンプレートの保存に失敗しました: {error}\",\n \"reach_templateDeleteFailed\": \"テンプレートの削除に失敗しました: {error}\",\n \"reach_queueRetryInvalid\": \"maxRetryDelayMs は retryDelayMs 以上にしてください\",\n \"reach_deadRequeueFailed\": \"配信不能メッセージが変更されたか、存在しません。復元する前に状態を再読み込みしてください\",\n \"reach_productionProviderRequired\": \"本番環境には配信プロバイダーが必要です。console は開発環境または明示的なテストモードでのみ使用できます\",\n \"reach_sensitivePolicyRequired\": \"認証情報の通知にはデータベースの受領記録、固定の冪等キーと有効期限が必要です\",\n \"reach_sensitiveDeliveryUnconfirmed\": \"認証情報の配信を確認できません。自動再送せず、受領記録の確認または認証情報の再発行が必要です。\",\n \"reach_messageExpired\": \"認証情報の期限が切れたため配信しませんでした\",\n \"reach_apiResponseTooLarge\": \"API 応答が 64 KiB の上限を超えています\",\n \"reach_apiResponseInvalid\": \"API 応答は空か、省略可能な messageId 文字列を持つ JSON オブジェクトである必要があります\",\n \"reach_apiRequestFailed\": \"API リクエストが失敗またはタイムアウトしました。配信結果は未確認です\",\n \"reach_apiHttpsRequired\": \"この API ポリシーでは HTTPS が必要で、自動リダイレクトは禁止されています\"\n}\n","{\n \"$schema\": \"https://inlang.com/schema/inlang-message-format\",\n \"reach_notInitialized\": \"触达模块尚未初始化,请先调用 reach.init()\",\n \"reach_initFailed\": \"触达模块初始化失败: {error}\",\n \"reach_unsupportedType\": \"不支持的触达类型: {type}\",\n \"reach_sendFailed\": \"发送失败: {error}\",\n \"reach_templateNotFound\": \"模板未找到: {template}\",\n \"reach_templateRenderFailed\": \"模板渲染失败: {error}\",\n \"reach_invalidRecipient\": \"无效的接收方: {recipient}\",\n \"reach_providerNotFound\": \"Provider 未找到: {provider}\",\n \"reach_providerRequired\": \"未指定 Provider,请在消息或模板中设置 provider 字段\",\n \"reach_dndBlocked\": \"当前处于免打扰时段,消息已丢弃\",\n \"reach_dndPersistenceRequired\": \"启用免打扰延迟发送需要先初始化数据库并确保队列表可用\",\n \"reach_dndEnqueueFailed\": \"消息未能可靠保存到延迟队列,请稍后重试\",\n \"reach_sendRecordSaveFailed\": \"保存触达发送记录失败\",\n \"reach_invalidStoredInteger\": \"发送记录的 {field} 不是安全的非负整数\",\n \"reach_claimLost\": \"消息的有效投递租约已丢失\",\n \"reach_unknownResolutionFailed\": \"未知投递结果不存在或已被其它操作裁定\",\n \"reach_queueInputInvalid\": \"队列回执或人工裁定参数无效\",\n \"reach_deliveryUnknown\": \"外部投递结果未知,需要人工核对,已停止自动重发\",\n \"reach_smsSendFailed\": \"短信发送失败: {error}\",\n \"reach_emailSendFailed\": \"邮件发送失败: {error}\",\n \"reach_apiSendFailed\": \"API 回调发送失败: {error}\",\n \"reach_configError\": \"触达配置校验失败:{error}\",\n \"reach_config_nameRequired\": \"Provider 名称不能为空\",\n \"reach_config_hostRequired\": \"SMTP 服务器地址不能为空\",\n \"reach_config_fromRequired\": \"发件人地址不能为空\",\n \"reach_config_accessKeyIdRequired\": \"AccessKey ID 不能为空\",\n \"reach_config_accessKeySecretRequired\": \"AccessKey Secret 不能为空\",\n \"reach_config_signNameRequired\": \"短信签名不能为空\",\n \"reach_config_urlRequired\": \"回调 URL 不能为空\",\n \"reach_config_dndTimeInvalid\": \"时间格式无效,须为 HH:mm(24 小时制)\",\n \"reach_config_templateNameRequired\": \"模板名称不能为空\",\n \"reach_config_templateProviderRequired\": \"模板绑定的 Provider 不能为空\",\n \"reach_config_templateBodyRequired\": \"模板正文不能为空\",\n \"reach_config_providersRequired\": \"至少需要配置一个 Provider\",\n \"reach_templateDbInitFailed\": \"模板数据库初始化失败: {error}\",\n \"reach_templateSaveFailed\": \"模板保存失败: {error}\",\n \"reach_templateDeleteFailed\": \"模板删除失败: {error}\",\n \"reach_queueRetryInvalid\": \"maxRetryDelayMs 不能小于 retryDelayMs\",\n \"reach_deadRequeueFailed\": \"死信已变化或不存在,请重新查询后恢复\",\n \"reach_productionProviderRequired\": \"生产环境必须配置真实触达服务;console 仅可用于开发或显式测试模式\",\n \"reach_sensitivePolicyRequired\": \"凭据通知需要数据库回执、稳定幂等键和有效期\",\n \"reach_sensitiveDeliveryUnconfirmed\": \"凭据投递尚未确认;不会自动重复发送,请查询回执或申请新凭据\",\n \"reach_messageExpired\": \"凭据已过期,未投递\",\n \"reach_apiResponseTooLarge\": \"API 回执超过 64 KiB 上限\",\n \"reach_apiResponseInvalid\": \"API 回执必须为空或含可选 messageId 字符串的 JSON 对象\",\n \"reach_apiRequestFailed\": \"API 请求失败或超时,投递结果未确认\",\n \"reach_apiHttpsRequired\": \"此 API 策略要求 HTTPS 且禁止自动重定向\"\n}\n","/**\n * @h-ai/reach — i18n\n *\n * 本文件提供触达模块的 i18n 文案访问入口。\n * @module reach-i18n\n */\n\nimport { core } from '@h-ai/core'\nimport messagesEnUS from '../messages/en-US.json'\nimport messagesJaJP from '../messages/ja-JP.json'\nimport messagesZhCN from '../messages/zh-CN.json'\n\n// ─── i18n ───\n\n/**\n * 内部消息键,由中文词典推导;中英词典同步维护。\n *\n * @example 模块内部上下文\n * ```ts\n * const key: ReachMessageKey = 'reach_notInitialized'\n * const message = reachM(key)\n * ```\n */\ntype ReachMessageKey = keyof typeof messagesZhCN\n\n/**\n * 获取当前locale的Reach消息;插值不应包含正文、收件人或凭据。\n * @param key - 已维护的消息键。\n * @param options - locale覆盖与params插值。\n * @returns 本地化消息。\n *\n * @example 模块内部上下文\n * ```ts\n * const message = reachM('reach_notInitialized', { locale: 'en-US' })\n * ```\n */\nexport const reachM = core.i18n.createMessageGetter<ReachMessageKey>({\n 'zh-CN': messagesZhCN,\n 'en-US': messagesEnUS,\n 'ja-JP': messagesJaJP,\n})\n","/**\n * @h-ai/reach — 配置 Schema\n *\n * 本文件定义触达模块的错误码、Zod Schema 和配置类型。\n * @module reach-config\n */\n\nimport { z } from 'zod'\n\nimport { reachM } from './reach-i18n.js'\n\n// ─── 单个 Provider 配置 Schema ───\n\n/**\n * 开发/测试日志适配器配置;不证明外部投递,生产共享状态守卫拒绝console。\n *\n * @example\n * ```ts\n * import { ConsoleProviderConfigSchema } from '@h-ai/reach'\n * const config = ConsoleProviderConfigSchema.parse({ name: 'dev-log', type: 'console' })\n * ```\n */\nexport const ConsoleProviderConfigSchema = z.object({\n /** Provider 唯一名称 */\n name: z.string().min(1, reachM('reach_config_nameRequired')),\n type: z.literal('console'),\n})\n\n/**\n * SMTP配置,默认TLS/465;凭据来自私有环境,SMTP没有接收端持久幂等能力声明。\n *\n * @example\n * ```ts\n * import { SmtpProviderConfigSchema } from '@h-ai/reach'\n * const config = SmtpProviderConfigSchema.parse({ name: 'email', type: 'smtp', host: 'smtp.example.com', from: 'noreply@example.com', user: process.env.HAI_REACH_PROVIDERS_0_USER, pass: process.env.HAI_REACH_PROVIDERS_0_PASS })\n * ```\n */\nexport const SmtpProviderConfigSchema = z.object({\n /** Provider 唯一名称 */\n name: z.string().min(1, reachM('reach_config_nameRequired')),\n type: z.literal('smtp'),\n /** SMTP 服务器地址 */\n host: z.string().min(1, reachM('reach_config_hostRequired')),\n /** SMTP 端口(默认 465) */\n port: z.number().int().min(1).max(65535).default(465),\n /** 是否使用 TLS(默认 true) */\n secure: z.boolean().default(true),\n /** SMTP 认证用户名 */\n user: z.string().optional(),\n /** SMTP 认证密码 */\n pass: z.string().optional(),\n /** 发件人地址 */\n from: z.string().min(1, reachM('reach_config_fromRequired')),\n})\n\n/**\n * 阿里云短信POP API配置;Secret只在服务端私有注入,发送需Provider模板编码等扩展参数。\n *\n * @example\n * ```ts\n * import { AliyunSmsProviderConfigSchema } from '@h-ai/reach'\n * const config = AliyunSmsProviderConfigSchema.parse({ name: 'sms', type: 'aliyun-sms', accessKeyId: process.env.HAI_REACH_PROVIDERS_0_ACCESSKEYID, accessKeySecret: process.env.HAI_REACH_PROVIDERS_0_ACCESSKEYSECRET, signName: process.env.HAI_REACH_PROVIDERS_0_SIGNNAME })\n * ```\n */\nexport const AliyunSmsProviderConfigSchema = z.object({\n /** Provider 唯一名称 */\n name: z.string().min(1, reachM('reach_config_nameRequired')),\n type: z.literal('aliyun-sms'),\n /** 阿里云 AccessKey ID */\n accessKeyId: z.string().min(1, reachM('reach_config_accessKeyIdRequired')),\n /** 阿里云 AccessKey Secret */\n accessKeySecret: z.string().min(1, reachM('reach_config_accessKeySecretRequired')),\n /** 短信签名 */\n signName: z.string().min(1, reachM('reach_config_signNameRequired')),\n /** API 端点(默认 dysmsapi.aliyuncs.com) */\n endpoint: z.string().default('dysmsapi.aliyuncs.com'),\n})\n\n/**\n * HTTP回调配置;idempotent默认false,只有接收端按稳定Idempotency-Key原子持久去重并覆盖旧请求迟到窗口才允许true。\n * 写入该布尔值或发送请求头本身不证明接收端具备此能力。\n *\n * @example\n * ```ts\n * import { ApiProviderConfigSchema } from '@h-ai/reach'\n * const config = ApiProviderConfigSchema.parse({ name: 'webhook', type: 'api', url: 'https://notify.example.com/events', idempotent: false })\n * ```\n */\nexport const ApiProviderConfigSchema = z.object({\n /** Provider 唯一名称 */\n name: z.string().min(1, reachM('reach_config_nameRequired')),\n type: z.literal('api'),\n /** 回调 URL */\n url: z.string()\n .url(reachM('reach_config_urlRequired'))\n .refine(value => ['http:', 'https:'].includes(new URL(value).protocol), {\n message: reachM('reach_config_urlRequired'),\n }),\n /** HTTP 方法(默认 POST) */\n method: z.enum(['POST', 'PUT']).default('POST'),\n /** 自定义请求头 */\n headers: z.record(z.string(), z.string()).optional(),\n /** 请求超时毫秒数(默认 10000) */\n timeout: z.number().int().min(0).default(10000),\n /** HTTP 重定向策略;默认拒绝。敏感发送始终拒绝,即使普通发送显式允许。 */\n redirect: z.enum(['error', 'follow']).default('error'),\n /** 强制 HTTPS;省略时生产敏感发送要求 HTTPS,显式 false 仅用于受信内网传输。 */\n requireHttps: z.boolean().optional(),\n /** 接收端按 Idempotency-Key 原子持久去重,且覆盖所有重试与旧节点迟到请求时才启用;默认 false。 */\n idempotent: z.boolean().default(false),\n})\n\n/**\n * 按type区分内置Provider配置,不把name当协议类型;name用于模板/消息路由。\n *\n * @example\n * ```ts\n * import { ProviderConfigSchema } from '@h-ai/reach'\n * const config = ProviderConfigSchema.parse({ name: 'webhook', type: 'api', url: 'https://notify.example.com/events', idempotent: false })\n * ```\n */\nexport const ProviderConfigSchema = z.discriminatedUnion('type', [\n ConsoleProviderConfigSchema,\n SmtpProviderConfigSchema,\n AliyunSmsProviderConfigSchema,\n ApiProviderConfigSchema,\n])\n\n/**\n * 由ProviderConfigSchema解析后的配置类型,默认值已经填充;与输入类型区分,秘密字段不写日志。\n *\n * @example\n * ```ts\n * import { ProviderConfigSchema, type ProviderConfig } from '@h-ai/reach'\n * const config: ProviderConfig = ProviderConfigSchema.parse({ name: 'webhook', type: 'api', url: 'https://notify.example.com/events', idempotent: false })\n * ```\n */\nexport type ProviderConfig = z.infer<typeof ProviderConfigSchema>\n\n/**\n * 由ConsoleProviderConfigSchema解析后的配置类型,默认值已经填充;与输入类型区分,秘密字段不写日志。\n *\n * @example\n * ```ts\n * import { ConsoleProviderConfigSchema, type ConsoleProviderConfig } from '@h-ai/reach'\n * const config: ConsoleProviderConfig = ConsoleProviderConfigSchema.parse({ name: 'dev-log', type: 'console' })\n * ```\n */\nexport type ConsoleProviderConfig = z.infer<typeof ConsoleProviderConfigSchema>\n\n/**\n * 由SmtpProviderConfigSchema解析后的配置类型,默认值已经填充;与输入类型区分,秘密字段不写日志。\n *\n * @example\n * ```ts\n * import { SmtpProviderConfigSchema, type SmtpProviderConfig } from '@h-ai/reach'\n * const config: SmtpProviderConfig = SmtpProviderConfigSchema.parse({ name: 'email', type: 'smtp', host: 'smtp.example.com', from: 'noreply@example.com', user: process.env.HAI_REACH_PROVIDERS_0_USER, pass: process.env.HAI_REACH_PROVIDERS_0_PASS })\n * ```\n */\nexport type SmtpProviderConfig = z.infer<typeof SmtpProviderConfigSchema>\n\n/**\n * 由AliyunSmsProviderConfigSchema解析后的配置类型,默认值已经填充;与输入类型区分,秘密字段不写日志。\n *\n * @example\n * ```ts\n * import { AliyunSmsProviderConfigSchema, type AliyunSmsProviderConfig } from '@h-ai/reach'\n * const config: AliyunSmsProviderConfig = AliyunSmsProviderConfigSchema.parse({ name: 'sms', type: 'aliyun-sms', accessKeyId: process.env.HAI_REACH_PROVIDERS_0_ACCESSKEYID, accessKeySecret: process.env.HAI_REACH_PROVIDERS_0_ACCESSKEYSECRET, signName: process.env.HAI_REACH_PROVIDERS_0_SIGNNAME })\n * ```\n */\nexport type AliyunSmsProviderConfig = z.infer<typeof AliyunSmsProviderConfigSchema>\n\n/**\n * 由ApiProviderConfigSchema解析后的配置类型,默认值已经填充;与输入类型区分,秘密字段不写日志。\n *\n * @example\n * ```ts\n * import { ApiProviderConfigSchema, type ApiProviderConfig } from '@h-ai/reach'\n * const config: ApiProviderConfig = ApiProviderConfigSchema.parse({ name: 'webhook', type: 'api', url: 'https://notify.example.com/events', idempotent: false })\n * ```\n */\nexport type ApiProviderConfig = z.infer<typeof ApiProviderConfigSchema>\n\n/**\n * 人工处置unknown的结构边界,仅sent/discarded,不提供重发;应用须先校验运维权限并隔离旧worker/核对外部效果。\n *\n * @example\n * ```ts\n * import { ReachUnknownResolutionSchema } from '@h-ai/reach'\n * const config = ReachUnknownResolutionSchema.parse({ status: 'discarded' })\n * ```\n */\nexport const ReachUnknownResolutionSchema = z.object({\n status: z.enum(['sent', 'discarded']),\n messageId: z.string().max(255).optional(),\n})\n\n/**\n * 持久入队回执UUID格式;合法UUID不代表存在,也不授予队列运维权限。\n *\n * @example\n * ```ts\n * import { ReachQueueIdSchema } from '@h-ai/reach'\n * const config = ReachQueueIdSchema.parse('00000000-0000-4000-8000-000000000001')\n * ```\n */\nexport const ReachQueueIdSchema = z.string().uuid()\n\n/**\n * 可靠队列重试策略,入队时随消息持久保存;maxAttempts含首次与崩溃恢复,指数退避有上限。\n *\n * @example\n * ```ts\n * import { ReachQueueConfigSchema } from '@h-ai/reach'\n * const config = ReachQueueConfigSchema.parse({ maxAttempts: 5, retryDelayMs: 60000, maxRetryDelayMs: 3600000 })\n * ```\n */\nexport const ReachQueueConfigSchema = z.object({\n /** 自动领取次数上限(含首次及崩溃恢复,默认 5,范围 1–100)。 */\n maxAttempts: z.number().int().min(1).max(100).default(5),\n /** 首次失败后的退避毫秒数(默认 60000,范围 1000–86400000)。 */\n retryDelayMs: z.number().int().min(1000).max(86_400_000).default(60_000),\n /** 指数退避上限毫秒数,不得小于 retryDelayMs(默认 3600000)。 */\n maxRetryDelayMs: z.number().int().min(1000).max(86_400_000).default(3_600_000),\n}).refine(value => value.maxRetryDelayMs >= value.retryDelayMs, {\n message: reachM('reach_queueRetryInvalid'),\n})\n\n/**\n * 由ReachQueueConfigSchema解析后的配置类型,默认值已经填充;与输入类型区分,秘密字段不写日志。\n *\n * @example\n * ```ts\n * import { ReachQueueConfigSchema, type ReachQueueConfig } from '@h-ai/reach'\n * const config: ReachQueueConfig = ReachQueueConfigSchema.parse({ maxAttempts: 5, retryDelayMs: 60000, maxRetryDelayMs: 3600000 })\n * ```\n */\nexport type ReachQueueConfig = z.infer<typeof ReachQueueConfigSchema>\n\n/**\n * 仅分页列出dead/unknown元数据,限制limit/offset;不返回正文/收件人,不替代服务端运维授权。\n *\n * @example\n * ```ts\n * import { ReachQueueListSchema } from '@h-ai/reach'\n * const config = ReachQueueListSchema.parse({ status: 'dead', limit: 10, offset: 0 })\n * ```\n */\nexport const ReachQueueListSchema = z.object({\n status: z.enum(['dead', 'unknown']),\n limit: z.number().int().min(1).max(100).default(50),\n offset: z.number().int().min(0).max(1_000_000).default(0),\n})\n\n/**\n * 恢复dead所需的最近updatedAt条件输入,阻止过时/并发恢复;unknown禁止此操作。\n *\n * @example\n * ```ts\n * import { ReachDeadRequeueSchema } from '@h-ai/reach'\n * const config = ReachDeadRequeueSchema.parse({ expectedUpdatedAt: 1730000000000 })\n * ```\n */\nexport const ReachDeadRequeueSchema = z.object({\n expectedUpdatedAt: z.number().int().nonnegative().max(Number.MAX_SAFE_INTEGER - 1),\n})\n\n// ─── DND(免打扰)配置 Schema ───\n\n/**\n * 本机时区的HH:mm免打扰配置,支持跨午夜;start=end表示不拦截。\n * discard返回DND_BLOCKED;delay要求数据库队列就绪且pending写入成功才返回deferred回执。\n *\n * @example\n * ```ts\n * import { DndConfigSchema } from '@h-ai/reach'\n * const config = DndConfigSchema.parse({ enabled: true, strategy: 'delay', start: '22:00', end: '08:00' })\n * ```\n */\nexport const DndConfigSchema = z.object({\n /** 是否启用免打扰(默认 false) */\n enabled: z.boolean().default(false),\n /** discard 丢弃;delay 要求数据库队列就绪并确认入队(默认 discard)。 */\n strategy: z.enum(['discard', 'delay']).default('discard'),\n /** 免打扰开始时间(HH:mm 格式) */\n start: z.string().regex(/^([01]\\d|2[0-3]):[0-5]\\d$/, reachM('reach_config_dndTimeInvalid')).default('00:00'),\n /** 免打扰结束时间(HH:mm 格式) */\n end: z.string().regex(/^([01]\\d|2[0-3]):[0-5]\\d$/, reachM('reach_config_dndTimeInvalid')).default('00:00'),\n})\n\n/**\n * 由DndConfigSchema解析后的配置类型,默认值已经填充;与输入类型区分,秘密字段不写日志。\n *\n * @example\n * ```ts\n * import { DndConfigSchema, type DndConfig } from '@h-ai/reach'\n * const config: DndConfig = DndConfigSchema.parse({ enabled: true, strategy: 'delay', start: '22:00', end: '08:00' })\n * ```\n */\nexport type DndConfig = z.infer<typeof DndConfigSchema>\n\n// ─── 模板配置 Schema ───\n\n/**\n * 绑定Provider名称的持久模板配置,{key}仅做文本替换;需要先初始化数据库,不提供HTML消毒。\n *\n * @example\n * ```ts\n * import { TemplateConfigSchema } from '@h-ai/reach'\n * const config = TemplateConfigSchema.parse({ name: 'welcome', provider: 'webhook', subject: 'Welcome {userName}', body: 'Hello {userName}' })\n * ```\n */\nexport const TemplateConfigSchema = z.object({\n /** 模板名称 */\n name: z.string().min(1, reachM('reach_config_templateNameRequired')),\n /** 绑定的 Provider 名称 */\n provider: z.string().min(1, reachM('reach_config_templateProviderRequired')),\n /** 邮件主题模板 */\n subject: z.string().optional(),\n /** 正文模板 */\n body: z.string().min(1, reachM('reach_config_templateBodyRequired')),\n})\n\n/**\n * 由TemplateConfigSchema解析后的配置类型,默认值已经填充;与输入类型区分,秘密字段不写日志。\n *\n * @example\n * ```ts\n * import { TemplateConfigSchema, type TemplateConfig } from '@h-ai/reach'\n * const config: TemplateConfig = TemplateConfigSchema.parse({ name: 'welcome', provider: 'webhook', subject: 'Welcome {userName}', body: 'Hello {userName}' })\n * ```\n */\nexport type TemplateConfig = z.infer<typeof TemplateConfigSchema>\n\n// ─── 模块配置 Schema ───\n\n/**\n * 模块配置Schema,解析Provider/模板/DND及持久重试默认值;Schema校验不证明外部服务可用或接收端幂等。\n *\n * @example\n * ```ts\n * import { ReachConfigSchema } from '@h-ai/reach'\n * const config = ReachConfigSchema.parse({ providers: [{ name: 'webhook', type: 'api', url: 'https://notify.example.com/events', idempotent: false }], templates: [{ name: 'welcome', provider: 'webhook', subject: 'Welcome {userName}', body: 'Hello {userName}' }], dnd: { enabled: true, strategy: 'delay', start: '22:00', end: '08:00' } })\n * ```\n */\nexport const ReachConfigSchema = z.object({\n /** Provider 配置列表 */\n providers: z.array(ProviderConfigSchema).min(1, reachM('reach_config_providersRequired')),\n /** 模板配置(通过配置文件注册) */\n templates: z.array(TemplateConfigSchema).optional(),\n /** 免打扰配置 */\n dnd: DndConfigSchema.optional(),\n /** 可靠队列重试策略;每条消息入队时持久保存。 */\n queue: ReachQueueConfigSchema.prefault({}),\n})\n\n/**\n * 由ReachConfigSchema解析后的配置类型,默认值已经填充;与输入类型区分,秘密字段不写日志。\n *\n * @example\n * ```ts\n * import { ReachConfigSchema, type ReachConfig } from '@h-ai/reach'\n * const config: ReachConfig = ReachConfigSchema.parse({ providers: [{ name: 'webhook', type: 'api', url: 'https://notify.example.com/events', idempotent: false }], templates: [{ name: 'welcome', provider: 'webhook', subject: 'Welcome {userName}', body: 'Hello {userName}' }], dnd: { enabled: true, strategy: 'delay', start: '22:00', end: '08:00' } })\n * ```\n */\nexport type ReachConfig = z.infer<typeof ReachConfigSchema>\n\n/**\n * reach.init接收的配置输入,queue默认值可省略;生产选真实Provider、共享数据库,不能用console假定通知完成。\n *\n * @example\n * ```ts\n * import { ReachConfigSchema, type ReachConfigInput } from '@h-ai/reach'\n * const input: ReachConfigInput = { providers: [{ name: 'webhook', type: 'api', url: 'https://notify.example.com/events', idempotent: false }], templates: [{ name: 'welcome', provider: 'webhook', subject: 'Welcome {userName}', body: 'Hello {userName}' }], dnd: { enabled: true, strategy: 'delay', start: '22:00', end: '08:00' } }\n * const parsed = ReachConfigSchema.parse(input)\n * ```\n */\nexport type ReachConfigInput = z.input<typeof ReachConfigSchema>\n","/**\n * @h-ai/reach — 类型定义\n *\n * 本文件定义触达模块的核心接口和类型。\n * @module reach-types\n */\n\nimport type { ErrorInfo, HaiResult } from '@h-ai/core'\nimport type { ReachConfig, ReachConfigInput } from './reach-config.js'\nimport { core } from '@h-ai/core'\n\n// ─── 错误定义(照 @h-ai/core 范式) ───\n\nconst ReachErrorInfo = {\n SEND_FAILED: '001:500',\n TEMPLATE_NOT_FOUND: '002:404',\n TEMPLATE_RENDER_FAILED: '003:500',\n INVALID_RECIPIENT: '004:400',\n PROVIDER_NOT_FOUND: '005:500',\n DND_BLOCKED: '006:403',\n NOT_INITIALIZED: '010:500',\n UNSUPPORTED_TYPE: '011:400',\n CONFIG_ERROR: '012:500',\n DELIVERY_REJECTED: '020:422',\n MESSAGE_EXPIRED: '021:410',\n} as const satisfies ErrorInfo\n\n/**\n * Reach标准错误集合;DND拒绝、发送失败与配置错误均通过HaiResult判断,不把失败当已入队。\n *\n * @example\n * ```ts\n * import { reach, HaiReachError } from '@h-ai/reach'\n * const result = await reach.send({ provider: 'webhook', to: 'user@example.com', body: 'hello' })\n * if (!result.success && result.error.code === HaiReachError.NOT_INITIALIZED.code) {\n * // 先执行依赖初始化,不能把未初始化失败当作触达成功。\n * const message = result.error.message\n * }\n * ```\n */\nexport const HaiReachError = core.error.buildHaiErrorsDef('reach', ReachErrorInfo)\n\n// ─── 渠道类型 ───\n\n/**\n * 渠道语义标签email/sms/api;消息路由实际使用配置中的Provider名称。\n *\n * @example\n * ```ts\n * import type { ReachChannel } from '@h-ai/reach'\n * const channel: ReachChannel = 'api'\n * ```\n */\nexport type ReachChannel = 'email' | 'sms' | 'api'\n\n// ─── 消息与模板 ───\n\n/**\n * 直接发送必须指定Provider名和内容;模板发送可由持久模板推导路由,vars做文本替换。\n * 队列幂等键固定为持久queueId;直接调用的业务重试由应用提供稳定idempotencyKey并核验接收端契约。\n *\n * @example\n * ```ts\n * import type { ReachMessage } from '@h-ai/reach'\n * const message: ReachMessage = { provider: 'webhook', to: 'user@example.com', template: 'welcome', vars: { userName: 'Alice' } }\n * ```\n */\nexport interface ReachMessage {\n /** 目标 Provider 名称;模板发送可省略,直接发送必须提供。 */\n provider?: string\n /** 接收方(邮箱地址或手机号) */\n to: string\n /** 邮件主题(直接发送时使用) */\n subject?: string\n /** 消息正文(直接发送时使用) */\n body?: string\n /** 模板名称(使用模板发送时指定) */\n template?: string\n /** 模板变量(使用模板发送时传入) */\n vars?: Record<string, string>\n /** Provider 扩展参数(如短信 Provider 的模板编码、签名等) */\n extra?: Record<string, unknown>\n /** API 接收端幂等键;直接发送可指定,队列投递固定使用持久化 queueId。 */\n idempotencyKey?: string\n /** 凭据通知只在内存中投递;正文、变量、收件人不持久化,不进入延迟或重投队列。 */\n sensitive?: boolean\n /** sensitive=true 时必填的凭据截止时间(Unix 毫秒),不延长业务凭据有效期。 */\n expiresAt?: number\n /** 遇到免打扰时交给 Reach 队列,或返回 DND_BLOCKED 让已有业务 outbox 延后;默认 queue。 */\n deferPolicy?: 'queue' | 'reject'\n}\n\n/**\n * 发送或可靠入队结果;deferred=true只证明持久pending写入成功,queueId用于后续查询,不代表已送达。\n *\n * @example\n * ```ts\n * import { reach, type SendResult } from '@h-ai/reach'\n * const { reldb } = await import('@h-ai/reldb')\n * const connected = await reldb.init({ type: 'postgresql', host: 'db', port: 5432, database: 'hai', user: 'postgres', password: process.env.HAI_DB_PASSWORD })\n * if (!connected.success) throw new Error(connected.error.message)\n * try {\n * const initialized = await reach.init({ providers: [{ name: 'webhook', type: 'api', url: 'https://notify.example.com/events', idempotent: false }], templates: [{ name: 'welcome', provider: 'webhook', subject: 'Welcome {userName}', body: 'Hello {userName}' }], dnd: { enabled: true, strategy: 'delay', start: '22:00', end: '08:00' } })\n * if (!initialized.success) throw new Error(initialized.error.message)\n * try {\n * const result = await reach.send({ provider: 'webhook', to: 'user@example.com', body: 'hello' })\n * if (!result.success) throw new Error(result.error.message)\n * const outcome: SendResult = result.data\n * if (outcome.deferred) {\n * const queued = await reach.queue.get(outcome.queueId)\n * if (!queued.success) throw new Error(queued.error.message)\n * }\n * }\n * finally { await reach.close() }\n * }\n * finally {\n * const closed = await reldb.close()\n * if (!closed.success) throw new Error(closed.error.message)\n * }\n * ```\n */\nexport type SendResult = {\n /** 无载荷凭据回执,可通过 queue.get 查询;不代表进入延迟队列。 */\n receiptId?: string\n /** 是否已完成 Provider 发送或可靠入队;具体状态同时检查 deferred。 */\n success: boolean\n /** Provider 返回的消息 ID(如有) */\n messageId?: string\n} & ({\n /** 已可靠入队,尚未投递。 */\n deferred: true\n /** 持久化回执 UUID,对应 hai_reach_send_log.queue_id。 */\n queueId: string\n} | {\n /** Provider 即时发送结果,不含入队回执。 */\n deferred?: false\n queueId?: never\n})\n\n/**\n * 持久队列状态;sending已开始外部效果,非幂等失败/失租约进unknown禁止自动重发;dead停止自动处理但可条件恢复。\n *\n * @example\n * ```ts\n * import type { ReachQueueStatus } from '@h-ai/reach'\n * const status: ReachQueueStatus = 'unknown'\n * ```\n */\nexport type ReachQueueStatus = 'pending' | 'processing' | 'sending' | 'unknown' | 'sent' | 'discarded' | 'dead'\n\n/**\n * 有界失败分类,不保存Provider响应正文、凭据或收件人;用于修复根因和处置死信。\n *\n * @example\n * ```ts\n * import type { ReachQueueFailure } from '@h-ai/reach'\n * const failure: ReachQueueFailure = 'provider_unavailable'\n * ```\n */\nexport type ReachQueueFailure = 'provider_unavailable' | 'invalid_message' | 'permanent_rejection' | 'transient_failure' | 'attempts_exhausted'\n\n/**\n * 不含消息正文/收件人的队列运维快照;时间为安全整数Unix毫秒,计数/策略来自持久消息。\n *\n * @example\n * ```ts\n * import { reach, type ReachQueueEntry } from '@h-ai/reach'\n * const { reldb } = await import('@h-ai/reldb')\n * const connected = await reldb.init({ type: 'postgresql', host: 'db', port: 5432, database: 'hai', user: 'postgres', password: process.env.HAI_DB_PASSWORD })\n * if (!connected.success) throw new Error(connected.error.message)\n * try {\n * const initialized = await reach.init({ providers: [{ name: 'webhook', type: 'api', url: 'https://notify.example.com/events', idempotent: false }], templates: [{ name: 'welcome', provider: 'webhook', subject: 'Welcome {userName}', body: 'Hello {userName}' }], dnd: { enabled: true, strategy: 'delay', start: '22:00', end: '08:00' } })\n * if (!initialized.success) throw new Error(initialized.error.message)\n * try {\n * const listed = await reach.queue.list({ status: 'dead', limit: 10 })\n * if (!listed.success) throw new Error(listed.error.message)\n * const entry: ReachQueueEntry | undefined = listed.data[0]\n * if (entry) { const attempts = entry.attemptCount }\n * }\n * finally { await reach.close() }\n * }\n * finally {\n * const closed = await reldb.close()\n * if (!closed.success) throw new Error(closed.error.message)\n * }\n * ```\n */\nexport interface ReachQueueEntry {\n /** 持久化入队回执。 */\n queueId: string\n /** Provider 配置名称。 */\n provider: string\n /** 当前处理状态。 */\n status: ReachQueueStatus\n /** 最近更新时间(Unix 毫秒)。 */\n updatedAt?: number\n /** 外部 Provider 确认的消息编号。 */\n messageId?: string\n /** 已领取次数;节点崩溃后的重新领取也计数。 */\n attemptCount: number\n /** 此条消息持久保存的自动领取上限。 */\n maxAttempts: number\n /** 下次允许自动领取的 Unix 毫秒时间;终态为 0。 */\n nextAttemptAt: number\n /** 最近失败分类,便于可信运维流程排查与修复。 */\n lastFailure?: ReachQueueFailure\n}\n\n/**\n * 仅列出需人工处理的dead/unknown;分页有界,不提供凭据或替代运维权限。\n *\n * @example\n * ```ts\n * import { reach, type ReachQueueListOptions } from '@h-ai/reach'\n * const { reldb } = await import('@h-ai/reldb')\n * const connected = await reldb.init({ type: 'postgresql', host: 'db', port: 5432, database: 'hai', user: 'postgres', password: process.env.HAI_DB_PASSWORD })\n * if (!connected.success) throw new Error(connected.error.message)\n * try {\n * const initialized = await reach.init({ providers: [{ name: 'webhook', type: 'api', url: 'https://notify.example.com/events', idempotent: false }], templates: [{ name: 'welcome', provider: 'webhook', subject: 'Welcome {userName}', body: 'Hello {userName}' }], dnd: { enabled: true, strategy: 'delay', start: '22:00', end: '08:00' } })\n * if (!initialized.success) throw new Error(initialized.error.message)\n * try {\n * const options: ReachQueueListOptions = { status: 'unknown', limit: 10, offset: 0 }\n * const listed = await reach.queue.list(options)\n * if (!listed.success) throw new Error(listed.error.message)\n * }\n * finally { await reach.close() }\n * }\n * finally {\n * const closed = await reldb.close()\n * if (!closed.success) throw new Error(closed.error.message)\n * }\n * ```\n */\nexport interface ReachQueueListOptions {\n /** 需要人工处理的状态。 */\n status: 'dead' | 'unknown'\n /** 每页 1–100 条,默认 50。 */\n limit?: number\n /** 跳过条数,默认 0,上限 1000000。 */\n offset?: number\n}\n\n/**\n * dead条件恢复输入;expectedUpdatedAt必须来自最近快照,不能用本机当前时间冒充版本。\n * 先修根因并确认旧执行与接收端幂等边界,unknown不能重排。\n *\n * @example\n * ```ts\n * import { reach, type ReachDeadRequeue } from '@h-ai/reach'\n * const { reldb } = await import('@h-ai/reldb')\n * const connected = await reldb.init({ type: 'postgresql', host: 'db', port: 5432, database: 'hai', user: 'postgres', password: process.env.HAI_DB_PASSWORD })\n * if (!connected.success) throw new Error(connected.error.message)\n * try {\n * const initialized = await reach.init({ providers: [{ name: 'webhook', type: 'api', url: 'https://notify.example.com/events', idempotent: false }], templates: [{ name: 'welcome', provider: 'webhook', subject: 'Welcome {userName}', body: 'Hello {userName}' }], dnd: { enabled: true, strategy: 'delay', start: '22:00', end: '08:00' } })\n * if (!initialized.success) throw new Error(initialized.error.message)\n * try {\n * const listed = await reach.queue.list({ status: 'dead', limit: 10 })\n * if (!listed.success) throw new Error(listed.error.message)\n * const entry = listed.data[0]\n * if (entry && entry.updatedAt !== undefined) {\n * const options: ReachDeadRequeue = { expectedUpdatedAt: entry.updatedAt }\n * // 此调用仅在受信运维流程核验完成后执行。\n * const restored = await reach.queue.requeueDead(entry.queueId, options)\n * if (!restored.success) throw new Error(restored.error.message)\n * }\n * }\n * finally { await reach.close() }\n * }\n * finally {\n * const closed = await reldb.close()\n * if (!closed.success) throw new Error(closed.error.message)\n * }\n * ```\n */\nexport interface ReachDeadRequeue {\n /** 必须等于最近查询的更新时间,阻止并发及过时请求。 */\n expectedUpdatedAt: number\n}\n\n/**\n * unknown人工裁定,不提供自动重发;先校验运维权限、停止/隔离旧worker并核对Provider实际记录。\n * discarded只终止后续处理,不能撤销已开始的外部请求或已发送消息。\n *\n * @example\n * ```ts\n * import { reach, type ReachUnknownResolution } from '@h-ai/reach'\n * const { reldb } = await import('@h-ai/reldb')\n * const connected = await reldb.init({ type: 'postgresql', host: 'db', port: 5432, database: 'hai', user: 'postgres', password: process.env.HAI_DB_PASSWORD })\n * if (!connected.success) throw new Error(connected.error.message)\n * try {\n * const initialized = await reach.init({ providers: [{ name: 'webhook', type: 'api', url: 'https://notify.example.com/events', idempotent: false }], templates: [{ name: 'welcome', provider: 'webhook', subject: 'Welcome {userName}', body: 'Hello {userName}' }], dnd: { enabled: true, strategy: 'delay', start: '22:00', end: '08:00' } })\n * if (!initialized.success) throw new Error(initialized.error.message)\n * try {\n * const listed = await reach.queue.list({ status: 'unknown', limit: 10 })\n * if (!listed.success) throw new Error(listed.error.message)\n * const entry = listed.data[0]\n * if (entry) {\n * // 可信运维前置:旧worker已隔离且外部投递记录已核对。\n * const resolution: ReachUnknownResolution = { status: 'discarded' }\n * const resolved = await reach.queue.resolveUnknown(entry.queueId, resolution)\n * if (!resolved.success) throw new Error(resolved.error.message)\n * }\n * }\n * finally { await reach.close() }\n * }\n * finally {\n * const closed = await reldb.close()\n * if (!closed.success) throw new Error(closed.error.message)\n * }\n * ```\n */\nexport interface ReachUnknownResolution {\n /** 经外部核对确认已投递,或终止后续处理。discarded 不撤销已经开始的外部请求。 */\n status: 'sent' | 'discarded'\n /** 确认已投递时,可记录 Provider 消息编号,最多 255 个字符。 */\n messageId?: string\n}\n\n/**\n * 受信服务端队列运维入口,需应用先授权;只查询安全元数据,恢复/裁定均由数据库条件提交。\n *\n * @example\n * ```ts\n * import { reach } from '@h-ai/reach'\n * const { reldb } = await import('@h-ai/reldb')\n * const connected = await reldb.init({ type: 'postgresql', host: 'db', port: 5432, database: 'hai', user: 'postgres', password: process.env.HAI_DB_PASSWORD })\n * if (!connected.success) throw new Error(connected.error.message)\n * try {\n * const initialized = await reach.init({ providers: [{ name: 'webhook', type: 'api', url: 'https://notify.example.com/events', idempotent: false }], templates: [{ name: 'welcome', provider: 'webhook', subject: 'Welcome {userName}', body: 'Hello {userName}' }], dnd: { enabled: true, strategy: 'delay', start: '22:00', end: '08:00' } })\n * if (!initialized.success) throw new Error(initialized.error.message)\n * try {\n * const listed = await reach.queue.list({ status: 'unknown', limit: 10 })\n * if (!listed.success) throw new Error(listed.error.message)\n * const count = listed.data.length\n * }\n * finally { await reach.close() }\n * }\n * finally {\n * const closed = await reldb.close()\n * if (!closed.success) throw new Error(closed.error.message)\n * }\n * ```\n */\nexport interface ReachQueueOperations {\n /**\n * 按回执查询队列状态。\n * @param queueId - 持久UUID回执,不代替运维权限。\n * @returns HaiResult;先检查success,条件提交失败不能伪装成功。\n *\n * @example\n * ```ts\n * import { reach } from '@h-ai/reach'\n * const { reldb } = await import('@h-ai/reldb')\n * const connected = await reldb.init({ type: 'postgresql', host: 'db', port: 5432, database: 'hai', user: 'postgres', password: process.env.HAI_DB_PASSWORD })\n * if (!connected.success) throw new Error(connected.error.message)\n * try {\n * const initialized = await reach.init({ providers: [{ name: 'webhook', type: 'api', url: 'https://notify.example.com/events', idempotent: false }], templates: [{ name: 'welcome', provider: 'webhook', subject: 'Welcome {userName}', body: 'Hello {userName}' }], dnd: { enabled: true, strategy: 'delay', start: '22:00', end: '08:00' } })\n * if (!initialized.success) throw new Error(initialized.error.message)\n * try {\n * const result = await reach.queue.get('00000000-0000-4000-8000-000000000001')\n * if (!result.success) throw new Error(result.error.message)\n * if (result.data) { const status = result.data.status }\n * }\n * finally { await reach.close() }\n * }\n * finally {\n * const closed = await reldb.close()\n * if (!closed.success) throw new Error(closed.error.message)\n * }\n * ```\n */\n get: (queueId: string) => Promise<HaiResult<ReachQueueEntry | null>>\n /**\n * 分页查询死信或未知结果,仅返回运维元数据。\n * @param options - 实际操作输入,按本方法约束校验。\n * @returns HaiResult;先检查success,条件提交失败不能伪装成功。\n *\n * @example\n * ```ts\n * import { reach } from '@h-ai/reach'\n * const { reldb } = await import('@h-ai/reldb')\n * const connected = await reldb.init({ type: 'postgresql', host: 'db', port: 5432, database: 'hai', user: 'postgres', password: process.env.HAI_DB_PASSWORD })\n * if (!connected.success) throw new Error(connected.error.message)\n * try {\n * const initialized = await reach.init({ providers: [{ name: 'webhook', type: 'api', url: 'https://notify.example.com/events', idempotent: false }], templates: [{ name: 'welcome', provider: 'webhook', subject: 'Welcome {userName}', body: 'Hello {userName}' }], dnd: { enabled: true, strategy: 'delay', start: '22:00', end: '08:00' } })\n * if (!initialized.success) throw new Error(initialized.error.message)\n * try {\n * const listed = await reach.queue.list({ status: 'unknown', limit: 10 })\n * if (!listed.success) throw new Error(listed.error.message)\n * const count = listed.data.length\n * }\n * finally { await reach.close() }\n * }\n * finally {\n * const closed = await reldb.close()\n * if (!closed.success) throw new Error(closed.error.message)\n * }\n * ```\n */\n list: (options: ReachQueueListOptions) => Promise<HaiResult<ReachQueueEntry[]>>\n /**\n * 修复根因并确认接收端幂等契约后恢复 dead;重置计数,保留 queueId/策略。unknown 不能重排。\n * @param queueId - 持久UUID回执,不代替运维权限。\n * @param options - 实际操作输入,按本方法约束校验。\n * @returns HaiResult;先检查success,条件提交失败不能伪装成功。\n *\n * @example\n * ```ts\n * import { reach, type ReachDeadRequeue } from '@h-ai/reach'\n * const { reldb } = await import('@h-ai/reldb')\n * const connected = await reldb.init({ type: 'postgresql', host: 'db', port: 5432, database: 'hai', user: 'postgres', password: process.env.HAI_DB_PASSWORD })\n * if (!connected.success) throw new Error(connected.error.message)\n * try {\n * const initialized = await reach.init({ providers: [{ name: 'webhook', type: 'api', url: 'https://notify.example.com/events', idempotent: false }], templates: [{ name: 'welcome', provider: 'webhook', subject: 'Welcome {userName}', body: 'Hello {userName}' }], dnd: { enabled: true, strategy: 'delay', start: '22:00', end: '08:00' } })\n * if (!initialized.success) throw new Error(initialized.error.message)\n * try {\n * const listed = await reach.queue.list({ status: 'dead', limit: 10 })\n * if (!listed.success) throw new Error(listed.error.message)\n * const entry = listed.data[0]\n * if (entry && entry.updatedAt !== undefined) {\n * const options: ReachDeadRequeue = { expectedUpdatedAt: entry.updatedAt }\n * // 此调用仅在受信运维流程核验完成后执行。\n * const restored = await reach.queue.requeueDead(entry.queueId, options)\n * if (!restored.success) throw new Error(restored.error.message)\n * }\n * }\n * finally { await reach.close() }\n * }\n * finally {\n * const closed = await reldb.close()\n * if (!closed.success) throw new Error(closed.error.message)\n * }\n * ```\n */\n requeueDead: (queueId: string, options: ReachDeadRequeue) => Promise<HaiResult<void>>\n /**\n * 停止/隔离旧 worker 并核对外部投递记录后,裁定 unknown;不重新发送。\n * @param queueId - 持久UUID回执,不代替运维权限。\n * @param resolution - 实际操作输入,按本方法约束校验。\n * @returns HaiResult;先检查success,条件提交失败不能伪装成功。\n *\n * @example\n * ```ts\n * import { reach, type ReachUnknownResolution } from '@h-ai/reach'\n * const { reldb } = await import('@h-ai/reldb')\n * const connected = await reldb.init({ type: 'postgresql', host: 'db', port: 5432, database: 'hai', user: 'postgres', password: process.env.HAI_DB_PASSWORD })\n * if (!connected.success) throw new Error(connected.error.message)\n * try {\n * const initialized = await reach.init({ providers: [{ name: 'webhook', type: 'api', url: 'https://notify.example.com/events', idempotent: false }], templates: [{ name: 'welcome', provider: 'webhook', subject: 'Welcome {userName}', body: 'Hello {userName}' }], dnd: { enabled: true, strategy: 'delay', start: '22:00', end: '08:00' } })\n * if (!initialized.success) throw new Error(initialized.error.message)\n * try {\n * const listed = await reach.queue.list({ status: 'unknown', limit: 10 })\n * if (!listed.success) throw new Error(listed.error.message)\n * const entry = listed.data[0]\n * if (entry) {\n * // 可信运维前置:旧worker已隔离且外部投递记录已核对。\n * const resolution: ReachUnknownResolution = { status: 'discarded' }\n * const resolved = await reach.queue.resolveUnknown(entry.queueId, resolution)\n * if (!resolved.success) throw new Error(resolved.error.message)\n * }\n * }\n * finally { await reach.close() }\n * }\n * finally {\n * const closed = await reldb.close()\n * if (!closed.success) throw new Error(closed.error.message)\n * }\n * ```\n */\n resolveUnknown: (queueId: string, resolution: ReachUnknownResolution) => Promise<HaiResult<void>>\n}\n\n/**\n * 持久文本模板,provider是配置名称,subject/body支持{key}替换;未提供的占位符保留原文。\n * 不提供HTML转义或消毒,渲染结果的最终安全上下文由使用方处理。\n *\n * @example\n * ```ts\n * import { reach, type ReachTemplate } from '@h-ai/reach'\n * const { reldb } = await import('@h-ai/reldb')\n * const connected = await reldb.init({ type: 'postgresql', host: 'db', port: 5432, database: 'hai', user: 'postgres', password: process.env.HAI_DB_PASSWORD })\n * if (!connected.success) throw new Error(connected.error.message)\n * try {\n * const initialized = await reach.init({ providers: [{ name: 'webhook', type: 'api', url: 'https://notify.example.com/events', idempotent: false }], templates: [{ name: 'welcome', provider: 'webhook', subject: 'Welcome {userName}', body: 'Hello {userName}' }], dnd: { enabled: true, strategy: 'delay', start: '22:00', end: '08:00' } })\n * if (!initialized.success) throw new Error(initialized.error.message)\n * try {\n * const template: ReachTemplate = { name: 'welcome', provider: 'webhook', subject: 'Welcome {userName}', body: 'Hello {userName}' }\n * const saved = await reach.template.save(template)\n * if (!saved.success) throw new Error(saved.error.message)\n * }\n * finally { await reach.close() }\n * }\n * finally {\n * const closed = await reldb.close()\n * if (!closed.success) throw new Error(closed.error.message)\n * }\n * ```\n */\nexport interface ReachTemplate {\n /** 模板名称(唯一标识) */\n name: string\n /** 绑定的 Provider 名称 */\n provider: string\n /** 邮件主题模板(支持 `{var}` 占位符,仅 email) */\n subject?: string\n /** 消息正文模板(支持 `{var}` 占位符) */\n body: string\n}\n\n/**\n * 渲染后的主题/正文;文本替换不等于HTML安全,可选subject按原模板保留。\n *\n * @example\n * ```ts\n * import { reach, type RenderedTemplate } from '@h-ai/reach'\n * const { reldb } = await import('@h-ai/reldb')\n * const connected = await reldb.init({ type: 'postgresql', host: 'db', port: 5432, database: 'hai', user: 'postgres', password: process.env.HAI_DB_PASSWORD })\n * if (!connected.success) throw new Error(connected.error.message)\n * try {\n * const initialized = await reach.init({ providers: [{ name: 'webhook', type: 'api', url: 'https://notify.example.com/events', idempotent: false }], templates: [{ name: 'welcome', provider: 'webhook', subject: 'Welcome {userName}', body: 'Hello {userName}' }], dnd: { enabled: true, strategy: 'delay', start: '22:00', end: '08:00' } })\n * if (!initialized.success) throw new Error(initialized.error.message)\n * try {\n * const rendered = await reach.template.render('welcome', { userName: 'Alice' })\n * if (!rendered.success) throw new Error(rendered.error.message)\n * const value: RenderedTemplate = rendered.data\n * const body = value.body\n * }\n * finally { await reach.close() }\n * }\n * finally {\n * const closed = await reldb.close()\n * if (!closed.success) throw new Error(closed.error.message)\n * }\n * ```\n */\nexport interface RenderedTemplate {\n /** 渲染后的主题 */\n subject?: string\n /** 渲染后的正文 */\n body: string\n}\n\n// ─── 操作接口 ───\n\n/**\n * 统一发送操作;失败HaiResult不表示已发送,成功结果仍检查内部success与deferred。\n * 即时外部发送成功不因可选日志失败而转失败,防调用方重复发送;延迟入队必须确认持久写入。\n *\n * @example\n * ```ts\n * import { reach } from '@h-ai/reach'\n * const { reldb } = await import('@h-ai/reldb')\n * const connected = await reldb.init({ type: 'postgresql', host: 'db', port: 5432, database: 'hai', user: 'postgres', password: process.env.HAI_DB_PASSWORD })\n * if (!connected.success) throw new Error(connected.error.message)\n * try {\n * const initialized = await reach.init({ providers: [{ name: 'webhook', type: 'api', url: 'https://notify.example.com/events', idempotent: false }], templates: [{ name: 'welcome', provider: 'webhook', subject: 'Welcome {userName}', body: 'Hello {userName}' }], dnd: { enabled: true, strategy: 'delay', start: '22:00', end: '08:00' } })\n * if (!initialized.success) throw new Error(initialized.error.message)\n * try {\n * const result = await reach.send({ provider: 'webhook', to: 'user@example.com', template: 'welcome', vars: { userName: 'Alice' } })\n * if (!result.success) throw new Error(result.error.message)\n * if (!result.data.success) throw new Error('Delivery was not accepted')\n * if (result.data.deferred) {\n * const queueId = result.data.queueId\n * // 持久入队成功,尚未证明接收端已完成外部投递。\n * }\n * else { const providerMessageId = result.data.messageId }\n * }\n * finally { await reach.close() }\n * }\n * finally {\n * const closed = await reldb.close()\n * if (!closed.success) throw new Error(closed.error.message)\n * }\n * ```\n */\nexport interface SendOperations {\n /**\n * 发送触达消息\n *\n * @param message - 触达消息\n * @returns 发送结果\n *\n * @example\n * ```ts\n * import { reach } from '@h-ai/reach'\n * const { reldb } = await import('@h-ai/reldb')\n * const connected = await reldb.init({ type: 'postgresql', host: 'db', port: 5432, database: 'hai', user: 'postgres', password: process.env.HAI_DB_PASSWORD })\n * if (!connected.success) throw new Error(connected.error.message)\n * try {\n * const initialized = await reach.init({ providers: [{ name: 'webhook', type: 'api', url: 'https://notify.example.com/events', idempotent: false }], templates: [{ name: 'welcome', provider: 'webhook', subject: 'Welcome {userName}', body: 'Hello {userName}' }], dnd: { enabled: true, strategy: 'delay', start: '22:00', end: '08:00' } })\n * if (!initialized.success) throw new Error(initialized.error.message)\n * try {\n * const result = await reach.send({ provider: 'webhook', to: 'user@example.com', template: 'welcome', vars: { userName: 'Alice' } })\n * if (!result.success) throw new Error(result.error.message)\n * if (!result.data.success) throw new Error('Delivery was not accepted')\n * if (result.data.deferred) {\n * const queueId = result.data.queueId\n * // 持久入队成功,尚未证明接收端已完成外部投递。\n * }\n * else { const providerMessageId = result.data.messageId }\n * }\n * finally { await reach.close() }\n * }\n * finally {\n * const closed = await reldb.close()\n * if (!closed.success) throw new Error(closed.error.message)\n * }\n * ```\n */\n send: (message: ReachMessage) => Promise<HaiResult<SendResult>>\n}\n\n// ─── 模板注册表接口 ───\n\n/**\n * 数据库模板操作;无数据库时不回退进程模板缓存,resolve失败、list为空,保存/删除返回失败。\n * saveBatch并发逐条upsert,失败时不承诺其它项回滚;需要原子业务批次时由受控事务流程实现。\n *\n * @example\n * ```ts\n * import { reach } from '@h-ai/reach'\n * const { reldb } = await import('@h-ai/reldb')\n * const connected = await reldb.init({ type: 'postgresql', host: 'db', port: 5432, database: 'hai', user: 'postgres', password: process.env.HAI_DB_PASSWORD })\n * if (!connected.success) throw new Error(connected.error.message)\n * try {\n * const initialized = await reach.init({ providers: [{ name: 'webhook', type: 'api', url: 'https://notify.example.com/events', idempotent: false }], templates: [{ name: 'welcome', provider: 'webhook', subject: 'Welcome {userName}', body: 'Hello {userName}' }], dnd: { enabled: true, strategy: 'delay', start: '22:00', end: '08:00' } })\n * if (!initialized.success) throw new Error(initialized.error.message)\n * try {\n * const saved = await reach.template.save({ name: 'welcome', provider: 'webhook', subject: 'Welcome {userName}', body: 'Hello {userName}' })\n * if (!saved.success) throw new Error(saved.error.message)\n * const rendered = await reach.template.render('welcome', { userName: 'Alice' })\n * if (!rendered.success) throw new Error(rendered.error.message)\n * }\n * finally { await reach.close() }\n * }\n * finally {\n * const closed = await reldb.close()\n * if (!closed.success) throw new Error(closed.error.message)\n * }\n * ```\n */\nexport interface ReachTemplateRegistry {\n /**\n * 按名称解析模板\n * @param name - 实际操作输入,按本方法约束校验。\n * @returns HaiResult;先检查success,条件提交失败不能伪装成功。\n *\n * @example\n * ```ts\n * import { reach } from '@h-ai/reach'\n * const { reldb } = await import('@h-ai/reldb')\n * const connected = await reldb.init({ type: 'postgresql', host: 'db', port: 5432, database: 'hai', user: 'postgres', password: process.env.HAI_DB_PASSWORD })\n * if (!connected.success) throw new Error(connected.error.message)\n * try {\n * const initialized = await reach.init({ providers: [{ name: 'webhook', type: 'api', url: 'https://notify.example.com/events', idempotent: false }], templates: [{ name: 'welcome', provider: 'webhook', subject: 'Welcome {userName}', body: 'Hello {userName}' }], dnd: { enabled: true, strategy: 'delay', start: '22:00', end: '08:00' } })\n * if (!initialized.success) throw new Error(initialized.error.message)\n * try {\n * const result = await reach.template.resolve('welcome')\n * if (!result.success) throw new Error(result.error.message)\n * const providerName = result.data.provider\n * }\n * finally { await reach.close() }\n * }\n * finally {\n * const closed = await reldb.close()\n * if (!closed.success) throw new Error(closed.error.message)\n * }\n * ```\n */\n resolve: (name: string) => Promise<HaiResult<ReachTemplate>>\n /**\n * 保存模板到数据库(upsert)\n * @param template - 实际操作输入,按本方法约束校验。\n * @returns HaiResult;先检查success,条件提交失败不能伪装成功。\n *\n * @example\n * ```ts\n * import { reach, type ReachTemplate } from '@h-ai/reach'\n * const { reldb } = await import('@h-ai/reldb')\n * const connected = await reldb.init({ type: 'postgresql', host: 'db', port: 5432, database: 'hai', user: 'postgres', password: process.env.HAI_DB_PASSWORD })\n * if (!connected.success) throw new Error(connected.error.message)\n * try {\n * const initialized = await reach.init({ providers: [{ name: 'webhook', type: 'api', url: 'https://notify.example.com/events', idempotent: false }], templates: [{ name: 'welcome', provider: 'webhook', subject: 'Welcome {userName}', body: 'Hello {userName}' }], dnd: { enabled: true, strategy: 'delay', start: '22:00', end: '08:00' } })\n * if (!initialized.success) throw new Error(initialized.error.message)\n * try {\n * const template: ReachTemplate = { name: 'welcome', provider: 'webhook', subject: 'Welcome {userName}', body: 'Hello {userName}' }\n * const saved = await reach.template.save(template)\n * if (!saved.success) throw new Error(saved.error.message)\n * }\n * finally { await reach.close() }\n * }\n * finally {\n * const closed = await reldb.close()\n * if (!closed.success) throw new Error(closed.error.message)\n * }\n * ```\n */\n save: (template: ReachTemplate) => Promise<HaiResult<void>>\n /**\n * 批量保存模板到数据库\n * @param templates - 实际操作输入,按本方法约束校验。\n * @returns HaiResult;先检查success,条件提交失败不能伪装成功。\n *\n * @example\n * ```ts\n * import { reach } from '@h-ai/reach'\n * const { reldb } = await import('@h-ai/reldb')\n * const connected = await reldb.init({ type: 'postgresql', host: 'db', port: 5432, database: 'hai', user: 'postgres', password: process.env.HAI_DB_PASSWORD })\n * if (!connected.success) throw new Error(connected.error.message)\n * try {\n * const initialized = await reach.init({ providers: [{ name: 'webhook', type: 'api', url: 'https://notify.example.com/events', idempotent: false }], templates: [{ name: 'welcome', provider: 'webhook', subject: 'Welcome {userName}', body: 'Hello {userName}' }], dnd: { enabled: true, strategy: 'delay', start: '22:00', end: '08:00' } })\n * if (!initialized.success) throw new Error(initialized.error.message)\n * try {\n * const result = await reach.template.saveBatch([{ name: 'receipt', provider: 'webhook', body: 'Receipt {orderId}' }, { name: 'reminder', provider: 'webhook', body: 'Reminder {orderId}' }])\n * if (!result.success) throw new Error(result.error.message)\n * // 非原子批次;失败时应核对已保存条目,不假定整体回滚。\n * }\n * finally { await reach.close() }\n * }\n * finally {\n * const closed = await reldb.close()\n * if (!closed.success) throw new Error(closed.error.message)\n * }\n * ```\n */\n saveBatch: (templates: ReachTemplate[]) => Promise<HaiResult<void>>\n /**\n * 从数据库删除模板\n * @param name - 实际操作输入,按本方法约束校验。\n * @returns HaiResult;先检查success,条件提交失败不能伪装成功。\n *\n * @example\n * ```ts\n * import { reach } from '@h-ai/reach'\n * const { reldb } = await import('@h-ai/reldb')\n * const connected = await reldb.init({ type: 'postgresql', host: 'db', port: 5432, database: 'hai', user: 'postgres', password: process.env.HAI_DB_PASSWORD })\n * if (!connected.success) throw new Error(connected.error.message)\n * try {\n * const initialized = await reach.init({ providers: [{ name: 'webhook', type: 'api', url: 'https://notify.example.com/events', idempotent: false }], templates: [{ name: 'welcome', provider: 'webhook', subject: 'Welcome {userName}', body: 'Hello {userName}' }], dnd: { enabled: true, strategy: 'delay', start: '22:00', end: '08:00' } })\n * if (!initialized.success) throw new Error(initialized.error.message)\n * try {\n * const result = await reach.template.remove('welcome')\n * if (!result.success) throw new Error(result.error.message)\n * }\n * finally { await reach.close() }\n * }\n * finally {\n * const closed = await reldb.close()\n * if (!closed.success) throw new Error(closed.error.message)\n * }\n * ```\n */\n remove: (name: string) => Promise<HaiResult<void>>\n /**\n * 列出所有模板\n * @returns HaiResult;先检查success,条件提交失败不能伪装成功。\n *\n * @example\n * ```ts\n * import { reach } from '@h-ai/reach'\n * const { reldb } = await import('@h-ai/reldb')\n * const connected = await reldb.init({ type: 'postgresql', host: 'db', port: 5432, database: 'hai', user: 'postgres', password: process.env.HAI_DB_PASSWORD })\n * if (!connected.success) throw new Error(connected.error.message)\n * try {\n * const initialized = await reach.init({ providers: [{ name: 'webhook', type: 'api', url: 'https://notify.example.com/events', idempotent: false }], templates: [{ name: 'welcome', provider: 'webhook', subject: 'Welcome {userName}', body: 'Hello {userName}' }], dnd: { enabled: true, strategy: 'delay', start: '22:00', end: '08:00' } })\n * if (!initialized.success) throw new Error(initialized.error.message)\n * try {\n * const result = await reach.template.list()\n * if (!result.success) throw new Error(result.error.message)\n * const count = result.data.length\n * }\n * finally { await reach.close() }\n * }\n * finally {\n * const closed = await reldb.close()\n * if (!closed.success) throw new Error(closed.error.message)\n * }\n * ```\n */\n list: () => Promise<HaiResult<ReachTemplate[]>>\n /**\n * 渲染模板(从数据库解析后渲染)\n * @param name - 实际操作输入,按本方法约束校验。\n * @param vars - 实际操作输入,按本方法约束校验。\n * @returns HaiResult;先检查success,条件提交失败不能伪装成功。\n *\n * @example\n * ```ts\n * import { reach, type RenderedTemplate } from '@h-ai/reach'\n * const { reldb } = await import('@h-ai/reldb')\n * const connected = await reldb.init({ type: 'postgresql', host: 'db', port: 5432, database: 'hai', user: 'postgres', password: process.env.HAI_DB_PASSWORD })\n * if (!connected.success) throw new Error(connected.error.message)\n * try {\n * const initialized = await reach.init({ providers: [{ name: 'webhook', type: 'api', url: 'https://notify.example.com/events', idempotent: false }], templates: [{ name: 'welcome', provider: 'webhook', subject: 'Welcome {userName}', body: 'Hello {userName}' }], dnd: { enabled: true, strategy: 'delay', start: '22:00', end: '08:00' } })\n * if (!initialized.success) throw new Error(initialized.error.message)\n * try {\n * const rendered = await reach.template.render('welcome', { userName: 'Alice' })\n * if (!rendered.success) throw new Error(rendered.error.message)\n * const value: RenderedTemplate = rendered.data\n * const body = value.body\n * }\n * finally { await reach.close() }\n * }\n * finally {\n * const closed = await reldb.close()\n * if (!closed.success) throw new Error(closed.error.message)\n * }\n * ```\n */\n render: (name: string, vars: Record<string, string>) => Promise<HaiResult<RenderedTemplate>>\n}\n\n// ─── 函数接口 ───\n\n/**\n * Reach生命周期、模板、发送及受信队列运维能力;先初始化数据库,再检查reach.init结果,反序close。\n * API Provider init只建立适配器配置,不探测远端可用性;实际send结果才能判断本次投递。\n *\n * @example\n * ```ts\n * import { reach } from '@h-ai/reach'\n * const { reldb } = await import('@h-ai/reldb')\n * const connected = await reldb.init({ type: 'postgresql', host: 'db', port: 5432, database: 'hai', user: 'postgres', password: process.env.HAI_DB_PASSWORD })\n * if (!connected.success) throw new Error(connected.error.message)\n * try {\n * const initialized = await reach.init({ providers: [{ name: 'webhook', type: 'api', url: 'https://notify.example.com/events', idempotent: false }], templates: [{ name: 'welcome', provider: 'webhook', subject: 'Welcome {userName}', body: 'Hello {userName}' }], dnd: { enabled: true, strategy: 'delay', start: '22:00', end: '08:00' } })\n * if (!initialized.success) throw new Error(initialized.error.message)\n * try {\n * const result = await reach.send({ provider: 'webhook', to: 'user@example.com', template: 'welcome', vars: { userName: 'Alice' } })\n * if (!result.success) throw new Error(result.error.message)\n * if (!result.data.success) throw new Error('Delivery was not accepted')\n * if (result.data.deferred) {\n * const queueId = result.data.queueId\n * // 持久入队成功,尚未证明接收端已完成外部投递。\n * }\n * else { const providerMessageId = result.data.messageId }\n * }\n * finally { await reach.close() }\n * }\n * finally {\n * const closed = await reldb.close()\n * if (!closed.success) throw new Error(closed.error.message)\n * }\n * ```\n */\nexport interface ReachFunctions extends SendOperations {\n /** 持久化队列查询、死信恢复与未知结果人工裁定。 */\n readonly queue: ReachQueueOperations\n /**\n * 初始化触达模块(注册多个 Provider)\n * @param config - 实际操作输入,按本方法约束校验。\n * @returns HaiResult;先检查success,条件提交失败不能伪装成功。\n *\n * @example\n * ```ts\n * import { reach } from '@h-ai/reach'\n * const { reldb } = await import('@h-ai/reldb')\n * const connected = await reldb.init({ type: 'postgresql', host: 'db', port: 5432, database: 'hai', user: 'postgres', password: process.env.HAI_DB_PASSWORD })\n * if (!connected.success) throw new Error(connected.error.message)\n * try {\n * const initialized = await reach.init({ providers: [{ name: 'webhook', type: 'api', url: 'https://notify.example.com/events', idempotent: false }], templates: [{ name: 'welcome', provider: 'webhook', subject: 'Welcome {userName}', body: 'Hello {userName}' }], dnd: { enabled: true, strategy: 'delay', start: '22:00', end: '08:00' } })\n * if (!initialized.success) throw new Error(initialized.error.message)\n * try {\n * const result = await reach.send({ provider: 'webhook', to: 'user@example.com', template: 'welcome', vars: { userName: 'Alice' } })\n * if (!result.success) throw new Error(result.error.message)\n * if (!result.data.success) throw new Error('Delivery was not accepted')\n * if (result.data.deferred) {\n * const queueId = result.data.queueId\n * // 持久入队成功,尚未证明接收端已完成外部投递。\n * }\n * else { const providerMessageId = result.data.messageId }\n * }\n * finally { await reach.close() }\n * }\n * finally {\n * const closed = await reldb.close()\n * if (!closed.success) throw new Error(closed.error.message)\n * }\n * ```\n */\n init: (config: ReachConfigInput) => Promise<HaiResult<void>>\n /** 当前脱敏后的配置快照(未初始化时为 null) */\n readonly config: ReachConfig | null\n /** 是否已初始化 */\n readonly isInitialized: boolean\n /** 模板注册表 */\n readonly template: ReachTemplateRegistry\n /**\n * 关闭连接并释放资源\n *\n * @example\n * ```ts\n * import { reach } from '@h-ai/reach'\n * const { reldb } = await import('@h-ai/reldb')\n * const connected = await reldb.init({ type: 'postgresql', host: 'db', port: 5432, database: 'hai', user: 'postgres', password: process.env.HAI_DB_PASSWORD })\n * if (!connected.success) throw new Error(connected.error.message)\n * try {\n * const initialized = await reach.init({ providers: [{ name: 'webhook', type: 'api', url: 'https://notify.example.com/events', idempotent: false }], templates: [{ name: 'welcome', provider: 'webhook', subject: 'Welcome {userName}', body: 'Hello {userName}' }], dnd: { enabled: true, strategy: 'delay', start: '22:00', end: '08:00' } })\n * if (!initialized.success) throw new Error(initialized.error.message)\n * try {\n * const result = await reach.send({ provider: 'webhook', to: 'user@example.com', template: 'welcome', vars: { userName: 'Alice' } })\n * if (!result.success) throw new Error(result.error.message)\n * if (!result.data.success) throw new Error('Delivery was not accepted')\n * if (result.data.deferred) {\n * const queueId = result.data.queueId\n * // 持久入队成功,尚未证明接收端已完成外部投递。\n * }\n * else { const providerMessageId = result.data.messageId }\n * }\n * finally { await reach.close() }\n * }\n * finally {\n * const closed = await reldb.close()\n * if (!closed.success) throw new Error(closed.error.message)\n * }\n * ```\n */\n close: () => Promise<void>\n}\n","/**\n * @h-ai/reach — 阿里云短信 Provider\n *\n * 通过阿里云 SMS HTTP API 发送短信的 Provider 实现(无需 SDK)。\n * @module reach-provider-aliyun-sms\n */\n\nimport type { HaiError, HaiResult } from '@h-ai/core'\nimport type { AliyunSmsProviderConfig, ProviderConfig } from '../reach-config.js'\n\nimport type { ReachMessage, SendResult } from '../reach-types.js'\nimport type { ReachProvider } from './reach-provider-types.js'\nimport { createHmac, randomUUID } from 'node:crypto'\nimport { core, err, ok } from '@h-ai/core'\n\nimport { reachM } from '../reach-i18n.js'\nimport { HaiReachError } from '../reach-types.js'\n\nconst logger = core.logger.child({ module: 'reach', scope: 'provider-aliyun-sms' })\nconst ISO_MILLISECONDS_SUFFIX_REGEX = /\\.\\d{3}Z$/\n\n/**\n * 将异常包装为 ReachError\n */\nfunction toReachError(error: unknown): HaiError {\n return {\n code: HaiReachError.SEND_FAILED.code,\n message: reachM('reach_smsSendFailed', {\n params: { error: error instanceof Error ? error.message : String(error) },\n }),\n cause: error,\n }\n}\n\n/**\n * 对字符串做 RFC 3986 编码\n */\nfunction percentEncode(str: string): string {\n return encodeURIComponent(str)\n .replace(/\\+/g, '%20')\n .replace(/\\*/g, '%2A')\n .replace(/~/g, '%7E')\n}\n\n/**\n * 按阿里云POP规范排序/编码并执行HMAC-SHA1;不能替换为框架默认HMAC-SM3。\n * @param params - 不含Signature的请求参数。\n * @param accessKeySecret - 私有Secret,禁止写日志。\n * @param method - 实际HTTP方法。\n * @returns Base64签名,只用于对应请求。\n *\n * @example 模块内部上下文\n * ```ts\n * const secret = process.env.HAI_REACH_PROVIDERS_0_ACCESSKEYSECRET\n * if (!secret) throw new Error('Missing SMS credential')\n * const signature = signRequest({ Action: 'SendSms', Format: 'JSON' }, secret, 'POST')\n * ```\n */\nfunction signRequest(params: Record<string, string>, accessKeySecret: string, method: string): string {\n const sortedKeys = Object.keys(params).sort()\n const canonicalQuery = sortedKeys\n .map(k => `${percentEncode(k)}=${percentEncode(params[k])}`)\n .join('&')\n\n const stringToSign = `${method}&${percentEncode('/')}&${percentEncode(canonicalQuery)}`\n // 阿里云 POP API 规范要求使用 HMAC-SHA1 签名\n const hmac = createHmac('sha1', `${accessKeySecret}&`)\n hmac.update(stringToSign)\n return hmac.digest('base64')\n}\n\n/**\n * 内部阿里云POP短信适配器,使用平台指定的HMAC-SHA1签名,不用框架HMAC-SM3替代。\n * 短信发送无接收端持久幂等声明,已开始后失败不自动接管重发。\n * @returns Provider;私有AccessKey由配置注入,不进入示例常量或日志。\n *\n * @example 模块内部上下文\n * ```ts\n * const provider = createAliyunSmsProvider()\n * const { AliyunSmsProviderConfigSchema } = await import('@h-ai/reach')\n * const config = AliyunSmsProviderConfigSchema.parse({ name: 'sms', type: 'aliyun-sms', accessKeyId: process.env.HAI_REACH_PROVIDERS_0_ACCESSKEYID, accessKeySecret: process.env.HAI_REACH_PROVIDERS_0_ACCESSKEYSECRET, signName: process.env.HAI_REACH_PROVIDERS_0_SIGNNAME })\n * const connected = await provider.connect(config)\n * if (!connected.success) throw new Error(connected.error.message)\n * try { const connectedNow = provider.isConnected(); const retrySafe = provider.supportsIdempotency }\n * finally { await provider.close() }\n * ```\n */\nexport function createAliyunSmsProvider(): ReachProvider {\n let smsConfig: AliyunSmsProviderConfig | null = null\n\n return {\n name: 'aliyun-sms',\n supportsIdempotency: false,\n\n async connect(config: ProviderConfig): Promise<HaiResult<void>> {\n if (config.type !== 'aliyun-sms') {\n return err(\n HaiReachError.CONFIG_ERROR,\n reachM('reach_unsupportedType', { params: { type: config.type } }),\n )\n }\n\n smsConfig = config\n logger.info('Aliyun SMS provider connected', { endpoint: config.endpoint })\n return ok(undefined)\n },\n\n async close(): Promise<void> {\n smsConfig = null\n logger.info('Aliyun SMS provider disconnected')\n },\n\n isConnected(): boolean {\n return smsConfig !== null\n },\n\n async send(message: ReachMessage): Promise<HaiResult<SendResult>> {\n if (!smsConfig) {\n return err(\n HaiReachError.NOT_INITIALIZED,\n reachM('reach_notInitialized'),\n )\n }\n\n const templateCode = message.extra?.templateCode as string | undefined\n logger.debug('Sending SMS', { to: message.to, templateCode })\n\n try {\n const params: Record<string, string> = {\n AccessKeyId: smsConfig.accessKeyId,\n Action: 'SendSms',\n Format: 'JSON',\n PhoneNumbers: message.to,\n SignName: smsConfig.signName,\n SignatureMethod: 'HMAC-SHA1',\n SignatureNonce: randomUUID(),\n SignatureVersion: '1.0',\n TemplateCode: templateCode ?? '',\n Timestamp: new Date().toISOString().replace(ISO_MILLISECONDS_SUFFIX_REGEX, 'Z'),\n Version: '2017-05-25',\n }\n\n if (message.vars) {\n params.TemplateParam = JSON.stringify(message.vars)\n }\n\n params.Signature = signRequest(params, smsConfig.accessKeySecret, 'GET')\n\n const queryString = Object.entries(params)\n .map(([k, v]) => `${percentEncode(k)}=${percentEncode(v)}`)\n .join('&')\n\n const url = `https://${smsConfig.endpoint}/?${queryString}`\n const response = await fetch(url)\n const body = await response.json() as { Code: string, Message: string, BizId?: string }\n\n if (body.Code !== 'OK') {\n logger.warn('SMS send returned non-OK status', { code: body.Code, message: body.Message })\n return err(\n HaiReachError.SEND_FAILED,\n reachM('reach_smsSendFailed', { params: { error: body.Message } }),\n )\n }\n\n logger.info('SMS sent', { to: message.to, bizId: body.BizId })\n return ok({ success: true, messageId: body.BizId })\n }\n catch (error) {\n logger.error('SMS send failed', { to: message.to, error })\n return err(toReachError(error))\n }\n },\n }\n}\n","import type { HaiError, HaiResult } from '@h-ai/core'\n/**\n * @h-ai/reach — API 回调 Provider\n *\n * 通用 HTTP API 回调 Provider,通过 HTTP 请求发送触达消息。\n * @module reach-provider-api\n */\n\nimport type { ApiProviderConfig, ProviderConfig } from '../reach-config.js'\nimport type { ReachMessage, SendResult } from '../reach-types.js'\n\nimport type { ReachProvider } from './reach-provider-types.js'\nimport process from 'node:process'\nimport { core, err, ok } from '@h-ai/core'\n\nimport { reachM } from '../reach-i18n.js'\nimport { HaiReachError } from '../reach-types.js'\n\nconst logger = core.logger.child({ module: 'reach', scope: 'provider-api' })\n\n/**\n * 日志只保留源站,去除URL用户信息、路径和查询业务数据,非法URL返回固定占位文本。\n * @param url - 已配置回调URL。\n * @returns 可用于结构化日志的源站。\n *\n * @example 模块内部上下文\n * ```ts\n * const endpoint = getSafeEndpoint('https://notify.example.com/private/path?trace=example')\n * // endpoint仅为https://notify.example.com。\n * ```\n */\nfunction getSafeEndpoint(url: string): string {\n try {\n return new URL(url).origin\n }\n catch {\n return '[invalid-url]'\n }\n}\n\n/**\n * 将异常包装为 ReachError\n */\nfunction toReachError(): HaiError {\n return {\n code: HaiReachError.SEND_FAILED.code,\n message: reachM('reach_apiSendFailed', {\n params: { error: reachM('reach_apiRequestFailed') },\n }),\n }\n}\n\n/** 成功回执仅包含可选消息编号;正文有界,解析失败不输出第三方内容。 */\nasync function readAcknowledgement(response: Response): Promise<HaiResult<SendResult>> {\n const reader = response.body?.getReader()\n let complete = false\n try {\n let text = ''\n let bytes = 0\n const decoder = new TextDecoder('utf-8', { fatal: true })\n if (reader) {\n while (true) {\n const chunk = await reader.read()\n if (chunk.done)\n break\n bytes += chunk.value.byteLength\n if (bytes > 65536)\n return err(HaiReachError.SEND_FAILED, reachM('reach_apiResponseTooLarge'))\n text += decoder.decode(chunk.value, { stream: true })\n }\n text += decoder.decode()\n }\n complete = true\n if (text.length === 0)\n return ok({ success: true })\n const result: unknown = JSON.parse(text)\n if (!result || typeof result !== 'object' || Array.isArray(result))\n return err(HaiReachError.SEND_FAILED, reachM('reach_apiResponseInvalid'))\n const messageId = 'messageId' in result ? result.messageId : undefined\n if (messageId !== undefined && (typeof messageId !== 'string' || messageId.length > 255))\n return err(HaiReachError.SEND_FAILED, reachM('reach_apiResponseInvalid'))\n return ok({ success: true, ...(messageId === undefined ? {} : { messageId }) })\n }\n catch {\n return err(HaiReachError.SEND_FAILED, reachM('reach_apiResponseInvalid'))\n }\n finally {\n if (!complete)\n await reader?.cancel().catch(() => undefined)\n reader?.releaseLock()\n }\n}\n\n/**\n * 内部HTTP回调适配器工厂,包用户通过reach.init配置Provider;connect不发送网络请求。\n * 只有接收端实际原子持久去重时才支持重试;默认false,稳定queueId放在请求头和JSON。\n * @returns Provider;超时中断不证明远端副作用被撤销。\n *\n * @example 模块内部上下文\n * ```ts\n * const provider = createApiProvider()\n * const { ApiProviderConfigSchema } = await import('@h-ai/reach')\n * const config = ApiProviderConfigSchema.parse({ name: 'webhook', type: 'api', url: 'https://notify.example.com/events', idempotent: false })\n * const connected = await provider.connect(config)\n * if (!connected.success) throw new Error(connected.error.message)\n * try { const connectedNow = provider.isConnected(); const retrySafe = provider.supportsIdempotency }\n * finally { await provider.close() }\n * ```\n */\nexport function createApiProvider(): ReachProvider {\n let apiConfig: ApiProviderConfig | null = null\n\n return {\n name: 'api',\n get supportsIdempotency() {\n return apiConfig?.idempotent === true\n },\n\n async connect(config: ProviderConfig): Promise<HaiResult<void>> {\n if (config.type !== 'api') {\n return err(\n HaiReachError.CONFIG_ERROR,\n reachM('reach_unsupportedType', { params: { type: config.type } }),\n )\n }\n\n apiConfig = config\n if ((config.requireHttps && new URL(config.url).protocol !== 'https:') || (config.requireHttps && config.redirect === 'follow')) {\n apiConfig = null\n return err(HaiReachError.CONFIG_ERROR, reachM('reach_apiHttpsRequired'))\n }\n logger.info('API provider connected', { endpoint: getSafeEndpoint(config.url), method: config.method })\n return ok(undefined)\n },\n\n async close(): Promise<void> {\n apiConfig = null\n logger.info('API provider disconnected')\n },\n\n isConnected(): boolean {\n return apiConfig !== null\n },\n\n async send(message: ReachMessage): Promise<HaiResult<SendResult>> {\n if (!apiConfig) {\n return err(\n HaiReachError.NOT_INITIALIZED,\n reachM('reach_notInitialized'),\n )\n }\n\n logger.debug('Sending via API callback', { endpoint: getSafeEndpoint(apiConfig.url) })\n\n const requireHttps = apiConfig.requireHttps ?? (message.sensitive === true && core.module.requiresSharedState(process.env))\n if (requireHttps && new URL(apiConfig.url).protocol !== 'https:')\n return err(HaiReachError.CONFIG_ERROR, reachM('reach_apiHttpsRequired'))\n\n try {\n const payload = {\n to: message.to,\n subject: message.subject,\n body: message.body,\n template: message.template,\n vars: message.vars,\n extra: message.extra,\n idempotencyKey: message.idempotencyKey,\n expiresAt: message.expiresAt,\n }\n\n const controller = new AbortController()\n const timeoutId = setTimeout(() => controller.abort(), apiConfig.timeout)\n\n try {\n const headers = new Headers({ 'Content-Type': 'application/json', ...apiConfig.headers })\n if (message.idempotencyKey)\n headers.set('Idempotency-Key', message.idempotencyKey)\n const response = await fetch(apiConfig.url, {\n method: apiConfig.method,\n headers,\n body: JSON.stringify(payload),\n signal: controller.signal,\n redirect: message.sensitive || apiConfig.redirect !== 'follow' ? 'manual' : 'follow',\n })\n\n if (!response.ok) {\n await response.body?.cancel()\n logger.warn('API callback returned non-OK status', { status: response.status })\n // 明确的客户端拒绝无需自动重试;超时、过早请求及限流仍属于暂时故障。\n const rejected = (response.status >= 300 && response.status < 400) || (response.status >= 400 && response.status < 500 && ![408, 425, 429].includes(response.status))\n return err(\n rejected ? HaiReachError.DELIVERY_REJECTED : HaiReachError.SEND_FAILED,\n reachM('reach_apiSendFailed', { params: { error: `HTTP ${response.status}` } }),\n )\n }\n\n const result = await readAcknowledgement(response)\n if (result.success)\n logger.info('API callback accepted', { hasMessageId: result.data.messageId !== undefined })\n return result\n }\n finally {\n clearTimeout(timeoutId)\n }\n }\n catch {\n logger.error('API callback send failed', { errorCode: HaiReachError.SEND_FAILED.code })\n return err(toReachError())\n }\n },\n }\n}\n","/**\n * @h-ai/reach — Console Provider\n *\n * 开发/测试用 Provider,仅将触达元数据输出到日志,保护验证码和令牌。\n * @module reach-provider-console\n */\n\nimport type { HaiResult } from '@h-ai/core'\nimport type { ProviderConfig } from '../reach-config.js'\n\nimport type { ReachMessage, SendResult } from '../reach-types.js'\nimport type { ReachProvider } from './reach-provider-types.js'\nimport { core, ok } from '@h-ai/core'\n\nconst logger = core.logger.child({ module: 'reach', scope: 'provider-console' })\n\n/**\n * 内部开发/测试日志适配器,不证明真实邮件/短信/API投递;生产守卫拒绝console。\n * @returns 仅本地调试Provider;supportsIdempotency=true表示测试日志可重试,不代表接收端持久去重。\n *\n * @example 模块内部上下文\n * ```ts\n * const provider = createConsoleProvider()\n * const config = { name: 'dev-log', type: 'console' as const }\n * const connected = await provider.connect(config)\n * if (!connected.success) throw new Error(connected.error.message)\n * try { const connectedNow = provider.isConnected(); const retrySafe = provider.supportsIdempotency }\n * finally { await provider.close() }\n * ```\n */\nexport function createConsoleProvider(): ReachProvider {\n let connected = false\n\n return {\n name: 'console',\n supportsIdempotency: true,\n\n async connect(_config: ProviderConfig): Promise<HaiResult<void>> {\n connected = true\n logger.info('Console provider connected')\n return ok(undefined)\n },\n\n async close(): Promise<void> {\n connected = false\n logger.info('Console provider disconnected')\n },\n\n isConnected(): boolean {\n return connected\n },\n\n async send(message: ReachMessage): Promise<HaiResult<SendResult>> {\n logger.debug('Sending message via console', {\n provider: message.provider,\n to: message.to,\n hasSubject: Boolean(message.subject),\n bodyLength: message.body?.length ?? 0,\n template: message.template,\n variableNames: Object.keys(message.vars ?? {}),\n })\n\n const messageId = `console-${Date.now()}`\n return ok({ success: true, messageId })\n },\n }\n}\n","/**\n * @h-ai/reach — SMTP Email Provider\n *\n * 使用 nodemailer 发送邮件的 Provider 实现。\n * @module reach-provider-smtp\n */\n\nimport type { HaiError, HaiResult } from '@h-ai/core'\nimport type { ProviderConfig, SmtpProviderConfig } from '../reach-config.js'\n\nimport type { ReachMessage, SendResult } from '../reach-types.js'\nimport type { ReachProvider } from './reach-provider-types.js'\nimport { createRequire } from 'node:module'\nimport { core, err, ok } from '@h-ai/core'\n\nimport { reachM } from '../reach-i18n.js'\nimport { HaiReachError } from '../reach-types.js'\n\nconst logger = core.logger.child({ module: 'reach', scope: 'provider-smtp' })\n\n/**\n * 将异常包装为 ReachError\n */\nfunction toReachError(error: unknown): HaiError {\n return {\n code: HaiReachError.SEND_FAILED.code,\n message: reachM('reach_emailSendFailed', {\n params: { error: error instanceof Error ? error.message : String(error) },\n }),\n cause: error,\n }\n}\n\n/**\n * 内部SMTP适配器,惰性加载nodemailer;SMTP无持久接收端幂等保证,已开始发送失败进入unknown。\n * @returns Provider;连接/发送先检查HaiResult,close释放自有传输连接。\n *\n * @example 模块内部上下文\n * ```ts\n * const provider = createSmtpProvider()\n * const { SmtpProviderConfigSchema } = await import('@h-ai/reach')\n * const config = SmtpProviderConfigSchema.parse({ name: 'email', type: 'smtp', host: 'smtp.example.com', from: 'noreply@example.com', user: process.env.HAI_REACH_PROVIDERS_0_USER, pass: process.env.HAI_REACH_PROVIDERS_0_PASS })\n * const connected = await provider.connect(config)\n * if (!connected.success) throw new Error(connected.error.message)\n * try { const connectedNow = provider.isConnected(); const retrySafe = provider.supportsIdempotency }\n * finally { await provider.close() }\n * ```\n */\nexport function createSmtpProvider(): ReachProvider {\n let transporter: unknown = null\n let smtpConfig: SmtpProviderConfig | null = null\n\n return {\n name: 'smtp',\n supportsIdempotency: false,\n\n async connect(config: ProviderConfig): Promise<HaiResult<void>> {\n if (config.type !== 'smtp') {\n return err(\n HaiReachError.CONFIG_ERROR,\n reachM('reach_unsupportedType', { params: { type: config.type } }),\n )\n }\n\n try {\n const require = createRequire(import.meta.url)\n const nodemailer = require('nodemailer')\n\n smtpConfig = config\n transporter = nodemailer.createTransport({\n host: config.host,\n port: config.port,\n secure: config.secure,\n auth: config.user\n ? { user: config.user, pass: config.pass }\n : undefined,\n })\n\n logger.info('SMTP provider connected', { host: config.host, port: config.port })\n return ok(undefined)\n }\n catch (error) {\n logger.error('SMTP connection failed', { error })\n return err(toReachError(error))\n }\n },\n\n async close(): Promise<void> {\n if (transporter && typeof (transporter as Record<string, unknown>).close === 'function') {\n (transporter as { close: () => void }).close()\n }\n transporter = null\n smtpConfig = null\n logger.info('SMTP provider disconnected')\n },\n\n isConnected(): boolean {\n return transporter !== null\n },\n\n async send(message: ReachMessage): Promise<HaiResult<SendResult>> {\n if (!transporter || !smtpConfig) {\n return err(\n HaiReachError.NOT_INITIALIZED,\n reachM('reach_notInitialized'),\n )\n }\n\n logger.debug('Sending email', { to: message.to, subject: message.subject })\n\n try {\n const sendMail = (transporter as { sendMail: (opts: unknown) => Promise<{ messageId: string }> }).sendMail.bind(transporter)\n const info = await sendMail({\n from: smtpConfig.from,\n to: message.to,\n subject: message.subject ?? '',\n html: message.body ?? '',\n })\n\n logger.info('Email sent', { to: message.to, messageId: info.messageId })\n return ok({ success: true, messageId: info.messageId })\n }\n catch (error) {\n logger.error('Email send failed', { to: message.to, error })\n return err(toReachError(error))\n }\n },\n }\n}\n","/**\n * 单条队列投递:发送前持久化门闩、在途续租及未知结果隔离。\n * @module reach-delivery\n */\nimport type { HaiResult } from '@h-ai/core'\nimport type { ReachProvider } from './providers/reach-provider-types.js'\nimport type { ReachMessage, SendResult } from './reach-types.js'\nimport type { SendLogRepository, StoredSendLog } from './repositories/reach-repository-send-log.js'\nimport { core, err } from '@h-ai/core'\nimport { reachM } from './reach-i18n.js'\nimport { HaiReachError } from './reach-types.js'\n\nconst logger = core.logger.child({ module: 'reach', scope: 'delivery' })\nconst DEFAULT_LEASE_MS = 10 * 60 * 1000\n\n/**\n * 内部投递已领取记录;外部调用前原子持久化sending,有效owner条件提交、在途续租。\n * 续租不能撤销副作用:非幂等请求开始后失败/失租约进入unknown;API仅在真实持久去重时以同queueId有限重试。\n * @param row - 当前owner已领取的记录。\n * @param owner - 本次唯一领取凭据。\n * @param provider - 对应已连接Provider。\n * @param repo - 共享数据库仓库。\n * @param leaseMs - 领取期限毫秒数。\n * @returns 已提交或明确失败/unknown;旧owner迟到不能覆盖新状态。\n *\n * @example 模块内部上下文\n * ```ts\n * async function deliverNext(provider: ReachProvider, repository: SendLogRepository) {\n * const owner = 'worker:' + crypto.randomUUID()\n * const claimed = await repository.claimPendingBatch(owner, { limit: 1 })\n * if (!claimed.success) throw new Error(claimed.error.message)\n * const row = claimed.data[0]\n * if (!row) return\n * // 调用方已按row.provider选择匹配的provider;否则应failClaim退避,不能发送到其它通道。\n * const result = await deliverClaimedMessage(row, owner, provider, repository)\n * if (!result.success) throw new Error(result.error.message)\n * }\n * ```\n */\nexport async function deliverClaimedMessage(\n row: StoredSendLog,\n owner: string,\n provider: ReachProvider,\n repo: SendLogRepository,\n leaseMs = DEFAULT_LEASE_MS,\n): Promise<HaiResult<void>> {\n const retrySafe = provider.supportsIdempotency\n if (!row.queueId || (row.retrySafe && !retrySafe)) {\n await repo.markUnknown(row.id, owner)\n return err(HaiReachError.SEND_FAILED, reachM('reach_deliveryUnknown'))\n }\n if (!provider.isConnected()) {\n await repo.failClaim(row, owner, 'provider_unavailable')\n return err(HaiReachError.NOT_INITIALIZED, reachM('reach_notInitialized'))\n }\n\n let message: ReachMessage\n try {\n message = {\n provider: row.provider,\n to: row.toAddr,\n subject: row.subject ?? undefined,\n body: row.body ?? undefined,\n template: row.template ?? undefined,\n vars: row.varsJson ? JSON.parse(row.varsJson) as Record<string, string> : undefined,\n extra: row.extraJson ? JSON.parse(row.extraJson) as Record<string, unknown> : undefined,\n idempotencyKey: row.queueId,\n }\n }\n catch (cause) {\n // 坏持久化载荷不会随重试改善;尚未调用外部服务,直接进入死信。\n await repo.failClaim(row, owner, 'invalid_message', true)\n return err(HaiReachError.SEND_FAILED, reachM('reach_sendRecordSaveFailed'), cause)\n }\n\n const started = await repo.beginDelivery(row.id, owner, retrySafe, leaseMs)\n if (!started.success)\n return started\n\n let stopped = false\n let lost = false\n let renewing: Promise<void> | undefined\n let timer: ReturnType<typeof setTimeout> | undefined\n const renew = async () => {\n try {\n const result = await repo.renewClaim(row.id, owner, leaseMs)\n lost = !result.success\n }\n catch {\n lost = true\n }\n if (!stopped && !lost) {\n timer = setTimeout(() => {\n renewing = renew()\n }, Math.max(1, Math.floor(leaseMs / 3)))\n }\n }\n timer = setTimeout(() => {\n renewing = renew()\n }, Math.max(1, Math.floor(leaseMs / 3)))\n\n let outcome: HaiResult<SendResult>\n try {\n outcome = await provider.send(message)\n }\n catch (cause) {\n outcome = err(HaiReachError.SEND_FAILED, reachM('reach_deliveryUnknown'), cause)\n }\n finally {\n stopped = true\n clearTimeout(timer)\n await renewing\n }\n\n if (lost) {\n if (!retrySafe)\n await repo.markUnknown(row.id, owner)\n return err(HaiReachError.SEND_FAILED, reachM('reach_claimLost'))\n }\n if (outcome.success && outcome.data.success) {\n const marked = await repo.markSent(row.id, owner, outcome.data.messageId)\n if (!marked.success && !retrySafe)\n await repo.markUnknown(row.id, owner)\n return marked\n }\n if (!retrySafe) {\n await repo.markUnknown(row.id, owner)\n logger.warn('Delivery outcome is unknown; automatic redelivery stopped', { queueId: row.queueId, provider: row.provider })\n return err(HaiReachError.SEND_FAILED, reachM('reach_deliveryUnknown'), outcome.success ? undefined : outcome.error)\n }\n // 接收端持久去重时,响应丢失也可以在之后使用同一个 queueId 重试。\n const permanent = !outcome.success && [HaiReachError.DELIVERY_REJECTED.code, HaiReachError.INVALID_RECIPIENT.code].includes(outcome.error.code)\n const released = await repo.failClaim(row, owner, permanent ? 'permanent_rejection' : 'transient_failure', permanent)\n if (!released.success)\n return released\n return err(HaiReachError.SEND_FAILED, reachM('reach_deliveryUnknown'), outcome.success ? undefined : outcome.error)\n}\n","/** 凭据仅驻留当前调用;持久回执占用幂等键,发送不明也不自动重投旧凭据。 */\nimport type { HaiResult } from '@h-ai/core'\nimport type { ReachProvider } from './providers/reach-provider-types.js'\nimport type { ReachMessage, SendResult } from './reach-types.js'\nimport type { SendLogRepository, StoredSendLog } from './repositories/reach-repository-send-log.js'\nimport { createHash } from 'node:crypto'\nimport { core, err, ok } from '@h-ai/core'\nimport { reachM } from './reach-i18n.js'\nimport { HaiReachError } from './reach-types.js'\n\n/**\n * 元数据回执与普通发送队列共用唯一键约束,不保存可重建凭据的载荷或摘要。\n * @example 模块内部上下文\n * ```ts\n * function receipt(row: StoredSendLog): HaiResult<SendResult> {\n * return previousResult(row)\n * }\n * ```\n */\nfunction previousResult(row: StoredSendLog): HaiResult<SendResult> {\n return row.status === 'sent'\n ? ok({ success: true, receiptId: row.queueId ?? undefined })\n : unconfirmed(row.queueId!)\n}\n\nfunction unconfirmed(receiptId: string): HaiResult<SendResult> {\n return { success: false, error: { ...HaiReachError.SEND_FAILED, message: reachM('reach_sensitiveDeliveryUnconfirmed'), ext: { receiptId } } }\n}\n\n/**\n * 只允许一个节点进行凭据投递;失败/崩溃只留下无载荷回执,不恢复发送。\n * @example 模块内部上下文\n * ```ts\n * async function deliver(message: ReachMessage, provider: ReachProvider, repository: SendLogRepository) {\n * const result = await sendSensitive(message, provider, repository, false)\n * if (!result.success) return result\n * return result\n * }\n * ```\n */\nexport async function sendSensitive(message: ReachMessage, provider: ReachProvider, repo: SendLogRepository | null | undefined, blocked: boolean): Promise<HaiResult<SendResult>> {\n if (!repo || !message.idempotencyKey?.trim() || message.idempotencyKey.length > 255 || !Number.isSafeInteger(message.expiresAt))\n return err(HaiReachError.CONFIG_ERROR, reachM('reach_sensitivePolicyRequired'))\n const hash = createHash('sha256').update(JSON.stringify([message.provider, message.to, message.idempotencyKey])).digest('hex')\n // 固定 UUID 保持既有回执查询契约;只散列随机操作键与路由,不散列低熵密码或验证码。\n const queueId = `${hash.slice(0, 8)}-${hash.slice(8, 12)}-5${hash.slice(13, 16)}-a${hash.slice(17, 20)}-${hash.slice(20, 32)}`\n const find = () => repo.findAll({ where: 'queue_id = ?', params: [queueId], limit: 1 })\n const existing = await find()\n if (!existing.success)\n return existing\n if (existing.data[0])\n return previousResult(existing.data[0])\n const now = Date.now()\n if (message.expiresAt! <= now)\n return err(HaiReachError.MESSAGE_EXPIRED, reachM('reach_messageExpired'))\n const created = await repo.create({\n queueId,\n provider: message.provider,\n toAddr: '[REDACTED]',\n subject: null,\n body: null,\n template: null,\n varsJson: null,\n extraJson: null,\n status: blocked ? 'discarded' : 'sending',\n retrySafe: false,\n createdAt: now,\n updatedAt: now,\n lockedUntil: Math.min(message.expiresAt!, now + 600_000),\n nextAttemptAt: 0,\n })\n if (!created.success || created.data.changes !== 1) {\n const previous = await find()\n if (previous.success && previous.data[0])\n return previousResult(previous.data[0])\n return unconfirmed(queueId)\n }\n if (blocked)\n return err(HaiReachError.DND_BLOCKED, reachM('reach_dndBlocked'))\n if (Date.now() >= message.expiresAt!) {\n await repo.settleSensitive(queueId, 'discarded')\n return err(HaiReachError.MESSAGE_EXPIRED, reachM('reach_messageExpired'))\n }\n let delivered: HaiResult<SendResult>\n try {\n // 允许宿主明确启用的开发 Console 输出,但通知内容不进入任何持久采集观察器。\n delivered = await core.context.run({ suppressLogObservers: true }, () => provider.send(message))\n }\n catch {\n delivered = err(HaiReachError.SEND_FAILED, reachM('reach_sensitiveDeliveryUnconfirmed'))\n }\n const success = delivered.success && delivered.data.success && !delivered.data.deferred\n const settled = await repo.settleSensitive(queueId, success ? 'sent' : 'unknown')\n if (!settled.success || !success)\n return unconfirmed(queueId)\n return ok({ success: true, receiptId: queueId })\n}\n","/**\n * @h-ai/reach — 发送逻辑\n *\n * 本文件封装触达模块的发送逻辑,包括: - 消息预处理(模板渲染 + Provider 路由推导) - DND(免打扰)时间段检查与策略处理(discard / delay) - 通过 SendLogRepository 持久化发送记录 - DND 恢复定时任务(flush pending 消息)\n * @module reach-send\n */\n\nimport type { HaiResult } from '@h-ai/core'\nimport type { ReachProvider } from './providers/reach-provider-types.js'\nimport type { DndConfig, ReachQueueConfig } from './reach-config.js'\nimport type { ReachMessage, ReachTemplateRegistry, SendResult } from './reach-types.js'\n\nimport type { SendLogRepository } from './repositories/reach-repository-send-log.js'\nimport { cache } from '@h-ai/cache'\nimport { core, err, ok } from '@h-ai/core'\n\nimport { ReachQueueConfigSchema } from './reach-config.js'\nimport { deliverClaimedMessage } from './reach-delivery.js'\nimport { reachM } from './reach-i18n.js'\nimport { sendSensitive } from './reach-send-sensitive.js'\nimport {\n HaiReachError,\n\n} from './reach-types.js'\n\nconst logger = core.logger.child({ module: 'reach', scope: 'send' })\nconst FLUSH_LOCK_KEY = 'hai:reach:flush-pending'\nconst FLUSH_LOCK_TTL = 300\nconst FLUSH_BATCH_SIZE = 100\nconst FLUSH_CLAIM_LOCK_MS = 10 * 60 * 1000\n\n/** 稳定的节点标识,用于分布式锁 owner(进程级别唯一) */\nconst reachNodeId = `reach:${crypto.randomUUID()}`\n\n// ─── DND(免打扰)检查 ───\n\n/**\n * 把已由DND Schema校验的HH:mm转换为当日分钟,不能直接用于未校验输入。\n * @param time - 已校验的24小时制文本。\n * @returns 0–1439分钟。\n *\n * @example 模块内部上下文\n * ```ts\n * const minutes = parseTimeToMinutes('22:30')\n * ```\n */\nfunction parseTimeToMinutes(time: string): number {\n const [h, m] = time.split(':').map(Number)\n return h * 60 + m\n}\n\n/**\n * 内部按节点本地时区判断半开区间DND,支持跨午夜;start=end不拦截,集群须统一时区/时钟。\n * @param dnd - 已校验免打扰配置。\n * @param now - 当前时间,测试可注入。\n * @returns 是否需要执行DND策略,不意味着已入队。\n *\n * @example 模块内部上下文\n * ```ts\n * const blocked = isDndBlocked({ enabled: true, strategy: 'delay', start: '22:00', end: '08:00' }, new Date(2026, 9, 23, 23, 0))\n * ```\n */\nexport function isDndBlocked(dnd: DndConfig | undefined, now: Date = new Date()): boolean {\n if (!dnd?.enabled) {\n return false\n }\n\n const startMin = parseTimeToMinutes(dnd.start)\n const endMin = parseTimeToMinutes(dnd.end)\n const currentMin = now.getHours() * 60 + now.getMinutes()\n\n if (startMin === endMin) {\n return false\n }\n\n if (startMin < endMin) {\n return currentMin >= startMin && currentMin < endMin\n }\n\n return currentMin >= startMin || currentMin < endMin\n}\n\n/**\n * 内部计算到下一次DND结束的毫秒数,按本地时区、秒精度调度;不是跨节点数据库时钟。\n * @param dnd - 已校验配置。\n * @param now - 当前本地时间。\n * @returns 下一结束点的延迟毫秒数。\n *\n * @example 模块内部上下文\n * ```ts\n * const delay = msUntilDndEnd({ enabled: true, strategy: 'delay', start: '22:00', end: '08:00' }, new Date(2026, 9, 23, 23, 0))\n * ```\n */\nexport function msUntilDndEnd(dnd: DndConfig, now: Date = new Date()): number {\n const endMin = parseTimeToMinutes(dnd.end)\n const currentMin = now.getHours() * 60 + now.getMinutes()\n const currentSec = now.getSeconds()\n\n let diffMin = endMin - currentMin\n if (diffMin <= 0) {\n diffMin += 24 * 60\n }\n return diffMin * 60 * 1000 - currentSec * 1000\n}\n\n// ─── 消息预处理 ───\n\n/**\n * 内部渲染模板并补全路由/主题/正文;消息显式字段优先,渲染失败原样返回。\n * @param message - 原消息,不原地修改。\n * @param templateRegistry - 真实数据库模板注册表。\n * @returns 补全消息或模板失败。\n *\n * @example 模块内部上下文\n * ```ts\n * async function prepare(message: ReachMessage, registry: ReachTemplateRegistry) {\n * const result = await preprocessMessage(message, registry)\n * if (!result.success) throw new Error(result.error.message)\n * return result.data\n * }\n * ```\n */\nexport async function preprocessMessage(\n message: ReachMessage,\n templateRegistry: ReachTemplateRegistry,\n): Promise<HaiResult<ReachMessage>> {\n if (!message.template) {\n return ok(message)\n }\n\n const rendered = await templateRegistry.render(message.template, message.vars ?? {})\n if (!rendered.success) {\n return rendered\n }\n\n const template = await templateRegistry.resolve(message.template)\n\n const processed: ReachMessage = {\n ...message,\n provider: message.provider || (template.success ? template.data.provider : '') || '',\n subject: message.subject ?? rendered.data.subject,\n body: message.body ?? rendered.data.body,\n }\n return ok(processed)\n}\n\n// ─── 发送记录持久化(通过 Repository) ───\n\n/**\n * 等待持久写入并检查实际changes=1,可靠入队不能由内存状态或未等待的Promise代替。\n * @param repo - 真实数据库仓库。\n * @param message - 已完成模板预处理的消息。\n * @param status - pending可靠队列或sent观察日志。\n * @param provider - Provider配置名称。\n * @param messageId - 可选外部消息编号。\n * @param queueId - 延迟入队时预生成的持久UUID回执。\n * @param queueConfig - 随消息保存的重试预算与退避策略。\n * @returns 持久写入HaiResult;调用方按即时/入队语义分别处理失败。\n *\n * @example 模块内部上下文\n * ```ts\n * async function enqueue(repository: SendLogRepository, message: ReachMessage, providerName: string) {\n * const queueId = crypto.randomUUID()\n * const saved = await saveSendRecord(repository, message, 'pending', providerName, undefined, queueId)\n * if (!saved.success) throw new Error(saved.error.message)\n * return queueId\n * }\n * ```\n */\nasync function saveSendRecord(\n repo: SendLogRepository,\n message: ReachMessage,\n status: 'sent' | 'pending',\n provider: string,\n messageId?: string,\n queueId?: string,\n queueConfig?: ReachQueueConfig,\n): Promise<HaiResult<void>> {\n try {\n const inserted = await repo.create({\n queueId: queueId ?? null,\n provider,\n toAddr: message.to,\n subject: message.subject ?? null,\n body: message.body ?? null,\n template: message.template ?? null,\n varsJson: message.vars ? JSON.stringify(message.vars) : null,\n extraJson: message.extra ? JSON.stringify(message.extra) : null,\n status,\n ...queueConfig,\n messageId: messageId ?? null,\n createdAt: Date.now(),\n })\n if (!inserted.success)\n return inserted\n if (inserted.data.changes !== 1)\n return err(HaiReachError.SEND_FAILED, reachM('reach_sendRecordSaveFailed'))\n return ok(undefined)\n }\n catch (cause) {\n return err(HaiReachError.SEND_FAILED, reachM('reach_sendRecordSaveFailed'), cause)\n }\n}\n\n// ─── DND 恢复定时任务 ───\n\n/** 定时器引用 */\nlet dndTimer: ReturnType<typeof setTimeout> | null = null\n\n/** 保存调度器参数,用于循环调度 */\nlet schedulerContext: { dndConfig: DndConfig, providers: Map<string, ReachProvider>, repo: SendLogRepository | null } | null = null\n\n/**\n * 内部启动本机扫描计时器;业务队列状态由共享数据库持有,本机计时器不决定投递所有权。\n * DND外立即扫描遗留消息,之后按分钟有界恢复;仅delay策略启用。\n * @param dndConfig - 已校验DND配置。\n * @param providers - 当前已连接Provider路由表。\n * @param repo - 真实持久队列;null不产生可靠入队能力。\n *\n * @example 模块内部上下文\n * ```ts\n * function start(config: DndConfig, providers: Map<string, ReachProvider>, repository: SendLogRepository) {\n * startDndScheduler(config, providers, repository)\n * // 生命周期结束调用resetSendState,不把停止计时器当作撤销外部投递。\n * }\n * ```\n */\nexport function startDndScheduler(\n dndConfig: DndConfig,\n providers: Map<string, ReachProvider>,\n repo: SendLogRepository | null,\n): void {\n stopDndScheduler()\n\n if (!dndConfig.enabled || dndConfig.strategy !== 'delay') {\n return\n }\n\n schedulerContext = { dndConfig, providers, repo }\n const context = schedulerContext\n\n // 如果当前不在 DND 时段,不需要启动定时器\n if (!isDndBlocked(dndConfig)) {\n // 但仍然尝试 flush 一次(可能上次 DND 结束后有残留 pending)\n flushPendingMessages(providers, repo).catch((error) => {\n logger.warn('Failed to flush pending messages on init', { error })\n }).finally(() => {\n if (schedulerContext === context)\n scheduleDndCheck()\n })\n return\n }\n\n scheduleFlushAtDndEnd()\n}\n\n/**\n * 在 DND 结束时触发 flush 并重新调度\n */\nfunction scheduleFlushAtDndEnd(): void {\n if (!schedulerContext)\n return\n const context = schedulerContext\n const { dndConfig, providers, repo } = context\n\n const delayMs = msUntilDndEnd(dndConfig)\n logger.info('DND scheduler started', { delayMs, dndEnd: dndConfig.end })\n\n dndTimer = setTimeout(() => {\n if (schedulerContext !== context)\n return\n logger.info('DND period ended, flushing pending messages')\n flushPendingMessages(providers, repo)\n .catch((error) => {\n logger.error('Failed to flush pending messages', { error })\n })\n .finally(() => {\n // 重新调度:等待下一个 DND 周期\n if (schedulerContext === context)\n scheduleDndCheck()\n })\n }, delayMs)\n}\n\n/**\n * 定期检查是否进入 DND 时段,进入后切换到 flush 调度\n */\nfunction scheduleDndCheck(): void {\n if (!schedulerContext)\n return\n const context = schedulerContext\n const { dndConfig, providers, repo } = context\n\n // 每分钟检查一次是否进入 DND\n const CHECK_INTERVAL = 60 * 1000\n dndTimer = setTimeout(() => {\n if (schedulerContext !== context)\n return\n if (isDndBlocked(dndConfig)) {\n scheduleFlushAtDndEnd()\n }\n else {\n // DND 时段外持续恢复遗留任务,避免节点启动时租约未过期就要等到下一天。\n flushPendingMessages(providers, repo)\n .catch(error => logger.warn('Failed to recover pending messages', { error }))\n .finally(() => {\n if (schedulerContext === context)\n scheduleDndCheck()\n })\n }\n }, CHECK_INTERVAL)\n}\n\n/**\n * 内部清除当前本机计时器;不清数据库、不停止已开始的外部Provider调用。\n * 完整生命周期关闭通过resetSendState同时废弃调度上下文。\n *\n * @example 模块内部上下文\n * ```ts\n * stopDndScheduler()\n * ```\n */\nexport function stopDndScheduler(): void {\n if (dndTimer !== null) {\n clearTimeout(dndTimer)\n dndTimer = null\n }\n}\n\n/**\n * 内部关闭时清除计时器并废弃旧调度上下文,迟到finally不会再排下一轮;不撤销在途外部请求。\n *\n * @example 模块内部上下文\n * ```ts\n * resetSendState()\n * ```\n */\nexport function resetSendState(): void {\n stopDndScheduler()\n schedulerContext = null\n}\n\n/**\n * 一轮最多100次逐条领取,固定eligibleBefore让退避消息留给下轮,坏消息不在满批循环自旋。\n * 缓存锁只减少扫描;资格由数据库owner/租约/sending门闩决定,外部效果依赖幂等或unknown隔离。\n * @param providers - 当前已连接路由表。\n * @param repo - 持久队列;缺省无可靠待发送队列。\n *\n * @example 模块内部上下文\n * ```ts\n * async function flush(providers: Map<string, ReachProvider>, repository: SendLogRepository) {\n * await flushPendingMessages(providers, repository)\n * // 生命周期调度负责下一轮;不在此函数外无界while重试。\n * }\n * ```\n */\nasync function flushPendingMessages(\n providers: Map<string, ReachProvider>,\n repo: SendLogRepository | null,\n): Promise<void> {\n if (!repo) {\n return\n }\n\n // 分布式锁:防止多节点同时 flush\n // 注意:这里的锁主要用于减少惊群和重复扫描;真正的行级正确性由 repo.claimPendingBatch 的 claim 事务保证。\n let lockOwner: string | null = null\n // flushOwner 是本轮 flush 的短期 lease owner。\n // 同一进程后续再次 flush 会生成新的 owner,避免旧批次在 lease 失效后误释放/误提交新批次的记录。\n const flushOwner = `${reachNodeId}:${crypto.randomUUID()}`\n if (cache.isInitialized) {\n const lockResult = await cache.lock.acquire(FLUSH_LOCK_KEY, { ttl: FLUSH_LOCK_TTL })\n if (!lockResult.success) {\n logger.warn('Failed to acquire flush lock, continuing with row claims only', { error: lockResult.error.message })\n }\n else if (!lockResult.data) {\n logger.info('Skipping flush, another node holds the lock')\n return\n }\n else {\n lockOwner = lockResult.data\n }\n }\n\n try {\n // 固定本轮资格截止点,耗时发送期间到期的重试留到下一轮,避免毒消息重复占用预算。\n const clock = await repo.currentTime()\n if (!clock.success) {\n logger.warn('Failed to read queue clock', { errorCode: clock.error.code })\n return\n }\n const eligibleBefore = clock.data\n for (let processed = 0; processed < FLUSH_BATCH_SIZE; processed += 1) {\n // 按实际执行逐条领取,避免后排消息排队等待时耗尽租约;单轮处理数量有界。\n const claimResult = await repo.claimPendingBatch(flushOwner, {\n limit: 1,\n lockMs: FLUSH_CLAIM_LOCK_MS,\n eligibleBefore,\n })\n if (!claimResult.success) {\n logger.warn('Failed to claim pending messages', { error: claimResult.error.message })\n return\n }\n\n if (claimResult.data.length === 0) {\n return\n }\n\n logger.debug('Flushing pending messages', { count: claimResult.data.length })\n\n for (const row of claimResult.data) {\n // 缺少 Provider 可由配置修复,但也必须消耗重试预算并退避,不能原地自旋。\n const provider = providers.get(row.provider)\n if (!provider) {\n logger.warn('Provider not found for pending message, releasing claim', { provider: row.provider, id: row.id })\n const releaseResult = await repo.failClaim(row, flushOwner, 'provider_unavailable')\n if (!releaseResult.success) {\n logger.warn('Failed to release pending message claim', { id: row.id, error: releaseResult.error.message })\n }\n continue\n }\n\n const sent = await deliverClaimedMessage(row, flushOwner, provider, repo, FLUSH_CLAIM_LOCK_MS)\n if (!sent.success)\n logger.warn('Pending message delivery did not commit', { id: row.id, code: sent.error.code })\n }\n }\n }\n finally {\n // 释放锁\n if (lockOwner) {\n await cache.lock.release(FLUSH_LOCK_KEY, lockOwner).catch((error: unknown) => {\n logger.warn('Failed to release flush lock', { error })\n })\n }\n }\n}\n\n// ─── 发送入口 ───\n\n/**\n * 内部发送编排:模板/路由、DND、即时投递或持久pending入队。\n * delay仅在数据库写入确认为1条后返回deferred+queueId;即时外部成功不因可选日志故障返回失败,避免重发。\n * @param message - 原始消息。\n * @param providers - 已连接Provider路由。\n * @param templateRegistry - 真实模板注册表。\n * @param dndConfig - 可选DND策略。\n * @param repo - 可选日志仓库;delay必须存在。\n * @param queueConfig - 入队时保存的有界重试策略。\n * @returns 发送/可靠入队结果;不声称接收端业务恰好执行一次。\n *\n * @example 模块内部上下文\n * ```ts\n * async function sendDirect(message: ReachMessage, providers: Map<string, ReachProvider>, registry: ReachTemplateRegistry, repository: SendLogRepository) {\n * const result = await executeSend(message, providers, registry, undefined, repository)\n * if (!result.success) throw new Error(result.error.message)\n * return result.data\n * }\n * ```\n */\nexport async function executeSend(\n message: ReachMessage,\n providers: Map<string, ReachProvider>,\n templateRegistry: ReachTemplateRegistry,\n dndConfig?: DndConfig,\n repo?: SendLogRepository | null,\n queueConfig: ReachQueueConfig = ReachQueueConfigSchema.parse({}),\n): Promise<HaiResult<SendResult>> {\n if (!message.to) {\n return err(\n HaiReachError.INVALID_RECIPIENT,\n reachM('reach_invalidRecipient', { params: { recipient: '' } }),\n )\n }\n\n const preprocessed = await preprocessMessage(message, templateRegistry)\n if (!preprocessed.success) {\n return preprocessed\n }\n\n // 校验 Provider 存在性(在 DND 检查前,确保 delay 也能正确路由)\n const providerName = preprocessed.data.provider\n if (!providerName) {\n return err(\n HaiReachError.PROVIDER_NOT_FOUND,\n reachM('reach_providerRequired'),\n )\n }\n\n if (!providers.has(providerName)) {\n return err(\n HaiReachError.PROVIDER_NOT_FOUND,\n reachM('reach_providerNotFound', { params: { provider: providerName } }),\n )\n }\n\n if (preprocessed.data.sensitive)\n return sendSensitive(preprocessed.data, providers.get(providerName)!, repo, isDndBlocked(dndConfig))\n\n // DND 检查\n if (isDndBlocked(dndConfig)) {\n if (message.deferPolicy === 'reject')\n return err(HaiReachError.DND_BLOCKED, reachM('reach_dndBlocked'))\n const strategy = dndConfig?.strategy ?? 'discard'\n\n if (strategy === 'delay') {\n if (!repo)\n return err(HaiReachError.SEND_FAILED, reachM('reach_dndPersistenceRequired'))\n\n // 预先生成并随消息落盘,避免依赖不同数据库的自增 ID 返回约定。\n const queueId = crypto.randomUUID()\n const saved = await saveSendRecord(repo, preprocessed.data, 'pending', providerName, undefined, queueId, queueConfig)\n if (!saved.success) {\n logger.warn('Failed to persist deferred message', { provider: providerName, code: saved.error.code })\n return err(\n HaiReachError.SEND_FAILED,\n reachM('reach_dndEnqueueFailed'),\n saved.error,\n )\n }\n logger.info('Message persisted for DND delivery', { provider: providerName, queueId })\n return ok({ success: true, deferred: true, queueId })\n }\n\n // discard 策略:直接拒绝\n logger.info('Message blocked by DND (discard strategy)', { provider: providerName, to: message.to })\n return err(\n HaiReachError.DND_BLOCKED,\n reachM('reach_dndBlocked'),\n )\n }\n\n const provider = providers.get(providerName)!\n\n logger.debug('Sending message', {\n provider: providerName,\n to: preprocessed.data.to,\n template: message.template,\n })\n\n const result = await provider.send(preprocessed.data)\n if (!result.success) {\n logger.warn('Message send failed', {\n provider: providerName,\n to: message.to,\n error: result.error.code,\n })\n return result\n }\n\n // 已完成的外部发送不因可选日志失败而返回失败,避免调用方重发;仍记录持久化失败。\n if (repo) {\n void saveSendRecord(repo, preprocessed.data, 'sent', providerName, result.data.messageId).then((saved) => {\n if (!saved.success)\n logger.warn('Failed to persist sent message log', { provider: providerName, code: saved.error.code })\n })\n }\n\n return result\n}\n","/**\n * @h-ai/reach — 模板引擎\n *\n * 本文件提供触达模块的模板定义与渲染功能。\n * 所有模板通过数据库持久化存储,避免多节点部署时的状态不一致。\n * @module reach-template\n */\n\nimport type { HaiResult } from '@h-ai/core'\nimport type { ReachTemplate, ReachTemplateRegistry, RenderedTemplate } from './reach-types.js'\n\nimport type { TemplateRepository } from './repositories/reach-repository-template.js'\nimport { err, ok } from '@h-ai/core'\n\nimport { reachM } from './reach-i18n.js'\nimport { HaiReachError } from './reach-types.js'\n\n// ─── 变量渲染 ───\n\n/**\n * 用自身属性替换{word}占位符,未知变量保留原文;不读取原型链,不做HTML转义。\n * @param template - 模板文本。\n * @param vars - 字符串变量。\n * @returns 渲染文本,最终HTML安全上下文另行处理。\n *\n * @example 模块内部上下文\n * ```ts\n * const text = renderString('Hello {name}, {missing}', { name: 'Alice' })\n * ```\n */\nfunction renderString(template: string, vars: Record<string, string>): string {\n return template.replace(/\\{(\\w+)\\}/g, (match, key: string) => {\n return Object.hasOwn(vars, key) ? vars[key] : match\n })\n}\n\n/**\n * 将持久模板转换为主题/正文,渲染异常返回TEMPLATE_RENDER_FAILED,不改写原模板。\n * @param template - 已解析模板。\n * @param vars - 变量字典。\n * @returns 渲染HaiResult,结果不代表内容已消毒或消息已发送。\n *\n * @example 模块内部上下文\n * ```ts\n * const result = renderTemplate({ name: 'welcome', provider: 'webhook', body: 'Hello {name}' }, { name: 'Alice' })\n * if (!result.success) throw new Error(result.error.message)\n * ```\n */\nfunction renderTemplate(template: ReachTemplate, vars: Record<string, string>): HaiResult<RenderedTemplate> {\n try {\n const rendered: RenderedTemplate = {\n subject: template.subject ? renderString(template.subject, vars) : undefined,\n body: renderString(template.body, vars),\n }\n return ok(rendered)\n }\n catch (error) {\n return err(\n HaiReachError.TEMPLATE_RENDER_FAILED,\n reachM('reach_templateRenderFailed', {\n params: { error: error instanceof Error ? error.message : String(error) },\n }),\n error,\n )\n }\n}\n\n// ─── 无数据库时的错误 ───\n\nfunction noDbError(msgKey: 'reach_templateSaveFailed' | 'reach_templateDeleteFailed'): HaiResult<never> {\n return err(\n HaiReachError.SEND_FAILED,\n reachM(msgKey, { params: { error: 'database not available' } }),\n )\n}\n\n// ─── 模板注册表工厂 ───\n\n/**\n * 内部数据库模板注册表工厂;无repo时明确不保存到内存,写入返回失败、解析找不到模板、列表为空。\n * @param repo - 已初始化的模板数据库仓库。\n * @returns 注册表;批量保存逐条upsert,不承诺事务全有全无。\n *\n * @example 模块内部上下文\n * ```ts\n * const registry = createTemplateRegistry()\n * const result = await registry.save({ name: 'welcome', provider: 'webhook', subject: 'Welcome {userName}', body: 'Hello {userName}' })\n * if (!result.success) { const code = result.error.code }\n * // 无数据库明确失败;应用通过reach.template使用持久注册表。\n * ```\n */\nexport function createTemplateRegistry(repo?: TemplateRepository | null): ReachTemplateRegistry {\n return {\n async resolve(name: string): Promise<HaiResult<ReachTemplate>> {\n if (!repo) {\n return err(\n HaiReachError.TEMPLATE_NOT_FOUND,\n reachM('reach_templateNotFound', { params: { template: name } }),\n )\n }\n\n const dbResult = await repo.findByName(name)\n if (!dbResult.success) {\n return dbResult\n }\n if (dbResult.data) {\n return ok({\n name: dbResult.data.name,\n provider: dbResult.data.provider,\n subject: dbResult.data.subject ?? undefined,\n body: dbResult.data.body,\n })\n }\n\n return err(\n HaiReachError.TEMPLATE_NOT_FOUND,\n reachM('reach_templateNotFound', { params: { template: name } }),\n )\n },\n\n async save(template: ReachTemplate): Promise<HaiResult<void>> {\n if (!repo) {\n return noDbError('reach_templateSaveFailed')\n }\n return repo.upsert(template)\n },\n\n async saveBatch(templates: ReachTemplate[]): Promise<HaiResult<void>> {\n if (!repo) {\n return noDbError('reach_templateSaveFailed')\n }\n const results = await Promise.all(templates.map(t => repo!.upsert(t)))\n const failed = results.find(r => !r.success)\n if (failed) {\n return failed\n }\n return ok(undefined)\n },\n\n async remove(name: string): Promise<HaiResult<void>> {\n if (!repo) {\n return noDbError('reach_templateDeleteFailed')\n }\n return repo.deleteByName(name)\n },\n\n async list(): Promise<HaiResult<ReachTemplate[]>> {\n if (!repo) {\n return ok([])\n }\n return repo.listTemplates()\n },\n\n async render(name: string, vars: Record<string, string>): Promise<HaiResult<RenderedTemplate>> {\n const resolved = await this.resolve(name)\n if (!resolved.success) {\n return resolved\n }\n return renderTemplate(resolved.data, vars)\n },\n }\n}\n","/** 发送账本升级仅处理 Reach 自己的表;旧在途消息隔离,不能猜测发送结果。 */\nimport type { ReldbFunctions, SchemaMigration } from '@h-ai/reldb'\nimport { randomUUID } from 'node:crypto'\nimport { ok } from '@h-ai/core'\nimport { migrateSchema, reldb, schemaColumns } from '@h-ai/reldb'\n\n/**\n * 预检或升级 Reach 队列。旧凭据相关载荷清除,待发凭据丢弃,保留发送状态和历史标识。\n * @example\n * ```ts\n * import { migrateReach } from '@h-ai/reach'\n * const plan = await migrateReach({ dryRun: true })\n * ```\n */\nexport function migrateReach(options: { dryRun?: boolean } = {}, db: ReldbFunctions = reldb) {\n const definitions = { queue_id: db.config?.type === 'mysql' ? 'VARCHAR(255)' : 'TEXT', retry_safe: 'BOOLEAN NOT NULL DEFAULT FALSE', attempt_count: 'BIGINT NOT NULL DEFAULT 0', max_attempts: 'BIGINT NOT NULL DEFAULT 5', next_attempt_at: 'BIGINT NOT NULL DEFAULT 0', retry_delay_ms: 'BIGINT NOT NULL DEFAULT 60000', max_retry_delay_ms: 'BIGINT NOT NULL DEFAULT 3600000', last_failure: 'TEXT' }\n const steps: SchemaMigration[] = [{\n version: 1,\n name: 'durable-delivery-ledger',\n async needed(sql) {\n const found = await schemaColumns(db, 'hai_reach_send_log', sql)\n return found.success ? ok(found.data.length > 0 && Object.keys(definitions).some(name => !found.data.includes(name))) : found\n },\n async apply(sql) {\n const found = await schemaColumns(db, 'hai_reach_send_log', sql)\n if (!found.success)\n return found\n for (const [name, definition] of Object.entries(definitions)) {\n if (found.data.includes(name))\n continue\n const added = await sql.execute(`ALTER TABLE hai_reach_send_log ADD COLUMN ${name} ${definition}`)\n if (!added.success)\n return added\n }\n return ok(undefined)\n },\n }, {\n version: 2,\n name: 'legacy-delivery-isolation',\n async needed(sql) {\n const found = await schemaColumns(db, 'hai_reach_send_log', sql)\n if (!found.success || !found.data.length)\n return found.success ? ok(false) : found\n if (!found.data.includes('queue_id'))\n return ok(true)\n const row = await sql.get('SELECT id FROM hai_reach_send_log WHERE queue_id IS NULL AND status IN (\\'pending\\',\\'processing\\',\\'unknown\\') LIMIT 1')\n return row.success ? ok(Boolean(row.data)) : row\n },\n async apply(sql) {\n const isolated = await sql.execute('UPDATE hai_reach_send_log SET status=\\'unknown\\',processing_owner=NULL,locked_until=NULL WHERE status=\\'processing\\' AND queue_id IS NULL')\n if (!isolated.success)\n return isolated\n while (true) {\n const rows = await sql.query<{ id: number }>('SELECT id FROM hai_reach_send_log WHERE queue_id IS NULL AND status IN (\\'pending\\',\\'unknown\\') ORDER BY id LIMIT 100')\n if (!rows.success)\n return rows\n for (const row of rows.data) {\n const saved = await sql.execute('UPDATE hai_reach_send_log SET queue_id=? WHERE id=? AND queue_id IS NULL', [randomUUID(), row.id])\n if (!saved.success)\n return saved\n }\n if (rows.data.length < 100)\n return ok(undefined)\n }\n },\n }, {\n version: 3,\n name: 'credential-payload-retirement',\n async needed(sql) {\n const found = await schemaColumns(db, 'hai_reach_send_log', sql)\n if (!found.success || !found.data.length)\n return found.success ? ok(false) : found\n // 旧数据无法可靠识别正文中的任意凭据,升级一次性退役旧载荷,元数据和状态保留。\n const ledger = await schemaColumns(db, 'hai_schema_migrations', sql)\n if (!ledger.success)\n return ledger\n const version = ledger.data.length ? await sql.get<{ version: number }>('SELECT version FROM hai_schema_migrations WHERE module=?', ['reach']) : ok(null)\n if (!version.success)\n return version\n if (Number(version.data?.version ?? 0) >= 3)\n return ok(false)\n const row = await sql.get('SELECT id FROM hai_reach_send_log WHERE subject IS NOT NULL OR body IS NOT NULL OR template IS NOT NULL OR vars_json IS NOT NULL OR extra_json IS NOT NULL LIMIT 1')\n return row.success ? ok(Boolean(row.data)) : row\n },\n async apply(sql) {\n return sql.execute('UPDATE hai_reach_send_log SET body=NULL,subject=NULL,vars_json=NULL,extra_json=NULL,template=NULL,status=CASE WHEN status IN (\\'pending\\',\\'processing\\') THEN \\'discarded\\' WHEN status=\\'sending\\' THEN \\'unknown\\' ELSE status END,processing_owner=NULL,locked_until=NULL WHERE subject IS NOT NULL OR body IS NOT NULL OR template IS NOT NULL OR vars_json IS NOT NULL OR extra_json IS NOT NULL')\n },\n }, {\n version: 4,\n name: 'unique-operation-receipts',\n async needed(sql) {\n const columns = await schemaColumns(db, 'hai_reach_send_log', sql)\n if (!columns.success || !columns.data.length)\n return columns.success ? ok(false) : columns\n if (!columns.data.includes('queue_id'))\n return ok(true)\n if (db.config?.type === 'sqlite') {\n const indexes = await sql.query<{ name: string, unique: number }>('PRAGMA index_list(hai_reach_send_log)')\n if (!indexes.success)\n return indexes\n for (const index of indexes.data) {\n if (!index.unique || !/^\\w+$/.test(index.name))\n continue\n const fields = await sql.query<{ name: string }>(`PRAGMA index_info(${index.name})`)\n if (!fields.success)\n return fields\n if (fields.data.length === 1 && fields.data[0]?.name === 'queue_id')\n return ok(false)\n }\n return ok(true)\n }\n const found = db.config?.type === 'mysql'\n ? await sql.get('SELECT index_name FROM information_schema.statistics WHERE table_schema=DATABASE() AND table_name=? AND non_unique=0 GROUP BY index_name HAVING COUNT(*)=1 AND MAX(column_name)=?', ['hai_reach_send_log', 'queue_id'])\n : await sql.get('SELECT i.indexrelid FROM pg_index i JOIN pg_class t ON t.oid=i.indrelid JOIN pg_namespace n ON n.oid=t.relnamespace JOIN pg_attribute a ON a.attrelid=t.oid AND a.attnum=ANY(i.indkey) WHERE t.relname=? AND n.nspname=current_schema() AND i.indisunique AND i.indnatts=1 AND a.attname=?', ['hai_reach_send_log', 'queue_id'])\n return found.success ? ok(!found.data) : found\n },\n async apply(sql) {\n return sql.execute('CREATE UNIQUE INDEX uq_hai_reach_queue_v1 ON hai_reach_send_log(queue_id)')\n },\n }]\n return migrateSchema(db, 'reach', steps, options)\n}\n","/**\n * @h-ai/reach — 发送日志存储实现\n *\n * 基于 @h-ai/reldb 的发送日志存储实现。\n * @module reach-repository-send-log\n */\n\nimport type { HaiResult, PaginatedResult } from '@h-ai/core'\nimport type { CrudPageOptions, CrudQueryOptions, DmlWithTxOperations, ReldbCrudFieldDefinition, ReldbCrudRepository, ReldbFunctions } from '@h-ai/reldb'\nimport type { ReachQueueFailure, ReachQueueStatus, ReachUnknownResolution } from '../reach-types.js'\nimport { err, ok } from '@h-ai/core'\nimport { BaseReldbCrudRepository } from '@h-ai/reldb'\n\nimport { reachM } from '../reach-i18n.js'\nimport { migrateReach } from '../reach-migration.js'\nimport { HaiReachError } from '../reach-types.js'\n\n// ─── 发送日志实体类型 ───\n\n/**\n * 内部发送日志状态别名,与公共队列状态一致;不把unknown自动降级成pending。\n *\n * @example 模块内部上下文\n * ```ts\n * const status: SendLogStatus = 'unknown'\n * ```\n */\nexport type SendLogStatus = ReachQueueStatus\n\n/**\n * 内部有界领取参数;owner唯一,now仅测试注入,eligibleBefore固定本轮资格截止避免毒消息自旋。\n *\n * @example 模块内部上下文\n * ```ts\n * const options: SendLogClaimOptions = { limit: 1, lockMs: 600000, eligibleBefore: Date.now() }\n * ```\n */\nexport interface SendLogClaimOptions {\n /** 单批最多 claim 的记录数 */\n limit?: number\n /** 仅本地 SQLite 测试可注入;共享数据库始终使用执行时钟。 */\n now?: number\n /** 本轮扫描截止时间;重试后 nextAttemptAt 超出本轮时不再重复领取。 */\n eligibleBefore?: number\n /** claim 锁持续时间(毫秒) */\n lockMs?: number\n}\n\n/**\n * 内部持久日志实体,包含敏感收件人与正文;仅运维安全投影可对外,绝不把整行写日志。\n * 毫秒时间/次数/BIGINT读取严格映射安全整数number,和模板Date时间列不同。\n *\n * @example 模块内部上下文\n * ```ts\n * const { reldb } = await import('@h-ai/reldb')\n * const connected = await reldb.init({ type: 'postgresql', host: 'db', port: 5432, database: 'hai', user: 'postgres', password: process.env.HAI_DB_PASSWORD })\n * if (!connected.success) throw new Error(connected.error.message)\n * try {\n * const created = await createSendLogRepository(reldb)\n * if (!created.success) throw new Error(created.error.message)\n * const repository = created.data\n * const pending = await repository.findPending()\n * if (!pending.success) throw new Error(pending.error.message)\n * const row: StoredSendLog | undefined = pending.data[0]\n * if (row) { const attempts = row.attemptCount }\n * }\n * finally {\n * const closed = await reldb.close()\n * if (!closed.success) throw new Error(closed.error.message)\n * }\n * ```\n */\nexport interface StoredSendLog {\n /** 自增主键;驱动 BIGSERIAL/BIGINT 值读取后校验为安全整数 number。 */\n id: number\n /** 入队回执 UUID;延迟消息创建时写入,可用 queue_id 列追踪持久化状态。 */\n queueId: string | null\n /** Provider 名称 */\n provider: string\n /** 接收方地址 */\n toAddr: string\n /** 邮件主题 */\n subject: string | null\n /** 消息正文 */\n body: string | null\n /** 模板名称 */\n template: string | null\n /** 模板变量 JSON */\n varsJson: string | null\n /** 扩展参数 JSON */\n extraJson: string | null\n /** 发送状态 */\n status: SendLogStatus\n /** 已开始的投递是否可由接收端持久幂等保护;用于崩溃恢复决策。 */\n retrySafe?: boolean\n /** 已领取次数;领取时原子递增,崩溃恢复同样消耗预算。 */\n attemptCount: number\n /** 入队时保存的自动领取次数上限。 */\n maxAttempts: number\n /** 下次可领取时间(Unix 毫秒;BIGINT 安全 number)。 */\n nextAttemptAt: number\n /** 入队时保存的首次退避毫秒数。 */\n retryDelayMs: number\n /** 入队时保存的指数退避上限毫秒数。 */\n maxRetryDelayMs: number\n /** 不含消息正文的最近失败分类。 */\n lastFailure: ReachQueueFailure | null\n /** Provider 返回的消息 ID */\n messageId: string | null\n /** 当前 claim owner(处理中时存在) */\n processingOwner?: string\n /** 当前 claim 过期时间(Unix 时间戳毫秒) */\n lockedUntil?: number\n /** 创建时间(Unix 时间戳毫秒;BIGINT 读取后校验为安全整数 number)。 */\n createdAt: number\n /** 最近更新时间(Unix 时间戳毫秒;BIGINT 读取后校验为安全整数 number)。 */\n updatedAt?: number\n}\n\n// ─── 发送日志存储接口 ───\n\n/**\n * 内部共享数据库队列/日志仓库;逐条条件领取和sending门闩决定资格,进程cache锁仅减少扫描。\n * 非幂等sending过期隔离unknown;有限重试/退避持久化,dead/unknown只走受信条件管理入口。\n *\n * @example 模块内部上下文\n * ```ts\n * const { reldb } = await import('@h-ai/reldb')\n * const connected = await reldb.init({ type: 'postgresql', host: 'db', port: 5432, database: 'hai', user: 'postgres', password: process.env.HAI_DB_PASSWORD })\n * if (!connected.success) throw new Error(connected.error.message)\n * try {\n * const created = await createSendLogRepository(reldb)\n * if (!created.success) throw new Error(created.error.message)\n * const repository = created.data\n * const claimed = await repository.claimPendingBatch('worker:' + crypto.randomUUID(), { limit: 1 })\n * if (!claimed.success) throw new Error(claimed.error.message)\n * const count = claimed.data.length\n * }\n * finally {\n * const closed = await reldb.close()\n * if (!closed.success) throw new Error(closed.error.message)\n * }\n * ```\n */\nexport interface SendLogRepository extends ReldbCrudRepository<StoredSendLog> {\n /** 只结算无载荷的凭据回执;不覆盖已恢复或人工裁定的状态。 */\n settleSensitive: (queueId: string, status: 'sent' | 'unknown' | 'discarded', messageId?: string) => Promise<HaiResult<void>>\n /**\n * 获取所有待发送记录(按创建时间升序)\n * @param tx - 可选受控数据库事务。\n * @returns HaiResult;先检查success,条件提交失败不能伪装成功。\n *\n * @example 模块内部上下文\n * ```ts\n * async function pending(repository: SendLogRepository) {\n * const rows = await repository.findPending()\n * if (!rows.success) throw new Error(rows.error.message)\n * return rows.data\n * // 普通查询没有投递权,发送前必须claim。\n * }\n * ```\n */\n findPending: (tx?: DmlWithTxOperations) => Promise<HaiResult<StoredSendLog[]>>\n /** 只供扫描批次冻结资格截止点;租约授权仍在最终 SQL 内判断。 */\n currentTime: () => Promise<HaiResult<number>>\n\n /**\n * 按创建时间顺序 claim 一批待发送记录。\n * 可接管已过期 processing 和接收端幂等的 sending;非幂等 sending 过期后隔离为 unknown。\n * PostgreSQL / MySQL 优先使用行锁 + `SKIP LOCKED` 降低多节点争抢;\n * SQLite 不支持该能力时退回条件更新(compare-and-set)模式。\n * @param owner - 当前唯一领取凭据,不记录或暴露给客户端。\n * @param options - 实际操作输入,按本方法约束校验。\n * @returns HaiResult;先检查success,条件提交失败不能伪装成功。\n *\n * @example 模块内部上下文\n * ```ts\n * async function claim(repository: SendLogRepository) {\n * const owner = 'worker:' + crypto.randomUUID()\n * const rows = await repository.claimPendingBatch(owner, { limit: 1, lockMs: 600000 })\n * if (!rows.success) throw new Error(rows.error.message)\n * return { owner, rows: rows.data }\n * }\n * ```\n */\n claimPendingBatch: (owner: string, options?: SendLogClaimOptions) => Promise<HaiResult<StoredSendLog[]>>\n\n /**\n * 取消一次领取并按失败策略退避/死信;不立即重排队,不绕过次数上限。\n * 仅有效 owner 可以释放 processing 或可安全重试的 sending;未知外部副作用不能重排队。\n * @param id - 已读取/领取的持久记录主键。\n * @param owner - 当前唯一领取凭据,不记录或暴露给客户端。\n * @param tx - 可选受控数据库事务。\n * @returns HaiResult;先检查success,条件提交失败不能伪装成功。\n *\n * @example 模块内部上下文\n * ```ts\n * async function release(repository: SendLogRepository, row: StoredSendLog, owner: string) {\n * const released = await repository.releaseClaim(row.id, owner)\n * if (!released.success) throw new Error(released.error.message)\n * // 只用于当前有效owner/安全状态;内部持久退避,不立即热循环重领。\n * }\n * ```\n */\n releaseClaim: (id: number, owner: string, tx?: DmlWithTxOperations) => Promise<HaiResult<void>>\n\n /**\n * 当前有效 owner 提交失败;永久失败/超限进入 dead,其余按持久策略退避。\n * @param row - 实际操作输入,按本方法约束校验。\n * @param owner - 当前唯一领取凭据,不记录或暴露给客户端。\n * @param reason - 实际操作输入,按本方法约束校验。\n * @param permanent - 实际操作输入,按本方法约束校验。\n * @returns HaiResult;先检查success,条件提交失败不能伪装成功。\n *\n * @example 模块内部上下文\n * ```ts\n * async function failed(repository: SendLogRepository, row: StoredSendLog, owner: string) {\n * const released = await repository.failClaim(row, owner, 'provider_unavailable')\n * if (!released.success) throw new Error(released.error.message)\n * }\n * ```\n */\n failClaim: (row: StoredSendLog, owner: string, reason: ReachQueueFailure, permanent?: boolean) => Promise<HaiResult<void>>\n /**\n * 根因修复后仅恢复指定版本的 dead,重置次数但保留幂等键和持久策略。\n * @param queueId - 持久UUID回执,不代替运维权限。\n * @param expectedUpdatedAt - 最近读取的更新时间版本,不能使用当前时间替代。\n * @returns HaiResult;先检查success,条件提交失败不能伪装成功。\n *\n * @example 模块内部上下文\n * ```ts\n * async function restore(repository: SendLogRepository, row: StoredSendLog) {\n * if (row.status !== 'dead' || !row.queueId || row.updatedAt === undefined) return\n * // 调用前已完成可信运维授权、根因修复与外部效果核对。\n * const restored = await repository.requeueDead(row.queueId, row.updatedAt)\n * if (!restored.success) throw new Error(restored.error.message)\n * }\n * ```\n */\n requeueDead: (queueId: string, expectedUpdatedAt: number) => Promise<HaiResult<void>>\n\n /**\n * 发送前原子校验有效 owner 并持久化 sending;失败时绝不能调用外部 Provider。\n * @param id - 已读取/领取的持久记录主键。\n * @param owner - 当前唯一领取凭据,不记录或暴露给客户端。\n * @param retrySafe - 真实接收端持久幂等能力声明。\n * @param lockMs - 实际操作输入,按本方法约束校验。\n * @returns HaiResult;先检查success,条件提交失败不能伪装成功。\n *\n * @example 模块内部上下文\n * ```ts\n * async function begin(repository: SendLogRepository, row: StoredSendLog, owner: string, receiverDeduplicates: boolean) {\n * const started = await repository.beginDelivery(row.id, owner, receiverDeduplicates)\n * if (!started.success) return started\n * // 仅此success分支可调用对应Provider;receiverDeduplicates须来自真实能力声明。\n * return started\n * }\n * ```\n */\n beginDelivery: (id: number, owner: string, retrySafe: boolean, lockMs?: number) => Promise<HaiResult<void>>\n /**\n * 仅续租尚未到期且仍由该 owner 持有的记录;过期后不得复活旧 owner。\n * @param id - 已读取/领取的持久记录主键。\n * @param owner - 当前唯一领取凭据,不记录或暴露给客户端。\n * @param lockMs - 实际操作输入,按本方法约束校验。\n * @returns HaiResult;先检查success,条件提交失败不能伪装成功。\n *\n * @example 模块内部上下文\n * ```ts\n * async function renew(repository: SendLogRepository, row: StoredSendLog, owner: string) {\n * const result = await repository.renewClaim(row.id, owner)\n * if (!result.success) return result\n * return result\n * // 续租失败不能恢复旧owner,已开始的外部效果按幂等/unknown边界处置。\n * }\n * ```\n */\n renewClaim: (id: number, owner: string, lockMs?: number) => Promise<HaiResult<void>>\n /**\n * 标记无法确定外部结果的记录,停止自动投递;仅当前 owner 可以提交。\n * @param id - 已读取/领取的持久记录主键。\n * @param owner - 当前唯一领取凭据,不记录或暴露给客户端。\n * @returns HaiResult;先检查success,条件提交失败不能伪装成功。\n *\n * @example 模块内部上下文\n * ```ts\n * async function isolate(repository: SendLogRepository, row: StoredSendLog, owner: string) {\n * const marked = await repository.markUnknown(row.id, owner)\n * if (!marked.success) throw new Error(marked.error.message)\n * }\n * ```\n */\n markUnknown: (id: number, owner: string) => Promise<HaiResult<void>>\n /**\n * 人工核对并隔离旧 worker 后裁定 unknown;条件提交阻止并发覆盖。\n * @param queueId - 持久UUID回执,不代替运维权限。\n * @param resolution - 实际操作输入,按本方法约束校验。\n * @returns HaiResult;先检查success,条件提交失败不能伪装成功。\n *\n * @example 模块内部上下文\n * ```ts\n * async function resolve(repository: SendLogRepository, queueId: string) {\n * // 可信运维已授权、隔离旧worker并核对外部记录,不进行自动重发。\n * const resolved = await repository.resolveUnknown(queueId, { status: 'discarded' })\n * if (!resolved.success) throw new Error(resolved.error.message)\n * }\n * ```\n */\n resolveUnknown: (queueId: string, resolution: ReachUnknownResolution) => Promise<HaiResult<void>>\n\n /**\n * 将记录标记为已发送。\n * 仅持有 sending 有效租约的 owner 可以提交;过期 owner 不能覆盖新节点状态。\n * @param id - 已读取/领取的持久记录主键。\n * @param owner - 当前唯一领取凭据,不记录或暴露给客户端。\n * @param messageId - 实际操作输入,按本方法约束校验。\n * @param tx - 可选受控数据库事务。\n * @returns HaiResult;先检查success,条件提交失败不能伪装成功。\n *\n * @example 模块内部上下文\n * ```ts\n * async function commit(repository: SendLogRepository, row: StoredSendLog, owner: string, messageId: string) {\n * const marked = await repository.markSent(row.id, owner, messageId)\n * if (!marked.success) throw new Error(marked.error.message)\n * // 提交失败不代表外部未发送,不得盲目重新调用Provider。\n * }\n * ```\n */\n markSent: (id: number, owner: string, messageId?: string, tx?: DmlWithTxOperations) => Promise<HaiResult<void>>\n}\n\n// ─── 发送日志存储实现 ───\n\n/** 表名 */\nconst TABLE_NAME = 'hai_reach_send_log'\nconst DEFAULT_CLAIM_BATCH_SIZE = 100\nconst DEFAULT_CLAIM_LOCK_MS = 10 * 60 * 1000\nfunction claimableWhere(clock: string): string {\n return `(attempt_count < max_attempts AND ((status = ? AND next_attempt_at <= ?) OR (status = ? AND updated_at < ? AND (locked_until IS NULL OR locked_until <= ${clock})) OR (status = ? AND updated_at < ? AND retry_safe = TRUE AND locked_until <= ${clock})))`\n}\n\n/**\n * 与固定领取SQL一一对应的参数,仅允许pending、过期processing或幂等sending重领。\n * @param eligibleBefore - 固定资格截止,避免本轮新退避消息被立即重复领取。\n * @returns 参数数组,绝不拼接不可信SQL。\n *\n * @example 模块内部上下文\n * ```ts\n * const now = Date.now()\n * const params = claimableParams(now)\n * ```\n */\nfunction claimableParams(eligibleBefore: number): unknown[] {\n return ['pending', eligibleBefore, 'processing', eligibleBefore, 'sending', eligibleBefore]\n}\n\nfunction buildSendLogError<T>(detail: string, cause?: unknown): HaiResult<T> {\n return err(\n HaiReachError.SEND_FAILED,\n reachM('reach_sendFailed', { params: { error: detail } }),\n cause,\n )\n}\n\n/**\n * 读取边界统一校验BIGINT时间/次数/策略,支持驱动number/bigint/十进制字符串并拒绝精度丢失。\n * @param row - 驱动返回的日志行;不改写原对象。\n * @returns 安全整数number行或失败,不能静默截断精度。\n *\n * @example 模块内部上下文\n * ```ts\n * function normalize(row: StoredSendLog) {\n * const result = normalizeSendLog(row)\n * if (!result.success) throw new Error(result.error.message)\n * return result.data\n * }\n * ```\n */\nfunction normalizeSendLog(row: StoredSendLog): HaiResult<StoredSendLog> {\n const normalized = { ...row }\n for (const field of ['id', 'createdAt', 'updatedAt', 'lockedUntil', 'nextAttemptAt', 'attemptCount', 'maxAttempts', 'retryDelayMs', 'maxRetryDelayMs'] as const) {\n const value: unknown = row[field]\n if (value === undefined && (field === 'updatedAt' || field === 'lockedUntil'))\n continue\n const validType = typeof value === 'number' || typeof value === 'bigint'\n || (typeof value === 'string' && /^\\d+$/.test(value))\n const integer = validType ? Number(value) : Number.NaN\n if (!Number.isSafeInteger(integer) || integer < 0)\n return buildSendLogError(reachM('reach_invalidStoredInteger', { params: { field } }))\n Object.assign(normalized, { [field]: integer })\n }\n return ok(normalized)\n}\n\n/**\n * 批量、分页及claim回读共用同一规范化,任一行无效即失败,不返回混合时间类型。\n * @param rows - 数据库读取行。\n * @returns 规范化数组或原失败。\n *\n * @example 模块内部上下文\n * ```ts\n * function normalizeBatch(rows: StoredSendLog[]) {\n * const result = normalizeSendLogs(rows)\n * if (!result.success) throw new Error(result.error.message)\n * return result.data\n * }\n * ```\n */\nfunction normalizeSendLogs(rows: StoredSendLog[]): HaiResult<StoredSendLog[]> {\n const normalized: StoredSendLog[] = []\n for (const row of rows) {\n const result = normalizeSendLog(row)\n if (!result.success)\n return result\n normalized.push(result.data)\n }\n return ok(normalized)\n}\n\n/** 字段定义 */\nconst SEND_LOG_FIELDS: ReldbCrudFieldDefinition[] = [\n ...[\n { fieldName: 'attemptCount', columnName: 'attempt_count', value: 0 },\n { fieldName: 'maxAttempts', columnName: 'max_attempts', value: 5 },\n { fieldName: 'retryDelayMs', columnName: 'retry_delay_ms', value: 60_000 },\n { fieldName: 'maxRetryDelayMs', columnName: 'max_retry_delay_ms', value: 3_600_000 },\n { fieldName: 'nextAttemptAt', columnName: 'next_attempt_at', value: 0 },\n ].map(({ fieldName, columnName, value }) => ({\n fieldName,\n columnName,\n def: { type: 'BIGINT' as const, notNull: true, defaultValue: value },\n select: true,\n create: true,\n update: true,\n })),\n {\n fieldName: 'lastFailure',\n columnName: 'last_failure',\n def: { type: 'TEXT' as const },\n select: true,\n create: true,\n update: true,\n },\n {\n fieldName: 'retrySafe',\n columnName: 'retry_safe',\n def: { type: 'BOOLEAN' as const, notNull: true, defaultValue: false },\n select: true,\n create: true,\n update: true,\n },\n {\n fieldName: 'queueId',\n columnName: 'queue_id',\n def: { type: 'TEXT' as const, unique: true },\n select: true,\n create: true,\n update: false,\n },\n {\n fieldName: 'id',\n columnName: 'id',\n def: { type: 'INTEGER' as const, primaryKey: true, autoIncrement: true },\n select: true,\n create: false,\n update: false,\n },\n {\n fieldName: 'provider',\n columnName: 'provider',\n def: { type: 'TEXT' as const, notNull: true },\n select: true,\n create: true,\n update: false,\n },\n {\n fieldName: 'toAddr',\n columnName: 'to_addr',\n def: { type: 'TEXT' as const, notNull: true },\n select: true,\n create: true,\n update: false,\n },\n {\n fieldName: 'subject',\n columnName: 'subject',\n def: { type: 'TEXT' as const },\n select: true,\n create: true,\n update: false,\n },\n {\n fieldName: 'body',\n columnName: 'body',\n def: { type: 'TEXT' as const },\n select: true,\n create: true,\n update: false,\n },\n {\n fieldName: 'template',\n columnName: 'template',\n def: { type: 'TEXT' as const },\n select: true,\n create: true,\n update: false,\n },\n {\n fieldName: 'varsJson',\n columnName: 'vars_json',\n def: { type: 'TEXT' as const },\n select: true,\n create: true,\n update: false,\n },\n {\n fieldName: 'extraJson',\n columnName: 'extra_json',\n def: { type: 'TEXT' as const },\n select: true,\n create: true,\n update: false,\n },\n {\n fieldName: 'status',\n columnName: 'status',\n def: { type: 'TEXT' as const, notNull: true },\n select: true,\n create: true,\n update: true,\n },\n {\n fieldName: 'messageId',\n columnName: 'message_id',\n def: { type: 'TEXT' as const },\n select: true,\n create: true,\n update: true,\n },\n {\n fieldName: 'processingOwner',\n columnName: 'processing_owner',\n def: { type: 'TEXT' as const },\n select: true,\n create: true,\n update: true,\n },\n {\n fieldName: 'lockedUntil',\n columnName: 'locked_until',\n def: { type: 'BIGINT' as const },\n select: true,\n create: true,\n update: true,\n },\n {\n fieldName: 'createdAt',\n columnName: 'created_at',\n def: { type: 'BIGINT' as const, notNull: true },\n select: true,\n create: true,\n update: false,\n },\n {\n fieldName: 'updatedAt',\n columnName: 'updated_at',\n def: { type: 'BIGINT' as const, notNull: true },\n select: true,\n create: true,\n update: true,\n },\n]\n\n/** 发送日志存储单例缓存 */\nlet sendLogRepoInstance: SendLogRepository | null = null\nlet sendLogRepoSqlOps: unknown = null\n\n/**\n * 内部关闭时释放仓库包装对象引用;不删除数据库记录,不是跨节点业务状态重置。\n *\n * @example 模块内部上下文\n * ```ts\n * resetSendLogRepoSingleton()\n * ```\n */\nexport function resetSendLogRepoSingleton(): void {\n sendLogRepoInstance = null\n sendLogRepoSqlOps = null\n}\n\n/**\n * 内部创建/复用当前db.sql生命周期的队列仓库,确认表就绪后才返回;缓存只持有包装对象。\n * @param db - 已初始化数据库;生产共享PostgreSQL/MySQL。\n * @returns 仓库HaiResult,建表/连接故障失败,不回退进程队列。\n *\n * @example 模块内部上下文\n * ```ts\n * const { reldb } = await import('@h-ai/reldb')\n * const connected = await reldb.init({ type: 'postgresql', host: 'db', port: 5432, database: 'hai', user: 'postgres', password: process.env.HAI_DB_PASSWORD })\n * if (!connected.success) throw new Error(connected.error.message)\n * try {\n * const created = await createSendLogRepository(reldb)\n * if (!created.success) throw new Error(created.error.message)\n * const repository = created.data\n * const pending = await repository.findPending()\n * if (!pending.success) throw new Error(pending.error.message)\n * const count = pending.data.length\n * }\n * finally {\n * const closed = await reldb.close()\n * if (!closed.success) throw new Error(closed.error.message)\n * }\n * ```\n */\nexport async function createSendLogRepository(db: ReldbFunctions): Promise<HaiResult<SendLogRepository>> {\n if (sendLogRepoInstance && sendLogRepoSqlOps === db.sql)\n return ok(sendLogRepoInstance)\n\n const migration = await migrateReach({}, db)\n if (!migration.success)\n return migration\n const repo = new DbSendLogRepository(db)\n // 触发表创建(BaseReldbCrudRepository 的表创建是异步的)\n const initResult = await repo.count()\n if (!initResult.success) {\n return err(\n HaiReachError.SEND_FAILED,\n reachM('reach_sendFailed', { params: { error: initResult.error.message } }),\n initResult.error,\n )\n }\n sendLogRepoInstance = repo\n sendLogRepoSqlOps = db.sql\n return ok(repo)\n}\n\n/**\n * 基于数据库的发送日志存储实现\n */\nclass DbSendLogRepository extends BaseReldbCrudRepository<StoredSendLog> implements SendLogRepository {\n /** 共享后端取真正执行时钟;本地 SQLite 保留可控的单进程测试时钟。 */\n private clockSql(localNow?: number): string {\n if (this.db.config?.type === 'postgresql')\n return 'CAST(FLOOR(EXTRACT(EPOCH FROM clock_timestamp()) * 1000) AS BIGINT)'\n if (this.db.config?.type === 'mysql')\n return 'CAST(UNIX_TIMESTAMP(SYSDATE(3)) * 1000 AS UNSIGNED)'\n return String(localNow ?? Date.now())\n }\n\n async currentTime(): Promise<HaiResult<number>> {\n const time = await this.sql().get<{ now: number | string }>(`SELECT ${this.clockSql()} AS now`)\n return time.success && time.data && Number.isSafeInteger(Number(time.data.now))\n ? ok(Number(time.data.now))\n : buildSendLogError(reachM('reach_claimLost'))\n }\n\n async settleSensitive(queueId: string, status: 'sent' | 'unknown' | 'discarded', messageId?: string): Promise<HaiResult<void>> {\n const settled = await this.sql().execute(\n `UPDATE ${TABLE_NAME} SET status = ?, message_id = ?, locked_until = NULL, updated_at = ? WHERE queue_id = ? AND status = 'sending' AND body IS NULL AND vars_json IS NULL AND retry_safe = FALSE`,\n [status, messageId ?? null, Date.now(), queueId],\n )\n if (!settled.success)\n return settled\n return settled.data.changes === 1 ? ok(undefined) : buildSendLogError(reachM('reach_sensitiveDeliveryUnconfirmed'))\n }\n\n constructor(db: ReldbFunctions) {\n super(db, {\n table: TABLE_NAME,\n fields: SEND_LOG_FIELDS,\n idColumn: 'id',\n idField: 'id',\n createTableIfNotExists: true,\n })\n }\n\n /** 单条可空查询统一返回经过校验的毫秒 number。 */\n override async findById(id: unknown, tx?: DmlWithTxOperations): Promise<HaiResult<StoredSendLog | null>> {\n const result = await super.findById(id, tx)\n if (!result.success || result.data === null)\n return result\n return normalizeSendLog(result.data)\n }\n\n /** 单条必需查询统一返回经过校验的毫秒 number。 */\n override async getById(id: unknown, tx?: DmlWithTxOperations): Promise<HaiResult<StoredSendLog>> {\n const result = await super.getById(id, tx)\n return result.success ? normalizeSendLog(result.data) : result\n }\n\n /** 普通列表与 claim 回读共用同一 BIGINT 转换边界。 */\n override async findAll(options?: CrudQueryOptions, tx?: DmlWithTxOperations): Promise<HaiResult<StoredSendLog[]>> {\n const result = await super.findAll(options, tx)\n return result.success ? normalizeSendLogs(result.data) : result\n }\n\n /** 分页结果同样拒绝不安全的时间值,不返回经过舍入的毫秒数。 */\n override async findPage(options: CrudPageOptions, tx?: DmlWithTxOperations): Promise<HaiResult<PaginatedResult<StoredSendLog>>> {\n const result = await super.findPage(options, tx)\n if (!result.success)\n return result\n const rows = normalizeSendLogs(result.data.items)\n return rows.success ? ok({ ...result.data, items: rows.data }) : rows\n }\n\n /** 获取所有待发送记录 */\n async findPending(tx?: DmlWithTxOperations): Promise<HaiResult<StoredSendLog[]>> {\n const result = await this.findAll({ where: 'status = ?', params: ['pending'], orderBy: 'created_at ASC' }, tx)\n if (!result.success) {\n return result\n }\n return ok(result.data)\n }\n\n /**\n * 使用数据库行锁 claim 一批记录。\n *\n * PostgreSQL / MySQL 支持 `FOR UPDATE SKIP LOCKED`:\n * - 当前事务锁住即将 claim 的行\n * - 并发事务会直接跳过这些行,而不是重复命中同一批记录\n * - 在 commit 前不会暴露给其它节点重复 claim\n */\n private async claimPendingIdsWithRowLocks(\n tx: DmlWithTxOperations,\n owner: string,\n clock: string,\n lockMs: number,\n limit: number,\n eligibleBefore: number,\n ): Promise<number[]> {\n const lockedRowsResult = await tx.query(\n `SELECT id\n FROM ${TABLE_NAME}\n WHERE ${claimableWhere(clock)}\n ORDER BY created_at ASC\n LIMIT ?\n FOR UPDATE SKIP LOCKED`,\n [...claimableParams(eligibleBefore), limit],\n )\n if (!lockedRowsResult.success) {\n throw lockedRowsResult.error\n }\n\n const claimedIds = lockedRowsResult.data.map((row) => {\n const id = typeof row.id === 'number' ? row.id : Number(row.id)\n if (!Number.isSafeInteger(id)) {\n throw new TypeError(`Invalid send log id returned when claiming pending batch: ${String(row.id)}`)\n }\n return id\n })\n if (claimedIds.length === 0) {\n return []\n }\n\n const placeholders = claimedIds.map(() => '?').join(', ')\n const updateResult = await tx.execute(\n `UPDATE ${TABLE_NAME}\n SET status = ?, processing_owner = ?, locked_until = ${clock} + ?, updated_at = ${clock}, attempt_count = attempt_count + 1\n WHERE id IN (${placeholders})`,\n ['processing', owner, lockMs, ...claimedIds],\n )\n if (!updateResult.success) {\n throw updateResult.error\n }\n\n return claimedIds\n }\n\n /**\n * SQLite 等不支持 `SKIP LOCKED` 的数据库回退到 compare-and-set claim。\n *\n * 流程是:先按顺序读候选,再用带条件的 UPDATE 抢占每一行。\n * 即使多个节点读到了同一候选,只有第一个满足条件的 UPDATE 会成功,\n * 从而避免同一行被两个节点同时 claim。\n */\n private async claimPendingIdsWithConditionalUpdates(\n tx: DmlWithTxOperations,\n owner: string,\n clock: string,\n lockMs: number,\n limit: number,\n eligibleBefore: number,\n ): Promise<number[]> {\n const candidatesResult = await this.findAll({\n where: claimableWhere(clock),\n params: claimableParams(eligibleBefore),\n orderBy: 'created_at ASC',\n limit,\n }, tx)\n if (!candidatesResult.success) {\n throw candidatesResult.error\n }\n\n const claimedIds: number[] = []\n for (const row of candidatesResult.data) {\n const updateResult = await tx.execute(\n `UPDATE ${TABLE_NAME}\n SET status = ?, processing_owner = ?, locked_until = ${clock} + ?, updated_at = ${clock}, attempt_count = attempt_count + 1\n WHERE id = ? AND ${claimableWhere(clock)}`,\n ['processing', owner, lockMs, row.id, ...claimableParams(eligibleBefore)],\n )\n if (!updateResult.success) {\n throw updateResult.error\n }\n if (updateResult.data.changes > 0) {\n claimedIds.push(row.id)\n }\n }\n\n return claimedIds\n }\n\n /** 根据已 claim 的 id 回读完整记录,统一返回给 flush 流程。 */\n private async loadClaimedRows(ids: number[], tx: DmlWithTxOperations): Promise<StoredSendLog[]> {\n const placeholders = ids.map(() => '?').join(', ')\n const claimedRowsResult = await this.findAll({\n where: `id IN (${placeholders})`,\n params: ids,\n orderBy: 'created_at ASC',\n }, tx)\n if (!claimedRowsResult.success) {\n throw claimedRowsResult.error\n }\n return claimedRowsResult.data\n }\n\n /** claim 一批待发送记录,避免多节点重复发送 */\n async claimPendingBatch(owner: string, options?: SendLogClaimOptions): Promise<HaiResult<StoredSendLog[]>> {\n const time = await this.currentTime()\n if (!time.success)\n return time\n const now = this.db.config?.type === 'sqlite' ? options?.now ?? time.data : time.data\n const clock = this.clockSql(now)\n const limit = options?.limit ?? DEFAULT_CLAIM_BATCH_SIZE\n const lockMs = options?.lockMs ?? DEFAULT_CLAIM_LOCK_MS\n const eligibleBefore = Math.min(now, options?.eligibleBefore ?? now)\n const dbType = this.db.config?.type\n\n const txResult = await this.db.tx.begin()\n if (!txResult.success) {\n return buildSendLogError(txResult.error.message, txResult.error)\n }\n\n const tx = txResult.data\n\n try {\n // 可能已产生不可去重的外部副作用:租约过期只能转 unknown,不能被新节点自动重发。\n const unknown = await tx.execute(\n `UPDATE ${TABLE_NAME} SET status = ?, processing_owner = NULL, locked_until = NULL, next_attempt_at = 0, updated_at = ${clock}\n WHERE status = ? AND retry_safe = FALSE AND (locked_until IS NULL OR locked_until <= ${clock})`,\n ['unknown', 'sending'],\n )\n if (!unknown.success)\n throw unknown.error\n // 最后一次领取后崩溃也不能无限接管;有效租约仍须等待到期,非幂等 sending 已先隔离。\n const exhausted = await tx.execute(\n `UPDATE ${TABLE_NAME}\n SET status = ?, last_failure = ?, processing_owner = NULL, locked_until = NULL, next_attempt_at = 0, updated_at = ${clock}\n WHERE attempt_count >= max_attempts\n AND (status = ? OR (status = ? AND (locked_until IS NULL OR locked_until <= ${clock}))\n OR (status = ? AND retry_safe = TRUE AND locked_until <= ${clock}))`,\n ['dead', 'attempts_exhausted', 'pending', 'processing', 'sending'],\n )\n if (!exhausted.success)\n throw exhausted.error\n const claimedIds = dbType === 'postgresql' || dbType === 'mysql'\n ? await this.claimPendingIdsWithRowLocks(tx, owner, clock, lockMs, limit, eligibleBefore)\n : await this.claimPendingIdsWithConditionalUpdates(tx, owner, clock, lockMs, limit, eligibleBefore)\n\n if (claimedIds.length === 0) {\n const commitResult = await tx.commit()\n if (!commitResult.success) {\n return buildSendLogError(commitResult.error.message, commitResult.error)\n }\n return ok([])\n }\n\n const claimedRows = await this.loadClaimedRows(claimedIds, tx)\n\n const commitResult = await tx.commit()\n if (!commitResult.success) {\n return buildSendLogError(commitResult.error.message, commitResult.error)\n }\n\n return ok(claimedRows)\n }\n catch (error) {\n await tx.rollback()\n return buildSendLogError(error instanceof Error ? error.message : String(error), error)\n }\n }\n\n /** 显式取消未完成的领取;仍按失败退避,避免调用方绕过自动重试预算。 */\n async releaseClaim(id: number, owner: string, tx?: DmlWithTxOperations): Promise<HaiResult<void>> {\n const row = await this.getById(id, tx)\n if (!row.success)\n return row\n return this.failClaim(row.data, owner, 'transient_failure', false, tx)\n }\n\n /** 失败提交与 owner、次数和有效租约同时比较,旧 worker 不能更改新领取的退避策略。 */\n async failClaim(row: StoredSendLog, owner: string, reason: ReachQueueFailure, permanent = false, tx?: DmlWithTxOperations): Promise<HaiResult<void>> {\n const clock = this.clockSql()\n const dead = permanent || row.attemptCount >= row.maxAttempts\n const delay = Math.min(row.maxRetryDelayMs, row.retryDelayMs * 2 ** Math.min(row.attemptCount - 1, 30))\n const result = await this.sql(tx).execute(\n `UPDATE ${TABLE_NAME}\n SET status = ?, last_failure = ?, next_attempt_at = ${dead ? '0' : `${clock} + ?`}, processing_owner = NULL, locked_until = NULL, updated_at = ${clock}\n WHERE id = ? AND processing_owner = ? AND locked_until > ${clock} AND attempt_count = ?\n AND (status = ? OR (status = ? AND retry_safe = TRUE))`,\n [dead ? 'dead' : 'pending', reason, ...dead ? [] : [delay], row.id, owner, row.attemptCount, 'processing', 'sending'],\n )\n if (!result.success) {\n return buildSendLogError(result.error.message, result.error)\n }\n if (result.data.changes === 0) {\n return buildSendLogError(reachM('reach_claimLost'))\n }\n return ok(undefined)\n }\n\n /** 仅当前 dead 版本可以恢复;保留 queueId,迟到的幂等 API 请求仍使用同一键。 */\n async requeueDead(queueId: string, expectedUpdatedAt: number): Promise<HaiResult<void>> {\n const clock = this.clockSql()\n const result = await this.sql().execute(\n `UPDATE ${TABLE_NAME} SET status = ?, attempt_count = 0, next_attempt_at = ${clock}, last_failure = NULL, updated_at = CASE WHEN updated_at >= ${clock} THEN updated_at + 1 ELSE ${clock} END\n WHERE queue_id = ? AND status = ? AND updated_at = ?`,\n ['pending', queueId, 'dead', expectedUpdatedAt],\n )\n return result.success && result.data.changes === 1 ? ok(undefined) : buildSendLogError(reachM('reach_deadRequeueFailed'))\n }\n\n /** 发送前持久化门闩;仅有效租约可从 processing 进入 sending。 */\n async beginDelivery(id: number, owner: string, retrySafe: boolean, lockMs = DEFAULT_CLAIM_LOCK_MS): Promise<HaiResult<void>> {\n const clock = this.clockSql()\n const flag = this.db.config?.type === 'postgresql' ? retrySafe : Number(retrySafe)\n const result = await this.sql().execute(\n `UPDATE ${TABLE_NAME} SET status = ?, retry_safe = ?, locked_until = ${clock} + ?, updated_at = ${clock}\n WHERE id = ? AND status = ? AND processing_owner = ? AND locked_until > ${clock}`,\n ['sending', flag, lockMs, id, 'processing', owner],\n )\n return result.success && result.data.changes === 1 ? ok(undefined) : buildSendLogError(reachM('reach_claimLost'))\n }\n\n /** 续租不接受已经过期的 owner,防止暂停的旧进程恢复后重新取得发送权。 */\n async renewClaim(id: number, owner: string, lockMs = DEFAULT_CLAIM_LOCK_MS): Promise<HaiResult<void>> {\n const clock = this.clockSql()\n const result = await this.sql().execute(\n `UPDATE ${TABLE_NAME} SET locked_until = ${clock} + ?, updated_at = ${clock}\n WHERE id = ? AND processing_owner = ? AND status IN (?, ?) AND locked_until > ${clock}`,\n [lockMs, id, owner, 'processing', 'sending'],\n )\n return result.success && result.data.changes === 1 ? ok(undefined) : buildSendLogError(reachM('reach_claimLost'))\n }\n\n /** 未知结果只能停止自动处理,不推断外部副作用已撤销。 */\n async markUnknown(id: number, owner: string): Promise<HaiResult<void>> {\n const result = await this.sql().execute(\n `UPDATE ${TABLE_NAME} SET status = ?, processing_owner = NULL, locked_until = NULL, next_attempt_at = 0, updated_at = ${this.clockSql()}\n WHERE id = ? AND processing_owner = ? AND status IN (?, ?)`,\n ['unknown', id, owner, 'processing', 'sending'],\n )\n return result.success && result.data.changes === 1 ? ok(undefined) : buildSendLogError(reachM('reach_claimLost'))\n }\n\n /** 人工裁定的条件提交,不提供重新排队或重新发送操作。 */\n async resolveUnknown(queueId: string, resolution: ReachUnknownResolution): Promise<HaiResult<void>> {\n const result = await this.sql().execute(\n `UPDATE ${TABLE_NAME} SET status = ?, message_id = ?, next_attempt_at = 0, updated_at = ${this.clockSql()} WHERE queue_id = ? AND status = ?`,\n [resolution.status, resolution.messageId ?? null, queueId, 'unknown'],\n )\n return result.success && result.data.changes === 1 ? ok(undefined) : buildSendLogError(reachM('reach_unknownResolutionFailed'))\n }\n\n /** 只有仍持有 sending 有效租约的 owner 可以确认投递。 */\n async markSent(id: number, owner: string, messageId?: string, tx?: DmlWithTxOperations): Promise<HaiResult<void>> {\n const clock = this.clockSql()\n const result = await this.sql(tx).execute(\n `UPDATE ${TABLE_NAME}\n SET status = ?, message_id = ?, processing_owner = NULL, locked_until = NULL, next_attempt_at = 0, updated_at = ${clock}\n WHERE id = ? AND status = ? AND processing_owner = ? AND locked_until > ${clock}`,\n ['sent', messageId ?? null, id, 'sending', owner],\n )\n if (!result.success) {\n return buildSendLogError(result.error.message, result.error)\n }\n if (result.data.changes === 0) {\n return buildSendLogError(reachM('reach_claimLost'))\n }\n return ok(undefined)\n }\n}\n","/**\n * @h-ai/reach — 模板存储实现\n *\n * 基于 @h-ai/reldb 的模板持久化存储。\n * 模板支持两种来源:配置文件(config)和数据库(db)。\n * @module reach-repository-template\n */\n\nimport type { HaiResult } from '@h-ai/core'\nimport type { DmlWithTxOperations, ReldbCrudFieldDefinition, ReldbCrudRepository, ReldbFunctions } from '@h-ai/reldb'\n\nimport type { ReachTemplate } from '../reach-types.js'\nimport { err, ok } from '@h-ai/core'\nimport { BaseReldbCrudRepository } from '@h-ai/reldb'\n\nimport { reachM } from '../reach-i18n.js'\nimport { HaiReachError } from '../reach-types.js'\n\n// ─── 模板存储实体类型 ───\n\n/**\n * 内部数据库模板行;createdAt/updatedAt由TIMESTAMP字段映射为Date,与发送队列BIGINT毫秒不同。\n *\n * @example 模块内部上下文\n * ```ts\n * const { reldb } = await import('@h-ai/reldb')\n * const connected = await reldb.init({ type: 'postgresql', host: 'db', port: 5432, database: 'hai', user: 'postgres', password: process.env.HAI_DB_PASSWORD })\n * if (!connected.success) throw new Error(connected.error.message)\n * try {\n * const created = await createTemplateRepository(reldb)\n * if (!created.success) throw new Error(created.error.message)\n * const repository = created.data\n * const found = await repository.findByName('welcome')\n * if (!found.success) throw new Error(found.error.message)\n * const row: StoredTemplate | undefined = found.data\n * if (row) { const updatedAt = row.updatedAt }\n * }\n * finally {\n * const closed = await reldb.close()\n * if (!closed.success) throw new Error(closed.error.message)\n * }\n * ```\n */\nexport interface StoredTemplate {\n /** 自增主键 */\n id: number\n /** 模板名称(唯一标识) */\n name: string\n /** 绑定的 Provider 名称 */\n provider: string\n /** 邮件主题模板 */\n subject: string | null\n /** 正文模板 */\n body: string\n /** 创建时间(TIMESTAMP 经仓库映射为 Date)。 */\n createdAt: Date\n /** 更新时间(TIMESTAMP 经仓库映射为 Date)。 */\n updatedAt: Date\n}\n\n// ─── 模板存储接口 ───\n\n/**\n * 内部模板数据库契约,唯一name支持upsert;表配置/数据库持有内容,进程缓存只持有仓库对象。\n *\n * @example 模块内部上下文\n * ```ts\n * const { reldb } = await import('@h-ai/reldb')\n * const connected = await reldb.init({ type: 'postgresql', host: 'db', port: 5432, database: 'hai', user: 'postgres', password: process.env.HAI_DB_PASSWORD })\n * if (!connected.success) throw new Error(connected.error.message)\n * try {\n * const created = await createTemplateRepository(reldb)\n * if (!created.success) throw new Error(created.error.message)\n * const repository = created.data\n * const saved = await repository.upsert({ name: 'welcome', provider: 'webhook', subject: 'Welcome {userName}', body: 'Hello {userName}' })\n * if (!saved.success) throw new Error(saved.error.message)\n * const found = await repository.findByName('welcome')\n * if (!found.success) throw new Error(found.error.message)\n * }\n * finally {\n * const closed = await reldb.close()\n * if (!closed.success) throw new Error(closed.error.message)\n * }\n * ```\n */\nexport interface TemplateRepository extends ReldbCrudRepository<StoredTemplate> {\n /**\n * 按名称查找模板\n * @param name - 实际操作输入,按本方法约束校验。\n * @param tx - 可选受控数据库事务。\n * @returns HaiResult;先检查success,条件提交失败不能伪装成功。\n *\n * @example 模块内部上下文\n * ```ts\n * async function find(repository: TemplateRepository, name: string) {\n * const found = await repository.findByName(name)\n * if (!found.success) throw new Error(found.error.message)\n * return found.data\n * }\n * ```\n */\n findByName: (name: string, tx?: DmlWithTxOperations) => Promise<HaiResult<StoredTemplate | undefined>>\n\n /**\n * 保存模板(存在则更新,不存在则插入)\n * @param template - 实际操作输入,按本方法约束校验。\n * @param tx - 可选受控数据库事务。\n * @returns HaiResult;先检查success,条件提交失败不能伪装成功。\n *\n * @example 模块内部上下文\n * ```ts\n * async function save(repository: TemplateRepository, template: ReachTemplate) {\n * const saved = await repository.upsert(template)\n * if (!saved.success) throw new Error(saved.error.message)\n * }\n * ```\n */\n upsert: (template: ReachTemplate, tx?: DmlWithTxOperations) => Promise<HaiResult<void>>\n\n /**\n * 按名称删除模板\n * @param name - 实际操作输入,按本方法约束校验。\n * @param tx - 可选受控数据库事务。\n * @returns HaiResult;先检查success,条件提交失败不能伪装成功。\n *\n * @example 模块内部上下文\n * ```ts\n * async function remove(repository: TemplateRepository, name: string) {\n * const removed = await repository.deleteByName(name)\n * if (!removed.success) throw new Error(removed.error.message)\n * }\n * ```\n */\n deleteByName: (name: string, tx?: DmlWithTxOperations) => Promise<HaiResult<void>>\n\n /**\n * 获取所有模板(转换为 ReachTemplate 格式)\n * @param tx - 可选受控数据库事务。\n * @returns HaiResult;先检查success,条件提交失败不能伪装成功。\n *\n * @example 模块内部上下文\n * ```ts\n * async function list(repository: TemplateRepository) {\n * const listed = await repository.listTemplates()\n * if (!listed.success) throw new Error(listed.error.message)\n * return listed.data\n * }\n * ```\n */\n listTemplates: (tx?: DmlWithTxOperations) => Promise<HaiResult<ReachTemplate[]>>\n}\n\n// ─── 模板存储实现 ───\n\n/** 表名 */\nconst TABLE_NAME = 'hai_reach_template'\n\n/** 字段定义 */\nconst TEMPLATE_FIELDS: ReldbCrudFieldDefinition[] = [\n {\n fieldName: 'id',\n columnName: 'id',\n def: { type: 'INTEGER' as const, primaryKey: true, autoIncrement: true },\n select: true,\n create: false,\n update: false,\n },\n {\n fieldName: 'name',\n columnName: 'name',\n def: { type: 'TEXT' as const, notNull: true, unique: true },\n select: true,\n create: true,\n update: true,\n },\n {\n fieldName: 'provider',\n columnName: 'provider',\n def: { type: 'TEXT' as const, notNull: true },\n select: true,\n create: true,\n update: true,\n },\n {\n fieldName: 'subject',\n columnName: 'subject',\n def: { type: 'TEXT' as const },\n select: true,\n create: true,\n update: true,\n },\n {\n fieldName: 'body',\n columnName: 'body',\n def: { type: 'TEXT' as const, notNull: true },\n select: true,\n create: true,\n update: true,\n },\n {\n fieldName: 'createdAt',\n columnName: 'created_at',\n def: { type: 'TIMESTAMP' as const, notNull: true },\n select: true,\n create: true,\n update: false,\n },\n {\n fieldName: 'updatedAt',\n columnName: 'updated_at',\n def: { type: 'TIMESTAMP' as const, notNull: true },\n select: true,\n create: true,\n update: true,\n },\n]\n\n/** 模板存储单例缓存 */\nlet templateRepoInstance: TemplateRepository | null = null\nlet templateRepoSqlOps: unknown = null\n\n/**\n * 内部关闭时释放旧db.sql对应的仓库引用,不删除持久模板或重置其它节点。\n *\n * @example 模块内部上下文\n * ```ts\n * resetTemplateRepoSingleton()\n * ```\n */\nexport function resetTemplateRepoSingleton(): void {\n templateRepoInstance = null\n templateRepoSqlOps = null\n}\n\n/**\n * 内部创建/复用当前数据库生命周期的模板仓库,并实际确认表可访问;连接故障返回失败。\n * @param db - 已初始化共享数据库。\n * @returns 模板仓库HaiResult;不使用进程模板内容缓存。\n *\n * @example 模块内部上下文\n * ```ts\n * const { reldb } = await import('@h-ai/reldb')\n * const connected = await reldb.init({ type: 'postgresql', host: 'db', port: 5432, database: 'hai', user: 'postgres', password: process.env.HAI_DB_PASSWORD })\n * if (!connected.success) throw new Error(connected.error.message)\n * try {\n * const created = await createTemplateRepository(reldb)\n * if (!created.success) throw new Error(created.error.message)\n * const repository = created.data\n * const listed = await repository.listTemplates()\n * if (!listed.success) throw new Error(listed.error.message)\n * const count = listed.data.length\n * }\n * finally {\n * const closed = await reldb.close()\n * if (!closed.success) throw new Error(closed.error.message)\n * }\n * ```\n */\nexport async function createTemplateRepository(db: ReldbFunctions): Promise<HaiResult<TemplateRepository>> {\n if (templateRepoInstance && templateRepoSqlOps === db.sql)\n return ok(templateRepoInstance)\n\n const repo = new DbTemplateRepository(db)\n // 触发表创建(BaseReldbCrudRepository 的表创建是异步的)\n const initResult = await repo.count()\n if (!initResult.success) {\n return err(\n HaiReachError.SEND_FAILED,\n reachM('reach_templateDbInitFailed', { params: { error: initResult.error.message } }),\n initResult.error,\n )\n }\n templateRepoInstance = repo\n templateRepoSqlOps = db.sql\n return ok(repo)\n}\n\n/**\n * 将 StoredTemplate 转换为 ReachTemplate\n */\nfunction toReachTemplate(stored: StoredTemplate): ReachTemplate {\n return {\n name: stored.name,\n provider: stored.provider,\n subject: stored.subject ?? undefined,\n body: stored.body,\n }\n}\n\n/**\n * 基于数据库的模板存储实现\n */\nclass DbTemplateRepository extends BaseReldbCrudRepository<StoredTemplate> implements TemplateRepository {\n constructor(db: ReldbFunctions) {\n super(db, {\n table: TABLE_NAME,\n fields: TEMPLATE_FIELDS,\n idColumn: 'id',\n idField: 'id',\n createTableIfNotExists: true,\n })\n }\n\n /** 按名称查找模板 */\n async findByName(name: string, tx?: DmlWithTxOperations): Promise<HaiResult<StoredTemplate | undefined>> {\n const result = await this.findAll({ where: 'name = ?', params: [name], limit: 1 }, tx)\n if (!result.success) {\n return result\n }\n return ok(result.data[0])\n }\n\n /** 保存模板(存在则更新,不存在则插入) */\n async upsert(template: ReachTemplate, tx?: DmlWithTxOperations): Promise<HaiResult<void>> {\n const existing = await this.findByName(template.name, tx)\n if (!existing.success) {\n return existing\n }\n\n const now = Date.now()\n\n if (existing.data) {\n // 更新\n const updateResult = await this.updateById(\n existing.data.id,\n {\n provider: template.provider,\n subject: template.subject ?? null,\n body: template.body,\n updatedAt: now,\n },\n tx,\n )\n if (!updateResult.success) {\n return updateResult\n }\n }\n else {\n // 插入\n const createResult = await this.create(\n {\n name: template.name,\n provider: template.provider,\n subject: template.subject ?? null,\n body: template.body,\n createdAt: now,\n updatedAt: now,\n },\n tx,\n )\n if (!createResult.success) {\n return createResult\n }\n }\n return ok(undefined)\n }\n\n /** 按名称删除模板 */\n async deleteByName(name: string, tx?: DmlWithTxOperations): Promise<HaiResult<void>> {\n const existing = await this.findByName(name, tx)\n if (!existing.success) {\n return existing\n }\n if (!existing.data) {\n return ok(undefined)\n }\n const result = await this.deleteById(existing.data.id, tx)\n if (!result.success) {\n return result\n }\n return ok(undefined)\n }\n\n /** 获取所有模板(转换为 ReachTemplate 格式) */\n async listTemplates(tx?: DmlWithTxOperations): Promise<HaiResult<ReachTemplate[]>> {\n const result = await this.findAll({ orderBy: 'name ASC' }, tx)\n if (!result.success) {\n return result\n }\n return ok(result.data.map(toReachTemplate))\n }\n}\n","/**\n * @h-ai/reach — 触达服务主入口\n *\n * 本文件提供统一的 `reach` 对象,聚合模块生命周期管理。\n * @module reach-main\n */\n\nimport type { HaiResult } from '@h-ai/core'\nimport type { ReldbFunctions } from '@h-ai/reldb'\nimport type { ReachProvider } from './providers/reach-provider-types.js'\nimport type { DndConfig, ProviderConfig, ReachConfig, ReachConfigInput } from './reach-config.js'\nimport type { ReachFunctions, ReachMessage, ReachQueueEntry, ReachTemplateRegistry, SendResult } from './reach-types.js'\nimport type { SendLogRepository, StoredSendLog } from './repositories/reach-repository-send-log.js'\nimport type { TemplateRepository } from './repositories/reach-repository-template.js'\nimport process from 'node:process'\n\nimport { core, err, ok } from '@h-ai/core'\nimport { reldb } from '@h-ai/reldb'\n\nimport { createAliyunSmsProvider } from './providers/reach-provider-aliyun-sms.js'\nimport { createApiProvider } from './providers/reach-provider-api.js'\nimport { createConsoleProvider } from './providers/reach-provider-console.js'\nimport { createSmtpProvider } from './providers/reach-provider-smtp.js'\nimport { ReachConfigSchema, ReachDeadRequeueSchema, ReachQueueIdSchema, ReachQueueListSchema, ReachUnknownResolutionSchema } from './reach-config.js'\nimport { reachM } from './reach-i18n.js'\nimport { executeSend, resetSendState, startDndScheduler } from './reach-send.js'\nimport { createTemplateRegistry } from './reach-template.js'\nimport {\n HaiReachError,\n\n} from './reach-types.js'\nimport { createSendLogRepository, resetSendLogRepoSingleton } from './repositories/reach-repository-send-log.js'\nimport { createTemplateRepository, resetTemplateRepoSingleton } from './repositories/reach-repository-template.js'\n\nconst logger = core.logger.child({ module: 'reach', scope: 'main' })\n\n// ─── 内部状态 ───\n\n/** 已注册的 Provider 实例(name → Provider) */\nlet providers: Map<string, ReachProvider> = new Map()\n\n/** 当前配置(未初始化时为 null) */\nlet currentConfig: ReachConfig | null = null\n\n/** DND 配置 */\nlet dndConfig: DndConfig | undefined\n\n/** 模板注册表(始终可用) */\nlet templateRegistry: ReachTemplateRegistry = createTemplateRegistry()\n\n/** 发送日志存储(db 可用时初始化) */\nlet sendLogRepo: SendLogRepository | null = null\n\n/** 模板存储(db 可用时初始化) */\nlet templateRepo: TemplateRepository | null = null\n\n/** 初始化进行中标志(防止并发调用) */\nlet initInProgress = false\n\n// ─── Provider 工厂 ───\n\n/**\n * 根据配置创建对应的 Provider\n */\nfunction createProvider(config: ProviderConfig): ReachProvider {\n switch (config.type) {\n case 'console':\n return createConsoleProvider()\n case 'smtp':\n return createSmtpProvider()\n case 'aliyun-sms':\n return createAliyunSmsProvider()\n case 'api':\n return createApiProvider()\n }\n}\n\n// ─── DB 动态引用(可选依赖) ───\n\n/**\n * 尝试获取 db 实例(可选依赖,不可用时返回 null)\n */\nasync function tryGetDb(): Promise<ReldbFunctions | null> {\n // 与 Repository 的公开静态导入一致,避免 SSR 打包后动态导入得到另一份未初始化单例。\n return reldb.isInitialized ? reldb : null\n}\n\n/**\n * 尝试初始化发送日志存储\n */\nasync function tryInitSendLogRepo(): Promise<SendLogRepository | null> {\n try {\n const db = await tryGetDb()\n if (!db) {\n return null\n }\n const result = await createSendLogRepository(db)\n if (!result.success) {\n logger.debug('Send log repository not initialized', { error: result.error.message })\n return null\n }\n return result.data\n }\n catch (error) {\n logger.debug('Send log repository not initialized', { error: error instanceof Error ? error.message : String(error) })\n return null\n }\n}\n\n/**\n * 尝试初始化模板存储\n */\nasync function tryInitTemplateRepo(): Promise<TemplateRepository | null> {\n try {\n const db = await tryGetDb()\n if (!db) {\n return null\n }\n const result = await createTemplateRepository(db)\n if (!result.success) {\n logger.debug('Template repository not initialized', { error: result.error.message })\n return null\n }\n return result.data\n }\n catch (error) {\n logger.debug('Template repository not initialized', { error: error instanceof Error ? error.message : String(error) })\n return null\n }\n}\n\n// ─── 未初始化工具集 ───\n\nconst notInitialized = core.module.createNotInitializedKit(\n HaiReachError.NOT_INITIALIZED,\n () => reachM('reach_notInitialized'),\n)\n\n// ─── 关闭逻辑 ───\n\n/**\n * 关闭所有 Provider 并释放资源(内部实现)\n */\nasync function doClose(): Promise<void> {\n if (providers.size === 0 && currentConfig === null) {\n logger.info('Reach module already closed, skipping')\n return\n }\n\n logger.info('Closing reach module')\n\n for (const [name, provider] of providers.entries()) {\n try {\n await provider.close()\n }\n catch (error) {\n logger.error('Provider close failed', { name, error })\n }\n }\n\n providers = new Map()\n currentConfig = null\n dndConfig = undefined\n templateRegistry = createTemplateRegistry()\n sendLogRepo = null\n templateRepo = null\n resetSendLogRepoSingleton()\n resetTemplateRepoSingleton()\n resetSendState()\n\n logger.info('Reach module closed')\n}\n\n// ─── 初始化逻辑 ───\n\n/**\n * 实际的初始化逻辑(由 init 包裹并发防护后调用)\n */\nasync function doInit(config: ReachConfigInput): Promise<HaiResult<void>> {\n if (providers.size > 0) {\n logger.warn('Reach module is already initialized, reinitializing')\n await doClose()\n }\n\n logger.info('Initializing reach module')\n\n const parseResult = ReachConfigSchema.safeParse(config)\n if (!parseResult.success) {\n logger.error('Reach config validation failed', { error: parseResult.error.message })\n return err(\n HaiReachError.CONFIG_ERROR,\n reachM('reach_configError', { params: { error: parseResult.error.message } }),\n parseResult.error,\n )\n }\n const parsed = parseResult.data\n // Console只证明本机日志调用;生产环境不能把它当作真实触达成功。\n if (core.module.requiresSharedState(process.env) && parsed.providers.some(provider => provider.type === 'console'))\n return err(HaiReachError.CONFIG_ERROR, reachM('reach_productionProviderRequired'))\n\n try {\n // delay 队列是可靠发送前置;连接任何 Provider、发布模块状态前先确认表已就绪。\n let requiredQueue: SendLogRepository | null = null\n if (parsed.dnd?.enabled && parsed.dnd.strategy === 'delay') {\n if (!reldb.isInitialized)\n return err(HaiReachError.CONFIG_ERROR, reachM('reach_dndPersistenceRequired'))\n const queue = await createSendLogRepository(reldb)\n if (!queue.success)\n return err(HaiReachError.CONFIG_ERROR, reachM('reach_dndPersistenceRequired'), queue.error)\n requiredQueue = queue.data\n }\n\n const newProviders = new Map<string, ReachProvider>()\n\n for (const providerConfig of parsed.providers) {\n const provider = createProvider(providerConfig)\n const connectResult = await provider.connect(providerConfig)\n if (!connectResult.success) {\n logger.error('Provider initialization failed', {\n name: providerConfig.name,\n type: providerConfig.type,\n code: connectResult.error.code,\n message: connectResult.error.message,\n })\n logger.info('Rolling back connected providers', { count: newProviders.size })\n for (const [name, p] of newProviders.entries()) {\n try {\n await p.close()\n }\n catch (rollbackError) {\n logger.error('Provider rollback close failed', { name, error: rollbackError })\n }\n }\n return connectResult\n }\n newProviders.set(providerConfig.name, provider)\n }\n\n providers = newProviders\n currentConfig = parsed\n dndConfig = parsed.dnd\n\n // 尝试初始化模板存储\n templateRepo = await tryInitTemplateRepo()\n\n // 创建模板注册表\n templateRegistry = createTemplateRegistry(templateRepo)\n\n // 配置文件中定义的模板写入数据库\n if (parsed.templates && templateRepo) {\n const saveResult = await templateRegistry.saveBatch(parsed.templates)\n if (!saveResult.success) {\n logger.warn('Failed to save config templates to database', { error: saveResult.error.message })\n }\n }\n\n // delay 使用已验证的必需队列;即时发送的普通日志仍是可选能力。\n sendLogRepo = requiredQueue ?? await tryInitSendLogRepo()\n\n // 启动 DND 恢复定时器\n if (dndConfig) {\n startDndScheduler(dndConfig, providers, sendLogRepo)\n }\n\n const providerNames = parsed.providers.map(p => p.name)\n logger.info('Reach module initialized', { providers: providerNames })\n return ok(undefined)\n }\n catch (error) {\n logger.error('Reach module initialization failed', { error })\n return err(\n HaiReachError.CONFIG_ERROR,\n reachM('reach_initFailed', {\n params: { error: error instanceof Error ? error.message : String(error) },\n }),\n error,\n )\n }\n}\n\n// ─── 触达服务对象 ───\n\n/**\n * 把敏感日志实体投影成运维元数据,排除收件人、正文、vars/extra和owner凭据。\n * @param row - 已确认queueId非空且字段规范化的队列行。\n * @returns 对外可查询的元数据;不能据此替代运维授权。\n *\n * @example 模块内部上下文\n * ```ts\n * function safeEntry(row: StoredSendLog): ReachQueueEntry | null {\n * if (!row.queueId) return null\n * return queueEntry(row)\n * }\n * ```\n */\nfunction queueEntry(row: StoredSendLog): ReachQueueEntry {\n return {\n queueId: row.queueId!,\n provider: row.provider,\n status: row.status,\n updatedAt: row.updatedAt,\n messageId: row.messageId ?? undefined,\n attemptCount: row.attemptCount,\n maxAttempts: row.maxAttempts,\n nextAttemptAt: row.nextAttemptAt,\n lastFailure: row.lastFailure ?? undefined,\n }\n}\n\n/**\n * 邮件/短信/API触达统一入口;先检查init与send的HaiResult,成功后还区分deferred和即时Provider结果。\n * 模板与delay队列使用已初始化数据库,生产共享后端;close停止本地调度/关闭Provider,不撤销已发外部请求。\n *\n * @example\n * ```ts\n * import { reach } from '@h-ai/reach'\n * const { reldb } = await import('@h-ai/reldb')\n * const connected = await reldb.init({ type: 'postgresql', host: 'db', port: 5432, database: 'hai', user: 'postgres', password: process.env.HAI_DB_PASSWORD })\n * if (!connected.success) throw new Error(connected.error.message)\n * try {\n * const initialized = await reach.init({ providers: [{ name: 'webhook', type: 'api', url: 'https://notify.example.com/events', idempotent: false }], templates: [{ name: 'welcome', provider: 'webhook', subject: 'Welcome {userName}', body: 'Hello {userName}' }], dnd: { enabled: true, strategy: 'delay', start: '22:00', end: '08:00' } })\n * if (!initialized.success) throw new Error(initialized.error.message)\n * try {\n * const result = await reach.send({ provider: 'webhook', to: 'user@example.com', template: 'welcome', vars: { userName: 'Alice' } })\n * if (!result.success) throw new Error(result.error.message)\n * if (!result.data.success) throw new Error('Delivery was not accepted')\n * if (result.data.deferred) {\n * const queueId = result.data.queueId\n * // 持久入队成功,尚未证明接收端已完成外部投递。\n * }\n * else { const providerMessageId = result.data.messageId }\n * }\n * finally { await reach.close() }\n * }\n * finally {\n * const closed = await reldb.close()\n * if (!closed.success) throw new Error(closed.error.message)\n * }\n * ```\n */\nexport const reach: ReachFunctions = {\n queue: {\n async get(queueId) {\n if (providers.size === 0)\n return notInitialized.result()\n if (!sendLogRepo)\n return err(HaiReachError.CONFIG_ERROR, reachM('reach_dndPersistenceRequired'))\n if (!ReachQueueIdSchema.safeParse(queueId).success)\n return err(HaiReachError.CONFIG_ERROR, reachM('reach_queueInputInvalid'))\n const rows = await sendLogRepo.findAll({ where: 'queue_id = ?', params: [queueId], limit: 1 })\n if (!rows.success)\n return rows\n const row = rows.data[0]\n return ok(row ? queueEntry(row) : null)\n },\n async list(options) {\n if (providers.size === 0)\n return notInitialized.result()\n if (!sendLogRepo)\n return err(HaiReachError.CONFIG_ERROR, reachM('reach_dndPersistenceRequired'))\n const parsed = ReachQueueListSchema.safeParse(options)\n if (!parsed.success)\n return err(HaiReachError.CONFIG_ERROR, reachM('reach_queueInputInvalid'))\n const rows = await sendLogRepo.findAll({\n where: 'status = ? AND queue_id IS NOT NULL',\n params: [parsed.data.status],\n orderBy: 'created_at ASC, id ASC',\n limit: parsed.data.limit,\n offset: parsed.data.offset,\n })\n return rows.success ? ok(rows.data.map(queueEntry)) : rows\n },\n async requeueDead(queueId, options) {\n if (providers.size === 0)\n return notInitialized.result()\n if (!sendLogRepo)\n return err(HaiReachError.CONFIG_ERROR, reachM('reach_dndPersistenceRequired'))\n const parsed = ReachDeadRequeueSchema.safeParse(options)\n if (!ReachQueueIdSchema.safeParse(queueId).success || !parsed.success)\n return err(HaiReachError.CONFIG_ERROR, reachM('reach_queueInputInvalid'))\n return sendLogRepo.requeueDead(queueId, parsed.data.expectedUpdatedAt)\n },\n async resolveUnknown(queueId, resolution) {\n if (providers.size === 0)\n return notInitialized.result()\n if (!sendLogRepo)\n return err(HaiReachError.CONFIG_ERROR, reachM('reach_dndPersistenceRequired'))\n const parsed = ReachUnknownResolutionSchema.safeParse(resolution)\n if (!ReachQueueIdSchema.safeParse(queueId).success || !parsed.success)\n return err(HaiReachError.CONFIG_ERROR, reachM('reach_queueInputInvalid'))\n return sendLogRepo.resolveUnknown(queueId, parsed.data)\n },\n },\n async init(config: ReachConfigInput): Promise<HaiResult<void>> {\n if (initInProgress) {\n logger.warn('Reach module init already in progress, skipping')\n return err(\n HaiReachError.CONFIG_ERROR,\n reachM('reach_configError', { params: { error: 'init already in progress' } }),\n )\n }\n\n initInProgress = true\n try {\n return await doInit(config)\n }\n finally {\n initInProgress = false\n }\n },\n\n async send(message: ReachMessage): Promise<HaiResult<SendResult>> {\n if (providers.size === 0) {\n return notInitialized.result()\n }\n\n const send = () => executeSend(message, providers, templateRegistry, dndConfig, sendLogRepo, currentConfig!.queue)\n return message.sensitive ? core.context.run({ suppressLogObservers: true }, send) : send()\n },\n\n get template(): ReachTemplateRegistry {\n return templateRegistry\n },\n\n get config(): ReachConfig | null {\n return currentConfig ? core.sanitize.sanitizeSensitiveFields(currentConfig) : null\n },\n\n get isInitialized(): boolean {\n return providers.size > 0\n },\n\n async close(): Promise<void> {\n await doClose()\n },\n}\n"]}