@atlaskit/feature-gate-js-client
Advanced tools
Comparing version
# @atlaskit/feature-gate-js-client | ||
## 4.26.1 | ||
### Patch Changes | ||
- [#118528](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/pull-requests/118528) | ||
[`648957220b9f8`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/648957220b9f8) - | ||
Updated babel configuration | ||
## 4.26.0 | ||
@@ -4,0 +12,0 @@ |
@@ -34,2 +34,3 @@ const crypto = require('crypto'); | ||
presets: ['@babel/preset-env'], | ||
configFile: path.join(__dirname, './babel.config.js'), | ||
}, | ||
@@ -36,0 +37,0 @@ }, |
@@ -1,2 +0,8 @@ | ||
import { Client as FeatureGateClient } from './client/Client'; | ||
export default FeatureGateClient; | ||
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { | ||
value: true | ||
}); | ||
exports.default = void 0; | ||
var _Client = require("./client/Client"); | ||
var _default = exports.default = _Client.Client; |
@@ -1,27 +0,42 @@ | ||
import _objectWithoutProperties from "@babel/runtime/helpers/objectWithoutProperties"; | ||
import _defineProperty from "@babel/runtime/helpers/defineProperty"; | ||
const _excluded = ["sdkKey", "environment", "updateUserCompletionCallback", "perimeter"]; | ||
"use strict"; | ||
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); | ||
Object.defineProperty(exports, "__esModule", { | ||
value: true | ||
}); | ||
exports.Client = void 0; | ||
var _objectWithoutProperties2 = _interopRequireDefault(require("@babel/runtime/helpers/objectWithoutProperties")); | ||
var _regenerator = _interopRequireDefault(require("@babel/runtime/regenerator")); | ||
var _slicedToArray2 = _interopRequireDefault(require("@babel/runtime/helpers/slicedToArray")); | ||
var _asyncToGenerator2 = _interopRequireDefault(require("@babel/runtime/helpers/asyncToGenerator")); | ||
var _classCallCheck2 = _interopRequireDefault(require("@babel/runtime/helpers/classCallCheck")); | ||
var _createClass2 = _interopRequireDefault(require("@babel/runtime/helpers/createClass")); | ||
var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty")); | ||
var _jsClient = require("@statsig/js-client"); | ||
var _subscriptions = _interopRequireDefault(require("../subscriptions")); | ||
var _DynamicConfig = require("./compat/DynamicConfig"); | ||
var _Layer = require("./compat/Layer"); | ||
var _types = require("./compat/types"); | ||
var _fetcher = _interopRequireDefault(require("./fetcher")); | ||
var _NoFetchDataAdapter = require("./NoFetchDataAdapter"); | ||
var _PersistentOverrideAdapter = require("./PersistentOverrideAdapter"); | ||
var _types2 = require("./types"); | ||
var _utils = require("./utils"); | ||
var _version = require("./version"); | ||
var _excluded = ["sdkKey", "environment", "updateUserCompletionCallback", "perimeter"]; | ||
function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; } | ||
function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; } | ||
import { _makeLayer, StatsigClient } from '@statsig/js-client'; | ||
import Subscriptions from '../subscriptions'; | ||
import { DynamicConfig } from './compat/DynamicConfig'; | ||
import { Layer } from './compat/Layer'; | ||
import { EvaluationReason } from './compat/types'; | ||
import Fetcher from './fetcher'; | ||
import { NoFetchDataAdapter } from './NoFetchDataAdapter'; | ||
import { LOCAL_STORAGE_KEY, PersistentOverrideAdapter } from './PersistentOverrideAdapter'; | ||
import { FeatureGateEnvironment, PerimeterType } from './types'; | ||
import { getOptionsWithDefaults, migrateInitializationOptions, shallowEquals, toStatsigUser } from './utils'; | ||
import { CLIENT_VERSION } from './version'; | ||
const DEFAULT_CLIENT_KEY = 'client-default-key'; | ||
function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { (0, _defineProperty2.default)(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; } | ||
var DEFAULT_CLIENT_KEY = 'client-default-key'; | ||
// default event logging api is Atlassian proxy rather than Statsig's domain, to avoid ad blockers | ||
const DEFAULT_EVENT_LOGGING_API = 'https://xp.atlassian.com/v1/rgstr'; | ||
export class Client { | ||
constructor({ | ||
localStorageKey = LOCAL_STORAGE_KEY | ||
} = {}) { | ||
_defineProperty(this, "initPromise", null); | ||
var DEFAULT_EVENT_LOGGING_API = 'https://xp.atlassian.com/v1/rgstr'; | ||
var Client = exports.Client = /*#__PURE__*/function () { | ||
function Client() { | ||
var _this = this; | ||
var _ref = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}, | ||
_ref$localStorageKey = _ref.localStorageKey, | ||
localStorageKey = _ref$localStorageKey === void 0 ? _PersistentOverrideAdapter.LOCAL_STORAGE_KEY : _ref$localStorageKey; | ||
(0, _classCallCheck2.default)(this, Client); | ||
(0, _defineProperty2.default)(this, "initPromise", null); | ||
/** True if an initialize method was called and completed successfully. */ | ||
_defineProperty(this, "initCompleted", false); | ||
(0, _defineProperty2.default)(this, "initCompleted", false); | ||
/** | ||
@@ -32,10 +47,10 @@ * True if an initialize method was called and completed, meaning the client is now usable. | ||
*/ | ||
_defineProperty(this, "initWithDefaults", false); | ||
_defineProperty(this, "hasCheckGateErrorOccurred", false); | ||
_defineProperty(this, "hasGetExperimentErrorOccurred", false); | ||
_defineProperty(this, "hasGetExperimentValueErrorOccurred", false); | ||
_defineProperty(this, "hasGetLayerErrorOccurred", false); | ||
_defineProperty(this, "hasGetLayerValueErrorOccurred", false); | ||
_defineProperty(this, "subscriptions", new Subscriptions()); | ||
_defineProperty(this, "dataAdapter", new NoFetchDataAdapter()); | ||
(0, _defineProperty2.default)(this, "initWithDefaults", false); | ||
(0, _defineProperty2.default)(this, "hasCheckGateErrorOccurred", false); | ||
(0, _defineProperty2.default)(this, "hasGetExperimentErrorOccurred", false); | ||
(0, _defineProperty2.default)(this, "hasGetExperimentValueErrorOccurred", false); | ||
(0, _defineProperty2.default)(this, "hasGetLayerErrorOccurred", false); | ||
(0, _defineProperty2.default)(this, "hasGetLayerValueErrorOccurred", false); | ||
(0, _defineProperty2.default)(this, "subscriptions", new _subscriptions.default()); | ||
(0, _defineProperty2.default)(this, "dataAdapter", new _NoFetchDataAdapter.NoFetchDataAdapter()); | ||
/** | ||
@@ -45,12 +60,12 @@ * Call this if modifying the values being served by the Statsig library since it has its own | ||
*/ | ||
_defineProperty(this, "statsigValuesUpdated", () => { | ||
if (this.user) { | ||
(0, _defineProperty2.default)(this, "statsigValuesUpdated", function () { | ||
if (_this.user) { | ||
// Trigger a reset of the memoize cache | ||
this.statsigClient.updateUserSync(this.user, { | ||
_this.statsigClient.updateUserSync(_this.user, { | ||
disableBackgroundCacheRefresh: true | ||
}); | ||
} | ||
this.subscriptions.anyUpdated(); | ||
_this.subscriptions.anyUpdated(); | ||
}); | ||
this.overrideAdapter = new PersistentOverrideAdapter(localStorageKey); | ||
this.overrideAdapter = new _PersistentOverrideAdapter.PersistentOverrideAdapter(localStorageKey); | ||
} | ||
@@ -74,911 +89,1251 @@ | ||
*/ | ||
async initialize(clientOptions, identifiers, customAttributes) { | ||
const clientOptionsWithDefaults = getOptionsWithDefaults(clientOptions); | ||
if (this.initPromise) { | ||
if (!shallowEquals(clientOptionsWithDefaults, this.initOptions)) { | ||
// eslint-disable-next-line no-console | ||
console.warn('Feature Gates client already initialized with different options. New options were not applied.'); | ||
return (0, _createClass2.default)(Client, [{ | ||
key: "initialize", | ||
value: (function () { | ||
var _initialize = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee(clientOptions, identifiers, customAttributes) { | ||
var _this2 = this; | ||
var clientOptionsWithDefaults, startTime; | ||
return _regenerator.default.wrap(function _callee$(_context) { | ||
while (1) switch (_context.prev = _context.next) { | ||
case 0: | ||
clientOptionsWithDefaults = (0, _utils.getOptionsWithDefaults)(clientOptions); | ||
if (!this.initPromise) { | ||
_context.next = 4; | ||
break; | ||
} | ||
if (!(0, _utils.shallowEquals)(clientOptionsWithDefaults, this.initOptions)) { | ||
// eslint-disable-next-line no-console | ||
console.warn('Feature Gates client already initialized with different options. New options were not applied.'); | ||
} | ||
return _context.abrupt("return", this.initPromise); | ||
case 4: | ||
startTime = performance.now(); | ||
this.initOptions = clientOptionsWithDefaults; | ||
this.initPromise = this.init(clientOptionsWithDefaults, identifiers, customAttributes).then(function () { | ||
_this2.initCompleted = true; | ||
_this2.initWithDefaults = true; | ||
}).finally(function () { | ||
var endTime = performance.now(); | ||
var totalTime = endTime - startTime; | ||
_this2.fireClientEvent(startTime, totalTime, 'initialize', _this2.initCompleted, clientOptionsWithDefaults.apiKey); | ||
}); | ||
return _context.abrupt("return", this.initPromise); | ||
case 8: | ||
case "end": | ||
return _context.stop(); | ||
} | ||
}, _callee, this); | ||
})); | ||
function initialize(_x, _x2, _x3) { | ||
return _initialize.apply(this, arguments); | ||
} | ||
return this.initPromise; | ||
} | ||
const startTime = performance.now(); | ||
this.initOptions = clientOptionsWithDefaults; | ||
this.initPromise = this.init(clientOptionsWithDefaults, identifiers, customAttributes).then(() => { | ||
this.initCompleted = true; | ||
this.initWithDefaults = true; | ||
}).finally(() => { | ||
const endTime = performance.now(); | ||
const totalTime = endTime - startTime; | ||
this.fireClientEvent(startTime, totalTime, 'initialize', this.initCompleted, clientOptionsWithDefaults.apiKey); | ||
}); | ||
return this.initPromise; | ||
} | ||
/** | ||
* @description | ||
* This method initializes the client using the provider given to call to fetch the bootstrap values. | ||
* If the client is initialized with an `analyticsWebClient`, it will send an operational event | ||
* to GASv3 with the following attributes: | ||
* - targetApp: the target app of the client | ||
* - clientVersion: the version of the client | ||
* - success: whether the initialization was successful | ||
* - startTime: the time when the initialization started | ||
* - totalTime: the total time it took to initialize the client | ||
* - apiKey: the api key used to initialize the client | ||
* @param clientOptions {ClientOptions} | ||
* @param provider {Provider} | ||
* @param identifiers {Identifiers} | ||
* @param customAttributes {CustomAttributes} | ||
* @returns {Promise<void>} | ||
*/ | ||
async initializeWithProvider(clientOptions, provider, identifiers, customAttributes) { | ||
const clientOptionsWithDefaults = getOptionsWithDefaults(clientOptions); | ||
if (this.initPromise) { | ||
if (!shallowEquals(clientOptionsWithDefaults, this.initOptions)) { | ||
return initialize; | ||
}() | ||
/** | ||
* @description | ||
* This method initializes the client using the provider given to call to fetch the bootstrap values. | ||
* If the client is initialized with an `analyticsWebClient`, it will send an operational event | ||
* to GASv3 with the following attributes: | ||
* - targetApp: the target app of the client | ||
* - clientVersion: the version of the client | ||
* - success: whether the initialization was successful | ||
* - startTime: the time when the initialization started | ||
* - totalTime: the total time it took to initialize the client | ||
* - apiKey: the api key used to initialize the client | ||
* @param clientOptions {ClientOptions} | ||
* @param provider {Provider} | ||
* @param identifiers {Identifiers} | ||
* @param customAttributes {CustomAttributes} | ||
* @returns {Promise<void>} | ||
*/ | ||
) | ||
}, { | ||
key: "initializeWithProvider", | ||
value: (function () { | ||
var _initializeWithProvider = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee2(clientOptions, provider, identifiers, customAttributes) { | ||
var _this3 = this; | ||
var clientOptionsWithDefaults, startTime; | ||
return _regenerator.default.wrap(function _callee2$(_context2) { | ||
while (1) switch (_context2.prev = _context2.next) { | ||
case 0: | ||
clientOptionsWithDefaults = (0, _utils.getOptionsWithDefaults)(clientOptions); | ||
if (!this.initPromise) { | ||
_context2.next = 4; | ||
break; | ||
} | ||
if (!(0, _utils.shallowEquals)(clientOptionsWithDefaults, this.initOptions)) { | ||
// eslint-disable-next-line no-console | ||
console.warn('Feature Gates client already initialized with different options. New options were not applied.'); | ||
} | ||
return _context2.abrupt("return", this.initPromise); | ||
case 4: | ||
startTime = performance.now(); | ||
this.initOptions = clientOptionsWithDefaults; | ||
this.provider = provider; | ||
this.provider.setClientVersion(_version.CLIENT_VERSION); | ||
if (this.provider.setApplyUpdateCallback) { | ||
this.provider.setApplyUpdateCallback(this.applyUpdateCallback.bind(this)); | ||
} | ||
this.initPromise = this.initWithProvider(clientOptionsWithDefaults, provider, identifiers, customAttributes).then(function () { | ||
_this3.initCompleted = true; | ||
_this3.initWithDefaults = true; | ||
}).finally(function () { | ||
var endTime = performance.now(); | ||
var totalTime = endTime - startTime; | ||
_this3.fireClientEvent(startTime, totalTime, 'initializeWithProvider', _this3.initCompleted, provider.getApiKey ? provider.getApiKey() : undefined); | ||
}); | ||
return _context2.abrupt("return", this.initPromise); | ||
case 11: | ||
case "end": | ||
return _context2.stop(); | ||
} | ||
}, _callee2, this); | ||
})); | ||
function initializeWithProvider(_x4, _x5, _x6, _x7) { | ||
return _initializeWithProvider.apply(this, arguments); | ||
} | ||
return initializeWithProvider; | ||
}()) | ||
}, { | ||
key: "applyUpdateCallback", | ||
value: function applyUpdateCallback(experimentsResult) { | ||
try { | ||
if (this.initCompleted || this.initWithDefaults) { | ||
this.assertInitialized(this.statsigClient); | ||
this.dataAdapter.setBootstrapData(experimentsResult.experimentValues); | ||
this.dataAdapter.setData(JSON.stringify(experimentsResult.experimentValues)); | ||
this.statsigValuesUpdated(); | ||
} | ||
} catch (error) { | ||
// eslint-disable-next-line no-console | ||
console.warn('Feature Gates client already initialized with different options. New options were not applied.'); | ||
console.warn('Error when attempting to apply update', error); | ||
} | ||
return this.initPromise; | ||
} | ||
const startTime = performance.now(); | ||
this.initOptions = clientOptionsWithDefaults; | ||
this.provider = provider; | ||
this.provider.setClientVersion(CLIENT_VERSION); | ||
if (this.provider.setApplyUpdateCallback) { | ||
this.provider.setApplyUpdateCallback(this.applyUpdateCallback.bind(this)); | ||
}, { | ||
key: "fireClientEvent", | ||
value: function fireClientEvent(startTime, totalTime, action, success) { | ||
var _analyticsWebClient, | ||
_this4 = this; | ||
var apiKey = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : undefined; | ||
(_analyticsWebClient = this.initOptions.analyticsWebClient) === null || _analyticsWebClient === void 0 || _analyticsWebClient.then(function (analyticsWebClient) { | ||
var attributes = _objectSpread({ | ||
targetApp: _this4.initOptions.targetApp, | ||
clientVersion: _version.CLIENT_VERSION, | ||
success: success, | ||
startTime: startTime, | ||
totalTime: totalTime | ||
}, apiKey && { | ||
apiKey: apiKey | ||
}); | ||
analyticsWebClient.sendOperationalEvent({ | ||
action: action, | ||
actionSubject: 'featureGatesClient', | ||
attributes: attributes, | ||
tags: ['measurement'], | ||
source: '@atlaskit/feature-gate-js-client' | ||
}); | ||
}).catch(function (err) { | ||
if (_this4.initOptions.environment !== _types2.FeatureGateEnvironment.Production) { | ||
// eslint-disable-next-line no-console | ||
console.error('Analytics web client promise did not resolve', err); | ||
} | ||
}); | ||
} | ||
this.initPromise = this.initWithProvider(clientOptionsWithDefaults, provider, identifiers, customAttributes).then(() => { | ||
this.initCompleted = true; | ||
this.initWithDefaults = true; | ||
}).finally(() => { | ||
const endTime = performance.now(); | ||
const totalTime = endTime - startTime; | ||
this.fireClientEvent(startTime, totalTime, 'initializeWithProvider', this.initCompleted, provider.getApiKey ? provider.getApiKey() : undefined); | ||
}); | ||
return this.initPromise; | ||
} | ||
applyUpdateCallback(experimentsResult) { | ||
try { | ||
if (this.initCompleted || this.initWithDefaults) { | ||
this.assertInitialized(this.statsigClient); | ||
this.dataAdapter.setBootstrapData(experimentsResult.experimentValues); | ||
this.dataAdapter.setData(JSON.stringify(experimentsResult.experimentValues)); | ||
this.statsigValuesUpdated(); | ||
}, { | ||
key: "initializeFromValues", | ||
value: function () { | ||
var _initializeFromValues = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee3(clientOptions, identifiers, customAttributes) { | ||
var _this5 = this; | ||
var initializeValues, | ||
clientOptionsWithDefaults, | ||
startTime, | ||
_args3 = arguments; | ||
return _regenerator.default.wrap(function _callee3$(_context3) { | ||
while (1) switch (_context3.prev = _context3.next) { | ||
case 0: | ||
initializeValues = _args3.length > 3 && _args3[3] !== undefined ? _args3[3] : {}; | ||
clientOptionsWithDefaults = (0, _utils.getOptionsWithDefaults)(clientOptions); | ||
if (!this.initPromise) { | ||
_context3.next = 5; | ||
break; | ||
} | ||
if (!(0, _utils.shallowEquals)(clientOptionsWithDefaults, this.initOptions)) { | ||
// eslint-disable-next-line no-console | ||
console.warn('Feature Gates client already initialized with different options. New options were not applied.'); | ||
} | ||
return _context3.abrupt("return", this.initPromise); | ||
case 5: | ||
// This makes sure the new Statsig client behaves like the old when bootstrap data is | ||
// passed, and `has_updates` isn't specified (which happens a lot in product integration tests). | ||
if (!Object.prototype.hasOwnProperty.call(initializeValues, 'has_updates')) { | ||
initializeValues['has_updates'] = true; | ||
} | ||
startTime = performance.now(); | ||
this.initOptions = clientOptionsWithDefaults; | ||
this.initPromise = this.initFromValues(clientOptionsWithDefaults, identifiers, customAttributes, initializeValues).then(function () { | ||
_this5.initCompleted = true; | ||
_this5.initWithDefaults = true; | ||
}).finally(function () { | ||
var endTime = performance.now(); | ||
var totalTime = endTime - startTime; | ||
_this5.fireClientEvent(startTime, totalTime, 'initializeFromValues', _this5.initCompleted); | ||
}); | ||
return _context3.abrupt("return", this.initPromise); | ||
case 10: | ||
case "end": | ||
return _context3.stop(); | ||
} | ||
}, _callee3, this); | ||
})); | ||
function initializeFromValues(_x8, _x9, _x10) { | ||
return _initializeFromValues.apply(this, arguments); | ||
} | ||
} catch (error) { | ||
// eslint-disable-next-line no-console | ||
console.warn('Error when attempting to apply update', error); | ||
return initializeFromValues; | ||
}() | ||
}, { | ||
key: "assertInitialized", | ||
value: function assertInitialized(statsigClient) { | ||
if (!statsigClient) { | ||
throw new Error('Client must be initialized before using this method'); | ||
} | ||
} | ||
} | ||
fireClientEvent(startTime, totalTime, action, success, apiKey = undefined) { | ||
var _analyticsWebClient; | ||
(_analyticsWebClient = this.initOptions.analyticsWebClient) === null || _analyticsWebClient === void 0 || _analyticsWebClient.then(analyticsWebClient => { | ||
const attributes = _objectSpread({ | ||
targetApp: this.initOptions.targetApp, | ||
clientVersion: CLIENT_VERSION, | ||
success, | ||
startTime, | ||
totalTime | ||
}, apiKey && { | ||
apiKey | ||
}); | ||
analyticsWebClient.sendOperationalEvent({ | ||
action, | ||
actionSubject: 'featureGatesClient', | ||
attributes, | ||
tags: ['measurement'], | ||
source: '@atlaskit/feature-gate-js-client' | ||
}); | ||
}).catch(err => { | ||
if (this.initOptions.environment !== FeatureGateEnvironment.Production) { | ||
// eslint-disable-next-line no-console | ||
console.error('Analytics web client promise did not resolve', err); | ||
/** | ||
* This method updates the user using a network call to fetch the new set of values. | ||
* @param fetchOptions {FetcherOptions} | ||
* @param identifiers {Identifiers} | ||
* @param customAttributes {CustomAttributes} | ||
*/ | ||
}, { | ||
key: "updateUser", | ||
value: (function () { | ||
var _updateUser = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee4(fetchOptions, identifiers, customAttributes) { | ||
var fetchOptionsWithDefaults, initializeValuesProducer; | ||
return _regenerator.default.wrap(function _callee4$(_context4) { | ||
while (1) switch (_context4.prev = _context4.next) { | ||
case 0: | ||
this.assertInitialized(this.statsigClient); | ||
fetchOptionsWithDefaults = (0, _utils.getOptionsWithDefaults)(fetchOptions); | ||
initializeValuesProducer = function initializeValuesProducer() { | ||
return _fetcher.default.fetchExperimentValues(fetchOptionsWithDefaults, identifiers, customAttributes).then(function (_ref2) { | ||
var experimentValues = _ref2.experimentValues, | ||
customAttributes = _ref2.customAttributes; | ||
return { | ||
experimentValues: experimentValues, | ||
customAttributesFromFetch: customAttributes | ||
}; | ||
}); | ||
}; | ||
_context4.next = 5; | ||
return this.updateUserUsingInitializeValuesProducer(initializeValuesProducer, identifiers, customAttributes); | ||
case 5: | ||
case "end": | ||
return _context4.stop(); | ||
} | ||
}, _callee4, this); | ||
})); | ||
function updateUser(_x11, _x12, _x13) { | ||
return _updateUser.apply(this, arguments); | ||
} | ||
}); | ||
} | ||
async initializeFromValues(clientOptions, identifiers, customAttributes, initializeValues = {}) { | ||
const clientOptionsWithDefaults = getOptionsWithDefaults(clientOptions); | ||
if (this.initPromise) { | ||
if (!shallowEquals(clientOptionsWithDefaults, this.initOptions)) { | ||
// eslint-disable-next-line no-console | ||
console.warn('Feature Gates client already initialized with different options. New options were not applied.'); | ||
return updateUser; | ||
}() | ||
/** | ||
* This method updates the user using the provider given on initialisation to get the new set of | ||
* values. | ||
* @param identifiers {Identifiers} | ||
* @param customAttributes {CustomAttributes} | ||
*/ | ||
) | ||
}, { | ||
key: "updateUserWithProvider", | ||
value: (function () { | ||
var _updateUserWithProvider = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee5(identifiers, customAttributes) { | ||
var _this6 = this; | ||
return _regenerator.default.wrap(function _callee5$(_context5) { | ||
while (1) switch (_context5.prev = _context5.next) { | ||
case 0: | ||
this.assertInitialized(this.statsigClient); | ||
if (this.provider) { | ||
_context5.next = 3; | ||
break; | ||
} | ||
throw new Error('Cannot update user using provider as the client was not initialised with a provider'); | ||
case 3: | ||
_context5.next = 5; | ||
return this.provider.setProfile(this.initOptions, identifiers, customAttributes); | ||
case 5: | ||
_context5.next = 7; | ||
return this.updateUserUsingInitializeValuesProducer(function () { | ||
return _this6.provider.getExperimentValues(); | ||
}, identifiers, customAttributes); | ||
case 7: | ||
case "end": | ||
return _context5.stop(); | ||
} | ||
}, _callee5, this); | ||
})); | ||
function updateUserWithProvider(_x14, _x15) { | ||
return _updateUserWithProvider.apply(this, arguments); | ||
} | ||
return this.initPromise; | ||
return updateUserWithProvider; | ||
}() | ||
/** | ||
* This method updates the user given a new set of bootstrap values obtained from one of the | ||
* server-side SDKs. | ||
* | ||
* @param identifiers {Identifiers} | ||
* @param customAttributes {CustomAttributes} | ||
* @param initializeValues {Record<string,unknown>} | ||
*/ | ||
) | ||
}, { | ||
key: "updateUserWithValues", | ||
value: (function () { | ||
var _updateUserWithValues = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee6(identifiers, customAttributes) { | ||
var initializeValues, | ||
initializeValuesProducer, | ||
_args6 = arguments; | ||
return _regenerator.default.wrap(function _callee6$(_context6) { | ||
while (1) switch (_context6.prev = _context6.next) { | ||
case 0: | ||
initializeValues = _args6.length > 2 && _args6[2] !== undefined ? _args6[2] : {}; | ||
this.assertInitialized(this.statsigClient); | ||
initializeValuesProducer = function initializeValuesProducer() { | ||
return Promise.resolve({ | ||
experimentValues: initializeValues, | ||
customAttributesFromFetch: customAttributes | ||
}); | ||
}; | ||
_context6.next = 5; | ||
return this.updateUserUsingInitializeValuesProducer(initializeValuesProducer, identifiers, customAttributes); | ||
case 5: | ||
case "end": | ||
return _context6.stop(); | ||
} | ||
}, _callee6, this); | ||
})); | ||
function updateUserWithValues(_x16, _x17) { | ||
return _updateUserWithValues.apply(this, arguments); | ||
} | ||
return updateUserWithValues; | ||
}()) | ||
}, { | ||
key: "initializeCalled", | ||
value: function initializeCalled() { | ||
return this.initPromise != null; | ||
} | ||
// This makes sure the new Statsig client behaves like the old when bootstrap data is | ||
// passed, and `has_updates` isn't specified (which happens a lot in product integration tests). | ||
if (!Object.prototype.hasOwnProperty.call(initializeValues, 'has_updates')) { | ||
initializeValues['has_updates'] = true; | ||
}, { | ||
key: "initializeCompleted", | ||
value: function initializeCompleted() { | ||
return this.initCompleted; | ||
} | ||
const startTime = performance.now(); | ||
this.initOptions = clientOptionsWithDefaults; | ||
this.initPromise = this.initFromValues(clientOptionsWithDefaults, identifiers, customAttributes, initializeValues).then(() => { | ||
this.initCompleted = true; | ||
this.initWithDefaults = true; | ||
}).finally(() => { | ||
const endTime = performance.now(); | ||
const totalTime = endTime - startTime; | ||
this.fireClientEvent(startTime, totalTime, 'initializeFromValues', this.initCompleted); | ||
}); | ||
return this.initPromise; | ||
} | ||
assertInitialized(statsigClient) { | ||
if (!statsigClient) { | ||
throw new Error('Client must be initialized before using this method'); | ||
} | ||
} | ||
/** | ||
* This method updates the user using a network call to fetch the new set of values. | ||
* @param fetchOptions {FetcherOptions} | ||
* @param identifiers {Identifiers} | ||
* @param customAttributes {CustomAttributes} | ||
*/ | ||
async updateUser(fetchOptions, identifiers, customAttributes) { | ||
this.assertInitialized(this.statsigClient); | ||
const fetchOptionsWithDefaults = getOptionsWithDefaults(fetchOptions); | ||
const initializeValuesProducer = () => Fetcher.fetchExperimentValues(fetchOptionsWithDefaults, identifiers, customAttributes).then(({ | ||
experimentValues, | ||
customAttributes | ||
}) => ({ | ||
experimentValues, | ||
customAttributesFromFetch: customAttributes | ||
})); | ||
await this.updateUserUsingInitializeValuesProducer(initializeValuesProducer, identifiers, customAttributes); | ||
} | ||
/** | ||
* This method updates the user using the provider given on initialisation to get the new set of | ||
* values. | ||
* @param identifiers {Identifiers} | ||
* @param customAttributes {CustomAttributes} | ||
*/ | ||
async updateUserWithProvider(identifiers, customAttributes) { | ||
this.assertInitialized(this.statsigClient); | ||
if (!this.provider) { | ||
throw new Error('Cannot update user using provider as the client was not initialised with a provider'); | ||
} | ||
await this.provider.setProfile(this.initOptions, identifiers, customAttributes); | ||
await this.updateUserUsingInitializeValuesProducer(() => this.provider.getExperimentValues(), identifiers, customAttributes); | ||
} | ||
/** | ||
* This method updates the user given a new set of bootstrap values obtained from one of the | ||
* server-side SDKs. | ||
* | ||
* @param identifiers {Identifiers} | ||
* @param customAttributes {CustomAttributes} | ||
* @param initializeValues {Record<string,unknown>} | ||
*/ | ||
async updateUserWithValues(identifiers, customAttributes, initializeValues = {}) { | ||
this.assertInitialized(this.statsigClient); | ||
const initializeValuesProducer = () => Promise.resolve({ | ||
experimentValues: initializeValues, | ||
customAttributesFromFetch: customAttributes | ||
}); | ||
await this.updateUserUsingInitializeValuesProducer(initializeValuesProducer, identifiers, customAttributes); | ||
} | ||
initializeCalled() { | ||
return this.initPromise != null; | ||
} | ||
initializeCompleted() { | ||
return this.initCompleted; | ||
} | ||
/** | ||
* Returns the value for a feature gate. Returns false if there are errors. | ||
* @param {string} gateName - The name of the feature gate. | ||
* @param {Object} options | ||
* @param {boolean} options.fireGateExposure | ||
* Whether or not to fire the exposure event for the gate. Defaults to true. | ||
* To log an exposure event manually at a later time, use {@link Client.manuallyLogGateExposure} | ||
* (see [Statsig docs about manually logging exposures](https://docs.statsig.com/client/jsClientSDK#manual-exposures-)). | ||
*/ | ||
checkGate(gateName, options = {}) { | ||
try { | ||
this.assertInitialized(this.statsigClient); | ||
const { | ||
fireGateExposure = true | ||
} = options; | ||
return this.statsigClient.checkGate(gateName, { | ||
disableExposureLog: !fireGateExposure | ||
}); | ||
} catch (error) { | ||
// Log the first occurrence of the error | ||
if (!this.hasCheckGateErrorOccurred) { | ||
// eslint-disable-next-line no-console | ||
console.warn({ | ||
msg: 'An error has occurred checking the feature gate. Only the first occurrence of this error is logged.', | ||
gateName, | ||
error | ||
/** | ||
* Returns the value for a feature gate. Returns false if there are errors. | ||
* @param {string} gateName - The name of the feature gate. | ||
* @param {Object} options | ||
* @param {boolean} options.fireGateExposure | ||
* Whether or not to fire the exposure event for the gate. Defaults to true. | ||
* To log an exposure event manually at a later time, use {@link Client.manuallyLogGateExposure} | ||
* (see [Statsig docs about manually logging exposures](https://docs.statsig.com/client/jsClientSDK#manual-exposures-)). | ||
*/ | ||
}, { | ||
key: "checkGate", | ||
value: function checkGate(gateName) { | ||
var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; | ||
try { | ||
this.assertInitialized(this.statsigClient); | ||
var _options$fireGateExpo = options.fireGateExposure, | ||
fireGateExposure = _options$fireGateExpo === void 0 ? true : _options$fireGateExpo; | ||
return this.statsigClient.checkGate(gateName, { | ||
disableExposureLog: !fireGateExposure | ||
}); | ||
this.hasCheckGateErrorOccurred = true; | ||
} catch (error) { | ||
// Log the first occurrence of the error | ||
if (!this.hasCheckGateErrorOccurred) { | ||
// eslint-disable-next-line no-console | ||
console.warn({ | ||
msg: 'An error has occurred checking the feature gate. Only the first occurrence of this error is logged.', | ||
gateName: gateName, | ||
error: error | ||
}); | ||
this.hasCheckGateErrorOccurred = true; | ||
} | ||
return false; | ||
} | ||
return false; | ||
} | ||
} | ||
isGateExist(gateName) { | ||
try { | ||
this.assertInitialized(this.statsigClient); | ||
const gate = this.statsigClient.getFeatureGate(gateName, { | ||
disableExposureLog: true | ||
}); | ||
return !gate.details.reason.includes('Unrecognized'); | ||
} catch (error) { | ||
// eslint-disable-next-line no-console | ||
console.error(`Error occurred when trying to check FeatureGate: ${error}`); | ||
// in case of error report true to avoid false positives. | ||
return true; | ||
} | ||
} | ||
isExperimentExist(experimentName) { | ||
try { | ||
this.assertInitialized(this.statsigClient); | ||
const config = this.statsigClient.getExperiment(experimentName, { | ||
disableExposureLog: true | ||
}); | ||
return !config.details.reason.includes('Unrecognized'); | ||
} catch (error) { | ||
// eslint-disable-next-line no-console | ||
console.error(`Error occurred when trying to check Experiment: ${error}`); | ||
// in case of error report true to avoid false positives. | ||
return true; | ||
} | ||
} | ||
/** | ||
* Manually log a gate exposure (see [Statsig docs about manually logging exposures](https://docs.statsig.com/client/jsClientSDK#manual-exposures-)). | ||
* This is useful if you have evaluated a gate earlier via {@link Client.checkGate} where | ||
* <code>options.fireGateExposure</code> is false. | ||
* @param gateName | ||
*/ | ||
manuallyLogGateExposure(gateName) { | ||
this.assertInitialized(this.statsigClient); | ||
// This is the approach recommended in the docs | ||
// https://docs.statsig.com/client/javascript-sdk/#manual-exposures- | ||
this.statsigClient.checkGate(gateName); | ||
} | ||
/** | ||
* Returns the entire config for a given experiment. | ||
* | ||
* @param {string} experimentName - The name of the experiment | ||
* @param {Object} options | ||
* @param {boolean} options.fireExperimentExposure - Whether or not to fire the exposure event | ||
* for the experiment. Defaults to true. To log an exposure event manually at a later time, use | ||
* {@link Client.manuallyLogExperimentExposure} (see [Statsig docs about manually logging exposures](https://docs.statsig.com/client/jsClientSDK#manual-exposures-)). | ||
* @returns The config for an experiment | ||
* @example | ||
* ```ts | ||
* const experimentConfig = client.getExperiment('example-experiment-name'); | ||
* const backgroundColor: string = experimentConfig.get('backgroundColor', 'yellow'); | ||
* ``` | ||
*/ | ||
getExperiment(experimentName, options = {}) { | ||
try { | ||
this.assertInitialized(this.statsigClient); | ||
const { | ||
fireExperimentExposure = true | ||
} = options; | ||
return DynamicConfig.fromExperiment(this.statsigClient.getExperiment(experimentName, { | ||
disableExposureLog: !fireExperimentExposure | ||
})); | ||
} catch (error) { | ||
// Log the first occurrence of the error | ||
if (!this.hasGetExperimentErrorOccurred) { | ||
}, { | ||
key: "isGateExist", | ||
value: function isGateExist(gateName) { | ||
try { | ||
this.assertInitialized(this.statsigClient); | ||
var gate = this.statsigClient.getFeatureGate(gateName, { | ||
disableExposureLog: true | ||
}); | ||
return !gate.details.reason.includes('Unrecognized'); | ||
} catch (error) { | ||
// eslint-disable-next-line no-console | ||
console.warn({ | ||
msg: 'An error has occurred getting the experiment. Only the first occurrence of this error is logged.', | ||
experimentName, | ||
error | ||
}); | ||
this.hasGetExperimentErrorOccurred = true; | ||
console.error("Error occurred when trying to check FeatureGate: ".concat(error)); | ||
// in case of error report true to avoid false positives. | ||
return true; | ||
} | ||
// Return a default value | ||
return new DynamicConfig(experimentName, {}, '', { | ||
time: Date.now(), | ||
reason: EvaluationReason.Error | ||
}); | ||
} | ||
} | ||
/** | ||
* Returns the value of a given parameter in an experiment config. | ||
* | ||
* @template T | ||
* @param {string} experimentName - The name of the experiment | ||
* @param {string} parameterName - The name of the parameter to fetch from the experiment config | ||
* @param {T} defaultValue - The value to serve if the experiment or parameter do not exist, or | ||
* if the returned value does not match the expected type. | ||
* @param {Object} options | ||
* @param {boolean} options.fireExperimentExposure - Whether or not to fire the exposure event | ||
* for the experiment. Defaults to true. To log an exposure event manually at a later time, use | ||
* {@link Client.manuallyLogExperimentExposure} (see [Statsig docs about manually logging exposures](https://docs.statsig.com/client/jsClientSDK#manual-exposures-)) | ||
* @param {function} options.typeGuard - A function that asserts that the return value has the | ||
* expected type. If this function returns false, then the default value will be returned | ||
* instead. This can be set to protect your code from unexpected values being set remotely. By | ||
* default, this will be done by asserting that the default value and value are the same primitive | ||
* type. | ||
* @returns The value of the parameter if the experiment and parameter both exist, otherwise the | ||
* default value. | ||
* @example | ||
``` ts | ||
type ValidColor = 'blue' | 'red' | 'yellow'; | ||
type ValidColorTypeCheck = (value: unknown) => value is ValidColor; | ||
const isValidColor: ValidColorTypeCheck = | ||
(value: unknown) => typeof value === 'string' && ['blue', 'red', 'yellow'].includes(value); | ||
const buttonColor: ValidColor = client.getExperimentValue( | ||
'example-experiment-name', | ||
'backgroundColor', | ||
'yellow', | ||
{ | ||
typeGuard: isValidColor | ||
} | ||
); | ||
``` | ||
*/ | ||
getExperimentValue(experimentName, parameterName, defaultValue, options = {}) { | ||
const experiment = this.getExperiment(experimentName, options); | ||
try { | ||
const { | ||
typeGuard | ||
} = options; | ||
return experiment.get(parameterName, defaultValue, typeGuard); | ||
} catch (error) { | ||
// Log the first occurrence of the error | ||
if (!this.hasGetExperimentValueErrorOccurred) { | ||
}, { | ||
key: "isExperimentExist", | ||
value: function isExperimentExist(experimentName) { | ||
try { | ||
this.assertInitialized(this.statsigClient); | ||
var config = this.statsigClient.getExperiment(experimentName, { | ||
disableExposureLog: true | ||
}); | ||
return !config.details.reason.includes('Unrecognized'); | ||
} catch (error) { | ||
// eslint-disable-next-line no-console | ||
console.warn({ | ||
msg: 'An error has occurred getting the experiment value. Only the first occurrence of this error is logged.', | ||
experimentName, | ||
defaultValue, | ||
options, | ||
error | ||
}); | ||
this.hasGetExperimentValueErrorOccurred = true; | ||
console.error("Error occurred when trying to check Experiment: ".concat(error)); | ||
// in case of error report true to avoid false positives. | ||
return true; | ||
} | ||
return defaultValue; | ||
} | ||
} | ||
/** | ||
* Manually log an experiment exposure (see [Statsig docs about manually logging exposures](https://docs.statsig.com/client/jsClientSDK#manual-exposures-)). | ||
* This is useful if you have evaluated an experiment earlier via {@link Client.getExperimentValue} or | ||
* {@link Client.getExperiment} where <code>options.fireExperimentExposure</code> is false. | ||
* @param experimentName | ||
*/ | ||
manuallyLogExperimentExposure(experimentName) { | ||
this.assertInitialized(this.statsigClient); | ||
// This is the approach recommended in the docs | ||
// https://docs.statsig.com/client/javascript-sdk/#manual-exposures- | ||
this.statsigClient.getExperiment(experimentName); | ||
} | ||
/** | ||
* Manually log a layer exposure (see [Statsig docs about manually logging exposures](https://docs.statsig.com/client/jsClientSDK#manual-exposures-)). | ||
* This is useful if you have evaluated a layer earlier via {@link Client.getLayerValue} where <code>options.fireExperimentExposure</code> is false. | ||
* @param layerName | ||
* @param parameterName | ||
*/ | ||
manuallyLogLayerExposure(layerName, parameterName) { | ||
var _this$statsigClient$g; | ||
this.assertInitialized(this.statsigClient); | ||
// This is the approach recommended in the docs | ||
// https://docs.statsig.com/client/javascript-sdk/#manual-exposures- | ||
(_this$statsigClient$g = this.statsigClient.getLayer(layerName)) === null || _this$statsigClient$g === void 0 || _this$statsigClient$g.get(parameterName); | ||
} | ||
shutdownStatsig() { | ||
this.assertInitialized(this.statsigClient); | ||
this.statsigClient.shutdown(); | ||
} | ||
/** | ||
* Adds a new override for the given gate. | ||
* | ||
* This method is additive, meaning you can call it multiple times with different gate names to | ||
* build your full set of overrides. | ||
* | ||
* Overrides are persisted to the `STATSIG_OVERRIDES` key in localStorage, so they | ||
* will continue to affect every client that is initialized on the same domain after this method | ||
* is called. If you are using this API for testing purposes, you should call | ||
* {@link Client.clearGateOverride} after your tests are completed to remove this | ||
* localStorage entry. | ||
* | ||
* @param {string} gateName | ||
* @param {boolean} value | ||
*/ | ||
overrideGate(gateName, value) { | ||
this.overrideAdapter.overrideGate(gateName, value); | ||
// Trigger a reset of the memoized gate value | ||
if (this.user) { | ||
var _this$statsigClient; | ||
(_this$statsigClient = this.statsigClient) === null || _this$statsigClient === void 0 || _this$statsigClient.updateUserSync(this.user, { | ||
disableBackgroundCacheRefresh: true | ||
}); | ||
/** | ||
* Manually log a gate exposure (see [Statsig docs about manually logging exposures](https://docs.statsig.com/client/jsClientSDK#manual-exposures-)). | ||
* This is useful if you have evaluated a gate earlier via {@link Client.checkGate} where | ||
* <code>options.fireGateExposure</code> is false. | ||
* @param gateName | ||
*/ | ||
}, { | ||
key: "manuallyLogGateExposure", | ||
value: function manuallyLogGateExposure(gateName) { | ||
this.assertInitialized(this.statsigClient); | ||
// This is the approach recommended in the docs | ||
// https://docs.statsig.com/client/javascript-sdk/#manual-exposures- | ||
this.statsigClient.checkGate(gateName); | ||
} | ||
this.statsigValuesUpdated(); | ||
} | ||
/** | ||
* Removes any overrides that have been set for the given gate. | ||
*/ | ||
clearGateOverride(gateName) { | ||
this.overrideAdapter.removeGateOverride(gateName); | ||
this.statsigValuesUpdated(); | ||
} | ||
/** | ||
* Adds a new override for the given config (or experiment). | ||
* | ||
* This method is additive, meaning you can call it multiple times with different experiment | ||
* names to build your full set of overrides. | ||
* | ||
* Overrides are persisted to the `STATSIG_OVERRIDES` key in localStorage, so they | ||
* will continue to affect every client that is initialized on the same domain after this method | ||
* is called. If you are using this API for testing purposes, you should call | ||
* {@link Client.clearConfigOverride} after your tests are completed to remove this | ||
* localStorage entry. | ||
* | ||
* @param {string} experimentName | ||
* @param {object} values | ||
*/ | ||
overrideConfig(experimentName, values) { | ||
this.overrideAdapter.overrideDynamicConfig(experimentName, values); | ||
this.statsigValuesUpdated(); | ||
} | ||
/** | ||
* Removes any overrides that have been set for the given experiment. | ||
* @param {string} experimentName | ||
*/ | ||
clearConfigOverride(experimentName) { | ||
this.overrideAdapter.removeDynamicConfigOverride(experimentName); | ||
this.statsigValuesUpdated(); | ||
} | ||
/** | ||
* Set overrides for gates, experiments and layers in batch. | ||
* | ||
* Note that these overrides are **not** additive and will completely replace any that have been | ||
* added via prior calls to {@link Client.overrideConfig} or | ||
* {@link Client.overrideGate}. | ||
* | ||
* Overrides are persisted to the `STATSIG_OVERRIDES` key in localStorage, so they | ||
* will continue to affect every client that is initialized on the same domain after this method | ||
* is called. If you are using this API for testing purposes, you should call | ||
* {@link Client.clearAllOverrides} after your tests are completed to remove this | ||
* localStorage entry. | ||
*/ | ||
setOverrides(overrides) { | ||
this.overrideAdapter.setOverrides(overrides); | ||
this.statsigValuesUpdated(); | ||
} | ||
/** | ||
* @returns The current overrides for gates, configs (including experiments) and layers. | ||
*/ | ||
getOverrides() { | ||
return this.overrideAdapter.getOverrides(); | ||
} | ||
/** | ||
* Clears overrides for all gates, configs (including experiments) and layers. | ||
*/ | ||
clearAllOverrides() { | ||
this.overrideAdapter.removeAllOverrides(); | ||
this.statsigValuesUpdated(); | ||
} | ||
/** | ||
* Returns whether the given identifiers and customAttributes align with the current | ||
* set that is being used by the client. | ||
* | ||
* If this method returns false, then the {@link Client.updateUser}, | ||
* {@link Client.updateUserWithValues} or {@link Client.updateUserWithProvider} | ||
* methods can be used to re-align these values. | ||
* | ||
* @param identifiers | ||
* @param customAttributes | ||
* @returns a flag indicating whether the clients current configuration aligns with the given values | ||
*/ | ||
isCurrentUser(identifiers, customAttributes) { | ||
return shallowEquals(this.currentIdentifiers, identifiers) && shallowEquals(this.currentAttributes, customAttributes); | ||
} | ||
/** | ||
* Subscribe to updates where the given callback will be called with the current checkGate value | ||
* @param gateName | ||
* @param callback | ||
* @param options | ||
* @returns off function to unsubscribe from updates | ||
*/ | ||
onGateUpdated(gateName, callback, options = {}) { | ||
const wrapCallback = value => { | ||
const { | ||
fireGateExposure = true | ||
} = options; | ||
if (fireGateExposure) { | ||
this.manuallyLogGateExposure(gateName); | ||
} | ||
/** | ||
* Returns the entire config for a given experiment. | ||
* | ||
* @param {string} experimentName - The name of the experiment | ||
* @param {Object} options | ||
* @param {boolean} options.fireExperimentExposure - Whether or not to fire the exposure event | ||
* for the experiment. Defaults to true. To log an exposure event manually at a later time, use | ||
* {@link Client.manuallyLogExperimentExposure} (see [Statsig docs about manually logging exposures](https://docs.statsig.com/client/jsClientSDK#manual-exposures-)). | ||
* @returns The config for an experiment | ||
* @example | ||
* ```ts | ||
* const experimentConfig = client.getExperiment('example-experiment-name'); | ||
* const backgroundColor: string = experimentConfig.get('backgroundColor', 'yellow'); | ||
* ``` | ||
*/ | ||
}, { | ||
key: "getExperiment", | ||
value: function getExperiment(experimentName) { | ||
var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; | ||
try { | ||
callback(value); | ||
this.assertInitialized(this.statsigClient); | ||
var _options$fireExperime = options.fireExperimentExposure, | ||
fireExperimentExposure = _options$fireExperime === void 0 ? true : _options$fireExperime; | ||
return _DynamicConfig.DynamicConfig.fromExperiment(this.statsigClient.getExperiment(experimentName, { | ||
disableExposureLog: !fireExperimentExposure | ||
})); | ||
} catch (error) { | ||
// eslint-disable-next-line no-console | ||
console.warn(`Error calling callback for gate ${gateName} with value ${value}`, error); | ||
// Log the first occurrence of the error | ||
if (!this.hasGetExperimentErrorOccurred) { | ||
// eslint-disable-next-line no-console | ||
console.warn({ | ||
msg: 'An error has occurred getting the experiment. Only the first occurrence of this error is logged.', | ||
experimentName: experimentName, | ||
error: error | ||
}); | ||
this.hasGetExperimentErrorOccurred = true; | ||
} | ||
// Return a default value | ||
return new _DynamicConfig.DynamicConfig(experimentName, {}, '', { | ||
time: Date.now(), | ||
reason: _types.EvaluationReason.Error | ||
}); | ||
} | ||
}; | ||
return this.subscriptions.onGateUpdated(gateName, wrapCallback, this.checkGate.bind(this), options); | ||
} | ||
} | ||
/** | ||
* Subscribe to updates where the given callback will be called with the current experiment value | ||
* @param experimentName | ||
* @param parameterName | ||
* @param defaultValue | ||
* @param callback | ||
* @param options | ||
* @returns off function to unsubscribe from updates | ||
*/ | ||
onExperimentValueUpdated(experimentName, parameterName, defaultValue, callback, options = {}) { | ||
const wrapCallback = value => { | ||
const { | ||
fireExperimentExposure = true | ||
} = options; | ||
if (fireExperimentExposure) { | ||
this.manuallyLogExperimentExposure(experimentName); | ||
} | ||
/** | ||
* Returns the value of a given parameter in an experiment config. | ||
* | ||
* @template T | ||
* @param {string} experimentName - The name of the experiment | ||
* @param {string} parameterName - The name of the parameter to fetch from the experiment config | ||
* @param {T} defaultValue - The value to serve if the experiment or parameter do not exist, or | ||
* if the returned value does not match the expected type. | ||
* @param {Object} options | ||
* @param {boolean} options.fireExperimentExposure - Whether or not to fire the exposure event | ||
* for the experiment. Defaults to true. To log an exposure event manually at a later time, use | ||
* {@link Client.manuallyLogExperimentExposure} (see [Statsig docs about manually logging exposures](https://docs.statsig.com/client/jsClientSDK#manual-exposures-)) | ||
* @param {function} options.typeGuard - A function that asserts that the return value has the | ||
* expected type. If this function returns false, then the default value will be returned | ||
* instead. This can be set to protect your code from unexpected values being set remotely. By | ||
* default, this will be done by asserting that the default value and value are the same primitive | ||
* type. | ||
* @returns The value of the parameter if the experiment and parameter both exist, otherwise the | ||
* default value. | ||
* @example | ||
``` ts | ||
type ValidColor = 'blue' | 'red' | 'yellow'; | ||
type ValidColorTypeCheck = (value: unknown) => value is ValidColor; | ||
const isValidColor: ValidColorTypeCheck = | ||
(value: unknown) => typeof value === 'string' && ['blue', 'red', 'yellow'].includes(value); | ||
const buttonColor: ValidColor = client.getExperimentValue( | ||
'example-experiment-name', | ||
'backgroundColor', | ||
'yellow', | ||
{ | ||
typeGuard: isValidColor | ||
} | ||
); | ||
``` | ||
*/ | ||
}, { | ||
key: "getExperimentValue", | ||
value: function getExperimentValue(experimentName, parameterName, defaultValue) { | ||
var options = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : {}; | ||
var experiment = this.getExperiment(experimentName, options); | ||
try { | ||
callback(value); | ||
var typeGuard = options.typeGuard; | ||
return experiment.get(parameterName, defaultValue, typeGuard); | ||
} catch (error) { | ||
// eslint-disable-next-line no-console | ||
console.warn(`Error calling callback for experiment ${experimentName} with value ${value}`, error); | ||
// Log the first occurrence of the error | ||
if (!this.hasGetExperimentValueErrorOccurred) { | ||
// eslint-disable-next-line no-console | ||
console.warn({ | ||
msg: 'An error has occurred getting the experiment value. Only the first occurrence of this error is logged.', | ||
experimentName: experimentName, | ||
defaultValue: defaultValue, | ||
options: options, | ||
error: error | ||
}); | ||
this.hasGetExperimentValueErrorOccurred = true; | ||
} | ||
return defaultValue; | ||
} | ||
}; | ||
return this.subscriptions.onExperimentValueUpdated(experimentName, parameterName, defaultValue, wrapCallback, this.getExperimentValue.bind(this), options); | ||
} | ||
} | ||
/** | ||
* Subscribe so on any update the callback will be called. | ||
* NOTE: The callback will be called whenever the values are updated even if the values have not | ||
* changed. | ||
* @param callback | ||
* @returns off function to unsubscribe from updates | ||
*/ | ||
onAnyUpdated(callback) { | ||
return this.subscriptions.onAnyUpdated(callback); | ||
} | ||
/** | ||
* Manually log an experiment exposure (see [Statsig docs about manually logging exposures](https://docs.statsig.com/client/jsClientSDK#manual-exposures-)). | ||
* This is useful if you have evaluated an experiment earlier via {@link Client.getExperimentValue} or | ||
* {@link Client.getExperiment} where <code>options.fireExperimentExposure</code> is false. | ||
* @param experimentName | ||
*/ | ||
}, { | ||
key: "manuallyLogExperimentExposure", | ||
value: function manuallyLogExperimentExposure(experimentName) { | ||
this.assertInitialized(this.statsigClient); | ||
// This is the approach recommended in the docs | ||
// https://docs.statsig.com/client/javascript-sdk/#manual-exposures- | ||
this.statsigClient.getExperiment(experimentName); | ||
} | ||
/** | ||
* This method initializes the client using a network call to fetch the bootstrap values for the | ||
* given user. | ||
* | ||
* @param clientOptions | ||
* @param identifiers | ||
* @param customAttributes | ||
* @private | ||
*/ | ||
async init(clientOptions, identifiers, customAttributes) { | ||
const fromValuesClientOptions = _objectSpread({}, clientOptions); | ||
let experimentValues; | ||
let customAttributesFromResult; | ||
try { | ||
// If client sdk key fetch fails, an error would be thrown and handled instead of waiting for | ||
// the experiment values request to be settled, and it will fall back to use default values. | ||
const clientSdkKeyPromise = Fetcher.fetchClientSdk(clientOptions).then(value => fromValuesClientOptions.sdkKey = value.clientSdkKey); | ||
const experimentValuesPromise = Fetcher.fetchExperimentValues(clientOptions, identifiers, customAttributes); | ||
/** | ||
* Manually log a layer exposure (see [Statsig docs about manually logging exposures](https://docs.statsig.com/client/jsClientSDK#manual-exposures-)). | ||
* This is useful if you have evaluated a layer earlier via {@link Client.getLayerValue} where <code>options.fireExperimentExposure</code> is false. | ||
* @param layerName | ||
* @param parameterName | ||
*/ | ||
}, { | ||
key: "manuallyLogLayerExposure", | ||
value: function manuallyLogLayerExposure(layerName, parameterName) { | ||
var _this$statsigClient$g; | ||
this.assertInitialized(this.statsigClient); | ||
// This is the approach recommended in the docs | ||
// https://docs.statsig.com/client/javascript-sdk/#manual-exposures- | ||
(_this$statsigClient$g = this.statsigClient.getLayer(layerName)) === null || _this$statsigClient$g === void 0 || _this$statsigClient$g.get(parameterName); | ||
} | ||
}, { | ||
key: "shutdownStatsig", | ||
value: function shutdownStatsig() { | ||
this.assertInitialized(this.statsigClient); | ||
this.statsigClient.shutdown(); | ||
} | ||
// Only wait for the experiment values request to finish and try to initialise the client | ||
// with experiment values if both requests are successful. Else an error would be thrown and | ||
// handled by the catch | ||
const [, experimentValuesResult] = await Promise.all([clientSdkKeyPromise, experimentValuesPromise]); | ||
experimentValues = experimentValuesResult.experimentValues; | ||
customAttributesFromResult = experimentValuesResult.customAttributes; | ||
} catch (error) { | ||
if (error instanceof Error) { | ||
// eslint-disable-next-line no-console | ||
console.error(`Error occurred when trying to fetch the Feature Gates client values, error: ${error === null || error === void 0 ? void 0 : error.message}`); | ||
/** | ||
* Adds a new override for the given gate. | ||
* | ||
* This method is additive, meaning you can call it multiple times with different gate names to | ||
* build your full set of overrides. | ||
* | ||
* Overrides are persisted to the `STATSIG_OVERRIDES` key in localStorage, so they | ||
* will continue to affect every client that is initialized on the same domain after this method | ||
* is called. If you are using this API for testing purposes, you should call | ||
* {@link Client.clearGateOverride} after your tests are completed to remove this | ||
* localStorage entry. | ||
* | ||
* @param {string} gateName | ||
* @param {boolean} value | ||
*/ | ||
}, { | ||
key: "overrideGate", | ||
value: function overrideGate(gateName, value) { | ||
this.overrideAdapter.overrideGate(gateName, value); | ||
// Trigger a reset of the memoized gate value | ||
if (this.user) { | ||
var _this$statsigClient; | ||
(_this$statsigClient = this.statsigClient) === null || _this$statsigClient === void 0 || _this$statsigClient.updateUserSync(this.user, { | ||
disableBackgroundCacheRefresh: true | ||
}); | ||
} | ||
// eslint-disable-next-line no-console | ||
console.warn(`Initialising Statsig client without values`); | ||
await this.initFromValues(fromValuesClientOptions, identifiers, customAttributes); | ||
throw error; | ||
this.statsigValuesUpdated(); | ||
} | ||
return this.initFromValues(fromValuesClientOptions, identifiers, customAttributesFromResult, experimentValues); | ||
} | ||
async initWithProvider(baseClientOptions, provider, identifiers, customAttributes) { | ||
const fromValuesClientOptions = _objectSpread(_objectSpread({}, baseClientOptions), {}, { | ||
disableCurrentPageLogging: true | ||
}); | ||
let experimentValues; | ||
let customAttributesFromResult; | ||
try { | ||
await provider.setProfile(baseClientOptions, identifiers, customAttributes); | ||
// If client sdk key fetch fails, an error would be thrown and handled instead of waiting for | ||
// the experiment values request to be settled, and it will fall back to use default values. | ||
const clientSdkKeyPromise = provider.getClientSdkKey().then(value => fromValuesClientOptions.sdkKey = value); | ||
const experimentValuesPromise = provider.getExperimentValues(); | ||
/** | ||
* Removes any overrides that have been set for the given gate. | ||
*/ | ||
}, { | ||
key: "clearGateOverride", | ||
value: function clearGateOverride(gateName) { | ||
this.overrideAdapter.removeGateOverride(gateName); | ||
this.statsigValuesUpdated(); | ||
} | ||
// Only wait for the experiment values request to finish and try to initialise the client | ||
// with experiment values if both requests are successful. Else an error would be thrown and | ||
// handled by the catch | ||
const [, experimentValuesResult] = await Promise.all([clientSdkKeyPromise, experimentValuesPromise]); | ||
experimentValues = experimentValuesResult.experimentValues; | ||
customAttributesFromResult = experimentValuesResult.customAttributesFromFetch; | ||
} catch (error) { | ||
if (error instanceof Error) { | ||
// eslint-disable-next-line no-console | ||
console.error(`Error occurred when trying to fetch the Feature Gates client values, error: ${error === null || error === void 0 ? void 0 : error.message}`); | ||
} | ||
// eslint-disable-next-line no-console | ||
console.warn(`Initialising Statsig client without values`); | ||
await this.initFromValues(fromValuesClientOptions, identifiers, customAttributes); | ||
throw error; | ||
/** | ||
* Adds a new override for the given config (or experiment). | ||
* | ||
* This method is additive, meaning you can call it multiple times with different experiment | ||
* names to build your full set of overrides. | ||
* | ||
* Overrides are persisted to the `STATSIG_OVERRIDES` key in localStorage, so they | ||
* will continue to affect every client that is initialized on the same domain after this method | ||
* is called. If you are using this API for testing purposes, you should call | ||
* {@link Client.clearConfigOverride} after your tests are completed to remove this | ||
* localStorage entry. | ||
* | ||
* @param {string} experimentName | ||
* @param {object} values | ||
*/ | ||
}, { | ||
key: "overrideConfig", | ||
value: function overrideConfig(experimentName, values) { | ||
this.overrideAdapter.overrideDynamicConfig(experimentName, values); | ||
this.statsigValuesUpdated(); | ||
} | ||
return this.initFromValues(fromValuesClientOptions, identifiers, customAttributesFromResult, experimentValues); | ||
} | ||
/** | ||
* This method initializes the client using a set of boostrap values obtained from one of the | ||
* server-side SDKs. | ||
* | ||
* @param clientOptions | ||
* @param identifiers | ||
* @param customAttributes | ||
* @param initializeValues | ||
* @private | ||
*/ | ||
async initFromValues(clientOptions, identifiers, customAttributes, initializeValues = {}) { | ||
var _newClientOptions$net; | ||
this.overrideAdapter.initFromStoredOverrides(); | ||
this.currentIdentifiers = identifiers; | ||
this.currentAttributes = customAttributes; | ||
const newClientOptions = migrateInitializationOptions(clientOptions); | ||
if (!newClientOptions.sdkKey) { | ||
newClientOptions.sdkKey = DEFAULT_CLIENT_KEY; | ||
/** | ||
* Removes any overrides that have been set for the given experiment. | ||
* @param {string} experimentName | ||
*/ | ||
}, { | ||
key: "clearConfigOverride", | ||
value: function clearConfigOverride(experimentName) { | ||
this.overrideAdapter.removeDynamicConfigOverride(experimentName); | ||
this.statsigValuesUpdated(); | ||
} | ||
if (!((_newClientOptions$net = newClientOptions.networkConfig) !== null && _newClientOptions$net !== void 0 && _newClientOptions$net.logEventUrl)) { | ||
newClientOptions.networkConfig = _objectSpread(_objectSpread({}, newClientOptions.networkConfig), {}, { | ||
logEventUrl: DEFAULT_EVENT_LOGGING_API | ||
}); | ||
/** | ||
* Set overrides for gates, experiments and layers in batch. | ||
* | ||
* Note that these overrides are **not** additive and will completely replace any that have been | ||
* added via prior calls to {@link Client.overrideConfig} or | ||
* {@link Client.overrideGate}. | ||
* | ||
* Overrides are persisted to the `STATSIG_OVERRIDES` key in localStorage, so they | ||
* will continue to affect every client that is initialized on the same domain after this method | ||
* is called. If you are using this API for testing purposes, you should call | ||
* {@link Client.clearAllOverrides} after your tests are completed to remove this | ||
* localStorage entry. | ||
*/ | ||
}, { | ||
key: "setOverrides", | ||
value: function setOverrides(overrides) { | ||
this.overrideAdapter.setOverrides(overrides); | ||
this.statsigValuesUpdated(); | ||
} | ||
if (newClientOptions.perimeter === PerimeterType.FEDRAMP_MODERATE) { | ||
// disable all logging in FedRAMP to prevent egress of sensitive data | ||
newClientOptions.disableLogging = true; | ||
/** | ||
* @returns The current overrides for gates, configs (including experiments) and layers. | ||
*/ | ||
}, { | ||
key: "getOverrides", | ||
value: function getOverrides() { | ||
return this.overrideAdapter.getOverrides(); | ||
} | ||
const { | ||
sdkKey, | ||
environment, | ||
updateUserCompletionCallback: _updateUserCompletionCallback, | ||
perimeter: _perimeter | ||
} = newClientOptions, | ||
restClientOptions = _objectWithoutProperties(newClientOptions, _excluded); | ||
this.sdkKey = sdkKey; | ||
this.user = toStatsigUser(identifiers, customAttributes, this.sdkKey); | ||
const statsigOptions = _objectSpread(_objectSpread({}, restClientOptions), {}, { | ||
environment: { | ||
tier: environment | ||
}, | ||
includeCurrentPageUrlWithEvents: false, | ||
dataAdapter: this.dataAdapter, | ||
overrideAdapter: this.overrideAdapter | ||
}); | ||
try { | ||
this.statsigClient = new StatsigClient(sdkKey, this.user, statsigOptions); | ||
this.dataAdapter.setBootstrapData(initializeValues); | ||
await this.statsigClient.initializeAsync(); | ||
} catch (error) { | ||
if (error instanceof Error) { | ||
// eslint-disable-next-line no-console | ||
console.error(`Error occurred when trying to initialise the Statsig client, error: ${error === null || error === void 0 ? void 0 : error.message}`); | ||
} | ||
// eslint-disable-next-line no-console | ||
console.warn(`Initialising Statsig client with default sdk key and without values`); | ||
this.statsigClient = new StatsigClient(DEFAULT_CLIENT_KEY, this.user, statsigOptions); | ||
this.dataAdapter.setBootstrapData(); | ||
await this.statsigClient.initializeAsync(); | ||
this.initWithDefaults = true; | ||
throw error; | ||
/** | ||
* Clears overrides for all gates, configs (including experiments) and layers. | ||
*/ | ||
}, { | ||
key: "clearAllOverrides", | ||
value: function clearAllOverrides() { | ||
this.overrideAdapter.removeAllOverrides(); | ||
this.statsigValuesUpdated(); | ||
} | ||
} | ||
/** | ||
* This method updates the user for this client with the bootstrap values returned from a given | ||
* Promise. | ||
* It uses the customAttributes from fetching experiment values to update the Statsig user but | ||
* uses the customAttributes from given input to check if the user has changed. | ||
* | ||
* @param {Identifiers} identifiers | ||
* @param {CustomAttributes} customAttributes | ||
* @param {Promise<InitializeValues>} getInitializeValues | ||
* @private | ||
*/ | ||
async updateUserUsingInitializeValuesProducer(getInitializeValues, identifiers, customAttributes) { | ||
this.assertInitialized(this.statsigClient); | ||
if (!this.initPromise) { | ||
throw new Error('The client must be initialized before you can update the user.'); | ||
/** | ||
* Returns whether the given identifiers and customAttributes align with the current | ||
* set that is being used by the client. | ||
* | ||
* If this method returns false, then the {@link Client.updateUser}, | ||
* {@link Client.updateUserWithValues} or {@link Client.updateUserWithProvider} | ||
* methods can be used to re-align these values. | ||
* | ||
* @param identifiers | ||
* @param customAttributes | ||
* @returns a flag indicating whether the clients current configuration aligns with the given values | ||
*/ | ||
}, { | ||
key: "isCurrentUser", | ||
value: function isCurrentUser(identifiers, customAttributes) { | ||
return (0, _utils.shallowEquals)(this.currentIdentifiers, identifiers) && (0, _utils.shallowEquals)(this.currentAttributes, customAttributes); | ||
} | ||
// If the user isn't changing at all, then exit immediately | ||
if (this.isCurrentUser(identifiers, customAttributes)) { | ||
return this.initPromise; | ||
/** | ||
* Subscribe to updates where the given callback will be called with the current checkGate value | ||
* @param gateName | ||
* @param callback | ||
* @param options | ||
* @returns off function to unsubscribe from updates | ||
*/ | ||
}, { | ||
key: "onGateUpdated", | ||
value: function onGateUpdated(gateName, callback) { | ||
var _this7 = this; | ||
var options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {}; | ||
var wrapCallback = function wrapCallback(value) { | ||
var _options$fireGateExpo2 = options.fireGateExposure, | ||
fireGateExposure = _options$fireGateExpo2 === void 0 ? true : _options$fireGateExpo2; | ||
if (fireGateExposure) { | ||
_this7.manuallyLogGateExposure(gateName); | ||
} | ||
try { | ||
callback(value); | ||
} catch (error) { | ||
// eslint-disable-next-line no-console | ||
console.warn("Error calling callback for gate ".concat(gateName, " with value ").concat(value), error); | ||
} | ||
}; | ||
return this.subscriptions.onGateUpdated(gateName, wrapCallback, this.checkGate.bind(this), options); | ||
} | ||
// Wait for the current initialize/update to finish | ||
const originalInitPromise = this.initPromise; | ||
try { | ||
await this.initPromise; | ||
} catch (err) { | ||
// Proceed with the user update even if the init failed, since this update | ||
// may put the client back into a valid state. | ||
/** | ||
* Subscribe to updates where the given callback will be called with the current experiment value | ||
* @param experimentName | ||
* @param parameterName | ||
* @param defaultValue | ||
* @param callback | ||
* @param options | ||
* @returns off function to unsubscribe from updates | ||
*/ | ||
}, { | ||
key: "onExperimentValueUpdated", | ||
value: function onExperimentValueUpdated(experimentName, parameterName, defaultValue, callback) { | ||
var _this8 = this; | ||
var options = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : {}; | ||
var wrapCallback = function wrapCallback(value) { | ||
var _options$fireExperime2 = options.fireExperimentExposure, | ||
fireExperimentExposure = _options$fireExperime2 === void 0 ? true : _options$fireExperime2; | ||
if (fireExperimentExposure) { | ||
_this8.manuallyLogExperimentExposure(experimentName); | ||
} | ||
try { | ||
callback(value); | ||
} catch (error) { | ||
// eslint-disable-next-line no-console | ||
console.warn("Error calling callback for experiment ".concat(experimentName, " with value ").concat(value), error); | ||
} | ||
}; | ||
return this.subscriptions.onExperimentValueUpdated(experimentName, parameterName, defaultValue, wrapCallback, this.getExperimentValue.bind(this), options); | ||
} | ||
const initializeValuesPromise = getInitializeValues(); | ||
const updateUserPromise = this.updateStatsigClientUser(initializeValuesPromise, identifiers, customAttributes); | ||
// We replace the init promise here since we are essentially re-initializing the client at this | ||
// point. Any subsequent calls to await client.initialize() or client.updateUser() | ||
// will now also await this user update. | ||
this.initPromise = updateUserPromise.catch(async () => { | ||
// If the update failed then it changed nothing, so revert back to the original promise. | ||
this.initPromise = originalInitPromise; | ||
/** | ||
* Subscribe so on any update the callback will be called. | ||
* NOTE: The callback will be called whenever the values are updated even if the values have not | ||
* changed. | ||
* @param callback | ||
* @returns off function to unsubscribe from updates | ||
*/ | ||
}, { | ||
key: "onAnyUpdated", | ||
value: function onAnyUpdated(callback) { | ||
return this.subscriptions.onAnyUpdated(callback); | ||
} | ||
// Set the user profile again to revert back to the current user | ||
if (this.provider) { | ||
await this.provider.setProfile(this.initOptions, this.currentIdentifiers, this.currentAttributes); | ||
/** | ||
* This method initializes the client using a network call to fetch the bootstrap values for the | ||
* given user. | ||
* | ||
* @param clientOptions | ||
* @param identifiers | ||
* @param customAttributes | ||
* @private | ||
*/ | ||
}, { | ||
key: "init", | ||
value: (function () { | ||
var _init = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee7(clientOptions, identifiers, customAttributes) { | ||
var fromValuesClientOptions, experimentValues, customAttributesFromResult, clientSdkKeyPromise, experimentValuesPromise, _yield$Promise$all, _yield$Promise$all2, experimentValuesResult; | ||
return _regenerator.default.wrap(function _callee7$(_context7) { | ||
while (1) switch (_context7.prev = _context7.next) { | ||
case 0: | ||
fromValuesClientOptions = _objectSpread({}, clientOptions); | ||
_context7.prev = 1; | ||
// If client sdk key fetch fails, an error would be thrown and handled instead of waiting for | ||
// the experiment values request to be settled, and it will fall back to use default values. | ||
clientSdkKeyPromise = _fetcher.default.fetchClientSdk(clientOptions).then(function (value) { | ||
return fromValuesClientOptions.sdkKey = value.clientSdkKey; | ||
}); | ||
experimentValuesPromise = _fetcher.default.fetchExperimentValues(clientOptions, identifiers, customAttributes); // Only wait for the experiment values request to finish and try to initialise the client | ||
// with experiment values if both requests are successful. Else an error would be thrown and | ||
// handled by the catch | ||
_context7.next = 6; | ||
return Promise.all([clientSdkKeyPromise, experimentValuesPromise]); | ||
case 6: | ||
_yield$Promise$all = _context7.sent; | ||
_yield$Promise$all2 = (0, _slicedToArray2.default)(_yield$Promise$all, 2); | ||
experimentValuesResult = _yield$Promise$all2[1]; | ||
experimentValues = experimentValuesResult.experimentValues; | ||
customAttributesFromResult = experimentValuesResult.customAttributes; | ||
_context7.next = 20; | ||
break; | ||
case 13: | ||
_context7.prev = 13; | ||
_context7.t0 = _context7["catch"](1); | ||
if (_context7.t0 instanceof Error) { | ||
// eslint-disable-next-line no-console | ||
console.error("Error occurred when trying to fetch the Feature Gates client values, error: ".concat(_context7.t0 === null || _context7.t0 === void 0 ? void 0 : _context7.t0.message)); | ||
} | ||
// eslint-disable-next-line no-console | ||
console.warn("Initialising Statsig client without values"); | ||
_context7.next = 19; | ||
return this.initFromValues(fromValuesClientOptions, identifiers, customAttributes); | ||
case 19: | ||
throw _context7.t0; | ||
case 20: | ||
return _context7.abrupt("return", this.initFromValues(fromValuesClientOptions, identifiers, customAttributesFromResult, experimentValues)); | ||
case 21: | ||
case "end": | ||
return _context7.stop(); | ||
} | ||
}, _callee7, this, [[1, 13]]); | ||
})); | ||
function init(_x18, _x19, _x20) { | ||
return _init.apply(this, arguments); | ||
} | ||
}); | ||
return updateUserPromise; | ||
} | ||
return init; | ||
}()) | ||
}, { | ||
key: "initWithProvider", | ||
value: function () { | ||
var _initWithProvider = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee8(baseClientOptions, provider, identifiers, customAttributes) { | ||
var fromValuesClientOptions, experimentValues, customAttributesFromResult, clientSdkKeyPromise, experimentValuesPromise, _yield$Promise$all3, _yield$Promise$all4, experimentValuesResult; | ||
return _regenerator.default.wrap(function _callee8$(_context8) { | ||
while (1) switch (_context8.prev = _context8.next) { | ||
case 0: | ||
fromValuesClientOptions = _objectSpread(_objectSpread({}, baseClientOptions), {}, { | ||
disableCurrentPageLogging: true | ||
}); | ||
_context8.prev = 1; | ||
_context8.next = 4; | ||
return provider.setProfile(baseClientOptions, identifiers, customAttributes); | ||
case 4: | ||
// If client sdk key fetch fails, an error would be thrown and handled instead of waiting for | ||
// the experiment values request to be settled, and it will fall back to use default values. | ||
clientSdkKeyPromise = provider.getClientSdkKey().then(function (value) { | ||
return fromValuesClientOptions.sdkKey = value; | ||
}); | ||
experimentValuesPromise = provider.getExperimentValues(); // Only wait for the experiment values request to finish and try to initialise the client | ||
// with experiment values if both requests are successful. Else an error would be thrown and | ||
// handled by the catch | ||
_context8.next = 8; | ||
return Promise.all([clientSdkKeyPromise, experimentValuesPromise]); | ||
case 8: | ||
_yield$Promise$all3 = _context8.sent; | ||
_yield$Promise$all4 = (0, _slicedToArray2.default)(_yield$Promise$all3, 2); | ||
experimentValuesResult = _yield$Promise$all4[1]; | ||
experimentValues = experimentValuesResult.experimentValues; | ||
customAttributesFromResult = experimentValuesResult.customAttributesFromFetch; | ||
_context8.next = 22; | ||
break; | ||
case 15: | ||
_context8.prev = 15; | ||
_context8.t0 = _context8["catch"](1); | ||
if (_context8.t0 instanceof Error) { | ||
// eslint-disable-next-line no-console | ||
console.error("Error occurred when trying to fetch the Feature Gates client values, error: ".concat(_context8.t0 === null || _context8.t0 === void 0 ? void 0 : _context8.t0.message)); | ||
} | ||
// eslint-disable-next-line no-console | ||
console.warn("Initialising Statsig client without values"); | ||
_context8.next = 21; | ||
return this.initFromValues(fromValuesClientOptions, identifiers, customAttributes); | ||
case 21: | ||
throw _context8.t0; | ||
case 22: | ||
return _context8.abrupt("return", this.initFromValues(fromValuesClientOptions, identifiers, customAttributesFromResult, experimentValues)); | ||
case 23: | ||
case "end": | ||
return _context8.stop(); | ||
} | ||
}, _callee8, this, [[1, 15]]); | ||
})); | ||
function initWithProvider(_x21, _x22, _x23, _x24) { | ||
return _initWithProvider.apply(this, arguments); | ||
} | ||
return initWithProvider; | ||
}() | ||
/** | ||
* This method initializes the client using a set of boostrap values obtained from one of the | ||
* server-side SDKs. | ||
* | ||
* @param clientOptions | ||
* @param identifiers | ||
* @param customAttributes | ||
* @param initializeValues | ||
* @private | ||
*/ | ||
}, { | ||
key: "initFromValues", | ||
value: (function () { | ||
var _initFromValues = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee9(clientOptions, identifiers, customAttributes) { | ||
var _newClientOptions$net; | ||
var initializeValues, | ||
newClientOptions, | ||
sdkKey, | ||
environment, | ||
_updateUserCompletionCallback, | ||
_perimeter, | ||
restClientOptions, | ||
statsigOptions, | ||
_args9 = arguments; | ||
return _regenerator.default.wrap(function _callee9$(_context9) { | ||
while (1) switch (_context9.prev = _context9.next) { | ||
case 0: | ||
initializeValues = _args9.length > 3 && _args9[3] !== undefined ? _args9[3] : {}; | ||
this.overrideAdapter.initFromStoredOverrides(); | ||
this.currentIdentifiers = identifiers; | ||
this.currentAttributes = customAttributes; | ||
newClientOptions = (0, _utils.migrateInitializationOptions)(clientOptions); | ||
if (!newClientOptions.sdkKey) { | ||
newClientOptions.sdkKey = DEFAULT_CLIENT_KEY; | ||
} | ||
if (!((_newClientOptions$net = newClientOptions.networkConfig) !== null && _newClientOptions$net !== void 0 && _newClientOptions$net.logEventUrl)) { | ||
newClientOptions.networkConfig = _objectSpread(_objectSpread({}, newClientOptions.networkConfig), {}, { | ||
logEventUrl: DEFAULT_EVENT_LOGGING_API | ||
}); | ||
} | ||
if (newClientOptions.perimeter === _types2.PerimeterType.FEDRAMP_MODERATE) { | ||
// disable all logging in FedRAMP to prevent egress of sensitive data | ||
newClientOptions.disableLogging = true; | ||
} | ||
sdkKey = newClientOptions.sdkKey, environment = newClientOptions.environment, _updateUserCompletionCallback = newClientOptions.updateUserCompletionCallback, _perimeter = newClientOptions.perimeter, restClientOptions = (0, _objectWithoutProperties2.default)(newClientOptions, _excluded); | ||
this.sdkKey = sdkKey; | ||
this.user = (0, _utils.toStatsigUser)(identifiers, customAttributes, this.sdkKey); | ||
statsigOptions = _objectSpread(_objectSpread({}, restClientOptions), {}, { | ||
environment: { | ||
tier: environment | ||
}, | ||
includeCurrentPageUrlWithEvents: false, | ||
dataAdapter: this.dataAdapter, | ||
overrideAdapter: this.overrideAdapter | ||
}); | ||
_context9.prev = 12; | ||
this.statsigClient = new _jsClient.StatsigClient(sdkKey, this.user, statsigOptions); | ||
this.dataAdapter.setBootstrapData(initializeValues); | ||
_context9.next = 17; | ||
return this.statsigClient.initializeAsync(); | ||
case 17: | ||
_context9.next = 29; | ||
break; | ||
case 19: | ||
_context9.prev = 19; | ||
_context9.t0 = _context9["catch"](12); | ||
if (_context9.t0 instanceof Error) { | ||
// eslint-disable-next-line no-console | ||
console.error("Error occurred when trying to initialise the Statsig client, error: ".concat(_context9.t0 === null || _context9.t0 === void 0 ? void 0 : _context9.t0.message)); | ||
} | ||
// eslint-disable-next-line no-console | ||
console.warn("Initialising Statsig client with default sdk key and without values"); | ||
this.statsigClient = new _jsClient.StatsigClient(DEFAULT_CLIENT_KEY, this.user, statsigOptions); | ||
this.dataAdapter.setBootstrapData(); | ||
_context9.next = 27; | ||
return this.statsigClient.initializeAsync(); | ||
case 27: | ||
this.initWithDefaults = true; | ||
throw _context9.t0; | ||
case 29: | ||
case "end": | ||
return _context9.stop(); | ||
} | ||
}, _callee9, this, [[12, 19]]); | ||
})); | ||
function initFromValues(_x25, _x26, _x27) { | ||
return _initFromValues.apply(this, arguments); | ||
} | ||
return initFromValues; | ||
}() | ||
/** | ||
* This method updates the user for this client with the bootstrap values returned from a given | ||
* Promise. | ||
* It uses the customAttributes from fetching experiment values to update the Statsig user but | ||
* uses the customAttributes from given input to check if the user has changed. | ||
* | ||
* @param {Identifiers} identifiers | ||
* @param {CustomAttributes} customAttributes | ||
* @param {Promise<InitializeValues>} getInitializeValues | ||
* @private | ||
*/ | ||
) | ||
}, { | ||
key: "updateUserUsingInitializeValuesProducer", | ||
value: (function () { | ||
var _updateUserUsingInitializeValuesProducer = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee11(getInitializeValues, identifiers, customAttributes) { | ||
var _this9 = this; | ||
var originalInitPromise, initializeValuesPromise, updateUserPromise; | ||
return _regenerator.default.wrap(function _callee11$(_context11) { | ||
while (1) switch (_context11.prev = _context11.next) { | ||
case 0: | ||
this.assertInitialized(this.statsigClient); | ||
if (this.initPromise) { | ||
_context11.next = 3; | ||
break; | ||
} | ||
throw new Error('The client must be initialized before you can update the user.'); | ||
case 3: | ||
if (!this.isCurrentUser(identifiers, customAttributes)) { | ||
_context11.next = 5; | ||
break; | ||
} | ||
return _context11.abrupt("return", this.initPromise); | ||
case 5: | ||
// Wait for the current initialize/update to finish | ||
originalInitPromise = this.initPromise; | ||
_context11.prev = 6; | ||
_context11.next = 9; | ||
return this.initPromise; | ||
case 9: | ||
_context11.next = 13; | ||
break; | ||
case 11: | ||
_context11.prev = 11; | ||
_context11.t0 = _context11["catch"](6); | ||
case 13: | ||
initializeValuesPromise = getInitializeValues(); | ||
updateUserPromise = this.updateStatsigClientUser(initializeValuesPromise, identifiers, customAttributes); // We replace the init promise here since we are essentially re-initializing the client at this | ||
// point. Any subsequent calls to await client.initialize() or client.updateUser() | ||
// will now also await this user update. | ||
this.initPromise = updateUserPromise.catch( /*#__PURE__*/(0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee10() { | ||
return _regenerator.default.wrap(function _callee10$(_context10) { | ||
while (1) switch (_context10.prev = _context10.next) { | ||
case 0: | ||
// If the update failed then it changed nothing, so revert back to the original promise. | ||
_this9.initPromise = originalInitPromise; | ||
/** | ||
* This method updates the user on the nested Statsig client | ||
* | ||
* @param identifiers | ||
* @param customAttributes | ||
* @param initializeValuesPromise | ||
* @private | ||
*/ | ||
async updateStatsigClientUser(initializeValuesPromise, identifiers, customAttributes) { | ||
var _this$initOptions, _this$initOptions$upd; | ||
this.assertInitialized(this.statsigClient); | ||
let initializeValues, user; | ||
try { | ||
initializeValues = await initializeValuesPromise; | ||
user = toStatsigUser(identifiers, initializeValues.customAttributesFromFetch, this.sdkKey); | ||
} catch (err) { | ||
var _updateUserCompletion, _ref; | ||
// Make sure the updateUserCompletionCallback is called for any errors in our custom code. | ||
// This is not necessary for the updateUserWithValues call, because the Statsig client will | ||
// already invoke the callback itself. | ||
const errMsg = err instanceof Error ? err.message : JSON.stringify(err); | ||
(_updateUserCompletion = (_ref = this.initOptions).updateUserCompletionCallback) === null || _updateUserCompletion === void 0 || _updateUserCompletion.call(_ref, false, errMsg); | ||
throw err; | ||
// Set the user profile again to revert back to the current user | ||
if (!_this9.provider) { | ||
_context10.next = 4; | ||
break; | ||
} | ||
_context10.next = 4; | ||
return _this9.provider.setProfile(_this9.initOptions, _this9.currentIdentifiers, _this9.currentAttributes); | ||
case 4: | ||
case "end": | ||
return _context10.stop(); | ||
} | ||
}, _callee10); | ||
}))); | ||
return _context11.abrupt("return", updateUserPromise); | ||
case 17: | ||
case "end": | ||
return _context11.stop(); | ||
} | ||
}, _callee11, this, [[6, 11]]); | ||
})); | ||
function updateUserUsingInitializeValuesProducer(_x28, _x29, _x30) { | ||
return _updateUserUsingInitializeValuesProducer.apply(this, arguments); | ||
} | ||
return updateUserUsingInitializeValuesProducer; | ||
}() | ||
/** | ||
* This method updates the user on the nested Statsig client | ||
* | ||
* @param identifiers | ||
* @param customAttributes | ||
* @param initializeValuesPromise | ||
* @private | ||
*/ | ||
) | ||
}, { | ||
key: "updateStatsigClientUser", | ||
value: (function () { | ||
var _updateStatsigClientUser = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee12(initializeValuesPromise, identifiers, customAttributes) { | ||
var _this$initOptions, _this$initOptions$upd; | ||
var initializeValues, user, _updateUserCompletion, _ref4, errMsg, success, errorMessage; | ||
return _regenerator.default.wrap(function _callee12$(_context12) { | ||
while (1) switch (_context12.prev = _context12.next) { | ||
case 0: | ||
this.assertInitialized(this.statsigClient); | ||
_context12.prev = 1; | ||
_context12.next = 4; | ||
return initializeValuesPromise; | ||
case 4: | ||
initializeValues = _context12.sent; | ||
user = (0, _utils.toStatsigUser)(identifiers, initializeValues.customAttributesFromFetch, this.sdkKey); | ||
_context12.next = 13; | ||
break; | ||
case 8: | ||
_context12.prev = 8; | ||
_context12.t0 = _context12["catch"](1); | ||
// Make sure the updateUserCompletionCallback is called for any errors in our custom code. | ||
// This is not necessary for the updateUserWithValues call, because the Statsig client will | ||
// already invoke the callback itself. | ||
errMsg = _context12.t0 instanceof Error ? _context12.t0.message : JSON.stringify(_context12.t0); | ||
(_updateUserCompletion = (_ref4 = this.initOptions).updateUserCompletionCallback) === null || _updateUserCompletion === void 0 || _updateUserCompletion.call(_ref4, false, errMsg); | ||
throw _context12.t0; | ||
case 13: | ||
success = true; | ||
errorMessage = null; | ||
_context12.prev = 15; | ||
this.dataAdapter.setBootstrapData(initializeValues.experimentValues); | ||
this.user = user; | ||
_context12.next = 20; | ||
return this.statsigClient.updateUserAsync(this.user); | ||
case 20: | ||
_context12.next = 26; | ||
break; | ||
case 22: | ||
_context12.prev = 22; | ||
_context12.t1 = _context12["catch"](15); | ||
success = false; | ||
errorMessage = String(_context12.t1); | ||
case 26: | ||
(_this$initOptions = this.initOptions) === null || _this$initOptions === void 0 || (_this$initOptions$upd = _this$initOptions.updateUserCompletionCallback) === null || _this$initOptions$upd === void 0 || _this$initOptions$upd.call(_this$initOptions, success, errorMessage); | ||
if (!success) { | ||
_context12.next = 33; | ||
break; | ||
} | ||
this.currentIdentifiers = identifiers; | ||
this.currentAttributes = customAttributes; | ||
this.subscriptions.anyUpdated(); | ||
_context12.next = 34; | ||
break; | ||
case 33: | ||
throw new Error('Failed to update user. An unexpected error occured.'); | ||
case 34: | ||
case "end": | ||
return _context12.stop(); | ||
} | ||
}, _callee12, this, [[1, 8], [15, 22]]); | ||
})); | ||
function updateStatsigClientUser(_x31, _x32, _x33) { | ||
return _updateStatsigClientUser.apply(this, arguments); | ||
} | ||
return updateStatsigClientUser; | ||
}()) | ||
}, { | ||
key: "getPackageVersion", | ||
value: | ||
/** | ||
* @returns string version of the current package in semver style. | ||
*/ | ||
function getPackageVersion() { | ||
return _version.CLIENT_VERSION; | ||
} | ||
let success = true; | ||
let errorMessage = null; | ||
try { | ||
this.dataAdapter.setBootstrapData(initializeValues.experimentValues); | ||
this.user = user; | ||
await this.statsigClient.updateUserAsync(this.user); | ||
} catch (err) { | ||
success = false; | ||
errorMessage = String(err); | ||
} | ||
(_this$initOptions = this.initOptions) === null || _this$initOptions === void 0 || (_this$initOptions$upd = _this$initOptions.updateUserCompletionCallback) === null || _this$initOptions$upd === void 0 || _this$initOptions$upd.call(_this$initOptions, success, errorMessage); | ||
if (success) { | ||
this.currentIdentifiers = identifiers; | ||
this.currentAttributes = customAttributes; | ||
this.subscriptions.anyUpdated(); | ||
} else { | ||
throw new Error('Failed to update user. An unexpected error occured.'); | ||
} | ||
} | ||
/** | ||
* @returns string version of the current package in semver style. | ||
*/ | ||
getPackageVersion() { | ||
return CLIENT_VERSION; | ||
} | ||
/** | ||
* Returns a specified layer otherwise returns an empty layer as a default value if the layer doesn't exist. | ||
* | ||
* @param {string} layerName - The name of the layer | ||
* @param {Object} options | ||
* @param {boolean} options.fireLayerExposure - Whether or not to fire the exposure event for the | ||
* layer. Defaults to true. To log an exposure event manually at a later time, use | ||
* {@link Client.manuallyLogLayerExposure} (see [Statsig docs about manually logging exposures](https://docs.statsig.com/client/jsClientSDK#manual-exposures-)). | ||
* @returns A layer | ||
* @example | ||
* ```ts | ||
* const layer = client.getLayer('example-layer-name'); | ||
* const exampletitle: string = layer.get("title", "Welcome to Statsig!"); | ||
* ``` | ||
*/ | ||
getLayer( /** The name of the layer */ | ||
layerName, options = {}) { | ||
try { | ||
this.assertInitialized(this.statsigClient); | ||
const { | ||
fireLayerExposure = true | ||
} = options; | ||
return Layer.fromLayer(this.statsigClient.getLayer(layerName, { | ||
disableExposureLog: !fireLayerExposure | ||
})); | ||
} catch (error) { | ||
// Log the first occurrence of the error | ||
if (!this.hasGetLayerErrorOccurred) { | ||
// eslint-disable-next-line no-console | ||
console.warn({ | ||
msg: 'An error has occurred getting the layer. Only the first occurrence of this error is logged.', | ||
layerName, | ||
error | ||
}); | ||
this.hasGetLayerErrorOccurred = true; | ||
/** | ||
* Returns a specified layer otherwise returns an empty layer as a default value if the layer doesn't exist. | ||
* | ||
* @param {string} layerName - The name of the layer | ||
* @param {Object} options | ||
* @param {boolean} options.fireLayerExposure - Whether or not to fire the exposure event for the | ||
* layer. Defaults to true. To log an exposure event manually at a later time, use | ||
* {@link Client.manuallyLogLayerExposure} (see [Statsig docs about manually logging exposures](https://docs.statsig.com/client/jsClientSDK#manual-exposures-)). | ||
* @returns A layer | ||
* @example | ||
* ```ts | ||
* const layer = client.getLayer('example-layer-name'); | ||
* const exampletitle: string = layer.get("title", "Welcome to Statsig!"); | ||
* ``` | ||
*/ | ||
}, { | ||
key: "getLayer", | ||
value: function getLayer( /** The name of the layer */ | ||
layerName) { | ||
var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; | ||
try { | ||
this.assertInitialized(this.statsigClient); | ||
var _options$fireLayerExp = options.fireLayerExposure, | ||
fireLayerExposure = _options$fireLayerExp === void 0 ? true : _options$fireLayerExp; | ||
return _Layer.Layer.fromLayer(this.statsigClient.getLayer(layerName, { | ||
disableExposureLog: !fireLayerExposure | ||
})); | ||
} catch (error) { | ||
// Log the first occurrence of the error | ||
if (!this.hasGetLayerErrorOccurred) { | ||
// eslint-disable-next-line no-console | ||
console.warn({ | ||
msg: 'An error has occurred getting the layer. Only the first occurrence of this error is logged.', | ||
layerName: layerName, | ||
error: error | ||
}); | ||
this.hasGetLayerErrorOccurred = true; | ||
} | ||
// Return a default value | ||
return _Layer.Layer.fromLayer((0, _jsClient._makeLayer)(layerName, { | ||
reason: 'Error' | ||
}, null)); | ||
} | ||
// Return a default value | ||
return Layer.fromLayer(_makeLayer(layerName, { | ||
reason: 'Error' | ||
}, null)); | ||
} | ||
} | ||
/** | ||
* Returns the value of a given parameter in a layer config. | ||
* | ||
* @template T | ||
* @param {string} layerName - The name of the layer | ||
* @param {string} parameterName - The name of the parameter to fetch from the layer config | ||
* @param {T} defaultValue - The value to serve if the layer or parameter do not exist, or if the | ||
* returned value does not match the expected type. | ||
* @param {Object} options | ||
* @param {boolean} options.fireLayerExposure - Whether or not to fire the exposure event for the | ||
* layer. Defaults to true. To log an exposure event manually at a later time, use | ||
* {@link Client.manuallyLogLayerExposure} (see [Statsig docs about manually logging exposures](https://docs.statsig.com/client/jsClientSDK#manual-exposures-)) | ||
* @param {function} options.typeGuard - A function that asserts that the return value has the expected type. If this function returns false, then the default value will be returned instead. This can be set to protect your code from unexpected values being set remotely. By default, this will be done by asserting that the default value and value are the same primitive type. | ||
* @returns The value of the parameter if the layer and parameter both exist, otherwise the default value. | ||
* @example | ||
* ``` ts | ||
* type ValidColor = 'blue' | 'red' | 'yellow'; | ||
* type ValidColorTypeCheck = (value: unknown) => value is ValidColor; | ||
* | ||
* const isValidColor: ValidColorTypeCheck = | ||
* (value: unknown) => typeof value === 'string' && ['blue', 'red', 'yellow'].includes(value); | ||
* | ||
* const buttonColor: ValidColor = client.getLayerValue( | ||
* 'example-layer-name', | ||
* 'backgroundColor', | ||
* 'yellow', | ||
* { | ||
* typeGuard: isValidColor | ||
* } | ||
* ); | ||
* ``` | ||
*/ | ||
getLayerValue(layerName, parameterName, defaultValue, options = {}) { | ||
const layer = this.getLayer(layerName, options); | ||
try { | ||
const { | ||
typeGuard | ||
} = options; | ||
return layer.get(parameterName, defaultValue, typeGuard); | ||
} catch (error) { | ||
// Log the first occurrence of the error | ||
if (!this.hasGetLayerValueErrorOccurred) { | ||
// eslint-disable-next-line no-console | ||
console.warn({ | ||
msg: 'An error has occurred getting the layer value. Only the first occurrence of this error is logged.', | ||
layerName, | ||
defaultValue, | ||
options, | ||
error | ||
}); | ||
this.hasGetLayerValueErrorOccurred = true; | ||
/** | ||
* Returns the value of a given parameter in a layer config. | ||
* | ||
* @template T | ||
* @param {string} layerName - The name of the layer | ||
* @param {string} parameterName - The name of the parameter to fetch from the layer config | ||
* @param {T} defaultValue - The value to serve if the layer or parameter do not exist, or if the | ||
* returned value does not match the expected type. | ||
* @param {Object} options | ||
* @param {boolean} options.fireLayerExposure - Whether or not to fire the exposure event for the | ||
* layer. Defaults to true. To log an exposure event manually at a later time, use | ||
* {@link Client.manuallyLogLayerExposure} (see [Statsig docs about manually logging exposures](https://docs.statsig.com/client/jsClientSDK#manual-exposures-)) | ||
* @param {function} options.typeGuard - A function that asserts that the return value has the expected type. If this function returns false, then the default value will be returned instead. This can be set to protect your code from unexpected values being set remotely. By default, this will be done by asserting that the default value and value are the same primitive type. | ||
* @returns The value of the parameter if the layer and parameter both exist, otherwise the default value. | ||
* @example | ||
* ``` ts | ||
* type ValidColor = 'blue' | 'red' | 'yellow'; | ||
* type ValidColorTypeCheck = (value: unknown) => value is ValidColor; | ||
* | ||
* const isValidColor: ValidColorTypeCheck = | ||
* (value: unknown) => typeof value === 'string' && ['blue', 'red', 'yellow'].includes(value); | ||
* | ||
* const buttonColor: ValidColor = client.getLayerValue( | ||
* 'example-layer-name', | ||
* 'backgroundColor', | ||
* 'yellow', | ||
* { | ||
* typeGuard: isValidColor | ||
* } | ||
* ); | ||
* ``` | ||
*/ | ||
}, { | ||
key: "getLayerValue", | ||
value: function getLayerValue(layerName, parameterName, defaultValue) { | ||
var options = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : {}; | ||
var layer = this.getLayer(layerName, options); | ||
try { | ||
var typeGuard = options.typeGuard; | ||
return layer.get(parameterName, defaultValue, typeGuard); | ||
} catch (error) { | ||
// Log the first occurrence of the error | ||
if (!this.hasGetLayerValueErrorOccurred) { | ||
// eslint-disable-next-line no-console | ||
console.warn({ | ||
msg: 'An error has occurred getting the layer value. Only the first occurrence of this error is logged.', | ||
layerName: layerName, | ||
defaultValue: defaultValue, | ||
options: options, | ||
error: error | ||
}); | ||
this.hasGetLayerValueErrorOccurred = true; | ||
} | ||
return defaultValue; | ||
} | ||
return defaultValue; | ||
} | ||
} | ||
} | ||
}]); | ||
}(); |
@@ -1,11 +0,19 @@ | ||
import { migrateEvaluationDetails } from '../utils'; | ||
"use strict"; | ||
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); | ||
Object.defineProperty(exports, "__esModule", { | ||
value: true | ||
}); | ||
exports.DynamicConfig = void 0; | ||
var _typeof2 = _interopRequireDefault(require("@babel/runtime/helpers/typeof")); | ||
var _classCallCheck2 = _interopRequireDefault(require("@babel/runtime/helpers/classCallCheck")); | ||
var _createClass2 = _interopRequireDefault(require("@babel/runtime/helpers/createClass")); | ||
var _utils = require("../utils"); | ||
// Reference: https://github.com/statsig-io/js-lite/blob/main/src/DynamicConfig.ts | ||
export class DynamicConfig { | ||
static fromExperiment(experiment) { | ||
var _experiment$__evaluat, _experiment$groupName; | ||
const config = new DynamicConfig(experiment.name, experiment.value, experiment.ruleID, migrateEvaluationDetails(experiment.details), (_experiment$__evaluat = experiment.__evaluation) === null || _experiment$__evaluat === void 0 ? void 0 : _experiment$__evaluat.secondary_exposures, (_experiment$groupName = experiment.groupName) !== null && _experiment$groupName !== void 0 ? _experiment$groupName : undefined); | ||
config.experiment = experiment; | ||
return config; | ||
} | ||
constructor(configName, configValue, ruleID, evaluationDetails, secondaryExposures = [], allocatedExperimentName = '', onDefaultValueFallback = null) { | ||
var DynamicConfig = exports.DynamicConfig = /*#__PURE__*/function () { | ||
function DynamicConfig(configName, configValue, ruleID, evaluationDetails) { | ||
var secondaryExposures = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : []; | ||
var allocatedExperimentName = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : ''; | ||
var onDefaultValueFallback = arguments.length > 6 && arguments[6] !== undefined ? arguments[6] : null; | ||
(0, _classCallCheck2.default)(this, DynamicConfig); | ||
this.value = configValue; | ||
@@ -19,45 +27,60 @@ this._name = configName; | ||
} | ||
get(key, defaultValue, typeGuard) { | ||
var _this$_onDefaultValue2; | ||
const val = this.getValue(key, defaultValue); | ||
if (val == null) { | ||
return defaultValue; | ||
} | ||
const expectedType = Array.isArray(defaultValue) ? 'array' : typeof defaultValue; | ||
const actualType = Array.isArray(val) ? 'array' : typeof val; | ||
if (typeGuard) { | ||
var _this$_onDefaultValue; | ||
if (typeGuard(val)) { | ||
return (0, _createClass2.default)(DynamicConfig, [{ | ||
key: "get", | ||
value: function get(key, defaultValue, typeGuard) { | ||
var _this$_onDefaultValue2; | ||
var val = this.getValue(key, defaultValue); | ||
if (val == null) { | ||
return defaultValue; | ||
} | ||
var expectedType = Array.isArray(defaultValue) ? 'array' : (0, _typeof2.default)(defaultValue); | ||
var actualType = Array.isArray(val) ? 'array' : (0, _typeof2.default)(val); | ||
if (typeGuard) { | ||
var _this$_onDefaultValue; | ||
if (typeGuard(val)) { | ||
this.fireExposure(key); | ||
return val; | ||
} | ||
(_this$_onDefaultValue = this._onDefaultValueFallback) === null || _this$_onDefaultValue === void 0 || _this$_onDefaultValue.call(this, this, key, expectedType, actualType); | ||
return defaultValue; | ||
} | ||
if (defaultValue == null || expectedType === actualType) { | ||
this.fireExposure(key); | ||
return val; | ||
} | ||
(_this$_onDefaultValue = this._onDefaultValueFallback) === null || _this$_onDefaultValue === void 0 || _this$_onDefaultValue.call(this, this, key, expectedType, actualType); | ||
(_this$_onDefaultValue2 = this._onDefaultValueFallback) === null || _this$_onDefaultValue2 === void 0 || _this$_onDefaultValue2.call(this, this, key, expectedType, actualType); | ||
return defaultValue; | ||
} | ||
if (defaultValue == null || expectedType === actualType) { | ||
}, { | ||
key: "getValue", | ||
value: function getValue(key, defaultValue) { | ||
if (key == null) { | ||
return this.value; | ||
} | ||
if (defaultValue == null) { | ||
defaultValue = null; | ||
} | ||
if (this.value[key] == null) { | ||
return defaultValue; | ||
} | ||
this.fireExposure(key); | ||
return val; | ||
return this.value[key]; | ||
} | ||
(_this$_onDefaultValue2 = this._onDefaultValueFallback) === null || _this$_onDefaultValue2 === void 0 || _this$_onDefaultValue2.call(this, this, key, expectedType, actualType); | ||
return defaultValue; | ||
} | ||
getValue(key, defaultValue) { | ||
if (key == null) { | ||
return this.value; | ||
}, { | ||
key: "fireExposure", | ||
value: function fireExposure(key) { | ||
// Call the wrapped experiment's get method to fire exposure | ||
if (this.experiment) { | ||
this.experiment.get(key); | ||
} | ||
} | ||
if (defaultValue == null) { | ||
defaultValue = null; | ||
}], [{ | ||
key: "fromExperiment", | ||
value: function fromExperiment(experiment) { | ||
var _experiment$__evaluat, _experiment$groupName; | ||
var config = new DynamicConfig(experiment.name, experiment.value, experiment.ruleID, (0, _utils.migrateEvaluationDetails)(experiment.details), (_experiment$__evaluat = experiment.__evaluation) === null || _experiment$__evaluat === void 0 ? void 0 : _experiment$__evaluat.secondary_exposures, (_experiment$groupName = experiment.groupName) !== null && _experiment$groupName !== void 0 ? _experiment$groupName : undefined); | ||
config.experiment = experiment; | ||
return config; | ||
} | ||
if (this.value[key] == null) { | ||
return defaultValue; | ||
} | ||
this.fireExposure(key); | ||
return this.value[key]; | ||
} | ||
fireExposure(key) { | ||
// Call the wrapped experiment's get method to fire exposure | ||
if (this.experiment) { | ||
this.experiment.get(key); | ||
} | ||
} | ||
} | ||
}]); | ||
}(); |
@@ -1,10 +0,21 @@ | ||
import { migrateEvaluationDetails } from '../utils'; | ||
"use strict"; | ||
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); | ||
Object.defineProperty(exports, "__esModule", { | ||
value: true | ||
}); | ||
exports.Layer = void 0; | ||
var _typeof2 = _interopRequireDefault(require("@babel/runtime/helpers/typeof")); | ||
var _classCallCheck2 = _interopRequireDefault(require("@babel/runtime/helpers/classCallCheck")); | ||
var _createClass2 = _interopRequireDefault(require("@babel/runtime/helpers/createClass")); | ||
var _utils = require("../utils"); | ||
// Reference: https://github.com/statsig-io/js-lite/blob/main/src/Layer.ts | ||
export class Layer { | ||
static fromLayer(layer) { | ||
var _layer$__evaluation, _layer$__evaluation2, _layer$__evaluation3, _layer$__evaluation4; | ||
const value = new Layer(layer.name, layer.__value, layer.ruleID, migrateEvaluationDetails(layer.details), (_layer, parameterName) => layer.get(parameterName), (_layer$__evaluation = layer.__evaluation) === null || _layer$__evaluation === void 0 ? void 0 : _layer$__evaluation.secondary_exposures, (_layer$__evaluation2 = layer.__evaluation) === null || _layer$__evaluation2 === void 0 ? void 0 : _layer$__evaluation2.undelegated_secondary_exposures, (_layer$__evaluation3 = layer.__evaluation) === null || _layer$__evaluation3 === void 0 ? void 0 : _layer$__evaluation3.allocated_experiment_name, (_layer$__evaluation4 = layer.__evaluation) === null || _layer$__evaluation4 === void 0 ? void 0 : _layer$__evaluation4.explicit_parameters); | ||
return value; | ||
} | ||
constructor(name, layerValue, ruleID, evaluationDetails, logParameterFunction = null, secondaryExposures = [], undelegatedSecondaryExposures = [], allocatedExperimentName = '', explicitParameters = []) { | ||
var Layer = exports.Layer = /*#__PURE__*/function () { | ||
function Layer(name, layerValue, ruleID, evaluationDetails) { | ||
var logParameterFunction = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : null; | ||
var secondaryExposures = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : []; | ||
var undelegatedSecondaryExposures = arguments.length > 6 && arguments[6] !== undefined ? arguments[6] : []; | ||
var allocatedExperimentName = arguments.length > 7 && arguments[7] !== undefined ? arguments[7] : ''; | ||
var explicitParameters = arguments.length > 8 && arguments[8] !== undefined ? arguments[8] : []; | ||
(0, _classCallCheck2.default)(this, Layer); | ||
this._logParameterFunction = logParameterFunction; | ||
@@ -20,37 +31,54 @@ this._name = name; | ||
} | ||
get(key, defaultValue, typeGuard) { | ||
const val = this._value[key]; | ||
if (val == null) { | ||
return (0, _createClass2.default)(Layer, [{ | ||
key: "get", | ||
value: function get(key, defaultValue, typeGuard) { | ||
var _this = this; | ||
var val = this._value[key]; | ||
if (val == null) { | ||
return defaultValue; | ||
} | ||
var logAndReturn = function logAndReturn() { | ||
_this._logLayerParameterExposure(key); | ||
return val; | ||
}; | ||
if (typeGuard) { | ||
return typeGuard(val) ? logAndReturn() : defaultValue; | ||
} | ||
if (defaultValue == null) { | ||
return logAndReturn(); | ||
} | ||
if ((0, _typeof2.default)(val) === (0, _typeof2.default)(defaultValue) && Array.isArray(defaultValue) === Array.isArray(val)) { | ||
return logAndReturn(); | ||
} | ||
return defaultValue; | ||
} | ||
const logAndReturn = () => { | ||
this._logLayerParameterExposure(key); | ||
return val; | ||
}; | ||
if (typeGuard) { | ||
return typeGuard(val) ? logAndReturn() : defaultValue; | ||
}, { | ||
key: "getValue", | ||
value: function getValue(key, defaultValue) { | ||
// eslint-disable-next-line eqeqeq | ||
if (defaultValue == undefined) { | ||
defaultValue = null; | ||
} | ||
var val = this._value[key]; | ||
if (val != null) { | ||
this._logLayerParameterExposure(key); | ||
} | ||
return val !== null && val !== void 0 ? val : defaultValue; | ||
} | ||
if (defaultValue == null) { | ||
return logAndReturn(); | ||
}, { | ||
key: "_logLayerParameterExposure", | ||
value: function _logLayerParameterExposure(parameterName) { | ||
var _this$_logParameterFu; | ||
(_this$_logParameterFu = this._logParameterFunction) === null || _this$_logParameterFu === void 0 || _this$_logParameterFu.call(this, this, parameterName); | ||
} | ||
if (typeof val === typeof defaultValue && Array.isArray(defaultValue) === Array.isArray(val)) { | ||
return logAndReturn(); | ||
}], [{ | ||
key: "fromLayer", | ||
value: function fromLayer(layer) { | ||
var _layer$__evaluation, _layer$__evaluation2, _layer$__evaluation3, _layer$__evaluation4; | ||
var value = new Layer(layer.name, layer.__value, layer.ruleID, (0, _utils.migrateEvaluationDetails)(layer.details), function (_layer, parameterName) { | ||
return layer.get(parameterName); | ||
}, (_layer$__evaluation = layer.__evaluation) === null || _layer$__evaluation === void 0 ? void 0 : _layer$__evaluation.secondary_exposures, (_layer$__evaluation2 = layer.__evaluation) === null || _layer$__evaluation2 === void 0 ? void 0 : _layer$__evaluation2.undelegated_secondary_exposures, (_layer$__evaluation3 = layer.__evaluation) === null || _layer$__evaluation3 === void 0 ? void 0 : _layer$__evaluation3.allocated_experiment_name, (_layer$__evaluation4 = layer.__evaluation) === null || _layer$__evaluation4 === void 0 ? void 0 : _layer$__evaluation4.explicit_parameters); | ||
return value; | ||
} | ||
return defaultValue; | ||
} | ||
getValue(key, defaultValue) { | ||
// eslint-disable-next-line eqeqeq | ||
if (defaultValue == undefined) { | ||
defaultValue = null; | ||
} | ||
const val = this._value[key]; | ||
if (val != null) { | ||
this._logLayerParameterExposure(key); | ||
} | ||
return val !== null && val !== void 0 ? val : defaultValue; | ||
} | ||
_logLayerParameterExposure(parameterName) { | ||
var _this$_logParameterFu; | ||
(_this$_logParameterFu = this._logParameterFunction) === null || _this$_logParameterFu === void 0 || _this$_logParameterFu.call(this, this, parameterName); | ||
} | ||
} | ||
}]); | ||
}(); |
@@ -0,4 +1,9 @@ | ||
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { | ||
value: true | ||
}); | ||
exports.EvaluationReason = void 0; | ||
// Reference: https://github.com/statsig-io/js-lite/blob/main/src/StatsigStore.ts | ||
export let EvaluationReason = /*#__PURE__*/function (EvaluationReason) { | ||
var EvaluationReason = exports.EvaluationReason = /*#__PURE__*/function (EvaluationReason) { | ||
EvaluationReason["Error"] = "Error"; | ||
@@ -15,4 +20,2 @@ EvaluationReason["LocalOverride"] = "LocalOverride"; | ||
return EvaluationReason; | ||
}({}); | ||
// Reference: https://github.com/statsig-io/js-lite/blob/main/src/StatsigSDKOptions.ts | ||
}({}); // Reference: https://github.com/statsig-io/js-lite/blob/main/src/StatsigSDKOptions.ts |
@@ -1,9 +0,47 @@ | ||
import _defineProperty from "@babel/runtime/helpers/defineProperty"; | ||
"use strict"; | ||
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); | ||
Object.defineProperty(exports, "__esModule", { | ||
value: true | ||
}); | ||
Object.defineProperty(exports, "CLIENT_VERSION", { | ||
enumerable: true, | ||
get: function get() { | ||
return _version.CLIENT_VERSION; | ||
} | ||
}); | ||
Object.defineProperty(exports, "DynamicConfig", { | ||
enumerable: true, | ||
get: function get() { | ||
return _DynamicConfig.DynamicConfig; | ||
} | ||
}); | ||
Object.defineProperty(exports, "EvaluationReason", { | ||
enumerable: true, | ||
get: function get() { | ||
return _types.EvaluationReason; | ||
} | ||
}); | ||
Object.defineProperty(exports, "FeatureGateEnvironment", { | ||
enumerable: true, | ||
get: function get() { | ||
return _types2.FeatureGateEnvironment; | ||
} | ||
}); | ||
Object.defineProperty(exports, "PerimeterType", { | ||
enumerable: true, | ||
get: function get() { | ||
return _types2.PerimeterType; | ||
} | ||
}); | ||
exports.default = void 0; | ||
var _classCallCheck2 = _interopRequireDefault(require("@babel/runtime/helpers/classCallCheck")); | ||
var _createClass2 = _interopRequireDefault(require("@babel/runtime/helpers/createClass")); | ||
var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty")); | ||
var _Client = require("./Client"); | ||
var _version = require("./version"); | ||
var _types = require("./compat/types"); | ||
var _DynamicConfig = require("./compat/DynamicConfig"); | ||
var _types2 = require("./types"); | ||
var _FeatureGates; | ||
import { Client } from './Client'; | ||
import { CLIENT_VERSION } from './version'; | ||
export { EvaluationReason } from './compat/types'; | ||
export { DynamicConfig } from './compat/DynamicConfig'; | ||
export { FeatureGateEnvironment, PerimeterType } from './types'; | ||
export { CLIENT_VERSION } from './version'; | ||
/** | ||
@@ -15,15 +53,23 @@ * Access the FeatureGates object via the default export. | ||
*/ | ||
class FeatureGates { | ||
static isGateExists(gateName) { | ||
return this.client.isGateExist(gateName); | ||
var FeatureGates = /*#__PURE__*/function () { | ||
function FeatureGates() { | ||
(0, _classCallCheck2.default)(this, FeatureGates); | ||
} | ||
static isExperimentExists(experimentName) { | ||
return this.client.isExperimentExist(experimentName); | ||
} | ||
} | ||
return (0, _createClass2.default)(FeatureGates, null, [{ | ||
key: "isGateExists", | ||
value: function isGateExists(gateName) { | ||
return this.client.isGateExist(gateName); | ||
} | ||
}, { | ||
key: "isExperimentExists", | ||
value: function isExperimentExists(experimentName) { | ||
return this.client.isExperimentExist(experimentName); | ||
} | ||
}]); | ||
}(); | ||
_FeatureGates = FeatureGates; | ||
_defineProperty(FeatureGates, "client", new Client()); | ||
_defineProperty(FeatureGates, "hasCheckGateErrorOccurred", false); | ||
_defineProperty(FeatureGates, "hasGetExperimentValueErrorOccurred", false); | ||
_defineProperty(FeatureGates, "checkGate", (gateName, options) => { | ||
(0, _defineProperty2.default)(FeatureGates, "client", new _Client.Client()); | ||
(0, _defineProperty2.default)(FeatureGates, "hasCheckGateErrorOccurred", false); | ||
(0, _defineProperty2.default)(FeatureGates, "hasGetExperimentValueErrorOccurred", false); | ||
(0, _defineProperty2.default)(FeatureGates, "checkGate", function (gateName, options) { | ||
try { | ||
@@ -33,3 +79,3 @@ // Check if the CRITERION override mechanism is available | ||
// Attempt to retrieve an override value for the feature gate | ||
const overrideValue = window.__CRITERION__.getFeatureFlagOverride(gateName); | ||
var overrideValue = window.__CRITERION__.getFeatureFlagOverride(gateName); | ||
// If an override value is found, return it immediately | ||
@@ -46,4 +92,4 @@ if (overrideValue !== undefined) { | ||
msg: 'An error has occurred checking the feature gate from criterion override. Only the first occurrence of this error is logged.', | ||
gateName, | ||
error | ||
gateName: gateName, | ||
error: error | ||
}); | ||
@@ -57,7 +103,7 @@ _FeatureGates.hasCheckGateErrorOccurred = true; | ||
}); | ||
_defineProperty(FeatureGates, "getExperimentValue", (experimentName, parameterName, defaultValue, options) => { | ||
(0, _defineProperty2.default)(FeatureGates, "getExperimentValue", function (experimentName, parameterName, defaultValue, options) { | ||
try { | ||
// Check if the CRITERION override mechanism is available | ||
if (typeof window !== 'undefined' && window.__CRITERION__ && typeof window.__CRITERION__.getExperimentValueOverride === 'function') { | ||
const overrideValue = window.__CRITERION__.getExperimentValueOverride(experimentName, parameterName); | ||
var overrideValue = window.__CRITERION__.getExperimentValueOverride(experimentName, parameterName); | ||
if (overrideValue !== undefined && overrideValue !== null) { | ||
@@ -73,6 +119,6 @@ return overrideValue; | ||
msg: 'An error has occurred getting the experiment value from criterion override. Only the first occurrence of this error is logged.', | ||
experimentName, | ||
defaultValue, | ||
options, | ||
error | ||
experimentName: experimentName, | ||
defaultValue: defaultValue, | ||
options: options, | ||
error: error | ||
}); | ||
@@ -87,30 +133,30 @@ _FeatureGates.hasGetExperimentValueErrorOccurred = true; | ||
}); | ||
_defineProperty(FeatureGates, "initializeCalled", _FeatureGates.client.initializeCalled.bind(_FeatureGates.client)); | ||
_defineProperty(FeatureGates, "initializeCompleted", _FeatureGates.client.initializeCompleted.bind(_FeatureGates.client)); | ||
_defineProperty(FeatureGates, "initialize", _FeatureGates.client.initialize.bind(_FeatureGates.client)); | ||
_defineProperty(FeatureGates, "initializeWithProvider", _FeatureGates.client.initializeWithProvider.bind(_FeatureGates.client)); | ||
_defineProperty(FeatureGates, "initializeFromValues", _FeatureGates.client.initializeFromValues.bind(_FeatureGates.client)); | ||
_defineProperty(FeatureGates, "manuallyLogGateExposure", _FeatureGates.client.manuallyLogGateExposure.bind(_FeatureGates.client)); | ||
_defineProperty(FeatureGates, "getExperiment", _FeatureGates.client.getExperiment.bind(_FeatureGates.client)); | ||
_defineProperty(FeatureGates, "manuallyLogExperimentExposure", _FeatureGates.client.manuallyLogExperimentExposure.bind(_FeatureGates.client)); | ||
_defineProperty(FeatureGates, "manuallyLogLayerExposure", _FeatureGates.client.manuallyLogLayerExposure.bind(_FeatureGates.client)); | ||
_defineProperty(FeatureGates, "shutdownStatsig", _FeatureGates.client.shutdownStatsig.bind(_FeatureGates.client)); | ||
_defineProperty(FeatureGates, "overrideGate", _FeatureGates.client.overrideGate.bind(_FeatureGates.client)); | ||
_defineProperty(FeatureGates, "clearGateOverride", _FeatureGates.client.clearGateOverride.bind(_FeatureGates.client)); | ||
_defineProperty(FeatureGates, "overrideConfig", _FeatureGates.client.overrideConfig.bind(_FeatureGates.client)); | ||
_defineProperty(FeatureGates, "clearConfigOverride", _FeatureGates.client.clearConfigOverride.bind(_FeatureGates.client)); | ||
_defineProperty(FeatureGates, "setOverrides", _FeatureGates.client.setOverrides.bind(_FeatureGates.client)); | ||
_defineProperty(FeatureGates, "getOverrides", _FeatureGates.client.getOverrides.bind(_FeatureGates.client)); | ||
_defineProperty(FeatureGates, "clearAllOverrides", _FeatureGates.client.clearAllOverrides.bind(_FeatureGates.client)); | ||
_defineProperty(FeatureGates, "isCurrentUser", _FeatureGates.client.isCurrentUser.bind(_FeatureGates.client)); | ||
_defineProperty(FeatureGates, "onGateUpdated", _FeatureGates.client.onGateUpdated.bind(_FeatureGates.client)); | ||
_defineProperty(FeatureGates, "onExperimentValueUpdated", _FeatureGates.client.onExperimentValueUpdated.bind(_FeatureGates.client)); | ||
_defineProperty(FeatureGates, "onAnyUpdated", _FeatureGates.client.onAnyUpdated.bind(_FeatureGates.client)); | ||
_defineProperty(FeatureGates, "updateUser", _FeatureGates.client.updateUser.bind(_FeatureGates.client)); | ||
_defineProperty(FeatureGates, "updateUserWithProvider", _FeatureGates.client.updateUserWithProvider.bind(_FeatureGates.client)); | ||
_defineProperty(FeatureGates, "updateUserWithValues", _FeatureGates.client.updateUserWithValues.bind(_FeatureGates.client)); | ||
_defineProperty(FeatureGates, "getPackageVersion", _FeatureGates.client.getPackageVersion.bind(_FeatureGates.client)); | ||
_defineProperty(FeatureGates, "getLayer", _FeatureGates.client.getLayer.bind(_FeatureGates.client)); | ||
_defineProperty(FeatureGates, "getLayerValue", _FeatureGates.client.getLayerValue.bind(_FeatureGates.client)); | ||
let boundFGJS = FeatureGates; | ||
(0, _defineProperty2.default)(FeatureGates, "initializeCalled", _FeatureGates.client.initializeCalled.bind(_FeatureGates.client)); | ||
(0, _defineProperty2.default)(FeatureGates, "initializeCompleted", _FeatureGates.client.initializeCompleted.bind(_FeatureGates.client)); | ||
(0, _defineProperty2.default)(FeatureGates, "initialize", _FeatureGates.client.initialize.bind(_FeatureGates.client)); | ||
(0, _defineProperty2.default)(FeatureGates, "initializeWithProvider", _FeatureGates.client.initializeWithProvider.bind(_FeatureGates.client)); | ||
(0, _defineProperty2.default)(FeatureGates, "initializeFromValues", _FeatureGates.client.initializeFromValues.bind(_FeatureGates.client)); | ||
(0, _defineProperty2.default)(FeatureGates, "manuallyLogGateExposure", _FeatureGates.client.manuallyLogGateExposure.bind(_FeatureGates.client)); | ||
(0, _defineProperty2.default)(FeatureGates, "getExperiment", _FeatureGates.client.getExperiment.bind(_FeatureGates.client)); | ||
(0, _defineProperty2.default)(FeatureGates, "manuallyLogExperimentExposure", _FeatureGates.client.manuallyLogExperimentExposure.bind(_FeatureGates.client)); | ||
(0, _defineProperty2.default)(FeatureGates, "manuallyLogLayerExposure", _FeatureGates.client.manuallyLogLayerExposure.bind(_FeatureGates.client)); | ||
(0, _defineProperty2.default)(FeatureGates, "shutdownStatsig", _FeatureGates.client.shutdownStatsig.bind(_FeatureGates.client)); | ||
(0, _defineProperty2.default)(FeatureGates, "overrideGate", _FeatureGates.client.overrideGate.bind(_FeatureGates.client)); | ||
(0, _defineProperty2.default)(FeatureGates, "clearGateOverride", _FeatureGates.client.clearGateOverride.bind(_FeatureGates.client)); | ||
(0, _defineProperty2.default)(FeatureGates, "overrideConfig", _FeatureGates.client.overrideConfig.bind(_FeatureGates.client)); | ||
(0, _defineProperty2.default)(FeatureGates, "clearConfigOverride", _FeatureGates.client.clearConfigOverride.bind(_FeatureGates.client)); | ||
(0, _defineProperty2.default)(FeatureGates, "setOverrides", _FeatureGates.client.setOverrides.bind(_FeatureGates.client)); | ||
(0, _defineProperty2.default)(FeatureGates, "getOverrides", _FeatureGates.client.getOverrides.bind(_FeatureGates.client)); | ||
(0, _defineProperty2.default)(FeatureGates, "clearAllOverrides", _FeatureGates.client.clearAllOverrides.bind(_FeatureGates.client)); | ||
(0, _defineProperty2.default)(FeatureGates, "isCurrentUser", _FeatureGates.client.isCurrentUser.bind(_FeatureGates.client)); | ||
(0, _defineProperty2.default)(FeatureGates, "onGateUpdated", _FeatureGates.client.onGateUpdated.bind(_FeatureGates.client)); | ||
(0, _defineProperty2.default)(FeatureGates, "onExperimentValueUpdated", _FeatureGates.client.onExperimentValueUpdated.bind(_FeatureGates.client)); | ||
(0, _defineProperty2.default)(FeatureGates, "onAnyUpdated", _FeatureGates.client.onAnyUpdated.bind(_FeatureGates.client)); | ||
(0, _defineProperty2.default)(FeatureGates, "updateUser", _FeatureGates.client.updateUser.bind(_FeatureGates.client)); | ||
(0, _defineProperty2.default)(FeatureGates, "updateUserWithProvider", _FeatureGates.client.updateUserWithProvider.bind(_FeatureGates.client)); | ||
(0, _defineProperty2.default)(FeatureGates, "updateUserWithValues", _FeatureGates.client.updateUserWithValues.bind(_FeatureGates.client)); | ||
(0, _defineProperty2.default)(FeatureGates, "getPackageVersion", _FeatureGates.client.getPackageVersion.bind(_FeatureGates.client)); | ||
(0, _defineProperty2.default)(FeatureGates, "getLayer", _FeatureGates.client.getLayer.bind(_FeatureGates.client)); | ||
(0, _defineProperty2.default)(FeatureGates, "getLayerValue", _FeatureGates.client.getLayerValue.bind(_FeatureGates.client)); | ||
var boundFGJS = FeatureGates; | ||
@@ -126,6 +172,5 @@ // This makes it possible to get a reference to the FeatureGates client at runtime. | ||
boundFGJS = window.__FEATUREGATES_JS__; | ||
const boundVersion = ((_boundFGJS = boundFGJS) === null || _boundFGJS === void 0 || (_boundFGJS$getPackage = _boundFGJS.getPackageVersion) === null || _boundFGJS$getPackage === void 0 ? void 0 : _boundFGJS$getPackage.call(_boundFGJS)) || '4.10.0 or earlier'; | ||
if (boundVersion !== CLIENT_VERSION) { | ||
const message = `Multiple versions of FeatureGateClients found on the current page. | ||
The currently bound version is ${boundVersion} when module version ${CLIENT_VERSION} was loading.`; | ||
var boundVersion = ((_boundFGJS = boundFGJS) === null || _boundFGJS === void 0 || (_boundFGJS$getPackage = _boundFGJS.getPackageVersion) === null || _boundFGJS$getPackage === void 0 ? void 0 : _boundFGJS$getPackage.call(_boundFGJS)) || '4.10.0 or earlier'; | ||
if (boundVersion !== _version.CLIENT_VERSION) { | ||
var message = "Multiple versions of FeatureGateClients found on the current page.\n The currently bound version is ".concat(boundVersion, " when module version ").concat(_version.CLIENT_VERSION, " was loading."); | ||
// eslint-disable-next-line no-console | ||
@@ -140,2 +185,2 @@ console.warn(message); | ||
*/ | ||
export default boundFGJS; | ||
var _default = exports.default = boundFGJS; |
@@ -1,5 +0,23 @@ | ||
export class ResponseError extends Error { | ||
constructor(message) { | ||
super(message); | ||
"use strict"; | ||
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); | ||
Object.defineProperty(exports, "__esModule", { | ||
value: true | ||
}); | ||
exports.ResponseError = void 0; | ||
var _createClass2 = _interopRequireDefault(require("@babel/runtime/helpers/createClass")); | ||
var _classCallCheck2 = _interopRequireDefault(require("@babel/runtime/helpers/classCallCheck")); | ||
var _possibleConstructorReturn2 = _interopRequireDefault(require("@babel/runtime/helpers/possibleConstructorReturn")); | ||
var _getPrototypeOf2 = _interopRequireDefault(require("@babel/runtime/helpers/getPrototypeOf")); | ||
var _inherits2 = _interopRequireDefault(require("@babel/runtime/helpers/inherits")); | ||
var _wrapNativeSuper2 = _interopRequireDefault(require("@babel/runtime/helpers/wrapNativeSuper")); | ||
function _callSuper(t, o, e) { return o = (0, _getPrototypeOf2.default)(o), (0, _possibleConstructorReturn2.default)(t, _isNativeReflectConstruct() ? Reflect.construct(o, e || [], (0, _getPrototypeOf2.default)(t).constructor) : o.apply(t, e)); } | ||
function _isNativeReflectConstruct() { try { var t = !Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {})); } catch (t) {} return (_isNativeReflectConstruct = function _isNativeReflectConstruct() { return !!t; })(); } | ||
var ResponseError = exports.ResponseError = /*#__PURE__*/function (_Error) { | ||
function ResponseError(message) { | ||
(0, _classCallCheck2.default)(this, ResponseError); | ||
return _callSuper(this, ResponseError, [message]); | ||
} | ||
} | ||
(0, _inherits2.default)(ResponseError, _Error); | ||
return (0, _createClass2.default)(ResponseError); | ||
}( /*#__PURE__*/(0, _wrapNativeSuper2.default)(Error)); |
@@ -1,110 +0,245 @@ | ||
import _defineProperty from "@babel/runtime/helpers/defineProperty"; | ||
"use strict"; | ||
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); | ||
Object.defineProperty(exports, "__esModule", { | ||
value: true | ||
}); | ||
exports.default = exports.STAGING_BASE_URL = exports.PROD_BASE_URL = exports.GATEWAY_BASE_URL = exports.FEDM_STAGING_BASE_URL = exports.FEDM_PROD_BASE_URL = exports.DEV_BASE_URL = void 0; | ||
var _regenerator = _interopRequireDefault(require("@babel/runtime/regenerator")); | ||
var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty")); | ||
var _asyncToGenerator2 = _interopRequireDefault(require("@babel/runtime/helpers/asyncToGenerator")); | ||
var _classCallCheck2 = _interopRequireDefault(require("@babel/runtime/helpers/classCallCheck")); | ||
var _createClass2 = _interopRequireDefault(require("@babel/runtime/helpers/createClass")); | ||
var _types = require("../types"); | ||
var _version = require("../version"); | ||
var _errors = require("./errors"); | ||
function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; } | ||
function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; } | ||
import { FeatureGateEnvironment, PerimeterType } from '../types'; | ||
import { CLIENT_VERSION } from '../version'; | ||
import { ResponseError } from './errors'; | ||
const DEFAULT_REQUEST_TIMEOUT_MS = 5000; | ||
export const PROD_BASE_URL = 'https://api.atlassian.com/flags'; | ||
export const STAGING_BASE_URL = 'https://api.stg.atlassian.com/flags'; | ||
export const DEV_BASE_URL = 'https://api.dev.atlassian.com/flags'; | ||
export const FEDM_STAGING_BASE_URL = 'https://api.stg.atlassian-us-gov-mod.com/flags'; | ||
export const FEDM_PROD_BASE_URL = 'https://api.atlassian-us-gov-mod.com/flags'; | ||
export const GATEWAY_BASE_URL = '/gateway/api/flags'; | ||
export default class Fetcher { | ||
static async fetchClientSdk(fetcherOptions) { | ||
const { | ||
targetApp | ||
} = fetcherOptions; | ||
const url = `/api/v2/frontend/clientSdkKey/${targetApp}`; | ||
try { | ||
return await this.fetchRequest(url, 'GET', fetcherOptions); | ||
} catch (error) { | ||
if (error instanceof Error) { | ||
throw error; | ||
} | ||
throw Error('Failed to retrieve client sdk key'); | ||
} | ||
function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { (0, _defineProperty2.default)(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; } | ||
var DEFAULT_REQUEST_TIMEOUT_MS = 5000; | ||
var PROD_BASE_URL = exports.PROD_BASE_URL = 'https://api.atlassian.com/flags'; | ||
var STAGING_BASE_URL = exports.STAGING_BASE_URL = 'https://api.stg.atlassian.com/flags'; | ||
var DEV_BASE_URL = exports.DEV_BASE_URL = 'https://api.dev.atlassian.com/flags'; | ||
var FEDM_STAGING_BASE_URL = exports.FEDM_STAGING_BASE_URL = 'https://api.stg.atlassian-us-gov-mod.com/flags'; | ||
var FEDM_PROD_BASE_URL = exports.FEDM_PROD_BASE_URL = 'https://api.atlassian-us-gov-mod.com/flags'; | ||
var GATEWAY_BASE_URL = exports.GATEWAY_BASE_URL = '/gateway/api/flags'; | ||
var Fetcher = exports.default = /*#__PURE__*/function () { | ||
function Fetcher() { | ||
(0, _classCallCheck2.default)(this, Fetcher); | ||
} | ||
static async fetchExperimentValues(fetcherOptions, identifiers, customAttributes) { | ||
const requestBody = { | ||
identifiers, | ||
customAttributes, | ||
targetApp: fetcherOptions.targetApp | ||
}; | ||
try { | ||
return await this.fetchRequest('/api/v2/frontend/experimentValues', 'POST', fetcherOptions, requestBody); | ||
} catch (error) { | ||
if (error instanceof Error) { | ||
throw error; | ||
return (0, _createClass2.default)(Fetcher, null, [{ | ||
key: "fetchClientSdk", | ||
value: function () { | ||
var _fetchClientSdk = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee(fetcherOptions) { | ||
var targetApp, url; | ||
return _regenerator.default.wrap(function _callee$(_context) { | ||
while (1) switch (_context.prev = _context.next) { | ||
case 0: | ||
targetApp = fetcherOptions.targetApp; | ||
url = "/api/v2/frontend/clientSdkKey/".concat(targetApp); | ||
_context.prev = 2; | ||
_context.next = 5; | ||
return this.fetchRequest(url, 'GET', fetcherOptions); | ||
case 5: | ||
return _context.abrupt("return", _context.sent); | ||
case 8: | ||
_context.prev = 8; | ||
_context.t0 = _context["catch"](2); | ||
if (!(_context.t0 instanceof Error)) { | ||
_context.next = 12; | ||
break; | ||
} | ||
throw _context.t0; | ||
case 12: | ||
throw Error('Failed to retrieve client sdk key'); | ||
case 13: | ||
case "end": | ||
return _context.stop(); | ||
} | ||
}, _callee, this, [[2, 8]]); | ||
})); | ||
function fetchClientSdk(_x) { | ||
return _fetchClientSdk.apply(this, arguments); | ||
} | ||
throw Error('Failed to retrieve experiment values'); | ||
} | ||
} | ||
static async handleResponseError(response) { | ||
if (!response.ok) { | ||
// Use text() instead of json() as the error body might not be json data | ||
const body = await response.text(); | ||
throw new ResponseError(`Non 2xx response status received, status: ${response.status}, body: ${JSON.stringify(body)}`); | ||
} | ||
if (response.status === 204) { | ||
throw new ResponseError('Unexpected 204 response'); | ||
} | ||
} | ||
static async extractResponseBody(response) { | ||
const value = await response.text(); | ||
return JSON.parse(value); | ||
} | ||
static getBaseUrl(serviceEnv, useGatewayUrl = false, perimeter) { | ||
if (useGatewayUrl) { | ||
return GATEWAY_BASE_URL; | ||
} | ||
if (perimeter === PerimeterType.FEDRAMP_MODERATE) { | ||
switch (serviceEnv) { | ||
case FeatureGateEnvironment.Production: | ||
return FEDM_PROD_BASE_URL; | ||
case FeatureGateEnvironment.Staging: | ||
return FEDM_STAGING_BASE_URL; | ||
default: | ||
throw new Error(`Invalid environment "${serviceEnv}" for "${perimeter}" perimeter`); | ||
return fetchClientSdk; | ||
}() | ||
}, { | ||
key: "fetchExperimentValues", | ||
value: function () { | ||
var _fetchExperimentValues = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee2(fetcherOptions, identifiers, customAttributes) { | ||
var requestBody; | ||
return _regenerator.default.wrap(function _callee2$(_context2) { | ||
while (1) switch (_context2.prev = _context2.next) { | ||
case 0: | ||
requestBody = { | ||
identifiers: identifiers, | ||
customAttributes: customAttributes, | ||
targetApp: fetcherOptions.targetApp | ||
}; | ||
_context2.prev = 1; | ||
_context2.next = 4; | ||
return this.fetchRequest('/api/v2/frontend/experimentValues', 'POST', fetcherOptions, requestBody); | ||
case 4: | ||
return _context2.abrupt("return", _context2.sent); | ||
case 7: | ||
_context2.prev = 7; | ||
_context2.t0 = _context2["catch"](1); | ||
if (!(_context2.t0 instanceof Error)) { | ||
_context2.next = 11; | ||
break; | ||
} | ||
throw _context2.t0; | ||
case 11: | ||
throw Error('Failed to retrieve experiment values'); | ||
case 12: | ||
case "end": | ||
return _context2.stop(); | ||
} | ||
}, _callee2, this, [[1, 7]]); | ||
})); | ||
function fetchExperimentValues(_x2, _x3, _x4) { | ||
return _fetchExperimentValues.apply(this, arguments); | ||
} | ||
} else if (perimeter === PerimeterType.COMMERCIAL) { | ||
switch (serviceEnv) { | ||
case FeatureGateEnvironment.Development: | ||
return DEV_BASE_URL; | ||
case FeatureGateEnvironment.Staging: | ||
return STAGING_BASE_URL; | ||
default: | ||
return PROD_BASE_URL; | ||
return fetchExperimentValues; | ||
}() | ||
}, { | ||
key: "handleResponseError", | ||
value: function () { | ||
var _handleResponseError = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee3(response) { | ||
var body; | ||
return _regenerator.default.wrap(function _callee3$(_context3) { | ||
while (1) switch (_context3.prev = _context3.next) { | ||
case 0: | ||
if (response.ok) { | ||
_context3.next = 5; | ||
break; | ||
} | ||
_context3.next = 3; | ||
return response.text(); | ||
case 3: | ||
body = _context3.sent; | ||
throw new _errors.ResponseError("Non 2xx response status received, status: ".concat(response.status, ", body: ").concat(JSON.stringify(body))); | ||
case 5: | ||
if (!(response.status === 204)) { | ||
_context3.next = 7; | ||
break; | ||
} | ||
throw new _errors.ResponseError('Unexpected 204 response'); | ||
case 7: | ||
case "end": | ||
return _context3.stop(); | ||
} | ||
}, _callee3); | ||
})); | ||
function handleResponseError(_x5) { | ||
return _handleResponseError.apply(this, arguments); | ||
} | ||
} else { | ||
throw new Error(`Invalid perimeter "${perimeter}"`); | ||
return handleResponseError; | ||
}() | ||
}, { | ||
key: "extractResponseBody", | ||
value: function () { | ||
var _extractResponseBody = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee4(response) { | ||
var value; | ||
return _regenerator.default.wrap(function _callee4$(_context4) { | ||
while (1) switch (_context4.prev = _context4.next) { | ||
case 0: | ||
_context4.next = 2; | ||
return response.text(); | ||
case 2: | ||
value = _context4.sent; | ||
return _context4.abrupt("return", JSON.parse(value)); | ||
case 4: | ||
case "end": | ||
return _context4.stop(); | ||
} | ||
}, _callee4); | ||
})); | ||
function extractResponseBody(_x6) { | ||
return _extractResponseBody.apply(this, arguments); | ||
} | ||
return extractResponseBody; | ||
}() | ||
}, { | ||
key: "getBaseUrl", | ||
value: function getBaseUrl(serviceEnv) { | ||
var useGatewayUrl = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false; | ||
var perimeter = arguments.length > 2 ? arguments[2] : undefined; | ||
if (useGatewayUrl) { | ||
return GATEWAY_BASE_URL; | ||
} | ||
if (perimeter === _types.PerimeterType.FEDRAMP_MODERATE) { | ||
switch (serviceEnv) { | ||
case _types.FeatureGateEnvironment.Production: | ||
return FEDM_PROD_BASE_URL; | ||
case _types.FeatureGateEnvironment.Staging: | ||
return FEDM_STAGING_BASE_URL; | ||
default: | ||
throw new Error("Invalid environment \"".concat(serviceEnv, "\" for \"").concat(perimeter, "\" perimeter")); | ||
} | ||
} else if (perimeter === _types.PerimeterType.COMMERCIAL) { | ||
switch (serviceEnv) { | ||
case _types.FeatureGateEnvironment.Development: | ||
return DEV_BASE_URL; | ||
case _types.FeatureGateEnvironment.Staging: | ||
return STAGING_BASE_URL; | ||
default: | ||
return PROD_BASE_URL; | ||
} | ||
} else { | ||
throw new Error("Invalid perimeter \"".concat(perimeter, "\"")); | ||
} | ||
} | ||
} | ||
static async fetchRequest(path, method, fetcherOptions, body) { | ||
const baseUrl = Fetcher.getBaseUrl(fetcherOptions.environment, fetcherOptions.useGatewayURL, fetcherOptions.perimeter); | ||
const fetchTimeout = fetcherOptions.fetchTimeoutMs || DEFAULT_REQUEST_TIMEOUT_MS; | ||
let abortSignal; | ||
if (AbortSignal.timeout) { | ||
abortSignal = AbortSignal.timeout(fetchTimeout); | ||
} else if (AbortController) { | ||
const abortController = new AbortController(); | ||
abortSignal = abortController.signal; | ||
setTimeout(() => abortController.abort(), fetchTimeout); | ||
} | ||
const response = await fetch(`${baseUrl}${path}`, _objectSpread({ | ||
method, | ||
headers: { | ||
'Content-Type': 'application/json', | ||
'X-Client-Name': 'feature-gate-js-client', | ||
'X-Client-Version': CLIENT_VERSION, | ||
'X-API-KEY': fetcherOptions.apiKey | ||
}, | ||
signal: abortSignal | ||
}, body && { | ||
body: JSON.stringify(body) | ||
})); | ||
await this.handleResponseError(response); | ||
return await this.extractResponseBody(response); | ||
} | ||
} | ||
}, { | ||
key: "fetchRequest", | ||
value: function () { | ||
var _fetchRequest = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee5(path, method, fetcherOptions, body) { | ||
var baseUrl, fetchTimeout, abortSignal, abortController, response; | ||
return _regenerator.default.wrap(function _callee5$(_context5) { | ||
while (1) switch (_context5.prev = _context5.next) { | ||
case 0: | ||
baseUrl = Fetcher.getBaseUrl(fetcherOptions.environment, fetcherOptions.useGatewayURL, fetcherOptions.perimeter); | ||
fetchTimeout = fetcherOptions.fetchTimeoutMs || DEFAULT_REQUEST_TIMEOUT_MS; | ||
if (AbortSignal.timeout) { | ||
abortSignal = AbortSignal.timeout(fetchTimeout); | ||
} else if (AbortController) { | ||
abortController = new AbortController(); | ||
abortSignal = abortController.signal; | ||
setTimeout(function () { | ||
return abortController.abort(); | ||
}, fetchTimeout); | ||
} | ||
_context5.next = 5; | ||
return fetch("".concat(baseUrl).concat(path), _objectSpread({ | ||
method: method, | ||
headers: { | ||
'Content-Type': 'application/json', | ||
'X-Client-Name': 'feature-gate-js-client', | ||
'X-Client-Version': _version.CLIENT_VERSION, | ||
'X-API-KEY': fetcherOptions.apiKey | ||
}, | ||
signal: abortSignal | ||
}, body && { | ||
body: JSON.stringify(body) | ||
})); | ||
case 5: | ||
response = _context5.sent; | ||
_context5.next = 8; | ||
return this.handleResponseError(response); | ||
case 8: | ||
_context5.next = 10; | ||
return this.extractResponseBody(response); | ||
case 10: | ||
return _context5.abrupt("return", _context5.sent); | ||
case 11: | ||
case "end": | ||
return _context5.stop(); | ||
} | ||
}, _callee5, this); | ||
})); | ||
function fetchRequest(_x7, _x8, _x9, _x10) { | ||
return _fetchRequest.apply(this, arguments); | ||
} | ||
return fetchRequest; | ||
}() | ||
}]); | ||
}(); |
@@ -1,1 +0,13 @@ | ||
export { default } from './Fetcher'; | ||
"use strict"; | ||
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); | ||
Object.defineProperty(exports, "__esModule", { | ||
value: true | ||
}); | ||
Object.defineProperty(exports, "default", { | ||
enumerable: true, | ||
get: function get() { | ||
return _Fetcher.default; | ||
} | ||
}); | ||
var _Fetcher = _interopRequireDefault(require("./Fetcher")); |
@@ -1,1 +0,5 @@ | ||
export {}; | ||
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { | ||
value: true | ||
}); |
@@ -1,6 +0,23 @@ | ||
import _defineProperty from "@babel/runtime/helpers/defineProperty"; | ||
"use strict"; | ||
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); | ||
Object.defineProperty(exports, "__esModule", { | ||
value: true | ||
}); | ||
exports.NoFetchDataAdapter = void 0; | ||
var _regenerator = _interopRequireDefault(require("@babel/runtime/regenerator")); | ||
var _asyncToGenerator2 = _interopRequireDefault(require("@babel/runtime/helpers/asyncToGenerator")); | ||
var _classCallCheck2 = _interopRequireDefault(require("@babel/runtime/helpers/classCallCheck")); | ||
var _createClass2 = _interopRequireDefault(require("@babel/runtime/helpers/createClass")); | ||
var _possibleConstructorReturn2 = _interopRequireDefault(require("@babel/runtime/helpers/possibleConstructorReturn")); | ||
var _getPrototypeOf2 = _interopRequireDefault(require("@babel/runtime/helpers/getPrototypeOf")); | ||
var _get2 = _interopRequireDefault(require("@babel/runtime/helpers/get")); | ||
var _inherits2 = _interopRequireDefault(require("@babel/runtime/helpers/inherits")); | ||
var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty")); | ||
var _jsClient = require("@statsig/js-client"); | ||
function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; } | ||
function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; } | ||
import { _getFullUserHash, _getStorageKey, DataAdapterCachePrefix, DataAdapterCore, StableID } from '@statsig/js-client'; | ||
function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { (0, _defineProperty2.default)(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; } | ||
function _callSuper(t, o, e) { return o = (0, _getPrototypeOf2.default)(o), (0, _possibleConstructorReturn2.default)(t, _isNativeReflectConstruct() ? Reflect.construct(o, e || [], (0, _getPrototypeOf2.default)(t).constructor) : o.apply(t, e)); } | ||
function _isNativeReflectConstruct() { try { var t = !Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {})); } catch (t) {} return (_isNativeReflectConstruct = function _isNativeReflectConstruct() { return !!t; })(); } | ||
function _superPropGet(t, o, e, r) { var p = (0, _get2.default)((0, _getPrototypeOf2.default)(1 & r ? t.prototype : t), o, e); return 2 & r && "function" == typeof p ? function (t) { return p.apply(e, t); } : p; } | ||
/** | ||
@@ -11,6 +28,9 @@ * Data adapter which only uses bootstrap data and will never fetch from network or cache. | ||
*/ | ||
export class NoFetchDataAdapter extends DataAdapterCore { | ||
constructor() { | ||
super('NoFetchDataAdapter', 'nofetch'); | ||
_defineProperty(this, "bootstrapResult", null); | ||
var NoFetchDataAdapter = exports.NoFetchDataAdapter = /*#__PURE__*/function (_DataAdapterCore) { | ||
function NoFetchDataAdapter() { | ||
var _this; | ||
(0, _classCallCheck2.default)(this, NoFetchDataAdapter); | ||
_this = _callSuper(this, NoFetchDataAdapter, ['NoFetchDataAdapter', 'nofetch']); | ||
(0, _defineProperty2.default)(_this, "bootstrapResult", null); | ||
return _this; | ||
} | ||
@@ -22,48 +42,110 @@ | ||
*/ | ||
setBootstrapData(data) { | ||
this.bootstrapResult = data ? { | ||
source: 'Bootstrap', | ||
data: JSON.stringify(data), | ||
receivedAt: Date.now(), | ||
stableID: StableID.get(this._getSdkKey()), | ||
fullUserHash: null | ||
} : null; | ||
} | ||
async prefetchData(_user, _options) {} | ||
async getDataAsync(_current, user, _options) { | ||
return this.bootstrapResult && _objectSpread(_objectSpread({}, this.bootstrapResult), {}, { | ||
fullUserHash: _getFullUserHash(user) | ||
}); | ||
} | ||
getDataSync(user) { | ||
return this.bootstrapResult && _objectSpread(_objectSpread({}, this.bootstrapResult), {}, { | ||
fullUserHash: _getFullUserHash(user) | ||
}); | ||
} | ||
async _fetchFromNetwork(_current, _user, _options) { | ||
return null; | ||
} | ||
_getCacheKey(user) { | ||
// Same logic as default data adapter | ||
// https://github.com/statsig-io/js-client-monorepo/blob/main/packages/js-client/src/StatsigEvaluationsDataAdapter.ts | ||
const key = _getStorageKey(this._getSdkKey(), user); | ||
return `${DataAdapterCachePrefix}.${this._cacheSuffix}.${key}`; | ||
} | ||
_isCachedResultValidFor204(_result, _user) { | ||
return false; | ||
} | ||
setDataLegacy(data, user) { | ||
super.setData(data, user); | ||
} | ||
(0, _inherits2.default)(NoFetchDataAdapter, _DataAdapterCore); | ||
return (0, _createClass2.default)(NoFetchDataAdapter, [{ | ||
key: "setBootstrapData", | ||
value: function setBootstrapData(data) { | ||
this.bootstrapResult = data ? { | ||
source: 'Bootstrap', | ||
data: JSON.stringify(data), | ||
receivedAt: Date.now(), | ||
stableID: _jsClient.StableID.get(this._getSdkKey()), | ||
fullUserHash: null | ||
} : null; | ||
} | ||
}, { | ||
key: "prefetchData", | ||
value: function () { | ||
var _prefetchData = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee(_user, _options) { | ||
return _regenerator.default.wrap(function _callee$(_context) { | ||
while (1) switch (_context.prev = _context.next) { | ||
case 0: | ||
case "end": | ||
return _context.stop(); | ||
} | ||
}, _callee); | ||
})); | ||
function prefetchData(_x, _x2) { | ||
return _prefetchData.apply(this, arguments); | ||
} | ||
return prefetchData; | ||
}() | ||
}, { | ||
key: "getDataAsync", | ||
value: function () { | ||
var _getDataAsync = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee2(_current, user, _options) { | ||
return _regenerator.default.wrap(function _callee2$(_context2) { | ||
while (1) switch (_context2.prev = _context2.next) { | ||
case 0: | ||
return _context2.abrupt("return", this.bootstrapResult && _objectSpread(_objectSpread({}, this.bootstrapResult), {}, { | ||
fullUserHash: (0, _jsClient._getFullUserHash)(user) | ||
})); | ||
case 1: | ||
case "end": | ||
return _context2.stop(); | ||
} | ||
}, _callee2, this); | ||
})); | ||
function getDataAsync(_x3, _x4, _x5) { | ||
return _getDataAsync.apply(this, arguments); | ||
} | ||
return getDataAsync; | ||
}() | ||
}, { | ||
key: "getDataSync", | ||
value: function getDataSync(user) { | ||
return this.bootstrapResult && _objectSpread(_objectSpread({}, this.bootstrapResult), {}, { | ||
fullUserHash: (0, _jsClient._getFullUserHash)(user) | ||
}); | ||
} | ||
}, { | ||
key: "_fetchFromNetwork", | ||
value: function () { | ||
var _fetchFromNetwork2 = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee3(_current, _user, _options) { | ||
return _regenerator.default.wrap(function _callee3$(_context3) { | ||
while (1) switch (_context3.prev = _context3.next) { | ||
case 0: | ||
return _context3.abrupt("return", null); | ||
case 1: | ||
case "end": | ||
return _context3.stop(); | ||
} | ||
}, _callee3); | ||
})); | ||
function _fetchFromNetwork(_x6, _x7, _x8) { | ||
return _fetchFromNetwork2.apply(this, arguments); | ||
} | ||
return _fetchFromNetwork; | ||
}() | ||
}, { | ||
key: "_getCacheKey", | ||
value: function _getCacheKey(user) { | ||
// Same logic as default data adapter | ||
// https://github.com/statsig-io/js-client-monorepo/blob/main/packages/js-client/src/StatsigEvaluationsDataAdapter.ts | ||
var key = (0, _jsClient._getStorageKey)(this._getSdkKey(), user); | ||
return "".concat(_jsClient.DataAdapterCachePrefix, ".").concat(this._cacheSuffix, ".").concat(key); | ||
} | ||
}, { | ||
key: "_isCachedResultValidFor204", | ||
value: function _isCachedResultValidFor204(_result, _user) { | ||
return false; | ||
} | ||
}, { | ||
key: "setDataLegacy", | ||
value: function setDataLegacy(data, user) { | ||
_superPropGet(NoFetchDataAdapter, "setData", this, 3)([data, user]); | ||
} | ||
// Do not stringify options property since that includes this adapter and will | ||
// cause a circular reference when Statsig sends diagnostic events and including | ||
// values is not necessary and makes the result huge | ||
toJSON() { | ||
const result = _objectSpread({}, this); | ||
delete result._options; | ||
delete result._inMemoryCache; | ||
delete result.bootstrapResult; | ||
return result; | ||
} | ||
} | ||
// Do not stringify options property since that includes this adapter and will | ||
// cause a circular reference when Statsig sends diagnostic events and including | ||
// values is not necessary and makes the result huge | ||
}, { | ||
key: "toJSON", | ||
value: function toJSON() { | ||
var result = _objectSpread({}, this); | ||
delete result._options; | ||
delete result._inMemoryCache; | ||
delete result.bootstrapResult; | ||
return result; | ||
} | ||
}]); | ||
}(_jsClient.DataAdapterCore); |
@@ -1,13 +0,25 @@ | ||
import _defineProperty from "@babel/runtime/helpers/defineProperty"; | ||
"use strict"; | ||
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); | ||
Object.defineProperty(exports, "__esModule", { | ||
value: true | ||
}); | ||
exports.PersistentOverrideAdapter = exports.LOCAL_STORAGE_KEY = void 0; | ||
var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty")); | ||
var _slicedToArray2 = _interopRequireDefault(require("@babel/runtime/helpers/slicedToArray")); | ||
var _classCallCheck2 = _interopRequireDefault(require("@babel/runtime/helpers/classCallCheck")); | ||
var _createClass2 = _interopRequireDefault(require("@babel/runtime/helpers/createClass")); | ||
var _clientCore = require("@statsig/client-core"); | ||
function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; } | ||
function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; } | ||
import { _DJB2, _makeTypedGet } from '@statsig/client-core'; | ||
const LOCAL_OVERRIDE_REASON = 'LocalOverride:Recognized'; | ||
export const LOCAL_STORAGE_KEY = 'STATSIG_OVERRIDES'; | ||
const LEGACY_LOCAL_STORAGE_KEY = 'STATSIG_JS_LITE_LOCAL_OVERRIDES'; | ||
const makeEmptyStore = () => ({ | ||
gates: {}, | ||
configs: {}, | ||
layers: {} | ||
}); | ||
function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { (0, _defineProperty2.default)(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; } | ||
var LOCAL_OVERRIDE_REASON = 'LocalOverride:Recognized'; | ||
var LOCAL_STORAGE_KEY = exports.LOCAL_STORAGE_KEY = 'STATSIG_OVERRIDES'; | ||
var LEGACY_LOCAL_STORAGE_KEY = 'STATSIG_JS_LITE_LOCAL_OVERRIDES'; | ||
var makeEmptyStore = function makeEmptyStore() { | ||
return { | ||
gates: {}, | ||
configs: {}, | ||
layers: {} | ||
}; | ||
}; | ||
@@ -22,162 +34,227 @@ /** | ||
*/ | ||
export class PersistentOverrideAdapter { | ||
constructor(localStorageKey) { | ||
var PersistentOverrideAdapter = exports.PersistentOverrideAdapter = /*#__PURE__*/function () { | ||
function PersistentOverrideAdapter(localStorageKey) { | ||
(0, _classCallCheck2.default)(this, PersistentOverrideAdapter); | ||
this._overrides = makeEmptyStore(); | ||
this._localStorageKey = localStorageKey; | ||
} | ||
parseStoredOverrides(localStorageKey) { | ||
try { | ||
const json = window.localStorage.getItem(localStorageKey); | ||
if (!json) { | ||
return (0, _createClass2.default)(PersistentOverrideAdapter, [{ | ||
key: "parseStoredOverrides", | ||
value: function parseStoredOverrides(localStorageKey) { | ||
try { | ||
var json = window.localStorage.getItem(localStorageKey); | ||
if (!json) { | ||
return makeEmptyStore(); | ||
} | ||
return JSON.parse(json); | ||
} catch (_unused) { | ||
return makeEmptyStore(); | ||
} | ||
return JSON.parse(json); | ||
} catch (_unused) { | ||
return makeEmptyStore(); | ||
} | ||
} | ||
mergeOverrides(...allOverrides) { | ||
const merged = makeEmptyStore(); | ||
for (const overrides of allOverrides) { | ||
for (const [name, value] of Object.entries((_overrides$gates = overrides.gates) !== null && _overrides$gates !== void 0 ? _overrides$gates : {})) { | ||
var _overrides$gates; | ||
merged.gates[name] = value; | ||
}, { | ||
key: "mergeOverrides", | ||
value: function mergeOverrides() { | ||
var merged = makeEmptyStore(); | ||
for (var _len = arguments.length, allOverrides = new Array(_len), _key = 0; _key < _len; _key++) { | ||
allOverrides[_key] = arguments[_key]; | ||
} | ||
for (const [name, value] of Object.entries((_overrides$configs = overrides.configs) !== null && _overrides$configs !== void 0 ? _overrides$configs : {})) { | ||
var _overrides$configs; | ||
merged.configs[name] = value; | ||
for (var _i = 0, _allOverrides = allOverrides; _i < _allOverrides.length; _i++) { | ||
var overrides = _allOverrides[_i]; | ||
for (var _i2 = 0, _Object$entries = Object.entries((_overrides$gates = overrides.gates) !== null && _overrides$gates !== void 0 ? _overrides$gates : {}); _i2 < _Object$entries.length; _i2++) { | ||
var _overrides$gates; | ||
var _Object$entries$_i = (0, _slicedToArray2.default)(_Object$entries[_i2], 2), | ||
name = _Object$entries$_i[0], | ||
value = _Object$entries$_i[1]; | ||
merged.gates[name] = value; | ||
} | ||
for (var _i3 = 0, _Object$entries2 = Object.entries((_overrides$configs = overrides.configs) !== null && _overrides$configs !== void 0 ? _overrides$configs : {}); _i3 < _Object$entries2.length; _i3++) { | ||
var _overrides$configs; | ||
var _Object$entries2$_i = (0, _slicedToArray2.default)(_Object$entries2[_i3], 2), | ||
_name = _Object$entries2$_i[0], | ||
_value = _Object$entries2$_i[1]; | ||
merged.configs[_name] = _value; | ||
} | ||
for (var _i4 = 0, _Object$entries3 = Object.entries((_overrides$layers = overrides.layers) !== null && _overrides$layers !== void 0 ? _overrides$layers : {}); _i4 < _Object$entries3.length; _i4++) { | ||
var _overrides$layers; | ||
var _Object$entries3$_i = (0, _slicedToArray2.default)(_Object$entries3[_i4], 2), | ||
_name2 = _Object$entries3$_i[0], | ||
_value2 = _Object$entries3$_i[1]; | ||
merged.layers[_name2] = _value2; | ||
} | ||
} | ||
for (const [name, value] of Object.entries((_overrides$layers = overrides.layers) !== null && _overrides$layers !== void 0 ? _overrides$layers : {})) { | ||
var _overrides$layers; | ||
merged.layers[name] = value; | ||
return merged; | ||
} | ||
}, { | ||
key: "initFromStoredOverrides", | ||
value: function initFromStoredOverrides() { | ||
this.setOverrides(this.mergeOverrides(this._overrides, this.parseStoredOverrides(LEGACY_LOCAL_STORAGE_KEY), this.parseStoredOverrides(this._localStorageKey))); | ||
} | ||
}, { | ||
key: "saveOverrides", | ||
value: function saveOverrides() { | ||
try { | ||
window.localStorage.setItem(this._localStorageKey, JSON.stringify(this._overrides)); | ||
} catch (_unused2) { | ||
// ignored - window is not defined in non-browser environments, and we don't save things there | ||
// (things like SSR, etc) | ||
} | ||
} | ||
return merged; | ||
} | ||
initFromStoredOverrides() { | ||
this.setOverrides(this.mergeOverrides(this._overrides, this.parseStoredOverrides(LEGACY_LOCAL_STORAGE_KEY), this.parseStoredOverrides(this._localStorageKey))); | ||
} | ||
saveOverrides() { | ||
try { | ||
window.localStorage.setItem(this._localStorageKey, JSON.stringify(this._overrides)); | ||
} catch (_unused2) { | ||
// ignored - window is not defined in non-browser environments, and we don't save things there | ||
// (things like SSR, etc) | ||
}, { | ||
key: "getOverrides", | ||
value: function getOverrides() { | ||
return Object.fromEntries(Object.entries(this._overrides).map(function (_ref) { | ||
var _ref2 = (0, _slicedToArray2.default)(_ref, 2), | ||
key = _ref2[0], | ||
container = _ref2[1]; | ||
var record = {}; | ||
for (var _i5 = 0, _Object$entries4 = Object.entries(container); _i5 < _Object$entries4.length; _i5++) { | ||
var _Object$entries4$_i = (0, _slicedToArray2.default)(_Object$entries4[_i5], 2), | ||
name = _Object$entries4$_i[0], | ||
value = _Object$entries4$_i[1]; | ||
if (Object.prototype.hasOwnProperty.call(container, (0, _clientCore._DJB2)(name))) { | ||
record[name] = value; | ||
} | ||
} | ||
return [key, record]; | ||
})); | ||
} | ||
} | ||
getOverrides() { | ||
return Object.fromEntries(Object.entries(this._overrides).map(([key, container]) => { | ||
const record = {}; | ||
for (const [name, value] of Object.entries(container)) { | ||
if (Object.prototype.hasOwnProperty.call(container, _DJB2(name))) { | ||
record[name] = value; | ||
}, { | ||
key: "setOverrides", | ||
value: function setOverrides(overrides) { | ||
var newOverrides = _objectSpread(_objectSpread({}, makeEmptyStore()), overrides); | ||
for (var _i6 = 0, _Object$values = Object.values(newOverrides); _i6 < _Object$values.length; _i6++) { | ||
var container = _Object$values[_i6]; | ||
for (var _i7 = 0, _Object$entries5 = Object.entries(container); _i7 < _Object$entries5.length; _i7++) { | ||
var _Object$entries5$_i = (0, _slicedToArray2.default)(_Object$entries5[_i7], 2), | ||
name = _Object$entries5$_i[0], | ||
value = _Object$entries5$_i[1]; | ||
var hash = (0, _clientCore._DJB2)(name); | ||
if (!Object.prototype.hasOwnProperty.call(container, hash)) { | ||
container[hash] = value; | ||
} | ||
} | ||
} | ||
return [key, record]; | ||
})); | ||
} | ||
setOverrides(overrides) { | ||
const newOverrides = _objectSpread(_objectSpread({}, makeEmptyStore()), overrides); | ||
for (const container of Object.values(newOverrides)) { | ||
for (const [name, value] of Object.entries(container)) { | ||
const hash = _DJB2(name); | ||
if (!Object.prototype.hasOwnProperty.call(container, hash)) { | ||
container[hash] = value; | ||
} | ||
this._overrides = newOverrides; | ||
this.saveOverrides(); | ||
} | ||
}, { | ||
key: "overrideGate", | ||
value: function overrideGate(name, value) { | ||
this._overrides.gates[name] = value; | ||
this._overrides.gates[(0, _clientCore._DJB2)(name)] = value; | ||
this.saveOverrides(); | ||
} | ||
}, { | ||
key: "removeGateOverride", | ||
value: function removeGateOverride(name) { | ||
delete this._overrides.gates[name]; | ||
delete this._overrides.gates[(0, _clientCore._DJB2)(name)]; | ||
this.saveOverrides(); | ||
} | ||
}, { | ||
key: "getGateOverride", | ||
value: function getGateOverride(current, _user) { | ||
var _this$_overrides$gate; | ||
var overridden = (_this$_overrides$gate = this._overrides.gates[current.name]) !== null && _this$_overrides$gate !== void 0 ? _this$_overrides$gate : this._overrides.gates[(0, _clientCore._DJB2)(current.name)]; | ||
if (overridden == null) { | ||
return null; | ||
} | ||
return _objectSpread(_objectSpread({}, current), {}, { | ||
value: overridden, | ||
details: _objectSpread(_objectSpread({}, current.details), {}, { | ||
reason: LOCAL_OVERRIDE_REASON | ||
}) | ||
}); | ||
} | ||
this._overrides = newOverrides; | ||
this.saveOverrides(); | ||
} | ||
overrideGate(name, value) { | ||
this._overrides.gates[name] = value; | ||
this._overrides.gates[_DJB2(name)] = value; | ||
this.saveOverrides(); | ||
} | ||
removeGateOverride(name) { | ||
delete this._overrides.gates[name]; | ||
delete this._overrides.gates[_DJB2(name)]; | ||
this.saveOverrides(); | ||
} | ||
getGateOverride(current, _user) { | ||
var _this$_overrides$gate; | ||
const overridden = (_this$_overrides$gate = this._overrides.gates[current.name]) !== null && _this$_overrides$gate !== void 0 ? _this$_overrides$gate : this._overrides.gates[_DJB2(current.name)]; | ||
if (overridden == null) { | ||
return null; | ||
}, { | ||
key: "overrideDynamicConfig", | ||
value: function overrideDynamicConfig(name, value) { | ||
this._overrides.configs[name] = value; | ||
this._overrides.configs[(0, _clientCore._DJB2)(name)] = value; | ||
this.saveOverrides(); | ||
} | ||
return _objectSpread(_objectSpread({}, current), {}, { | ||
value: overridden, | ||
details: _objectSpread(_objectSpread({}, current.details), {}, { | ||
reason: LOCAL_OVERRIDE_REASON | ||
}) | ||
}); | ||
} | ||
overrideDynamicConfig(name, value) { | ||
this._overrides.configs[name] = value; | ||
this._overrides.configs[_DJB2(name)] = value; | ||
this.saveOverrides(); | ||
} | ||
removeDynamicConfigOverride(name) { | ||
delete this._overrides.configs[name]; | ||
delete this._overrides.configs[_DJB2(name)]; | ||
this.saveOverrides(); | ||
} | ||
getDynamicConfigOverride(current, _user) { | ||
return this._getConfigOverride(current, this._overrides.configs); | ||
} | ||
overrideExperiment(name, value) { | ||
this._overrides.configs[name] = value; | ||
this._overrides.configs[_DJB2(name)] = value; | ||
this.saveOverrides(); | ||
} | ||
removeExperimentOverride(name) { | ||
delete this._overrides.configs[name]; | ||
delete this._overrides.configs[_DJB2(name)]; | ||
this.saveOverrides(); | ||
} | ||
getExperimentOverride(current, _user) { | ||
return this._getConfigOverride(current, this._overrides.configs); | ||
} | ||
overrideLayer(name, value) { | ||
this._overrides.layers[name] = value; | ||
this._overrides.layers[_DJB2(name)] = value; | ||
this.saveOverrides(); | ||
} | ||
removeLayerOverride(name) { | ||
delete this._overrides.layers[name]; | ||
delete this._overrides.layers[_DJB2(name)]; | ||
this.saveOverrides(); | ||
} | ||
removeAllOverrides() { | ||
this._overrides = makeEmptyStore(); | ||
window.localStorage.removeItem(LOCAL_STORAGE_KEY); | ||
} | ||
getLayerOverride(current, _user) { | ||
var _this$_overrides$laye; | ||
const overridden = (_this$_overrides$laye = this._overrides.layers[current.name]) !== null && _this$_overrides$laye !== void 0 ? _this$_overrides$laye : this._overrides.layers[_DJB2(current.name)]; | ||
if (overridden == null) { | ||
return null; | ||
}, { | ||
key: "removeDynamicConfigOverride", | ||
value: function removeDynamicConfigOverride(name) { | ||
delete this._overrides.configs[name]; | ||
delete this._overrides.configs[(0, _clientCore._DJB2)(name)]; | ||
this.saveOverrides(); | ||
} | ||
return _objectSpread(_objectSpread({}, current), {}, { | ||
__value: overridden, | ||
get: _makeTypedGet(current.name, overridden), | ||
details: _objectSpread(_objectSpread({}, current.details), {}, { | ||
reason: LOCAL_OVERRIDE_REASON | ||
}) | ||
}); | ||
} | ||
_getConfigOverride(current, lookup) { | ||
var _lookup$current$name; | ||
const overridden = (_lookup$current$name = lookup[current.name]) !== null && _lookup$current$name !== void 0 ? _lookup$current$name : lookup[_DJB2(current.name)]; | ||
if (overridden == null) { | ||
return null; | ||
}, { | ||
key: "getDynamicConfigOverride", | ||
value: function getDynamicConfigOverride(current, _user) { | ||
return this._getConfigOverride(current, this._overrides.configs); | ||
} | ||
return _objectSpread(_objectSpread({}, current), {}, { | ||
value: overridden, | ||
get: _makeTypedGet(current.name, overridden), | ||
details: _objectSpread(_objectSpread({}, current.details), {}, { | ||
reason: LOCAL_OVERRIDE_REASON | ||
}) | ||
}); | ||
} | ||
} | ||
}, { | ||
key: "overrideExperiment", | ||
value: function overrideExperiment(name, value) { | ||
this._overrides.configs[name] = value; | ||
this._overrides.configs[(0, _clientCore._DJB2)(name)] = value; | ||
this.saveOverrides(); | ||
} | ||
}, { | ||
key: "removeExperimentOverride", | ||
value: function removeExperimentOverride(name) { | ||
delete this._overrides.configs[name]; | ||
delete this._overrides.configs[(0, _clientCore._DJB2)(name)]; | ||
this.saveOverrides(); | ||
} | ||
}, { | ||
key: "getExperimentOverride", | ||
value: function getExperimentOverride(current, _user) { | ||
return this._getConfigOverride(current, this._overrides.configs); | ||
} | ||
}, { | ||
key: "overrideLayer", | ||
value: function overrideLayer(name, value) { | ||
this._overrides.layers[name] = value; | ||
this._overrides.layers[(0, _clientCore._DJB2)(name)] = value; | ||
this.saveOverrides(); | ||
} | ||
}, { | ||
key: "removeLayerOverride", | ||
value: function removeLayerOverride(name) { | ||
delete this._overrides.layers[name]; | ||
delete this._overrides.layers[(0, _clientCore._DJB2)(name)]; | ||
this.saveOverrides(); | ||
} | ||
}, { | ||
key: "removeAllOverrides", | ||
value: function removeAllOverrides() { | ||
this._overrides = makeEmptyStore(); | ||
window.localStorage.removeItem(LOCAL_STORAGE_KEY); | ||
} | ||
}, { | ||
key: "getLayerOverride", | ||
value: function getLayerOverride(current, _user) { | ||
var _this$_overrides$laye; | ||
var overridden = (_this$_overrides$laye = this._overrides.layers[current.name]) !== null && _this$_overrides$laye !== void 0 ? _this$_overrides$laye : this._overrides.layers[(0, _clientCore._DJB2)(current.name)]; | ||
if (overridden == null) { | ||
return null; | ||
} | ||
return _objectSpread(_objectSpread({}, current), {}, { | ||
__value: overridden, | ||
get: (0, _clientCore._makeTypedGet)(current.name, overridden), | ||
details: _objectSpread(_objectSpread({}, current.details), {}, { | ||
reason: LOCAL_OVERRIDE_REASON | ||
}) | ||
}); | ||
} | ||
}, { | ||
key: "_getConfigOverride", | ||
value: function _getConfigOverride(current, lookup) { | ||
var _lookup$current$name; | ||
var overridden = (_lookup$current$name = lookup[current.name]) !== null && _lookup$current$name !== void 0 ? _lookup$current$name : lookup[(0, _clientCore._DJB2)(current.name)]; | ||
if (overridden == null) { | ||
return null; | ||
} | ||
return _objectSpread(_objectSpread({}, current), {}, { | ||
value: overridden, | ||
get: (0, _clientCore._makeTypedGet)(current.name, overridden), | ||
details: _objectSpread(_objectSpread({}, current.details), {}, { | ||
reason: LOCAL_OVERRIDE_REASON | ||
}) | ||
}); | ||
} | ||
}]); | ||
}(); |
@@ -0,5 +1,10 @@ | ||
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { | ||
value: true | ||
}); | ||
exports.PerimeterType = exports.NON_BOOLEAN_VALUE = exports.FeatureGateEnvironment = void 0; | ||
/** | ||
* The identifiers for the user. Options are restricted to the set that is currently supported. | ||
*/ | ||
/** | ||
@@ -13,3 +18,2 @@ * Base client options. Does not include any options specific to providers | ||
*/ | ||
/** | ||
@@ -23,8 +27,6 @@ * The options for the client. | ||
*/ | ||
/** | ||
* The custom attributes for the user. | ||
*/ | ||
export let FeatureGateEnvironment = /*#__PURE__*/function (FeatureGateEnvironment) { | ||
var FeatureGateEnvironment = exports.FeatureGateEnvironment = /*#__PURE__*/function (FeatureGateEnvironment) { | ||
FeatureGateEnvironment["Development"] = "development"; | ||
@@ -34,6 +36,4 @@ FeatureGateEnvironment["Staging"] = "staging"; | ||
return FeatureGateEnvironment; | ||
}({}); | ||
// If adding new values here, please check FeatureGates.getDefaultPerimeter to make sure it still returns something sensible. | ||
export let PerimeterType = /*#__PURE__*/function (PerimeterType) { | ||
}({}); // If adding new values here, please check FeatureGates.getDefaultPerimeter to make sure it still returns something sensible. | ||
var PerimeterType = exports.PerimeterType = /*#__PURE__*/function (PerimeterType) { | ||
PerimeterType["COMMERCIAL"] = "commercial"; | ||
@@ -43,5 +43,5 @@ PerimeterType["FEDRAMP_MODERATE"] = "fedramp-moderate"; | ||
}({}); | ||
export const NON_BOOLEAN_VALUE = 'non_boolean'; | ||
var NON_BOOLEAN_VALUE = exports.NON_BOOLEAN_VALUE = 'non_boolean'; | ||
// Type magic to get the JSDoc comments from the Client class methods to appear on the static | ||
// methods in FeatureGates where the property name and function type are identical |
@@ -1,18 +0,28 @@ | ||
import _objectWithoutProperties from "@babel/runtime/helpers/objectWithoutProperties"; | ||
import _defineProperty from "@babel/runtime/helpers/defineProperty"; | ||
const _excluded = ["api", "disableCurrentPageLogging", "loggingIntervalMillis", "loggingBufferMaxSize", "localMode", "eventLoggingApi", "eventLoggingApiForRetries", "disableLocalStorage", "ignoreWindowUndefined", "disableAllLogging", "initTimeoutMs", "disableNetworkKeepalive", "overrideStableID", "disableErrorLogging", "disableAutoMetricsLogging"]; | ||
"use strict"; | ||
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); | ||
Object.defineProperty(exports, "__esModule", { | ||
value: true | ||
}); | ||
exports.toStatsigUser = exports.shallowEquals = exports.migrateInitializationOptions = exports.migrateEvaluationDetails = exports.getOptionsWithDefaults = void 0; | ||
var _objectWithoutProperties2 = _interopRequireDefault(require("@babel/runtime/helpers/objectWithoutProperties")); | ||
var _slicedToArray2 = _interopRequireDefault(require("@babel/runtime/helpers/slicedToArray")); | ||
var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty")); | ||
var _jsClient = require("@statsig/js-client"); | ||
var _atlassianContext = require("@atlaskit/atlassian-context"); | ||
var _types = require("./compat/types"); | ||
var _types2 = require("./types"); | ||
var _excluded = ["api", "disableCurrentPageLogging", "loggingIntervalMillis", "loggingBufferMaxSize", "localMode", "eventLoggingApi", "eventLoggingApiForRetries", "disableLocalStorage", "ignoreWindowUndefined", "disableAllLogging", "initTimeoutMs", "disableNetworkKeepalive", "overrideStableID", "disableErrorLogging", "disableAutoMetricsLogging"]; | ||
function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; } | ||
function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; } | ||
import { StableID } from '@statsig/js-client'; | ||
import { isFedRamp } from '@atlaskit/atlassian-context'; | ||
import { EvaluationReason } from './compat/types'; | ||
import { PerimeterType } from './types'; | ||
export const getOptionsWithDefaults = options => _objectSpread({ | ||
/** | ||
* If more federal PerimeterTypes are added in the future, this should be updated so | ||
* that isFedRamp() === true always returns the strictest perimeter. | ||
*/ | ||
perimeter: isFedRamp() ? PerimeterType.FEDRAMP_MODERATE : PerimeterType.COMMERCIAL | ||
}, options); | ||
export const shallowEquals = (objectA, objectB) => { | ||
function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { (0, _defineProperty2.default)(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; } | ||
var getOptionsWithDefaults = exports.getOptionsWithDefaults = function getOptionsWithDefaults(options) { | ||
return _objectSpread({ | ||
/** | ||
* If more federal PerimeterTypes are added in the future, this should be updated so | ||
* that isFedRamp() === true always returns the strictest perimeter. | ||
*/ | ||
perimeter: (0, _atlassianContext.isFedRamp)() ? _types2.PerimeterType.FEDRAMP_MODERATE : _types2.PerimeterType.COMMERCIAL | ||
}, options); | ||
}; | ||
var shallowEquals = exports.shallowEquals = function shallowEquals(objectA, objectB) { | ||
if (!objectA && !objectB) { | ||
@@ -24,13 +34,21 @@ return true; | ||
} | ||
const aEntries = Object.entries(objectA); | ||
const bEntries = Object.entries(objectB); | ||
var aEntries = Object.entries(objectA); | ||
var bEntries = Object.entries(objectB); | ||
if (aEntries.length !== bEntries.length) { | ||
return false; | ||
} | ||
const ascendingKeyOrder = ([key1], [key2]) => key1.localeCompare(key2); | ||
var ascendingKeyOrder = function ascendingKeyOrder(_ref, _ref2) { | ||
var _ref3 = (0, _slicedToArray2.default)(_ref, 1), | ||
key1 = _ref3[0]; | ||
var _ref4 = (0, _slicedToArray2.default)(_ref2, 1), | ||
key2 = _ref4[0]; | ||
return key1.localeCompare(key2); | ||
}; | ||
aEntries.sort(ascendingKeyOrder); | ||
bEntries.sort(ascendingKeyOrder); | ||
for (let i = 0; i < aEntries.length; i++) { | ||
const [, aValue] = aEntries[i]; | ||
const [, bValue] = bEntries[i]; | ||
for (var i = 0; i < aEntries.length; i++) { | ||
var _aEntries$i = (0, _slicedToArray2.default)(aEntries[i], 2), | ||
aValue = _aEntries$i[1]; | ||
var _bEntries$i = (0, _slicedToArray2.default)(bEntries[i], 2), | ||
bValue = _bEntries$i[1]; | ||
if (aValue !== bValue) { | ||
@@ -47,6 +65,6 @@ return false; | ||
*/ | ||
export const toStatsigUser = (identifiers, customAttributes, sdkKey) => { | ||
const user = { | ||
var toStatsigUser = exports.toStatsigUser = function toStatsigUser(identifiers, customAttributes, sdkKey) { | ||
var user = { | ||
customIDs: !(customAttributes !== null && customAttributes !== void 0 && customAttributes.stableID) && sdkKey ? _objectSpread({ | ||
stableID: StableID.get(sdkKey) | ||
stableID: _jsClient.StableID.get(sdkKey) | ||
}, identifiers) : identifiers, | ||
@@ -60,28 +78,22 @@ custom: customAttributes | ||
}; | ||
export const migrateInitializationOptions = options => { | ||
const { | ||
api, | ||
disableCurrentPageLogging, | ||
loggingIntervalMillis, | ||
loggingBufferMaxSize, | ||
localMode, | ||
eventLoggingApi, | ||
eventLoggingApiForRetries, | ||
disableLocalStorage, | ||
ignoreWindowUndefined, | ||
disableAllLogging, | ||
// No equivalent but is pointless anyway since our Statsig init is synchronous | ||
initTimeoutMs: _initTimeoutMs, | ||
// No equivalent in new client but probably not important? | ||
disableNetworkKeepalive: _disableNetworkKeepalive, | ||
// Needs to be implemented manually but unused according to zoekt | ||
overrideStableID: _overrideStableID, | ||
// No equivalent for these but can't see them actually used anywhere in old client? | ||
disableErrorLogging: _disableErrorLogging, | ||
disableAutoMetricsLogging: _disableAutoMetricsLogging | ||
} = options, | ||
rest = _objectWithoutProperties(options, _excluded); | ||
var migrateInitializationOptions = exports.migrateInitializationOptions = function migrateInitializationOptions(options) { | ||
var api = options.api, | ||
disableCurrentPageLogging = options.disableCurrentPageLogging, | ||
loggingIntervalMillis = options.loggingIntervalMillis, | ||
loggingBufferMaxSize = options.loggingBufferMaxSize, | ||
localMode = options.localMode, | ||
eventLoggingApi = options.eventLoggingApi, | ||
eventLoggingApiForRetries = options.eventLoggingApiForRetries, | ||
disableLocalStorage = options.disableLocalStorage, | ||
ignoreWindowUndefined = options.ignoreWindowUndefined, | ||
disableAllLogging = options.disableAllLogging, | ||
_initTimeoutMs = options.initTimeoutMs, | ||
_disableNetworkKeepalive = options.disableNetworkKeepalive, | ||
_overrideStableID = options.overrideStableID, | ||
_disableErrorLogging = options.disableErrorLogging, | ||
_disableAutoMetricsLogging = options.disableAutoMetricsLogging, | ||
rest = (0, _objectWithoutProperties2.default)(options, _excluded); | ||
return _objectSpread(_objectSpread({}, rest), {}, { | ||
networkConfig: { | ||
api, | ||
api: api, | ||
logEventUrl: eventLoggingApi ? eventLoggingApi + 'rgstr' : undefined, | ||
@@ -93,3 +105,3 @@ logEventFallbackUrls: eventLoggingApiForRetries ? [eventLoggingApiForRetries] : undefined, | ||
loggingIntervalMs: loggingIntervalMillis, | ||
loggingBufferMaxSize, | ||
loggingBufferMaxSize: loggingBufferMaxSize, | ||
disableStorage: disableLocalStorage, | ||
@@ -99,10 +111,19 @@ disableLogging: disableAllLogging | ||
}; | ||
const evaluationReasonMappings = Object.entries(EvaluationReason).map(([key, value]) => [key.toLowerCase(), value]); | ||
export const migrateEvaluationDetails = details => { | ||
var evaluationReasonMappings = Object.entries(_types.EvaluationReason).map(function (_ref5) { | ||
var _ref6 = (0, _slicedToArray2.default)(_ref5, 2), | ||
key = _ref6[0], | ||
value = _ref6[1]; | ||
return [key.toLowerCase(), value]; | ||
}); | ||
var migrateEvaluationDetails = exports.migrateEvaluationDetails = function migrateEvaluationDetails(details) { | ||
var _evaluationReasonMapp, _evaluationReasonMapp2, _details$receivedAt; | ||
const reasonLower = details.reason.toLowerCase(); | ||
var reasonLower = details.reason.toLowerCase(); | ||
return { | ||
reason: (_evaluationReasonMapp = (_evaluationReasonMapp2 = evaluationReasonMappings.find(([key]) => reasonLower.includes(key))) === null || _evaluationReasonMapp2 === void 0 ? void 0 : _evaluationReasonMapp2[1]) !== null && _evaluationReasonMapp !== void 0 ? _evaluationReasonMapp : EvaluationReason.Unknown, | ||
reason: (_evaluationReasonMapp = (_evaluationReasonMapp2 = evaluationReasonMappings.find(function (_ref7) { | ||
var _ref8 = (0, _slicedToArray2.default)(_ref7, 1), | ||
key = _ref8[0]; | ||
return reasonLower.includes(key); | ||
})) === null || _evaluationReasonMapp2 === void 0 ? void 0 : _evaluationReasonMapp2[1]) !== null && _evaluationReasonMapp !== void 0 ? _evaluationReasonMapp : _types.EvaluationReason.Unknown, | ||
time: (_details$receivedAt = details.receivedAt) !== null && _details$receivedAt !== void 0 ? _details$receivedAt : Date.now() | ||
}; | ||
}; |
@@ -0,2 +1,8 @@ | ||
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { | ||
value: true | ||
}); | ||
exports.CLIENT_VERSION = void 0; | ||
/// <reference types="node" /> | ||
export const CLIENT_VERSION = "4.26.0"; | ||
var CLIENT_VERSION = exports.CLIENT_VERSION = "4.26.1"; |
@@ -1,4 +0,54 @@ | ||
export { default, FeatureGateEnvironment, PerimeterType, CLIENT_VERSION } from './client/FeatureGates'; | ||
export { DynamicConfig } from './client/compat/DynamicConfig'; | ||
export { Layer } from './client/compat/Layer'; | ||
export { EvaluationReason } from './client/compat/types'; | ||
"use strict"; | ||
var _typeof = require("@babel/runtime/helpers/typeof"); | ||
Object.defineProperty(exports, "__esModule", { | ||
value: true | ||
}); | ||
Object.defineProperty(exports, "CLIENT_VERSION", { | ||
enumerable: true, | ||
get: function get() { | ||
return _FeatureGates.CLIENT_VERSION; | ||
} | ||
}); | ||
Object.defineProperty(exports, "DynamicConfig", { | ||
enumerable: true, | ||
get: function get() { | ||
return _DynamicConfig.DynamicConfig; | ||
} | ||
}); | ||
Object.defineProperty(exports, "EvaluationReason", { | ||
enumerable: true, | ||
get: function get() { | ||
return _types.EvaluationReason; | ||
} | ||
}); | ||
Object.defineProperty(exports, "FeatureGateEnvironment", { | ||
enumerable: true, | ||
get: function get() { | ||
return _FeatureGates.FeatureGateEnvironment; | ||
} | ||
}); | ||
Object.defineProperty(exports, "Layer", { | ||
enumerable: true, | ||
get: function get() { | ||
return _Layer.Layer; | ||
} | ||
}); | ||
Object.defineProperty(exports, "PerimeterType", { | ||
enumerable: true, | ||
get: function get() { | ||
return _FeatureGates.PerimeterType; | ||
} | ||
}); | ||
Object.defineProperty(exports, "default", { | ||
enumerable: true, | ||
get: function get() { | ||
return _FeatureGates.default; | ||
} | ||
}); | ||
var _FeatureGates = _interopRequireWildcard(require("./client/FeatureGates")); | ||
var _DynamicConfig = require("./client/compat/DynamicConfig"); | ||
var _Layer = require("./client/compat/Layer"); | ||
var _types = require("./client/compat/types"); | ||
function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function _getRequireWildcardCache(e) { return e ? t : r; })(e); } | ||
function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != _typeof(e) && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && {}.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; } |
@@ -1,1 +0,13 @@ | ||
export { default } from './Subscriptions'; | ||
"use strict"; | ||
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); | ||
Object.defineProperty(exports, "__esModule", { | ||
value: true | ||
}); | ||
Object.defineProperty(exports, "default", { | ||
enumerable: true, | ||
get: function get() { | ||
return _Subscriptions.default; | ||
} | ||
}); | ||
var _Subscriptions = _interopRequireDefault(require("./Subscriptions")); |
@@ -1,68 +0,93 @@ | ||
import _defineProperty from "@babel/runtime/helpers/defineProperty"; | ||
"use strict"; | ||
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); | ||
Object.defineProperty(exports, "__esModule", { | ||
value: true | ||
}); | ||
exports.default = exports.ALL_FEATURE_VALUES = void 0; | ||
var _classCallCheck2 = _interopRequireDefault(require("@babel/runtime/helpers/classCallCheck")); | ||
var _createClass2 = _interopRequireDefault(require("@babel/runtime/helpers/createClass")); | ||
var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty")); | ||
var _eventemitter = require("eventemitter2"); | ||
function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; } | ||
function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; } | ||
import { EventEmitter2 } from 'eventemitter2'; | ||
export const ALL_FEATURE_VALUES = '@all-features'; | ||
export default class Subscriptions { | ||
constructor() { | ||
_defineProperty(this, "eventToValue", new Map()); | ||
this.emitter = new EventEmitter2(); | ||
function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { (0, _defineProperty2.default)(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; } | ||
var ALL_FEATURE_VALUES = exports.ALL_FEATURE_VALUES = '@all-features'; | ||
var Subscriptions = exports.default = /*#__PURE__*/function () { | ||
function Subscriptions() { | ||
(0, _classCallCheck2.default)(this, Subscriptions); | ||
(0, _defineProperty2.default)(this, "eventToValue", new Map()); | ||
this.emitter = new _eventemitter.EventEmitter2(); | ||
} | ||
onGateUpdated(gateName, callback, checkGate, options) { | ||
const value = checkGate(gateName, _objectSpread(_objectSpread({}, options), {}, { | ||
fireGateExposure: false | ||
})); | ||
if (this.eventToValue.get(callback) === undefined) { | ||
this.eventToValue.set(callback, value); | ||
} | ||
const wrapCallback = () => { | ||
const value = checkGate(gateName, _objectSpread(_objectSpread({}, options), {}, { | ||
return (0, _createClass2.default)(Subscriptions, [{ | ||
key: "onGateUpdated", | ||
value: function onGateUpdated(gateName, callback, checkGate, options) { | ||
var _this = this; | ||
var value = checkGate(gateName, _objectSpread(_objectSpread({}, options), {}, { | ||
fireGateExposure: false | ||
})); | ||
const existingValue = this.eventToValue.get(callback); | ||
if (existingValue !== value) { | ||
if (this.eventToValue.get(callback) === undefined) { | ||
this.eventToValue.set(callback, value); | ||
callback(value); | ||
} | ||
}; | ||
this.emitter.on(gateName, wrapCallback); | ||
return () => { | ||
this.emitter.off(gateName, wrapCallback); | ||
}; | ||
} | ||
onExperimentValueUpdated(experimentName, parameterName, defaultValue, callback, getExperimentValue, options) { | ||
const experimentEventName = `${experimentName}.${parameterName}`; | ||
const value = getExperimentValue(experimentName, parameterName, defaultValue, _objectSpread(_objectSpread({}, options), {}, { | ||
fireExperimentExposure: false | ||
})); | ||
if (this.eventToValue.get(callback) === undefined) { | ||
this.eventToValue.set(callback, value); | ||
var wrapCallback = function wrapCallback() { | ||
var value = checkGate(gateName, _objectSpread(_objectSpread({}, options), {}, { | ||
fireGateExposure: false | ||
})); | ||
var existingValue = _this.eventToValue.get(callback); | ||
if (existingValue !== value) { | ||
_this.eventToValue.set(callback, value); | ||
callback(value); | ||
} | ||
}; | ||
this.emitter.on(gateName, wrapCallback); | ||
return function () { | ||
_this.emitter.off(gateName, wrapCallback); | ||
}; | ||
} | ||
const wrapCallback = () => { | ||
const value = getExperimentValue(experimentName, parameterName, defaultValue, _objectSpread(_objectSpread({}, options), {}, { | ||
}, { | ||
key: "onExperimentValueUpdated", | ||
value: function onExperimentValueUpdated(experimentName, parameterName, defaultValue, callback, getExperimentValue, options) { | ||
var _this2 = this; | ||
var experimentEventName = "".concat(experimentName, ".").concat(parameterName); | ||
var value = getExperimentValue(experimentName, parameterName, defaultValue, _objectSpread(_objectSpread({}, options), {}, { | ||
fireExperimentExposure: false | ||
})); | ||
const existingValue = this.eventToValue.get(callback); | ||
if (existingValue !== value) { | ||
if (this.eventToValue.get(callback) === undefined) { | ||
this.eventToValue.set(callback, value); | ||
callback(value); | ||
} | ||
}; | ||
this.emitter.on(experimentEventName, wrapCallback); | ||
return () => { | ||
this.emitter.off(experimentEventName, wrapCallback); | ||
}; | ||
} | ||
onAnyUpdated(callback) { | ||
this.emitter.on(ALL_FEATURE_VALUES, callback); | ||
return () => { | ||
this.emitter.off(ALL_FEATURE_VALUES, callback); | ||
}; | ||
} | ||
anyUpdated() { | ||
this.emitter.emit(ALL_FEATURE_VALUES); | ||
this.emitter.eventNames().filter(name => name !== ALL_FEATURE_VALUES).forEach(event => { | ||
this.emitter.emit(event); | ||
}); | ||
} | ||
} | ||
var wrapCallback = function wrapCallback() { | ||
var value = getExperimentValue(experimentName, parameterName, defaultValue, _objectSpread(_objectSpread({}, options), {}, { | ||
fireExperimentExposure: false | ||
})); | ||
var existingValue = _this2.eventToValue.get(callback); | ||
if (existingValue !== value) { | ||
_this2.eventToValue.set(callback, value); | ||
callback(value); | ||
} | ||
}; | ||
this.emitter.on(experimentEventName, wrapCallback); | ||
return function () { | ||
_this2.emitter.off(experimentEventName, wrapCallback); | ||
}; | ||
} | ||
}, { | ||
key: "onAnyUpdated", | ||
value: function onAnyUpdated(callback) { | ||
var _this3 = this; | ||
this.emitter.on(ALL_FEATURE_VALUES, callback); | ||
return function () { | ||
_this3.emitter.off(ALL_FEATURE_VALUES, callback); | ||
}; | ||
} | ||
}, { | ||
key: "anyUpdated", | ||
value: function anyUpdated() { | ||
var _this4 = this; | ||
this.emitter.emit(ALL_FEATURE_VALUES); | ||
this.emitter.eventNames().filter(function (name) { | ||
return name !== ALL_FEATURE_VALUES; | ||
}).forEach(function (event) { | ||
_this4.emitter.emit(event); | ||
}); | ||
} | ||
}]); | ||
}(); |
@@ -1,6 +0,2 @@ | ||
import _objectWithoutProperties from "@babel/runtime/helpers/objectWithoutProperties"; | ||
import _defineProperty from "@babel/runtime/helpers/defineProperty"; | ||
const _excluded = ["sdkKey", "environment", "updateUserCompletionCallback", "perimeter"]; | ||
function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; } | ||
function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; } | ||
import { _makeLayer, StatsigClient } from '@statsig/js-client'; | ||
@@ -153,3 +149,3 @@ import Subscriptions from '../subscriptions'; | ||
(_analyticsWebClient = this.initOptions.analyticsWebClient) === null || _analyticsWebClient === void 0 ? void 0 : _analyticsWebClient.then(analyticsWebClient => { | ||
const attributes = _objectSpread({ | ||
const attributes = { | ||
targetApp: this.initOptions.targetApp, | ||
@@ -159,6 +155,7 @@ clientVersion: CLIENT_VERSION, | ||
startTime, | ||
totalTime | ||
}, apiKey && { | ||
apiKey | ||
}); | ||
totalTime, | ||
...(apiKey && { | ||
apiKey | ||
}) | ||
}; | ||
analyticsWebClient.sendOperationalEvent({ | ||
@@ -662,3 +659,5 @@ action, | ||
async init(clientOptions, identifiers, customAttributes) { | ||
const fromValuesClientOptions = _objectSpread({}, clientOptions); | ||
const fromValuesClientOptions = { | ||
...clientOptions | ||
}; | ||
let experimentValues; | ||
@@ -691,5 +690,6 @@ let customAttributesFromResult; | ||
async initWithProvider(baseClientOptions, provider, identifiers, customAttributes) { | ||
const fromValuesClientOptions = _objectSpread(_objectSpread({}, baseClientOptions), {}, { | ||
const fromValuesClientOptions = { | ||
...baseClientOptions, | ||
disableCurrentPageLogging: true | ||
}); | ||
}; | ||
let experimentValues; | ||
@@ -744,5 +744,6 @@ let customAttributesFromResult; | ||
if (!((_newClientOptions$net = newClientOptions.networkConfig) !== null && _newClientOptions$net !== void 0 && _newClientOptions$net.logEventUrl)) { | ||
newClientOptions.networkConfig = _objectSpread(_objectSpread({}, newClientOptions.networkConfig), {}, { | ||
newClientOptions.networkConfig = { | ||
...newClientOptions.networkConfig, | ||
logEventUrl: DEFAULT_EVENT_LOGGING_API | ||
}); | ||
}; | ||
} | ||
@@ -754,11 +755,12 @@ if (newClientOptions.perimeter === PerimeterType.FEDRAMP_MODERATE) { | ||
const { | ||
sdkKey, | ||
environment, | ||
updateUserCompletionCallback: _updateUserCompletionCallback, | ||
perimeter: _perimeter | ||
} = newClientOptions, | ||
restClientOptions = _objectWithoutProperties(newClientOptions, _excluded); | ||
sdkKey, | ||
environment, | ||
updateUserCompletionCallback: _updateUserCompletionCallback, | ||
perimeter: _perimeter, | ||
...restClientOptions | ||
} = newClientOptions; | ||
this.sdkKey = sdkKey; | ||
this.user = toStatsigUser(identifiers, customAttributes, this.sdkKey); | ||
const statsigOptions = _objectSpread(_objectSpread({}, restClientOptions), {}, { | ||
const statsigOptions = { | ||
...restClientOptions, | ||
environment: { | ||
@@ -770,3 +772,3 @@ tier: environment | ||
overrideAdapter: this.overrideAdapter | ||
}); | ||
}; | ||
try { | ||
@@ -773,0 +775,0 @@ this.statsigClient = new StatsigClient(sdkKey, this.user, statsigOptions); |
@@ -1,4 +0,1 @@ | ||
import _defineProperty from "@babel/runtime/helpers/defineProperty"; | ||
function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; } | ||
function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; } | ||
import { FeatureGateEnvironment, PerimeterType } from '../types'; | ||
@@ -95,3 +92,3 @@ import { CLIENT_VERSION } from '../version'; | ||
} | ||
const response = await fetch(`${baseUrl}${path}`, _objectSpread({ | ||
const response = await fetch(`${baseUrl}${path}`, { | ||
method, | ||
@@ -104,6 +101,7 @@ headers: { | ||
}, | ||
signal: abortSignal | ||
}, body && { | ||
body: JSON.stringify(body) | ||
})); | ||
signal: abortSignal, | ||
...(body && { | ||
body: JSON.stringify(body) | ||
}) | ||
}); | ||
await this.handleResponseError(response); | ||
@@ -110,0 +108,0 @@ return await this.extractResponseBody(response); |
import _defineProperty from "@babel/runtime/helpers/defineProperty"; | ||
function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; } | ||
function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; } | ||
import { _getFullUserHash, _getStorageKey, DataAdapterCachePrefix, DataAdapterCore, StableID } from '@statsig/js-client'; | ||
@@ -32,10 +30,12 @@ | ||
async getDataAsync(_current, user, _options) { | ||
return this.bootstrapResult && _objectSpread(_objectSpread({}, this.bootstrapResult), {}, { | ||
return this.bootstrapResult && { | ||
...this.bootstrapResult, | ||
fullUserHash: _getFullUserHash(user) | ||
}); | ||
}; | ||
} | ||
getDataSync(user) { | ||
return this.bootstrapResult && _objectSpread(_objectSpread({}, this.bootstrapResult), {}, { | ||
return this.bootstrapResult && { | ||
...this.bootstrapResult, | ||
fullUserHash: _getFullUserHash(user) | ||
}); | ||
}; | ||
} | ||
@@ -62,3 +62,5 @@ async _fetchFromNetwork(_current, _user, _options) { | ||
toJSON() { | ||
const result = _objectSpread({}, this); | ||
const result = { | ||
...this | ||
}; | ||
delete result._options; | ||
@@ -65,0 +67,0 @@ delete result._inMemoryCache; |
@@ -1,4 +0,1 @@ | ||
import _defineProperty from "@babel/runtime/helpers/defineProperty"; | ||
function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; } | ||
function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; } | ||
import { _DJB2, _makeTypedGet } from '@statsig/client-core'; | ||
@@ -34,3 +31,3 @@ const LOCAL_OVERRIDE_REASON = 'LocalOverride:Recognized'; | ||
return JSON.parse(json); | ||
} catch (_unused) { | ||
} catch { | ||
return makeEmptyStore(); | ||
@@ -63,3 +60,3 @@ } | ||
window.localStorage.setItem(this._localStorageKey, JSON.stringify(this._overrides)); | ||
} catch (_unused2) { | ||
} catch { | ||
// ignored - window is not defined in non-browser environments, and we don't save things there | ||
@@ -81,3 +78,6 @@ // (things like SSR, etc) | ||
setOverrides(overrides) { | ||
const newOverrides = _objectSpread(_objectSpread({}, makeEmptyStore()), overrides); | ||
const newOverrides = { | ||
...makeEmptyStore(), | ||
...overrides | ||
}; | ||
for (const container of Object.values(newOverrides)) { | ||
@@ -110,8 +110,10 @@ for (const [name, value] of Object.entries(container)) { | ||
} | ||
return _objectSpread(_objectSpread({}, current), {}, { | ||
return { | ||
...current, | ||
value: overridden, | ||
details: _objectSpread(_objectSpread({}, current.details), {}, { | ||
details: { | ||
...current.details, | ||
reason: LOCAL_OVERRIDE_REASON | ||
}) | ||
}); | ||
} | ||
}; | ||
} | ||
@@ -164,9 +166,11 @@ overrideDynamicConfig(name, value) { | ||
} | ||
return _objectSpread(_objectSpread({}, current), {}, { | ||
return { | ||
...current, | ||
__value: overridden, | ||
get: _makeTypedGet(current.name, overridden), | ||
details: _objectSpread(_objectSpread({}, current.details), {}, { | ||
details: { | ||
...current.details, | ||
reason: LOCAL_OVERRIDE_REASON | ||
}) | ||
}); | ||
} | ||
}; | ||
} | ||
@@ -179,10 +183,12 @@ _getConfigOverride(current, lookup) { | ||
} | ||
return _objectSpread(_objectSpread({}, current), {}, { | ||
return { | ||
...current, | ||
value: overridden, | ||
get: _makeTypedGet(current.name, overridden), | ||
details: _objectSpread(_objectSpread({}, current.details), {}, { | ||
details: { | ||
...current.details, | ||
reason: LOCAL_OVERRIDE_REASON | ||
}) | ||
}); | ||
} | ||
}; | ||
} | ||
} |
@@ -1,6 +0,1 @@ | ||
import _objectWithoutProperties from "@babel/runtime/helpers/objectWithoutProperties"; | ||
import _defineProperty from "@babel/runtime/helpers/defineProperty"; | ||
const _excluded = ["api", "disableCurrentPageLogging", "loggingIntervalMillis", "loggingBufferMaxSize", "localMode", "eventLoggingApi", "eventLoggingApiForRetries", "disableLocalStorage", "ignoreWindowUndefined", "disableAllLogging", "initTimeoutMs", "disableNetworkKeepalive", "overrideStableID", "disableErrorLogging", "disableAutoMetricsLogging"]; | ||
function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; } | ||
function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; } | ||
import { StableID } from '@statsig/js-client'; | ||
@@ -10,3 +5,3 @@ import { isFedRamp } from '@atlaskit/atlassian-context'; | ||
import { PerimeterType } from './types'; | ||
export const getOptionsWithDefaults = options => _objectSpread({ | ||
export const getOptionsWithDefaults = options => ({ | ||
/** | ||
@@ -16,4 +11,5 @@ * If more federal PerimeterTypes are added in the future, this should be updated so | ||
*/ | ||
perimeter: isFedRamp() ? PerimeterType.FEDRAMP_MODERATE : PerimeterType.COMMERCIAL | ||
}, options); | ||
perimeter: isFedRamp() ? PerimeterType.FEDRAMP_MODERATE : PerimeterType.COMMERCIAL, | ||
...options | ||
}); | ||
export const shallowEquals = (objectA, objectB) => { | ||
@@ -50,5 +46,6 @@ if (!objectA && !objectB) { | ||
const user = { | ||
customIDs: !(customAttributes !== null && customAttributes !== void 0 && customAttributes.stableID) && sdkKey ? _objectSpread({ | ||
stableID: StableID.get(sdkKey) | ||
}, identifiers) : identifiers, | ||
customIDs: !(customAttributes !== null && customAttributes !== void 0 && customAttributes.stableID) && sdkKey ? { | ||
stableID: StableID.get(sdkKey), | ||
...identifiers | ||
} : identifiers, | ||
custom: customAttributes | ||
@@ -63,24 +60,25 @@ }; | ||
const { | ||
api, | ||
disableCurrentPageLogging, | ||
loggingIntervalMillis, | ||
loggingBufferMaxSize, | ||
localMode, | ||
eventLoggingApi, | ||
eventLoggingApiForRetries, | ||
disableLocalStorage, | ||
ignoreWindowUndefined, | ||
disableAllLogging, | ||
// No equivalent but is pointless anyway since our Statsig init is synchronous | ||
initTimeoutMs: _initTimeoutMs, | ||
// No equivalent in new client but probably not important? | ||
disableNetworkKeepalive: _disableNetworkKeepalive, | ||
// Needs to be implemented manually but unused according to zoekt | ||
overrideStableID: _overrideStableID, | ||
// No equivalent for these but can't see them actually used anywhere in old client? | ||
disableErrorLogging: _disableErrorLogging, | ||
disableAutoMetricsLogging: _disableAutoMetricsLogging | ||
} = options, | ||
rest = _objectWithoutProperties(options, _excluded); | ||
return _objectSpread(_objectSpread({}, rest), {}, { | ||
api, | ||
disableCurrentPageLogging, | ||
loggingIntervalMillis, | ||
loggingBufferMaxSize, | ||
localMode, | ||
eventLoggingApi, | ||
eventLoggingApiForRetries, | ||
disableLocalStorage, | ||
ignoreWindowUndefined, | ||
disableAllLogging, | ||
// No equivalent but is pointless anyway since our Statsig init is synchronous | ||
initTimeoutMs: _initTimeoutMs, | ||
// No equivalent in new client but probably not important? | ||
disableNetworkKeepalive: _disableNetworkKeepalive, | ||
// Needs to be implemented manually but unused according to zoekt | ||
overrideStableID: _overrideStableID, | ||
// No equivalent for these but can't see them actually used anywhere in old client? | ||
disableErrorLogging: _disableErrorLogging, | ||
disableAutoMetricsLogging: _disableAutoMetricsLogging, | ||
...rest | ||
} = options; | ||
return { | ||
...rest, | ||
networkConfig: { | ||
@@ -97,3 +95,3 @@ api, | ||
disableLogging: disableAllLogging | ||
}); | ||
}; | ||
}; | ||
@@ -100,0 +98,0 @@ const evaluationReasonMappings = Object.entries(EvaluationReason).map(([key, value]) => [key.toLowerCase(), value]); |
/// <reference types="node" /> | ||
export const CLIENT_VERSION = "4.26.0"; | ||
export const CLIENT_VERSION = "4.26.1"; |
import _defineProperty from "@babel/runtime/helpers/defineProperty"; | ||
function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; } | ||
function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; } | ||
import { EventEmitter2 } from 'eventemitter2'; | ||
@@ -12,5 +10,6 @@ export const ALL_FEATURE_VALUES = '@all-features'; | ||
onGateUpdated(gateName, callback, checkGate, options) { | ||
const value = checkGate(gateName, _objectSpread(_objectSpread({}, options), {}, { | ||
const value = checkGate(gateName, { | ||
...options, | ||
fireGateExposure: false | ||
})); | ||
}); | ||
if (this.eventToValue.get(callback) === undefined) { | ||
@@ -20,5 +19,6 @@ this.eventToValue.set(callback, value); | ||
const wrapCallback = () => { | ||
const value = checkGate(gateName, _objectSpread(_objectSpread({}, options), {}, { | ||
const value = checkGate(gateName, { | ||
...options, | ||
fireGateExposure: false | ||
})); | ||
}); | ||
const existingValue = this.eventToValue.get(callback); | ||
@@ -37,5 +37,6 @@ if (existingValue !== value) { | ||
const experimentEventName = `${experimentName}.${parameterName}`; | ||
const value = getExperimentValue(experimentName, parameterName, defaultValue, _objectSpread(_objectSpread({}, options), {}, { | ||
const value = getExperimentValue(experimentName, parameterName, defaultValue, { | ||
...options, | ||
fireExperimentExposure: false | ||
})); | ||
}); | ||
if (this.eventToValue.get(callback) === undefined) { | ||
@@ -45,5 +46,6 @@ this.eventToValue.set(callback, value); | ||
const wrapCallback = () => { | ||
const value = getExperimentValue(experimentName, parameterName, defaultValue, _objectSpread(_objectSpread({}, options), {}, { | ||
const value = getExperimentValue(experimentName, parameterName, defaultValue, { | ||
...options, | ||
fireExperimentExposure: false | ||
})); | ||
}); | ||
const existingValue = this.eventToValue.get(callback); | ||
@@ -50,0 +52,0 @@ if (existingValue !== value) { |
import _objectWithoutProperties from "@babel/runtime/helpers/objectWithoutProperties"; | ||
import _slicedToArray from "@babel/runtime/helpers/slicedToArray"; | ||
import _asyncToGenerator from "@babel/runtime/helpers/asyncToGenerator"; | ||
import _classCallCheck from "@babel/runtime/helpers/classCallCheck"; | ||
import _createClass from "@babel/runtime/helpers/createClass"; | ||
import _defineProperty from "@babel/runtime/helpers/defineProperty"; | ||
const _excluded = ["sdkKey", "environment", "updateUserCompletionCallback", "perimeter"]; | ||
var _excluded = ["sdkKey", "environment", "updateUserCompletionCallback", "perimeter"]; | ||
function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; } | ||
function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; } | ||
import _regeneratorRuntime from "@babel/runtime/regenerator"; | ||
import { _makeLayer, StatsigClient } from '@statsig/js-client'; | ||
@@ -17,9 +22,12 @@ import Subscriptions from '../subscriptions'; | ||
import { CLIENT_VERSION } from './version'; | ||
const DEFAULT_CLIENT_KEY = 'client-default-key'; | ||
var DEFAULT_CLIENT_KEY = 'client-default-key'; | ||
// default event logging api is Atlassian proxy rather than Statsig's domain, to avoid ad blockers | ||
const DEFAULT_EVENT_LOGGING_API = 'https://xp.atlassian.com/v1/rgstr'; | ||
export class Client { | ||
constructor({ | ||
localStorageKey = LOCAL_STORAGE_KEY | ||
} = {}) { | ||
var DEFAULT_EVENT_LOGGING_API = 'https://xp.atlassian.com/v1/rgstr'; | ||
export var Client = /*#__PURE__*/function () { | ||
function Client() { | ||
var _this = this; | ||
var _ref = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}, | ||
_ref$localStorageKey = _ref.localStorageKey, | ||
localStorageKey = _ref$localStorageKey === void 0 ? LOCAL_STORAGE_KEY : _ref$localStorageKey; | ||
_classCallCheck(this, Client); | ||
_defineProperty(this, "initPromise", null); | ||
@@ -45,10 +53,10 @@ /** True if an initialize method was called and completed successfully. */ | ||
*/ | ||
_defineProperty(this, "statsigValuesUpdated", () => { | ||
if (this.user) { | ||
_defineProperty(this, "statsigValuesUpdated", function () { | ||
if (_this.user) { | ||
// Trigger a reset of the memoize cache | ||
this.statsigClient.updateUserSync(this.user, { | ||
_this.statsigClient.updateUserSync(_this.user, { | ||
disableBackgroundCacheRefresh: true | ||
}); | ||
} | ||
this.subscriptions.anyUpdated(); | ||
_this.subscriptions.anyUpdated(); | ||
}); | ||
@@ -74,911 +82,1251 @@ this.overrideAdapter = new PersistentOverrideAdapter(localStorageKey); | ||
*/ | ||
async initialize(clientOptions, identifiers, customAttributes) { | ||
const clientOptionsWithDefaults = getOptionsWithDefaults(clientOptions); | ||
if (this.initPromise) { | ||
if (!shallowEquals(clientOptionsWithDefaults, this.initOptions)) { | ||
// eslint-disable-next-line no-console | ||
console.warn('Feature Gates client already initialized with different options. New options were not applied.'); | ||
return _createClass(Client, [{ | ||
key: "initialize", | ||
value: (function () { | ||
var _initialize = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee(clientOptions, identifiers, customAttributes) { | ||
var _this2 = this; | ||
var clientOptionsWithDefaults, startTime; | ||
return _regeneratorRuntime.wrap(function _callee$(_context) { | ||
while (1) switch (_context.prev = _context.next) { | ||
case 0: | ||
clientOptionsWithDefaults = getOptionsWithDefaults(clientOptions); | ||
if (!this.initPromise) { | ||
_context.next = 4; | ||
break; | ||
} | ||
if (!shallowEquals(clientOptionsWithDefaults, this.initOptions)) { | ||
// eslint-disable-next-line no-console | ||
console.warn('Feature Gates client already initialized with different options. New options were not applied.'); | ||
} | ||
return _context.abrupt("return", this.initPromise); | ||
case 4: | ||
startTime = performance.now(); | ||
this.initOptions = clientOptionsWithDefaults; | ||
this.initPromise = this.init(clientOptionsWithDefaults, identifiers, customAttributes).then(function () { | ||
_this2.initCompleted = true; | ||
_this2.initWithDefaults = true; | ||
}).finally(function () { | ||
var endTime = performance.now(); | ||
var totalTime = endTime - startTime; | ||
_this2.fireClientEvent(startTime, totalTime, 'initialize', _this2.initCompleted, clientOptionsWithDefaults.apiKey); | ||
}); | ||
return _context.abrupt("return", this.initPromise); | ||
case 8: | ||
case "end": | ||
return _context.stop(); | ||
} | ||
}, _callee, this); | ||
})); | ||
function initialize(_x, _x2, _x3) { | ||
return _initialize.apply(this, arguments); | ||
} | ||
return this.initPromise; | ||
} | ||
const startTime = performance.now(); | ||
this.initOptions = clientOptionsWithDefaults; | ||
this.initPromise = this.init(clientOptionsWithDefaults, identifiers, customAttributes).then(() => { | ||
this.initCompleted = true; | ||
this.initWithDefaults = true; | ||
}).finally(() => { | ||
const endTime = performance.now(); | ||
const totalTime = endTime - startTime; | ||
this.fireClientEvent(startTime, totalTime, 'initialize', this.initCompleted, clientOptionsWithDefaults.apiKey); | ||
}); | ||
return this.initPromise; | ||
} | ||
/** | ||
* @description | ||
* This method initializes the client using the provider given to call to fetch the bootstrap values. | ||
* If the client is initialized with an `analyticsWebClient`, it will send an operational event | ||
* to GASv3 with the following attributes: | ||
* - targetApp: the target app of the client | ||
* - clientVersion: the version of the client | ||
* - success: whether the initialization was successful | ||
* - startTime: the time when the initialization started | ||
* - totalTime: the total time it took to initialize the client | ||
* - apiKey: the api key used to initialize the client | ||
* @param clientOptions {ClientOptions} | ||
* @param provider {Provider} | ||
* @param identifiers {Identifiers} | ||
* @param customAttributes {CustomAttributes} | ||
* @returns {Promise<void>} | ||
*/ | ||
async initializeWithProvider(clientOptions, provider, identifiers, customAttributes) { | ||
const clientOptionsWithDefaults = getOptionsWithDefaults(clientOptions); | ||
if (this.initPromise) { | ||
if (!shallowEquals(clientOptionsWithDefaults, this.initOptions)) { | ||
return initialize; | ||
}() | ||
/** | ||
* @description | ||
* This method initializes the client using the provider given to call to fetch the bootstrap values. | ||
* If the client is initialized with an `analyticsWebClient`, it will send an operational event | ||
* to GASv3 with the following attributes: | ||
* - targetApp: the target app of the client | ||
* - clientVersion: the version of the client | ||
* - success: whether the initialization was successful | ||
* - startTime: the time when the initialization started | ||
* - totalTime: the total time it took to initialize the client | ||
* - apiKey: the api key used to initialize the client | ||
* @param clientOptions {ClientOptions} | ||
* @param provider {Provider} | ||
* @param identifiers {Identifiers} | ||
* @param customAttributes {CustomAttributes} | ||
* @returns {Promise<void>} | ||
*/ | ||
) | ||
}, { | ||
key: "initializeWithProvider", | ||
value: (function () { | ||
var _initializeWithProvider = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee2(clientOptions, provider, identifiers, customAttributes) { | ||
var _this3 = this; | ||
var clientOptionsWithDefaults, startTime; | ||
return _regeneratorRuntime.wrap(function _callee2$(_context2) { | ||
while (1) switch (_context2.prev = _context2.next) { | ||
case 0: | ||
clientOptionsWithDefaults = getOptionsWithDefaults(clientOptions); | ||
if (!this.initPromise) { | ||
_context2.next = 4; | ||
break; | ||
} | ||
if (!shallowEquals(clientOptionsWithDefaults, this.initOptions)) { | ||
// eslint-disable-next-line no-console | ||
console.warn('Feature Gates client already initialized with different options. New options were not applied.'); | ||
} | ||
return _context2.abrupt("return", this.initPromise); | ||
case 4: | ||
startTime = performance.now(); | ||
this.initOptions = clientOptionsWithDefaults; | ||
this.provider = provider; | ||
this.provider.setClientVersion(CLIENT_VERSION); | ||
if (this.provider.setApplyUpdateCallback) { | ||
this.provider.setApplyUpdateCallback(this.applyUpdateCallback.bind(this)); | ||
} | ||
this.initPromise = this.initWithProvider(clientOptionsWithDefaults, provider, identifiers, customAttributes).then(function () { | ||
_this3.initCompleted = true; | ||
_this3.initWithDefaults = true; | ||
}).finally(function () { | ||
var endTime = performance.now(); | ||
var totalTime = endTime - startTime; | ||
_this3.fireClientEvent(startTime, totalTime, 'initializeWithProvider', _this3.initCompleted, provider.getApiKey ? provider.getApiKey() : undefined); | ||
}); | ||
return _context2.abrupt("return", this.initPromise); | ||
case 11: | ||
case "end": | ||
return _context2.stop(); | ||
} | ||
}, _callee2, this); | ||
})); | ||
function initializeWithProvider(_x4, _x5, _x6, _x7) { | ||
return _initializeWithProvider.apply(this, arguments); | ||
} | ||
return initializeWithProvider; | ||
}()) | ||
}, { | ||
key: "applyUpdateCallback", | ||
value: function applyUpdateCallback(experimentsResult) { | ||
try { | ||
if (this.initCompleted || this.initWithDefaults) { | ||
this.assertInitialized(this.statsigClient); | ||
this.dataAdapter.setBootstrapData(experimentsResult.experimentValues); | ||
this.dataAdapter.setData(JSON.stringify(experimentsResult.experimentValues)); | ||
this.statsigValuesUpdated(); | ||
} | ||
} catch (error) { | ||
// eslint-disable-next-line no-console | ||
console.warn('Feature Gates client already initialized with different options. New options were not applied.'); | ||
console.warn('Error when attempting to apply update', error); | ||
} | ||
return this.initPromise; | ||
} | ||
const startTime = performance.now(); | ||
this.initOptions = clientOptionsWithDefaults; | ||
this.provider = provider; | ||
this.provider.setClientVersion(CLIENT_VERSION); | ||
if (this.provider.setApplyUpdateCallback) { | ||
this.provider.setApplyUpdateCallback(this.applyUpdateCallback.bind(this)); | ||
}, { | ||
key: "fireClientEvent", | ||
value: function fireClientEvent(startTime, totalTime, action, success) { | ||
var _analyticsWebClient, | ||
_this4 = this; | ||
var apiKey = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : undefined; | ||
(_analyticsWebClient = this.initOptions.analyticsWebClient) === null || _analyticsWebClient === void 0 || _analyticsWebClient.then(function (analyticsWebClient) { | ||
var attributes = _objectSpread({ | ||
targetApp: _this4.initOptions.targetApp, | ||
clientVersion: CLIENT_VERSION, | ||
success: success, | ||
startTime: startTime, | ||
totalTime: totalTime | ||
}, apiKey && { | ||
apiKey: apiKey | ||
}); | ||
analyticsWebClient.sendOperationalEvent({ | ||
action: action, | ||
actionSubject: 'featureGatesClient', | ||
attributes: attributes, | ||
tags: ['measurement'], | ||
source: '@atlaskit/feature-gate-js-client' | ||
}); | ||
}).catch(function (err) { | ||
if (_this4.initOptions.environment !== FeatureGateEnvironment.Production) { | ||
// eslint-disable-next-line no-console | ||
console.error('Analytics web client promise did not resolve', err); | ||
} | ||
}); | ||
} | ||
this.initPromise = this.initWithProvider(clientOptionsWithDefaults, provider, identifiers, customAttributes).then(() => { | ||
this.initCompleted = true; | ||
this.initWithDefaults = true; | ||
}).finally(() => { | ||
const endTime = performance.now(); | ||
const totalTime = endTime - startTime; | ||
this.fireClientEvent(startTime, totalTime, 'initializeWithProvider', this.initCompleted, provider.getApiKey ? provider.getApiKey() : undefined); | ||
}); | ||
return this.initPromise; | ||
} | ||
applyUpdateCallback(experimentsResult) { | ||
try { | ||
if (this.initCompleted || this.initWithDefaults) { | ||
this.assertInitialized(this.statsigClient); | ||
this.dataAdapter.setBootstrapData(experimentsResult.experimentValues); | ||
this.dataAdapter.setData(JSON.stringify(experimentsResult.experimentValues)); | ||
this.statsigValuesUpdated(); | ||
}, { | ||
key: "initializeFromValues", | ||
value: function () { | ||
var _initializeFromValues = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee3(clientOptions, identifiers, customAttributes) { | ||
var _this5 = this; | ||
var initializeValues, | ||
clientOptionsWithDefaults, | ||
startTime, | ||
_args3 = arguments; | ||
return _regeneratorRuntime.wrap(function _callee3$(_context3) { | ||
while (1) switch (_context3.prev = _context3.next) { | ||
case 0: | ||
initializeValues = _args3.length > 3 && _args3[3] !== undefined ? _args3[3] : {}; | ||
clientOptionsWithDefaults = getOptionsWithDefaults(clientOptions); | ||
if (!this.initPromise) { | ||
_context3.next = 5; | ||
break; | ||
} | ||
if (!shallowEquals(clientOptionsWithDefaults, this.initOptions)) { | ||
// eslint-disable-next-line no-console | ||
console.warn('Feature Gates client already initialized with different options. New options were not applied.'); | ||
} | ||
return _context3.abrupt("return", this.initPromise); | ||
case 5: | ||
// This makes sure the new Statsig client behaves like the old when bootstrap data is | ||
// passed, and `has_updates` isn't specified (which happens a lot in product integration tests). | ||
if (!Object.prototype.hasOwnProperty.call(initializeValues, 'has_updates')) { | ||
initializeValues['has_updates'] = true; | ||
} | ||
startTime = performance.now(); | ||
this.initOptions = clientOptionsWithDefaults; | ||
this.initPromise = this.initFromValues(clientOptionsWithDefaults, identifiers, customAttributes, initializeValues).then(function () { | ||
_this5.initCompleted = true; | ||
_this5.initWithDefaults = true; | ||
}).finally(function () { | ||
var endTime = performance.now(); | ||
var totalTime = endTime - startTime; | ||
_this5.fireClientEvent(startTime, totalTime, 'initializeFromValues', _this5.initCompleted); | ||
}); | ||
return _context3.abrupt("return", this.initPromise); | ||
case 10: | ||
case "end": | ||
return _context3.stop(); | ||
} | ||
}, _callee3, this); | ||
})); | ||
function initializeFromValues(_x8, _x9, _x10) { | ||
return _initializeFromValues.apply(this, arguments); | ||
} | ||
} catch (error) { | ||
// eslint-disable-next-line no-console | ||
console.warn('Error when attempting to apply update', error); | ||
return initializeFromValues; | ||
}() | ||
}, { | ||
key: "assertInitialized", | ||
value: function assertInitialized(statsigClient) { | ||
if (!statsigClient) { | ||
throw new Error('Client must be initialized before using this method'); | ||
} | ||
} | ||
} | ||
fireClientEvent(startTime, totalTime, action, success, apiKey = undefined) { | ||
var _analyticsWebClient; | ||
(_analyticsWebClient = this.initOptions.analyticsWebClient) === null || _analyticsWebClient === void 0 || _analyticsWebClient.then(analyticsWebClient => { | ||
const attributes = _objectSpread({ | ||
targetApp: this.initOptions.targetApp, | ||
clientVersion: CLIENT_VERSION, | ||
success, | ||
startTime, | ||
totalTime | ||
}, apiKey && { | ||
apiKey | ||
}); | ||
analyticsWebClient.sendOperationalEvent({ | ||
action, | ||
actionSubject: 'featureGatesClient', | ||
attributes, | ||
tags: ['measurement'], | ||
source: '@atlaskit/feature-gate-js-client' | ||
}); | ||
}).catch(err => { | ||
if (this.initOptions.environment !== FeatureGateEnvironment.Production) { | ||
// eslint-disable-next-line no-console | ||
console.error('Analytics web client promise did not resolve', err); | ||
/** | ||
* This method updates the user using a network call to fetch the new set of values. | ||
* @param fetchOptions {FetcherOptions} | ||
* @param identifiers {Identifiers} | ||
* @param customAttributes {CustomAttributes} | ||
*/ | ||
}, { | ||
key: "updateUser", | ||
value: (function () { | ||
var _updateUser = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee4(fetchOptions, identifiers, customAttributes) { | ||
var fetchOptionsWithDefaults, initializeValuesProducer; | ||
return _regeneratorRuntime.wrap(function _callee4$(_context4) { | ||
while (1) switch (_context4.prev = _context4.next) { | ||
case 0: | ||
this.assertInitialized(this.statsigClient); | ||
fetchOptionsWithDefaults = getOptionsWithDefaults(fetchOptions); | ||
initializeValuesProducer = function initializeValuesProducer() { | ||
return Fetcher.fetchExperimentValues(fetchOptionsWithDefaults, identifiers, customAttributes).then(function (_ref2) { | ||
var experimentValues = _ref2.experimentValues, | ||
customAttributes = _ref2.customAttributes; | ||
return { | ||
experimentValues: experimentValues, | ||
customAttributesFromFetch: customAttributes | ||
}; | ||
}); | ||
}; | ||
_context4.next = 5; | ||
return this.updateUserUsingInitializeValuesProducer(initializeValuesProducer, identifiers, customAttributes); | ||
case 5: | ||
case "end": | ||
return _context4.stop(); | ||
} | ||
}, _callee4, this); | ||
})); | ||
function updateUser(_x11, _x12, _x13) { | ||
return _updateUser.apply(this, arguments); | ||
} | ||
}); | ||
} | ||
async initializeFromValues(clientOptions, identifiers, customAttributes, initializeValues = {}) { | ||
const clientOptionsWithDefaults = getOptionsWithDefaults(clientOptions); | ||
if (this.initPromise) { | ||
if (!shallowEquals(clientOptionsWithDefaults, this.initOptions)) { | ||
// eslint-disable-next-line no-console | ||
console.warn('Feature Gates client already initialized with different options. New options were not applied.'); | ||
return updateUser; | ||
}() | ||
/** | ||
* This method updates the user using the provider given on initialisation to get the new set of | ||
* values. | ||
* @param identifiers {Identifiers} | ||
* @param customAttributes {CustomAttributes} | ||
*/ | ||
) | ||
}, { | ||
key: "updateUserWithProvider", | ||
value: (function () { | ||
var _updateUserWithProvider = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee5(identifiers, customAttributes) { | ||
var _this6 = this; | ||
return _regeneratorRuntime.wrap(function _callee5$(_context5) { | ||
while (1) switch (_context5.prev = _context5.next) { | ||
case 0: | ||
this.assertInitialized(this.statsigClient); | ||
if (this.provider) { | ||
_context5.next = 3; | ||
break; | ||
} | ||
throw new Error('Cannot update user using provider as the client was not initialised with a provider'); | ||
case 3: | ||
_context5.next = 5; | ||
return this.provider.setProfile(this.initOptions, identifiers, customAttributes); | ||
case 5: | ||
_context5.next = 7; | ||
return this.updateUserUsingInitializeValuesProducer(function () { | ||
return _this6.provider.getExperimentValues(); | ||
}, identifiers, customAttributes); | ||
case 7: | ||
case "end": | ||
return _context5.stop(); | ||
} | ||
}, _callee5, this); | ||
})); | ||
function updateUserWithProvider(_x14, _x15) { | ||
return _updateUserWithProvider.apply(this, arguments); | ||
} | ||
return this.initPromise; | ||
return updateUserWithProvider; | ||
}() | ||
/** | ||
* This method updates the user given a new set of bootstrap values obtained from one of the | ||
* server-side SDKs. | ||
* | ||
* @param identifiers {Identifiers} | ||
* @param customAttributes {CustomAttributes} | ||
* @param initializeValues {Record<string,unknown>} | ||
*/ | ||
) | ||
}, { | ||
key: "updateUserWithValues", | ||
value: (function () { | ||
var _updateUserWithValues = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee6(identifiers, customAttributes) { | ||
var initializeValues, | ||
initializeValuesProducer, | ||
_args6 = arguments; | ||
return _regeneratorRuntime.wrap(function _callee6$(_context6) { | ||
while (1) switch (_context6.prev = _context6.next) { | ||
case 0: | ||
initializeValues = _args6.length > 2 && _args6[2] !== undefined ? _args6[2] : {}; | ||
this.assertInitialized(this.statsigClient); | ||
initializeValuesProducer = function initializeValuesProducer() { | ||
return Promise.resolve({ | ||
experimentValues: initializeValues, | ||
customAttributesFromFetch: customAttributes | ||
}); | ||
}; | ||
_context6.next = 5; | ||
return this.updateUserUsingInitializeValuesProducer(initializeValuesProducer, identifiers, customAttributes); | ||
case 5: | ||
case "end": | ||
return _context6.stop(); | ||
} | ||
}, _callee6, this); | ||
})); | ||
function updateUserWithValues(_x16, _x17) { | ||
return _updateUserWithValues.apply(this, arguments); | ||
} | ||
return updateUserWithValues; | ||
}()) | ||
}, { | ||
key: "initializeCalled", | ||
value: function initializeCalled() { | ||
return this.initPromise != null; | ||
} | ||
// This makes sure the new Statsig client behaves like the old when bootstrap data is | ||
// passed, and `has_updates` isn't specified (which happens a lot in product integration tests). | ||
if (!Object.prototype.hasOwnProperty.call(initializeValues, 'has_updates')) { | ||
initializeValues['has_updates'] = true; | ||
}, { | ||
key: "initializeCompleted", | ||
value: function initializeCompleted() { | ||
return this.initCompleted; | ||
} | ||
const startTime = performance.now(); | ||
this.initOptions = clientOptionsWithDefaults; | ||
this.initPromise = this.initFromValues(clientOptionsWithDefaults, identifiers, customAttributes, initializeValues).then(() => { | ||
this.initCompleted = true; | ||
this.initWithDefaults = true; | ||
}).finally(() => { | ||
const endTime = performance.now(); | ||
const totalTime = endTime - startTime; | ||
this.fireClientEvent(startTime, totalTime, 'initializeFromValues', this.initCompleted); | ||
}); | ||
return this.initPromise; | ||
} | ||
assertInitialized(statsigClient) { | ||
if (!statsigClient) { | ||
throw new Error('Client must be initialized before using this method'); | ||
} | ||
} | ||
/** | ||
* This method updates the user using a network call to fetch the new set of values. | ||
* @param fetchOptions {FetcherOptions} | ||
* @param identifiers {Identifiers} | ||
* @param customAttributes {CustomAttributes} | ||
*/ | ||
async updateUser(fetchOptions, identifiers, customAttributes) { | ||
this.assertInitialized(this.statsigClient); | ||
const fetchOptionsWithDefaults = getOptionsWithDefaults(fetchOptions); | ||
const initializeValuesProducer = () => Fetcher.fetchExperimentValues(fetchOptionsWithDefaults, identifiers, customAttributes).then(({ | ||
experimentValues, | ||
customAttributes | ||
}) => ({ | ||
experimentValues, | ||
customAttributesFromFetch: customAttributes | ||
})); | ||
await this.updateUserUsingInitializeValuesProducer(initializeValuesProducer, identifiers, customAttributes); | ||
} | ||
/** | ||
* This method updates the user using the provider given on initialisation to get the new set of | ||
* values. | ||
* @param identifiers {Identifiers} | ||
* @param customAttributes {CustomAttributes} | ||
*/ | ||
async updateUserWithProvider(identifiers, customAttributes) { | ||
this.assertInitialized(this.statsigClient); | ||
if (!this.provider) { | ||
throw new Error('Cannot update user using provider as the client was not initialised with a provider'); | ||
} | ||
await this.provider.setProfile(this.initOptions, identifiers, customAttributes); | ||
await this.updateUserUsingInitializeValuesProducer(() => this.provider.getExperimentValues(), identifiers, customAttributes); | ||
} | ||
/** | ||
* This method updates the user given a new set of bootstrap values obtained from one of the | ||
* server-side SDKs. | ||
* | ||
* @param identifiers {Identifiers} | ||
* @param customAttributes {CustomAttributes} | ||
* @param initializeValues {Record<string,unknown>} | ||
*/ | ||
async updateUserWithValues(identifiers, customAttributes, initializeValues = {}) { | ||
this.assertInitialized(this.statsigClient); | ||
const initializeValuesProducer = () => Promise.resolve({ | ||
experimentValues: initializeValues, | ||
customAttributesFromFetch: customAttributes | ||
}); | ||
await this.updateUserUsingInitializeValuesProducer(initializeValuesProducer, identifiers, customAttributes); | ||
} | ||
initializeCalled() { | ||
return this.initPromise != null; | ||
} | ||
initializeCompleted() { | ||
return this.initCompleted; | ||
} | ||
/** | ||
* Returns the value for a feature gate. Returns false if there are errors. | ||
* @param {string} gateName - The name of the feature gate. | ||
* @param {Object} options | ||
* @param {boolean} options.fireGateExposure | ||
* Whether or not to fire the exposure event for the gate. Defaults to true. | ||
* To log an exposure event manually at a later time, use {@link Client.manuallyLogGateExposure} | ||
* (see [Statsig docs about manually logging exposures](https://docs.statsig.com/client/jsClientSDK#manual-exposures-)). | ||
*/ | ||
checkGate(gateName, options = {}) { | ||
try { | ||
this.assertInitialized(this.statsigClient); | ||
const { | ||
fireGateExposure = true | ||
} = options; | ||
return this.statsigClient.checkGate(gateName, { | ||
disableExposureLog: !fireGateExposure | ||
}); | ||
} catch (error) { | ||
// Log the first occurrence of the error | ||
if (!this.hasCheckGateErrorOccurred) { | ||
// eslint-disable-next-line no-console | ||
console.warn({ | ||
msg: 'An error has occurred checking the feature gate. Only the first occurrence of this error is logged.', | ||
gateName, | ||
error | ||
/** | ||
* Returns the value for a feature gate. Returns false if there are errors. | ||
* @param {string} gateName - The name of the feature gate. | ||
* @param {Object} options | ||
* @param {boolean} options.fireGateExposure | ||
* Whether or not to fire the exposure event for the gate. Defaults to true. | ||
* To log an exposure event manually at a later time, use {@link Client.manuallyLogGateExposure} | ||
* (see [Statsig docs about manually logging exposures](https://docs.statsig.com/client/jsClientSDK#manual-exposures-)). | ||
*/ | ||
}, { | ||
key: "checkGate", | ||
value: function checkGate(gateName) { | ||
var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; | ||
try { | ||
this.assertInitialized(this.statsigClient); | ||
var _options$fireGateExpo = options.fireGateExposure, | ||
fireGateExposure = _options$fireGateExpo === void 0 ? true : _options$fireGateExpo; | ||
return this.statsigClient.checkGate(gateName, { | ||
disableExposureLog: !fireGateExposure | ||
}); | ||
this.hasCheckGateErrorOccurred = true; | ||
} catch (error) { | ||
// Log the first occurrence of the error | ||
if (!this.hasCheckGateErrorOccurred) { | ||
// eslint-disable-next-line no-console | ||
console.warn({ | ||
msg: 'An error has occurred checking the feature gate. Only the first occurrence of this error is logged.', | ||
gateName: gateName, | ||
error: error | ||
}); | ||
this.hasCheckGateErrorOccurred = true; | ||
} | ||
return false; | ||
} | ||
return false; | ||
} | ||
} | ||
isGateExist(gateName) { | ||
try { | ||
this.assertInitialized(this.statsigClient); | ||
const gate = this.statsigClient.getFeatureGate(gateName, { | ||
disableExposureLog: true | ||
}); | ||
return !gate.details.reason.includes('Unrecognized'); | ||
} catch (error) { | ||
// eslint-disable-next-line no-console | ||
console.error(`Error occurred when trying to check FeatureGate: ${error}`); | ||
// in case of error report true to avoid false positives. | ||
return true; | ||
} | ||
} | ||
isExperimentExist(experimentName) { | ||
try { | ||
this.assertInitialized(this.statsigClient); | ||
const config = this.statsigClient.getExperiment(experimentName, { | ||
disableExposureLog: true | ||
}); | ||
return !config.details.reason.includes('Unrecognized'); | ||
} catch (error) { | ||
// eslint-disable-next-line no-console | ||
console.error(`Error occurred when trying to check Experiment: ${error}`); | ||
// in case of error report true to avoid false positives. | ||
return true; | ||
} | ||
} | ||
/** | ||
* Manually log a gate exposure (see [Statsig docs about manually logging exposures](https://docs.statsig.com/client/jsClientSDK#manual-exposures-)). | ||
* This is useful if you have evaluated a gate earlier via {@link Client.checkGate} where | ||
* <code>options.fireGateExposure</code> is false. | ||
* @param gateName | ||
*/ | ||
manuallyLogGateExposure(gateName) { | ||
this.assertInitialized(this.statsigClient); | ||
// This is the approach recommended in the docs | ||
// https://docs.statsig.com/client/javascript-sdk/#manual-exposures- | ||
this.statsigClient.checkGate(gateName); | ||
} | ||
/** | ||
* Returns the entire config for a given experiment. | ||
* | ||
* @param {string} experimentName - The name of the experiment | ||
* @param {Object} options | ||
* @param {boolean} options.fireExperimentExposure - Whether or not to fire the exposure event | ||
* for the experiment. Defaults to true. To log an exposure event manually at a later time, use | ||
* {@link Client.manuallyLogExperimentExposure} (see [Statsig docs about manually logging exposures](https://docs.statsig.com/client/jsClientSDK#manual-exposures-)). | ||
* @returns The config for an experiment | ||
* @example | ||
* ```ts | ||
* const experimentConfig = client.getExperiment('example-experiment-name'); | ||
* const backgroundColor: string = experimentConfig.get('backgroundColor', 'yellow'); | ||
* ``` | ||
*/ | ||
getExperiment(experimentName, options = {}) { | ||
try { | ||
this.assertInitialized(this.statsigClient); | ||
const { | ||
fireExperimentExposure = true | ||
} = options; | ||
return DynamicConfig.fromExperiment(this.statsigClient.getExperiment(experimentName, { | ||
disableExposureLog: !fireExperimentExposure | ||
})); | ||
} catch (error) { | ||
// Log the first occurrence of the error | ||
if (!this.hasGetExperimentErrorOccurred) { | ||
}, { | ||
key: "isGateExist", | ||
value: function isGateExist(gateName) { | ||
try { | ||
this.assertInitialized(this.statsigClient); | ||
var gate = this.statsigClient.getFeatureGate(gateName, { | ||
disableExposureLog: true | ||
}); | ||
return !gate.details.reason.includes('Unrecognized'); | ||
} catch (error) { | ||
// eslint-disable-next-line no-console | ||
console.warn({ | ||
msg: 'An error has occurred getting the experiment. Only the first occurrence of this error is logged.', | ||
experimentName, | ||
error | ||
}); | ||
this.hasGetExperimentErrorOccurred = true; | ||
console.error("Error occurred when trying to check FeatureGate: ".concat(error)); | ||
// in case of error report true to avoid false positives. | ||
return true; | ||
} | ||
// Return a default value | ||
return new DynamicConfig(experimentName, {}, '', { | ||
time: Date.now(), | ||
reason: EvaluationReason.Error | ||
}); | ||
} | ||
} | ||
/** | ||
* Returns the value of a given parameter in an experiment config. | ||
* | ||
* @template T | ||
* @param {string} experimentName - The name of the experiment | ||
* @param {string} parameterName - The name of the parameter to fetch from the experiment config | ||
* @param {T} defaultValue - The value to serve if the experiment or parameter do not exist, or | ||
* if the returned value does not match the expected type. | ||
* @param {Object} options | ||
* @param {boolean} options.fireExperimentExposure - Whether or not to fire the exposure event | ||
* for the experiment. Defaults to true. To log an exposure event manually at a later time, use | ||
* {@link Client.manuallyLogExperimentExposure} (see [Statsig docs about manually logging exposures](https://docs.statsig.com/client/jsClientSDK#manual-exposures-)) | ||
* @param {function} options.typeGuard - A function that asserts that the return value has the | ||
* expected type. If this function returns false, then the default value will be returned | ||
* instead. This can be set to protect your code from unexpected values being set remotely. By | ||
* default, this will be done by asserting that the default value and value are the same primitive | ||
* type. | ||
* @returns The value of the parameter if the experiment and parameter both exist, otherwise the | ||
* default value. | ||
* @example | ||
``` ts | ||
type ValidColor = 'blue' | 'red' | 'yellow'; | ||
type ValidColorTypeCheck = (value: unknown) => value is ValidColor; | ||
const isValidColor: ValidColorTypeCheck = | ||
(value: unknown) => typeof value === 'string' && ['blue', 'red', 'yellow'].includes(value); | ||
const buttonColor: ValidColor = client.getExperimentValue( | ||
'example-experiment-name', | ||
'backgroundColor', | ||
'yellow', | ||
{ | ||
typeGuard: isValidColor | ||
} | ||
); | ||
``` | ||
*/ | ||
getExperimentValue(experimentName, parameterName, defaultValue, options = {}) { | ||
const experiment = this.getExperiment(experimentName, options); | ||
try { | ||
const { | ||
typeGuard | ||
} = options; | ||
return experiment.get(parameterName, defaultValue, typeGuard); | ||
} catch (error) { | ||
// Log the first occurrence of the error | ||
if (!this.hasGetExperimentValueErrorOccurred) { | ||
}, { | ||
key: "isExperimentExist", | ||
value: function isExperimentExist(experimentName) { | ||
try { | ||
this.assertInitialized(this.statsigClient); | ||
var config = this.statsigClient.getExperiment(experimentName, { | ||
disableExposureLog: true | ||
}); | ||
return !config.details.reason.includes('Unrecognized'); | ||
} catch (error) { | ||
// eslint-disable-next-line no-console | ||
console.warn({ | ||
msg: 'An error has occurred getting the experiment value. Only the first occurrence of this error is logged.', | ||
experimentName, | ||
defaultValue, | ||
options, | ||
error | ||
}); | ||
this.hasGetExperimentValueErrorOccurred = true; | ||
console.error("Error occurred when trying to check Experiment: ".concat(error)); | ||
// in case of error report true to avoid false positives. | ||
return true; | ||
} | ||
return defaultValue; | ||
} | ||
} | ||
/** | ||
* Manually log an experiment exposure (see [Statsig docs about manually logging exposures](https://docs.statsig.com/client/jsClientSDK#manual-exposures-)). | ||
* This is useful if you have evaluated an experiment earlier via {@link Client.getExperimentValue} or | ||
* {@link Client.getExperiment} where <code>options.fireExperimentExposure</code> is false. | ||
* @param experimentName | ||
*/ | ||
manuallyLogExperimentExposure(experimentName) { | ||
this.assertInitialized(this.statsigClient); | ||
// This is the approach recommended in the docs | ||
// https://docs.statsig.com/client/javascript-sdk/#manual-exposures- | ||
this.statsigClient.getExperiment(experimentName); | ||
} | ||
/** | ||
* Manually log a layer exposure (see [Statsig docs about manually logging exposures](https://docs.statsig.com/client/jsClientSDK#manual-exposures-)). | ||
* This is useful if you have evaluated a layer earlier via {@link Client.getLayerValue} where <code>options.fireExperimentExposure</code> is false. | ||
* @param layerName | ||
* @param parameterName | ||
*/ | ||
manuallyLogLayerExposure(layerName, parameterName) { | ||
var _this$statsigClient$g; | ||
this.assertInitialized(this.statsigClient); | ||
// This is the approach recommended in the docs | ||
// https://docs.statsig.com/client/javascript-sdk/#manual-exposures- | ||
(_this$statsigClient$g = this.statsigClient.getLayer(layerName)) === null || _this$statsigClient$g === void 0 || _this$statsigClient$g.get(parameterName); | ||
} | ||
shutdownStatsig() { | ||
this.assertInitialized(this.statsigClient); | ||
this.statsigClient.shutdown(); | ||
} | ||
/** | ||
* Adds a new override for the given gate. | ||
* | ||
* This method is additive, meaning you can call it multiple times with different gate names to | ||
* build your full set of overrides. | ||
* | ||
* Overrides are persisted to the `STATSIG_OVERRIDES` key in localStorage, so they | ||
* will continue to affect every client that is initialized on the same domain after this method | ||
* is called. If you are using this API for testing purposes, you should call | ||
* {@link Client.clearGateOverride} after your tests are completed to remove this | ||
* localStorage entry. | ||
* | ||
* @param {string} gateName | ||
* @param {boolean} value | ||
*/ | ||
overrideGate(gateName, value) { | ||
this.overrideAdapter.overrideGate(gateName, value); | ||
// Trigger a reset of the memoized gate value | ||
if (this.user) { | ||
var _this$statsigClient; | ||
(_this$statsigClient = this.statsigClient) === null || _this$statsigClient === void 0 || _this$statsigClient.updateUserSync(this.user, { | ||
disableBackgroundCacheRefresh: true | ||
}); | ||
/** | ||
* Manually log a gate exposure (see [Statsig docs about manually logging exposures](https://docs.statsig.com/client/jsClientSDK#manual-exposures-)). | ||
* This is useful if you have evaluated a gate earlier via {@link Client.checkGate} where | ||
* <code>options.fireGateExposure</code> is false. | ||
* @param gateName | ||
*/ | ||
}, { | ||
key: "manuallyLogGateExposure", | ||
value: function manuallyLogGateExposure(gateName) { | ||
this.assertInitialized(this.statsigClient); | ||
// This is the approach recommended in the docs | ||
// https://docs.statsig.com/client/javascript-sdk/#manual-exposures- | ||
this.statsigClient.checkGate(gateName); | ||
} | ||
this.statsigValuesUpdated(); | ||
} | ||
/** | ||
* Removes any overrides that have been set for the given gate. | ||
*/ | ||
clearGateOverride(gateName) { | ||
this.overrideAdapter.removeGateOverride(gateName); | ||
this.statsigValuesUpdated(); | ||
} | ||
/** | ||
* Adds a new override for the given config (or experiment). | ||
* | ||
* This method is additive, meaning you can call it multiple times with different experiment | ||
* names to build your full set of overrides. | ||
* | ||
* Overrides are persisted to the `STATSIG_OVERRIDES` key in localStorage, so they | ||
* will continue to affect every client that is initialized on the same domain after this method | ||
* is called. If you are using this API for testing purposes, you should call | ||
* {@link Client.clearConfigOverride} after your tests are completed to remove this | ||
* localStorage entry. | ||
* | ||
* @param {string} experimentName | ||
* @param {object} values | ||
*/ | ||
overrideConfig(experimentName, values) { | ||
this.overrideAdapter.overrideDynamicConfig(experimentName, values); | ||
this.statsigValuesUpdated(); | ||
} | ||
/** | ||
* Removes any overrides that have been set for the given experiment. | ||
* @param {string} experimentName | ||
*/ | ||
clearConfigOverride(experimentName) { | ||
this.overrideAdapter.removeDynamicConfigOverride(experimentName); | ||
this.statsigValuesUpdated(); | ||
} | ||
/** | ||
* Set overrides for gates, experiments and layers in batch. | ||
* | ||
* Note that these overrides are **not** additive and will completely replace any that have been | ||
* added via prior calls to {@link Client.overrideConfig} or | ||
* {@link Client.overrideGate}. | ||
* | ||
* Overrides are persisted to the `STATSIG_OVERRIDES` key in localStorage, so they | ||
* will continue to affect every client that is initialized on the same domain after this method | ||
* is called. If you are using this API for testing purposes, you should call | ||
* {@link Client.clearAllOverrides} after your tests are completed to remove this | ||
* localStorage entry. | ||
*/ | ||
setOverrides(overrides) { | ||
this.overrideAdapter.setOverrides(overrides); | ||
this.statsigValuesUpdated(); | ||
} | ||
/** | ||
* @returns The current overrides for gates, configs (including experiments) and layers. | ||
*/ | ||
getOverrides() { | ||
return this.overrideAdapter.getOverrides(); | ||
} | ||
/** | ||
* Clears overrides for all gates, configs (including experiments) and layers. | ||
*/ | ||
clearAllOverrides() { | ||
this.overrideAdapter.removeAllOverrides(); | ||
this.statsigValuesUpdated(); | ||
} | ||
/** | ||
* Returns whether the given identifiers and customAttributes align with the current | ||
* set that is being used by the client. | ||
* | ||
* If this method returns false, then the {@link Client.updateUser}, | ||
* {@link Client.updateUserWithValues} or {@link Client.updateUserWithProvider} | ||
* methods can be used to re-align these values. | ||
* | ||
* @param identifiers | ||
* @param customAttributes | ||
* @returns a flag indicating whether the clients current configuration aligns with the given values | ||
*/ | ||
isCurrentUser(identifiers, customAttributes) { | ||
return shallowEquals(this.currentIdentifiers, identifiers) && shallowEquals(this.currentAttributes, customAttributes); | ||
} | ||
/** | ||
* Subscribe to updates where the given callback will be called with the current checkGate value | ||
* @param gateName | ||
* @param callback | ||
* @param options | ||
* @returns off function to unsubscribe from updates | ||
*/ | ||
onGateUpdated(gateName, callback, options = {}) { | ||
const wrapCallback = value => { | ||
const { | ||
fireGateExposure = true | ||
} = options; | ||
if (fireGateExposure) { | ||
this.manuallyLogGateExposure(gateName); | ||
} | ||
/** | ||
* Returns the entire config for a given experiment. | ||
* | ||
* @param {string} experimentName - The name of the experiment | ||
* @param {Object} options | ||
* @param {boolean} options.fireExperimentExposure - Whether or not to fire the exposure event | ||
* for the experiment. Defaults to true. To log an exposure event manually at a later time, use | ||
* {@link Client.manuallyLogExperimentExposure} (see [Statsig docs about manually logging exposures](https://docs.statsig.com/client/jsClientSDK#manual-exposures-)). | ||
* @returns The config for an experiment | ||
* @example | ||
* ```ts | ||
* const experimentConfig = client.getExperiment('example-experiment-name'); | ||
* const backgroundColor: string = experimentConfig.get('backgroundColor', 'yellow'); | ||
* ``` | ||
*/ | ||
}, { | ||
key: "getExperiment", | ||
value: function getExperiment(experimentName) { | ||
var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; | ||
try { | ||
callback(value); | ||
this.assertInitialized(this.statsigClient); | ||
var _options$fireExperime = options.fireExperimentExposure, | ||
fireExperimentExposure = _options$fireExperime === void 0 ? true : _options$fireExperime; | ||
return DynamicConfig.fromExperiment(this.statsigClient.getExperiment(experimentName, { | ||
disableExposureLog: !fireExperimentExposure | ||
})); | ||
} catch (error) { | ||
// eslint-disable-next-line no-console | ||
console.warn(`Error calling callback for gate ${gateName} with value ${value}`, error); | ||
// Log the first occurrence of the error | ||
if (!this.hasGetExperimentErrorOccurred) { | ||
// eslint-disable-next-line no-console | ||
console.warn({ | ||
msg: 'An error has occurred getting the experiment. Only the first occurrence of this error is logged.', | ||
experimentName: experimentName, | ||
error: error | ||
}); | ||
this.hasGetExperimentErrorOccurred = true; | ||
} | ||
// Return a default value | ||
return new DynamicConfig(experimentName, {}, '', { | ||
time: Date.now(), | ||
reason: EvaluationReason.Error | ||
}); | ||
} | ||
}; | ||
return this.subscriptions.onGateUpdated(gateName, wrapCallback, this.checkGate.bind(this), options); | ||
} | ||
} | ||
/** | ||
* Subscribe to updates where the given callback will be called with the current experiment value | ||
* @param experimentName | ||
* @param parameterName | ||
* @param defaultValue | ||
* @param callback | ||
* @param options | ||
* @returns off function to unsubscribe from updates | ||
*/ | ||
onExperimentValueUpdated(experimentName, parameterName, defaultValue, callback, options = {}) { | ||
const wrapCallback = value => { | ||
const { | ||
fireExperimentExposure = true | ||
} = options; | ||
if (fireExperimentExposure) { | ||
this.manuallyLogExperimentExposure(experimentName); | ||
} | ||
/** | ||
* Returns the value of a given parameter in an experiment config. | ||
* | ||
* @template T | ||
* @param {string} experimentName - The name of the experiment | ||
* @param {string} parameterName - The name of the parameter to fetch from the experiment config | ||
* @param {T} defaultValue - The value to serve if the experiment or parameter do not exist, or | ||
* if the returned value does not match the expected type. | ||
* @param {Object} options | ||
* @param {boolean} options.fireExperimentExposure - Whether or not to fire the exposure event | ||
* for the experiment. Defaults to true. To log an exposure event manually at a later time, use | ||
* {@link Client.manuallyLogExperimentExposure} (see [Statsig docs about manually logging exposures](https://docs.statsig.com/client/jsClientSDK#manual-exposures-)) | ||
* @param {function} options.typeGuard - A function that asserts that the return value has the | ||
* expected type. If this function returns false, then the default value will be returned | ||
* instead. This can be set to protect your code from unexpected values being set remotely. By | ||
* default, this will be done by asserting that the default value and value are the same primitive | ||
* type. | ||
* @returns The value of the parameter if the experiment and parameter both exist, otherwise the | ||
* default value. | ||
* @example | ||
``` ts | ||
type ValidColor = 'blue' | 'red' | 'yellow'; | ||
type ValidColorTypeCheck = (value: unknown) => value is ValidColor; | ||
const isValidColor: ValidColorTypeCheck = | ||
(value: unknown) => typeof value === 'string' && ['blue', 'red', 'yellow'].includes(value); | ||
const buttonColor: ValidColor = client.getExperimentValue( | ||
'example-experiment-name', | ||
'backgroundColor', | ||
'yellow', | ||
{ | ||
typeGuard: isValidColor | ||
} | ||
); | ||
``` | ||
*/ | ||
}, { | ||
key: "getExperimentValue", | ||
value: function getExperimentValue(experimentName, parameterName, defaultValue) { | ||
var options = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : {}; | ||
var experiment = this.getExperiment(experimentName, options); | ||
try { | ||
callback(value); | ||
var typeGuard = options.typeGuard; | ||
return experiment.get(parameterName, defaultValue, typeGuard); | ||
} catch (error) { | ||
// eslint-disable-next-line no-console | ||
console.warn(`Error calling callback for experiment ${experimentName} with value ${value}`, error); | ||
// Log the first occurrence of the error | ||
if (!this.hasGetExperimentValueErrorOccurred) { | ||
// eslint-disable-next-line no-console | ||
console.warn({ | ||
msg: 'An error has occurred getting the experiment value. Only the first occurrence of this error is logged.', | ||
experimentName: experimentName, | ||
defaultValue: defaultValue, | ||
options: options, | ||
error: error | ||
}); | ||
this.hasGetExperimentValueErrorOccurred = true; | ||
} | ||
return defaultValue; | ||
} | ||
}; | ||
return this.subscriptions.onExperimentValueUpdated(experimentName, parameterName, defaultValue, wrapCallback, this.getExperimentValue.bind(this), options); | ||
} | ||
} | ||
/** | ||
* Subscribe so on any update the callback will be called. | ||
* NOTE: The callback will be called whenever the values are updated even if the values have not | ||
* changed. | ||
* @param callback | ||
* @returns off function to unsubscribe from updates | ||
*/ | ||
onAnyUpdated(callback) { | ||
return this.subscriptions.onAnyUpdated(callback); | ||
} | ||
/** | ||
* Manually log an experiment exposure (see [Statsig docs about manually logging exposures](https://docs.statsig.com/client/jsClientSDK#manual-exposures-)). | ||
* This is useful if you have evaluated an experiment earlier via {@link Client.getExperimentValue} or | ||
* {@link Client.getExperiment} where <code>options.fireExperimentExposure</code> is false. | ||
* @param experimentName | ||
*/ | ||
}, { | ||
key: "manuallyLogExperimentExposure", | ||
value: function manuallyLogExperimentExposure(experimentName) { | ||
this.assertInitialized(this.statsigClient); | ||
// This is the approach recommended in the docs | ||
// https://docs.statsig.com/client/javascript-sdk/#manual-exposures- | ||
this.statsigClient.getExperiment(experimentName); | ||
} | ||
/** | ||
* This method initializes the client using a network call to fetch the bootstrap values for the | ||
* given user. | ||
* | ||
* @param clientOptions | ||
* @param identifiers | ||
* @param customAttributes | ||
* @private | ||
*/ | ||
async init(clientOptions, identifiers, customAttributes) { | ||
const fromValuesClientOptions = _objectSpread({}, clientOptions); | ||
let experimentValues; | ||
let customAttributesFromResult; | ||
try { | ||
// If client sdk key fetch fails, an error would be thrown and handled instead of waiting for | ||
// the experiment values request to be settled, and it will fall back to use default values. | ||
const clientSdkKeyPromise = Fetcher.fetchClientSdk(clientOptions).then(value => fromValuesClientOptions.sdkKey = value.clientSdkKey); | ||
const experimentValuesPromise = Fetcher.fetchExperimentValues(clientOptions, identifiers, customAttributes); | ||
/** | ||
* Manually log a layer exposure (see [Statsig docs about manually logging exposures](https://docs.statsig.com/client/jsClientSDK#manual-exposures-)). | ||
* This is useful if you have evaluated a layer earlier via {@link Client.getLayerValue} where <code>options.fireExperimentExposure</code> is false. | ||
* @param layerName | ||
* @param parameterName | ||
*/ | ||
}, { | ||
key: "manuallyLogLayerExposure", | ||
value: function manuallyLogLayerExposure(layerName, parameterName) { | ||
var _this$statsigClient$g; | ||
this.assertInitialized(this.statsigClient); | ||
// This is the approach recommended in the docs | ||
// https://docs.statsig.com/client/javascript-sdk/#manual-exposures- | ||
(_this$statsigClient$g = this.statsigClient.getLayer(layerName)) === null || _this$statsigClient$g === void 0 || _this$statsigClient$g.get(parameterName); | ||
} | ||
}, { | ||
key: "shutdownStatsig", | ||
value: function shutdownStatsig() { | ||
this.assertInitialized(this.statsigClient); | ||
this.statsigClient.shutdown(); | ||
} | ||
// Only wait for the experiment values request to finish and try to initialise the client | ||
// with experiment values if both requests are successful. Else an error would be thrown and | ||
// handled by the catch | ||
const [, experimentValuesResult] = await Promise.all([clientSdkKeyPromise, experimentValuesPromise]); | ||
experimentValues = experimentValuesResult.experimentValues; | ||
customAttributesFromResult = experimentValuesResult.customAttributes; | ||
} catch (error) { | ||
if (error instanceof Error) { | ||
// eslint-disable-next-line no-console | ||
console.error(`Error occurred when trying to fetch the Feature Gates client values, error: ${error === null || error === void 0 ? void 0 : error.message}`); | ||
/** | ||
* Adds a new override for the given gate. | ||
* | ||
* This method is additive, meaning you can call it multiple times with different gate names to | ||
* build your full set of overrides. | ||
* | ||
* Overrides are persisted to the `STATSIG_OVERRIDES` key in localStorage, so they | ||
* will continue to affect every client that is initialized on the same domain after this method | ||
* is called. If you are using this API for testing purposes, you should call | ||
* {@link Client.clearGateOverride} after your tests are completed to remove this | ||
* localStorage entry. | ||
* | ||
* @param {string} gateName | ||
* @param {boolean} value | ||
*/ | ||
}, { | ||
key: "overrideGate", | ||
value: function overrideGate(gateName, value) { | ||
this.overrideAdapter.overrideGate(gateName, value); | ||
// Trigger a reset of the memoized gate value | ||
if (this.user) { | ||
var _this$statsigClient; | ||
(_this$statsigClient = this.statsigClient) === null || _this$statsigClient === void 0 || _this$statsigClient.updateUserSync(this.user, { | ||
disableBackgroundCacheRefresh: true | ||
}); | ||
} | ||
// eslint-disable-next-line no-console | ||
console.warn(`Initialising Statsig client without values`); | ||
await this.initFromValues(fromValuesClientOptions, identifiers, customAttributes); | ||
throw error; | ||
this.statsigValuesUpdated(); | ||
} | ||
return this.initFromValues(fromValuesClientOptions, identifiers, customAttributesFromResult, experimentValues); | ||
} | ||
async initWithProvider(baseClientOptions, provider, identifiers, customAttributes) { | ||
const fromValuesClientOptions = _objectSpread(_objectSpread({}, baseClientOptions), {}, { | ||
disableCurrentPageLogging: true | ||
}); | ||
let experimentValues; | ||
let customAttributesFromResult; | ||
try { | ||
await provider.setProfile(baseClientOptions, identifiers, customAttributes); | ||
// If client sdk key fetch fails, an error would be thrown and handled instead of waiting for | ||
// the experiment values request to be settled, and it will fall back to use default values. | ||
const clientSdkKeyPromise = provider.getClientSdkKey().then(value => fromValuesClientOptions.sdkKey = value); | ||
const experimentValuesPromise = provider.getExperimentValues(); | ||
/** | ||
* Removes any overrides that have been set for the given gate. | ||
*/ | ||
}, { | ||
key: "clearGateOverride", | ||
value: function clearGateOverride(gateName) { | ||
this.overrideAdapter.removeGateOverride(gateName); | ||
this.statsigValuesUpdated(); | ||
} | ||
// Only wait for the experiment values request to finish and try to initialise the client | ||
// with experiment values if both requests are successful. Else an error would be thrown and | ||
// handled by the catch | ||
const [, experimentValuesResult] = await Promise.all([clientSdkKeyPromise, experimentValuesPromise]); | ||
experimentValues = experimentValuesResult.experimentValues; | ||
customAttributesFromResult = experimentValuesResult.customAttributesFromFetch; | ||
} catch (error) { | ||
if (error instanceof Error) { | ||
// eslint-disable-next-line no-console | ||
console.error(`Error occurred when trying to fetch the Feature Gates client values, error: ${error === null || error === void 0 ? void 0 : error.message}`); | ||
} | ||
// eslint-disable-next-line no-console | ||
console.warn(`Initialising Statsig client without values`); | ||
await this.initFromValues(fromValuesClientOptions, identifiers, customAttributes); | ||
throw error; | ||
/** | ||
* Adds a new override for the given config (or experiment). | ||
* | ||
* This method is additive, meaning you can call it multiple times with different experiment | ||
* names to build your full set of overrides. | ||
* | ||
* Overrides are persisted to the `STATSIG_OVERRIDES` key in localStorage, so they | ||
* will continue to affect every client that is initialized on the same domain after this method | ||
* is called. If you are using this API for testing purposes, you should call | ||
* {@link Client.clearConfigOverride} after your tests are completed to remove this | ||
* localStorage entry. | ||
* | ||
* @param {string} experimentName | ||
* @param {object} values | ||
*/ | ||
}, { | ||
key: "overrideConfig", | ||
value: function overrideConfig(experimentName, values) { | ||
this.overrideAdapter.overrideDynamicConfig(experimentName, values); | ||
this.statsigValuesUpdated(); | ||
} | ||
return this.initFromValues(fromValuesClientOptions, identifiers, customAttributesFromResult, experimentValues); | ||
} | ||
/** | ||
* This method initializes the client using a set of boostrap values obtained from one of the | ||
* server-side SDKs. | ||
* | ||
* @param clientOptions | ||
* @param identifiers | ||
* @param customAttributes | ||
* @param initializeValues | ||
* @private | ||
*/ | ||
async initFromValues(clientOptions, identifiers, customAttributes, initializeValues = {}) { | ||
var _newClientOptions$net; | ||
this.overrideAdapter.initFromStoredOverrides(); | ||
this.currentIdentifiers = identifiers; | ||
this.currentAttributes = customAttributes; | ||
const newClientOptions = migrateInitializationOptions(clientOptions); | ||
if (!newClientOptions.sdkKey) { | ||
newClientOptions.sdkKey = DEFAULT_CLIENT_KEY; | ||
/** | ||
* Removes any overrides that have been set for the given experiment. | ||
* @param {string} experimentName | ||
*/ | ||
}, { | ||
key: "clearConfigOverride", | ||
value: function clearConfigOverride(experimentName) { | ||
this.overrideAdapter.removeDynamicConfigOverride(experimentName); | ||
this.statsigValuesUpdated(); | ||
} | ||
if (!((_newClientOptions$net = newClientOptions.networkConfig) !== null && _newClientOptions$net !== void 0 && _newClientOptions$net.logEventUrl)) { | ||
newClientOptions.networkConfig = _objectSpread(_objectSpread({}, newClientOptions.networkConfig), {}, { | ||
logEventUrl: DEFAULT_EVENT_LOGGING_API | ||
}); | ||
/** | ||
* Set overrides for gates, experiments and layers in batch. | ||
* | ||
* Note that these overrides are **not** additive and will completely replace any that have been | ||
* added via prior calls to {@link Client.overrideConfig} or | ||
* {@link Client.overrideGate}. | ||
* | ||
* Overrides are persisted to the `STATSIG_OVERRIDES` key in localStorage, so they | ||
* will continue to affect every client that is initialized on the same domain after this method | ||
* is called. If you are using this API for testing purposes, you should call | ||
* {@link Client.clearAllOverrides} after your tests are completed to remove this | ||
* localStorage entry. | ||
*/ | ||
}, { | ||
key: "setOverrides", | ||
value: function setOverrides(overrides) { | ||
this.overrideAdapter.setOverrides(overrides); | ||
this.statsigValuesUpdated(); | ||
} | ||
if (newClientOptions.perimeter === PerimeterType.FEDRAMP_MODERATE) { | ||
// disable all logging in FedRAMP to prevent egress of sensitive data | ||
newClientOptions.disableLogging = true; | ||
/** | ||
* @returns The current overrides for gates, configs (including experiments) and layers. | ||
*/ | ||
}, { | ||
key: "getOverrides", | ||
value: function getOverrides() { | ||
return this.overrideAdapter.getOverrides(); | ||
} | ||
const { | ||
sdkKey, | ||
environment, | ||
updateUserCompletionCallback: _updateUserCompletionCallback, | ||
perimeter: _perimeter | ||
} = newClientOptions, | ||
restClientOptions = _objectWithoutProperties(newClientOptions, _excluded); | ||
this.sdkKey = sdkKey; | ||
this.user = toStatsigUser(identifiers, customAttributes, this.sdkKey); | ||
const statsigOptions = _objectSpread(_objectSpread({}, restClientOptions), {}, { | ||
environment: { | ||
tier: environment | ||
}, | ||
includeCurrentPageUrlWithEvents: false, | ||
dataAdapter: this.dataAdapter, | ||
overrideAdapter: this.overrideAdapter | ||
}); | ||
try { | ||
this.statsigClient = new StatsigClient(sdkKey, this.user, statsigOptions); | ||
this.dataAdapter.setBootstrapData(initializeValues); | ||
await this.statsigClient.initializeAsync(); | ||
} catch (error) { | ||
if (error instanceof Error) { | ||
// eslint-disable-next-line no-console | ||
console.error(`Error occurred when trying to initialise the Statsig client, error: ${error === null || error === void 0 ? void 0 : error.message}`); | ||
} | ||
// eslint-disable-next-line no-console | ||
console.warn(`Initialising Statsig client with default sdk key and without values`); | ||
this.statsigClient = new StatsigClient(DEFAULT_CLIENT_KEY, this.user, statsigOptions); | ||
this.dataAdapter.setBootstrapData(); | ||
await this.statsigClient.initializeAsync(); | ||
this.initWithDefaults = true; | ||
throw error; | ||
/** | ||
* Clears overrides for all gates, configs (including experiments) and layers. | ||
*/ | ||
}, { | ||
key: "clearAllOverrides", | ||
value: function clearAllOverrides() { | ||
this.overrideAdapter.removeAllOverrides(); | ||
this.statsigValuesUpdated(); | ||
} | ||
} | ||
/** | ||
* This method updates the user for this client with the bootstrap values returned from a given | ||
* Promise. | ||
* It uses the customAttributes from fetching experiment values to update the Statsig user but | ||
* uses the customAttributes from given input to check if the user has changed. | ||
* | ||
* @param {Identifiers} identifiers | ||
* @param {CustomAttributes} customAttributes | ||
* @param {Promise<InitializeValues>} getInitializeValues | ||
* @private | ||
*/ | ||
async updateUserUsingInitializeValuesProducer(getInitializeValues, identifiers, customAttributes) { | ||
this.assertInitialized(this.statsigClient); | ||
if (!this.initPromise) { | ||
throw new Error('The client must be initialized before you can update the user.'); | ||
/** | ||
* Returns whether the given identifiers and customAttributes align with the current | ||
* set that is being used by the client. | ||
* | ||
* If this method returns false, then the {@link Client.updateUser}, | ||
* {@link Client.updateUserWithValues} or {@link Client.updateUserWithProvider} | ||
* methods can be used to re-align these values. | ||
* | ||
* @param identifiers | ||
* @param customAttributes | ||
* @returns a flag indicating whether the clients current configuration aligns with the given values | ||
*/ | ||
}, { | ||
key: "isCurrentUser", | ||
value: function isCurrentUser(identifiers, customAttributes) { | ||
return shallowEquals(this.currentIdentifiers, identifiers) && shallowEquals(this.currentAttributes, customAttributes); | ||
} | ||
// If the user isn't changing at all, then exit immediately | ||
if (this.isCurrentUser(identifiers, customAttributes)) { | ||
return this.initPromise; | ||
/** | ||
* Subscribe to updates where the given callback will be called with the current checkGate value | ||
* @param gateName | ||
* @param callback | ||
* @param options | ||
* @returns off function to unsubscribe from updates | ||
*/ | ||
}, { | ||
key: "onGateUpdated", | ||
value: function onGateUpdated(gateName, callback) { | ||
var _this7 = this; | ||
var options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {}; | ||
var wrapCallback = function wrapCallback(value) { | ||
var _options$fireGateExpo2 = options.fireGateExposure, | ||
fireGateExposure = _options$fireGateExpo2 === void 0 ? true : _options$fireGateExpo2; | ||
if (fireGateExposure) { | ||
_this7.manuallyLogGateExposure(gateName); | ||
} | ||
try { | ||
callback(value); | ||
} catch (error) { | ||
// eslint-disable-next-line no-console | ||
console.warn("Error calling callback for gate ".concat(gateName, " with value ").concat(value), error); | ||
} | ||
}; | ||
return this.subscriptions.onGateUpdated(gateName, wrapCallback, this.checkGate.bind(this), options); | ||
} | ||
// Wait for the current initialize/update to finish | ||
const originalInitPromise = this.initPromise; | ||
try { | ||
await this.initPromise; | ||
} catch (err) { | ||
// Proceed with the user update even if the init failed, since this update | ||
// may put the client back into a valid state. | ||
/** | ||
* Subscribe to updates where the given callback will be called with the current experiment value | ||
* @param experimentName | ||
* @param parameterName | ||
* @param defaultValue | ||
* @param callback | ||
* @param options | ||
* @returns off function to unsubscribe from updates | ||
*/ | ||
}, { | ||
key: "onExperimentValueUpdated", | ||
value: function onExperimentValueUpdated(experimentName, parameterName, defaultValue, callback) { | ||
var _this8 = this; | ||
var options = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : {}; | ||
var wrapCallback = function wrapCallback(value) { | ||
var _options$fireExperime2 = options.fireExperimentExposure, | ||
fireExperimentExposure = _options$fireExperime2 === void 0 ? true : _options$fireExperime2; | ||
if (fireExperimentExposure) { | ||
_this8.manuallyLogExperimentExposure(experimentName); | ||
} | ||
try { | ||
callback(value); | ||
} catch (error) { | ||
// eslint-disable-next-line no-console | ||
console.warn("Error calling callback for experiment ".concat(experimentName, " with value ").concat(value), error); | ||
} | ||
}; | ||
return this.subscriptions.onExperimentValueUpdated(experimentName, parameterName, defaultValue, wrapCallback, this.getExperimentValue.bind(this), options); | ||
} | ||
const initializeValuesPromise = getInitializeValues(); | ||
const updateUserPromise = this.updateStatsigClientUser(initializeValuesPromise, identifiers, customAttributes); | ||
// We replace the init promise here since we are essentially re-initializing the client at this | ||
// point. Any subsequent calls to await client.initialize() or client.updateUser() | ||
// will now also await this user update. | ||
this.initPromise = updateUserPromise.catch(async () => { | ||
// If the update failed then it changed nothing, so revert back to the original promise. | ||
this.initPromise = originalInitPromise; | ||
/** | ||
* Subscribe so on any update the callback will be called. | ||
* NOTE: The callback will be called whenever the values are updated even if the values have not | ||
* changed. | ||
* @param callback | ||
* @returns off function to unsubscribe from updates | ||
*/ | ||
}, { | ||
key: "onAnyUpdated", | ||
value: function onAnyUpdated(callback) { | ||
return this.subscriptions.onAnyUpdated(callback); | ||
} | ||
// Set the user profile again to revert back to the current user | ||
if (this.provider) { | ||
await this.provider.setProfile(this.initOptions, this.currentIdentifiers, this.currentAttributes); | ||
/** | ||
* This method initializes the client using a network call to fetch the bootstrap values for the | ||
* given user. | ||
* | ||
* @param clientOptions | ||
* @param identifiers | ||
* @param customAttributes | ||
* @private | ||
*/ | ||
}, { | ||
key: "init", | ||
value: (function () { | ||
var _init = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee7(clientOptions, identifiers, customAttributes) { | ||
var fromValuesClientOptions, experimentValues, customAttributesFromResult, clientSdkKeyPromise, experimentValuesPromise, _yield$Promise$all, _yield$Promise$all2, experimentValuesResult; | ||
return _regeneratorRuntime.wrap(function _callee7$(_context7) { | ||
while (1) switch (_context7.prev = _context7.next) { | ||
case 0: | ||
fromValuesClientOptions = _objectSpread({}, clientOptions); | ||
_context7.prev = 1; | ||
// If client sdk key fetch fails, an error would be thrown and handled instead of waiting for | ||
// the experiment values request to be settled, and it will fall back to use default values. | ||
clientSdkKeyPromise = Fetcher.fetchClientSdk(clientOptions).then(function (value) { | ||
return fromValuesClientOptions.sdkKey = value.clientSdkKey; | ||
}); | ||
experimentValuesPromise = Fetcher.fetchExperimentValues(clientOptions, identifiers, customAttributes); // Only wait for the experiment values request to finish and try to initialise the client | ||
// with experiment values if both requests are successful. Else an error would be thrown and | ||
// handled by the catch | ||
_context7.next = 6; | ||
return Promise.all([clientSdkKeyPromise, experimentValuesPromise]); | ||
case 6: | ||
_yield$Promise$all = _context7.sent; | ||
_yield$Promise$all2 = _slicedToArray(_yield$Promise$all, 2); | ||
experimentValuesResult = _yield$Promise$all2[1]; | ||
experimentValues = experimentValuesResult.experimentValues; | ||
customAttributesFromResult = experimentValuesResult.customAttributes; | ||
_context7.next = 20; | ||
break; | ||
case 13: | ||
_context7.prev = 13; | ||
_context7.t0 = _context7["catch"](1); | ||
if (_context7.t0 instanceof Error) { | ||
// eslint-disable-next-line no-console | ||
console.error("Error occurred when trying to fetch the Feature Gates client values, error: ".concat(_context7.t0 === null || _context7.t0 === void 0 ? void 0 : _context7.t0.message)); | ||
} | ||
// eslint-disable-next-line no-console | ||
console.warn("Initialising Statsig client without values"); | ||
_context7.next = 19; | ||
return this.initFromValues(fromValuesClientOptions, identifiers, customAttributes); | ||
case 19: | ||
throw _context7.t0; | ||
case 20: | ||
return _context7.abrupt("return", this.initFromValues(fromValuesClientOptions, identifiers, customAttributesFromResult, experimentValues)); | ||
case 21: | ||
case "end": | ||
return _context7.stop(); | ||
} | ||
}, _callee7, this, [[1, 13]]); | ||
})); | ||
function init(_x18, _x19, _x20) { | ||
return _init.apply(this, arguments); | ||
} | ||
}); | ||
return updateUserPromise; | ||
} | ||
return init; | ||
}()) | ||
}, { | ||
key: "initWithProvider", | ||
value: function () { | ||
var _initWithProvider = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee8(baseClientOptions, provider, identifiers, customAttributes) { | ||
var fromValuesClientOptions, experimentValues, customAttributesFromResult, clientSdkKeyPromise, experimentValuesPromise, _yield$Promise$all3, _yield$Promise$all4, experimentValuesResult; | ||
return _regeneratorRuntime.wrap(function _callee8$(_context8) { | ||
while (1) switch (_context8.prev = _context8.next) { | ||
case 0: | ||
fromValuesClientOptions = _objectSpread(_objectSpread({}, baseClientOptions), {}, { | ||
disableCurrentPageLogging: true | ||
}); | ||
_context8.prev = 1; | ||
_context8.next = 4; | ||
return provider.setProfile(baseClientOptions, identifiers, customAttributes); | ||
case 4: | ||
// If client sdk key fetch fails, an error would be thrown and handled instead of waiting for | ||
// the experiment values request to be settled, and it will fall back to use default values. | ||
clientSdkKeyPromise = provider.getClientSdkKey().then(function (value) { | ||
return fromValuesClientOptions.sdkKey = value; | ||
}); | ||
experimentValuesPromise = provider.getExperimentValues(); // Only wait for the experiment values request to finish and try to initialise the client | ||
// with experiment values if both requests are successful. Else an error would be thrown and | ||
// handled by the catch | ||
_context8.next = 8; | ||
return Promise.all([clientSdkKeyPromise, experimentValuesPromise]); | ||
case 8: | ||
_yield$Promise$all3 = _context8.sent; | ||
_yield$Promise$all4 = _slicedToArray(_yield$Promise$all3, 2); | ||
experimentValuesResult = _yield$Promise$all4[1]; | ||
experimentValues = experimentValuesResult.experimentValues; | ||
customAttributesFromResult = experimentValuesResult.customAttributesFromFetch; | ||
_context8.next = 22; | ||
break; | ||
case 15: | ||
_context8.prev = 15; | ||
_context8.t0 = _context8["catch"](1); | ||
if (_context8.t0 instanceof Error) { | ||
// eslint-disable-next-line no-console | ||
console.error("Error occurred when trying to fetch the Feature Gates client values, error: ".concat(_context8.t0 === null || _context8.t0 === void 0 ? void 0 : _context8.t0.message)); | ||
} | ||
// eslint-disable-next-line no-console | ||
console.warn("Initialising Statsig client without values"); | ||
_context8.next = 21; | ||
return this.initFromValues(fromValuesClientOptions, identifiers, customAttributes); | ||
case 21: | ||
throw _context8.t0; | ||
case 22: | ||
return _context8.abrupt("return", this.initFromValues(fromValuesClientOptions, identifiers, customAttributesFromResult, experimentValues)); | ||
case 23: | ||
case "end": | ||
return _context8.stop(); | ||
} | ||
}, _callee8, this, [[1, 15]]); | ||
})); | ||
function initWithProvider(_x21, _x22, _x23, _x24) { | ||
return _initWithProvider.apply(this, arguments); | ||
} | ||
return initWithProvider; | ||
}() | ||
/** | ||
* This method initializes the client using a set of boostrap values obtained from one of the | ||
* server-side SDKs. | ||
* | ||
* @param clientOptions | ||
* @param identifiers | ||
* @param customAttributes | ||
* @param initializeValues | ||
* @private | ||
*/ | ||
}, { | ||
key: "initFromValues", | ||
value: (function () { | ||
var _initFromValues = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee9(clientOptions, identifiers, customAttributes) { | ||
var _newClientOptions$net; | ||
var initializeValues, | ||
newClientOptions, | ||
sdkKey, | ||
environment, | ||
_updateUserCompletionCallback, | ||
_perimeter, | ||
restClientOptions, | ||
statsigOptions, | ||
_args9 = arguments; | ||
return _regeneratorRuntime.wrap(function _callee9$(_context9) { | ||
while (1) switch (_context9.prev = _context9.next) { | ||
case 0: | ||
initializeValues = _args9.length > 3 && _args9[3] !== undefined ? _args9[3] : {}; | ||
this.overrideAdapter.initFromStoredOverrides(); | ||
this.currentIdentifiers = identifiers; | ||
this.currentAttributes = customAttributes; | ||
newClientOptions = migrateInitializationOptions(clientOptions); | ||
if (!newClientOptions.sdkKey) { | ||
newClientOptions.sdkKey = DEFAULT_CLIENT_KEY; | ||
} | ||
if (!((_newClientOptions$net = newClientOptions.networkConfig) !== null && _newClientOptions$net !== void 0 && _newClientOptions$net.logEventUrl)) { | ||
newClientOptions.networkConfig = _objectSpread(_objectSpread({}, newClientOptions.networkConfig), {}, { | ||
logEventUrl: DEFAULT_EVENT_LOGGING_API | ||
}); | ||
} | ||
if (newClientOptions.perimeter === PerimeterType.FEDRAMP_MODERATE) { | ||
// disable all logging in FedRAMP to prevent egress of sensitive data | ||
newClientOptions.disableLogging = true; | ||
} | ||
sdkKey = newClientOptions.sdkKey, environment = newClientOptions.environment, _updateUserCompletionCallback = newClientOptions.updateUserCompletionCallback, _perimeter = newClientOptions.perimeter, restClientOptions = _objectWithoutProperties(newClientOptions, _excluded); | ||
this.sdkKey = sdkKey; | ||
this.user = toStatsigUser(identifiers, customAttributes, this.sdkKey); | ||
statsigOptions = _objectSpread(_objectSpread({}, restClientOptions), {}, { | ||
environment: { | ||
tier: environment | ||
}, | ||
includeCurrentPageUrlWithEvents: false, | ||
dataAdapter: this.dataAdapter, | ||
overrideAdapter: this.overrideAdapter | ||
}); | ||
_context9.prev = 12; | ||
this.statsigClient = new StatsigClient(sdkKey, this.user, statsigOptions); | ||
this.dataAdapter.setBootstrapData(initializeValues); | ||
_context9.next = 17; | ||
return this.statsigClient.initializeAsync(); | ||
case 17: | ||
_context9.next = 29; | ||
break; | ||
case 19: | ||
_context9.prev = 19; | ||
_context9.t0 = _context9["catch"](12); | ||
if (_context9.t0 instanceof Error) { | ||
// eslint-disable-next-line no-console | ||
console.error("Error occurred when trying to initialise the Statsig client, error: ".concat(_context9.t0 === null || _context9.t0 === void 0 ? void 0 : _context9.t0.message)); | ||
} | ||
// eslint-disable-next-line no-console | ||
console.warn("Initialising Statsig client with default sdk key and without values"); | ||
this.statsigClient = new StatsigClient(DEFAULT_CLIENT_KEY, this.user, statsigOptions); | ||
this.dataAdapter.setBootstrapData(); | ||
_context9.next = 27; | ||
return this.statsigClient.initializeAsync(); | ||
case 27: | ||
this.initWithDefaults = true; | ||
throw _context9.t0; | ||
case 29: | ||
case "end": | ||
return _context9.stop(); | ||
} | ||
}, _callee9, this, [[12, 19]]); | ||
})); | ||
function initFromValues(_x25, _x26, _x27) { | ||
return _initFromValues.apply(this, arguments); | ||
} | ||
return initFromValues; | ||
}() | ||
/** | ||
* This method updates the user for this client with the bootstrap values returned from a given | ||
* Promise. | ||
* It uses the customAttributes from fetching experiment values to update the Statsig user but | ||
* uses the customAttributes from given input to check if the user has changed. | ||
* | ||
* @param {Identifiers} identifiers | ||
* @param {CustomAttributes} customAttributes | ||
* @param {Promise<InitializeValues>} getInitializeValues | ||
* @private | ||
*/ | ||
) | ||
}, { | ||
key: "updateUserUsingInitializeValuesProducer", | ||
value: (function () { | ||
var _updateUserUsingInitializeValuesProducer = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee11(getInitializeValues, identifiers, customAttributes) { | ||
var _this9 = this; | ||
var originalInitPromise, initializeValuesPromise, updateUserPromise; | ||
return _regeneratorRuntime.wrap(function _callee11$(_context11) { | ||
while (1) switch (_context11.prev = _context11.next) { | ||
case 0: | ||
this.assertInitialized(this.statsigClient); | ||
if (this.initPromise) { | ||
_context11.next = 3; | ||
break; | ||
} | ||
throw new Error('The client must be initialized before you can update the user.'); | ||
case 3: | ||
if (!this.isCurrentUser(identifiers, customAttributes)) { | ||
_context11.next = 5; | ||
break; | ||
} | ||
return _context11.abrupt("return", this.initPromise); | ||
case 5: | ||
// Wait for the current initialize/update to finish | ||
originalInitPromise = this.initPromise; | ||
_context11.prev = 6; | ||
_context11.next = 9; | ||
return this.initPromise; | ||
case 9: | ||
_context11.next = 13; | ||
break; | ||
case 11: | ||
_context11.prev = 11; | ||
_context11.t0 = _context11["catch"](6); | ||
case 13: | ||
initializeValuesPromise = getInitializeValues(); | ||
updateUserPromise = this.updateStatsigClientUser(initializeValuesPromise, identifiers, customAttributes); // We replace the init promise here since we are essentially re-initializing the client at this | ||
// point. Any subsequent calls to await client.initialize() or client.updateUser() | ||
// will now also await this user update. | ||
this.initPromise = updateUserPromise.catch( /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee10() { | ||
return _regeneratorRuntime.wrap(function _callee10$(_context10) { | ||
while (1) switch (_context10.prev = _context10.next) { | ||
case 0: | ||
// If the update failed then it changed nothing, so revert back to the original promise. | ||
_this9.initPromise = originalInitPromise; | ||
/** | ||
* This method updates the user on the nested Statsig client | ||
* | ||
* @param identifiers | ||
* @param customAttributes | ||
* @param initializeValuesPromise | ||
* @private | ||
*/ | ||
async updateStatsigClientUser(initializeValuesPromise, identifiers, customAttributes) { | ||
var _this$initOptions, _this$initOptions$upd; | ||
this.assertInitialized(this.statsigClient); | ||
let initializeValues, user; | ||
try { | ||
initializeValues = await initializeValuesPromise; | ||
user = toStatsigUser(identifiers, initializeValues.customAttributesFromFetch, this.sdkKey); | ||
} catch (err) { | ||
var _updateUserCompletion, _ref; | ||
// Make sure the updateUserCompletionCallback is called for any errors in our custom code. | ||
// This is not necessary for the updateUserWithValues call, because the Statsig client will | ||
// already invoke the callback itself. | ||
const errMsg = err instanceof Error ? err.message : JSON.stringify(err); | ||
(_updateUserCompletion = (_ref = this.initOptions).updateUserCompletionCallback) === null || _updateUserCompletion === void 0 || _updateUserCompletion.call(_ref, false, errMsg); | ||
throw err; | ||
// Set the user profile again to revert back to the current user | ||
if (!_this9.provider) { | ||
_context10.next = 4; | ||
break; | ||
} | ||
_context10.next = 4; | ||
return _this9.provider.setProfile(_this9.initOptions, _this9.currentIdentifiers, _this9.currentAttributes); | ||
case 4: | ||
case "end": | ||
return _context10.stop(); | ||
} | ||
}, _callee10); | ||
}))); | ||
return _context11.abrupt("return", updateUserPromise); | ||
case 17: | ||
case "end": | ||
return _context11.stop(); | ||
} | ||
}, _callee11, this, [[6, 11]]); | ||
})); | ||
function updateUserUsingInitializeValuesProducer(_x28, _x29, _x30) { | ||
return _updateUserUsingInitializeValuesProducer.apply(this, arguments); | ||
} | ||
return updateUserUsingInitializeValuesProducer; | ||
}() | ||
/** | ||
* This method updates the user on the nested Statsig client | ||
* | ||
* @param identifiers | ||
* @param customAttributes | ||
* @param initializeValuesPromise | ||
* @private | ||
*/ | ||
) | ||
}, { | ||
key: "updateStatsigClientUser", | ||
value: (function () { | ||
var _updateStatsigClientUser = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee12(initializeValuesPromise, identifiers, customAttributes) { | ||
var _this$initOptions, _this$initOptions$upd; | ||
var initializeValues, user, _updateUserCompletion, _ref4, errMsg, success, errorMessage; | ||
return _regeneratorRuntime.wrap(function _callee12$(_context12) { | ||
while (1) switch (_context12.prev = _context12.next) { | ||
case 0: | ||
this.assertInitialized(this.statsigClient); | ||
_context12.prev = 1; | ||
_context12.next = 4; | ||
return initializeValuesPromise; | ||
case 4: | ||
initializeValues = _context12.sent; | ||
user = toStatsigUser(identifiers, initializeValues.customAttributesFromFetch, this.sdkKey); | ||
_context12.next = 13; | ||
break; | ||
case 8: | ||
_context12.prev = 8; | ||
_context12.t0 = _context12["catch"](1); | ||
// Make sure the updateUserCompletionCallback is called for any errors in our custom code. | ||
// This is not necessary for the updateUserWithValues call, because the Statsig client will | ||
// already invoke the callback itself. | ||
errMsg = _context12.t0 instanceof Error ? _context12.t0.message : JSON.stringify(_context12.t0); | ||
(_updateUserCompletion = (_ref4 = this.initOptions).updateUserCompletionCallback) === null || _updateUserCompletion === void 0 || _updateUserCompletion.call(_ref4, false, errMsg); | ||
throw _context12.t0; | ||
case 13: | ||
success = true; | ||
errorMessage = null; | ||
_context12.prev = 15; | ||
this.dataAdapter.setBootstrapData(initializeValues.experimentValues); | ||
this.user = user; | ||
_context12.next = 20; | ||
return this.statsigClient.updateUserAsync(this.user); | ||
case 20: | ||
_context12.next = 26; | ||
break; | ||
case 22: | ||
_context12.prev = 22; | ||
_context12.t1 = _context12["catch"](15); | ||
success = false; | ||
errorMessage = String(_context12.t1); | ||
case 26: | ||
(_this$initOptions = this.initOptions) === null || _this$initOptions === void 0 || (_this$initOptions$upd = _this$initOptions.updateUserCompletionCallback) === null || _this$initOptions$upd === void 0 || _this$initOptions$upd.call(_this$initOptions, success, errorMessage); | ||
if (!success) { | ||
_context12.next = 33; | ||
break; | ||
} | ||
this.currentIdentifiers = identifiers; | ||
this.currentAttributes = customAttributes; | ||
this.subscriptions.anyUpdated(); | ||
_context12.next = 34; | ||
break; | ||
case 33: | ||
throw new Error('Failed to update user. An unexpected error occured.'); | ||
case 34: | ||
case "end": | ||
return _context12.stop(); | ||
} | ||
}, _callee12, this, [[1, 8], [15, 22]]); | ||
})); | ||
function updateStatsigClientUser(_x31, _x32, _x33) { | ||
return _updateStatsigClientUser.apply(this, arguments); | ||
} | ||
return updateStatsigClientUser; | ||
}()) | ||
}, { | ||
key: "getPackageVersion", | ||
value: | ||
/** | ||
* @returns string version of the current package in semver style. | ||
*/ | ||
function getPackageVersion() { | ||
return CLIENT_VERSION; | ||
} | ||
let success = true; | ||
let errorMessage = null; | ||
try { | ||
this.dataAdapter.setBootstrapData(initializeValues.experimentValues); | ||
this.user = user; | ||
await this.statsigClient.updateUserAsync(this.user); | ||
} catch (err) { | ||
success = false; | ||
errorMessage = String(err); | ||
} | ||
(_this$initOptions = this.initOptions) === null || _this$initOptions === void 0 || (_this$initOptions$upd = _this$initOptions.updateUserCompletionCallback) === null || _this$initOptions$upd === void 0 || _this$initOptions$upd.call(_this$initOptions, success, errorMessage); | ||
if (success) { | ||
this.currentIdentifiers = identifiers; | ||
this.currentAttributes = customAttributes; | ||
this.subscriptions.anyUpdated(); | ||
} else { | ||
throw new Error('Failed to update user. An unexpected error occured.'); | ||
} | ||
} | ||
/** | ||
* @returns string version of the current package in semver style. | ||
*/ | ||
getPackageVersion() { | ||
return CLIENT_VERSION; | ||
} | ||
/** | ||
* Returns a specified layer otherwise returns an empty layer as a default value if the layer doesn't exist. | ||
* | ||
* @param {string} layerName - The name of the layer | ||
* @param {Object} options | ||
* @param {boolean} options.fireLayerExposure - Whether or not to fire the exposure event for the | ||
* layer. Defaults to true. To log an exposure event manually at a later time, use | ||
* {@link Client.manuallyLogLayerExposure} (see [Statsig docs about manually logging exposures](https://docs.statsig.com/client/jsClientSDK#manual-exposures-)). | ||
* @returns A layer | ||
* @example | ||
* ```ts | ||
* const layer = client.getLayer('example-layer-name'); | ||
* const exampletitle: string = layer.get("title", "Welcome to Statsig!"); | ||
* ``` | ||
*/ | ||
getLayer( /** The name of the layer */ | ||
layerName, options = {}) { | ||
try { | ||
this.assertInitialized(this.statsigClient); | ||
const { | ||
fireLayerExposure = true | ||
} = options; | ||
return Layer.fromLayer(this.statsigClient.getLayer(layerName, { | ||
disableExposureLog: !fireLayerExposure | ||
})); | ||
} catch (error) { | ||
// Log the first occurrence of the error | ||
if (!this.hasGetLayerErrorOccurred) { | ||
// eslint-disable-next-line no-console | ||
console.warn({ | ||
msg: 'An error has occurred getting the layer. Only the first occurrence of this error is logged.', | ||
layerName, | ||
error | ||
}); | ||
this.hasGetLayerErrorOccurred = true; | ||
/** | ||
* Returns a specified layer otherwise returns an empty layer as a default value if the layer doesn't exist. | ||
* | ||
* @param {string} layerName - The name of the layer | ||
* @param {Object} options | ||
* @param {boolean} options.fireLayerExposure - Whether or not to fire the exposure event for the | ||
* layer. Defaults to true. To log an exposure event manually at a later time, use | ||
* {@link Client.manuallyLogLayerExposure} (see [Statsig docs about manually logging exposures](https://docs.statsig.com/client/jsClientSDK#manual-exposures-)). | ||
* @returns A layer | ||
* @example | ||
* ```ts | ||
* const layer = client.getLayer('example-layer-name'); | ||
* const exampletitle: string = layer.get("title", "Welcome to Statsig!"); | ||
* ``` | ||
*/ | ||
}, { | ||
key: "getLayer", | ||
value: function getLayer( /** The name of the layer */ | ||
layerName) { | ||
var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; | ||
try { | ||
this.assertInitialized(this.statsigClient); | ||
var _options$fireLayerExp = options.fireLayerExposure, | ||
fireLayerExposure = _options$fireLayerExp === void 0 ? true : _options$fireLayerExp; | ||
return Layer.fromLayer(this.statsigClient.getLayer(layerName, { | ||
disableExposureLog: !fireLayerExposure | ||
})); | ||
} catch (error) { | ||
// Log the first occurrence of the error | ||
if (!this.hasGetLayerErrorOccurred) { | ||
// eslint-disable-next-line no-console | ||
console.warn({ | ||
msg: 'An error has occurred getting the layer. Only the first occurrence of this error is logged.', | ||
layerName: layerName, | ||
error: error | ||
}); | ||
this.hasGetLayerErrorOccurred = true; | ||
} | ||
// Return a default value | ||
return Layer.fromLayer(_makeLayer(layerName, { | ||
reason: 'Error' | ||
}, null)); | ||
} | ||
// Return a default value | ||
return Layer.fromLayer(_makeLayer(layerName, { | ||
reason: 'Error' | ||
}, null)); | ||
} | ||
} | ||
/** | ||
* Returns the value of a given parameter in a layer config. | ||
* | ||
* @template T | ||
* @param {string} layerName - The name of the layer | ||
* @param {string} parameterName - The name of the parameter to fetch from the layer config | ||
* @param {T} defaultValue - The value to serve if the layer or parameter do not exist, or if the | ||
* returned value does not match the expected type. | ||
* @param {Object} options | ||
* @param {boolean} options.fireLayerExposure - Whether or not to fire the exposure event for the | ||
* layer. Defaults to true. To log an exposure event manually at a later time, use | ||
* {@link Client.manuallyLogLayerExposure} (see [Statsig docs about manually logging exposures](https://docs.statsig.com/client/jsClientSDK#manual-exposures-)) | ||
* @param {function} options.typeGuard - A function that asserts that the return value has the expected type. If this function returns false, then the default value will be returned instead. This can be set to protect your code from unexpected values being set remotely. By default, this will be done by asserting that the default value and value are the same primitive type. | ||
* @returns The value of the parameter if the layer and parameter both exist, otherwise the default value. | ||
* @example | ||
* ``` ts | ||
* type ValidColor = 'blue' | 'red' | 'yellow'; | ||
* type ValidColorTypeCheck = (value: unknown) => value is ValidColor; | ||
* | ||
* const isValidColor: ValidColorTypeCheck = | ||
* (value: unknown) => typeof value === 'string' && ['blue', 'red', 'yellow'].includes(value); | ||
* | ||
* const buttonColor: ValidColor = client.getLayerValue( | ||
* 'example-layer-name', | ||
* 'backgroundColor', | ||
* 'yellow', | ||
* { | ||
* typeGuard: isValidColor | ||
* } | ||
* ); | ||
* ``` | ||
*/ | ||
getLayerValue(layerName, parameterName, defaultValue, options = {}) { | ||
const layer = this.getLayer(layerName, options); | ||
try { | ||
const { | ||
typeGuard | ||
} = options; | ||
return layer.get(parameterName, defaultValue, typeGuard); | ||
} catch (error) { | ||
// Log the first occurrence of the error | ||
if (!this.hasGetLayerValueErrorOccurred) { | ||
// eslint-disable-next-line no-console | ||
console.warn({ | ||
msg: 'An error has occurred getting the layer value. Only the first occurrence of this error is logged.', | ||
layerName, | ||
defaultValue, | ||
options, | ||
error | ||
}); | ||
this.hasGetLayerValueErrorOccurred = true; | ||
/** | ||
* Returns the value of a given parameter in a layer config. | ||
* | ||
* @template T | ||
* @param {string} layerName - The name of the layer | ||
* @param {string} parameterName - The name of the parameter to fetch from the layer config | ||
* @param {T} defaultValue - The value to serve if the layer or parameter do not exist, or if the | ||
* returned value does not match the expected type. | ||
* @param {Object} options | ||
* @param {boolean} options.fireLayerExposure - Whether or not to fire the exposure event for the | ||
* layer. Defaults to true. To log an exposure event manually at a later time, use | ||
* {@link Client.manuallyLogLayerExposure} (see [Statsig docs about manually logging exposures](https://docs.statsig.com/client/jsClientSDK#manual-exposures-)) | ||
* @param {function} options.typeGuard - A function that asserts that the return value has the expected type. If this function returns false, then the default value will be returned instead. This can be set to protect your code from unexpected values being set remotely. By default, this will be done by asserting that the default value and value are the same primitive type. | ||
* @returns The value of the parameter if the layer and parameter both exist, otherwise the default value. | ||
* @example | ||
* ``` ts | ||
* type ValidColor = 'blue' | 'red' | 'yellow'; | ||
* type ValidColorTypeCheck = (value: unknown) => value is ValidColor; | ||
* | ||
* const isValidColor: ValidColorTypeCheck = | ||
* (value: unknown) => typeof value === 'string' && ['blue', 'red', 'yellow'].includes(value); | ||
* | ||
* const buttonColor: ValidColor = client.getLayerValue( | ||
* 'example-layer-name', | ||
* 'backgroundColor', | ||
* 'yellow', | ||
* { | ||
* typeGuard: isValidColor | ||
* } | ||
* ); | ||
* ``` | ||
*/ | ||
}, { | ||
key: "getLayerValue", | ||
value: function getLayerValue(layerName, parameterName, defaultValue) { | ||
var options = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : {}; | ||
var layer = this.getLayer(layerName, options); | ||
try { | ||
var typeGuard = options.typeGuard; | ||
return layer.get(parameterName, defaultValue, typeGuard); | ||
} catch (error) { | ||
// Log the first occurrence of the error | ||
if (!this.hasGetLayerValueErrorOccurred) { | ||
// eslint-disable-next-line no-console | ||
console.warn({ | ||
msg: 'An error has occurred getting the layer value. Only the first occurrence of this error is logged.', | ||
layerName: layerName, | ||
defaultValue: defaultValue, | ||
options: options, | ||
error: error | ||
}); | ||
this.hasGetLayerValueErrorOccurred = true; | ||
} | ||
return defaultValue; | ||
} | ||
return defaultValue; | ||
} | ||
} | ||
} | ||
}]); | ||
}(); |
@@ -0,11 +1,12 @@ | ||
import _typeof from "@babel/runtime/helpers/typeof"; | ||
import _classCallCheck from "@babel/runtime/helpers/classCallCheck"; | ||
import _createClass from "@babel/runtime/helpers/createClass"; | ||
import { migrateEvaluationDetails } from '../utils'; | ||
// Reference: https://github.com/statsig-io/js-lite/blob/main/src/DynamicConfig.ts | ||
export class DynamicConfig { | ||
static fromExperiment(experiment) { | ||
var _experiment$__evaluat, _experiment$groupName; | ||
const config = new DynamicConfig(experiment.name, experiment.value, experiment.ruleID, migrateEvaluationDetails(experiment.details), (_experiment$__evaluat = experiment.__evaluation) === null || _experiment$__evaluat === void 0 ? void 0 : _experiment$__evaluat.secondary_exposures, (_experiment$groupName = experiment.groupName) !== null && _experiment$groupName !== void 0 ? _experiment$groupName : undefined); | ||
config.experiment = experiment; | ||
return config; | ||
} | ||
constructor(configName, configValue, ruleID, evaluationDetails, secondaryExposures = [], allocatedExperimentName = '', onDefaultValueFallback = null) { | ||
export var DynamicConfig = /*#__PURE__*/function () { | ||
function DynamicConfig(configName, configValue, ruleID, evaluationDetails) { | ||
var secondaryExposures = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : []; | ||
var allocatedExperimentName = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : ''; | ||
var onDefaultValueFallback = arguments.length > 6 && arguments[6] !== undefined ? arguments[6] : null; | ||
_classCallCheck(this, DynamicConfig); | ||
this.value = configValue; | ||
@@ -19,45 +20,60 @@ this._name = configName; | ||
} | ||
get(key, defaultValue, typeGuard) { | ||
var _this$_onDefaultValue2; | ||
const val = this.getValue(key, defaultValue); | ||
if (val == null) { | ||
return defaultValue; | ||
} | ||
const expectedType = Array.isArray(defaultValue) ? 'array' : typeof defaultValue; | ||
const actualType = Array.isArray(val) ? 'array' : typeof val; | ||
if (typeGuard) { | ||
var _this$_onDefaultValue; | ||
if (typeGuard(val)) { | ||
return _createClass(DynamicConfig, [{ | ||
key: "get", | ||
value: function get(key, defaultValue, typeGuard) { | ||
var _this$_onDefaultValue2; | ||
var val = this.getValue(key, defaultValue); | ||
if (val == null) { | ||
return defaultValue; | ||
} | ||
var expectedType = Array.isArray(defaultValue) ? 'array' : _typeof(defaultValue); | ||
var actualType = Array.isArray(val) ? 'array' : _typeof(val); | ||
if (typeGuard) { | ||
var _this$_onDefaultValue; | ||
if (typeGuard(val)) { | ||
this.fireExposure(key); | ||
return val; | ||
} | ||
(_this$_onDefaultValue = this._onDefaultValueFallback) === null || _this$_onDefaultValue === void 0 || _this$_onDefaultValue.call(this, this, key, expectedType, actualType); | ||
return defaultValue; | ||
} | ||
if (defaultValue == null || expectedType === actualType) { | ||
this.fireExposure(key); | ||
return val; | ||
} | ||
(_this$_onDefaultValue = this._onDefaultValueFallback) === null || _this$_onDefaultValue === void 0 || _this$_onDefaultValue.call(this, this, key, expectedType, actualType); | ||
(_this$_onDefaultValue2 = this._onDefaultValueFallback) === null || _this$_onDefaultValue2 === void 0 || _this$_onDefaultValue2.call(this, this, key, expectedType, actualType); | ||
return defaultValue; | ||
} | ||
if (defaultValue == null || expectedType === actualType) { | ||
}, { | ||
key: "getValue", | ||
value: function getValue(key, defaultValue) { | ||
if (key == null) { | ||
return this.value; | ||
} | ||
if (defaultValue == null) { | ||
defaultValue = null; | ||
} | ||
if (this.value[key] == null) { | ||
return defaultValue; | ||
} | ||
this.fireExposure(key); | ||
return val; | ||
return this.value[key]; | ||
} | ||
(_this$_onDefaultValue2 = this._onDefaultValueFallback) === null || _this$_onDefaultValue2 === void 0 || _this$_onDefaultValue2.call(this, this, key, expectedType, actualType); | ||
return defaultValue; | ||
} | ||
getValue(key, defaultValue) { | ||
if (key == null) { | ||
return this.value; | ||
}, { | ||
key: "fireExposure", | ||
value: function fireExposure(key) { | ||
// Call the wrapped experiment's get method to fire exposure | ||
if (this.experiment) { | ||
this.experiment.get(key); | ||
} | ||
} | ||
if (defaultValue == null) { | ||
defaultValue = null; | ||
}], [{ | ||
key: "fromExperiment", | ||
value: function fromExperiment(experiment) { | ||
var _experiment$__evaluat, _experiment$groupName; | ||
var config = new DynamicConfig(experiment.name, experiment.value, experiment.ruleID, migrateEvaluationDetails(experiment.details), (_experiment$__evaluat = experiment.__evaluation) === null || _experiment$__evaluat === void 0 ? void 0 : _experiment$__evaluat.secondary_exposures, (_experiment$groupName = experiment.groupName) !== null && _experiment$groupName !== void 0 ? _experiment$groupName : undefined); | ||
config.experiment = experiment; | ||
return config; | ||
} | ||
if (this.value[key] == null) { | ||
return defaultValue; | ||
} | ||
this.fireExposure(key); | ||
return this.value[key]; | ||
} | ||
fireExposure(key) { | ||
// Call the wrapped experiment's get method to fire exposure | ||
if (this.experiment) { | ||
this.experiment.get(key); | ||
} | ||
} | ||
} | ||
}]); | ||
}(); |
@@ -0,10 +1,14 @@ | ||
import _typeof from "@babel/runtime/helpers/typeof"; | ||
import _classCallCheck from "@babel/runtime/helpers/classCallCheck"; | ||
import _createClass from "@babel/runtime/helpers/createClass"; | ||
import { migrateEvaluationDetails } from '../utils'; | ||
// Reference: https://github.com/statsig-io/js-lite/blob/main/src/Layer.ts | ||
export class Layer { | ||
static fromLayer(layer) { | ||
var _layer$__evaluation, _layer$__evaluation2, _layer$__evaluation3, _layer$__evaluation4; | ||
const value = new Layer(layer.name, layer.__value, layer.ruleID, migrateEvaluationDetails(layer.details), (_layer, parameterName) => layer.get(parameterName), (_layer$__evaluation = layer.__evaluation) === null || _layer$__evaluation === void 0 ? void 0 : _layer$__evaluation.secondary_exposures, (_layer$__evaluation2 = layer.__evaluation) === null || _layer$__evaluation2 === void 0 ? void 0 : _layer$__evaluation2.undelegated_secondary_exposures, (_layer$__evaluation3 = layer.__evaluation) === null || _layer$__evaluation3 === void 0 ? void 0 : _layer$__evaluation3.allocated_experiment_name, (_layer$__evaluation4 = layer.__evaluation) === null || _layer$__evaluation4 === void 0 ? void 0 : _layer$__evaluation4.explicit_parameters); | ||
return value; | ||
} | ||
constructor(name, layerValue, ruleID, evaluationDetails, logParameterFunction = null, secondaryExposures = [], undelegatedSecondaryExposures = [], allocatedExperimentName = '', explicitParameters = []) { | ||
export var Layer = /*#__PURE__*/function () { | ||
function Layer(name, layerValue, ruleID, evaluationDetails) { | ||
var logParameterFunction = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : null; | ||
var secondaryExposures = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : []; | ||
var undelegatedSecondaryExposures = arguments.length > 6 && arguments[6] !== undefined ? arguments[6] : []; | ||
var allocatedExperimentName = arguments.length > 7 && arguments[7] !== undefined ? arguments[7] : ''; | ||
var explicitParameters = arguments.length > 8 && arguments[8] !== undefined ? arguments[8] : []; | ||
_classCallCheck(this, Layer); | ||
this._logParameterFunction = logParameterFunction; | ||
@@ -20,37 +24,54 @@ this._name = name; | ||
} | ||
get(key, defaultValue, typeGuard) { | ||
const val = this._value[key]; | ||
if (val == null) { | ||
return _createClass(Layer, [{ | ||
key: "get", | ||
value: function get(key, defaultValue, typeGuard) { | ||
var _this = this; | ||
var val = this._value[key]; | ||
if (val == null) { | ||
return defaultValue; | ||
} | ||
var logAndReturn = function logAndReturn() { | ||
_this._logLayerParameterExposure(key); | ||
return val; | ||
}; | ||
if (typeGuard) { | ||
return typeGuard(val) ? logAndReturn() : defaultValue; | ||
} | ||
if (defaultValue == null) { | ||
return logAndReturn(); | ||
} | ||
if (_typeof(val) === _typeof(defaultValue) && Array.isArray(defaultValue) === Array.isArray(val)) { | ||
return logAndReturn(); | ||
} | ||
return defaultValue; | ||
} | ||
const logAndReturn = () => { | ||
this._logLayerParameterExposure(key); | ||
return val; | ||
}; | ||
if (typeGuard) { | ||
return typeGuard(val) ? logAndReturn() : defaultValue; | ||
}, { | ||
key: "getValue", | ||
value: function getValue(key, defaultValue) { | ||
// eslint-disable-next-line eqeqeq | ||
if (defaultValue == undefined) { | ||
defaultValue = null; | ||
} | ||
var val = this._value[key]; | ||
if (val != null) { | ||
this._logLayerParameterExposure(key); | ||
} | ||
return val !== null && val !== void 0 ? val : defaultValue; | ||
} | ||
if (defaultValue == null) { | ||
return logAndReturn(); | ||
}, { | ||
key: "_logLayerParameterExposure", | ||
value: function _logLayerParameterExposure(parameterName) { | ||
var _this$_logParameterFu; | ||
(_this$_logParameterFu = this._logParameterFunction) === null || _this$_logParameterFu === void 0 || _this$_logParameterFu.call(this, this, parameterName); | ||
} | ||
if (typeof val === typeof defaultValue && Array.isArray(defaultValue) === Array.isArray(val)) { | ||
return logAndReturn(); | ||
}], [{ | ||
key: "fromLayer", | ||
value: function fromLayer(layer) { | ||
var _layer$__evaluation, _layer$__evaluation2, _layer$__evaluation3, _layer$__evaluation4; | ||
var value = new Layer(layer.name, layer.__value, layer.ruleID, migrateEvaluationDetails(layer.details), function (_layer, parameterName) { | ||
return layer.get(parameterName); | ||
}, (_layer$__evaluation = layer.__evaluation) === null || _layer$__evaluation === void 0 ? void 0 : _layer$__evaluation.secondary_exposures, (_layer$__evaluation2 = layer.__evaluation) === null || _layer$__evaluation2 === void 0 ? void 0 : _layer$__evaluation2.undelegated_secondary_exposures, (_layer$__evaluation3 = layer.__evaluation) === null || _layer$__evaluation3 === void 0 ? void 0 : _layer$__evaluation3.allocated_experiment_name, (_layer$__evaluation4 = layer.__evaluation) === null || _layer$__evaluation4 === void 0 ? void 0 : _layer$__evaluation4.explicit_parameters); | ||
return value; | ||
} | ||
return defaultValue; | ||
} | ||
getValue(key, defaultValue) { | ||
// eslint-disable-next-line eqeqeq | ||
if (defaultValue == undefined) { | ||
defaultValue = null; | ||
} | ||
const val = this._value[key]; | ||
if (val != null) { | ||
this._logLayerParameterExposure(key); | ||
} | ||
return val !== null && val !== void 0 ? val : defaultValue; | ||
} | ||
_logLayerParameterExposure(parameterName) { | ||
var _this$_logParameterFu; | ||
(_this$_logParameterFu = this._logParameterFunction) === null || _this$_logParameterFu === void 0 || _this$_logParameterFu.call(this, this, parameterName); | ||
} | ||
} | ||
}]); | ||
}(); |
// Reference: https://github.com/statsig-io/js-lite/blob/main/src/StatsigStore.ts | ||
export let EvaluationReason = /*#__PURE__*/function (EvaluationReason) { | ||
export var EvaluationReason = /*#__PURE__*/function (EvaluationReason) { | ||
EvaluationReason["Error"] = "Error"; | ||
@@ -5,0 +5,0 @@ EvaluationReason["LocalOverride"] = "LocalOverride"; |
@@ -0,1 +1,3 @@ | ||
import _classCallCheck from "@babel/runtime/helpers/classCallCheck"; | ||
import _createClass from "@babel/runtime/helpers/createClass"; | ||
import _defineProperty from "@babel/runtime/helpers/defineProperty"; | ||
@@ -15,10 +17,18 @@ var _FeatureGates; | ||
*/ | ||
class FeatureGates { | ||
static isGateExists(gateName) { | ||
return this.client.isGateExist(gateName); | ||
var FeatureGates = /*#__PURE__*/function () { | ||
function FeatureGates() { | ||
_classCallCheck(this, FeatureGates); | ||
} | ||
static isExperimentExists(experimentName) { | ||
return this.client.isExperimentExist(experimentName); | ||
} | ||
} | ||
return _createClass(FeatureGates, null, [{ | ||
key: "isGateExists", | ||
value: function isGateExists(gateName) { | ||
return this.client.isGateExist(gateName); | ||
} | ||
}, { | ||
key: "isExperimentExists", | ||
value: function isExperimentExists(experimentName) { | ||
return this.client.isExperimentExist(experimentName); | ||
} | ||
}]); | ||
}(); | ||
_FeatureGates = FeatureGates; | ||
@@ -28,3 +38,3 @@ _defineProperty(FeatureGates, "client", new Client()); | ||
_defineProperty(FeatureGates, "hasGetExperimentValueErrorOccurred", false); | ||
_defineProperty(FeatureGates, "checkGate", (gateName, options) => { | ||
_defineProperty(FeatureGates, "checkGate", function (gateName, options) { | ||
try { | ||
@@ -34,3 +44,3 @@ // Check if the CRITERION override mechanism is available | ||
// Attempt to retrieve an override value for the feature gate | ||
const overrideValue = window.__CRITERION__.getFeatureFlagOverride(gateName); | ||
var overrideValue = window.__CRITERION__.getFeatureFlagOverride(gateName); | ||
// If an override value is found, return it immediately | ||
@@ -47,4 +57,4 @@ if (overrideValue !== undefined) { | ||
msg: 'An error has occurred checking the feature gate from criterion override. Only the first occurrence of this error is logged.', | ||
gateName, | ||
error | ||
gateName: gateName, | ||
error: error | ||
}); | ||
@@ -58,7 +68,7 @@ _FeatureGates.hasCheckGateErrorOccurred = true; | ||
}); | ||
_defineProperty(FeatureGates, "getExperimentValue", (experimentName, parameterName, defaultValue, options) => { | ||
_defineProperty(FeatureGates, "getExperimentValue", function (experimentName, parameterName, defaultValue, options) { | ||
try { | ||
// Check if the CRITERION override mechanism is available | ||
if (typeof window !== 'undefined' && window.__CRITERION__ && typeof window.__CRITERION__.getExperimentValueOverride === 'function') { | ||
const overrideValue = window.__CRITERION__.getExperimentValueOverride(experimentName, parameterName); | ||
var overrideValue = window.__CRITERION__.getExperimentValueOverride(experimentName, parameterName); | ||
if (overrideValue !== undefined && overrideValue !== null) { | ||
@@ -74,6 +84,6 @@ return overrideValue; | ||
msg: 'An error has occurred getting the experiment value from criterion override. Only the first occurrence of this error is logged.', | ||
experimentName, | ||
defaultValue, | ||
options, | ||
error | ||
experimentName: experimentName, | ||
defaultValue: defaultValue, | ||
options: options, | ||
error: error | ||
}); | ||
@@ -115,3 +125,3 @@ _FeatureGates.hasGetExperimentValueErrorOccurred = true; | ||
_defineProperty(FeatureGates, "getLayerValue", _FeatureGates.client.getLayerValue.bind(_FeatureGates.client)); | ||
let boundFGJS = FeatureGates; | ||
var boundFGJS = FeatureGates; | ||
@@ -127,6 +137,5 @@ // This makes it possible to get a reference to the FeatureGates client at runtime. | ||
boundFGJS = window.__FEATUREGATES_JS__; | ||
const boundVersion = ((_boundFGJS = boundFGJS) === null || _boundFGJS === void 0 || (_boundFGJS$getPackage = _boundFGJS.getPackageVersion) === null || _boundFGJS$getPackage === void 0 ? void 0 : _boundFGJS$getPackage.call(_boundFGJS)) || '4.10.0 or earlier'; | ||
var boundVersion = ((_boundFGJS = boundFGJS) === null || _boundFGJS === void 0 || (_boundFGJS$getPackage = _boundFGJS.getPackageVersion) === null || _boundFGJS$getPackage === void 0 ? void 0 : _boundFGJS$getPackage.call(_boundFGJS)) || '4.10.0 or earlier'; | ||
if (boundVersion !== CLIENT_VERSION) { | ||
const message = `Multiple versions of FeatureGateClients found on the current page. | ||
The currently bound version is ${boundVersion} when module version ${CLIENT_VERSION} was loading.`; | ||
var message = "Multiple versions of FeatureGateClients found on the current page.\n The currently bound version is ".concat(boundVersion, " when module version ").concat(CLIENT_VERSION, " was loading."); | ||
// eslint-disable-next-line no-console | ||
@@ -133,0 +142,0 @@ console.warn(message); |
@@ -1,5 +0,16 @@ | ||
export class ResponseError extends Error { | ||
constructor(message) { | ||
super(message); | ||
import _createClass from "@babel/runtime/helpers/createClass"; | ||
import _classCallCheck from "@babel/runtime/helpers/classCallCheck"; | ||
import _possibleConstructorReturn from "@babel/runtime/helpers/possibleConstructorReturn"; | ||
import _getPrototypeOf from "@babel/runtime/helpers/getPrototypeOf"; | ||
import _inherits from "@babel/runtime/helpers/inherits"; | ||
import _wrapNativeSuper from "@babel/runtime/helpers/wrapNativeSuper"; | ||
function _callSuper(t, o, e) { return o = _getPrototypeOf(o), _possibleConstructorReturn(t, _isNativeReflectConstruct() ? Reflect.construct(o, e || [], _getPrototypeOf(t).constructor) : o.apply(t, e)); } | ||
function _isNativeReflectConstruct() { try { var t = !Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {})); } catch (t) {} return (_isNativeReflectConstruct = function _isNativeReflectConstruct() { return !!t; })(); } | ||
export var ResponseError = /*#__PURE__*/function (_Error) { | ||
function ResponseError(message) { | ||
_classCallCheck(this, ResponseError); | ||
return _callSuper(this, ResponseError, [message]); | ||
} | ||
} | ||
_inherits(ResponseError, _Error); | ||
return _createClass(ResponseError); | ||
}( /*#__PURE__*/_wrapNativeSuper(Error)); |
import _defineProperty from "@babel/runtime/helpers/defineProperty"; | ||
import _asyncToGenerator from "@babel/runtime/helpers/asyncToGenerator"; | ||
import _classCallCheck from "@babel/runtime/helpers/classCallCheck"; | ||
import _createClass from "@babel/runtime/helpers/createClass"; | ||
function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; } | ||
function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; } | ||
import _regeneratorRuntime from "@babel/runtime/regenerator"; | ||
import { FeatureGateEnvironment, PerimeterType } from '../types'; | ||
import { CLIENT_VERSION } from '../version'; | ||
import { ResponseError } from './errors'; | ||
const DEFAULT_REQUEST_TIMEOUT_MS = 5000; | ||
export const PROD_BASE_URL = 'https://api.atlassian.com/flags'; | ||
export const STAGING_BASE_URL = 'https://api.stg.atlassian.com/flags'; | ||
export const DEV_BASE_URL = 'https://api.dev.atlassian.com/flags'; | ||
export const FEDM_STAGING_BASE_URL = 'https://api.stg.atlassian-us-gov-mod.com/flags'; | ||
export const FEDM_PROD_BASE_URL = 'https://api.atlassian-us-gov-mod.com/flags'; | ||
export const GATEWAY_BASE_URL = '/gateway/api/flags'; | ||
export default class Fetcher { | ||
static async fetchClientSdk(fetcherOptions) { | ||
const { | ||
targetApp | ||
} = fetcherOptions; | ||
const url = `/api/v2/frontend/clientSdkKey/${targetApp}`; | ||
try { | ||
return await this.fetchRequest(url, 'GET', fetcherOptions); | ||
} catch (error) { | ||
if (error instanceof Error) { | ||
throw error; | ||
} | ||
throw Error('Failed to retrieve client sdk key'); | ||
} | ||
var DEFAULT_REQUEST_TIMEOUT_MS = 5000; | ||
export var PROD_BASE_URL = 'https://api.atlassian.com/flags'; | ||
export var STAGING_BASE_URL = 'https://api.stg.atlassian.com/flags'; | ||
export var DEV_BASE_URL = 'https://api.dev.atlassian.com/flags'; | ||
export var FEDM_STAGING_BASE_URL = 'https://api.stg.atlassian-us-gov-mod.com/flags'; | ||
export var FEDM_PROD_BASE_URL = 'https://api.atlassian-us-gov-mod.com/flags'; | ||
export var GATEWAY_BASE_URL = '/gateway/api/flags'; | ||
var Fetcher = /*#__PURE__*/function () { | ||
function Fetcher() { | ||
_classCallCheck(this, Fetcher); | ||
} | ||
static async fetchExperimentValues(fetcherOptions, identifiers, customAttributes) { | ||
const requestBody = { | ||
identifiers, | ||
customAttributes, | ||
targetApp: fetcherOptions.targetApp | ||
}; | ||
try { | ||
return await this.fetchRequest('/api/v2/frontend/experimentValues', 'POST', fetcherOptions, requestBody); | ||
} catch (error) { | ||
if (error instanceof Error) { | ||
throw error; | ||
return _createClass(Fetcher, null, [{ | ||
key: "fetchClientSdk", | ||
value: function () { | ||
var _fetchClientSdk = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee(fetcherOptions) { | ||
var targetApp, url; | ||
return _regeneratorRuntime.wrap(function _callee$(_context) { | ||
while (1) switch (_context.prev = _context.next) { | ||
case 0: | ||
targetApp = fetcherOptions.targetApp; | ||
url = "/api/v2/frontend/clientSdkKey/".concat(targetApp); | ||
_context.prev = 2; | ||
_context.next = 5; | ||
return this.fetchRequest(url, 'GET', fetcherOptions); | ||
case 5: | ||
return _context.abrupt("return", _context.sent); | ||
case 8: | ||
_context.prev = 8; | ||
_context.t0 = _context["catch"](2); | ||
if (!(_context.t0 instanceof Error)) { | ||
_context.next = 12; | ||
break; | ||
} | ||
throw _context.t0; | ||
case 12: | ||
throw Error('Failed to retrieve client sdk key'); | ||
case 13: | ||
case "end": | ||
return _context.stop(); | ||
} | ||
}, _callee, this, [[2, 8]]); | ||
})); | ||
function fetchClientSdk(_x) { | ||
return _fetchClientSdk.apply(this, arguments); | ||
} | ||
throw Error('Failed to retrieve experiment values'); | ||
} | ||
} | ||
static async handleResponseError(response) { | ||
if (!response.ok) { | ||
// Use text() instead of json() as the error body might not be json data | ||
const body = await response.text(); | ||
throw new ResponseError(`Non 2xx response status received, status: ${response.status}, body: ${JSON.stringify(body)}`); | ||
} | ||
if (response.status === 204) { | ||
throw new ResponseError('Unexpected 204 response'); | ||
} | ||
} | ||
static async extractResponseBody(response) { | ||
const value = await response.text(); | ||
return JSON.parse(value); | ||
} | ||
static getBaseUrl(serviceEnv, useGatewayUrl = false, perimeter) { | ||
if (useGatewayUrl) { | ||
return GATEWAY_BASE_URL; | ||
} | ||
if (perimeter === PerimeterType.FEDRAMP_MODERATE) { | ||
switch (serviceEnv) { | ||
case FeatureGateEnvironment.Production: | ||
return FEDM_PROD_BASE_URL; | ||
case FeatureGateEnvironment.Staging: | ||
return FEDM_STAGING_BASE_URL; | ||
default: | ||
throw new Error(`Invalid environment "${serviceEnv}" for "${perimeter}" perimeter`); | ||
return fetchClientSdk; | ||
}() | ||
}, { | ||
key: "fetchExperimentValues", | ||
value: function () { | ||
var _fetchExperimentValues = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee2(fetcherOptions, identifiers, customAttributes) { | ||
var requestBody; | ||
return _regeneratorRuntime.wrap(function _callee2$(_context2) { | ||
while (1) switch (_context2.prev = _context2.next) { | ||
case 0: | ||
requestBody = { | ||
identifiers: identifiers, | ||
customAttributes: customAttributes, | ||
targetApp: fetcherOptions.targetApp | ||
}; | ||
_context2.prev = 1; | ||
_context2.next = 4; | ||
return this.fetchRequest('/api/v2/frontend/experimentValues', 'POST', fetcherOptions, requestBody); | ||
case 4: | ||
return _context2.abrupt("return", _context2.sent); | ||
case 7: | ||
_context2.prev = 7; | ||
_context2.t0 = _context2["catch"](1); | ||
if (!(_context2.t0 instanceof Error)) { | ||
_context2.next = 11; | ||
break; | ||
} | ||
throw _context2.t0; | ||
case 11: | ||
throw Error('Failed to retrieve experiment values'); | ||
case 12: | ||
case "end": | ||
return _context2.stop(); | ||
} | ||
}, _callee2, this, [[1, 7]]); | ||
})); | ||
function fetchExperimentValues(_x2, _x3, _x4) { | ||
return _fetchExperimentValues.apply(this, arguments); | ||
} | ||
} else if (perimeter === PerimeterType.COMMERCIAL) { | ||
switch (serviceEnv) { | ||
case FeatureGateEnvironment.Development: | ||
return DEV_BASE_URL; | ||
case FeatureGateEnvironment.Staging: | ||
return STAGING_BASE_URL; | ||
default: | ||
return PROD_BASE_URL; | ||
return fetchExperimentValues; | ||
}() | ||
}, { | ||
key: "handleResponseError", | ||
value: function () { | ||
var _handleResponseError = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee3(response) { | ||
var body; | ||
return _regeneratorRuntime.wrap(function _callee3$(_context3) { | ||
while (1) switch (_context3.prev = _context3.next) { | ||
case 0: | ||
if (response.ok) { | ||
_context3.next = 5; | ||
break; | ||
} | ||
_context3.next = 3; | ||
return response.text(); | ||
case 3: | ||
body = _context3.sent; | ||
throw new ResponseError("Non 2xx response status received, status: ".concat(response.status, ", body: ").concat(JSON.stringify(body))); | ||
case 5: | ||
if (!(response.status === 204)) { | ||
_context3.next = 7; | ||
break; | ||
} | ||
throw new ResponseError('Unexpected 204 response'); | ||
case 7: | ||
case "end": | ||
return _context3.stop(); | ||
} | ||
}, _callee3); | ||
})); | ||
function handleResponseError(_x5) { | ||
return _handleResponseError.apply(this, arguments); | ||
} | ||
} else { | ||
throw new Error(`Invalid perimeter "${perimeter}"`); | ||
return handleResponseError; | ||
}() | ||
}, { | ||
key: "extractResponseBody", | ||
value: function () { | ||
var _extractResponseBody = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee4(response) { | ||
var value; | ||
return _regeneratorRuntime.wrap(function _callee4$(_context4) { | ||
while (1) switch (_context4.prev = _context4.next) { | ||
case 0: | ||
_context4.next = 2; | ||
return response.text(); | ||
case 2: | ||
value = _context4.sent; | ||
return _context4.abrupt("return", JSON.parse(value)); | ||
case 4: | ||
case "end": | ||
return _context4.stop(); | ||
} | ||
}, _callee4); | ||
})); | ||
function extractResponseBody(_x6) { | ||
return _extractResponseBody.apply(this, arguments); | ||
} | ||
return extractResponseBody; | ||
}() | ||
}, { | ||
key: "getBaseUrl", | ||
value: function getBaseUrl(serviceEnv) { | ||
var useGatewayUrl = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false; | ||
var perimeter = arguments.length > 2 ? arguments[2] : undefined; | ||
if (useGatewayUrl) { | ||
return GATEWAY_BASE_URL; | ||
} | ||
if (perimeter === PerimeterType.FEDRAMP_MODERATE) { | ||
switch (serviceEnv) { | ||
case FeatureGateEnvironment.Production: | ||
return FEDM_PROD_BASE_URL; | ||
case FeatureGateEnvironment.Staging: | ||
return FEDM_STAGING_BASE_URL; | ||
default: | ||
throw new Error("Invalid environment \"".concat(serviceEnv, "\" for \"").concat(perimeter, "\" perimeter")); | ||
} | ||
} else if (perimeter === PerimeterType.COMMERCIAL) { | ||
switch (serviceEnv) { | ||
case FeatureGateEnvironment.Development: | ||
return DEV_BASE_URL; | ||
case FeatureGateEnvironment.Staging: | ||
return STAGING_BASE_URL; | ||
default: | ||
return PROD_BASE_URL; | ||
} | ||
} else { | ||
throw new Error("Invalid perimeter \"".concat(perimeter, "\"")); | ||
} | ||
} | ||
} | ||
static async fetchRequest(path, method, fetcherOptions, body) { | ||
const baseUrl = Fetcher.getBaseUrl(fetcherOptions.environment, fetcherOptions.useGatewayURL, fetcherOptions.perimeter); | ||
const fetchTimeout = fetcherOptions.fetchTimeoutMs || DEFAULT_REQUEST_TIMEOUT_MS; | ||
let abortSignal; | ||
if (AbortSignal.timeout) { | ||
abortSignal = AbortSignal.timeout(fetchTimeout); | ||
} else if (AbortController) { | ||
const abortController = new AbortController(); | ||
abortSignal = abortController.signal; | ||
setTimeout(() => abortController.abort(), fetchTimeout); | ||
} | ||
const response = await fetch(`${baseUrl}${path}`, _objectSpread({ | ||
method, | ||
headers: { | ||
'Content-Type': 'application/json', | ||
'X-Client-Name': 'feature-gate-js-client', | ||
'X-Client-Version': CLIENT_VERSION, | ||
'X-API-KEY': fetcherOptions.apiKey | ||
}, | ||
signal: abortSignal | ||
}, body && { | ||
body: JSON.stringify(body) | ||
})); | ||
await this.handleResponseError(response); | ||
return await this.extractResponseBody(response); | ||
} | ||
} | ||
}, { | ||
key: "fetchRequest", | ||
value: function () { | ||
var _fetchRequest = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee5(path, method, fetcherOptions, body) { | ||
var baseUrl, fetchTimeout, abortSignal, abortController, response; | ||
return _regeneratorRuntime.wrap(function _callee5$(_context5) { | ||
while (1) switch (_context5.prev = _context5.next) { | ||
case 0: | ||
baseUrl = Fetcher.getBaseUrl(fetcherOptions.environment, fetcherOptions.useGatewayURL, fetcherOptions.perimeter); | ||
fetchTimeout = fetcherOptions.fetchTimeoutMs || DEFAULT_REQUEST_TIMEOUT_MS; | ||
if (AbortSignal.timeout) { | ||
abortSignal = AbortSignal.timeout(fetchTimeout); | ||
} else if (AbortController) { | ||
abortController = new AbortController(); | ||
abortSignal = abortController.signal; | ||
setTimeout(function () { | ||
return abortController.abort(); | ||
}, fetchTimeout); | ||
} | ||
_context5.next = 5; | ||
return fetch("".concat(baseUrl).concat(path), _objectSpread({ | ||
method: method, | ||
headers: { | ||
'Content-Type': 'application/json', | ||
'X-Client-Name': 'feature-gate-js-client', | ||
'X-Client-Version': CLIENT_VERSION, | ||
'X-API-KEY': fetcherOptions.apiKey | ||
}, | ||
signal: abortSignal | ||
}, body && { | ||
body: JSON.stringify(body) | ||
})); | ||
case 5: | ||
response = _context5.sent; | ||
_context5.next = 8; | ||
return this.handleResponseError(response); | ||
case 8: | ||
_context5.next = 10; | ||
return this.extractResponseBody(response); | ||
case 10: | ||
return _context5.abrupt("return", _context5.sent); | ||
case 11: | ||
case "end": | ||
return _context5.stop(); | ||
} | ||
}, _callee5, this); | ||
})); | ||
function fetchRequest(_x7, _x8, _x9, _x10) { | ||
return _fetchRequest.apply(this, arguments); | ||
} | ||
return fetchRequest; | ||
}() | ||
}]); | ||
}(); | ||
export { Fetcher as default }; |
@@ -0,4 +1,15 @@ | ||
import _asyncToGenerator from "@babel/runtime/helpers/asyncToGenerator"; | ||
import _classCallCheck from "@babel/runtime/helpers/classCallCheck"; | ||
import _createClass from "@babel/runtime/helpers/createClass"; | ||
import _possibleConstructorReturn from "@babel/runtime/helpers/possibleConstructorReturn"; | ||
import _getPrototypeOf from "@babel/runtime/helpers/getPrototypeOf"; | ||
import _get from "@babel/runtime/helpers/get"; | ||
import _inherits from "@babel/runtime/helpers/inherits"; | ||
import _defineProperty from "@babel/runtime/helpers/defineProperty"; | ||
function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; } | ||
function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; } | ||
import _regeneratorRuntime from "@babel/runtime/regenerator"; | ||
function _callSuper(t, o, e) { return o = _getPrototypeOf(o), _possibleConstructorReturn(t, _isNativeReflectConstruct() ? Reflect.construct(o, e || [], _getPrototypeOf(t).constructor) : o.apply(t, e)); } | ||
function _isNativeReflectConstruct() { try { var t = !Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {})); } catch (t) {} return (_isNativeReflectConstruct = function _isNativeReflectConstruct() { return !!t; })(); } | ||
function _superPropGet(t, o, e, r) { var p = _get(_getPrototypeOf(1 & r ? t.prototype : t), o, e); return 2 & r && "function" == typeof p ? function (t) { return p.apply(e, t); } : p; } | ||
import { _getFullUserHash, _getStorageKey, DataAdapterCachePrefix, DataAdapterCore, StableID } from '@statsig/js-client'; | ||
@@ -11,6 +22,9 @@ | ||
*/ | ||
export class NoFetchDataAdapter extends DataAdapterCore { | ||
constructor() { | ||
super('NoFetchDataAdapter', 'nofetch'); | ||
_defineProperty(this, "bootstrapResult", null); | ||
export var NoFetchDataAdapter = /*#__PURE__*/function (_DataAdapterCore) { | ||
function NoFetchDataAdapter() { | ||
var _this; | ||
_classCallCheck(this, NoFetchDataAdapter); | ||
_this = _callSuper(this, NoFetchDataAdapter, ['NoFetchDataAdapter', 'nofetch']); | ||
_defineProperty(_this, "bootstrapResult", null); | ||
return _this; | ||
} | ||
@@ -22,48 +36,110 @@ | ||
*/ | ||
setBootstrapData(data) { | ||
this.bootstrapResult = data ? { | ||
source: 'Bootstrap', | ||
data: JSON.stringify(data), | ||
receivedAt: Date.now(), | ||
stableID: StableID.get(this._getSdkKey()), | ||
fullUserHash: null | ||
} : null; | ||
} | ||
async prefetchData(_user, _options) {} | ||
async getDataAsync(_current, user, _options) { | ||
return this.bootstrapResult && _objectSpread(_objectSpread({}, this.bootstrapResult), {}, { | ||
fullUserHash: _getFullUserHash(user) | ||
}); | ||
} | ||
getDataSync(user) { | ||
return this.bootstrapResult && _objectSpread(_objectSpread({}, this.bootstrapResult), {}, { | ||
fullUserHash: _getFullUserHash(user) | ||
}); | ||
} | ||
async _fetchFromNetwork(_current, _user, _options) { | ||
return null; | ||
} | ||
_getCacheKey(user) { | ||
// Same logic as default data adapter | ||
// https://github.com/statsig-io/js-client-monorepo/blob/main/packages/js-client/src/StatsigEvaluationsDataAdapter.ts | ||
const key = _getStorageKey(this._getSdkKey(), user); | ||
return `${DataAdapterCachePrefix}.${this._cacheSuffix}.${key}`; | ||
} | ||
_isCachedResultValidFor204(_result, _user) { | ||
return false; | ||
} | ||
setDataLegacy(data, user) { | ||
super.setData(data, user); | ||
} | ||
_inherits(NoFetchDataAdapter, _DataAdapterCore); | ||
return _createClass(NoFetchDataAdapter, [{ | ||
key: "setBootstrapData", | ||
value: function setBootstrapData(data) { | ||
this.bootstrapResult = data ? { | ||
source: 'Bootstrap', | ||
data: JSON.stringify(data), | ||
receivedAt: Date.now(), | ||
stableID: StableID.get(this._getSdkKey()), | ||
fullUserHash: null | ||
} : null; | ||
} | ||
}, { | ||
key: "prefetchData", | ||
value: function () { | ||
var _prefetchData = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee(_user, _options) { | ||
return _regeneratorRuntime.wrap(function _callee$(_context) { | ||
while (1) switch (_context.prev = _context.next) { | ||
case 0: | ||
case "end": | ||
return _context.stop(); | ||
} | ||
}, _callee); | ||
})); | ||
function prefetchData(_x, _x2) { | ||
return _prefetchData.apply(this, arguments); | ||
} | ||
return prefetchData; | ||
}() | ||
}, { | ||
key: "getDataAsync", | ||
value: function () { | ||
var _getDataAsync = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee2(_current, user, _options) { | ||
return _regeneratorRuntime.wrap(function _callee2$(_context2) { | ||
while (1) switch (_context2.prev = _context2.next) { | ||
case 0: | ||
return _context2.abrupt("return", this.bootstrapResult && _objectSpread(_objectSpread({}, this.bootstrapResult), {}, { | ||
fullUserHash: _getFullUserHash(user) | ||
})); | ||
case 1: | ||
case "end": | ||
return _context2.stop(); | ||
} | ||
}, _callee2, this); | ||
})); | ||
function getDataAsync(_x3, _x4, _x5) { | ||
return _getDataAsync.apply(this, arguments); | ||
} | ||
return getDataAsync; | ||
}() | ||
}, { | ||
key: "getDataSync", | ||
value: function getDataSync(user) { | ||
return this.bootstrapResult && _objectSpread(_objectSpread({}, this.bootstrapResult), {}, { | ||
fullUserHash: _getFullUserHash(user) | ||
}); | ||
} | ||
}, { | ||
key: "_fetchFromNetwork", | ||
value: function () { | ||
var _fetchFromNetwork2 = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee3(_current, _user, _options) { | ||
return _regeneratorRuntime.wrap(function _callee3$(_context3) { | ||
while (1) switch (_context3.prev = _context3.next) { | ||
case 0: | ||
return _context3.abrupt("return", null); | ||
case 1: | ||
case "end": | ||
return _context3.stop(); | ||
} | ||
}, _callee3); | ||
})); | ||
function _fetchFromNetwork(_x6, _x7, _x8) { | ||
return _fetchFromNetwork2.apply(this, arguments); | ||
} | ||
return _fetchFromNetwork; | ||
}() | ||
}, { | ||
key: "_getCacheKey", | ||
value: function _getCacheKey(user) { | ||
// Same logic as default data adapter | ||
// https://github.com/statsig-io/js-client-monorepo/blob/main/packages/js-client/src/StatsigEvaluationsDataAdapter.ts | ||
var key = _getStorageKey(this._getSdkKey(), user); | ||
return "".concat(DataAdapterCachePrefix, ".").concat(this._cacheSuffix, ".").concat(key); | ||
} | ||
}, { | ||
key: "_isCachedResultValidFor204", | ||
value: function _isCachedResultValidFor204(_result, _user) { | ||
return false; | ||
} | ||
}, { | ||
key: "setDataLegacy", | ||
value: function setDataLegacy(data, user) { | ||
_superPropGet(NoFetchDataAdapter, "setData", this, 3)([data, user]); | ||
} | ||
// Do not stringify options property since that includes this adapter and will | ||
// cause a circular reference when Statsig sends diagnostic events and including | ||
// values is not necessary and makes the result huge | ||
toJSON() { | ||
const result = _objectSpread({}, this); | ||
delete result._options; | ||
delete result._inMemoryCache; | ||
delete result.bootstrapResult; | ||
return result; | ||
} | ||
} | ||
// Do not stringify options property since that includes this adapter and will | ||
// cause a circular reference when Statsig sends diagnostic events and including | ||
// values is not necessary and makes the result huge | ||
}, { | ||
key: "toJSON", | ||
value: function toJSON() { | ||
var result = _objectSpread({}, this); | ||
delete result._options; | ||
delete result._inMemoryCache; | ||
delete result.bootstrapResult; | ||
return result; | ||
} | ||
}]); | ||
}(DataAdapterCore); |
import _defineProperty from "@babel/runtime/helpers/defineProperty"; | ||
import _slicedToArray from "@babel/runtime/helpers/slicedToArray"; | ||
import _classCallCheck from "@babel/runtime/helpers/classCallCheck"; | ||
import _createClass from "@babel/runtime/helpers/createClass"; | ||
function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; } | ||
function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; } | ||
import { _DJB2, _makeTypedGet } from '@statsig/client-core'; | ||
const LOCAL_OVERRIDE_REASON = 'LocalOverride:Recognized'; | ||
export const LOCAL_STORAGE_KEY = 'STATSIG_OVERRIDES'; | ||
const LEGACY_LOCAL_STORAGE_KEY = 'STATSIG_JS_LITE_LOCAL_OVERRIDES'; | ||
const makeEmptyStore = () => ({ | ||
gates: {}, | ||
configs: {}, | ||
layers: {} | ||
}); | ||
var LOCAL_OVERRIDE_REASON = 'LocalOverride:Recognized'; | ||
export var LOCAL_STORAGE_KEY = 'STATSIG_OVERRIDES'; | ||
var LEGACY_LOCAL_STORAGE_KEY = 'STATSIG_JS_LITE_LOCAL_OVERRIDES'; | ||
var makeEmptyStore = function makeEmptyStore() { | ||
return { | ||
gates: {}, | ||
configs: {}, | ||
layers: {} | ||
}; | ||
}; | ||
@@ -22,162 +27,227 @@ /** | ||
*/ | ||
export class PersistentOverrideAdapter { | ||
constructor(localStorageKey) { | ||
export var PersistentOverrideAdapter = /*#__PURE__*/function () { | ||
function PersistentOverrideAdapter(localStorageKey) { | ||
_classCallCheck(this, PersistentOverrideAdapter); | ||
this._overrides = makeEmptyStore(); | ||
this._localStorageKey = localStorageKey; | ||
} | ||
parseStoredOverrides(localStorageKey) { | ||
try { | ||
const json = window.localStorage.getItem(localStorageKey); | ||
if (!json) { | ||
return _createClass(PersistentOverrideAdapter, [{ | ||
key: "parseStoredOverrides", | ||
value: function parseStoredOverrides(localStorageKey) { | ||
try { | ||
var json = window.localStorage.getItem(localStorageKey); | ||
if (!json) { | ||
return makeEmptyStore(); | ||
} | ||
return JSON.parse(json); | ||
} catch (_unused) { | ||
return makeEmptyStore(); | ||
} | ||
return JSON.parse(json); | ||
} catch (_unused) { | ||
return makeEmptyStore(); | ||
} | ||
} | ||
mergeOverrides(...allOverrides) { | ||
const merged = makeEmptyStore(); | ||
for (const overrides of allOverrides) { | ||
for (const [name, value] of Object.entries((_overrides$gates = overrides.gates) !== null && _overrides$gates !== void 0 ? _overrides$gates : {})) { | ||
var _overrides$gates; | ||
merged.gates[name] = value; | ||
}, { | ||
key: "mergeOverrides", | ||
value: function mergeOverrides() { | ||
var merged = makeEmptyStore(); | ||
for (var _len = arguments.length, allOverrides = new Array(_len), _key = 0; _key < _len; _key++) { | ||
allOverrides[_key] = arguments[_key]; | ||
} | ||
for (const [name, value] of Object.entries((_overrides$configs = overrides.configs) !== null && _overrides$configs !== void 0 ? _overrides$configs : {})) { | ||
var _overrides$configs; | ||
merged.configs[name] = value; | ||
for (var _i = 0, _allOverrides = allOverrides; _i < _allOverrides.length; _i++) { | ||
var overrides = _allOverrides[_i]; | ||
for (var _i2 = 0, _Object$entries = Object.entries((_overrides$gates = overrides.gates) !== null && _overrides$gates !== void 0 ? _overrides$gates : {}); _i2 < _Object$entries.length; _i2++) { | ||
var _overrides$gates; | ||
var _Object$entries$_i = _slicedToArray(_Object$entries[_i2], 2), | ||
name = _Object$entries$_i[0], | ||
value = _Object$entries$_i[1]; | ||
merged.gates[name] = value; | ||
} | ||
for (var _i3 = 0, _Object$entries2 = Object.entries((_overrides$configs = overrides.configs) !== null && _overrides$configs !== void 0 ? _overrides$configs : {}); _i3 < _Object$entries2.length; _i3++) { | ||
var _overrides$configs; | ||
var _Object$entries2$_i = _slicedToArray(_Object$entries2[_i3], 2), | ||
_name = _Object$entries2$_i[0], | ||
_value = _Object$entries2$_i[1]; | ||
merged.configs[_name] = _value; | ||
} | ||
for (var _i4 = 0, _Object$entries3 = Object.entries((_overrides$layers = overrides.layers) !== null && _overrides$layers !== void 0 ? _overrides$layers : {}); _i4 < _Object$entries3.length; _i4++) { | ||
var _overrides$layers; | ||
var _Object$entries3$_i = _slicedToArray(_Object$entries3[_i4], 2), | ||
_name2 = _Object$entries3$_i[0], | ||
_value2 = _Object$entries3$_i[1]; | ||
merged.layers[_name2] = _value2; | ||
} | ||
} | ||
for (const [name, value] of Object.entries((_overrides$layers = overrides.layers) !== null && _overrides$layers !== void 0 ? _overrides$layers : {})) { | ||
var _overrides$layers; | ||
merged.layers[name] = value; | ||
return merged; | ||
} | ||
}, { | ||
key: "initFromStoredOverrides", | ||
value: function initFromStoredOverrides() { | ||
this.setOverrides(this.mergeOverrides(this._overrides, this.parseStoredOverrides(LEGACY_LOCAL_STORAGE_KEY), this.parseStoredOverrides(this._localStorageKey))); | ||
} | ||
}, { | ||
key: "saveOverrides", | ||
value: function saveOverrides() { | ||
try { | ||
window.localStorage.setItem(this._localStorageKey, JSON.stringify(this._overrides)); | ||
} catch (_unused2) { | ||
// ignored - window is not defined in non-browser environments, and we don't save things there | ||
// (things like SSR, etc) | ||
} | ||
} | ||
return merged; | ||
} | ||
initFromStoredOverrides() { | ||
this.setOverrides(this.mergeOverrides(this._overrides, this.parseStoredOverrides(LEGACY_LOCAL_STORAGE_KEY), this.parseStoredOverrides(this._localStorageKey))); | ||
} | ||
saveOverrides() { | ||
try { | ||
window.localStorage.setItem(this._localStorageKey, JSON.stringify(this._overrides)); | ||
} catch (_unused2) { | ||
// ignored - window is not defined in non-browser environments, and we don't save things there | ||
// (things like SSR, etc) | ||
}, { | ||
key: "getOverrides", | ||
value: function getOverrides() { | ||
return Object.fromEntries(Object.entries(this._overrides).map(function (_ref) { | ||
var _ref2 = _slicedToArray(_ref, 2), | ||
key = _ref2[0], | ||
container = _ref2[1]; | ||
var record = {}; | ||
for (var _i5 = 0, _Object$entries4 = Object.entries(container); _i5 < _Object$entries4.length; _i5++) { | ||
var _Object$entries4$_i = _slicedToArray(_Object$entries4[_i5], 2), | ||
name = _Object$entries4$_i[0], | ||
value = _Object$entries4$_i[1]; | ||
if (Object.prototype.hasOwnProperty.call(container, _DJB2(name))) { | ||
record[name] = value; | ||
} | ||
} | ||
return [key, record]; | ||
})); | ||
} | ||
} | ||
getOverrides() { | ||
return Object.fromEntries(Object.entries(this._overrides).map(([key, container]) => { | ||
const record = {}; | ||
for (const [name, value] of Object.entries(container)) { | ||
if (Object.prototype.hasOwnProperty.call(container, _DJB2(name))) { | ||
record[name] = value; | ||
}, { | ||
key: "setOverrides", | ||
value: function setOverrides(overrides) { | ||
var newOverrides = _objectSpread(_objectSpread({}, makeEmptyStore()), overrides); | ||
for (var _i6 = 0, _Object$values = Object.values(newOverrides); _i6 < _Object$values.length; _i6++) { | ||
var container = _Object$values[_i6]; | ||
for (var _i7 = 0, _Object$entries5 = Object.entries(container); _i7 < _Object$entries5.length; _i7++) { | ||
var _Object$entries5$_i = _slicedToArray(_Object$entries5[_i7], 2), | ||
name = _Object$entries5$_i[0], | ||
value = _Object$entries5$_i[1]; | ||
var hash = _DJB2(name); | ||
if (!Object.prototype.hasOwnProperty.call(container, hash)) { | ||
container[hash] = value; | ||
} | ||
} | ||
} | ||
return [key, record]; | ||
})); | ||
} | ||
setOverrides(overrides) { | ||
const newOverrides = _objectSpread(_objectSpread({}, makeEmptyStore()), overrides); | ||
for (const container of Object.values(newOverrides)) { | ||
for (const [name, value] of Object.entries(container)) { | ||
const hash = _DJB2(name); | ||
if (!Object.prototype.hasOwnProperty.call(container, hash)) { | ||
container[hash] = value; | ||
} | ||
this._overrides = newOverrides; | ||
this.saveOverrides(); | ||
} | ||
}, { | ||
key: "overrideGate", | ||
value: function overrideGate(name, value) { | ||
this._overrides.gates[name] = value; | ||
this._overrides.gates[_DJB2(name)] = value; | ||
this.saveOverrides(); | ||
} | ||
}, { | ||
key: "removeGateOverride", | ||
value: function removeGateOverride(name) { | ||
delete this._overrides.gates[name]; | ||
delete this._overrides.gates[_DJB2(name)]; | ||
this.saveOverrides(); | ||
} | ||
}, { | ||
key: "getGateOverride", | ||
value: function getGateOverride(current, _user) { | ||
var _this$_overrides$gate; | ||
var overridden = (_this$_overrides$gate = this._overrides.gates[current.name]) !== null && _this$_overrides$gate !== void 0 ? _this$_overrides$gate : this._overrides.gates[_DJB2(current.name)]; | ||
if (overridden == null) { | ||
return null; | ||
} | ||
return _objectSpread(_objectSpread({}, current), {}, { | ||
value: overridden, | ||
details: _objectSpread(_objectSpread({}, current.details), {}, { | ||
reason: LOCAL_OVERRIDE_REASON | ||
}) | ||
}); | ||
} | ||
this._overrides = newOverrides; | ||
this.saveOverrides(); | ||
} | ||
overrideGate(name, value) { | ||
this._overrides.gates[name] = value; | ||
this._overrides.gates[_DJB2(name)] = value; | ||
this.saveOverrides(); | ||
} | ||
removeGateOverride(name) { | ||
delete this._overrides.gates[name]; | ||
delete this._overrides.gates[_DJB2(name)]; | ||
this.saveOverrides(); | ||
} | ||
getGateOverride(current, _user) { | ||
var _this$_overrides$gate; | ||
const overridden = (_this$_overrides$gate = this._overrides.gates[current.name]) !== null && _this$_overrides$gate !== void 0 ? _this$_overrides$gate : this._overrides.gates[_DJB2(current.name)]; | ||
if (overridden == null) { | ||
return null; | ||
}, { | ||
key: "overrideDynamicConfig", | ||
value: function overrideDynamicConfig(name, value) { | ||
this._overrides.configs[name] = value; | ||
this._overrides.configs[_DJB2(name)] = value; | ||
this.saveOverrides(); | ||
} | ||
return _objectSpread(_objectSpread({}, current), {}, { | ||
value: overridden, | ||
details: _objectSpread(_objectSpread({}, current.details), {}, { | ||
reason: LOCAL_OVERRIDE_REASON | ||
}) | ||
}); | ||
} | ||
overrideDynamicConfig(name, value) { | ||
this._overrides.configs[name] = value; | ||
this._overrides.configs[_DJB2(name)] = value; | ||
this.saveOverrides(); | ||
} | ||
removeDynamicConfigOverride(name) { | ||
delete this._overrides.configs[name]; | ||
delete this._overrides.configs[_DJB2(name)]; | ||
this.saveOverrides(); | ||
} | ||
getDynamicConfigOverride(current, _user) { | ||
return this._getConfigOverride(current, this._overrides.configs); | ||
} | ||
overrideExperiment(name, value) { | ||
this._overrides.configs[name] = value; | ||
this._overrides.configs[_DJB2(name)] = value; | ||
this.saveOverrides(); | ||
} | ||
removeExperimentOverride(name) { | ||
delete this._overrides.configs[name]; | ||
delete this._overrides.configs[_DJB2(name)]; | ||
this.saveOverrides(); | ||
} | ||
getExperimentOverride(current, _user) { | ||
return this._getConfigOverride(current, this._overrides.configs); | ||
} | ||
overrideLayer(name, value) { | ||
this._overrides.layers[name] = value; | ||
this._overrides.layers[_DJB2(name)] = value; | ||
this.saveOverrides(); | ||
} | ||
removeLayerOverride(name) { | ||
delete this._overrides.layers[name]; | ||
delete this._overrides.layers[_DJB2(name)]; | ||
this.saveOverrides(); | ||
} | ||
removeAllOverrides() { | ||
this._overrides = makeEmptyStore(); | ||
window.localStorage.removeItem(LOCAL_STORAGE_KEY); | ||
} | ||
getLayerOverride(current, _user) { | ||
var _this$_overrides$laye; | ||
const overridden = (_this$_overrides$laye = this._overrides.layers[current.name]) !== null && _this$_overrides$laye !== void 0 ? _this$_overrides$laye : this._overrides.layers[_DJB2(current.name)]; | ||
if (overridden == null) { | ||
return null; | ||
}, { | ||
key: "removeDynamicConfigOverride", | ||
value: function removeDynamicConfigOverride(name) { | ||
delete this._overrides.configs[name]; | ||
delete this._overrides.configs[_DJB2(name)]; | ||
this.saveOverrides(); | ||
} | ||
return _objectSpread(_objectSpread({}, current), {}, { | ||
__value: overridden, | ||
get: _makeTypedGet(current.name, overridden), | ||
details: _objectSpread(_objectSpread({}, current.details), {}, { | ||
reason: LOCAL_OVERRIDE_REASON | ||
}) | ||
}); | ||
} | ||
_getConfigOverride(current, lookup) { | ||
var _lookup$current$name; | ||
const overridden = (_lookup$current$name = lookup[current.name]) !== null && _lookup$current$name !== void 0 ? _lookup$current$name : lookup[_DJB2(current.name)]; | ||
if (overridden == null) { | ||
return null; | ||
}, { | ||
key: "getDynamicConfigOverride", | ||
value: function getDynamicConfigOverride(current, _user) { | ||
return this._getConfigOverride(current, this._overrides.configs); | ||
} | ||
return _objectSpread(_objectSpread({}, current), {}, { | ||
value: overridden, | ||
get: _makeTypedGet(current.name, overridden), | ||
details: _objectSpread(_objectSpread({}, current.details), {}, { | ||
reason: LOCAL_OVERRIDE_REASON | ||
}) | ||
}); | ||
} | ||
} | ||
}, { | ||
key: "overrideExperiment", | ||
value: function overrideExperiment(name, value) { | ||
this._overrides.configs[name] = value; | ||
this._overrides.configs[_DJB2(name)] = value; | ||
this.saveOverrides(); | ||
} | ||
}, { | ||
key: "removeExperimentOverride", | ||
value: function removeExperimentOverride(name) { | ||
delete this._overrides.configs[name]; | ||
delete this._overrides.configs[_DJB2(name)]; | ||
this.saveOverrides(); | ||
} | ||
}, { | ||
key: "getExperimentOverride", | ||
value: function getExperimentOverride(current, _user) { | ||
return this._getConfigOverride(current, this._overrides.configs); | ||
} | ||
}, { | ||
key: "overrideLayer", | ||
value: function overrideLayer(name, value) { | ||
this._overrides.layers[name] = value; | ||
this._overrides.layers[_DJB2(name)] = value; | ||
this.saveOverrides(); | ||
} | ||
}, { | ||
key: "removeLayerOverride", | ||
value: function removeLayerOverride(name) { | ||
delete this._overrides.layers[name]; | ||
delete this._overrides.layers[_DJB2(name)]; | ||
this.saveOverrides(); | ||
} | ||
}, { | ||
key: "removeAllOverrides", | ||
value: function removeAllOverrides() { | ||
this._overrides = makeEmptyStore(); | ||
window.localStorage.removeItem(LOCAL_STORAGE_KEY); | ||
} | ||
}, { | ||
key: "getLayerOverride", | ||
value: function getLayerOverride(current, _user) { | ||
var _this$_overrides$laye; | ||
var overridden = (_this$_overrides$laye = this._overrides.layers[current.name]) !== null && _this$_overrides$laye !== void 0 ? _this$_overrides$laye : this._overrides.layers[_DJB2(current.name)]; | ||
if (overridden == null) { | ||
return null; | ||
} | ||
return _objectSpread(_objectSpread({}, current), {}, { | ||
__value: overridden, | ||
get: _makeTypedGet(current.name, overridden), | ||
details: _objectSpread(_objectSpread({}, current.details), {}, { | ||
reason: LOCAL_OVERRIDE_REASON | ||
}) | ||
}); | ||
} | ||
}, { | ||
key: "_getConfigOverride", | ||
value: function _getConfigOverride(current, lookup) { | ||
var _lookup$current$name; | ||
var overridden = (_lookup$current$name = lookup[current.name]) !== null && _lookup$current$name !== void 0 ? _lookup$current$name : lookup[_DJB2(current.name)]; | ||
if (overridden == null) { | ||
return null; | ||
} | ||
return _objectSpread(_objectSpread({}, current), {}, { | ||
value: overridden, | ||
get: _makeTypedGet(current.name, overridden), | ||
details: _objectSpread(_objectSpread({}, current.details), {}, { | ||
reason: LOCAL_OVERRIDE_REASON | ||
}) | ||
}); | ||
} | ||
}]); | ||
}(); |
@@ -27,3 +27,3 @@ /** | ||
export let FeatureGateEnvironment = /*#__PURE__*/function (FeatureGateEnvironment) { | ||
export var FeatureGateEnvironment = /*#__PURE__*/function (FeatureGateEnvironment) { | ||
FeatureGateEnvironment["Development"] = "development"; | ||
@@ -36,3 +36,3 @@ FeatureGateEnvironment["Staging"] = "staging"; | ||
// If adding new values here, please check FeatureGates.getDefaultPerimeter to make sure it still returns something sensible. | ||
export let PerimeterType = /*#__PURE__*/function (PerimeterType) { | ||
export var PerimeterType = /*#__PURE__*/function (PerimeterType) { | ||
PerimeterType["COMMERCIAL"] = "commercial"; | ||
@@ -42,5 +42,5 @@ PerimeterType["FEDRAMP_MODERATE"] = "fedramp-moderate"; | ||
}({}); | ||
export const NON_BOOLEAN_VALUE = 'non_boolean'; | ||
export var NON_BOOLEAN_VALUE = 'non_boolean'; | ||
// Type magic to get the JSDoc comments from the Client class methods to appear on the static | ||
// methods in FeatureGates where the property name and function type are identical |
import _objectWithoutProperties from "@babel/runtime/helpers/objectWithoutProperties"; | ||
import _slicedToArray from "@babel/runtime/helpers/slicedToArray"; | ||
import _defineProperty from "@babel/runtime/helpers/defineProperty"; | ||
const _excluded = ["api", "disableCurrentPageLogging", "loggingIntervalMillis", "loggingBufferMaxSize", "localMode", "eventLoggingApi", "eventLoggingApiForRetries", "disableLocalStorage", "ignoreWindowUndefined", "disableAllLogging", "initTimeoutMs", "disableNetworkKeepalive", "overrideStableID", "disableErrorLogging", "disableAutoMetricsLogging"]; | ||
var _excluded = ["api", "disableCurrentPageLogging", "loggingIntervalMillis", "loggingBufferMaxSize", "localMode", "eventLoggingApi", "eventLoggingApiForRetries", "disableLocalStorage", "ignoreWindowUndefined", "disableAllLogging", "initTimeoutMs", "disableNetworkKeepalive", "overrideStableID", "disableErrorLogging", "disableAutoMetricsLogging"]; | ||
function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; } | ||
@@ -10,10 +11,12 @@ function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; } | ||
import { PerimeterType } from './types'; | ||
export const getOptionsWithDefaults = options => _objectSpread({ | ||
/** | ||
* If more federal PerimeterTypes are added in the future, this should be updated so | ||
* that isFedRamp() === true always returns the strictest perimeter. | ||
*/ | ||
perimeter: isFedRamp() ? PerimeterType.FEDRAMP_MODERATE : PerimeterType.COMMERCIAL | ||
}, options); | ||
export const shallowEquals = (objectA, objectB) => { | ||
export var getOptionsWithDefaults = function getOptionsWithDefaults(options) { | ||
return _objectSpread({ | ||
/** | ||
* If more federal PerimeterTypes are added in the future, this should be updated so | ||
* that isFedRamp() === true always returns the strictest perimeter. | ||
*/ | ||
perimeter: isFedRamp() ? PerimeterType.FEDRAMP_MODERATE : PerimeterType.COMMERCIAL | ||
}, options); | ||
}; | ||
export var shallowEquals = function shallowEquals(objectA, objectB) { | ||
if (!objectA && !objectB) { | ||
@@ -25,13 +28,21 @@ return true; | ||
} | ||
const aEntries = Object.entries(objectA); | ||
const bEntries = Object.entries(objectB); | ||
var aEntries = Object.entries(objectA); | ||
var bEntries = Object.entries(objectB); | ||
if (aEntries.length !== bEntries.length) { | ||
return false; | ||
} | ||
const ascendingKeyOrder = ([key1], [key2]) => key1.localeCompare(key2); | ||
var ascendingKeyOrder = function ascendingKeyOrder(_ref, _ref2) { | ||
var _ref3 = _slicedToArray(_ref, 1), | ||
key1 = _ref3[0]; | ||
var _ref4 = _slicedToArray(_ref2, 1), | ||
key2 = _ref4[0]; | ||
return key1.localeCompare(key2); | ||
}; | ||
aEntries.sort(ascendingKeyOrder); | ||
bEntries.sort(ascendingKeyOrder); | ||
for (let i = 0; i < aEntries.length; i++) { | ||
const [, aValue] = aEntries[i]; | ||
const [, bValue] = bEntries[i]; | ||
for (var i = 0; i < aEntries.length; i++) { | ||
var _aEntries$i = _slicedToArray(aEntries[i], 2), | ||
aValue = _aEntries$i[1]; | ||
var _bEntries$i = _slicedToArray(bEntries[i], 2), | ||
bValue = _bEntries$i[1]; | ||
if (aValue !== bValue) { | ||
@@ -48,4 +59,4 @@ return false; | ||
*/ | ||
export const toStatsigUser = (identifiers, customAttributes, sdkKey) => { | ||
const user = { | ||
export var toStatsigUser = function toStatsigUser(identifiers, customAttributes, sdkKey) { | ||
var user = { | ||
customIDs: !(customAttributes !== null && customAttributes !== void 0 && customAttributes.stableID) && sdkKey ? _objectSpread({ | ||
@@ -61,28 +72,22 @@ stableID: StableID.get(sdkKey) | ||
}; | ||
export const migrateInitializationOptions = options => { | ||
const { | ||
api, | ||
disableCurrentPageLogging, | ||
loggingIntervalMillis, | ||
loggingBufferMaxSize, | ||
localMode, | ||
eventLoggingApi, | ||
eventLoggingApiForRetries, | ||
disableLocalStorage, | ||
ignoreWindowUndefined, | ||
disableAllLogging, | ||
// No equivalent but is pointless anyway since our Statsig init is synchronous | ||
initTimeoutMs: _initTimeoutMs, | ||
// No equivalent in new client but probably not important? | ||
disableNetworkKeepalive: _disableNetworkKeepalive, | ||
// Needs to be implemented manually but unused according to zoekt | ||
overrideStableID: _overrideStableID, | ||
// No equivalent for these but can't see them actually used anywhere in old client? | ||
disableErrorLogging: _disableErrorLogging, | ||
disableAutoMetricsLogging: _disableAutoMetricsLogging | ||
} = options, | ||
export var migrateInitializationOptions = function migrateInitializationOptions(options) { | ||
var api = options.api, | ||
disableCurrentPageLogging = options.disableCurrentPageLogging, | ||
loggingIntervalMillis = options.loggingIntervalMillis, | ||
loggingBufferMaxSize = options.loggingBufferMaxSize, | ||
localMode = options.localMode, | ||
eventLoggingApi = options.eventLoggingApi, | ||
eventLoggingApiForRetries = options.eventLoggingApiForRetries, | ||
disableLocalStorage = options.disableLocalStorage, | ||
ignoreWindowUndefined = options.ignoreWindowUndefined, | ||
disableAllLogging = options.disableAllLogging, | ||
_initTimeoutMs = options.initTimeoutMs, | ||
_disableNetworkKeepalive = options.disableNetworkKeepalive, | ||
_overrideStableID = options.overrideStableID, | ||
_disableErrorLogging = options.disableErrorLogging, | ||
_disableAutoMetricsLogging = options.disableAutoMetricsLogging, | ||
rest = _objectWithoutProperties(options, _excluded); | ||
return _objectSpread(_objectSpread({}, rest), {}, { | ||
networkConfig: { | ||
api, | ||
api: api, | ||
logEventUrl: eventLoggingApi ? eventLoggingApi + 'rgstr' : undefined, | ||
@@ -94,3 +99,3 @@ logEventFallbackUrls: eventLoggingApiForRetries ? [eventLoggingApiForRetries] : undefined, | ||
loggingIntervalMs: loggingIntervalMillis, | ||
loggingBufferMaxSize, | ||
loggingBufferMaxSize: loggingBufferMaxSize, | ||
disableStorage: disableLocalStorage, | ||
@@ -100,10 +105,19 @@ disableLogging: disableAllLogging | ||
}; | ||
const evaluationReasonMappings = Object.entries(EvaluationReason).map(([key, value]) => [key.toLowerCase(), value]); | ||
export const migrateEvaluationDetails = details => { | ||
var evaluationReasonMappings = Object.entries(EvaluationReason).map(function (_ref5) { | ||
var _ref6 = _slicedToArray(_ref5, 2), | ||
key = _ref6[0], | ||
value = _ref6[1]; | ||
return [key.toLowerCase(), value]; | ||
}); | ||
export var migrateEvaluationDetails = function migrateEvaluationDetails(details) { | ||
var _evaluationReasonMapp, _evaluationReasonMapp2, _details$receivedAt; | ||
const reasonLower = details.reason.toLowerCase(); | ||
var reasonLower = details.reason.toLowerCase(); | ||
return { | ||
reason: (_evaluationReasonMapp = (_evaluationReasonMapp2 = evaluationReasonMappings.find(([key]) => reasonLower.includes(key))) === null || _evaluationReasonMapp2 === void 0 ? void 0 : _evaluationReasonMapp2[1]) !== null && _evaluationReasonMapp !== void 0 ? _evaluationReasonMapp : EvaluationReason.Unknown, | ||
reason: (_evaluationReasonMapp = (_evaluationReasonMapp2 = evaluationReasonMappings.find(function (_ref7) { | ||
var _ref8 = _slicedToArray(_ref7, 1), | ||
key = _ref8[0]; | ||
return reasonLower.includes(key); | ||
})) === null || _evaluationReasonMapp2 === void 0 ? void 0 : _evaluationReasonMapp2[1]) !== null && _evaluationReasonMapp !== void 0 ? _evaluationReasonMapp : EvaluationReason.Unknown, | ||
time: (_details$receivedAt = details.receivedAt) !== null && _details$receivedAt !== void 0 ? _details$receivedAt : Date.now() | ||
}; | ||
}; |
/// <reference types="node" /> | ||
export const CLIENT_VERSION = "4.26.0"; | ||
export var CLIENT_VERSION = "4.26.1"; |
@@ -0,1 +1,3 @@ | ||
import _classCallCheck from "@babel/runtime/helpers/classCallCheck"; | ||
import _createClass from "@babel/runtime/helpers/createClass"; | ||
import _defineProperty from "@babel/runtime/helpers/defineProperty"; | ||
@@ -5,65 +7,82 @@ function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; } | ||
import { EventEmitter2 } from 'eventemitter2'; | ||
export const ALL_FEATURE_VALUES = '@all-features'; | ||
export default class Subscriptions { | ||
constructor() { | ||
export var ALL_FEATURE_VALUES = '@all-features'; | ||
var Subscriptions = /*#__PURE__*/function () { | ||
function Subscriptions() { | ||
_classCallCheck(this, Subscriptions); | ||
_defineProperty(this, "eventToValue", new Map()); | ||
this.emitter = new EventEmitter2(); | ||
} | ||
onGateUpdated(gateName, callback, checkGate, options) { | ||
const value = checkGate(gateName, _objectSpread(_objectSpread({}, options), {}, { | ||
fireGateExposure: false | ||
})); | ||
if (this.eventToValue.get(callback) === undefined) { | ||
this.eventToValue.set(callback, value); | ||
} | ||
const wrapCallback = () => { | ||
const value = checkGate(gateName, _objectSpread(_objectSpread({}, options), {}, { | ||
return _createClass(Subscriptions, [{ | ||
key: "onGateUpdated", | ||
value: function onGateUpdated(gateName, callback, checkGate, options) { | ||
var _this = this; | ||
var value = checkGate(gateName, _objectSpread(_objectSpread({}, options), {}, { | ||
fireGateExposure: false | ||
})); | ||
const existingValue = this.eventToValue.get(callback); | ||
if (existingValue !== value) { | ||
if (this.eventToValue.get(callback) === undefined) { | ||
this.eventToValue.set(callback, value); | ||
callback(value); | ||
} | ||
}; | ||
this.emitter.on(gateName, wrapCallback); | ||
return () => { | ||
this.emitter.off(gateName, wrapCallback); | ||
}; | ||
} | ||
onExperimentValueUpdated(experimentName, parameterName, defaultValue, callback, getExperimentValue, options) { | ||
const experimentEventName = `${experimentName}.${parameterName}`; | ||
const value = getExperimentValue(experimentName, parameterName, defaultValue, _objectSpread(_objectSpread({}, options), {}, { | ||
fireExperimentExposure: false | ||
})); | ||
if (this.eventToValue.get(callback) === undefined) { | ||
this.eventToValue.set(callback, value); | ||
var wrapCallback = function wrapCallback() { | ||
var value = checkGate(gateName, _objectSpread(_objectSpread({}, options), {}, { | ||
fireGateExposure: false | ||
})); | ||
var existingValue = _this.eventToValue.get(callback); | ||
if (existingValue !== value) { | ||
_this.eventToValue.set(callback, value); | ||
callback(value); | ||
} | ||
}; | ||
this.emitter.on(gateName, wrapCallback); | ||
return function () { | ||
_this.emitter.off(gateName, wrapCallback); | ||
}; | ||
} | ||
const wrapCallback = () => { | ||
const value = getExperimentValue(experimentName, parameterName, defaultValue, _objectSpread(_objectSpread({}, options), {}, { | ||
}, { | ||
key: "onExperimentValueUpdated", | ||
value: function onExperimentValueUpdated(experimentName, parameterName, defaultValue, callback, getExperimentValue, options) { | ||
var _this2 = this; | ||
var experimentEventName = "".concat(experimentName, ".").concat(parameterName); | ||
var value = getExperimentValue(experimentName, parameterName, defaultValue, _objectSpread(_objectSpread({}, options), {}, { | ||
fireExperimentExposure: false | ||
})); | ||
const existingValue = this.eventToValue.get(callback); | ||
if (existingValue !== value) { | ||
if (this.eventToValue.get(callback) === undefined) { | ||
this.eventToValue.set(callback, value); | ||
callback(value); | ||
} | ||
}; | ||
this.emitter.on(experimentEventName, wrapCallback); | ||
return () => { | ||
this.emitter.off(experimentEventName, wrapCallback); | ||
}; | ||
} | ||
onAnyUpdated(callback) { | ||
this.emitter.on(ALL_FEATURE_VALUES, callback); | ||
return () => { | ||
this.emitter.off(ALL_FEATURE_VALUES, callback); | ||
}; | ||
} | ||
anyUpdated() { | ||
this.emitter.emit(ALL_FEATURE_VALUES); | ||
this.emitter.eventNames().filter(name => name !== ALL_FEATURE_VALUES).forEach(event => { | ||
this.emitter.emit(event); | ||
}); | ||
} | ||
} | ||
var wrapCallback = function wrapCallback() { | ||
var value = getExperimentValue(experimentName, parameterName, defaultValue, _objectSpread(_objectSpread({}, options), {}, { | ||
fireExperimentExposure: false | ||
})); | ||
var existingValue = _this2.eventToValue.get(callback); | ||
if (existingValue !== value) { | ||
_this2.eventToValue.set(callback, value); | ||
callback(value); | ||
} | ||
}; | ||
this.emitter.on(experimentEventName, wrapCallback); | ||
return function () { | ||
_this2.emitter.off(experimentEventName, wrapCallback); | ||
}; | ||
} | ||
}, { | ||
key: "onAnyUpdated", | ||
value: function onAnyUpdated(callback) { | ||
var _this3 = this; | ||
this.emitter.on(ALL_FEATURE_VALUES, callback); | ||
return function () { | ||
_this3.emitter.off(ALL_FEATURE_VALUES, callback); | ||
}; | ||
} | ||
}, { | ||
key: "anyUpdated", | ||
value: function anyUpdated() { | ||
var _this4 = this; | ||
this.emitter.emit(ALL_FEATURE_VALUES); | ||
this.emitter.eventNames().filter(function (name) { | ||
return name !== ALL_FEATURE_VALUES; | ||
}).forEach(function (event) { | ||
_this4.emitter.emit(event); | ||
}); | ||
} | ||
}]); | ||
}(); | ||
export { Subscriptions as default }; |
{ | ||
"name": "@atlaskit/feature-gate-js-client", | ||
"version": "4.26.0", | ||
"version": "4.26.1", | ||
"description": "Atlassians wrapper for the Statsig js-lite client.", | ||
@@ -5,0 +5,0 @@ "author": "Atlassian Pty Ltd", |
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is too big to display
887495
9.37%15695
11.61%