UNPKG

@splitsoftware/splitio-react

Version:

A React library to easily integrate and use Split JS SDK

128 lines (127 loc) 5.63 kB
import memoizeOne from 'memoize-one'; import shallowEqual from 'shallowequal'; import { CONTROL_WITH_CONFIG, WARN_NAMES_AND_FLAGSETS } from './constants'; // idempotent operation export function getSplitClient(factory, key) { // factory.client is an idempotent operation var client = (key !== undefined ? factory.client(key) : factory.client()); // Remove EventEmitter warning emitted when using multiple SDK hooks or components. // Unlike JS SDK, users don't need to access the client directly, making the warning irrelevant. client.setMaxListeners && client.setMaxListeners(0); return client; } // Util used to get client status. // It might be removed in the future, if the JS SDK extends its public API with a `getStatus` method export function getStatus(client) { var status = client && client.__getStatus(); return { isReady: status ? status.isReady : false, isReadyFromCache: status ? status.isReadyFromCache : false, isTimedout: status ? status.isTimedout : false, hasTimedout: status ? status.hasTimedout : false, isDestroyed: status ? status.isDestroyed : false, lastUpdate: status ? status.lastUpdate : 0, }; } /** * Manage client attributes binding */ // @TODO should reset attributes rather than set/merge them, to keep SFP and hooks pure. export function initAttributes(client, attributes) { if (client && attributes) client.setAttributes(attributes); } // Input validation utils that will be replaced eventually function validateFeatureFlags(maybeFeatureFlags, listName) { if (listName === void 0) { listName = 'feature flag names'; } if (Array.isArray(maybeFeatureFlags) && maybeFeatureFlags.length > 0) { var validatedArray_1 = []; // Remove invalid values maybeFeatureFlags.forEach(function (maybeFeatureFlag) { var featureFlagName = validateFeatureFlag(maybeFeatureFlag); if (featureFlagName) validatedArray_1.push(featureFlagName); }); // Strip off duplicated values if we have valid feature flag names then return if (validatedArray_1.length) return uniq(validatedArray_1); } console.log("[ERROR] ".concat(listName, " must be a non-empty array.")); return false; } var TRIMMABLE_SPACES_REGEX = /^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/; function validateFeatureFlag(maybeFeatureFlag, item) { if (item === void 0) { item = 'feature flag name'; } if (maybeFeatureFlag == undefined) { console.log("[ERROR] you passed a null or undefined ".concat(item, ", ").concat(item, " must be a non-empty string.")); } else if (!isString(maybeFeatureFlag)) { console.log("[ERROR] you passed an invalid ".concat(item, ", ").concat(item, " must be a non-empty string.")); } else { if (TRIMMABLE_SPACES_REGEX.test(maybeFeatureFlag)) { console.log("[WARN] ".concat(item, " \"").concat(maybeFeatureFlag, "\" has extra whitespace, trimming.")); maybeFeatureFlag = maybeFeatureFlag.trim(); } if (maybeFeatureFlag.length > 0) { return maybeFeatureFlag; } else { console.log("[ERROR] you passed an empty ".concat(item, ", ").concat(item, " must be a non-empty string.")); } } return false; } export function getControlTreatmentsWithConfig(featureFlagNames) { // validate featureFlags Names var validatedFeatureFlagNames = validateFeatureFlags(featureFlagNames); // return empty object if the returned value is false if (!validatedFeatureFlagNames) return {}; // return control treatments for each validated feature flag name return validatedFeatureFlagNames.reduce(function (pValue, cValue) { pValue[cValue] = CONTROL_WITH_CONFIG; return pValue; }, {}); } /** * Removes duplicate items on an array of strings. */ function uniq(arr) { var seen = {}; return arr.filter(function (item) { return Object.prototype.hasOwnProperty.call(seen, item) ? false : seen[item] = true; }); } /** * Checks if a given value is a string. */ function isString(val) { return typeof val === 'string' || val instanceof String; } /** * Gets a memoized version of the `client.getTreatmentsWithConfig` method. * It is used to avoid duplicated impressions, because the result treatments are the same given the same `client` instance, `lastUpdate` timestamp, and list of feature flag `names` and `attributes`. */ export function memoizeGetTreatmentsWithConfig() { return memoizeOne(evaluateFeatureFlags, argsAreEqual); } function argsAreEqual(newArgs, lastArgs) { return newArgs[0] === lastArgs[0] && // client newArgs[1] === lastArgs[1] && // lastUpdate shallowEqual(newArgs[2], lastArgs[2]) && // names shallowEqual(newArgs[3], lastArgs[3]) && // attributes shallowEqual(newArgs[4], lastArgs[4]) && // client attributes shallowEqual(newArgs[5], lastArgs[5]); // flagSets } function evaluateFeatureFlags(client, _lastUpdate, names, attributes, _clientAttributes, flagSets, options) { if (names && flagSets) console.log(WARN_NAMES_AND_FLAGSETS); return client && client.__getStatus().isOperational && (names || flagSets) ? names ? client.getTreatmentsWithConfig(names, attributes, options) : client.getTreatmentsWithConfigByFlagSets(flagSets, attributes, options) : names ? getControlTreatmentsWithConfig(names) : {}; // empty object when evaluating with flag sets and client is not ready }