import { type ActionLedgerRequestContextValues } from "@voyant-travel/action-ledger"; import type { AnyDrizzleDb } from "@voyant-travel/db"; import type { ToolHandlerActionPolicyContext } from "@voyant-travel/tools"; import type { PostgresJsDatabase } from "drizzle-orm/postgres-js"; import { type NotificationSendOperation, type notificationChannelEnum, type notificationTargetTypeEnum } from "./schema.js"; import { NotificationError, type NotificationService } from "./service-shared.js"; import type { SendTemplatedNotificationInput } from "./tools.js"; import type { NotificationAttachment, NotificationProvider, NotificationResult } from "./types.js"; export declare const NOTIFICATION_SEND_REQUESTED_EVENT = "notification.send-requested"; export declare const NOTIFICATION_SEND_COMPLETED_EVENT = "notification.sent"; export declare const NOTIFICATION_SEND_DEAD_LETTERED_EVENT = "notification.send-dead-lettered"; export declare class NotificationDurableProviderError extends NotificationError { constructor(provider: string, reason: string); } export interface NotificationEnqueueRequest { idempotencyKey: string; templateId?: string | null; templateSlug?: string | null; channel?: (typeof notificationChannelEnum.enumValues)[number]; provider?: string | null; to: string; from?: string | null; subject?: string | null; html?: string | null; text?: string | null; attachments?: ReadonlyArray | null; data?: Record | null; targetType: (typeof notificationTargetTypeEnum.enumValues)[number]; targetId?: string | null; bookingId?: string | null; invoiceId?: string | null; paymentSessionId?: string | null; reminderRunId?: string | null; personId?: string | null; organizationId?: string | null; metadata?: Record | null; scheduledFor?: string | Date | null; /** * Domain event emitted atomically with successful provider settlement. * Delivery/provider identifiers are added by the durable worker. */ terminalEvent?: { name: string; data: Record; } | null; } export interface EnqueueNotificationInput { db: AnyDrizzleDb; registry: NotificationService; input: NotificationEnqueueRequest; testHooks?: { afterPrepare?: (tx: AnyDrizzleDb, operationId: string) => Promise; }; } export interface ExecuteDurableNotificationSendInput { db: AnyDrizzleDb; context: ActionLedgerRequestContextValues; admitted: ToolHandlerActionPolicyContext; dispatcher: NotificationService; input: SendTemplatedNotificationInput; testHooks?: { afterPrepare?: (tx: AnyDrizzleDb, operationId: string) => Promise; }; } /** Admit the template-only Tool command and persist the immutable pending send. */ export declare function executeDurableNotificationSendCommand(input: ExecuteDurableNotificationSendInput): Promise>>; /** * The only notification mutation path. It resolves the provider and template, * fingerprints the complete canonical delivery request, and atomically writes * the delivery, durable operation, reminder link, and requested outbox event. * Provider code is worker-only. */ export declare function enqueueNotification({ db, registry, input, testHooks, }: EnqueueNotificationInput): Promise<{ bookingId: string | null; provider: string; id: string; channel: "email" | "sms"; status: "pending" | "sent" | "failed" | "cancelled"; fromAddress: string | null; metadata: Record | null; createdAt: Date; updatedAt: Date; templateId: string | null; templateSlug: string | null; targetType: "booking" | "booking_payment_schedule" | "booking_guarantee" | "invoice" | "payment_session" | "person" | "organization" | "other"; targetId: string | null; personId: string | null; organizationId: string | null; invoiceId: string | null; paymentSessionId: string | null; providerMessageId: string | null; toAddress: string; subject: string | null; htmlBody: string | null; textBody: string | null; payloadData: Record | null; errorMessage: string | null; scheduledFor: Date | null; sentAt: Date | null; failedAt: Date | null; }>; export interface DrainDurableNotificationSendsOptions { limit?: number; now?: Date; visibilityTimeoutMs?: number; retryBaseMs?: number; testHooks?: { afterProviderAccepted?: (operation: NotificationSendOperation, result: NotificationResult) => Promise; beforeCompletionEvent?: (tx: AnyDrizzleDb, operation: NotificationSendOperation) => Promise; }; } export interface DrainDurableNotificationSendsResult { claimed: number; sent: number; retried: number; deadLettered: number; } /** * Claim and process recoverable provider work. Every retry invokes the * provider's one idempotent mutation with the same provider-scoped key. */ export declare function drainDurableNotificationSends(db: PostgresJsDatabase, providers: ReadonlyArray, options?: DrainDurableNotificationSendsOptions): Promise; /** Whether queued or leased sends require the exact selected provider runtime. */ export declare function hasRecoverableNotificationSends(db: AnyDrizzleDb): Promise;