// Wildduck Event Bus Type Definitions // Event system types for in-memory event distribution export namespace Wildduck { // ============================================================================ // Event Type Constants // ============================================================================ /** * All event types supported by Wildduck event bus (15 total) */ export type WildduckEventType = // Message Events (4) | 'message.added' | 'message.flags.changed' | 'message.moved' | 'message.deleted' // Address Events (2) | 'address.user.created' | 'address.user.deleted' // Mailbox Events (3) | 'mailbox.created' | 'mailbox.renamed' | 'mailbox.deleted' // Settings Events (3) | 'settings.updated' | 'autoreply.user.enabled' | 'autoreply.user.disabled' // Filter Events (2) | 'filter.created' | 'filter.deleted' // Spam Events (2) | 'marked.spam' | 'marked.ham'; /** * Event categories */ export type WildduckEventCategory = | 'address' | 'mailbox' | 'message' | 'settings' | 'filter' | 'spam' | 'autoreply' | 'user' | 'asp' | 'mfa' | 'dkim' | 'cert' | 'domainalias'; /** * Event actions */ export type WildduckEventAction = | 'created' | 'updated' | 'deleted' | 'moved' | 'changed' | 'added' | 'enabled' | 'disabled' | 'renamed' | 'marked'; /** * Event source types */ export type WildduckEventSource = | 'api' | 'imap' | 'imap-append' | 'imap-copy' | 'imap-move' | 'lmtp' | 'pop3' | 'smtp' | 'task' | 'webhook' | 'unknown'; // ============================================================================ // Core Event Types // ============================================================================ /** * Base event structure - all events follow this normalized format */ export interface WildduckEvent { /** * Full event type (e.g., 'user.created', 'message.added') */ type: WildduckEventType; /** * Event category (first part of type, e.g., 'user', 'message') */ category: WildduckEventCategory; /** * Event action (last part of type, e.g., 'created', 'added') */ action: WildduckEventAction; /** * Event timestamp */ timestamp: Date; /** * Event source (where the event originated) */ source: WildduckEventSource; /** * User ID (MongoDB ObjectId as string) */ user?: string; /** * Address ID (MongoDB ObjectId as string) */ address?: string; /** * Mailbox ID (MongoDB ObjectId as string) */ mailbox?: string; /** * Message ID (MongoDB ObjectId as string) */ message?: string; /** * Event-specific data (non-boolean fields) */ data?: Record; /** * Event metadata (boolean flags) */ metadata?: Record; } /** * Raw event data from lib/events.js publish() function * This is what gets emitted before transformation */ export interface WildduckRawEventData { /** * Event type string */ ev: string; /** * Event timestamp */ time?: number; /** * Event source */ source?: WildduckEventSource; /** * User ID */ user?: string; /** * Address ID */ address?: string; /** * Mailbox ID */ mailbox?: string; /** * Message ID */ message?: string; /** * Additional event-specific fields */ [key: string]: any; } // ============================================================================ // Event Attachment System // ============================================================================ /** * Base class for event attachments * Extend this class to create custom event handlers */ export abstract class WildduckEventAttachment { /** * Unique name for this attachment */ name: string; /** * Optional configuration */ options: Record; /** * Create a new attachment * @param name - Unique name for this attachment * @param options - Optional configuration */ constructor(name: string, options?: Record); /** * Handle an event (must be implemented by subclass) * @param event - Normalized event object */ abstract onEvent(event: WildduckEvent): Promise; /** * Determine if this attachment should handle an event * Override to filter events by type, category, or other criteria * @param event - The event to check * @returns True if event should be handled */ shouldHandle(event: WildduckEvent): boolean | Promise; /** * Called when attachment is registered with event bus * Override to perform initialization */ onStart(): Promise; /** * Called when event bus is shutting down * Override to perform cleanup */ onStop(): Promise; } // ============================================================================ // Event Bus Interface // ============================================================================ /** * Event bus singleton for event distribution */ export interface WildduckEventBus { /** * Register an attachment to receive events * @param attachment - The attachment instance to register * @throws Error if attachment is invalid or name already registered */ registerAttachment(attachment: WildduckEventAttachment): void; /** * Unregister an attachment * @param name - The name of the attachment to unregister * @returns True if attachment was removed, false if not found */ unregisterAttachment(name: string): boolean; /** * Emit an event to all registered attachments * Events are emitted asynchronously using process.nextTick() * @param eventData - Raw event data from lib/events.js publish() */ emit(eventData: WildduckRawEventData): void; /** * Transform raw event data to normalized format * @param data - Raw event data from publish() * @returns Normalized event object or null if invalid */ transformEvent(data: WildduckRawEventData): WildduckEvent | null; /** * Get list of registered attachment names * @returns Array of attachment names */ getRegisteredAttachments(): string[]; /** * Get set of all event types that have been emitted * @returns Set of event type strings */ getEventTypes(): Set; /** * Gracefully shutdown all attachments */ shutdown(): Promise; } // ============================================================================ // Specific Event Data Structures // ============================================================================ /** * Message added event data */ export interface WildduckMessageAddedEventData { path?: string; subject?: string; from?: string; to?: string[]; messageId?: string; } /** * Message flags changed event data */ export interface WildduckMessageFlagsChangedEventData { flags?: string[]; added?: string[]; removed?: string[]; } /** * Message moved event data */ export interface WildduckMessageMovedEventData { sourceMailbox?: string; targetMailbox?: string; path?: string; } /** * Mailbox created event data */ export interface WildduckMailboxCreatedEventData { path?: string; specialUse?: string; } /** * Mailbox renamed event data */ export interface WildduckMailboxRenamedEventData { oldPath?: string; newPath?: string; } /** * Address created event data */ export interface WildduckAddressCreatedEventData { address?: string; name?: string; } /** * Filter created event data */ export interface WildduckFilterCreatedEventData { name?: string; } /** * Settings updated event data */ export interface WildduckSettingsUpdatedEventData { key?: string; value?: any; } // ============================================================================ // Built-in Attachments // ============================================================================ /** * Test tracker attachment for testing * Auto-registers in NODE_ENV=test */ export interface WildduckTestTracker extends WildduckEventAttachment { /** * Get all tracked events */ getEvents(): WildduckEvent[]; /** * Get events by type */ getEventsByType(type: WildduckEventType): WildduckEvent[]; /** * Get events by category */ getEventsByCategory(category: WildduckEventCategory): WildduckEvent[]; /** * Get events by source */ getEventsBySource(source: WildduckEventSource): WildduckEvent[]; /** * Get events by user ID */ getEventsByUser(userId: string): WildduckEvent[]; /** * Get events matching a condition */ getEventsWhere(predicate: (event: WildduckEvent) => boolean): WildduckEvent[]; /** * Assert that an event was emitted */ assertEventEmitted(type: WildduckEventType): void; /** * Wait for a specific event to be emitted */ waitForEvent(type: WildduckEventType, timeout?: number): Promise; /** * Clear all tracked events */ clear(): void; /** * Get event count */ getEventCount(): number; } /** * Debug logger attachment * Auto-registers when DEBUG_EVENTS=1 */ export interface WildduckDebugLogger extends WildduckEventAttachment { /** * Log event to console */ log(event: WildduckEvent): void; } // ============================================================================ // Event Publishing // ============================================================================ /** * Publish an event to the event bus and webhook queue * @param redis - Redis client for webhook queue * @param data - Event data to publish */ export function WildduckPublish(redis: any, data: WildduckRawEventData): Promise; /** * Event type constants for importing */ export const WildduckEventTypes: { MESSAGE_ADDED: 'message.added'; MESSAGE_FLAGS_CHANGED: 'message.flags.changed'; MESSAGE_MOVED: 'message.moved'; MESSAGE_DELETED: 'message.deleted'; ADDRESS_USER_CREATED: 'address.user.created'; ADDRESS_USER_DELETED: 'address.user.deleted'; MAILBOX_CREATED: 'mailbox.created'; MAILBOX_RENAMED: 'mailbox.renamed'; MAILBOX_DELETED: 'mailbox.deleted'; SETTINGS_UPDATED: 'settings.updated'; AUTOREPLY_USER_ENABLED: 'autoreply.user.enabled'; AUTOREPLY_USER_DISABLED: 'autoreply.user.disabled'; FILTER_CREATED: 'filter.created'; FILTER_DELETED: 'filter.deleted'; MARKED_SPAM: 'marked.spam'; MARKED_HAM: 'marked.ham'; CATEGORY: { ADDRESS: 'address'; MAILBOX: 'mailbox'; MESSAGE: 'message'; SETTINGS: 'settings'; FILTER: 'filter'; SPAM: 'spam'; AUTOREPLY: 'autoreply'; USER: 'user'; ASP: 'asp'; MFA: 'mfa'; DKIM: 'dkim'; CERT: 'cert'; DOMAINALIAS: 'domainalias'; }; ACTION: { CREATED: 'created'; UPDATED: 'updated'; DELETED: 'deleted'; MOVED: 'moved'; CHANGED: 'changed'; ADDED: 'added'; ENABLED: 'enabled'; DISABLED: 'disabled'; RENAMED: 'renamed'; MARKED: 'marked'; }; SOURCE: { API: 'api'; IMAP: 'imap'; IMAP_APPEND: 'imap-append'; IMAP_COPY: 'imap-copy'; IMAP_MOVE: 'imap-move'; LMTP: 'lmtp'; POP3: 'pop3'; SMTP: 'smtp'; TASK: 'task'; WEBHOOK: 'webhook'; UNKNOWN: 'unknown'; }; }; }