import { GoodVibesSdkError } from '@pellux/goodvibes-errors'; import type { FeatureFlagManager } from '../runtime/feature-flags/index.js'; import type { ConfigManager } from '../config/manager.js'; /** * The three possible outcomes for a single integration delivery attempt. * * - `delivered` , message reached the destination successfully * - `retrying` , delivery failed with a retryable error; queued for retry * - `dead_letter`, all retry attempts exhausted or terminal failure; moved to DLQ */ export type DeliveryOutcome = 'delivered' | 'retrying' | 'dead_letter'; /** * Classification of a delivery failure. * * - `retryable`, transient error; should be retried with backoff * (network timeout, HTTP 429, HTTP 5xx) * - `terminal` , permanent error; should not be retried * (HTTP 400/401/403/404, invalid URL, message too large) */ export type DeliveryFailureClass = 'retryable' | 'terminal'; /** * Classify a delivery error as retryable or terminal. * * Rules (in order): * 1. Network errors with no HTTP status → retryable (timeout, ECONNREFUSED, etc.) * 2. HTTP 4xx (except 408/429) → terminal (auth failure, bad request, not found) * 3. HTTP 429 / 5xx → retryable * 4. Unknown → retryable (prefer retry over silent drop) */ export declare function classifyDeliveryError(error: unknown): DeliveryFailureClass; /** Typed delivery error that carries an explicit failure classification. */ export declare class DeliveryError extends GoodVibesSdkError { readonly failureClass: DeliveryFailureClass; readonly statusCode?: number | undefined; readonly code: 'DELIVERY_ERROR'; constructor(message: string, failureClass: DeliveryFailureClass, statusCode?: number | undefined); } /** * A single entry in the dead-letter queue. * Immutable snapshot of a delivery that exhausted all retry attempts. */ export interface DeadLetterEntry { /** Unique entry identifier. */ readonly id: string; /** Integration channel (e.g. "slack", "discord", "webhook"). */ readonly channel: string; /** Event name that triggered the delivery. */ readonly event: string; /** Message payload that failed to deliver. */ readonly payload: string; /** Epoch ms when the entry was created (first attempt). */ readonly createdAt: number; /** Epoch ms when the entry moved to the DLQ. */ readonly deadAt: number; /** Number of delivery attempts made. */ readonly attempts: number; /** Final error message. */ readonly finalError: string; /** Failure class of the final error. */ readonly failureClass: DeliveryFailureClass; } /** Counters for delivery SLO tracking. */ export interface DeliveryMetrics { /** Total delivery attempts (all channels combined). */ readonly totalAttempts: number; /** Successfully delivered messages. */ readonly delivered: number; /** Messages currently queued for retry. */ readonly retrying: number; /** Messages moved to the dead-letter queue. */ readonly deadLettered: number; /** Total entries in the DLQ (including previously replayed). */ readonly dlqSize: number; } /** Configuration for the DeliveryQueue. */ export interface DeliveryQueueConfig { /** * Maximum retry attempts after the initial delivery attempt. * E.g., maxRetries: 3 means 4 total attempts (1 initial + 3 retries). */ maxRetries: number; /** Initial backoff delay in ms (default: 1000). */ initialDelayMs: number; /** Maximum backoff delay in ms (default: 30_000). */ maxDelayMs: number; /** Maximum dead-letter queue size; oldest entries evicted when exceeded (default: 500). */ maxDlqSize: number; /** * When true, SLO enforcement is active: dead-letter events are logged at * error level and metrics are updated. When false, failures are logged at * warn level only. * * Controlled by the integrations.delivery.sloEnforced setting (the `integration-delivery-slo` gate). */ sloEnforced: boolean; } export interface DeliveryQueueOptions extends Partial { readonly featureFlags?: Pick | null | undefined; /** * Optional config source. When supplied, retry/backoff/DLQ/SLO defaults are read * from integrations.delivery.*, explicit option fields still override, and the * gate remains the fallback source for sloEnforced. */ readonly configManager?: Pick | null | undefined; } /** * Delivery queue with retry/backoff and dead-letter storage. * * Wrap any integration send operation with `enqueue()`. The queue: * 1. Attempts delivery immediately. * 2. On retryable failure: schedules retry with exponential backoff + jitter. * 3. On terminal failure or exhausted retries: moves entry to DLQ. * 4. Emits `delivery:dead_letter` events to registered listeners. * * Dead-letter entries can be replayed via `replay()` or cleared with `clearDlq()`. * * Enable SLO enforcement via the integrations.delivery.sloEnforced setting to * surface dead-letter failures as error-level log entries and expose them in * integration diagnostics. */ export declare class DeliveryQueue { private readonly _config; private readonly _dlq; private readonly _pending; private readonly _timers; private readonly _listeners; private _totalAttempts; private _delivered; private _retrying; private _deadLettered; constructor(config?: DeliveryQueueOptions); /** * Enqueue a delivery attempt. * * @param channel - Integration channel identifier (e.g. "slack"). * @param event - Event name for tracing. * @param payload - Message text to deliver. * @param deliver - Async function that performs the actual delivery. * @returns The delivery outcome for the immediate attempt. */ enqueue(channel: string, event: string, payload: string, deliver: () => Promise): Promise; /** * Replay all dead-letter entries. * * Each entry is re-enqueued with a fresh retry budget. The DLQ is cleared * on a per-entry basis as each replayed entry resolves. * * @param deliver - Optional delivery function override. When omitted, * the original delivery function is not available (DLQ is persistent * storage), so a no-op is used and the caller must provide one. * * @returns Array of per-entry replay results. */ replay(deliver: (entry: DeadLetterEntry) => Promise): Promise>; /** * Register a listener invoked whenever an entry moves to the DLQ. * Returns an unsubscribe function. */ onDeadLetter(listener: (entry: DeadLetterEntry) => void): () => void; /** Get current dead-letter queue contents (snapshot). */ getDlq(): readonly DeadLetterEntry[]; /** Clear all dead-letter entries. */ clearDlq(): number; /** Whether SLO enforcement is active for this queue. */ get sloEnforced(): boolean; /** Current delivery metrics snapshot. */ getMetrics(): DeliveryMetrics; /** * Cancel all pending retry timers and clear internal state. * Call on shutdown to prevent timer leaks. */ dispose(): void; private _attempt; private _moveToDlq; private _computeDelay; } /** * Snapshot of a DeliveryQueue for display in integration diagnostics. */ export interface IntegrationQueueStatus { /** Integration channel identifier. */ readonly channel: string; /** Current delivery metrics. */ readonly metrics: DeliveryMetrics; /** Dead-letter entries (most recent first, capped at 50 for display). */ readonly dlqEntries: readonly DeadLetterEntry[]; /** Whether SLO enforcement is active. */ readonly sloEnforced: boolean; /** Epoch ms of this snapshot. */ readonly capturedAt: number; } /** * Produce a diagnostics snapshot for a channel's DeliveryQueue. */ export declare function snapshotQueueStatus(channel: string, queue: DeliveryQueue, sloEnforced: boolean): IntegrationQueueStatus; //# sourceMappingURL=delivery.d.ts.map