import { InfrastructureException, InfrastructureExceptionOptions, PlanetCore, GravitoOrbit } from '@gravito/core'; /** * Notification system type definitions. */ /** * 支援取消的發送選項 * * 用於在發送通知時支援 AbortController 取消功能。 * * @example * ```typescript * const controller = new AbortController() * setTimeout(() => controller.abort(), 5000) * * await channel.send(notification, notifiable, { * signal: controller.signal * }) * ``` * * @public */ interface AbortableSendOptions { /** * AbortSignal 用於取消請求 * * 當 signal 被 abort 時,底層的網路請求(如 fetch)會被取消。 * 這對於實作 timeout 或使用者主動取消特別有用。 */ signal?: AbortSignal; } /** * Notification channel interface. * @public */ interface NotificationChannel { /** * Send a notification through the specified channel. * * @param notification - The notification instance containing data. * @param notifiable - The recipient of the notification. * @param options - Optional abort options (v4.0.0+) */ send(notification: Notification, notifiable: Notifiable, options?: AbortableSendOptions): Promise; } /** * Interface for recipients that can receive notifications. * @public */ interface Notifiable { /** * Unique identifier for the recipient (e.g., User ID). */ getNotifiableId(): string | number; /** * Optional recipient type (useful for polymorphic notifications). */ getNotifiableType?(): string; /** * Optional list of preferred channels for this specific recipient. */ preferredNotificationChannels?(): string[]; /** * Optional notification preferences for this specific recipient. * 取得用戶的通知偏好設定,用於控制通知的發送行為。 */ getNotificationPreferences?(): Promise<{ /** 啟用的通道列表(如果設定,則只允許這些通道) */ enabledChannels?: string[]; /** 禁用的通道列表(優先於 enabledChannels) */ disabledChannels?: string[]; /** 禁用的通知類型列表(Notification class names) */ disabledNotifications?: string[]; }>; } /** * Payload for email notifications. * @public */ interface MailMessage { /** Email subject line */ subject: string; /** View template path */ view?: string; /** Data to pass to the view template */ data?: Record; /** Inline HTML content */ html?: string; /** Plain text content */ text?: string; /** Sender address */ from?: string; /** Target recipient(s) */ to?: string | string[]; /** Carbon copy recipient(s) */ cc?: string | string[]; /** Blind carbon copy recipient(s) */ bcc?: string | string[]; } /** * Payload for database-stored notifications. * @public */ interface DatabaseNotification { /** Type identifier for the notification */ type: string; /** JSON-serializable data payload */ data: Record; /** Timestamp when the notification was marked as read */ readAt?: Date | null; } /** * Payload for real-time broadcast notifications. * @public */ interface BroadcastNotification { /** Event/Type identifier for the broadcast */ type: string; /** Data to be broadcasted to subscribers */ data: Record; } /** * Payload for Slack channel notifications. * @public */ interface SlackMessage { /** Main message text */ text: string; /** Target Slack channel */ channel?: string; /** Custom bot username */ username?: string; /** Icon emoji for the bot */ iconEmoji?: string; /** Array of Slack attachments for rich formatting */ attachments?: Array<{ color?: string; title?: string; text?: string; fields?: Array<{ title: string; value: string; short?: boolean; }>; }>; } /** * Payload for SMS notifications. * @public */ interface SmsMessage { /** Recipient phone number */ to: string; /** Message content */ message: string; /** Sender ID or phone number */ from?: string; } /** * Result of a single channel delivery attempt. * @public */ interface SendResult { success: boolean; channel: string; error?: Error; duration?: number; } /** * Aggregated result of a notification delivery. * @public */ interface NotificationResult { notification: string; notifiable: string | number; results: SendResult[]; allSuccess: boolean; timestamp: Date; } /** * Options for sending notifications. * @public */ interface SendOptions { /** If true, throws an AggregateError when any channel fails */ throwOnError?: boolean; /** Whether to send to channels in parallel (default: true) */ parallel?: boolean; /** Maximum number of concurrent channel sends (default: unlimited) */ concurrency?: number; /** Retry configuration */ retry?: Partial | boolean; } /** * Retry configuration options. * @public */ interface RetryConfig { /** Maximum number of retry attempts (default: 3) */ maxAttempts: number; /** Base delay in milliseconds (default: 1000) */ baseDelay: number; /** Backoff strategy: 'fixed' | 'linear' | 'exponential' (default: 'exponential') */ backoff: 'fixed' | 'linear' | 'exponential'; /** Maximum delay in milliseconds (default: 30000) */ maxDelay: number; /** Function to determine if an error is retryable */ retryableErrors?: (error: Error) => boolean; } /** * Interface for notifications that should retry on failure. * @public */ interface ShouldRetry { /** Retry configuration for this notification */ retry?: Partial; } /** * Result of a batch notification delivery. * @public */ interface BatchResult { total: number; success: number; failed: number; results: NotificationResult[]; duration: number; } interface NotificationHookPayload { notification: Notification; notifiable: Notifiable; channels: string[]; } interface ChannelHookPayload { notification: Notification; notifiable: Notifiable; channel: string; } interface ChannelSuccessPayload extends ChannelHookPayload { duration: number; } interface ChannelFailurePayload extends ChannelHookPayload { error: Error; duration: number; } interface NotificationCompletePayload { notification: Notification; notifiable: Notifiable; results: SendResult[]; allSuccess: boolean; totalDuration: number; } interface NotificationBatchStartPayload { notification: Notification; count: number; } interface NotificationBatchCompletePayload { notification: Notification; total: number; success: number; failed: number; duration: number; } /** * Notification preference provider interface. * * 用於提供用戶通知偏好的介面。可以從資料庫、快取或其他來源載入用戶的偏好設定。 * * @public */ interface NotificationPreference { /** * Get user notification preferences. * * 獲取用戶的通知偏好設定。 * * @param notifiable - 接收通知的用戶 * @returns 用戶的通知偏好設定 * * @example * ```typescript * class DatabasePreferenceProvider implements NotificationPreference { * async getUserPreferences(notifiable: Notifiable) { * const prefs = await db.query( * 'SELECT * FROM user_preferences WHERE user_id = ?', * [notifiable.getNotifiableId()] * ) * return { * enabledChannels: prefs.enabled_channels, * disabledChannels: prefs.disabled_channels, * disabledNotifications: prefs.disabled_notifications * } * } * } * ``` */ getUserPreferences(notifiable: Notifiable): Promise<{ /** 啟用的通道列表(如果設定,則只允許這些通道) */ enabledChannels: string[]; /** 禁用的通道列表(優先於 enabledChannels) */ disabledChannels: string[]; /** 禁用的通知類型列表(Notification class names) */ disabledNotifications: string[]; }>; } /** * Marker interface for notifications that should be queued. * * Implementing this interface will cause the notification to be dispatched * to a background queue automatically by the `NotificationManager`. * * @public * @since 3.0.0 */ interface ShouldQueue { /** The name of the queue to push this notification to. */ queue?: string | undefined; /** The connection name (e.g., 'redis', 'database') to use for queuing. */ connection?: string | undefined; /** Delay in seconds before the notification is delivered. */ delay?: number | undefined; } /** * Base Notification class. * * All application notifications should extend this class. * * @public * @since 3.0.0 */ declare abstract class Notification { /** * Specify which channels should be used for delivery. * @param notifiable - Recipient * @returns Channel names */ abstract via(notifiable: Notifiable): string[]; /** * Get mail message (optional). * Implement this if the notification will be sent via the mail channel. */ toMail?(notifiable: Notifiable): MailMessage; /** * Get database notification (optional). * Implement this if the notification will be stored via the database channel. */ toDatabase?(notifiable: Notifiable): DatabaseNotification; /** * Get broadcast notification (optional). * Implement this if the notification will be sent via the broadcast channel. */ toBroadcast?(notifiable: Notifiable): BroadcastNotification; /** * Get Slack message (optional). * Implement this if the notification will be sent via the Slack channel. */ toSlack?(notifiable: Notifiable): SlackMessage; /** * Get SMS message (optional). * Implement this if the notification will be sent via the SMS channel. */ toSms?(notifiable: Notifiable): SmsMessage; /** * Check whether this notification should be queued. */ shouldQueue(): boolean; /** * Get queue configuration. */ getQueueConfig(): { queue?: string | undefined; connection?: string | undefined; delay?: number | undefined; }; } /** * Broadcast channel 配置選項。 */ interface BroadcastChannelConfig { /** * Timeout duration in milliseconds. Default: 10000ms (10s). */ timeout?: number; /** * Callback function triggered when a timeout occurs. */ onTimeout?: (channel: string, notification: Notification) => void; } /** * Broadcast channel. * * Sends notifications via a broadcast service. */ declare class BroadcastChannel implements NotificationChannel { private broadcastService; private config?; private timeoutChannel; constructor(broadcastService: { broadcast(channel: string, event: string, data: Record): Promise; }, config?: BroadcastChannelConfig | undefined); send(notification: Notification, notifiable: Notifiable, options?: AbortableSendOptions): Promise; } /** * @fileoverview Flare error codes * * Namespaced error codes for the Flare notification module. * * @module @gravito/flare/errors */ /** * Error codes for Flare module operations. * Follows dot-separated namespace convention. */ declare const FlareErrorCodes: { readonly RATE_LIMIT_EXCEEDED: "flare.rate_limit_exceeded"; readonly SERIALIZATION_FAILED: "flare.serialization_failed"; readonly TEMPLATE_NOT_DEFINED: "flare.template_not_defined"; readonly NOTIFIABLE_MISSING_EMAIL: "flare.notifiable_missing_email"; readonly INVALID_CONFIG: "flare.invalid_config"; readonly NOTIFICATION_METHOD_NOT_IMPLEMENTED: "flare.notification_method_not_implemented"; readonly UNSUPPORTED_PROVIDER: "flare.unsupported_provider"; readonly CREDENTIALS_MISSING: "flare.credentials_missing"; readonly SEND_FAILED: "flare.send_failed"; }; type FlareErrorCode = (typeof FlareErrorCodes)[keyof typeof FlareErrorCodes]; /** * @fileoverview Flare error types * * @module @gravito/flare/errors */ /** * Base error class for Flare module. * * Provides structured error handling with error codes and messages. * Extends InfrastructureException for unified error handling across Gravito. * * @example * ```typescript * throw new FlareError('flare.send_failed', 'Failed to send notification') * ``` * @public */ declare class FlareError extends InfrastructureException { /** * Creates a new FlareError instance. * * @param code - The error code. * @param message - The error message. * @param options - Optional infrastructure exception options. */ constructor(code: FlareErrorCode, message: string, options?: InfrastructureExceptionOptions); } /** * Database channel 配置選項。 */ interface DatabaseChannelConfig { /** * Timeout duration in milliseconds. Default: 10000ms (10s). */ timeout?: number; /** * Callback function triggered when a timeout occurs. */ onTimeout?: (channel: string, notification: Notification) => void; } /** * Database channel. * * Persists notifications to a database. */ declare class DatabaseChannel implements NotificationChannel { private dbService; private config?; private timeoutChannel; constructor(dbService: { insertNotification(data: { notifiableId: string | number; notifiableType: string; type: string; data: Record; }): Promise; }, config?: DatabaseChannelConfig | undefined); send(notification: Notification, notifiable: Notifiable, options?: AbortableSendOptions): Promise; } /** * Mail channel 配置選項。 */ interface MailChannelConfig { /** * Timeout duration in milliseconds. Default: 30000ms (30s). */ timeout?: number; /** * Callback function triggered when a timeout occurs. */ onTimeout?: (channel: string, notification: Notification) => void; } /** * Mail channel. * * Sends notifications via the mail service. */ declare class MailChannel implements NotificationChannel { private mailService; private config?; private timeoutChannel; constructor(mailService: { send(message: MailMessage): Promise; }, config?: MailChannelConfig | undefined); send(notification: Notification, notifiable: Notifiable, options?: AbortableSendOptions): Promise; } /** * Slack channel configuration. */ interface SlackChannelConfig { webhookUrl: string; defaultChannel?: string; /** * Timeout 時間(毫秒),預設 30000ms (30秒)。 */ timeout?: number; /** * Timeout 發生時的回調函數。 */ onTimeout?: (channel: string, notification: Notification) => void; } /** * Slack channel. * * Sends notifications via a Slack webhook. */ declare class SlackChannel implements NotificationChannel { private config; private timeoutChannel; constructor(config: SlackChannelConfig); send(notification: Notification, notifiable: Notifiable, options?: AbortableSendOptions): Promise; } /** * SMS channel configuration. */ interface SmsChannelConfig { provider: string; apiKey?: string; apiSecret?: string; from?: string; region?: string; accessKeyId?: string; secretAccessKey?: string; /** * Timeout duration in milliseconds. Default: 30000ms (30s). */ timeout?: number; /** * Callback function triggered when a timeout occurs. */ onTimeout?: (channel: string, notification: Notification) => void; } /** * SMS channel. * * Sends notifications via an SMS provider. */ declare class SmsChannel implements NotificationChannel { private config; private timeoutChannel; constructor(config: SmsChannelConfig); send(notification: Notification, notifiable: Notifiable, options?: AbortableSendOptions): Promise; /** * Send SMS via Twilio with AbortSignal support. */ private sendViaTwilio; /** * Send SMS via AWS SNS with AbortSignal support. */ private sendViaAwsSns; } /** * Configuration options for TimeoutChannel. */ interface TimeoutConfig { /** * Timeout duration in milliseconds. */ timeout: number; /** * Optional callback function triggered when a timeout occurs. * * @param channel - The name of the channel that timed out. * @param notification - The notification instance that was being sent. */ onTimeout?: (channel: string, notification: Notification) => void; } /** * Exception thrown when a notification send operation exceeds the configured timeout. */ declare class TimeoutError extends Error { constructor(message: string); } /** * Exception thrown when a request is aborted by an external AbortController signal. */ declare class AbortError extends Error { constructor(message: string); } /** * Decorator for notification channels that adds timeout and cancellation support. * * Implements actual request cancellation using AbortController and Promise.race. * Compatible with v4.0.0+ cancellation architecture. * * @example * ```typescript * const slackChannel = new SlackChannel(config); * const timeoutChannel = new TimeoutChannel(slackChannel, { * timeout: 5000, * onTimeout: (channel, notification) => { * console.error(`Channel ${channel} timed out`); * } * }); * * // Send with timeout * await timeoutChannel.send(notification, user); * * // Support external manual abort * const controller = new AbortController(); * setTimeout(() => controller.abort(), 3000); * await timeoutChannel.send(notification, user, { signal: controller.signal }); * ``` * * @remarks * In v4.0.0, this class uses AbortController to allow underlying fetch requests * to be physically cancelled, while using Promise.race to ensure the timeout * error is thrown immediately without waiting for the underlying request to finish. */ declare class TimeoutChannel implements NotificationChannel { private inner; private config; constructor(inner: NotificationChannel, config: TimeoutConfig); /** * Sends a notification through the inner channel with a timeout guard. * * @param notification - The notification to send. * @param notifiable - The recipient of the notification. * @param options - Send options including an optional AbortSignal. * @returns A promise that resolves when the notification is sent. * @throws {TimeoutError} Thrown if the operation exceeds the configured timeout. * @throws {AbortError} Thrown if the operation is aborted via the provided signal. */ send(notification: Notification, notifiable: Notifiable, options?: AbortableSendOptions): Promise; } interface NotificationMetric { notification: string; channel: string; success: boolean; duration: number; timestamp: Date; error?: string; retryCount?: number; } interface MetricsSummary { totalSent: number; totalSuccess: number; totalFailed: number; avgDuration: number; byChannel: Record; byNotification: Record; } declare class NotificationMetricsCollector { private metrics; private readonly maxHistory; constructor(maxHistory?: number); record(metric: NotificationMetric): void; getSummary(since?: Date): MetricsSummary; getRecentFailures(limit?: number): NotificationMetric[]; getSlowNotifications(threshold: number, limit?: number): NotificationMetric[]; clear(): void; } declare function toPrometheusFormat(summary: MetricsSummary): string; /** * Middleware type definitions for notification channels. * * 中介層允許在通知發送前後執行自訂邏輯,例如: * - 限流(Rate Limiting) * - 日誌記錄 * - 資料轉換 * - 權限檢查 * - 錯誤處理 * * @packageDocumentation */ /** * 中介層優先級常數 * * 提供預定義的優先級值,用於控制中介層的執行順序。 * 優先級越高(數字越大)越先執行。 * * @example * ```typescript * const securityMiddleware: ChannelMiddleware = { * name: 'security', * priority: MiddlewarePriority.SECURITY, // 最高優先級,最先執行 * async handle(notification, notifiable, channel, next) { * // 安全檢查... * await next() * } * } * ``` * * @public */ declare const MiddlewarePriority: { /** 最高優先級:安全檢查 (100) */ readonly SECURITY: 100; /** 高優先級:限流 (80) */ readonly RATE_LIMIT: 80; /** 中等優先級:驗證 (50) */ readonly VALIDATION: 50; /** 預設優先級 (0) */ readonly DEFAULT: 0; /** 低優先級:日誌記錄 (-50) */ readonly LOGGING: -50; /** 最低優先級:監控 (-100) */ readonly MONITORING: -100; }; /** * 中介層優先級值型別 * * @public */ type MiddlewarePriorityValue = (typeof MiddlewarePriority)[keyof typeof MiddlewarePriority]; /** * Channel middleware interface. * * 中介層提供一個統一的擴展機制,允許在通知發送到特定通道時執行額外的邏輯。 * 中介層可以: * - 在發送前檢查或修改通知 * - 決定是否允許發送(透過是否呼叫 next) * - 在發送後執行清理或記錄操作 * * @example * ```typescript * // 建立一個記錄中介層 * const loggingMiddleware: ChannelMiddleware = { * name: 'logging', * async handle(notification, notifiable, channel, next) { * console.log(`Sending ${notification.constructor.name} via ${channel}`) * await next() * console.log(`Sent successfully`) * } * } * * // 註冊到 NotificationManager * manager.use(loggingMiddleware) * ``` * * @public */ interface ChannelMiddleware { /** * Middleware name (for debugging and identification). * * 中介層名稱,用於除錯和識別。 */ name: string; /** * Middleware priority (optional, default: 0). * * 中介層優先級,數字越大越先執行。 * 預設為 0。相同優先級的中介層按照註冊順序執行。 * * @default 0 * * @example * ```typescript * // 安全檢查應該最先執行 * const securityMiddleware: ChannelMiddleware = { * name: 'security', * priority: MiddlewarePriority.SECURITY, // 100 * async handle(notification, notifiable, channel, next) { * // 檢查權限... * await next() * } * } * * // 限流應該在安全檢查之後 * const rateLimitMiddleware: ChannelMiddleware = { * name: 'rate-limit', * priority: MiddlewarePriority.RATE_LIMIT, // 80 * async handle(notification, notifiable, channel, next) { * // 檢查限流... * await next() * } * } * * // 日誌記錄應該最後執行 * const loggingMiddleware: ChannelMiddleware = { * name: 'logging', * priority: MiddlewarePriority.LOGGING, // -50 * async handle(notification, notifiable, channel, next) { * console.log('Sending...') * await next() * console.log('Sent!') * } * } * ``` */ priority?: number; /** * Handle the notification before it's sent to the channel. * * 處理通知發送請求。此方法會在通知發送到通道之前被呼叫。 * * @param notification - The notification instance being sent * @param notifiable - The recipient of the notification * @param channel - The channel name (e.g., 'mail', 'sms') * @param next - Call this function to continue the middleware chain and send the notification * * @throws {Error} 可以拋出錯誤來阻止通知發送 * * @example * ```typescript * async handle(notification, notifiable, channel, next) { * // 發送前的邏輯 * if (shouldBlock(channel)) { * throw new Error('Channel blocked') * } * * // 繼續執行 * await next() * * // 發送後的邏輯 * console.log('Sent successfully') * } * ``` */ handle(notification: Notification, notifiable: Notifiable, channel: string, next: () => Promise): Promise; } /** * User preference middleware for notification channels. * * This middleware filters notification channels and notification types based on user settings, * allowing users to customize how they receive notifications. * * @packageDocumentation */ /** * User preference middleware for filtering notification channels. * * This middleware filters notifications based on user preferences, supporting: * - Enabling specific channels (enabledChannels) * - Disabling specific channels (disabledChannels, takes precedence over enabledChannels) * - Disabling specific notification types (disabledNotifications) * * @example * ```typescript * // Using Notifiable preferences * const middleware = new PreferenceMiddleware(); * manager.use(middleware); * * // Using custom preference provider * const dbProvider: NotificationPreference = { * async getUserPreferences(notifiable) { * return await db.getUserPrefs(notifiable.getNotifiableId()); * } * }; * const middleware = new PreferenceMiddleware(dbProvider); * manager.use(middleware); * ``` * * @public */ declare class PreferenceMiddleware implements ChannelMiddleware { private preferenceProvider?; private logger?; /** * Middleware name. */ readonly name = "preference"; /** * Middleware priority (medium priority for validation). */ readonly priority: 50; /** * Create a new PreferenceMiddleware instance. * * @param preferenceProvider - Optional preference provider; uses Notifiable method if not provided. * @param logger - Optional logger instance for recording errors. * * @example * ```typescript * // Without provider (reads from Notifiable.getNotificationPreferences) * const middleware = new PreferenceMiddleware(); * * // Using database provider and logger * const middleware = new PreferenceMiddleware(new DatabasePreferenceProvider(), logger); * ``` */ constructor(preferenceProvider?: NotificationPreference | undefined, logger?: { error: (message: string, ...args: unknown[]) => void; } | undefined); /** * Handle the notification and apply user preference filtering. * * Processes the notification and filters based on user preferences: * 1. If notification type is in disabledNotifications, it is skipped. * 2. If channel is in disabledChannels, it is skipped. * 3. If enabledChannels is set, only channels in that list are allowed. * 4. If preference loading fails, the notification is allowed as a fallback. * * @param notification - The notification to send. * @param notifiable - The recipient. * @param channel - The channel name. * @param next - Callback to proceed to the next middleware or send operation. * @returns A promise that resolves when processing is complete. */ handle(notification: Notification, notifiable: Notifiable, channel: string, next: () => Promise): Promise; /** * Get user preferences from Notifiable or custom provider. * * Priority: Notifiable.getNotificationPreferences > preferenceProvider. * * @param notifiable - The recipient. * @returns The user preferences or null if not found. */ private getPreferences; /** * Check if notification type is disabled by user. * * @param notification - The notification instance. * @param preferences - User preferences. * @returns True if the notification is disabled. */ private isNotificationDisabled; /** * Check if channel is allowed by user preferences. * * Priority: * 1. disabledChannels (if listed, it is denied) * 2. enabledChannels (if set, only listed are allowed) * 3. Allow all if neither are set. * * @param channel - The channel name. * @param preferences - User preferences. * @returns True if the channel is allowed. */ private isChannelAllowed; } /** * Rate limiting middleware for notification channels. * * 此中介層使用 Token Bucket 演算法對不同的通知通道進行限流, * 防止短時間內發送過多通知,保護下游服務。 * * @packageDocumentation */ /** * Rate limit configuration for a specific channel. * * 通道限流配置,支援多個時間窗口的限流。 * * @public */ interface ChannelRateLimitConfig { /** * Maximum requests per second. * 每秒最大請求數 */ maxPerSecond?: number; /** * Maximum requests per minute. * 每分鐘最大請求數 */ maxPerMinute?: number; /** * Maximum requests per hour. * 每小時最大請求數 */ maxPerHour?: number; } /** * Rate limit configuration for all channels. * * 所有通道的限流配置,key 為通道名稱。 * * @public */ interface RateLimitConfig { [channel: string]: ChannelRateLimitConfig; } /** * Cache store interface for rate limiting. * * 快取儲存介面,用於支援分散式限流。 * 預設使用記憶體儲存,可替換為 Redis 等分散式快取。 * * @public */ interface CacheStore { get(key: string): Promise; put(key: string, value: T, ttl: number): Promise; forget(key: string): Promise; } /** * Simple in-memory cache store implementation. * * 簡單的記憶體快取實作,用於單機環境。 * 生產環境建議使用 Redis 等分散式快取。 * * @public */ declare class MemoryStore implements CacheStore { private cache; private cleanupInterval?; constructor(cleanupIntervalMs?: number); get(key: string): Promise; put(key: string, value: T, ttl: number): Promise; forget(key: string): Promise; /** * 清理所有資源,停止清理計時器 */ destroy(): void; } /** * Rate limiting middleware using Token Bucket algorithm. * * 此中介層為每個通道維護獨立的 Token Bucket, * 支援每秒、每分鐘、每小時三種時間窗口的限流。 * * @example * ```typescript * const rateLimiter = new RateLimitMiddleware({ * email: { * maxPerSecond: 10, * maxPerMinute: 100, * maxPerHour: 1000 * }, * sms: { * maxPerSecond: 5, * maxPerMinute: 50 * } * }) * * manager.use(rateLimiter) * ``` * * @public */ declare class RateLimitMiddleware implements ChannelMiddleware { private config; /** * Middleware name. */ readonly name = "rate-limit"; /** * Middleware priority (high priority, executes early in the chain). * 中介層優先級(高優先級,在鏈中較早執行) */ readonly priority: 80; /** * Token buckets for each channel and time window. * Key format: `{channel}:{window}` (e.g., 'email:second', 'sms:minute') */ private buckets; /** * Cache store for distributed rate limiting. */ store: CacheStore; /** * Create a new RateLimitMiddleware instance. * * @param config - Rate limit configuration for each channel * @param store - Optional cache store for distributed rate limiting (future use) * * @example * ```typescript * // 使用預設記憶體儲存 * const middleware = new RateLimitMiddleware({ * email: { maxPerSecond: 10 } * }) * * // 使用 Redis 儲存(分散式環境) * const middleware = new RateLimitMiddleware({ * email: { maxPerSecond: 10 } * }, redisStore) * ``` */ constructor(config: RateLimitConfig, store?: CacheStore); /** * Initialize token buckets for all configured channels. * * 為所有配置的通道初始化 Token Bucket。 * * @private */ private initializeBuckets; /** * Handle the notification and apply rate limiting. * * 處理通知並應用限流規則。如果超過任一時間窗口的限制, * 將拋出錯誤阻止通知發送。 * * @param notification - The notification being sent * @param notifiable - The recipient * @param channel - The channel name * @param next - Continue to the next middleware or send the notification * * @throws {Error} 當超過限流時拋出錯誤 */ handle(_notification: Notification, _notifiable: Notifiable, channel: string, next: () => Promise): Promise; /** * Get current rate limit status for a channel. * * 獲取指定通道的當前限流狀態(用於除錯和監控)。 * * @param channel - The channel name * @returns Current token counts for each time window * * @example * ```typescript * const status = middleware.getStatus('email') * console.log(`Email remaining: ${status.second}/${config.email.maxPerSecond}`) * ``` */ getStatus(channel: string): { second?: number; minute?: number; hour?: number; }; /** * Reset rate limit for a specific channel. * * 重置指定通道的限流計數(用於測試或手動重置)。 * * @param channel - The channel name to reset * * @example * ```typescript * // 在測試中重置限流 * middleware.reset('email') * ``` */ reset(channel: string): void; } /** * Notification manager. * * Responsible for managing notification channels and delivering notifications. */ declare class NotificationManager { private core; /** * Channel registry. */ private channels; /** * Middleware stack for intercepting channel sends. */ private middlewares; /** * Indicates whether the middleware stack needs re-sorting. */ private middlewaresDirty; /** * Queue manager (optional, injected by `orbit-queue`). */ private queueManager?; /** * Type-safe hook emitter for notification events. */ private hookEmitter; constructor(core: PlanetCore); private metrics?; /** * Enable metrics collection. */ enableMetrics(maxHistory?: number): void; /** * Get metrics summary. */ getMetrics(since?: Date): MetricsSummary | undefined; /** * Get recent failures. */ getRecentFailures(limit?: number): NotificationMetric[]; /** * Register a notification channel. * * @param name - The name of the channel. * @param channel - The channel instance. */ channel(name: string, channel: NotificationChannel): void; /** * Register a middleware for intercepting channel sends. * * Middleware will be executed in the order they are registered. * Each middleware can modify, block, or monitor the notification flow. * * @param middleware - The middleware instance to register. * * @example * ```typescript * import { RateLimitMiddleware } from '@gravito/flare' * * const rateLimiter = new RateLimitMiddleware({ * email: { maxPerSecond: 10 }, * sms: { maxPerSecond: 5 } * }) * * manager.use(rateLimiter) * ``` */ use(middleware: ChannelMiddleware): void; /** * Register the queue manager (called by `orbit-queue`). * * @param manager - The queue manager implementation. */ setQueueManager(manager: NotificationManager['queueManager']): void; /** * Send a notification. * * @param notifiable - The recipient of the notification. * @param notification - The notification instance. * @param options - Options for sending. * @returns A promise that resolves to the notification result. * * @example * ```typescript * const result = await notificationManager.send(user, new InvoicePaid(invoice)) * if (!result.allSuccess) { ... } * ``` */ send(notifiable: Notifiable, notification: Notification, options?: SendOptions): Promise; /** * Batch send notification to multiple recipients. * * @param notifiables - List of recipients. * @param notification - The notification instance. * @param options - Options for sending. * @returns A promise that resolves to the batch result. */ sendBatch(notifiables: Notifiable[], notification: Notification, options?: SendOptions & { /** Batch concurrency (default: 10) */ batchConcurrency?: number; }): Promise; /** * Batch send notification to multiple recipients (streaming). * * @param notifiables - AsyncIterable or Iterable of recipients. * @param notification - The notification instance. * @param options - Options for sending. * @yields Notification results as they are processed. */ sendBatchStream(notifiables: AsyncIterable | Iterable, notification: Notification, options?: SendOptions & { batchSize?: number; }): AsyncGenerator; /** * Send immediately (without queue). */ private sendNow; private sendSequential; private sendParallel; private sendWithConcurrencyLimit; private processWithConcurrency; private sendToChannel; private executeChannelSend; /** * Retrieves the sorted list of middleware (Lazy sorting). * * Uses a lazy evaluation strategy: sorting only happens when needed to avoid * overhead on every `use()` call. * Sorting rules: * 1. Higher priority (larger number) executes first. * 2. Stable sort is maintained for identical priorities (registration order). * * @returns The sorted list of middleware. * @private */ private getSortedMiddlewares; /** * Execute middleware chain recursively. * * @param index - Current middleware index * @param notification - The notification being sent * @param notifiable - The recipient * @param channelName - The channel name * @param finalHandler - The final handler to execute (actual channel.send) * @private */ private executeMiddlewareChain; private getRetryConfig; /** * Serialize notification (for queuing). * * @param notification - The notification to serialize. * @returns A plain object representation of the notification. */ private serializeNotification; } /** * Options for configuring OrbitFlare. * @public */ interface OrbitFlareOptions { /** Enable or disable the email notification channel. */ enableMail?: boolean; /** Enable or disable the database storage notification channel. */ enableDatabase?: boolean; /** Enable or disable the real-time broadcast notification channel. */ enableBroadcast?: boolean; /** Enable or disable the Slack notification channel. */ enableSlack?: boolean; /** Enable or disable the SMS notification channel. */ enableSms?: boolean; /** Custom channel configuration records for flexible extension. */ channels?: Record; /** Channel middleware to apply to all notifications. */ middleware?: ChannelMiddleware[]; /** Custom notification preference provider. */ preferenceProvider?: NotificationPreference; /** Enable automatic preference filtering middleware (default: false). */ enablePreference?: boolean; } /** * OrbitFlare is the official notification orbit for Gravito. * It provides a unified API for sending notifications across multiple channels * (Mail, Database, Broadcast, Slack, SMS) and supports background queuing. * * @example * ```typescript * const flare = new OrbitFlare({ * enableSlack: true, * channels: { slack: { webhookUrl: '...' } } * }); * core.addOrbit(flare); * * // Usage in controller * await ctx.get('notifications').send(user, new WelcomeNotification()); * ``` * @public */ declare class OrbitFlare implements GravitoOrbit { private options; constructor(options?: OrbitFlareOptions); /** * Configure OrbitFlare. * * @param options - The OrbitFlare configuration options. * @returns A new OrbitFlare instance. */ static configure(options?: OrbitFlareOptions): OrbitFlare; private validateOptions; private isValidUrl; /** * Install OrbitFlare into PlanetCore. * * @param core - The PlanetCore instance. */ install(core: PlanetCore): Promise; private setupMailChannel; private setupDatabaseChannel; private setupBroadcastChannel; private setupSlackChannel; private setupSmsChannel; private setupQueueIntegration; private setupMiddleware; private isMailService; private isDatabaseService; private isBroadcastService; private isQueueService; } declare module '@gravito/core' { interface GravitoVariables { /** Notification manager for multi-channel notifications */ notifications?: NotificationManager; } } interface TemplateData { [key: string]: unknown; } interface MailTemplate { subject: string; view?: string; data?: TemplateData; } interface SlackTemplate { text: string; channel?: string; attachments?: Array<{ color?: string; title?: string; text?: string; }>; } declare abstract class TemplatedNotification extends Notification { protected data: TemplateData; with(data: TemplateData): this; protected abstract mailTemplate(): MailTemplate; protected slackTemplate?(): SlackTemplate; toMail(notifiable: Notifiable): MailMessage; toSlack(_notifiable: Notifiable): SlackMessage; /** * 將 Markdown 模板渲染為 HTML。 * 先執行變數插值(`{{var}}`),再將 Markdown 轉為 HTML。 * 內建 XSS 防護:過濾 script、iframe、event handler 等危險 HTML。 * * 適用於郵件、Slack 等富文本通知頻道。 * * @param template - Markdown 格式的模板字串 * @returns 安全的 HTML 字串 * * @example * ```typescript * const html = this.renderMarkdown('# Hello {{name}}') * await sendEmail({ htmlBody: html }) * ``` */ protected renderMarkdown(template: string): string; protected interpolate(text: string): string; private getRecipientEmail; } /** * FlareHooks 型別定義 * * 提供型別安全的 hook 事件系統,包含所有通知相關的 hook 事件和 payload 型別。 * * @packageDocumentation */ /** * FlareHooks 事件名稱列舉 * * 包含所有通知系統支援的 hook 事件。 * * @public */ type FlareHookEvent = 'notification:sending' | 'notification:queued' | 'notification:sent' | 'notification:channel:sending' | 'notification:channel:sent' | 'notification:channel:failed' | 'notification:channel:retry' | 'notification:batch:start' | 'notification:batch:complete'; /** * 各 hook 事件的 Payload 型別映射 * * 定義每個 hook 事件對應的 payload 結構,提供完整的型別安全。 * * @example * ```typescript * // 監聽通知發送事件 * hooks.on('notification:sending', (payload: FlareHookPayloads['notification:sending']) => { * console.log(`Sending ${payload.notification.constructor.name} to ${payload.notifiable.getNotifiableId()}`) * }) * ``` * * @public */ interface FlareHookPayloads { /** * 通知開始發送前觸發 */ 'notification:sending': { /** 要發送的通知實例 */ notification: Notification; /** 接收通知的對象 */ notifiable: Notifiable; /** 要發送的通道列表 */ channels: string[]; }; /** * 通知被加入佇列時觸發 */ 'notification:queued': { /** 要發送的通知實例 */ notification: Notification; /** 接收通知的對象 */ notifiable: Notifiable; /** 要發送的通道列表 */ channels: string[]; }; /** * 通知發送完成後觸發 */ 'notification:sent': { /** 已發送的通知實例 */ notification: Notification; /** 接收通知的對象 */ notifiable: Notifiable; /** 各通道的發送結果 */ results: SendResult[]; /** 是否所有通道都發送成功 */ allSuccess: boolean; /** 總執行時間(毫秒) */ totalDuration: number; }; /** * 通道開始發送前觸發 */ 'notification:channel:sending': { /** 要發送的通知實例 */ notification: Notification; /** 接收通知的對象 */ notifiable: Notifiable; /** 通道名稱 */ channel: string; }; /** * 通道發送成功後觸發 */ 'notification:channel:sent': { /** 已發送的通知實例 */ notification: Notification; /** 接收通知的對象 */ notifiable: Notifiable; /** 通道名稱 */ channel: string; /** 發送耗時(毫秒) */ duration: number; }; /** * 通道發送失敗時觸發 */ 'notification:channel:failed': { /** 發送失敗的通知實例 */ notification: Notification; /** 接收通知的對象 */ notifiable: Notifiable; /** 通道名稱 */ channel: string; /** 錯誤物件 */ error: Error; /** 發送耗時(毫秒) */ duration: number; }; /** * 通道重試時觸發 */ 'notification:channel:retry': { /** 要重試的通知實例 */ notification: Notification; /** 接收通知的對象 */ notifiable: Notifiable; /** 通道名稱 */ channel: string; /** 錯誤物件 */ error: Error; /** 當前重試次數 */ attempt: number; /** 下次重試延遲(毫秒) */ nextDelay: number; }; /** * 批次發送開始時觸發 */ 'notification:batch:start': { /** 要發送的通知實例 */ notification: Notification; /** 接收者數量 */ count: number; }; /** * 批次發送完成時觸發 */ 'notification:batch:complete': { /** 已發送的通知實例 */ notification: Notification; /** 總接收者數量 */ total: number; /** 成功數量 */ success: number; /** 失敗數量 */ failed: number; /** 總執行時間(毫秒) */ duration: number; }; } /** * FlareHooks 介面 * * 提供型別安全的 hook 系統介面,用於發送和監聽通知事件。 * * @remarks * 此介面主要用於型別檢查,實際的 hook 系統由 @gravito/core 提供。 * * @example * ```typescript * // 發送型別安全的 hook 事件 * const emitter = createHookEmitter(core) * await emitter.emit('notification:sending', { * notification, * notifiable, * channels: ['mail', 'sms'] * }) * ``` * * @public */ interface FlareHooks { /** * 發送 hook 事件 * * @param event - 事件名稱 * @param payload - 事件 payload,型別由事件名稱決定 */ emit(event: E, payload: FlareHookPayloads[E]): Promise; /** * 監聽 hook 事件 * * @param event - 事件名稱 * @param handler - 事件處理函數 */ on(event: E, handler: (payload: FlareHookPayloads[E]) => void): void; /** * 移除 hook 事件監聽器 * * @param event - 事件名稱 * @param handler - 要移除的事件處理函數 */ off(event: E, handler: (payload: FlareHookPayloads[E]) => void): void; } /** * Hook Emitter 輔助工具 * * 提供型別安全的 hook emit 功能,封裝底層 @gravito/core 的 hooks 系統。 * * @packageDocumentation */ /** * 型別安全的 Hook Emitter * * 封裝 PlanetCore 的 hooks.emit,提供完整的型別檢查和 IntelliSense 支援。 * * @public */ interface HookEmitter { /** * 發送 hook 事件 * * @param event - 事件名稱 * @param payload - 事件 payload,型別由事件名稱決定 * @returns Promise,在所有監聽器執行完成後 resolve * * @example * ```typescript * const emitter = createHookEmitter(core) * await emitter.emit('notification:sending', { * notification, * notifiable, * channels: ['mail', 'sms'] * }) * ``` */ emit(event: E, payload: FlareHookPayloads[E]): Promise; } /** * 建立型別安全的 hook emitter * * 將 PlanetCore 的 hooks 系統包裝成型別安全的 emitter, * 避免在 NotificationManager 中使用 `any` 型別斷言。 * * @param core - PlanetCore 實例 * @returns 型別安全的 HookEmitter 實例 * * @example * ```typescript * export class NotificationManager { * private hookEmitter: HookEmitter * * constructor(private core: PlanetCore) { * this.hookEmitter = createHookEmitter(core) * } * * async send(notifiable: Notifiable, notification: Notification) { * await this.hookEmitter.emit('notification:sending', { * notification, * notifiable, * channels: notification.via(notifiable) * }) * // ... * } * } * ``` * * @public */ declare function createHookEmitter(core: PlanetCore): HookEmitter; /** * Lazy Loading 通知基類 * * 用於處理包含大量資料或需要資料庫查詢的通知。 * 只儲存識別符 (ID),在需要時才載入完整資料。 * * @typeParam TData - 載入的資料型別 * * @example * ```typescript * class InvoicePaidNotification extends LazyNotification { * constructor(private invoiceId: string) { * super() * } * * protected async loadData(notifiable: Notifiable): Promise { * return await db.invoices.findById(this.invoiceId) * } * * toMail(notifiable: Notifiable): MailMessage { * const invoice = this.getCached() * if (!invoice) { * throw new Error('Invoice data not loaded') * } * return { * subject: `Invoice #${invoice.number} paid`, * text: `Amount: ${invoice.amount}` * } * } * } * ``` */ declare abstract class LazyNotification extends Notification { /** * 快取的資料 */ private _cachedData?; /** * 載入資料的抽象方法(由子類實作) * * @param notifiable - 通知接收者 * @returns 載入的資料 */ protected abstract loadData(notifiable: Notifiable): Promise; /** * 取得快取的資料 * * @returns 快取的資料,如果尚未載入則回傳 undefined */ protected getCached(): TData | undefined; /** * 設定快取資料 * * @param data - 要快取的資料 */ protected setCached(data: TData): void; /** * 檢查資料是否已載入 * * @returns 如果資料已快取則回傳 true */ protected isLoaded(): boolean; /** * 清除快取 */ protected clearCache(): void; /** * 載入資料並快取(如果尚未載入) * * @param notifiable - 通知接收者 * @returns 載入的資料 */ protected ensureLoaded(notifiable: Notifiable): Promise; } /** * Deep serialize object, handling special types. */ declare function deepSerialize(obj: unknown, seen?: WeakSet): unknown; /** * Deserialize object, restoring special types. */ declare function deepDeserialize(obj: unknown): unknown; /** * 序列化檢查結果介面 */ interface SerializationCheckResult { /** 是否可安全序列化 */ serializable: boolean; /** 有問題的屬性路徑列表 */ problematicPaths: string[]; /** 警告訊息列表 */ warnings: string[]; } /** * 檢查物件是否可安全序列化 * * @param obj - 要檢查的物件 * @param path - 當前路徑(用於巢狀物件追蹤) * @returns 序列化檢查結果 * * @example * ```typescript * const result = checkSerializable({ name: 'test', fn: () => {} }) * console.log(result.serializable) // false * console.log(result.problematicPaths) // ['fn'] * ``` */ declare function checkSerializable(obj: unknown, path?: string): SerializationCheckResult; /** * 斷言物件可序列化,不可序列化時拋出錯誤 * * @param obj - 要檢查的物件 * @throws {Error} 當物件不可序列化時 * * @example * ```typescript * assertSerializable({ name: 'test' }) // OK * assertSerializable({ fn: () => {} }) // 拋出錯誤 * ``` */ declare function assertSerializable(obj: unknown): void; /** * Simple Token Bucket algorithm for rate limiting. * * Token Bucket 是一種常用的限流演算法,用於控制請求頻率。 * 它透過一個固定容量的桶來存儲 tokens,並以固定速率補充。 * 每個請求需要消耗一個或多個 tokens,當桶內 tokens 不足時請求被拒絕。 * * @example * ```typescript * // 創建一個容量為 10,每秒補充 5 個 tokens 的桶 * const bucket = new TokenBucket(10, 5) * * // 嘗試消耗 1 個 token * if (bucket.tryConsume()) { * // 請求被允許 * } else { * // 請求被限流 * } * ``` * * @public */ declare class TokenBucket { private capacity; private refillRate; /** * 當前可用的 tokens 數量 */ private tokens; /** * 上次補充 tokens 的時間戳(毫秒) */ private lastRefill; /** * 創建一個新的 TokenBucket 實例 * * @param capacity - 桶的最大容量(tokens 上限) * @param refillRate - 每秒補充的 tokens 數量 * * @example * ```typescript * // 每秒最多 100 個請求 * const bucket = new TokenBucket(100, 100) * ``` */ constructor(capacity: number, refillRate: number); /** * 嘗試從桶中消耗指定數量的 tokens * * 此方法會先執行 token 補充,然後檢查是否有足夠的 tokens。 * 如果有足夠的 tokens,則消耗並返回 true;否則返回 false 且不消耗。 * * @param tokens - 要消耗的 tokens 數量(預設為 1) * @returns 如果成功消耗則返回 true,否則返回 false * * @example * ```typescript * const bucket = new TokenBucket(10, 1) * * // 嘗試消耗 1 個 token * if (bucket.tryConsume()) { * console.log('請求被允許') * } * * // 嘗試消耗 3 個 tokens * if (bucket.tryConsume(3)) { * console.log('批次請求被允許') * } * ``` */ tryConsume(tokens?: number): boolean; /** * 獲取當前可用的 tokens 數量 * * 此方法會先執行補充操作,然後返回當前的 tokens 數量。 * * @returns 當前可用的 tokens 數量 * * @example * ```typescript * const bucket = new TokenBucket(10, 1) * console.log(`剩餘 ${bucket.getTokens()} 個 tokens`) * ``` */ getTokens(): number; /** * 根據經過的時間補充 tokens * * 此方法計算自上次補充以來經過的時間,並根據 refillRate 補充相應數量的 tokens。 * tokens 數量不會超過容量上限。 * * @private */ private refill; } export { AbortError, type BatchResult, BroadcastChannel, type BroadcastNotification, type CacheStore, type ChannelFailurePayload, type ChannelHookPayload, type ChannelMiddleware, type ChannelRateLimitConfig, type ChannelSuccessPayload, DatabaseChannel, type DatabaseNotification, FlareError, type FlareErrorCode, FlareErrorCodes, type FlareHookEvent, type FlareHookPayloads, type FlareHooks, type HookEmitter, LazyNotification, MailChannel, type MailChannelConfig, type MailMessage, type MailTemplate, MemoryStore, type MetricsSummary, MiddlewarePriority, type MiddlewarePriorityValue, type Notifiable, Notification, type NotificationBatchCompletePayload, type NotificationBatchStartPayload, type NotificationChannel, type NotificationCompletePayload, type NotificationHookPayload, NotificationManager, type NotificationMetric, NotificationMetricsCollector, type NotificationPreference, type NotificationResult, OrbitFlare, type OrbitFlareOptions, PreferenceMiddleware, type RateLimitConfig, RateLimitMiddleware, type RetryConfig, type SendOptions, type SendResult, type SerializationCheckResult, type ShouldQueue, type ShouldRetry, SlackChannel, type SlackChannelConfig, type SlackMessage, type SlackTemplate, SmsChannel, type SmsChannelConfig, type SmsMessage, type TemplateData, TemplatedNotification, TimeoutChannel, type TimeoutConfig, TimeoutError, TokenBucket, assertSerializable, checkSerializable, createHookEmitter, deepDeserialize, deepSerialize, toPrometheusFormat };