@optimizely/optimizely-sdk
Version:
JavaScript SDK for Optimizely X Full Stack
1,497 lines (1,415 loc) • 316 kB
JavaScript
(function (global, factory) {
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
typeof define === 'function' && define.amd ? define(['exports'], factory) :
(global = global || self, factory(global.optimizelySdk = {}));
}(this, (function (exports) { 'use strict';
var commonjsGlobal = typeof globalThis !== 'undefined' ? globalThis : typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : {};
function unwrapExports (x) {
return x && x.__esModule && Object.prototype.hasOwnProperty.call(x, 'default') ? x['default'] : x;
}
function createCommonjsModule(fn, module) {
return module = { exports: {} }, fn(module, module.exports), module.exports;
}
var errorHandler = createCommonjsModule(function (module, exports) {
Object.defineProperty(exports, "__esModule", { value: true });
/**
* @export
* @class NoopErrorHandler
* @implements {ErrorHandler}
*/
var NoopErrorHandler = /** @class */ (function () {
function NoopErrorHandler() {
}
/**
* @param {Error} exception
* @memberof NoopErrorHandler
*/
NoopErrorHandler.prototype.handleError = function (exception) {
// no-op
return;
};
return NoopErrorHandler;
}());
exports.NoopErrorHandler = NoopErrorHandler;
var globalErrorHandler = new NoopErrorHandler();
/**
* @export
* @param {ErrorHandler} handler
*/
function setErrorHandler(handler) {
globalErrorHandler = handler;
}
exports.setErrorHandler = setErrorHandler;
/**
* @export
* @returns {ErrorHandler}
*/
function getErrorHandler() {
return globalErrorHandler;
}
exports.getErrorHandler = getErrorHandler;
/**
* @export
*/
function resetErrorHandler() {
globalErrorHandler = new NoopErrorHandler();
}
exports.resetErrorHandler = resetErrorHandler;
});
unwrapExports(errorHandler);
var errorHandler_1 = errorHandler.NoopErrorHandler;
var errorHandler_2 = errorHandler.setErrorHandler;
var errorHandler_3 = errorHandler.getErrorHandler;
var errorHandler_4 = errorHandler.resetErrorHandler;
var models = createCommonjsModule(function (module, exports) {
Object.defineProperty(exports, "__esModule", { value: true });
/**
* Copyright 2019, Optimizely
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
var LogLevel;
(function (LogLevel) {
LogLevel[LogLevel["NOTSET"] = 0] = "NOTSET";
LogLevel[LogLevel["DEBUG"] = 1] = "DEBUG";
LogLevel[LogLevel["INFO"] = 2] = "INFO";
LogLevel[LogLevel["WARNING"] = 3] = "WARNING";
LogLevel[LogLevel["ERROR"] = 4] = "ERROR";
})(LogLevel = exports.LogLevel || (exports.LogLevel = {}));
});
unwrapExports(models);
var models_1 = models.LogLevel;
var rngBrowser = createCommonjsModule(function (module) {
// Unique ID creation requires a high quality random # generator. In the
// browser this is a little complicated due to unknown quality of Math.random()
// and inconsistent support for the `crypto` API. We do the best we can via
// feature-detection
// getRandomValues needs to be invoked in a context where "this" is a Crypto
// implementation. Also, find the complete implementation of crypto on IE11.
var getRandomValues = (typeof(crypto) != 'undefined' && crypto.getRandomValues && crypto.getRandomValues.bind(crypto)) ||
(typeof(msCrypto) != 'undefined' && typeof window.msCrypto.getRandomValues == 'function' && msCrypto.getRandomValues.bind(msCrypto));
if (getRandomValues) {
// WHATWG crypto RNG - http://wiki.whatwg.org/wiki/Crypto
var rnds8 = new Uint8Array(16); // eslint-disable-line no-undef
module.exports = function whatwgRNG() {
getRandomValues(rnds8);
return rnds8;
};
} else {
// Math.random()-based (RNG)
//
// If all else fails, use Math.random(). It's fast, but is of unspecified
// quality.
var rnds = new Array(16);
module.exports = function mathRNG() {
for (var i = 0, r; i < 16; i++) {
if ((i & 0x03) === 0) r = Math.random() * 0x100000000;
rnds[i] = r >>> ((i & 0x03) << 3) & 0xff;
}
return rnds;
};
}
});
/**
* Convert array of 16 byte values to UUID string format of the form:
* XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX
*/
var byteToHex = [];
for (var i = 0; i < 256; ++i) {
byteToHex[i] = (i + 0x100).toString(16).substr(1);
}
function bytesToUuid(buf, offset) {
var i = offset || 0;
var bth = byteToHex;
// join used to fix memory issue caused by concatenation: https://bugs.chromium.org/p/v8/issues/detail?id=3175#c4
return ([bth[buf[i++]], bth[buf[i++]],
bth[buf[i++]], bth[buf[i++]], '-',
bth[buf[i++]], bth[buf[i++]], '-',
bth[buf[i++]], bth[buf[i++]], '-',
bth[buf[i++]], bth[buf[i++]], '-',
bth[buf[i++]], bth[buf[i++]],
bth[buf[i++]], bth[buf[i++]],
bth[buf[i++]], bth[buf[i++]]]).join('');
}
var bytesToUuid_1 = bytesToUuid;
// **`v1()` - Generate time-based UUID**
//
// Inspired by https://github.com/LiosK/UUID.js
// and http://docs.python.org/library/uuid.html
var _nodeId;
var _clockseq;
// Previous uuid creation time
var _lastMSecs = 0;
var _lastNSecs = 0;
// See https://github.com/broofa/node-uuid for API details
function v1(options, buf, offset) {
var i = buf && offset || 0;
var b = buf || [];
options = options || {};
var node = options.node || _nodeId;
var clockseq = options.clockseq !== undefined ? options.clockseq : _clockseq;
// node and clockseq need to be initialized to random values if they're not
// specified. We do this lazily to minimize issues related to insufficient
// system entropy. See #189
if (node == null || clockseq == null) {
var seedBytes = rngBrowser();
if (node == null) {
// Per 4.5, create and 48-bit node id, (47 random bits + multicast bit = 1)
node = _nodeId = [
seedBytes[0] | 0x01,
seedBytes[1], seedBytes[2], seedBytes[3], seedBytes[4], seedBytes[5]
];
}
if (clockseq == null) {
// Per 4.2.2, randomize (14 bit) clockseq
clockseq = _clockseq = (seedBytes[6] << 8 | seedBytes[7]) & 0x3fff;
}
}
// UUID timestamps are 100 nano-second units since the Gregorian epoch,
// (1582-10-15 00:00). JSNumbers aren't precise enough for this, so
// time is handled internally as 'msecs' (integer milliseconds) and 'nsecs'
// (100-nanoseconds offset from msecs) since unix epoch, 1970-01-01 00:00.
var msecs = options.msecs !== undefined ? options.msecs : new Date().getTime();
// Per 4.2.1.2, use count of uuid's generated during the current clock
// cycle to simulate higher resolution clock
var nsecs = options.nsecs !== undefined ? options.nsecs : _lastNSecs + 1;
// Time since last uuid creation (in msecs)
var dt = (msecs - _lastMSecs) + (nsecs - _lastNSecs)/10000;
// Per 4.2.1.2, Bump clockseq on clock regression
if (dt < 0 && options.clockseq === undefined) {
clockseq = clockseq + 1 & 0x3fff;
}
// Reset nsecs if clock regresses (new clockseq) or we've moved onto a new
// time interval
if ((dt < 0 || msecs > _lastMSecs) && options.nsecs === undefined) {
nsecs = 0;
}
// Per 4.2.1.2 Throw error if too many uuids are requested
if (nsecs >= 10000) {
throw new Error('uuid.v1(): Can\'t create more than 10M uuids/sec');
}
_lastMSecs = msecs;
_lastNSecs = nsecs;
_clockseq = clockseq;
// Per 4.1.4 - Convert from unix epoch to Gregorian epoch
msecs += 12219292800000;
// `time_low`
var tl = ((msecs & 0xfffffff) * 10000 + nsecs) % 0x100000000;
b[i++] = tl >>> 24 & 0xff;
b[i++] = tl >>> 16 & 0xff;
b[i++] = tl >>> 8 & 0xff;
b[i++] = tl & 0xff;
// `time_mid`
var tmh = (msecs / 0x100000000 * 10000) & 0xfffffff;
b[i++] = tmh >>> 8 & 0xff;
b[i++] = tmh & 0xff;
// `time_high_and_version`
b[i++] = tmh >>> 24 & 0xf | 0x10; // include version
b[i++] = tmh >>> 16 & 0xff;
// `clock_seq_hi_and_reserved` (Per 4.2.2 - include variant)
b[i++] = clockseq >>> 8 | 0x80;
// `clock_seq_low`
b[i++] = clockseq & 0xff;
// `node`
for (var n = 0; n < 6; ++n) {
b[i + n] = node[n];
}
return buf ? buf : bytesToUuid_1(b);
}
var v1_1 = v1;
function v4(options, buf, offset) {
var i = buf && offset || 0;
if (typeof(options) == 'string') {
buf = options === 'binary' ? new Array(16) : null;
options = null;
}
options = options || {};
var rnds = options.random || (options.rng || rngBrowser)();
// Per 4.4, set bits for version and `clock_seq_hi_and_reserved`
rnds[6] = (rnds[6] & 0x0f) | 0x40;
rnds[8] = (rnds[8] & 0x3f) | 0x80;
// Copy bytes to buffer, if provided
if (buf) {
for (var ii = 0; ii < 16; ++ii) {
buf[i + ii] = rnds[ii];
}
}
return buf || bytesToUuid_1(rnds);
}
var v4_1 = v4;
var uuid = v4_1;
uuid.v1 = v1_1;
uuid.v4 = v4_1;
var uuid_1 = uuid;
var lib = createCommonjsModule(function (module, exports) {
Object.defineProperty(exports, "__esModule", { value: true });
/**
* Copyright 2019, Optimizely
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
function getTimestamp() {
return new Date().getTime();
}
exports.getTimestamp = getTimestamp;
function generateUUID() {
return uuid_1.v4();
}
exports.generateUUID = generateUUID;
/**
* Validates a value is a valid TypeScript enum
*
* @export
* @param {object} enumToCheck
* @param {*} value
* @returns {boolean}
*/
function isValidEnum(enumToCheck, value) {
var found = false;
var keys = Object.keys(enumToCheck);
for (var index = 0; index < keys.length; index++) {
if (value === enumToCheck[keys[index]]) {
found = true;
break;
}
}
return found;
}
exports.isValidEnum = isValidEnum;
function groupBy(arr, grouperFn) {
var grouper = {};
arr.forEach(function (item) {
var key = grouperFn(item);
grouper[key] = grouper[key] || [];
grouper[key].push(item);
});
return objectValues(grouper);
}
exports.groupBy = groupBy;
function objectValues(obj) {
return Object.keys(obj).map(function (key) { return obj[key]; });
}
exports.objectValues = objectValues;
function find(arr, cond) {
var found;
for (var _i = 0, arr_1 = arr; _i < arr_1.length; _i++) {
var item = arr_1[_i];
if (cond(item)) {
found = item;
break;
}
}
return found;
}
exports.find = find;
function keyBy(arr, keyByFn) {
var map = {};
arr.forEach(function (item) {
var key = keyByFn(item);
map[key] = item;
});
return map;
}
exports.keyBy = keyBy;
function sprintf(format) {
var args = [];
for (var _i = 1; _i < arguments.length; _i++) {
args[_i - 1] = arguments[_i];
}
var i = 0;
return format.replace(/%s/g, function () {
var arg = args[i++];
var type = typeof arg;
if (type === 'function') {
return arg();
}
else if (type === 'string') {
return arg;
}
else {
return String(arg);
}
});
}
exports.sprintf = sprintf;
});
unwrapExports(lib);
var lib_1 = lib.getTimestamp;
var lib_2 = lib.generateUUID;
var lib_3 = lib.isValidEnum;
var lib_4 = lib.groupBy;
var lib_5 = lib.objectValues;
var lib_6 = lib.find;
var lib_7 = lib.keyBy;
var lib_8 = lib.sprintf;
var logger = createCommonjsModule(function (module, exports) {
Object.defineProperty(exports, "__esModule", { value: true });
/**
* Copyright 2019, Optimizely
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
var stringToLogLevel = {
NOTSET: 0,
DEBUG: 1,
INFO: 2,
WARNING: 3,
ERROR: 4,
};
function coerceLogLevel(level) {
if (typeof level !== 'string') {
return level;
}
level = level.toUpperCase();
if (level === 'WARN') {
level = 'WARNING';
}
if (!stringToLogLevel[level]) {
return level;
}
return stringToLogLevel[level];
}
var DefaultLogManager = /** @class */ (function () {
function DefaultLogManager() {
this.defaultLoggerFacade = new OptimizelyLogger();
this.loggers = {};
}
DefaultLogManager.prototype.getLogger = function (name) {
if (!name) {
return this.defaultLoggerFacade;
}
if (!this.loggers[name]) {
this.loggers[name] = new OptimizelyLogger({ messagePrefix: name });
}
return this.loggers[name];
};
return DefaultLogManager;
}());
var ConsoleLogHandler = /** @class */ (function () {
/**
* Creates an instance of ConsoleLogger.
* @param {ConsoleLogHandlerConfig} config
* @memberof ConsoleLogger
*/
function ConsoleLogHandler(config) {
if (config === void 0) { config = {}; }
this.logLevel = models.LogLevel.NOTSET;
if (config.logLevel !== undefined && lib.isValidEnum(models.LogLevel, config.logLevel)) {
this.setLogLevel(config.logLevel);
}
this.logToConsole = config.logToConsole !== undefined ? !!config.logToConsole : true;
this.prefix = config.prefix !== undefined ? config.prefix : '[OPTIMIZELY]';
}
/**
* @param {LogLevel} level
* @param {string} message
* @memberof ConsoleLogger
*/
ConsoleLogHandler.prototype.log = function (level, message) {
if (!this.shouldLog(level) || !this.logToConsole) {
return;
}
var logMessage = this.prefix + " - " + this.getLogLevelName(level) + " " + this.getTime() + " " + message;
this.consoleLog(level, [logMessage]);
};
/**
* @param {LogLevel} level
* @memberof ConsoleLogger
*/
ConsoleLogHandler.prototype.setLogLevel = function (level) {
level = coerceLogLevel(level);
if (!lib.isValidEnum(models.LogLevel, level) || level === undefined) {
this.logLevel = models.LogLevel.ERROR;
}
else {
this.logLevel = level;
}
};
/**
* @returns {string}
* @memberof ConsoleLogger
*/
ConsoleLogHandler.prototype.getTime = function () {
return new Date().toISOString();
};
/**
* @private
* @param {LogLevel} targetLogLevel
* @returns {boolean}
* @memberof ConsoleLogger
*/
ConsoleLogHandler.prototype.shouldLog = function (targetLogLevel) {
return targetLogLevel >= this.logLevel;
};
/**
* @private
* @param {LogLevel} logLevel
* @returns {string}
* @memberof ConsoleLogger
*/
ConsoleLogHandler.prototype.getLogLevelName = function (logLevel) {
switch (logLevel) {
case models.LogLevel.DEBUG:
return 'DEBUG';
case models.LogLevel.INFO:
return 'INFO ';
case models.LogLevel.WARNING:
return 'WARN ';
case models.LogLevel.ERROR:
return 'ERROR';
default:
return 'NOTSET';
}
};
/**
* @private
* @param {LogLevel} logLevel
* @param {string[]} logArguments
* @memberof ConsoleLogger
*/
ConsoleLogHandler.prototype.consoleLog = function (logLevel, logArguments) {
switch (logLevel) {
case models.LogLevel.DEBUG:
console.log.apply(console, logArguments);
break;
case models.LogLevel.INFO:
console.info.apply(console, logArguments);
break;
case models.LogLevel.WARNING:
console.warn.apply(console, logArguments);
break;
case models.LogLevel.ERROR:
console.error.apply(console, logArguments);
break;
default:
console.log.apply(console, logArguments);
}
};
return ConsoleLogHandler;
}());
exports.ConsoleLogHandler = ConsoleLogHandler;
var globalLogLevel = models.LogLevel.NOTSET;
var globalLogHandler = null;
var OptimizelyLogger = /** @class */ (function () {
function OptimizelyLogger(opts) {
if (opts === void 0) { opts = {}; }
this.messagePrefix = '';
if (opts.messagePrefix) {
this.messagePrefix = opts.messagePrefix;
}
}
/**
* @param {(LogLevel | LogInputObject)} levelOrObj
* @param {string} [message]
* @memberof OptimizelyLogger
*/
OptimizelyLogger.prototype.log = function (level, message) {
this.internalLog(coerceLogLevel(level), {
message: message,
splat: [],
});
};
OptimizelyLogger.prototype.info = function (message) {
var splat = [];
for (var _i = 1; _i < arguments.length; _i++) {
splat[_i - 1] = arguments[_i];
}
this.namedLog(models.LogLevel.INFO, message, splat);
};
OptimizelyLogger.prototype.debug = function (message) {
var splat = [];
for (var _i = 1; _i < arguments.length; _i++) {
splat[_i - 1] = arguments[_i];
}
this.namedLog(models.LogLevel.DEBUG, message, splat);
};
OptimizelyLogger.prototype.warn = function (message) {
var splat = [];
for (var _i = 1; _i < arguments.length; _i++) {
splat[_i - 1] = arguments[_i];
}
this.namedLog(models.LogLevel.WARNING, message, splat);
};
OptimizelyLogger.prototype.error = function (message) {
var splat = [];
for (var _i = 1; _i < arguments.length; _i++) {
splat[_i - 1] = arguments[_i];
}
this.namedLog(models.LogLevel.ERROR, message, splat);
};
OptimizelyLogger.prototype.format = function (data) {
return "" + (this.messagePrefix ? this.messagePrefix + ': ' : '') + lib.sprintf.apply(void 0, [data.message].concat(data.splat));
};
OptimizelyLogger.prototype.internalLog = function (level, data) {
if (!globalLogHandler) {
return;
}
if (level < globalLogLevel) {
return;
}
globalLogHandler.log(level, this.format(data));
if (data.error && data.error instanceof Error) {
errorHandler.getErrorHandler().handleError(data.error);
}
};
OptimizelyLogger.prototype.namedLog = function (level, message, splat) {
var error;
if (message instanceof Error) {
error = message;
message = error.message;
this.internalLog(level, {
error: error,
message: message,
splat: splat,
});
return;
}
if (splat.length === 0) {
this.internalLog(level, {
message: message,
splat: splat,
});
return;
}
var last = splat[splat.length - 1];
if (last instanceof Error) {
error = last;
splat.splice(-1);
}
this.internalLog(level, { message: message, error: error, splat: splat });
};
return OptimizelyLogger;
}());
var globalLogManager = new DefaultLogManager();
function getLogger(name) {
return globalLogManager.getLogger(name);
}
exports.getLogger = getLogger;
function setLogHandler(logger) {
globalLogHandler = logger;
}
exports.setLogHandler = setLogHandler;
function setLogLevel(level) {
level = coerceLogLevel(level);
if (!lib.isValidEnum(models.LogLevel, level) || level === undefined) {
globalLogLevel = models.LogLevel.ERROR;
}
else {
globalLogLevel = level;
}
}
exports.setLogLevel = setLogLevel;
function getLogLevel() {
return globalLogLevel;
}
exports.getLogLevel = getLogLevel;
/**
* Resets all global logger state to it's original
*/
function resetLogger() {
globalLogManager = new DefaultLogManager();
globalLogLevel = models.LogLevel.NOTSET;
}
exports.resetLogger = resetLogger;
});
unwrapExports(logger);
var logger_1 = logger.ConsoleLogHandler;
var logger_2 = logger.getLogger;
var logger_3 = logger.setLogHandler;
var logger_4 = logger.setLogLevel;
var logger_5 = logger.getLogLevel;
var logger_6 = logger.resetLogger;
var lib$1 = createCommonjsModule(function (module, exports) {
function __export(m) {
for (var p in m) if (!exports.hasOwnProperty(p)) exports[p] = m[p];
}
Object.defineProperty(exports, "__esModule", { value: true });
/**
* Copyright 2019, Optimizely
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
__export(errorHandler);
__export(models);
__export(logger);
});
unwrapExports(lib$1);
var lib_1$1 = lib$1.ConsoleLogHandler;
var lib_2$1 = lib$1.getLogger;
var lib_3$1 = lib$1.setLogLevel;
var lib_4$1 = lib$1.LogLevel;
var lib_5$1 = lib$1.setLogHandler;
var lib_6$1 = lib$1.setErrorHandler;
var lib_7$1 = lib$1.getErrorHandler;
var events = createCommonjsModule(function (module, exports) {
Object.defineProperty(exports, "__esModule", { value: true });
function areEventContextsEqual(eventA, eventB) {
var contextA = eventA.context;
var contextB = eventB.context;
return (contextA.accountId === contextB.accountId &&
contextA.projectId === contextB.projectId &&
contextA.clientName === contextB.clientName &&
contextA.clientVersion === contextB.clientVersion &&
contextA.revision === contextB.revision &&
contextA.anonymizeIP === contextB.anonymizeIP &&
contextA.botFiltering === contextB.botFiltering);
}
exports.areEventContextsEqual = areEventContextsEqual;
});
unwrapExports(events);
var events_1 = events.areEventContextsEqual;
var eventQueue = createCommonjsModule(function (module, exports) {
/**
* Copyright 2019, Optimizely
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
Object.defineProperty(exports, "__esModule", { value: true });
var logger = lib$1.getLogger('EventProcessor');
var Timer = /** @class */ (function () {
function Timer(_a) {
var timeout = _a.timeout, callback = _a.callback;
this.timeout = Math.max(timeout, 0);
this.callback = callback;
}
Timer.prototype.start = function () {
this.timeoutId = setTimeout(this.callback, this.timeout);
};
Timer.prototype.refresh = function () {
this.stop();
this.start();
};
Timer.prototype.stop = function () {
if (this.timeoutId) {
clearTimeout(this.timeoutId);
}
};
return Timer;
}());
var SingleEventQueue = /** @class */ (function () {
function SingleEventQueue(_a) {
var sink = _a.sink;
this.sink = sink;
}
SingleEventQueue.prototype.start = function () {
// no-op
};
SingleEventQueue.prototype.stop = function () {
// no-op
return Promise.resolve();
};
SingleEventQueue.prototype.enqueue = function (event) {
this.sink([event]);
};
return SingleEventQueue;
}());
exports.SingleEventQueue = SingleEventQueue;
var DefaultEventQueue = /** @class */ (function () {
function DefaultEventQueue(_a) {
var flushInterval = _a.flushInterval, maxQueueSize = _a.maxQueueSize, sink = _a.sink, batchComparator = _a.batchComparator;
this.buffer = [];
this.maxQueueSize = Math.max(maxQueueSize, 1);
this.sink = sink;
this.batchComparator = batchComparator;
this.timer = new Timer({
callback: this.flush.bind(this),
timeout: flushInterval,
});
this.started = false;
}
DefaultEventQueue.prototype.start = function () {
this.started = true;
// dont start the timer until the first event is enqueued
};
DefaultEventQueue.prototype.stop = function () {
this.started = false;
var result = this.sink(this.buffer);
this.buffer = [];
this.timer.stop();
return result;
};
DefaultEventQueue.prototype.enqueue = function (event) {
if (!this.started) {
logger.warn('Queue is stopped, not accepting event');
return;
}
// If new event cannot be included into the current batch, flush so it can
// be in its own new batch.
var bufferedEvent = this.buffer[0];
if (bufferedEvent && !this.batchComparator(bufferedEvent, event)) {
this.flush();
}
// start the timer when the first event is put in
if (this.buffer.length === 0) {
this.timer.refresh();
}
this.buffer.push(event);
if (this.buffer.length >= this.maxQueueSize) {
this.flush();
}
};
DefaultEventQueue.prototype.flush = function () {
this.sink(this.buffer);
this.buffer = [];
this.timer.stop();
};
return DefaultEventQueue;
}());
exports.DefaultEventQueue = DefaultEventQueue;
});
unwrapExports(eventQueue);
var eventQueue_1 = eventQueue.SingleEventQueue;
var eventQueue_2 = eventQueue.DefaultEventQueue;
var lib$2 = createCommonjsModule(function (module, exports) {
Object.defineProperty(exports, "__esModule", { value: true });
/**
* Copyright 2019, Optimizely
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
function generateUUID() {
return uuid_1.v4();
}
exports.generateUUID = generateUUID;
function getTimestamp() {
return new Date().getTime();
}
exports.getTimestamp = getTimestamp;
/**
* Validates a value is a valid TypeScript enum
*
* @export
* @param {object} enumToCheck
* @param {*} value
* @returns {boolean}
*/
function isValidEnum(enumToCheck, value) {
var found = false;
var keys = Object.keys(enumToCheck);
for (var index = 0; index < keys.length; index++) {
if (value === enumToCheck[keys[index]]) {
found = true;
break;
}
}
return found;
}
exports.isValidEnum = isValidEnum;
function groupBy(arr, grouperFn) {
var grouper = {};
arr.forEach(function (item) {
var key = grouperFn(item);
grouper[key] = grouper[key] || [];
grouper[key].push(item);
});
return objectValues(grouper);
}
exports.groupBy = groupBy;
function objectValues(obj) {
return Object.keys(obj).map(function (key) { return obj[key]; });
}
exports.objectValues = objectValues;
function objectEntries(obj) {
return Object.keys(obj).map(function (key) { return [key, obj[key]]; });
}
exports.objectEntries = objectEntries;
function find(arr, cond) {
var found;
for (var _i = 0, arr_1 = arr; _i < arr_1.length; _i++) {
var item = arr_1[_i];
if (cond(item)) {
found = item;
break;
}
}
return found;
}
exports.find = find;
function keyBy(arr, keyByFn) {
var map = {};
arr.forEach(function (item) {
var key = keyByFn(item);
map[key] = item;
});
return map;
}
exports.keyBy = keyBy;
function sprintf(format) {
var args = [];
for (var _i = 1; _i < arguments.length; _i++) {
args[_i - 1] = arguments[_i];
}
var i = 0;
return format.replace(/%s/g, function () {
var arg = args[i++];
var type = typeof arg;
if (type === 'function') {
return arg();
}
else if (type === 'string') {
return arg;
}
else {
return String(arg);
}
});
}
exports.sprintf = sprintf;
/*
* Notification types for use with NotificationCenter
* Format is EVENT: <list of parameters to callback>
*
* SDK consumers can use these to register callbacks with the notification center.
*
* @deprecated since 3.1.0
* ACTIVATE: An impression event will be sent to Optimizely
* Callbacks will receive an object argument with the following properties:
* - experiment {Object}
* - userId {string}
* - attributes {Object|undefined}
* - variation {Object}
* - logEvent {Object}
*
* DECISION: A decision is made in the system. i.e. user activation,
* feature access or feature-variable value retrieval
* Callbacks will receive an object argument with the following properties:
* - type {string}
* - userId {string}
* - attributes {Object|undefined}
* - decisionInfo {Object|undefined}
*
* LOG_EVENT: A batch of events, which could contain impressions and/or conversions,
* will be sent to Optimizely
* Callbacks will receive an object argument with the following properties:
* - url {string}
* - httpVerb {string}
* - params {Object}
*
* OPTIMIZELY_CONFIG_UPDATE: This Optimizely instance has been updated with a new
* config
*
* TRACK: A conversion event will be sent to Optimizely
* Callbacks will receive the an object argument with the following properties:
* - eventKey {string}
* - userId {string}
* - attributes {Object|undefined}
* - eventTags {Object|undefined}
* - logEvent {Object}
*
*/
var NOTIFICATION_TYPES;
(function (NOTIFICATION_TYPES) {
NOTIFICATION_TYPES["ACTIVATE"] = "ACTIVATE:experiment, user_id,attributes, variation, event";
NOTIFICATION_TYPES["DECISION"] = "DECISION:type, userId, attributes, decisionInfo";
NOTIFICATION_TYPES["LOG_EVENT"] = "LOG_EVENT:logEvent";
NOTIFICATION_TYPES["OPTIMIZELY_CONFIG_UPDATE"] = "OPTIMIZELY_CONFIG_UPDATE";
NOTIFICATION_TYPES["TRACK"] = "TRACK:event_key, user_id, attributes, event_tags, event";
})(NOTIFICATION_TYPES = exports.NOTIFICATION_TYPES || (exports.NOTIFICATION_TYPES = {}));
});
unwrapExports(lib$2);
var lib_1$2 = lib$2.generateUUID;
var lib_2$2 = lib$2.getTimestamp;
var lib_3$2 = lib$2.isValidEnum;
var lib_4$2 = lib$2.groupBy;
var lib_5$2 = lib$2.objectValues;
var lib_6$2 = lib$2.objectEntries;
var lib_7$2 = lib$2.find;
var lib_8$1 = lib$2.keyBy;
var lib_9 = lib$2.sprintf;
var lib_10 = lib$2.NOTIFICATION_TYPES;
var eventProcessor = createCommonjsModule(function (module, exports) {
Object.defineProperty(exports, "__esModule", { value: true });
exports.DEFAULT_FLUSH_INTERVAL = 30000; // Unit is ms - default flush interval is 30s
exports.DEFAULT_BATCH_SIZE = 10;
var logger = lib$1.getLogger('EventProcessor');
function validateAndGetFlushInterval(flushInterval) {
if (flushInterval <= 0) {
logger.warn("Invalid flushInterval " + flushInterval + ", defaulting to " + exports.DEFAULT_FLUSH_INTERVAL);
flushInterval = exports.DEFAULT_FLUSH_INTERVAL;
}
return flushInterval;
}
exports.validateAndGetFlushInterval = validateAndGetFlushInterval;
function validateAndGetBatchSize(batchSize) {
batchSize = Math.floor(batchSize);
if (batchSize < 1) {
logger.warn("Invalid batchSize " + batchSize + ", defaulting to " + exports.DEFAULT_BATCH_SIZE);
batchSize = exports.DEFAULT_BATCH_SIZE;
}
batchSize = Math.max(1, batchSize);
return batchSize;
}
exports.validateAndGetBatchSize = validateAndGetBatchSize;
function getQueue(batchSize, flushInterval, sink, batchComparator) {
var queue;
if (batchSize > 1) {
queue = new eventQueue.DefaultEventQueue({
flushInterval: flushInterval,
maxQueueSize: batchSize,
sink: sink,
batchComparator: batchComparator,
});
}
else {
queue = new eventQueue.SingleEventQueue({ sink: sink });
}
return queue;
}
exports.getQueue = getQueue;
function sendEventNotification(notificationCenter, event) {
if (notificationCenter) {
notificationCenter.sendNotifications(lib$2.NOTIFICATION_TYPES.LOG_EVENT, event);
}
}
exports.sendEventNotification = sendEventNotification;
});
unwrapExports(eventProcessor);
var eventProcessor_1 = eventProcessor.DEFAULT_FLUSH_INTERVAL;
var eventProcessor_2 = eventProcessor.DEFAULT_BATCH_SIZE;
var eventProcessor_3 = eventProcessor.validateAndGetFlushInterval;
var eventProcessor_4 = eventProcessor.validateAndGetBatchSize;
var eventProcessor_5 = eventProcessor.getQueue;
var eventProcessor_6 = eventProcessor.sendEventNotification;
var pendingEventsStore = createCommonjsModule(function (module, exports) {
Object.defineProperty(exports, "__esModule", { value: true });
/**
* Copyright 2019, Optimizely
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
var logger = lib$1.getLogger('EventProcessor');
var LocalStorageStore = /** @class */ (function () {
function LocalStorageStore(_a) {
var key = _a.key, _b = _a.maxValues, maxValues = _b === void 0 ? 1000 : _b;
this.LS_KEY = key;
this.maxValues = maxValues;
}
LocalStorageStore.prototype.get = function (key) {
return this.getMap()[key] || null;
};
LocalStorageStore.prototype.set = function (key, value) {
var map = this.getMap();
map[key] = value;
this.replace(map);
};
LocalStorageStore.prototype.remove = function (key) {
var map = this.getMap();
delete map[key];
this.replace(map);
};
LocalStorageStore.prototype.values = function () {
return lib$2.objectValues(this.getMap());
};
LocalStorageStore.prototype.clear = function () {
this.replace({});
};
LocalStorageStore.prototype.replace = function (map) {
try {
// This is a temporary fix to support React Native which does not have localStorage.
window.localStorage && localStorage.setItem(this.LS_KEY, JSON.stringify(map));
this.clean();
}
catch (e) {
logger.error(e);
}
};
LocalStorageStore.prototype.clean = function () {
var map = this.getMap();
var keys = Object.keys(map);
var toRemove = keys.length - this.maxValues;
if (toRemove < 1) {
return;
}
var entries = keys.map(function (key) { return ({
key: key,
value: map[key]
}); });
entries.sort(function (a, b) { return a.value.timestamp - b.value.timestamp; });
for (var i = 0; i < toRemove; i++) {
delete map[entries[i].key];
}
this.replace(map);
};
LocalStorageStore.prototype.getMap = function () {
try {
// This is a temporary fix to support React Native which does not have localStorage.
var data = window.localStorage && localStorage.getItem(this.LS_KEY);
if (data) {
return JSON.parse(data) || {};
}
}
catch (e) {
logger.error(e);
}
return {};
};
return LocalStorageStore;
}());
exports.LocalStorageStore = LocalStorageStore;
});
unwrapExports(pendingEventsStore);
var pendingEventsStore_1 = pendingEventsStore.LocalStorageStore;
var pendingEventsDispatcher = createCommonjsModule(function (module, exports) {
var __extends = (commonjsGlobal && commonjsGlobal.__extends) || (function () {
var extendStatics = function (d, b) {
extendStatics = Object.setPrototypeOf ||
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
return extendStatics(d, b);
};
return function (d, b) {
extendStatics(d, b);
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
/**
* Copyright 2019, Optimizely
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
var logger = lib$1.getLogger('EventProcessor');
var PendingEventsDispatcher = /** @class */ (function () {
function PendingEventsDispatcher(_a) {
var eventDispatcher = _a.eventDispatcher, store = _a.store;
this.dispatcher = eventDispatcher;
this.store = store;
}
PendingEventsDispatcher.prototype.dispatchEvent = function (request, callback) {
this.send({
uuid: lib$2.generateUUID(),
timestamp: lib$2.getTimestamp(),
request: request,
}, callback);
};
PendingEventsDispatcher.prototype.sendPendingEvents = function () {
var _this = this;
var pendingEvents = this.store.values();
logger.debug('Sending %s pending events from previous page', pendingEvents.length);
pendingEvents.forEach(function (item) {
try {
_this.send(item, function () { });
}
catch (e) { }
});
};
PendingEventsDispatcher.prototype.send = function (entry, callback) {
var _this = this;
this.store.set(entry.uuid, entry);
this.dispatcher.dispatchEvent(entry.request, function (response) {
_this.store.remove(entry.uuid);
callback(response);
});
};
return PendingEventsDispatcher;
}());
exports.PendingEventsDispatcher = PendingEventsDispatcher;
var LocalStoragePendingEventsDispatcher = /** @class */ (function (_super) {
__extends(LocalStoragePendingEventsDispatcher, _super);
function LocalStoragePendingEventsDispatcher(_a) {
var eventDispatcher = _a.eventDispatcher;
return _super.call(this, {
eventDispatcher: eventDispatcher,
store: new pendingEventsStore.LocalStorageStore({
// TODO make this configurable
maxValues: 100,
key: 'fs_optly_pending_events',
}),
}) || this;
}
return LocalStoragePendingEventsDispatcher;
}(PendingEventsDispatcher));
exports.LocalStoragePendingEventsDispatcher = LocalStoragePendingEventsDispatcher;
});
unwrapExports(pendingEventsDispatcher);
var pendingEventsDispatcher_1 = pendingEventsDispatcher.PendingEventsDispatcher;
var pendingEventsDispatcher_2 = pendingEventsDispatcher.LocalStoragePendingEventsDispatcher;
var buildEventV1 = createCommonjsModule(function (module, exports) {
var __assign = (commonjsGlobal && commonjsGlobal.__assign) || function () {
__assign = Object.assign || function(t) {
for (var s, i = 1, n = arguments.length; i < n; i++) {
s = arguments[i];
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
t[p] = s[p];
}
return t;
};
return __assign.apply(this, arguments);
};
Object.defineProperty(exports, "__esModule", { value: true });
var ACTIVATE_EVENT_KEY = 'campaign_activated';
var CUSTOM_ATTRIBUTE_FEATURE_TYPE = 'custom';
var BOT_FILTERING_KEY = '$opt_bot_filtering';
/**
* Given an array of batchable Decision or ConversionEvent events it returns
* a single EventV1 with proper batching
*
* @param {ProcessableEvent[]} events
* @returns {EventV1}
*/
function makeBatchedEventV1(events) {
var visitors = [];
var data = events[0];
events.forEach(function (event) {
if (event.type === 'conversion' || event.type === 'impression') {
var visitor = makeVisitor(event);
if (event.type === 'impression') {
visitor.snapshots.push(makeDecisionSnapshot(event));
}
else if (event.type === 'conversion') {
visitor.snapshots.push(makeConversionSnapshot(event));
}
visitors.push(visitor);
}
});
return {
client_name: data.context.clientName,
client_version: data.context.clientVersion,
account_id: data.context.accountId,
project_id: data.context.projectId,
revision: data.context.revision,
anonymize_ip: data.context.anonymizeIP,
enrich_decisions: true,
visitors: visitors,
};
}
exports.makeBatchedEventV1 = makeBatchedEventV1;
function makeConversionSnapshot(conversion) {
var tags = __assign({}, conversion.tags);
delete tags['revenue'];
delete tags['value'];
var event = {
entity_id: conversion.event.id,
key: conversion.event.key,
timestamp: conversion.timestamp,
uuid: conversion.uuid,
};
if (conversion.tags) {
event.tags = conversion.tags;
}
if (conversion.value != null) {
event.value = conversion.value;
}
if (conversion.revenue != null) {
event.revenue = conversion.revenue;
}
return {
events: [event],
};
}
function makeDecisionSnapshot(event) {
var layer = event.layer, experiment = event.experiment, variation = event.variation;
var layerId = layer ? layer.id : null;
var experimentId = experiment ? experiment.id : null;
var variationId = variation ? variation.id : null;
return {
decisions: [
{
campaign_id: layerId,
experiment_id: experimentId,
variation_id: variationId,
},
],
events: [
{
entity_id: layerId,
timestamp: event.timestamp,
key: ACTIVATE_EVENT_KEY,
uuid: event.uuid,
},
],
};
}
function makeVisitor(data) {
var visitor = {
snapshots: [],
visitor_id: data.user.id,
attributes: [],
};
data.user.attributes.forEach(function (attr) {
visitor.attributes.push({
entity_id: attr.entityId,
key: attr.key,
type: 'custom',
value: attr.value,
});
});
if (typeof data.context.botFiltering === 'boolean') {
visitor.attributes.push({
entity_id: BOT_FILTERING_KEY,
key: BOT_FILTERING_KEY,
type: CUSTOM_ATTRIBUTE_FEATURE_TYPE,
value: data.context.botFiltering,
});
}
return visitor;
}
/**
* Event for usage with v1 logtier
*
* @export
* @interface EventBuilderV1
*/
function buildImpressionEventV1(data) {
var visitor = makeVisitor(data);
visitor.snapshots.push(makeDecisionSnapshot(data));
return {
client_name: data.context.clientName,
client_version: data.context.clientVersion,
account_id: data.context.accountId,
project_id: data.context.projectId,
revision: data.context.revision,
anonymize_ip: data.context.anonymizeIP,
enrich_decisions: true,
visitors: [visitor],
};
}
exports.buildImpressionEventV1 = buildImpressionEventV1;
function buildConversionEventV1(data) {
var visitor = makeVisitor(data);
visitor.snapshots.push(makeConversionSnapshot(data));
return {
client_name: data.context.clientName,
client_version: data.context.clientVersion,
account_id: data.context.accountId,
project_id: data.context.projectId,
revision: data.context.revision,
anonymize_ip: data.context.anonymizeIP,
enrich_decisions: true,
visitors: [visitor],
};
}
exports.buildConversionEventV1 = buildConversionEventV1;
function formatEvents(events) {
return {
url: 'https://logx.optimizely.com/v1/events',
httpVerb: 'POST',
params: makeBatchedEventV1(events),
};
}
exports.formatEvents = formatEvents;
});
unwrapExports(buildEventV1);
var buildEventV1_1 = buildEventV1.makeBatchedEventV1;
var buildEventV1_2 = buildEventV1.buildImpressionEventV1;
var buildEventV1_3 = buildEventV1.buildConversionEventV1;
var buildEventV1_4 = buildEventV1.formatEvents;
var requestTracker = createCommonjsModule(function (module, exports) {
/**
* Copyright 2020, Optimizely
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
Object.defineProperty(exports, "__esModule", { value: true });
/**
* RequestTracker keeps track of in-flight requests for EventProcessor using
* an internal counter. It exposes methods for adding a new request to be
* tracked, and getting a Promise representing the completion of currently
* tracked requests.
*/
var RequestTracker = /** @class */ (function () {
function RequestTracker() {
this.reqsInFlightCount = 0;
this.reqsCompleteResolvers = [];
}
/**
* Track the argument request (represented by a Promise). reqPromise will feed
* into the state of Promises returned by onRequestsComplete.
* @param {Promise<void>} reqPromise
*/
RequestTracker.prototype.trackRequest = function (reqPromise) {
var _this = this;
this.reqsInFlightCount++;
var onReqComplete = function () {
_this.reqsInF