vwo-fme-node-sdk
Version:
VWO Node/JavaScript SDK for Feature Management and Experimentation
944 lines (924 loc) • 603 kB
JavaScript
/*!
* vwo-fme-javascript-sdk - v1.21.0
* URL - https://github.com/wingify/vwo-node-sdk
*
* Copyright 2024-2025 Wingify Software Pvt. Ltd.
*
* 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.
*
* Dependencies used -
* 1. murmurhash - ^2.0.1
* 2. superstruct - ^0.14.x
* 3. uuid - ^9.0.1
* 4. vwo-fme-sdk-log-messages - ^1.*.*
*/
(function webpackUniversalModuleDefinition(root, factory) {
// CommonJS2
if(typeof exports === 'object' && typeof module === 'object')
module.exports = factory();
// AMD
else if(typeof define === 'function' && define.amd)
define([], factory);
// CommonJS
else if(typeof exports === 'object')
exports["vwoSdk"] = factory();
// Root
else
root["vwoSdk"] = factory();
})(this, () => {
return /******/ (() => { // webpackBootstrap
/******/ var __webpack_modules__ = ({
/***/ "./lib/VWO.ts":
/*!********************!*\
!*** ./lib/VWO.ts ***!
\********************/
/***/ (function(__unused_webpack_module, exports, __webpack_require__) {
"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
var __generator = (this && this.__generator) || function (thisArg, body) {
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype);
return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
function verb(n) { return function (v) { return step([n, v]); }; }
function step(op) {
if (f) throw new TypeError("Generator is already executing.");
while (g && (g = 0, op[0] && (_ = 0)), _) try {
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
if (y = 0, t) op = [op[0] & 2, t.value];
switch (op[0]) {
case 0: case 1: t = op; break;
case 4: _.label++; return { value: op[1], done: false };
case 5: _.label++; y = op[1]; op = [0]; continue;
case 7: op = _.ops.pop(); _.trys.pop(); continue;
default:
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
if (t[2]) _.ops.pop();
_.trys.pop(); continue;
}
op = body.call(thisArg, _);
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
}
};
Object.defineProperty(exports, "__esModule", ({ value: true }));
exports.VWO = void 0;
exports.init = init;
exports.onInit = onInit;
/**
* Copyright 2024-2025 Wingify Software Pvt. Ltd.
*
* 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 VWOBuilder_1 = __webpack_require__(/*! ./VWOBuilder */ "./lib/VWOBuilder.ts");
var DataTypeUtil_1 = __webpack_require__(/*! ./utils/DataTypeUtil */ "./lib/utils/DataTypeUtil.ts");
var PromiseUtil_1 = __webpack_require__(/*! ./utils/PromiseUtil */ "./lib/utils/PromiseUtil.ts");
var log_messages_1 = __webpack_require__(/*! ./enums/log-messages */ "./lib/enums/log-messages/index.ts");
var LogMessageUtil_1 = __webpack_require__(/*! ./utils/LogMessageUtil */ "./lib/utils/LogMessageUtil.ts");
var PlatformEnum_1 = __webpack_require__(/*! ./enums/PlatformEnum */ "./lib/enums/PlatformEnum.ts");
var ApiEnum_1 = __webpack_require__(/*! ./enums/ApiEnum */ "./lib/enums/ApiEnum.ts");
var VWO = /** @class */ (function () {
/**
* Constructor for the VWO class.
* Initializes a new instance of VWO with the provided options.
* @param {Record<string, dynamic>} options - Configuration options for the VWO instance.
* @returns The instance of VWO.
*/
function VWO(options) {
return VWO.setInstance(options);
}
/**
* Sets the singleton instance of VWO.
* Configures and builds the VWO instance using the provided options.
* @param {Record<string, dynamic>} options - Configuration options for setting up VWO.
* @returns A Promise resolving to the configured VWO instance.
*/
VWO.setInstance = function (options) {
var _this = this;
var optionsVWOBuilder = options === null || options === void 0 ? void 0 : options.vwoBuilder;
this.vwoBuilder = optionsVWOBuilder || new VWOBuilder_1.VWOBuilder(options);
this.instance = this.vwoBuilder
.setLogger() // Sets up logging for debugging and monitoring.
.setSettingsService() // Sets the settings service for configuration management.
.setStorage() // Configures storage for data persistence.
.setNetworkManager() // Configures network management for API communication.
.setSegmentation() // Sets up segmentation for targeted functionality.
// .initBatching() // Initializes batching for bulk data processing.
.initPolling() // Starts polling mechanism for regular updates.
.initBatching()
.initUsageStats(); // Initializes usage statistics for the SDK.
// .setAnalyticsCallback() // Sets up analytics callback for data analysis.
if (options === null || options === void 0 ? void 0 : options.settings) {
return Promise.resolve(this.vwoBuilder.build(options.settings));
}
return this.vwoBuilder.getSettings().then(function (settings) {
return _this.vwoBuilder.build(settings); // Builds the VWO instance with the fetched settings.
});
};
Object.defineProperty(VWO, "Instance", {
/**
* Gets the singleton instance of VWO.
* @returns The singleton instance of VWO.
*/
get: function () {
return this.instance;
},
enumerable: false,
configurable: true
});
return VWO;
}());
exports.VWO = VWO;
var _global = {};
/**
* Initializes a new instance of VWO with the provided options.
* @param options Configuration options for the VWO instance.
* @property {string} sdkKey - The SDK key for the VWO account.
* @property {string} accountId - The account ID for the VWO account.
* @property {GatewayServiceModel} gatewayService - The gateway service configuration.
* @property {StorageService} storage - The storage configuration.
* @returns
*/
function init(options) {
return __awaiter(this, void 0, void 0, function () {
var apiName, date, msg, msg, msg, instance, msg;
return __generator(this, function (_a) {
apiName = ApiEnum_1.ApiEnum.INIT;
date = new Date().toISOString();
try {
if (!(0, DataTypeUtil_1.isObject)(options)) {
msg = (0, LogMessageUtil_1.buildMessage)(log_messages_1.ErrorLogMessagesEnum.INIT_OPTIONS_ERROR, {
date: date,
});
console.error(msg); // Ensures options is an object.
}
if (!(options === null || options === void 0 ? void 0 : options.sdkKey) || !(0, DataTypeUtil_1.isString)(options === null || options === void 0 ? void 0 : options.sdkKey)) {
msg = (0, LogMessageUtil_1.buildMessage)(log_messages_1.ErrorLogMessagesEnum.INIT_OPTIONS_SDK_KEY_ERROR, {
date: date,
});
console.error(msg); // Validates sdkKey presence and type.
}
if (!options.accountId) {
msg = (0, LogMessageUtil_1.buildMessage)(log_messages_1.ErrorLogMessagesEnum.INIT_OPTIONS_ACCOUNT_ID_ERROR, {
date: date,
});
console.error(msg); // Validates accountId presence and type.
}
if (true) {
options.platform = PlatformEnum_1.PlatformEnum.CLIENT;
}
else {}
instance = new VWO(options);
_global = {
vwoInitDeferred: new PromiseUtil_1.Deferred(),
isSettingsFetched: false,
instance: null,
};
return [2 /*return*/, instance.then(function (_vwoInstance) {
_global.isSettingsFetched = true;
_global.instance = _vwoInstance;
_global.vwoInitDeferred.resolve(_vwoInstance);
return _vwoInstance;
})];
}
catch (err) {
msg = (0, LogMessageUtil_1.buildMessage)(log_messages_1.ErrorLogMessagesEnum.API_THROW_ERROR, {
apiName: apiName,
err: err,
});
console.info("[INFO]: VWO-SDK ".concat(new Date().toISOString(), " ").concat(msg));
}
return [2 /*return*/];
});
});
}
function onInit() {
return __awaiter(this, void 0, void 0, function () {
var apiName, date_1, msg, msg;
return __generator(this, function (_a) {
apiName = ApiEnum_1.ApiEnum.ON_INIT;
try {
_global.vwoInitDeferred = new PromiseUtil_1.Deferred();
date_1 = new Date().toISOString();
// If settings are already fetched, resolve the promise
if (_global.isSettingsFetched) {
msg = (0, LogMessageUtil_1.buildMessage)(log_messages_1.InfoLogMessagesEnum.ON_INIT_ALREADY_RESOLVED, {
date: date_1,
apiName: apiName,
});
console.info(msg);
_global.vwoInitDeferred.resolve(_global.instance);
}
else {
// wait for five seconds, else reject the promise
setTimeout(function () {
if (_global.isSettingsFetched) {
return;
}
var msg = (0, LogMessageUtil_1.buildMessage)(log_messages_1.InfoLogMessagesEnum.ON_INIT_SETTINGS_FAILED, {
date: date_1,
});
console.error(msg);
_global.vwoInitDeferred.reject(new Error('VWO settings could not be fetched'));
}, 5000);
}
return [2 /*return*/, _global.vwoInitDeferred.promise];
}
catch (err) {
msg = (0, LogMessageUtil_1.buildMessage)(log_messages_1.ErrorLogMessagesEnum.API_THROW_ERROR, {
apiName: apiName,
err: err,
});
console.info("[INFO]: VWO-SDK ".concat(new Date().toISOString(), " ").concat(msg));
}
return [2 /*return*/];
});
});
}
/***/ }),
/***/ "./lib/VWOBuilder.ts":
/*!***************************!*\
!*** ./lib/VWOBuilder.ts ***!
\***************************/
/***/ (function(__unused_webpack_module, exports, __webpack_require__) {
"use strict";
var __assign = (this && this.__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 }));
exports.VWOBuilder = void 0;
var logger_1 = __webpack_require__(/*! ./packages/logger */ "./lib/packages/logger/index.ts");
var network_layer_1 = __webpack_require__(/*! ./packages/network-layer */ "./lib/packages/network-layer/index.ts");
var segmentation_evaluator_1 = __webpack_require__(/*! ./packages/segmentation-evaluator */ "./lib/packages/segmentation-evaluator/index.ts");
var storage_1 = __webpack_require__(/*! ./packages/storage */ "./lib/packages/storage/index.ts");
var VWOClient_1 = __webpack_require__(/*! ./VWOClient */ "./lib/VWOClient.ts");
var SettingsService_1 = __webpack_require__(/*! ./services/SettingsService */ "./lib/services/SettingsService.ts");
var log_messages_1 = __webpack_require__(/*! ./enums/log-messages */ "./lib/enums/log-messages/index.ts");
var DataTypeUtil_1 = __webpack_require__(/*! ./utils/DataTypeUtil */ "./lib/utils/DataTypeUtil.ts");
var FunctionUtil_1 = __webpack_require__(/*! ./utils/FunctionUtil */ "./lib/utils/FunctionUtil.ts");
var LogMessageUtil_1 = __webpack_require__(/*! ./utils/LogMessageUtil */ "./lib/utils/LogMessageUtil.ts");
var PromiseUtil_1 = __webpack_require__(/*! ./utils/PromiseUtil */ "./lib/utils/PromiseUtil.ts");
var SettingsUtil_1 = __webpack_require__(/*! ./utils/SettingsUtil */ "./lib/utils/SettingsUtil.ts");
var UuidUtil_1 = __webpack_require__(/*! ./utils/UuidUtil */ "./lib/utils/UuidUtil.ts");
var BatchEventsQueue_1 = __webpack_require__(/*! ./services/BatchEventsQueue */ "./lib/services/BatchEventsQueue.ts");
var BatchEventsDispatcher_1 = __webpack_require__(/*! ./utils/BatchEventsDispatcher */ "./lib/utils/BatchEventsDispatcher.ts");
var UsageStatsUtil_1 = __webpack_require__(/*! ./utils/UsageStatsUtil */ "./lib/utils/UsageStatsUtil.ts");
var VWOBuilder = /** @class */ (function () {
function VWOBuilder(options) {
this.options = options;
}
/**
* Sets the network manager with the provided client and development mode options.
* @returns {this} The instance of this builder.
*/
VWOBuilder.prototype.setNetworkManager = function () {
var _a, _b, _c, _d;
var networkInstance = network_layer_1.NetworkManager.Instance;
// Attach the network client from options
networkInstance.attachClient((_b = (_a = this.options) === null || _a === void 0 ? void 0 : _a.network) === null || _b === void 0 ? void 0 : _b.client, (_c = this.options) === null || _c === void 0 ? void 0 : _c.retryConfig);
logger_1.LogManager.Instance.debug((0, LogMessageUtil_1.buildMessage)(log_messages_1.DebugLogMessagesEnum.SERVICE_INITIALIZED, {
service: "Network Layer",
}));
// Set the development mode based on options
networkInstance.getConfig().setDevelopmentMode((_d = this.options) === null || _d === void 0 ? void 0 : _d.isDevelopmentMode);
return this;
};
VWOBuilder.prototype.initBatching = function () {
var _this = this;
if (this.options.batchEventData) {
if (this.settingFileManager.isGatewayServiceProvided) {
logger_1.LogManager.Instance.info((0, LogMessageUtil_1.buildMessage)(log_messages_1.InfoLogMessagesEnum.GATEWAY_AND_BATCH_EVENTS_CONFIG_MISMATCH));
return this;
}
if ((!(0, DataTypeUtil_1.isNumber)(this.options.batchEventData.eventsPerRequest) ||
this.options.batchEventData.eventsPerRequest <= 0) &&
(!(0, DataTypeUtil_1.isNumber)(this.options.batchEventData.requestTimeInterval) ||
this.options.batchEventData.requestTimeInterval <= 0)) {
logger_1.LogManager.Instance.error('Invalid batch events config, should be an object, eventsPerRequest should be a number greater than 0 and requestTimeInterval should be a number greater than 0');
return this;
}
this.batchEventsQueue = new BatchEventsQueue_1.BatchEventsQueue(Object.assign({}, this.options.batchEventData, {
dispatcher: function (events, callback) {
return BatchEventsDispatcher_1.BatchEventsDispatcher.dispatch({
ev: events,
}, callback, Object.assign({}, {
a: _this.options.accountId,
env: _this.options.sdkKey,
}));
},
}));
this.batchEventsQueue.flushAndClearTimer.bind(this.batchEventsQueue);
}
return this;
};
/**
* Sets the segmentation evaluator with the provided segmentation options.
* @returns {this} The instance of this builder.
*/
VWOBuilder.prototype.setSegmentation = function () {
var _a;
segmentation_evaluator_1.SegmentationManager.Instance.attachEvaluator((_a = this.options) === null || _a === void 0 ? void 0 : _a.segmentation);
logger_1.LogManager.Instance.debug((0, LogMessageUtil_1.buildMessage)(log_messages_1.DebugLogMessagesEnum.SERVICE_INITIALIZED, {
service: "Segmentation Evaluator",
}));
return this;
};
/**
* Fetches settings asynchronously, ensuring no parallel fetches.
* @param {boolean} [force=false] - Force fetch ignoring cache.
* @returns {Promise<SettingsModel>} A promise that resolves to the fetched settings.
*/
VWOBuilder.prototype.fetchSettings = function (force) {
var _this = this;
var deferredObject = new PromiseUtil_1.Deferred();
// Check if a fetch operation is already in progress
if (!this.isSettingsFetchInProgress) {
this.isSettingsFetchInProgress = true;
this.settingFileManager.getSettings(force).then(function (settings) {
// if force is false, update original settings, if true the request is from polling and no need to update original settings
if (!force) {
_this.originalSettings = settings;
}
_this.isSettingsFetchInProgress = false;
deferredObject.resolve(settings);
});
return deferredObject.promise;
}
else {
// Avoid parallel fetches by recursively calling fetchSettings
return this.fetchSettings(force);
}
};
/**
* Gets the settings, fetching them if not cached or if forced.
* @param {boolean} [force=false] - Force fetch ignoring cache.
* @returns {Promise<SettingsModel>} A promise that resolves to the settings.
*/
VWOBuilder.prototype.getSettings = function (force) {
var deferredObject = new PromiseUtil_1.Deferred();
try {
// Use cached settings if available and not forced to fetch
if (!force && this.settings) {
logger_1.LogManager.Instance.info('Using already fetched and cached settings');
deferredObject.resolve(this.settings);
}
else {
// Fetch settings if not cached or forced
this.fetchSettings(force).then(function (settings) {
deferredObject.resolve(settings);
});
}
}
catch (err) {
logger_1.LogManager.Instance.error('Failed to fetch settings. Error: ' + err);
deferredObject.resolve({});
}
return deferredObject.promise;
};
/**
* Sets the storage connector based on the provided storage options.
* @returns {this} The instance of this builder.
*/
VWOBuilder.prototype.setStorage = function () {
var _a, _b, _c, _d;
if (this.options.storage) {
// Attach the storage connector from options
this.storage = storage_1.Storage.Instance.attachConnector(this.options.storage);
}
else if ( true && typeof window !== 'undefined' && window.localStorage) {
// eslint-disable-next-line @typescript-eslint/no-var-requires
var BrowserStorageConnector = (__webpack_require__(/*! ./packages/storage/connectors/BrowserStorageConnector */ "./lib/packages/storage/connectors/BrowserStorageConnector.ts").BrowserStorageConnector);
// Pass clientStorage config to BrowserStorageConnector
this.storage = storage_1.Storage.Instance.attachConnector(new BrowserStorageConnector(__assign(__assign({}, this.options.clientStorage), { alwaysUseCachedSettings: (_a = this.options.clientStorage) === null || _a === void 0 ? void 0 : _a.alwaysUseCachedSettings, ttl: (_b = this.options.clientStorage) === null || _b === void 0 ? void 0 : _b.ttl })));
logger_1.LogManager.Instance.debug((0, LogMessageUtil_1.buildMessage)(log_messages_1.DebugLogMessagesEnum.SERVICE_INITIALIZED, {
service: ((_d = (_c = this.options) === null || _c === void 0 ? void 0 : _c.clientStorage) === null || _d === void 0 ? void 0 : _d.provider) === sessionStorage ? "Session Storage" : "Local Storage",
}));
}
else {
// Set storage to null if no storage options provided
this.storage = null;
}
return this;
};
/**
* Sets the settings manager with the provided options.
* @returns {this} The instance of this builder.
*/
VWOBuilder.prototype.setSettingsService = function () {
this.settingFileManager = new SettingsService_1.SettingsService(this.options);
return this;
};
/**
* Sets the logger with the provided logger options.
* @returns {this} The instance of this builder.
*/
VWOBuilder.prototype.setLogger = function () {
this.logManager = new logger_1.LogManager(this.options.logger || {});
logger_1.LogManager.Instance.debug((0, LogMessageUtil_1.buildMessage)(log_messages_1.DebugLogMessagesEnum.SERVICE_INITIALIZED, {
service: "Logger",
}));
return this;
};
/**
* Sets the analytics callback with the provided analytics options.
* @returns {this} The instance of this builder.
*/
/* setAnalyticsCallback(): this {
if (!isObject(this.options.analyticsEvent)) {
// TODO: add logging here
return this;
}
if (!isFunction(this.options.analyticsEvent.eventCallback)) {
// TODO: add logging here
return this;
}
if (
this.options.analyticsEvent.isBatchingSupported &&
!isBoolean(this.options.analyticsEvent.isBatchingSupported)
) {
// TODO:- add logging here
return this;
}
// AnalyticsEvent.Instance.attachCallback(
// this.options.analyticsEvent.eventCallback,
// this.options.analyticsEvent.isBatchingSupported
// );
return this;
} */
/**
* Generates a random user ID based on the provided API key.
* @returns {string} The generated random user ID.
*/
VWOBuilder.prototype.getRandomUserId = function () {
var apiName = 'getRandomUserId';
try {
logger_1.LogManager.Instance.debug((0, LogMessageUtil_1.buildMessage)(log_messages_1.DebugLogMessagesEnum.API_CALLED, {
apiName: apiName,
}));
return (0, UuidUtil_1.getRandomUUID)(this.options.sdkKey);
}
catch (err) {
logger_1.LogManager.Instance.error((0, LogMessageUtil_1.buildMessage)(log_messages_1.ErrorLogMessagesEnum.API_THROW_ERROR, {
apiName: apiName,
err: err,
}));
}
};
/**
* Initializes the batching with the provided batch events options.
* @returns {this} The instance of this builder.
*/
/* initBatching(): this {
if (!isObject(this.options.batchEvents)) {
// TODO:- add logging here
return this;
}
if (
isObject(this.options.batchEvents) &&
(!(
(isNumber(this.options.batchEvents.eventsPerRequest) &&
this.options.batchEvents.eventsPerRequest > 0 &&
this.options.batchEvents.eventsPerRequest <= Constants.MAX_EVENTS_PER_REQUEST) ||
(isNumber(this.options.batchEvents.requestTimeInterval) && this.options.batchEvents.requestTimeInterval >= 1)
) ||
!isFunction(this.options.batchEvents.flushCallback))
) {
LogManager.Instance.error('Invalid batchEvents config');
// throw new Error('Invalid batchEvents config');
return this;
}
// BatchEventsQueue.Instance.setBatchConfig(this.options.batchEvents, this.options.sdkKey); // TODO
return this;
} */
/**
* Initializes the polling with the provided poll interval.
* @returns {this} The instance of this builder.
*/
VWOBuilder.prototype.initPolling = function () {
if (!this.options.pollInterval) {
return this;
}
if (this.options.pollInterval && !(0, DataTypeUtil_1.isNumber)(this.options.pollInterval)) {
logger_1.LogManager.Instance.error((0, LogMessageUtil_1.buildMessage)(log_messages_1.ErrorLogMessagesEnum.INIT_OPTIONS_INVALID, {
key: 'pollInterval',
correctType: 'number',
}));
return this;
}
if (this.options.pollInterval && this.options.pollInterval < 1000) {
logger_1.LogManager.Instance.error((0, LogMessageUtil_1.buildMessage)(log_messages_1.ErrorLogMessagesEnum.INIT_OPTIONS_INVALID, {
key: 'pollInterval',
correctType: 'number',
}));
return this;
}
this.checkAndPoll();
return this;
};
/**
* Initializes usage statistics for the SDK.
* @returns {this} The instance of this builder.
*/
VWOBuilder.prototype.initUsageStats = function () {
if (this.options.isUsageStatsDisabled) {
return this;
}
UsageStatsUtil_1.UsageStatsUtil.getInstance().setUsageStats(this.options);
return this;
};
/**
* Builds a new VWOClient instance with the provided settings.
* @param {SettingsModel} settings - The settings for the VWOClient.
* @returns {VWOClient} The new VWOClient instance.
*/
VWOBuilder.prototype.build = function (settings) {
this.vwoInstance = new VWOClient_1.VWOClient(settings, this.options);
return this.vwoInstance;
};
/**
* Checks and polls for settings updates at the provided interval.
*/
VWOBuilder.prototype.checkAndPoll = function () {
var _this = this;
var pollingInterval = this.options.pollInterval;
setInterval(function () {
_this.getSettings(true)
.then(function (latestSettings) {
var lastSettings = JSON.stringify(_this.originalSettings);
var stringifiedLatestSettings = JSON.stringify(latestSettings);
if (stringifiedLatestSettings !== lastSettings) {
_this.originalSettings = latestSettings;
var clonedSettings = (0, FunctionUtil_1.cloneObject)(latestSettings);
logger_1.LogManager.Instance.info(log_messages_1.InfoLogMessagesEnum.POLLING_SET_SETTINGS);
(0, SettingsUtil_1.setSettingsAndAddCampaignsToRules)(clonedSettings, _this.vwoInstance);
}
else {
logger_1.LogManager.Instance.info(log_messages_1.InfoLogMessagesEnum.POLLING_NO_CHANGE_IN_SETTINGS);
}
})
.catch(function () {
logger_1.LogManager.Instance.error(log_messages_1.ErrorLogMessagesEnum.POLLING_FETCH_SETTINGS_FAILED);
});
}, pollingInterval);
};
return VWOBuilder;
}());
exports.VWOBuilder = VWOBuilder;
/***/ }),
/***/ "./lib/VWOClient.ts":
/*!**************************!*\
!*** ./lib/VWOClient.ts ***!
\**************************/
/***/ (function(__unused_webpack_module, exports, __webpack_require__) {
"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
var __generator = (this && this.__generator) || function (thisArg, body) {
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype);
return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
function verb(n) { return function (v) { return step([n, v]); }; }
function step(op) {
if (f) throw new TypeError("Generator is already executing.");
while (g && (g = 0, op[0] && (_ = 0)), _) try {
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
if (y = 0, t) op = [op[0] & 2, t.value];
switch (op[0]) {
case 0: case 1: t = op; break;
case 4: _.label++; return { value: op[1], done: false };
case 5: _.label++; y = op[1]; op = [0]; continue;
case 7: op = _.ops.pop(); _.trys.pop(); continue;
default:
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
if (t[2]) _.ops.pop();
_.trys.pop(); continue;
}
op = body.call(thisArg, _);
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
}
};
Object.defineProperty(exports, "__esModule", ({ value: true }));
exports.VWOClient = void 0;
/**
* Copyright 2024-2025 Wingify Software Pvt. Ltd.
*
* 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_1 = __webpack_require__(/*! ./packages/logger */ "./lib/packages/logger/index.ts");
var GetFlag_1 = __webpack_require__(/*! ./api/GetFlag */ "./lib/api/GetFlag.ts");
var SetAttribute_1 = __webpack_require__(/*! ./api/SetAttribute */ "./lib/api/SetAttribute.ts");
var TrackEvent_1 = __webpack_require__(/*! ./api/TrackEvent */ "./lib/api/TrackEvent.ts");
var log_messages_1 = __webpack_require__(/*! ./enums/log-messages */ "./lib/enums/log-messages/index.ts");
var BatchEventsQueue_1 = __webpack_require__(/*! ./services/BatchEventsQueue */ "./lib/services/BatchEventsQueue.ts");
var SettingsSchemaValidation_1 = __webpack_require__(/*! ./models/schemas/SettingsSchemaValidation */ "./lib/models/schemas/SettingsSchemaValidation.ts");
var ContextModel_1 = __webpack_require__(/*! ./models/user/ContextModel */ "./lib/models/user/ContextModel.ts");
var HooksService_1 = __webpack_require__(/*! ./services/HooksService */ "./lib/services/HooksService.ts");
var UrlUtil_1 = __webpack_require__(/*! ./utils/UrlUtil */ "./lib/utils/UrlUtil.ts");
var DataTypeUtil_1 = __webpack_require__(/*! ./utils/DataTypeUtil */ "./lib/utils/DataTypeUtil.ts");
var LogMessageUtil_1 = __webpack_require__(/*! ./utils/LogMessageUtil */ "./lib/utils/LogMessageUtil.ts");
var PromiseUtil_1 = __webpack_require__(/*! ./utils/PromiseUtil */ "./lib/utils/PromiseUtil.ts");
var SettingsUtil_1 = __webpack_require__(/*! ./utils/SettingsUtil */ "./lib/utils/SettingsUtil.ts");
var VariationModel_1 = __webpack_require__(/*! ./models/campaign/VariationModel */ "./lib/models/campaign/VariationModel.ts");
var NetworkUtil_1 = __webpack_require__(/*! ./utils/NetworkUtil */ "./lib/utils/NetworkUtil.ts");
var SettingsService_1 = __webpack_require__(/*! ./services/SettingsService */ "./lib/services/SettingsService.ts");
var ApiEnum_1 = __webpack_require__(/*! ./enums/ApiEnum */ "./lib/enums/ApiEnum.ts");
var VWOClient = /** @class */ (function () {
function VWOClient(settings, options) {
this.options = options;
(0, SettingsUtil_1.setSettingsAndAddCampaignsToRules)(settings, this);
UrlUtil_1.UrlUtil.init({
collectionPrefix: this.settings.getCollectionPrefix(),
});
(0, NetworkUtil_1.setShouldWaitForTrackingCalls)(this.options.shouldWaitForTrackingCalls || false);
logger_1.LogManager.Instance.info(log_messages_1.InfoLogMessagesEnum.CLIENT_INITIALIZED);
this.vwoClientInstance = this;
return this;
}
/**
* Retrieves the value of a feature flag for a given feature key and context.
* This method validates the feature key and context, ensures the settings are valid, and then uses the FlagApi to get the flag value.
*
* @param {string} featureKey - The key of the feature to retrieve.
* @param {ContextModel} context - The context in which the feature flag is being retrieved, must include a valid user ID.
* @returns {Promise<Flag>} - A promise that resolves to the feature flag value.
*/
VWOClient.prototype.getFlag = function (featureKey, context) {
var apiName = ApiEnum_1.ApiEnum.GET_FLAG;
var deferredObject = new PromiseUtil_1.Deferred();
var errorReturnSchema = new GetFlag_1.Flag(false, new VariationModel_1.VariationModel());
try {
var hooksService = new HooksService_1.default(this.options);
logger_1.LogManager.Instance.debug((0, LogMessageUtil_1.buildMessage)(log_messages_1.DebugLogMessagesEnum.API_CALLED, {
apiName: apiName,
}));
// Validate featureKey is a string
if (!(0, DataTypeUtil_1.isString)(featureKey)) {
logger_1.LogManager.Instance.error((0, LogMessageUtil_1.buildMessage)(log_messages_1.ErrorLogMessagesEnum.API_INVALID_PARAM, {
apiName: apiName,
key: 'featureKey',
type: (0, DataTypeUtil_1.getType)(featureKey),
correctType: 'string',
}));
throw new TypeError('TypeError: featureKey should be a string');
}
// Validate settings are loaded and valid
if (!new SettingsSchemaValidation_1.SettingsSchema().isSettingsValid(this.originalSettings)) {
logger_1.LogManager.Instance.error(log_messages_1.ErrorLogMessagesEnum.API_SETTING_INVALID);
throw new Error('TypeError: Invalid Settings');
}
// Validate user ID is present in context
if (!context || !context.id) {
logger_1.LogManager.Instance.error(log_messages_1.ErrorLogMessagesEnum.API_CONTEXT_INVALID);
throw new TypeError('TypeError: Invalid context');
}
var contextModel = new ContextModel_1.ContextModel().modelFromDictionary(context);
GetFlag_1.FlagApi.get(featureKey, this.settings, contextModel, hooksService)
.then(function (data) {
deferredObject.resolve(data);
})
.catch(function () {
deferredObject.resolve(errorReturnSchema);
});
}
catch (err) {
logger_1.LogManager.Instance.info((0, LogMessageUtil_1.buildMessage)(log_messages_1.ErrorLogMessagesEnum.API_THROW_ERROR, {
apiName: apiName,
err: err,
}));
deferredObject.resolve(errorReturnSchema);
}
return deferredObject.promise;
};
/**
* Tracks an event with specified properties and context.
* This method validates the types of the inputs and ensures the settings and user context are valid before proceeding.
*
* @param {string} eventName - The name of the event to track.
* @param {ContextModel} context - The context in which the event is being tracked, must include a valid user ID.
* @param {Record<string, dynamic>} eventProperties - The properties associated with the event.
* @returns {Promise<Record<string, boolean>>} - A promise that resolves to the result of the tracking operation.
*/
VWOClient.prototype.trackEvent = function (eventName, context, eventProperties) {
var _a;
if (eventProperties === void 0) { eventProperties = {}; }
var apiName = ApiEnum_1.ApiEnum.TRACK_EVENT;
var deferredObject = new PromiseUtil_1.Deferred();
try {
var hooksService = new HooksService_1.default(this.options);
// Log the API call
logger_1.LogManager.Instance.debug((0, LogMessageUtil_1.buildMessage)(log_messages_1.DebugLogMessagesEnum.API_CALLED, {
apiName: apiName,
}));
// Validate eventName is a string
if (!(0, DataTypeUtil_1.isString)(eventName)) {
logger_1.LogManager.Instance.error((0, LogMessageUtil_1.buildMessage)(log_messages_1.ErrorLogMessagesEnum.API_INVALID_PARAM, {
apiName: apiName,
key: 'eventName',
type: (0, DataTypeUtil_1.getType)(eventName),
correctType: 'string',
}));
throw new TypeError('TypeError: Event-name should be a string');
}
// Validate eventProperties is an object
if (!(0, DataTypeUtil_1.isObject)(eventProperties)) {
logger_1.LogManager.Instance.error((0, LogMessageUtil_1.buildMessage)(log_messages_1.ErrorLogMessagesEnum.API_INVALID_PARAM, {
apiName: apiName,
key: 'eventProperties',
type: (0, DataTypeUtil_1.getType)(eventProperties),
correctType: 'object',
}));
throw new TypeError('TypeError: eventProperties should be an object');
}
// Validate settings are loaded and valid
if (!new SettingsSchemaValidation_1.SettingsSchema().isSettingsValid(this.originalSettings)) {
logger_1.LogManager.Instance.error(log_messages_1.ErrorLogMessagesEnum.API_SETTING_INVALID);
throw new Error('TypeError: Invalid Settings');
}
// Validate user ID is present in context
if (!context || !context.id) {
logger_1.LogManager.Instance.error(log_messages_1.ErrorLogMessagesEnum.API_CONTEXT_INVALID);
throw new TypeError('TypeError: Invalid context');
}
var contextModel = new ContextModel_1.ContextModel().modelFromDictionary(context);
// Proceed with tracking the event
new TrackEvent_1.TrackApi()
.track(this.settings, eventName, contextModel, eventProperties, hooksService)
.then(function (data) {
deferredObject.resolve(data);
})
.catch(function () {
var _a;
deferredObject.resolve((_a = {}, _a[eventName] = false, _a));
});
}
catch (err) {
// Log any errors encountered during the operation
logger_1.LogManager.Instance.info((0, LogMessageUtil_1.buildMessage)(log_messages_1.ErrorLogMessagesEnum.API_THROW_ERROR, {
apiName: apiName,
err: err,
}));
deferredObject.resolve((_a = {}, _a[eventName] = false, _a));
}
return deferredObject.promise;
};
/**
* Sets an attribute or multiple attributes for a user in the provided context.
* This method validates the types of the inputs before proceeding with the API call.
* There are two cases handled:
* 1. When attributes are passed as a map (key-value pairs).
* 2. When a single attribute (key-value) is passed.
*
* @param {string | Record<string, boolean | string | number>} attributeOrAttributes - Either a single attribute key (string) and value (boolean | string | number),
* or a map of attributes with keys and values (boolean | string | number).
* @param {boolean | string | number | Record<string, any>} [attributeValueOrContext] - The value for the attribute in case of a single attribute, or the context when multiple attributes are passed.
* @param {Record<string, any>} [context] - The context which must include a valid user ID. This is required if multiple attributes are passed.
*/
VWOClient.prototype.setAttribute = function (attributeOrAttributes, attributeValueOrContext, context) {
return __awaiter(this, void 0, void 0, function () {
var apiName, attributes, contextModel, attributeKey, attributeValue, contextModel, attributeMap, err_1;
var _a;
return __generator(this, function (_b) {
switch (_b.label) {
case 0:
apiName = ApiEnum_1.ApiEnum.SET_ATTRIBUTE;
_b.label = 1;
case 1:
_b.trys.push([1, 6, , 7]);
if (!(0, DataTypeUtil_1.isObject)(attributeOrAttributes)) return [3 /*break*/, 3];
// Log the API call
logger_1.LogManager.Instance.debug((0, LogMessageUtil_1.buildMessage)(log_messages_1.DebugLogMessagesEnum.API_CALLED, {
apiName: apiName,
}));
if (Object.entries(attributeOrAttributes).length < 1) {
logger_1.LogManager.Instance.error((0, LogMessageUtil_1.buildMessage)('Attributes map must contain atleast 1 key-value pair', {
apiName: apiName,
key: 'attributes',
type: (0, DataTypeUtil_1.getType)(attributeOrAttributes),
correctType: 'object',
}));
throw new TypeError('TypeError: Attributes should be an object containing atleast 1 key-value pair');
}
attributes = attributeOrAttributes;
// Validate attributes is an object
if (!(0, DataTypeUtil_1.isObject)(attributes)) {
throw new TypeError('TypeError: attributes should be an object containing key-value pairs');
}
// Validate that each attribute value is of a supported type
Object.entries(attributes).forEach(function (_a) {
var key = _a[0], value = _a[1];
if (typeof value !== 'boolean' && typeof value !== 'string' && typeof value !== 'number') {
logger_1.LogManager.Instance.error((0, LogMessageUtil_1.buildMessage)(log_messages_1.ErrorLogMessagesEnum.API_INVALID_PARAM, {
apiName: apiName,
key: key,
type: (0, DataTypeUtil_1.getType)(value),
correctType: ' boolean, string or number',
}));
throw new TypeError("Invalid attribute type for key \"".concat(key, "\". Expected boolean, string or number, but got ").concat((0, DataTypeUtil_1.getType)(value)));
}
// Reject arrays and objects explicitly
if (Array.isArray(value) || (typeof value === 'object' && value !== null)) {
logger_1.LogManager.Instance.error((0, LogMessageUtil_1.buildMessage)(log_messages_1.ErrorLogMessagesEnum.API_INVALID_PARAM, {
apiName: apiName,
key: key,
type: (0, DataTypeUtil_1.getType)(value),
correctType: ' boolean | string | number | null',
}));
throw new TypeError("Invalid attribute value for key \"".concat(key, "\". Arrays and objects are not supported."));
}
});
// If we have only two arguments (attributeMap and context)
if (!context && attributeValueOrContext) {
context = attributeValueOrContext; // Assign context explicitly
}
// Validate user ID is present in context
if (!context || !context.id) {
logger_1.LogManager.Instance.error(log_messages_1.ErrorLogMessagesEnum.API_CONTEXT_INVALID);
}
contextModel = new ContextModel_1.ContextModel().modelFromDictionary(context);
// Proceed with setting the attributes if validation is successful
return [4 /*yield*/, new SetAttribute_1.SetAttributeApi().setAttribute(this.settings, attributes, contextModel)];
case 2:
// Proceed with setting the attributes if validation is successful
_b.sent();
return [3 /*break*/, 5];
case 3:
attributeKey = attributeOrAttributes;
attributeValue = attributeValueOrContext;
// Validate attributeKey is a string
if (!(0, DataTypeUtil_1.isString)(attributeKey)) {
throw new TypeError('attributeKey should be a string');
}
// Validate attributeValue is of valid type
if (!(0, DataTypeUtil_1.isBoolean)(attributeValue) && !(0, DataTypeUtil_1.isString)(attributeValue) && !(0, DataTypeUtil_1.isNumber)(attributeValue)) {
throw new TypeError('attributeValue should be a boolean, string, or number');
}
// Validate user ID is present in context
if (!context || !context.id) {
throw new TypeError('Invalid context');
}
contextModel = new ContextModel_1.ContextModel().modelFromDictionary(context);
attributeMap = (_a = {}, _a[attributeKey] = attributeValue, _a);
// Proceed with setting the attribute map if validation is successful
return [4 /*yield*/, new SetAttribute_1.SetAttributeApi().setAttribute(this.settings, attributeMap, contextModel)];
case 4:
// Proceed with setting the attribute map if validation is successful
_b.sent();
_b.label = 5;
case 5: return [3 /*break*/, 7];