@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
JavaScript
/**
* 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