UNPKG

@k-msg/core

Version:

Core types and interfaces for K-Message platform

1 lines 59.5 kB
{"version":3,"sources":["../src/index.ts","../src/errors.ts","../src/test-utils.ts","../src/retry.ts"],"sourcesContent":["/**\n * Core AlimTalk Platform types and interfaces\n */\n\n// Export error handling system\nexport * from './errors';\n\n// Export test utilities\nexport * from './test-utils';\n\n// Export retry and error recovery\nexport * from './retry';\n\nexport interface ProviderHealthStatus {\n healthy: boolean;\n issues: string[];\n data?: {\n balance?: string;\n status?: string;\n code?: number;\n message?: string;\n };\n}\n\nexport interface PlatformHealthStatus {\n healthy: boolean;\n providers: Record<string, boolean>;\n issues: string[];\n}\n\nexport interface PlatformInfo {\n version: string;\n providers: string[];\n features: string[];\n}\n\nexport interface MessageSendOptions {\n templateId: string;\n recipients: { phoneNumber: string; variables?: Record<string, any> }[];\n variables: Record<string, any>;\n}\n\nexport interface MessageSendResult {\n results: Array<{\n messageId?: string;\n status: string;\n phoneNumber: string;\n error?: { message: string };\n }>;\n summary: {\n total: number;\n sent: number;\n failed: number;\n };\n}\n\nexport interface BaseProvider {\n id: string;\n name: string;\n healthCheck(): Promise<ProviderHealthStatus>;\n sendMessage?(templateCode: string, phoneNumber: string, variables: Record<string, any>, options?: any): Promise<any>;\n getTemplates?(page: number, size: number, filters?: any): Promise<any>;\n createTemplate?(name: string, content: string, category?: string, buttons?: any[]): Promise<any>;\n modifyTemplate?(templateCode: string, name: string, content: string, buttons?: any[]): Promise<any>;\n deleteTemplate?(templateCode: string): Promise<any>;\n getHistory?(page: number, size: number, filters?: any): Promise<any>;\n cancelReservation?(messageId: string): Promise<any>;\n}\n\n/**\n * Core AlimTalk Platform interface\n */\nexport interface IAlimTalkPlatform {\n // Basic information\n getInfo(): PlatformInfo;\n \n // Provider management\n registerProvider(provider: BaseProvider): void;\n getProvider(providerId: string): BaseProvider | null;\n listProviders(): string[];\n \n // Health monitoring\n healthCheck(): Promise<PlatformHealthStatus>;\n \n // Messaging interface\n messages: {\n send(options: MessageSendOptions): Promise<MessageSendResult>;\n getStatus(messageId: string): Promise<string>;\n };\n}\n\n/**\n * Configuration interface\n */\nexport interface PlatformConfig {\n providers: string[];\n defaultProvider: string;\n features: {\n enableBulkSending?: boolean;\n enableScheduling?: boolean;\n enableAnalytics?: boolean;\n };\n}\n\n/**\n * Template categories\n */\nexport enum TemplateCategory {\n AUTHENTICATION = 'AUTHENTICATION',\n NOTIFICATION = 'NOTIFICATION',\n PROMOTION = 'PROMOTION',\n INFORMATION = 'INFORMATION',\n RESERVATION = 'RESERVATION',\n SHIPPING = 'SHIPPING',\n PAYMENT = 'PAYMENT'\n}","/**\n * Standardized error handling system for K-Message Platform\n */\n\nexport enum KMessageErrorCode {\n // General errors\n UNKNOWN_ERROR = 'UNKNOWN_ERROR',\n VALIDATION_ERROR = 'VALIDATION_ERROR',\n CONFIGURATION_ERROR = 'CONFIGURATION_ERROR',\n \n // Provider errors\n PROVIDER_NOT_FOUND = 'PROVIDER_NOT_FOUND',\n PROVIDER_NOT_AVAILABLE = 'PROVIDER_NOT_AVAILABLE',\n PROVIDER_AUTHENTICATION_FAILED = 'PROVIDER_AUTHENTICATION_FAILED',\n PROVIDER_CONNECTION_FAILED = 'PROVIDER_CONNECTION_FAILED',\n PROVIDER_RATE_LIMITED = 'PROVIDER_RATE_LIMITED',\n PROVIDER_INSUFFICIENT_BALANCE = 'PROVIDER_INSUFFICIENT_BALANCE',\n \n // Template errors\n TEMPLATE_NOT_FOUND = 'TEMPLATE_NOT_FOUND',\n TEMPLATE_VALIDATION_FAILED = 'TEMPLATE_VALIDATION_FAILED',\n TEMPLATE_CREATION_FAILED = 'TEMPLATE_CREATION_FAILED',\n TEMPLATE_MODIFICATION_FAILED = 'TEMPLATE_MODIFICATION_FAILED',\n TEMPLATE_DELETION_FAILED = 'TEMPLATE_DELETION_FAILED',\n \n // Message errors\n MESSAGE_SEND_FAILED = 'MESSAGE_SEND_FAILED',\n MESSAGE_INVALID_PHONE_NUMBER = 'MESSAGE_INVALID_PHONE_NUMBER',\n MESSAGE_INVALID_VARIABLES = 'MESSAGE_INVALID_VARIABLES',\n MESSAGE_QUOTA_EXCEEDED = 'MESSAGE_QUOTA_EXCEEDED',\n MESSAGE_RESERVATION_FAILED = 'MESSAGE_RESERVATION_FAILED',\n MESSAGE_CANCELLATION_FAILED = 'MESSAGE_CANCELLATION_FAILED',\n \n // Network errors\n NETWORK_TIMEOUT = 'NETWORK_TIMEOUT',\n NETWORK_CONNECTION_FAILED = 'NETWORK_CONNECTION_FAILED',\n NETWORK_SERVICE_UNAVAILABLE = 'NETWORK_SERVICE_UNAVAILABLE',\n \n // API errors\n API_INVALID_REQUEST = 'API_INVALID_REQUEST',\n API_UNAUTHORIZED = 'API_UNAUTHORIZED',\n API_FORBIDDEN = 'API_FORBIDDEN',\n API_NOT_FOUND = 'API_NOT_FOUND',\n API_TOO_MANY_REQUESTS = 'API_TOO_MANY_REQUESTS',\n API_INTERNAL_SERVER_ERROR = 'API_INTERNAL_SERVER_ERROR'\n}\n\nexport interface KMessageErrorContext {\n providerId?: string;\n templateCode?: string;\n phoneNumber?: string;\n messageId?: string;\n requestId?: string;\n timestamp?: Date;\n [key: string]: any;\n}\n\nexport class KMessageError extends Error {\n public readonly code: KMessageErrorCode;\n public readonly context: KMessageErrorContext;\n public readonly retryable: boolean;\n public readonly statusCode?: number;\n public readonly cause?: Error;\n\n constructor(\n code: KMessageErrorCode,\n message: string,\n context: KMessageErrorContext = {},\n options: {\n retryable?: boolean;\n statusCode?: number;\n cause?: Error;\n } = {}\n ) {\n super(message);\n this.name = 'KMessageError';\n this.code = code;\n this.context = {\n ...context,\n timestamp: context.timestamp || new Date()\n };\n this.retryable = options.retryable ?? this.isRetryableByDefault(code);\n this.statusCode = options.statusCode;\n this.cause = options.cause;\n\n // Maintains proper stack trace for where our error was thrown\n if (Error.captureStackTrace) {\n Error.captureStackTrace(this, KMessageError);\n }\n }\n\n private isRetryableByDefault(code: KMessageErrorCode): boolean {\n const retryableCodes = [\n KMessageErrorCode.NETWORK_TIMEOUT,\n KMessageErrorCode.NETWORK_CONNECTION_FAILED,\n KMessageErrorCode.NETWORK_SERVICE_UNAVAILABLE,\n KMessageErrorCode.PROVIDER_CONNECTION_FAILED,\n KMessageErrorCode.PROVIDER_RATE_LIMITED,\n KMessageErrorCode.API_TOO_MANY_REQUESTS,\n KMessageErrorCode.API_INTERNAL_SERVER_ERROR\n ];\n return retryableCodes.includes(code);\n }\n\n toJSON() {\n return {\n name: this.name,\n code: this.code,\n message: this.message,\n context: this.context,\n retryable: this.retryable,\n statusCode: this.statusCode,\n stack: this.stack\n };\n }\n}\n\nexport class ProviderError extends KMessageError {\n constructor(\n providerId: string,\n code: KMessageErrorCode,\n message: string,\n context: Omit<KMessageErrorContext, 'providerId'> = {},\n options: {\n retryable?: boolean;\n statusCode?: number;\n cause?: Error;\n } = {}\n ) {\n super(code, message, { ...context, providerId }, options);\n this.name = 'ProviderError';\n }\n}\n\nexport class TemplateError extends KMessageError {\n constructor(\n templateCode: string,\n code: KMessageErrorCode,\n message: string,\n context: Omit<KMessageErrorContext, 'templateCode'> = {},\n options: {\n retryable?: boolean;\n statusCode?: number;\n cause?: Error;\n } = {}\n ) {\n super(code, message, { ...context, templateCode }, options);\n this.name = 'TemplateError';\n }\n}\n\nexport class MessageError extends KMessageError {\n constructor(\n phoneNumber: string,\n code: KMessageErrorCode,\n message: string,\n context: Omit<KMessageErrorContext, 'phoneNumber'> = {},\n options: {\n retryable?: boolean;\n statusCode?: number;\n cause?: Error;\n } = {}\n ) {\n super(code, message, { ...context, phoneNumber }, options);\n this.name = 'MessageError';\n }\n}\n\n/**\n * Error factory functions for common error scenarios\n */\nexport const ErrorFactory = {\n providerNotFound: (providerId: string) =>\n new ProviderError(\n providerId,\n KMessageErrorCode.PROVIDER_NOT_FOUND,\n `Provider '${providerId}' not found`,\n {},\n { retryable: false, statusCode: 404 }\n ),\n\n providerNotAvailable: (providerId: string, reason?: string) =>\n new ProviderError(\n providerId,\n KMessageErrorCode.PROVIDER_NOT_AVAILABLE,\n `Provider '${providerId}' is not available${reason ? `: ${reason}` : ''}`,\n {},\n { retryable: true, statusCode: 503 }\n ),\n\n authenticationFailed: (providerId: string, details?: string) =>\n new ProviderError(\n providerId,\n KMessageErrorCode.PROVIDER_AUTHENTICATION_FAILED,\n `Authentication failed for provider '${providerId}'${details ? `: ${details}` : ''}`,\n {},\n { retryable: false, statusCode: 401 }\n ),\n\n templateNotFound: (templateCode: string) =>\n new TemplateError(\n templateCode,\n KMessageErrorCode.TEMPLATE_NOT_FOUND,\n `Template '${templateCode}' not found`,\n {},\n { retryable: false, statusCode: 404 }\n ),\n\n invalidPhoneNumber: (phoneNumber: string) =>\n new MessageError(\n phoneNumber,\n KMessageErrorCode.MESSAGE_INVALID_PHONE_NUMBER,\n `Invalid phone number format: ${phoneNumber}`,\n {},\n { retryable: false, statusCode: 400 }\n ),\n\n networkTimeout: (providerId?: string, timeout?: number) =>\n new KMessageError(\n KMessageErrorCode.NETWORK_TIMEOUT,\n `Network request timed out${timeout ? ` after ${timeout}ms` : ''}`,\n { providerId },\n { retryable: true, statusCode: 408 }\n ),\n\n rateLimited: (providerId: string, retryAfter?: number) =>\n new ProviderError(\n providerId,\n KMessageErrorCode.PROVIDER_RATE_LIMITED,\n `Rate limit exceeded for provider '${providerId}'${retryAfter ? `. Retry after ${retryAfter}s` : ''}`,\n { retryAfter },\n { retryable: true, statusCode: 429 }\n ),\n\n insufficientBalance: (providerId: string, balance?: string) =>\n new ProviderError(\n providerId,\n KMessageErrorCode.PROVIDER_INSUFFICIENT_BALANCE,\n `Insufficient balance for provider '${providerId}'${balance ? `. Current balance: ${balance}` : ''}`,\n { balance },\n { retryable: false, statusCode: 402 }\n ),\n\n fromHttpStatus: (statusCode: number, message: string, context: KMessageErrorContext = {}) => {\n let code: KMessageErrorCode;\n let retryable = false;\n\n switch (statusCode) {\n case 400:\n code = KMessageErrorCode.API_INVALID_REQUEST;\n break;\n case 401:\n code = KMessageErrorCode.API_UNAUTHORIZED;\n break;\n case 403:\n code = KMessageErrorCode.API_FORBIDDEN;\n break;\n case 404:\n code = KMessageErrorCode.API_NOT_FOUND;\n break;\n case 408:\n code = KMessageErrorCode.NETWORK_TIMEOUT;\n retryable = true;\n break;\n case 429:\n code = KMessageErrorCode.API_TOO_MANY_REQUESTS;\n retryable = true;\n break;\n case 500:\n case 502:\n case 503:\n case 504:\n code = KMessageErrorCode.API_INTERNAL_SERVER_ERROR;\n retryable = true;\n break;\n default:\n code = KMessageErrorCode.UNKNOWN_ERROR;\n retryable = statusCode >= 500 && statusCode < 600;\n }\n\n return new KMessageError(code, message, context, { retryable, statusCode });\n }\n};\n\n/**\n * Result wrapper for operations that can fail\n */\nexport type Result<T, E = KMessageError> = \n | { success: true; data: T }\n | { success: false; error: E };\n\nexport const Result = {\n success: <T>(data: T): Result<T> => ({ success: true, data }),\n failure: <E = KMessageError>(error: E): Result<never, E> => ({ success: false, error }),\n \n fromPromise: async <T>(promise: Promise<T>): Promise<Result<T>> => {\n try {\n const data = await promise;\n return Result.success(data);\n } catch (error) {\n if (error instanceof KMessageError) {\n return Result.failure(error);\n }\n return Result.failure(new KMessageError(\n KMessageErrorCode.UNKNOWN_ERROR,\n error instanceof Error ? error.message : 'Unknown error occurred',\n {},\n { cause: error instanceof Error ? error : undefined }\n ));\n }\n }\n};\n\n/**\n * Error handling utilities\n */\nexport const ErrorUtils = {\n isRetryable: (error: Error): boolean => {\n if (error instanceof KMessageError) {\n return error.retryable;\n }\n // For non-KMessageError, consider network-related errors as retryable\n const retryableMessages = ['timeout', 'ECONNRESET', 'ECONNREFUSED', 'ETIMEDOUT'];\n return retryableMessages.some(msg => \n error.message.toLowerCase().includes(msg.toLowerCase())\n );\n },\n\n getStatusCode: (error: Error): number => {\n if (error instanceof KMessageError && error.statusCode) {\n return error.statusCode;\n }\n return 500; // Default to internal server error\n },\n\n formatErrorForClient: (error: Error) => {\n if (error instanceof KMessageError) {\n return {\n code: error.code,\n message: error.message,\n retryable: error.retryable,\n context: error.context\n };\n }\n \n return {\n code: KMessageErrorCode.UNKNOWN_ERROR,\n message: error.message || 'Unknown error occurred',\n retryable: false,\n context: {}\n };\n },\n\n formatErrorForLogging: (error: Error) => {\n const baseInfo = {\n name: error.name,\n message: error.message,\n stack: error.stack\n };\n\n if (error instanceof KMessageError) {\n return {\n ...baseInfo,\n code: error.code,\n context: error.context,\n retryable: error.retryable,\n statusCode: error.statusCode\n };\n }\n\n return baseInfo;\n }\n};","/**\n * Test utilities for K-Message Platform\n */\n\nimport { expect } from 'bun:test';\nimport type { BaseProvider, ProviderHealthStatus, PlatformHealthStatus } from './index';\nimport { KMessageError, KMessageErrorCode, ProviderError, TemplateError, MessageError } from './errors';\n\n/**\n * Mock provider for testing\n */\nexport class MockProvider implements BaseProvider {\n public readonly id: string;\n public readonly name: string;\n private _healthy: boolean = true;\n private _issues: string[] = [];\n private _balance: string = '1000';\n private _templates: any[] = [];\n private _history: any[] = [];\n\n constructor(id: string = 'mock', name: string = 'Mock Provider') {\n this.id = id;\n this.name = name;\n }\n\n // Health check simulation\n async healthCheck(): Promise<ProviderHealthStatus> {\n return {\n healthy: this._healthy,\n issues: [...this._issues],\n data: {\n balance: this._balance,\n status: this._healthy ? 'connected' : 'disconnected',\n code: this._healthy ? 200 : 500,\n message: this._healthy ? 'OK' : 'Service unavailable'\n }\n };\n }\n\n // Test helpers\n setHealthy(healthy: boolean, issues: string[] = []) {\n this._healthy = healthy;\n this._issues = issues;\n }\n\n setBalance(balance: string) {\n this._balance = balance;\n }\n\n setTemplates(templates: any[]) {\n this._templates = templates;\n }\n\n setHistory(history: any[]) {\n this._history = history;\n }\n\n // Provider methods with mock implementations\n async sendMessage(templateCode: string, phoneNumber: string, variables: Record<string, any>, options?: any) {\n if (!this._healthy) {\n throw new MessageError(phoneNumber, KMessageErrorCode.MESSAGE_SEND_FAILED, 'Provider is unhealthy');\n }\n \n if (phoneNumber === '01000000000') {\n throw new MessageError(phoneNumber, KMessageErrorCode.MESSAGE_INVALID_PHONE_NUMBER, 'Invalid phone number');\n }\n\n return {\n success: true,\n messageId: `mock_${Date.now()}`,\n status: 'sent',\n error: null\n };\n }\n\n async getTemplates(page: number = 1, size: number = 15, filters?: any) {\n if (!this._healthy) {\n throw new ProviderError(this.id, KMessageErrorCode.PROVIDER_NOT_AVAILABLE, 'Provider is unhealthy');\n }\n\n const start = (page - 1) * size;\n const end = start + size;\n const list = this._templates.slice(start, end);\n\n return {\n code: 200,\n message: 'Success',\n totalCount: this._templates.length,\n list\n };\n }\n\n async createTemplate(name: string, content: string, category?: string, buttons?: any[]) {\n if (!this._healthy) {\n throw new TemplateError(name, KMessageErrorCode.TEMPLATE_CREATION_FAILED, 'Provider is unhealthy');\n }\n\n if (name === 'invalid_template') {\n throw new TemplateError(name, KMessageErrorCode.TEMPLATE_VALIDATION_FAILED, 'Template validation failed');\n }\n\n const templateCode = `mock_${name}_${Date.now()}`;\n this._templates.push({\n templateCode,\n templateName: name,\n templateContent: content,\n status: 'Y',\n createDate: new Date().toISOString()\n });\n\n return {\n success: true,\n templateCode,\n status: 'created',\n error: null\n };\n }\n\n async modifyTemplate(templateCode: string, name: string, content: string, buttons?: any[]) {\n if (!this._healthy) {\n throw new TemplateError(templateCode, KMessageErrorCode.TEMPLATE_MODIFICATION_FAILED, 'Provider is unhealthy');\n }\n\n const template = this._templates.find(t => t.templateCode === templateCode);\n if (!template) {\n throw new TemplateError(templateCode, KMessageErrorCode.TEMPLATE_NOT_FOUND, 'Template not found');\n }\n\n template.templateName = name;\n template.templateContent = content;\n\n return {\n success: true,\n templateCode,\n status: 'modified',\n error: null\n };\n }\n\n async deleteTemplate(templateCode: string) {\n if (!this._healthy) {\n throw new TemplateError(templateCode, KMessageErrorCode.TEMPLATE_DELETION_FAILED, 'Provider is unhealthy');\n }\n\n const index = this._templates.findIndex(t => t.templateCode === templateCode);\n if (index === -1) {\n return {\n code: 404,\n message: 'Template not found'\n };\n }\n\n this._templates.splice(index, 1);\n return {\n code: 200,\n message: 'Template deleted successfully'\n };\n }\n\n async getHistory(page: number = 1, size: number = 15, filters?: any) {\n if (!this._healthy) {\n throw new ProviderError(this.id, KMessageErrorCode.PROVIDER_NOT_AVAILABLE, 'Provider is unhealthy');\n }\n\n const start = (page - 1) * size;\n const end = start + size;\n const list = this._history.slice(start, end);\n\n return {\n code: 200,\n message: 'Success',\n totalCount: this._history.length,\n list\n };\n }\n\n async cancelReservation(messageId: string) {\n if (!this._healthy) {\n throw new KMessageError(KMessageErrorCode.MESSAGE_CANCELLATION_FAILED, 'Provider is unhealthy');\n }\n\n return {\n code: 200,\n message: 'Reservation cancelled successfully'\n };\n }\n}\n\n/**\n * Test assertion helpers\n */\nexport const TestAssertions = {\n /**\n * Assert that an error is a KMessageError with specific code\n */\n assertKMessageError: (error: unknown, expectedCode: KMessageErrorCode, expectedMessage?: string) => {\n expect(error).toBeInstanceOf(KMessageError);\n const kError = error as KMessageError;\n expect(kError.code).toBe(expectedCode);\n if (expectedMessage) {\n expect(kError.message).toContain(expectedMessage);\n }\n },\n\n /**\n * Assert that an error is retryable\n */\n assertRetryable: (error: KMessageError, expected: boolean = true) => {\n expect(error.retryable).toBe(expected);\n },\n\n /**\n * Assert that a health status is healthy\n */\n assertHealthy: (health: ProviderHealthStatus | PlatformHealthStatus, expected: boolean = true) => {\n expect(health.healthy).toBe(expected);\n if (!expected) {\n expect(health.issues.length).toBeGreaterThan(0);\n }\n },\n\n /**\n * Assert that a provider result has expected structure\n */\n assertProviderResult: (result: any, expectSuccess: boolean = true) => {\n expect(result).toHaveProperty('success');\n expect(result.success).toBe(expectSuccess);\n \n if (expectSuccess) {\n expect(result.error).toBeNull();\n } else {\n expect(result.error).toBeDefined();\n }\n },\n\n /**\n * Assert that API response has expected structure\n */\n assertApiResponse: (response: any, expectedCode: number = 200) => {\n expect(response).toHaveProperty('code');\n expect(response).toHaveProperty('message');\n expect(response.code).toBe(expectedCode);\n }\n};\n\n/**\n * Test data generators\n */\nexport const TestData = {\n createMockTemplate: (overrides: Partial<any> = {}) => ({\n templateCode: 'mock_template_001',\n templateName: 'Mock Template',\n templateContent: '[#{서비스명}] 안녕하세요, #{고객명}님!',\n status: 'Y',\n createDate: '2024-01-01 12:00:00',\n ...overrides\n }),\n\n createMockMessage: (overrides: Partial<any> = {}) => ({\n seqNo: 12345,\n phone: '01012345678',\n templateCode: 'mock_template_001',\n statusCode: 'OK',\n statusCodeName: '성공',\n requestDate: '2024-01-01 12:00:00',\n sendDate: '2024-01-01 12:01:00',\n receiveDate: '2024-01-01 12:01:30',\n sendMessage: '[MyApp] 안녕하세요, 홍길동님!',\n ...overrides\n }),\n\n createMockVariables: (overrides: Record<string, any> = {}) => ({\n 서비스명: 'MyApp',\n 고객명: '홍길동',\n 인증코드: '123456',\n ...overrides\n }),\n\n generatePhoneNumber: (valid: boolean = true) => {\n if (valid) {\n const numbers = ['010', '011', '016', '017', '018', '019'];\n const prefix = numbers[Math.floor(Math.random() * numbers.length)];\n const suffix = Math.floor(Math.random() * 100000000).toString().padStart(8, '0');\n return prefix + suffix;\n } else {\n return '01000000000'; // Known invalid number for testing\n }\n }\n};\n\n/**\n * Test environment setup helpers\n */\nexport const TestSetup = {\n /**\n * Create a test environment with mock providers\n */\n createTestEnvironment: () => {\n const mockProviders = {\n healthy: new MockProvider('healthy', 'Healthy Provider'),\n unhealthy: new MockProvider('unhealthy', 'Unhealthy Provider'),\n rateLimited: new MockProvider('ratelimited', 'Rate Limited Provider')\n };\n\n // Configure unhealthy provider\n mockProviders.unhealthy.setHealthy(false, ['Connection failed', 'Authentication error']);\n \n // Configure rate limited provider\n mockProviders.rateLimited.setHealthy(true);\n\n return {\n providers: mockProviders,\n cleanup: () => {\n // Cleanup logic if needed\n }\n };\n },\n\n /**\n * Create test data for various scenarios\n */\n createTestScenarios: () => ({\n validMessage: {\n templateCode: 'valid_template',\n phoneNumber: TestData.generatePhoneNumber(true),\n variables: TestData.createMockVariables()\n },\n invalidMessage: {\n templateCode: 'invalid_template',\n phoneNumber: TestData.generatePhoneNumber(false),\n variables: {}\n },\n templates: [\n TestData.createMockTemplate({ templateCode: 'template_001', templateName: 'Welcome Message' }),\n TestData.createMockTemplate({ templateCode: 'template_002', templateName: 'OTP Message' }),\n TestData.createMockTemplate({ templateCode: 'template_003', templateName: 'Notification' })\n ],\n history: [\n TestData.createMockMessage({ seqNo: 1, templateCode: 'template_001' }),\n TestData.createMockMessage({ seqNo: 2, templateCode: 'template_002' }),\n TestData.createMockMessage({ seqNo: 3, templateCode: 'template_003' })\n ]\n })\n};\n\n/**\n * Performance testing helpers\n */\nexport const PerformanceTest = {\n /**\n * Measure execution time of a function\n */\n measureTime: async <T>(fn: () => Promise<T>): Promise<{ result: T; duration: number }> => {\n const start = performance.now();\n const result = await fn();\n const duration = performance.now() - start;\n return { result, duration };\n },\n\n /**\n * Run a function multiple times and get statistics\n */\n benchmark: async <T>(fn: () => Promise<T>, iterations: number = 10): Promise<{\n results: T[];\n statistics: {\n min: number;\n max: number;\n average: number;\n median: number;\n };\n }> => {\n const results: T[] = [];\n const durations: number[] = [];\n\n for (let i = 0; i < iterations; i++) {\n const { result, duration } = await PerformanceTest.measureTime(fn);\n results.push(result);\n durations.push(duration);\n }\n\n durations.sort((a, b) => a - b);\n const statistics = {\n min: durations[0],\n max: durations[durations.length - 1],\n average: durations.reduce((sum, d) => sum + d, 0) / durations.length,\n median: durations[Math.floor(durations.length / 2)]\n };\n\n return { results, statistics };\n }\n};","/**\n * Error recovery and retry patterns for K-Message Platform\n */\n\nimport { KMessageError, KMessageErrorCode, ErrorUtils } from './errors';\n\nexport interface RetryOptions {\n maxAttempts: number;\n initialDelay: number;\n maxDelay: number;\n backoffMultiplier: number;\n jitter: boolean;\n retryCondition?: (error: Error, attempt: number) => boolean;\n onRetry?: (error: Error, attempt: number) => void;\n}\n\nexport interface CircuitBreakerOptions {\n failureThreshold: number;\n timeout: number;\n resetTimeout: number;\n onOpen?: () => void;\n onHalfOpen?: () => void;\n onClose?: () => void;\n}\n\nexport interface BulkOperationOptions {\n concurrency: number;\n retryOptions: RetryOptions;\n failFast: boolean;\n onProgress?: (completed: number, total: number, failed: number) => void;\n}\n\n/**\n * Exponential backoff retry mechanism\n */\nexport class RetryHandler {\n private static defaultOptions: RetryOptions = {\n maxAttempts: 3,\n initialDelay: 1000,\n maxDelay: 30000,\n backoffMultiplier: 2,\n jitter: true,\n retryCondition: (error) => ErrorUtils.isRetryable(error)\n };\n\n static async execute<T>(\n operation: () => Promise<T>,\n options: Partial<RetryOptions> = {}\n ): Promise<T> {\n const opts = { ...this.defaultOptions, ...options };\n let lastError: Error;\n let delay = opts.initialDelay;\n\n for (let attempt = 1; attempt <= opts.maxAttempts; attempt++) {\n try {\n return await operation();\n } catch (error) {\n lastError = error as Error;\n\n // Check if we should retry\n if (attempt === opts.maxAttempts || !opts.retryCondition!(lastError, attempt)) {\n throw lastError;\n }\n\n // Calculate delay with optional jitter\n const actualDelay = opts.jitter\n ? delay + Math.random() * delay * 0.1\n : delay;\n\n // Call retry callback if provided\n opts.onRetry?.(lastError, attempt);\n\n // Wait before retrying\n await new Promise(resolve => setTimeout(resolve, actualDelay));\n\n // Increase delay for next attempt\n delay = Math.min(delay * opts.backoffMultiplier, opts.maxDelay);\n }\n }\n\n throw lastError!;\n }\n\n static createRetryableFunction<T extends any[], R>(\n func: (...args: T) => Promise<R>,\n options: Partial<RetryOptions> = {}\n ): (...args: T) => Promise<R> {\n return async (...args: T) => {\n return this.execute(() => func(...args), options);\n };\n }\n}\n\n/**\n * Circuit breaker pattern implementation\n */\nexport class CircuitBreaker {\n private state: 'CLOSED' | 'OPEN' | 'HALF_OPEN' = 'CLOSED';\n private failureCount = 0;\n private lastFailureTime = 0;\n private nextAttemptTime = 0;\n\n constructor(private options: CircuitBreakerOptions) {}\n\n async execute<T>(operation: () => Promise<T>): Promise<T> {\n const now = Date.now();\n\n switch (this.state) {\n case 'OPEN':\n if (now < this.nextAttemptTime) {\n throw new KMessageError(\n KMessageErrorCode.NETWORK_SERVICE_UNAVAILABLE,\n 'Circuit breaker is OPEN',\n { state: this.state, nextAttemptTime: this.nextAttemptTime }\n );\n }\n this.state = 'HALF_OPEN';\n this.options.onHalfOpen?.();\n break;\n\n case 'HALF_OPEN':\n // Allow one request to test if service is back\n break;\n\n case 'CLOSED':\n // Normal operation\n break;\n }\n\n try {\n const result = await Promise.race([\n operation(),\n new Promise<never>((_, reject) =>\n setTimeout(() => reject(\n new KMessageError(\n KMessageErrorCode.NETWORK_TIMEOUT,\n 'Circuit breaker timeout',\n { timeout: this.options.timeout }\n )\n ), this.options.timeout)\n )\n ]);\n\n // Success - close circuit if it was half-open\n if (this.state === 'HALF_OPEN') {\n this.state = 'CLOSED';\n this.failureCount = 0;\n this.options.onClose?.();\n }\n\n return result;\n } catch (error) {\n this.recordFailure();\n throw error;\n }\n }\n\n private recordFailure(): void {\n this.failureCount++;\n this.lastFailureTime = Date.now();\n\n if (this.failureCount >= this.options.failureThreshold) {\n this.state = 'OPEN';\n this.nextAttemptTime = this.lastFailureTime + this.options.resetTimeout;\n this.options.onOpen?.();\n }\n }\n\n getState(): string {\n return this.state;\n }\n\n getFailureCount(): number {\n return this.failureCount;\n }\n\n reset(): void {\n this.state = 'CLOSED';\n this.failureCount = 0;\n this.lastFailureTime = 0;\n this.nextAttemptTime = 0;\n }\n}\n\n/**\n * Bulk operation handler with error recovery\n */\nexport class BulkOperationHandler {\n static async execute<T, R>(\n items: T[],\n operation: (item: T) => Promise<R>,\n options: Partial<BulkOperationOptions> = {}\n ): Promise<{\n successful: { item: T; result: R }[];\n failed: { item: T; error: Error }[];\n summary: {\n total: number;\n successful: number;\n failed: number;\n duration: number;\n };\n }> {\n const opts: BulkOperationOptions = {\n concurrency: 5,\n retryOptions: {\n maxAttempts: 3,\n initialDelay: 1000,\n maxDelay: 10000,\n backoffMultiplier: 2,\n jitter: true\n },\n failFast: false,\n ...options\n };\n\n const startTime = Date.now();\n const successful: { item: T; result: R }[] = [];\n const failed: { item: T; error: Error }[] = [];\n let completed = 0;\n\n // Create operation with retry\n const retryableOperation = RetryHandler.createRetryableFunction(\n operation,\n opts.retryOptions\n );\n\n // Process items in batches with controlled concurrency\n const batches = this.createBatches(items, opts.concurrency);\n\n for (const batch of batches) {\n const batchPromises = batch.map(async (item) => {\n try {\n const result = await retryableOperation(item);\n successful.push({ item, result });\n } catch (error) {\n failed.push({ item, error: error as Error });\n\n // Fail fast if configured\n if (opts.failFast) {\n throw new KMessageError(\n KMessageErrorCode.MESSAGE_SEND_FAILED,\n `Bulk operation failed fast after ${failed.length} failures`,\n { totalItems: items.length, failedCount: failed.length }\n );\n }\n } finally {\n completed++;\n opts.onProgress?.(completed, items.length, failed.length);\n }\n });\n\n await Promise.allSettled(batchPromises);\n\n // Early termination if fail fast is enabled and we have failures\n if (opts.failFast && failed.length > 0) {\n break;\n }\n }\n\n const duration = Date.now() - startTime;\n\n return {\n successful,\n failed,\n summary: {\n total: items.length,\n successful: successful.length,\n failed: failed.length,\n duration\n }\n };\n }\n\n private static createBatches<T>(items: T[], batchSize: number): T[][] {\n const batches: T[][] = [];\n for (let i = 0; i < items.length; i += batchSize) {\n batches.push(items.slice(i, i + batchSize));\n }\n return batches;\n }\n}\n\n/**\n * Rate limiter for API calls\n */\nexport class RateLimiter {\n private requests: number[] = [];\n\n constructor(\n private maxRequests: number,\n private windowMs: number\n ) {}\n\n async acquire(): Promise<void> {\n const now = Date.now();\n \n // Remove old requests outside the window\n this.requests = this.requests.filter(time => now - time < this.windowMs);\n\n if (this.requests.length >= this.maxRequests) {\n const oldestRequest = Math.min(...this.requests);\n const waitTime = this.windowMs - (now - oldestRequest);\n \n if (waitTime > 0) {\n await new Promise(resolve => setTimeout(resolve, waitTime));\n return this.acquire(); // Retry after waiting\n }\n }\n\n this.requests.push(now);\n }\n\n canMakeRequest(): boolean {\n const now = Date.now();\n this.requests = this.requests.filter(time => now - time < this.windowMs);\n return this.requests.length < this.maxRequests;\n }\n\n getRemainingRequests(): number {\n const now = Date.now();\n this.requests = this.requests.filter(time => now - time < this.windowMs);\n return Math.max(0, this.maxRequests - this.requests.length);\n }\n}\n\n/**\n * Health check monitor with automatic recovery\n */\nexport class HealthMonitor {\n private healthStatus: Map<string, boolean> = new Map();\n private lastCheck: Map<string, number> = new Map();\n private checkInterval: number;\n private intervalId?: NodeJS.Timeout;\n\n constructor(\n private services: Map<string, () => Promise<boolean>>,\n checkIntervalMs: number = 30000\n ) {\n this.checkInterval = checkIntervalMs;\n }\n\n start(): void {\n this.intervalId = setInterval(() => {\n this.checkAllServices();\n }, this.checkInterval);\n\n // Initial check\n this.checkAllServices();\n }\n\n stop(): void {\n if (this.intervalId) {\n clearInterval(this.intervalId);\n this.intervalId = undefined;\n }\n }\n\n private async checkAllServices(): Promise<void> {\n const checks = Array.from(this.services.entries()).map(\n async ([serviceName, healthCheck]) => {\n try {\n const isHealthy = await healthCheck();\n const wasHealthy = this.healthStatus.get(serviceName);\n \n this.healthStatus.set(serviceName, isHealthy);\n this.lastCheck.set(serviceName, Date.now());\n\n // Log status changes\n if (wasHealthy !== undefined && wasHealthy !== isHealthy) {\n console.log(`Service ${serviceName} status changed: ${wasHealthy ? 'healthy' : 'unhealthy'} -> ${isHealthy ? 'healthy' : 'unhealthy'}`);\n }\n } catch (error) {\n this.healthStatus.set(serviceName, false);\n this.lastCheck.set(serviceName, Date.now());\n console.error(`Health check failed for ${serviceName}:`, error);\n }\n }\n );\n\n await Promise.allSettled(checks);\n }\n\n getServiceHealth(serviceName: string): boolean | undefined {\n return this.healthStatus.get(serviceName);\n }\n\n getAllHealth(): Record<string, boolean> {\n return Object.fromEntries(this.healthStatus);\n }\n\n isServiceHealthy(serviceName: string): boolean {\n return this.healthStatus.get(serviceName) === true;\n }\n\n getLastCheckTime(serviceName: string): number | undefined {\n return this.lastCheck.get(serviceName);\n }\n}\n\n/**\n * Graceful degradation handler\n */\nexport class GracefulDegradation {\n private fallbackStrategies: Map<string, () => Promise<any>> = new Map();\n\n registerFallback<T>(\n operationName: string,\n fallbackFunction: () => Promise<T>\n ): void {\n this.fallbackStrategies.set(operationName, fallbackFunction);\n }\n\n async executeWithFallback<T>(\n operationName: string,\n primaryOperation: () => Promise<T>,\n options: {\n timeout?: number;\n retryOptions?: Partial<RetryOptions>;\n } = {}\n ): Promise<T> {\n const timeout = options.timeout || 10000;\n \n try {\n // Try primary operation with timeout\n const result = await Promise.race([\n RetryHandler.execute(primaryOperation, options.retryOptions),\n new Promise<never>((_, reject) =>\n setTimeout(() => reject(\n new KMessageError(\n KMessageErrorCode.NETWORK_TIMEOUT,\n `Operation ${operationName} timed out`,\n { operationName, timeout }\n )\n ), timeout)\n )\n ]);\n\n return result;\n } catch (error) {\n console.warn(`Primary operation ${operationName} failed, attempting fallback:`, error);\n\n const fallback = this.fallbackStrategies.get(operationName);\n if (fallback) {\n try {\n return await fallback();\n } catch (fallbackError) {\n throw new KMessageError(\n KMessageErrorCode.UNKNOWN_ERROR,\n `Both primary and fallback operations failed for ${operationName}`,\n { \n operationName,\n primaryError: (error as Error).message,\n fallbackError: (fallbackError as Error).message\n },\n { cause: error as Error }\n );\n }\n }\n\n throw error;\n }\n }\n}\n\n/**\n * Error recovery utilities\n */\nexport const ErrorRecovery = {\n /**\n * Create a resilient function that combines multiple recovery patterns\n */\n createResilientFunction<T extends any[], R>(\n func: (...args: T) => Promise<R>,\n options: {\n retryOptions?: Partial<RetryOptions>;\n circuitBreaker?: CircuitBreakerOptions;\n rateLimiter?: { maxRequests: number; windowMs: number };\n timeout?: number;\n fallback?: (...args: T) => Promise<R>;\n } = {}\n ): (...args: T) => Promise<R> {\n let circuitBreaker: CircuitBreaker | undefined;\n let rateLimiter: RateLimiter | undefined;\n\n if (options.circuitBreaker) {\n circuitBreaker = new CircuitBreaker(options.circuitBreaker);\n }\n\n if (options.rateLimiter) {\n rateLimiter = new RateLimiter(\n options.rateLimiter.maxRequests,\n options.rateLimiter.windowMs\n );\n }\n\n return async (...args: T): Promise<R> => {\n // Apply rate limiting\n if (rateLimiter) {\n await rateLimiter.acquire();\n }\n\n const operation = async () => {\n const wrappedFunc = async () => {\n if (options.timeout) {\n return Promise.race([\n func(...args),\n new Promise<never>((_, reject) =>\n setTimeout(() => reject(\n new KMessageError(\n KMessageErrorCode.NETWORK_TIMEOUT,\n 'Operation timed out',\n { timeout: options.timeout }\n )\n ), options.timeout)\n )\n ]);\n }\n return func(...args);\n };\n\n if (circuitBreaker) {\n return circuitBreaker.execute(wrappedFunc);\n }\n return wrappedFunc();\n };\n\n try {\n return await RetryHandler.execute(operation, options.retryOptions);\n } catch (error) {\n if (options.fallback) {\n console.warn('Primary operation failed, using fallback:', error);\n return options.fallback(...args);\n }\n throw error;\n }\n };\n }\n};"],"mappings":"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