/** * Feature Registry * * Central registry for managing SDK features * * @module utils/FeatureRegistry */ import type { IFeatureModule } from '../types/IFeatureModule'; import { Feature } from '../types/Feature'; import { FeatureNotEnabledError } from '../errors/FeatureNotEnabledError'; import { OrbisError, OrbisErrorCode } from '../errors/OrbisError'; /** * FeatureRegistry class * * Manages registration, initialization, and access to SDK features. * Provides a centralized way to enable/disable and interact with features. */ export class FeatureRegistry { private features: Map = new Map(); private initialized = false; /** * Register a feature module * * @param feature - Feature enum value * @param module - Feature module instance */ register(feature: Feature, module: IFeatureModule): void { if (this.initialized) { throw new OrbisError( 'Cannot register features after initialization', OrbisErrorCode.INVALID_OPERATION ); } if (this.features.has(feature)) { throw new OrbisError( `Feature '${feature}' is already registered`, OrbisErrorCode.INVALID_OPERATION, undefined, { feature } ); } // Validate that module name matches feature if (module.name !== feature) { throw new OrbisError( `Feature module name '${module.name}' does not match feature '${feature}'`, OrbisErrorCode.INVALID_OPERATION, undefined, { feature, moduleName: module.name } ); } this.features.set(feature, module); } /** * Unregister a feature module * * @param feature - Feature enum value */ unregister(feature: Feature): void { if (this.initialized) { throw new OrbisError( 'Cannot unregister features after initialization', OrbisErrorCode.INVALID_OPERATION ); } this.features.delete(feature); } /** * Check if a feature is registered * * @param feature - Feature enum value * @returns True if registered, false otherwise */ has(feature: Feature): boolean { return this.features.has(feature); } /** * Get a feature module * * @param feature - Feature enum value * @param throwIfNotEnabled - Whether to throw if feature is not enabled * @returns Feature module instance or undefined * @throws {FeatureNotEnabledError} If feature is not enabled and throwIfNotEnabled is true */ get( feature: Feature, throwIfNotEnabled = true ): T | undefined { const module = this.features.get(feature) as T | undefined; if (!module && throwIfNotEnabled) { throw new FeatureNotEnabledError(feature); } return module; } /** * Get all registered features * * @returns Array of feature names */ getAll(): Feature[] { return Array.from(this.features.keys()) as Feature[]; } /** * Get all registered feature modules * * @returns Array of feature modules */ getAllModules(): IFeatureModule[] { return Array.from(this.features.values()); } /** * Check if a feature is enabled and ready * * @param feature - Feature enum value * @returns True if enabled and ready, false otherwise */ isReady(feature: Feature): boolean { const module = this.features.get(feature); return module ? module.isReady() : false; } /** * Get status of all features * * @returns Map of feature statuses */ getStatus(): Record> { const status: Record> = {}; for (const [feature, module] of this.features) { status[feature] = module.getStatus(); } return status; } /** * Mark registry as initialized * * Prevents further registration/unregistration of features. */ markInitialized(): void { this.initialized = true; } /** * Check if registry is initialized * * @returns True if initialized, false otherwise */ isInitialized(): boolean { return this.initialized; } /** * Clear all registered features * * Only allowed before initialization. */ clear(): void { if (this.initialized) { throw new OrbisError( 'Cannot clear features after initialization', OrbisErrorCode.INVALID_OPERATION ); } this.features.clear(); } /** * Get count of registered features * * @returns Number of registered features */ count(): number { return this.features.size; } }