/** * Main SwMock Class */ import type { SwMockInstance, SwMockInitOptions, SwMockConfig, SwMockStatus, SwMockPlugin, CapturedRequest, NetworkFailureConfig, HttpErrorScenario, TimeoutScenario, ConnectionFailureScenario, ResponseDelayScenario, IntermittentFailureScenario, MockEndpointConfig } from '../types'; import { SwCommunication } from './communication'; import { DEFAULT_CONFIG, PRESET_CONFIGS, mergeConfigs, validateConfig } from './config'; import { EventEmitter } from '../utils/events'; export class SwMock extends EventEmitter implements SwMockInstance { private communication: SwCommunication; private plugins: SwMockPlugin[] = []; private isInitialized = false; constructor() { super(); this.communication = new SwCommunication(); } /** * Initialize SwMock system */ async init(options: SwMockInitOptions = {}): Promise { if (this.isInitialized) { console.warn('[SwMock] Already initialized'); return; } try { // Validate options const errors = validateConfig(options); if (errors.length > 0) { throw new Error(`Invalid configuration: ${errors.join(', ')}`); } // Initialize communication with custom worker URL and scope if provided if (options.workerUrl || options.scope) { this.communication = new SwCommunication( options.workerUrl || '/sw-mock-worker.js', options.scope || '/' ); } await this.communication.init(); // Apply initial configuration if (Object.keys(options).length > 0) { const currentConfig = await this.communication.getConfig(); const newConfig = mergeConfigs(currentConfig, options); await this.communication.updateConfig(newConfig); } // Auto-enable if specified if (options.enabled !== undefined) { if (options.enabled) { await this.enable(); } else { await this.disable(); } } this.isInitialized = true; this.emit('init', options); console.log('[SwMock] Initialized successfully'); } catch (error) { console.error('[SwMock] Initialization failed:', error); this.emit('error', error); throw error; } } /** * Enable mock functionality */ async enable(): Promise { await this.communication.enableMock(); this.emit('enable'); } /** * Disable mock functionality */ async disable(): Promise { await this.communication.disableMock(); this.emit('disable'); } /** * Toggle mock functionality */ async toggle(): Promise { const result = await this.communication.toggleMock(); this.emit(result ? 'enable' : 'disable'); return result; } /** * Get current status */ async getStatus(): Promise { const config = await this.communication.getConfig(); return { enabled: config.enabled, debug: config.debug, defaultDelay: config.defaultDelay, globalDelay: config.globalDelay, interceptPatterns: config.interceptPatterns, excludePatterns: config.excludePatterns, endpointsCount: Object.keys(config.endpoints).length, serviceWorkerReady: this.communication.isReady(), mockDataPath: config.mockDataPath, scope: this.communication.getScope(), networkFailure: { enabled: config.networkFailure?.enabled || false, httpErrors: config.networkFailure?.scenarios?.httpErrors?.enabled || false, timeout: config.networkFailure?.scenarios?.timeout?.enabled || false, connectionFailure: config.networkFailure?.scenarios?.connectionFailure?.enabled || false, responseDelay: config.networkFailure?.scenarios?.responseDelay?.enabled || false, intermittentFailure: config.networkFailure?.scenarios?.intermittentFailure?.enabled || false } }; } /** * Get current configuration */ async getConfig(): Promise { return await this.communication.getConfig(); } /** * Update configuration */ async updateConfig(config: Partial): Promise { const errors = validateConfig(config); if (errors.length > 0) { throw new Error(`Invalid configuration: ${errors.join(', ')}`); } await this.communication.updateConfig(config); this.emit('config-update', config); } /** * Reset configuration */ async resetConfig(): Promise { await this.communication.resetConfig(); this.emit('config-update', DEFAULT_CONFIG); } // Network Failure Methods async enableNetworkFailure(): Promise { await this.communication.enableNetworkFailure(); } async disableNetworkFailure(): Promise { await this.communication.disableNetworkFailure(); } async configureNetworkFailure(config: Partial): Promise { await this.communication.updateNetworkFailureConfig(config); } async resetNetworkFailureState(): Promise { await this.communication.resetNetworkFailureState(); } // Scenario Methods async enableHttpErrors(options: Partial = {}): Promise { const config = { scenarios: { httpErrors: { enabled: true, statusCodes: options.statusCodes || [400, 401, 403, 404, 500, 502, 503, 504], rate: options.rate || 0.1, distribution: options.distribution || 'random' } } } as Partial; await this.configureNetworkFailure(config); } async enableTimeouts(options: Partial = {}): Promise { const config = { scenarios: { timeout: { enabled: true, duration: options.duration || 30000, rate: options.rate || 0.05 } } } as Partial; await this.configureNetworkFailure(config); } async enableConnectionFailures(options: Partial = {}): Promise { const config = { scenarios: { connectionFailure: { enabled: true, rate: options.rate || 0.02 } } } as Partial; await this.configureNetworkFailure(config); } async enableResponseDelays(options: Partial = {}): Promise { const config = { scenarios: { responseDelay: { enabled: true, minDelay: options.minDelay || 1000, maxDelay: options.maxDelay || 5000, rate: options.rate || 0.3 } } } as Partial; await this.configureNetworkFailure(config); } async enableIntermittentFailures(options: Partial = {}): Promise { const config = { scenarios: { intermittentFailure: { enabled: true, failureDuration: options.failureDuration || 10000, recoveryDuration: options.recoveryDuration || 30000, rate: options.rate || 0.1 } } } as Partial; await this.configureNetworkFailure(config); } // Preset Modes async enableDevelopmentMode(): Promise { await this.enableNetworkFailure(); await this.configureNetworkFailure(PRESET_CONFIGS.development.networkFailure!); } async enableTestingMode(): Promise { await this.enableNetworkFailure(); await this.configureNetworkFailure(PRESET_CONFIGS.testing.networkFailure!); } async enableStressTestMode(): Promise { await this.enableNetworkFailure(); await this.configureNetworkFailure(PRESET_CONFIGS.stress.networkFailure!); } // Debug and Monitoring async enableDebug(): Promise { await this.updateConfig({ debug: true }); } async disableDebug(): Promise { await this.updateConfig({ debug: false }); } async startCapture(): Promise { await this.communication.startCapture(); } async stopCapture(): Promise { return await this.communication.stopCapture(); } exportCapturedRequests(): void { // This will be implemented in the utils console.warn('[SwMock] exportCapturedRequests not implemented yet'); } // Endpoint Management async addMockEndpoint(endpoint: string, config: MockEndpointConfig): Promise { const currentConfig = await this.getConfig(); const newEndpoints = { ...currentConfig.endpoints, [endpoint]: config }; await this.updateConfig({ endpoints: newEndpoints }); } async removeMockEndpoint(endpoint: string): Promise { const currentConfig = await this.getConfig(); const newEndpoints = { ...currentConfig.endpoints }; delete newEndpoints[endpoint]; await this.updateConfig({ endpoints: newEndpoints }); } async setGlobalDelay(delay: number): Promise { await this.updateConfig({ globalDelay: delay }); } // Pattern Management async addInterceptPattern(pattern: string): Promise { const currentConfig = await this.getConfig(); const patterns = [...currentConfig.interceptPatterns]; if (!patterns.includes(pattern)) { patterns.push(pattern); await this.updateConfig({ interceptPatterns: patterns }); } } async removeInterceptPattern(pattern: string): Promise { const currentConfig = await this.getConfig(); const patterns = currentConfig.interceptPatterns.filter(p => p !== pattern); await this.updateConfig({ interceptPatterns: patterns }); } async addExcludePattern(pattern: string): Promise { const currentConfig = await this.getConfig(); const patterns = [...currentConfig.excludePatterns]; if (!patterns.includes(pattern)) { patterns.push(pattern); await this.updateConfig({ excludePatterns: patterns }); } } async removeExcludePattern(pattern: string): Promise { const currentConfig = await this.getConfig(); const patterns = currentConfig.excludePatterns.filter(p => p !== pattern); await this.updateConfig({ excludePatterns: patterns }); } // Utility Methods async clearDatabase(): Promise { await this.communication.clearDatabase(); } use(plugin: SwMockPlugin): void { this.plugins.push(plugin); plugin.install(this); } }