UNPKG

@varia-bly/variably-sdk

Version:

Official JavaScript/TypeScript SDK for Variably feature flags, experimentation, LLM experiments with React hooks, and real-time dynamic configurations

109 lines 4.15 kB
/** * Real-time Feature Gate synchronization helper * Manages WebSocket subscriptions and cache invalidation */ import { GraphQLSubscriptionClient } from './graphql-subscription-client'; export class RealTimeGateSync { constructor(config, cache, logger) { this.subscriptionClient = null; const realTimeConfig = config.realTimeUpdates || {}; this.config = { enabled: realTimeConfig.enabled || false, projectId: realTimeConfig.projectId || '', autoInvalidateCache: realTimeConfig.autoInvalidateCache !== false }; this.baseUrl = config.baseUrl || 'http://localhost:4000'; this.apiKey = config.apiKey; this.cache = cache; this.logger = logger; } /** * Initialize and start real-time subscriptions */ async start() { if (!this.config.enabled) { this.logger.debug('Real-time updates not enabled, skipping subscription setup'); return; } if (!this.config.projectId) { this.logger.warn('Real-time updates enabled but projectId not provided'); return; } try { this.logger.info('Starting real-time Feature Gate subscriptions', { projectId: this.config.projectId }); this.subscriptionClient = new GraphQLSubscriptionClient({ url: this.baseUrl, apiKey: this.apiKey, projectId: this.config.projectId }, this.logger); // Connect to WebSocket await this.subscriptionClient.connect(); // Subscribe to all Feature Gate updates for this project this.subscriptionClient.subscribeToFeatureGates(this.config.projectId); // Handle Feature Gate updates this.subscriptionClient.onAnyFeatureGateUpdate((gateUpdate) => { this.handleGateUpdate(gateUpdate); }); this.logger.info('Real-time Feature Gate subscriptions started successfully'); } catch (error) { this.logger.error('Failed to start real-time subscriptions', { error: error instanceof Error ? error.message : String(error) }); } } /** * Stop real-time subscriptions */ stop() { if (this.subscriptionClient) { this.subscriptionClient.disconnect(); this.subscriptionClient = null; this.logger.info('Real-time Feature Gate subscriptions stopped'); } } /** * Check if real-time sync is active */ isActive() { return this.subscriptionClient !== null && this.subscriptionClient.isConnected(); } /** * Get connection state */ getConnectionState() { return this.subscriptionClient?.getConnectionState() || 'disconnected'; } /** * Handle Feature Gate update from subscription */ handleGateUpdate(gateUpdate) { this.logger.info('Received real-time Feature Gate update', { gateKey: gateUpdate.gateKey, isEnabled: gateUpdate.isEnabled, projectId: gateUpdate.projectId }); if (this.config.autoInvalidateCache) { // Invalidate all cache entries for this gate this.invalidateGateCache(gateUpdate.gateKey); } } /** * Invalidate cache entries for a specific gate */ invalidateGateCache(gateKey) { // We need to invalidate all cached evaluations for this gate // Since cache keys include user context, we can't target specific entries // Instead, we'll mark the gate as updated so next evaluation will fetch fresh data // For now, we can clear the entire cache (simple but effective) // In production, you might want to implement selective cache invalidation const invalidatedCount = this.cache.clear(); this.logger.info('Cache invalidated for Feature Gate update', { gateKey, invalidatedEntries: invalidatedCount }); } } //# sourceMappingURL=realtime-gate-sync.js.map