UNPKG

myfflags-sdk

Version:
164 lines (159 loc) 5.42 kB
'use strict'; Object.defineProperty(exports, '__esModule', { value: true }); var CryptoJS = require('crypto-js'); /** * MFFS (My F* Flags) SDK Class * Classe principal para interagir com o sistema de flags */ class MFFS { constructor(config) { this.cache = new Map(); this.config = { environment: 'production', cacheTimeout: 5 * 60 * 1000, // 5 minutos ...config }; } /** * Verifica se uma feature flag está habilitada para um usuário */ async isFeatureEnabled(flagName, userContext) { try { const flags = await this.getFlags(); const flag = flags.find(f => f.name === flagName); if (!flag || !flag.enabled) { return false; } // Verificar se está habilitada para o ambiente atual if (!flag.environments[this.config.environment]) { return false; } // Aplicar regras de targeting if (!this.evaluateTargetingRules(flag.targetingRules, userContext)) { return false; } // Aplicar rollout percentual usando hash determinístico return this.evaluateRolloutPercentage(flag, userContext.userId); } catch (error) { console.error('Error evaluating feature flag:', error); return false; } } /** * Busca todas as flags habilitadas para um usuário */ async getEnabledFeatures(userContext) { try { const flags = await this.getFlags(); const enabledFlags = []; for (const flag of flags) { if (await this.isFeatureEnabled(flag.name, userContext)) { enabledFlags.push(flag.name); } } return enabledFlags; } catch (error) { console.error('Error getting enabled features:', error); return []; } } /** * Busca flags do servidor (com cache) */ async getFlags() { const cacheKey = this.config.environment; const cached = this.cache.get(cacheKey); // Verificar se o cache ainda é válido if (cached && Date.now() - cached.timestamp < this.config.cacheTimeout) { return cached.flags; } try { const response = await fetch(`${this.config.apiUrl}/api/flags/public/${this.config.environment}`, { headers: this.config.apiKey ? { 'Authorization': `Bearer ${this.config.apiKey}` } : {} }); if (!response.ok) { throw new Error(`HTTP error! status: ${response.status}`); } const data = await response.json(); const flags = data.flags || []; // Atualizar cache this.cache.set(cacheKey, { flags, timestamp: Date.now() }); return flags; } catch (error) { console.error('Error fetching flags:', error); // Retornar cache anterior se disponível if (cached) { return cached.flags; } return []; } } /** * Avalia regras de targeting */ evaluateTargetingRules(rules, userContext) { if (rules.length === 0) { return true; } return rules.every(rule => { const userValue = userContext[rule.attribute]; if (userValue === undefined) { return false; } const userValueStr = String(userValue); const ruleValueStr = rule.value; switch (rule.operator) { case 'equals': return userValueStr === ruleValueStr; case 'not_equals': return userValueStr !== ruleValueStr; case 'contains': return userValueStr.toLowerCase().includes(ruleValueStr.toLowerCase()); case 'not_contains': return !userValueStr.toLowerCase().includes(ruleValueStr.toLowerCase()); case 'greater_than': return parseFloat(userValueStr) > parseFloat(ruleValueStr); case 'less_than': return parseFloat(userValueStr) < parseFloat(ruleValueStr); default: return false; } }); } /** * Avalia rollout percentual usando hash determinístico */ evaluateRolloutPercentage(flag, userId) { // Criar hash determinístico baseado no userId + flagName const hashInput = `${userId}:${flag.name}`; const hash = CryptoJS.MD5(hashInput).toString(); // Converter os primeiros 8 caracteres do hash em número const hashNumber = parseInt(hash.substring(0, 8), 16); const percentage = hashNumber % 100; return percentage < flag.rolloutPercentage; } /** * Limpa o cache */ clearCache() { this.cache.clear(); } /** * Atualiza a configuração */ updateConfig(newConfig) { this.config = { ...this.config, ...newConfig }; this.clearCache(); } } exports.MFFS = MFFS; exports.default = MFFS; //# sourceMappingURL=index.js.map