UNPKG

@splitsoftware/splitio-react

Version:

A React library to easily integrate and use Split JS SDK

137 lines (136 loc) 6.26 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.memoizeGetTreatmentsWithConfig = exports.getControlTreatmentsWithConfig = exports.initAttributes = exports.getStatus = exports.getSplitClient = void 0; var tslib_1 = require("tslib"); var memoize_one_1 = (0, tslib_1.__importDefault)(require("memoize-one")); var shallowequal_1 = (0, tslib_1.__importDefault)(require("shallowequal")); var constants_1 = require("./constants"); // idempotent operation 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; } exports.getSplitClient = getSplitClient; // 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 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, }; } exports.getStatus = getStatus; /** * Manage client attributes binding */ // @TODO should reset attributes rather than set/merge them, to keep SFP and hooks pure. function initAttributes(client, attributes) { if (client && attributes) client.setAttributes(attributes); } exports.initAttributes = initAttributes; // 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; } 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] = constants_1.CONTROL_WITH_CONFIG; return pValue; }, {}); } exports.getControlTreatmentsWithConfig = getControlTreatmentsWithConfig; /** * 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`. */ function memoizeGetTreatmentsWithConfig() { return (0, memoize_one_1.default)(evaluateFeatureFlags, argsAreEqual); } exports.memoizeGetTreatmentsWithConfig = memoizeGetTreatmentsWithConfig; function argsAreEqual(newArgs, lastArgs) { return newArgs[0] === lastArgs[0] && // client newArgs[1] === lastArgs[1] && // lastUpdate (0, shallowequal_1.default)(newArgs[2], lastArgs[2]) && // names (0, shallowequal_1.default)(newArgs[3], lastArgs[3]) && // attributes (0, shallowequal_1.default)(newArgs[4], lastArgs[4]) && // client attributes (0, shallowequal_1.default)(newArgs[5], lastArgs[5]); // flagSets } function evaluateFeatureFlags(client, _lastUpdate, names, attributes, _clientAttributes, flagSets, options) { if (names && flagSets) console.log(constants_1.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 }