@splitsoftware/splitio-react
Version:
A React library to easily integrate and use Split JS SDK
137 lines (136 loc) • 6.26 kB
JavaScript
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
}
;