/** * Console Message Service * * Singleton service for displaying user-facing console messages to stdout. * Messages are loaded from JSON files and rendered using Mustache templates. * * Features: * - Singleton pattern for global access * - Configurable prefix (default: [SeaLights]) * - Quiet mode support (--quiet flag or SL_QUIET env var) * - Lazy loading of message files * - No third-party dependencies * * @example * // Initialize once at startup * ConsoleMessageService.initialize({ * prefix: '[SeaLights]', * quiet: false, * messagesPath: './messages' * }); * * // Use anywhere in the application * const cms = ConsoleMessageService.getInstance(); * cms.print('config-new-build', { * appName: 'MyApp', * branchName: 'main', * buildName: '1.0.0' * }); */ import { TemplateData } from './utils/simple-mustache'; import { MessageKey, MessageParamsMap } from '../messages/message-keys.generated'; import { Logger } from './contracts'; /** * Conditional args type for print methods. * Returns empty tuple if message has no params, otherwise requires the params object. */ type PrintArgs = MessageParamsMap[K] extends Record ? [] : [data: MessageParamsMap[K]]; /** * Configuration options for Console Message Service */ export interface IConsoleMessageConfig { /** Prefix to prepend to all messages (default: '[SeaLights]') */ prefix?: string; /** Whether to suppress all console output (default: false) */ quiet?: boolean; /** Path to messages directory (default: computed from __dirname) */ messagesPath?: string; /** Throttle window in milliseconds to prevent duplicate messages (default: 1000) */ throttleMs?: number; /** Optional logger for INFO-level logging of messages (for file/remote logging) */ logger?: Logger; } /** * Internal configuration with required fields (except logger which remains optional) */ type InternalConfig = Required> & { logger?: Logger; }; /** * Console Message Service - Singleton */ export declare class ConsoleMessageService { private static instance; private config; private messages; private loadedFiles; private messageTimestamps; private stateTrackers; /** Maximum number of throttle entries before cleanup (prevents memory leak) */ private static readonly MAX_THROTTLE_ENTRIES; /** * Private constructor (singleton pattern) */ private constructor(); /** * Initialize the console message service (call once at startup) * If already initialized, this is a no-op. Use reset() in tests to re-initialize. * * @param config - Configuration options */ static initialize(config?: IConsoleMessageConfig): void; /** * Get the singleton instance * * @returns Console message service instance */ static getInstance(): ConsoleMessageService; /** * Reset the service (for testing) */ static reset(): void; /** * Print a message to stdout * * @param messageKey - Message key (type-safe enum value) * @param data - Data to substitute in template (type-safe based on message key) */ print(messageKey: K, ...args: PrintArgs): void; /** * Print an arbitrary string to stdout * * @param message - Message string to print */ printString(message: string): void; /** * Print a message only when the state changes. * This is useful for messages that should only be shown once when a state * transitions (e.g., from no active test stage to active test stage). * * @param stateKey - Unique key to track this state (e.g., 'test-stage-status') * @param currentState - Current state value (e.g., 'active' or 'inactive') * @param messageKey - Message key to print (type-safe enum value) * @param data - Data to substitute in template (type-safe based on message key) * @returns True if the message was printed (state changed), false otherwise */ printOnStateChange(stateKey: string, currentState: string, messageKey: K, ...args: PrintArgs): boolean; /** * Get configuration */ getConfig(): Readonly; /** * Check if a message should be throttled * * @param messageKey - Message key * @param data - Data to substitute * @returns True if message should be throttled (skipped) */ private isThrottled; /** * Record timestamp for a message * * @param messageKey - Message key * @param data - Data to substitute */ private recordMessageTimestamp; /** * Remove timestamps older than throttle window (cleanup for memory management) */ private cleanupOldTimestamps; /** * Create a hash from message key and data * * @param messageKey - Message key * @param data - Data to substitute * @returns Hash string */ private createMessageHash; /** * Render a message template with data (without printing) * * @param messageKey - Message key * @param data - Data to substitute * @returns Array of rendered lines */ private renderMessage; /** * Render a message template with data as a single string (without printing) * Convenience method that joins multiple lines with newlines. * * @param messageKey - Message key * @param data - Data to substitute * @returns Rendered message as a single string */ renderMessageAsString(messageKey: MessageKey, data?: TemplateData): string; /** * Ensure messages are loaded (lazy loading) */ private ensureMessagesLoaded; /** * Load all message files from the messages directory */ private loadAllMessages; /** * Load messages from a specific directory * * @param dirPath - Directory path */ private loadMessagesFromDirectory; /** * Load messages from a JSON file * * @param filePath - Path to JSON file */ private loadMessageFile; /** * Output lines to stdout with prefix * * @param lines - Lines to output */ private outputLines; /** * Log warning to stderr (respects quiet mode) * * @param message - Warning message */ private logWarning; /** * Log error to stderr (respects quiet mode) * * @param message - Error message */ private logError; /** * Check if NODE_DEBUG includes 'sl' or 'sl-console' (console logging enabled) * * @returns True if NODE_DEBUG includes 'sl' or 'sl-console' */ private isNodeDebugConsoleEnabled; /** * Check if NODE_DEBUG includes 'sl-file' (file logging enabled) * * @returns True if NODE_DEBUG includes 'sl-file' */ private isNodeDebugFileEnabled; /** * Log a message at INFO level if appropriate. * Logs when: * - quiet=true (console output is suppressed, need to log for visibility) * - quiet=false AND NODE_DEBUG doesn't include 'sl' (no console duplication) * - quiet=false AND NODE_DEBUG includes both 'sl' and 'sl-file' (user wants file logging) * * Skips logging when: * - quiet=false AND NODE_DEBUG=sl only (would duplicate console output, no file logging requested) * * @param lines - Lines to log */ private logInfoIfAppropriate; } export {};