diff --git a/.babelrc b/.babelrc index 6034133e..f82b4985 100644 --- a/.babelrc +++ b/.babelrc @@ -6,9 +6,8 @@ }] ], "plugins": [ - "es6-promise", "@babel/plugin-transform-flow-comments", - ["@babel/plugin-transform-runtime", {"version": "7.18.10"}] + ["@babel/plugin-transform-runtime", {"version": "7.24.3"}] ], "overrides": [ { @@ -18,9 +17,6 @@ ["@babel/preset-env", { "targets": ">0.25%, not dead, ie 11" }] - ], - "plugins": [ - "@babel/plugin-proposal-class-properties" ] } ] diff --git a/.eslintrc b/.eslintrc index ddfefbf3..7b38aa48 100644 --- a/.eslintrc +++ b/.eslintrc @@ -13,7 +13,9 @@ "eslint:recommended", "plugin:flowtype/recommended" ], - "plugins": ["flowtype"], + "plugins": [ + "flowtype" + ], "globals": { "Promise": true, "__ADJUST__NAMESPACE": true, @@ -21,9 +23,10 @@ "Utils": true }, "rules": { - "semi": ["error", "never"], - "space-before-function-paren": ["error", "always"], - "quotes": ["error", "single"], + "quotes": [ + "error", + "single" + ], "prefer-arrow-callback": "error", "prefer-object-spread": "error", "flowtype/no-types-missing-file-annotation": "off" @@ -35,20 +38,17 @@ "plugin:@typescript-eslint/eslint-recommended", "plugin:@typescript-eslint/recommended" ], - "files": ["./src/**/*.ts"], + "files": [ + "./src/**/*.ts" + ], "parser": "@typescript-eslint/parser", "parserOptions": { "project": "./tsconfig.json" }, - "plugins": ["@typescript-eslint"], + "plugins": [ + "@typescript-eslint" + ], "rules": { - "semi": ["error", "never"], - "space-before-function-paren": ["error", { - "anonymous": "always", - "named": "never", - "asyncArrow": "always" - } - ], "eol-last": "error", "@typescript-eslint/ban-types": "warn", "@typescript-eslint/explicit-module-boundary-types": "off", @@ -56,12 +56,18 @@ } }, { - "extends": ["plugin:jest/style"], - "files": ["./src/**/*.spec.*"], + "extends": [ + "plugin:jest/style" + ], + "files": [ + "./src/**/*.spec.*" + ], "env": { "jest": true }, - "plugins": ["jest"], + "plugins": [ + "jest" + ], "rules": { "jest/prefer-to-have-length": "off", "jest/no-disabled-tests": "error", diff --git a/CHANGELOG.md b/CHANGELOG.md index e216da0c..da592d9b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,15 @@ +### Version 5.7.0 (16th August 2024) +#### Added +- Added asynchronous function `waitForWebUUID`, returning a Promise which resolves when `web_uuid` becomes available. +- Added asynchronous function `waitForAttribution`, returning a Promise which resolves when attribution data received from Adjust Backend. + +#### Changed +- [UrlStrategy reworked](https://dev.adjust.com/en/sdk/web/features/privacy#url-strategy). +- [Third Party Sharing reworked](https://dev.adjust.com/en/sdk/web/features/privacy). +- Outdated Smart Banners removed. + +--- + ### Version 5.6.0 (30th January 2023) #### Added - Added a return of Promise from `trackEvent` method. diff --git a/Jenkinsfile b/Jenkinsfile index deb6846c..a0f95f4b 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -2,7 +2,7 @@ pipeline { tools { - nodejs 'nodejs_14.20.0' + nodejs 'nodejs_20.11.1' } agent { node { diff --git a/VERSION b/VERSION index 1bc788d3..42cdd0b5 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -5.6.0 +5.7.0 diff --git a/dist/INTEGRITY b/dist/INTEGRITY index db7b9441..25f3862b 100644 --- a/dist/INTEGRITY +++ b/dist/INTEGRITY @@ -1 +1 @@ -sha384-BqbTn9xyk5DPznti1ZP8ksxKiOFhKufLBFWm5eNMCnZABFSG1eqQfcu5dsiZJHu5 \ No newline at end of file +sha384-WE6fJqvcE0mjcC/fPYooIQAOhsHpTDVYjC52i6Asn3LECEpjHyI5zUwD8/5esthg \ No newline at end of file diff --git a/dist/adjust-latest.d.ts b/dist/adjust-latest.d.ts index 2a299987..ca663a2b 100644 --- a/dist/adjust-latest.d.ts +++ b/dist/adjust-latest.d.ts @@ -99,6 +99,33 @@ declare namespace Adjust { state: string } + /** + * @deprecated + */ + type UrlStartegyLiterals = 'china' | 'india'; + + interface UrlStrategyConfig { + /** The country or countries of data residence, or the endpoints to which you want to send SDK traffic. */ + domains: Array; + + /** Whether the source should prefix a subdomain. */ + useSubdomains: boolean; + + /** Whether the domain should be used for data residency. */ + isDataResidency?: boolean; + } + + interface ThirdPartySharingOptions { + isEnabled: boolean; + granularOptions: Record>; + partnerSharingSettings: Record>; + } + + class ThirdPartySharing implements ThirdPartySharingOptions { + public addGranularOption(partnerName: string, key: string, value: string) + public addPartnerSharingSetting(partnerName: string, key: string, value: boolean) + } + interface InitOptions { /** Required to initialise SDK instance, please make sure to provide valid app token. */ @@ -122,19 +149,26 @@ declare namespace Adjust { eventDeduplicationListLimit?: number; /** Optional. By default all requests go to Adjust's endpoints. You are able to redirect all requests to your custom - * endpoint. */ + * endpoint. + * + * @deprecated use {@link urlStrategy} property instead + */ customUrl?: string; /** Optional. The data residency feature allows you to choose the country in which Adjust will store your data. This * is useful if you are operating in a country with strict privacy requirements. When you set up data residency, - * Adjust will store your data in a data center located in the region your have chosen. */ + * Adjust will store your data in a data center located in the region your have chosen. + * + * @deprecated use {@link urlStrategy} property instead + */ dataResidency?: 'EU' | 'TR' | 'US'; - /** Optional. The Adjust SDK can use the url strategy setting to prioritise regional endpoints. */ - urlStrategy?: 'india' | 'china'; + /** Optional. The URL strategy feature allows you to set either: + * - The country in which Adjust stores your data (data residency). + * - The endpoint to which the Adjust SDK sends traffic (URL strategy).*/ + urlStrategy?: UrlStartegyLiterals | UrlStrategyConfig; - /** - * Optional. A custom namespace for SDK data storage. If not set then default one is used. + /** Optional. A custom namespace for SDK data storage. If not set then default one is used. * It's useful when there are multiple applications on the same domain to allow SDK distinguish storages and don't * mix the data up. * @@ -169,8 +203,7 @@ declare namespace Adjust { */ logLevel?: LogLevel; - /** - * Optional. Query selector to define html container if you want to see your logs directly on the screen. This could + /** Optional. Query selector to define html container if you want to see your logs directly on the screen. This could * be useful when testing on mobile devices. * * @example @@ -206,9 +239,16 @@ declare namespace Adjust { * * @example * const attribution = Adjust.getAttribution(); + * + * @deprecated Use {@link waitForAttribution} instead */ function getAttribution(): Attribution | undefined + /** + * Returns a promise which resolves when current attribution information becomes available + */ + function waitForAttribution(): Promise + /** * Get web_uuid - a unique ID of user generated per subdomain and per browser * @@ -216,9 +256,16 @@ declare namespace Adjust { * * @example * const webUuid = Adjust.getWebUUID(); + * + * @deprecated Use {@link waitForWebUUID} instead */ function getWebUUID(): string | undefined + /** + * Returns a promise which resolves when `web_uuid` becomes available + */ + function waitForWebUUID(): Promise + /** * Set referrer manually. Please note that `referrer` should be URL-encoded. * @@ -369,59 +416,15 @@ declare namespace Adjust { * * Marketing Opt-out, which is disabling third-party sharing ability. This method will notify our backed in the same * manner as it does for GDPR Forget me. + * + * @deprecated Use {@link trackThirdPartySharing} instead */ function disableThirdPartySharing(): void - interface SmartBannerOptions { - - /** Web token to initialise Smart Banner */ - webToken: string; - - /** Optional. Logging level used by SDK instance. By default this param is set to `error`. We highly recommend that - * you use `verbose` when testing in order to see precise logs and to make sure integration is done properly. - * Here are more details about each log level: - * - `verbose` - will print detailed messages in case of certain actions - * - `info` - will print only basic info messages, warnings and errors - * - `warning` - will print only warning and error messages - * - `error` - will print only error message - * - `none` - won't print anything - */ - logLevel?: LogLevel; - - /** Optional. The data residency feature allows you to choose the country in which Adjust will store your data. This - * is useful if you are operating in a country with strict privacy requirements. When you set up data residency, - * Adjust will store your data in a data center located in the region your have chosen. */ - dataResidency?: 'EU' | 'TR' | 'US'; - - /** Optional. Callback which is called when SmartBanner view is created and shown. */ - onCreated?: () => any; - - /** Optional. Callback which is called when SmartBanner is being dismissed with a closing button on it. */ - onDismissed?: () => any; - } - /** - * Initiate Smart Banner. - * - * This method gets Smart Banner data and creates Smart Banner UI. - * - * @param {SmartBannerOptions} options Options to initiate Smart Banner. - * - * @example - * Adjust.initSmartBanner({ - * webToken: 'YOUR_WEB_TOKEN', - * logLevel: 'verbose' - * }); - * - * @example - * Adjust.initSmartBanner({ - * webToken: 'YOUR_WEB_TOKEN', - * logLevel: 'error', - * dataResidency: 'EU', - * }); + * Track third party sharing */ - function initSmartBanner(options: SmartBannerOptions): void - + function trackThirdPartySharing(adjustThirdPartySharing: ThirdPartySharingOptions): void } export default Adjust diff --git a/dist/adjust-latest.js b/dist/adjust-latest.js index f15722ff..05ca403f 100644 --- a/dist/adjust-latest.js +++ b/dist/adjust-latest.js @@ -9,7444 +9,5208 @@ root["Adjust"] = factory(); })(self, () => { return /******/ (() => { // webpackBootstrap -/******/ var __webpack_modules__ = ({ - -/***/ 841: -/***/ ((module, __webpack_exports__, __webpack_require__) => { - -"use strict"; -/* harmony export */ __webpack_require__.d(__webpack_exports__, { -/* harmony export */ "Z": () => (__WEBPACK_DEFAULT_EXPORT__) -/* harmony export */ }); -/* harmony import */ var _node_modules_css_loader_dist_runtime_noSourceMaps_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(81); -/* harmony import */ var _node_modules_css_loader_dist_runtime_noSourceMaps_js__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_node_modules_css_loader_dist_runtime_noSourceMaps_js__WEBPACK_IMPORTED_MODULE_0__); -/* harmony import */ var _node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(645); -/* harmony import */ var _node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(_node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1__); -/* harmony import */ var _node_modules_css_loader_dist_runtime_getUrl_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(667); -/* harmony import */ var _node_modules_css_loader_dist_runtime_getUrl_js__WEBPACK_IMPORTED_MODULE_2___default = /*#__PURE__*/__webpack_require__.n(_node_modules_css_loader_dist_runtime_getUrl_js__WEBPACK_IMPORTED_MODULE_2__); -// Imports - - - -var ___CSS_LOADER_URL_IMPORT_0___ = new URL(/* asset import */ __webpack_require__(529), __webpack_require__.b); -var ___CSS_LOADER_EXPORT___ = _node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1___default()((_node_modules_css_loader_dist_runtime_noSourceMaps_js__WEBPACK_IMPORTED_MODULE_0___default())); -var ___CSS_LOADER_URL_REPLACEMENT_0___ = _node_modules_css_loader_dist_runtime_getUrl_js__WEBPACK_IMPORTED_MODULE_2___default()(___CSS_LOADER_URL_IMPORT_0___); -// Module -___CSS_LOADER_EXPORT___.push([module.id, ".adjust-smart-banner__AEqYlWgPonspKfseFq2N{height:76px}@media(min-width: 428px){.adjust-smart-banner__AEqYlWgPonspKfseFq2N{height:0}}.adjust-smart-banner__NVk5vwju_4kdaKzGWJPq{position:fixed;left:0;right:0;z-index:10000000}.adjust-smart-banner__NVk5vwju_4kdaKzGWJPq.adjust-smart-banner__jOV7BvlxDT7ATfbLPh3j{top:0}.adjust-smart-banner__NVk5vwju_4kdaKzGWJPq.adjust-smart-banner__XmomYv1VVQYz0lEtn9Q2{bottom:0}.adjust-smart-banner__NVk5vwju_4kdaKzGWJPq .adjust-smart-banner__eXKzWnRDn4RWUiSSeVYK{margin:0 auto;max-width:428px;background:#fff}.adjust-smart-banner__NVk5vwju_4kdaKzGWJPq .adjust-smart-banner__eXKzWnRDn4RWUiSSeVYK .adjust-smart-banner__r3JnN_RNhpzArrmKQ8jI{display:flex;align-items:center;padding:10px 8px 10px 4px}.adjust-smart-banner__NVk5vwju_4kdaKzGWJPq .adjust-smart-banner__eXKzWnRDn4RWUiSSeVYK .adjust-smart-banner__r3JnN_RNhpzArrmKQ8jI .adjust-smart-banner__VFuxsD_KzqNSxQecFmao{width:32px;height:32px;border:none;background:url(" + ___CSS_LOADER_URL_REPLACEMENT_0___ + ");background-repeat:no-repeat;background-position:center center;background-size:8px 8px,auto;cursor:pointer}.adjust-smart-banner__NVk5vwju_4kdaKzGWJPq .adjust-smart-banner__eXKzWnRDn4RWUiSSeVYK .adjust-smart-banner__r3JnN_RNhpzArrmKQ8jI .adjust-smart-banner__hqvH8Y5fwbegVLKnoYv_{width:56px;height:56px;overflow:hidden;background-color:#6e7492;border-radius:8px}.adjust-smart-banner__NVk5vwju_4kdaKzGWJPq .adjust-smart-banner__eXKzWnRDn4RWUiSSeVYK .adjust-smart-banner__r3JnN_RNhpzArrmKQ8jI .adjust-smart-banner__hqvH8Y5fwbegVLKnoYv_ .adjust-smart-banner__Ll9XMTDiX4Drgeydp0Oc{display:flex;align-items:center;justify-content:center;width:100%;height:100%;color:#353a52;font-weight:bold;font-size:23px;font-family:ArialMt,Arial,sans-serif;line-height:32px;background-color:#e0e2ec}.adjust-smart-banner__NVk5vwju_4kdaKzGWJPq .adjust-smart-banner__eXKzWnRDn4RWUiSSeVYK .adjust-smart-banner__r3JnN_RNhpzArrmKQ8jI .adjust-smart-banner__hqvH8Y5fwbegVLKnoYv_ .adjust-smart-banner__VYRfEif2Ph2_984rXQy8{width:100%}.adjust-smart-banner__NVk5vwju_4kdaKzGWJPq .adjust-smart-banner__eXKzWnRDn4RWUiSSeVYK .adjust-smart-banner__r3JnN_RNhpzArrmKQ8jI .adjust-smart-banner__I8xX0C5dUcR53pY0aEys{flex:1 1 0%;min-height:0;min-width:0;margin:0 12px}.adjust-smart-banner__NVk5vwju_4kdaKzGWJPq .adjust-smart-banner__eXKzWnRDn4RWUiSSeVYK .adjust-smart-banner__r3JnN_RNhpzArrmKQ8jI .adjust-smart-banner__JJLdp2l7YvnsUXudojWA{overflow:hidden;text-overflow:ellipsis}.adjust-smart-banner__NVk5vwju_4kdaKzGWJPq .adjust-smart-banner__eXKzWnRDn4RWUiSSeVYK .adjust-smart-banner__r3JnN_RNhpzArrmKQ8jI h4{margin:5px 0 8px;color:#353a52;font-family:Arial-BoldMT,ArialMt,Arial,sans-serif;font-size:12px;font-weight:bold;line-height:16px;white-space:nowrap}.adjust-smart-banner__NVk5vwju_4kdaKzGWJPq .adjust-smart-banner__eXKzWnRDn4RWUiSSeVYK .adjust-smart-banner__r3JnN_RNhpzArrmKQ8jI p{margin:8px 0 7px;color:#353a52;font-family:ArialMt,Arial,sans-serif;font-size:9px;line-height:11px;max-height:22px;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical}.adjust-smart-banner__NVk5vwju_4kdaKzGWJPq .adjust-smart-banner__eXKzWnRDn4RWUiSSeVYK .adjust-smart-banner__r3JnN_RNhpzArrmKQ8jI .adjust-smart-banner__risKVvV3T0vjKiSTR9l0{color:#6e7492;background:#f9fafc;border:1px solid #cdd0e0;border-radius:4px;border-color:#6e7492;box-shadow:inset 0px -1px 0px 0px #e0e2ec;padding:4px 6.5px;display:inline-block;vertical-align:middle;text-align:center;font-family:ArialMt,Arial,sans-serif;font-size:12px;font-weight:500;line-height:16px;cursor:pointer;text-decoration:none}", ""]); -// Exports -___CSS_LOADER_EXPORT___.locals = { - "bannerContainer": "adjust-smart-banner__AEqYlWgPonspKfseFq2N", - "banner": "adjust-smart-banner__NVk5vwju_4kdaKzGWJPq", - "stickyToTop": "adjust-smart-banner__jOV7BvlxDT7ATfbLPh3j", - "stickyToBottom": "adjust-smart-banner__XmomYv1VVQYz0lEtn9Q2", - "bannerBody": "adjust-smart-banner__eXKzWnRDn4RWUiSSeVYK", - "content": "adjust-smart-banner__r3JnN_RNhpzArrmKQ8jI", - "dismiss": "adjust-smart-banner__VFuxsD_KzqNSxQecFmao", - "appIcon": "adjust-smart-banner__hqvH8Y5fwbegVLKnoYv_", - "placeholder": "adjust-smart-banner__Ll9XMTDiX4Drgeydp0Oc", - "image": "adjust-smart-banner__VYRfEif2Ph2_984rXQy8", - "textContainer": "adjust-smart-banner__I8xX0C5dUcR53pY0aEys", - "bannerText": "adjust-smart-banner__JJLdp2l7YvnsUXudojWA", - "action": "adjust-smart-banner__risKVvV3T0vjKiSTR9l0" -}; -/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (___CSS_LOADER_EXPORT___); - - -/***/ }), - -/***/ 645: -/***/ ((module) => { - -"use strict"; - - -/* - MIT License http://www.opensource.org/licenses/mit-license.php - Author Tobias Koppers @sokra -*/ -module.exports = function (cssWithMappingToString) { - var list = []; // return the list of modules as css string - - list.toString = function toString() { - return this.map(function (item) { - var content = ""; - var needLayer = typeof item[5] !== "undefined"; - - if (item[4]) { - content += "@supports (".concat(item[4], ") {"); - } - - if (item[2]) { - content += "@media ".concat(item[2], " {"); - } - - if (needLayer) { - content += "@layer".concat(item[5].length > 0 ? " ".concat(item[5]) : "", " {"); - } - - content += cssWithMappingToString(item); - - if (needLayer) { - content += "}"; - } - - if (item[2]) { - content += "}"; - } - - if (item[4]) { - content += "}"; - } - - return content; - }).join(""); - }; // import a list of modules into the list - - - list.i = function i(modules, media, dedupe, supports, layer) { - if (typeof modules === "string") { - modules = [[null, modules, undefined]]; - } - - var alreadyImportedModules = {}; - - if (dedupe) { - for (var k = 0; k < this.length; k++) { - var id = this[k][0]; - - if (id != null) { - alreadyImportedModules[id] = true; - } - } - } - - for (var _k = 0; _k < modules.length; _k++) { - var item = [].concat(modules[_k]); - - if (dedupe && alreadyImportedModules[item[0]]) { - continue; - } - - if (typeof layer !== "undefined") { - if (typeof item[5] === "undefined") { - item[5] = layer; - } else { - item[1] = "@layer".concat(item[5].length > 0 ? " ".concat(item[5]) : "", " {").concat(item[1], "}"); - item[5] = layer; - } - } - - if (media) { - if (!item[2]) { - item[2] = media; - } else { - item[1] = "@media ".concat(item[2], " {").concat(item[1], "}"); - item[2] = media; - } - } - - if (supports) { - if (!item[4]) { - item[4] = "".concat(supports); - } else { - item[1] = "@supports (".concat(item[4], ") {").concat(item[1], "}"); - item[4] = supports; - } - } - - list.push(item); - } - }; - - return list; -}; - -/***/ }), - -/***/ 667: -/***/ ((module) => { - -"use strict"; - +/******/ "use strict"; +/******/ // The require scope +/******/ var __webpack_require__ = {}; +/******/ +/************************************************************************/ +/******/ /* webpack/runtime/define property getters */ +/******/ (() => { +/******/ // define getter functions for harmony exports +/******/ __webpack_require__.d = (exports, definition) => { +/******/ for(var key in definition) { +/******/ if(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) { +/******/ Object.defineProperty(exports, key, { enumerable: true, get: definition[key] }); +/******/ } +/******/ } +/******/ }; +/******/ })(); +/******/ +/******/ /* webpack/runtime/hasOwnProperty shorthand */ +/******/ (() => { +/******/ __webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop)) +/******/ })(); +/******/ +/************************************************************************/ +var __webpack_exports__ = {}; -module.exports = function (url, options) { - if (!options) { - options = {}; - } +// EXPORTS +__webpack_require__.d(__webpack_exports__, { + "default": () => (/* binding */ main) +}); - if (!url) { - return url; - } +;// CONCATENATED MODULE: ./node_modules/@babel/runtime/helpers/esm/typeof.js +function _typeof(o) { + "@babel/helpers - typeof"; - url = String(url.__esModule ? url.default : url); // If url is already wrapped in quotes, remove them + return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { + return typeof o; + } : function (o) { + return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; + }, _typeof(o); +} +;// CONCATENATED MODULE: ./node_modules/@babel/runtime/helpers/esm/toPrimitive.js - if (/^['"].*['"]$/.test(url)) { - url = url.slice(1, -1); +function toPrimitive(t, r) { + if ("object" != _typeof(t) || !t) return t; + var e = t[Symbol.toPrimitive]; + if (void 0 !== e) { + var i = e.call(t, r || "default"); + if ("object" != _typeof(i)) return i; + throw new TypeError("@@toPrimitive must return a primitive value."); } + return ("string" === r ? String : Number)(t); +} +;// CONCATENATED MODULE: ./node_modules/@babel/runtime/helpers/esm/toPropertyKey.js - if (options.hash) { - url += options.hash; - } // Should url be wrapped? - // See https://drafts.csswg.org/css-values-3/#urls +function toPropertyKey(t) { + var i = toPrimitive(t, "string"); + return "symbol" == _typeof(i) ? i : i + ""; +} +;// CONCATENATED MODULE: ./node_modules/@babel/runtime/helpers/esm/defineProperty.js - if (/["'() \t\n]|(%20)/.test(url) || options.needQuotes) { - return "\"".concat(url.replace(/"/g, '\\"').replace(/\n/g, "\\n"), "\""); +function _defineProperty(obj, key, value) { + key = toPropertyKey(key); + if (key in obj) { + Object.defineProperty(obj, key, { + value: value, + enumerable: true, + configurable: true, + writable: true + }); + } else { + obj[key] = value; } - - return url; -}; - -/***/ }), - -/***/ 81: -/***/ ((module) => { - -"use strict"; - - -module.exports = function (i) { - return i[1]; -}; - -/***/ }), - -/***/ 702: -/***/ (function(module, __unused_webpack_exports, __webpack_require__) { - -/*! - * @overview es6-promise - a tiny implementation of Promises/A+. - * @copyright Copyright (c) 2014 Yehuda Katz, Tom Dale, Stefan Penner and contributors (Conversion to ES6 API by Jake Archibald) - * @license Licensed under MIT license - * See https://raw.githubusercontent.com/stefanpenner/es6-promise/master/LICENSE - * @version v4.2.8+1e68dce6 - */ - -(function (global, factory) { - true ? module.exports = factory() : - 0; -}(this, (function () { 'use strict'; - -function objectOrFunction(x) { - var type = typeof x; - return x !== null && (type === 'object' || type === 'function'); + return obj; } +;// CONCATENATED MODULE: ./node_modules/@babel/runtime/helpers/esm/objectSpread2.js -function isFunction(x) { - return typeof x === 'function'; +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 _objectSpread2(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; } - - - -var _isArray = void 0; -if (Array.isArray) { - _isArray = Array.isArray; -} else { - _isArray = function (x) { - return Object.prototype.toString.call(x) === '[object Array]'; - }; +;// CONCATENATED MODULE: ./node_modules/@babel/runtime/helpers/esm/objectWithoutPropertiesLoose.js +function _objectWithoutPropertiesLoose(source, excluded) { + if (source == null) return {}; + var target = {}; + var sourceKeys = Object.keys(source); + var key, i; + for (i = 0; i < sourceKeys.length; i++) { + key = sourceKeys[i]; + if (excluded.indexOf(key) >= 0) continue; + target[key] = source[key]; + } + return target; } +;// CONCATENATED MODULE: ./node_modules/@babel/runtime/helpers/esm/objectWithoutProperties.js -var isArray = _isArray; - -var len = 0; -var vertxNext = void 0; -var customSchedulerFn = void 0; - -var asap = function asap(callback, arg) { - queue[len] = callback; - queue[len + 1] = arg; - len += 2; - if (len === 2) { - // If len is 2, that means that we need to schedule an async flush. - // If additional callbacks are queued before the queue is flushed, they - // will be processed by this flush that we are scheduling. - if (customSchedulerFn) { - customSchedulerFn(flush); - } else { - scheduleFlush(); +function _objectWithoutProperties(source, excluded) { + if (source == null) return {}; + var target = _objectWithoutPropertiesLoose(source, excluded); + var key, i; + if (Object.getOwnPropertySymbols) { + var sourceSymbolKeys = Object.getOwnPropertySymbols(source); + for (i = 0; i < sourceSymbolKeys.length; i++) { + key = sourceSymbolKeys[i]; + if (excluded.indexOf(key) >= 0) continue; + if (!Object.prototype.propertyIsEnumerable.call(source, key)) continue; + target[key] = source[key]; } } -}; - -function setScheduler(scheduleFn) { - customSchedulerFn = scheduleFn; -} - -function setAsap(asapFn) { - asap = asapFn; + return target; } - -var browserWindow = typeof window !== 'undefined' ? window : undefined; -var browserGlobal = browserWindow || {}; -var BrowserMutationObserver = browserGlobal.MutationObserver || browserGlobal.WebKitMutationObserver; -var isNode = typeof self === 'undefined' && typeof process !== 'undefined' && {}.toString.call(process) === '[object process]'; - -// test for web worker but not in IE10 -var isWorker = typeof Uint8ClampedArray !== 'undefined' && typeof importScripts !== 'undefined' && typeof MessageChannel !== 'undefined'; - -// node -function useNextTick() { - // node version 0.10.x displays a deprecation warning when nextTick is used recursively - // see https://github.com/cujojs/when/issues/410 for details - return function () { - return process.nextTick(flush); - }; +;// CONCATENATED MODULE: ./src/sdk/constants.ts +var SECOND = 1000; +var MINUTE = SECOND * 60; +var HOUR = MINUTE * 60; +var DAY = HOUR * 24; +var DISABLE_REASONS = /*#__PURE__*/function (DISABLE_REASONS) { + DISABLE_REASONS["REASON_GENERAL"] = "general"; + DISABLE_REASONS["REASON_GDPR"] = "gdpr"; + return DISABLE_REASONS; +}({}); +var HTTP_ERRORS = { + 'TRANSACTION_ERROR': 'XHR transaction failed due to an error', + 'SERVER_MALFORMED_RESPONSE': 'Response from server is malformed', + 'SERVER_INTERNAL_ERROR': 'Internal error occurred on the server', + 'SERVER_CANNOT_PROCESS': 'Server was not able to process the request, probably due to error coming from the client', + 'NO_CONNECTION': 'No internet connectivity', + 'SKIP': 'Skipping slower attempt', + 'MISSING_URL': 'Url is not provided' +}; +var STORAGE_TYPES = /*#__PURE__*/function (STORAGE_TYPES) { + STORAGE_TYPES["NO_STORAGE"] = "noStorage"; + STORAGE_TYPES["INDEXED_DB"] = "indexedDB"; + STORAGE_TYPES["LOCAL_STORAGE"] = "localStorage"; + return STORAGE_TYPES; +}({}); +var ENDPOINTS = { + default: 'adjust.com', + india: 'adjust.net.in', + china: 'adjust.world', + world: 'adjust.world', + EU: 'eu.adjust.com', + TR: 'tr.adjust.com', + US: 'us.adjust.com' +}; +var BASE_URL_PREFIX = 'https://app.'; +var GDPR_URL_PREFIX = 'https://gdpr.'; +var BASE_URL_NO_SUB_DOMAIN_PREFIX = 'https://'; +var PUB_SUB_EVENTS = { + WEB_UUID_CREATED: 'activity:web_uuid', + ATTRIBUTION_RECEIVED: 'activity:attribution' +}; +;// CONCATENATED MODULE: ./node_modules/@babel/runtime/helpers/esm/arrayWithHoles.js +function _arrayWithHoles(arr) { + if (Array.isArray(arr)) return arr; } - -// vertx -function useVertxTimer() { - if (typeof vertxNext !== 'undefined') { - return function () { - vertxNext(flush); - }; +;// CONCATENATED MODULE: ./node_modules/@babel/runtime/helpers/esm/iterableToArrayLimit.js +function _iterableToArrayLimit(r, l) { + var t = null == r ? null : "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"]; + if (null != t) { + var e, + n, + i, + u, + a = [], + f = !0, + o = !1; + try { + if (i = (t = t.call(r)).next, 0 === l) { + if (Object(t) !== t) return; + f = !1; + } else for (; !(f = (e = i.call(t)).done) && (a.push(e.value), a.length !== l); f = !0); + } catch (r) { + o = !0, n = r; + } finally { + try { + if (!f && null != t["return"] && (u = t["return"](), Object(u) !== u)) return; + } finally { + if (o) throw n; + } + } + return a; } - - return useSetTimeout(); } - -function useMutationObserver() { - var iterations = 0; - var observer = new BrowserMutationObserver(flush); - var node = document.createTextNode(''); - observer.observe(node, { characterData: true }); - - return function () { - node.data = iterations = ++iterations % 2; - }; +;// CONCATENATED MODULE: ./node_modules/@babel/runtime/helpers/esm/arrayLikeToArray.js +function _arrayLikeToArray(arr, len) { + if (len == null || len > arr.length) len = arr.length; + for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i]; + return arr2; } +;// CONCATENATED MODULE: ./node_modules/@babel/runtime/helpers/esm/unsupportedIterableToArray.js -// web worker -function useMessageChannel() { - var channel = new MessageChannel(); - channel.port1.onmessage = flush; - return function () { - return channel.port2.postMessage(0); - }; +function _unsupportedIterableToArray(o, minLen) { + if (!o) return; + if (typeof o === "string") return _arrayLikeToArray(o, minLen); + var n = Object.prototype.toString.call(o).slice(8, -1); + if (n === "Object" && o.constructor) n = o.constructor.name; + if (n === "Map" || n === "Set") return Array.from(o); + if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); } - -function useSetTimeout() { - // Store setTimeout reference so es6-promise will be unaffected by - // other code modifying setTimeout (like sinon.useFakeTimers()) - var globalSetTimeout = setTimeout; - return function () { - return globalSetTimeout(flush, 1); - }; +;// CONCATENATED MODULE: ./node_modules/@babel/runtime/helpers/esm/nonIterableRest.js +function _nonIterableRest() { + throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } +;// CONCATENATED MODULE: ./node_modules/@babel/runtime/helpers/esm/slicedToArray.js -var queue = new Array(1000); -function flush() { - for (var i = 0; i < len; i += 2) { - var callback = queue[i]; - var arg = queue[i + 1]; - - callback(arg); - queue[i] = undefined; - queue[i + 1] = undefined; - } - len = 0; -} -function attemptVertx() { - try { - var vertx = Function('return this')().require('vertx'); - vertxNext = vertx.runOnLoop || vertx.runOnContext; - return useVertxTimer(); - } catch (e) { - return useSetTimeout(); - } +function _slicedToArray(arr, i) { + return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _unsupportedIterableToArray(arr, i) || _nonIterableRest(); } +;// CONCATENATED MODULE: ./src/sdk/utilities.ts -var scheduleFlush = void 0; -// Decide what async method to use to triggering processing of queued callbacks: -if (isNode) { - scheduleFlush = useNextTick(); -} else if (BrowserMutationObserver) { - scheduleFlush = useMutationObserver(); -} else if (isWorker) { - scheduleFlush = useMessageChannel(); -} else if (browserWindow === undefined && "function" === 'function') { - scheduleFlush = attemptVertx(); -} else { - scheduleFlush = useSetTimeout(); -} -function then(onFulfillment, onRejection) { - var parent = this; - var child = new this.constructor(noop); - if (child[PROMISE_ID] === undefined) { - makePromise(child); +/** + * Build human readable list + */ +function buildList(array /*: Array*/) /*: string*/{ + if (!array.length) { + return ''; } - - var _state = parent._state; - - - if (_state) { - var callback = arguments[_state - 1]; - asap(function () { - return invokeCallback(_state, child, callback, parent._result); - }); - } else { - subscribe(parent, child, onFulfillment, onRejection); + if (array.length === 1) { + return "".concat(array[0]); } - - return child; + var lastIndex = array.length - 1; + var firstPart = array.slice(0, lastIndex).join(', '); + return "".concat(firstPart, " and ").concat(array[lastIndex]); } /** - `Promise.resolve` returns a promise that will become resolved with the - passed `value`. It is shorthand for the following: - - ```javascript - let promise = new Promise(function(resolve, reject){ - resolve(1); - }); - - promise.then(function(value){ - // value === 1 - }); - ``` - - Instead of writing the above, your code now simply becomes the following: - - ```javascript - let promise = Promise.resolve(1); - - promise.then(function(value){ - // value === 1 - }); - ``` - - @method resolve - @static - @param {Any} value value that the returned promise will be resolved with - Useful for tooling. - @return {Promise} a promise that will become fulfilled with the given - `value` -*/ -function resolve$1(object) { - /*jshint validthis:true */ - var Constructor = this; - - if (object && typeof object === 'object' && object.constructor === Constructor) { - return object; - } - - var promise = new Constructor(noop); - resolve(promise, object); - return promise; + * Check if object is empty + */ +function isEmpty(obj /*: Record*/) /*: boolean*/{ + return !Object.keys(obj).length && obj.constructor === Object; } -var PROMISE_ID = Math.random().toString(36).substring(2); - -function noop() {} - -var PENDING = void 0; -var FULFILLED = 1; -var REJECTED = 2; - -function selfFulfillment() { - return new TypeError("You cannot resolve a promise with itself"); -} - -function cannotReturnOwn() { - return new TypeError('A promises callback cannot return that same promise.'); +/** + * Check if value is object + */ +function isObject(obj /*: any*/) /*: boolean*/{ + // eslint-disable-line @typescript-eslint/no-explicit-any + return _typeof(obj) === 'object' && obj !== null && !(obj instanceof Array); } -function tryThen(then$$1, value, fulfillmentHandler, rejectionHandler) { +/** + * Check if string is valid json + */ +function isValidJson(string /*: string*/) /*: boolean*/{ try { - then$$1.call(value, fulfillmentHandler, rejectionHandler); + var json = JSON.parse(string); + return isObject(json); } catch (e) { - return e; + return false; } } -function handleForeignThenable(promise, thenable, then$$1) { - asap(function (promise) { - var sealed = false; - var error = tryThen(then$$1, thenable, function (value) { - if (sealed) { - return; - } - sealed = true; - if (thenable !== value) { - resolve(promise, value); - } else { - fulfill(promise, value); - } - }, function (reason) { - if (sealed) { - return; - } - sealed = true; - - reject(promise, reason); - }, 'Settle: ' + (promise._label || ' unknown promise')); - - if (!sealed && error) { - sealed = true; - reject(promise, error); - } - }, promise); -} - -function handleOwnThenable(promise, thenable) { - if (thenable._state === FULFILLED) { - fulfill(promise, thenable._result); - } else if (thenable._state === REJECTED) { - reject(promise, thenable._result); - } else { - subscribe(thenable, undefined, function (value) { - return resolve(promise, value); - }, function (reason) { - return reject(promise, reason); - }); +/** + * Find index of an element in the list and return it + */ +function findIndex /*:: >*/(array /*: Array*/, key /*: K | Array*/, target /*: T*/) /*: number*/{ + function isEqual(item /*: T*/) { + return Array.isArray(key) ? key.every(function (k) { + return item[k] === target[k]; + }) : item[key] === target; } -} - -function handleMaybeThenable(promise, maybeThenable, then$$1) { - if (maybeThenable.constructor === promise.constructor && then$$1 === then && maybeThenable.constructor.resolve === resolve$1) { - handleOwnThenable(promise, maybeThenable); - } else { - if (then$$1 === undefined) { - fulfill(promise, maybeThenable); - } else if (isFunction(then$$1)) { - handleForeignThenable(promise, maybeThenable, then$$1); - } else { - fulfill(promise, maybeThenable); + for (var i = 0; i < array.length; i += 1) { + if (isEqual(array[i])) { + return i; } } + return -1; } -function resolve(promise, value) { - if (promise === value) { - reject(promise, selfFulfillment()); - } else if (objectOrFunction(value)) { - var then$$1 = void 0; - try { - then$$1 = value.then; - } catch (error) { - reject(promise, error); - return; - } - handleMaybeThenable(promise, value, then$$1); - } else { - fulfill(promise, value); - } +/** + * Convert array with key/value item structure into key/value pairs object + */ +function convertToMap /*:: */() /*: Record*/{ + var array /*: Array<{ key: string, value: T }>*/ = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : []; + return array.reduce(function (acc, o) { + return _objectSpread2(_objectSpread2({}, acc), {}, _defineProperty({}, o.key, o.value)); + }, {}); } -function publishRejection(promise) { - if (promise._onerror) { - promise._onerror(promise._result); - } - - publish(promise); +/** + * Find intersecting values of provided array against given values + */ +function intersection /*:: */() /*: Array*/{ + var array /*: Array*/ = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : []; + var values /*: Array*/ = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : []; + return array.filter(function (item) { + return values.indexOf(item) !== -1; + }); } -function fulfill(promise, value) { - if (promise._state !== PENDING) { - return; - } - - promise._result = value; - promise._state = FULFILLED; - - if (promise._subscribers.length !== 0) { - asap(publish, promise); - } +/** + * Check if particular url is a certain request + */ +function isRequest(url /*: string*/, requestName /*: string*/) /*: boolean*/{ + var regex = new RegExp("\\/".concat(requestName, "(\\/.*|\\?.*){0,1}$")); + return regex.test(url); } -function reject(promise, reason) { - if (promise._state !== PENDING) { - return; - } - promise._state = REJECTED; - promise._result = reason; - - asap(publishRejection, promise); +/** + * Extract the host name for the url + */ +function getHostName() /*: string*/{ + var url = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : ''; + return url.replace(/^(http(s)*:\/\/)*(www\.)*/, '').split('/')[0].split('?')[0]; } -function subscribe(parent, child, onFulfillment, onRejection) { - var _subscribers = parent._subscribers; - var length = _subscribers.length; - - - parent._onerror = null; - - _subscribers[length] = child; - _subscribers[length + FULFILLED] = onFulfillment; - _subscribers[length + REJECTED] = onRejection; - - if (length === 0 && parent._state) { - asap(publish, parent); - } +/** + * Transform array entry into object key:value pair entry + */ +function reducer /*:: */(acc /*: Record*/, _ref /*:: */) /*: Record*/{ + var _ref2 = _slicedToArray(_ref /*:: */, 2), + key = _ref2[0], + value = _ref2[1]; + return _objectSpread2(_objectSpread2({}, acc), {}, _defineProperty({}, key, value)); } -function publish(promise) { - var subscribers = promise._subscribers; - var settled = promise._state; - - if (subscribers.length === 0) { - return; - } - - var child = void 0, - callback = void 0, - detail = promise._result; - - for (var i = 0; i < subscribers.length; i += 3) { - child = subscribers[i]; - callback = subscribers[i + settled]; - - if (child) { - invokeCallback(settled, child, callback, detail); - } else { - callback(detail); - } - } - - promise._subscribers.length = 0; +/** + * Extracts object entries in the [key, value] format + */ +function entries /*:: */(object /*: Record*/) /*: Array<[K, T]>*/{ + return Object.keys(object).map(function (key /*: K*/) { + return [key, object[key]]; + }); } -function invokeCallback(settled, promise, callback, detail) { - var hasCallback = isFunction(callback), - value = void 0, - error = void 0, - succeeded = true; - - if (hasCallback) { - try { - value = callback(detail); - } catch (e) { - succeeded = false; - error = e; - } - - if (promise === value) { - reject(promise, cannotReturnOwn()); - return; - } - } else { - value = detail; - } +/** + * Extracts object values + */ +function values /*:: */(object /*: Record*/) /*: Array*/{ + return Object.keys(object).map(function (key /*: string*/) { + return object[key]; + }); +} - if (promise._state !== PENDING) { - // noop - } else if (hasCallback && succeeded) { - resolve(promise, value); - } else if (succeeded === false) { - reject(promise, error); - } else if (settled === FULFILLED) { - fulfill(promise, value); - } else if (settled === REJECTED) { - reject(promise, value); +/** + * Check if value is empty in any way (empty object, false value, zero) and use it as predicate method + */ +function isEmptyEntry(value /*: any*/) /*: boolean*/{ + // eslint-disable-line @typescript-eslint/no-explicit-any + if (isObject(value)) { + return !isEmpty(value); } + return !!value || value === 0; } - -function initializePromise(promise, resolver) { +function isLocalStorageSupported() /*: boolean*/{ try { - resolver(function resolvePromise(value) { - resolve(promise, value); - }, function rejectPromise(reason) { - reject(promise, reason); - }); + var uid = new Date().toString(); + var storage = window.localStorage; + storage.setItem(uid, uid); + var result = storage.getItem(uid) === uid; + storage.removeItem(uid); + var support = !!(result && storage); + return support; } catch (e) { - reject(promise, e); + return false; } } -var id = 0; -function nextId() { - return id++; -} - -function makePromise(promise) { - promise[PROMISE_ID] = id++; - promise._state = undefined; - promise._result = undefined; - promise._subscribers = []; -} - -function validationError() { - return new Error('Array Methods must be provided an Array'); -} - -var Enumerator = function () { - function Enumerator(Constructor, input) { - this._instanceConstructor = Constructor; - this.promise = new Constructor(noop); - - if (!this.promise[PROMISE_ID]) { - makePromise(this.promise); - } - - if (isArray(input)) { - this.length = input.length; - this._remaining = input.length; - - this._result = new Array(this.length); +;// CONCATENATED MODULE: ./src/sdk/globals.js +/*:: declare var __ADJUST__NAMESPACE: string*/ +/*:: declare var __ADJUST__SDK_VERSION: string*/ +/*:: declare var process: {| + env: {| + NODE_ENV: 'development' | 'production' | 'test' + |} +|}*/ +var Globals = { + namespace: "adjust-sdk" || 0, + version: "5.7.0" || 0, + env: "production" +}; +/* harmony default export */ const globals = (Globals); +;// CONCATENATED MODULE: ./src/sdk/logger.js - if (this.length === 0) { - fulfill(this.promise, this._result); - } else { - this.length = this.length || 0; - this._enumerate(input); - if (this._remaining === 0) { - fulfill(this.promise, this._result); - } - } - } else { - reject(this.promise, validationError()); - } - } - Enumerator.prototype._enumerate = function _enumerate(input) { - for (var i = 0; this._state === PENDING && i < input.length; i++) { - this._eachEntry(input[i], i); - } - }; - Enumerator.prototype._eachEntry = function _eachEntry(entry, i) { - var c = this._instanceConstructor; - var resolve$$1 = c.resolve; +/*:: import { type LogOptionsT } from './types';*/ +/*:: type LogLevelT = $PropertyType*/ +/*:: type MethodNameT = 'log' | 'info' | 'error' | 'warn'*/ +var LEVEL_NONE = 'none'; +var LEVEL_ERROR = 'error'; +var LEVEL_WARNING = 'warning'; +var LEVEL_INFO = 'info'; +var LEVEL_VERBOSE = 'verbose'; +/** + * Logger levels + * - none -> nothing is printed to the console + * - error -> prints only error + * - info -> prints info and error + * - verbose -> prints log, info and error + * + * @type {Object} + * @private + */ +var _levels = _defineProperty(_defineProperty(_defineProperty(_defineProperty(_defineProperty({}, LEVEL_NONE, -1), LEVEL_ERROR, 0), LEVEL_WARNING, 1), LEVEL_INFO, 2), LEVEL_VERBOSE, 3); - if (resolve$$1 === resolve$1) { - var _then = void 0; - var error = void 0; - var didError = false; - try { - _then = entry.then; - } catch (e) { - didError = true; - error = e; - } +/** + * Spaces placed after log level tag in console to align messages. + * + * @type {Object} + * @private + */ +var _spaces = { + 'log': ' ', + 'info': ' ', + 'warn': ' ', + 'error': '' +}; - if (_then === then && entry._state !== PENDING) { - this._settledAt(entry._state, i, entry._result); - } else if (typeof _then !== 'function') { - this._remaining--; - this._result[i] = entry; - } else if (c === Promise$1) { - var promise = new c(noop); - if (didError) { - reject(promise, error); - } else { - handleMaybeThenable(promise, entry, _then); - } - this._willSettleAt(promise, i); - } else { - this._willSettleAt(new c(function (resolve$$1) { - return resolve$$1(entry); - }), i); - } - } else { - this._willSettleAt(resolve$$1(entry), i); - } - }; +/** + * Default logger level per environment + * + * @type {Object} + * @private + */ +var _envLogLevels = { + development: LEVEL_VERBOSE, + production: LEVEL_ERROR, + test: LEVEL_VERBOSE +}; - Enumerator.prototype._settledAt = function _settledAt(state, i, value) { - var promise = this.promise; +/** + * Current logger level + */ +var _level = _getDefaultLogLevel(); +/** + * Optional output container to display logs for easier debugging + * + * @type {string} + * @private + */ +var _output = ''; - if (promise._state === PENDING) { - this._remaining--; +/** + * Get default logger error per environment and fallback to error level when unknown env + * + * @returns {string} + * @private + */ +function _getDefaultLogLevel() /*: LogLevelT*/{ + return _envLogLevels[globals.env] || LEVEL_ERROR; +} - if (state === REJECTED) { - reject(promise, value); - } else { - this._result[i] = value; - } - } +/** + * Set logger level, fallback to default log level + * + * @param {string=} logLevel + * @param {string=} logOutput + */ +function setLogLevel(logLevel /*: LogLevelT*/, logOutput /*: string*/) /*: void*/{ + var exists = !logLevel || Object.keys(_levels).indexOf(logLevel) !== -1; + if (!exists) { + _log('error', 'error', 'You must set one of the available log levels: verbose, info, warning, error or none'); + return; + } + _level = logLevel || _getDefaultLogLevel(); + _output = logOutput || _output; + _log('info', logLevel, "Log level set to ".concat(_level)); +} - if (this._remaining === 0) { - fulfill(promise, this._result); - } - }; +/** + * Output the message to the console + * + * @param {string} methodName + * @param {string} logLevel + * @param {Array} args + * @private + */ +function _log(methodName /*: MethodNameT*/, logLevel /*: LogLevelT*/) /*: void*/{ + var _console; + if (_levels[_level] < _levels[logLevel]) { + return; + } + var time = new Date().toISOString(); + var spaces = _spaces[methodName]; + var messagePrefix = ["[".concat(globals.namespace, "]"), time, "".concat(methodName.toUpperCase(), ":").concat(spaces)]; + var outputContainer = _output ? document.querySelector(_output) : null; + for (var _len = arguments.length, args = new Array(_len > 2 ? _len - 2 : 0), _key = 2; _key < _len; _key++) { + args[_key - 2] = arguments[_key]; + } + (_console = console)[methodName].apply(_console, messagePrefix.concat(args)); // eslint-disable-line - Enumerator.prototype._willSettleAt = function _willSettleAt(promise, i) { - var enumerator = this; + if (outputContainer) { + outputContainer.textContent += "".concat(messagePrefix.join(' '), " ").concat(args.map(function (m) { + return isObject(m) ? JSON.stringify(m) : m; + }).join(' '), "\n"); + outputContainer.scrollTop = outputContainer.scrollHeight; + } +} - subscribe(promise, undefined, function (value) { - return enumerator._settledAt(FULFILLED, i, value); - }, function (reason) { - return enumerator._settledAt(REJECTED, i, reason); - }); +/** + * Apply predefined log level and return log method + * + * @param {string} name + * @param {string} logLevel + * @returns {Function: (Array) => void} + * @private + */ +function _applyLevel(name /*: MethodNameT*/, logLevel /*: LogLevelT*/) { + return function () { + for (var _len2 = arguments.length, args = new Array(_len2), _key2 = 0; _key2 < _len2; _key2++) { + args[_key2] = arguments[_key2]; + } + _log.apply(void 0, [name, logLevel].concat(args)); }; +} +var Logger = { + setLogLevel: setLogLevel, + log: _applyLevel('log', LEVEL_VERBOSE), + info: _applyLevel('info', LEVEL_INFO), + warn: _applyLevel('warn', LEVEL_WARNING), + error: _applyLevel('error', LEVEL_ERROR) +}; +/* harmony default export */ const logger = (Logger); +;// CONCATENATED MODULE: ./src/sdk/config.ts - return Enumerator; -}(); -/** - `Promise.all` accepts an array of promises, and returns a new promise which - is fulfilled with an array of fulfillment values for the passed promises, or - rejected with the reason of the first passed promise to be rejected. It casts all - elements of the passed iterable to promises as it runs this algorithm. - Example: - ```javascript - let promise1 = resolve(1); - let promise2 = resolve(2); - let promise3 = resolve(3); - let promises = [ promise1, promise2, promise3 ]; - Promise.all(promises).then(function(array){ - // The array here would be [ 1, 2, 3 ]; - }); - ``` +/** Base parameters set by client */ - If any of the `promises` given to `all` are rejected, the first promise - that is rejected will be given as an argument to the returned promises's - rejection handler. For example: +/** Custom config set by client */ +/*:: export type InitOptions = BaseParams & CustomConfig & { + attributionCallback: (eventName: string, attribution: Attribution) => unknown +}*/ +var _baseParams /*: BaseParams | null*/ = null; +var _customConfig /*: CustomConfig | null*/ = null; - Example: +/** Mandatory fields to set for sdk initialization */ +var _mandatory /*: Array<(keyof MandatoryParams)>*/ = ['appToken', 'environment']; - ```javascript - let promise1 = resolve(1); - let promise2 = reject(new Error("2")); - let promise3 = reject(new Error("3")); - let promises = [ promise1, promise2, promise3 ]; +/** Allowed params to be sent with each request */ +var _allowedParams /*: Array<(keyof BaseParams)>*/ = [].concat(_mandatory, ['defaultTracker', 'externalDeviceId']); - Promise.all(promises).then(function(array){ - // Code here never runs because there are rejected promises! - }, function(error) { - // error.message === "2" - }); - ``` +/** Allowed configuration overrides */ +var _allowedConfig /*: Array<(keyof CustomConfig)>*/ = ['customUrl', 'dataResidency', 'urlStrategy', 'eventDeduplicationListLimit', 'namespace']; - @method all - @static - @param {Array} entries array of promises - @param {String} label optional string for labeling the promise. - Useful for tooling. - @return {Promise} promise that is fulfilled when all `promises` have been - fulfilled, or rejected if any of them become rejected. - @static -*/ -function all(entries) { - return new Enumerator(this, entries).promise; +/** + * Check of configuration has been initialized + */ +function isInitialised() /*: boolean*/{ + return _mandatory.reduce(function (acc, key) { + return acc && !!_baseParams && !!_baseParams[key]; + }, true); } /** - `Promise.race` returns a new promise which is settled in the same way as the - first passed promise to settle. - - Example: - - ```javascript - let promise1 = new Promise(function(resolve, reject){ - setTimeout(function(){ - resolve('promise 1'); - }, 200); - }); - - let promise2 = new Promise(function(resolve, reject){ - setTimeout(function(){ - resolve('promise 2'); - }, 100); - }); - - Promise.race([promise1, promise2]).then(function(result){ - // result === 'promise 2' because it was resolved before promise1 - // was resolved. - }); - ``` - - `Promise.race` is deterministic in that only the state of the first - settled promise matters. For example, even if other promises given to the - `promises` array argument are resolved, but the first settled promise has - become rejected before the other promises became fulfilled, the returned - promise will become rejected: - - ```javascript - let promise1 = new Promise(function(resolve, reject){ - setTimeout(function(){ - resolve('promise 1'); - }, 200); - }); - - let promise2 = new Promise(function(resolve, reject){ - setTimeout(function(){ - reject(new Error('promise 2')); - }, 100); - }); - - Promise.race([promise1, promise2]).then(function(result){ - // Code here never runs - }, function(reason){ - // reason.message === 'promise 2' because promise 2 became rejected before - // promise 1 became fulfilled - }); - ``` - - An example real-world use case is implementing timeouts: - - ```javascript - Promise.race([ajax('foo.json'), timeout(5000)]) - ``` - - @method race - @static - @param {Array} promises array of promises to observe - Useful for tooling. - @return {Promise} a promise which settles in the same way as the first passed - promise to settle. -*/ -function race(entries) { - /*jshint validthis:true */ - var Constructor = this; - - if (!isArray(entries)) { - return new Constructor(function (_, reject) { - return reject(new TypeError('You must pass an array to race.')); - }); - } else { - return new Constructor(function (resolve, reject) { - var length = entries.length; - for (var i = 0; i < length; i++) { - Constructor.resolve(entries[i]).then(resolve, reject); - } - }); + * Set base params and custom config for the sdk to run + */ +function set(options /*: InitOptions*/) /*: void*/{ + if (hasMissing(options)) { + return; } + _baseParams = _allowedParams.filter(function (key) { + return !!options[key]; + }).map(function (key) { + return [key, options[key]]; + }).reduce(function (acc, item) { + return reducer(acc, item); + }, {}); + _customConfig = _allowedConfig.filter(function (key) { + return !!options[key]; + }).map(function (key) { + return [key, options[key]]; + }).reduce(reducer, {}); } /** - `Promise.reject` returns a promise rejected with the passed `reason`. - It is shorthand for the following: - - ```javascript - let promise = new Promise(function(resolve, reject){ - reject(new Error('WHOOPS')); - }); - - promise.then(function(value){ - // Code here doesn't run because the promise is rejected! - }, function(reason){ - // reason.message === 'WHOOPS' - }); - ``` - - Instead of writing the above, your code now simply becomes the following: - - ```javascript - let promise = Promise.reject(new Error('WHOOPS')); - - promise.then(function(value){ - // Code here doesn't run because the promise is rejected! - }, function(reason){ - // reason.message === 'WHOOPS' - }); - ``` - - @method reject - @static - @param {Any} reason value that the returned promise will be rejected with. - Useful for tooling. - @return {Promise} a promise rejected with the given `reason`. -*/ -function reject$1(reason) { - /*jshint validthis:true */ - var Constructor = this; - var promise = new Constructor(noop); - reject(promise, reason); - return promise; -} - -function needsResolver() { - throw new TypeError('You must pass a resolver function as the first argument to the promise constructor'); + * Get base params set by client + */ +function getBaseParams() /*: Partial*/{ + return _baseParams ? _objectSpread2({}, _baseParams) // intentionally returns a copy + : {}; } -function needsNew() { - throw new TypeError("Failed to construct 'Promise': Please use the 'new' operator, this object constructor cannot be called as a function."); +/** + * Get custom config set by client + */ +function getCustomConfig() /*: CustomConfig*/{ + return _customConfig ? _objectSpread2({}, _customConfig) // intentionally returns a copy + : {}; } /** - Promise objects represent the eventual result of an asynchronous operation. The - primary way of interacting with a promise is through its `then` method, which - registers callbacks to receive either a promise's eventual value or the reason - why the promise cannot be fulfilled. - - Terminology - ----------- - - - `promise` is an object or function with a `then` method whose behavior conforms to this specification. - - `thenable` is an object or function that defines a `then` method. - - `value` is any legal JavaScript value (including undefined, a thenable, or a promise). - - `exception` is a value that is thrown using the throw statement. - - `reason` is a value that indicates why a promise was rejected. - - `settled` the final resting state of a promise, fulfilled or rejected. - - A promise can be in one of three states: pending, fulfilled, or rejected. - - Promises that are fulfilled have a fulfillment value and are in the fulfilled - state. Promises that are rejected have a rejection reason and are in the - rejected state. A fulfillment value is never a thenable. - - Promises can also be said to *resolve* a value. If this value is also a - promise, then the original promise's settled state will match the value's - settled state. So a promise that *resolves* a promise that rejects will - itself reject, and a promise that *resolves* a promise that fulfills will - itself fulfill. - - - Basic Usage: - ------------ - - ```js - let promise = new Promise(function(resolve, reject) { - // on success - resolve(value); - - // on failure - reject(reason); - }); - - promise.then(function(value) { - // on fulfillment - }, function(reason) { - // on rejection + * Check if there are missing mandatory parameters + */ +function hasMissing(params /*: BaseParams*/) /*: boolean*/{ + var missing = _mandatory.filter(function (value) { + return !params[value]; }); - ``` - - Advanced Usage: - --------------- - - Promises shine when abstracting away asynchronous interactions such as - `XMLHttpRequest`s. - - ```js - function getJSON(url) { - return new Promise(function(resolve, reject){ - let xhr = new XMLHttpRequest(); - - xhr.open('GET', url); - xhr.onreadystatechange = handler; - xhr.responseType = 'json'; - xhr.setRequestHeader('Accept', 'application/json'); - xhr.send(); - - function handler() { - if (this.readyState === this.DONE) { - if (this.status === 200) { - resolve(this.response); - } else { - reject(new Error('getJSON: `' + url + '` failed with status: [' + this.status + ']')); - } - } - }; - }); + if (missing.length) { + logger.error("You must define ".concat(buildList(missing))); + return true; } + return false; +} - getJSON('/posts.json').then(function(json) { - // on fulfillment - }, function(reason) { - // on rejection - }); - ``` +/** + * Restore config to its default state + */ +function destroy() /*: void*/{ + _baseParams = null; + _customConfig = null; +} +var Config = { + sessionWindow: 30 * MINUTE, + sessionTimerWindow: 60 * SECOND, + requestValidityWindow: 28 * DAY, + set: set, + getBaseParams: getBaseParams, + getCustomConfig: getCustomConfig, + isInitialised: isInitialised, + hasMissing: hasMissing, + destroy: destroy +}; +/* harmony default export */ const sdk_config = (Config); +;// CONCATENATED MODULE: ./src/sdk/storage/scheme.ts - Unlike callbacks, promises are great composable primitives. - ```js - Promise.all([ - getJSON('/posts'), - getJSON('/comments') - ]).then(function(values){ - values[0] // => postsJSON - values[1] // => commentsJSON - return values; - }); - ``` - - @class Promise - @param {Function} resolver - Useful for tooling. - @constructor -*/ - -var Promise$1 = function () { - function Promise(resolver) { - this[PROMISE_ID] = nextId(); - this._result = this._state = undefined; - this._subscribers = []; - - if (noop !== resolver) { - typeof resolver !== 'function' && needsResolver(); - this instanceof Promise ? initializePromise(this, resolver) : needsNew(); - } - } +/** + * Field with predefined values + */ - /** - The primary way of interacting with a promise is through its `then` method, - which registers callbacks to receive either a promise's eventual value or the - reason why the promise cannot be fulfilled. - ```js - findUser().then(function(user){ - // user is available - }, function(reason){ - // user is unavailable, and you are given the reason why - }); - ``` - Chaining - -------- - The return value of `then` is itself a promise. This second, 'downstream' - promise is resolved with the return value of the first promise's fulfillment - or rejection handler, or rejected if the handler throws an exception. - ```js - findUser().then(function (user) { - return user.name; - }, function (reason) { - return 'default name'; - }).then(function (userName) { - // If `findUser` fulfilled, `userName` will be the user's name, otherwise it - // will be `'default name'` - }); - findUser().then(function (user) { - throw new Error('Found user, but still unhappy'); - }, function (reason) { - throw new Error('`findUser` rejected and we're unhappy'); - }).then(function (value) { - // never reached - }, function (reason) { - // if `findUser` fulfilled, `reason` will be 'Found user, but still unhappy'. - // If `findUser` rejected, `reason` will be '`findUser` rejected and we're unhappy'. - }); - ``` - If the downstream promise does not specify a rejection handler, rejection reasons will be propagated further downstream. - ```js - findUser().then(function (user) { - throw new PedagogicalException('Upstream error'); - }).then(function (value) { - // never reached - }).then(function (value) { - // never reached - }, function (reason) { - // The `PedgagocialException` is propagated all the way down to here - }); - ``` - Assimilation - ------------ - Sometimes the value you want to propagate to a downstream promise can only be - retrieved asynchronously. This can be achieved by returning a promise in the - fulfillment or rejection handler. The downstream promise will then be pending - until the returned promise is settled. This is called *assimilation*. - ```js - findUser().then(function (user) { - return findCommentsByAuthor(user); - }).then(function (comments) { - // The user's comments are now available - }); - ``` - If the assimliated promise rejects, then the downstream promise will also reject. - ```js - findUser().then(function (user) { - return findCommentsByAuthor(user); - }).then(function (comments) { - // If `findCommentsByAuthor` fulfills, we'll have the value here - }, function (reason) { - // If `findCommentsByAuthor` rejects, we'll have the reason here - }); - ``` - Simple Example - -------------- - Synchronous Example - ```javascript - let result; - try { - result = findResult(); - // success - } catch(reason) { - // failure - } - ``` - Errback Example - ```js - findResult(function(result, err){ - if (err) { - // failure - } else { - // success - } - }); - ``` - Promise Example; - ```javascript - findResult().then(function(result){ - // success - }, function(reason){ - // failure - }); - ``` - Advanced Example - -------------- - Synchronous Example - ```javascript - let author, books; - try { - author = findAuthor(); - books = findBooksByAuthor(author); - // success - } catch(reason) { - // failure - } - ``` - Errback Example - ```js - function foundBooks(books) { - } - function failure(reason) { - } - findAuthor(function(author, err){ - if (err) { - failure(err); - // failure - } else { - try { - findBoooksByAuthor(author, function(books, err) { - if (err) { - failure(err); - } else { - try { - foundBooks(books); - } catch(reason) { - failure(reason); - } - } - }); - } catch(error) { - failure(err); +/** + * Field containing a nested one + */ + +/** + * Composite key, stored as a field because of IE doesn't support composite keys feature + */ +var StoreName = /*#__PURE__*/function (StoreName) { + StoreName["Queue"] = "queue"; + StoreName["ActivityState"] = "activityState"; + StoreName["GlobalParams"] = "globalParams"; + StoreName["EventDeduplication"] = "eventDeduplication"; + return StoreName; +}(StoreName || {}); +var PreferencesStoreName = /*#__PURE__*/function (PreferencesStoreName) { + PreferencesStoreName["Preferences"] = "preferences"; + return PreferencesStoreName; +}(PreferencesStoreName || {}); +var ShortStoreName = /*#__PURE__*/function (ShortStoreName) { + ShortStoreName["Queue"] = "q"; + ShortStoreName["ActivityState"] = "as"; + ShortStoreName["GlobalParams"] = "gp"; + ShortStoreName["EventDeduplication"] = "ed"; + return ShortStoreName; +}(ShortStoreName || {}); +var ShortPreferencesStoreName = /*#__PURE__*/function (ShortPreferencesStoreName) { + ShortPreferencesStoreName["Preferences"] = "p"; + return ShortPreferencesStoreName; +}(ShortPreferencesStoreName || {}); +var _queueScheme /*: StoreOptions*/ = { + keyPath: 'timestamp', + autoIncrement: false, + fields: { + url: { + key: 'u', + values: { + '/session': 1, + '/event': 2, + '/gdpr_forget_device': 3, + '/sdk_click': 4, + '/disable_third_party_sharing': 5 + } + }, + method: { + key: 'm', + values: { + GET: 1, + POST: 2, + PUT: 3, + DELETE: 4 + } + }, + timestamp: 't', + createdAt: 'ca', + params: { + key: 'p', + keys: { + timeSpent: 'ts', + sessionLength: 'sl', + sessionCount: 'sc', + eventCount: 'ec', + lastInterval: 'li', + eventToken: 'et', + revenue: 're', + currency: 'cu', + callbackParams: 'cp', + partnerParams: 'pp' } - // success } - }); - ``` - Promise Example; - ```javascript - findAuthor(). - then(findBooksByAuthor). - then(function(books){ - // found books - }).catch(function(reason){ - // something went wrong - }); - ``` - @method then - @param {Function} onFulfilled - @param {Function} onRejected - Useful for tooling. - @return {Promise} - */ - - /** - `catch` is simply sugar for `then(undefined, onRejection)` which makes it the same - as the catch block of a try/catch statement. - ```js - function findAuthor(){ - throw new Error('couldn't find that author'); - } - // synchronous - try { - findAuthor(); - } catch(reason) { - // something went wrong } - // async with promises - findAuthor().catch(function(reason){ - // something went wrong - }); - ``` - @method catch - @param {Function} onRejection - Useful for tooling. - @return {Promise} - */ - - - Promise.prototype.catch = function _catch(onRejection) { - return this.then(null, onRejection); - }; - - /** - `finally` will be invoked regardless of the promise's fate just as native - try/catch/finally behaves - - Synchronous example: - - ```js - findAuthor() { - if (Math.random() > 0.5) { - throw new Error(); +}; +var _activityStateScheme /*: StoreOptions*/ = { + keyPath: 'uuid', + autoIncrement: false, + fields: { + uuid: { + key: 'u', + values: { + unknown: '-' + } + }, + timeSpent: 'ts', + sessionLength: 'sl', + sessionCount: 'sc', + eventCount: 'ec', + lastActive: 'la', + lastInterval: 'li', + installed: { + key: 'in', + values: { + false: 0, + true: 1 + } + }, + attribution: { + key: 'at', + keys: { + adid: 'a', + tracker_token: 'tt', + tracker_name: 'tn', + network: 'nt', + campaign: 'cm', + adgroup: 'ag', + creative: 'cr', + click_label: 'cl', + state: { + key: 'st', + values: { + installed: 1, + reattributed: 2 + } + } } - return new Author(); - } - - try { - return findAuthor(); // succeed or fail - } catch(error) { - return findOtherAuther(); - } finally { - // always runs - // doesn't affect the return value - } - ``` - - Asynchronous example: - - ```js - findAuthor().catch(function(reason){ - return findOtherAuther(); - }).finally(function(){ - // author was either found, or not - }); - ``` - - @method finally - @param {Function} callback - @return {Promise} - */ - - - Promise.prototype.finally = function _finally(callback) { - var promise = this; - var constructor = promise.constructor; - - if (isFunction(callback)) { - return promise.then(function (value) { - return constructor.resolve(callback()).then(function () { - return value; - }); - }, function (reason) { - return constructor.resolve(callback()).then(function () { - throw reason; - }); - }); - } - - return promise.then(callback, callback); - }; - - return Promise; -}(); - -Promise$1.prototype.then = then; -Promise$1.all = all; -Promise$1.race = race; -Promise$1.resolve = resolve$1; -Promise$1.reject = reject$1; -Promise$1._setScheduler = setScheduler; -Promise$1._setAsap = setAsap; -Promise$1._asap = asap; - -/*global self*/ -function polyfill() { - var local = void 0; - - if (typeof __webpack_require__.g !== 'undefined') { - local = __webpack_require__.g; - } else if (typeof self !== 'undefined') { - local = self; - } else { - try { - local = Function('return this')(); - } catch (e) { - throw new Error('polyfill failed because global object is unavailable in this environment'); - } - } - - var P = local.Promise; - - if (P) { - var promiseToString = null; - try { - promiseToString = Object.prototype.toString.call(P.resolve()); - } catch (e) { - // silently ignored - } - - if (promiseToString === '[object Promise]' && !P.cast) { - return; } } - - local.Promise = Promise$1; -} - -// Strange compat.. -Promise$1.polyfill = polyfill; -Promise$1.Promise = Promise$1; - -return Promise$1; - -}))); - - - -//# sourceMappingURL=es6-promise.map - - -/***/ }), - -/***/ 379: -/***/ ((module) => { - -"use strict"; - - -var stylesInDOM = []; - -function getIndexByIdentifier(identifier) { - var result = -1; - - for (var i = 0; i < stylesInDOM.length; i++) { - if (stylesInDOM[i].identifier === identifier) { - result = i; - break; +}; +var _globalParamsScheme /*: StoreOptions*/ = { + keyPath: 'keyType', + autoIncrement: false, + index: 'type', + fields: { + keyType: { + key: 'kt', + composite: ['key', 'type'] + }, + key: 'k', + value: 'v', + type: { + key: 't', + values: { + callback: 1, + partner: 2 + } } } - - return result; -} - -function modulesToDom(list, options) { - var idCountMap = {}; - var identifiers = []; - - for (var i = 0; i < list.length; i++) { - var item = list[i]; - var id = options.base ? item[0] + options.base : item[0]; - var count = idCountMap[id] || 0; - var identifier = "".concat(id, " ").concat(count); - idCountMap[id] = count + 1; - var indexByIdentifier = getIndexByIdentifier(identifier); - var obj = { - css: item[1], - media: item[2], - sourceMap: item[3], - supports: item[4], - layer: item[5] - }; - - if (indexByIdentifier !== -1) { - stylesInDOM[indexByIdentifier].references++; - stylesInDOM[indexByIdentifier].updater(obj); - } else { - var updater = addElementStyle(obj, options); - options.byIndex = i; - stylesInDOM.splice(i, 0, { - identifier: identifier, - updater: updater, - references: 1 - }); - } - - identifiers.push(identifier); +}; +var _eventDeduplicationScheme /*: StoreOptions*/ = { + keyPath: 'internalId', + autoIncrement: true, + fields: { + internalId: 'ii', + id: 'i' } - - return identifiers; -} - -function addElementStyle(obj, options) { - var api = options.domAPI(options); - api.update(obj); - - var updater = function updater(newObj) { - if (newObj) { - if (newObj.css === obj.css && newObj.media === obj.media && newObj.sourceMap === obj.sourceMap && newObj.supports === obj.supports && newObj.layer === obj.layer) { - return; +}; +var _preferencesScheme /*: StoreOptionsOptionalKey*/ = { + fields: { + thirdPartySharingDisabled: { + key: 'td', + keys: { + reason: { + key: 'r', + values: _defineProperty({}, DISABLE_REASONS.REASON_GENERAL, 1) + }, + pending: { + key: 'p', + values: { + false: 0, + true: 1 + } + } } - - api.update(obj = newObj); - } else { - api.remove(); - } - }; - - return updater; -} - -module.exports = function (list, options) { - options = options || {}; - list = list || []; - var lastIdentifiers = modulesToDom(list, options); - return function update(newList) { - newList = newList || []; - - for (var i = 0; i < lastIdentifiers.length; i++) { - var identifier = lastIdentifiers[i]; - var index = getIndexByIdentifier(identifier); - stylesInDOM[index].references--; - } - - var newLastIdentifiers = modulesToDom(newList, options); - - for (var _i = 0; _i < lastIdentifiers.length; _i++) { - var _identifier = lastIdentifiers[_i]; - - var _index = getIndexByIdentifier(_identifier); - - if (stylesInDOM[_index].references === 0) { - stylesInDOM[_index].updater(); - - stylesInDOM.splice(_index, 1); + }, + sdkDisabled: { + key: 'sd', + keys: { + reason: { + key: 'r', + values: _defineProperty(_defineProperty({}, DISABLE_REASONS.REASON_GENERAL, 1), DISABLE_REASONS.REASON_GDPR, 2) + }, + pending: { + key: 'p', + values: { + false: 0, + true: 1 + } + } } } - - lastIdentifiers = newLastIdentifiers; - }; + } }; - -/***/ }), - -/***/ 569: -/***/ ((module) => { - -"use strict"; - - -var memo = {}; -/* istanbul ignore next */ - -function getTarget(target) { - if (typeof memo[target] === "undefined") { - var styleTarget = document.querySelector(target); // Special case to return head of iframe instead of iframe itself - - if (window.HTMLIFrameElement && styleTarget instanceof window.HTMLIFrameElement) { - try { - // This will throw an exception if access to iframe is blocked - // due to cross-origin restrictions - styleTarget = styleTarget.contentDocument.head; - } catch (e) { - // istanbul ignore next - styleTarget = null; - } - } - - memo[target] = styleTarget; +var scheme /*: Scheme*/ = { + queue: { + name: ShortStoreName.Queue, + scheme: _queueScheme + }, + activityState: { + name: ShortStoreName.ActivityState, + scheme: _activityStateScheme + }, + globalParams: { + name: ShortStoreName.GlobalParams, + scheme: _globalParamsScheme + }, + eventDeduplication: { + name: ShortStoreName.EventDeduplication, + scheme: _eventDeduplicationScheme + }, + preferences: { + name: ShortPreferencesStoreName.Preferences, + scheme: _preferencesScheme, + permanent: true } - - return memo[target]; +}; +function isPredefinedValuesField(field /*: Maybe*/) /*: field is StoreFieldPredefinedValues*/{ + return !!field && Object.prototype.hasOwnProperty.call(field, 'values'); } -/* istanbul ignore next */ - - -function insertBySelector(insert, style) { - var target = getTarget(insert); - - if (!target) { - throw new Error("Couldn't find a style target. This probably means that the value for the 'insert' parameter is invalid."); - } - - target.appendChild(style); +function isNestingStoreField(field /*: Maybe*/) /*: field is StoreFieldNestingFields*/{ + return !!field && Object.prototype.hasOwnProperty.call(field, 'keys'); } - -module.exports = insertBySelector; - -/***/ }), - -/***/ 216: -/***/ ((module) => { - -"use strict"; - - -/* istanbul ignore next */ -function insertStyleElement(options) { - var element = document.createElement("style"); - options.setAttributes(element, options.attributes); - options.insert(element, options.options); - return element; +function isCompositeKeyStoreField(field /*: Maybe*/) /*: field is StoreFieldCompositeKey*/{ + return !!field && Object.prototype.hasOwnProperty.call(field, 'composite'); +} +function isComplexStoreField(field /*: Maybe*/) /*: field is StoreFieldComplex*/{ + return !!field && typeof field !== 'string'; } -module.exports = insertStyleElement; - -/***/ }), - -/***/ 565: -/***/ ((module, __unused_webpack_exports, __webpack_require__) => { +/* harmony default export */ const storage_scheme = (scheme); +;// CONCATENATED MODULE: ./src/sdk/storage/scheme-map.ts -"use strict"; -/* istanbul ignore next */ -function setAttributesWithoutAttributes(styleElement) { - var nonce = true ? __webpack_require__.nc : 0; - if (nonce) { - styleElement.setAttribute("nonce", nonce); +/** + * Cast value into it's original type + */ +function _parseValue(value /*: string*/) /*: any*/{ + // eslint-disable-line @typescript-eslint/no-explicit-any + try { + return JSON.parse(value); + } catch (e) { + return value; } } -module.exports = setAttributesWithoutAttributes; - -/***/ }), - -/***/ 795: -/***/ ((module) => { - -"use strict"; - +/** + * Flip key/value pairs + */ +function _flipObject(obj /*: Record*/) /*: Record*/{ + return entries(obj).map(function (_ref) { + var _ref2 = _slicedToArray(_ref, 2), + key = _ref2[0], + value = _ref2[1]; + return [value, _parseValue(key)]; + }).reduce(reducer, {}); +} -/* istanbul ignore next */ -function apply(styleElement, options, obj) { - var css = ""; +/** + * Flip store name definition names: + * - short key pointing the long one along with additional configuration + */ +function _flipStoreNames(obj /*: StoresConfigurationMap*/) /*: StoresConfigurationMapFlipped*/{ + var flippedConfigs /*: Array<[ShortStoreNames, StoreConfigurationFlipped]>*/ = entries(obj).map(function (_ref3 /*:: */) { + var _ref4 = _slicedToArray(_ref3 /*:: */, 2), + name = _ref4[0], + options = _ref4[1]; + var config = { + name: name, + permanent: options.permanent + }; + return [options.name, config]; + }); + return flippedConfigs.reduce(reducer, {}); +} - if (obj.supports) { - css += "@supports (".concat(obj.supports, ") {"); - } +/** + * Flip store scheme values + */ +function _flipStoreScheme(storeName /*: StoreNames*/, key /*: string*/, scheme /*: StoreFieldScheme*/) { + var values = isPredefinedValuesField(scheme) ? { + values: _flipObject(scheme.values) + } : {}; + var keys = isNestingStoreField(scheme) ? { + keys: _flipScheme(storeName, scheme.keys) + } : {}; + var composite = isCompositeKeyStoreField(scheme) ? { + composite: scheme.composite.map(function (key) { + return _getShortKey(storeName, key); + }) + } : {}; + return _objectSpread2(_objectSpread2(_objectSpread2({ + key: key + }, values), keys), composite); +} - if (obj.media) { - css += "@media ".concat(obj.media, " {"); - } +/** + * Flip general scheme recursivelly + */ +function _flipScheme(storeName /*: StoreNames*/, fieldsScheme /*: StoreFields*/) { + return entries(fieldsScheme).map(function (_ref5 /*:: */) { + var _ref6 = _slicedToArray(_ref5 /*:: */, 2), + key = _ref6[0], + scheme = _ref6[1]; + return isComplexStoreField(scheme) ? [scheme.key, _flipStoreScheme(storeName, key, scheme)] : [scheme, key]; + }).reduce(reducer, {}); +} - var needLayer = typeof obj.layer !== "undefined"; +/** + * Extend base scheme with some more maps for encoding + */ +function _prepareLeft() /*: StoreScheme*/{ + var storesOptions /*: Array<[StoreNames, StoreOptionsOptionalKey]>*/ = entries(storage_scheme).map(function (_ref7 /*:: */) { + var _ref8 = _slicedToArray(_ref7 /*:: */, 2), + storeName = _ref8[0], + store = _ref8[1]; + var options /*: StoreOptionsOptionalKey*/ = { + keyPath: store.scheme.keyPath, + autoIncrement: store.scheme.autoIncrement, + index: store.scheme.index, + fields: store.scheme.fields + }; + return [storeName, options]; + }); + return storesOptions.reduce(reducer, {}); +} - if (needLayer) { - css += "@layer".concat(obj.layer.length > 0 ? " ".concat(obj.layer) : "", " {"); - } +/** + * Prepare scheme for decoding + */ +function _prepareRight() /*: StoreScheme*/{ + var storesOptionsEncoded /*: Array<[StoreNames, StoreOptionsOptionalKey]>*/ = entries(Left).map(function (_ref9) { + var _ref10 = _slicedToArray(_ref9, 2), + storeName = _ref10[0], + storeScheme = _ref10[1]; + var options /*: StoreOptionsOptionalKey*/ = { + keyPath: _getShortKey(storeName, storeScheme.keyPath), + autoIncrement: storeScheme.autoIncrement, + index: _getShortKey(storeName, storeScheme.index), + fields: _flipScheme(storeName, storeScheme.fields) + }; + return [storeName, options]; + }); + return storesOptionsEncoded.reduce(reducer, {}); +} - css += obj.css; +/** + * Get available values for encoding + */ +function _getValuesMap() /*: Record*/{ + // all pairs of predefined keys and values such as {GET: 1} + return entries(storage_scheme).reduce(function (acc, _ref11) { + var _ref12 = _slicedToArray(_ref11, 2), + store = _ref12[1]; + return acc.concat(store.scheme.fields); + }, []).map(function (scheme) { + return values(scheme).filter(isPredefinedValuesField).map(function (map) { + return entries(map.values); + }).reduce(function (acc, map) { + return acc.concat(map); + }, []); + }).reduce(function (acc, map) { + return acc.concat(map); + }, []).reduce(reducer, {}); +} - if (needLayer) { - css += "}"; +/** + * Get short key version of a specified key + */ +function _getShortKey(storeName /*: StoreNames*/, key /*: Maybe*/) /*: Maybe*/{ + if (!key) { + return undefined; } - - if (obj.media) { - css += "}"; + var map = storage_scheme[storeName].scheme.fields[key]; + if (isComplexStoreField(map)) { + return map.key; } + return map || key; +} - if (obj.supports) { - css += "}"; +/** + * Get store names and their general configuration (if store is permanent or not) + */ +function _getStoreNames() /*: StoresConfigurationMap*/{ + var storeNames /*: Array<[StoreNames, StoreConfiguration]>*/ = entries(storage_scheme).map(function (_ref13) { + var _ref14 = _slicedToArray(_ref13, 2), + name = _ref14[0], + store = _ref14[1]; + var config = { + name: store.name, + permanent: store.permanent + }; + return [name, config]; + }); + return storeNames.reduce(reducer, {}); +} +var Left = _prepareLeft(); +var Right = _prepareRight(); +var Values = _getValuesMap(); +var StoreNamesAndConfigs = _getStoreNames(); +/* harmony default export */ const scheme_map = ({ + left: Left, + right: Right, + values: Values, + storeNames: { + left: StoreNamesAndConfigs, + right: _flipStoreNames(StoreNamesAndConfigs) } +}); +;// CONCATENATED MODULE: ./src/sdk/storage/types.ts - var sourceMap = obj.sourceMap; +var KeyRangeCondition = /*#__PURE__*/function (KeyRangeCondition) { + KeyRangeCondition["LowerBound"] = "lowerBound"; + KeyRangeCondition["UpperBound"] = "upperBound"; + return KeyRangeCondition; +}(KeyRangeCondition || {}); +function valueIsRecord(value /*: StoredValue | Record*/) /*: value is Record*/{ + return isObject(value); +} - if (sourceMap && typeof btoa !== "undefined") { - css += "\n/*# sourceMappingURL=data:application/json;base64,".concat(btoa(unescape(encodeURIComponent(JSON.stringify(sourceMap)))), " */"); - } // For old IE +;// CONCATENATED MODULE: ./src/sdk/storage/converter.ts - /* istanbul ignore if */ - options.styleTagTransform(css, styleElement, options.options); -} -function removeStyleElement(styleElement) { - // istanbul ignore if - if (styleElement.parentNode === null) { - return false; - } - styleElement.parentNode.removeChild(styleElement); -} -/* istanbul ignore next */ -function domAPI(options) { - var styleElement = options.insertStyleElement(options); - return { - update: function update(obj) { - apply(styleElement, options, obj); - }, - remove: function remove() { - removeStyleElement(styleElement); - } - }; +var Direction = /*#__PURE__*/function (Direction) { + Direction["right"] = "right"; + Direction["left"] = "left"; + return Direction; +}(Direction || {}); +/** + * Get value from the map if available + */ +function _getValue(map /*: Nullable>*/, value /*: StoredValue*/) /*: StoredValue*/{ + return map ? map[value] !== undefined ? map[value] : value : value; } -module.exports = domAPI; - -/***/ }), - -/***/ 589: -/***/ ((module) => { - -"use strict"; +/** + * Convert key and value by defined scheme + */ +function _convert(storeName /*: StoreNameType*/, dir /*: Direction*/, key /*: string*/, value /*: StoredValue | StoredRecord*/, scheme /*: StoreFieldScheme*/) /*: [string, unknown]*/{ + if (!scheme) { + return [key, value]; + } + var encodedKey = isComplexStoreField(scheme) ? scheme.key : scheme; + if (valueIsRecord(value)) { + var keys = isNestingStoreField(scheme) ? scheme.keys : null; + return [encodedKey, convertRecord(storeName, dir, value, keys)]; + } + var valuesMap = isPredefinedValuesField(scheme) ? scheme.values : null; + return [encodedKey, _getValue(valuesMap, value)]; +} +/** + * Convert record by defined direction and scheme + */ -/* istanbul ignore next */ -function styleTagTransform(css, styleElement) { - if (styleElement.styleSheet) { - styleElement.styleSheet.cssText = css; - } else { - while (styleElement.firstChild) { - styleElement.removeChild(styleElement.firstChild); - } +/** + * Convert record by defined direction and scheme + */ - styleElement.appendChild(document.createTextNode(css)); +/** + * Convert record by defined direction and scheme + * Note: the function signature is duplicated because TS hides function implementation + */ +function convertRecord(storeName /*: StoreNameType*/, dir /*: Direction*/, record /*: Maybe*/, scheme /*: StoreFields*/) /*: Maybe*/{ + if (!record) { + return undefined; } + var _scheme /*: StoreFields*/ = scheme || scheme_map[dir][convertStoreName(storeName, Direction.right)].fields; + return entries(record).map(function (_ref) { + var _ref2 = _slicedToArray(_ref, 2), + key = _ref2[0], + value = _ref2[1]; + return _convert(storeName, dir, key, value, _scheme[key]); + }).reduce(function (acc, _ref3) { + var _ref4 = _slicedToArray(_ref3, 2), + key = _ref4[0], + value = _ref4[1]; + return _objectSpread2(_objectSpread2({}, acc), {}, _defineProperty({}, key, value)); + }, {}); } -module.exports = styleTagTransform; - -/***/ }), +/** + * Convert records by defined direction + */ +function convertRecords(storeName /*: StoreNameType*/, dir /*: Direction*/) /*: Array*/{ + var records /*: Array*/ = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : []; + return records.map(function (record) { + return convertRecord(storeName, dir, record); + }); +} -/***/ 529: -/***/ ((module) => { +/** + * Convert values by defined direction + */ +function convertValues(storeName /*: StoreNameType*/, dir /*: Direction*/, target /*: StoredRecordId*/) /*: StoredValue | Array*/{ + var scheme /*: StoreOptions*/ = scheme_map[dir][convertStoreName(storeName, Direction.right)]; + var keyPathScheme = scheme.fields[scheme.keyPath]; + var values = target instanceof Array ? target.slice() : [target]; + var keys = isCompositeKeyStoreField(keyPathScheme) ? keyPathScheme.composite : [scheme.keyPath]; + var converted = keys.map(function (key /*: string*/, index /*: number*/) { + var field = scheme.fields[key]; + var predefinedValuesMap = isPredefinedValuesField(field) ? field.values : null; + return _getValue(predefinedValuesMap, values[index]); + }); + return converted.length === 1 ? converted[0] : converted; +} -"use strict"; -module.exports = "data:image/svg+xml;utf8, "; +/** + * Encode value by defined scheme + */ +function encodeValue(target /*: StoredValue*/) /*: StoredValue*/{ + return scheme_map.values[target] || target; +} -/***/ }) +/** + * Convert store name by defined direction + */ +function convertStoreName(storeName /*: StoreNameType*/, dir /*: Direction*/) /*: StoreNameType*/{ + return (scheme_map.storeNames[dir][storeName] || {}).name || storeName; +} -/******/ }); -/************************************************************************/ -/******/ // The module cache -/******/ var __webpack_module_cache__ = {}; -/******/ -/******/ // The require function -/******/ function __webpack_require__(moduleId) { -/******/ // Check if module is in cache -/******/ var cachedModule = __webpack_module_cache__[moduleId]; -/******/ if (cachedModule !== undefined) { -/******/ return cachedModule.exports; -/******/ } -/******/ // Create a new module (and put it into the cache) -/******/ var module = __webpack_module_cache__[moduleId] = { -/******/ id: moduleId, -/******/ // no module.loaded needed -/******/ exports: {} -/******/ }; -/******/ -/******/ // Execute the module function -/******/ __webpack_modules__[moduleId].call(module.exports, module, module.exports, __webpack_require__); -/******/ -/******/ // Return the exports of the module -/******/ return module.exports; -/******/ } -/******/ -/******/ // expose the modules object (__webpack_modules__) -/******/ __webpack_require__.m = __webpack_modules__; -/******/ -/************************************************************************/ -/******/ /* webpack/runtime/compat get default export */ -/******/ (() => { -/******/ // getDefaultExport function for compatibility with non-harmony modules -/******/ __webpack_require__.n = (module) => { -/******/ var getter = module && module.__esModule ? -/******/ () => (module['default']) : -/******/ () => (module); -/******/ __webpack_require__.d(getter, { a: getter }); -/******/ return getter; -/******/ }; -/******/ })(); -/******/ -/******/ /* webpack/runtime/define property getters */ -/******/ (() => { -/******/ // define getter functions for harmony exports -/******/ __webpack_require__.d = (exports, definition) => { -/******/ for(var key in definition) { -/******/ if(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) { -/******/ Object.defineProperty(exports, key, { enumerable: true, get: definition[key] }); -/******/ } -/******/ } -/******/ }; -/******/ })(); -/******/ -/******/ /* webpack/runtime/global */ -/******/ (() => { -/******/ __webpack_require__.g = (function() { -/******/ if (typeof globalThis === 'object') return globalThis; -/******/ try { -/******/ return this || new Function('return this')(); -/******/ } catch (e) { -/******/ if (typeof window === 'object') return window; -/******/ } -/******/ })(); -/******/ })(); -/******/ -/******/ /* webpack/runtime/hasOwnProperty shorthand */ -/******/ (() => { -/******/ __webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop)) -/******/ })(); -/******/ -/******/ /* webpack/runtime/jsonp chunk loading */ -/******/ (() => { -/******/ __webpack_require__.b = document.baseURI || self.location.href; -/******/ -/******/ // object to store loaded and loading chunks -/******/ // undefined = chunk not loaded, null = chunk preloaded/prefetched -/******/ // [resolve, reject, Promise] = chunk loading, 0 = chunk loaded -/******/ var installedChunks = { -/******/ 91: 0, -/******/ 57: 0 -/******/ }; -/******/ -/******/ // no chunk on demand loading -/******/ -/******/ // no prefetching -/******/ -/******/ // no preloaded -/******/ -/******/ // no HMR -/******/ -/******/ // no HMR manifest -/******/ -/******/ // no on chunks loaded -/******/ -/******/ // no jsonp function -/******/ })(); -/******/ -/******/ /* webpack/runtime/nonce */ -/******/ (() => { -/******/ __webpack_require__.nc = undefined; -/******/ })(); -/******/ -/************************************************************************/ -var __webpack_exports__ = {}; -// This entry need to be wrapped in an IIFE because it need to be in strict mode. -(() => { -"use strict"; - -// EXPORTS -__webpack_require__.d(__webpack_exports__, { - "default": () => (/* binding */ main) -}); - -;// CONCATENATED MODULE: ./node_modules/@babel/runtime/helpers/esm/defineProperty.js -function _defineProperty(obj, key, value) { - if (key in obj) { - Object.defineProperty(obj, key, { - value: value, - enumerable: true, - configurable: true, - writable: true - }); - } else { - obj[key] = value; - } - return obj; +/** + * Decode error message by replacing short store name with long readable one + */ +function decodeErrorMessage(storeName /*: ShortStoreNames*/, error /*: Error*/) /*: Error*/{ + return { + name: error.name, + message: error.message.replace("\"".concat(storeName, "\""), convertStoreName(storeName, Direction.right)) + }; } -;// CONCATENATED MODULE: ./node_modules/@babel/runtime/helpers/esm/objectSpread2.js -function ownKeys(object, enumerableOnly) { - var keys = Object.keys(object); - if (Object.getOwnPropertySymbols) { - var symbols = Object.getOwnPropertySymbols(object); - enumerableOnly && (symbols = symbols.filter(function (sym) { - return Object.getOwnPropertyDescriptor(object, sym).enumerable; - })), keys.push.apply(keys, symbols); - } - return keys; -} -function _objectSpread2(target) { - for (var i = 1; i < arguments.length; i++) { - var source = null != arguments[i] ? arguments[i] : {}; - i % 2 ? ownKeys(Object(source), !0).forEach(function (key) { - _defineProperty(target, key, source[key]); - }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys(Object(source)).forEach(function (key) { - Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); - }); - } - return target; -} -;// CONCATENATED MODULE: ./node_modules/@babel/runtime/helpers/esm/objectWithoutPropertiesLoose.js -function _objectWithoutPropertiesLoose(source, excluded) { - if (source == null) return {}; - var target = {}; - var sourceKeys = Object.keys(source); - var key, i; - for (i = 0; i < sourceKeys.length; i++) { - key = sourceKeys[i]; - if (excluded.indexOf(key) >= 0) continue; - target[key] = source[key]; +;// CONCATENATED MODULE: ./node_modules/@babel/runtime/helpers/esm/classCallCheck.js +function _classCallCheck(instance, Constructor) { + if (!(instance instanceof Constructor)) { + throw new TypeError("Cannot call a class as a function"); } - return target; } -;// CONCATENATED MODULE: ./node_modules/@babel/runtime/helpers/esm/objectWithoutProperties.js +;// CONCATENATED MODULE: ./node_modules/@babel/runtime/helpers/esm/createClass.js -function _objectWithoutProperties(source, excluded) { - if (source == null) return {}; - var target = _objectWithoutPropertiesLoose(source, excluded); - var key, i; - if (Object.getOwnPropertySymbols) { - var sourceSymbolKeys = Object.getOwnPropertySymbols(source); - for (i = 0; i < sourceSymbolKeys.length; i++) { - key = sourceSymbolKeys[i]; - if (excluded.indexOf(key) >= 0) continue; - if (!Object.prototype.propertyIsEnumerable.call(source, key)) continue; - target[key] = source[key]; - } +function _defineProperties(target, props) { + for (var i = 0; i < props.length; i++) { + var descriptor = props[i]; + descriptor.enumerable = descriptor.enumerable || false; + descriptor.configurable = true; + if ("value" in descriptor) descriptor.writable = true; + Object.defineProperty(target, toPropertyKey(descriptor.key), descriptor); } - return target; } -;// CONCATENATED MODULE: ./node_modules/@babel/runtime/helpers/esm/arrayWithHoles.js -function _arrayWithHoles(arr) { - if (Array.isArray(arr)) return arr; +function _createClass(Constructor, protoProps, staticProps) { + if (protoProps) _defineProperties(Constructor.prototype, protoProps); + if (staticProps) _defineProperties(Constructor, staticProps); + Object.defineProperty(Constructor, "prototype", { + writable: false + }); + return Constructor; } -;// CONCATENATED MODULE: ./node_modules/@babel/runtime/helpers/esm/iterableToArrayLimit.js -function _iterableToArrayLimit(arr, i) { - var _i = arr == null ? null : typeof Symbol !== "undefined" && arr[Symbol.iterator] || arr["@@iterator"]; - if (_i == null) return; - var _arr = []; - var _n = true; - var _d = false; - var _s, _e; - try { - for (_i = _i.call(arr); !(_n = (_s = _i.next()).done); _n = true) { - _arr.push(_s.value); - if (i && _arr.length === i) break; - } - } catch (err) { - _d = true; - _e = err; - } finally { - try { - if (!_n && _i["return"] != null) _i["return"](); - } finally { - if (_d) throw _e; +;// CONCATENATED MODULE: ./src/sdk/time.js +/** + * Prepend zero to be used in certain format + * + * @param {number} value + * @param {number} power + * @returns {string} + * @private + */ +function _prependZero(value /*: number*/) /*: string*/{ + var power /*: number*/ = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 1; + var formatted = value + ''; + for (var i = 1; i <= power; i += 1) { + if (value < Math.pow(10, i)) { + formatted = "0".concat(formatted); } } - return _arr; -} -;// CONCATENATED MODULE: ./node_modules/@babel/runtime/helpers/esm/arrayLikeToArray.js -function _arrayLikeToArray(arr, len) { - if (len == null || len > arr.length) len = arr.length; - for (var i = 0, arr2 = new Array(len); i < len; i++) { - arr2[i] = arr[i]; - } - return arr2; + return formatted; } -;// CONCATENATED MODULE: ./node_modules/@babel/runtime/helpers/esm/unsupportedIterableToArray.js -function _unsupportedIterableToArray(o, minLen) { - if (!o) return; - if (typeof o === "string") return _arrayLikeToArray(o, minLen); - var n = Object.prototype.toString.call(o).slice(8, -1); - if (n === "Object" && o.constructor) n = o.constructor.name; - if (n === "Map" || n === "Set") return Array.from(o); - if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); -} -;// CONCATENATED MODULE: ./node_modules/@babel/runtime/helpers/esm/nonIterableRest.js -function _nonIterableRest() { - throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); +/** + * Get formatted date (YYYY-MM-DD) + * + * @param date + * @returns {string} + * @private + */ +function _getDate(date /*: Date*/) /*: string*/{ + var day = _prependZero(date.getDate()); + var month = _prependZero(date.getMonth() + 1); + var year = date.getFullYear(); + return [year, month, day].join('-'); } -;// CONCATENATED MODULE: ./node_modules/@babel/runtime/helpers/esm/slicedToArray.js - - - -function _slicedToArray(arr, i) { - return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _unsupportedIterableToArray(arr, i) || _nonIterableRest(); +/** + * Get formatted hours, minutes, seconds and milliseconds (HH:mm:ss.SSS) + * + * @param {Date} date + * @returns {string} + * @private + */ +function _getTime(date /*: Date*/) /*: string*/{ + var hours = _prependZero(date.getHours(), 1); + var minutes = _prependZero(date.getMinutes()); + var seconds = _prependZero(date.getSeconds()); + var milliseconds = _prependZero(date.getMilliseconds(), 2); + return [hours, minutes, seconds].join(':') + '.' + milliseconds; } -;// CONCATENATED MODULE: ./node_modules/@babel/runtime/helpers/esm/arrayWithoutHoles.js -function _arrayWithoutHoles(arr) { - if (Array.isArray(arr)) return _arrayLikeToArray(arr); +/** + * Get formatted timezone (ZZ) + * + * @param {Date} date + * @returns {string} + * @private + */ +function _getTimezone(date /*: Date*/) /*: string*/{ + var offsetInMinutes = date.getTimezoneOffset(); + var hoursOffset = _prependZero(Math.floor(Math.abs(offsetInMinutes) / 60)); + var minutesOffset = _prependZero(Math.abs(offsetInMinutes) % 60); + var sign = offsetInMinutes > 0 ? '-' : '+'; + return sign + hoursOffset + minutesOffset; } -;// CONCATENATED MODULE: ./node_modules/@babel/runtime/helpers/esm/iterableToArray.js -function _iterableToArray(iter) { - if (typeof Symbol !== "undefined" && iter[Symbol.iterator] != null || iter["@@iterator"] != null) return Array.from(iter); + +/** + * Get the timestamp in the backend format + * + * @param {number=} timestamp + * @returns {string} + */ +function getTimestamp(timestamp /*: number*/) /*: string*/{ + var d = timestamp ? new Date(timestamp) : new Date(); + var date = _getDate(d); + var time = _getTime(d); + var timezone = _getTimezone(d); + return "".concat(date, "T").concat(time, "Z").concat(timezone); } -;// CONCATENATED MODULE: ./node_modules/@babel/runtime/helpers/esm/nonIterableSpread.js -function _nonIterableSpread() { - throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); + +/** + * Calculate time passed between two dates in milliseconds + * + * @param {number} d1 + * @param {number} d2 + * @returns {number} + */ +function timePassed(d1 /*: number*/, d2 /*: number*/) /*: number*/{ + if (isNaN(d1) || isNaN(d2)) { + return 0; + } + return Math.abs(d2 - d1); } -;// CONCATENATED MODULE: ./node_modules/@babel/runtime/helpers/esm/toConsumableArray.js +;// CONCATENATED MODULE: ./src/sdk/pub-sub.ts +/** + * List of events with subscribed callbacks + */ +var _list /*: Record>*/ = {}; -function _toConsumableArray(arr) { - return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _unsupportedIterableToArray(arr) || _nonIterableSpread(); +/** + * Reference to timeout ids so they can be cleared on destroy + */ +var _timeoutIds /*: Array>*/ = []; + +/** + * Get unique id for the callback to use for unsubscribe + */ +function _getId() /*: string*/{ + return 'id' + Math.random().toString(36).substring(2, 16); } -;// CONCATENATED MODULE: ./src/sdk/constants.js -var SECOND = 1000; -var MINUTE = SECOND * 60; -var HOUR = MINUTE * 60; -var DAY = HOUR * 24; -var REASON_GENERAL = 'general'; -var REASON_GDPR = 'gdpr'; -var HTTP_ERRORS = { - 'TRANSACTION_ERROR': 'XHR transaction failed due to an error', - 'SERVER_MALFORMED_RESPONSE': 'Response from server is malformed', - 'SERVER_INTERNAL_ERROR': 'Internal error occurred on the server', - 'SERVER_CANNOT_PROCESS': 'Server was not able to process the request, probably due to error coming from the client', - 'NO_CONNECTION': 'No internet connectivity', - 'SKIP': 'Skipping slower attempt', - 'MISSING_URL': 'Url is not provided' -}; -var STORAGE_TYPES = { - NO_STORAGE: 'noStorage', - INDEXED_DB: 'indexedDB', - LOCAL_STORAGE: 'localStorage' -}; -var ENDPOINTS = { - default: { - endpointName: 'Default', - app: 'https://app.adjust.com', - gdpr: 'https://gdpr.adjust.com' - }, - india: { - endpointName: 'Indian', - app: 'https://app.adjust.net.in', - gdpr: 'https://gdpr.adjust.net.in' - }, - china: { - endpointName: 'Chinese', - app: 'https://app.adjust.world', - gdpr: 'https://gdpr.adjust.world' - }, - EU: { - endpointName: 'EU', - app: 'https://app.eu.adjust.com', - gdpr: 'https://gdpr.eu.adjust.com' - }, - TR: { - endpointName: 'TR', - app: 'https://app.tr.adjust.com', - gdpr: 'https://gdpr.tr.adjust.com' - }, - US: { - endpointName: 'US', - app: 'https://app.us.adjust.com', - gdpr: 'https://gdpr.us.adjust.com' + +/** + * Subscribe to a certain event + */ +function subscribe /*:: */(name /*: string*/, cb /*: (name: string, arg: T) => unknown*/) /*: string*/{ + var id = _getId(); + var callback /*: CallbackWithId*/ = { + id: id, + cb: cb + }; + if (!_list[name]) { + _list[name] = []; } -}; -;// CONCATENATED MODULE: ./node_modules/@babel/runtime/helpers/esm/typeof.js -function _typeof(obj) { - "@babel/helpers - typeof"; + _list[name].push(callback); + return id; +} + +/** + * Unsubscribe particular callback from an event + */ +function unsubscribe(id /*: string*/) { + if (!id) { + return; + } + entries(_list).some(function (_ref) { + var _ref2 = _slicedToArray(_ref, 2), + callbacks = _ref2[1]; + return callbacks.some(function + /*:: */ + (callback /*: CallbackWithId*/, i /*: number*/) { + if (callback.id === id) { + callbacks.splice(i, 1); + } + }); + }); +} - return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (obj) { - return typeof obj; - } : function (obj) { - return obj && "function" == typeof Symbol && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; - }, _typeof(obj); +/** + * Publish certain event with optional arguments + */ +function publish /*:: */(name /*: string*/, args /*: T*/) /*: void*/{ + if (!_list[name]) { + return; + } + _list[name].forEach(function (item /*: CallbackWithId*/) { + if (typeof item.cb === 'function') { + _timeoutIds.push(setTimeout(function () { + return item.cb(name, args); + })); + } + }); } -;// CONCATENATED MODULE: ./src/sdk/utilities.ts + +/** + * Destroy all registered events with their callbacks + */ +function pub_sub_destroy() /*: void*/{ + _timeoutIds.forEach(clearTimeout); + _timeoutIds = []; + _list = {}; +} + +;// CONCATENATED MODULE: ./src/sdk/activity-state.js + +/*:: // +import { type UrlT, type ActivityStateMapT, type AttributionMapT, type CommonRequestParams } from './types';*/ + + + + /** - * Build human readable list + * Reference to the activity state + * + * @type {Object} + * @private */ -function buildList(array /*: Array*/) /*: string*/{ - if (!array.length) { - return ''; - } - if (array.length === 1) { - return "".concat(array[0]); - } - var lastIndex = array.length - 1; - var firstPart = array.slice(0, lastIndex).join(', '); - return "".concat(firstPart, " and ").concat(array[lastIndex]); +var _activityState /*: ActivityStateMapT*/ = {}; + +/** + * Started flag, if activity state has been initiated + * + * @type {boolean} + * @private + */ +var _started /*: boolean*/ = false; + +/** + * Active flag, if in foreground + * + * @type {boolean} + * @private + */ +var _active /*: boolean*/ = false; + +/** + * Get current activity state + * + * @returns {Object} + */ +function currentGetter() /*: ActivityStateMapT*/{ + return _started ? _objectSpread2({}, _activityState) : {}; } /** - * Check if object is empty + * Set current activity state + * + * @param {Object} params */ -function isEmpty(obj /*: Record*/) /*: boolean*/{ - return !Object.keys(obj).length && obj.constructor === Object; +function currentSetter() { + var params /*: ActivityStateMapT*/ = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; + _activityState = _started ? _objectSpread2({}, params) : {}; } /** - * Check if value is object + * Initiate in-memory activity state + * + * @param {Object} params */ -function isObject(obj /*: any*/) /*: boolean*/{ - // eslint-disable-line @typescript-eslint/no-explicit-any - return _typeof(obj) === 'object' && obj !== null && !(obj instanceof Array); +function init(params /*: ActivityStateMapT*/) { + _started = true; + currentSetter(params); } /** - * Check if string is valid json + * Check if activity state is started + * + * @returns {boolean} */ -function isValidJson(string /*: string*/) /*: boolean*/{ - try { - var json = JSON.parse(string); - return isObject(json); - } catch (e) { - return false; - } +function isStarted() { + return _started; } /** - * Find index of an element in the list and return it + * Update last active point + * + * @private */ -function findIndex /*:: >*/(array /*: Array*/, key /*: K | Array*/, target /*: T*/) /*: number*/{ - function isEqual(item /*: T*/) { - return Array.isArray(key) ? key.every(function (k) { - return item[k] === target[k]; - }) : item[key] === target; - } - for (var i = 0; i < array.length; i += 1) { - if (isEqual(array[i])) { - return i; - } +function updateLastActive() /*: void*/{ + if (!_started) { + return; } - return -1; + _activityState.lastInterval = _getLastInterval(); + _activityState.lastActive = Date.now(); } /** - * Convert array with key/value item structure into key/value pairs object + * Update activity state with new params + * + * @param {Object} params + * @private */ -function convertToMap /*:: */() /*: Record*/{ - var array /*: Array<{ key: string, value: T }>*/ = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : []; - return array.reduce(function (acc, o) { - return _objectSpread2(_objectSpread2({}, acc), {}, _defineProperty({}, o.key, o.value)); - }, {}); +function _update(params /*: ActivityStateMapT*/) /*: void*/{ + _activityState = _objectSpread2(_objectSpread2({}, _activityState), params); } /** - * Find intersecting values of provided array against given values + * Set active flag to true when going foreground */ -function intersection /*:: */() /*: Array*/{ - var array /*: Array*/ = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : []; - var values /*: Array*/ = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : []; - return array.filter(function (item) { - return values.indexOf(item) !== -1; - }); +function toForeground() /*: void*/{ + _active = true; } /** - * Check if particular url is a certain request + * Set active flag to false when going background */ -function isRequest(url /*: string*/, requestName /*: string*/) /*: boolean*/{ - var regex = new RegExp("\\/".concat(requestName, "(\\/.*|\\?.*){0,1}$")); - return regex.test(url); +function toBackground() /*: void*/{ + _active = false; } /** - * Extract the host name for the url + * Get time offset from the last active point + * + * @returns {number} + * @private */ -function getHostName() /*: string*/{ - var url = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : ''; - return url.replace(/^(http(s)*:\/\/)*(www\.)*/, '').split('/')[0].split('?')[0]; +function _getOffset() /*: number*/{ + var lastActive = _activityState.lastActive; + return Math.round(timePassed(lastActive, Date.now()) / SECOND); } /** - * Transform array entry into object key:value pair entry + * Get time spent with optional offset from last point + * + * @returns {number} + * @private */ -function reducer /*:: */(acc /*: Record*/, _ref /*:: */) /*: Record*/{ - var _ref2 = _slicedToArray(_ref /*:: */, 2), - key = _ref2[0], - value = _ref2[1]; - return _objectSpread2(_objectSpread2({}, acc), {}, _defineProperty({}, key, value)); +function _getTimeSpent() /*: number*/{ + return (_activityState.timeSpent || 0) + (_active ? _getOffset() : 0); } /** - * Extracts object entries in the [key, value] format + * Get session length with optional offset from last point + * + * @returns {number} + * @private */ -function entries /*:: */(object /*: Record*/) /*: Array<[K, T]>*/{ - return Object.keys(object).map(function (key /*: K*/) { - return [key, object[key]]; - }); +function _getSessionLength() /*: number*/{ + var lastActive = _activityState.lastActive; + var withinWindow = timePassed(lastActive, Date.now()) < sdk_config.sessionWindow; + var withOffset = _active || !_active && withinWindow; + return (_activityState.sessionLength || 0) + (withOffset ? _getOffset() : 0); } /** - * Extracts object values + * Get total number of sessions so far + * + * @returns {number} + * @private */ -function values /*:: */(object /*: Record*/) /*: Array*/{ - return Object.keys(object).map(function (key /*: string*/) { - return object[key]; - }); +function _getSessionCount() /*: number*/{ + return _activityState.sessionCount || 0; } /** - * Check if value is empty in any way (empty object, false value, zero) and use it as predicate method + * Get total number of events so far + * + * @returns {number} + * @private */ -function isEmptyEntry(value /*: any*/) /*: boolean*/{ - // eslint-disable-line @typescript-eslint/no-explicit-any - if (isObject(value)) { - return !isEmpty(value); - } - return !!value || value === 0; -} -function isLocalStorageSupported() /*: boolean*/{ - try { - var uid = new Date().toString(); - var storage = window.localStorage; - storage.setItem(uid, uid); - var result = storage.getItem(uid) === uid; - storage.removeItem(uid); - var support = !!(result && storage); - return support; - } catch (e) { - return false; - } +function _getEventCount() /*: number*/{ + return _activityState.eventCount || 0; } -;// CONCATENATED MODULE: ./src/sdk/globals.js -/*:: declare var __ADJUST__NAMESPACE: string*/ -/*:: declare var __ADJUST__SDK_VERSION: string*/ -/*:: declare var process: {| - env: {| - NODE_ENV: 'development' | 'production' | 'test' - |} -|}*/ -var Globals = { - namespace: "adjust-sdk" || 0, - version: "5.6.0" || 0, - env: "production" -}; -/* harmony default export */ const globals = (Globals); -;// CONCATENATED MODULE: ./src/sdk/logger.js - -var _levels2; - - -/*:: import { type LogOptionsT } from './types';*/ -/*:: type LogLevelT = $PropertyType*/ -/*:: type MethodNameT = 'log' | 'info' | 'error' | 'warn'*/ -var LEVEL_NONE = 'none'; -var LEVEL_ERROR = 'error'; -var LEVEL_WARNING = 'warning'; -var LEVEL_INFO = 'info'; -var LEVEL_VERBOSE = 'verbose'; - -/** - * Logger levels - * - none -> nothing is printed to the console - * - error -> prints only error - * - info -> prints info and error - * - verbose -> prints log, info and error - * - * @type {Object} - * @private - */ -var _levels = (_levels2 = {}, _defineProperty(_levels2, LEVEL_NONE, -1), _defineProperty(_levels2, LEVEL_ERROR, 0), _defineProperty(_levels2, LEVEL_WARNING, 1), _defineProperty(_levels2, LEVEL_INFO, 2), _defineProperty(_levels2, LEVEL_VERBOSE, 3), _levels2); - -/** - * Spaces placed after log level tag in console to align messages. - * - * @type {Object} - * @private - */ -var _spaces = { - 'log': ' ', - 'info': ' ', - 'warn': ' ', - 'error': '' -}; - -/** - * Default logger level per environment - * - * @type {Object} - * @private - */ -var _envLogLevels = { - development: LEVEL_VERBOSE, - production: LEVEL_ERROR, - test: LEVEL_VERBOSE -}; - -/** - * Current logger level - */ -var _level = _getDefaultLogLevel(); - /** - * Optional output container to display logs for easier debugging + * Get time passed since last activity was recorded * - * @type {string} + * @returns {number} * @private */ -var _output = ''; +function _getLastInterval() /*: number*/{ + var lastActive = _activityState.lastActive; + if (lastActive) { + return Math.round(timePassed(lastActive, Date.now()) / SECOND); + } + return -1; +} /** - * Get default logger error per environment and fallback to error level when unknown env - * - * @returns {string} - * @private + * Initiate session params and go to foreground */ -function _getDefaultLogLevel() /*: LogLevelT*/{ - return _envLogLevels[globals.env] || LEVEL_ERROR; +function initParams() /*: void*/{ + updateSessionOffset(); + toForeground(); } /** - * Set logger level, fallback to default log level + * Get activity state params that are sent with each request * - * @param {string=} logLevel - * @param {string=} logOutput + * @returns {Object} */ -function setLogLevel(logLevel /*: LogLevelT*/, logOutput /*: string*/) /*: void*/{ - var exists = !logLevel || Object.keys(_levels).indexOf(logLevel) !== -1; - if (!exists) { - _log('error', 'error', 'You must set one of the available log levels: verbose, info, warning, error or none'); - return; +function getParams(url /*: UrlT*/) /*: ?CommonRequestParams*/{ + if (!_started) { + return null; } - _level = logLevel || _getDefaultLogLevel(); - _output = logOutput || _output; - _log('info', logLevel, "Log level set to ".concat(_level)); + var lastInterval = _activityState.lastInterval >= 0 ? _activityState.lastInterval : 0; + var baseParams /*: CommonRequestParams*/ = { + timeSpent: _activityState.timeSpent || 0, + sessionLength: _activityState.sessionLength || 0, + sessionCount: _activityState.sessionCount || 1, + lastInterval: lastInterval || 0 + }; + if (url && isRequest(url, 'event')) { + baseParams.eventCount = _activityState.eventCount; + } + return baseParams; } /** - * Output the message to the console + * Update activity state parameters depending on the endpoint which has been run * - * @param {string} methodName - * @param {string} logLevel - * @param {Array} args - * @private + * @param {string} url + * @param {boolean=false} auto */ -function _log(methodName /*: MethodNameT*/, logLevel /*: LogLevelT*/) /*: void*/{ - var _console; - if (_levels[_level] < _levels[logLevel]) { +function updateParams(url /*: string*/, auto /*: boolean*/) /*: void*/{ + if (!_started) { return; } - var time = new Date().toISOString(); - var spaces = _spaces[methodName]; - var messagePrefix = ["[".concat(globals.namespace, "]"), time, "".concat(methodName.toUpperCase(), ":").concat(spaces)]; - var outputContainer = _output ? document.querySelector(_output) : null; - for (var _len = arguments.length, args = new Array(_len > 2 ? _len - 2 : 0), _key = 2; _key < _len; _key++) { - args[_key - 2] = arguments[_key]; + var params = {}; + params.timeSpent = _getTimeSpent(); + params.sessionLength = _getSessionLength(); + if (isRequest(url, 'session')) { + params.sessionCount = _getSessionCount() + 1; } - (_console = console)[methodName].apply(_console, messagePrefix.concat(args)); // eslint-disable-line - - if (outputContainer) { - outputContainer.textContent += "".concat(messagePrefix.join(' '), " ").concat(args.map(function (m) { - return isObject(m) ? JSON.stringify(m) : m; - }).join(' '), "\n"); - outputContainer.scrollTop = outputContainer.scrollHeight; + if (isRequest(url, 'event')) { + params.eventCount = _getEventCount() + 1; + } + _update(params); + if (!auto) { + updateLastActive(); } } /** - * Apply predefined log level and return log method - * - * @param {string} name - * @param {string} logLevel - * @returns {Function: (Array) => void} - * @private + * Update installed flag - first session has been finished */ -function _applyLevel(name /*: MethodNameT*/, logLevel /*: LogLevelT*/) { - return function () { - for (var _len2 = arguments.length, args = new Array(_len2), _key2 = 0; _key2 < _len2; _key2++) { - args[_key2] = arguments[_key2]; - } - _log.apply(void 0, [name, logLevel].concat(args)); - }; +function updateInstalled() /*: void*/{ + if (!_started) { + return; + } + if (_activityState.installed) { + return; + } + _update({ + installed: true + }); } -var Logger = { - setLogLevel: setLogLevel, - log: _applyLevel('log', LEVEL_VERBOSE), - info: _applyLevel('info', LEVEL_INFO), - warn: _applyLevel('warn', LEVEL_WARNING), - error: _applyLevel('error', LEVEL_ERROR) -}; -/* harmony default export */ const logger = (Logger); -;// CONCATENATED MODULE: ./src/sdk/config.js - - - -/*:: // -import { type BaseParamsT, type CustomConfigT, type InitOptionsT, type BaseParamsListT, type BaseParamsMandatoryListT, type CustomConfigListT } from './types';*/ - - - /** - * Base parameters set by client - * - app token - * - environment - * - default tracker - * - external device ID - * - * @type {Object} - * @private + * Update session params which depend on the time offset since last measure point */ -var _baseParams /*: BaseParamsT*/ = {}; +function updateSessionOffset() /*: void*/{ + if (!_started) { + return; + } + var timeSpent = _getTimeSpent(); + var sessionLength = _getSessionLength(); + _update({ + timeSpent: timeSpent, + sessionLength: sessionLength + }); + updateLastActive(); +} /** - * Custom config set by client - * - url override - * - event deduplication list limit - * - * @type {Object} - * @private + * Update session length */ -var _customConfig /*: CustomConfigT*/ = {}; +function updateSessionLength() /*: void*/{ + if (!_started) { + return; + } + var sessionLength = _getSessionLength(); + _update({ + sessionLength: sessionLength + }); + updateLastActive(); +} /** - * Mandatory fields to set for sdk initialization - * - * @type {string[]} - * @private + * Reset time spent and session length to zero */ -var _mandatory /*: BaseParamsMandatoryListT*/ = ['appToken', 'environment']; +function resetSessionOffset() /*: void*/{ + if (!_started) { + return; + } + _update({ + timeSpent: 0, + sessionLength: 0 + }); +} /** - * Allowed params to be sent with each request - * - * @type {string[]} - * @private + * Destroy current activity state */ -var _allowedParams /*: BaseParamsListT*/ = [].concat(_mandatory, ['defaultTracker', 'externalDeviceId']); - +function activity_state_destroy() /*: void*/{ + _activityState = {}; + _started = false; + _active = false; +} +function getAttribution() /*: AttributionMapT | null*/{ + if (!_started) { + return null; + } + if (!_activityState.attribution) { + logger.log('No attribution data yet'); + return null; + } + return _activityState.attribution; +} +function waitForAttribution() /*: Promise*/{ + if (_activityState.attribution) { + return Promise.resolve(_activityState.attribution); + } + return new Promise(function (resolve) { + return subscribe(PUB_SUB_EVENTS.ATTRIBUTION_RECEIVED, function (_name /*: string*/, attribution /*: AttributionMapT*/) { + return resolve(attribution); + }); + }); +} +function getWebUUID() /*: string*/{ + if (!_started) { + return null; + } + return _activityState.uuid; +} +function waitForWebUUID() /*: Promise*/{ + if (_activityState.uuid) { + return Promise.resolve(_activityState.uuid); + } + return new Promise(function (resolve) { + return subscribe(PUB_SUB_EVENTS.WEB_UUID_CREATED, function (_name /*: string*/, webUuid /*: string*/) { + return resolve(webUuid); + }); + }); +} +var ActivityState = { + get current() { + return currentGetter(); + }, + set current(value) { + currentSetter(value); + }, + init: init, + isStarted: isStarted, + toForeground: toForeground, + toBackground: toBackground, + initParams: initParams, + getParams: getParams, + updateParams: updateParams, + updateInstalled: updateInstalled, + updateSessionOffset: updateSessionOffset, + updateSessionLength: updateSessionLength, + resetSessionOffset: resetSessionOffset, + updateLastActive: updateLastActive, + destroy: activity_state_destroy, + getAttribution: getAttribution, + getWebUUID: getWebUUID, + waitForAttribution: waitForAttribution, + waitForWebUUID: waitForWebUUID +}; +/* harmony default export */ const activity_state = (ActivityState); +;// CONCATENATED MODULE: ./src/sdk/storage/quick-storage.ts + + + + + + + + + +var InMemoryStorage = /*#__PURE__*/function () { + function InMemoryStorage() { + _classCallCheck(this, InMemoryStorage); + _defineProperty(this, "items", {}); + } + return _createClass(InMemoryStorage, [{ + key: "getItem", + value: function getItem(key /*: string*/) /*: string | null*/{ + return Object.prototype.hasOwnProperty.call(this.items, key) ? this.items[key] : null; + } + }, { + key: "removeItem", + value: function removeItem(key /*: string*/) /*: void*/{ + delete this.items[key]; + } + }, { + key: "setItem", + value: function setItem(key /*: string*/, value /*: string*/) /*: void*/{ + this.items[key] = value; + } + }]); +}(); +var QuickStorage = /*#__PURE__*/function () { + function QuickStorage() { + var _this = this; + _classCallCheck(this, QuickStorage); + _defineProperty(this, "defaultName", globals.namespace); + _defineProperty(this, "storageName", this.defaultName); + _defineProperty(this, "storeNames", scheme_map.storeNames.left); + this.storesMap = {}; + if (isLocalStorageSupported()) { + this.storage = window.localStorage; + } else { + this.storage = new InMemoryStorage(); + } + var read = this.read.bind(this); + var write = this.write.bind(this); + values(this.storeNames).forEach(function (store) { + var shortStoreName = store.name; + Object.defineProperty(_this.storesMap, shortStoreName, { + get: function get() { + return read(shortStoreName); + }, + set: function set(value) { + write(shortStoreName, value); + } + }); + }); + Object.freeze(this.storesMap); + } + + /** + * Sets custom name to use in data keys and updates existing keys in localStorage + */ + return _createClass(QuickStorage, [{ + key: "read", + value: + /** + * Get the value for specified key + */ + function read(key /*: ShortStoreName | ShortPreferencesStoreName*/) /*: Nullable*/{ + var valueToParse = this.storage.getItem("".concat(this.storageName, ".").concat(key)); + var value = valueToParse ? JSON.parse(valueToParse) : null; + if (key === ShortPreferencesStoreName.Preferences && value) { + return convertRecord(ShortPreferencesStoreName.Preferences, Direction.right, value); + } + return value; + } + + /** + * Set the value for specified key + */ + }, { + key: "write", + value: function write(key /*: ShortStoreName | ShortPreferencesStoreName*/, value /*: StoreContent*/) { + if (!value) { + this.storage.removeItem("".concat(this.storageName, ".").concat(key)); + } else { + this.storage.setItem("".concat(this.storageName, ".").concat(key), JSON.stringify(value instanceof Array ? value : convertRecord(ShortPreferencesStoreName.Preferences, Direction.left, value))); + } + } + + /** + * Clear all data related to the sdk + */ + }, { + key: "clear", + value: function clear() { + this.deleteData(); + } + + /** + * Clear all data related to the sdk + * + * @param wipe if true then also remove permanent data such as user's preferences + */ + }, { + key: "deleteData", + value: function deleteData() { + var _this2 = this; + var wipe = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false; + values(this.storeNames).forEach(function (store) { + if (wipe || !store.permanent) { + _this2.storage.removeItem("".concat(_this2.storageName, ".").concat(store.name)); + } + }); + } + }, { + key: "setCustomName", + value: function setCustomName(customName /*: string*/) { + var _this3 = this; + if (!customName || !customName.length) { + return; + } + var newName = "".concat(globals.namespace, "-").concat(customName); + + // Clone data + values(this.storeNames).forEach(function (store) { + var key = store.name; + var rawData = _this3.storage.getItem("".concat(_this3.storageName, ".").concat(key)); // Get data from the store, no need to encode it + if (rawData) { + _this3.storage.setItem("".concat(newName, ".").concat(key), rawData); // Put data into a new store + } + }); + this.deleteData(true); + this.storageName = newName; + } + }, { + key: "stores", + get: function get() { + return this.storesMap; + } + }]); +}(); +/* harmony default export */ const quick_storage = (new QuickStorage()); +;// CONCATENATED MODULE: ./src/sdk/preferences.ts + + + + /** - * Allowed configuration overrides - * - * @type {string[]} - * @private + * Name of the store used by preferences */ -var _allowedConfig /*: CustomConfigListT*/ = ['customUrl', 'dataResidency', 'urlStrategy', 'eventDeduplicationListLimit', 'namespace']; +var _storeName /*: string*/ = storage_scheme.preferences.name; /** - * Global configuration object used across the sdk - * - * @type {{ - * namespace: string, - * version: string, - * sessionWindow: number, - * sessionTimerWindow: number, - * requestValidityWindow: number - * }} + * Local reference to be used for recovering preserved state */ -var _baseConfig = { - sessionWindow: 30 * MINUTE, - sessionTimerWindow: 60 * SECOND, - requestValidityWindow: 28 * DAY -}; +var _preferences /*: PreferencesT | null*/ = null; +_preferences = _getPreferences(); /** - * Check of configuration has been initialized + * Get preferences stored in the localStorage * - * @returns {boolean} + * @returns {Object} + * @private */ -function isInitialised() /*: boolean*/{ - return _mandatory.reduce(function (acc, key) { - return acc && !!_baseParams[key]; - }, true); +function _getPreferences() /*: PreferencesT | null*/{ + if (!_preferences) { + _setPreferences(); + } + return _preferences ? _objectSpread2({}, _preferences) : null; } /** - * Get base params set by client + * Set local reference of the preserved preferences * - * @returns {Object} + * @private */ -function getBaseParams() /*: BaseParamsT*/{ - return _objectSpread2({}, _baseParams); +function _setPreferences() /*: void*/{ + _preferences = quick_storage.stores[_storeName]; } /** - * Set base params and custom config for the sdk to run + * Get current disabled state * - * @param {Object} options + * @returns {Object|null} */ -function set(options /*: InitOptionsT*/) /*: void*/{ - if (hasMissing(options)) { - return; - } - var filteredParams = [].concat(_toConsumableArray(_allowedParams), _allowedConfig).filter(function (key) { - return !!options[key]; - }).map(function (key) { - return [key, options[key]]; - }); - _baseParams = filteredParams.filter(function (_ref) { - var _ref2 = _slicedToArray(_ref, 1), - key = _ref2[0]; - return _allowedParams.indexOf(key) !== -1; - }).reduce(reducer, {}); - _customConfig = filteredParams.filter(function (_ref3) { - var _ref4 = _slicedToArray(_ref3, 1), - key = _ref4[0]; - return _allowedConfig.indexOf(key) !== -1; - }).reduce(reducer, {}); +function getDisabled() /*: SdkDisabledT | null*/{ + var preferences = _getPreferences(); + return preferences && preferences.sdkDisabled || null; } /** - * Get custom config set by client + * Set current disabled state * - * @returns {Object} + * @param {Object|null} value */ -function getCustomConfig() /*: CustomConfigT*/{ - return _objectSpread2({}, _customConfig); +function setDisabled(value /*: SdkDisabledT | null*/) /*: void*/{ + var sdkDisabled = value ? _objectSpread2({}, value) : null; + quick_storage.stores[_storeName] = _objectSpread2(_objectSpread2({}, _getPreferences()), {}, { + sdkDisabled: sdkDisabled + }); + _setPreferences(); } /** - * Check if there are missing mandatory parameters - * - * @param {Object} params - * @returns {boolean} - * @private + * Reload current preferences from localStorage if changed outside of current scope (e.g. tab) */ -function hasMissing(params /*: BaseParamsT*/) /*: boolean*/{ - var missing = _mandatory.filter(function (value) { - return !params[value]; - }); - if (missing.length) { - logger.error("You must define ".concat(buildList(missing))); - return true; +function reload() /*: void*/{ + var stored /*: PreferencesT*/ = quick_storage.stores[_storeName] || {}; + var sdkDisabled /*: SdkDisabledT | null*/ = (_preferences || {}).sdkDisabled || null; + if (stored.sdkDisabled && !sdkDisabled) { + publish('sdk:shutdown'); } - return false; + _setPreferences(); } /** - * Restore config to its default state + * Recover preferences from memory if storage was lost */ -function destroy() /*: void*/{ - _baseParams = {}; - _customConfig = {}; +function recover() /*: void*/{ + var stored /*: PreferencesT*/ = quick_storage.stores[_storeName]; + if (!stored) { + quick_storage.stores[_storeName] = _objectSpread2({}, _preferences); + } } -var Config = _objectSpread2(_objectSpread2({}, _baseConfig), {}, { - set: set, - getBaseParams: getBaseParams, - getCustomConfig: getCustomConfig, - isInitialised: isInitialised, - hasMissing: hasMissing, - destroy: destroy -}); -/* harmony default export */ const config = (Config); -;// CONCATENATED MODULE: ./src/sdk/storage/scheme.ts -var _values2; +;// CONCATENATED MODULE: ./src/sdk/storage/indexeddb.ts -var StoreName; -(function (StoreName) { - StoreName["Queue"] = "queue"; - StoreName["ActivityState"] = "activityState"; - StoreName["GlobalParams"] = "globalParams"; - StoreName["EventDeduplication"] = "eventDeduplication"; -})(StoreName || (StoreName = {})); -var PreferencesStoreName; -(function (PreferencesStoreName) { - PreferencesStoreName["Preferences"] = "preferences"; -})(PreferencesStoreName || (PreferencesStoreName = {})); -var ShortStoreName; -(function (ShortStoreName) { - ShortStoreName["Queue"] = "q"; - ShortStoreName["ActivityState"] = "as"; - ShortStoreName["GlobalParams"] = "gp"; - ShortStoreName["EventDeduplication"] = "ed"; -})(ShortStoreName || (ShortStoreName = {})); -var ShortPreferencesStoreName; -(function (ShortPreferencesStoreName) { - ShortPreferencesStoreName["Preferences"] = "p"; -})(ShortPreferencesStoreName || (ShortPreferencesStoreName = {})); -var _queueScheme /*: StoreOptions*/ = { - keyPath: 'timestamp', - autoIncrement: false, - fields: { - url: { - key: 'u', - values: { - '/session': 1, - '/event': 2, - '/gdpr_forget_device': 3, - '/sdk_click': 4, - '/disable_third_party_sharing': 5 - } - }, - method: { - key: 'm', - values: { - GET: 1, - POST: 2, - PUT: 3, - DELETE: 4 - } - }, - timestamp: 't', - createdAt: 'ca', - params: { - key: 'p', - keys: { - timeSpent: 'ts', - sessionLength: 'sl', - sessionCount: 'sc', - eventCount: 'ec', - lastInterval: 'li', - eventToken: 'et', - revenue: 're', - currency: 'cu', - callbackParams: 'cp', - partnerParams: 'pp' - } - } - } -}; -var _activityStateScheme /*: StoreOptions*/ = { - keyPath: 'uuid', - autoIncrement: false, - fields: { - uuid: { - key: 'u', - values: { - unknown: '-' - } - }, - timeSpent: 'ts', - sessionLength: 'sl', - sessionCount: 'sc', - eventCount: 'ec', - lastActive: 'la', - lastInterval: 'li', - installed: { - key: 'in', - values: { - false: 0, - true: 1 - } - }, - attribution: { - key: 'at', - keys: { - adid: 'a', - tracker_token: 'tt', - tracker_name: 'tn', - network: 'nt', - campaign: 'cm', - adgroup: 'ag', - creative: 'cr', - click_label: 'cl', - state: { - key: 'st', - values: { - installed: 1, - reattributed: 2 - } - } - } - } - } -}; -var _globalParamsScheme /*: StoreOptions*/ = { - keyPath: 'keyType', - autoIncrement: false, - index: 'type', - fields: { - keyType: { - key: 'kt', - composite: ['key', 'type'] - }, - key: 'k', - value: 'v', - type: { - key: 't', - values: { - callback: 1, - partner: 2 - } - } - } -}; -var _eventDeduplicationScheme /*: StoreOptions*/ = { - keyPath: 'internalId', - autoIncrement: true, - fields: { - internalId: 'ii', - id: 'i' - } -}; -var _preferencesScheme /*: StoreOptionsOptionalKey*/ = { - fields: { - thirdPartySharingDisabled: { - key: 'td', - keys: { - reason: { - key: 'r', - values: _defineProperty({}, REASON_GENERAL, 1) - }, - pending: { - key: 'p', - values: { - false: 0, - true: 1 - } - } - } - }, - sdkDisabled: { - key: 'sd', - keys: { - reason: { - key: 'r', - values: (_values2 = {}, _defineProperty(_values2, REASON_GENERAL, 1), _defineProperty(_values2, REASON_GDPR, 2), _values2) - }, - pending: { - key: 'p', - values: { - false: 0, - true: 1 - } - } - } - } - } -}; -var scheme /*: Scheme*/ = { - queue: { - name: ShortStoreName.Queue, - scheme: _queueScheme - }, - activityState: { - name: ShortStoreName.ActivityState, - scheme: _activityStateScheme - }, - globalParams: { - name: ShortStoreName.GlobalParams, - scheme: _globalParamsScheme - }, - eventDeduplication: { - name: ShortStoreName.EventDeduplication, - scheme: _eventDeduplicationScheme - }, - preferences: { - name: ShortPreferencesStoreName.Preferences, - scheme: _preferencesScheme, - permanent: true - } -}; -function isPredefinedValuesField(field /*: Maybe*/) /*: field is StoreFieldPredefinedValues*/{ - return !!field && Object.prototype.hasOwnProperty.call(field, 'values'); -} -function isNestingStoreField(field /*: Maybe*/) /*: field is StoreFieldNestingFields*/{ - return !!field && Object.prototype.hasOwnProperty.call(field, 'keys'); -} -function isCompositeKeyStoreField(field /*: Maybe*/) /*: field is StoreFieldCompositeKey*/{ - return !!field && Object.prototype.hasOwnProperty.call(field, 'composite'); -} -function isComplexStoreField(field /*: Maybe*/) /*: field is StoreFieldComplex*/{ - return !!field && typeof field !== 'string'; -} -/* harmony default export */ const storage_scheme = (scheme); -;// CONCATENATED MODULE: ./src/sdk/storage/scheme-map.ts -/** - * Cast value into it's original type - */ -function _parseValue(value /*: string*/) /*: any*/{ - // eslint-disable-line @typescript-eslint/no-explicit-any - try { - return JSON.parse(value); - } catch (e) { - return value; - } -} -/** - * Flip key/value pairs - */ -function _flipObject(obj /*: Record*/) /*: Record*/{ - return entries(obj).map(function (_ref) { - var _ref2 = _slicedToArray(_ref, 2), - key = _ref2[0], - value = _ref2[1]; - return [value, _parseValue(key)]; - }).reduce(reducer, {}); -} -/** - * Flip store name definition names: - * - short key pointing the long one along with additional configuration - */ -function _flipStoreNames(obj /*: StoresConfigurationMap*/) /*: StoresConfigurationMapFlipped*/{ - var flippedConfigs /*: Array<[ShortStoreNames, StoreConfigurationFlipped]>*/ = entries(obj).map(function (_ref3 /*:: */) { - var _ref4 = _slicedToArray(_ref3 /*:: */, 2), - name = _ref4[0], - options = _ref4[1]; - var config = { - name: name, - permanent: options.permanent - }; - return [options.name, config]; - }); - return flippedConfigs.reduce(reducer, {}); -} -/** - * Flip store scheme values - */ -function _flipStoreScheme(storeName /*: StoreNames*/, key /*: string*/, scheme /*: StoreFieldScheme*/) { - var values = isPredefinedValuesField(scheme) ? { - values: _flipObject(scheme.values) - } : {}; - var keys = isNestingStoreField(scheme) ? { - keys: _flipScheme(storeName, scheme.keys) - } : {}; - var composite = isCompositeKeyStoreField(scheme) ? { - composite: scheme.composite.map(function (key) { - return _getShortKey(storeName, key); - }) - } : {}; - return _objectSpread2(_objectSpread2(_objectSpread2({ - key: key - }, values), keys), composite); -} -/** - * Flip general scheme recursivelly - */ -function _flipScheme(storeName /*: StoreNames*/, fieldsScheme /*: StoreFields*/) { - return entries(fieldsScheme).map(function (_ref5 /*:: */) { - var _ref6 = _slicedToArray(_ref5 /*:: */, 2), - key = _ref6[0], - scheme = _ref6[1]; - return isComplexStoreField(scheme) ? [scheme.key, _flipStoreScheme(storeName, key, scheme)] : [scheme, key]; - }).reduce(reducer, {}); -} -/** - * Extend base scheme with some more maps for encoding - */ -function _prepareLeft() /*: StoreScheme*/{ - var storesOptions /*: Array<[StoreNames, StoreOptionsOptionalKey]>*/ = entries(storage_scheme).map(function (_ref7 /*:: */) { - var _ref8 = _slicedToArray(_ref7 /*:: */, 2), - storeName = _ref8[0], - store = _ref8[1]; - var options /*: StoreOptionsOptionalKey*/ = { - keyPath: store.scheme.keyPath, - autoIncrement: store.scheme.autoIncrement, - index: store.scheme.index, - fields: store.scheme.fields - }; - return [storeName, options]; - }); - return storesOptions.reduce(reducer, {}); -} -/** - * Prepare scheme for decoding - */ -function _prepareRight() /*: StoreScheme*/{ - var storesOptionsEncoded /*: Array<[StoreNames, StoreOptionsOptionalKey]>*/ = entries(Left).map(function (_ref9) { - var _ref10 = _slicedToArray(_ref9, 2), - storeName = _ref10[0], - storeScheme = _ref10[1]; - var options /*: StoreOptionsOptionalKey*/ = { - keyPath: _getShortKey(storeName, storeScheme.keyPath), - autoIncrement: storeScheme.autoIncrement, - index: _getShortKey(storeName, storeScheme.index), - fields: _flipScheme(storeName, storeScheme.fields) - }; - return [storeName, options]; - }); - return storesOptionsEncoded.reduce(reducer, {}); -} -/** - * Get available values for encoding - */ -function _getValuesMap() /*: Record*/{ - // all pairs of predefined keys and values such as {GET: 1} - return entries(storage_scheme).reduce(function (acc, _ref11) { - var _ref12 = _slicedToArray(_ref11, 2), - store = _ref12[1]; - return acc.concat(store.scheme.fields); - }, []).map(function (scheme) { - return values(scheme).filter(isPredefinedValuesField).map(function (map) { - return entries(map.values); - }).reduce(function (acc, map) { - return acc.concat(map); - }, []); - }).reduce(function (acc, map) { - return acc.concat(map); - }, []).reduce(reducer, {}); -} -/** - * Get short key version of a specified key - */ -function _getShortKey(storeName /*: StoreNames*/, key /*: Maybe*/) /*: Maybe*/{ - if (!key) { - return undefined; - } - var map = storage_scheme[storeName].scheme.fields[key]; - if (isComplexStoreField(map)) { - return map.key; - } - return map || key; -} -/** - * Get store names and their general configuration (if store is permanent or not) - */ -function _getStoreNames() /*: StoresConfigurationMap*/{ - var storeNames /*: Array<[StoreNames, StoreConfiguration]>*/ = entries(storage_scheme).map(function (_ref13) { - var _ref14 = _slicedToArray(_ref13, 2), - name = _ref14[0], - store = _ref14[1]; - var config = { - name: store.name, - permanent: store.permanent - }; - return [name, config]; - }); - return storeNames.reduce(reducer, {}); -} -var Left = _prepareLeft(); -var Right = _prepareRight(); -var Values = _getValuesMap(); -var StoreNamesAndConfigs = _getStoreNames(); -/* harmony default export */ const scheme_map = ({ - left: Left, - right: Right, - values: Values, - storeNames: { - left: StoreNamesAndConfigs, - right: _flipStoreNames(StoreNamesAndConfigs) +var Action = /*#__PURE__*/function (Action) { + Action["add"] = "add"; + Action["put"] = "put"; + Action["get"] = "get"; + Action["list"] = "list"; + Action["clear"] = "clear"; + Action["delete"] = "delete"; + return Action; +}(Action || {}); +var AccessMode = /*#__PURE__*/function (AccessMode) { + AccessMode["readonly"] = "readonly"; + AccessMode["readwrite"] = "readwrite"; + return AccessMode; +}(AccessMode || {}); +var IndexedDBWrapper = /*#__PURE__*/function () { + function IndexedDBWrapper() { + _classCallCheck(this, IndexedDBWrapper); + _defineProperty(this, "dbDefaultName", globals.namespace); + _defineProperty(this, "dbName", this.dbDefaultName); + _defineProperty(this, "dbVersion", 1); + _defineProperty(this, "indexedDbConnection", null); + _defineProperty(this, "notSupportedError", { + name: 'IDBNotSupported', + message: 'IndexedDB is not supported' + }); + _defineProperty(this, "databaseOpenError", { + name: 'CannotOpenDatabaseError', + message: 'Cannot open a database' + }); + _defineProperty(this, "noConnectionError", { + name: 'NoDatabaseConnection', + message: 'Cannot open a transaction' + }); + var idb = IndexedDBWrapper.getIndexedDB(); + if (!idb) { + throw this.notSupportedError; + } + this.idbFactory = idb; } -}); -;// CONCATENATED MODULE: ./src/sdk/storage/types.ts -var _Promise = typeof Promise === 'undefined' ? (__webpack_require__(702).Promise) : Promise; - -var KeyRangeCondition; -(function (KeyRangeCondition) { - KeyRangeCondition["LowerBound"] = "lowerBound"; - KeyRangeCondition["UpperBound"] = "upperBound"; -})(KeyRangeCondition || (KeyRangeCondition = {})); -function valueIsRecord(value /*: StoredValue | Record*/) /*: value is Record*/{ - return isObject(value); -} - -;// CONCATENATED MODULE: ./src/sdk/storage/converter.ts - - - - + /** + * Sets custom name if provided and migrates database + */ + return _createClass(IndexedDBWrapper, [{ + key: "setCustomName", + value: function setCustomName(customName /*: string*/) /*: Promise*/{ + if (customName && customName.length > 0) { + this.dbName = "".concat(globals.namespace, "-").concat(customName); + return this.migrateDb(this.dbDefaultName, this.dbName); + } + return Promise.resolve(); + } + /** + * Opens database with defined name and resolves with database connection if successed + * @param name name of database to open + * @param version optional version of database schema + * @param upgradeCallback optional `IDBOpenRequest.onupgradeneeded` event handler + */ + }, { + key: "openDatabase", + value: function openDatabase(name /*: string*/, upgradeCallback /*: (event: IDBVersionChangeEvent, reject: () => void) => void*/, version /*: number*/) /*: Promise*/{ + var _this = this; + return IndexedDBWrapper.isSupported().then(function (supported) { + if (!supported) { + return Promise.reject(_this.notSupportedError); + } else { + return new Promise(function (resolve, reject) { + var request = _this.idbFactory.open(name, version); + if (upgradeCallback) { + request.onupgradeneeded = function (event) { + return upgradeCallback(event, reject); + }; + } + request.onsuccess = function (event /*: IDBOpenDBEvent*/) { + var connection = event.target.result; + if (connection) { + resolve(connection); + } else { + reject(_this.databaseOpenError); + } + }; + request.onerror = reject; + }); + } + }); + } -var Direction; -(function (Direction) { - Direction["right"] = "right"; - Direction["left"] = "left"; -})(Direction || (Direction = {})); -/** - * Get value from the map if available - */ -function _getValue(map /*: Nullable>*/, value /*: StoredValue*/) /*: StoredValue*/{ - return map ? map[value] !== undefined ? map[value] : value : value; -} + /** + * Checks if database with passed name exists + */ + }, { + key: "databaseExists", + value: function databaseExists(name /*: string*/) /*: Promise*/{ + var _this2 = this; + return new Promise(function (resolve /*: (result: boolean) => void*/) { + var existed = true; + _this2.openDatabase(name, function () { + existed = false; + }).then(function (connection) { + connection.close(); + if (existed) { + return; + } -/** - * Convert key and value by defined scheme - */ -function _convert(storeName /*: StoreNameType*/, dir /*: Direction*/, key /*: string*/, value /*: StoredValue | StoredRecord*/, scheme /*: StoreFieldScheme*/) /*: [string, unknown]*/{ - if (!scheme) { - return [key, value]; - } - var encodedKey = isComplexStoreField(scheme) ? scheme.key : scheme; - if (valueIsRecord(value)) { - var keys = isNestingStoreField(scheme) ? scheme.keys : null; - return [encodedKey, convertRecord(storeName, dir, value, keys)]; - } - var valuesMap = isPredefinedValuesField(scheme) ? scheme.values : null; - return [encodedKey, _getValue(valuesMap, value)]; -} + // We didn't have this database before the check, so remove it + return _this2.deleteDatabaseByName(name); + }).then(function () { + return resolve(existed); + }); + }); + } + }, { + key: "cloneData", + value: function cloneData(defaultDbConnection /*: IDBDatabase*/, customDbConnection /*: IDBDatabase*/) /*: Promise*/{ + var _this3 = this; + // Function to clone a single store + var cloneStore = function cloneStore(storeName /*: ShortStoreName*/) { + var connection = _this3.indexedDbConnection; + _this3.indexedDbConnection = defaultDbConnection; + return _this3.getAll(storeName) // Get all records from default-named database + .then(function (records) { + _this3.indexedDbConnection = customDbConnection; + if (records.length < 1) { + // There is no records in the store + return; + } + return _this3.addBulk(storeName, records, true); // Put all records into custom-named database + }).then(function () { + _this3.indexedDbConnection = connection; // Restore initial state + }); + }; -/** - * Convert record by defined direction and scheme - */ + // Type guard to filter stores + function isStoreName(key /*: ShortStoreNames*/) /*: key is ShortStoreName*/{ + return key !== 'p'; + } -/** - * Convert record by defined direction and scheme - * Note: the function signature is duplicated because TS hides function implementation - */ -function convertRecord(storeName /*: StoreNameType*/, dir /*: Direction*/, record /*: Maybe*/, scheme /*: StoreFields*/) /*: Maybe*/{ - if (!record) { - return undefined; - } - var _scheme /*: StoreFields*/ = scheme || scheme_map[dir][convertStoreName(storeName, Direction.right)].fields; - return entries(record).map(function (_ref) { - var _ref2 = _slicedToArray(_ref, 2), - key = _ref2[0], - value = _ref2[1]; - return _convert(storeName, dir, key, value, _scheme[key]); - }).reduce(function (acc, _ref3) { - var _ref4 = _slicedToArray(_ref3, 2), - key = _ref4[0], - value = _ref4[1]; - return _objectSpread2(_objectSpread2({}, acc), {}, _defineProperty({}, key, value)); - }, {}); -} + // Get names of stores + var storeNames /*: ShortStoreName[]*/ = values(scheme_map.storeNames.left).map(function (store) { + return store.name; + }).filter(isStoreName); + var cloneStorePromises = storeNames.map(function (name) { + return function () { + return cloneStore(name); + }; + }); -/** - * Convert records by defined direction - */ -function convertRecords(storeName /*: StoreNameType*/, dir /*: Direction*/) /*: Array*/{ - var records /*: Array*/ = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : []; - return records.map(function (record) { - return convertRecord(storeName, dir, record); - }); -} + // Run clone operations one by one + return cloneStorePromises.reduce(function (previousTask, currentTask) { + return previousTask.then(currentTask); + }, Promise.resolve()); + } -/** - * Convert values by defined direction - */ -function convertValues(storeName /*: StoreNameType*/, dir /*: Direction*/, target /*: StoredRecordId*/) /*: StoredValue | Array*/{ - var scheme /*: StoreOptions*/ = scheme_map[dir][convertStoreName(storeName, Direction.right)]; - var keyPathScheme = scheme.fields[scheme.keyPath]; - var values = target instanceof Array ? target.slice() : [target]; - var keys = isCompositeKeyStoreField(keyPathScheme) ? keyPathScheme.composite : [scheme.keyPath]; - var converted = keys.map(function (key /*: string*/, index /*: number*/) { - var field = scheme.fields[key]; - var predefinedValuesMap = isPredefinedValuesField(field) ? field.values : null; - return _getValue(predefinedValuesMap, values[index]); - }); - return converted.length === 1 ? converted[0] : converted; -} + /** + * Migrates created database with default name to custom + * The IndexedDb API doesn't provide method to rename existing database so we have to create a new database, clone + * data and remove the old one. + */ + }, { + key: "migrateDb", + value: function migrateDb(defaultName /*: string*/, customName /*: string*/) /*: Promise*/{ + var _this4 = this; + return this.databaseExists(defaultName).then(function (defaultExists) { + if (defaultExists) { + // Migration hadn't finished yet + return Promise.all([_this4.openDatabase(defaultName, _this4.handleUpgradeNeeded, _this4.dbVersion), + // Open the default database, migrate version if needed + _this4.openDatabase(customName, _this4.handleUpgradeNeeded, _this4.dbVersion) // Open or create a new database, migrate version if needed + ]).then(function (_ref) { + var _ref2 = _slicedToArray(_ref, 2), + defaultDbConnection = _ref2[0], + customDbConnection = _ref2[1]; + return _this4.cloneData(defaultDbConnection, customDbConnection).then(function () { + _this4.indexedDbConnection = customDbConnection; + defaultDbConnection.close(); + return _this4.deleteDatabaseByName(defaultName); + }); + }).then(function () { + return logger.info('Database migration finished'); + }); + } else { + // There is no default-named database, let's just create or open a custom-named one + return _this4.openDatabase(customName, _this4.handleUpgradeNeeded, _this4.dbVersion).then(function (customDbConnection) { + _this4.indexedDbConnection = customDbConnection; + }); + } + }); + } -/** - * Encode value by defined scheme - */ -function encodeValue(target /*: StoredValue*/) /*: StoredValue*/{ - return scheme_map.values[target] || target; -} + /** + * Handle database upgrade/initialization + * - store activity state from memory if database unexpectedly got lost in the middle of the window session + * - migrate data from localStorage if available on browser upgrade + */ + }, { + key: "handleUpgradeNeeded", + value: function handleUpgradeNeeded(e /*: IDBVersionChangeEvent*/, reject /*: (reason: Event) => void*/) { + var db = e.target.result; + e.target.transaction.onerror = reject; + e.target.transaction.onabort = reject; + var storeNames = scheme_map.storeNames.left; + var activityState = activity_state.current || {}; + var inMemoryAvailable = activityState && !isEmpty(activityState); + entries(storeNames).filter(function (_ref3) { + var _ref4 = _slicedToArray(_ref3, 2), + store = _ref4[1]; + return !store.permanent; + }).forEach(function (_ref5) { + var _ref6 = _slicedToArray(_ref5, 2), + longStoreName = _ref6[0], + store = _ref6[1]; + var shortStoreName = store.name; + var options = scheme_map.right[longStoreName]; + var objectStore = db.createObjectStore(shortStoreName, { + keyPath: options.keyPath, + autoIncrement: options.autoIncrement || false + }); + if (options.index) { + objectStore.createIndex("".concat(options.index, "Index"), options.index); + } + if (shortStoreName === ShortStoreName.ActivityState && inMemoryAvailable) { + objectStore.add(convertRecord(longStoreName, Direction.left, activityState)); + logger.info('Activity state has been recovered'); + return; + } + var localStorageRecord /*: Nullable>*/ = quick_storage.stores[shortStoreName]; + if (localStorageRecord) { + localStorageRecord.forEach(function (record) { + return objectStore.add(record); + }); + logger.info("Migration from localStorage done for ".concat(longStoreName, " store")); + } + }); + recover(); + quick_storage.clear(); + } -/** - * Convert store name by defined direction - */ -function convertStoreName(storeName /*: StoreNameType*/, dir /*: Direction*/) /*: StoreNameType*/{ - return (scheme_map.storeNames[dir][storeName] || {}).name || storeName; -} + /** + * Open the database connection and create store if not existent + */ + }, { + key: "open", + value: function open() /*: Promise<{ success: boolean }>*/{ + var _this5 = this; + if (this.indexedDbConnection) { + return Promise.resolve({ + success: true + }); + } + return this.openDatabase(this.dbName, this.handleUpgradeNeeded, this.dbVersion).then(function (connection) { + _this5.indexedDbConnection = connection; + _this5.indexedDbConnection.onclose = function () { + return _this5.destroy; + }; + return { + success: true + }; + }); + } -/** - * Decode error message by replacing short store name with long readable one - */ -function decodeErrorMessage(storeName /*: ShortStoreNames*/, error /*: Error*/) /*: Error*/{ - return { - name: error.name, - message: error.message.replace("\"".concat(storeName, "\""), convertStoreName(storeName, Direction.right)) - }; -} + /** + * Get transaction and the store + */ + }, { + key: "getTransactionStore", + value: function getTransactionStore(_ref7 /*:: */, reject /*: (reason: Event) => void*/, db /*: IDBDatabase*/) /*: Transaction*/{ + var storeName = _ref7 /*:: */.storeName, + mode = _ref7 /*:: */.mode; + var transaction /*: IDBTransaction*/ = db.transaction([storeName], mode); + var store = transaction.objectStore(storeName); + var options = scheme_map.right[convertStoreName(storeName, Direction.right)]; + var index; + if (options.index) { + index = store.index("".concat(options.index, "Index")); + } + transaction.onerror = reject; + transaction.onabort = reject; + return { + transaction: transaction, + store: store, + index: index, + options: options + }; + } -;// CONCATENATED MODULE: ./node_modules/@babel/runtime/helpers/esm/classCallCheck.js -function _classCallCheck(instance, Constructor) { - if (!(instance instanceof Constructor)) { - throw new TypeError("Cannot call a class as a function"); - } -} -;// CONCATENATED MODULE: ./node_modules/@babel/runtime/helpers/esm/createClass.js -function _defineProperties(target, props) { - for (var i = 0; i < props.length; i++) { - var descriptor = props[i]; - descriptor.enumerable = descriptor.enumerable || false; - descriptor.configurable = true; - if ("value" in descriptor) descriptor.writable = true; - Object.defineProperty(target, descriptor.key, descriptor); - } -} -function _createClass(Constructor, protoProps, staticProps) { - if (protoProps) _defineProperties(Constructor.prototype, protoProps); - if (staticProps) _defineProperties(Constructor, staticProps); - Object.defineProperty(Constructor, "prototype", { - writable: false - }); - return Constructor; -} -;// CONCATENATED MODULE: ./src/sdk/time.js - -/** - * Prepend zero to be used in certain format - * - * @param {number} value - * @param {number} power - * @returns {string} - * @private - */ -function _prependZero(value /*: number*/) /*: string*/{ - var power /*: number*/ = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 1; - var formatted = value + ''; - for (var i = 1; i <= power; i += 1) { - if (value < Math.pow(10, i)) { - formatted = "0".concat(formatted); + /** + * Override the error by extracting only name and message of the error + */ + }, { + key: "overrideError", + value: function overrideError(reject /*: (reason: Error) => void*/, error /*: IDBError*/) { + var _error$target$error = error.target.error, + name = _error$target$error.name, + message = _error$target$error.message; + return reject({ + name: name, + message: message + }); } - } - return formatted; -} - -/** - * Get formatted date (YYYY-MM-DD) - * - * @param date - * @returns {string} - * @private - */ -function _getDate(date /*: Date*/) /*: string*/{ - var day = _prependZero(date.getDate()); - var month = _prependZero(date.getMonth() + 1); - var year = date.getFullYear(); - return [year, month, day].join('-'); -} - -/** - * Get formatted hours, minutes, seconds and milliseconds (HH:mm:ss.SSS) - * - * @param {Date} date - * @returns {string} - * @private - */ -function _getTime(date /*: Date*/) /*: string*/{ - var hours = _prependZero(date.getHours(), 1); - var minutes = _prependZero(date.getMinutes()); - var seconds = _prependZero(date.getSeconds()); - var milliseconds = _prependZero(date.getMilliseconds(), 2); - return [hours, minutes, seconds].join(':') + '.' + milliseconds; -} - -/** - * Get formatted timezone (ZZ) - * - * @param {Date} date - * @returns {string} - * @private - */ -function _getTimezone(date /*: Date*/) /*: string*/{ - var offsetInMinutes = date.getTimezoneOffset(); - var hoursOffset = _prependZero(Math.floor(Math.abs(offsetInMinutes) / 60)); - var minutesOffset = _prependZero(Math.abs(offsetInMinutes) % 60); - var sign = offsetInMinutes > 0 ? '-' : '+'; - return sign + hoursOffset + minutesOffset; -} - -/** - * Get the timestamp in the backend format - * - * @param {number=} timestamp - * @returns {string} - */ -function getTimestamp(timestamp /*: number*/) /*: string*/{ - var d = timestamp ? new Date(timestamp) : new Date(); - var date = _getDate(d); - var time = _getTime(d); - var timezone = _getTimezone(d); - return "".concat(date, "T").concat(time, "Z").concat(timezone); -} - -/** - * Calculate time passed between two dates in milliseconds - * - * @param {number} d1 - * @param {number} d2 - * @returns {number} - */ -function timePassed(d1 /*: number*/, d2 /*: number*/) /*: number*/{ - if (isNaN(d1) || isNaN(d2)) { - return 0; - } - return Math.abs(d2 - d1); -} - -;// CONCATENATED MODULE: ./src/sdk/activity-state.js -/*:: // -import { type UrlT, type ActivityStateMapT, type AttributionMapT, type CommonRequestParams } from './types';*/ + /** + * Get list of composite keys if available + */ + }, { + key: "getCompositeKeys", + value: function getCompositeKeys(options /*: StoreOptions*/) /*: Nullable>*/{ + var keyField = options.fields[options.keyPath]; + return isCompositeKeyStoreField(keyField) ? keyField.composite : null; + } + /** + * Check if target is an object + */ + }, { + key: "targetIsObject", + value: function targetIsObject(target /*: Nullable*/) /*: target is Record*/{ + return isObject(target); + } + /** + * Prepare the target to be queried depending on the composite key if defined + */ + }, { + key: "prepareTarget", + value: function prepareTarget(options /*: StoreOptions*/, target /*: Nullable*/, action /*: Action*/) /*: Nullable*/{ + if (action === Action.clear || !target) { + return null; // No target needed when we clear the whole store + } + var composite = this.getCompositeKeys(options); + var needObjectTarget = [Action.add, Action.put].indexOf(action) !== -1; + if (needObjectTarget) { + if (this.targetIsObject(target)) { + // target is a StoredRecord + // extend target with composite path if needed and return it + return composite ? _objectSpread2(_defineProperty({}, options.keyPath, composite.map(function (key) { + return target[key]; + }).join('')), target) : target; + } + return null; + } + // target is StoredRecordId (plain or composite) + return target instanceof Array ? target.join('') : target; + } + /** + * Prepare the result to be return depending on the composite key definition + */ + }, { + key: "prepareResult", + value: function prepareResult(options /*: StoreOptions*/, target /*: Nullable*/) /*: Nullable>*/{ + var composite = this.getCompositeKeys(options); + if (composite && this.targetIsObject(target)) { + return composite.map(function (key) { + return target[key]; + }); + } + return null; + } + /** + * Initiate the database request + */ + }, { + key: "initRequest", + value: function initRequest(_ref8 /*:: */) /*: Promise>*/{ + var _this6 = this; + var storeName = _ref8 /*:: */.storeName, + _ref8$target = _ref8 /*:: */.target, + target = _ref8$target === void 0 ? null : _ref8$target, + action = _ref8 /*:: */.action, + _ref8$mode = _ref8 /*:: */.mode, + mode = _ref8$mode === void 0 ? AccessMode.readonly : _ref8$mode; + return this.open().then(function () { + return new Promise(function (resolve, reject) { + if (!_this6.indexedDbConnection) { + reject(_this6.noConnectionError); + } else { + var _this6$getTransaction = _this6.getTransactionStore({ + storeName: storeName, + mode: mode + }, reject, _this6.indexedDbConnection), + store = _this6$getTransaction.store, + options = _this6$getTransaction.options; + var request = store[action](_this6.prepareTarget(options, target, action)); + var _result = _this6.prepareResult(options, target); + request.onsuccess = function () { + if (action === Action.get && !request.result) { + reject({ + name: 'NotRecordFoundError', + message: "Requested record not found in \"".concat(storeName, "\" store") + }); + } else { + resolve(_result || request.result || target); + } + }; + request.onerror = function (error /*: Event*/) { + return _this6.overrideError(reject, error); + }; + } + }); + }); + } -/** - * Reference to the activity state - * - * @type {Object} - * @private - */ -var _activityState /*: ActivityStateMapT*/ = {}; + /** + * Initiate bulk database request by reusing the same transaction to perform the operation + */ + }, { + key: "initBulkRequest", + value: function initBulkRequest(_ref9 /*:: */) /*: Promise>*/{ + var _this7 = this; + var storeName = _ref9 /*:: */.storeName, + target = _ref9 /*:: */.target, + action = _ref9 /*:: */.action, + _ref9$mode = _ref9 /*:: */.mode, + mode = _ref9$mode === void 0 ? AccessMode.readwrite : _ref9$mode; + if (!target || target && !target.length) { + return Promise.reject({ + name: 'NoTargetDefined', + message: "No array provided to perform ".concat(action, " bulk operation into \"").concat(storeName, "\" store") + }); + } + return this.open().then(function () { + return new Promise(function (resolve, reject) { + if (!_this7.indexedDbConnection) { + reject(_this7.noConnectionError); + } else { + var _this7$getTransaction = _this7.getTransactionStore({ + storeName: storeName, + mode: mode + }, reject, _this7.indexedDbConnection), + transaction = _this7$getTransaction.transaction, + store = _this7$getTransaction.store, + options = _this7$getTransaction.options; -/** - * Started flag, if activity state has been initiated - * - * @type {boolean} - * @private - */ -var _started /*: boolean*/ = false; - -/** - * Active flag, if in foreground - * - * @type {boolean} - * @private - */ -var _active /*: boolean*/ = false; + // Array contains or StoredRecord either RecordIds, but not both at the same time + var _result2 = new Array(); + var current = target[0]; + transaction.oncomplete = function () { + return resolve(_result2); + }; + var request = function request(req) { + req.onerror = function (error) { + return _this7.overrideError(reject, error); + }; + req.onsuccess = function () { + _result2.push(_this7.prepareResult(options, current) || req.result); + current = target[_result2.length]; + if (_result2.length < target.length) { + request(store[action](_this7.prepareTarget(options, current, action))); + } + }; + }; + request(store[action](_this7.prepareTarget(options, current, action))); + } + }); + }); + } -/** - * Get current activity state - * - * @returns {Object} - */ -function currentGetter() /*: ActivityStateMapT*/{ - return _started ? _objectSpread2({}, _activityState) : {}; -} + /** + * Open cursor for bulk operations or listing + */ + }, { + key: "openCursor", + value: function openCursor(_ref10 /*:: */) /*: Promise>*/{ + var _this8 = this; + var storeName = _ref10 /*:: */.storeName, + action = _ref10 /*:: */.action, + _ref10$range = _ref10 /*:: */.range, + range = _ref10$range === void 0 ? null : _ref10$range, + _ref10$firstOnly = _ref10 /*:: */.firstOnly, + firstOnly = _ref10$firstOnly === void 0 ? false : _ref10$firstOnly, + _ref10$mode = _ref10 /*:: */.mode, + mode = _ref10$mode === void 0 ? AccessMode.readonly : _ref10$mode; + return this.open().then(function () { + return new Promise(function (resolve, reject) { + if (!_this8.indexedDbConnection) { + reject(_this8.noConnectionError); + } else { + var _this8$getTransaction = _this8.getTransactionStore({ + storeName: storeName, + mode: mode + }, reject, _this8.indexedDbConnection), + transaction = _this8$getTransaction.transaction, + store = _this8$getTransaction.store, + index = _this8$getTransaction.index, + options = _this8$getTransaction.options; + var cursorRequest /*: OpenIDBCursorRequest*/ = (index || store).openCursor(range); + var items = new Array(); + transaction.oncomplete = function () { + return resolve(items); + }; + cursorRequest.onsuccess = function (e) { + var cursor = e.target.result; + if (cursor) { + if (action === Action.delete) { + cursor.delete(); + items.push(_this8.prepareResult(options, cursor.value) || cursor.value[options.keyPath]); + } else { + items.push(cursor.value); + } + if (!firstOnly) { + cursor.continue(); + } + } + }; + cursorRequest.onerror = function (error) { + return _this8.overrideError(reject, error); + }; + } + }); + }); + } + }, { + key: "deleteDatabaseByName", + value: function deleteDatabaseByName(dbName /*: string*/) /*: Promise*/{ + var _this9 = this; + return new Promise(function (resolve, reject) { + var request = _this9.idbFactory.deleteDatabase(dbName); + request.onerror = function (error) { + return _this9.overrideError(reject, error); + }; + request.onsuccess = function () { + return resolve(); + }; + request.onblocked = function (e) { + return reject(e.target); + }; + }); + } -/** - * Set current activity state - * - * @param {Object} params - */ -function currentSetter() { - var params /*: ActivityStateMapT*/ = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; - _activityState = _started ? _objectSpread2({}, params) : {}; -} + /** + * Get all records from particular store + */ + }, { + key: "getAll", + value: function getAll(storeName /*: ShortStoreName*/) /*: Promise>*/{ + var firstOnly = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false; + return this.openCursor({ + storeName: storeName, + action: Action.list, + firstOnly: firstOnly + }); + } -/** - * Initiate in-memory activity state - * - * @param {Object} params - */ -function init(params /*: ActivityStateMapT*/) { - _started = true; - currentSetter(params); -} + /** + * Get the first row from the store + */ + }, { + key: "getFirst", + value: function getFirst(storeName /*: ShortStoreName*/) /*: Promise>*/{ + return this.getAll(storeName, true).then(function (all) { + return all.length ? all[0] : undefined; + }); + } -/** - * Check if activity state is started - * - * @returns {boolean} - */ -function isStarted() { - return _started; -} + /** + * Get item from a particular store + */ + }, { + key: "getItem", + value: function getItem(storeName /*: ShortStoreName*/, target /*: StoredRecordId*/) /*: Promise*/{ + return this.initRequest({ + storeName: storeName, + target: target, + action: Action.get + }); + } -/** - * Update last active point - * - * @private - */ -function updateLastActive() /*: void*/{ - if (!_started) { - return; - } - _activityState.lastInterval = _getLastInterval(); - _activityState.lastActive = Date.now(); -} + /** + * Return filtered result by value on available index + */ + }, { + key: "filterBy", + value: function filterBy(storeName /*: ShortStoreName*/, by /*: StoredValue*/) /*: Promise>*/{ + var range = IDBKeyRange.only(by); + return this.openCursor({ + storeName: storeName, + action: Action.list, + range: range + }); + } -/** - * Update activity state with new params - * - * @param {Object} params - * @private - */ -function _update(params /*: ActivityStateMapT*/) /*: void*/{ - _activityState = _objectSpread2(_objectSpread2({}, _activityState), params); -} + /** + * Add item to a particular store + */ + }, { + key: "addItem", + value: function addItem(storeName /*: ShortStoreName*/, target /*: StoredRecord*/) /*: Promise*/{ + return this.initRequest({ + storeName: storeName, + target: target, + action: Action.add, + mode: AccessMode.readwrite + }); + } -/** - * Set active flag to true when going foreground - */ -function toForeground() /*: void*/{ - _active = true; -} + /** + * Add multiple items into particular store + */ + }, { + key: "addBulk", + value: function addBulk(storeName /*: ShortStoreName*/, target /*: Array*/, overwrite /*: boolean*/) /*: Promise>*/{ + return this.initBulkRequest({ + storeName: storeName, + target: target, + action: overwrite ? Action.put : Action.add, + mode: AccessMode.readwrite + }); + } -/** - * Set active flag to false when going background - */ -function toBackground() /*: void*/{ - _active = false; -} + /** + * Update item in a particular store + */ + }, { + key: "updateItem", + value: function updateItem(storeName /*: ShortStoreName*/, target /*: StoredRecord*/) /*: Promise*/{ + return this.initRequest({ + storeName: storeName, + target: target, + action: Action.put, + mode: AccessMode.readwrite + }); + } -/** - * Get time offset from the last active point - * - * @returns {number} - * @private - */ -function _getOffset() /*: number*/{ - var lastActive = _activityState.lastActive; - return Math.round(timePassed(lastActive, Date.now()) / SECOND); -} + /** + * Delete item from a particular store + */ + }, { + key: "deleteItem", + value: function deleteItem(storeName /*: ShortStoreName*/, target /*: StoredRecordId*/) /*: Promise*/{ + return this.initRequest({ + storeName: storeName, + target: target, + action: Action.delete, + mode: AccessMode.readwrite + }); + } -/** - * Get time spent with optional offset from last point - * - * @returns {number} - * @private - */ -function _getTimeSpent() /*: number*/{ - return (_activityState.timeSpent || 0) + (_active ? _getOffset() : 0); -} + /** + * Delete items until certain bound (primary key as a bound scope) + */ + }, { + key: "deleteBulk", + value: function deleteBulk(storeName /*: ShortStoreName*/, value /*: StoredValue*/, condition /*: KeyRangeCondition*/) /*: Promise>*/{ + var range = condition ? IDBKeyRange[condition](value) : IDBKeyRange.only(value); + return this.openCursor({ + storeName: storeName, + action: Action.delete, + range: range, + mode: AccessMode.readwrite + }); + } -/** - * Get session length with optional offset from last point - * - * @returns {number} - * @private - */ -function _getSessionLength() /*: number*/{ - var lastActive = _activityState.lastActive; - var withinWindow = timePassed(lastActive, Date.now()) < config.sessionWindow; - var withOffset = _active || !_active && withinWindow; - return (_activityState.sessionLength || 0) + (withOffset ? _getOffset() : 0); -} + /** + * Trim the store from the left by specified length + */ + }, { + key: "trimItems", + value: function trimItems(storeName /*: ShortStoreName*/, length /*: number*/) /*: Promise>*/{ + var _this10 = this; + var options = scheme_map.right[convertStoreName(storeName, Direction.right)]; + return this.getAll(storeName).then(function (records) { + return records.length ? records[length - 1] : null; + }).then(function (record) { + return record ? _this10.deleteBulk(storeName, record[options.keyPath], KeyRangeCondition.UpperBound) : []; + }); + } -/** - * Get total number of sessions so far - * - * @returns {number} - * @private - */ -function _getSessionCount() /*: number*/{ - return _activityState.sessionCount || 0; -} + /** + * Count the number of records in the store + */ + }, { + key: "count", + value: function count(storeName /*: ShortStoreName*/) /*: Promise*/{ + var _this11 = this; + return this.open().then(function () { + return new Promise(function (resolve, reject) { + if (!_this11.indexedDbConnection) { + reject(_this11.noConnectionError); + } else { + var _this11$getTransactio = _this11.getTransactionStore({ + storeName: storeName, + mode: AccessMode.readonly + }, reject, _this11.indexedDbConnection), + store = _this11$getTransactio.store; + var request = store.count(); + request.onsuccess = function () { + return resolve(request.result); + }; + request.onerror = function (error) { + return _this11.overrideError(reject, error); + }; + } + }); + }); + } -/** - * Get total number of events so far - * - * @returns {number} - * @private - */ -function _getEventCount() /*: number*/{ - return _activityState.eventCount || 0; -} + /** + * Clear all records from a particular store + */ + }, { + key: "clear", + value: function clear(storeName /*: ShortStoreName*/) /*: Promise*/{ + return this.initRequest({ + storeName: storeName, + action: Action.clear, + mode: AccessMode.readwrite + }); + } + + /** + * Close the database and destroy the reference to it + */ + }, { + key: "destroy", + value: function destroy() /*: void*/{ + if (this.indexedDbConnection) { + this.indexedDbConnection.close(); + } + this.indexedDbConnection = null; + } + + /** + * Close db connection and delete the db + * WARNING: should be used only by adjust's demo app! + */ + }, { + key: "deleteDatabase", + value: function deleteDatabase() /*: Promise*/{ + this.destroy(); + return this.deleteDatabaseByName(this.dbName); + } + }], [{ + key: "tryOpen", + value: + /** + * Tries to open a temporary database + */ + function tryOpen(db /*: IDBFactory*/) /*: Promise*/{ + return new Promise(function (resolve) { + try { + var request = db.open(IndexedDBWrapper.dbValidationName); + request.onsuccess = function () { + request.result.close(); + db.deleteDatabase(IndexedDBWrapper.dbValidationName); + resolve(true); + }; + request.onerror = function () { + return resolve(false); + }; + } catch (error) { + resolve(false); + } + }); + } + + /** + * Check if IndexedDB is supported in the current browser (exclude iOS forcefully) + */ + }, { + key: "isSupported", + value: function isSupported() /*: Promise*/{ + if (IndexedDBWrapper.isSupportedPromise) { + return IndexedDBWrapper.isSupportedPromise; + } else { + var notSupportedMessage = 'IndexedDB is not supported in this browser'; + IndexedDBWrapper.isSupportedPromise = new Promise(function (resolve) { + var indexedDB = IndexedDBWrapper.getIndexedDB(); + var iOS = !!navigator.platform && /iPad|iPhone|iPod/.test(navigator.platform); + if (!indexedDB || iOS) { + logger.warn(notSupportedMessage); + resolve(false); + } else { + var dbOpenablePromise = IndexedDBWrapper.tryOpen(indexedDB).then(function (dbOpenable) { + if (!dbOpenable) { + logger.warn(notSupportedMessage); + } + return dbOpenable; + }); + resolve(dbOpenablePromise); + } + }); + } + return IndexedDBWrapper.isSupportedPromise; + } + /** + * Get indexedDB instance + */ + }, { + key: "getIndexedDB", + value: function getIndexedDB() /*: Maybe*/{ + return window.indexedDB || window.mozIndexedDB || window.webkitIndexedDB || window.msIndexedDB; + } + }]); +}(); +_defineProperty(IndexedDBWrapper, "dbValidationName", 'validate-db-openable'); /** - * Get time passed since last activity was recorded - * - * @returns {number} - * @private + * Cached promise of IndexedDB validation */ -function _getLastInterval() /*: number*/{ - var lastActive = _activityState.lastActive; - if (lastActive) { - return Math.round(timePassed(lastActive, Date.now()) / SECOND); - } - return -1; +_defineProperty(IndexedDBWrapper, "isSupportedPromise", null); + +;// CONCATENATED MODULE: ./node_modules/@babel/runtime/helpers/esm/arrayWithoutHoles.js + +function _arrayWithoutHoles(arr) { + if (Array.isArray(arr)) return _arrayLikeToArray(arr); +} +;// CONCATENATED MODULE: ./node_modules/@babel/runtime/helpers/esm/iterableToArray.js +function _iterableToArray(iter) { + if (typeof Symbol !== "undefined" && iter[Symbol.iterator] != null || iter["@@iterator"] != null) return Array.from(iter); } +;// CONCATENATED MODULE: ./node_modules/@babel/runtime/helpers/esm/nonIterableSpread.js +function _nonIterableSpread() { + throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); +} +;// CONCATENATED MODULE: ./node_modules/@babel/runtime/helpers/esm/toConsumableArray.js -/** - * Initiate session params and go to foreground - */ -function initParams() /*: void*/{ - updateSessionOffset(); - toForeground(); + + + +function _toConsumableArray(arr) { + return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _unsupportedIterableToArray(arr) || _nonIterableSpread(); } +;// CONCATENATED MODULE: ./src/sdk/storage/localstorage.ts -/** - * Get activity state params that are sent with each request - * - * @returns {Object} - */ -function getParams(url /*: UrlT*/) /*: ?CommonRequestParams*/{ - if (!_started) { - return null; - } - var lastInterval = _activityState.lastInterval >= 0 ? _activityState.lastInterval : 0; - var baseParams /*: CommonRequestParams*/ = { - timeSpent: _activityState.timeSpent || 0, - sessionLength: _activityState.sessionLength || 0, - sessionCount: _activityState.sessionCount || 1, - lastInterval: lastInterval || 0 - }; - if (url && isRequest(url, 'event')) { - baseParams.eventCount = _activityState.eventCount; - } - return baseParams; -} -/** - * Update activity state parameters depending on the endpoint which has been run - * - * @param {string} url - * @param {boolean=false} auto - */ -function updateParams(url /*: string*/, auto /*: boolean*/) /*: void*/{ - if (!_started) { - return; - } - var params = {}; - params.timeSpent = _getTimeSpent(); - params.sessionLength = _getSessionLength(); - if (isRequest(url, 'session')) { - params.sessionCount = _getSessionCount() + 1; - } - if (isRequest(url, 'event')) { - params.eventCount = _getEventCount() + 1; - } - _update(params); - if (!auto) { - updateLastActive(); - } -} -/** - * Update installed flag - first session has been finished - */ -function updateInstalled() /*: void*/{ - if (!_started) { - return; - } - if (_activityState.installed) { - return; - } - _update({ - installed: true - }); -} -/** - * Update session params which depend on the time offset since last measure point - */ -function updateSessionOffset() /*: void*/{ - if (!_started) { - return; - } - var timeSpent = _getTimeSpent(); - var sessionLength = _getSessionLength(); - _update({ - timeSpent: timeSpent, - sessionLength: sessionLength - }); - updateLastActive(); -} -/** - * Update session length - */ -function updateSessionLength() /*: void*/{ - if (!_started) { - return; - } - var sessionLength = _getSessionLength(); - _update({ - sessionLength: sessionLength - }); - updateLastActive(); -} -/** - * Reset time spent and session length to zero - */ -function resetSessionOffset() /*: void*/{ - if (!_started) { - return; - } - _update({ - timeSpent: 0, - sessionLength: 0 - }); -} -/** - * Destroy current activity state - */ -function activity_state_destroy() /*: void*/{ - _activityState = {}; - _started = false; - _active = false; -} -function getAttribution() /*: AttributionMapT | null*/{ - if (!_started) { - return null; - } - if (!_activityState.attribution) { - logger.log('No attribution data yet'); - return null; - } - return _activityState.attribution; -} -function getWebUUID() /*: string*/{ - if (!_started) { - return null; - } - return _activityState.uuid; -} -var ActivityState = { - get current() { - return currentGetter(); - }, - set current(value) { - currentSetter(value); - }, - init: init, - isStarted: isStarted, - toForeground: toForeground, - toBackground: toBackground, - initParams: initParams, - getParams: getParams, - updateParams: updateParams, - updateInstalled: updateInstalled, - updateSessionOffset: updateSessionOffset, - updateSessionLength: updateSessionLength, - resetSessionOffset: resetSessionOffset, - updateLastActive: updateLastActive, - destroy: activity_state_destroy, - getAttribution: getAttribution, - getWebUUID: getWebUUID -}; -/* harmony default export */ const activity_state = (ActivityState); -;// CONCATENATED MODULE: ./src/sdk/pub-sub.js -/*:: type CallbackT = {| - id: string, - cb: (string, T) => mixed -|}*/ -/** - * List of events with subscribed callbacks - * - * @type {Object} - * @private - */ -var _list = {}; -/** - * Reference to timeout ids so they can be cleared on destroy - * - * @type {Array} - * @private - */ -var _timeoutIds = []; -/** - * Get unique id for the callback to use for unsubscribe - * - * @returns {string} - * @private - */ -function _getId() /*: string*/{ - return 'id' + Math.random().toString(36).substr(2, 16); -} -/** - * Subscribe to a certain event - * - * @param {string} name - * @param {Function} cb - * @returns {string} - */ -function subscribe /*:: */(name /*: string*/, cb /*: $PropertyType, 'cb'>*/) /*: string*/{ - var id = _getId(); - var callback /*: CallbackT*/ = { - id: id, - cb: cb - }; - if (!_list[name]) { - _list[name] = []; - } - _list[name].push(callback); - return id; -} -/** - * Unsubscribe particular callback from an event - * - * @param {string} id - */ -function unsubscribe(id /*: string*/) /*: void*/{ - if (!id) { - return; - } - entries(_list).some(function (_ref) { - var _ref2 = _slicedToArray(_ref, 2), - callbacks = _ref2[1]; - return callbacks.some(function - /*:: */ - (callback /*: CallbackT*/, i /*: number*/) { - if (callback.id === id) { - callbacks.splice(i, 1); - return true; - } - }); - }); -} -/** - * Publish certain event with optional arguments - * - * @param {string} name - * @param {*} args - * @returns {Array} - */ -function publish /*:: */(name /*: string*/, args /*: T*/) /*: void*/{ - if (!_list[name]) { - return; + +var LocalStorageWrapper = /*#__PURE__*/function () { + function LocalStorageWrapper() { + _classCallCheck(this, LocalStorageWrapper); } - _list[name].forEach(function (item /*: CallbackT*/) { - if (typeof item.cb === 'function') { - _timeoutIds.push(setTimeout(function () { - return item.cb(name, args); - })); + return _createClass(LocalStorageWrapper, [{ + key: "open", + value: + /** + * Prepare schema details if not existent + */ + function open() /*: Promise*/{ + return LocalStorageWrapper.isSupported().then(function (supported) { + if (!supported) { + return { + status: 'error', + error: { + name: 'LSNotSupported', + message: 'LocalStorage is not supported' + } + }; + } + var storeNames = scheme_map.storeNames.left; + var activityState = activity_state.current || {}; + var inMemoryAvailable = activityState && !isEmpty(activityState); + entries(storeNames).filter(function (_ref) { + var _ref2 = _slicedToArray(_ref, 2), + store = _ref2[1]; + return !store.permanent; + }).forEach(function (_ref3) { + var _ref4 = _slicedToArray(_ref3, 2), + longStoreName = _ref4[0], + store = _ref4[1]; + var shortStoreName = store.name; + if (shortStoreName === ShortStoreName.ActivityState && !quick_storage.stores[shortStoreName]) { + quick_storage.stores[shortStoreName] = inMemoryAvailable ? [convertRecord(longStoreName, Direction.left, activityState)] : []; + } else if (!quick_storage.stores[shortStoreName]) { + quick_storage.stores[shortStoreName] = []; + } + }); + recover(); + return { + status: 'success' + }; + }); } - }); -} -/** - * Destroy all registered events with their callbacks - */ -function pub_sub_destroy() /*: void*/{ - _timeoutIds.forEach(clearTimeout); - _timeoutIds = []; - _list = {}; -} - -;// CONCATENATED MODULE: ./src/sdk/storage/quick-storage.ts - - - - - - - - - -var InMemoryStorage = /*#__PURE__*/function () { - function InMemoryStorage() { - _classCallCheck(this, InMemoryStorage); - _defineProperty(this, "items", {}); - } - _createClass(InMemoryStorage, [{ - key: "getItem", - value: function getItem(key /*: string*/) /*: string | null*/{ - return Object.prototype.hasOwnProperty.call(this.items, key) ? this.items[key] : null; - } + /** + * Get list of composite keys if available + */ }, { - key: "removeItem", - value: function removeItem(key /*: string*/) /*: void*/{ - delete this.items[key]; + key: "getCompositeKeys", + value: function getCompositeKeys(options /*: StoreOptions*/) /*: Nullable>*/{ + var field = options.fields[options.keyPath]; + return isCompositeKeyStoreField(field) ? field.composite : null; } + + /** + * Get composite keys when defined or fallback to primary key for particular store + */ }, { - key: "setItem", - value: function setItem(key /*: string*/, value /*: string*/) /*: void*/{ - this.items[key] = value; + key: "getKeys", + value: function getKeys(storeName /*: ShortStoreName*/) /*: Array*/{ + var name = convertStoreName(storeName, Direction.right); + var options /*: StoreOptions*/ = scheme_map.right[name]; + return this.getCompositeKeys(options) || [options.keyPath]; } - }]); - return InMemoryStorage; -}(); -var QuickStorage = /*#__PURE__*/function () { - function QuickStorage() { - var _this = this; - _classCallCheck(this, QuickStorage); - _defineProperty(this, "defaultName", globals.namespace); - _defineProperty(this, "storageName", this.defaultName); - _defineProperty(this, "storeNames", scheme_map.storeNames.left); - _defineProperty(this, "storesMap", void 0); - _defineProperty(this, "storage", void 0); - this.storesMap = {}; - if (isLocalStorageSupported()) { - this.storage = window.localStorage; - } else { - this.storage = new InMemoryStorage(); + + /** + * Return next index using the current one and undefined if current is undefined + */ + }, { + key: "nextIndex", + value: function nextIndex(current /*: Maybe*/) /*: Maybe*/{ + return typeof current === 'number' ? current + 1 : undefined; } - var read = this.read.bind(this); - var write = this.write.bind(this); - values(this.storeNames).forEach(function (store) { - var shortStoreName = store.name; - Object.defineProperty(_this.storesMap, shortStoreName, { - get: function get() { - return read(shortStoreName); - }, - set: function set(value) { - write(shortStoreName, value); + + /** + * Initiate quasi-database request + */ + }, { + key: "initRequest", + value: function initRequest /*:: */(_ref5 /*:: */, action /*: Action*/) /*: Promise*/{ + var _this = this; + var storeName = _ref5 /*:: */.storeName, + id = _ref5 /*:: */.id, + item = _ref5 /*:: */.item; + var options = scheme_map.right[convertStoreName(storeName, Direction.right)]; + return this.open().then(function (open) { + if (open.status === 'error') { + return Promise.reject(open.error); } + return new Promise(function (resolve, reject) { + var items /*: Array*/ = quick_storage.stores[storeName]; + var keys = _this.getKeys(storeName); + var lastId = (items[items.length - 1] || {})[options.keyPath] || 0; + var target /*: StoredRecord*/; + if (!id) { + target = _objectSpread2({}, item); + } else { + var ids = Array.isArray(id) ? id.slice() : [id]; + target = keys.map(function (key, index) { + return [key, ids[index]]; + }).reduce(reducer, {}); + } + var index = target ? findIndex(items, keys, target) : 0; + return action(resolve, reject, { + keys: keys, + items: items, + index: index, + options: options, + lastId: lastId + }); + }); }); - }); - Object.freeze(this.storesMap); - } + } - /** - * Sets custom name to use in data keys and updates existing keys in localStorage - */ - _createClass(QuickStorage, [{ - key: "read", - value: /** - * Get the value for specified key + * Sort the array by provided key (key can be a composite one) + * - by default sorts in ascending order by primary keys + * - force order by provided value */ - function read(key /*: ShortStoreName | ShortPreferencesStoreName*/) /*: Nullable*/{ - var valueToParse = this.storage.getItem("".concat(this.storageName, ".").concat(key)); - var value = valueToParse ? JSON.parse(valueToParse) : null; - if (key === ShortPreferencesStoreName.Preferences && value) { - return convertRecord(ShortPreferencesStoreName.Preferences, Direction.right, value); + }, { + key: "sort", + value: function sort /*:: */(items /*: Array*/, keys /*: Array*/, exact /*: Nullable*/) /*: Array*/{ + var clone = _toConsumableArray(items); + var reversed = keys.slice().reverse(); + function compare(a /*: T*/, b /*: T*/, key /*: string*/) { + var expr1 = exact ? exact === a[key] : a[key] < b[key]; + var expr2 = exact ? exact > a[key] : a[key] > b[key]; + return expr1 ? -1 : expr2 ? 1 : 0; } - return value; + return clone.sort(function (a, b) { + return reversed.reduce(function (acc, key) { + return acc || compare(a, b, key); + }, 0); + }); } /** - * Set the value for specified key + * Prepare the target to be queried depending on the composite key if defined */ }, { - key: "write", - value: function write(key /*: ShortStoreName | ShortPreferencesStoreName*/, value /*: StoreContent*/) { - if (!value) { - this.storage.removeItem("".concat(this.storageName, ".").concat(key)); - } else { - this.storage.setItem("".concat(this.storageName, ".").concat(key), JSON.stringify(value instanceof Array ? value : convertRecord(ShortPreferencesStoreName.Preferences, Direction.left, value))); - } + key: "prepareTarget", + value: function prepareTarget(options /*: StoreOptions*/, target /*: StoredRecord*/, next /*: number*/) /*: StoredRecord*/{ + var composite = this.getCompositeKeys(options); + return composite ? _objectSpread2(_defineProperty({}, options.keyPath, composite.map(function (key) { + return target[key]; + }).join('')), target) : options.autoIncrement && next ? _objectSpread2(_defineProperty({}, options.keyPath, next), target) : _objectSpread2({}, target); } /** - * Clear all data related to the sdk + * Prepare the result to be return depending on the composite key definition */ }, { - key: "clear", - value: function clear() { - this.deleteData(); + key: "prepareResult", + value: function prepareResult(options /*: StoreOptions*/, target /*: StoredRecord*/) /*: StoredRecordId*/{ + var composite = this.getCompositeKeys(options); + if (composite) { + return composite.map(function (key) { + return target[key]; + }).filter(function (value) { + return /*: value is StoredValue*/!valueIsRecord(value); + }); + } + return target[options.keyPath]; } /** - * Clear all data related to the sdk - * - * @param wipe if true then also remove permanent data such as user's preferences + * Get all records from particular store */ }, { - key: "deleteData", - value: function deleteData() { + key: "getAll", + value: function getAll(storeName /*: ShortStoreName*/) /*: Promise>*/{ var _this2 = this; - var wipe = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false; - values(this.storeNames).forEach(function (store) { - if (wipe || !store.permanent) { - _this2.storage.removeItem("".concat(_this2.storageName, ".").concat(store.name)); + var firstOnly = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false; + return this.open().then(function (open) { + if (open.status === 'error') { + return Promise.reject(open.error); } + return new Promise(function (resolve, reject) { + var value = quick_storage.stores[storeName]; + if (value instanceof Array) { + resolve(firstOnly ? [value[0]] : _this2.sort(value, _this2.getKeys(storeName))); + } else { + reject({ + name: 'NotFoundError', + message: "No objectStore named ".concat(storeName, " in this database") + }); + } + }); }); } - }, { - key: "setCustomName", - value: function setCustomName(customName /*: string*/) { - var _this3 = this; - if (!customName || !customName.length) { - return; - } - var newName = "".concat(globals.namespace, "-").concat(customName); - // Clone data - values(this.storeNames).forEach(function (store) { - var key = store.name; - var rawData = _this3.storage.getItem("".concat(_this3.storageName, ".").concat(key)); // Get data from the store, no need to encode it - if (rawData) { - _this3.storage.setItem("".concat(newName, ".").concat(key), rawData); // Put data into a new store - } + /** + * Get the first row from the store + */ + }, { + key: "getFirst", + value: function getFirst(storeName /*: ShortStoreName*/) /*: Promise>*/{ + return this.getAll(storeName, true).then(function (all) { + return all.length ? all[0] : undefined; }); - - this.deleteData(true); - this.storageName = newName; } + + /** + * Get item from a particular store + */ }, { - key: "stores", - get: function get() { - return this.storesMap; + key: "getItem", + value: function getItem(storeName /*: ShortStoreName*/, id /*: StoredRecordId*/) /*: Promise*/{ + var _this3 = this; + var action /*: Action*/ = function action /*: Action*/(resolve, reject, _ref6) { + var items = _ref6.items, + index = _ref6.index, + options = _ref6.options; + if (index === -1) { + reject({ + name: 'NotRecordFoundError', + message: "Requested record not found in \"".concat(storeName, "\" store") + }); + } else { + resolve(_this3.prepareTarget(options, items[index])); + } + }; + return this.initRequest({ + storeName: storeName, + id: id + }, action); } - }]); - return QuickStorage; -}(); -/* harmony default export */ const quick_storage = (new QuickStorage()); -;// CONCATENATED MODULE: ./src/sdk/preferences.js - - - - - -/*:: type SdkDisabledT = {| - reason: REASON_GENERAL | REASON_GDPR, - pending: boolean -|}*/ -/*:: type ThirdPartySharingDisabledT = {| - reason: REASON_GENERAL, - pending: boolean -|}*/ -/*:: type PreferencesT = {| - thirdPartySharingDisabled?: ?ThirdPartySharingDisabledT, - sdkDisabled?: ?SdkDisabledT -|}*/ -/** - * Name of the store used by preferences - * - * @type {string} - * @private - */ -var _storeName /*: string*/ = storage_scheme.preferences.name; - -/** - * Local reference to be used for recovering preserved state - * - * @type {Object} - * @private - */ -var _preferences /*: ?PreferencesT*/ = _getPreferences(); - -/** - * Get preferences stored in the localStorage - * - * @returns {Object} - * @private - */ -function _getPreferences() /*: ?PreferencesT*/{ - if (!_preferences) { - _setPreferences(); - } - return _preferences ? _objectSpread2({}, _preferences) : null; -} - -/** - * Set local reference of the preserved preferences - * - * @private - */ -function _setPreferences() /*: void*/{ - _preferences = quick_storage.stores[_storeName]; -} - -/** - * Get current disabled state - * - * @returns {Object|null} - */ -function getDisabled() /*: ?SdkDisabledT*/{ - var preferences = _getPreferences(); - return preferences ? preferences.sdkDisabled : null; -} - -/** - * Set current disabled state - * - * @param {Object|null} value - */ -function setDisabled(value /*: ?SdkDisabledT*/) /*: void*/{ - var sdkDisabled = value ? _objectSpread2({}, value) : null; - quick_storage.stores[_storeName] = _objectSpread2(_objectSpread2({}, _getPreferences()), {}, { - sdkDisabled: sdkDisabled - }); - _setPreferences(); -} - -/** - * Get current third-party-sharing disabled state - * - * @returns {Object} - * @private - */ -function getThirdPartySharing() /*: ?ThirdPartySharingDisabledT*/{ - var preferences = _getPreferences(); - return preferences ? preferences.thirdPartySharingDisabled : null; -} - -/** - * Set current third-party-sharing disabled state - * - * @param {Object=} value - * @private - */ -function setThirdPartySharing(value /*: ?ThirdPartySharingDisabledT*/) /*: void*/{ - var thirdPartySharingDisabled = value ? _objectSpread2({}, value) : null; - quick_storage.stores[_storeName] = _objectSpread2(_objectSpread2({}, _getPreferences()), {}, { - thirdPartySharingDisabled: thirdPartySharingDisabled - }); - _setPreferences(); -} - -/** - * Reload current preferences from localStorage if changed outside of current scope (e.g. tab) - */ -function reload() /*: void*/{ - var stored /*: PreferencesT*/ = quick_storage.stores[_storeName] || {}; - var sdkDisabled /*: ?SdkDisabledT*/ = (_preferences || {}).sdkDisabled || null; - if (stored.sdkDisabled && !sdkDisabled) { - publish('sdk:shutdown'); - } - _setPreferences(); -} - -/** - * Recover preferences from memory if storage was lost - */ -function recover() /*: void*/{ - var stored /*: ?PreferencesT*/ = quick_storage.stores[_storeName]; - if (!stored) { - quick_storage.stores[_storeName] = _objectSpread2({}, _preferences); - } -} - -;// CONCATENATED MODULE: ./src/sdk/storage/indexeddb.ts - - - - - -var indexeddb_Promise = typeof Promise === 'undefined' ? (__webpack_require__(702).Promise) : Promise; - - - - - - - - - - -var Action; -(function (Action) { - Action["add"] = "add"; - Action["put"] = "put"; - Action["get"] = "get"; - Action["list"] = "list"; - Action["clear"] = "clear"; - Action["delete"] = "delete"; -})(Action || (Action = {})); -var AccessMode; -(function (AccessMode) { - AccessMode["readonly"] = "readonly"; - AccessMode["readwrite"] = "readwrite"; -})(AccessMode || (AccessMode = {})); -var IndexedDBWrapper = /*#__PURE__*/function () { - function IndexedDBWrapper() { - _classCallCheck(this, IndexedDBWrapper); - _defineProperty(this, "dbDefaultName", globals.namespace); - _defineProperty(this, "dbName", this.dbDefaultName); - _defineProperty(this, "dbVersion", 1); - _defineProperty(this, "idbFactory", void 0); - _defineProperty(this, "indexedDbConnection", null); - _defineProperty(this, "notSupportedError", { - name: 'IDBNotSupported', - message: 'IndexedDB is not supported' - }); - _defineProperty(this, "databaseOpenError", { - name: 'CannotOpenDatabaseError', - message: 'Cannot open a database' - }); - _defineProperty(this, "noConnectionError", { - name: 'NoDatabaseConnection', - message: 'Cannot open a transaction' - }); - var idb = IndexedDBWrapper.getIndexedDB(); - if (!idb) { - throw this.notSupportedError; - } - this.idbFactory = idb; - } - /** - * Sets custom name if provided and migrates database - */ - _createClass(IndexedDBWrapper, [{ - key: "setCustomName", - value: function setCustomName(customName /*: string*/) /*: Promise*/{ - if (customName && customName.length > 0) { - this.dbName = "".concat(globals.namespace, "-").concat(customName); - return this.migrateDb(this.dbDefaultName, this.dbName); - } - return indexeddb_Promise.resolve(); + /** + * Return filtered result by value on available index + */ + }, { + key: "filterBy", + value: function filterBy(storeName /*: ShortStoreName*/, by /*: StoredValue*/) /*: Promise>*/{ + return this.getAll(storeName).then(function (result /*: Array*/) { + return result.filter(function (item) { + var store = scheme_map.right[convertStoreName(storeName, Direction.right)]; + var indexedValue = store.index && item[store.index]; + return indexedValue === by; + }); + }); } /** - * Opens database with defined name and resolves with database connection if successed - * @param name name of database to open - * @param version optional version of database schema - * @param upgradeCallback optional `IDBOpenRequest.onupgradeneeded` event handler + * Add item to a particular store */ }, { - key: "openDatabase", - value: function openDatabase(name /*: string*/, upgradeCallback /*: (event: IDBVersionChangeEvent, reject: () => void) => void*/, version /*: number*/) /*: Promise*/{ - var _this = this; - return IndexedDBWrapper.isSupported().then(function (supported) { - if (!supported) { - return indexeddb_Promise.reject(_this.notSupportedError); - } else { - return new indexeddb_Promise(function (resolve, reject) { - var request = _this.idbFactory.open(name, version); - if (upgradeCallback) { - request.onupgradeneeded = function (event) { - return upgradeCallback(event, reject); - }; - } - request.onsuccess = function (event /*: IDBOpenDBEvent*/) { - var connection = event.target.result; - if (connection) { - resolve(connection); - } else { - reject(_this.databaseOpenError); - } - }; - request.onerror = reject; + key: "addItem", + value: function addItem(storeName /*: ShortStoreName*/, item /*: StoredRecord*/) /*: Promise*/{ + var _this4 = this; + return this.initRequest({ + storeName: storeName, + item: item + }, function (resolve, reject, _ref7) { + var items = _ref7.items, + index = _ref7.index, + options = _ref7.options, + lastId = _ref7.lastId; + if (index !== -1) { + reject({ + name: 'ConstraintError', + message: "Constraint was not satisfied, trying to add existing item into \"".concat(storeName, "\" store") }); + } else { + items.push(_this4.prepareTarget(options, item, _this4.nextIndex(lastId))); + quick_storage.stores[storeName] = items; + resolve(_this4.prepareResult(options, item)); } }); } /** - * Checks if database with passed name exists + * Add multiple items into particular store */ }, { - key: "databaseExists", - value: function databaseExists(name /*: string*/) /*: Promise*/{ - var _this2 = this; - return new indexeddb_Promise(function (resolve /*: (result: boolean) => void*/) { - var existed = true; - _this2.openDatabase(name, function () { - existed = false; - }).then(function (connection) { - connection.close(); - if (existed) { - return; - } - - // We didn't have this database before the check, so remove it - return _this2.deleteDatabaseByName(name); - }).then(function () { - return resolve(existed); - }); - }); - } - }, { - key: "cloneData", - value: function cloneData(defaultDbConnection /*: IDBDatabase*/, customDbConnection /*: IDBDatabase*/) /*: Promise*/{ - var _this3 = this; - // Function to clone a single store - var cloneStore = function cloneStore(storeName /*: ShortStoreName*/) { - var connection = _this3.indexedDbConnection; - _this3.indexedDbConnection = defaultDbConnection; - return _this3.getAll(storeName) // Get all records from default-named database - .then(function (records) { - _this3.indexedDbConnection = customDbConnection; - if (records.length < 1) { - // There is no records in the store - return; - } - return _this3.addBulk(storeName, records, true); // Put all records into custom-named database - }).then(function () { - _this3.indexedDbConnection = connection; // Restore initial state + key: "addBulk", + value: function addBulk(storeName /*: ShortStoreName*/, target /*: Array*/, overwrite /*: boolean*/) /*: Promise>*/{ + var _this5 = this; + return this.initRequest({ + storeName: storeName + }, function (resolve, reject, _ref8) { + var keys = _ref8.keys, + items = _ref8.items, + options = _ref8.options, + lastId = _ref8.lastId; + if (!target || target && !target.length) { + reject({ + name: 'NoTargetDefined', + message: "No array provided to perform add bulk operation into \"".concat(storeName, "\" store") + }); + return; + } + var id = lastId; + var newItems = target.map(function (item) { + return _this5.prepareTarget(options, item, id = _this5.nextIndex(id)); }); - }; - - // Type guard to filter stores - function isStoreName(key /*: ShortStoreNames*/) /*: key is ShortStoreName*/{ - return key !== 'p'; - } - - // Get names of stores - var storeNames /*: ShortStoreName[]*/ = values(scheme_map.storeNames.left).map(function (store) { - return store.name; - }).filter(isStoreName); - var cloneStorePromises = storeNames.map(function (name) { - return function () { - return cloneStore(name); - }; - }); - - // Run clone operations one by one - return cloneStorePromises.reduce(function (previousTask, currentTask) { - return previousTask.then(currentTask); - }, indexeddb_Promise.resolve()); - } - - /** - * Migrates created database with default name to custom - * The IndexedDb API doesn't provide method to rename existing database so we have to create a new database, clone - * data and remove the old one. - */ - }, { - key: "migrateDb", - value: function migrateDb(defaultName /*: string*/, customName /*: string*/) /*: Promise*/{ - var _this4 = this; - return this.databaseExists(defaultName).then(function (defaultExists) { - if (defaultExists) { - // Migration hadn't finished yet - return indexeddb_Promise.all([_this4.openDatabase(defaultName, _this4.handleUpgradeNeeded, _this4.dbVersion), - // Open the default database, migrate version if needed - _this4.openDatabase(customName, _this4.handleUpgradeNeeded, _this4.dbVersion) // Open or create a new database, migrate version if needed - ]).then(function (_ref) { - var _ref2 = _slicedToArray(_ref, 2), - defaultDbConnection = _ref2[0], - customDbConnection = _ref2[1]; - return _this4.cloneData(defaultDbConnection, customDbConnection).then(function () { - _this4.indexedDbConnection = customDbConnection; - defaultDbConnection.close(); - return _this4.deleteDatabaseByName(defaultName); - }); - }).then(function () { - return logger.info('Database migration finished'); + var overlapping = newItems.filter(function (item) { + return findIndex(items, keys, item) !== -1; + }).map(function (item) { + return item[options.keyPath]; + }); + var currentItems = overwrite ? items.filter(function (item) { + return overlapping.indexOf(item[options.keyPath]) === -1; + }) : _toConsumableArray(items); + if (overlapping.length && !overwrite) { + reject({ + name: 'ConstraintError', + message: "Constraint was not satisfied, trying to add existing items into \"".concat(storeName, "\" store") }); } else { - // There is no default-named database, let's just create or open a custom-named one - return _this4.openDatabase(customName, _this4.handleUpgradeNeeded, _this4.dbVersion).then(function (customDbConnection) { - _this4.indexedDbConnection = customDbConnection; + quick_storage.stores[storeName] = _this5.sort([].concat(_toConsumableArray(currentItems), _toConsumableArray(newItems)), keys); + var result = target.map(function (item) { + return _this5.prepareResult(options, item); }); + resolve(result); } }); } /** - * Handle database upgrade/initialization - * - store activity state from memory if database unexpectedly got lost in the middle of the window session - * - migrate data from localStorage if available on browser upgrade + * Update item in a particular store */ }, { - key: "handleUpgradeNeeded", - value: function handleUpgradeNeeded(e /*: IDBVersionChangeEvent*/, reject /*: (reason: Event) => void*/) { - var db = e.target.result; - e.target.transaction.onerror = reject; - e.target.transaction.onabort = reject; - var storeNames = scheme_map.storeNames.left; - var activityState = activity_state.current || {}; - var inMemoryAvailable = activityState && !isEmpty(activityState); - entries(storeNames).filter(function (_ref3) { - var _ref4 = _slicedToArray(_ref3, 2), - store = _ref4[1]; - return !store.permanent; - }).forEach(function (_ref5) { - var _ref6 = _slicedToArray(_ref5, 2), - longStoreName = _ref6[0], - store = _ref6[1]; - var shortStoreName = store.name; - var options = scheme_map.right[longStoreName]; - var objectStore = db.createObjectStore(shortStoreName, { - keyPath: options.keyPath, - autoIncrement: options.autoIncrement || false - }); - if (options.index) { - objectStore.createIndex("".concat(options.index, "Index"), options.index); - } - if (shortStoreName === ShortStoreName.ActivityState && inMemoryAvailable) { - objectStore.add(convertRecord(longStoreName, Direction.left, activityState)); - logger.info('Activity state has been recovered'); - return; - } - var localStorageRecord /*: Nullable>*/ = quick_storage.stores[shortStoreName]; - if (localStorageRecord) { - localStorageRecord.forEach(function (record) { - return objectStore.add(record); - }); - logger.info("Migration from localStorage done for ".concat(longStoreName, " store")); + key: "updateItem", + value: function updateItem(storeName /*: ShortStoreName*/, item /*: StoredRecord*/) /*: Promise*/{ + var _this6 = this; + return this.initRequest({ + storeName: storeName, + item: item + }, function (resolve, _, _ref9) { + var items = _ref9.items, + index = _ref9.index, + options = _ref9.options, + lastId = _ref9.lastId; + var nextId = index === -1 ? _this6.nextIndex(lastId) : undefined; + var target = _this6.prepareTarget(options, item, nextId); + if (index === -1) { + items.push(target); + } else { + items.splice(index, 1, target); } + quick_storage.stores[storeName] = items; + resolve(_this6.prepareResult(options, item)); }); - recover(); - quick_storage.clear(); } /** - * Open the database connection and create store if not existent + * Delete item from a particular store */ }, { - key: "open", - value: function open() /*: Promise<{ success: boolean }>*/{ - var _this5 = this; - if (this.indexedDbConnection) { - return indexeddb_Promise.resolve({ - success: true - }); - } - return this.openDatabase(this.dbName, this.handleUpgradeNeeded, this.dbVersion).then(function (connection) { - _this5.indexedDbConnection = connection; - _this5.indexedDbConnection.onclose = function () { - return _this5.destroy; - }; - return { - success: true - }; + key: "deleteItem", + value: function deleteItem(storeName /*: ShortStoreName*/, id /*: StoredRecordId*/) /*: Promise*/{ + return this.initRequest({ + storeName: storeName, + id: id + }, function (resolve, _, _ref10) { + var items = _ref10.items, + index = _ref10.index; + if (index !== -1) { + items.splice(index, 1); + quick_storage.stores[storeName] = items; + } + resolve(id); }); } /** - * Get transaction and the store + * Find index of the item with the closest value to the bound */ }, { - key: "getTransactionStore", - value: function getTransactionStore(_ref7 /*:: */, reject /*: (reason: Event) => void*/, db /*: IDBDatabase*/) /*: Transaction*/{ - var storeName = _ref7 /*:: */.storeName, - mode = _ref7 /*:: */.mode; - var transaction /*: IDBTransaction*/ = db.transaction([storeName], mode); - var store = transaction.objectStore(storeName); - var options = scheme_map.right[convertStoreName(storeName, Direction.right)]; - var index; - if (options.index) { - index = store.index("".concat(options.index, "Index")); + key: "findMax", + value: function findMax(array /*: Array*/, key /*: string*/, value /*: StoredValue*/) /*: number*/{ + if (!array.length) { + return -1; } - transaction.onerror = reject; - transaction.onabort = reject; - return { - transaction: transaction, - store: store, - index: index, - options: options + var max = { + index: -1, + value: typeof value === 'string' ? '' : 0 }; + for (var i = 0; i < array.length; i += 1) { + if (array[i][key] <= value) { + if (array[i][key] >= max.value) { + max = { + value: array[i][key], + index: i + }; + } + } else { + return max.index; + } + } + return max.index; } /** - * Override the error by extracting only name and message of the error + * Delete items until certain bound (primary key as a bound scope) + * Returns array of deleted elements */ }, { - key: "overrideError", - value: function overrideError(reject /*: (reason: Error) => void*/, error /*: IDBError*/) { - var _error$target$error = error.target.error, - name = _error$target$error.name, - message = _error$target$error.message; - return reject({ - name: name, - message: message + key: "deleteBulk", + value: function deleteBulk(storeName /*: ShortStoreName*/, value /*: StoredValue*/, condition /*: KeyRangeCondition*/) /*: Promise>*/{ + var _this7 = this; + return this.getAll(storeName).then(function (items /*: Array*/) { + var keys = _this7.getKeys(storeName); + var key = scheme_map.right[convertStoreName(storeName, Direction.right)].index || keys[0]; + var exact = condition ? null : value; + var sorted /*: Array*/ = _this7.sort(items, keys, exact); + var index = _this7.findMax(sorted, key, value); + if (index === -1) { + return []; + } + var start = condition === KeyRangeCondition.LowerBound ? index : 0; + var end = !condition || condition === KeyRangeCondition.UpperBound ? index + 1 : sorted.length; + var deleted /*: Array*/ = sorted.splice(start, end).map(function (item) { + return keys.length === 1 ? item[key] : keys.map(function (k) { + return item[k]; + }); + }); + quick_storage.stores[storeName] = sorted; + return deleted; }); } /** - * Get list of composite keys if available + * Trim the store from the left by specified length */ }, { - key: "getCompositeKeys", - value: function getCompositeKeys(options /*: StoreOptions*/) /*: Nullable>*/{ - var keyField = options.fields[options.keyPath]; - return isCompositeKeyStoreField(keyField) ? keyField.composite : null; - } - - /** - * Check if target is an object - */ - }, { - key: "targetIsObject", - value: function targetIsObject(target /*: Nullable*/) /*: target is Record*/{ - return isObject(target); + key: "trimItems", + value: function trimItems(storeName /*: ShortStoreName*/, length /*: number*/) /*: Promise>*/{ + var _this8 = this; + var convertedName = convertStoreName(storeName, Direction.right); + var options /*: StoreOptions*/ = scheme_map.right[convertedName]; + return this.getAll(storeName).then(function (records /*: Array>*/) { + return records.length ? records[length - 1] : null; + }).then(function (record) { + return record ? _this8.deleteBulk(storeName, record[options.keyPath], KeyRangeCondition.UpperBound) : []; + }); } /** - * Prepare the target to be queried depending on the composite key if defined + * Count the number of records in the store */ }, { - key: "prepareTarget", - value: function prepareTarget(options /*: StoreOptions*/, target /*: Nullable*/, action /*: Action*/) /*: Nullable*/{ - if (action === Action.clear || !target) { - return null; // No target needed when we clear the whole store - } - - var composite = this.getCompositeKeys(options); - var needObjectTarget = [Action.add, Action.put].indexOf(action) !== -1; - if (needObjectTarget) { - if (this.targetIsObject(target)) { - // target is a StoredRecord - // extend target with composite path if needed and return it - return composite ? _objectSpread2(_defineProperty({}, options.keyPath, composite.map(function (key) { - return target[key]; - }).join('')), target) : target; + key: "count", + value: function count(storeName /*: ShortStoreName*/) /*: Promise*/{ + return this.open().then(function (open) { + if (open.status === 'error') { + return Promise.reject(open.error); } - return null; - } - - // target is StoredRecordId (plain or composite) - return target instanceof Array ? target.join('') : target; - } - - /** - * Prepare the result to be return depending on the composite key definition - */ - }, { - key: "prepareResult", - value: function prepareResult(options /*: StoreOptions*/, target /*: Nullable*/) /*: Nullable>*/{ - var composite = this.getCompositeKeys(options); - if (composite && this.targetIsObject(target)) { - return composite.map(function (key) { - return target[key]; - }); - } - return null; - } - - /** - * Initiate the database request - */ - }, { - key: "initRequest", - value: function initRequest(_ref8 /*:: */) /*: Promise>*/{ - var _this6 = this; - var storeName = _ref8 /*:: */.storeName, - _ref8$target = _ref8 /*:: */.target, - target = _ref8$target === void 0 ? null : _ref8$target, - action = _ref8 /*:: */.action, - _ref8$mode = _ref8 /*:: */.mode, - mode = _ref8$mode === void 0 ? AccessMode.readonly : _ref8$mode; - return this.open().then(function () { - return new indexeddb_Promise(function (resolve, reject) { - if (!_this6.indexedDbConnection) { - reject(_this6.noConnectionError); - } else { - var _this6$getTransaction = _this6.getTransactionStore({ - storeName: storeName, - mode: mode - }, reject, _this6.indexedDbConnection), - store = _this6$getTransaction.store, - options = _this6$getTransaction.options; - var request = store[action](_this6.prepareTarget(options, target, action)); - var _result = _this6.prepareResult(options, target); - request.onsuccess = function () { - if (action === Action.get && !request.result) { - reject({ - name: 'NotRecordFoundError', - message: "Requested record not found in \"".concat(storeName, "\" store") - }); - } else { - resolve(_result || request.result || target); - } - }; - request.onerror = function (error /*: Event*/) { - return _this6.overrideError(reject, error); - }; - } - }); + var records = quick_storage.stores[storeName]; + return Promise.resolve(records instanceof Array ? records.length : 1); }); } /** - * Initiate bulk database request by reusing the same transaction to perform the operation + * Clear all records from a particular store */ }, { - key: "initBulkRequest", - value: function initBulkRequest(_ref9 /*:: */) /*: Promise>*/{ - var _this7 = this; - var storeName = _ref9 /*:: */.storeName, - target = _ref9 /*:: */.target, - action = _ref9 /*:: */.action, - _ref9$mode = _ref9 /*:: */.mode, - mode = _ref9$mode === void 0 ? AccessMode.readwrite : _ref9$mode; - if (!target || target && !target.length) { - return indexeddb_Promise.reject({ - name: 'NoTargetDefined', - message: "No array provided to perform ".concat(action, " bulk operation into \"").concat(storeName, "\" store") - }); - } - return this.open().then(function () { - return new indexeddb_Promise(function (resolve, reject) { - if (!_this7.indexedDbConnection) { - reject(_this7.noConnectionError); - } else { - var _this7$getTransaction = _this7.getTransactionStore({ - storeName: storeName, - mode: mode - }, reject, _this7.indexedDbConnection), - transaction = _this7$getTransaction.transaction, - store = _this7$getTransaction.store, - options = _this7$getTransaction.options; - - // Array contains or StoredRecord either RecordIds, but not both at the same time - var _result2 = new Array(); - var current = target[0]; - transaction.oncomplete = function () { - return resolve(_result2); - }; - var request = function request(req) { - req.onerror = function (error) { - return _this7.overrideError(reject, error); - }; - req.onsuccess = function () { - _result2.push(_this7.prepareResult(options, current) || req.result); - current = target[_result2.length]; - if (_result2.length < target.length) { - request(store[action](_this7.prepareTarget(options, current, action))); - } - }; - }; - request(store[action](_this7.prepareTarget(options, current, action))); - } + key: "clear", + value: function clear(storeName /*: ShortStoreName*/) /*: Promise*/{ + return this.open().then(function (open) { + if (open.status === 'error') { + return Promise.reject(open.error); + } + return new Promise(function (resolve) { + quick_storage.stores[storeName] = []; + resolve(); }); }); } /** - * Open cursor for bulk operations or listing + * Does nothing, it simply matches the common storage interface */ }, { - key: "openCursor", - value: function openCursor(_ref10 /*:: */) /*: Promise>*/{ - var _this8 = this; - var storeName = _ref10 /*:: */.storeName, - action = _ref10 /*:: */.action, - _ref10$range = _ref10 /*:: */.range, - range = _ref10$range === void 0 ? null : _ref10$range, - _ref10$firstOnly = _ref10 /*:: */.firstOnly, - firstOnly = _ref10$firstOnly === void 0 ? false : _ref10$firstOnly, - _ref10$mode = _ref10 /*:: */.mode, - mode = _ref10$mode === void 0 ? AccessMode.readonly : _ref10$mode; - return this.open().then(function () { - return new indexeddb_Promise(function (resolve, reject) { - if (!_this8.indexedDbConnection) { - reject(_this8.noConnectionError); - } else { - var _this8$getTransaction = _this8.getTransactionStore({ - storeName: storeName, - mode: mode - }, reject, _this8.indexedDbConnection), - transaction = _this8$getTransaction.transaction, - store = _this8$getTransaction.store, - index = _this8$getTransaction.index, - options = _this8$getTransaction.options; - var cursorRequest /*: OpenIDBCursorRequest*/ = (index || store).openCursor(range); - var items = new Array(); - transaction.oncomplete = function () { - return resolve(items); - }; - cursorRequest.onsuccess = function (e) { - var cursor = e.target.result; - if (cursor) { - if (action === Action.delete) { - cursor.delete(); - items.push(_this8.prepareResult(options, cursor.value) || cursor.value[options.keyPath]); - } else { - items.push(cursor.value); - } - if (!firstOnly) { - cursor.continue(); - } - } - }; - cursorRequest.onerror = function (error) { - return _this8.overrideError(reject, error); - }; - } - }); - }); - } - }, { - key: "deleteDatabaseByName", - value: function deleteDatabaseByName(dbName /*: string*/) /*: Promise*/{ - var _this9 = this; - return new indexeddb_Promise(function (resolve, reject) { - var request = _this9.idbFactory.deleteDatabase(dbName); - request.onerror = function (error) { - return _this9.overrideError(reject, error); - }; - request.onsuccess = function () { - return resolve(); - }; - request.onblocked = function (e) { - return reject(e.target); - }; - }); - } + key: "destroy", + value: function destroy() {} // eslint-disable-line /** - * Get all records from particular store + * Does nothing, it simply matches the common storage interface */ }, { - key: "getAll", - value: function getAll(storeName /*: ShortStoreName*/) /*: Promise>*/{ - var firstOnly = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false; - return this.openCursor({ - storeName: storeName, - action: Action.list, - firstOnly: firstOnly - }); - } - + key: "deleteDatabase", + value: function deleteDatabase() {} // eslint-disable-line + }], [{ + key: "isSupported", + value: /** - * Get the first row from the store + * Check if LocalStorage is supported in the current browser */ - }, { - key: "getFirst", - value: function getFirst(storeName /*: ShortStoreName*/) /*: Promise>*/{ - return this.getAll(storeName, true).then(function (all) { - return all.length ? all[0] : undefined; - }); - } - - /** - * Get item from a particular store - */ - }, { - key: "getItem", - value: function getItem(storeName /*: ShortStoreName*/, target /*: StoredRecordId*/) /*: Promise*/{ - return this.initRequest({ - storeName: storeName, - target: target, - action: Action.get - }); - } - - /** - * Return filtered result by value on available index - */ - }, { - key: "filterBy", - value: function filterBy(storeName /*: ShortStoreName*/, by /*: StoredValue*/) /*: Promise>*/{ - var range = IDBKeyRange.only(by); - return this.openCursor({ - storeName: storeName, - action: Action.list, - range: range - }); - } - - /** - * Add item to a particular store - */ - }, { - key: "addItem", - value: function addItem(storeName /*: ShortStoreName*/, target /*: StoredRecord*/) /*: Promise*/{ - return this.initRequest({ - storeName: storeName, - target: target, - action: Action.add, - mode: AccessMode.readwrite - }); - } - - /** - * Add multiple items into particular store - */ - }, { - key: "addBulk", - value: function addBulk(storeName /*: ShortStoreName*/, target /*: Array*/, overwrite /*: boolean*/) /*: Promise>*/{ - return this.initBulkRequest({ - storeName: storeName, - target: target, - action: overwrite ? Action.put : Action.add, - mode: AccessMode.readwrite - }); - } - - /** - * Update item in a particular store - */ - }, { - key: "updateItem", - value: function updateItem(storeName /*: ShortStoreName*/, target /*: StoredRecord*/) /*: Promise*/{ - return this.initRequest({ - storeName: storeName, - target: target, - action: Action.put, - mode: AccessMode.readwrite - }); - } - - /** - * Delete item from a particular store - */ - }, { - key: "deleteItem", - value: function deleteItem(storeName /*: ShortStoreName*/, target /*: StoredRecordId*/) /*: Promise*/{ - return this.initRequest({ - storeName: storeName, - target: target, - action: Action.delete, - mode: AccessMode.readwrite - }); - } - - /** - * Delete items until certain bound (primary key as a bound scope) - */ - }, { - key: "deleteBulk", - value: function deleteBulk(storeName /*: ShortStoreName*/, value /*: StoredValue*/, condition /*: KeyRangeCondition*/) /*: Promise>*/{ - var range = condition ? IDBKeyRange[condition](value) : IDBKeyRange.only(value); - return this.openCursor({ - storeName: storeName, - action: Action.delete, - range: range, - mode: AccessMode.readwrite - }); - } - - /** - * Trim the store from the left by specified length - */ - }, { - key: "trimItems", - value: function trimItems(storeName /*: ShortStoreName*/, length /*: number*/) /*: Promise>*/{ - var _this10 = this; - var options = scheme_map.right[convertStoreName(storeName, Direction.right)]; - return this.getAll(storeName).then(function (records) { - return records.length ? records[length - 1] : null; - }).then(function (record) { - return record ? _this10.deleteBulk(storeName, record[options.keyPath], KeyRangeCondition.UpperBound) : []; - }); - } - - /** - * Count the number of records in the store - */ - }, { - key: "count", - value: function count(storeName /*: ShortStoreName*/) /*: Promise*/{ - var _this11 = this; - return this.open().then(function () { - return new indexeddb_Promise(function (resolve, reject) { - if (!_this11.indexedDbConnection) { - reject(_this11.noConnectionError); - } else { - var _this11$getTransactio = _this11.getTransactionStore({ - storeName: storeName, - mode: AccessMode.readonly - }, reject, _this11.indexedDbConnection), - store = _this11$getTransactio.store; - var request = store.count(); - request.onsuccess = function () { - return resolve(request.result); - }; - request.onerror = function (error) { - return _this11.overrideError(reject, error); - }; - } - }); - }); - } - - /** - * Clear all records from a particular store - */ - }, { - key: "clear", - value: function clear(storeName /*: ShortStoreName*/) /*: Promise*/{ - return this.initRequest({ - storeName: storeName, - action: Action.clear, - mode: AccessMode.readwrite - }); - } - - /** - * Close the database and destroy the reference to it - */ - }, { - key: "destroy", - value: function destroy() /*: void*/{ - if (this.indexedDbConnection) { - this.indexedDbConnection.close(); - } - this.indexedDbConnection = null; - } - - /** - * Close db connection and delete the db - * WARNING: should be used only by adjust's demo app! - */ - }, { - key: "deleteDatabase", - value: function deleteDatabase() /*: Promise*/{ - this.destroy(); - return this.deleteDatabaseByName(this.dbName); - } - }], [{ - key: "tryOpen", - value: - /** - * Cached promise of IndexedDB validation - */ - - /** - * Tries to open a temporary database - */ - function tryOpen(db /*: IDBFactory*/) /*: Promise*/{ - return new indexeddb_Promise(function (resolve) { - try { - var request = db.open(IndexedDBWrapper.dbValidationName); - request.onsuccess = function () { - request.result.close(); - db.deleteDatabase(IndexedDBWrapper.dbValidationName); - resolve(true); - }; - request.onerror = function () { - return resolve(false); - }; - } catch (error) { - resolve(false); - } - }); - } - - /** - * Check if IndexedDB is supported in the current browser (exclude iOS forcefully) - */ - }, { - key: "isSupported", - value: function isSupported() /*: Promise*/{ - if (IndexedDBWrapper.isSupportedPromise) { - return IndexedDBWrapper.isSupportedPromise; + function isSupported() /*: Promise*/{ + if (LocalStorageWrapper.isSupportedPromise) { + return LocalStorageWrapper.isSupportedPromise; } else { - var notSupportedMessage = 'IndexedDB is not supported in this browser'; - IndexedDBWrapper.isSupportedPromise = new indexeddb_Promise(function (resolve) { - var indexedDB = IndexedDBWrapper.getIndexedDB(); - var iOS = !!navigator.platform && /iPad|iPhone|iPod/.test(navigator.platform); - if (!indexedDB || iOS) { - logger.warn(notSupportedMessage); - resolve(false); - } else { - var dbOpenablePromise = IndexedDBWrapper.tryOpen(indexedDB).then(function (dbOpenable) { - if (!dbOpenable) { - logger.warn(notSupportedMessage); - } - return dbOpenable; - }); - resolve(dbOpenablePromise); + LocalStorageWrapper.isSupportedPromise = new Promise(function (resolve /*: (value: boolean) => void*/) { + var supported = isLocalStorageSupported(); + if (!supported) { + logger.warn('LocalStorage is not supported in this browser'); } + resolve(supported); }); - } - return IndexedDBWrapper.isSupportedPromise; - } - - /** - * Get indexedDB instance - */ - }, { - key: "getIndexedDB", - value: function getIndexedDB() /*: Maybe*/{ - return window.indexedDB || window.mozIndexedDB || window.webkitIndexedDB || window.msIndexedDB; - } - }]); - return IndexedDBWrapper; -}(); -_defineProperty(IndexedDBWrapper, "dbValidationName", 'validate-db-openable'); -_defineProperty(IndexedDBWrapper, "isSupportedPromise", null); - -;// CONCATENATED MODULE: ./src/sdk/storage/localstorage.ts - - - - - - -var localstorage_Promise = typeof Promise === 'undefined' ? (__webpack_require__(702).Promise) : Promise; - - - - - - - - - -var LocalStorageWrapper = /*#__PURE__*/function () { - function LocalStorageWrapper() { - _classCallCheck(this, LocalStorageWrapper); - } - _createClass(LocalStorageWrapper, [{ - key: "open", - value: - /** - * Prepare schema details if not existent - */ - function open() /*: Promise*/{ - return LocalStorageWrapper.isSupported().then(function (supported) { - if (!supported) { - return { - status: 'error', - error: { - name: 'LSNotSupported', - message: 'LocalStorage is not supported' - } - }; - } - var storeNames = scheme_map.storeNames.left; - var activityState = activity_state.current || {}; - var inMemoryAvailable = activityState && !isEmpty(activityState); - entries(storeNames).filter(function (_ref) { - var _ref2 = _slicedToArray(_ref, 2), - store = _ref2[1]; - return !store.permanent; - }).forEach(function (_ref3) { - var _ref4 = _slicedToArray(_ref3, 2), - longStoreName = _ref4[0], - store = _ref4[1]; - var shortStoreName = store.name; - if (shortStoreName === ShortStoreName.ActivityState && !quick_storage.stores[shortStoreName]) { - quick_storage.stores[shortStoreName] = inMemoryAvailable ? [convertRecord(longStoreName, Direction.left, activityState)] : []; - } else if (!quick_storage.stores[shortStoreName]) { - quick_storage.stores[shortStoreName] = []; - } - }); - recover(); - return { - status: 'success' - }; - }); - } - - /** - * Get list of composite keys if available - */ - }, { - key: "getCompositeKeys", - value: function getCompositeKeys(options /*: StoreOptions*/) /*: Nullable>*/{ - var field = options.fields[options.keyPath]; - return isCompositeKeyStoreField(field) ? field.composite : null; - } - - /** - * Get composite keys when defined or fallback to primary key for particular store - */ - }, { - key: "getKeys", - value: function getKeys(storeName /*: ShortStoreName*/) /*: Array*/{ - var name = convertStoreName(storeName, Direction.right); - var options /*: StoreOptions*/ = scheme_map.right[name]; - return this.getCompositeKeys(options) || [options.keyPath]; - } - - /** - * Return next index using the current one and undefined if current is undefined - */ - }, { - key: "nextIndex", - value: function nextIndex(current /*: Maybe*/) /*: Maybe*/{ - return typeof current === 'number' ? current + 1 : undefined; - } - - /** - * Initiate quasi-database request - */ - }, { - key: "initRequest", - value: function initRequest /*:: */(_ref5 /*:: */, action /*: Action*/) /*: Promise*/{ - var _this = this; - var storeName = _ref5 /*:: */.storeName, - id = _ref5 /*:: */.id, - item = _ref5 /*:: */.item; - var options = scheme_map.right[convertStoreName(storeName, Direction.right)]; - return this.open().then(function (open) { - if (open.status === 'error') { - return localstorage_Promise.reject(open.error); - } - return new localstorage_Promise(function (resolve, reject) { - var items /*: Array*/ = quick_storage.stores[storeName]; - var keys = _this.getKeys(storeName); - var lastId = (items[items.length - 1] || {})[options.keyPath] || 0; - var target /*: StoredRecord*/; - if (!id) { - target = _objectSpread2({}, item); - } else { - var ids = Array.isArray(id) ? id.slice() : [id]; - target = keys.map(function (key, index) { - return [key, ids[index]]; - }).reduce(reducer, {}); - } - var index = target ? findIndex(items, keys, target) : 0; - return action(resolve, reject, { - keys: keys, - items: items, - index: index, - options: options, - lastId: lastId - }); - }); - }); - } - - /** - * Sort the array by provided key (key can be a composite one) - * - by default sorts in ascending order by primary keys - * - force order by provided value - */ - }, { - key: "sort", - value: function sort /*:: */(items /*: Array*/, keys /*: Array*/, exact /*: Nullable*/) /*: Array*/{ - var clone = _toConsumableArray(items); - var reversed = keys.slice().reverse(); - function compare(a /*: T*/, b /*: T*/, key /*: string*/) { - var expr1 = exact ? exact === a[key] : a[key] < b[key]; - var expr2 = exact ? exact > a[key] : a[key] > b[key]; - return expr1 ? -1 : expr2 ? 1 : 0; - } - return clone.sort(function (a, b) { - return reversed.reduce(function (acc, key) { - return acc || compare(a, b, key); - }, 0); - }); - } - - /** - * Prepare the target to be queried depending on the composite key if defined - */ - }, { - key: "prepareTarget", - value: function prepareTarget(options /*: StoreOptions*/, target /*: StoredRecord*/, next /*: number*/) /*: StoredRecord*/{ - var composite = this.getCompositeKeys(options); - return composite ? _objectSpread2(_defineProperty({}, options.keyPath, composite.map(function (key) { - return target[key]; - }).join('')), target) : options.autoIncrement && next ? _objectSpread2(_defineProperty({}, options.keyPath, next), target) : _objectSpread2({}, target); - } - - /** - * Prepare the result to be return depending on the composite key definition - */ - }, { - key: "prepareResult", - value: function prepareResult(options /*: StoreOptions*/, target /*: StoredRecord*/) /*: StoredRecordId*/{ - var composite = this.getCompositeKeys(options); - if (composite) { - return composite.map(function (key) { - return target[key]; - }).filter(function (value) { - return (/*: value is StoredValue*/!valueIsRecord(value) - ); - }); - } - return target[options.keyPath]; - } - - /** - * Get all records from particular store - */ - }, { - key: "getAll", - value: function getAll(storeName /*: ShortStoreName*/) /*: Promise>*/{ - var _this2 = this; - var firstOnly = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false; - return this.open().then(function (open) { - if (open.status === 'error') { - return localstorage_Promise.reject(open.error); - } - return new localstorage_Promise(function (resolve, reject) { - var value = quick_storage.stores[storeName]; - if (value instanceof Array) { - resolve(firstOnly ? [value[0]] : _this2.sort(value, _this2.getKeys(storeName))); - } else { - reject({ - name: 'NotFoundError', - message: "No objectStore named ".concat(storeName, " in this database") - }); - } - }); - }); - } - - /** - * Get the first row from the store - */ - }, { - key: "getFirst", - value: function getFirst(storeName /*: ShortStoreName*/) /*: Promise>*/{ - return this.getAll(storeName, true).then(function (all) { - return all.length ? all[0] : undefined; - }); - } - - /** - * Get item from a particular store - */ - }, { - key: "getItem", - value: function getItem(storeName /*: ShortStoreName*/, id /*: StoredRecordId*/) /*: Promise*/{ - var _this3 = this; - var action /*: Action*/ = function action /*: Action*/(resolve, reject, _ref6) { - var items = _ref6.items, - index = _ref6.index, - options = _ref6.options; - if (index === -1) { - reject({ - name: 'NotRecordFoundError', - message: "Requested record not found in \"".concat(storeName, "\" store") - }); - } else { - resolve(_this3.prepareTarget(options, items[index])); - } - }; - return this.initRequest({ - storeName: storeName, - id: id - }, action); - } - - /** - * Return filtered result by value on available index - */ - }, { - key: "filterBy", - value: function filterBy(storeName /*: ShortStoreName*/, by /*: StoredValue*/) /*: Promise>*/{ - return this.getAll(storeName).then(function (result /*: Array*/) { - return result.filter(function (item) { - var store = scheme_map.right[convertStoreName(storeName, Direction.right)]; - var indexedValue = store.index && item[store.index]; - return indexedValue === by; - }); - }); - } - - /** - * Add item to a particular store - */ - }, { - key: "addItem", - value: function addItem(storeName /*: ShortStoreName*/, item /*: StoredRecord*/) /*: Promise*/{ - var _this4 = this; - return this.initRequest({ - storeName: storeName, - item: item - }, function (resolve, reject, _ref7) { - var items = _ref7.items, - index = _ref7.index, - options = _ref7.options, - lastId = _ref7.lastId; - if (index !== -1) { - reject({ - name: 'ConstraintError', - message: "Constraint was not satisfied, trying to add existing item into \"".concat(storeName, "\" store") - }); - } else { - items.push(_this4.prepareTarget(options, item, _this4.nextIndex(lastId))); - quick_storage.stores[storeName] = items; - resolve(_this4.prepareResult(options, item)); - } - }); - } - - /** - * Add multiple items into particular store - */ - }, { - key: "addBulk", - value: function addBulk(storeName /*: ShortStoreName*/, target /*: Array*/, overwrite /*: boolean*/) /*: Promise>*/{ - var _this5 = this; - return this.initRequest({ - storeName: storeName - }, function (resolve, reject, _ref8) { - var keys = _ref8.keys, - items = _ref8.items, - options = _ref8.options, - lastId = _ref8.lastId; - if (!target || target && !target.length) { - reject({ - name: 'NoTargetDefined', - message: "No array provided to perform add bulk operation into \"".concat(storeName, "\" store") - }); - return; - } - var id = lastId; - var newItems = target.map(function (item) { - return _this5.prepareTarget(options, item, id = _this5.nextIndex(id)); - }); - var overlapping = newItems.filter(function (item) { - return findIndex(items, keys, item) !== -1; - }).map(function (item) { - return item[options.keyPath]; - }); - var currentItems = overwrite ? items.filter(function (item) { - return overlapping.indexOf(item[options.keyPath]) === -1; - }) : _toConsumableArray(items); - if (overlapping.length && !overwrite) { - reject({ - name: 'ConstraintError', - message: "Constraint was not satisfied, trying to add existing items into \"".concat(storeName, "\" store") - }); - } else { - quick_storage.stores[storeName] = _this5.sort([].concat(_toConsumableArray(currentItems), _toConsumableArray(newItems)), keys); - var result = target.map(function (item) { - return _this5.prepareResult(options, item); - }); - resolve(result); - } - }); - } - - /** - * Update item in a particular store - */ - }, { - key: "updateItem", - value: function updateItem(storeName /*: ShortStoreName*/, item /*: StoredRecord*/) /*: Promise*/{ - var _this6 = this; - return this.initRequest({ - storeName: storeName, - item: item - }, function (resolve, _, _ref9) { - var items = _ref9.items, - index = _ref9.index, - options = _ref9.options, - lastId = _ref9.lastId; - var nextId = index === -1 ? _this6.nextIndex(lastId) : undefined; - var target = _this6.prepareTarget(options, item, nextId); - if (index === -1) { - items.push(target); - } else { - items.splice(index, 1, target); - } - quick_storage.stores[storeName] = items; - resolve(_this6.prepareResult(options, item)); - }); - } - - /** - * Delete item from a particular store - */ - }, { - key: "deleteItem", - value: function deleteItem(storeName /*: ShortStoreName*/, id /*: StoredRecordId*/) /*: Promise*/{ - return this.initRequest({ - storeName: storeName, - id: id - }, function (resolve, _, _ref10) { - var items = _ref10.items, - index = _ref10.index; - if (index !== -1) { - items.splice(index, 1); - quick_storage.stores[storeName] = items; - } - resolve(id); - }); - } - - /** - * Find index of the item with the closest value to the bound - */ - }, { - key: "findMax", - value: function findMax(array /*: Array*/, key /*: string*/, value /*: StoredValue*/) /*: number*/{ - if (!array.length) { - return -1; - } - var max = { - index: -1, - value: typeof value === 'string' ? '' : 0 - }; - for (var i = 0; i < array.length; i += 1) { - if (array[i][key] <= value) { - if (array[i][key] >= max.value) { - max = { - value: array[i][key], - index: i - }; - } - } else { - return max.index; - } - } - return max.index; - } - - /** - * Delete items until certain bound (primary key as a bound scope) - * Returns array of deleted elements - */ - }, { - key: "deleteBulk", - value: function deleteBulk(storeName /*: ShortStoreName*/, value /*: StoredValue*/, condition /*: KeyRangeCondition*/) /*: Promise>*/{ - var _this7 = this; - return this.getAll(storeName).then(function (items /*: Array*/) { - var keys = _this7.getKeys(storeName); - var key = scheme_map.right[convertStoreName(storeName, Direction.right)].index || keys[0]; - var exact = condition ? null : value; - var sorted /*: Array*/ = _this7.sort(items, keys, exact); - var index = _this7.findMax(sorted, key, value); - if (index === -1) { - return []; - } - var start = condition === KeyRangeCondition.LowerBound ? index : 0; - var end = !condition || condition === KeyRangeCondition.UpperBound ? index + 1 : sorted.length; - var deleted /*: Array*/ = sorted.splice(start, end).map(function (item) { - return keys.length === 1 ? item[key] : keys.map(function (k) { - return item[k]; - }); - }); - quick_storage.stores[storeName] = sorted; - return deleted; - }); - } - - /** - * Trim the store from the left by specified length - */ - }, { - key: "trimItems", - value: function trimItems(storeName /*: ShortStoreName*/, length /*: number*/) /*: Promise>*/{ - var _this8 = this; - var convertedName = convertStoreName(storeName, Direction.right); - var options /*: StoreOptions*/ = scheme_map.right[convertedName]; - return this.getAll(storeName).then(function (records /*: Array>*/) { - return records.length ? records[length - 1] : null; - }).then(function (record) { - return record ? _this8.deleteBulk(storeName, record[options.keyPath], KeyRangeCondition.UpperBound) : []; - }); - } - - /** - * Count the number of records in the store - */ - }, { - key: "count", - value: function count(storeName /*: ShortStoreName*/) /*: Promise*/{ - return this.open().then(function (open) { - if (open.status === 'error') { - return localstorage_Promise.reject(open.error); - } - var records = quick_storage.stores[storeName]; - return localstorage_Promise.resolve(records instanceof Array ? records.length : 1); - }); - } - - /** - * Clear all records from a particular store - */ - }, { - key: "clear", - value: function clear(storeName /*: ShortStoreName*/) /*: Promise*/{ - return this.open().then(function (open) { - if (open.status === 'error') { - return localstorage_Promise.reject(open.error); - } - return new localstorage_Promise(function (resolve) { - quick_storage.stores[storeName] = []; - resolve(); - }); - }); - } - - /** - * Does nothing, it simply matches the common storage interface - */ - }, { - key: "destroy", - value: function destroy() {} // eslint-disable-line - - /** - * Does nothing, it simply matches the common storage interface - */ - }, { - key: "deleteDatabase", - value: function deleteDatabase() {} // eslint-disable-line - }], [{ - key: "isSupported", - value: - /** - * Cached promise of LocalStorage validation - */ - - /** - * Check if LocalStorage is supported in the current browser - */ - function isSupported() /*: Promise*/{ - if (LocalStorageWrapper.isSupportedPromise) { - return LocalStorageWrapper.isSupportedPromise; - } else { - LocalStorageWrapper.isSupportedPromise = new localstorage_Promise(function (resolve /*: (value: boolean) => void*/) { - var supported = isLocalStorageSupported(); - if (!supported) { - logger.warn('LocalStorage is not supported in this browser'); - } - resolve(supported); - }); - } - return LocalStorageWrapper.isSupportedPromise; - } - }]); - return LocalStorageWrapper; -}(); -_defineProperty(LocalStorageWrapper, "isSupportedPromise", null); - -;// CONCATENATED MODULE: ./src/sdk/storage/storage.ts - - -var storage_Promise = typeof Promise === 'undefined' ? (__webpack_require__(702).Promise) : Promise; - - - - - - - -var StorageType; -(function (StorageType) { - StorageType[StorageType["noStorage"] = STORAGE_TYPES.NO_STORAGE] = "noStorage"; - StorageType[StorageType["indexedDB"] = STORAGE_TYPES.INDEXED_DB] = "indexedDB"; - StorageType[StorageType["localStorage"] = STORAGE_TYPES.LOCAL_STORAGE] = "localStorage"; -})(StorageType || (StorageType = {})); -/** - * Methods to extend - */ -var _methods /*: CommonStorageMethods*/ = { - getAll: _getAll, - getFirst: _getFirst, - getItem: _getItem, - filterBy: _filterBy, - addItem: _addItem, - addBulk: _addBulk, - updateItem: _updateItem, - deleteItem: _deleteItem, - deleteBulk: _deleteBulk, - trimItems: _trimItems, - count: _count, - clear: _clear, - destroy: _destroy, - deleteDatabase: _deleteDatabase -}; - -/** - * Extends storage's getAll method by decoding returned records - */ -function _getAll(storage /*: IStorage*/, storeName /*: ShortStoreName*/, firstOnly /*: boolean*/) { - return storage.getAll(storeName, firstOnly).then(function (records) { - return convertRecords(storeName, Direction.right, records); - }); -} - -/** - * Extends storage's getFirst method by decoding returned record - */ -function _getFirst(storage /*: IStorage*/, storeName /*: ShortStoreName*/) { - return storage.getFirst(storeName).then(function (record) { - return convertRecord(storeName, Direction.right, record); - }); -} - -/** - * Extends storage's getItem method by encoding target value and then decoding returned record - */ -function _getItem(storage /*: IStorage*/, storeName /*: ShortStoreName*/, target /*: StoredRecordId*/) { - return storage.getItem(storeName, convertValues(storeName, Direction.left, target)).then(function (record) { - return convertRecord(storeName, Direction.right, record); - }).catch(function (error) { - return storage_Promise.reject(decodeErrorMessage(storeName, error)); - }); -} - -/** - * Extends storage's filterBy method by encoding target value and then decoding returned records - */ -function _filterBy(storage /*: IStorage*/, storeName /*: ShortStoreName*/, target /*: string*/) { - return storage.filterBy(storeName, encodeValue(target)).then(function (records) { - return convertRecords(storeName, Direction.right, records); - }); -} - -/** - * Extends storage's addItem method by encoding target record and then decoding returned keys - */ -function _addItem(storage /*: IStorage*/, storeName /*: ShortStoreName*/, record /*: StoredRecord*/) { - var convertedRecord = convertRecord(storeName, Direction.left, record); - return storage.addItem(storeName, convertedRecord).then(function (target) { - return convertValues(storeName, Direction.right, target); - }).catch(function (error) { - return storage_Promise.reject(decodeErrorMessage(storeName, error)); - }); -} - -/** - * Extends storage's addBulk method by encoding target records and then decoding returned keys - */ -function _addBulk(storage /*: IStorage*/, storeName /*: ShortStoreName*/, records /*: Array*/, overwrite /*: boolean*/) { - var convertedRecords /*: Array*/ = convertRecords(storeName, Direction.left, records); - return storage.addBulk(storeName, convertedRecords, overwrite).then(function (values) { - return values.map(function (target) { - return convertValues(storeName, Direction.right, target); - }); - }).catch(function (error) { - return storage_Promise.reject(decodeErrorMessage(storeName, error)); - }); -} - -/** - * Extends storage's updateItem method by encoding target record and then decoding returned keys - */ -function _updateItem(storage /*: IStorage*/, storeName /*: ShortStoreName*/, record /*: StoredRecord*/) { - var convertedRecord = convertRecord(storeName, Direction.left, record); - return storage.updateItem(storeName, convertedRecord).then(function (target) { - return convertValues(storeName, Direction.right, target); - }); -} - -/** - * Extends storage's deleteItem method by encoding target value and then decoding returned keys - */ -function _deleteItem(storage /*: IStorage*/, storeName /*: ShortStoreName*/, target /*: StoredRecordId*/) { - return storage.deleteItem(storeName, convertValues(storeName, Direction.left, target)).then(function (target) { - return convertValues(storeName, Direction.right, target); - }); -} - -/** - * Extends storage's deleteBulk method by encoding target value and then decoding returned records that are deleted - */ -function _deleteBulk(storage /*: IStorage*/, storeName /*: ShortStoreName*/, value /*: StoredValue*/, condition /*: KeyRangeCondition*/) { - return storage.deleteBulk(storeName, encodeValue(value), condition).then(function (records) { - return records.map(function (record) { - return convertValues(storeName, Direction.right, record); - }); - }); -} - -/** - * Extends storage's trimItems method by passing encoded storage name - */ -function _trimItems(storage /*: IStorage*/, storeName /*: ShortStoreName*/, length /*: number*/) { - return storage.trimItems(storeName, length); -} - -/** - * Extends storage's count method by passing encoded storage name - */ -function _count(storage /*: IStorage*/, storeName /*: ShortStoreName*/) { - return storage.count(storeName); -} - -/** - * Extends storage's clear method by passing encoded storage name - */ -function _clear(storage /*: IStorage*/, storeName /*: ShortStoreName*/) { - return storage.clear(storeName); -} - -/** - * Calls storage's destroy method - */ -function _destroy(storage /*: IStorage*/) { - return storage.destroy(); -} - -/** - * Calls storage's deleteDatabase method - */ -function _deleteDatabase(storage /*: IndexedDB | LocalStorage*/) { - return storage.deleteDatabase(); -} - -/** - * Augment whitelisted methods with encoding/decoding functionality - */ -function _augment() /*: StorageMethods*/{ - var methods /*: Array<[MethodName, StorageMethod]>*/ = entries(_methods).map(function (_ref /*:: */) { - var _ref2 = _slicedToArray(_ref /*:: */, 2), - methodName = _ref2[0], - method = _ref2[1]; - var augmentedMethod /*: StorageMethod*/ = function augmentedMethod /*: StorageMethod*/(storeName /*: StoreName*/) { - for (var _len = arguments.length, args = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) { - args[_key - 1] = arguments[_key]; - } - return storage_init().then(function (_ref3) { - var storage = _ref3.storage; - if (storage) { - return method.call.apply(method, [null, storage, convertStoreName(storeName, Direction.left)].concat(args)); - } - }); - }; - return [methodName, augmentedMethod]; - }); - return methods.reduce(reducer, {}); -} - -/** - * Type of available storage - */ -var type /*: StorageType*/; - -/** - * Returns type of used storage which is one of possible values INDEXED_DB, LOCAL_STORAGE or NO_STORAGE if there is no - * storage available - */ -function getType() /*: StorageType*/{ - return type; -} - -/** - * Cached promise of Storage initialization - */ -var _initializationPromise /*: Nullable>*/ = null; - -/** - * Check which storage is available and pick it up - * Prefer indexedDB over localStorage - */ -function storage_init(dbName /*: string*/) /*: Promise*/{ - var storage /*: Nullable*/ = null; - if (_initializationPromise !== null) { - return _initializationPromise; - } else { - _initializationPromise = storage_Promise.all([IndexedDBWrapper.isSupported(), LocalStorageWrapper.isSupported()]).then(function (_ref4) { - var _ref5 = _slicedToArray(_ref4, 2), - idbSupported = _ref5[0], - lsSupported = _ref5[1]; - quick_storage.setCustomName(dbName); - if (idbSupported) { - type = StorageType.indexedDB; - var idb = new IndexedDBWrapper(); - return idb.setCustomName(dbName).then(function () { - return storage = idb; - }); - } else if (lsSupported) { - type = StorageType.localStorage; - storage = new LocalStorageWrapper(); - return storage_Promise.resolve(storage); - } else { - logger.error('There is no storage available, app will run with minimum set of features'); - type = StorageType.noStorage; - storage = null; - return storage_Promise.resolve(storage); - } - }).then(function () { - return { - type: type, - storage: storage - }; - }); - } - return _initializationPromise; -} -/* harmony default export */ const storage = (_objectSpread2({ - init: storage_init, - getType: getType -}, _augment())); -;// CONCATENATED MODULE: ./src/sdk/default-params.js - - -var default_params_Promise = typeof Promise === 'undefined' ? (__webpack_require__(702).Promise) : Promise; -/*:: // -import { type NavigatorT, type CreatedAtT, type SentAtT, type WebUuidT, type TrackEnabledT, type PlatformT, type LanguageT, type MachineTypeT, type QueueSizeT, type DefaultParamsT } from './types';*/ - - - - -/** - * Get created at timestamp - * - * @returns {{createdAt: string}} - * @private - */ -function _getCreatedAt() /*: CreatedAtT*/{ - return { - createdAt: getTimestamp() - }; -} - -/** - * Get sent at timestamp - * - * @returns {{sentAt: string}} - * @private - */ -function _getSentAt() /*: SentAtT*/{ - return { - sentAt: getTimestamp() - }; -} - -/** - * Read uuid from the activity state - * - * @returns {{webUuid: string}} - * @private - */ -function _getWebUuid() /*: WebUuidT*/{ - return { - webUuid: activity_state.current.uuid - }; -} - -/** - * Get track enabled parameter by reading doNotTrack - * - * @returns {{trackingEnabled: boolean}|null} - * @private - */ -function _getTrackEnabled() /*: ?TrackEnabledT*/{ - var navigatorExt = (navigator /*: NavigatorT*/); - var isNavigatorDNT = typeof navigatorExt.doNotTrack !== 'undefined'; - var isWindowDNT = typeof window.doNotTrack !== 'undefined'; - var isMsDNT = typeof navigatorExt.msDoNotTrack !== 'undefined'; - var dnt = isNavigatorDNT ? navigatorExt.doNotTrack : isWindowDNT ? window.doNotTrack : isMsDNT ? navigatorExt.msDoNotTrack : null; - if (parseInt(dnt, 10) === 0 || dnt === 'no') { - return { - trackingEnabled: true - }; - } - if (parseInt(dnt, 10) === 1 || dnt === 'yes') { - return { - trackingEnabled: false - }; - } - return null; -} - -/** - * Get platform parameter => hardcoded to `web` - * - * @returns {{platform: string}} - * @private - */ -function _getPlatform() /*: PlatformT*/{ - return { - platform: 'web' - }; -} - -/** - * Get language preferences - * - * @returns {{language: string, country: string|undefined}} - * @private - */ -function _getLanguage() /*: LanguageT*/{ - var navigatorExt = (navigator /*: NavigatorT*/); - var _split = (navigatorExt.language || navigatorExt.userLanguage || 'en').split('-'), - _split2 = _slicedToArray(_split, 2), - language = _split2[0], - country = _split2[1]; - return { - language: language, - country: country ? '' + country.toLowerCase() : undefined - }; -} - -/** - * Get machine type from navigator.platform property - * - * @returns {{machineType: (string|undefined)}} - */ -function _getMachineType() /*: MachineTypeT*/{ - var ua = navigator.userAgent || navigator.vendor; - var overrideWin32 = navigator.platform === 'Win32' && (ua.indexOf('WOW64') !== -1 || ua.indexOf('Win64') !== -1); - return { - machineType: overrideWin32 ? 'Win64' : navigator.platform - }; -} - -/** - * Get the current queue size - * - * @returns {Promise} - * @private - */ -function _getQueueSize() /*: Promise*/{ - return storage.getAll('queue').then(function (records) { - return { - queueSize: records.length - }; - }); -} -function defaultParams() /*: Promise*/{ - return _getQueueSize().then(function (queueSize) { - return _objectSpread2(_objectSpread2(_objectSpread2(_objectSpread2(_objectSpread2(_objectSpread2(_objectSpread2(_objectSpread2({}, _getCreatedAt()), _getSentAt()), _getWebUuid()), _getTrackEnabled()), _getPlatform()), _getLanguage()), _getMachineType()), queueSize); - }); -} -;// CONCATENATED MODULE: ./src/sdk/http.js - - -var http_Promise = typeof Promise === 'undefined' ? (__webpack_require__(702).Promise) : Promise; -/*:: // -import { type UrlT, type DefaultParamsT, type HttpSuccessResponseT, type HttpErrorResponseT, type HttpRequestParamsT, type ErrorCodeT } from './types';*/ - - - - - - - -/*:: type ParamsWithAttemptsT = $PropertyType*/ -/** - * Get filtered response from successful request - * - * @param {Object} xhr - * @param {String} url - * @returns {Object} - * @private - */ -function _getSuccessResponse(xhr /*: XMLHttpRequest*/, url /*: UrlT*/) /*: HttpSuccessResponseT*/{ - var result = JSON.parse(xhr.responseText); - var response = { - status: 'success', - adid: result.adid, - timestamp: result.timestamp, - ask_in: result.ask_in, - retry_in: result.retry_in, - continue_in: result.continue_in, - tracking_state: result.tracking_state, - attribution: undefined, - message: undefined - }; - if (isRequest(url, 'attribution')) { - response.attribution = result.attribution; - response.message = result.message; - } - return entries(response).filter(function (_ref) { - var _ref2 = _slicedToArray(_ref, 2), - value = _ref2[1]; - return !!value; - }).reduce(reducer, {}); -} - -/** - * Get an error object which is about to be passed to resolve or reject method - * - * @param {Object} xhr - * @param {string} code - * @param {boolean=} proceed - * @returns {Object} - * @private - */ -function _getErrorResponse(xhr /*: XMLHttpRequest*/, code /*: ErrorCodeT*/) /*: HttpErrorResponseT*/{ - var proceed /*: boolean*/ = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false; - return { - status: 'error', - action: proceed ? 'CONTINUE' : 'RETRY', - response: isValidJson(xhr.responseText) ? JSON.parse(xhr.responseText) : xhr.responseText, - message: HTTP_ERRORS[code], - code: code - }; -} - -/** - * Encode parameter depending on the type - * - * @param {string} key - * @param {*} value - * @returns {string} - * @private - */ -function _encodeParam(_ref3 /*:: */) /*: string*/{ - var _ref4 = _slicedToArray(_ref3 /*:: */, 2), - key = _ref4[0], - value = _ref4[1]; - var encodedKey = encodeURIComponent(key); - var encodedValue = value; - if (typeof value === 'string') { - encodedValue = encodeURIComponent(value); - } - if (isObject(value)) { - encodedValue = encodeURIComponent(JSON.stringify(value) || ''); - } - return [encodedKey, encodedValue].join('='); -} - -/** - * Creates the log key with some spaces appended to it - * - * @param {string} header - * @param {string} str - * @returns {string} - * @private - */ -function _logKey(header /*: string*/, str /*: string*/) /*: string*/{ - var spaces = header.slice(0, header.length - str.length - 1).split('').reduce(function (acc) { - return acc.concat(' '); - }, ''); - return "".concat(str).concat(spaces, ":"); -} - -/** - * Encode key-value pairs to be used in url - * - * @param {Object} params - * @param {Object} defaultParams - * @returns {string} - * @private - */ -function _encodeParams(params /*: ParamsWithAttemptsT*/, defaultParams /*: DefaultParamsT*/) /*: string*/{ - var logParamsHeader = 'REQUEST PARAMETERS:'; - var toSnakeCase = function toSnakeCase(key) { - return key.replace(/([A-Z])/g, function ($1) { - return "_".concat($1.toLowerCase()); - }); - }; - var allParams = entries(_objectSpread2(_objectSpread2(_objectSpread2({}, config.getBaseParams()), defaultParams), params)).map(function (_ref5 /*:: */) { - var _ref6 = _slicedToArray(_ref5 /*:: */, 2), - key = _ref6[0], - value = _ref6[1]; - return [toSnakeCase(key), value]; - }); - logger.log(logParamsHeader); - return allParams.filter(function (_ref7) { - var _ref8 = _slicedToArray(_ref7, 2), - value = _ref8[1]; - return isEmptyEntry(value); - }).map(function (_ref9) { - var _ref10 = _slicedToArray(_ref9, 2), - key = _ref10[0], - value = _ref10[1]; - logger.log(_logKey(logParamsHeader, key), value); - return _encodeParam([key, value]); - }).join('&'); -} - -/** - * Handle xhr response from server - * - * @param {Function} reject - * @param {Function} resolve - * @param {Object} xhr - * @param {string} url - * @private - */ -function _handleReadyStateChange(reject, resolve, _ref11 /*:: */) { - var xhr = _ref11 /*:: */.xhr, - url = _ref11 /*:: */.url; - if (xhr.readyState !== 4) { - return; - } - var okStatus = xhr.status >= 200 && xhr.status < 300; - var validJson = isValidJson(xhr.responseText); - if (xhr.status === 0) { - reject(_getErrorResponse(xhr, 'NO_CONNECTION')); - } else { - if (validJson) { - return okStatus ? resolve(_getSuccessResponse(xhr, url)) : resolve(_getErrorResponse(xhr, 'SERVER_CANNOT_PROCESS', true)); - } else { - return okStatus ? reject(_getErrorResponse(xhr, 'SERVER_MALFORMED_RESPONSE')) : reject(_getErrorResponse(xhr, 'SERVER_INTERNAL_ERROR')); - } - } -} - -/** - * Prepare url and params depending on the resource type - * - * @param {string} url - * @param {string} method - * @param {Object} params - * @param {Object} defaultParams - * @returns {{encodedParams: string, fullUrl: string}} - * @private - */ -function _prepareUrlAndParams(_ref12 /*:: */, defaultParams /*: DefaultParamsT*/) /*: {fullUrl: string, encodedParams: string}*/{ - var endpoint = _ref12 /*:: */.endpoint, - url = _ref12 /*:: */.url, - method = _ref12 /*:: */.method, - params = _ref12 /*:: */.params; - var encodedParams = _encodeParams(params, defaultParams); - return { - fullUrl: endpoint + url + (method === 'GET' ? "?".concat(encodedParams) : ''), - encodedParams: encodedParams - }; -} - -/** - * Set headers for the xhr object - * - * @param {XMLHttpRequest} xhr - * @param {string} method - * @private - */ -function _prepareHeaders(xhr /*: XMLHttpRequest*/, method /*: $PropertyType*/) /*: void*/{ - var logHeader = 'REQUEST HEADERS:'; - var headers = [['Client-SDK', "js".concat(globals.version)], ['Content-Type', method === 'POST' ? 'application/x-www-form-urlencoded' : 'application/json']]; - logger.log(logHeader); - headers.forEach(function (_ref13) { - var _ref14 = _slicedToArray(_ref13, 2), - key = _ref14[0], - value = _ref14[1]; - xhr.setRequestHeader(key, value); - logger.log(_logKey(logHeader, key), value); - }); -} - -/** - * Build xhr to perform all kind of api requests - * - * @param {string} url - * @param {string} [method='GET'] - * @param {Object} [params={}] - * @param {Object} defaultParams - * @returns {Promise} - */ -function _buildXhr(_ref15 /*:: */, defaultParams /*: DefaultParamsT*/) /*: Promise*/{ - var endpoint = _ref15 /*:: */.endpoint, - url = _ref15 /*:: */.url, - _ref15$method = _ref15 /*:: */.method, - method = _ref15$method === void 0 ? 'GET' : _ref15$method, - _ref15$params = _ref15 /*:: */.params, - params = _ref15$params === void 0 ? {} : _ref15$params; - var _prepareUrlAndParams2 = _prepareUrlAndParams({ - endpoint: endpoint, - url: url, - method: method, - params: params - }, defaultParams), - fullUrl = _prepareUrlAndParams2.fullUrl, - encodedParams = _prepareUrlAndParams2.encodedParams; - return new http_Promise(function (resolve, reject) { - var xhr = new XMLHttpRequest(); - xhr.open(method, fullUrl, true); - _prepareHeaders(xhr, method); - xhr.onreadystatechange = function () { - return _handleReadyStateChange(reject, resolve, { - xhr: xhr, - url: url - }); - }; - xhr.onerror = function () { - return reject(_getErrorResponse(xhr, 'TRANSACTION_ERROR')); - }; - xhr.send(method === 'GET' ? undefined : encodedParams); - }); -} - + } + return LocalStorageWrapper.isSupportedPromise; + } + }]); +}(); /** - * Intercept response from backend - * - * @param {Object} result - * @param {string} result.status - * @param {string} url - * @returns {Object} - * @private + * Cached promise of LocalStorage validation */ -function _interceptResponse(result /*: HttpSuccessResponseT | HttpErrorResponseT*/, url /*: UrlT*/) /*: HttpSuccessResponseT | HttpErrorResponseT*/{ - if (result.status === 'success') { - return _interceptSuccess(result, url); - } - return result; -} +_defineProperty(LocalStorageWrapper, "isSupportedPromise", null); + +;// CONCATENATED MODULE: ./src/sdk/storage/storage.ts + + + -/** - * Intercept successful response from backend and: - * - always check if tracking_state is set to `opted_out` and if yes disable sdk - * - check if ask_in parameter is present in order to check if attribution have been changed - * - emit session finish event if session request - * - * @param {Object} result - * @param {string} result.tracking_state - * @param {number} result.ask_in - * @param {string} url - * @returns {Object} - * @private - */ -function _interceptSuccess(result /*: HttpSuccessResponseT*/, url) /*: HttpSuccessResponseT*/{ - var isGdprRequest = isRequest(url, 'gdpr_forget_device'); - var isAttributionRequest = isRequest(url, 'attribution'); - var isSessionRequest = isRequest(url, 'session'); - var isThirdPartySharingOptOutRequest = isRequest(url, 'disable_third_party_sharing'); - var optedOut = result.tracking_state === 'opted_out'; - if (!isGdprRequest && optedOut) { - publish('sdk:gdpr-forget-me'); - return result; - } - if (!isAttributionRequest && !isGdprRequest && !optedOut && result.ask_in) { - publish('attribution:check', result); - } - if (isSessionRequest) { - publish('session:finished', result); - } - if (isThirdPartySharingOptOutRequest) { - publish('sdk:third-party-sharing-opt-out'); - return result; - } - return result; -} -/** - * Http request factory to perform all kind of api requests - * - * @param {Object} options - * @returns {Promise} - */ -function http(options /*: HttpRequestParamsT*/) /*: Promise*/{ - return defaultParams().then(function (defaultParams) { - return _buildXhr(options, defaultParams); - }).then(function (result) { - return _interceptResponse(result, options.url); - }); -} -;// CONCATENATED MODULE: ./src/sdk/backoff.js -/*:: // -import { type BackOffStrategyT } from './types';*/ + + + +// eslint-disable-line @typescript-eslint/no-explicit-any +var StorageType = function (StorageType) { + StorageType[StorageType["noStorage"] = STORAGE_TYPES.NO_STORAGE] = "noStorage"; + StorageType[StorageType["indexedDB"] = STORAGE_TYPES.INDEXED_DB] = "indexedDB"; + StorageType[StorageType["localStorage"] = STORAGE_TYPES.LOCAL_STORAGE] = "localStorage"; + return StorageType; +}(StorageType || {}); /** - * Options for the back-off strategy for different environments - * - * @type {Object} + * Methods to extend */ -var _options = { - long: { - delay: 2 * MINUTE, - maxDelay: DAY, - minRange: 0.5, - maxRange: 1.0 - }, - short: { - delay: 200, - maxDelay: HOUR, - minRange: 0.5, - maxRange: 1.0 - }, - test: { - delay: 100, - maxDelay: 300 - } +var _methods /*: CommonStorageMethods*/ = { + getAll: _getAll, + getFirst: _getFirst, + getItem: _getItem, + filterBy: _filterBy, + addItem: _addItem, + addBulk: _addBulk, + updateItem: _updateItem, + deleteItem: _deleteItem, + deleteBulk: _deleteBulk, + trimItems: _trimItems, + count: _count, + clear: _clear, + destroy: _destroy, + deleteDatabase: _deleteDatabase }; /** - * Get random number in provided range - * - * @param {number} min - * @param {number} max - * @returns {number} - * @private + * Extends storage's getAll method by decoding returned records */ -function _randomInRange(min, max) { - return Math.random() * (max - min) + min; +function _getAll(storage /*: IStorage*/, storeName /*: ShortStoreName*/, firstOnly /*: boolean*/) { + return storage.getAll(storeName, firstOnly).then(function (records) { + return convertRecords(storeName, Direction.right, records); + }); } /** - * Calculate exponential back-off with optional jitter factor applied - * - * @param {number} attempts - * @param {string} strategy - * @returns {number} + * Extends storage's getFirst method by decoding returned record */ -function backOff(attempts /*: number*/, strategy /*: ?BackOffStrategyT*/) /*: number*/{ - strategy = strategy || 'long'; - var options = false ? 0 : _options[strategy]; - var delay = options.delay * Math.pow(2, attempts - 1); - delay = Math.min(delay, options.maxDelay); - if (options.minRange && options.maxRange) { - delay = delay * _randomInRange(options.minRange, options.maxRange); - } - return Math.round(delay); +function _getFirst(storage /*: IStorage*/, storeName /*: ShortStoreName*/) { + return storage.getFirst(storeName).then(function (record) { + return convertRecord(storeName, Direction.right, record); + }); } -;// CONCATENATED MODULE: ./src/sdk/listeners.js - -/*:: // -import { type DocumentT } from './types';*/ - -/*:: type EventCbT = (e: Event) => void*/ -/*:: type PageVisibilityHiddenAttr = 'hidden' | 'mozHidden' | 'msHidden' | 'oHidden' | 'webkitHidden'*/ -/*:: type PageVisibilityEventName = 'visibilitychange' | 'mozvisibilitychange' | 'msvisibilitychange' | 'ovisibilitychange' | 'webkitvisibilitychange'*/ -/*:: type PageVisibilityApiMap = {| - hidden: PageVisibilityHiddenAttr, - visibilityChange: PageVisibilityEventName -|}*/ -var _connected /*: boolean*/ = navigator.onLine; /** - * Bind to online and offline events + * Extends storage's getItem method by encoding target value and then decoding returned record */ -function register() /*: void*/{ - on(window, 'online', _handleOnline); - on(window, 'offline', _handleOffline); +function _getItem(storage /*: IStorage*/, storeName /*: ShortStoreName*/, target /*: StoredRecordId*/) { + return storage.getItem(storeName, convertValues(storeName, Direction.left, target)).then(function (record) { + return convertRecord(storeName, Direction.right, record); + }).catch(function (error) { + return Promise.reject(decodeErrorMessage(storeName, error)); + }); } /** - * Handle online event, set connected flag to true - * - * @private + * Extends storage's filterBy method by encoding target value and then decoding returned records */ -function _handleOnline() /*: void*/{ - _connected = true; +function _filterBy(storage /*: IStorage*/, storeName /*: ShortStoreName*/, target /*: string*/) { + return storage.filterBy(storeName, encodeValue(target)).then(function (records) { + return convertRecords(storeName, Direction.right, records); + }); } /** - * Handle offline event, set connected flag to false - * @private + * Extends storage's addItem method by encoding target record and then decoding returned keys */ -function _handleOffline() /*: void*/{ - _connected = false; +function _addItem(storage /*: IStorage*/, storeName /*: ShortStoreName*/, record /*: StoredRecord*/) { + var convertedRecord = convertRecord(storeName, Direction.left, record); + return storage.addItem(storeName, convertedRecord).then(function (target) { + return convertValues(storeName, Direction.right, target); + }).catch(function (error) { + return Promise.reject(decodeErrorMessage(storeName, error)); + }); } /** - * Bind event to an element - * - * @param {Window|Document} element - * @param {string} eventName - * @param {Function} func + * Extends storage's addBulk method by encoding target records and then decoding returned keys */ -function on(element /*: Document | any*/, eventName /*: string*/, func /*: EventCbT*/) /*: void*/{ - if (element.addEventListener) { - element.addEventListener(eventName, func, false); - } +function _addBulk(storage /*: IStorage*/, storeName /*: ShortStoreName*/, records /*: Array*/, overwrite /*: boolean*/) { + var convertedRecords /*: Array*/ = convertRecords(storeName, Direction.left, records); + return storage.addBulk(storeName, convertedRecords, overwrite).then(function (values) { + return values.map(function (target) { + return convertValues(storeName, Direction.right, target); + }); + }).catch(function (error) { + return Promise.reject(decodeErrorMessage(storeName, error)); + }); } /** - * Unbind event off an element - * - * @param {Window|Document} element - * @param {string} eventName - * @param {Function} func + * Extends storage's updateItem method by encoding target record and then decoding returned keys */ -function off(element /*: Document | any*/, eventName /*: string*/, func /*: EventCbT*/) /*: void*/{ - if (element.removeEventListener) { - element.removeEventListener(eventName, func, false); - } +function _updateItem(storage /*: IStorage*/, storeName /*: ShortStoreName*/, record /*: StoredRecord*/) { + var convertedRecord = convertRecord(storeName, Direction.left, record); + return storage.updateItem(storeName, convertedRecord).then(function (target) { + return convertValues(storeName, Direction.right, target); + }); } /** - * Get Page Visibility API attributes that can be accessed depending on the browser implementation - * - * @returns {{hidden: string, visibilityChange: string}|null} - * @private + * Extends storage's deleteItem method by encoding target value and then decoding returned keys */ -function getVisibilityApiAccess() /*: ?PageVisibilityApiMap*/{ - var documentExt = (document /*: DocumentT*/); - if (typeof documentExt.hidden !== 'undefined') { - return { - hidden: 'hidden', - visibilityChange: 'visibilitychange' - }; - } - var accessMap /*: {[key: PageVisibilityHiddenAttr]: PageVisibilityEventName}*/ = { - mozHidden: 'mozvisibilitychange', - msHidden: 'msvisibilitychange', - oHidden: 'ovisibilitychange', - webkitHidden: 'webkitvisibilitychange' - }; - var accessMapEntries = entries(accessMap); - for (var i = 0; i < accessMapEntries.length; i += 1) { - var _accessMapEntries$i = _slicedToArray(accessMapEntries[i], 2), - hidden = _accessMapEntries$i[0], - visibilityChange = _accessMapEntries$i[1]; - if (typeof documentExt[hidden] !== 'undefined') { - return { - hidden: hidden, - visibilityChange: visibilityChange - }; - } - } - return null; +function _deleteItem(storage /*: IStorage*/, storeName /*: ShortStoreName*/, target /*: StoredRecordId*/) { + return storage.deleteItem(storeName, convertValues(storeName, Direction.left, target)).then(function (target) { + return convertValues(storeName, Direction.right, target); + }); } /** - * Check if connected to internet - * - * @returns {boolean} + * Extends storage's deleteBulk method by encoding target value and then decoding returned records that are deleted */ -function isConnected() /*: boolean*/{ - return _connected; +function _deleteBulk(storage /*: IStorage*/, storeName /*: ShortStoreName*/, value /*: StoredValue*/, condition /*: KeyRangeCondition*/) { + return storage.deleteBulk(storeName, encodeValue(value), condition).then(function (records) { + return records.map(function (record) { + return convertValues(storeName, Direction.right, record); + }); + }); } /** - * Unbind from online and offline events + * Extends storage's trimItems method by passing encoded storage name */ -function listeners_destroy() /*: void*/{ - off(window, 'online', _handleOnline); - off(window, 'offline', _handleOffline); +function _trimItems(storage /*: IStorage*/, storeName /*: ShortStoreName*/, length /*: number*/) { + return storage.trimItems(storeName, length); } -;// CONCATENATED MODULE: ./src/sdk/url-strategy.ts - -var _endpointMap; - - +/** + * Extends storage's count method by passing encoded storage name + */ +function _count(storage /*: IStorage*/, storeName /*: ShortStoreName*/) { + return storage.count(storeName); +} -var UrlStrategy; -(function (UrlStrategy) { - UrlStrategy["Default"] = "default"; - UrlStrategy["India"] = "india"; - UrlStrategy["China"] = "china"; -})(UrlStrategy || (UrlStrategy = {})); -var DataResidency; -(function (DataResidency) { - DataResidency["EU"] = "EU"; - DataResidency["TR"] = "TR"; - DataResidency["US"] = "US"; -})(DataResidency || (DataResidency = {})); -function incorrectOptionIgnoredMessage(higherPriority /*: string*/, lowerPriority /*: string*/) { - logger.warn("Both ".concat(higherPriority, " and ").concat(lowerPriority, " are set in config, ").concat(lowerPriority, " will be ignored")); +/** + * Extends storage's clear method by passing encoded storage name + */ +function _clear(storage /*: IStorage*/, storeName /*: ShortStoreName*/) { + return storage.clear(storeName); } /** - * Returns a map of base URLs or a list of endpoint names depending on SDK configuration + * Calls storage's destroy method */ -function getEndpointPreference() /*: BaseUrlsMap | EndpointName[]*/{ - var _Config$getCustomConf = config.getCustomConfig(), - customUrl = _Config$getCustomConf.customUrl, - urlStrategy = _Config$getCustomConf.urlStrategy, - dataResidency = _Config$getCustomConf.dataResidency; - if (customUrl) { - // If custom URL is set then send all requests there - if (dataResidency || urlStrategy) { - incorrectOptionIgnoredMessage('customUrl', dataResidency ? 'dataResidency' : 'urlStrategy'); - } - return { - app: customUrl, - gdpr: customUrl - }; - } - if (dataResidency && urlStrategy) { - incorrectOptionIgnoredMessage('dataResidency', 'urlStrategy'); - } - if (dataResidency) { - return [dataResidency]; - } - if (urlStrategy === UrlStrategy.India) { - return [UrlStrategy.India, UrlStrategy.Default]; - } - if (urlStrategy === UrlStrategy.China) { - return [UrlStrategy.China, UrlStrategy.Default]; - } - return [UrlStrategy.Default, UrlStrategy.India, UrlStrategy.China]; +function _destroy(storage /*: IStorage*/) { + return storage.destroy(); } -var endpointMap /*: Record*/ = (_endpointMap = {}, _defineProperty(_endpointMap, UrlStrategy.Default, ENDPOINTS["default"]), _defineProperty(_endpointMap, UrlStrategy.India, ENDPOINTS.india), _defineProperty(_endpointMap, UrlStrategy.China, ENDPOINTS.china), _defineProperty(_endpointMap, DataResidency.EU, ENDPOINTS.EU), _defineProperty(_endpointMap, DataResidency.TR, ENDPOINTS.TR), _defineProperty(_endpointMap, DataResidency.US, ENDPOINTS.US), _endpointMap); -function getPreferredUrls(endpoints /*: Partial>*/) /*: BaseUrlsMap[]*/{ - var preference = getEndpointPreference(); - if (!Array.isArray(preference)) { - return [preference]; - } else { - var res = preference.map(function (strategy) { - return endpoints[strategy] || null; - }).filter(function (i) { - return (/*: i is BaseUrlsMap*/!!i - ); - }); - return res; - } + +/** + * Calls storage's deleteDatabase method + */ +function _deleteDatabase(storage /*: IndexedDB | LocalStorage*/) { + return storage.deleteDatabase(); } -function getBaseUrlsIterator() /*: BaseUrlsIterator*/{ - var endpoints /*: Partial>*/ = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : endpointMap; - var _urls = getPreferredUrls(endpoints); - var _counter = 0; - return { - next: function next() { - if (_counter < _urls.length) { - return { - value: _urls[_counter++], - done: false - }; - } else { - return { - value: undefined, - done: true - }; + +/** + * Augment whitelisted methods with encoding/decoding functionality + */ +function _augment() /*: StorageMethods*/{ + var methods /*: Array<[MethodName, StorageMethod]>*/ = entries(_methods).map(function (_ref /*:: */) { + var _ref2 = _slicedToArray(_ref /*:: */, 2), + methodName = _ref2[0], + method = _ref2[1]; + var augmentedMethod /*: StorageMethod*/ = function augmentedMethod /*: StorageMethod*/(storeName /*: StoreName*/) { + for (var _len = arguments.length, args = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) { + args[_key - 1] = arguments[_key]; } - }, - reset: function reset() { - _counter = 0; - } - }; + return storage_init().then(function (_ref3) { + var storage = _ref3.storage; + if (storage) { + return method.call.apply(method, [null, storage, convertStoreName(storeName, Direction.left)].concat(args)); + } + }); + }; + return [methodName, augmentedMethod]; + }); + return methods.reduce(reducer, {}); } -;// CONCATENATED MODULE: ./src/sdk/request.js - - -var request_Promise = typeof Promise === 'undefined' ? (__webpack_require__(702).Promise) : Promise; -/*:: // -import { type HttpSuccessResponseT, type HttpErrorResponseT, type HttpContinueCbT, type BackOffStrategyT, type WaitT, type UrlT, type MethodT, type RequestParamsT, type HttpRequestParamsT } from './types';*/ - - - - - - - - -/*:: type RequestConfigT = {| - url?: UrlT, - method?: MethodT, - params?: RequestParamsT, - continueCb?: HttpContinueCbT, - strategy?: BackOffStrategyT, - wait?: ?WaitT -|}*/ -/*:: type DefaultConfigT = {| - url?: UrlT, - method: MethodT, - params?: RequestParamsT, - continueCb?: HttpContinueCbT -|}*/ -/*:: type AttemptsT = number*/ -/*:: type StartAtT = number*/ -var DEFAULT_ATTEMPTS /*: AttemptsT*/ = 0; -var DEFAULT_WAIT /*: WaitT*/ = 150; -var MAX_WAIT /*: WaitT*/ = 0x7FFFFFFF; // 2^31 - 1 -var NO_CONNECTION_WAIT = 60 * SECOND; -var Request = function Request() { - var _ref = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}, - url = _ref.url, - _ref$method = _ref.method, - method = _ref$method === void 0 ? 'GET' : _ref$method, - _ref$params = _ref.params, - params = _ref$params === void 0 ? {} : _ref$params, - continueCb = _ref.continueCb, - strategy = _ref.strategy, - wait = _ref.wait; - /** - * Global param values set on request instantiation and later used for restore - * - * @type {{url: string, method: string, params: Object, continueCb: Function}} - * @private - */ - var _default /*: DefaultConfigT*/ = { - url: url, - method: method, - params: params, - continueCb: continueCb - }; +/** + * Type of available storage + */ +var type /*: StorageType*/; - /** - * Url param per instance or per request - * - * @type {string} - * @private - */ - var _url /*: ?UrlT*/ = url; +/** + * Returns type of used storage which is one of possible values INDEXED_DB, LOCAL_STORAGE or NO_STORAGE if there is no + * storage available + */ +function getType() /*: StorageType*/{ + return type; +} - /** - * Method param per instance or per request, defaults to `GET` - * - * @type {string} - * @private - */ - var _method /*: MethodT*/ = method; +/** + * Cached promise of Storage initialization + */ +var _initializationPromise /*: Nullable>*/ = null; - /** - * Request params per instance or per request - * - * @type {Object} - * @private - */ - var _params /*: RequestParamsT*/ = _objectSpread2({}, params); +/** + * Check which storage is available and pick it up + * Prefer indexedDB over localStorage + */ +function storage_init(dbName /*: string*/) /*: Promise*/{ + var storage /*: Nullable*/ = null; + if (_initializationPromise !== null) { + return _initializationPromise; + } else { + _initializationPromise = Promise.all([IndexedDBWrapper.isSupported(), LocalStorageWrapper.isSupported()]).then(function (_ref4) { + var _ref5 = _slicedToArray(_ref4, 2), + idbSupported = _ref5[0], + lsSupported = _ref5[1]; + quick_storage.setCustomName(dbName); + if (idbSupported) { + type = StorageType.indexedDB; + var idb = new IndexedDBWrapper(); + return idb.setCustomName(dbName).then(function () { + return storage = idb; + }); + } else if (lsSupported) { + type = StorageType.localStorage; + storage = new LocalStorageWrapper(); + return Promise.resolve(storage); + } else { + logger.error('There is no storage available, app will run with minimum set of features'); + type = StorageType.noStorage; + storage = null; + return Promise.resolve(storage); + } + }).then(function () { + return { + type: type, + storage: storage + }; + }); + } + return _initializationPromise; +} +/* harmony default export */ const storage = (_objectSpread2({ + init: storage_init, + getType: getType +}, _augment())); +;// CONCATENATED MODULE: ./src/sdk/default-params.js - /** - * Optional continue callback per instance or per request - * - * @type {Function} - * @private - */ - var _continueCb /*: ?HttpContinueCbT*/ = continueCb; - /** - * Back-off strategy - * - * @type {string|null} - * @private - */ - var _strategy /*: ?BackOffStrategyT*/ = strategy; +/*:: // +import { type NavigatorT, type CreatedAtT, type SentAtT, type WebUuidT, type TrackEnabledT, type PlatformT, type LanguageT, type MachineTypeT, type QueueSizeT, type DefaultParamsT } from './types';*/ - /** - * Url Startegy iterator to go through endpoints to retry to send request - */ - var _baseUrlsIterator /*: BaseUrlsIterator*/; - /** - * Current base urls map to send request - */ - var _baseUrlsIteratorCurrent /*: { value: BaseUrlsMap, done: boolean }*/; - /** - * Reset iterator state and get the first endpoint to use it in the next try - */ - var _resetBaseUrlsIterator = function _resetBaseUrlsIterator() { - _baseUrlsIterator.reset(); - _baseUrlsIteratorCurrent = _baseUrlsIterator.next(); + +/** + * Get created at timestamp + * + * @returns {{createdAt: string}} + * @private + */ +function _getCreatedAt() /*: CreatedAtT*/{ + return { + createdAt: getTimestamp() }; +} - /** - * Returns base url depending on request path - */ - var _getBaseUrl = function _getBaseUrl(urlsMap /*: BaseUrlsMap*/, url /*: UrlT*/) /*: string*/{ - var base = url === '/gdpr_forget_device' ? 'gdpr' : 'app'; - return urlsMap[base]; +/** + * Get sent at timestamp + * + * @returns {{sentAt: string}} + * @private + */ +function _getSentAt() /*: SentAtT*/{ + return { + sentAt: getTimestamp() }; +} - /** - * Timeout id to be used for clearing - * - * @type {number|null} - * @private - */ - var _timeoutId /*: ?TimeoutID*/ = null; +/** + * Read uuid from the activity state + * + * @returns {{webUuid: string}} + * @private + */ +function _getWebUuid() /*: WebUuidT*/{ + return { + webUuid: activity_state.current.uuid + }; +} - /** - * Number of request and connection attempts - * - * @type {{request: number, connection: number}} - * @private - */ - var _attempts - /*: { - request: AttemptsT, - connection: AttemptsT - }*/ - = { - request: DEFAULT_ATTEMPTS, - connection: DEFAULT_ATTEMPTS +/** + * Get track enabled parameter by reading doNotTrack + * + * @returns {{trackingEnabled: boolean}|null} + * @private + */ +function _getTrackEnabled() /*: ?TrackEnabledT*/{ + var navigatorExt = (navigator /*: NavigatorT*/); + var isNavigatorDNT = typeof navigatorExt.doNotTrack !== 'undefined'; + var isWindowDNT = typeof window.doNotTrack !== 'undefined'; + var isMsDNT = typeof navigatorExt.msDoNotTrack !== 'undefined'; + var dnt = isNavigatorDNT ? navigatorExt.doNotTrack : isWindowDNT ? window.doNotTrack : isMsDNT ? navigatorExt.msDoNotTrack : null; + if (parseInt(dnt, 10) === 0 || dnt === 'no') { + return { + trackingEnabled: true + }; + } + if (parseInt(dnt, 10) === 1 || dnt === 'yes') { + return { + trackingEnabled: false + }; + } + return null; +} + +/** + * Get platform parameter => hardcoded to `web` + * + * @returns {{platform: string}} + * @private + */ +function _getPlatform() /*: PlatformT*/{ + return { + platform: 'web' }; +} - /** - * Waiting time for the request to be sent - * - * @type {number} - * @private - */ - var _wait /*: WaitT*/ = _prepareWait(wait); +/** + * Get language preferences + * + * @returns {{language: string, country: string|undefined}} + * @private + */ +function _getLanguage() /*: LanguageT*/{ + var navigatorExt = (navigator /*: NavigatorT*/); + var _split = (navigatorExt.language || navigatorExt.userLanguage || 'en').split('-'), + _split2 = _slicedToArray(_split, 2), + language = _split2[0], + country = _split2[1]; + return { + language: language, + country: country ? '' + country.toLowerCase() : undefined + }; +} - /** - * Timestamp when the request has been scheduled - * - * @type {Date|null} - * @private - */ - var _startAt /*: ?StartAtT*/ = null; +/** + * Get machine type from navigator.platform property + * + * @returns {{machineType: (string|undefined)}} + */ +function _getMachineType() /*: MachineTypeT*/{ + var ua = navigator.userAgent || navigator.vendor; + var overrideWin32 = navigator.platform === 'Win32' && (ua.indexOf('WOW64') !== -1 || ua.indexOf('Win64') !== -1); + return { + machineType: overrideWin32 ? 'Win64' : navigator.platform + }; +} - /** - * Ensure that wait is not more than maximum 32int so it does not cause overflow in setTimeout - * - * @param {number} wait - * @returns {number} - * @private - */ - function _prepareWait(wait /*: ?WaitT*/) /*: WaitT*/{ - wait = wait || DEFAULT_WAIT; - return wait > MAX_WAIT ? MAX_WAIT : wait; - } +/** + * Get the current queue size + * + * @returns {Promise} + * @private + */ +function _getQueueSize() /*: Promise*/{ + return storage.getAll('queue').then(function (records) { + return { + queueSize: records.length + }; + }); +} +function defaultParams() /*: Promise*/{ + return _getQueueSize().then(function (queueSize) { + return _objectSpread2(_objectSpread2(_objectSpread2(_objectSpread2(_objectSpread2(_objectSpread2(_objectSpread2(_objectSpread2({}, _getCreatedAt()), _getSentAt()), _getWebUuid()), _getTrackEnabled()), _getPlatform()), _getLanguage()), _getMachineType()), queueSize); + }); +} +;// CONCATENATED MODULE: ./src/sdk/http.js - /** - * Override current parameters if available - * - * @param {string=} url - * @param {string=} method - * @param {Object=} params - * @param {Function=} continueCb - * @private - */ - function _prepareParams(_ref2 /*:: */) /*: void*/{ - var url = _ref2 /*:: */.url, - method = _ref2 /*:: */.method, - params = _ref2 /*:: */.params, - continueCb = _ref2 /*:: */.continueCb; - if (url) { - _url = url; - } - if (method) { - _method = method; - } - if (!isEmpty(params)) { - _params = _objectSpread2({}, params); - } - _params = _objectSpread2({ - createdAt: getTimestamp() - }, _params); - if (typeof continueCb === 'function') { - _continueCb = continueCb; - } - } - /** - * Clear previous attempt if new one is about to happen faster - * - * @param {number} wait - * @returns {boolean} - * @private - */ - function _skip(wait /*: ?WaitT*/) /*: boolean*/{ - if (!_startAt) { - return false; - } - if (_timeoutId) { - var remainingTime = _wait - (Date.now() - _startAt); - if (wait && remainingTime < wait) { - return true; - } - clear(); - } - return false; +/*:: // +import { type UrlT, type DefaultParamsT, type HttpSuccessResponseT, type HttpErrorResponseT, type HttpRequestParamsT, type ErrorCodeT } from './types';*/ + + + + + + + +/*:: type ParamsWithAttemptsT = $PropertyType*/ +/** + * Get filtered response from successful request + * + * @param {Object} xhr + * @param {String} url + * @returns {Object} + * @private + */ +function _getSuccessResponse(xhr /*: XMLHttpRequest*/, url /*: UrlT*/) /*: HttpSuccessResponseT*/{ + var result = JSON.parse(xhr.responseText); + var response = { + status: 'success', + adid: result.adid, + timestamp: result.timestamp, + ask_in: result.ask_in, + retry_in: result.retry_in, + continue_in: result.continue_in, + tracking_state: result.tracking_state, + attribution: undefined, + message: undefined + }; + if (isRequest(url, 'attribution')) { + response.attribution = result.attribution; + response.message = result.message; } + return entries(response).filter(function (_ref) { + var _ref2 = _slicedToArray(_ref, 2), + value = _ref2[1]; + return !!value; + }).reduce(reducer, {}); +} - /** - * Prepare request to be sent away - * - * @param {number=} wait - * @param {boolean=false} retrying - * @returns {Promise} - * @private - */ - function _prepareRequest(_ref3 /*:: */) /*: Promise*/{ - var wait = _ref3 /*:: */.wait, - retrying = _ref3 /*:: */.retrying; - if (!_baseUrlsIterator) { - _baseUrlsIterator = getBaseUrlsIterator(); - _baseUrlsIteratorCurrent = _baseUrlsIterator.next(); - } - _wait = wait ? _prepareWait(wait) : _wait; - if (_skip(wait)) { - return request_Promise.resolve({ - status: 'error', - action: 'CONTINUE', - response: '', - message: HTTP_ERRORS.SKIP, - code: 'SKIP' - }); - } - if (!_url) { - logger.error('You must define url for the request to be sent'); - return request_Promise.reject({ - status: 'error', - action: 'CONTINUE', - response: '', - message: HTTP_ERRORS.MISSING_URL, - code: 'MISSING_URL' - }); - } - logger.log("".concat(retrying ? 'Re-trying' : 'Trying', " request ").concat(_url, " in ").concat(_wait, "ms")); - _startAt = Date.now(); - return _preRequest({ - endpoint: _getBaseUrl(_baseUrlsIteratorCurrent.value, _url), - url: _url, - method: _method, - params: _objectSpread2({ - attempts: 1 - }, _params) - }); - } +/** + * Get an error object which is about to be passed to resolve or reject method + * + * @param {Object} xhr + * @param {string} code + * @param {boolean=} proceed + * @returns {Object} + * @private + */ +function _getErrorResponse(xhr /*: XMLHttpRequest*/, code /*: ErrorCodeT*/) /*: HttpErrorResponseT*/{ + var proceed /*: boolean*/ = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false; + return { + status: 'error', + action: proceed ? 'CONTINUE' : 'RETRY', + response: isValidJson(xhr.responseText) ? JSON.parse(xhr.responseText) : xhr.responseText, + message: HTTP_ERRORS[code], + code: code + }; +} - /** - * Check if there is internet connect and if not then setup the timeout - * - * @param {Object} options - * @returns {Promise} - * @private - */ - function _preRequest(options /*: HttpRequestParamsT*/) /*: Promise*/{ - _clearTimeout(); - if (isConnected()) { - return _request(options); - } - _attempts.connection += 1; - logger.log("No internet connectivity, trying request ".concat(options.url, " in ").concat(NO_CONNECTION_WAIT, "ms")); - return new request_Promise(function (resolve) { - _timeoutId = setTimeout(function () { - resolve(_preRequest(options)); - }, NO_CONNECTION_WAIT); - }); +/** + * Encode parameter depending on the type + * + * @param {string} key + * @param {*} value + * @returns {string} + * @private + */ +function _encodeParam(_ref3 /*:: */) /*: string*/{ + var _ref4 = _slicedToArray(_ref3 /*:: */, 2), + key = _ref4[0], + value = _ref4[1]; + var encodedKey = encodeURIComponent(key); + var encodedValue = value; + if (typeof value === 'string') { + encodedValue = encodeURIComponent(value); } - - /** - * Do the timed-out request with retry mechanism - * - * @param {Object} options - * @returns {Promise} - * @private - */ - function _request(options /*: HttpRequestParamsT*/) /*: Promise*/{ - return new request_Promise(function (resolve, reject) { - _timeoutId = setTimeout(function () { - _startAt = null; - var filteredParams = entries(options.params).filter(function (_ref4) { - var _ref5 = _slicedToArray(_ref4, 2), - value = _ref5[1]; - return isEmptyEntry(value); - }).reduce(reducer, {}); - return http({ - endpoint: options.endpoint, - url: options.url, - method: options.method, - params: _objectSpread2(_objectSpread2({}, filteredParams), {}, { - attempts: (_attempts.request ? _attempts.request + 1 : 1) + _attempts.connection - }) - }).then(function (result) { - return _continue(result, resolve); - }).catch(function (result) { - return _error(result, resolve, reject); - }); - }, _wait); - }); + if (isObject(value)) { + encodedValue = encodeURIComponent(JSON.stringify(value) || ''); } - - /** - * Restore to global parameters - * - * @private - */ - function _restore() /*: void*/{ - _url = _default.url; - _method = _default.method; - _params = _objectSpread2({}, _default.params); - _continueCb = _default.continueCb; + if (key === 'granular_third_party_sharing_options' || key === 'partner_sharing_settings') { + return [encodedKey, encodedValue].join(encodeURIComponent('=')); } + return [encodedKey, encodedValue].join('='); +} - /** - * Finish the request by restoring and clearing - * - * @param {boolean=false} failed - * @private - */ - function _finish(failed /*: boolean*/) /*: void*/{ - logger.log("Request ".concat(_url || 'unknown', " ").concat(failed ? 'failed' : 'has been finished')); - _attempts.request = DEFAULT_ATTEMPTS; - _attempts.connection = DEFAULT_ATTEMPTS; - _wait = DEFAULT_WAIT; - _restore(); - clear(); - } +/** + * Creates the log key with some spaces appended to it + * + * @param {string} header + * @param {string} str + * @returns {string} + * @private + */ +function _logKey(header /*: string*/, str /*: string*/) /*: string*/{ + var spaces = header.slice(0, header.length - str.length - 1).split('').reduce(function (acc) { + return acc.concat(' '); + }, ''); + return "".concat(str).concat(spaces, ":"); +} - /** - * Retry request with optional new waiting period - * - * @param {number=} wait - * @returns {Promise} - * @private - */ - function _retry(wait /*: WaitT*/) /*: Promise*/{ - _attempts.request += 1; - clear(); - return _prepareRequest({ - wait: wait || backOff(_attempts.request, _strategy), - retrying: true +/** + * Encode key-value pairs to be used in url + * + * @param {Object} params + * @param {Object} defaultParams + * @returns {string} + * @private + */ +function _encodeParams(params /*: ParamsWithAttemptsT*/, defaultParams /*: DefaultParamsT*/) /*: string*/{ + var logParamsHeader = 'REQUEST PARAMETERS:'; + var toSnakeCase = function toSnakeCase(key) { + return key.replace(/([A-Z])/g, function ($1) { + return "_".concat($1.toLowerCase()); }); - } - - /** - * Decide how to continue, either: - * - retry if requested - * - call custom success callback - * - or finish the request by default - * - * @param {Object} result - * @param {number} result.retry_in - * @param {Function} resolve - * @private - */ - function _continue(result /*: HttpSuccessResponseT | HttpErrorResponseT*/, resolve) /*: void*/{ - if (result && result.retry_in) { - resolve(_retry(result.retry_in)); - return; - } - _resetBaseUrlsIterator(); - if (typeof _continueCb === 'function') { - _continueCb(result, _finish, _retry); + }; + var allParams = entries(_objectSpread2(_objectSpread2(_objectSpread2({}, sdk_config.getBaseParams()), defaultParams), params)).map(function (_ref5 /*:: */) { + var _ref6 = _slicedToArray(_ref5 /*:: */, 2), + key = _ref6[0], + value = _ref6[1]; + return [toSnakeCase(key), value]; + }); + logger.log(logParamsHeader); + return allParams.filter(function (_ref7) { + var _ref8 = _slicedToArray(_ref7, 2), + value = _ref8[1]; + return isEmptyEntry(value); + }).map(function (_ref9) { + var _ref10 = _slicedToArray(_ref9, 2), + key = _ref10[0], + value = _ref10[1]; + logger.log(_logKey(logParamsHeader, key), value); + return _encodeParam([key, value]); + }).join('&'); +} + +/** + * Handle xhr response from server + * + * @param {Function} reject + * @param {Function} resolve + * @param {Object} xhr + * @param {string} url + * @private + */ +function _handleReadyStateChange(reject, resolve, _ref11 /*:: */) { + var xhr = _ref11 /*:: */.xhr, + url = _ref11 /*:: */.url; + if (xhr.readyState !== 4) { + return; + } + var okStatus = xhr.status >= 200 && xhr.status < 300; + var validJson = isValidJson(xhr.responseText); + if (xhr.status === 0) { + reject(_getErrorResponse(xhr, 'NO_CONNECTION')); + } else { + if (validJson) { + return okStatus ? resolve(_getSuccessResponse(xhr, url)) : resolve(_getErrorResponse(xhr, 'SERVER_CANNOT_PROCESS', true)); } else { - _finish(); + return okStatus ? reject(_getErrorResponse(xhr, 'SERVER_MALFORMED_RESPONSE')) : reject(_getErrorResponse(xhr, 'SERVER_INTERNAL_ERROR')); } - resolve(result); } +} - /** - * Ensure to resolve on retry and finish request when unknown error - * - * @param {Object} result - * @param {Function} resolve - * @param {Function} reject - * @private - */ - function _error(result /*: HttpErrorResponseT*/, resolve, reject) /*: void*/{ - if (result && result.action === 'RETRY') { - if (result.code === 'NO_CONNECTION') { - var nextEndpoint = _baseUrlsIterator.next(); // get next endpoint +/** + * Prepare url and params depending on the resource type + * + * @param {string} url + * @param {string} method + * @param {Object} params + * @param {Object} defaultParams + * @returns {{encodedParams: string, fullUrl: string}} + * @private + */ +function _prepareUrlAndParams(_ref12 /*:: */, defaultParams /*: DefaultParamsT*/) /*: {fullUrl: string, encodedParams: string}*/{ + var endpoint = _ref12 /*:: */.endpoint, + url = _ref12 /*:: */.url, + method = _ref12 /*:: */.method, + params = _ref12 /*:: */.params; + var encodedParams = _encodeParams(params, defaultParams); + return { + fullUrl: endpoint + url + (method === 'GET' ? "?".concat(encodedParams) : ''), + encodedParams: encodedParams + }; +} - if (!nextEndpoint.done) { - // next endpoint exists - _baseUrlsIteratorCurrent = nextEndpoint; // use the endpoint in the next try - resolve(_retry(DEFAULT_WAIT)); - } else { - // no more endpoints, seems there is no connection at all - _resetBaseUrlsIterator(); - resolve(_retry(NO_CONNECTION_WAIT)); - } - } else { - resolve(_retry()); - } - return; - } - _finish(true); - reject(result || {}); - } +/** + * Set headers for the xhr object + * + * @param {XMLHttpRequest} xhr + * @param {string} method + * @private + */ +function _prepareHeaders(xhr /*: XMLHttpRequest*/, method /*: $PropertyType*/) /*: void*/{ + var logHeader = 'REQUEST HEADERS:'; + var headers = [['Client-SDK', "js".concat(globals.version)], ['Content-Type', method === 'POST' ? 'application/x-www-form-urlencoded' : 'application/json']]; + logger.log(logHeader); + headers.forEach(function (_ref13) { + var _ref14 = _slicedToArray(_ref13, 2), + key = _ref14[0], + value = _ref14[1]; + xhr.setRequestHeader(key, value); + logger.log(_logKey(logHeader, key), value); + }); +} - /** - * Send the request after specified or default waiting period - * - * @param {string=} url - * @param {string=} method - * @param {Object=} params - * @param {Function=} continueCb - * @param {number=} wait - * @returns {Promise} - */ - function send() /*: Promise*/{ - var _ref6 = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}, - url = _ref6.url, - method = _ref6.method, - _ref6$params = _ref6.params, - params = _ref6$params === void 0 ? {} : _ref6$params, - continueCb = _ref6.continueCb, - wait = _ref6.wait; - _prepareParams({ +/** + * Build xhr to perform all kind of api requests + * + * @param {string} url + * @param {string} [method='GET'] + * @param {Object} [params={}] + * @param {Object} defaultParams + * @returns {Promise} + */ +function _buildXhr(_ref15 /*:: */, defaultParams /*: DefaultParamsT*/) /*: Promise*/{ + var endpoint = _ref15 /*:: */.endpoint, + url = _ref15 /*:: */.url, + _ref15$method = _ref15 /*:: */.method, + method = _ref15$method === void 0 ? 'GET' : _ref15$method, + _ref15$params = _ref15 /*:: */.params, + params = _ref15$params === void 0 ? {} : _ref15$params; + var _prepareUrlAndParams2 = _prepareUrlAndParams({ + endpoint: endpoint, url: url, method: method, - params: params, - continueCb: continueCb - }); - return _prepareRequest({ - wait: wait - }); - } + params: params + }, defaultParams), + fullUrl = _prepareUrlAndParams2.fullUrl, + encodedParams = _prepareUrlAndParams2.encodedParams; + return new Promise(function (resolve, reject) { + var xhr = new XMLHttpRequest(); + xhr.open(method, fullUrl, true); + _prepareHeaders(xhr, method); + xhr.onreadystatechange = function () { + return _handleReadyStateChange(reject, resolve, { + xhr: xhr, + url: url + }); + }; + xhr.onerror = function () { + return reject(_getErrorResponse(xhr, 'TRANSACTION_ERROR')); + }; + xhr.send(method === 'GET' ? undefined : encodedParams); + }); +} - /** - * Check if request is running - * - * @returns {boolean} - */ - function isRunning() /*: boolean*/{ - return !!_timeoutId; +/** + * Intercept response from backend + * + * @param {Object} result + * @param {string} result.status + * @param {string} url + * @returns {Object} + * @private + */ +function _interceptResponse(result /*: HttpSuccessResponseT | HttpErrorResponseT*/, url /*: UrlT*/) /*: HttpSuccessResponseT | HttpErrorResponseT*/{ + if (result.status === 'success') { + return _interceptSuccess(result, url); } + return result; +} - /** - * Clear request/connection timeout - * - * @private - */ - function _clearTimeout() /*: void*/{ - if (_timeoutId) { - clearTimeout(_timeoutId); - } - _timeoutId = null; +/** + * Intercept successful response from backend and: + * - always check if tracking_state is set to `opted_out` and if yes disable sdk + * - check if ask_in parameter is present in order to check if attribution have been changed + * - emit session finish event if session request + * + * @param {Object} result + * @param {string} result.tracking_state + * @param {number} result.ask_in + * @param {string} url + * @returns {Object} + * @private + */ +function _interceptSuccess(result /*: HttpSuccessResponseT*/, url) /*: HttpSuccessResponseT*/{ + var isGdprRequest = isRequest(url, 'gdpr_forget_device'); + var isAttributionRequest = isRequest(url, 'attribution'); + var isSessionRequest = isRequest(url, 'session'); + var optedOut = result.tracking_state === 'opted_out'; + if (!isGdprRequest && optedOut) { + publish('sdk:gdpr-forget-me'); + return result; } - - /** - * Clear the current request - */ - function clear() /*: void*/{ - var stillRunning = !!_startAt; - _clearTimeout(); - _startAt = null; - if (stillRunning) { - _wait = DEFAULT_WAIT; - _attempts.request = DEFAULT_ATTEMPTS; - _attempts.connection = DEFAULT_ATTEMPTS; - logger.log("Previous ".concat(_url || 'unknown', " request attempt canceled")); - _restore(); - } + if (!isAttributionRequest && !isGdprRequest && !optedOut && result.ask_in) { + publish('attribution:check', result); } - return { - send: send, - isRunning: isRunning, - clear: clear - }; -}; -/* harmony default export */ const request = (Request); -;// CONCATENATED MODULE: ./src/sdk/disable.js - + if (isSessionRequest) { + publish('session:finished', result); + } + return result; +} +/** + * Http request factory to perform all kind of api requests + * + * @param {Object} options + * @returns {Promise} + */ +function http(options /*: HttpRequestParamsT*/) /*: Promise*/{ + return defaultParams().then(function (defaultParams) { + return _buildXhr(options, defaultParams); + }).then(function (result) { + return _interceptResponse(result, options.url); + }); +} +;// CONCATENATED MODULE: ./src/sdk/backoff.js +/*:: // +import { type BackOffStrategyT } from './types';*/ -/*:: type StatusT = 'on' | 'off' | 'paused'*/ -/*:: type ReasonT = REASON_GDPR | REASON_GENERAL*/ -/*:: type PendingT = boolean*/ -/*:: type ReasonMapT = {| - reason: ReasonT, - pending: PendingT -|}*/ /** - * Get the disable action name depending on the reason + * Options for the back-off strategy for different environments * - * @param {string} reason - * @returns {string} - * @private + * @type {Object} */ -var _disableReason = function _disableReason(reason /*: ReasonT*/) { - return reason === REASON_GDPR ? 'GDPR disable' : 'disable'; +var _options = { + long: { + delay: 2 * MINUTE, + maxDelay: DAY, + minRange: 0.5, + maxRange: 1.0 + }, + short: { + delay: 200, + maxDelay: HOUR, + minRange: 0.5, + maxRange: 1.0 + }, + test: { + delay: 100, + maxDelay: 300 + } }; /** - * Get log messages depending on the disable reason + * Get random number in provided range * - * @param {string} reason - * @returns {Object} + * @param {number} min + * @param {number} max + * @returns {number} * @private */ -var _logMessages = function _logMessages(reason /*: ReasonT*/) { - return { - start: { - inProgress: "Adjust SDK ".concat(_disableReason(reason), " process has already started"), - done: "Adjust SDK ".concat(_disableReason(reason), " process is now started") - }, - finish: { - inProgress: "Adjust SDK ".concat(_disableReason(reason), " process has already finished"), - done: "Adjust SDK ".concat(_disableReason(reason), " process is now finished") - } - }; -}; +function _randomInRange(min, max) { + return Math.random() * (max - min) + min; +} /** - * Start or finish disable process + * Calculate exponential back-off with optional jitter factor applied * - * @param {string} reason - * @param {boolean} pending - * @param {string} expectedAction - * @returns {boolean} - * @private + * @param {number} attempts + * @param {string} strategy + * @returns {number} */ -function _disable(_ref /*:: */, expectedAction /*: 'start' | 'finish'*/) /*: boolean*/{ - var reason = _ref /*:: */.reason, - pending = _ref /*:: */.pending; - var disabled = getDisabled() || {}; - var action = expectedAction === 'start' && disabled.pending ? 'start' : 'finish'; - var shouldNotStart = expectedAction === 'start' && disabled.reason; - var shouldNotFinish = expectedAction === 'finish' && disabled.reason && !disabled.pending; - if (shouldNotStart || shouldNotFinish) { - logger.log(_logMessages(disabled.reason)[action].inProgress); - return false; +function backOff(attempts /*: number*/, strategy /*: ?BackOffStrategyT*/) /*: number*/{ + strategy = strategy || 'long'; + var options = false ? 0 : _options[strategy]; + var delay = options.delay * Math.pow(2, attempts - 1); + delay = Math.min(delay, options.maxDelay); + if (options.minRange && options.maxRange) { + delay = delay * _randomInRange(options.minRange, options.maxRange); } - logger.log(_logMessages(reason)[action].done); - setDisabled({ - reason: reason || REASON_GENERAL, - pending: pending - }); - return true; + return Math.round(delay); } +;// CONCATENATED MODULE: ./src/sdk/listeners.js + +/*:: // +import { type DocumentT } from './types';*/ + +/*:: type EventCbT = (e: Event) => void*/ +/*:: type PageVisibilityHiddenAttr = 'hidden' | 'mozHidden' | 'msHidden' | 'oHidden' | 'webkitHidden'*/ +/*:: type PageVisibilityEventName = 'visibilitychange' | 'mozvisibilitychange' | 'msvisibilitychange' | 'ovisibilitychange' | 'webkitvisibilitychange'*/ +/*:: type PageVisibilityApiMap = {| + hidden: PageVisibilityHiddenAttr, + visibilityChange: PageVisibilityEventName +|}*/ +var _connected /*: boolean*/ = navigator.onLine; /** - * Disable sdk due to a particular reason + * Bind to online and offline events + */ +function register() /*: void*/{ + on(window, 'online', _handleOnline); + on(window, 'offline', _handleOffline); +} + +/** + * Handle online event, set connected flag to true * - * @param {string} reason - * @param {boolean} pending * @private */ -function disable(reason /*: ?ReasonT*/) /*: boolean*/{ - var pending /*: ?PendingT*/ = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false; - return _disable({ - reason: reason, - pending: pending || false - }, 'start'); +function _handleOnline() /*: void*/{ + _connected = true; } /** - * Finish disable process if previously set to pending state - * - * @param {string} reason - * @returns {boolean} + * Handle offline event, set connected flag to false + * @private */ -function finish(reason /*: ReasonT*/) /*: boolean*/{ - return _disable({ - reason: reason, - pending: false - }, 'finish'); +function _handleOffline() /*: void*/{ + _connected = false; } /** - * Enable sdk if not GDPR forgotten + * Bind event to an element + * + * @param {Window|Document} element + * @param {string} eventName + * @param {Function} func */ -function restore() /*: boolean*/{ - var disabled = getDisabled() || {}; - if (disabled.reason === REASON_GDPR) { - logger.log('Adjust SDK is disabled due to GDPR-Forget-Me request and it can not be re-enabled'); - return false; - } - if (!disabled.reason) { - logger.log('Adjust SDK is already enabled'); - return false; +function on(element /*: Document | any*/, eventName /*: string*/, func /*: EventCbT*/) /*: void*/{ + if (element.addEventListener) { + element.addEventListener(eventName, func, false); } - logger.log('Adjust SDK has been enabled'); - setDisabled(null); - return true; } /** - * Get the current status of the sdk - * - on: not disabled - * - paused: partially disabled, waiting for the opt-out confirmation from the backend - * - off: completely disabled + * Unbind event off an element * - * @returns {string} + * @param {Window|Document} element + * @param {string} eventName + * @param {Function} func */ -function disable_status() /*: StatusT*/{ - var disabled = getDisabled() || {}; - if (disabled.reason === REASON_GENERAL || disabled.reason === REASON_GDPR && !disabled.pending) { - return 'off'; - } else if (disabled.reason === REASON_GDPR && disabled.pending) { - return 'paused'; +function off(element /*: Document | any*/, eventName /*: string*/, func /*: EventCbT*/) /*: void*/{ + if (element.removeEventListener) { + element.removeEventListener(eventName, func, false); } - return 'on'; } -;// CONCATENATED MODULE: ./src/sdk/identity.js - -var identity_Promise = typeof Promise === 'undefined' ? (__webpack_require__(702).Promise) : Promise; -/*:: // -import { type ActivityStateMapT } from './types';*/ - - - - - - - -/*:: type InterceptT = {| - exists: boolean, - stored?: ?ActivityStateMapT -|}*/ /** - * Name of the store used by activityState + * Get Page Visibility API attributes that can be accessed depending on the browser implementation * - * @type {string} + * @returns {{hidden: string, visibilityChange: string}|null} * @private */ -var identity_storeName = 'activityState'; +function getVisibilityApiAccess() /*: ?PageVisibilityApiMap*/{ + var documentExt = (document /*: DocumentT*/); + if (typeof documentExt.hidden !== 'undefined') { + return { + hidden: 'hidden', + visibilityChange: 'visibilitychange' + }; + } + var accessMap /*: {[key: PageVisibilityHiddenAttr]: PageVisibilityEventName}*/ = { + mozHidden: 'mozvisibilitychange', + msHidden: 'msvisibilitychange', + oHidden: 'ovisibilitychange', + webkitHidden: 'webkitvisibilitychange' + }; + var accessMapEntries = entries(accessMap); + for (var i = 0; i < accessMapEntries.length; i += 1) { + var _accessMapEntries$i = _slicedToArray(accessMapEntries[i], 2), + hidden = _accessMapEntries$i[0], + visibilityChange = _accessMapEntries$i[1]; + if (typeof documentExt[hidden] !== 'undefined') { + return { + hidden: hidden, + visibilityChange: visibilityChange + }; + } + } + return null; +} /** - * Boolean used in start in order to avoid duplicated activity state + * Check if connected to internet * - * @type {boolean} - * @private + * @returns {boolean} */ -var _starting /*: boolean*/ = false; +function isConnected() /*: boolean*/{ + return _connected; +} /** - * Generate random uuid v4 - * - * @returns {string} - * @private + * Unbind from online and offline events */ -function _generateUuid() /*: string*/{ - var seed = Date.now(); - return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) { - var r = (seed + Math.random() * 16) % 16 | 0; - seed = Math.floor(seed / 16); - return (c === 'x' ? r : r & (0x3 | 0x8)).toString(16); - }); +function listeners_destroy() /*: void*/{ + off(window, 'online', _handleOnline); + off(window, 'offline', _handleOffline); +} + +;// CONCATENATED MODULE: ./node_modules/@babel/runtime/helpers/esm/createForOfIteratorHelper.js + +function _createForOfIteratorHelper(o, allowArrayLike) { + var it = typeof Symbol !== "undefined" && o[Symbol.iterator] || o["@@iterator"]; + if (!it) { + if (Array.isArray(o) || (it = _unsupportedIterableToArray(o)) || allowArrayLike && o && typeof o.length === "number") { + if (it) o = it; + var i = 0; + var F = function F() {}; + return { + s: F, + n: function n() { + if (i >= o.length) return { + done: true + }; + return { + done: false, + value: o[i++] + }; + }, + e: function e(_e) { + throw _e; + }, + f: F + }; + } + throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); + } + var normalCompletion = true, + didErr = false, + err; + return { + s: function s() { + it = it.call(o); + }, + n: function n() { + var step = it.next(); + normalCompletion = step.done; + return step; + }, + e: function e(_e2) { + didErr = true; + err = _e2; + }, + f: function f() { + try { + if (!normalCompletion && it["return"] != null) it["return"](); + } finally { + if (didErr) throw err; + } + } + }; +} +;// CONCATENATED MODULE: ./src/sdk/url-strategy.ts + + + + + +/*:: export interface UrlStrategyConfig { + /** The country or countries of data residence, or the endpoints to which you want to send SDK traffic. *-/ + domains: Array; + + /** Whether the source should prefix a subdomain. *-/ + useSubdomains: boolean; + + /** Whether the domain should be used for data residency. *-/ + isDataResidency?: boolean; +}*/ +function getDefaultUrlStrategyConfig(endpoints /*: Record*/) { + return { + domains: [endpoints.default, endpoints.world], + useSubdomains: true, + isDataResidency: false + }; +} +var UrlStrategy = /*#__PURE__*/function (UrlStrategy) { + UrlStrategy["Default"] = "default"; + UrlStrategy["India"] = "india"; + UrlStrategy["China"] = "china"; + return UrlStrategy; +}(UrlStrategy || {}); +var DataResidency = /*#__PURE__*/function (DataResidency) { + DataResidency["EU"] = "EU"; + DataResidency["TR"] = "TR"; + DataResidency["US"] = "US"; + return DataResidency; +}(DataResidency || {}); +function incorrectOptionIgnoredMessage(higherPriority /*: string*/, lowerPriority /*: string*/) { + logger.warn("Both ".concat(higherPriority, " and ").concat(lowerPriority, " are set in config, ").concat(lowerPriority, " will be ignored")); } /** - * Inspect stored activity state and check if disable needs to be repeated - * - * @param {Object=} stored - * @returns {Object} - * @private + * In case if deprecated parameters or no urlStrategy provided returns the most appropriate UrlStrategyConfig, + * and `null` otherwise */ -function _intercept(stored /*: ActivityStateMapT*/) /*: InterceptT*/{ - if (!stored) { +function transfromDeprecatedParamsToUrlStrategyConfig(endpoints /*: Record*/) /*: UrlStrategyConfig | null*/{ + var _Config$getCustomConf = sdk_config.getCustomConfig(), + customUrl = _Config$getCustomConf.customUrl, + urlStrategy = _Config$getCustomConf.urlStrategy, + dataResidency = _Config$getCustomConf.dataResidency; + if (customUrl) { + // If custom URL is set then send all requests there + logger.warn('customUrl is deprecated, use urlStrategy instead'); + if (dataResidency || urlStrategy) { + incorrectOptionIgnoredMessage('customUrl', dataResidency ? 'dataResidency' : 'urlStrategy'); + } return { - exists: false + domains: [customUrl], + useSubdomains: false, + isDataResidency: false }; } - if (stored.uuid === 'unknown') { - disable({ - reason: REASON_GDPR - }); - activity_state.destroy(); + if (dataResidency && urlStrategy) { + incorrectOptionIgnoredMessage('dataResidency', 'urlStrategy'); + } + if (dataResidency) { + logger.warn('dataResidency is deprecated, use urlStrategy instead'); return { - exists: true, - stored: null + domains: [endpoints[dataResidency]], + useSubdomains: true, + isDataResidency: true }; } - activity_state.init(stored); - return { - exists: true, - stored: stored - }; + if (typeof urlStrategy === 'string') { + logger.warn('urlStrategy string literals (\'china\' and \'india\') are deprected, use UrlStartegyConfig instead'); + if (urlStrategy === UrlStrategy.India) { + return { + domains: [endpoints.india, endpoints.default], + useSubdomains: true, + isDataResidency: false + }; + } + if (urlStrategy === UrlStrategy.China) { + return { + domains: [endpoints.china, endpoints.default], + useSubdomains: true, + isDataResidency: false + }; + } + } + if (!urlStrategy) { + return getDefaultUrlStrategyConfig(endpoints); + } + return null; } /** - * Cache stored activity state into running memory - * - * @returns {Promise} + * Checks if passed UrlStrategyConfig is valid and returns it, returns `DEFAULT_URL_STRATEGY_CONFIG` otherwise */ -function start() /*: Promise*/{ - if (_starting) { - return identity_Promise.reject({ - interrupted: true, - message: 'Adjust SDK start already in progress' - }); - } - _starting = true; - return storage.getFirst(identity_storeName).then(_intercept).then(function (result /*: InterceptT*/) { - if (result.exists) { - _starting = false; - return result.stored; +function validateUrlStrategyConfig(endpoints /*: Record*/) /*: UrlStrategyConfig*/{ + var _Config$getCustomConf2 = sdk_config.getCustomConfig(), + urlStrategy = _Config$getCustomConf2.urlStrategy; + if (urlStrategy && _typeof(urlStrategy) === 'object') { + var config = urlStrategy; + if (!config.domains || !Array.isArray(config.domains) || config.domains.length < 1) { + logger.warn('Invalid urlStartegy: `domains` should be a non-empty array'); + return getDefaultUrlStrategyConfig(endpoints); } - var activityState = isEmpty(activity_state.current) ? { - uuid: _generateUuid() - } : activity_state.current; - return storage.addItem(identity_storeName, activityState).then(function () { - activity_state.init(activityState); - reload(); - _starting = false; - return activityState; - }); - }); + return { + domains: config.domains, + useSubdomains: !!config.useSubdomains, + isDataResidency: !!config.isDataResidency + }; + } + return getDefaultUrlStrategyConfig(endpoints); } - -/** - * Check if sdk is running at all (totally disabled or inactive activity state) - * - * @returns {boolean} - * @private - */ -function _isLive() { - return disable_status() !== 'off' && activity_state.isStarted(); +function getUrlStrategyConfig(endpoints /*: Record*/) /*: UrlStrategyConfig*/{ + return transfromDeprecatedParamsToUrlStrategyConfig(endpoints) || validateUrlStrategyConfig(endpoints); } +function getPreferredUrls(endpoints /*: Record*/) /*: BaseUrlsMap[]*/{ + var urlStrategyConfig /*: UrlStrategyConfig*/ = getUrlStrategyConfig(endpoints); + var urls = []; -/** - * Persist changes made directly in activity state and update lastActive flag - * - * @returns {Promise} - */ -function persist() /*: Promise*/{ - if (!_isLive()) { - return identity_Promise.resolve(null); + //if (urlStrategyConfig.isDataResidency) { } + var _iterator = _createForOfIteratorHelper(urlStrategyConfig.domains), + _step; + try { + for (_iterator.s(); !(_step = _iterator.n()).done;) { + var domain = _step.value; + var map = urlStrategyConfig.useSubdomains ? { + app: "".concat(BASE_URL_PREFIX).concat(domain), + gdpr: "".concat(GDPR_URL_PREFIX).concat(domain) + } : { + app: "".concat(BASE_URL_NO_SUB_DOMAIN_PREFIX).concat(domain), + gdpr: "".concat(BASE_URL_NO_SUB_DOMAIN_PREFIX).concat(domain) + }; + urls.push(map); + } + } catch (err) { + _iterator.e(err); + } finally { + _iterator.f(); } - var activityState = _objectSpread2(_objectSpread2({}, activity_state.current), {}, { - lastActive: Date.now() - }); - return storage.updateItem(identity_storeName, activityState).then(function () { - return activity_state.current = activityState; - }); + return urls; } - -/** - * Sync in-memory activityState with the one from store - * - should be used when change from another tab is possible and critical - * - * @returns {Promise} - */ -function sync() /*: Promise*/{ - return storage.getFirst(identity_storeName).then(function (activityState /*: ActivityStateMapT*/) { - var current = activity_state.current; - var lastActive = current.lastActive || 0; - if (_isLive() && lastActive < activityState.lastActive) { - // Checking if another SDK instance was installed while this one was in backgound - var installedUpdated = !current.installed && activityState.installed; - var sessionCountUpdated = (current.sessionCount || 0) < (activityState.sessionCount || 0); - if (installedUpdated || sessionCountUpdated) { - publish('sdk:installed'); +function getBaseUrlsIterator() /*: BaseUrlsIterator*/{ + var endpoints /*: Record*/ = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : ENDPOINTS; + var _urls = getPreferredUrls(endpoints); + var _counter = 0; + return { + next: function next() { + if (_counter < _urls.length) { + return { + value: _urls[_counter++], + done: false + }; + } else { + return { + value: undefined, + done: true + }; } - activity_state.current = activityState; - reload(); + }, + reset: function reset() { + _counter = 0; } - return activityState; - }); + }; } -/** - * Clear activity state store - set uuid to be unknown - */ -function clear() /*: void*/{ - var newActivityState = { - uuid: 'unknown' +;// CONCATENATED MODULE: ./src/sdk/request.js + + +/*:: // +import { type HttpSuccessResponseT, type HttpErrorResponseT, type HttpContinueCbT, type BackOffStrategyT, type WaitT, type UrlT, type MethodT, type RequestParamsT, type HttpRequestParamsT } from './types';*/ + + + + + + + + +/*:: type RequestConfigT = {| + url?: UrlT, + method?: MethodT, + params?: RequestParamsT, + continueCb?: HttpContinueCbT, + strategy?: BackOffStrategyT, + wait?: ?WaitT +|}*/ +/*:: type DefaultConfigT = {| + url?: UrlT, + method: MethodT, + params?: RequestParamsT, + continueCb?: HttpContinueCbT +|}*/ +/*:: type AttemptsT = number*/ +/*:: type StartAtT = number*/ +var DEFAULT_ATTEMPTS /*: AttemptsT*/ = 0; +var DEFAULT_WAIT /*: WaitT*/ = 150; +var MAX_WAIT /*: WaitT*/ = 0x7FFFFFFF; // 2^31 - 1 +var NO_CONNECTION_WAIT = 60 * SECOND; +var Request = function Request() { + var _ref = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}, + url = _ref.url, + _ref$method = _ref.method, + method = _ref$method === void 0 ? 'GET' : _ref$method, + _ref$params = _ref.params, + params = _ref$params === void 0 ? {} : _ref$params, + continueCb = _ref.continueCb, + strategy = _ref.strategy, + wait = _ref.wait; + /** + * Global param values set on request instantiation and later used for restore + * + * @type {{url: string, method: string, params: Object, continueCb: Function}} + * @private + */ + var _default /*: DefaultConfigT*/ = { + url: url, + method: method, + params: params, + continueCb: continueCb + }; + + /** + * Url param per instance or per request + * + * @type {string} + * @private + */ + var _url /*: ?UrlT*/ = url; + + /** + * Method param per instance or per request, defaults to `GET` + * + * @type {string} + * @private + */ + var _method /*: MethodT*/ = method; + + /** + * Request params per instance or per request + * + * @type {Object} + * @private + */ + var _params /*: RequestParamsT*/ = _objectSpread2({}, params); + + /** + * Optional continue callback per instance or per request + * + * @type {Function} + * @private + */ + var _continueCb /*: ?HttpContinueCbT*/ = continueCb; + + /** + * Back-off strategy + * + * @type {string|null} + * @private + */ + var _strategy /*: ?BackOffStrategyT*/ = strategy; + + /** + * Url Startegy iterator to go through endpoints to retry to send request + */ + var _baseUrlsIterator /*: BaseUrlsIterator*/; + + /** + * Current base urls map to send request + */ + var _baseUrlsIteratorCurrent /*: { value: BaseUrlsMap, done: boolean }*/; + + /** + * Reset iterator state and get the first endpoint to use it in the next try + */ + var _resetBaseUrlsIterator = function _resetBaseUrlsIterator() { + _baseUrlsIterator.reset(); + _baseUrlsIteratorCurrent = _baseUrlsIterator.next(); + }; + + /** + * Returns base url depending on request path + */ + var _getBaseUrl = function _getBaseUrl(urlsMap /*: BaseUrlsMap*/, url /*: UrlT*/) /*: string*/{ + var base = url === '/gdpr_forget_device' ? 'gdpr' : 'app'; + return urlsMap[base]; }; - activity_state.current = newActivityState; - return storage.clear(identity_storeName).then(function () { - return storage.addItem(identity_storeName, newActivityState); - }); -} -/** - * Destroy current activity state - */ -function identity_destroy() /*: void*/{ - activity_state.destroy(); -} + /** + * Timeout id to be used for clearing + * + * @type {number|null} + * @private + */ + var _timeoutId /*: ?TimeoutID*/ = null; -;// CONCATENATED MODULE: ./src/sdk/queue.js + /** + * Number of request and connection attempts + * + * @type {{request: number, connection: number}} + * @private + */ + var _attempts + /*: { + request: AttemptsT, + connection: AttemptsT + }*/ + = { + request: DEFAULT_ATTEMPTS, + connection: DEFAULT_ATTEMPTS + }; + /** + * Waiting time for the request to be sent + * + * @type {number} + * @private + */ + var _wait /*: WaitT*/ = _prepareWait(wait); -var queue_Promise = typeof Promise === 'undefined' ? (__webpack_require__(702).Promise) : Promise; -/*:: // -import { type HttpSuccessResponseT, type HttpErrorResponseT, type HttpFinishCbT, type WaitT, type UrlT, type MethodT, type RequestParamsT, type ActivityStateMapT } from './types';*/ + /** + * Timestamp when the request has been scheduled + * + * @type {Date|null} + * @private + */ + var _startAt /*: ?StartAtT*/ = null; + + /** + * Ensure that wait is not more than maximum 32int so it does not cause overflow in setTimeout + * + * @param {number} wait + * @returns {number} + * @private + */ + function _prepareWait(wait /*: ?WaitT*/) /*: WaitT*/{ + wait = wait || DEFAULT_WAIT; + return wait > MAX_WAIT ? MAX_WAIT : wait; + } + /** + * Override current parameters if available + * + * @param {string=} url + * @param {string=} method + * @param {Object=} params + * @param {Function=} continueCb + * @private + */ + function _prepareParams(_ref2 /*:: */) /*: void*/{ + var url = _ref2 /*:: */.url, + method = _ref2 /*:: */.method, + params = _ref2 /*:: */.params, + continueCb = _ref2 /*:: */.continueCb; + if (url) { + _url = url; + } + if (method) { + _method = method; + } + if (!isEmpty(params)) { + _params = _objectSpread2({}, params); + } + _params = _objectSpread2({ + createdAt: getTimestamp() + }, _params); + if (typeof continueCb === 'function') { + _continueCb = continueCb; + } + } + /** + * Clear previous attempt if new one is about to happen faster + * + * @param {number} wait + * @returns {boolean} + * @private + */ + function _skip(wait /*: ?WaitT*/) /*: boolean*/{ + if (!_startAt) { + return false; + } + if (_timeoutId) { + var remainingTime = _wait - (Date.now() - _startAt); + if (wait && remainingTime < wait) { + return true; + } + clear(); + } + return false; + } + /** + * Prepare request to be sent away + * + * @param {number=} wait + * @param {boolean=false} retrying + * @returns {Promise} + * @private + */ + function _prepareRequest(_ref3 /*:: */) /*: Promise*/{ + var wait = _ref3 /*:: */.wait, + retrying = _ref3 /*:: */.retrying; + if (!_baseUrlsIterator) { + _baseUrlsIterator = getBaseUrlsIterator(); + _baseUrlsIteratorCurrent = _baseUrlsIterator.next(); + } + _wait = wait ? _prepareWait(wait) : _wait; + if (_skip(wait)) { + return Promise.resolve({ + status: 'error', + action: 'CONTINUE', + response: '', + message: HTTP_ERRORS['SKIP'], + code: 'SKIP' + }); + } + if (!_url) { + logger.error('You must define url for the request to be sent'); + return Promise.reject({ + status: 'error', + action: 'CONTINUE', + response: '', + message: HTTP_ERRORS['MISSING_URL'], + code: 'MISSING_URL' + }); + } + logger.log("".concat(retrying ? 'Re-trying' : 'Trying', " request ").concat(_url, " in ").concat(_wait, "ms")); + _startAt = Date.now(); + return _preRequest({ + endpoint: _getBaseUrl(_baseUrlsIteratorCurrent.value, _url), + url: _url, + method: _method, + params: _objectSpread2({ + attempts: 1 + }, _params) + }); + } + /** + * Check if there is internet connect and if not then setup the timeout + * + * @param {Object} options + * @returns {Promise} + * @private + */ + function _preRequest(options /*: HttpRequestParamsT*/) /*: Promise*/{ + _clearTimeout(); + if (isConnected()) { + return _request(options); + } + _attempts.connection += 1; + logger.log("No internet connectivity, trying request ".concat(options.url, " in ").concat(NO_CONNECTION_WAIT, "ms")); + return new Promise(function (resolve) { + _timeoutId = setTimeout(function () { + resolve(_preRequest(options)); + }, NO_CONNECTION_WAIT); + }); + } + /** + * Do the timed-out request with retry mechanism + * + * @param {Object} options + * @returns {Promise} + * @private + */ + function _request(options /*: HttpRequestParamsT*/) /*: Promise*/{ + return new Promise(function (resolve, reject) { + _timeoutId = setTimeout(function () { + _startAt = null; + var filteredParams = entries(options.params).filter(function (_ref4) { + var _ref5 = _slicedToArray(_ref4, 2), + value = _ref5[1]; + return isEmptyEntry(value); + }).reduce(reducer, {}); + return http({ + endpoint: options.endpoint, + url: options.url, + method: options.method, + params: _objectSpread2(_objectSpread2({}, filteredParams), {}, { + attempts: (_attempts.request ? _attempts.request + 1 : 1) + _attempts.connection + }) + }).then(function (result) { + return _continue(result, resolve); + }).catch(function (result) { + return _error(result, resolve, reject); + }); + }, _wait); + }); + } + /** + * Restore to global parameters + * + * @private + */ + function _restore() /*: void*/{ + _url = _default.url; + _method = _default.method; + _params = _objectSpread2({}, _default.params); + _continueCb = _default.continueCb; + } + /** + * Finish the request by restoring and clearing + * + * @param {boolean=false} failed + * @private + */ + function _finish(failed /*: boolean*/) /*: void*/{ + logger.log("Request ".concat(_url || 'unknown', " ").concat(failed ? 'failed' : 'has been finished')); + _attempts.request = DEFAULT_ATTEMPTS; + _attempts.connection = DEFAULT_ATTEMPTS; + _wait = DEFAULT_WAIT; + _restore(); + clear(); + } -/*:: type PendingT = {| - timestamp: number, - url: UrlT, - method?: MethodT, - createdAt?: number, - params: RequestParamsT -|}*/ -/** - * Http request instance - * - * @type {Object} - * @private - */ -var _request = request({ - strategy: 'long', - continueCb: _continue -}); + /** + * Retry request with optional new waiting period + * + * @param {number=} wait + * @returns {Promise} + * @private + */ + function _retry(wait /*: WaitT*/) /*: Promise*/{ + _attempts.request += 1; + clear(); + return _prepareRequest({ + wait: wait || backOff(_attempts.request, _strategy), + retrying: true + }); + } -/** - * Check if in offline mode - * - * @type {boolean} - * @private - */ -var _isOffline = false; + /** + * Decide how to continue, either: + * - retry if requested + * - call custom success callback + * - or finish the request by default + * + * @param {Object} result + * @param {number} result.retry_in + * @param {Function} resolve + * @private + */ + function _continue(result /*: HttpSuccessResponseT | HttpErrorResponseT*/, resolve) /*: void*/{ + if (result && result.retry_in) { + resolve(_retry(result.retry_in)); + return; + } + _resetBaseUrlsIterator(); + if (typeof _continueCb === 'function') { + _continueCb(result, _finish, _retry); + } else { + _finish(); + } + resolve(result); + } -/** - * Name of the store used by queue - * - * @type {string} - * @private - */ -var queue_storeName = 'queue'; + /** + * Ensure to resolve on retry and finish request when unknown error + * + * @param {Object} result + * @param {Function} resolve + * @param {Function} reject + * @private + */ + function _error(result /*: HttpErrorResponseT*/, resolve, reject) /*: void*/{ + if (result && result.action === 'RETRY') { + if (result.code === 'NO_CONNECTION') { + var nextEndpoint = _baseUrlsIterator.next(); // get next endpoint -/** - * Current running state and task timestamp - * - * @type {{running: boolean, timestamp: void|number, pause: void|Object}} - * @private - */ -var _current -/*: {| - running: boolean, - timestamp: ?number, - pause: ?{| - timestamp: number, - wait: WaitT - |} -|}*/ -= { - running: false, - timestamp: null, - pause: null -}; + if (!nextEndpoint.done) { + // next endpoint exists + _baseUrlsIteratorCurrent = nextEndpoint; // use the endpoint in the next try + resolve(_retry(DEFAULT_WAIT)); + } else { + // no more endpoints, seems there is no connection at all + _resetBaseUrlsIterator(); + resolve(_retry(NO_CONNECTION_WAIT)); + } + } else { + resolve(_retry()); + } + return; + } + _finish(true); + reject(result || {}); + } -/** - * Remove from the top and continue running pending requests - * - * @param {Object} result - * @param {Function} finish - * @returns {Promise} - * @private - */ -function _continue(result /*: HttpSuccessResponseT | HttpErrorResponseT*/, finish /*: HttpFinishCbT*/) /*: Promise*/{ - var wait = result && result.continue_in || null; - _current.pause = wait ? { - timestamp: Date.now(), - wait: wait - } : null; - return storage.getFirst(queue_storeName).then(function (pending) { - return pending ? storage.deleteItem(queue_storeName, pending.timestamp) : null; - }).then(function () { - finish(); - _current.running = false; - return run({ + /** + * Send the request after specified or default waiting period + * + * @param {string=} url + * @param {string=} method + * @param {Object=} params + * @param {Function=} continueCb + * @param {number=} wait + * @returns {Promise} + */ + function send() /*: Promise*/{ + var _ref6 = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}, + url = _ref6.url, + method = _ref6.method, + _ref6$params = _ref6.params, + params = _ref6$params === void 0 ? {} : _ref6$params, + continueCb = _ref6.continueCb, + wait = _ref6.wait; + _prepareParams({ + url: url, + method: method, + params: params, + continueCb: continueCb + }); + return _prepareRequest({ wait: wait }); - }); -} - -/** - * Correct timestamp if equal or less then previous one to avoid constraint errors - * Cases when needed: - * - test environment - * - when pushing to queue synchronously, one after an other - * - * @returns {number} - * @private - */ -function _prepareTimestamp() /*: number*/{ - var timestamp = Date.now(); - if (_current.timestamp && timestamp <= _current.timestamp) { - timestamp = _current.timestamp + 1; } - _current.timestamp = timestamp; - return timestamp; -} -/** - * Persist activity state change with session offset reset after session request - * - * @param {string} url - * @returns {Promise} - * @private - */ -function _persist(url) /*: Promise*/{ - if (isRequest(url, 'session')) { - activity_state.resetSessionOffset(); + /** + * Check if request is running + * + * @returns {boolean} + */ + function isRunning() /*: boolean*/{ + return !!_timeoutId; } - activity_state.updateLastActive(); - return persist(); -} -/** - * Push request to the queue - * - * @param {string} url - * @param {string} method - * @param {Object=} params - * @param {boolean=} auto - * @param {number=} timestamp - * @returns {Promise} - */ -function push(_ref /*:: */) { - var url = _ref /*:: */.url, - method = _ref /*:: */.method, - params = _ref /*:: */.params; - var _ref2 = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}, - auto = _ref2.auto, - timestamp = _ref2.timestamp; - activity_state.updateParams(url, auto); - var filteredParams = entries(params || {}).filter(function (_ref3) { - var _ref4 = _slicedToArray(_ref3, 2), - value = _ref4[1]; - return isEmptyEntry(value); - }).reduce(reducer, {}); - var pending /*: PendingT*/ = { - timestamp: _prepareTimestamp(), - url: url, - method: method, - params: _objectSpread2(_objectSpread2({}, activity_state.getParams(url)), filteredParams) - }; - if (timestamp) { - pending.createdAt = timestamp; + /** + * Clear request/connection timeout + * + * @private + */ + function _clearTimeout() /*: void*/{ + if (_timeoutId) { + clearTimeout(_timeoutId); + } + _timeoutId = null; } - return storage.addItem(queue_storeName, pending).then(function () { - return _persist(url); - }).then(function () { - return _current.running ? {} : run(); - }); -} -/** - * Prepare to send pending request if available - * - * @param {number} timestamp - * @param {number=} createdAt - * @param {string=} url - * @param {string=} method - * @param {Object=} params - * @param {number=} wait - * @returns {Promise} - * @private - */ -function _prepareToSend() /*: Promise*/{ - var _ref5 = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}, - timestamp = _ref5.timestamp, - createdAt = _ref5.createdAt, - url = _ref5.url, - method = _ref5.method, - params = _ref5.params; - var wait /*:: ?: ?WaitT*/ = arguments.length > 1 ? arguments[1] : undefined; - var activityState = activity_state.current || {}; - var firstSession = url === '/session' && !activityState.installed; - var noPending = !url && !method && !params; - if (_isOffline && !firstSession || noPending) { - _current.running = false; - return queue_Promise.resolve({}); + /** + * Clear the current request + */ + function clear() /*: void*/{ + var stillRunning = !!_startAt; + _clearTimeout(); + _startAt = null; + if (stillRunning) { + _wait = DEFAULT_WAIT; + _attempts.request = DEFAULT_ATTEMPTS; + _attempts.connection = DEFAULT_ATTEMPTS; + logger.log("Previous ".concat(_url || 'unknown', " request attempt canceled")); + _restore(); + } } - return _request.send({ - url: url, - method: method, - params: _objectSpread2(_objectSpread2({}, params), {}, { - createdAt: getTimestamp(createdAt || timestamp) - }), - wait: wait || _checkWait() - }); -} + return { + send: send, + isRunning: isRunning, + clear: clear + }; +}; +/* harmony default export */ const request = (Request); +;// CONCATENATED MODULE: ./src/sdk/disable.ts + + /** - * Check if there is waiting period required + * Get the disable action name depending on the reason * - * @returns {void|number} + * @param {string} reason + * @returns {string} * @private */ -function _checkWait() /*: ?WaitT*/{ - var _ref6 = _current.pause || {}, - timestamp = _ref6.timestamp, - wait = _ref6.wait; - var rest = Date.now() - (timestamp || 0); - return rest < wait ? wait - rest : null; -} +var _disableReason = function _disableReason(reason /*: ReasonT*/) { + return reason === DISABLE_REASONS.REASON_GDPR ? 'GDPR disable' : 'disable'; +}; /** - * Run all pending requests + * Get log messages depending on the disable reason * - * @param {boolean=false} cleanUp - * @param {number=} wait - * @returns {Promise} + * @param {string} reason + * @returns {Object} + * @private */ -function run() /*: Promise*/{ - var _ref7 = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}, - cleanUp = _ref7.cleanUp, - wait = _ref7.wait; - if (_current.running) { - return queue_Promise.resolve({}); - } - _current.running = true; - var chain = queue_Promise.resolve({}); - if (cleanUp) { - chain = chain.then(_cleanUp); - } - return chain.then(function () { - return storage.getFirst(queue_storeName); - }).then(function (pending) { - return _prepareToSend(pending, wait); - }); -} +var _logMessages = function _logMessages(reason /*: ReasonT*/) { + return { + start: { + inProgress: "Adjust SDK ".concat(_disableReason(reason), " process has already started"), + done: "Adjust SDK ".concat(_disableReason(reason), " process is now started") + }, + finish: { + inProgress: "Adjust SDK ".concat(_disableReason(reason), " process has already finished"), + done: "Adjust SDK ".concat(_disableReason(reason), " process is now finished") + } + }; +}; /** - * Set offline mode to on or off - * - if on then all requests are queued - * - if off then run all pending requests + * Start or finish disable process * - * @param {boolean} state + * @param {string} reason + * @param {boolean} pending + * @param {string} expectedAction + * @returns {boolean} + * @private */ -function setOffline(state /*: boolean*/) /*: void*/{ - if (state === undefined) { - logger.error('State not provided, true or false has to be defined'); - return; - } - if (state === _isOffline) { - logger.error("The app is already in ".concat(state ? 'offline' : 'online', " mode")); - return; - } - var wasOffline = _isOffline; - _isOffline = state; - if (!state && wasOffline) { - run(); +function _disable(_ref /*:: */, expectedAction /*: 'start' | 'finish'*/) /*: boolean*/{ + var reason = _ref /*:: */.reason, + pending = _ref /*:: */.pending; + var _ref2 = getDisabled() || {}, + savedReason = _ref2.reason, + savedPending = _ref2.pending; + var action = expectedAction === 'start' && savedPending ? 'start' : 'finish'; + var shouldNotStart = expectedAction === 'start' && savedReason; + var shouldNotFinish = expectedAction === 'finish' && savedReason && !savedPending; + if (shouldNotStart || shouldNotFinish) { + logger.log(_logMessages(savedReason)[action].inProgress); + return false; } - logger.info("The app is now in ".concat(state ? 'offline' : 'online', " mode")); + logger.log(_logMessages(reason)[action].done); + setDisabled({ + reason: reason || DISABLE_REASONS.REASON_GENERAL, + pending: pending + }); + return true; } /** - * Clean up stale pending requests + * Disable sdk due to a particular reason * + * @param {string} reason + * @param {boolean} pending * @private - * @returns {Promise} */ -function _cleanUp() /*: Promise*/{ - var upperBound = Date.now() - config.requestValidityWindow; - return storage.deleteBulk(queue_storeName, upperBound, 'upperBound'); +function disable(reason /*: ReasonT*/) /*: boolean*/{ + var pending = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false; + return _disable({ + reason: reason, + pending: pending + }, 'start'); } /** - * Check if there is pending timeout to be flushed - * i.e. if queue is running + * Finish disable process if previously set to pending state * + * @param {string} reason * @returns {boolean} */ -function isRunning() /*: boolean*/{ - return _current.running; +function finish(reason /*: ReasonT*/) /*: boolean*/{ + return _disable({ + reason: reason, + pending: false + }, 'finish'); } /** - * Clear queue store + * Enable sdk if not GDPR forgotten */ -function queue_clear() /*: void*/{ - return storage.clear(queue_storeName); +function restore() /*: boolean*/{ + var _ref3 = getDisabled() || {}, + reason = _ref3.reason; + if (reason === DISABLE_REASONS.REASON_GDPR) { + logger.log('Adjust SDK is disabled due to GDPR-Forget-Me request and it can not be re-enabled'); + return false; + } + if (!reason) { + logger.log('Adjust SDK is already enabled'); + return false; + } + logger.log('Adjust SDK has been enabled'); + setDisabled(null); + return true; } /** - * Destroy queue by clearing current timeout + * Get the current status of the sdk + * - on: not disabled + * - paused: partially disabled, waiting for the opt-out confirmation from the backend + * - off: completely disabled + * + * @returns {string} */ -function queue_destroy() /*: void*/{ - _request.clear(); - _current.running = false; - _current.timestamp = null; - _current.pause = null; +function disable_status() /*: StatusT*/{ + var _ref4 = getDisabled() || {}, + reason = _ref4.reason, + pending = _ref4.pending; + if (reason === DISABLE_REASONS.REASON_GENERAL || reason === DISABLE_REASONS.REASON_GDPR && !pending) { + return 'off'; + } else if (reason === DISABLE_REASONS.REASON_GDPR && pending) { + return 'paused'; + } + return 'on'; } -;// CONCATENATED MODULE: ./src/sdk/global-params.js +;// CONCATENATED MODULE: ./src/sdk/identity.ts -var global_params_Promise = typeof Promise === 'undefined' ? (__webpack_require__(702).Promise) : Promise; -/*:: // -import { type GlobalParamsT, type GlobalParamsMapT } from './types';*/ -/*:: type TypeT = 'callback' | 'partner'*/ -/*:: type KeysT = [string, TypeT]*/ -/*:: type KeysArrayT = Array*/ -/** - * Name of the store used by global params - * - * @type {string} - * @private - */ -var global_params_storeName = 'globalParams'; -/** - * Error message for type missing - * - * @type {Object} - * @private - */ -var _error = { - short: 'No type provided', - long: 'Global parameter type not provided, `callback` or `partner` types are available' -}; -/** - * Omit type parameter from the collection - * - * @param {Array} params - * @returns {Array} - * @private - */ -function _omitType(params) /*: Array*/{ - return (params || []).map(function (_ref) { - var key = _ref.key, - value = _ref.value; + + + +/** Name of the store used by activityState */ +var identity_storeName = StoreName.ActivityState; + +/** Boolean used in start in order to avoid duplicated activity state */ +var _starting /*: boolean*/ = false; + +/** Generate random uuid v4 */ +function _generateUuid() /*: string*/{ + var seed = Date.now(); + return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) { + var r = (seed + Math.random() * 16) % 16 | 0; + seed = Math.floor(seed / 16); + return (c === 'x' ? r : r & (0x3 | 0x8)).toString(16); + }); +} + +/** Inspect stored activity state and check if disable needs to be repeated */ +function _intercept(stored /*: ActivityStateMapT*/) /*: InterceptT*/{ + if (!stored) { return { - key: key, - value: value + exists: false }; - }); + } + if (stored.uuid === 'unknown') { + disable(DISABLE_REASONS.REASON_GDPR); + activity_state.destroy(); + return { + exists: true, + stored: null + }; + } + activity_state.init(stored); + return { + exists: true, + stored: stored + }; } /** - * Get callback and partner global parameters - * - * @returns {Promise} + * Cache stored activity state into running memory */ -function get() /*: Promise*/{ - return global_params_Promise.all([storage.filterBy(global_params_storeName, 'callback'), storage.filterBy(global_params_storeName, 'partner')]).then(function (_ref2) { - var _ref3 = _slicedToArray(_ref2, 2), - callbackParams = _ref3[0], - partnerParams = _ref3[1]; - return { - callbackParams: _omitType(callbackParams), - partnerParams: _omitType(partnerParams) - }; +function start() /*: Promise*/{ + if (_starting) { + return Promise.reject({ + interrupted: true, + message: 'Adjust SDK start already in progress' + }); + } + _starting = true; + return storage.getFirst(identity_storeName).then(_intercept).then(function (result /*: InterceptT*/) { + if (result.exists) { + _starting = false; + return result.stored; + } + var activityState = isEmpty(activity_state.current) ? { + uuid: _generateUuid() + } : activity_state.current; + return storage.addItem(identity_storeName, activityState).then(function () { + activity_state.init(activityState); + reload(); + _starting = false; + return activityState; + }); + }).then(function (activityState /*: ActivityStateMapT | null*/) { + if (activityState) { + publish(PUB_SUB_EVENTS.WEB_UUID_CREATED, activityState.uuid); + } else { + publish(PUB_SUB_EVENTS.WEB_UUID_CREATED, 'gdpr_forgotten'); + } + return activityState; }); } +/** Check if sdk is running at all (totally disabled or inactive activity state) */ +function _isLive() { + return disable_status() !== 'off' && activity_state.isStarted(); +} + /** - * Add global parameters, either callback or partner params - * - * @param {Array} params - * @param {string} type - * @returns {Promise} + * Persist changes made directly in activity state and update lastActive flag */ -function add(params /*: Array*/, type /*: TypeT*/) /*: void | Promise*/{ - if (type === undefined) { - logger.error(_error.long); - return global_params_Promise.reject({ - message: _error.short - }); +function persist() /*: Promise*/{ + if (!_isLive()) { + return Promise.resolve(null); } - /*:: type GlobalParamsWithTypeT = {|...GlobalParamsT, type: string|}*/ - var map /*: {[key: string]: string}*/ = convertToMap(params); - var prepared /*: Array*/ = Object.keys(map).map(function (key) { - return { - key: key, - value: map[key], - type: type - }; + var activityState = _objectSpread2(_objectSpread2({}, activity_state.current), {}, { + lastActive: Date.now() }); - return global_params_Promise.all([storage.filterBy(global_params_storeName, type), storage.addBulk(global_params_storeName, prepared, true)]).then(function (_ref4) { - var _ref5 = _slicedToArray(_ref4, 2), - oldParams = _ref5[0], - newParams = _ref5[1]; - var intersecting = intersection(oldParams.map(function (param) { - return param.key; - }), newParams.map(function (param) { - return param[0]; - })); - logger.log("Following ".concat(type, " parameters have been saved: ").concat(prepared.map(function (p) { - return "".concat(p.key, ":").concat(p.value); - }).join(', '))); - if (intersecting.length) { - logger.log("Keys: ".concat(intersecting.join(', '), " already existed so their values have been updated")); - } - return newParams; + return storage.updateItem(identity_storeName, activityState).then(function () { + return activity_state.current = activityState; }); } /** - * Remove global parameter by key and type - * - * @param {string} key - * @param {string} type - * @returns {Promise} + * Sync in-memory activityState with the one from store + * - should be used when change from another tab is possible and critical */ -function remove(key /*: string*/, type /*: TypeT*/) /*: void | Promise*/{ - if (type === undefined) { - logger.error(_error.long); - return global_params_Promise.reject({ - message: _error.short - }); - } - return storage.deleteItem(global_params_storeName, [key, type]).then(function (result) { - logger.log("".concat(key, " ").concat(type, " parameter has been deleted")); - return result; +function sync() /*: Promise*/{ + return storage.getFirst(identity_storeName).then(function (activityState /*: ActivityStateMapT*/) { + var current = activity_state.current; + var lastActive = current.lastActive || 0; + if (_isLive() && lastActive < activityState.lastActive) { + // Checking if another SDK instance was installed while this one was in backgound + var installedUpdated = !current.installed && activityState.installed; + var sessionCountUpdated = (current.sessionCount || 0) < (activityState.sessionCount || 0); + if (installedUpdated || sessionCountUpdated) { + publish('sdk:installed'); + } + activity_state.current = activityState; + reload(); + } + return activityState; }); } /** - * Remove all global parameters of certain type - * @param {string} type - * @returns {Promise} + * Clear activity state store - set uuid to be unknown */ -function removeAll(type /*: TypeT*/) /*: void | Promise*/{ - if (type === undefined) { - logger.error(_error.long); - return global_params_Promise.reject({ - message: _error.short - }); - } - return storage.deleteBulk(global_params_storeName, type).then(function (result) { - logger.log("All ".concat(type, " parameters have been deleted")); - return result; +function clear() /*: Promise*/{ + var newActivityState = { + uuid: 'unknown' + }; + activity_state.current = newActivityState; + return storage.clear(identity_storeName).then(function () { + return storage.addItem(identity_storeName, newActivityState); }); } /** - * Clear globalParams store + * Destroy current activity state */ -function global_params_clear() /*: void*/{ - return storage.clear(global_params_storeName); +function identity_destroy() /*: void*/{ + activity_state.destroy(); } -;// CONCATENATED MODULE: ./src/sdk/session.js -var session_Promise = typeof Promise === 'undefined' ? (__webpack_require__(702).Promise) : Promise; -/*:: // -import { type DocumentT, type HttpSuccessResponseT, type HttpErrorResponseT, type GlobalParamsMapT, type SessionRequestParamsT } from './types';*/ - - - - +;// CONCATENATED MODULE: ./src/sdk/queue.js +/*:: // +import { type HttpSuccessResponseT, type HttpErrorResponseT, type HttpFinishCbT, type WaitT, type UrlT, type MethodT, type RequestParamsT, type ActivityStateMapT } from './types';*/ @@ -7455,395 +5219,456 @@ import { type DocumentT, type HttpSuccessResponseT, type HttpErrorResponseT, typ +/*:: type PendingT = {| + timestamp: number, + url: UrlT, + method?: MethodT, + createdAt?: number, + params: RequestParamsT +|}*/ /** - * Flag to mark if session watch is already on + * Http request instance * - * @type {boolean} + * @type {Object} * @private */ -var _running = false; +var _request = request({ + strategy: 'long', + continueCb: _continue +}); /** - * Reference to interval id to be used for clearing + * Check if in offline mode * - * @type {number} + * @type {boolean} * @private */ -var _idInterval /*: ?IntervalID*/; +var _isOffline = false; /** - * Reference to timeout id to be used for clearing + * Name of the store used by queue * - * @type {number} + * @type {string} * @private */ -var _idTimeout /*: ?TimeoutID*/; +var queue_storeName = 'queue'; /** - * Browser-specific prefixes for accessing Page Visibility API + * Current running state and task timestamp * - * @type {{hidden, visibilityChange}} + * @type {{running: boolean, timestamp: void|number, pause: void|Object}} * @private */ -var _pva; +var _current +/*: {| + running: boolean, + timestamp: ?number, + pause: ?{| + timestamp: number, + wait: WaitT + |} +|}*/ += { + running: false, + timestamp: null, + pause: null +}; /** - * Reference to the document casted to a plain object + * Remove from the top and continue running pending requests * - * @type {Document} + * @param {Object} result + * @param {Function} finish + * @returns {Promise} + * @private */ -var documentExt = (document /*: DocumentT*/); +function _continue(result /*: HttpSuccessResponseT | HttpErrorResponseT*/, finish /*: HttpFinishCbT*/) /*: Promise*/{ + var wait = result && result.continue_in || null; + _current.pause = wait ? { + timestamp: Date.now(), + wait: wait + } : null; + return storage.getFirst(queue_storeName).then(function (pending) { + return pending ? storage.deleteItem(queue_storeName, pending.timestamp) : null; + }).then(function () { + finish(); + _current.running = false; + return run({ + wait: wait + }); + }); +} /** - * Initiate session watch: - * - bind to visibility change event to track window state (if out of focus or closed) - * - initiate activity state params and visibility state - * - check session initially - * - set the timer to update last active timestamp + * Correct timestamp if equal or less then previous one to avoid constraint errors + * Cases when needed: + * - test environment + * - when pushing to queue synchronously, one after an other * - * @returns {Promise} + * @returns {number} + * @private */ -function watch() /*: Promise*/{ - _pva = getVisibilityApiAccess(); - if (_running) { - return session_Promise.reject({ - interrupted: true, - message: 'Session watch already initiated' - }); - } - _running = true; - subscribe('session:finished', _handleSessionRequestFinish); - if (_pva) { - on(documentExt, _pva.visibilityChange, _handleVisibilityChange); - } - if (_pva && documentExt[_pva.hidden]) { - logger.log('Session request attempt canceled because the tab is still hidden'); - return session_Promise.resolve({}); +function _prepareTimestamp() /*: number*/{ + var timestamp = Date.now(); + if (_current.timestamp && timestamp <= _current.timestamp) { + timestamp = _current.timestamp + 1; } - activity_state.initParams(); - return _checkSession(); + _current.timestamp = timestamp; + return timestamp; } /** - * Check if session watch is running + * Persist activity state change with session offset reset after session request * - * @returns {boolean} + * @param {string} url + * @returns {Promise} + * @private */ -function session_isRunning() /*: boolean*/{ - return _running; +function _persist(url) /*: Promise*/{ + if (isRequest(url, 'session')) { + activity_state.resetSessionOffset(); + } + activity_state.updateLastActive(); + return persist(); } /** - * Destroy session watch + * Push request to the queue + * + * @param {string} url + * @param {string} method + * @param {Object=} params + * @param {boolean=} auto + * @param {number=} timestamp + * @returns {Promise} */ -function session_destroy() /*: void*/{ - _running = false; - activity_state.toBackground(); - _stopTimer(); - if (_pva) { - clearTimeout(_idTimeout); - off(documentExt, _pva.visibilityChange, _handleVisibilityChange); - on(documentExt, _pva.visibilityChange, _restoreAfterAsyncEnable); +function push(_ref /*:: */) { + var url = _ref /*:: */.url, + method = _ref /*:: */.method, + params = _ref /*:: */.params; + var _ref2 = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}, + auto = _ref2.auto, + timestamp = _ref2.timestamp; + activity_state.updateParams(url, auto); + var filteredParams = entries(params || {}).filter(function (_ref3) { + var _ref4 = _slicedToArray(_ref3, 2), + value = _ref4[1]; + return isEmptyEntry(value); + }).reduce(reducer, {}); + var pending /*: PendingT*/ = { + timestamp: _prepareTimestamp(), + url: url, + method: method, + params: _objectSpread2(_objectSpread2({}, activity_state.getParams(url)), filteredParams) + }; + if (timestamp) { + pending.createdAt = timestamp; } + return storage.addItem(queue_storeName, pending).then(function () { + return _persist(url); + }).then(function () { + return _current.running ? {} : run(); + }); } /** - * Handle transit to background: - * - stop the timer - * - update session params - * - persist changes (store updated activity state) + * Prepare to send pending request if available * + * @param {number} timestamp + * @param {number=} createdAt + * @param {string=} url + * @param {string=} method + * @param {Object=} params + * @param {number=} wait * @returns {Promise} * @private */ -function _handleBackground() /*: Promise*/{ - _stopTimer(); - activity_state.updateSessionOffset(); - activity_state.toBackground(); - return persist(); +function _prepareToSend() /*: Promise*/{ + var _ref5 = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}, + timestamp = _ref5.timestamp, + createdAt = _ref5.createdAt, + url = _ref5.url, + method = _ref5.method, + params = _ref5.params; + var wait /*:: ?: ?WaitT*/ = arguments.length > 1 ? arguments[1] : undefined; + var activityState = activity_state.current || {}; + var firstSession = url === '/session' && !activityState.installed; + var noPending = !url && !method && !params; + if (_isOffline && !firstSession || noPending) { + _current.running = false; + return Promise.resolve({}); + } + return _request.send({ + url: url, + method: method, + params: _objectSpread2(_objectSpread2({}, params), {}, { + createdAt: getTimestamp(createdAt || timestamp) + }), + wait: wait || _checkWait() + }); } /** - * Handle transit to foreground: - * - update session length - * - check for the session and restart the timer + * Check if there is waiting period required * - * @returns {Promise} + * @returns {void|number} * @private */ -function _handleForeground() /*: Promise*/{ - return sync().then(function () { - activity_state.updateSessionLength(); - activity_state.toForeground(); - }).then(_checkSession); +function _checkWait() /*: ?WaitT*/{ + var _ref6 = _current.pause || {}, + timestamp = _ref6.timestamp, + wait = _ref6.wait; + var rest = Date.now() - (timestamp || 0); + return rest < wait ? wait - rest : null; } /** - * Handle visibility change and perform appropriate actions + * Run all pending requests * - * @private + * @param {boolean=false} cleanUp + * @param {number=} wait + * @returns {Promise} */ -function _handleVisibilityChange() /*: void*/{ - clearTimeout(_idTimeout); - var handler = _pva && documentExt[_pva.hidden] ? _handleBackground : _handleForeground; - _idTimeout = setTimeout(handler, 0); -} -function _restoreAfterAsyncEnable() /*: void*/{ - if (!_pva || documentExt[_pva.hidden]) { - return; +function run() /*: Promise*/{ + var _ref7 = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}, + cleanUp = _ref7.cleanUp, + wait = _ref7.wait; + if (_current.running) { + return Promise.resolve({}); } - reload(); - if (!_running && disable_status() === 'on') { - off(documentExt, _pva.visibilityChange, _restoreAfterAsyncEnable); - main.__internal__.restartAfterAsyncEnable(); + _current.running = true; + var chain = Promise.resolve({}); + if (cleanUp) { + chain = chain.then(_cleanUp); } + return chain.then(function () { + return storage.getFirst(queue_storeName); + }).then(function (pending) { + return _prepareToSend(pending, wait); + }); } /** - * Handle session request finish; update installed state + * Set offline mode to on or off + * - if on then all requests are queued + * - if off then run all pending requests * - * @param {string} e - * @param {Object} result - * @returns {Promise|void} - * @private + * @param {boolean} state */ -function _handleSessionRequestFinish(e /*: string*/, result /*: HttpSuccessResponseT | HttpErrorResponseT*/) /*: ?Promise*/{ - if (result && result.status === 'error') { - logger.error('Session was not successful, error was returned from the server:', result.response); +function setOffline(state /*: boolean*/) /*: void*/{ + if (state === undefined) { + logger.error('State not provided, true or false has to be defined'); return; } - activity_state.updateInstalled(); - publish('sdk:installed'); - return persist(); -} - -/** - * Start the session timer, every N seconds: - * - update session params - * - persist changes (store updated activity state) - * - * @private - */ -function _startTimer() /*: void*/{ - _stopTimer(); - _idInterval = setInterval(function () { - activity_state.updateSessionOffset(); - return persist(); - }, config.sessionTimerWindow); + if (state === _isOffline) { + logger.error("The app is already in ".concat(state ? 'offline' : 'online', " mode")); + return; + } + var wasOffline = _isOffline; + _isOffline = state; + if (!state && wasOffline) { + run(); + } + logger.info("The app is now in ".concat(state ? 'offline' : 'online', " mode")); } /** - * Stop the session timer + * Clean up stale pending requests * * @private + * @returns {Promise} */ -function _stopTimer() /*: void*/{ - clearInterval(_idInterval); +function _cleanUp() /*: Promise*/{ + var upperBound = Date.now() - sdk_config.requestValidityWindow; + return storage.deleteBulk(queue_storeName, upperBound, 'upperBound'); } /** - * Prepare parameters for the session tracking + * Check if there is pending timeout to be flushed + * i.e. if queue is running * - * @param {Array} callbackParams - * @param {Array} partnerParams - * @returns {Object} - * @private + * @returns {boolean} */ -function _prepareParams(_ref /*:: */) /*: SessionRequestParamsT*/{ - var callbackParams = _ref /*:: */.callbackParams, - partnerParams = _ref /*:: */.partnerParams; - return { - callbackParams: callbackParams.length ? convertToMap(callbackParams) : null, - partnerParams: partnerParams.length ? convertToMap(partnerParams) : null - }; +function isRunning() /*: boolean*/{ + return _current.running; } /** - * Track session by sending the request to the server - * - * @private + * Clear queue store */ -function _trackSession() /*: Promise*/{ - return get().then(function (globalParams) { - push({ - url: '/session', - method: 'POST', - params: _prepareParams(globalParams) - }, { - auto: true - }); - }); +function queue_clear() /*: void*/{ + return storage.clear(queue_storeName); } /** - * Check if session needs to be tracked - * - * @private + * Destroy queue by clearing current timeout */ -function _checkSession() /*: Promise*/{ - _startTimer(); - var activityState = activity_state.current; - var lastInterval = activityState.lastInterval; - var isEnqueued = activityState.sessionCount > 0; - var currentWindow = lastInterval * SECOND; - if (!isEnqueued || isEnqueued && currentWindow >= config.sessionWindow) { - return _trackSession(); - } - publish('attribution:check'); - return persist(); +function queue_destroy() /*: void*/{ + _request.clear(); + _current.running = false; + _current.timestamp = null; + _current.pause = null; } -;// CONCATENATED MODULE: ./src/sdk/attribution.js - +;// CONCATENATED MODULE: ./src/sdk/global-params.js -var attribution_Promise = typeof Promise === 'undefined' ? (__webpack_require__(702).Promise) : Promise; /*:: // -import { type HttpSuccessResponseT, type HttpErrorResponseT, type HttpFinishCbT, type HttpRetryCbT, type AttributionStateT, type AttributionWhiteListT, type ActivityStateMapT, type AttributionMapT } from './types';*/ - - - - +import { type GlobalParamsT, type GlobalParamsMapT } from './types';*/ +/*:: type TypeT = 'callback' | 'partner'*/ +/*:: type KeysT = [string, TypeT]*/ +/*:: type KeysArrayT = Array*/ /** - * Http request instance + * Name of the store used by global params * - * @type {Object} + * @type {string} * @private */ -var attribution_request = request({ - url: '/attribution', - strategy: 'short', - continueCb: attribution_continue -}); +var global_params_storeName = 'globalParams'; /** - * List of valid attribution parameters + * Error message for type missing * - * @type {string[]} + * @type {Object} * @private */ -var _whitelist /*: AttributionWhiteListT*/ = ['tracker_token', 'tracker_name', 'network', 'campaign', 'adgroup', 'creative', 'click_label', 'state']; +var _error = { + short: 'No type provided', + long: 'Global parameter type not provided, `callback` or `partner` types are available' +}; /** - * Check if new attribution is the same as old one + * Omit type parameter from the collection * - * @param {string} adid - * @param {Object=} attribution - * @returns {boolean} + * @param {Array} params + * @returns {Array} * @private */ -function _isSame(_ref /*:: */) /*: boolean*/{ - var adid = _ref /*:: */.adid, - attribution = _ref /*:: */.attribution; - var oldAttribution = activity_state.current.attribution || {}; - var anyDifferent = attribution && _whitelist.some(function (k) { - return oldAttribution[k] !== attribution[k]; +function _omitType(params) /*: Array*/{ + return (params || []).map(function (_ref) { + var key = _ref.key, + value = _ref.value; + return { + key: key, + value: value + }; }); - return !anyDifferent && adid === oldAttribution.adid; } /** - * Check if attribution result is valid + * Get callback and partner global parameters * - * @param {string} adid - * @param {Object=} attribution - * @returns {boolean} - * @private + * @returns {Promise} */ -function _isValid(_ref2 /*:: */) /*: boolean*/{ - var _ref2$adid = _ref2 /*:: */.adid, - adid = _ref2$adid === void 0 ? '' : _ref2$adid, - _ref2$attribution = _ref2 /*:: */.attribution, - attribution = _ref2$attribution === void 0 ? {} : _ref2$attribution; - return !!adid && !!intersection(_whitelist, Object.keys(attribution)).length; +function get() /*: Promise*/{ + return Promise.all([storage.filterBy(global_params_storeName, 'callback'), storage.filterBy(global_params_storeName, 'partner')]).then(function (_ref2) { + var _ref3 = _slicedToArray(_ref2, 2), + callbackParams = _ref3[0], + partnerParams = _ref3[1]; + return { + callbackParams: _omitType(callbackParams), + partnerParams: _omitType(partnerParams) + }; + }); } /** - * Update attribution and initiate client's callback + * Add global parameters, either callback or partner params * - * @param {Object} result - * @private + * @param {Array} params + * @param {string} type + * @returns {Promise} */ -function _setAttribution(result /*: HttpSuccessResponseT*/) /*: Promise*/{ - if (isEmpty(result) || !_isValid(result) || _isSame(result)) { - return attribution_Promise.resolve({ - state: 'same' +function add(params /*: Array*/, type /*: TypeT*/) /*: void | Promise*/{ + if (type === undefined) { + logger.error(_error.long); + return Promise.reject({ + message: _error.short }); } - var attribution /*: AttributionMapT*/ = entries(result.attribution).filter(function (_ref3) { - var _ref4 = _slicedToArray(_ref3, 1), - key = _ref4[0]; - return _whitelist.indexOf(key) !== -1; - }).reduce(reducer, { - adid: result.adid - }); - activity_state.current = _objectSpread2(_objectSpread2({}, activity_state.current), {}, { - attribution: attribution - }); - return persist().then(function () { - publish('attribution:change', attribution); - logger.info('Attribution has been updated'); + /*:: type GlobalParamsWithTypeT = {|...GlobalParamsT, type: string|}*/ + var map /*: {[key: string]: string}*/ = convertToMap(params); + var prepared /*: Array*/ = Object.keys(map).map(function (key) { return { - state: 'changed' + key: key, + value: map[key], + type: type }; }); + return Promise.all([storage.filterBy(global_params_storeName, type), storage.addBulk(global_params_storeName, prepared, true)]).then(function (_ref4) { + var _ref5 = _slicedToArray(_ref4, 2), + oldParams = _ref5[0], + newParams = _ref5[1]; + var intersecting = intersection(oldParams.map(function (param) { + return param.key; + }), newParams.map(function (param) { + return param[0]; + })); + logger.log("Following ".concat(type, " parameters have been saved: ").concat(prepared.map(function (p) { + return "".concat(p.key, ":").concat(p.value); + }).join(', '))); + if (intersecting.length) { + logger.log("Keys: ".concat(intersecting.join(', '), " already existed so their values have been updated")); + } + return newParams; + }); } /** - * Store attribution or make another request if attribution not yet available + * Remove global parameter by key and type * - * @param {Object} result - * @param {Function} finish - * @param {Function} retry + * @param {string} key + * @param {string} type * @returns {Promise} - * @private */ -function attribution_continue(result /*: HttpSuccessResponseT | HttpErrorResponseT*/, finish /*: HttpFinishCbT*/, retry /*: HttpRetryCbT*/) /*: Promise*/{ - if (!result || result && result.status === 'error') { - finish(); - return attribution_Promise.resolve({ - state: 'unknown' +function remove(key /*: string*/, type /*: TypeT*/) /*: void | Promise*/{ + if (type === undefined) { + logger.error(_error.long); + return Promise.reject({ + message: _error.short }); } - if (!result.ask_in) { - finish(); - return _setAttribution(result); - } - return retry(result.ask_in); + return storage.deleteItem(global_params_storeName, [key, type]).then(function (result) { + logger.log("".concat(key, " ").concat(type, " parameter has been deleted")); + return result; + }); } /** - * Request attribution if session asked for it - * - * @param {Object=} sessionResult - * @param {number=} sessionResult.ask_in + * Remove all global parameters of certain type + * @param {string} type + * @returns {Promise} */ -function check(sessionResult /*: HttpSuccessResponseT*/) /*: Promise*/{ - var activityState = activity_state.current; - var askIn = (sessionResult || {}).ask_in; - if (!askIn && (activityState.attribution || !activityState.installed)) { - return attribution_Promise.resolve(activityState); +function removeAll(type /*: TypeT*/) /*: void | Promise*/{ + if (type === undefined) { + logger.error(_error.long); + return Promise.reject({ + message: _error.short + }); } - attribution_request.send({ - params: _objectSpread2({ - initiatedBy: !sessionResult ? 'sdk' : 'backend' - }, activity_state.getParams()), - wait: askIn + return storage.deleteBulk(global_params_storeName, type).then(function (result) { + logger.log("All ".concat(type, " parameters have been deleted")); + return result; }); - activity_state.updateSessionOffset(); - return persist(); } /** - * Destroy attribution by clearing running request + * Clear globalParams store */ -function attribution_destroy() /*: void*/{ - attribution_request.clear(); +function global_params_clear() /*: void*/{ + return storage.clear(global_params_storeName); } -;// CONCATENATED MODULE: ./src/sdk/gdpr-forget-device.js +;// CONCATENATED MODULE: ./src/sdk/session.js +/*:: // +import { type DocumentT, type HttpSuccessResponseT, type HttpErrorResponseT, type GlobalParamsMapT, type SessionRequestParamsT } from './types';*/ + + + + + @@ -7854,1522 +5679,849 @@ function attribution_destroy() /*: void*/{ /** - * Http request instance + * Flag to mark if session watch is already on * - * @type {Object} + * @type {boolean} * @private */ -var gdpr_forget_device_request = request({ - url: '/gdpr_forget_device', - method: 'POST', - strategy: 'short' -}); +var _running = false; /** - * Log messages used in different scenarios + * Reference to interval id to be used for clearing * - * @type {Object} + * @type {number} * @private */ -var gdpr_forget_device_logMessages = { - running: 'Adjust SDK is running pending GDPR Forget Me request', - pending: 'Adjust SDK will run GDPR Forget Me request after initialisation', - paused: 'Adjust SDK is already prepared to send GDPR Forget Me request', - off: 'Adjust SDK is already disabled' -}; +var _idInterval /*: ?IntervalID*/; /** - * Request GDPR-Forget-Me in order to disable sdk + * Reference to timeout id to be used for clearing * - * @param {boolean} force - * @returns {boolean} + * @type {number} + * @private */ -function forget(force /*: boolean*/) /*: boolean*/{ - var sdkStatus = disable_status(); - if (!force && sdkStatus !== 'on') { - logger.log(gdpr_forget_device_logMessages[sdkStatus]); - return false; - } - if (!config.isInitialised()) { - logger.log(gdpr_forget_device_logMessages.pending); - return true; - } - gdpr_forget_device_request.send({ - params: _objectSpread2({}, activity_state.getParams()) - }).then(function () { - publish('sdk:gdpr-forget-me'); - }); - return true; -} +var _idTimeout /*: ?TimeoutID*/; /** - * Start disable of the sdk due to GDPR-Forget-me request + * Browser-specific prefixes for accessing Page Visibility API * - * @returns {boolean} + * @type {{hidden, visibilityChange}} + * @private */ -function gdpr_forget_device_disable() { - return disable(REASON_GDPR, true); -} +var _pva; /** - * Finish disable of the sdk due to GDRP-Forget-me request + * Reference to the document casted to a plain object * - * @returns {boolean} + * @type {Document} */ -function gdpr_forget_device_finish() { - return finish(REASON_GDPR); -} +var documentExt = (document /*: DocumentT*/); /** - * Check if there is pending GDPR-Forget-Me request + * Initiate session watch: + * - bind to visibility change event to track window state (if out of focus or closed) + * - initiate activity state params and visibility state + * - check session initially + * - set the timer to update last active timestamp + * + * @returns {Promise} */ -function gdpr_forget_device_check() /*: void*/{ - if (disable_status() === 'paused') { - logger.log(gdpr_forget_device_logMessages.running); - forget(true); +function watch() /*: Promise*/{ + _pva = getVisibilityApiAccess(); + if (_running) { + return Promise.reject({ + interrupted: true, + message: 'Session watch already initiated' + }); + } + _running = true; + subscribe('session:finished', _handleSessionRequestFinish); + if (_pva) { + on(documentExt, _pva.visibilityChange, _handleVisibilityChange); + } + if (_pva && documentExt[_pva.hidden]) { + logger.log('Session request attempt canceled because the tab is still hidden'); + return Promise.resolve({}); } + activity_state.initParams(); + return _checkSession(); } /** - * Destroy by clearing running request + * Check if session watch is running + * + * @returns {boolean} */ -function gdpr_forget_device_destroy() /*: void*/{ - gdpr_forget_device_request.clear(); +function session_isRunning() /*: boolean*/{ + return _running; } -;// CONCATENATED MODULE: ./src/sdk/third-party-sharing.js - - - - - +/** + * Destroy session watch + */ +function session_destroy() /*: void*/{ + _running = false; + activity_state.toBackground(); + _stopTimer(); + if (_pva) { + clearTimeout(_idTimeout); + off(documentExt, _pva.visibilityChange, _handleVisibilityChange); + on(documentExt, _pva.visibilityChange, _restoreAfterAsyncEnable); + } +} -/*:: type ThirdPartySharingStatusT = 'pending' | 'on' | 'off'*/ /** - * Log messages used in different scenarios + * Handle transit to background: + * - stop the timer + * - update session params + * - persist changes (store updated activity state) * - * @type {Object} + * @returns {Promise} * @private */ -var third_party_sharing_logMessages = { - running: 'Adjust SDK is running pending third-party sharing opt-out request', - delayed: 'Adjust SDK will run third-party sharing opt-out request after initialisation', - pending: 'Adjust SDK already queued third-party sharing opt-out request', - off: 'Third-party sharing opt-out is already done', - start: { - inProgress: 'Third-party sharing opt-out has already started', - done: 'Third-party sharing opt-out is now started' - }, - finish: { - inProgress: 'Third-party sharing opt-out has already finished', - done: 'Third-party sharing opt-out is now finished' - } -}; +function _handleBackground() /*: Promise*/{ + _stopTimer(); + activity_state.updateSessionOffset(); + activity_state.toBackground(); + return persist(); +} /** - * Get the status of the third-party sharing + * Handle transit to foreground: + * - update session length + * - check for the session and restart the timer * - * @returns {string} + * @returns {Promise} * @private */ -function _status() /*: ThirdPartySharingStatusT*/{ - var disabled = getThirdPartySharing() || {}; - if (disabled.reason) { - return disabled.pending ? 'pending' : 'off'; - } - return 'on'; +function _handleForeground() /*: Promise*/{ + return sync().then(function () { + activity_state.updateSessionLength(); + activity_state.toForeground(); + }).then(_checkSession); } /** - * Request third-party sharing opt-out request + * Handle visibility change and perform appropriate actions * - * @param {boolean} force - * @returns {boolean} + * @private */ -function optOut(force /*: boolean*/) { - var status = _status(); - if (!force && status !== 'on') { - logger.log(third_party_sharing_logMessages[status]); - return false; +function _handleVisibilityChange() /*: void*/{ + clearTimeout(_idTimeout); + var handler = _pva && documentExt[_pva.hidden] ? _handleBackground : _handleForeground; + _idTimeout = setTimeout(handler, 0); +} +function _restoreAfterAsyncEnable() /*: void*/{ + if (!_pva || documentExt[_pva.hidden]) { + return; } - if (!config.isInitialised()) { - logger.log(third_party_sharing_logMessages.delayed); - return true; + reload(); + if (!_running && disable_status() === 'on') { + off(documentExt, _pva.visibilityChange, _restoreAfterAsyncEnable); + main.__internal__.restartAfterAsyncEnable(); } - push({ - url: '/disable_third_party_sharing', - method: 'POST' - }); - return true; } /** - * Start or finish thrid-party sharing disable process + * Handle session request finish; update installed state * - * @param {boolean} pending - * @param {string} expectedAction - * @returns {boolean} + * @param {string} e + * @param {Object} result + * @returns {Promise|void} * @private */ -function third_party_sharing_disable(pending /*: boolean*/, expectedAction /*: 'start' | 'finish'*/) /*: boolean*/{ - var disabled = getThirdPartySharing() || {}; - var action = expectedAction === 'start' && pending ? 'start' : 'finish'; - var shouldNotStart = expectedAction === 'start' && disabled.reason; - var shouldNotFinish = expectedAction === 'finish' && disabled.reason && !disabled.pending; - if (shouldNotStart || shouldNotFinish) { - logger.log(third_party_sharing_logMessages[action].inProgress); - return false; +function _handleSessionRequestFinish(e /*: string*/, result /*: HttpSuccessResponseT | HttpErrorResponseT*/) /*: ?Promise*/{ + if (result && result.status === 'error') { + logger.error('Session was not successful, error was returned from the server:', result.response); + return; } - logger.log(third_party_sharing_logMessages[action].done); - setThirdPartySharing({ - reason: REASON_GENERAL, - pending: pending - }); - return true; + activity_state.updateInstalled(); + publish('sdk:installed'); + return persist(); } /** - * Start the third-party sharing disable process + * Start the session timer, every N seconds: + * - update session params + * - persist changes (store updated activity state) * - * @returns {boolean} + * @private */ -function sdk_third_party_sharing_disable() /*: boolean*/{ - return third_party_sharing_disable(true, 'start'); +function _startTimer() /*: void*/{ + _stopTimer(); + _idInterval = setInterval(function () { + activity_state.updateSessionOffset(); + return persist(); + }, sdk_config.sessionTimerWindow); } /** - * Finalize the third-party sharing process + * Stop the session timer * - * @returns {boolean} - */ -function third_party_sharing_finish() { - return third_party_sharing_disable(false, 'finish'); -} - -/** - * Check if there s pending third-party sharing opt-out request + * @private */ -function third_party_sharing_check() /*: void*/{ - if (_status() === 'pending') { - logger.log(third_party_sharing_logMessages.running); - optOut(true); - } +function _stopTimer() /*: void*/{ + clearInterval(_idInterval); } -;// CONCATENATED MODULE: ./src/sdk/scheduler.js - - -/*:: type TaskT = {| - method: (timestamp?: number) => mixed, - description: string, - timestamp: number -|}*/ /** - * Delayed tasks list + * Prepare parameters for the session tracking * - * @type {Array} + * @param {Array} callbackParams + * @param {Array} partnerParams + * @returns {Object} * @private */ -var _tasks /*: Array*/ = []; +function _prepareParams(_ref /*:: */) /*: SessionRequestParamsT*/{ + var callbackParams = _ref /*:: */.callbackParams, + partnerParams = _ref /*:: */.partnerParams; + return { + callbackParams: callbackParams.length ? convertToMap(callbackParams) : null, + partnerParams: partnerParams.length ? convertToMap(partnerParams) : null + }; +} /** - * Put the dask in the delayed list + * Track session by sending the request to the server * - * @param {Function} method - * @param {string} description + * @private */ -function delay(method /*: $PropertyType*/, description /*: $PropertyType*/) /*: void*/{ - _tasks.push({ - method: method, - description: description, - timestamp: Date.now() +function _trackSession() /*: Promise*/{ + return get().then(function (globalParams) { + push({ + url: '/session', + method: 'POST', + params: _prepareParams(globalParams) + }, { + auto: true + }); }); } /** - * Flush all delayed tasks + * Check if session needs to be tracked + * + * @private */ -function flush() /*: void*/{ - _tasks.forEach(function (task /*: TaskT*/) { - if (typeof task.method === 'function') { - logger.log("Delayed ".concat(task.description, " task is running now")); - task.method(task.timestamp); - } - }); - _tasks = []; +function _checkSession() /*: Promise*/{ + _startTimer(); + var activityState = activity_state.current; + var lastInterval = activityState.lastInterval; + var isEnqueued = activityState.sessionCount > 0; + var currentWindow = lastInterval * SECOND; + if (!isEnqueued || isEnqueued && currentWindow >= sdk_config.sessionWindow) { + return _trackSession(); + } + publish('attribution:check'); + return persist(); } -/** - * Destroy all pending tasks - */ -function scheduler_destroy() /*: void*/{ - _tasks = []; -} +;// CONCATENATED MODULE: ./src/sdk/attribution.js -;// CONCATENATED MODULE: ./src/sdk/event.js -var event_Promise = typeof Promise === 'undefined' ? (__webpack_require__(702).Promise) : Promise; /*:: // -import { type EventParamsT, type EventRequestParamsT, type GlobalParamsMapT, type GlobalKeyValueParamsT } from './types';*/ +import { type HttpSuccessResponseT, type HttpErrorResponseT, type HttpFinishCbT, type HttpRetryCbT, type AttributionStateT, type AttributionWhiteListT, type ActivityStateMapT, type AttributionMapT } from './types';*/ + -/*:: type RevenueT = { - revenue: string, - currency: string -}*/ -var DEFAULT_EVENT_DEDUPLICATION_LIST_LIMIT = 10; /** - * Name of the store used by event deduplication ids + * Http request instance * - * @type {string} + * @type {Object} * @private */ -var event_storeName = 'eventDeduplication'; +var attribution_request = request({ + url: '/attribution', + strategy: 'short', + continueCb: attribution_continue +}); /** - * Get revenue value if positive and limit to 5 decimal places + * List of valid attribution parameters * - * @param {number=} revenue - * @param {string=} currency - * @returns {Object} + * @type {string[]} * @private */ -function _getRevenue(revenue /*: number | void*/, currency /*: string | void*/) /*: RevenueT*/{ - if (isNaN(revenue)) { - return {}; - } - revenue = parseFloat(revenue); - if (revenue < 0 || !currency) { - return {}; - } - return { - revenue: revenue.toFixed(5), - currency: currency - }; -} +var _whitelist /*: AttributionWhiteListT*/ = ['tracker_token', 'tracker_name', 'network', 'campaign', 'adgroup', 'creative', 'click_label', 'state']; /** - * Prepare parameters for the event tracking + * Check if new attribution is the same as old one * - * @param {Object} params - * @param {string} params.eventToken - * @param {number=} params.revenue - * @param {string=} params.currency - * @param {Array=} params.callbackParams - * @param {Array=} params.partnerParams - * @param {Array} callbackParams - * @param {Array} partnerParams - * @returns {Object} + * @param {string} adid + * @param {Object=} attribution + * @returns {boolean} * @private */ -function event_prepareParams(params /*: EventParamsT*/, _ref /*:: */) /*: EventRequestParamsT*/{ - var callbackParams = _ref /*:: */.callbackParams, - partnerParams = _ref /*:: */.partnerParams; - var globalParams = {}; - var baseParams = _objectSpread2({ - eventToken: params.eventToken, - deduplicationId: params.deduplicationId - }, _getRevenue(params.revenue, params.currency)); - var eventCallbackParams /*: GlobalKeyValueParamsT*/ = _objectSpread2(_objectSpread2({}, convertToMap(callbackParams)), convertToMap(params.callbackParams)); - var eventPartnerParams /*: GlobalKeyValueParamsT*/ = _objectSpread2(_objectSpread2({}, convertToMap(partnerParams)), convertToMap(params.partnerParams)); - if (!isEmpty(eventCallbackParams)) { - globalParams.callbackParams = eventCallbackParams; - } - if (!isEmpty(eventPartnerParams)) { - globalParams.partnerParams = eventPartnerParams; - } - return _objectSpread2(_objectSpread2({}, baseParams), globalParams); +function _isSame(_ref /*:: */) /*: boolean*/{ + var adid = _ref /*:: */.adid, + attribution = _ref /*:: */.attribution; + var oldAttribution = activity_state.current.attribution || {}; + var anyDifferent = attribution && _whitelist.some(function (k) { + return oldAttribution[k] !== attribution[k]; + }); + return !anyDifferent && adid === oldAttribution.adid; } /** - * Get event deduplication ids + * Check if attribution result is valid * - * @returns {Promise} + * @param {string} adid + * @param {Object=} attribution + * @returns {boolean} * @private */ -function _getEventDeduplicationIds() /*: Promise>*/{ - return storage.getAll(event_storeName).then(function (records) { - return records.map(function (record) { - return record.id; - }); - }); +function _isValid(_ref2 /*:: */) /*: boolean*/{ + var _ref2$adid = _ref2 /*:: */.adid, + adid = _ref2$adid === void 0 ? '' : _ref2$adid, + _ref2$attribution = _ref2 /*:: */.attribution, + attribution = _ref2$attribution === void 0 ? {} : _ref2$attribution; + return !!adid && !!intersection(_whitelist, Object.keys(attribution)).length; } /** - * Push event deduplication id and trim the store if out of the limit + * Update attribution and initiate client's callback * - * @param {string} id - * @returns {Promise} + * @param {Object} result * @private */ -function _pushEventDeduplicationId(id /*: string*/) /*: Promise*/{ - var customLimit = config.getCustomConfig().eventDeduplicationListLimit; - var limit = customLimit > 0 ? customLimit : DEFAULT_EVENT_DEDUPLICATION_LIST_LIMIT; - return storage.count(event_storeName).then(function (count) { - var chain = event_Promise.resolve(); - if (count >= limit) { - var removeLength = count - limit + 1; - logger.log("Event deduplication list limit has been reached. Oldest ids are about to be removed (".concat(removeLength, " of them)")); - chain = storage.trimItems(event_storeName, removeLength); - } - return chain; - }).then(function () { - logger.info("New event deduplication id is added to the list: ".concat(id)); - return storage.addItem(event_storeName, { - id: id +function _setAttribution(result /*: HttpSuccessResponseT*/) /*: Promise*/{ + if (isEmpty(result) || !_isValid(result) || _isSame(result)) { + return Promise.resolve({ + state: 'same' }); + } + var attribution /*: AttributionMapT*/ = entries(result.attribution).filter(function (_ref3) { + var _ref4 = _slicedToArray(_ref3, 1), + key = _ref4[0]; + return _whitelist.indexOf(key) !== -1; + }).reduce(reducer, { + adid: result.adid + }); + activity_state.current = _objectSpread2(_objectSpread2({}, activity_state.current), {}, { + attribution: attribution + }); + return persist().then(function () { + publish('attribution:change', attribution); + publish(PUB_SUB_EVENTS.ATTRIBUTION_RECEIVED, attribution); + logger.info('Attribution has been updated'); + return { + state: 'changed' + }; }); } /** - * Check if deduplication id is already stored - * - if yes then reject - * - if not then push the id into storage + * Store attribution or make another request if attribution not yet available * - * @param {string=} id + * @param {Object} result + * @param {Function} finish + * @param {Function} retry * @returns {Promise} * @private */ -function _checkEventDeduplicationId(id /*: string*/) /*: Promise*/{ - if (!id) { - return event_Promise.resolve(); - } - return _getEventDeduplicationIds().then(function (list) { - return list.indexOf(id) === -1 ? _pushEventDeduplicationId(id) : event_Promise.reject({ - message: "Event won't be tracked, since it was previously tracked with the same deduplication id ".concat(id) +function attribution_continue(result /*: HttpSuccessResponseT | HttpErrorResponseT*/, finish /*: HttpFinishCbT*/, retry /*: HttpRetryCbT*/) /*: Promise*/{ + if (!result || result && result.status === 'error') { + finish(); + return Promise.resolve({ + state: 'unknown' }); - }); + } + if (!result.ask_in) { + finish(); + return _setAttribution(result); + } + return retry(result.ask_in); } /** - * Track event by sending the request to the server + * Request attribution if session asked for it * - * @param {Object} params - * @param {number=} timestamp - * @return Promise + * @param {Object=} sessionResult + * @param {number=} sessionResult.ask_in */ -function event_event(params /*: EventParamsT*/, timestamp /*: number*/) /*: Promise*/{ - if (!params || params && (isEmpty(params) || !params.eventToken)) { - var reason = 'You must provide event token in order to track event'; - logger.error(reason); - return event_Promise.reject(reason); +function check(sessionResult /*: HttpSuccessResponseT*/) /*: Promise*/{ + var activityState = activity_state.current; + var askIn = (sessionResult || {}).ask_in; + if (!askIn && (activityState.attribution || !activityState.installed)) { + return Promise.resolve(activityState); } - return _checkEventDeduplicationId(params.deduplicationId).then(get).then(function (globalParams) { - return push({ - url: '/event', - method: 'POST', - params: event_prepareParams(params, globalParams) - }, { - timestamp: timestamp - }); - }).catch(function (error) { - if (error && error.message) { - logger.error(error.message); - } - return event_Promise.reject(error); + attribution_request.send({ + params: _objectSpread2({ + initiatedBy: !sessionResult ? 'sdk' : 'backend' + }, activity_state.getParams()), + wait: askIn }); + activity_state.updateSessionOffset(); + return persist(); } -;// CONCATENATED MODULE: ./src/sdk/sdk-click.js -/*:: // -import { type SdkClickRequestParamsT } from './types';*/ + +/** + * Destroy attribution by clearing running request + */ +function attribution_destroy() /*: void*/{ + attribution_request.clear(); +} + +;// CONCATENATED MODULE: ./src/sdk/gdpr-forget-device.ts + + + + + /** - * Check the following: - * - redirected from somewhere other then client's website - * - there is adjust_referrer query param + * Http request instance * - * @returns {boolean} + * @type {Object} * @private */ -function _getReferrer() /*: ?string*/{ - return window.location.search.substring(1).split('&').map(function (pair) { - return pair.split('='); - }).reduce(reducer, {})['adjust_referrer']; -} +var gdpr_forget_device_request = request({ + url: '/gdpr_forget_device', + method: 'POST', + strategy: 'short' +}); /** - * Prepare params for the sdk click request + * Log messages used in different scenarios * - * @param {string} referrer - * @returns {Object} + * @type {Object} * @private */ -function sdk_click_prepareParams(referrer) /*: SdkClickRequestParamsT*/{ - return { - clickTime: getTimestamp(), - source: 'web_referrer', - referrer: decodeURIComponent(referrer) - }; -} +var gdpr_forget_device_logMessages = { + running: 'Adjust SDK is running pending GDPR Forget Me request', + pending: 'Adjust SDK will run GDPR Forget Me request after initialisation', + paused: 'Adjust SDK is already prepared to send GDPR Forget Me request', + off: 'Adjust SDK is already disabled' +}; /** - * Sends sdk_click request with manually settled referrer or with automatically grabbed one + * Request GDPR-Forget-Me in order to disable sdk + * + * @param {boolean} force + * @returns {boolean} */ -function sdkClick(manualReferrer /*: string*/, timestamp /*: number*/) /*: void*/{ - var referrer; - if (manualReferrer) { - referrer = manualReferrer; - } else { - referrer = _getReferrer(); +function forget(force /*: boolean*/) /*: boolean*/{ + var sdkStatus = disable_status(); + if (!force && sdkStatus !== 'on') { + logger.log(gdpr_forget_device_logMessages[sdkStatus]); + return false; } - if (referrer) { - push({ - url: '/sdk_click', - method: 'POST', - params: sdk_click_prepareParams(referrer) - }, { - timestamp: timestamp - }); + if (!sdk_config.isInitialised()) { + logger.log(gdpr_forget_device_logMessages.pending); + return true; } + gdpr_forget_device_request.send({ + params: _objectSpread2({}, activity_state.getParams()) + }).then(function () { + publish('sdk:gdpr-forget-me'); + }); + return true; } -;// CONCATENATED MODULE: ./src/sdk/smart-banner/detect-os.ts + /** - * Operation systems + * Start disable of the sdk due to GDPR-Forget-me request + * + * @returns {boolean} */ -var DeviceOS; +function gdpr_forget_device_disable() { + return disable(DISABLE_REASONS.REASON_GDPR, true); +} /** - * Returns one of android, ios, windows, windows-phone or undefined for another OS. + * Finish disable of the sdk due to GDRP-Forget-me request + * + * @returns {boolean} */ -(function (DeviceOS) { - DeviceOS["Android"] = "android"; - DeviceOS["iOS"] = "ios"; - DeviceOS["WindowsPC"] = "windows"; - DeviceOS["WindowsPhone"] = "windows-phone"; -})(DeviceOS || (DeviceOS = {})); -function getDeviceOS() /*: Maybe*/{ - var _navigator, _navigator$userAgent; - var userAgent = (_navigator = navigator) === null || _navigator === void 0 ? void 0 : (_navigator$userAgent = _navigator.userAgent) === null || _navigator$userAgent === void 0 ? void 0 : _navigator$userAgent.toLowerCase(); - if (!userAgent || userAgent.length < 1) { - return undefined; - } - if (/ipad|iphone|ipod/.test(userAgent)) { - return DeviceOS.iOS; - } - - // Checking Windows first because Lumia devices could have for example - // "Mozilla/5.0 (Windows Mobile 10; Android 8.0.0; Microsoft; Lumia 950XL) ..." user agent - if (userAgent.includes('windows')) { - if (/phone|mobile/.test(userAgent)) { - return DeviceOS.WindowsPhone; - } - return DeviceOS.WindowsPC; - } - if (userAgent.includes('android')) { - return DeviceOS.Android; - } - return undefined; +function gdpr_forget_device_finish() { + return finish(DISABLE_REASONS.REASON_GDPR); } -;// CONCATENATED MODULE: ./src/sdk/smart-banner/utilities.ts + /** - * Wraps JSON.parse() with try-catch. - * Returns parsed object if successfully parsed and null otherwise. + * Check if there is pending GDPR-Forget-Me request */ -function parseJson(str /*: string | null*/) /*: any*/{ - if (!str) { - return null; - } - try { - return JSON.parse(str); - } catch (error) { - return null; +function gdpr_forget_device_check() /*: void*/{ + if (disable_status() === 'paused') { + logger.log(gdpr_forget_device_logMessages.running); + forget(true); } } -;// CONCATENATED MODULE: ./src/sdk/smart-banner/storage/local-storage.ts - - - -var LocalStorage = /*#__PURE__*/function () { - function LocalStorage() { - var storageName /*: string*/ = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 'adjust-smart-banner'; - _classCallCheck(this, LocalStorage); - this.storageName /*:: */ = storageName /*:: */; - } - _createClass(LocalStorage, [{ - key: "setItem", - value: function setItem(key /*: string*/, value /*: any*/) /*: void*/{ - localStorage.setItem("".concat(this.storageName, ".").concat(key), JSON.stringify(value)); - } - }, { - key: "getItem", - value: function getItem(key /*: string*/) /*: any | null*/{ - var value = localStorage.getItem("".concat(this.storageName, ".").concat(key)); - return parseJson(value); - } - }, { - key: "removeItem", - value: function removeItem(key /*: string*/) /*: void*/{ - localStorage.removeItem("".concat(this.storageName, ".").concat(key)); - } - }]); - return LocalStorage; -}(); -;// CONCATENATED MODULE: ./src/sdk/smart-banner/storage/in-memory-storage.ts - - - -var in_memory_storage_InMemoryStorage = /*#__PURE__*/function () { - function InMemoryStorage() { - _classCallCheck(this, InMemoryStorage); - _defineProperty(this, "items", {}); - } - _createClass(InMemoryStorage, [{ - key: "setItem", - value: function setItem(key /*: string*/, value /*: any*/) /*: void*/{ - this.items[key] = value; - } - }, { - key: "getItem", - value: function getItem(key /*: string*/) /*: any | null*/{ - return Object.prototype.hasOwnProperty.call(this.items, key) ? this.items[key] : null; - } - }, { - key: "removeItem", - value: function removeItem(key /*: string*/) /*: void*/{ - delete this.items[key]; - } - }]); - return InMemoryStorage; -}(); -;// CONCATENATED MODULE: ./src/sdk/smart-banner/storage/factory.ts - - - - - -var StorageFactory = /*#__PURE__*/function () { - function StorageFactory() { - _classCallCheck(this, StorageFactory); - } - _createClass(StorageFactory, null, [{ - key: "isLocalStorageSupported", - value: function isLocalStorageSupported() /*: boolean*/{ - try { - var uid = new Date().toString(); - var storage = window.localStorage; - storage.setItem(uid, uid); - var result = storage.getItem(uid) === uid; - storage.removeItem(uid); - var support = !!(result && storage); - return support; - } catch (e) { - return false; - } - } - }, { - key: "createStorage", - value: function createStorage() /*: Storage*/{ - if (this.isLocalStorageSupported()) { - return new LocalStorage(); - } - return new in_memory_storage_InMemoryStorage(); - } - }]); - return StorageFactory; -}(); -;// CONCATENATED MODULE: ./src/sdk/smart-banner/api.ts -var api_Promise = typeof Promise === 'undefined' ? (__webpack_require__(702).Promise) : Promise; - -var Position; -(function (Position) { - Position["Top"] = "top"; - Position["Bottom"] = "bottom"; -})(Position || (Position = {})); -/*:: export interface SmartBannerData { - appId: string; - appName: string; - position: Position; - imageUrl?: string; - header: string; - description: string; - buttonText: string; - dismissInterval: number; - trackerToken: string; - deeplinkPath?: string; -}*/ /** - * Ensures response contains general info: title, description, button_label and tracker_token and converts response - * to SmartBannerData + * Destroy by clearing running request */ -function validate(response /*: Partial*/) /*: SmartBannerData | null*/{ - var title = response.title, - description = response.description, - button_label = response.button_label, - tracker_token = response.tracker_token; - if (title && description && button_label && tracker_token) { - var _response$app, _response$app2; - return { - appId: ((_response$app = response.app) === null || _response$app === void 0 ? void 0 : _response$app.default_store_app_id) || '', - appName: ((_response$app2 = response.app) === null || _response$app2 === void 0 ? void 0 : _response$app2.name) || '', - position: response.position || Position.Bottom, - imageUrl: response.image_url, - header: title, - description: description, - buttonText: button_label, - trackerToken: tracker_token, - deeplinkPath: response.deeplink_path, - dismissInterval: 24 * 60 * 60 * 1000 // 1 day in millis before show banner next time - }; - } - - return null; -} -function fetchSmartBannerData(webToken /*: string*/, deviceOs /*: DeviceOS*/, network /*: Network*/) /*: Promise*/{ - var path = '/smart_banner'; - return network.request(path, { - 'app_web_token': webToken - }).then(function (banners) { - var banner = banners.find(function (item) { - return item.platform === deviceOs; - }); - if (!banner) { - return null; - } - return validate(banner); - }).catch(function (error) { - logger.error('Network error occurred during loading Smart Banner: ' + JSON.stringify(error)); - return null; - }); +function gdpr_forget_device_destroy() /*: void*/{ + gdpr_forget_device_request.clear(); } -// EXTERNAL MODULE: ./node_modules/style-loader/dist/runtime/injectStylesIntoStyleTag.js -var injectStylesIntoStyleTag = __webpack_require__(379); -var injectStylesIntoStyleTag_default = /*#__PURE__*/__webpack_require__.n(injectStylesIntoStyleTag); -// EXTERNAL MODULE: ./node_modules/style-loader/dist/runtime/styleDomAPI.js -var styleDomAPI = __webpack_require__(795); -var styleDomAPI_default = /*#__PURE__*/__webpack_require__.n(styleDomAPI); -// EXTERNAL MODULE: ./node_modules/style-loader/dist/runtime/insertBySelector.js -var insertBySelector = __webpack_require__(569); -var insertBySelector_default = /*#__PURE__*/__webpack_require__.n(insertBySelector); -// EXTERNAL MODULE: ./node_modules/style-loader/dist/runtime/setAttributesWithoutAttributes.js -var setAttributesWithoutAttributes = __webpack_require__(565); -var setAttributesWithoutAttributes_default = /*#__PURE__*/__webpack_require__.n(setAttributesWithoutAttributes); -// EXTERNAL MODULE: ./node_modules/style-loader/dist/runtime/insertStyleElement.js -var insertStyleElement = __webpack_require__(216); -var insertStyleElement_default = /*#__PURE__*/__webpack_require__.n(insertStyleElement); -// EXTERNAL MODULE: ./node_modules/style-loader/dist/runtime/styleTagTransform.js -var styleTagTransform = __webpack_require__(589); -var styleTagTransform_default = /*#__PURE__*/__webpack_require__.n(styleTagTransform); -// EXTERNAL MODULE: ./node_modules/css-loader/dist/cjs.js??ruleSet[1].rules[1].use[1]!./node_modules/sass-loader/dist/cjs.js!./src/sdk/smart-banner/assets/styles.module.scss -var styles_module = __webpack_require__(841); -;// CONCATENATED MODULE: ./src/sdk/smart-banner/assets/styles.module.scss - - - - - - - - - - - -var options = {}; - -options.styleTagTransform = (styleTagTransform_default()); -options.setAttributes = (setAttributesWithoutAttributes_default()); - - options.insert = insertBySelector_default().bind(null, "head"); - -options.domAPI = (styleDomAPI_default()); -options.insertStyleElement = (insertStyleElement_default()); - -var update = injectStylesIntoStyleTag_default()(styles_module/* default */.Z, options); - - - - - /* harmony default export */ const assets_styles_module = (styles_module/* default */.Z && styles_module/* default.locals */.Z.locals ? styles_module/* default.locals */.Z.locals : undefined); - -;// CONCATENATED MODULE: ./src/sdk/smart-banner/assets/template.ts - -/* harmony default export */ const template = (function (positionStyle /*: string*/, header /*: string*/, description /*: string*/, buttonText /*: string*/, href /*: string*/) { - return "\n
\n
\n
\n \n
\n
\n \"").concat(header,\n
\n
\n

").concat(header, "

\n

").concat(description, "

\n
\n ").concat(buttonText, "\n
\n
\n
"); -}); -;// CONCATENATED MODULE: ./src/sdk/smart-banner/view/app-icon.ts - - - -var app_icon_Promise = typeof Promise === 'undefined' ? (__webpack_require__(702).Promise) : Promise; -var AppIcon = /*#__PURE__*/function () { - function AppIcon(bannerData /*: AppIconData*/, image /*: HTMLImageElement*/, placeholder /*: HTMLElement*/) { - _classCallCheck(this, AppIcon); - _defineProperty(this, "appTraceUrl", function (appId /*: string*/) { - return "https://www.apptrace.com/api/app/".concat(appId, "/artwork_url_small"); - }); - _defineProperty(this, "appName", void 0); - _defineProperty(this, "image", void 0); - _defineProperty(this, "placeholder", void 0); - this.image = image; - this.placeholder = placeholder; - this.appName = bannerData.appName; - var sources = this.getSources(bannerData); - this.showImage(sources); - } - _createClass(AppIcon, [{ - key: "getSources", - value: function getSources(bannerData /*: AppIconData*/) /*: string[]*/{ - var sourcesArray /*: string[]*/ = []; - if (bannerData.imageUrl) { - sourcesArray.push(bannerData.imageUrl); - } - sourcesArray.push(this.appTraceUrl(bannerData.appId)); - return sourcesArray; - } - }, { - key: "showImage", - value: function showImage(sources /*: string[]*/) /*: Promise*/{ - var _this = this; - var imageLoadingPromise = sources.reduce(function (acc, url) { - return acc.catch(function () { - return _this.loadImage(url, _this.image); - }); - }, app_icon_Promise.reject()); - return imageLoadingPromise.then(function () { - _this.placeholder.remove(); - }).catch(function () { - _this.image.remove(); - _this.placeholder.innerText = _this.appName.length ? _this.appName[0].toUpperCase() : ''; - }); - } - }, { - key: "loadImage", - value: function loadImage(url /*: string*/, image /*: HTMLImageElement*/) { - return new app_icon_Promise(function (resolve, reject) { - image.onload = resolve; - image.onerror = reject; - image.src = url; - }); - } - }]); - return AppIcon; -}(); -;// CONCATENATED MODULE: ./src/sdk/smart-banner/view/smart-banner-view.ts +;// CONCATENATED MODULE: ./src/sdk/track-third-party-sharing.ts -var SmartBannerView = /*#__PURE__*/function () { - function SmartBannerView(data /*: SmartBannerData*/, onDismiss /*: () => void*/, endpoint /*: string*/) { - _classCallCheck(this, SmartBannerView); - _defineProperty(this, "parent", document.body); - _defineProperty(this, "banner", void 0); - _defineProperty(this, "dismissButton", null); - _defineProperty(this, "onDismiss", void 0); - this.onDismiss = onDismiss; - this.render(data, endpoint); - } - _createClass(SmartBannerView, [{ - key: "render", - value: function render(bannerData /*: SmartBannerData*/, endpoint /*: string*/) { - this.banner = document.createElement('div'); - this.banner.setAttribute('class', assets_styles_module.bannerContainer); - var positionStyle = bannerData.position === Position.Top ? assets_styles_module.stickyToTop : assets_styles_module.stickyToBottom; - var query = bannerData.deeplinkPath ? "?deeplink=".concat(encodeURIComponent(bannerData.deeplinkPath)) : ''; - var href = "".concat(endpoint, "/").concat(bannerData.trackerToken).concat(query); - this.banner.innerHTML = template(positionStyle, bannerData.header, bannerData.description, bannerData.buttonText, href); - if (bannerData.position === Position.Top) { - this.parent.insertBefore(this.banner, this.parent.firstChild); - } else { - this.parent.appendChild(this.banner); - } - this.dismissButton = this.getElemByClass(assets_styles_module.dismiss); - if (this.dismissButton) { - this.dismissButton.addEventListener('click', this.onDismiss); - } - var appIconPlaceholder = this.getElemByClass(assets_styles_module.placeholder); - var appIconImage = this.getElemByClass(assets_styles_module.image); - if (appIconImage && appIconPlaceholder) { - new AppIcon(bannerData, appIconImage, appIconPlaceholder); - } +/*:: export interface ThirdPartySharingOptions { + isEnabled: boolean; + granularOptions: Record>; + partnerSharingSettings: Record>; +}*/ +var ThirdPartySharing = /*#__PURE__*/function () { + function ThirdPartySharing(isEnabled /*: boolean*/) { + _classCallCheck(this, ThirdPartySharing); + _defineProperty(this, "_granularOptions", {}); + _defineProperty(this, "_partnerSharingSettings", {}); + if (typeof isEnabled !== 'boolean') { + logger.warn("isEnabled should be boolean, converting ".concat(isEnabled, " results ").concat(!!isEnabled)); } - }, { - key: "show", - value: function show() { - this.banner.hidden = false; + this._isEnabled = !!isEnabled; + } + return _createClass(ThirdPartySharing, [{ + key: "isEnabled", + get: function get() /*: boolean*/{ + return this._isEnabled; } }, { - key: "hide", - value: function hide() { - this.banner.hidden = true; + key: "granularOptions", + get: function get() /*: Record>*/{ + return this._granularOptions; } }, { - key: "destroy", - value: function destroy() { - this.removeDismissButtonHandler(); - this.banner.remove(); + key: "partnerSharingSettings", + get: function get() /*: Record>*/{ + return this._partnerSharingSettings; } }, { - key: "removeDismissButtonHandler", - value: function removeDismissButtonHandler() { - if (this.dismissButton && this.onDismiss) { - this.dismissButton.removeEventListener('click', this.onDismiss); - this.dismissButton = null; + key: "addGranularOption", + value: function addGranularOption(partnerName /*: string*/, key /*: string*/, value /*: string*/) { + if (!partnerName || !key || value === undefined) { + logger.error('Cannot add granular option, partnerName, key and value are mandatory'); + return; + } + var pair = _defineProperty({}, key, value); + if (this.granularOptions[partnerName]) { + this.granularOptions[partnerName] = _objectSpread2(_objectSpread2({}, this.granularOptions[partnerName]), pair); + } else { + this.granularOptions[partnerName] = pair; } } }, { - key: "getElemByClass", - value: function getElemByClass /*:: */(classNames /*: string*/) /*: T | null*/{ - if (this.banner) { - var elements = this.banner.getElementsByClassName(classNames); - return elements.length > 0 ? elements[0] : null; + key: "addPartnerSharingSetting", + value: function addPartnerSharingSetting(partnerName /*: string*/, key /*: string*/, value /*: boolean*/) { + if (!partnerName || !key || value === undefined) { + logger.error('Cannot add partner sharing setting, partnerName, key and value are mandatory'); + return; } - return null; - } - }]); - return SmartBannerView; -}(); -;// CONCATENATED MODULE: ./src/sdk/smart-banner/network/errors.ts -/*:: export interface NetworkError { - status: number; - message: string; -}*/ -var NoConnectionError /*: NetworkError*/ = { - status: 0, - message: 'No internet connectivity' -}; -;// CONCATENATED MODULE: ./src/sdk/smart-banner/network/xhr-network.ts - - - -var xhr_network_Promise = typeof Promise === 'undefined' ? (__webpack_require__(702).Promise) : Promise; - - - -/** Sends HTTP GET request using XMLHttpRequest */ -var XhrNetwork = /*#__PURE__*/function () { - function XhrNetwork(origin /*: string*/) { - _classCallCheck(this, XhrNetwork); - this.origin /*:: ?*/ = origin /*:: ?*/; - } - _createClass(XhrNetwork, [{ - key: "endpoint", - get: function get() /*: string*/{ - if (!this.origin) { - throw Error('XhrNetwork: Origin not defined'); + var pair = _defineProperty({}, key, value); + if (this.partnerSharingSettings[partnerName]) { + this.partnerSharingSettings[partnerName] = _objectSpread2(_objectSpread2({}, this.partnerSharingSettings[partnerName]), pair); + } else { + this.partnerSharingSettings[partnerName] = pair; } - return this.origin; - }, - set: function set(value /*: string*/) { - this.origin = value; - } - - /** - * Creates an XMLHttpRequest object and sends a GET request with provided encoded URL - * @param url encoded URL - */ - }, { - key: "xhr", - value: function xhr /*:: */(url /*: string*/) /*: Promise*/{ - return new xhr_network_Promise(function (resolve, reject /*: (err: NetworkError) => void*/) { - var xhr = new XMLHttpRequest(); - xhr.open('GET', url); - var headers = [['Client-SDK', "js".concat(globals.version)], ['Content-Type', 'application/json']]; - headers.forEach(function (_ref) { - var _ref2 = _slicedToArray(_ref, 2), - key = _ref2[0], - value = _ref2[1]; - xhr.setRequestHeader(key, value); - }); - xhr.onerror = function () { - return reject(NoConnectionError); - }; - xhr.onreadystatechange = function () { - if (xhr.readyState !== 4) { - return; - } - var okStatus = xhr.status >= 200 && xhr.status < 300; - var json = parseJson(xhr.responseText); - if (xhr.status === 0) { - reject(NoConnectionError); - } else { - if (okStatus) { - resolve(json); - } else { - reject({ - status: xhr.status, - message: json || xhr.responseText || '' - }); - } - } - }; - xhr.send(); - }); - } - }, { - key: "encodeParams", - value: function encodeParams(params /*: Record*/) /*: string*/{ - return Object.keys(params).map(function (key) { - return [encodeURIComponent(key), encodeURIComponent(params[key])].join('='); - }).join('&'); - } - }, { - key: "request", - value: function request /*:: */(path /*: string*/, params /*: Record*/) /*: Promise*/{ - var encodedParams = params ? "?".concat(this.encodeParams(params)) : ''; - return this.xhr("".concat(this.endpoint).concat(path).concat(encodedParams)); } }]); - return XhrNetwork; }(); -;// CONCATENATED MODULE: ./node_modules/@babel/runtime/helpers/esm/assertThisInitialized.js -function _assertThisInitialized(self) { - if (self === void 0) { - throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); - } - return self; -} -;// CONCATENATED MODULE: ./node_modules/@babel/runtime/helpers/esm/setPrototypeOf.js -function _setPrototypeOf(o, p) { - _setPrototypeOf = Object.setPrototypeOf ? Object.setPrototypeOf.bind() : function _setPrototypeOf(o, p) { - o.__proto__ = p; - return o; - }; - return _setPrototypeOf(o, p); -} -;// CONCATENATED MODULE: ./node_modules/@babel/runtime/helpers/esm/inherits.js - -function _inherits(subClass, superClass) { - if (typeof superClass !== "function" && superClass !== null) { - throw new TypeError("Super expression must either be null or a function"); +function trackThirdPartySharing(adjustThirdPartySharing /*: ThirdPartySharingOptions*/) { + if (!adjustThirdPartySharing || adjustThirdPartySharing.isEnabled === undefined) { + logger.error('Can not track third-party sharing without parameters'); + return; } - subClass.prototype = Object.create(superClass && superClass.prototype, { - constructor: { - value: subClass, - writable: true, - configurable: true - } - }); - Object.defineProperty(subClass, "prototype", { - writable: false - }); - if (superClass) _setPrototypeOf(subClass, superClass); -} -;// CONCATENATED MODULE: ./node_modules/@babel/runtime/helpers/esm/getPrototypeOf.js -function _getPrototypeOf(o) { - _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf.bind() : function _getPrototypeOf(o) { - return o.__proto__ || Object.getPrototypeOf(o); + var params = { + sharing: adjustThirdPartySharing.isEnabled ? 'enable' : 'disable', + granularThirdPartySharingOptions: adjustThirdPartySharing.granularOptions, + partnerSharingSettings: adjustThirdPartySharing.partnerSharingSettings }; - return _getPrototypeOf(o); -} -;// CONCATENATED MODULE: ./node_modules/@babel/runtime/helpers/esm/isNativeReflectConstruct.js -function _isNativeReflectConstruct() { - if (typeof Reflect === "undefined" || !Reflect.construct) return false; - if (Reflect.construct.sham) return false; - if (typeof Proxy === "function") return true; - try { - Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {})); - return true; - } catch (e) { - return false; - } + push({ + url: '/third_party_sharing', + method: 'POST', + params: params + }); } -;// CONCATENATED MODULE: ./node_modules/@babel/runtime/helpers/esm/possibleConstructorReturn.js +;// CONCATENATED MODULE: ./src/sdk/scheduler.js +/*:: type TaskT = {| + method: (timestamp?: number) => mixed, + description: string, + timestamp: number +|}*/ +/** + * Delayed tasks list + * + * @type {Array} + * @private + */ +var _tasks /*: Array*/ = []; -function _possibleConstructorReturn(self, call) { - if (call && (_typeof(call) === "object" || typeof call === "function")) { - return call; - } else if (call !== void 0) { - throw new TypeError("Derived constructors may only return object or undefined"); - } - return _assertThisInitialized(self); +/** + * Put the dask in the delayed list + * + * @param {Function} method + * @param {string} description + */ +function delay(method /*: $PropertyType*/, description /*: $PropertyType*/) /*: void*/{ + _tasks.push({ + method: method, + description: description, + timestamp: Date.now() + }); } -;// CONCATENATED MODULE: ./node_modules/@babel/runtime/helpers/esm/createSuper.js - - -function _createSuper(Derived) { - var hasNativeReflectConstruct = _isNativeReflectConstruct(); - return function _createSuperInternal() { - var Super = _getPrototypeOf(Derived), - result; - if (hasNativeReflectConstruct) { - var NewTarget = _getPrototypeOf(this).constructor; - result = Reflect.construct(Super, arguments, NewTarget); - } else { - result = Super.apply(this, arguments); +/** + * Flush all delayed tasks + */ +function flush() /*: void*/{ + _tasks.forEach(function (task /*: TaskT*/) { + if (typeof task.method === 'function') { + logger.log("Delayed ".concat(task.description, " task is running now")); + task.method(task.timestamp); } - return _possibleConstructorReturn(this, result); - }; + }); + _tasks = []; } -;// CONCATENATED MODULE: ./src/sdk/smart-banner/network/network.ts +/** + * Destroy all pending tasks + */ +function scheduler_destroy() /*: void*/{ + _tasks = []; +} -var network_Promise = typeof Promise === 'undefined' ? (__webpack_require__(702).Promise) : Promise; -/*:: export interface Network { - endpoint: string; - request: (path: string, params?: Record) => Promise; -}*/ -var NetworkDecorator = /*#__PURE__*/function () { - function NetworkDecorator(network /*: Network*/) { - _classCallCheck(this, NetworkDecorator); - this.network /*:: */ = network /*:: */; - } - _createClass(NetworkDecorator, [{ - key: "endpoint", - get: function get() /*: string*/{ - return this.network.endpoint; - }, - set: function set(value /*: string*/) { - this.network.endpoint = value; - } - }, { - key: "request", - value: function request /*:: */(path /*: string*/, params /*: Record*/) /*: Promise*/{ - return this.network.request(path, params); - } - }]); - return NetworkDecorator; -}(); -;// CONCATENATED MODULE: ./src/sdk/smart-banner/network/url-strategy/url-strategy.ts +;// CONCATENATED MODULE: ./src/sdk/event.js + +/*:: // +import { type EventParamsT, type EventRequestParamsT, type GlobalParamsMapT, type GlobalKeyValueParamsT } from './types';*/ -var url_strategy_Promise = typeof Promise === 'undefined' ? (__webpack_require__(702).Promise) : Promise; -/*:: export type BaseUrlsMap = { - endpointName: string; - app: string; - gdpr: string; + +/*:: type RevenueT = { + revenue: string, + currency: string }*/ -var url_strategy_UrlStrategy = /*#__PURE__*/function () { - function UrlStrategy(preferredUrls /*: () => BaseUrlsMap[]*/) { - _classCallCheck(this, UrlStrategy); - this.preferredUrls /*:: */ = preferredUrls /*:: */; - } +var DEFAULT_EVENT_DEDUPLICATION_LIST_LIMIT = 10; - /** - * Gets the list of preferred endpoints and wraps `sendRequest` function with iterative retries until available - * endpoint found or another error occurred. - */ - _createClass(UrlStrategy, [{ - key: "retries", - value: function retries /*:: */(sendRequest /*: (urls: BaseUrlsMap) => Promise*/) /*: Promise*/{ - var _this = this; - var attempt = 0; - var trySendRequest = function trySendRequest() /*: Promise*/{ - var preferredUrls = _this.preferredUrls(); - if (!preferredUrls || preferredUrls.length === 0) { - logger.error(UrlStrategy.NoPreferredUrlsDefinedError.message); - throw UrlStrategy.NoPreferredUrlsDefinedError; - } - var urlsMap = preferredUrls[attempt++]; - return sendRequest(urlsMap).catch(function (reason /*: NetworkError*/) { - if (reason === NoConnectionError) { - logger.log("Failed to connect ".concat(urlsMap.endpointName, " endpoint")); - if (attempt < preferredUrls.length) { - logger.log("Trying ".concat(preferredUrls[attempt].endpointName, " one")); - return trySendRequest(); // Trying next endpoint - } - } +/** + * Name of the store used by event deduplication ids + * + * @type {string} + * @private + */ +var event_storeName = 'eventDeduplication'; - // Another error occurred or we ran out of attempts, re-throw - throw reason; - }); - }; - return trySendRequest(); - } - }]); - return UrlStrategy; -}(); -_defineProperty(url_strategy_UrlStrategy, "NoPreferredUrlsDefinedError", new ReferenceError('UrlStrategy: No preferred URL defined')); -;// CONCATENATED MODULE: ./src/sdk/smart-banner/network/url-strategy/blocked-url-bypass.ts - - -var BlockedUrlBypass; -(function (_BlockedUrlBypass) { - var _endpoints; - var Default = _BlockedUrlBypass.Default = 'default'; - var India = _BlockedUrlBypass.India = 'india'; - var China = _BlockedUrlBypass.China = 'china'; - /*:: */ - var endpoints /*:: */ = (_endpoints = {}, _defineProperty(_endpoints, BlockedUrlBypass.Default, ENDPOINTS["default"]), _defineProperty(_endpoints, BlockedUrlBypass.India, ENDPOINTS.india), _defineProperty(_endpoints, BlockedUrlBypass.China, ENDPOINTS.china), _endpoints); - var getPreferredUrlsWithOption = function getPreferredUrlsWithOption(endpoints /*:: */, option /*:: */) { - if (option === BlockedUrlBypass.India) { - return [endpoints[BlockedUrlBypass.India], endpoints[BlockedUrlBypass.Default]]; - } - if (option === BlockedUrlBypass.China) { - return [endpoints[BlockedUrlBypass.China], endpoints[BlockedUrlBypass.Default]]; - } - return [endpoints[BlockedUrlBypass.Default], endpoints[BlockedUrlBypass.India], endpoints[BlockedUrlBypass.China]]; - }; - function preferredUrlsGetter(option /*:: */) { - var endpointsMap /*:: */ = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : endpoints; - return function () { - return getPreferredUrlsWithOption(endpointsMap, option); - }; +/** + * Get revenue value if positive and limit to 5 decimal places + * + * @param {number=} revenue + * @param {string=} currency + * @returns {Object} + * @private + */ +function _getRevenue(revenue /*: number | void*/, currency /*: string | void*/) /*: RevenueT*/{ + if (isNaN(revenue)) { + return {}; } - _BlockedUrlBypass.preferredUrlsGetter = preferredUrlsGetter; -})(BlockedUrlBypass || (BlockedUrlBypass = {})); -;// CONCATENATED MODULE: ./src/sdk/smart-banner/network/url-strategy/custom-url.ts -var CustomUrl; -(function (_CustomUrl) { - var getPreferredUrlsWithOption = function getPreferredUrlsWithOption(customUrl /*:: */) { - return [{ - endpointName: "Custom (".concat(customUrl, ")"), - app: customUrl, - gdpr: customUrl - }]; - }; - function preferredUrlsGetter(customUrl /*:: */) { - return function () { - return getPreferredUrlsWithOption(customUrl); - }; + revenue = parseFloat(revenue); + if (revenue < 0 || !currency) { + return {}; } - _CustomUrl.preferredUrlsGetter = preferredUrlsGetter; -})(CustomUrl || (CustomUrl = {})); -;// CONCATENATED MODULE: ./src/sdk/smart-banner/network/url-strategy/data-residency.ts - - -var data_residency_DataResidency; -(function (_DataResidency) { - var _endpoints; - var EU = _DataResidency.EU = 'EU'; - var TR = _DataResidency.TR = 'TR'; - var US = _DataResidency.US = 'US'; - /*:: */ - var endpoints /*:: */ = (_endpoints = {}, _defineProperty(_endpoints, data_residency_DataResidency.EU, ENDPOINTS.EU), _defineProperty(_endpoints, data_residency_DataResidency.TR, ENDPOINTS.TR), _defineProperty(_endpoints, data_residency_DataResidency.US, ENDPOINTS.US), _endpoints); - var getPreferredUrlsWithOption = function getPreferredUrlsWithOption(endpoints /*:: */, option /*:: */) { - return [endpoints[option]]; + return { + revenue: revenue.toFixed(5), + currency: currency }; - function preferredUrlsGetter(option /*:: */) { - var endpointsMap /*:: */ = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : endpoints; - return function () { - return getPreferredUrlsWithOption(endpointsMap, option); - }; - } - _DataResidency.preferredUrlsGetter = preferredUrlsGetter; -})(data_residency_DataResidency || (data_residency_DataResidency = {})); -;// CONCATENATED MODULE: ./src/sdk/smart-banner/network/url-strategy/url-strategy-factory.ts - - - - +} -/*:: export type UrlStrategyConfig = { - customUrl: string; - urlStrategy?: never; - dataResidency?: never; -} | { - customUrl?: never; - dataResidency: DataResidency.Region; - urlStrategy?: never; -} | { - customUrl?: never; - dataResidency?: never; - urlStrategy?: BlockedUrlBypass.Strategy; -}*/ -var UrlStrategyFactory; -(function (_UrlStrategyFactory) { - var incorrectOptionIgnoredMessage = function incorrectOptionIgnoredMessage(higherPriority /*:: */, lowerPriority /*:: */) { - logger.warn("Both ".concat(higherPriority, " and ").concat(lowerPriority, " are set in config, ").concat(lowerPriority, " will be ignored")); - }; - function create(config /*:: */) /*:: */{ - var customUrl = config.customUrl, - dataResidency = config.dataResidency, - urlStrategy = config.urlStrategy; - if (customUrl) { - if (dataResidency || urlStrategy) { - incorrectOptionIgnoredMessage('customUrl', dataResidency ? 'dataResidency' : 'urlStrategy'); - } - return new url_strategy_UrlStrategy(CustomUrl.preferredUrlsGetter(customUrl)); - } else if (dataResidency) { - if (urlStrategy) { - incorrectOptionIgnoredMessage('dataResidency', 'urlStrategy'); - } - return new url_strategy_UrlStrategy(data_residency_DataResidency.preferredUrlsGetter(dataResidency)); - } else { - return new url_strategy_UrlStrategy(BlockedUrlBypass.preferredUrlsGetter(urlStrategy)); - } +/** + * Prepare parameters for the event tracking + * + * @param {Object} params + * @param {string} params.eventToken + * @param {number=} params.revenue + * @param {string=} params.currency + * @param {Array=} params.callbackParams + * @param {Array=} params.partnerParams + * @param {Array} callbackParams + * @param {Array} partnerParams + * @returns {Object} + * @private + */ +function event_prepareParams(params /*: EventParamsT*/, _ref /*:: */) /*: EventRequestParamsT*/{ + var callbackParams = _ref /*:: */.callbackParams, + partnerParams = _ref /*:: */.partnerParams; + var globalParams = {}; + var baseParams = _objectSpread2({ + eventToken: params.eventToken, + deduplicationId: params.deduplicationId + }, _getRevenue(params.revenue, params.currency)); + var eventCallbackParams /*: GlobalKeyValueParamsT*/ = _objectSpread2(_objectSpread2({}, convertToMap(callbackParams)), convertToMap(params.callbackParams)); + var eventPartnerParams /*: GlobalKeyValueParamsT*/ = _objectSpread2(_objectSpread2({}, convertToMap(partnerParams)), convertToMap(params.partnerParams)); + if (!isEmpty(eventCallbackParams)) { + globalParams.callbackParams = eventCallbackParams; } - _UrlStrategyFactory.create = create; -})(UrlStrategyFactory || (UrlStrategyFactory = {})); -;// CONCATENATED MODULE: ./src/sdk/smart-banner/network/url-startegy-network.ts - - - - - - -var url_startegy_network_Promise = typeof Promise === 'undefined' ? (__webpack_require__(702).Promise) : Promise; + if (!isEmpty(eventPartnerParams)) { + globalParams.partnerParams = eventPartnerParams; + } + return _objectSpread2(_objectSpread2({}, baseParams), globalParams); +} +/** + * Get event deduplication ids + * + * @returns {Promise} + * @private + */ +function _getEventDeduplicationIds() /*: Promise>*/{ + return storage.getAll(event_storeName).then(function (records) { + return records.map(function (record) { + return record.id; + }); + }); +} +/** + * Push event deduplication id and trim the store if out of the limit + * + * @param {string} id + * @returns {Promise} + * @private + */ +function _pushEventDeduplicationId(id /*: string*/) /*: Promise*/{ + var customLimit = sdk_config.getCustomConfig().eventDeduplicationListLimit; + var limit = customLimit > 0 ? customLimit : DEFAULT_EVENT_DEDUPLICATION_LIST_LIMIT; + return storage.count(event_storeName).then(function (count) { + var chain = Promise.resolve(); + if (count >= limit) { + var removeLength = count - limit + 1; + logger.log("Event deduplication list limit has been reached. Oldest ids are about to be removed (".concat(removeLength, " of them)")); + chain = storage.trimItems(event_storeName, removeLength); + } + return chain; + }).then(function () { + logger.info("New event deduplication id is added to the list: ".concat(id)); + return storage.addItem(event_storeName, { + id: id + }); + }); +} -var NetworkWithUrlStrategy = /*#__PURE__*/function (_NetworkDecorator) { - _inherits(NetworkWithUrlStrategy, _NetworkDecorator); - var _super = _createSuper(NetworkWithUrlStrategy); - function NetworkWithUrlStrategy(network /*: Network*/, _ref /*:: */) { - var _this; - var urlStrategy = _ref /*:: */.urlStrategy, - urlStrategyConfig = _ref /*:: */.urlStrategyConfig; - _classCallCheck(this, NetworkWithUrlStrategy); - _this = _super.call(this, network); - _defineProperty(_assertThisInitialized(_this), "lastSuccessfulEndpoint", void 0); - _defineProperty(_assertThisInitialized(_this), "urlStrategy", void 0); - _this.urlStrategy = urlStrategy || UrlStrategyFactory.create(urlStrategyConfig); - return _this; +/** + * Check if deduplication id is already stored + * - if yes then reject + * - if not then push the id into storage + * + * @param {string=} id + * @returns {Promise} + * @private + */ +function _checkEventDeduplicationId(id /*: string*/) /*: Promise*/{ + if (!id) { + return Promise.resolve(); } + return _getEventDeduplicationIds().then(function (list) { + return list.indexOf(id) === -1 ? _pushEventDeduplicationId(id) : Promise.reject({ + message: "Event won't be tracked, since it was previously tracked with the same deduplication id ".concat(id) + }); + }); +} - /** - * Returns last succesfull endpoint or default (`https://app.adjust.com`) one - */ - _createClass(NetworkWithUrlStrategy, [{ - key: "endpoint", - get: function get() /*: string*/{ - return this.lastSuccessfulEndpoint || NetworkWithUrlStrategy.DEFAULT_ENDPOINT; - } - - /** - * Sends a request to provided path choosing origin with UrlStrategy and caches used origin if it was successfully - * reached - * - * @param path - * @param params non-encoded parameters of the request - */ - }, { - key: "request", - value: function request /*:: */(path /*: string*/, params /*: Record*/) /*: Promise*/{ - var _this2 = this; - return this.urlStrategy.retries(function (baseUrlsMap) { - _this2.network.endpoint = baseUrlsMap.app; - return _this2.network.request(path, params).then(function (result /*: T*/) { - _this2.lastSuccessfulEndpoint = baseUrlsMap.app; - return result; - }).catch(function (err /*: NetworkError*/) { - _this2.lastSuccessfulEndpoint = undefined; - throw err; - }); - }); +/** + * Track event by sending the request to the server + * + * @param {Object} params + * @param {number=} timestamp + * @return Promise + */ +function event_event(params /*: EventParamsT*/, timestamp /*: number*/) /*: Promise*/{ + if (!params || params && (isEmpty(params) || !params.eventToken)) { + var reason = 'You must provide event token in order to track event'; + logger.error(reason); + return Promise.reject(reason); + } + return _checkEventDeduplicationId(params.deduplicationId).then(get).then(function (globalParams) { + return push({ + url: '/event', + method: 'POST', + params: event_prepareParams(params, globalParams) + }, { + timestamp: timestamp + }); + }).catch(function (error) { + if (error && error.message) { + logger.error(error.message); } - }]); - return NetworkWithUrlStrategy; -}(NetworkDecorator); -_defineProperty(NetworkWithUrlStrategy, "DEFAULT_ENDPOINT", ENDPOINTS["default"].app); -(function (_NetworkWithUrlStrategy) { - /*:: */ -})(NetworkWithUrlStrategy || (NetworkWithUrlStrategy = {})); -;// CONCATENATED MODULE: ./src/sdk/smart-banner/smart-banner.ts - - - -var smart_banner_Promise = typeof Promise === 'undefined' ? (__webpack_require__(702).Promise) : Promise; - + return Promise.reject(error); + }); +} +;// CONCATENATED MODULE: ./src/sdk/sdk-click.js +/*:: // +import { type SdkClickRequestParamsT } from './types';*/ +/** + * Check the following: + * - redirected from somewhere other then client's website + * - there is adjust_referrer query param + * + * @returns {boolean} + * @private + */ +function _getReferrer() /*: ?string*/{ + return window.location.search.substring(1).split('&').map(function (pair) { + return pair.split('='); + }).reduce(reducer, {})['adjust_referrer']; +} +/** + * Prepare params for the sdk click request + * + * @param {string} referrer + * @returns {Object} + * @private + */ +function sdk_click_prepareParams(referrer) /*: SdkClickRequestParamsT*/{ + return { + clickTime: getTimestamp(), + source: 'web_referrer', + referrer: decodeURIComponent(referrer) + }; +} /** - * Adjust Web SDK Smart Banner + * Sends sdk_click request with manually settled referrer or with automatically grabbed one */ -var SmartBanner = /*#__PURE__*/function () { - function SmartBanner(_ref /*:: */, network /*: Network*/) { - var webToken = _ref /*:: */.webToken, - _ref$logLevel = _ref /*:: */.logLevel, - logLevel = _ref$logLevel === void 0 ? 'error' : _ref$logLevel, - dataResidency = _ref /*:: */.dataResidency, - onCreated = _ref /*:: */.onCreated, - onDismissed = _ref /*:: */.onDismissed; - _classCallCheck(this, SmartBanner); - _defineProperty(this, "STORAGE_KEY_DISMISSED", 'closed'); - _defineProperty(this, "network", void 0); - _defineProperty(this, "storage", void 0); - _defineProperty(this, "timer", null); - _defineProperty(this, "dataFetchPromise", void 0); - _defineProperty(this, "banner", void 0); - _defineProperty(this, "onCreated", void 0); - _defineProperty(this, "onDismissed", void 0); - this.onCreated = onCreated; - this.onDismissed = onDismissed; - logger.setLogLevel(logLevel); - var config = dataResidency ? { - dataResidency: dataResidency - } : {}; - this.network = network || new NetworkWithUrlStrategy(new XhrNetwork(), { - urlStrategyConfig: config +function sdkClick(manualReferrer /*: string*/, timestamp /*: number*/) /*: void*/{ + var referrer; + if (manualReferrer) { + referrer = manualReferrer; + } else { + referrer = _getReferrer(); + } + if (referrer) { + push({ + url: '/sdk_click', + method: 'POST', + params: sdk_click_prepareParams(referrer) + }, { + timestamp: timestamp }); - this.storage = StorageFactory.createStorage(); - this.init(webToken); } - - /** - * Initiate Smart Banner - * - * @param webToken token used to get data from backend - */ - _createClass(SmartBanner, [{ - key: "init", - value: function init(webToken /*: string*/) { - var _this = this; - if (this.banner) { - logger.error('Smart Banner already exists'); - return; - } - if (this.dataFetchPromise) { - logger.error('Smart Banner is initialising already'); - return; - } - var deviceOs = getDeviceOS(); - if (!deviceOs) { - logger.log('This platform is not one of the targeting ones, Smart Banner will not be shown'); - return; - } - this.dataFetchPromise = fetchSmartBannerData(webToken, deviceOs, this.network); - this.dataFetchPromise.then(function (bannerData) { - _this.dataFetchPromise = null; - if (!bannerData) { - logger.log("No Smart Banners for ".concat(deviceOs, " platform found")); - return; - } - var whenToShow = _this.getDateToShowAgain(bannerData.dismissInterval); - if (Date.now() < whenToShow) { - logger.log('Smart Banner was dismissed'); - _this.scheduleCreation(webToken, whenToShow); - return; - } - logger.log('Creating Smart Banner'); - _this.banner = new SmartBannerView(bannerData, function () { - return _this.dismiss(webToken, bannerData.dismissInterval); - }, _this.network.endpoint); - logger.log('Smart Banner created'); - if (_this.onCreated) { - _this.onCreated(); - } - }); - } - - /** - * Show Smart Banner - */ - }, { - key: "show", - value: function show() /*: void*/{ - var _this2 = this; - if (this.banner) { - this.banner.show(); - return; - } - if (this.dataFetchPromise) { - logger.log('Smart Banner will be shown after initialisation finished'); - this.dataFetchPromise.then(function () { - logger.log('Initialisation finished, showing Smart Banner'); - _this2.show(); - }); - return; - } - logger.error('There is no Smart Banner to show, have you called initialisation?'); - } - - /** - * Hide Smart Banner - */ - }, { - key: "hide", - value: function hide() /*: void*/{ - var _this3 = this; - if (this.banner) { - this.banner.hide(); - return; - } - if (this.dataFetchPromise) { - logger.log('Smart Banner will be hidden after initialisation finished'); - this.dataFetchPromise.then(function () { - logger.log('Initialisation finished, hiding Smart Banner'); - _this3.hide(); - }); - return; - } - logger.error('There is no Smart Banner to hide, have you called initialisation?'); - } - - /** - * Removes Smart Banner from DOM - */ - }, { - key: "destroy", - value: function destroy() { - if (this.banner) { - this.banner.destroy(); - this.banner = null; - logger.log('Smart Banner removed'); - } else { - logger.error('There is no Smart Banner to remove'); - } - } - - /** - * Schedules next Smart Banner show and removes banner from DOM - */ - }, { - key: "dismiss", - value: function dismiss(webToken /*: string*/, dismissInterval /*: number*/) { - logger.log('Smart Banner dismissed'); - this.storage.setItem(this.STORAGE_KEY_DISMISSED, Date.now()); - var whenToShow = this.getDateToShowAgain(dismissInterval); - this.scheduleCreation(webToken, whenToShow); - this.destroy(); - if (this.onDismissed) { - this.onDismissed(); - } - } - - /** - * Sets a timeout to schedule next Smart Banner show - */ - }, { - key: "scheduleCreation", - value: function scheduleCreation(webToken /*: string*/, when /*: number*/) { - var _this4 = this; - if (this.timer) { - logger.log('Clearing previously scheduled creation of Smart Banner'); - clearTimeout(this.timer); - this.timer = null; - } - var delay = when - Date.now(); - this.timer = setTimeout(function () { - _this4.timer = null; - _this4.init(webToken); - }, delay); - logger.log('Smart Banner creation scheduled on ' + new Date(when)); - } - - /** - * Returns date when Smart Banner should be shown again - */ - }, { - key: "getDateToShowAgain", - value: function getDateToShowAgain(dismissInterval /*: number*/) /*: number*/{ - var dismissedDate = this.storage.getItem(this.STORAGE_KEY_DISMISSED); - if (!dismissedDate || typeof dismissedDate !== 'number') { - return Date.now(); - } - return dismissedDate + dismissInterval; - } - }]); - return SmartBanner; -}(); +} ;// CONCATENATED MODULE: ./src/sdk/main.js var _excluded = ["logLevel", "logOutput"]; -var main_Promise = typeof Promise === 'undefined' ? (__webpack_require__(702).Promise) : Promise; /*:: // -import { type InitOptionsT, type LogOptionsT, type EventParamsT, type GlobalParamsT, type CustomErrorT, type ActivityStateMapT, type SmartBannerOptionsT, type AttributionMapT } from './types';*/ - +import { type InitOptionsT, type LogOptionsT, type EventParamsT, type GlobalParamsT, type CustomErrorT, type ActivityStateMapT, type AttributionMapT } from './types';*/ @@ -9388,14 +6540,14 @@ import { type InitOptionsT, type LogOptionsT, type EventParamsT, type GlobalPara -/*:: type InitConfigT = $ReadOnly<{|...InitOptionsT, ...LogOptionsT|}>*/ +/*:: type InitConfigT = $ReadOnly<{|...InitOptionsT, ...LogOptionsT |}>*/ /** * In-memory parameters to be used if restarting * * @type {Object} * @private */ -var main_options /*: ?InitOptionsT*/ = null; +var main_options /*: ? InitOptionsT*/ = null; /** * Flag to mark id sdk is in starting process @@ -9421,13 +6573,6 @@ var _isStarted /*: boolean*/ = false; */ var _isInstalled /*: boolean*/ = false; -/** - * SmartBanner instance - * - * @private - */ -var _smartBanner /*: ?SmartBanner*/ = null; - /** * Initiate the instance with parameters * @@ -9445,7 +6590,7 @@ function initSdk() /*: void*/{ logger.error('You already initiated your instance'); return; } - if (config.hasMissing(options)) { + if (sdk_config.hasMissing(options)) { return; } _isInitialising = true; @@ -9464,6 +6609,8 @@ function initSdk() /*: void*/{ * Get user's current attribution information * * @returns {AttributionMapT|undefined} current attribution information if available or `undefined` otherwise + * + * @deprecated Use {@link waitForAttribution} instead */ function main_getAttribution() /*: ?AttributionMapT*/{ return _preCheck('get attribution', function () { @@ -9471,16 +6618,40 @@ function main_getAttribution() /*: ?AttributionMapT*/{ }); } +/** + * Returns a promise which resolves when current attribution information becomes available + */ +function main_waitForAttribution() /*: Promise*/{ + return _preCheck('get attribution', function () { + return activity_state.waitForAttribution(); + }, { + schedule: false + }); +} + /** * Get `web_uuid` - a unique ID of user generated per subdomain and per browser * * @returns {string|undefined} `web_uuid` if available or `undefined` otherwise + * + * @deprecated Use {@link waitForWebUUID} instead */ function main_getWebUUID() /*: ?string*/{ return _preCheck('get web_uuid', function () { return activity_state.getWebUUID(); }); } + +/** + * Returns a promise which resolves when `web_uuid` becomes available + */ +function main_waitForWebUUID() /*: Promise*/{ + return _preCheck('get web_uuid', function () { + return activity_state.waitForWebUUID(); + }, { + schedule: false + }); +} function setReferrer(referrer /*: string*/) { if (!referrer || typeof referrer !== 'string') { logger.error('You must provide a string referrer'); @@ -9589,7 +6760,7 @@ function switchBackToOnlineMode() /*: void*/{ */ function stop() /*: void*/{ var done = disable(); - if (done && config.isInitialised()) { + if (done && sdk_config.isInitialised()) { _shutdown(); } } @@ -9613,52 +6784,51 @@ function gdprForgetMe() /*: void*/{ return; } done = gdpr_forget_device_disable(); - if (done && config.isInitialised()) { + if (done && sdk_config.isInitialised()) { _pause(); } } /** * Disable third party sharing + * + * @deprecated Use {@link trackThirdPartySharing} instead */ function disableThirdPartySharing() /*: void*/{ - _preCheck('disable third-party sharing', _handleDisableThirdPartySharing, { - schedule: true + main_trackThirdPartySharing({ + isEnabled: false }); } -function initSmartBanner(options /*: SmartBannerOptionsT*/) /*: void*/{ - if (_smartBanner) { - logger.error('Smart Banner already initialised'); - return; - } - _smartBanner = new SmartBanner(options); + +/** + * Track third party sharing + */ +function main_trackThirdPartySharing(adjustThirdPartySharing /*: ThirdPartySharingOptions*/) /*: void*/{ + var callback = function callback() { + return activity_state.waitForWebUUID() // ensure we have web_uuid to be sent with request + .then(function () { + return trackThirdPartySharing(adjustThirdPartySharing); + }); + }; + _preCheck('third-party sharing', callback, { + schedule: false, + optionalInit: true + }); } -function showSmartBanner() /*: void*/{ - if (!_smartBanner) { - logger.error('Smart Banner is not initialised yet'); - return; - } - _smartBanner.show(); + +/** @deprecated */ +function initSmartBanner() /*: void*/{ + logger.error('function `initSmartBanner` is deprecated'); } -function hideSmartBanner() /*: void*/{ - if (!_smartBanner) { - logger.error('Smart Banner is not initialised yet'); - return; - } - _smartBanner.hide(); + +/** @deprecated */ +function showSmartBanner() /*: void*/{ + logger.error('function `showSmartBanner` is deprecated'); } -/** - * Handle third party sharing disable - * - * @private - */ -function _handleDisableThirdPartySharing() /*: void*/{ - var done = optOut(); - if (!done) { - return; - } - sdk_third_party_sharing_disable(); +/** @deprecated */ +function hideSmartBanner() /*: void*/{ + logger.error('function `hideSmartBanner` is deprecated'); } /** @@ -9671,7 +6841,7 @@ function _handleGdprForgetMe() /*: void*/{ return; } gdpr_forget_device_finish(); - main_Promise.all([clear(), global_params_clear(), queue_clear()]).then(main_destroy); + Promise.all([clear(), global_params_clear(), queue_clear()]).then(main_destroy); } /** @@ -9680,7 +6850,7 @@ function _handleGdprForgetMe() /*: void*/{ * @private */ function _isInitialised() /*: boolean*/{ - return _isInitialising || config.isInitialised(); + return _isInitialising || sdk_config.isInitialised(); } /** @@ -9713,7 +6883,7 @@ function _shutdown(async) /*: void*/{ identity_destroy(); listeners_destroy(); storage.destroy(); - config.destroy(); + sdk_config.destroy(); } /** @@ -9740,29 +6910,26 @@ function main_continue(activityState /*: ActivityStateMapT*/) /*: Promise* logger.log("Adjust SDK is starting with web_uuid set to ".concat(activityState.uuid)); var isInstalled = activity_state.current.installed; gdpr_forget_device_check(); - if (!isInstalled) { - third_party_sharing_check(); - } var sdkStatus = disable_status(); var message = function message(rest) { return "Adjust SDK start has been interrupted ".concat(rest); }; if (sdkStatus === 'off') { _shutdown(); - return main_Promise.reject({ + return Promise.reject({ interrupted: true, message: message('due to complete async disable') }); } if (sdkStatus === 'paused') { _pause(); - return main_Promise.reject({ + return Promise.reject({ interrupted: true, message: message('due to partial async disable') }); } if (_isStarted) { - return main_Promise.reject({ + return Promise.reject({ interrupted: true, message: message('due to multiple synchronous start attempt') }); @@ -9775,7 +6942,6 @@ function main_continue(activityState /*: ActivityStateMapT*/) /*: Promise* _isStarted = true; if (isInstalled) { _handleSdkInstalled(); - third_party_sharing_check(); } }); } @@ -9833,14 +6999,13 @@ function _start(options /*: InitOptionsT*/) /*: void*/{ logger.log('Adjust SDK is disabled, can not start the sdk'); return; } - config.set(options); + sdk_config.set(options); register(); subscribe('sdk:installed', _handleSdkInstalled); subscribe('sdk:shutdown', function () { return _shutdown(true); }); subscribe('sdk:gdpr-forget-me', _handleGdprForgetMe); - subscribe('sdk:third-party-sharing-opt-out', third_party_sharing_finish); subscribe('attribution:check', function (e, result) { return check(result); }); @@ -9853,19 +7018,19 @@ function _internalTrackEvent(params /*: EventParamsT*/) { if (storage.getType() === STORAGE_TYPES.NO_STORAGE) { var reason = 'Adjust SDK can not track event, no storage available'; logger.log(reason); - return main_Promise.reject(reason); + return Promise.reject(reason); } if (disable_status() !== 'on') { var _reason = 'Adjust SDK is disabled, can not track event'; logger.log(_reason); - return main_Promise.reject(_reason); + return Promise.reject(_reason); } if (!_isInitialised()) { var _reason2 = 'Adjust SDK can not track event, sdk instance is not initialized'; logger.error(_reason2); - return main_Promise.reject(_reason2); + return Promise.reject(_reason2); } - return new main_Promise(function (resolve) { + return new Promise(function (resolve) { var _callback = function _callback(timestamp) { return resolve(event_event(params, timestamp)); }; @@ -9925,6 +7090,8 @@ var Adjust = { initSdk: initSdk, getAttribution: main_getAttribution, getWebUUID: main_getWebUUID, + waitForAttribution: main_waitForAttribution, + waitForWebUUID: main_waitForWebUUID, setReferrer: setReferrer, trackEvent: trackEvent, addGlobalCallbackParameters: addGlobalCallbackParameters, @@ -9939,6 +7106,8 @@ var Adjust = { restart: restart, gdprForgetMe: gdprForgetMe, disableThirdPartySharing: disableThirdPartySharing, + trackThirdPartySharing: main_trackThirdPartySharing, + ThirdPartySharing: ThirdPartySharing, initSmartBanner: initSmartBanner, showSmartBanner: showSmartBanner, hideSmartBanner: hideSmartBanner, @@ -9951,8 +7120,6 @@ var Adjust = { } }; /* harmony default export */ const main = (Adjust); -})(); - __webpack_exports__ = __webpack_exports__["default"]; /******/ return __webpack_exports__; /******/ })() diff --git a/dist/adjust-latest.min.js b/dist/adjust-latest.min.js index d8f5eea0..3be11e33 100644 --- a/dist/adjust-latest.min.js +++ b/dist/adjust-latest.min.js @@ -1,2 +1 @@ -/*! For license information please see adjust-latest.min.js.LICENSE.txt */ -!function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t():"function"==typeof define&&define.amd?define([],t):"object"==typeof exports?exports.Adjust=t():e.Adjust=t()}(self,(()=>(()=>{var e={841:(e,t,n)=>{"use strict";n.d(t,{Z:()=>f});var r=n(81),o=n.n(r),i=n(645),a=n.n(i),s=n(667),u=n.n(s),c=new URL(n(529),n.b),l=a()(o()),d=u()(c);l.push([e.id,".adjust-smart-banner__AEqYlWgPonspKfseFq2N{height:76px}@media(min-width: 428px){.adjust-smart-banner__AEqYlWgPonspKfseFq2N{height:0}}.adjust-smart-banner__NVk5vwju_4kdaKzGWJPq{position:fixed;left:0;right:0;z-index:10000000}.adjust-smart-banner__NVk5vwju_4kdaKzGWJPq.adjust-smart-banner__jOV7BvlxDT7ATfbLPh3j{top:0}.adjust-smart-banner__NVk5vwju_4kdaKzGWJPq.adjust-smart-banner__XmomYv1VVQYz0lEtn9Q2{bottom:0}.adjust-smart-banner__NVk5vwju_4kdaKzGWJPq .adjust-smart-banner__eXKzWnRDn4RWUiSSeVYK{margin:0 auto;max-width:428px;background:#fff}.adjust-smart-banner__NVk5vwju_4kdaKzGWJPq .adjust-smart-banner__eXKzWnRDn4RWUiSSeVYK .adjust-smart-banner__r3JnN_RNhpzArrmKQ8jI{display:flex;align-items:center;padding:10px 8px 10px 4px}.adjust-smart-banner__NVk5vwju_4kdaKzGWJPq .adjust-smart-banner__eXKzWnRDn4RWUiSSeVYK .adjust-smart-banner__r3JnN_RNhpzArrmKQ8jI .adjust-smart-banner__VFuxsD_KzqNSxQecFmao{width:32px;height:32px;border:none;background:url("+d+");background-repeat:no-repeat;background-position:center center;background-size:8px 8px,auto;cursor:pointer}.adjust-smart-banner__NVk5vwju_4kdaKzGWJPq .adjust-smart-banner__eXKzWnRDn4RWUiSSeVYK .adjust-smart-banner__r3JnN_RNhpzArrmKQ8jI .adjust-smart-banner__hqvH8Y5fwbegVLKnoYv_{width:56px;height:56px;overflow:hidden;background-color:#6e7492;border-radius:8px}.adjust-smart-banner__NVk5vwju_4kdaKzGWJPq .adjust-smart-banner__eXKzWnRDn4RWUiSSeVYK .adjust-smart-banner__r3JnN_RNhpzArrmKQ8jI .adjust-smart-banner__hqvH8Y5fwbegVLKnoYv_ .adjust-smart-banner__Ll9XMTDiX4Drgeydp0Oc{display:flex;align-items:center;justify-content:center;width:100%;height:100%;color:#353a52;font-weight:bold;font-size:23px;font-family:ArialMt,Arial,sans-serif;line-height:32px;background-color:#e0e2ec}.adjust-smart-banner__NVk5vwju_4kdaKzGWJPq .adjust-smart-banner__eXKzWnRDn4RWUiSSeVYK .adjust-smart-banner__r3JnN_RNhpzArrmKQ8jI .adjust-smart-banner__hqvH8Y5fwbegVLKnoYv_ .adjust-smart-banner__VYRfEif2Ph2_984rXQy8{width:100%}.adjust-smart-banner__NVk5vwju_4kdaKzGWJPq .adjust-smart-banner__eXKzWnRDn4RWUiSSeVYK .adjust-smart-banner__r3JnN_RNhpzArrmKQ8jI .adjust-smart-banner__I8xX0C5dUcR53pY0aEys{flex:1 1 0%;min-height:0;min-width:0;margin:0 12px}.adjust-smart-banner__NVk5vwju_4kdaKzGWJPq .adjust-smart-banner__eXKzWnRDn4RWUiSSeVYK .adjust-smart-banner__r3JnN_RNhpzArrmKQ8jI .adjust-smart-banner__JJLdp2l7YvnsUXudojWA{overflow:hidden;text-overflow:ellipsis}.adjust-smart-banner__NVk5vwju_4kdaKzGWJPq .adjust-smart-banner__eXKzWnRDn4RWUiSSeVYK .adjust-smart-banner__r3JnN_RNhpzArrmKQ8jI h4{margin:5px 0 8px;color:#353a52;font-family:Arial-BoldMT,ArialMt,Arial,sans-serif;font-size:12px;font-weight:bold;line-height:16px;white-space:nowrap}.adjust-smart-banner__NVk5vwju_4kdaKzGWJPq .adjust-smart-banner__eXKzWnRDn4RWUiSSeVYK .adjust-smart-banner__r3JnN_RNhpzArrmKQ8jI p{margin:8px 0 7px;color:#353a52;font-family:ArialMt,Arial,sans-serif;font-size:9px;line-height:11px;max-height:22px;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical}.adjust-smart-banner__NVk5vwju_4kdaKzGWJPq .adjust-smart-banner__eXKzWnRDn4RWUiSSeVYK .adjust-smart-banner__r3JnN_RNhpzArrmKQ8jI .adjust-smart-banner__risKVvV3T0vjKiSTR9l0{color:#6e7492;background:#f9fafc;border:1px solid #cdd0e0;border-radius:4px;border-color:#6e7492;box-shadow:inset 0px -1px 0px 0px #e0e2ec;padding:4px 6.5px;display:inline-block;vertical-align:middle;text-align:center;font-family:ArialMt,Arial,sans-serif;font-size:12px;font-weight:500;line-height:16px;cursor:pointer;text-decoration:none}",""]),l.locals={bannerContainer:"adjust-smart-banner__AEqYlWgPonspKfseFq2N",banner:"adjust-smart-banner__NVk5vwju_4kdaKzGWJPq",stickyToTop:"adjust-smart-banner__jOV7BvlxDT7ATfbLPh3j",stickyToBottom:"adjust-smart-banner__XmomYv1VVQYz0lEtn9Q2",bannerBody:"adjust-smart-banner__eXKzWnRDn4RWUiSSeVYK",content:"adjust-smart-banner__r3JnN_RNhpzArrmKQ8jI",dismiss:"adjust-smart-banner__VFuxsD_KzqNSxQecFmao",appIcon:"adjust-smart-banner__hqvH8Y5fwbegVLKnoYv_",placeholder:"adjust-smart-banner__Ll9XMTDiX4Drgeydp0Oc",image:"adjust-smart-banner__VYRfEif2Ph2_984rXQy8",textContainer:"adjust-smart-banner__I8xX0C5dUcR53pY0aEys",bannerText:"adjust-smart-banner__JJLdp2l7YvnsUXudojWA",action:"adjust-smart-banner__risKVvV3T0vjKiSTR9l0"};const f=l},645:e=>{"use strict";e.exports=function(e){var t=[];return t.toString=function(){return this.map((function(t){var n="",r=void 0!==t[5];return t[4]&&(n+="@supports (".concat(t[4],") {")),t[2]&&(n+="@media ".concat(t[2]," {")),r&&(n+="@layer".concat(t[5].length>0?" ".concat(t[5]):""," {")),n+=e(t),r&&(n+="}"),t[2]&&(n+="}"),t[4]&&(n+="}"),n})).join("")},t.i=function(e,n,r,o,i){"string"==typeof e&&(e=[[null,e,void 0]]);var a={};if(r)for(var s=0;s0?" ".concat(l[5]):""," {").concat(l[1],"}")),l[5]=i),n&&(l[2]?(l[1]="@media ".concat(l[2]," {").concat(l[1],"}"),l[2]=n):l[2]=n),o&&(l[4]?(l[1]="@supports (".concat(l[4],") {").concat(l[1],"}"),l[4]=o):l[4]="".concat(o)),t.push(l))}},t}},667:e=>{"use strict";e.exports=function(e,t){return t||(t={}),e?(e=String(e.__esModule?e.default:e),/^['"].*['"]$/.test(e)&&(e=e.slice(1,-1)),t.hash&&(e+=t.hash),/["'() \t\n]|(%20)/.test(e)||t.needQuotes?'"'.concat(e.replace(/"/g,'\\"').replace(/\n/g,"\\n"),'"'):e):e}},81:e=>{"use strict";e.exports=function(e){return e[1]}},702:function(e,t,n){e.exports=function(){"use strict";function e(e){var t=typeof e;return null!==e&&("object"===t||"function"===t)}function t(e){return"function"==typeof e}var r=Array.isArray?Array.isArray:function(e){return"[object Array]"===Object.prototype.toString.call(e)},o=0,i=void 0,a=void 0,s=function(e,t){k[o]=e,k[o+1]=t,2===(o+=2)&&(a?a(w):S())};function u(e){a=e}function c(e){s=e}var l="undefined"!=typeof window?window:void 0,d=l||{},f=d.MutationObserver||d.WebKitMutationObserver,p="undefined"==typeof self&&"undefined"!=typeof process&&"[object process]"==={}.toString.call(process),h="undefined"!=typeof Uint8ClampedArray&&"undefined"!=typeof importScripts&&"undefined"!=typeof MessageChannel;function m(){return function(){return process.nextTick(w)}}function v(){return void 0!==i?function(){i(w)}:b()}function g(){var e=0,t=new f(w),n=document.createTextNode("");return t.observe(n,{characterData:!0}),function(){n.data=e=++e%2}}function y(){var e=new MessageChannel;return e.port1.onmessage=w,function(){return e.port2.postMessage(0)}}function b(){var e=setTimeout;return function(){return e(w,1)}}var k=new Array(1e3);function w(){for(var e=0;e{"use strict";var t=[];function n(e){for(var n=-1,r=0;r{"use strict";var t={};e.exports=function(e,n){var r=function(e){if(void 0===t[e]){var n=document.querySelector(e);if(window.HTMLIFrameElement&&n instanceof window.HTMLIFrameElement)try{n=n.contentDocument.head}catch(e){n=null}t[e]=n}return t[e]}(e);if(!r)throw new Error("Couldn't find a style target. This probably means that the value for the 'insert' parameter is invalid.");r.appendChild(n)}},216:e=>{"use strict";e.exports=function(e){var t=document.createElement("style");return e.setAttributes(t,e.attributes),e.insert(t,e.options),t}},565:(e,t,n)=>{"use strict";e.exports=function(e){var t=n.nc;t&&e.setAttribute("nonce",t)}},795:e=>{"use strict";e.exports=function(e){var t=e.insertStyleElement(e);return{update:function(n){!function(e,t,n){var r="";n.supports&&(r+="@supports (".concat(n.supports,") {")),n.media&&(r+="@media ".concat(n.media," {"));var o=void 0!==n.layer;o&&(r+="@layer".concat(n.layer.length>0?" ".concat(n.layer):""," {")),r+=n.css,o&&(r+="}"),n.media&&(r+="}"),n.supports&&(r+="}");var i=n.sourceMap;i&&"undefined"!=typeof btoa&&(r+="\n/*# sourceMappingURL=data:application/json;base64,".concat(btoa(unescape(encodeURIComponent(JSON.stringify(i))))," */")),t.styleTagTransform(r,e,t.options)}(t,e,n)},remove:function(){!function(e){if(null===e.parentNode)return!1;e.parentNode.removeChild(e)}(t)}}}},589:e=>{"use strict";e.exports=function(e,t){if(t.styleSheet)t.styleSheet.cssText=e;else{for(;t.firstChild;)t.removeChild(t.firstChild);t.appendChild(document.createTextNode(e))}}},529:e=>{"use strict";e.exports="data:image/svg+xml;utf8, "}},t={};function n(r){var o=t[r];if(void 0!==o)return o.exports;var i=t[r]={id:r,exports:{}};return e[r].call(i.exports,i,i.exports,n),i.exports}n.m=e,n.n=e=>{var t=e&&e.__esModule?()=>e.default:()=>e;return n.d(t,{a:t}),t},n.d=(e,t)=>{for(var r in t)n.o(t,r)&&!n.o(e,r)&&Object.defineProperty(e,r,{enumerable:!0,get:t[r]})},n.g=function(){if("object"==typeof globalThis)return globalThis;try{return this||new Function("return this")()}catch(e){if("object"==typeof window)return window}}(),n.o=(e,t)=>Object.prototype.hasOwnProperty.call(e,t),n.b=document.baseURI||self.location.href,n.nc=void 0;var r={};return(()=>{"use strict";function e(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function t(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,r)}return n}function o(n){for(var r=1;r=0||(o[n]=e[n]);return o}(e,t);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);for(r=0;r=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(o[n]=e[n])}return o}function a(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,r=new Array(t);nIo});var l=1e3,d=36e5,f=24*d,p="general",h="gdpr",m={TRANSACTION_ERROR:"XHR transaction failed due to an error",SERVER_MALFORMED_RESPONSE:"Response from server is malformed",SERVER_INTERNAL_ERROR:"Internal error occurred on the server",SERVER_CANNOT_PROCESS:"Server was not able to process the request, probably due to error coming from the client",NO_CONNECTION:"No internet connectivity",SKIP:"Skipping slower attempt",MISSING_URL:"Url is not provided"},v="noStorage",g="indexedDB",y="localStorage",b={endpointName:"Default",app:"https://app.adjust.com",gdpr:"https://gdpr.adjust.com"},k={endpointName:"Indian",app:"https://app.adjust.net.in",gdpr:"https://gdpr.adjust.net.in"},w={endpointName:"Chinese",app:"https://app.adjust.world",gdpr:"https://gdpr.adjust.world"},_={endpointName:"EU",app:"https://app.eu.adjust.com",gdpr:"https://gdpr.eu.adjust.com"},S={endpointName:"TR",app:"https://app.tr.adjust.com",gdpr:"https://gdpr.tr.adjust.com"},j={endpointName:"US",app:"https://app.us.adjust.com",gdpr:"https://gdpr.us.adjust.com"};function P(e){return P="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e},P(e)}function x(e){return!Object.keys(e).length&&e.constructor===Object}function D(e){return"object"===P(e)&&null!==e&&!(e instanceof Array)}function N(e){try{return D(JSON.parse(e))}catch(e){return!1}}function I(e,t,n){function r(e){return Array.isArray(t)?t.every((function(t){return e[t]===n[t]})):e[t]===n}for(var o=0;o0&&void 0!==arguments[0]?arguments[0]:[];return t.reduce((function(t,n){return o(o({},t),{},e({},n.key,n.value))}),{})}function T(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:[],t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:[];return e.filter((function(e){return-1!==t.indexOf(e)}))}function R(e,t){return new RegExp("\\/".concat(t,"(\\/.*|\\?.*){0,1}$")).test(e)}function C(t,n){var r=u(n,2),i=r[0],a=r[1];return o(o({},t),{},e({},i,a))}function E(e){return Object.keys(e).map((function(t){return[t,e[t]]}))}function O(e){return Object.keys(e).map((function(t){return e[t]}))}function K(e){return D(e)?!x(e):!!e||0===e}function B(){try{var e=(new Date).toString(),t=window.localStorage;t.setItem(e,e);var n=t.getItem(e)===e;return t.removeItem(e),!(!n||!t)}catch(e){return!1}}const U={namespace:"adjust-sdk",version:"5.6.0",env:"production"};var q,L="error",M="warning",W="info",V="verbose",F=(e(q={},"none",-1),e(q,L,0),e(q,M,1),e(q,W,2),e(q,V,3),q),z={log:" ",info:" ",warn:" ",error:""},G={development:V,production:L,test:V},Y=X(),J="";function X(){return G[U.env]||L}function Q(e,t){var n;if(!(F[Y]2?s-2:0),c=2;c2&&void 0!==arguments[2]?arguments[2]:[];return n.map((function(n){return Ce(e,t,n)}))}function Oe(e,t,n){var r=xe[t][Be(e,Ae.right)],o=r.fields[r.keyPath],i=n instanceof Array?n.slice():[n],a=(me(o)?o.composite:[r.keyPath]).map((function(e,t){var n=r.fields[e];return Re(pe(n)?n.values:null,i[t])}));return 1===a.length?a[0]:a}function Ke(e){return xe.values[e]||e}function Be(e,t){return(xe.storeNames[t][e]||{}).name||e}function Ue(e,t){return{name:t.name,message:t.message.replace('"'.concat(e,'"'),Be(e,Ae.right))}}function qe(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}function Le(e,t){for(var n=0;n1&&void 0!==arguments[1]?arguments[1]:1,n=e+"",r=1;r<=t;r+=1)e0?"-":"+")+We(Math.floor(Math.abs(t)/60))+We(Math.abs(t)%60)}(t);return"".concat(n,"T").concat(r,"Z").concat(o)}function Fe(e,t){return isNaN(e)||isNaN(t)?0:Math.abs(t-e)}!function(e){e.LowerBound="lowerBound",e.UpperBound="upperBound"}(Ie||(Ie={})),function(e){e.right="right",e.left="left"}(Ae||(Ae={}));var ze={},Ge=!1,Ye=!1;function Je(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};ze=Ge?o({},e):{}}function Xe(){Ge&&(ze.lastInterval=function(){var e=ze.lastActive;if(e)return Math.round(Fe(e,Date.now())/l);return-1}(),ze.lastActive=Date.now())}function Qe(e){ze=o(o({},ze),e)}function He(){Ye=!0}function Ze(){var e=ze.lastActive;return Math.round(Fe(e,Date.now())/l)}function $e(){return(ze.timeSpent||0)+(Ye?Ze():0)}function et(){var e=Fe(ze.lastActive,Date.now())=0?ze.lastInterval:0,n={timeSpent:ze.timeSpent||0,sessionLength:ze.sessionLength||0,sessionCount:ze.sessionCount||1,lastInterval:t||0};return e&&R(e,"event")&&(n.eventCount=ze.eventCount),n},updateParams:function(e,t){if(Ge){var n={};n.timeSpent=$e(),n.sessionLength=et(),R(e,"session")&&(n.sessionCount=(ze.sessionCount||0)+1),R(e,"event")&&(n.eventCount=(ze.eventCount||0)+1),Qe(n),t||Xe()}},updateInstalled:function(){Ge&&(ze.installed||Qe({installed:!0}))},updateSessionOffset:tt,updateSessionLength:function(){Ge&&(Qe({sessionLength:et()}),Xe())},resetSessionOffset:function(){Ge&&Qe({timeSpent:0,sessionLength:0})},updateLastActive:Xe,destroy:function(){ze={},Ge=!1,Ye=!1},getAttribution:function(){return Ge?ze.attribution?ze.attribution:(Z.log("No attribution data yet"),null):null},getWebUUID:function(){return Ge?ze.uuid:null}};var rt={},ot=[];function it(e,t){var n="id"+Math.random().toString(36).substr(2,16),r={id:n,cb:t};return rt[e]||(rt[e]=[]),rt[e].push(r),n}function at(e,t){rt[e]&&rt[e].forEach((function(n){"function"==typeof n.cb&&ot.push(setTimeout((function(){return n.cb(e,t)})))}))}var st=function(){function t(){qe(this,t),e(this,"items",{})}return Me(t,[{key:"getItem",value:function(e){return Object.prototype.hasOwnProperty.call(this.items,e)?this.items[e]:null}},{key:"removeItem",value:function(e){delete this.items[e]}},{key:"setItem",value:function(e,t){this.items[e]=t}}]),t}();const ut=new(function(){function t(){var n=this;qe(this,t),e(this,"defaultName",U.namespace),e(this,"storageName",this.defaultName),e(this,"storeNames",xe.storeNames.left),e(this,"storesMap",void 0),e(this,"storage",void 0),this.storesMap={},B()?this.storage=window.localStorage:this.storage=new st;var r=this.read.bind(this),o=this.write.bind(this);O(this.storeNames).forEach((function(e){var t=e.name;Object.defineProperty(n.storesMap,t,{get:function(){return r(t)},set:function(e){o(t,e)}})})),Object.freeze(this.storesMap)}return Me(t,[{key:"read",value:function(e){var t=this.storage.getItem("".concat(this.storageName,".").concat(e)),n=t?JSON.parse(t):null;return e===de.Preferences&&n?Ce(de.Preferences,Ae.right,n):n}},{key:"write",value:function(e,t){t?this.storage.setItem("".concat(this.storageName,".").concat(e),JSON.stringify(t instanceof Array?t:Ce(de.Preferences,Ae.left,t))):this.storage.removeItem("".concat(this.storageName,".").concat(e))}},{key:"clear",value:function(){this.deleteData()}},{key:"deleteData",value:function(){var e=this,t=arguments.length>0&&void 0!==arguments[0]&&arguments[0];O(this.storeNames).forEach((function(n){!t&&n.permanent||e.storage.removeItem("".concat(e.storageName,".").concat(n.name))}))}},{key:"setCustomName",value:function(e){var t=this;if(e&&e.length){var n="".concat(U.namespace,"-").concat(e);O(this.storeNames).forEach((function(e){var r=e.name,o=t.storage.getItem("".concat(t.storageName,".").concat(r));o&&t.storage.setItem("".concat(n,".").concat(r),o)})),this.deleteData(!0),this.storageName=n}}},{key:"stores",get:function(){return this.storesMap}}]),t}());var ct=ge.preferences.name,lt=dt();function dt(){return lt||ft(),lt?o({},lt):null}function ft(){lt=ut.stores[ct]}function pt(){var e=dt();return e?e.sdkDisabled:null}function ht(e){var t=e?o({},e):null;ut.stores[ct]=o(o({},dt()),{},{sdkDisabled:t}),ft()}function mt(){var e=dt();return e?e.thirdPartySharingDisabled:null}function vt(){var e=ut.stores[ct]||{},t=(lt||{}).sdkDisabled||null;e.sdkDisabled&&!t&&at("sdk:shutdown"),ft()}function gt(){ut.stores[ct]||(ut.stores[ct]=o({},lt))}var yt,bt,kt="undefined"==typeof Promise?n(702).Promise:Promise;!function(e){e.add="add",e.put="put",e.get="get",e.list="list",e.clear="clear",e.delete="delete"}(yt||(yt={})),function(e){e.readonly="readonly",e.readwrite="readwrite"}(bt||(bt={}));var wt=function(){function t(){qe(this,t),e(this,"dbDefaultName",U.namespace),e(this,"dbName",this.dbDefaultName),e(this,"dbVersion",1),e(this,"idbFactory",void 0),e(this,"indexedDbConnection",null),e(this,"notSupportedError",{name:"IDBNotSupported",message:"IndexedDB is not supported"}),e(this,"databaseOpenError",{name:"CannotOpenDatabaseError",message:"Cannot open a database"}),e(this,"noConnectionError",{name:"NoDatabaseConnection",message:"Cannot open a transaction"});var n=t.getIndexedDB();if(!n)throw this.notSupportedError;this.idbFactory=n}return Me(t,[{key:"setCustomName",value:function(e){return e&&e.length>0?(this.dbName="".concat(U.namespace,"-").concat(e),this.migrateDb(this.dbDefaultName,this.dbName)):kt.resolve()}},{key:"openDatabase",value:function(e,n,r){var o=this;return t.isSupported().then((function(t){return t?new kt((function(t,i){var a=o.idbFactory.open(e,r);n&&(a.onupgradeneeded=function(e){return n(e,i)}),a.onsuccess=function(e){var n=e.target.result;n?t(n):i(o.databaseOpenError)},a.onerror=i})):kt.reject(o.notSupportedError)}))}},{key:"databaseExists",value:function(e){var t=this;return new kt((function(n){var r=!0;t.openDatabase(e,(function(){r=!1})).then((function(n){if(n.close(),!r)return t.deleteDatabaseByName(e)})).then((function(){return n(r)}))}))}},{key:"cloneData",value:function(e,t){var n=this;return O(xe.storeNames.left).map((function(e){return e.name})).filter((function(e){return"p"!==e})).map((function(r){return function(){return o=r,i=n.indexedDbConnection,n.indexedDbConnection=e,n.getAll(o).then((function(e){if(n.indexedDbConnection=t,!(e.length<1))return n.addBulk(o,e,!0)})).then((function(){n.indexedDbConnection=i}));var o,i}})).reduce((function(e,t){return e.then(t)}),kt.resolve())}},{key:"migrateDb",value:function(e,t){var n=this;return this.databaseExists(e).then((function(r){return r?kt.all([n.openDatabase(e,n.handleUpgradeNeeded,n.dbVersion),n.openDatabase(t,n.handleUpgradeNeeded,n.dbVersion)]).then((function(t){var r=u(t,2),o=r[0],i=r[1];return n.cloneData(o,i).then((function(){return n.indexedDbConnection=i,o.close(),n.deleteDatabaseByName(e)}))})).then((function(){return Z.info("Database migration finished")})):n.openDatabase(t,n.handleUpgradeNeeded,n.dbVersion).then((function(e){n.indexedDbConnection=e}))}))}},{key:"handleUpgradeNeeded",value:function(e,t){var n=e.target.result;e.target.transaction.onerror=t,e.target.transaction.onabort=t;var r=xe.storeNames.left,o=nt.current||{},i=o&&!x(o);E(r).filter((function(e){return!u(e,2)[1].permanent})).forEach((function(e){var t=u(e,2),r=t[0],a=t[1].name,s=xe.right[r],c=n.createObjectStore(a,{keyPath:s.keyPath,autoIncrement:s.autoIncrement||!1});if(s.index&&c.createIndex("".concat(s.index,"Index"),s.index),a===le.ActivityState&&i)return c.add(Ce(r,Ae.left,o)),void Z.info("Activity state has been recovered");var l=ut.stores[a];l&&(l.forEach((function(e){return c.add(e)})),Z.info("Migration from localStorage done for ".concat(r," store")))})),gt(),ut.clear()}},{key:"open",value:function(){var e=this;return this.indexedDbConnection?kt.resolve({success:!0}):this.openDatabase(this.dbName,this.handleUpgradeNeeded,this.dbVersion).then((function(t){return e.indexedDbConnection=t,e.indexedDbConnection.onclose=function(){return e.destroy},{success:!0}}))}},{key:"getTransactionStore",value:function(e,t,n){var r,o=e.storeName,i=e.mode,a=n.transaction([o],i),s=a.objectStore(o),u=xe.right[Be(o,Ae.right)];return u.index&&(r=s.index("".concat(u.index,"Index"))),a.onerror=t,a.onabort=t,{transaction:a,store:s,index:r,options:u}}},{key:"overrideError",value:function(e,t){var n=t.target.error;return e({name:n.name,message:n.message})}},{key:"getCompositeKeys",value:function(e){var t=e.fields[e.keyPath];return me(t)?t.composite:null}},{key:"targetIsObject",value:function(e){return D(e)}},{key:"prepareTarget",value:function(t,n,r){if(r===yt.clear||!n)return null;var i=this.getCompositeKeys(t);return-1!==[yt.add,yt.put].indexOf(r)?this.targetIsObject(n)?i?o(e({},t.keyPath,i.map((function(e){return n[e]})).join("")),n):n:null:n instanceof Array?n.join(""):n}},{key:"prepareResult",value:function(e,t){var n=this.getCompositeKeys(e);return n&&this.targetIsObject(t)?n.map((function(e){return t[e]})):null}},{key:"initRequest",value:function(e){var t=this,n=e.storeName,r=e.target,o=void 0===r?null:r,i=e.action,a=e.mode,s=void 0===a?bt.readonly:a;return this.open().then((function(){return new kt((function(e,r){if(t.indexedDbConnection){var a=t.getTransactionStore({storeName:n,mode:s},r,t.indexedDbConnection),u=a.store,c=a.options,l=u[i](t.prepareTarget(c,o,i)),d=t.prepareResult(c,o);l.onsuccess=function(){i!==yt.get||l.result?e(d||l.result||o):r({name:"NotRecordFoundError",message:'Requested record not found in "'.concat(n,'" store')})},l.onerror=function(e){return t.overrideError(r,e)}}else r(t.noConnectionError)}))}))}},{key:"initBulkRequest",value:function(e){var t=this,n=e.storeName,r=e.target,o=e.action,i=e.mode,a=void 0===i?bt.readwrite:i;return!r||r&&!r.length?kt.reject({name:"NoTargetDefined",message:"No array provided to perform ".concat(o,' bulk operation into "').concat(n,'" store')}):this.open().then((function(){return new kt((function(e,i){if(t.indexedDbConnection){var s=t.getTransactionStore({storeName:n,mode:a},i,t.indexedDbConnection),u=s.transaction,c=s.store,l=s.options,d=new Array,f=r[0];u.oncomplete=function(){return e(d)};!function e(n){n.onerror=function(e){return t.overrideError(i,e)},n.onsuccess=function(){d.push(t.prepareResult(l,f)||n.result),f=r[d.length],d.length1&&void 0!==arguments[1]&&arguments[1];return this.openCursor({storeName:e,action:yt.list,firstOnly:t})}},{key:"getFirst",value:function(e){return this.getAll(e,!0).then((function(e){return e.length?e[0]:void 0}))}},{key:"getItem",value:function(e,t){return this.initRequest({storeName:e,target:t,action:yt.get})}},{key:"filterBy",value:function(e,t){var n=IDBKeyRange.only(t);return this.openCursor({storeName:e,action:yt.list,range:n})}},{key:"addItem",value:function(e,t){return this.initRequest({storeName:e,target:t,action:yt.add,mode:bt.readwrite})}},{key:"addBulk",value:function(e,t,n){return this.initBulkRequest({storeName:e,target:t,action:n?yt.put:yt.add,mode:bt.readwrite})}},{key:"updateItem",value:function(e,t){return this.initRequest({storeName:e,target:t,action:yt.put,mode:bt.readwrite})}},{key:"deleteItem",value:function(e,t){return this.initRequest({storeName:e,target:t,action:yt.delete,mode:bt.readwrite})}},{key:"deleteBulk",value:function(e,t,n){var r=n?IDBKeyRange[n](t):IDBKeyRange.only(t);return this.openCursor({storeName:e,action:yt.delete,range:r,mode:bt.readwrite})}},{key:"trimItems",value:function(e,t){var n=this,r=xe.right[Be(e,Ae.right)];return this.getAll(e).then((function(e){return e.length?e[t-1]:null})).then((function(t){return t?n.deleteBulk(e,t[r.keyPath],Ie.UpperBound):[]}))}},{key:"count",value:function(e){var t=this;return this.open().then((function(){return new kt((function(n,r){if(t.indexedDbConnection){var o=t.getTransactionStore({storeName:e,mode:bt.readonly},r,t.indexedDbConnection).store.count();o.onsuccess=function(){return n(o.result)},o.onerror=function(e){return t.overrideError(r,e)}}else r(t.noConnectionError)}))}))}},{key:"clear",value:function(e){return this.initRequest({storeName:e,action:yt.clear,mode:bt.readwrite})}},{key:"destroy",value:function(){this.indexedDbConnection&&this.indexedDbConnection.close(),this.indexedDbConnection=null}},{key:"deleteDatabase",value:function(){return this.destroy(),this.deleteDatabaseByName(this.dbName)}}],[{key:"tryOpen",value:function(e){return new kt((function(n){try{var r=e.open(t.dbValidationName);r.onsuccess=function(){r.result.close(),e.deleteDatabase(t.dbValidationName),n(!0)},r.onerror=function(){return n(!1)}}catch(e){n(!1)}}))}},{key:"isSupported",value:function(){if(t.isSupportedPromise)return t.isSupportedPromise;var e="IndexedDB is not supported in this browser";return t.isSupportedPromise=new kt((function(n){var r=t.getIndexedDB(),o=!!navigator.platform&&/iPad|iPhone|iPod/.test(navigator.platform);!r||o?(Z.warn(e),n(!1)):n(t.tryOpen(r).then((function(t){return t||Z.warn(e),t})))})),t.isSupportedPromise}},{key:"getIndexedDB",value:function(){return window.indexedDB||window.mozIndexedDB||window.webkitIndexedDB||window.msIndexedDB}}]),t}();e(wt,"dbValidationName","validate-db-openable"),e(wt,"isSupportedPromise",null);var _t="undefined"==typeof Promise?n(702).Promise:Promise,St=function(){function t(){qe(this,t)}return Me(t,[{key:"open",value:function(){return t.isSupported().then((function(e){if(!e)return{status:"error",error:{name:"LSNotSupported",message:"LocalStorage is not supported"}};var t=xe.storeNames.left,n=nt.current||{},r=n&&!x(n);return E(t).filter((function(e){return!u(e,2)[1].permanent})).forEach((function(e){var t=u(e,2),o=t[0],i=t[1].name;i!==le.ActivityState||ut.stores[i]?ut.stores[i]||(ut.stores[i]=[]):ut.stores[i]=r?[Ce(o,Ae.left,n)]:[]})),gt(),{status:"success"}}))}},{key:"getCompositeKeys",value:function(e){var t=e.fields[e.keyPath];return me(t)?t.composite:null}},{key:"getKeys",value:function(e){var t=Be(e,Ae.right),n=xe.right[t];return this.getCompositeKeys(n)||[n.keyPath]}},{key:"nextIndex",value:function(e){return"number"==typeof e?e+1:void 0}},{key:"initRequest",value:function(e,t){var n=this,r=e.storeName,i=e.id,a=e.item,s=xe.right[Be(r,Ae.right)];return this.open().then((function(e){return"error"===e.status?_t.reject(e.error):new _t((function(e,u){var c,l=ut.stores[r],d=n.getKeys(r),f=(l[l.length-1]||{})[s.keyPath]||0;if(i){var p=Array.isArray(i)?i.slice():[i];c=d.map((function(e,t){return[e,p[t]]})).reduce(C,{})}else c=o({},a);var h=c?I(l,d,c):0;return t(e,u,{keys:d,items:l,index:h,options:s,lastId:f})}))}))}},{key:"sort",value:function(e,t,n){var r=c(e),o=t.slice().reverse();return r.sort((function(e,t){return o.reduce((function(r,o){return r||function(e,t,r){var o=n?n===e[r]:e[r]e[r]:e[r]>t[r];return o?-1:i?1:0}(e,t,o)}),0)}))}},{key:"prepareTarget",value:function(t,n,r){var i=this.getCompositeKeys(t);return i?o(e({},t.keyPath,i.map((function(e){return n[e]})).join("")),n):t.autoIncrement&&r?o(e({},t.keyPath,r),n):o({},n)}},{key:"prepareResult",value:function(e,t){var n=this.getCompositeKeys(e);return n?n.map((function(e){return t[e]})).filter((function(e){return!Te(e)})):t[e.keyPath]}},{key:"getAll",value:function(e){var t=this,n=arguments.length>1&&void 0!==arguments[1]&&arguments[1];return this.open().then((function(r){return"error"===r.status?_t.reject(r.error):new _t((function(r,o){var i=ut.stores[e];i instanceof Array?r(n?[i[0]]:t.sort(i,t.getKeys(e))):o({name:"NotFoundError",message:"No objectStore named ".concat(e," in this database")})}))}))}},{key:"getFirst",value:function(e){return this.getAll(e,!0).then((function(e){return e.length?e[0]:void 0}))}},{key:"getItem",value:function(e,t){var n=this;return this.initRequest({storeName:e,id:t},(function(t,r,o){var i=o.items,a=o.index,s=o.options;-1===a?r({name:"NotRecordFoundError",message:'Requested record not found in "'.concat(e,'" store')}):t(n.prepareTarget(s,i[a]))}))}},{key:"filterBy",value:function(e,t){return this.getAll(e).then((function(n){return n.filter((function(n){var r=xe.right[Be(e,Ae.right)];return(r.index&&n[r.index])===t}))}))}},{key:"addItem",value:function(e,t){var n=this;return this.initRequest({storeName:e,item:t},(function(r,o,i){var a=i.items,s=i.index,u=i.options,c=i.lastId;-1!==s?o({name:"ConstraintError",message:'Constraint was not satisfied, trying to add existing item into "'.concat(e,'" store')}):(a.push(n.prepareTarget(u,t,n.nextIndex(c))),ut.stores[e]=a,r(n.prepareResult(u,t)))}))}},{key:"addBulk",value:function(e,t,n){var r=this;return this.initRequest({storeName:e},(function(o,i,a){var s=a.keys,u=a.items,l=a.options,d=a.lastId;if(!t||t&&!t.length)i({name:"NoTargetDefined",message:'No array provided to perform add bulk operation into "'.concat(e,'" store')});else{var f=d,p=t.map((function(e){return r.prepareTarget(l,e,f=r.nextIndex(f))})),h=p.filter((function(e){return-1!==I(u,s,e)})).map((function(e){return e[l.keyPath]})),m=n?u.filter((function(e){return-1===h.indexOf(e[l.keyPath])})):c(u);if(h.length&&!n)i({name:"ConstraintError",message:'Constraint was not satisfied, trying to add existing items into "'.concat(e,'" store')});else ut.stores[e]=r.sort([].concat(c(m),c(p)),s),o(t.map((function(e){return r.prepareResult(l,e)})))}}))}},{key:"updateItem",value:function(e,t){var n=this;return this.initRequest({storeName:e,item:t},(function(r,o,i){var a=i.items,s=i.index,u=i.options,c=i.lastId,l=-1===s?n.nextIndex(c):void 0,d=n.prepareTarget(u,t,l);-1===s?a.push(d):a.splice(s,1,d),ut.stores[e]=a,r(n.prepareResult(u,t))}))}},{key:"deleteItem",value:function(e,t){return this.initRequest({storeName:e,id:t},(function(n,r,o){var i=o.items,a=o.index;-1!==a&&(i.splice(a,1),ut.stores[e]=i),n(t)}))}},{key:"findMax",value:function(e,t,n){if(!e.length)return-1;for(var r={index:-1,value:"string"==typeof n?"":0},o=0;o=r.value&&(r={value:e[o][t],index:o})}return r.index}},{key:"deleteBulk",value:function(e,t,n){var r=this;return this.getAll(e).then((function(o){var i=r.getKeys(e),a=xe.right[Be(e,Ae.right)].index||i[0],s=n?null:t,u=r.sort(o,i,s),c=r.findMax(u,a,t);if(-1===c)return[];var l=n===Ie.LowerBound?c:0,d=n&&n!==Ie.UpperBound?u.length:c+1,f=u.splice(l,d).map((function(e){return 1===i.length?e[a]:i.map((function(t){return e[t]}))}));return ut.stores[e]=u,f}))}},{key:"trimItems",value:function(e,t){var n=this,r=Be(e,Ae.right),o=xe.right[r];return this.getAll(e).then((function(e){return e.length?e[t-1]:null})).then((function(t){return t?n.deleteBulk(e,t[o.keyPath],Ie.UpperBound):[]}))}},{key:"count",value:function(e){return this.open().then((function(t){if("error"===t.status)return _t.reject(t.error);var n=ut.stores[e];return _t.resolve(n instanceof Array?n.length:1)}))}},{key:"clear",value:function(e){return this.open().then((function(t){return"error"===t.status?_t.reject(t.error):new _t((function(t){ut.stores[e]=[],t()}))}))}},{key:"destroy",value:function(){}},{key:"deleteDatabase",value:function(){}}],[{key:"isSupported",value:function(){return t.isSupportedPromise||(t.isSupportedPromise=new _t((function(e){var t=B();t||Z.warn("LocalStorage is not supported in this browser"),e(t)}))),t.isSupportedPromise}}]),t}();e(St,"isSupportedPromise",null);var jt,Pt="undefined"==typeof Promise?n(702).Promise:Promise;!function(e){e[e.noStorage=v]="noStorage",e[e.indexedDB=g]="indexedDB",e[e.localStorage=y]="localStorage"}(jt||(jt={}));var xt,Dt={getAll:function(e,t,n){return e.getAll(t,n).then((function(e){return Ee(t,Ae.right,e)}))},getFirst:function(e,t){return e.getFirst(t).then((function(e){return Ce(t,Ae.right,e)}))},getItem:function(e,t,n){return e.getItem(t,Oe(t,Ae.left,n)).then((function(e){return Ce(t,Ae.right,e)})).catch((function(e){return Pt.reject(Ue(t,e))}))},filterBy:function(e,t,n){return e.filterBy(t,Ke(n)).then((function(e){return Ee(t,Ae.right,e)}))},addItem:function(e,t,n){var r=Ce(t,Ae.left,n);return e.addItem(t,r).then((function(e){return Oe(t,Ae.right,e)})).catch((function(e){return Pt.reject(Ue(t,e))}))},addBulk:function(e,t,n,r){var o=Ee(t,Ae.left,n);return e.addBulk(t,o,r).then((function(e){return e.map((function(e){return Oe(t,Ae.right,e)}))})).catch((function(e){return Pt.reject(Ue(t,e))}))},updateItem:function(e,t,n){var r=Ce(t,Ae.left,n);return e.updateItem(t,r).then((function(e){return Oe(t,Ae.right,e)}))},deleteItem:function(e,t,n){return e.deleteItem(t,Oe(t,Ae.left,n)).then((function(e){return Oe(t,Ae.right,e)}))},deleteBulk:function(e,t,n,r){return e.deleteBulk(t,Ke(n),r).then((function(e){return e.map((function(e){return Oe(t,Ae.right,e)}))}))},trimItems:function(e,t,n){return e.trimItems(t,n)},count:function(e,t){return e.count(t)},clear:function(e,t){return e.clear(t)},destroy:function(e){return e.destroy()},deleteDatabase:function(e){return e.deleteDatabase()}};var Nt=null;function It(e){var t=null;return null!==Nt?Nt:Nt=Pt.all([wt.isSupported(),St.isSupported()]).then((function(n){var r=u(n,2),o=r[0],i=r[1];if(ut.setCustomName(e),o){xt=jt.indexedDB;var a=new wt;return a.setCustomName(e).then((function(){return t=a}))}return i?(xt=jt.localStorage,t=new St,Pt.resolve(t)):(Z.error("There is no storage available, app will run with minimum set of features"),xt=jt.noStorage,t=null,Pt.resolve(t))})).then((function(){return{type:xt,storage:t}}))}const At=o({init:It,getType:function(){return xt}},(Tt=E(Dt).map((function(e){var t=u(e,2),n=t[0],r=t[1];return[n,function(e){for(var t=arguments.length,n=new Array(t>1?t-1:0),o=1;o2&&void 0!==arguments[2]&&arguments[2];return{status:"error",action:n?"CONTINUE":"RETRY",response:N(e.responseText)?JSON.parse(e.responseText):e.responseText,message:m[t],code:t}}function Ot(e,t){var n=e.slice(0,e.length-t.length-1).split("").reduce((function(e){return e.concat(" ")}),"");return"".concat(t).concat(n,":")}function Kt(e,t){var n="REQUEST PARAMETERS:",r=function(e){return e.replace(/([A-Z])/g,(function(e){return"_".concat(e.toLowerCase())}))},i=E(o(o(o({},ae.getBaseParams()),t),e)).map((function(e){var t=u(e,2),n=t[0],o=t[1];return[r(n),o]}));return Z.log(n),i.filter((function(e){return K(u(e,2)[1])})).map((function(e){var t=u(e,2),r=t[0],o=t[1];return Z.log(Ot(n,r),o),function(e){var t=u(e,2),n=t[0],r=t[1],o=encodeURIComponent(n),i=r;return"string"==typeof r&&(i=encodeURIComponent(r)),D(r)&&(i=encodeURIComponent(JSON.stringify(r)||"")),[o,i].join("=")}([r,o])})).join("&")}function Bt(e,t,n){var r=n.xhr,o=n.url;if(4===r.readyState){var i=r.status>=200&&r.status<300,a=N(r.responseText);if(0!==r.status)return a?t(i?function(e,t){var n=JSON.parse(e.responseText),r={status:"success",adid:n.adid,timestamp:n.timestamp,ask_in:n.ask_in,retry_in:n.retry_in,continue_in:n.continue_in,tracking_state:n.tracking_state,attribution:void 0,message:void 0};return R(t,"attribution")&&(r.attribution=n.attribution,r.message=n.message),E(r).filter((function(e){return!!u(e,2)[1]})).reduce(C,{})}(r,o):Et(r,"SERVER_CANNOT_PROCESS",!0)):e(Et(r,i?"SERVER_MALFORMED_RESPONSE":"SERVER_INTERNAL_ERROR"));e(Et(r,"NO_CONNECTION"))}}function Ut(e,t){var n=e.endpoint,r=e.url,o=e.method,i=void 0===o?"GET":o,a=e.params,s=function(e,t){var n=e.endpoint,r=e.url,o=e.method,i=Kt(e.params,t);return{fullUrl:n+r+("GET"===o?"?".concat(i):""),encodedParams:i}}({endpoint:n,url:r,method:i,params:void 0===a?{}:a},t),c=s.fullUrl,l=s.encodedParams;return new Ct((function(e,t){var n=new XMLHttpRequest;n.open(i,c,!0),function(e,t){var n="REQUEST HEADERS:",r=[["Client-SDK","js".concat(U.version)],["Content-Type","POST"===t?"application/x-www-form-urlencoded":"application/json"]];Z.log(n),r.forEach((function(t){var r=u(t,2),o=r[0],i=r[1];e.setRequestHeader(o,i),Z.log(Ot(n,o),i)}))}(n,i),n.onreadystatechange=function(){return Bt(t,e,{xhr:n,url:r})},n.onerror=function(){return t(Et(n,"TRANSACTION_ERROR"))},n.send("GET"===i?void 0:l)}))}function qt(e,t){return"success"===e.status?function(e,t){var n=R(t,"gdpr_forget_device"),r=R(t,"attribution"),o=R(t,"session"),i=R(t,"disable_third_party_sharing"),a="opted_out"===e.tracking_state;if(!n&&a)return at("sdk:gdpr-forget-me"),e;r||n||a||!e.ask_in||at("attribution:check",e);o&&at("session:finished",e);if(i)return at("sdk:third-party-sharing-opt-out"),e;return e}(e,t):e}function Lt(e){return Rt().then((function(t){return Ut(e,t)})).then((function(t){return qt(t,e.url)}))}var Mt={long:{delay:12e4,maxDelay:f,minRange:.5,maxRange:1},short:{delay:200,maxDelay:d,minRange:.5,maxRange:1},test:{delay:100,maxDelay:300}};function Wt(e,t){var n,r,o=Mt[t=t||"long"],i=o.delay*Math.pow(2,e-1);return i=Math.min(i,o.maxDelay),o.minRange&&o.maxRange&&(i*=(n=o.minRange,r=o.maxRange,Math.random()*(r-n)+n)),Math.round(i)}var Vt,Ft,zt,Gt=navigator.onLine;function Yt(){Gt=!0}function Jt(){Gt=!1}function Xt(e,t,n){e.addEventListener&&e.addEventListener(t,n,!1)}function Qt(e,t,n){e.removeEventListener&&e.removeEventListener(t,n,!1)}function Ht(){return Gt}function Zt(e,t){Z.warn("Both ".concat(e," and ").concat(t," are set in config, ").concat(t," will be ignored"))}!function(e){e.Default="default",e.India="india",e.China="china"}(Ft||(Ft={})),function(e){e.EU="EU",e.TR="TR",e.US="US"}(zt||(zt={}));var $t=(e(Vt={},Ft.Default,b),e(Vt,Ft.India,k),e(Vt,Ft.China,w),e(Vt,zt.EU,_),e(Vt,zt.TR,S),e(Vt,zt.US,j),Vt);function en(e){var t,n,r,o,i=(t=ae.getCustomConfig(),n=t.customUrl,r=t.urlStrategy,o=t.dataResidency,n?((o||r)&&Zt("customUrl",o?"dataResidency":"urlStrategy"),{app:n,gdpr:n}):(o&&r&&Zt("dataResidency","urlStrategy"),o?[o]:r===Ft.India?[Ft.India,Ft.Default]:r===Ft.China?[Ft.China,Ft.Default]:[Ft.Default,Ft.India,Ft.China]));return Array.isArray(i)?i.map((function(t){return e[t]||null})).filter((function(e){return!!e})):[i]}function tn(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:$t,t=en(e),n=0;return{next:function(){return n0&&void 0!==arguments[0]?arguments[0]:{},r=n.url,i=n.method,a=void 0===i?"GET":i,s=n.params,c=void 0===s?{}:s,l=n.continueCb,d=n.strategy,f=n.wait,p={url:r,method:a,params:c,continueCb:l},h=r,v=a,g=o({},c),y=l,b=d,k=function(){e.reset(),t=e.next()},w=function(e,t){return e["/gdpr_forget_device"===t?"gdpr":"app"]},_=null,S={request:0,connection:0},j=D(f),P=null;function D(e){return(e=e||rn)>on?on:e}function N(e){var t=e.url,n=e.method,r=e.params,i=e.continueCb;t&&(h=t),n&&(v=n),x(r)||(g=o({},r)),g=o({createdAt:Ve()},g),"function"==typeof i&&(y=i)}function I(e){if(!P)return!1;if(_){var t=j-(Date.now()-P);if(e&&t0&&void 0!==arguments[0]?arguments[0]:{},t=e.url,n=e.method,r=e.params,o=void 0===r?{}:r,i=e.continueCb,a=e.wait;return N({url:t,method:n,params:o,continueCb:i}),A({wait:a})}function W(){return!!_}function V(){_&&clearTimeout(_),_=null}function F(){var e=!!P;V(),P=null,e&&(j=rn,S.request=0,S.connection=0,Z.log("Previous ".concat(h||"unknown"," request attempt canceled")),O())}return{send:M,isRunning:W,clear:F}};var un=function(e){return e===h?"GDPR disable":"disable"},cn=function(e){return{start:{inProgress:"Adjust SDK ".concat(un(e)," process has already started"),done:"Adjust SDK ".concat(un(e)," process is now started")},finish:{inProgress:"Adjust SDK ".concat(un(e)," process has already finished"),done:"Adjust SDK ".concat(un(e)," process is now finished")}}};function ln(e,t){var n=e.reason,r=e.pending,o=pt()||{},i="start"===t&&o.pending?"start":"finish",a="start"===t&&o.reason,s="finish"===t&&o.reason&&!o.pending;return a||s?(Z.log(cn(o.reason)[i].inProgress),!1):(Z.log(cn(n)[i].done),ht({reason:n||p,pending:r}),!0)}function dn(e){var t=arguments.length>1&&void 0!==arguments[1]&&arguments[1];return ln({reason:e,pending:t||!1},"start")}function fn(){var e=pt()||{};return e.reason===p||e.reason===h&&!e.pending?"off":e.reason===h&&e.pending?"paused":"on"}var pn="undefined"==typeof Promise?n(702).Promise:Promise,hn="activityState",mn=!1;function vn(e){return e?"unknown"===e.uuid?(dn({reason:h}),nt.destroy(),{exists:!0,stored:null}):(nt.init(e),{exists:!0,stored:e}):{exists:!1}}function gn(){return"off"!==fn()&&nt.isStarted()}function yn(){if(!gn())return pn.resolve(null);var e=o(o({},nt.current),{},{lastActive:Date.now()});return At.updateItem(hn,e).then((function(){return nt.current=e}))}var bn="undefined"==typeof Promise?n(702).Promise:Promise,kn=sn({strategy:"long",continueCb:function(e,t){var n=e&&e.continue_in||null;return Sn.pause=n?{timestamp:Date.now(),wait:n}:null,At.getFirst(_n).then((function(e){return e?At.deleteItem(_n,e.timestamp):null})).then((function(){return t(),Sn.running=!1,In({wait:n})}))}}),wn=!1,_n="queue",Sn={running:!1,timestamp:null,pause:null};function jn(){var e=Date.now();return Sn.timestamp&&e<=Sn.timestamp&&(e=Sn.timestamp+1),Sn.timestamp=e,e}function Pn(e){return R(e,"session")&&nt.resetSessionOffset(),nt.updateLastActive(),yn()}function xn(e){var t=e.url,n=e.method,r=e.params,i=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{},a=i.auto,s=i.timestamp;nt.updateParams(t,a);var c=E(r||{}).filter((function(e){return K(u(e,2)[1])})).reduce(C,{}),l={timestamp:jn(),url:t,method:n,params:o(o({},nt.getParams(t)),c)};return s&&(l.createdAt=s),At.addItem(_n,l).then((function(){return Pn(t)})).then((function(){return Sn.running?{}:In()}))}function Dn(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{},t=e.timestamp,n=e.createdAt,r=e.url,i=e.method,a=e.params,s=arguments.length>1?arguments[1]:void 0,u=nt.current||{},c="/session"===r&&!u.installed,l=!r&&!i&&!a;return wn&&!c||l?(Sn.running=!1,bn.resolve({})):kn.send({url:r,method:i,params:o(o({},a),{},{createdAt:Ve(n||t)}),wait:s||Nn()})}function Nn(){var e=Sn.pause||{},t=e.timestamp,n=e.wait,r=Date.now()-(t||0);return r0&&void 0!==arguments[0]?arguments[0]:{},t=e.cleanUp,n=e.wait;if(Sn.running)return bn.resolve({});Sn.running=!0;var r=bn.resolve({});return t&&(r=r.then(Tn)),r.then((function(){return At.getFirst(_n)})).then((function(e){return Dn(e,n)}))}function An(e){if(void 0!==e)if(e!==wn){var t=wn;wn=e,!e&&t&&In(),Z.info("The app is now in ".concat(e?"offline":"online"," mode"))}else Z.error("The app is already in ".concat(e?"offline":"online"," mode"));else Z.error("State not provided, true or false has to be defined")}function Tn(){var e=Date.now()-ae.requestValidityWindow;return At.deleteBulk(_n,e,"upperBound")}var Rn="undefined"==typeof Promise?n(702).Promise:Promise,Cn="globalParams",En="No type provided",On="Global parameter type not provided, `callback` or `partner` types are available";function Kn(e){return(e||[]).map((function(e){return{key:e.key,value:e.value}}))}function Bn(){return Rn.all([At.filterBy(Cn,"callback"),At.filterBy(Cn,"partner")]).then((function(e){var t=u(e,2),n=t[0],r=t[1];return{callbackParams:Kn(n),partnerParams:Kn(r)}}))}function Un(e,t){if(void 0===t)return Z.error(On),Rn.reject({message:En});var n=A(e),r=Object.keys(n).map((function(e){return{key:e,value:n[e],type:t}}));return Rn.all([At.filterBy(Cn,t),At.addBulk(Cn,r,!0)]).then((function(e){var n=u(e,2),o=n[0],i=n[1],a=T(o.map((function(e){return e.key})),i.map((function(e){return e[0]})));return Z.log("Following ".concat(t," parameters have been saved: ").concat(r.map((function(e){return"".concat(e.key,":").concat(e.value)})).join(", "))),a.length&&Z.log("Keys: ".concat(a.join(", ")," already existed so their values have been updated")),i}))}function qn(e,t){return void 0===t?(Z.error(On),Rn.reject({message:En})):At.deleteItem(Cn,[e,t]).then((function(n){return Z.log("".concat(e," ").concat(t," parameter has been deleted")),n}))}function Ln(e){return void 0===e?(Z.error(On),Rn.reject({message:En})):At.deleteBulk(Cn,e).then((function(t){return Z.log("All ".concat(e," parameters have been deleted")),t}))}var Mn,Wn,Vn,Fn="undefined"==typeof Promise?n(702).Promise:Promise,zn=!1,Gn=document;function Yn(){return Vn=function(){var e=document;if(void 0!==e.hidden)return{hidden:"hidden",visibilityChange:"visibilitychange"};for(var t=E({mozHidden:"mozvisibilitychange",msHidden:"msvisibilitychange",oHidden:"ovisibilitychange",webkitHidden:"webkitvisibilitychange"}),n=0;n0;return!n||n&&t*l>=ae.sessionWindow?Bn().then((function(e){var t,n,r;xn({url:"/session",method:"POST",params:(t=e,n=t.callbackParams,r=t.partnerParams,{callbackParams:n.length?A(n):null,partnerParams:r.length?A(r):null})},{auto:!0})})):(at("attribution:check"),yn())}var tr="undefined"==typeof Promise?n(702).Promise:Promise,nr=sn({url:"/attribution",strategy:"short",continueCb:function(e,t,n){if(!e||e&&"error"===e.status)return t(),tr.resolve({state:"unknown"});if(!e.ask_in)return t(),function(e){if(x(e)||!function(e){var t=e.adid,n=void 0===t?"":t,r=e.attribution,o=void 0===r?{}:r;return!!n&&!!T(rr,Object.keys(o)).length}(e)||function(e){var t=e.adid,n=e.attribution,r=nt.current.attribution||{};return!(n&&rr.some((function(e){return r[e]!==n[e]})))&&t===r.adid}(e))return tr.resolve({state:"same"});var t=E(e.attribution).filter((function(e){var t=u(e,1)[0];return-1!==rr.indexOf(t)})).reduce(C,{adid:e.adid});return nt.current=o(o({},nt.current),{},{attribution:t}),yn().then((function(){return at("attribution:change",t),Z.info("Attribution has been updated"),{state:"changed"}}))}(e);return n(e.ask_in)}}),rr=["tracker_token","tracker_name","network","campaign","adgroup","creative","click_label","state"];var or=sn({url:"/gdpr_forget_device",method:"POST",strategy:"short"}),ir={running:"Adjust SDK is running pending GDPR Forget Me request",pending:"Adjust SDK will run GDPR Forget Me request after initialisation",paused:"Adjust SDK is already prepared to send GDPR Forget Me request",off:"Adjust SDK is already disabled"};function ar(e){var t=fn();return e||"on"===t?ae.isInitialised()?(or.send({params:o({},nt.getParams())}).then((function(){at("sdk:gdpr-forget-me")})),!0):(Z.log(ir.pending),!0):(Z.log(ir[t]),!1)}function sr(){return ln({reason:h,pending:!1},"finish")}var ur={running:"Adjust SDK is running pending third-party sharing opt-out request",delayed:"Adjust SDK will run third-party sharing opt-out request after initialisation",pending:"Adjust SDK already queued third-party sharing opt-out request",off:"Third-party sharing opt-out is already done",start:{inProgress:"Third-party sharing opt-out has already started",done:"Third-party sharing opt-out is now started"},finish:{inProgress:"Third-party sharing opt-out has already finished",done:"Third-party sharing opt-out is now finished"}};function cr(){var e=mt()||{};return e.reason?e.pending?"pending":"off":"on"}function lr(e){var t=cr();return e||"on"===t?ae.isInitialised()?(xn({url:"/disable_third_party_sharing",method:"POST"}),!0):(Z.log(ur.delayed),!0):(Z.log(ur[t]),!1)}function dr(e,t){var n,r,i=mt()||{},a="start"===t&&e?"start":"finish",s="start"===t&&i.reason,u="finish"===t&&i.reason&&!i.pending;return s||u?(Z.log(ur[a].inProgress),!1):(Z.log(ur[a].done),r=(n={reason:p,pending:e})?o({},n):null,ut.stores[ct]=o(o({},dt()),{},{thirdPartySharingDisabled:r}),ft(),!0)}function fr(){return dr(!1,"finish")}function pr(){"pending"===cr()&&(Z.log(ur.running),lr(!0))}var hr=[];function mr(e,t){hr.push({method:e,description:t,timestamp:Date.now()})}var vr,gr="undefined"==typeof Promise?n(702).Promise:Promise,yr="eventDeduplication";function br(e,t){var n,r,i=t.callbackParams,a=t.partnerParams,s={},u=o({eventToken:e.eventToken,deduplicationId:e.deduplicationId},(n=e.revenue,r=e.currency,isNaN(n)||(n=parseFloat(n))<0||!r?{}:{revenue:n.toFixed(5),currency:r})),c=o(o({},A(i)),A(e.callbackParams)),l=o(o({},A(a)),A(e.partnerParams));return x(c)||(s.callbackParams=c),x(l)||(s.partnerParams=l),o(o({},u),s)}function kr(e){return e?At.getAll(yr).then((function(e){return e.map((function(e){return e.id}))})).then((function(t){return-1===t.indexOf(e)?function(e){var t=ae.getCustomConfig().eventDeduplicationListLimit,n=t>0?t:10;return At.count(yr).then((function(e){var t=gr.resolve();if(e>=n){var r=e-n+1;Z.log("Event deduplication list limit has been reached. Oldest ids are about to be removed (".concat(r," of them)")),t=At.trimItems(yr,r)}return t})).then((function(){return Z.info("New event deduplication id is added to the list: ".concat(e)),At.addItem(yr,{id:e})}))}(e):gr.reject({message:"Event won't be tracked, since it was previously tracked with the same deduplication id ".concat(e)})})):gr.resolve()}function wr(e){return{clickTime:Ve(),source:"web_referrer",referrer:decodeURIComponent(e)}}function _r(e,t){var n;(n=e||window.location.search.substring(1).split("&").map((function(e){return e.split("=")})).reduce(C,{}).adjust_referrer)&&xn({url:"/sdk_click",method:"POST",params:wr(n)},{timestamp:t})}function Sr(e){if(!e)return null;try{return JSON.parse(e)}catch(e){return null}}!function(e){e.Android="android",e.iOS="ios",e.WindowsPC="windows",e.WindowsPhone="windows-phone"}(vr||(vr={}));var jr,Pr=function(){function e(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:"adjust-smart-banner";qe(this,e),this.storageName=t}return Me(e,[{key:"setItem",value:function(e,t){localStorage.setItem("".concat(this.storageName,".").concat(e),JSON.stringify(t))}},{key:"getItem",value:function(e){return Sr(localStorage.getItem("".concat(this.storageName,".").concat(e)))}},{key:"removeItem",value:function(e){localStorage.removeItem("".concat(this.storageName,".").concat(e))}}]),e}(),xr=function(){function t(){qe(this,t),e(this,"items",{})}return Me(t,[{key:"setItem",value:function(e,t){this.items[e]=t}},{key:"getItem",value:function(e){return Object.prototype.hasOwnProperty.call(this.items,e)?this.items[e]:null}},{key:"removeItem",value:function(e){delete this.items[e]}}]),t}(),Dr=function(){function e(){qe(this,e)}return Me(e,null,[{key:"isLocalStorageSupported",value:function(){try{var e=(new Date).toString(),t=window.localStorage;t.setItem(e,e);var n=t.getItem(e)===e;return t.removeItem(e),!(!n||!t)}catch(e){return!1}}},{key:"createStorage",value:function(){return this.isLocalStorageSupported()?new Pr:new xr}}]),e}();"undefined"==typeof Promise&&n(702).Promise;function Nr(e,t,n){return n.request("/smart_banner",{app_web_token:e}).then((function(e){var n,r,o,i,a,s,u,c=e.find((function(e){return e.platform===t}));return c?(i=(n=c).title,a=n.description,s=n.button_label,u=n.tracker_token,i&&a&&s&&u?{appId:(null===(r=n.app)||void 0===r?void 0:r.default_store_app_id)||"",appName:(null===(o=n.app)||void 0===o?void 0:o.name)||"",position:n.position||jr.Bottom,imageUrl:n.image_url,header:i,description:a,buttonText:s,trackerToken:u,deeplinkPath:n.deeplink_path,dismissInterval:864e5}:null):null})).catch((function(e){return Z.error("Network error occurred during loading Smart Banner: "+JSON.stringify(e)),null}))}!function(e){e.Top="top",e.Bottom="bottom"}(jr||(jr={}));var Ir=n(379),Ar=n.n(Ir),Tr=n(795),Rr=n.n(Tr),Cr=n(569),Er=n.n(Cr),Or=n(565),Kr=n.n(Or),Br=n(216),Ur=n.n(Br),qr=n(589),Lr=n.n(qr),Mr=n(841),Wr={};Wr.styleTagTransform=Lr(),Wr.setAttributes=Kr(),Wr.insert=Er().bind(null,"head"),Wr.domAPI=Rr(),Wr.insertStyleElement=Ur();Ar()(Mr.Z,Wr);const Vr=Mr.Z&&Mr.Z.locals?Mr.Z.locals:void 0;var Fr="undefined"==typeof Promise?n(702).Promise:Promise,zr=function(){function t(n,r,o){qe(this,t),e(this,"appTraceUrl",(function(e){return"https://www.apptrace.com/api/app/".concat(e,"/artwork_url_small")})),e(this,"appName",void 0),e(this,"image",void 0),e(this,"placeholder",void 0),this.image=r,this.placeholder=o,this.appName=n.appName;var i=this.getSources(n);this.showImage(i)}return Me(t,[{key:"getSources",value:function(e){var t=[];return e.imageUrl&&t.push(e.imageUrl),t.push(this.appTraceUrl(e.appId)),t}},{key:"showImage",value:function(e){var t=this;return e.reduce((function(e,n){return e.catch((function(){return t.loadImage(n,t.image)}))}),Fr.reject()).then((function(){t.placeholder.remove()})).catch((function(){t.image.remove(),t.placeholder.innerText=t.appName.length?t.appName[0].toUpperCase():""}))}},{key:"loadImage",value:function(e,t){return new Fr((function(n,r){t.onload=n,t.onerror=r,t.src=e}))}}]),t}(),Gr=function(){function t(n,r,o){qe(this,t),e(this,"parent",document.body),e(this,"banner",void 0),e(this,"dismissButton",null),e(this,"onDismiss",void 0),this.onDismiss=r,this.render(n,o)}return Me(t,[{key:"render",value:function(e,t){this.banner=document.createElement("div"),this.banner.setAttribute("class",Vr.bannerContainer);var n=e.position===jr.Top?Vr.stickyToTop:Vr.stickyToBottom,r=e.deeplinkPath?"?deeplink=".concat(encodeURIComponent(e.deeplinkPath)):"",o="".concat(t,"/").concat(e.trackerToken).concat(r);this.banner.innerHTML=function(e,t,n,r,o){return'\n
\n
\n
\n \n
\n
\n ').concat(t,'\n
\n
\n

').concat(t,'

\n

').concat(n,'

\n
\n 1&&void 0!==arguments[1]?arguments[1]:r;return function(){return o(t,e)}}}(to||(to={})),(no||(no={})).preferredUrlsGetter=function(e){return function(){return function(e){return[{endpointName:"Custom (".concat(e,")"),app:e,gdpr:e}]}(e)}},function(t){t.EU="EU",t.TR="TR",t.US="US";var n,r=(e(n={},ro.EU,_),e(n,ro.TR,S),e(n,ro.US,j),n),o=function(e,t){return[e[t]]};t.preferredUrlsGetter=function(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:r;return function(){return o(t,e)}}}(ro||(ro={})),function(e){var t=function(e,t){Z.warn("Both ".concat(e," and ").concat(t," are set in config, ").concat(t," will be ignored"))};e.create=function(e){var n=e.customUrl,r=e.dataResidency,o=e.urlStrategy;return n?((r||o)&&t("customUrl",r?"dataResidency":"urlStrategy"),new ao(no.preferredUrlsGetter(n))):r?(o&&t("dataResidency","urlStrategy"),new ao(ro.preferredUrlsGetter(r))):new ao(to.preferredUrlsGetter(o))}}(oo||(oo={}));"undefined"==typeof Promise&&n(702).Promise;var so=function(t){!function(e,t){if("function"!=typeof t&&null!==t)throw new TypeError("Super expression must either be null or a function");e.prototype=Object.create(t&&t.prototype,{constructor:{value:e,writable:!0,configurable:!0}}),Object.defineProperty(e,"prototype",{writable:!1}),t&&Hr(e,t)}(r,t);var n=eo(r);function r(t,o){var i,a=o.urlStrategy,s=o.urlStrategyConfig;return qe(this,r),e(Qr(i=n.call(this,t)),"lastSuccessfulEndpoint",void 0),e(Qr(i),"urlStrategy",void 0),i.urlStrategy=a||oo.create(s),i}return Me(r,[{key:"endpoint",get:function(){return this.lastSuccessfulEndpoint||r.DEFAULT_ENDPOINT}},{key:"request",value:function(e,t){var n=this;return this.urlStrategy.retries((function(r){return n.network.endpoint=r.app,n.network.request(e,t).then((function(e){return n.lastSuccessfulEndpoint=r.app,e})).catch((function(e){throw n.lastSuccessfulEndpoint=void 0,e}))}))}}]),r}(io);e(so,"DEFAULT_ENDPOINT",b.app),so||(so={});"undefined"==typeof Promise&&n(702).Promise;var uo=function(){function t(n,r){var o=n.webToken,i=n.logLevel,a=void 0===i?"error":i,s=n.dataResidency,u=n.onCreated,c=n.onDismissed;qe(this,t),e(this,"STORAGE_KEY_DISMISSED","closed"),e(this,"network",void 0),e(this,"storage",void 0),e(this,"timer",null),e(this,"dataFetchPromise",void 0),e(this,"banner",void 0),e(this,"onCreated",void 0),e(this,"onDismissed",void 0),this.onCreated=u,this.onDismissed=c,Z.setLogLevel(a);var l=s?{dataResidency:s}:{};this.network=r||new so(new Xr,{urlStrategyConfig:l}),this.storage=Dr.createStorage(),this.init(o)}return Me(t,[{key:"init",value:function(e){var t=this;if(this.banner)Z.error("Smart Banner already exists");else if(this.dataFetchPromise)Z.error("Smart Banner is initialising already");else{var n=function(){var e,t,n=null===(e=navigator)||void 0===e||null===(t=e.userAgent)||void 0===t?void 0:t.toLowerCase();if(n&&!(n.length<1))return/ipad|iphone|ipod/.test(n)?vr.iOS:n.includes("windows")?/phone|mobile/.test(n)?vr.WindowsPhone:vr.WindowsPC:n.includes("android")?vr.Android:void 0}();n?(this.dataFetchPromise=Nr(e,n,this.network),this.dataFetchPromise.then((function(r){if(t.dataFetchPromise=null,r){var o=t.getDateToShowAgain(r.dismissInterval);if(Date.now()
diff --git a/src/demo/set-referrer/set-referrer.js b/src/demo/set-referrer/set-referrer.js index 800b11df..6a57c7bd 100644 --- a/src/demo/set-referrer/set-referrer.js +++ b/src/demo/set-referrer/set-referrer.js @@ -33,7 +33,7 @@ function _handleSave (e) { _ui.submitButton.disabled = true const referrerConfig = { - [_form.reffererKey.value]: _form.reffererValue.value + [_form.referrerKey.value]: _form.referrerValue.value } _setJson(referrerConfig) @@ -81,12 +81,12 @@ function _handleToggle (e) { function _prepareForm () { const referrerConfig = getItem('referrerConfig') || {..._defaultReferrerConfig} - _form.reffererKey = _ui.referrerConfigForm.querySelector('#refferer-key') - _form.reffererValue = _ui.referrerConfigForm.querySelector('#refferer-value') + _form.referrerKey = _ui.referrerConfigForm.querySelector('#referrer-key') + _form.referrerValue = _ui.referrerConfigForm.querySelector('#referrer-value') Object.keys(referrerConfig).map(key => { - _form.reffererKey.value = key - _form.reffererValue.value = referrerConfig[key] + _form.referrerKey.value = key + _form.referrerValue.value = referrerConfig[key] }) _setJson(referrerConfig) @@ -94,8 +94,8 @@ function _prepareForm () { function _setJson (referrerConfig) { Object.keys(referrerConfig).map(key => { - _form.reffererKey.value = key - _form.reffererValue.value = referrerConfig[key] + _form.referrerKey.value = key + _form.referrerValue.value = referrerConfig[key] }) _ui.referrerConfigJson.textContent = `Adjust.setReferrer("${_referrerConfigToString(referrerConfig)}")` diff --git a/src/demo/tabs/tabs.js b/src/demo/tabs/tabs.js index 9535c9ee..70e0e670 100644 --- a/src/demo/tabs/tabs.js +++ b/src/demo/tabs/tabs.js @@ -1,6 +1,6 @@ -import {hyphenToCamelCase} from '../utils' -import {getItem, setItem, clear} from '../storage' -import {write, clear as clearLog} from '../log' +import { hyphenToCamelCase } from '../utils' +import { getItem, setItem, clear } from '../storage' +import { write, clear as clearLog } from '../log' import Adjust from '../../sdk/main' const _ui = {} @@ -10,8 +10,8 @@ let _disabled = false let _defaultAppConfig = {} let _timeoutId = null -function init (defaultAppConfig) { - _defaultAppConfig = {...defaultAppConfig} +function init(defaultAppConfig) { + _defaultAppConfig = { ...defaultAppConfig } _ui.logTab = document.getElementById('log-tab') _ui.logTabContainer = document.getElementById('log-tab-container') @@ -37,7 +37,7 @@ function init (defaultAppConfig) { _ui.resetButton.addEventListener('click', _handleReset, false) } -function _handleTab (e) { +function _handleTab(e) { const key = hyphenToCamelCase(e.target.id) const tab = _ui[key] const container = _ui[key + 'Container'] @@ -55,22 +55,22 @@ function _handleTab (e) { _active.container = container } -function _getAppConfig (form) { +function _getAppConfig(form) { return Object.keys(form) .map(key => [key, form[key].value]) .filter(([, value]) => value) - .reduce((acc, [key, value]) => ({...acc, [key]: value}), {}) + .reduce((acc, [key, value]) => ({ ...acc, [key]: value }), {}) } -function _reflectConfigInForm (appConfig, form) { +function _reflectConfigInForm(appConfig, form) { Object.keys(form).map(key => form[key].value = appConfig[key] || '') } -function _handleClearLog () { +function _handleClearLog() { clearLog() } -function _handleSave (e) { +function _handleSave(e) { e.preventDefault() if (_disabled) { @@ -91,7 +91,7 @@ function _handleSave (e) { _ui.submitButton.classList.remove('loading') _ui.submitButton.disabled = false - _handleTab({target: {id: 'log-tab'}}) + _handleTab({ target: { id: 'log-tab' } }) Adjust.__testonly__.destroy() Adjust.initSdk({ ...appConfig, @@ -100,7 +100,7 @@ function _handleSave (e) { }) } -function _handleRestoreDefaultConfig () { +function _handleRestoreDefaultConfig() { if (_disabled) { return } @@ -111,7 +111,7 @@ function _handleRestoreDefaultConfig () { setItem('appConfig', null) - const appConfig = {..._defaultAppConfig} + const appConfig = { ..._defaultAppConfig } _reflectConfigInForm(appConfig, _form) _setJson(appConfig) @@ -124,7 +124,7 @@ function _handleRestoreDefaultConfig () { }) } -function _handleReset () { +function _handleReset() { if (_disabled) { return } @@ -151,25 +151,18 @@ function _handleReset () { }) } -function _handleAttributionChange (e, result) { +function _handleAttributionChange(e, result) { write('NEW ATTRIBUTION') write(JSON.stringify(result, undefined, 2)) } -function _prepareForm () { - const appConfig = getItem('appConfig') || {..._defaultAppConfig} +function _prepareForm() { + const appConfig = getItem('appConfig') || { ..._defaultAppConfig } Adjust.initSdk({ ...appConfig, attributionCallback: _handleAttributionChange }) - Adjust.initSmartBanner({ - ...appConfig, - webToken: 'p6o2pnb1zkzk', - logLevel: 'verbose', - onCreated: () => write('Hey, where is a Smart Banner!'), - onDismissed: () => write('Oh, you have dismissed the Smart Banner'), - }) _form.appToken = _ui.appConfigForm.querySelector('#app-token') _form.environment = _ui.appConfigForm.querySelector('#environment') @@ -186,7 +179,7 @@ function _prepareForm () { _setJson(appConfig) } -function _setJson (appConfig) { +function _setJson(appConfig) { _ui.appConfigJson.textContent = `Adjust.initSdk(${JSON.stringify(appConfig, undefined, 2)})` } diff --git a/src/demo/track-third-party-sharing/track-third-party-sharing.html b/src/demo/track-third-party-sharing/track-third-party-sharing.html new file mode 100644 index 00000000..26c4e7b5 --- /dev/null +++ b/src/demo/track-third-party-sharing/track-third-party-sharing.html @@ -0,0 +1,32 @@ +
+
+ + +
+ +
+
+
+
+
+ + +
+ +
+

Granular options

+
+ +
+

Partner sharing settings

+
+ +
+ + +
+ +

+    
+
+
diff --git a/src/demo/track-third-party-sharing/track-third-party-sharing.js b/src/demo/track-third-party-sharing/track-third-party-sharing.js new file mode 100644 index 00000000..db6509d5 --- /dev/null +++ b/src/demo/track-third-party-sharing/track-third-party-sharing.js @@ -0,0 +1,130 @@ +import Adjust from '../../sdk/main' +import { getItem, setItem } from '../storage' +import DynamicParams from '../dynamic-params/dynamic-params' + +const _ui = {} +let _tpsOptions = {} +let _disabled = false +let _timeoutId = null +let _granularOptions = null +let _partnerSharingSettings = null + +function init() { + _tpsOptions = getItem('tpsOptions') || { isEnabled: true, granularOptions: [], partnerSharingSettings: [] } + + _ui.tpsOptionsForm = document.getElementById('tps-config-form') + _ui.tpsOptionsJson = document.getElementById('tps-config-json') + _ui.trackTPSButton = document.getElementById('track-tps-button') + _ui.toggleButton = document.getElementById('tps-side-form-toggle') + _ui.enableTPS = document.getElementById('enable-tps') + _ui.submitButton = _ui.tpsOptionsForm.querySelector('button[type="submit"]') + + _ui.tpsOptionsForm.addEventListener('submit', _handleSave) + _ui.toggleButton.addEventListener('click', _handleToggle) + _ui.trackTPSButton.addEventListener('click', _handleTrackTPS) + + _ui.enableTPS.addEventListener('change', () => { + _tpsOptions.isEnabled = _ui.enableTPS.checked + setItem('tpsOptions', _tpsOptions) + _setJson(_tpsOptions) + }) + + _granularOptions = DynamicParams('tps-granular', ['partnerName', 'key', 'value'], _tpsOptions.granularOptions, + () => { + _tpsOptions.granularOptions = _granularOptions.query() + _setJson(_tpsOptions) + setItem('tpsOptions', _tpsOptions) + }) + _granularOptions.init() + + _partnerSharingSettings = DynamicParams('tps-partner-sharing', ['partnerName', 'key', 'value'], _tpsOptions.partnerSharingSettings, + () => { + _tpsOptions.partnerSharingSettings = _partnerSharingSettings.query() + _setJson(_tpsOptions) + setItem('tpsOptions', _tpsOptions) + }) + _partnerSharingSettings.init() + + _setJson(_tpsOptions) +} + +function _trackThirdPartySharing(tpsOptions) { + const options = new Adjust.ThirdPartySharing(tpsOptions.isEnabled) + + for (const option of tpsOptions.granularOptions) { + options.addGranularOption(option.partnerName, option.key, option.value) + } + + for (const option of tpsOptions.partnerSharingSettings) { + const value = option.value === 'false' ? false : !!option.value + options.addPartnerSharingSetting(option.partnerName, option.key, value) + } + + Adjust.trackThirdPartySharing(options) +} + +function _handleSave(e) { + e.preventDefault() + + if (_disabled) { + return + } + + _disabled = true + _ui.submitButton.classList.add('loading') + _ui.submitButton.disabled = true + + clearTimeout(_timeoutId) + _timeoutId = setTimeout(() => { + _disabled = false + _ui.submitButton.classList.remove('loading') + _ui.submitButton.disabled = false + + _trackThirdPartySharing(_tpsOptions) + }, 1000) +} + +function _handleTrackTPS() { + if (_disabled) { + return + } + + _disabled = true + _ui.trackTPSButton.classList.add('loading') + _ui.trackTPSButton.disabled = true + + clearTimeout(_timeoutId) + _timeoutId = setTimeout(() => { + _disabled = false + _ui.trackTPSButton.classList.remove('loading') + _ui.trackTPSButton.disabled = false + + _trackThirdPartySharing(_tpsOptions) + }, 1000) +} + +function _handleToggle(e) { + const target = e.target + const sideForm = target.parentNode.nextElementSibling + + sideForm.classList.toggle('show') + target.classList.toggle('active') +} + +function _setJson(tpsOptions) { + let text = `const options = new Adjust.ThirdPartySharingOptions(${tpsOptions.isEnabled});\n` + + for (const option of tpsOptions.granularOptions) { + text += `option.addGranularOption('${option.partnerName}', '${option.key}', '${option.value}')\n` + } + + for (const option of tpsOptions.partnerSharingSettings) { + text += `option.addPartnerSharingSetting('${option.partnerName}', '${option.key}', '${option.value}')\n` + } + + text += 'Adjust.trackThirdPartySharing(options);' + + _ui.tpsOptionsJson.textContent = text +} + +export default init diff --git a/src/demo/utils.js b/src/demo/utils.js index 9213aa80..3307d58b 100644 --- a/src/demo/utils.js +++ b/src/demo/utils.js @@ -2,6 +2,10 @@ function hyphenToCamelCase (string) { return string.replace(/(-\w)/g, ([, m]) => m.toUpperCase()) } +function capitalize(string) { + return string.charAt(0).toUpperCase() + string.slice(1); +} + function debounce (fn, wait = 500) { let timeout return function () { @@ -12,5 +16,6 @@ function debounce (fn, wait = 500) { export { hyphenToCamelCase, + capitalize, debounce } diff --git a/src/global.d.ts b/src/global.d.ts index 6cce3454..64d14a62 100644 --- a/src/global.d.ts +++ b/src/global.d.ts @@ -10,10 +10,12 @@ interface Window { } interface IDBError extends Event { + // eslint-disable-next-line @typescript-eslint/no-explicit-any target: any } interface IDBRequest { + // eslint-disable-next-line @typescript-eslint/no-explicit-any onerror: ((this: IDBRequest, ev: IDBError) => any) | null; } @@ -35,6 +37,7 @@ interface IDBOpenDBEvent extends Event { } interface IDBOpenDBRequest extends IDBRequest { + // eslint-disable-next-line @typescript-eslint/no-explicit-any onsuccess: ((this: IDBOpenDBRequest, ev: IDBOpenDBEvent) => any) | null; } @@ -47,5 +50,6 @@ interface OpenIDBCursorEvent extends Event { } interface OpenIDBCursorRequest extends IDBRequest { + // eslint-disable-next-line @typescript-eslint/no-explicit-any onsuccess: ((this: OpenIDBCursorRequest, ev: OpenIDBCursorEvent) => any) | null; } diff --git a/src/index.html b/src/index.html index 9f095ddf..ad282be2 100644 --- a/src/index.html +++ b/src/index.html @@ -26,7 +26,7 @@

Web SDK Demo

<%- include('./demo/stop/stop.html') %> <%- include('./demo/restart/restart.html') %> <%- include('./demo/gdpr-forget-me/gdpr-forget-me.html') %> - <%- include('./demo/disable-third-party-sharing/disable-third-party-sharing.html') %> + <%- include('./demo/track-third-party-sharing/track-third-party-sharing.html') %> <%- include('./demo/get-web-uuid/get-web-uuid.html') %> <%- include('./demo/get-attribution/get-attribution.html') %> <%- include('./demo/set-referrer/set-referrer.html') %> diff --git a/src/sdk/__mocks__/url-strategy.ts b/src/sdk/__mocks__/url-strategy.ts index 5655b679..35bf7f72 100644 --- a/src/sdk/__mocks__/url-strategy.ts +++ b/src/sdk/__mocks__/url-strategy.ts @@ -1,27 +1,39 @@ -import type { BaseUrlsMap, UrlStrategy } from '../url-strategy' +import { BaseUrlsMap } from '../url-strategy' -const urlStrategyModule = jest.requireActual('../url-strategy') +type Endpoints = keyof typeof testEndpoints -const testEndpoints = { - default: { app: 'app.default', gdpr: '' }, - india: { app: 'app.india', gdpr: '' }, - china: { app: 'app.china', gdpr: '' } +export const testEndpoints = { + default: 'default', + world: 'world', } -const singleEndpoint = { default: { app: 'app', gdpr: 'gdpr' } } - -export const mockEndpoints = { - endpoints: testEndpoints, - singleEndpoint +export const singleEndpoint = { + default: 'default' } -export function urlStrategyRetries( - sendRequest: (urls: BaseUrlsMap) => Promise, - endpoints: Partial> = mockEndpoints.endpoints -) { - return urlStrategyModule.urlStrategyRetries(sendRequest, endpoints) -} +export function getBaseUrlsIterator(endpoints: Partial> = singleEndpoint) { + const _urls = [] as BaseUrlsMap[] + + for (const i in endpoints) { + const urlMap = { + app: 'app.' + i, + gdpr: 'gdpr.' + i + } + _urls.push(urlMap) + } + + let _counter = 0 -export function getBaseUrlsIterator(endpoints: Partial> = mockEndpoints.singleEndpoint) { - return urlStrategyModule.getBaseUrlsIterator(endpoints) + return { + next: () => { + if (_counter < _urls.length) { + return { value: _urls[_counter++], done: false } + } else { + return { value: undefined, done: true } + } + }, + reset: () => { + _counter = 0 + } + } } diff --git a/src/sdk/__tests__/_setup/_structuredClone.js b/src/sdk/__tests__/_setup/_structuredClone.js new file mode 100644 index 00000000..0de16db8 --- /dev/null +++ b/src/sdk/__tests__/_setup/_structuredClone.js @@ -0,0 +1 @@ +import 'core-js/stable/structured-clone'; diff --git a/src/sdk/__tests__/activity-state.spec.js b/src/sdk/__tests__/activity-state.spec.js index 355f3455..139839d3 100644 --- a/src/sdk/__tests__/activity-state.spec.js +++ b/src/sdk/__tests__/activity-state.spec.js @@ -1,5 +1,9 @@ import * as ActivityState from '../activity-state' -import {MINUTE, SECOND} from '../constants' +import { + MINUTE, SECOND, + PUB_SUB_EVENTS +} from '../constants' +import { publish } from '../pub-sub' jest.mock('../logger') @@ -16,7 +20,7 @@ describe('activity state functionality', () => { beforeEach(() => { currentTime = now dateNowSpy.mockReturnValue(currentTime) - ActivityState.default.init({uuid: 'some-uuid'}) + ActivityState.default.init({ uuid: 'some-uuid' }) ActivityState.default.initParams() }) @@ -32,7 +36,7 @@ describe('activity state functionality', () => { it('ensures that only copy is returned', () => { - const currentActivityState = {uuid: '123'} + const currentActivityState = { uuid: '123' } ActivityState.default.current = currentActivityState @@ -41,16 +45,16 @@ describe('activity state functionality', () => { currentActivityState.bla = 'truc' - expect(currentActivityState).toEqual({uuid: '123', bla: 'truc'}) - expect(ActivityState.default.current).toEqual({uuid: '123'}) + expect(currentActivityState).toEqual({ uuid: '123', bla: 'truc' }) + expect(ActivityState.default.current).toEqual({ uuid: '123' }) }) it('destroys activity state', () => { - ActivityState.default.current = {uuid: '123'} + ActivityState.default.current = { uuid: '123' } - expect(ActivityState.default.current).toEqual({uuid: '123'}) + expect(ActivityState.default.current).toEqual({ uuid: '123' }) ActivityState.default.destroy() @@ -319,36 +323,72 @@ describe('activity state functionality', () => { describe('getting web-uuid', () => { - it('returns actual uuid', () => { - expect(ActivityState.default.getWebUUID()).toBe('some-uuid') - }) + describe('sync getter', () => { + it('returns actual uuid', () => { + expect(ActivityState.default.getWebUUID()).toBe('some-uuid') + }) - it('returns null when ActivityState is not initialised', () => { - ActivityState.default.destroy() - localStorage.clear() + it('returns null when ActivityState is not initialised', () => { + ActivityState.default.destroy() + localStorage.clear() - expect(ActivityState.default.getWebUUID()).toBeNull() + expect(ActivityState.default.getWebUUID()).toBeNull() + }) }) + describe('async waitForWebUUID function', () => { + it('resolves when web_uuid cached', async () => { + await expect(ActivityState.default.waitForWebUUID()).resolves.toBe('some-uuid'); + }) + + it('resolves when receives attribution with pub-sub', async () => { + ActivityState.default.destroy() + localStorage.clear() + + const webUuidPromise = ActivityState.default.waitForWebUUID(); + + publish(PUB_SUB_EVENTS.WEB_UUID_CREATED, 'new_web_uuid'); + + await expect(webUuidPromise).resolves.toBe('new_web_uuid'); + }) + }) }) describe('getting attribution', () => { - it('returns null when ActivityState is not initialised', () => { - ActivityState.default.destroy() - localStorage.clear() + describe('sync getter', () => { + it('returns null when ActivityState is not initialised', () => { + ActivityState.default.destroy() + localStorage.clear() - expect(ActivityState.default.getAttribution()).toBeNull() - }) + expect(ActivityState.default.getAttribution()).toBeNull() + }) + + it('returns null when not attributed', () => { + expect(ActivityState.default.getAttribution()).toBeNull() + }) + + it('returns actual attribution', () => { + ActivityState.default.current = { ...ActivityState.default.current, attribution: { adid: 'dummy-adid' } } - it('returns null when not attributed', () => { - expect(ActivityState.default.getAttribution()).toBeNull() + expect(ActivityState.default.getAttribution()).toEqual({ adid: 'dummy-adid' }) + }) }) - it('returns actual attribution', () => { - ActivityState.default.current = { ...ActivityState.default.current, attribution: { adid: 'dummy-adid' } } + describe('async waitForAttribution function', () => { + it('resolves when attribution cached', async () => { + ActivityState.default.current = { ...ActivityState.default.current, attribution: { adid: 'dummy-adid' } } + + await expect(ActivityState.default.waitForAttribution()).resolves.toEqual({ adid: 'dummy-adid' }); + }) + + it('resolves when receives attribution with pub-sub', async () => { + const attrPromise = ActivityState.default.waitForAttribution(); + + publish(PUB_SUB_EVENTS.ATTRIBUTION_RECEIVED, { adid: 'new-adid' }) - expect(ActivityState.default.getAttribution()).toEqual({ adid: 'dummy-adid' }) + await expect(attrPromise).resolves.toEqual({ adid: 'new-adid' }); + }) }) }) diff --git a/src/sdk/__tests__/attribution.spec.js b/src/sdk/__tests__/attribution.spec.js index 3f272e80..d5d4479d 100644 --- a/src/sdk/__tests__/attribution.spec.js +++ b/src/sdk/__tests__/attribution.spec.js @@ -6,6 +6,7 @@ import * as Identity from '../identity' import * as ActivityState from '../activity-state' import * as Logger from '../logger' import * as Storage from '../storage/storage' +import { PUB_SUB_EVENTS } from '../constants' jest.mock('../http') jest.mock('../logger') @@ -537,7 +538,7 @@ describe('test attribution functionality', () => { ActivityState.default.current = {installed: 1} http.default.mockResolvedValue(newAttribution) - expect.assertions(8) + expect.assertions(9) Attribution.check({ask_in: 2000}) // initiate another attribution call @@ -555,8 +556,9 @@ describe('test attribution functionality', () => { expect(activityState.attribution).toEqual(formatted) expect(ActivityState.default.current.attribution).toEqual(formatted) expect(Identity.persist).toHaveBeenCalledTimes(3) - expect(PubSub.publish).toHaveBeenCalledTimes(1) + expect(PubSub.publish).toHaveBeenCalledTimes(2) expect(PubSub.publish).toHaveBeenCalledWith('attribution:change', formatted) + expect(PubSub.publish).toHaveBeenCalledWith(PUB_SUB_EVENTS.ATTRIBUTION_RECEIVED, formatted) }) }) diff --git a/src/sdk/__tests__/event.spec.js b/src/sdk/__tests__/event.spec.js index 89b66ee0..2990b464 100644 --- a/src/sdk/__tests__/event.spec.js +++ b/src/sdk/__tests__/event.spec.js @@ -27,7 +27,7 @@ const appOptions = { function expectRequest (requestConfig, timestamp) { const fullConfig = { - endpoint: 'app', + endpoint: 'app.default', ...requestConfig, params: { attempts: 1, diff --git a/src/sdk/__tests__/gdpr-forget-device.spec.js b/src/sdk/__tests__/gdpr-forget-device.spec.js index f46b064c..ddbe0d9c 100644 --- a/src/sdk/__tests__/gdpr-forget-device.spec.js +++ b/src/sdk/__tests__/gdpr-forget-device.spec.js @@ -24,7 +24,7 @@ function expectRequest () { } const fullConfig = { - endpoint: 'gdpr', + endpoint: 'gdpr.default', ...requestConfig, params: { attempts: 1, diff --git a/src/sdk/__tests__/http.spec.js b/src/sdk/__tests__/http.spec.js index 9382b080..9c74ab32 100644 --- a/src/sdk/__tests__/http.spec.js +++ b/src/sdk/__tests__/http.spec.js @@ -784,27 +784,5 @@ describe('perform api requests', () => { mockXHR.onreadystatechange() }) }) - - it('broadcasts third-party-sharing-opt-out event when this request is finished', () => { - - prepare({message: 'bla'}) - - expect.assertions(2) - - http.default({ - endpoint: 'app', - url: '/disable_third_party_sharing' - }).then(result => { - expect(result).toEqual({ - status: 'success' - }) - expect(PubSub.publish).toHaveBeenCalledWith('sdk:third-party-sharing-opt-out') - }) - - return Utils.flushPromises() - .then(() => { - mockXHR.onreadystatechange() - }) - }) }) }) diff --git a/src/sdk/__tests__/main/main-enabled.gdpr.spec.js b/src/sdk/__tests__/main/main-enabled.gdpr.spec.js index fa986807..655ca5df 100644 --- a/src/sdk/__tests__/main/main-enabled.gdpr.spec.js +++ b/src/sdk/__tests__/main/main-enabled.gdpr.spec.js @@ -193,7 +193,7 @@ describe('main entry point - test GDPR-Forget-Me when in initially enabled state }) it('initiates and prevents running all static methods and track event and runs forget-me request', () => { - expect.assertions(40) + expect.assertions(39) AdjustInstance.initSdk(suite.config) @@ -239,7 +239,7 @@ describe('main entry point - test GDPR-Forget-Me when in initially enabled state }) it('initiates and prevents running all static methods and track event and runs forget-me request', () => { - expect.assertions(40) + expect.assertions(39) AdjustInstance.initSdk(suite.config) diff --git a/src/sdk/__tests__/main/main.storage-available.spec.js b/src/sdk/__tests__/main/main.storage-available.spec.js index 52027542..c8be0cf2 100644 --- a/src/sdk/__tests__/main/main.storage-available.spec.js +++ b/src/sdk/__tests__/main/main.storage-available.spec.js @@ -306,274 +306,93 @@ describe('main entry point - test instance initiation when storage is available' }) describe('marketing opt-out - queue order check', () => { - it('disables third-party sharing before init when running the sdk for the first time', () => { + it('disables third-party sharing before init when running the sdk for the first time', async () => { AdjustInstance.disableThirdPartySharing() AdjustInstance.initSdk(suite.config) expect.assertions(3) - return Utils.flushPromises() - .then(() => { - PubSub.publish('sdk:installed') - jest.runOnlyPendingTimers() - - const requests = Queue.push.mock.calls + await Utils.flushPromises() + jest.runOnlyPendingTimers() - expect(requests.length).toBe(2) - expect(requests[0][0].url).toBe('/disable_third_party_sharing') - expect(requests[1][0].url).toBe('/session') - }) - }) + await Utils.flushPromises() + jest.runOnlyPendingTimers() - it('disables third-party sharing before init when not running sdk for the first time', () => { - return Storage.default.addItem('activityState', {uuid: 'bla', installed: true}) - .then(() => { - AdjustInstance.disableThirdPartySharing() - AdjustInstance.initSdk(suite.config) - - expect.assertions(3) - - return Utils.flushPromises() - .then(() => { - PubSub.publish('sdk:installed') - jest.runOnlyPendingTimers() + const requests = Queue.push.mock.calls - const requests = Queue.push.mock.calls + // FIXME: the actual order of requests is: first /third_party_sharing, then /session, but it doesn't + // seem to be doable to emulate event loop properly and force the requests to run in the same order + // they do in browser + expect(requests.length).toBe(2) + expect(requests[0][0].url).toBe('/session') + expect(requests[1][0].url).toBe('/third_party_sharing') - expect(requests.length).toBe(2) - expect(requests[0][0].url).toBe('/session') - expect(requests[1][0].url).toBe('/disable_third_party_sharing') - }) - }) }) - it('disables third-party sharing asynchronously after init', () => { + it('disables third-party sharing before init when running sdk not for the first time', async () => { + await Storage.default.addItem('activityState', { uuid: 'bla', installed: true }) + + AdjustInstance.disableThirdPartySharing() AdjustInstance.initSdk(suite.config) expect.assertions(3) - return Utils.flushPromises() - .then(() => { - PubSub.publish('sdk:installed') - jest.runOnlyPendingTimers() + await Utils.flushPromises() + jest.runOnlyPendingTimers() - AdjustInstance.disableThirdPartySharing() + await Utils.flushPromises() + jest.runOnlyPendingTimers() - const requests = Queue.push.mock.calls + const requests = Queue.push.mock.calls - expect(requests.length).toBe(2) - expect(requests[0][0].url).toBe('/session') - expect(requests[1][0].url).toBe('/disable_third_party_sharing') - - return Utils.flushPromises() - }) + // FIXME: the actual order of requests is: first /third_party_sharing, then /session, but it doesn't + // seem to be doable to emulate event loop properly and force the requests to run in the same order + // they do in browser + expect(requests.length).toBe(2) + expect(requests[0][0].url).toBe('/session') + expect(requests[1][0].url).toBe('/third_party_sharing') }) - it('disables third-party sharing synchronously after init', () => { + it('disables third-party sharing asynchronously after init', async () => { AdjustInstance.initSdk(suite.config) - AdjustInstance.disableThirdPartySharing() - expect.assertions(3) - - return Utils.flushPromises() - .then(() => { - PubSub.publish('sdk:installed') - jest.runOnlyPendingTimers() - - const requests = Queue.push.mock.calls - - expect(requests.length).toBe(2) - expect(requests[0][0].url).toBe('/session') - expect(requests[1][0].url).toBe('/disable_third_party_sharing') - - return Utils.flushPromises() - }) - }) - - describe('test multiple marketing opt-out requests in a row', () => { - it('prevents multiple opt-out requests when requesting opt-out multiple times before init', () => { - AdjustInstance.disableThirdPartySharing() - AdjustInstance.disableThirdPartySharing() - AdjustInstance.initSdk(suite.config) - - expect.assertions(8) - - expect(Logger.default.log).toHaveBeenCalledTimes(3) - expect(Logger.default.log).toHaveBeenCalledWith('Adjust SDK will run third-party sharing opt-out request after initialisation') - expect(Logger.default.log).toHaveBeenCalledWith('Third-party sharing opt-out is now started') - expect(Logger.default.log).toHaveBeenCalledWith('Adjust SDK already queued third-party sharing opt-out request') - - return Utils.flushPromises() - .then(() => { - PubSub.publish('sdk:installed') - jest.runOnlyPendingTimers() - - const requests = Queue.push.mock.calls - - expect(Logger.default.log).toHaveBeenCalledWith('Adjust SDK is running pending third-party sharing opt-out request') - expect(requests.length).toBe(2) - expect(requests[0][0].url).toBe('/disable_third_party_sharing') - expect(requests[1][0].url).toBe('/session') - - return Utils.flushPromises() - }) - }) - - it('prevents multiple opt-out requests when requesting opt-out multiple times synchronously after init', () => { - expect.assertions(8) - - AdjustInstance.initSdk(suite.config) - - return Utils.flushPromises() - .then(() => { - - AdjustInstance.disableThirdPartySharing() - AdjustInstance.disableThirdPartySharing() - - const logCallsCount = Logger.default.log.mock.calls.length - expect(Logger.default.log).toHaveBeenNthCalledWith(logCallsCount - 1, 'Running disable third-party sharing is delayed until Adjust SDK is up') - expect(Logger.default.log).toHaveBeenNthCalledWith(logCallsCount, 'Running disable third-party sharing is delayed until Adjust SDK is up') - - return Utils.flushPromises() - .then(() => { - PubSub.publish('sdk:installed') - jest.runOnlyPendingTimers() - - const requests = Queue.push.mock.calls - expect(Logger.default.log).toHaveBeenCalledWith('Delayed disable third-party sharing task is running now') - expect(Logger.default.log).toHaveBeenCalledWith('Third-party sharing opt-out is now started') - expect(Logger.default.log).toHaveBeenCalledWith('Adjust SDK already queued third-party sharing opt-out request') - expect(requests.length).toBe(2) - expect(requests[0][0].url).toBe('/session') - expect(requests[1][0].url).toBe('/disable_third_party_sharing') - - return Utils.flushPromises() - }) - }) - }) - - it('prevents multiple opt-out requests when requesting opt-out multiple times synchronously before and after init', () => { - AdjustInstance.disableThirdPartySharing() - AdjustInstance.initSdk(suite.config) - - return Utils.flushPromises() - .then(() => { - - AdjustInstance.disableThirdPartySharing() - - expect.assertions(9) - - expect(Logger.default.log).toHaveBeenCalledWith('Adjust SDK will run third-party sharing opt-out request after initialisation') - expect(Logger.default.log).toHaveBeenCalledWith('Third-party sharing opt-out is now started') - expect(Logger.default.log).toHaveBeenCalledWith('Running disable third-party sharing is delayed until Adjust SDK is up') - - return Utils.flushPromises() - .then(() => { - PubSub.publish('sdk:installed') - jest.runOnlyPendingTimers() - - const requests = Queue.push.mock.calls - - expect(Logger.default.log).toHaveBeenCalledWith('Adjust SDK is running pending third-party sharing opt-out request') - expect(Logger.default.log).toHaveBeenCalledWith('Delayed disable third-party sharing task is running now') - expect(Logger.default.log).toHaveBeenCalledWith('Adjust SDK already queued third-party sharing opt-out request') - expect(requests.length).toBe(2) - expect(requests[0][0].url).toBe('/disable_third_party_sharing') - expect(requests[1][0].url).toBe('/session') - - return Utils.flushPromises() - }) - }) - }) - - it('prevents multiple opt-out requests when requesting opt-out multiple times asynchronously before and after init', () => { - AdjustInstance.disableThirdPartySharing() - AdjustInstance.initSdk(suite.config) - - expect.assertions(7) - - expect(Logger.default.log).toHaveBeenCalledWith('Adjust SDK will run third-party sharing opt-out request after initialisation') - expect(Logger.default.log).toHaveBeenCalledWith('Third-party sharing opt-out is now started') - - return Utils.flushPromises() - .then(() => { - PubSub.publish('sdk:installed') - jest.runOnlyPendingTimers() - - AdjustInstance.disableThirdPartySharing() - - const requests = Queue.push.mock.calls - - expect(Logger.default.log).toHaveBeenCalledWith('Adjust SDK is running pending third-party sharing opt-out request') - expect(Logger.default.log).toHaveBeenCalledWith('Adjust SDK already queued third-party sharing opt-out request') - expect(requests.length).toBe(2) - expect(requests[0][0].url).toBe('/disable_third_party_sharing') - expect(requests[1][0].url).toBe('/session') - - return Utils.flushPromises() - }) - }) - - it('prevents multiple opt-out requests when requesting opt-out multiple times synchronously and then asynchronously after init', () => { - AdjustInstance.initSdk(suite.config) - - return Utils.flushPromises() - .then(() => { - expect.assertions(7) - - AdjustInstance.disableThirdPartySharing() - - expect(Logger.default.log).toHaveBeenLastCalledWith('Running disable third-party sharing is delayed until Adjust SDK is up') - - return Utils.flushPromises() - .then(() => { - PubSub.publish('sdk:installed') - jest.runOnlyPendingTimers() + expect.assertions(3) - AdjustInstance.disableThirdPartySharing() + await Utils.flushPromises() + jest.runOnlyPendingTimers() - const requests = Queue.push.mock.calls + AdjustInstance.disableThirdPartySharing() - expect(Logger.default.log).toHaveBeenCalledWith('Delayed disable third-party sharing task is running now') - expect(Logger.default.log).toHaveBeenCalledWith('Third-party sharing opt-out is now started') - expect(Logger.default.log).toHaveBeenCalledWith('Adjust SDK already queued third-party sharing opt-out request') - expect(requests.length).toBe(2) - expect(requests[0][0].url).toBe('/session') - expect(requests[1][0].url).toBe('/disable_third_party_sharing') + await Utils.flushPromises() + jest.runOnlyPendingTimers() - return Utils.flushPromises() - }) - }) - }) + const requests = Queue.push.mock.calls - it('prevents multiple opt-out requests when requesting opt-out multiple times asynchronously after init', () => { - AdjustInstance.initSdk(suite.config) + expect(requests.length).toBe(2) + expect(requests[0][0].url).toBe('/session') + expect(requests[1][0].url).toBe('/third_party_sharing') - expect.assertions(5) + return Utils.flushPromises() + }) - return Utils.flushPromises() - .then(() => { + it('disables third-party sharing synchronously after init', async () => { + AdjustInstance.initSdk(suite.config) + AdjustInstance.disableThirdPartySharing() + expect.assertions(3) - return Utils.flushPromises() - .then(() => { - PubSub.publish('sdk:installed') - jest.runOnlyPendingTimers() + await Utils.flushPromises() + jest.runOnlyPendingTimers() - AdjustInstance.disableThirdPartySharing() - AdjustInstance.disableThirdPartySharing() + await Utils.flushPromises() + jest.runOnlyPendingTimers() - const requests = Queue.push.mock.calls + const requests = Queue.push.mock.calls - expect(Logger.default.log).toHaveBeenCalledWith('Third-party sharing opt-out is now started') - expect(Logger.default.log).toHaveBeenCalledWith('Adjust SDK already queued third-party sharing opt-out request') - expect(requests.length).toBe(2) - expect(requests[0][0].url).toBe('/session') - expect(requests[1][0].url).toBe('/disable_third_party_sharing') + expect(requests.length).toBe(2) + expect(requests[0][0].url).toBe('/session') + expect(requests[1][0].url).toBe('/third_party_sharing') - return Utils.flushPromises() - }) - }) - }) + return Utils.flushPromises() }) }) }) diff --git a/src/sdk/__tests__/main/main.storage-not-available.spec.js b/src/sdk/__tests__/main/main.storage-not-available.spec.js index ba26f894..91959489 100644 --- a/src/sdk/__tests__/main/main.storage-not-available.spec.js +++ b/src/sdk/__tests__/main/main.storage-not-available.spec.js @@ -20,8 +20,8 @@ const mockGetType = () => STORAGE_TYPES.NO_STORAGE jest.mock('../../storage/storage', () => ({ init: () => mockInit(), getType: () => mockGetType() - } -)) + }) +) describe('main entry point - test instance initiation when storage is not available', () => { diff --git a/src/sdk/__tests__/main/main.suite.js b/src/sdk/__tests__/main/main.suite.js index 2114d4df..1623be6a 100644 --- a/src/sdk/__tests__/main/main.suite.js +++ b/src/sdk/__tests__/main/main.suite.js @@ -40,10 +40,9 @@ function _startFirstPart () { expect(PubSub.subscribe.mock.calls[0][0]).toEqual('sdk:installed') expect(PubSub.subscribe.mock.calls[1][0]).toEqual('sdk:shutdown') expect(PubSub.subscribe.mock.calls[2][0]).toEqual('sdk:gdpr-forget-me') - expect(PubSub.subscribe.mock.calls[3][0]).toEqual('sdk:third-party-sharing-opt-out') - expect(PubSub.subscribe.mock.calls[4][0]).toEqual('attribution:check') - expect(PubSub.subscribe.mock.calls[5][0]).toEqual('attribution:change') - expect(PubSub.subscribe.mock.calls[5][1]).toEqual(config.attributionCallback) + expect(PubSub.subscribe.mock.calls[3][0]).toEqual('attribution:check') + expect(PubSub.subscribe.mock.calls[4][0]).toEqual('attribution:change') + expect(PubSub.subscribe.mock.calls[4][1]).toEqual(config.attributionCallback) expect(Identity.start).toHaveBeenCalledTimes(1) @@ -60,7 +59,7 @@ function expectStart_Async () { expect(sdkClick.default).toHaveBeenCalledTimes(1) }) - return {assertions: 16, promise} + return {assertions: 15, promise} } function expectPartialStartWithGdprRequest_Async () { @@ -74,7 +73,7 @@ function expectPartialStartWithGdprRequest_Async () { expectGdprRequest() }) - return {assertions: 18, promise} + return {assertions: 17, promise} } // if restart then 8 assertions and 10 ones otherwise @@ -489,13 +488,11 @@ function teardown () { localStorage.clear() jest.clearAllMocks() Preferences.setDisabled(null) - Preferences.setThirdPartySharing(null) } function teardownAndDisable (reason = 'general') { teardown() Preferences.setDisabled({reason}) - Preferences.setThirdPartySharing(null) } export default function Suite (instance) { diff --git a/src/sdk/__tests__/preferences.spec.js b/src/sdk/__tests__/preferences.spec.js index 8b8f251f..290f3842 100644 --- a/src/sdk/__tests__/preferences.spec.js +++ b/src/sdk/__tests__/preferences.spec.js @@ -20,37 +20,24 @@ describe('activity state functionality', () => { jest.spyOn(PubSub, 'publish') expect(Preferences.getDisabled()).toBeNull() - expect(Preferences.getThirdPartySharing()).toBeNull() Preferences.setDisabled({reason: 'gdpr', pending: false}) - Preferences.setThirdPartySharing({reason: 'general', pending: false}) expect(Preferences.getDisabled()).toEqual({ reason: 'gdpr', pending: false }) - expect(Preferences.getThirdPartySharing()).toEqual({ - reason: 'general', - pending: false - }) Preferences.setDisabled(null) - Preferences.setThirdPartySharing(null) expect(Preferences.getDisabled()).toBeNull() - expect(Preferences.getThirdPartySharing()).toBeNull() Preferences.setDisabled({reason: 'gdpr', pending: true}) - Preferences.setThirdPartySharing({reason: 'general', pending: true}) expect(Preferences.getDisabled()).toEqual({ reason: 'gdpr', pending: true }) - expect(Preferences.getThirdPartySharing()).toEqual({ - reason: 'general', - pending: true - }) }) }) @@ -62,32 +49,22 @@ describe('activity state functionality', () => { jest.spyOn(PubSub, 'publish') QuickStorage.default.stores[storeName] = { - sdkDisabled: {reason: 'general', pending: false}, - thirdPartySharingDisabled: {reason: 'general', pending: true} + sdkDisabled: {reason: 'general', pending: false} } expect(Preferences.getDisabled()).toEqual({ reason: 'general', pending: false }) - expect(Preferences.getThirdPartySharing()).toEqual({ - reason: 'general', - pending: true - }) QuickStorage.default.stores[storeName] = { - sdkDisabled: {reason: 'gdpr', pending: false}, - thirdPartySharingDisabled: {reason: 'general', pending: false} + sdkDisabled: {reason: 'gdpr', pending: false} } expect(Preferences.getDisabled()).toEqual({ reason: 'general', pending: false }) - expect(Preferences.getThirdPartySharing()).toEqual({ - reason: 'general', - pending: true - }) Preferences.reload() @@ -96,17 +73,11 @@ describe('activity state functionality', () => { reason: 'gdpr', pending: false }) - expect(Preferences.getThirdPartySharing()).toEqual({ - reason: 'general', - pending: false - }) Preferences.setDisabled(null) - Preferences.setThirdPartySharing(null) QuickStorage.default.stores[storeName] = { - sdkDisabled: {reason: 'gdpr', pending: true}, - thirdPartySharingDisabled: {reason: 'general', pending: true} + sdkDisabled: {reason: 'gdpr', pending: true} } Preferences.reload() @@ -116,10 +87,6 @@ describe('activity state functionality', () => { reason: 'gdpr', pending: true }) - expect(Preferences.getThirdPartySharing()).toEqual({ - reason: 'general', - pending: true - }) }) }) @@ -128,11 +95,9 @@ describe('activity state functionality', () => { const Preferences = require('../preferences') Preferences.setDisabled({reason: 'gdpr', pending: false}) - Preferences.setThirdPartySharing({reason: 'general', pending: true}) expect(QuickStorage.default.stores[storeName]).toEqual({ sdkDisabled: {reason: 'gdpr', pending: false}, - thirdPartySharingDisabled: {reason: 'general', pending: true} }) localStorage.clear() @@ -141,10 +106,6 @@ describe('activity state functionality', () => { reason: 'gdpr', pending: false }) - expect(Preferences.getThirdPartySharing()).toEqual({ - reason: 'general', - pending: true - }) expect(QuickStorage.default.stores[storeName]).toBeNull() Preferences.recover() @@ -153,13 +114,8 @@ describe('activity state functionality', () => { reason: 'gdpr', pending: false }) - expect(Preferences.getThirdPartySharing()).toEqual({ - reason: 'general', - pending: true - }) expect(QuickStorage.default.stores[storeName]).toEqual({ sdkDisabled: {reason: 'gdpr', pending: false}, - thirdPartySharingDisabled: {reason: 'general', pending: true} }) }) }) diff --git a/src/sdk/__tests__/request.spec.js b/src/sdk/__tests__/request.spec.js index 17477823..24eadd35 100644 --- a/src/sdk/__tests__/request.spec.js +++ b/src/sdk/__tests__/request.spec.js @@ -4,6 +4,7 @@ import * as Time from '../time' import * as Logger from '../logger' import * as Listeners from '../listeners' import * as UrlStartegy from '../url-strategy' +import * as Constants from '../constants' jest.mock('../http') jest.mock('../logger') @@ -65,7 +66,7 @@ describe('test request functionality', () => { jest.runOnlyPendingTimers() expect(http.default).toHaveBeenCalledWith({ - endpoint: 'app', + endpoint: 'app.default', url: '/global-request', method: 'GET', params: { @@ -103,7 +104,7 @@ describe('test request functionality', () => { jest.runOnlyPendingTimers() expect(http.default).toHaveBeenCalledWith({ - endpoint: 'app', + endpoint: 'app.default', url: '/global-request', method: 'GET', params: { @@ -125,10 +126,10 @@ describe('test request functionality', () => { createdAtSpy.mockReturnValueOnce(now) - http.default.mockResolvedValue({wait: 3000}) + http.default.mockResolvedValue({ wait: 3000 }) someRequest.send({ - continueCb (result, finish, retry) { + continueCb(result, finish, retry) { if (result.wait) { return retry(result.wait) } @@ -205,12 +206,12 @@ describe('test request functionality', () => { finish() }) - http.default.mockResolvedValue({wait: 1300}) + http.default.mockResolvedValue({ wait: 1300 }) someRequest.send({ url: '/other-request', method: 'POST', - params: {something: 'else'}, + params: { something: 'else' }, continueCb }) @@ -224,7 +225,7 @@ describe('test request functionality', () => { expect(http.default).toHaveBeenCalledTimes(1) expect(http.default).toHaveBeenLastCalledWith({ - endpoint: 'app', + endpoint: 'app.default', url: '/other-request', method: 'POST', params: { @@ -245,7 +246,7 @@ describe('test request functionality', () => { expect(setTimeout).toHaveBeenLastCalledWith(expect.any(Function), 1300) expect(http.default).toHaveBeenCalledTimes(2) expect(http.default).toHaveBeenLastCalledWith({ - endpoint: 'app', + endpoint: 'app.default', url: '/other-request', method: 'POST', params: { @@ -269,7 +270,7 @@ describe('test request functionality', () => { expect(setTimeout).toHaveBeenLastCalledWith(expect.any(Function), 1300) expect(http.default).toHaveBeenCalledTimes(1) expect(http.default).toHaveBeenLastCalledWith({ - endpoint: 'app', + endpoint: 'app.default', url: '/other-request', method: 'POST', params: { @@ -301,7 +302,7 @@ describe('test request functionality', () => { jest.runOnlyPendingTimers() expect(http.default).toHaveBeenCalledWith({ - endpoint: 'app', + endpoint: 'app.default', url: '/global-request', method: 'GET', params: { @@ -329,7 +330,7 @@ describe('test request functionality', () => { .mockReturnValueOnce(now) .mockReturnValueOnce(newNow) - http.default.mockResolvedValue({retry_in: 666}) + http.default.mockResolvedValue({ retry_in: 666 }) someRequest.send({ url: '/some-request', @@ -346,7 +347,7 @@ describe('test request functionality', () => { expect(http.default).toHaveBeenCalledTimes(1) expect(http.default).toHaveBeenLastCalledWith({ - endpoint: 'app', + endpoint: 'app.default', url: '/some-request', method: 'POST', params: { @@ -360,7 +361,7 @@ describe('test request functionality', () => { .then(() => { expect(Logger.default.log).toHaveBeenLastCalledWith('Re-trying request /some-request in 666ms') - http.default.mockResolvedValue({retry_in: 777}) + http.default.mockResolvedValue({ retry_in: 777 }) jest.runOnlyPendingTimers() @@ -368,7 +369,7 @@ describe('test request functionality', () => { expect(setTimeout).toHaveBeenLastCalledWith(expect.any(Function), 666) expect(http.default).toHaveBeenCalledTimes(2) expect(http.default).toHaveBeenLastCalledWith({ - endpoint: 'app', + endpoint: 'app.default', url: '/some-request', method: 'POST', params: { @@ -391,7 +392,7 @@ describe('test request functionality', () => { expect(setTimeout).toHaveBeenLastCalledWith(expect.any(Function), 777) expect(http.default).toHaveBeenCalledTimes(1) expect(http.default).toHaveBeenLastCalledWith({ - endpoint: 'app', + endpoint: 'app.default', url: '/some-request', method: 'POST', params: { @@ -420,7 +421,7 @@ describe('test request functionality', () => { jest.runOnlyPendingTimers() expect(http.default).toHaveBeenCalledWith({ - endpoint: 'app', + endpoint: 'app.default', url: '/global-request', method: 'GET', params: { @@ -448,7 +449,7 @@ describe('test request functionality', () => { .mockReturnValueOnce(now) .mockReturnValueOnce(newNow) - http.default.mockResolvedValue({retry_in: 2592000000}) + http.default.mockResolvedValue({ retry_in: 2592000000 }) someRequest.send({ url: '/some-request' @@ -790,7 +791,7 @@ describe('test request functionality', () => { expect.assertions(27) - someRequest.send({url: '/new-request'}) + someRequest.send({ url: '/new-request' }) expect(Logger.default.log).toHaveBeenLastCalledWith('Trying request /new-request in 150ms') expect(setTimeout).toHaveBeenCalledTimes(1) @@ -868,7 +869,7 @@ describe('test request functionality', () => { jest.runOnlyPendingTimers() expect(http.default).toHaveBeenCalledWith({ - endpoint: 'app', + endpoint: 'app.default', url: '/global-request', method: 'GET', params: { @@ -888,7 +889,7 @@ describe('test request functionality', () => { expect.assertions(6) - someRequest.send({wait: 2000}) + someRequest.send({ wait: 2000 }) expect(Logger.default.log).toHaveBeenLastCalledWith('Trying request /global-request in 2000ms') expect(setTimeout).toHaveBeenLastCalledWith(expect.any(Function), 2000) @@ -960,7 +961,7 @@ describe('test request functionality', () => { .then(() => { expect(Logger.default.log).toHaveBeenLastCalledWith('Previous /other request attempt canceled') - someRequest.send({wait: 500}) + someRequest.send({ wait: 500 }) expect(Logger.default.log).toHaveBeenLastCalledWith('Trying request /global-request in 500ms') expect(setTimeout).toHaveBeenLastCalledWith(expect.any(Function), 500) @@ -991,7 +992,7 @@ describe('test request functionality', () => { expect.assertions(18) - someRequest.send({url: '/some-new-request'}) + someRequest.send({ url: '/some-new-request' }) expect(Logger.default.log).toHaveBeenLastCalledWith('Trying request /some-new-request in 150ms') expect(setTimeout).toHaveBeenCalledTimes(1) @@ -1045,7 +1046,7 @@ describe('test request functionality', () => { jest.runOnlyPendingTimers() expect(http.default).toHaveBeenCalledWith({ - endpoint: 'app', + endpoint: 'app.default', url: '/global-request', method: 'GET', params: { @@ -1069,11 +1070,11 @@ describe('test request functionality', () => { .mockReturnValueOnce(now) .mockReturnValueOnce(newNow) - http.default.mockRejectedValue({message: 'Unknown error'}) + http.default.mockRejectedValue({ message: 'Unknown error' }) expect.assertions(17) - let promise = someRequest.send({url: '/failed-request'}) + let promise = someRequest.send({ url: '/failed-request' }) expect(Logger.default.log).toHaveBeenLastCalledWith('Trying request /failed-request in 150ms') expect(setTimeout).toHaveBeenCalledTimes(1) @@ -1090,7 +1091,7 @@ describe('test request functionality', () => { return promise .catch(error => { - expect(error).toEqual({message: 'Unknown error'}) + expect(error).toEqual({ message: 'Unknown error' }) expect(someRequest.isRunning()).toBeFalsy() expect(clearTimeout).toHaveBeenCalledTimes(1) expect(Logger.default.log).toHaveBeenLastCalledWith('Request /failed-request failed') @@ -1099,7 +1100,7 @@ describe('test request functionality', () => { http.default.mockClear() clearTimeout.mockClear() - promise = someRequest.send({url: '/another-failed-request'}) + promise = someRequest.send({ url: '/another-failed-request' }) expect(Logger.default.log).toHaveBeenLastCalledWith('Trying request /another-failed-request in 150ms') expect(someRequest.isRunning()).toBeTruthy() @@ -1107,7 +1108,7 @@ describe('test request functionality', () => { jest.runOnlyPendingTimers() expect(http.default).toHaveBeenCalledWith({ - endpoint: 'app', + endpoint: 'app.default', url: '/another-failed-request', method: 'GET', params: { @@ -1133,7 +1134,7 @@ describe('test request functionality', () => { expect.assertions(9) - someRequest.send({wait: 1000}) + someRequest.send({ wait: 1000 }) expect(Logger.default.log).toHaveBeenLastCalledWith('Trying request /global-request in 1000ms') expect(setTimeout).toHaveBeenLastCalledWith(expect.any(Function), 1000) @@ -1166,7 +1167,7 @@ describe('test request functionality', () => { expect.assertions(9) - someRequest.send({wait: 1000}) + someRequest.send({ wait: 1000 }) expect(Logger.default.log).toHaveBeenLastCalledWith('Trying request /global-request in 1000ms') expect(setTimeout).toHaveBeenLastCalledWith(expect.any(Function), 1000) @@ -1177,7 +1178,7 @@ describe('test request functionality', () => { jest.advanceTimersByTime(500) // initiate another request after 500ms - someRequest.send({wait: 2000}) + someRequest.send({ wait: 2000 }) expect(Logger.default.log).not.toHaveBeenCalledWith('Previous /global-request request attempt canceled') expect(Logger.default.log).not.toHaveBeenCalledWith('Trying request /global-request in 150ms') @@ -1218,7 +1219,7 @@ describe('test request functionality', () => { jest.runOnlyPendingTimers() expect(http.default).toHaveBeenCalledWith({ - endpoint: 'app', + endpoint: 'app.default', url: '/another-global-request', method: 'GET', params: { @@ -1247,7 +1248,7 @@ describe('test request functionality', () => { jest.runOnlyPendingTimers() expect(http.default).toHaveBeenCalledWith({ - endpoint: 'app', + endpoint: 'app.default', url: '/new-url', method: 'POST', params: { @@ -1262,7 +1263,7 @@ describe('test request functionality', () => { .then(() => { expect(Logger.default.log).toHaveBeenCalledWith('Request /new-url has been finished') - req.send({wait: 1000}) + req.send({ wait: 1000 }) expect(Logger.default.log).toHaveBeenLastCalledWith('Trying request /another-global-request in 1000ms') expect(setTimeout).toHaveBeenLastCalledWith(expect.any(Function), 1000) @@ -1301,7 +1302,7 @@ describe('test request functionality', () => { jest.runOnlyPendingTimers() expect(http.default).toHaveBeenCalledWith({ - endpoint: 'app', + endpoint: 'app.default', url: '/another-global-request', method: 'GET', params: { @@ -1337,7 +1338,7 @@ describe('test request functionality', () => { jest.runOnlyPendingTimers() expect(http.default).toHaveBeenCalledWith({ - endpoint: 'app', + endpoint: 'app.default', url: '/another-global-request', method: 'GET', params: { @@ -1354,7 +1355,7 @@ describe('test request functionality', () => { newContinueCb.mockClear() - req.send({wait: 400}) + req.send({ wait: 400 }) expect(Logger.default.log).toHaveBeenLastCalledWith('Trying request /another-global-request in 400ms') expect(setTimeout).toHaveBeenLastCalledWith(expect.any(Function), 400) @@ -1362,7 +1363,7 @@ describe('test request functionality', () => { jest.runOnlyPendingTimers() expect(http.default).toHaveBeenCalledWith({ - endpoint: 'app', + endpoint: 'app.default', url: '/another-global-request', method: 'GET', params: { @@ -1383,7 +1384,7 @@ describe('test request functionality', () => { it('does not send the request when url not defined', () => { const errorRequest = Request.default({ - params: {some: 'param'} + params: { some: 'param' } }) expect.assertions(4) @@ -1405,11 +1406,20 @@ describe('test request functionality', () => { }) describe('url startegy retries functionality', () => { - const testEndpoints = jest.requireMock('../url-strategy').mockEndpoints.endpoints + Constants.BASE_URL_PREFIX = 'app.'; // eslint-disable-line no-import-assign + Constants.GDPR_URL_PREFIX = 'gdpr.'; // eslint-disable-line no-import-assign + + const testEndpoints = jest.requireMock('../url-strategy').testEndpoints // let getBaseUrlsIterator to return pre-created iterator so it's possible to spy iterator methods const iterator = jest.requireActual(('../url-strategy')).getBaseUrlsIterator(testEndpoints) + // returns an object containing `app` and `gdpr` endpoints + const getIteratorValueFromEndpoint = (domain) => ({ + app: Constants.BASE_URL_PREFIX + domain, + gdpr: Constants.GDPR_URL_PREFIX + domain + }) + const expectHttpCall = (times, endpoint, url) => { expect(http.default).toHaveBeenCalledTimes(times) expect(http.default).toHaveBeenCalledWith({ @@ -1446,7 +1456,7 @@ describe('test request functionality', () => { jest.restoreAllMocks() }) - it('does not retries if request succesfully sent', () => { + it('does not retry if request succesfully sent', () => { Request .default({ url: '/global-request', @@ -1460,7 +1470,7 @@ describe('test request functionality', () => { expect(UrlStartegy.getBaseUrlsIterator).toHaveBeenCalled() expect(iterator.next).toHaveBeenCalledTimes(1) - expect(iterator.next).toHaveReturnedWith({ value: testEndpoints.default, done: false }) + expect(iterator.next).toHaveReturnedWith({ value: getIteratorValueFromEndpoint(testEndpoints.default), done: false }) expect(Logger.default.log).toHaveBeenLastCalledWith('Trying request /global-request in 150ms') @@ -1484,7 +1494,7 @@ describe('test request functionality', () => { // iterator was reset and next called in request successful callback expect(iterator.next).toHaveBeenCalledTimes(2) expect(iterator.next).toHaveReturnedTimes(2) - expect(iterator.next).toHaveReturnedWith({ value: testEndpoints.default, done: false }) + expect(iterator.next).toHaveReturnedWith({ value: getIteratorValueFromEndpoint(testEndpoints.default), done: false }) }) }) @@ -1495,10 +1505,10 @@ describe('test request functionality', () => { .default({ url: '/global-request' }) .send() - expect.assertions(38) + expect.assertions(30) expect(UrlStartegy.getBaseUrlsIterator).toHaveBeenCalled() - expect(iterator.next).toHaveReturnedWith({ value: testEndpoints.default, done: false }) + expect(iterator.next).toHaveReturnedWith({ value: getIteratorValueFromEndpoint(testEndpoints.default), done: false }) clearIteratorMock(iterator) expect(Logger.default.log).toHaveBeenLastCalledWith('Trying request /global-request in 150ms') @@ -1509,89 +1519,67 @@ describe('test request functionality', () => { return Utils.flushPromises() .then(() => { - expect(iterator.next).toHaveReturnedWith({ value: testEndpoints.india, done: false }) + expect(iterator.next).toHaveReturnedWith({ value: getIteratorValueFromEndpoint(testEndpoints.world), done: false }) clearIteratorMock(iterator) expect(Logger.default.log).toHaveBeenLastCalledWith('Re-trying request /global-request in 150ms') jest.runOnlyPendingTimers() - expectHttpCall(2, 'app.india', '/global-request') + expectHttpCall(2, 'app.world', '/global-request') return Utils.flushPromises() }) .then(() => { - expect(iterator.next).toHaveReturnedWith({ value: testEndpoints.china, done: false }) - clearIteratorMock(iterator) - - expect(Logger.default.log).toHaveBeenLastCalledWith('Re-trying request /global-request in 150ms') - - jest.runOnlyPendingTimers() - - expectHttpCall(3, 'app.china', '/global-request') - - return Utils.flushPromises() - }).then(() => { expect(iterator.next).toHaveReturnedWith({ value: undefined, done: true }) expect(iterator.reset).toHaveBeenCalled() - expect(iterator.next).toHaveReturnedWith({ value: testEndpoints.default, done: false }) + expect(iterator.next).toHaveReturnedWith({ value: getIteratorValueFromEndpoint(testEndpoints.default), done: false }) clearIteratorMock(iterator) expect(Logger.default.log).toHaveBeenLastCalledWith('Re-trying request /global-request in 60000ms') jest.runOnlyPendingTimers() - expectHttpCall(4, 'app.default', '/global-request') + expectHttpCall(3, 'app.default', '/global-request') return Utils.flushPromises() }).then(() => { - expect(iterator.next).toHaveReturnedWith({ value: testEndpoints.india, done: false }) + expect(iterator.next).toHaveReturnedWith({ value: getIteratorValueFromEndpoint(testEndpoints.world), done: false }) clearIteratorMock(iterator) expect(Logger.default.log).toHaveBeenLastCalledWith('Re-trying request /global-request in 150ms') jest.runOnlyPendingTimers() - expectHttpCall(5, 'app.india', '/global-request') + expectHttpCall(4, 'app.world', '/global-request') return Utils.flushPromises() }) .then(() => { - expect(iterator.next).toHaveReturnedWith({ value: testEndpoints.china, done: false }) - clearIteratorMock(iterator) - - expect(Logger.default.log).toHaveBeenLastCalledWith('Re-trying request /global-request in 150ms') - - jest.runOnlyPendingTimers() - - expectHttpCall(6, 'app.china', '/global-request') - - return Utils.flushPromises() - }).then(() => { expect(iterator.next).toHaveReturnedWith({ value: undefined, done: true }) expect(iterator.reset).toHaveBeenCalled() - expect(iterator.next).toHaveReturnedWith({ value: testEndpoints.default, done: false }) + expect(iterator.next).toHaveReturnedWith({ value: getIteratorValueFromEndpoint(testEndpoints.default), done: false }) clearIteratorMock(iterator) expect(Logger.default.log).toHaveBeenLastCalledWith('Re-trying request /global-request in 60000ms') jest.runOnlyPendingTimers() - expectHttpCall(7, 'app.default', '/global-request') + expectHttpCall(5, 'app.default', '/global-request') http.default.mockResolvedValue({}) // let http successfully resolve next time return Utils.flushPromises() }) .then(() => { - expect(iterator.next).toHaveReturnedWith({ value: testEndpoints.india, done: false }) + expect(iterator.next).toHaveReturnedWith({ value: getIteratorValueFromEndpoint(testEndpoints.world), done: false }) clearIteratorMock(iterator) expect(Logger.default.log).toHaveBeenLastCalledWith('Re-trying request /global-request in 150ms') jest.runOnlyPendingTimers() - expectHttpCall(8, 'app.india', '/global-request') + expectHttpCall(6, 'app.world', '/global-request') return Utils.flushPromises() }) @@ -1613,7 +1601,7 @@ describe('test request functionality', () => { expect.assertions(14) expect(UrlStartegy.getBaseUrlsIterator).toHaveBeenCalled() - expect(iterator.next).toHaveReturnedWith({ value: testEndpoints.default, done: false }) + expect(iterator.next).toHaveReturnedWith({ value: getIteratorValueFromEndpoint(testEndpoints.default), done: false }) clearIteratorMock(iterator) expect(Logger.default.log).toHaveBeenLastCalledWith('Trying request /global-request in 150ms') diff --git a/src/sdk/__tests__/sdk-click.spec.js b/src/sdk/__tests__/sdk-click.spec.js index a52da2ba..ca22879d 100644 --- a/src/sdk/__tests__/sdk-click.spec.js +++ b/src/sdk/__tests__/sdk-click.spec.js @@ -64,7 +64,7 @@ describe('test sdk-click functionality', () => { } const fullConfig = { - endpoint: 'app', + endpoint: 'app.default', ...requestConfig, params: { attempts: 1, @@ -113,7 +113,7 @@ describe('test sdk-click functionality', () => { } const fullConfig = { - endpoint: 'app', + endpoint: 'app.default', ...requestConfig, params: { attempts: 1, diff --git a/src/sdk/__tests__/session.spec.js b/src/sdk/__tests__/session.spec.js index ea3b210f..4496e241 100644 --- a/src/sdk/__tests__/session.spec.js +++ b/src/sdk/__tests__/session.spec.js @@ -253,7 +253,7 @@ describe('test session functionality', () => { expect(setInterval).toHaveBeenCalledTimes(1) expect(activityState.timeSpent).toBe(0) expect(activityState.sessionLength).toBe(0) - expect(activityState.lastInterval).toEqual(-1) + expect(activityState.lastInterval).toBe(-1) return Utils.flushPromises() .then(() => { @@ -379,7 +379,7 @@ describe('test session functionality', () => { jest.runOnlyPendingTimers() expect(http.default).toHaveBeenCalledWith({ - endpoint: 'app', + endpoint: 'app.default', url: '/session', method: 'POST', params: { @@ -450,7 +450,7 @@ describe('test session functionality', () => { jest.advanceTimersByTime(150) expect(http.default).toHaveBeenCalledWith({ - endpoint: 'app', + endpoint: 'app.default', url: '/session', method: 'POST', params: { @@ -579,7 +579,7 @@ describe('test session functionality', () => { expect(clearInterval).toHaveBeenCalledTimes(1) expect(activityState.timeSpent).toBe(0) expect(activityState.sessionLength).toBe(0) - expect(activityState.lastInterval).toEqual(-1) + expect(activityState.lastInterval).toBe(-1) PubSub.publish('session:finished', {adid: 'bla'}) jest.runOnlyPendingTimers() @@ -785,7 +785,7 @@ describe('test session functionality', () => { jest.runOnlyPendingTimers() expect(http.default).toHaveBeenCalledWith({ - endpoint: 'app', + endpoint: 'app.default', url: '/session', method: 'POST', params: { @@ -857,7 +857,7 @@ describe('test session functionality', () => { jest.runOnlyPendingTimers() expect(http.default).toHaveBeenCalledWith({ - endpoint: 'app', + endpoint: 'app.default', url: '/session', method: 'POST', params: { @@ -961,7 +961,7 @@ describe('test session functionality', () => { jest.runOnlyPendingTimers() expect(http.default).toHaveBeenCalledWith({ - endpoint: 'app', + endpoint: 'app.default', url: '/session', method: 'POST', params: { diff --git a/src/sdk/__tests__/smart-banner/detect-os.spec.ts b/src/sdk/__tests__/smart-banner/detect-os.spec.ts deleted file mode 100644 index 8d0a80d5..00000000 --- a/src/sdk/__tests__/smart-banner/detect-os.spec.ts +++ /dev/null @@ -1,42 +0,0 @@ -import { getDeviceOS, DeviceOS } from '../../smart-banner/detect-os' - -describe('Returns recognizable device OS', () => { - afterAll(() => { - jest.restoreAllMocks() - }) - - function mockUserAgent(userAgent: string) { - Utils.setGlobalProp(global.navigator, 'userAgent') - jest.spyOn(global.navigator, 'userAgent', 'get').mockReturnValue(userAgent) - } - - const testSet: [string, DeviceOS | undefined][] = [ - [ - 'Mozilla/5.0 (Windows Mobile 10; Android 8.0.0; Microsoft; Lumia 950XL) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.92 Mobile Safari/537.36 Edge/40.15254.369', - DeviceOS.WindowsPhone - ], - [ - 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.169 Safari/537.36', - DeviceOS.WindowsPC - ], - [ - 'Mozilla/5.0 (Linux; Android 10; SAMSUNG SM-G975U) AppleWebKit/537.36 (KHTML, like Gecko) SamsungBrowser/11.1 Chrome/75.0.3770.143 Mobile Safari/537.36', - DeviceOS.Android - ], - [ - 'Mozilla/5.0 (iPad; CPU OS 12_4_5 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/12.1.2 Mobile/15E148 Safari/604.1', - DeviceOS.iOS - ], - [ - 'Mozilla/5.0 (X11; Linux x86_64; rv:45.0) Gecko/20100101 Thunderbird/45.8.0', - undefined - ], - ] - - - test.each(testSet)('getDeviceOS() for %s returns %s', (userAgent: string, expected: DeviceOS) => { - mockUserAgent(userAgent) - expect(getDeviceOS()).toEqual(expected) - }) - -}) diff --git a/src/sdk/__tests__/smart-banner/network/api.spec.ts b/src/sdk/__tests__/smart-banner/network/api.spec.ts deleted file mode 100644 index fb2000a4..00000000 --- a/src/sdk/__tests__/smart-banner/network/api.spec.ts +++ /dev/null @@ -1,88 +0,0 @@ -import Logger from '../../../logger' -import { DeviceOS } from '../../../smart-banner/detect-os' -import { fetchSmartBannerData, Position } from '../../../smart-banner/api' -import { Network } from '../../../smart-banner/network/network' - -jest.mock('../../../logger') - -describe('Smart banner API tests', () => { - describe('fetchSmartBannerData', () => { - const webToken = 'abc123' - const platform = DeviceOS.iOS - - const serverResponseMock = { - platform: 'ios', - position: 'bottom', - tracker_token: 'none', - title: 'Run App Name', - description: 'You can run or install App Name', - button_label: 'Go!' - } - - const testNetwork: Network = { - endpoint: 'test-endpoint', - request: jest.fn() - } - - let requestSpy: jest.SpyInstance - - beforeAll(() => { - jest.spyOn(Logger, 'error') - - requestSpy = jest.spyOn(testNetwork, 'request') - }) - - afterEach(() => { - jest.clearAllMocks() - }) - - it('returns data when request is succesfull', async () => { - expect.assertions(2) - requestSpy.mockResolvedValueOnce([serverResponseMock]) - - const smartBannerData = await fetchSmartBannerData(webToken, platform, testNetwork) - - expect(smartBannerData).not.toBeNull() - expect(smartBannerData).toEqual({ - appId: '', - appName: '', - position: Position.Bottom, - header: serverResponseMock.title, - description: serverResponseMock.description, - buttonText: serverResponseMock.button_label, - trackerToken: serverResponseMock.tracker_token, - dismissInterval: 24 * 60 * 60 * 1000, // 1 day in millis before show banner next time - }) - }) - - it('returns null when no banners for platform', async () => { - expect.assertions(1) - requestSpy.mockResolvedValueOnce([{ ...serverResponseMock, platform: 'android' }]) - - const smartBannerData = await fetchSmartBannerData(webToken, platform, testNetwork) - - expect(smartBannerData).toBeNull() - }) - - it('returns null when response invalid', async () => { - expect.assertions(1) - requestSpy.mockResolvedValueOnce([{ ...serverResponseMock, title: '' }]) - - const smartBannerData = await fetchSmartBannerData(webToken, platform, testNetwork) - - expect(smartBannerData).toBeNull() - }) - - it('returns null when network error occurred', async () => { - expect.assertions(2) - - const error = { status: 404, message: 'Not found' } - requestSpy.mockRejectedValueOnce(error) - - const smartBannerData = await fetchSmartBannerData(webToken, platform, testNetwork) - - expect(smartBannerData).toBeNull() - expect(Logger.error).toHaveBeenCalledWith('Network error occurred during loading Smart Banner: ' + JSON.stringify(error)) - }) - }) -}) diff --git a/src/sdk/__tests__/smart-banner/network/url-startegy-network.spec.ts b/src/sdk/__tests__/smart-banner/network/url-startegy-network.spec.ts deleted file mode 100644 index 491f484b..00000000 --- a/src/sdk/__tests__/smart-banner/network/url-startegy-network.spec.ts +++ /dev/null @@ -1,111 +0,0 @@ -import { Network } from '../../../smart-banner/network/network' -import { NetworkWithUrlStrategy } from '../../../smart-banner/network/url-startegy-network' -import { UrlStrategy } from '../../../smart-banner/network/url-strategy/url-strategy' -import { UrlStrategyFactory } from '../../../smart-banner/network/url-strategy/url-strategy-factory' - -jest.mock('../../../logger') - -describe('NetworkWithUrlStrategy', () => { - - const baseUrls = { - endpointName: 'test', - app: 'app.test', - gdpr: 'gdpr.test' - } - - const urlStrategyMock = new UrlStrategy(() => [baseUrls]) - - const networkMock: Network = { - endpoint: '', - request: (_: string, __?: Record) => Promise.resolve('all good') as any - } - - describe('instantiation', () => { - - beforeAll(() => { - jest.resetAllMocks() - jest.spyOn(UrlStrategyFactory, 'create').mockImplementation(() => urlStrategyMock) - }) - - afterEach(() => { - jest.clearAllMocks() - }) - - it('could be instantiated with provided UrlStrategy', () => { - const network = new NetworkWithUrlStrategy(networkMock, { urlStrategy: urlStrategyMock }) - - expect(UrlStrategyFactory.create).not.toHaveBeenCalled() - expect(network).toBeInstanceOf(NetworkWithUrlStrategy) - }) - - it('could be instantiated with UrlStrategyConfig', () => { - const urlStrategyConfig = {} - const network = new NetworkWithUrlStrategy(networkMock, { urlStrategyConfig }) - - expect(UrlStrategyFactory.create).toHaveBeenCalledWith(urlStrategyConfig) - expect(network).toBeInstanceOf(NetworkWithUrlStrategy) - }) - }) - - describe('request', () => { - beforeAll(() => { - jest.spyOn(networkMock, 'request') - jest.spyOn(urlStrategyMock, 'retries') - }) - - afterEach(() => { - jest.clearAllMocks() - }) - - it('sends request with inner Network instance and uses UrlStrategy retries', async () => { - expect.assertions(3) - - const network = new NetworkWithUrlStrategy(networkMock, { urlStrategy: urlStrategyMock }) - const result = await network.request('/whatever', { foo: 'bar', n: 42 }) - - expect(result).toBe('all good') - expect(urlStrategyMock.retries).toHaveBeenCalled() - expect(networkMock.request).toHaveBeenCalledWith('/whatever', { foo: 'bar', n: 42 }) - }) - }) - - describe('endpoint property', () => { - beforeAll(() => { - jest.spyOn(networkMock, 'request') - jest.spyOn(urlStrategyMock, 'retries') - }) - - afterEach(() => { - jest.clearAllMocks() - }) - - const defaultEndpoint = 'https://app.adjust.com' - - it('returns default endpoint before the first request', () => { - const network = new NetworkWithUrlStrategy(networkMock, { urlStrategy: urlStrategyMock }) - - expect(network.endpoint).toEqual(defaultEndpoint) - }) - - it('returns last endpoint after successful request', async () => { - const network = new NetworkWithUrlStrategy(networkMock, { urlStrategy: urlStrategyMock }) - - await network.request('/whatever') - - expect(network.endpoint).toEqual(baseUrls.app) - }) - - it('returns default endpoint after failed request', async () => { - const network = new NetworkWithUrlStrategy(networkMock, { urlStrategy: urlStrategyMock }) - jest.spyOn(networkMock, 'request').mockRejectedValueOnce('Error!') - - try { - await network.request('/whatever') - } catch (err) { - // nothing to do here - } - - expect(network.endpoint).toEqual(defaultEndpoint) - }) - }) -}) diff --git a/src/sdk/__tests__/smart-banner/network/url-strategy/blocked-url-bypass.spec.ts b/src/sdk/__tests__/smart-banner/network/url-strategy/blocked-url-bypass.spec.ts deleted file mode 100644 index 521aec6f..00000000 --- a/src/sdk/__tests__/smart-banner/network/url-strategy/blocked-url-bypass.spec.ts +++ /dev/null @@ -1,54 +0,0 @@ -import { BlockedUrlBypass } from '../../../../smart-banner/network/url-strategy/blocked-url-bypass' -import { BaseUrlsMap } from '../../../../smart-banner/network/url-strategy/url-strategy' - -describe('BlockedUrlBypass', () => { - const testEndpoints: Record = { - [BlockedUrlBypass.Default]: { - endpointName: BlockedUrlBypass.Default, - app: 'app', - gdpr: 'gdpr' - }, - [BlockedUrlBypass.India]: { - endpointName: 'Indian', - app: 'app.adjust.net.in', - gdpr: 'gdpr.adjust.net.in' - }, - [BlockedUrlBypass.China]: { - endpointName: 'Chinese', - app: 'app.adjust.world', - gdpr: 'gdpr.adjust.world' - } - } - - it.each([ - [BlockedUrlBypass.China, 2, [BlockedUrlBypass.Default]], - [BlockedUrlBypass.India, 2, [BlockedUrlBypass.Default]], - [BlockedUrlBypass.Default, 3, [BlockedUrlBypass.India, BlockedUrlBypass.China]], - ])('returns urls map array depending on strategy', (strategy: BlockedUrlBypass.Strategy, retriesNumber, nextEndpoints) => { - const resultingFn = BlockedUrlBypass.preferredUrlsGetter(strategy, testEndpoints) - - expect(resultingFn).toEqual(expect.any(Function)) - - const baseUrlsMap = resultingFn() - - expect(baseUrlsMap.length).toEqual(retriesNumber) - expect(baseUrlsMap[0]).toEqual(testEndpoints[strategy]) - - for (let i = 0; i < nextEndpoints.length; i++) { - expect(baseUrlsMap[i + 1]).toEqual(testEndpoints[nextEndpoints[i]]) - } - }) - - it('returns default strategy if option is undefined', () => { - const resultingFn = BlockedUrlBypass.preferredUrlsGetter(undefined, testEndpoints) - - expect(resultingFn).toEqual(expect.any(Function)) - - const baseUrlsMap = resultingFn() - - expect(baseUrlsMap.length).toBe(3) - expect(baseUrlsMap[0]).toEqual(testEndpoints[BlockedUrlBypass.Default]) - expect(baseUrlsMap[1]).toEqual(testEndpoints[BlockedUrlBypass.India]) - expect(baseUrlsMap[2]).toEqual(testEndpoints[BlockedUrlBypass.China]) - }) -}) diff --git a/src/sdk/__tests__/smart-banner/network/url-strategy/custom-url.spec.ts b/src/sdk/__tests__/smart-banner/network/url-strategy/custom-url.spec.ts deleted file mode 100644 index 678516a6..00000000 --- a/src/sdk/__tests__/smart-banner/network/url-strategy/custom-url.spec.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { CustomUrl } from '../../../../smart-banner/network/url-strategy/custom-url' - -describe('CustomUrl', () => { - it('returns urls map with custom url', () => { - const url = 'custom.url' - - const expectedUrlMap = { - endpointName: `Custom (${url})`, - app: url, - gdpr: url - } - - const resultingFn = CustomUrl.preferredUrlsGetter(url) - - expect(resultingFn).toEqual(expect.any(Function)) - expect(resultingFn()).toEqual([expectedUrlMap]) - }) -}) diff --git a/src/sdk/__tests__/smart-banner/network/url-strategy/data-residency.spec.ts b/src/sdk/__tests__/smart-banner/network/url-strategy/data-residency.spec.ts deleted file mode 100644 index a7dc3fc2..00000000 --- a/src/sdk/__tests__/smart-banner/network/url-strategy/data-residency.spec.ts +++ /dev/null @@ -1,38 +0,0 @@ -import { DataResidency } from '../../../../smart-banner/network/url-strategy/data-residency' -import { BaseUrlsMap } from '../../../../smart-banner/network/url-strategy/url-strategy' - -describe('DataResidency', () => { - - const testEndpoints: Record = { - [DataResidency.EU]: { - endpointName: 'EU', - app: 'app.eu', - gdpr: 'gdpr.eu' - }, - [DataResidency.TR]: { - endpointName: 'TR', - app: 'app.tr', - gdpr: 'gdpr.tr' - }, - [DataResidency.US]: { - endpointName: 'US', - app: 'app.us', - gdpr: 'gdpr.us' - } - } - - it.each([ - DataResidency.EU, - DataResidency.TR, - DataResidency.US - ])('returns urls map depending on region', (dataResidency: DataResidency.Region) => { - const resultingFn = DataResidency.preferredUrlsGetter(dataResidency, testEndpoints) - - expect(resultingFn).toEqual(expect.any(Function)) - - const baseUrlsMap = resultingFn() - - expect(baseUrlsMap.length).toBe(1) - expect(baseUrlsMap[0]).toEqual(testEndpoints[dataResidency]) - }) -}) diff --git a/src/sdk/__tests__/smart-banner/network/url-strategy/url-strategy-factory.spec.ts b/src/sdk/__tests__/smart-banner/network/url-strategy/url-strategy-factory.spec.ts deleted file mode 100644 index 7f0eeb94..00000000 --- a/src/sdk/__tests__/smart-banner/network/url-strategy/url-strategy-factory.spec.ts +++ /dev/null @@ -1,81 +0,0 @@ -import Logger from '../../../../logger' -import * as UrlStrategyModule from '../../../../smart-banner/network/url-strategy/url-strategy' -import { UrlStrategyFactory } from '../../../../smart-banner/network/url-strategy/url-strategy-factory' -import { BlockedUrlBypass } from '../../../../smart-banner/network/url-strategy//blocked-url-bypass' -import { CustomUrl } from '../../../../smart-banner/network/url-strategy//custom-url' -import { DataResidency } from '../../../../smart-banner/network/url-strategy//data-residency' - -jest.mock('../../../../logger') - -describe('UrlStrategyFactory', () => { - const urlStrategyConstructorMock = jest.fn() - - const urlsMap = { endpointName: 'foo.bar', app: 'app', gdpr: 'gdpr' } - - const customUrlMock = () => [urlsMap] - const dataResidencyMock = () => [urlsMap] - const blockedUrlBypassMock = () => [urlsMap] - - beforeAll(() => { - jest.spyOn(UrlStrategyModule, 'UrlStrategy').mockImplementation(urlStrategyConstructorMock) - jest.spyOn(CustomUrl, 'preferredUrlsGetter').mockImplementation(() => customUrlMock) - jest.spyOn(DataResidency, 'preferredUrlsGetter').mockImplementation(() => dataResidencyMock) - jest.spyOn(BlockedUrlBypass, 'preferredUrlsGetter').mockImplementation(() => blockedUrlBypassMock) - }) - - afterEach(() => { - jest.clearAllMocks() - }) - - it('creates CustomStrategy if customUrl is set in config', () => { - UrlStrategyFactory.create({ customUrl: 'custom.url' }) - - expect(CustomUrl.preferredUrlsGetter).toHaveBeenCalledWith('custom.url') - expect(urlStrategyConstructorMock).toHaveBeenCalledWith(customUrlMock) - }) - - it.each([ - DataResidency.EU, - DataResidency.TR, - DataResidency.US - ])('creates DataResidency if dataResidency is set in config', (region: DataResidency.Region) => { - UrlStrategyFactory.create({ dataResidency: region }) - - expect(DataResidency.preferredUrlsGetter).toHaveBeenCalledWith(region) - expect(urlStrategyConstructorMock).toHaveBeenCalledWith(dataResidencyMock) - }) - - it.each([ - BlockedUrlBypass.China, - BlockedUrlBypass.India - ])('creates BlockedUrlBypass if urlStrategy is set in config', (strategy: BlockedUrlBypass.Strategy) => { - UrlStrategyFactory.create({ urlStrategy: strategy }) - - expect(BlockedUrlBypass.preferredUrlsGetter).toHaveBeenCalledWith(strategy) - expect(urlStrategyConstructorMock).toHaveBeenCalledWith(blockedUrlBypassMock) - }) - - it('creates BlockedUrlBypass if config is empty', () => { - UrlStrategyFactory.create({}) - - expect(BlockedUrlBypass.preferredUrlsGetter).toHaveBeenCalled() - expect(urlStrategyConstructorMock).toHaveBeenCalledWith(blockedUrlBypassMock) - }) - - it.each([ - [BlockedUrlBypass.China, DataResidency.EU], - [BlockedUrlBypass.China, DataResidency.US], - [BlockedUrlBypass.China, DataResidency.TR], - [BlockedUrlBypass.India, DataResidency.EU], - [BlockedUrlBypass.India, DataResidency.US], - [BlockedUrlBypass.India, DataResidency.TR] - ])('prefers DataResidency and prints warning if both dataResidency and urlStartegy are set in config', (strategy, region) => { - jest.spyOn(Logger, 'warn') - - UrlStrategyFactory.create({ urlStrategy: strategy, dataResidency: region } as any) - - expect(DataResidency.preferredUrlsGetter).toHaveBeenCalledWith(region) - expect(urlStrategyConstructorMock).toHaveBeenCalledWith(dataResidencyMock) - expect(Logger.warn).toHaveBeenCalledWith('Both dataResidency and urlStrategy are set in config, urlStrategy will be ignored') - }) -}) diff --git a/src/sdk/__tests__/smart-banner/network/url-strategy/url-strategy.spec.ts b/src/sdk/__tests__/smart-banner/network/url-strategy/url-strategy.spec.ts deleted file mode 100644 index 2fc1231c..00000000 --- a/src/sdk/__tests__/smart-banner/network/url-strategy/url-strategy.spec.ts +++ /dev/null @@ -1,114 +0,0 @@ -import Logger from '../../../../logger' -import { BaseUrlsMap, UrlStrategy } from '../../../../smart-banner/network/url-strategy/url-strategy' -import { NoConnectionError } from '../../../../smart-banner/network/errors' - -jest.mock('../../../../logger') - -describe('UrlStrategy', () => { - const urls: BaseUrlsMap[] = [{ - endpointName: 'foo', - app: 'foo', - gdpr: 'foo' - }, { - endpointName: 'bar', - app: 'bar', - gdpr: 'bar' - }] - - const preferredUrlsMock = jest.fn() - const testedUrlStrategy = new UrlStrategy(preferredUrlsMock) - const sendRequestMock = jest.fn() - - beforeAll(() => { - jest.spyOn(Logger, 'error') - jest.spyOn(Logger, 'log') - }) - - afterEach(() => { - jest.clearAllMocks() - }) - - describe('preferredUrls sanity check', () => { - it('throws error if there is no enpoint defined', async () => { - preferredUrlsMock.mockImplementation(() => undefined) - - expect.assertions(2) - - try { - await testedUrlStrategy.retries(sendRequestMock) - } catch (err) { - expect(err).toBe(UrlStrategy.NoPreferredUrlsDefinedError) - expect(Logger.error).toHaveBeenCalledWith(UrlStrategy.NoPreferredUrlsDefinedError.message) - } - }) - - it('throws error if array of endpoints is empty', async () => { - preferredUrlsMock.mockImplementation(() => []) - - expect.assertions(2) - - try { - await testedUrlStrategy.retries(sendRequestMock) - } catch (err) { - expect(err).toBe(UrlStrategy.NoPreferredUrlsDefinedError) - expect(Logger.error).toHaveBeenCalledWith(UrlStrategy.NoPreferredUrlsDefinedError.message) - } - }) - }) - - describe('retries functionality', () => { - beforeAll(() => { - preferredUrlsMock.mockImplementation(() => urls) - }) - - it('tries to reach next endpoint if could not connect', async () => { - sendRequestMock - .mockRejectedValueOnce(NoConnectionError) - .mockResolvedValueOnce('all good') - - expect.assertions(4) - - const result = await testedUrlStrategy.retries(sendRequestMock) - - expect(sendRequestMock).toHaveBeenCalledTimes(urls.length) - expect(Logger.log).toHaveBeenCalledWith(`Failed to connect ${urls[0].endpointName} endpoint`) - expect(Logger.log).toHaveBeenCalledWith(`Trying ${urls[1].endpointName} one`) - expect(result).toBe('all good') - }) - - it('re-throws if there is no available endpoint', async () => { - sendRequestMock.mockRejectedValue(NoConnectionError) - - expect.assertions(6) - - try { - await testedUrlStrategy.retries(sendRequestMock) - } - catch (err) { - expect(err).toEqual(NoConnectionError) - } - - expect(sendRequestMock).toHaveBeenCalledTimes(urls.length) - expect(Logger.log).toHaveBeenCalledWith(`Failed to connect ${urls[0].endpointName} endpoint`) - expect(Logger.log).toHaveBeenCalledWith(`Trying ${urls[1].endpointName} one`) - expect(Logger.log).toHaveBeenCalledWith(`Failed to connect ${urls[1].endpointName} endpoint`) - expect(testedUrlStrategy.retries).toThrow() - }) - - it('re-throws if other error occured', async () => { - sendRequestMock.mockRejectedValue({ status: 404, message: 'not found' }) - - expect.assertions(3) - - try { - await testedUrlStrategy.retries(sendRequestMock) - } - catch (err) { - expect(err).toEqual({ status: 404, message: 'not found' }) - } - - expect(sendRequestMock).toHaveBeenCalledTimes(1) - expect(testedUrlStrategy.retries).toThrow() - }) - }) -}) diff --git a/src/sdk/__tests__/smart-banner/network/xhr-network.spec.ts b/src/sdk/__tests__/smart-banner/network/xhr-network.spec.ts deleted file mode 100644 index 0247c302..00000000 --- a/src/sdk/__tests__/smart-banner/network/xhr-network.spec.ts +++ /dev/null @@ -1,86 +0,0 @@ -import { XhrNetwork } from '../../../smart-banner/network/xhr-network' -import { NoConnectionError } from '../../../smart-banner/network/errors' - -jest.mock('../../../logger') - -describe('XhrNetwork tests', () => { - const testEndpoint = 'test.test' - - const xhrMock: Partial = { - open: jest.fn(), - setRequestHeader: jest.fn(), - send: jest.fn(), - onerror: jest.fn(), - onreadystatechange: jest.fn() - } - - const testedNetwork = new XhrNetwork(testEndpoint) - - beforeAll(() => { - jest.spyOn(window, 'XMLHttpRequest').mockImplementation(() => xhrMock as XMLHttpRequest) - }) - - afterEach(() => { - jest.clearAllMocks() - }) - - describe('request method', () => { - it('sends request to path with encoded params', async () => { - expect.assertions(1) - - testedNetwork.request('/whatever', { foo: 'bar', n: 42 }) - - const expectedUrl = `${testEndpoint}/whatever?foo=bar&n=42` - - expect(xhrMock.open).toHaveBeenCalledWith('GET', expectedUrl) - }) - - it('sends request to path without params', async () => { - expect.assertions(1) - - testedNetwork.request('/whatever') - - const expectedUrl = `${testEndpoint}/whatever` - - expect(xhrMock.open).toHaveBeenCalledWith('GET', expectedUrl) - }) - - it('throws NoConnectionError if request failed due to network connection issue', async () => { - expect.assertions(1) - - jest.spyOn(xhrMock, 'send').mockImplementationOnce(() => { (xhrMock as any).onerror() }) - - try { - await testedNetwork.request('/whatever') - } catch (error) { - expect(error).toEqual(NoConnectionError) - } - }) - - it('throws an error if request failed', async () => { - expect.assertions(1) - - const err = { status: 400, message: 'Bad request' } - - jest.spyOn(xhrMock, 'send').mockImplementationOnce(() => { - const xhrFailedMock = xhrMock as any - xhrFailedMock.readyState = 4 - xhrFailedMock.status = err.status - xhrFailedMock.responseText = err.message - xhrFailedMock.onreadystatechange() - }) - - try { - await testedNetwork.request('/whatever') - } catch (error) { - expect(error).toEqual(err) - } - }) - }) - - describe('endpoint property', () => { - it('returns endpoint', () => { - expect(testedNetwork.endpoint).toEqual(testEndpoint) - }) - }) -}) diff --git a/src/sdk/__tests__/smart-banner/smart-banner.spec.ts b/src/sdk/__tests__/smart-banner/smart-banner.spec.ts deleted file mode 100644 index 018e4d90..00000000 --- a/src/sdk/__tests__/smart-banner/smart-banner.spec.ts +++ /dev/null @@ -1,282 +0,0 @@ -import Logger from '../../logger' -import * as Api from '../../smart-banner/api' -import * as DetectOS from '../../smart-banner/detect-os' -import { StorageFactory } from '../../smart-banner/storage/factory' -import { LocalStorage } from '../../smart-banner/storage/local-storage' -import { SmartBanner } from '../../smart-banner/smart-banner' - -jest.mock('../../logger') - -jest.useFakeTimers() - -const storage = new LocalStorage -jest.spyOn(StorageFactory, 'createStorage').mockImplementation(() => storage) - -describe('Smart Banner tests', () => { - const webToken = 'abc123' - const defaultDismissInterval = 60 * 60 * 1000 // 1 hour in millis - const platform = DetectOS.DeviceOS.iOS - const bannerData: Api.SmartBannerData = { - appId: 'none', - appName: 'Adjust Web SDK', - header: 'Adjust Smart Banners', - description: 'Not so smart actually, but deeplinks do the magic anyway', - buttonText: 'Let\'s go!', - dismissInterval: defaultDismissInterval, - position: Api.Position.Top, - trackerToken: 'abcd' - } - const onCreatedCallbackSpy = jest.fn() - const onDismissedCallbackSpy = jest.fn() - - let smartBanner - - beforeAll(() => { - jest.spyOn(document, 'createElement') - jest.spyOn(Logger, 'log') - jest.spyOn(Logger, 'error') - jest.spyOn(global, 'setTimeout') - - smartBanner = new SmartBanner({ webToken, onCreated: onCreatedCallbackSpy, onDismissed: onDismissedCallbackSpy }) - }) - - beforeEach(() => { - jest.spyOn(DetectOS, 'getDeviceOS').mockReturnValue(platform) - jest.spyOn(Api, 'fetchSmartBannerData').mockResolvedValue(bannerData) - }) - - afterEach(() => { - jest.clearAllMocks() - }) - - describe('initialisation', () => { - - afterEach(() => { - smartBanner.destroy() - }) - - it(('initialises and renders banner'), async () => { - expect.assertions(6) - - smartBanner.init(webToken) - await Utils.flushPromises() // resolves data fetch promise that allows initialisation to finish - - expect(Logger.log).toHaveBeenCalledWith('Creating Smart Banner') - expect(document.createElement).toHaveBeenCalled() - expect(smartBanner.banner).not.toBeNull() - expect(smartBanner.dismissButton).not.toBeNull() - expect(Logger.log).toHaveBeenCalledWith('Smart Banner created') - expect(onCreatedCallbackSpy).toHaveBeenCalled() - }) - - describe('can not call init repeatedly', () => { - it('initialisation in progress', async () => { - expect.assertions(2) - - smartBanner.init(webToken) // setup - - smartBanner.init(webToken) - - expect(Logger.error).toHaveBeenCalledWith('Smart Banner is initialising already') - - await Utils.flushPromises() // tear down - - expect(onCreatedCallbackSpy).toHaveBeenCalledTimes(1) - }) - - it('initialisation finished', async () => { - expect.assertions(2) - - smartBanner.init(webToken) // setup - await Utils.flushPromises() // allow initialisation to finish - - smartBanner.init(webToken) - - expect(Logger.error).toHaveBeenCalledWith('Smart Banner already exists') - expect(onCreatedCallbackSpy).toHaveBeenCalledTimes(1) - }) - }) - - it('logs message when no banner for platform', async () => { - jest.spyOn(Api, 'fetchSmartBannerData').mockResolvedValueOnce(null) - - expect.assertions(2) - - smartBanner.init(webToken) - await Utils.flushPromises() - - expect(Logger.log).toHaveBeenCalledWith(`No Smart Banners for ${platform} platform found`) - expect(onCreatedCallbackSpy).not.toHaveBeenCalled() - }) - - it('logs message when no target platform', () => { - jest.spyOn(DetectOS, 'getDeviceOS').mockReturnValueOnce(undefined) - - smartBanner.init(webToken) - - expect(Logger.log).toHaveBeenCalledWith('This platform is not one of the targeting ones, Smart Banner will not be shown') - expect(onCreatedCallbackSpy).not.toHaveBeenCalled() - }) - }) - - describe('hide and show', () => { - beforeAll(() => { - jest.spyOn(smartBanner, 'hide') - jest.spyOn(smartBanner, 'show') - }) - - describe('Smart Banner initialised', () => { - beforeEach(() => { - smartBanner.init(webToken) - return Utils.flushPromises() // resolves data fetch promise that allows initialisation to finish - .then(() => { - jest.spyOn(smartBanner.banner, 'hide') - jest.spyOn(smartBanner.banner, 'show') - }) - }) - - afterEach(() => { - smartBanner.destroy() - }) - - it('hides banner', () => { - smartBanner.hide() - - expect(smartBanner.banner.hide).toHaveBeenCalled() - }) - - it('shows banner', () => { - smartBanner.show() - - expect(smartBanner.banner.show).toHaveBeenCalled() - }) - }) - - describe('Smart Banner is still initialising', () => { - afterEach(() => { - smartBanner.destroy() - jest.clearAllMocks() - }) - - it('logs a message when hide called and hides after initialisation finished', async () => { - expect.assertions(3) - - smartBanner.init() - smartBanner.hide() - - expect(Logger.log).toHaveBeenCalledWith('Smart Banner will be hidden after initialisation finished') - - await Utils.flushPromises() // resolves data fetch promise that allows initialisation to finish - - expect(Logger.log).toHaveBeenCalledWith('Initialisation finished, hiding Smart Banner') - expect(smartBanner.hide).toHaveBeenCalledTimes(2) - }) - - it('logs a message when show called and shows after initialisation finished', async () => { - expect.assertions(3) - - smartBanner.init() - smartBanner.show() - - expect(Logger.log).toHaveBeenCalledWith('Smart Banner will be shown after initialisation finished') - - await Utils.flushPromises() // resolves data fetch promise that allows initialisation to finish - - expect(Logger.log).toHaveBeenCalledWith('Initialisation finished, showing Smart Banner') - expect(smartBanner.show).toHaveBeenCalledTimes(2) - }) - }) - - describe('Smart Banner was not initialised', () => { - it('logs an error when hide called', () => { - smartBanner.hide() - - expect(Logger.error).toHaveBeenCalledWith('There is no Smart Banner to hide, have you called initialisation?') - }) - - it('logs an error when show called', () => { - smartBanner.show() - - expect(Logger.error).toHaveBeenCalledWith('There is no Smart Banner to show, have you called initialisation?') - }) - }) - }) - - describe('dismiss', () => { - const now = Date.now() - - beforeAll(() => { - jest.spyOn(Date, 'now').mockReturnValue(now) - jest.spyOn(storage, 'setItem') - jest.spyOn(smartBanner, 'init') - jest.spyOn(smartBanner, 'destroy') - }) - - beforeEach(() => { - smartBanner.init() - return Utils.flushPromises() - .then(() => { - jest.clearAllMocks() - - smartBanner.dismiss(webToken, defaultDismissInterval) - }) - }) - - afterEach(() => { - localStorage.clear() - }) - - it('banner removed from DOM when dismissed', () => { - expect.assertions(8) - - expect(storage.setItem).toHaveBeenCalledWith(smartBanner.STORAGE_KEY_DISMISSED, now) // add timestamp in Local Storage - - expect(Logger.log).toHaveBeenCalledWith('Smart Banner dismissed') - expect(smartBanner.destroy).toHaveBeenCalled() - - expect(setTimeout).toHaveBeenCalledWith(expect.any(Function), defaultDismissInterval) // next initialisation scheduled - expect(Logger.log).toHaveBeenCalledWith('Smart Banner creation scheduled on ' + new Date(now + defaultDismissInterval)) - - expect(Logger.log).toHaveBeenCalledWith('Smart Banner removed') // banner removed from DOM - expect(smartBanner.banner).toBeNull() - - expect(onDismissedCallbackSpy).toHaveBeenCalled() - }) - - it('intialisation reschedules banner display when dismiss interval has not over', async () => { - expect.assertions(6) - - smartBanner.init(webToken) - await Utils.flushPromises() - - expect(Logger.log).toHaveBeenCalledWith('Smart Banner was dismissed') - expect(Logger.log).toHaveBeenCalledWith('Clearing previously scheduled creation of Smart Banner') - - expect(setTimeout).toHaveBeenCalledWith(expect.any(Function), defaultDismissInterval) // initialisation scheduled - expect(Logger.log).toHaveBeenCalledWith('Smart Banner creation scheduled on ' + new Date(now + defaultDismissInterval)) - - expect(onCreatedCallbackSpy).not.toHaveBeenCalled() - expect(onDismissedCallbackSpy).toHaveBeenCalledTimes(1) - }) - - it('banner is displayed again when dismiss interval is over', async () => { - expect.assertions(7) - - jest.spyOn(Date, 'now').mockReturnValue(now + defaultDismissInterval) - jest.advanceTimersByTime(defaultDismissInterval) - - expect(smartBanner.init).toHaveBeenCalled() - - await Utils.flushPromises() - - expect(Logger.log).toHaveBeenCalledWith('Creating Smart Banner') - expect(document.createElement).toHaveBeenCalled() - expect(smartBanner.banner).not.toBeNull() - expect(smartBanner.dismissButton).not.toBeNull() - expect(Logger.log).toHaveBeenCalledWith('Smart Banner created') - expect(onCreatedCallbackSpy).toHaveBeenCalled() - - smartBanner.destroy() - }) - }) -}) diff --git a/src/sdk/__tests__/smart-banner/storage/factory.spec.ts b/src/sdk/__tests__/smart-banner/storage/factory.spec.ts deleted file mode 100644 index 0f1b2fc3..00000000 --- a/src/sdk/__tests__/smart-banner/storage/factory.spec.ts +++ /dev/null @@ -1,31 +0,0 @@ -import { StorageFactory } from '../../../smart-banner/storage/factory' -import { LocalStorage } from '../../../smart-banner/storage/local-storage' -import { InMemoryStorage } from '../../../smart-banner/storage/in-memory-storage' - -jest.mock('../../../smart-banner/storage/local-storage') -jest.mock('../../../smart-banner/storage/in-memory-storage') - -describe('StorageFactory', () => { - - afterEach(() => { - jest.clearAllMocks() - }) - - it('creates LocalStorage by default', () => { - StorageFactory.createStorage() - - expect(LocalStorage).toHaveBeenCalledTimes(1) - expect(InMemoryStorage).not.toHaveBeenCalled() - }) - - it('creates InMemoryStorage if LocalStorage not supported', () => { - jest.spyOn(window, 'localStorage', 'get').mockImplementationOnce(() => { - throw new Error('EmulatedSecurityError') - }) - - StorageFactory.createStorage() - - expect(InMemoryStorage).toHaveBeenCalledTimes(1) - expect(LocalStorage).not.toHaveBeenCalled() - }) -}) diff --git a/src/sdk/__tests__/smart-banner/storage/in-memory-storage.spec.ts b/src/sdk/__tests__/smart-banner/storage/in-memory-storage.spec.ts deleted file mode 100644 index a62c72e4..00000000 --- a/src/sdk/__tests__/smart-banner/storage/in-memory-storage.spec.ts +++ /dev/null @@ -1,42 +0,0 @@ -import { InMemoryStorage } from '../../../smart-banner/storage/in-memory-storage' - -describe('In-memory storage', () => { - let storage: InMemoryStorage - - beforeEach(() => { - storage = new InMemoryStorage - }) - - const key = 'test' - const value = { data: 'test-data' } - - it('writes record', () => { - storage.setItem(key, value) - - expect(storage['items'][key]).toEqual(value) - }) - - it('reads stored record', () => { - storage['items'][key] = value - - const actual = storage.getItem(key) - - expect(actual).toEqual(value) - }) - - it('removes record', () => { - storage['items'][key] = value - - expect(storage.getItem(key)).toEqual(value) - - storage.removeItem(key) - - expect(storage['items'][key]).toBeUndefined() - }) - - it('returns null when no such record', () => { - const noExistentValue = storage.getItem(key) - - expect(noExistentValue).toBeNull() - }) -}) diff --git a/src/sdk/__tests__/smart-banner/storage/local-storage.spec.ts b/src/sdk/__tests__/smart-banner/storage/local-storage.spec.ts deleted file mode 100644 index 09e86bbb..00000000 --- a/src/sdk/__tests__/smart-banner/storage/local-storage.spec.ts +++ /dev/null @@ -1,61 +0,0 @@ -import { LocalStorage } from '../../../smart-banner/storage/local-storage' - -describe('Local storage', () => { - const storage = new LocalStorage - - beforeAll(() => { - jest.spyOn(localStorage, 'getItem') - jest.spyOn(localStorage, 'setItem') - jest.spyOn(localStorage, 'removeItem') - }) - - afterEach(() => { - localStorage.clear() - jest.clearAllMocks() - }) - - const key = 'test' - const value = { data: 'test-data' } - - const lsKey = `adjust-smart-banner.${key}` - const lsValue = JSON.stringify(value) - - it('writes record', () => { - storage.setItem(key, value) - - expect(localStorage.getItem(lsKey)).toEqual(lsValue) - }) - - it('reads stored record', () => { - localStorage.setItem(lsKey, lsValue) - - const actual = storage.getItem(key) - - expect(actual).toEqual(value) - expect(localStorage.getItem).toHaveBeenCalled() - }) - - it('removes record', () => { - localStorage.setItem(lsKey, lsValue) - - storage.removeItem(key) - - expect(localStorage.getItem(lsKey)).toBeNull() - expect(localStorage.removeItem).toHaveBeenCalled() - }) - - it('returns null when no such record', () => { - const noExistentValue = storage.getItem(key) - - expect(noExistentValue).toBeNull() - expect(localStorage.getItem).toHaveBeenCalled() - }) - - it('does not throw on invalid JSON', () => { - localStorage.setItem(lsKey, '{hello":"world"}') - - let invalidValue - expect(() => { invalidValue = storage.getItem(key) }).not.toThrow() - expect(invalidValue).toBeNull() - }) -}) diff --git a/src/sdk/__tests__/smart-banner/utilities.spec.ts b/src/sdk/__tests__/smart-banner/utilities.spec.ts deleted file mode 100644 index 761787dc..00000000 --- a/src/sdk/__tests__/smart-banner/utilities.spec.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { parseJson } from '../../smart-banner/utilities' - -describe('Utilities tests', () => { - describe('parseJson', () => { - - it('returns parsed object for valid JSON string', () => { - const expectedObj = {key: 'value'} - const stringToParse = '{"key": "value"}' - - expect(parseJson(stringToParse)).toEqual(expectedObj) - }) - - it('returns null for invalid JSON string', () => { - const stringToParse = '{"key": "value}' - - expect(parseJson(stringToParse)).toBeNull() - }) - - it('returns null for no parameter or empty one passed', () => { - expect(parseJson(null)).toBeNull() - expect(parseJson(undefined)).toBeNull() - expect(parseJson('')).toBeNull() - }) - }) -}) diff --git a/src/sdk/__tests__/third-party-sharing.spec.js b/src/sdk/__tests__/third-party-sharing.spec.js deleted file mode 100644 index 2c6aace8..00000000 --- a/src/sdk/__tests__/third-party-sharing.spec.js +++ /dev/null @@ -1,173 +0,0 @@ -import * as ThirdPartySharing from '../third-party-sharing' -import * as Config from '../config' -import * as Queue from '../queue' -import * as http from '../http' -import * as Time from '../time' -import * as Logger from '../logger' -import * as ActivityState from '../activity-state' -import * as Preferences from '../preferences' - -jest.mock('../http') -jest.mock('../logger') -jest.mock('../url-strategy') -jest.useFakeTimers() - -const appOptions = { - appToken: '123abc', - environment: 'sandbox' -} - -function expectRequest () { - const requestConfig = { - url: '/disable_third_party_sharing', - method: 'POST' - } - - const fullConfig = { - endpoint: 'app', - ...requestConfig, - params: { - attempts: 1, - createdAt: 'some-time', - timeSpent: 0, - sessionLength: 0, - sessionCount: 1, - lastInterval: 0 - } - } - - jest.runOnlyPendingTimers() - - expect(Queue.push).toHaveBeenCalledWith(requestConfig) - - const promise = Utils.flushPromises() - .then(() => { - jest.runOnlyPendingTimers() - - expect(http.default).toHaveBeenCalledWith(fullConfig) - - return Utils.flushPromises() - }) - .then(() => { - ThirdPartySharing.finish() - - expect(Logger.default.log).toHaveBeenCalledWith('Third-party sharing opt-out is now finished') - - Queue.push.mockClear() - http.default.mockClear() - }) - - return {promise, assertions: 3} -} - -function expectNotRequest () { - jest.runOnlyPendingTimers() - expect(Queue.push).not.toHaveBeenCalled() -} - -describe('Third-party sharing opt-out functionality', () => { - - beforeAll(() => { - jest.spyOn(Queue, 'push') - jest.spyOn(http, 'default') - jest.spyOn(Time, 'getTimestamp').mockReturnValue('some-time') - jest.spyOn(Logger.default, 'log') - - ActivityState.default.init({uuid: 'some-uuid'}) - }) - - afterEach(() => { - Preferences.setThirdPartySharing(null) - jest.clearAllMocks() - Queue.destroy() - localStorage.clear() - }) - - afterAll(() => { - jest.restoreAllMocks() - Config.default.destroy() - ActivityState.default.destroy() - }) - - it('queue third-party sharing opt-out until sdk is initialised', () => { - - ThirdPartySharing.optOut() - ThirdPartySharing.disable() - - expect(Logger.default.log).toHaveBeenCalledWith('Adjust SDK will run third-party sharing opt-out request after initialisation') - expect(Logger.default.log).toHaveBeenLastCalledWith('Third-party sharing opt-out is now started') - expectNotRequest() - - Config.default.set(appOptions) - ThirdPartySharing.check() - - expect(Logger.default.log).toHaveBeenLastCalledWith('Adjust SDK is running pending third-party sharing opt-out request') - - const a = expectRequest() - - expect.assertions(a.assertions + 4) - - return a.promise - }) - - it('runs third-party sharing opt-out request and prevents subsequent one', () => { - - ThirdPartySharing.optOut() - ThirdPartySharing.disable() - - const a = expectRequest() - - ThirdPartySharing.optOut() - - expect.assertions(a.assertions + 4) - - expect(Logger.default.log).toHaveBeenCalledTimes(2) - expect(Logger.default.log).toHaveBeenCalledWith('Third-party sharing opt-out is now started') - expect(Logger.default.log).toHaveBeenLastCalledWith('Adjust SDK already queued third-party sharing opt-out request') - - return a.promise - .then(() => { - expectNotRequest() - }) - }) - - it('prevents running opt-out request if third-party tracking already disabled', () => { - - Preferences.setThirdPartySharing({reason: 'general', pending: true}) - ThirdPartySharing.optOut() - - expect(Logger.default.log).toHaveBeenCalledTimes(1) - expect(Logger.default.log).toHaveBeenLastCalledWith('Adjust SDK already queued third-party sharing opt-out request') - - Preferences.setThirdPartySharing({reason: 'general'}) - ThirdPartySharing.optOut() - - expect(Logger.default.log).toHaveBeenCalledTimes(2) - expect(Logger.default.log).toHaveBeenLastCalledWith('Third-party sharing opt-out is already done') - }) - - it('prevents third-party-sharing disable process if already started or finished', () => { - - ThirdPartySharing.disable() - - expect(Logger.default.log).toHaveBeenCalledTimes(1) - expect(Logger.default.log).toHaveBeenLastCalledWith('Third-party sharing opt-out is now started') - - ThirdPartySharing.disable() - - expect(Logger.default.log).toHaveBeenCalledTimes(2) - expect(Logger.default.log).toHaveBeenLastCalledWith('Third-party sharing opt-out has already started') - - ThirdPartySharing.finish() - - expect(Logger.default.log).toHaveBeenCalledTimes(3) - expect(Logger.default.log).toHaveBeenLastCalledWith('Third-party sharing opt-out is now finished') - - ThirdPartySharing.finish() - - expect(Logger.default.log).toHaveBeenCalledTimes(4) - expect(Logger.default.log).toHaveBeenLastCalledWith('Third-party sharing opt-out has already finished') - - }) - -}) diff --git a/src/sdk/__tests__/track-third-party-sharing.spec.ts b/src/sdk/__tests__/track-third-party-sharing.spec.ts new file mode 100644 index 00000000..a3d93314 --- /dev/null +++ b/src/sdk/__tests__/track-third-party-sharing.spec.ts @@ -0,0 +1,200 @@ +import { + trackThirdPartySharing, + ThirdPartySharing +} from '../track-third-party-sharing'; +import * as Logger from '../logger' +import * as Queue from '../queue' + +/* eslint-disable @typescript-eslint/no-explicit-any */ + +jest.mock('../logger') + +describe('third party sharing functionality', () => { + beforeAll(() => { + jest.spyOn(Logger.default, 'warn') + jest.spyOn(Logger.default, 'error') + jest.spyOn(Queue, 'push') + }) + + afterAll(() => { + jest.restoreAllMocks() + jest.clearAllTimers() + }) + + describe('ThirdPartySharing class', () => { + it.each([ + //[value, expected, logsWarning] + [true, true, false], + [false, false, false], + [0, false, true], + [undefined, false, true], + [null, false, true], + [1, true, true], + ['string', true, true], + ])('initialises with isEnabled flag', (value, expected, logsWarning) => { + const options = new ThirdPartySharing(value as any) + expect(options.isEnabled).toBe(expected) + + if (logsWarning) { + expect(Logger.default.warn).toHaveBeenCalledWith(`isEnabled should be boolean, converting ${value} results ${expected}`) + } + }) + + describe('granular options', () => { + it('adds granular option', () => { + const options = new ThirdPartySharing(true) + options.addGranularOption('partnerName', 'optionKey', 'value') + + expect(options.granularOptions).toEqual({ 'partnerName': { 'optionKey': 'value' } }) + }) + + it('adds multiple granular options', () => { + const options = new ThirdPartySharing(true) + options.addGranularOption('partner_1', 'optionKey', '1') + options.addGranularOption('partner_2', 'optionKey', '0') + + expect(options.granularOptions).toEqual({ 'partner_1': { 'optionKey': '1' }, 'partner_2': { 'optionKey': '0' } }) + }) + + it('adds multiple granular options to single partnerName', () => { + const options = new ThirdPartySharing(true) + options.addGranularOption('partnerName', 'key_1', '1') + options.addGranularOption('partnerName', 'key_2', '0') + + expect(options.granularOptions).toEqual({ 'partnerName': { 'key_1': '1', 'key_2': '0' } }) + }) + + it('replaces granular option with same partnerName and key', () => { + const options = new ThirdPartySharing(true) + options.addGranularOption('partnerName', 'key', 'first') + options.addGranularOption('partnerName', 'key', 'second') + + expect(options.granularOptions).toEqual({ 'partnerName': { 'key': 'second' } }) + }) + + it.each([ + ['partnerName', 'key', undefined], + ['partnerName', undefined, undefined], + [undefined, 'key', 'value'], + ['partner', '', 'value'], + ['', 'key', 'value'], + ['', '', ''], + ])('logs an error if any parameter is absent', (partnerName, key, value) => { + const options = new ThirdPartySharing(true) + options.addGranularOption(partnerName as any, key as any, value as any) + + expect(Logger.default.error).toHaveBeenCalledWith('Cannot add granular option, partnerName, key and value are mandatory') + }) + }) + + describe('partner sharing settings', () => { + it('adds partner sharing setting', () => { + const options = new ThirdPartySharing(true) + options.addPartnerSharingSetting('partnerName', 'optionKey', true) + + expect(options.partnerSharingSettings).toEqual({ 'partnerName': { 'optionKey': true } }) + }) + + it('adds multiple partner sharing settings', () => { + const options = new ThirdPartySharing(true) + options.addPartnerSharingSetting('partner_1', 'optionKey', true) + options.addPartnerSharingSetting('partner_2', 'optionKey', false) + + expect(options.partnerSharingSettings).toEqual({ 'partner_1': { 'optionKey': true }, 'partner_2': { 'optionKey': false } }) + }) + + it('adds partner sharing settings to single partnerName', () => { + const options = new ThirdPartySharing(true) + options.addPartnerSharingSetting('partnerName', 'key_1', true) + options.addPartnerSharingSetting('partnerName', 'key_2', false) + + expect(options.partnerSharingSettings).toEqual({ 'partnerName': { 'key_1': true, 'key_2': false } }) + }) + + it('replaces partner sharing setting with same partnerName and key', () => { + const options = new ThirdPartySharing(true) + options.addPartnerSharingSetting('partnerName', 'key', true) + options.addPartnerSharingSetting('partnerName', 'key', false) + + expect(options.partnerSharingSettings).toEqual({ 'partnerName': { 'key': false } }) + }) + + it.each([ + ['partnerName', 'key', undefined], + ['partnerName', undefined, undefined], + [undefined, 'key', true], + ['partner', '', true], + ['', 'key', true], + ['', '', true], + ])('logs an error if any parameter is absent', (partnerName, key, value) => { + const options = new ThirdPartySharing(true) + options.addPartnerSharingSetting(partnerName as any, key as any, value as any) + + expect(Logger.default.error).toHaveBeenCalledWith('Cannot add granular option, partnerName, key and value are mandatory') + }) + }) + + }) + + describe('trackThirdPartySharing', () => { + it('attaches isEnabled to request parameters', () => { + const options = new ThirdPartySharing(true) + trackThirdPartySharing(options) + + expect(Queue.push).toHaveBeenCalledWith({ + url: '/third_party_sharing', + method: 'POST', + params: { + sharing: 'enable', + granularThirdPartySharingOptions: {}, + partnerSharingSettings: {} + } + }) + }) + + it('attaches granular options to request parameters', () => { + const options = new ThirdPartySharing(true) + options.addGranularOption('partner', 'key', 'value') + trackThirdPartySharing(options) + + expect(Queue.push).toHaveBeenCalledWith({ + url: '/third_party_sharing', + method: 'POST', + params: { + sharing: 'enable', + granularThirdPartySharingOptions: { partner: { key: 'value'} }, + partnerSharingSettings: {} + } + }) + }) + + it('attaches partner sharing settings to request parameters', () => { + const options = new ThirdPartySharing(true) + options.addPartnerSharingSetting('partner', 'key', false) + trackThirdPartySharing(options) + + expect(Queue.push).toHaveBeenCalledWith({ + url: '/third_party_sharing', + method: 'POST', + params: { + sharing: 'enable', + granularThirdPartySharingOptions: {}, + partnerSharingSettings: { partner: { key: false} }, + } + }) + }) + + it.each([ + [undefined], + [null], + [''], + [[]], + [{}], + [{ hello: 'hi' }] + ])('logs an error message when no options provided', (options) => { + trackThirdPartySharing(options as any) + + expect(Logger.default.error).toHaveBeenCalledWith('Can not track third-party sharing without parameters') + }) + }) +}) diff --git a/src/sdk/__tests__/url-strategy.spec.ts b/src/sdk/__tests__/url-strategy.spec.ts index c498d287..6cabde40 100644 --- a/src/sdk/__tests__/url-strategy.spec.ts +++ b/src/sdk/__tests__/url-strategy.spec.ts @@ -1,37 +1,43 @@ -import { UrlStrategy, getBaseUrlsIterator, BaseUrlsMap, BaseUrlsIterator, DataResidency } from '../url-strategy' +import { + UrlStrategy, + getBaseUrlsIterator, + BaseUrlsMap, + BaseUrlsIterator, + DataResidency, + UrlStrategyConfig +} from '../url-strategy' import * as Globals from '../globals' import * as Logger from '../logger' +import * as Constants from '../constants'; + +// @ts-expect-error Value changed just for convenience in tests +Constants.BASE_URL_PREFIX = 'app.'; + +// @ts-expect-error Value changed just for convenience in tests +Constants.GDPR_URL_PREFIX = 'gdpr.'; + +// @ts-expect-error Value changed just for convenience in tests +Constants.BASE_URL_NO_SUB_DOMAIN_PREFIX = ''; jest.mock('../logger') describe('test url strategy', () => { const testEndpoints = { - [UrlStrategy.Default]: { - app: 'app.default', - gdpr: 'gdpr.default' - }, - [UrlStrategy.India]: { - app: 'app.india', - gdpr: 'gdpr.india' - }, - [UrlStrategy.China]: { - app: 'app.china', - gdpr: 'gdpr.china' - }, - [DataResidency.EU]: { - app: 'app.eu', - gdpr: 'gdpr.eu' - }, - [DataResidency.TR]: { - app: 'app.tr', - gdpr: 'gdpr.tr' - }, - [DataResidency.US]: { - app: 'app.us', - gdpr: 'gdpr.us' - } + default: 'default', + india: 'india', + china: 'china', + world: 'world', + EU: 'eu', + TR: 'tr', + US: 'us', } + // returns an object containing `app` and `gdpr` endpoints + const getIteratorValue = (endpoint: string) => ({ + app: Constants.BASE_URL_PREFIX + endpoint, + gdpr: Constants.GDPR_URL_PREFIX + endpoint + }) + let Config const options = { @@ -60,29 +66,28 @@ describe('test url strategy', () => { jest.restoreAllMocks() }) - describe('BaseUrlsIterator tests', () => { + const iterateThrough = (iterator: BaseUrlsIterator, iterationsNumber?: number) => { + const results: BaseUrlsMap[] = [] + let current + let steps = iterationsNumber === undefined ? -1 : iterationsNumber - const iterateThrough = (iterator: BaseUrlsIterator, iterationsNumber?: number) => { - const results: BaseUrlsMap[] = [] - let current - let steps = iterationsNumber === undefined ? -1 : iterationsNumber + do { + current = iterator.next() + if (current.value) { + results.push(current.value) + } + } while (!current.done && --steps !== 0) - do { - current = iterator.next() - if (current.value) { - results.push(current.value) - } - } while (!current.done && --steps !== 0) + return results + } - return results - } + describe('BaseUrlsIterator tests', () => { it('returns all values through iteration when default url startegy used', () => { const iterator = getBaseUrlsIterator(testEndpoints) - expect(iterator.next()).toEqual({ value: testEndpoints.default, done: false }) - expect(iterator.next()).toEqual({ value: testEndpoints.india, done: false }) - expect(iterator.next()).toEqual({ value: testEndpoints.china, done: false }) + expect(iterator.next()).toEqual({ value: getIteratorValue(testEndpoints.default), done: false }) + expect(iterator.next()).toEqual({ value: getIteratorValue(testEndpoints.world), done: false }) expect(iterator.next()).toEqual({ value: undefined, done: true }) }) @@ -92,8 +97,8 @@ describe('test url strategy', () => { const values = iterateThrough(getBaseUrlsIterator(testEndpoints)) expect(values.length).toBe(2) - expect(values[0]).toEqual(testEndpoints.india) - expect(values[1]).toEqual(testEndpoints.default) + expect(values[0]).toEqual(getIteratorValue(testEndpoints.india)) + expect(values[1]).toEqual(getIteratorValue(testEndpoints.default)) }) it('prefers Chinese enpoint and does not try reach Indian one when china url strategy set', () => { @@ -102,8 +107,8 @@ describe('test url strategy', () => { const values = iterateThrough(getBaseUrlsIterator(testEndpoints)) expect(values.length).toBe(2) - expect(values[0]).toEqual(testEndpoints.china) - expect(values[1]).toEqual(testEndpoints.default) + expect(values[0]).toEqual(getIteratorValue(testEndpoints.china)) + expect(values[1]).toEqual(getIteratorValue(testEndpoints.default)) }) it('does not override custom url', () => { @@ -119,7 +124,7 @@ describe('test url strategy', () => { describe('reset allows to restart iteration', () => { it('iterates through all endpoints twice in default order', () => { - const defaultEndpointsNumber = 3 // number of endpoints to try if default url strategy used + const defaultEndpointsNumber = 2 // number of endpoints to try if default url strategy used const iterator = getBaseUrlsIterator(testEndpoints) @@ -141,55 +146,135 @@ describe('test url strategy', () => { iterator.reset() const secondIteration = iterateThrough(iterator, 2) iterator.reset() - const thirdIteration = iterateThrough(iterator, 3) - iterator.reset() expect(firstIteration.length).toBe(1) expect(secondIteration.length).toBe(2) - expect(thirdIteration.length).toBe(3) - expect(firstIteration[0]).toEqual(testEndpoints.default) - expect(secondIteration[0]).toEqual(testEndpoints.default) - expect(thirdIteration[0]).toEqual(testEndpoints.default) + expect(firstIteration[0]).toEqual(getIteratorValue(testEndpoints.default)) + expect(secondIteration[0]).toEqual(getIteratorValue(testEndpoints.default)) - expect(secondIteration[1]).toEqual(testEndpoints.india) - expect(thirdIteration[1]).toEqual(testEndpoints.india) - - expect(thirdIteration[2]).toEqual(testEndpoints.china) + expect(secondIteration[1]).toEqual(getIteratorValue(testEndpoints.world)) }) }) - describe('data residency', () => { + }) - it.each([ - DataResidency.EU, - DataResidency.US, - DataResidency.TR - ])('tries to reach only regional endpoint if data residency set', (dataResidency) => { - Config.set({ ...options, dataResidency: dataResidency }) + describe('Data Residency', () => { - const values = iterateThrough(getBaseUrlsIterator(testEndpoints)) + it.each([ + DataResidency.EU, + DataResidency.US, + DataResidency.TR + ])('tries to reach only regional endpoint if data residency set', (dataResidency) => { + Config.set({ ...options, dataResidency: dataResidency }) - expect(values.length).toBe(1) - expect(values[0]).toEqual(testEndpoints[dataResidency]) - }) + const values = iterateThrough(getBaseUrlsIterator(testEndpoints)) - it.each([ - [UrlStrategy.China, DataResidency.EU], - [UrlStrategy.China, DataResidency.US], - [UrlStrategy.China, DataResidency.TR], - [UrlStrategy.India, DataResidency.EU], - [UrlStrategy.India, DataResidency.US], - [UrlStrategy.India, DataResidency.TR] - ])('drops url strategy if data residency set', (urlStrategy, dataResidency) => { - Config.set({ ...options, urlStrategy: urlStrategy, dataResidency: dataResidency }) - - const values = iterateThrough(getBaseUrlsIterator(testEndpoints)) - - expect(Logger.default.warn).toHaveBeenCalledWith('Both dataResidency and urlStrategy are set in config, urlStrategy will be ignored') - expect(values.length).toBe(1) - expect(values[0]).toEqual(testEndpoints[dataResidency]) - }) + expect(values.length).toBe(1) + expect(values[0]).toEqual(getIteratorValue(testEndpoints[dataResidency])) + }) + + it.each([ + [UrlStrategy.China, DataResidency.EU], + [UrlStrategy.China, DataResidency.US], + [UrlStrategy.China, DataResidency.TR], + [UrlStrategy.India, DataResidency.EU], + [UrlStrategy.India, DataResidency.US], + [UrlStrategy.India, DataResidency.TR] + ])('drops url strategy if data residency set', (urlStrategy, dataResidency) => { + Config.set({ ...options, urlStrategy: urlStrategy, dataResidency: dataResidency }) + + const values = iterateThrough(getBaseUrlsIterator(testEndpoints)) + + expect(Logger.default.warn).toHaveBeenCalledWith('Both dataResidency and urlStrategy are set in config, urlStrategy will be ignored') + expect(values.length).toBe(1) + expect(values[0]).toEqual(getIteratorValue(testEndpoints[dataResidency])) + }) + }) + + describe('Set URL Strategy as a list of domains', () => { + it('logs a warning and uses default endpoints if passed domain list is not defined', () => { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const urlStrategy: UrlStrategyConfig = { domains: undefined, useSubdomains: true } as any + Config.set({ ...options, urlStrategy }) + + const values = iterateThrough(getBaseUrlsIterator(testEndpoints)) + + expect(values.length).toBe(2) + expect(values[0]).toEqual(getIteratorValue(testEndpoints.default)) + expect(values[1]).toEqual(getIteratorValue(testEndpoints.world)) + expect(Logger.default.warn).toHaveBeenCalledWith('Invalid urlStartegy: `domains` should be a non-empty array') + }) + + it('logs a warning and uses default endpoints if passed domain list is empty', () => { + const urlStrategy: UrlStrategyConfig = { domains: [], useSubdomains: true } + Config.set({ ...options, urlStrategy }) + + const values = iterateThrough(getBaseUrlsIterator(testEndpoints)) + + expect(values.length).toBe(2) + expect(values[0]).toEqual(getIteratorValue(testEndpoints.default)) + expect(values[1]).toEqual(getIteratorValue(testEndpoints.world)) + expect(Logger.default.warn).toHaveBeenCalledWith('Invalid urlStartegy: `domains` should be a non-empty array') + }) + + it('uses passed endpoints', () => { + const urlStrategy: UrlStrategyConfig = { domains: ['example.com', 'my.domain.org', 'page.de'], useSubdomains: true } + Config.set({ ...options, urlStrategy }) + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const values = iterateThrough(getBaseUrlsIterator([] as any)) // passing endpoints needed only for backward compatibility + + expect(values.length).toBe(3) + expect(values[0]).toEqual(getIteratorValue('example.com')) + expect(values[1]).toEqual(getIteratorValue('my.domain.org')) + expect(values[2]).toEqual(getIteratorValue('page.de')) + }) + + it('does not add subdomains when `useSubdomains` is false', () => { + const urlStrategy: UrlStrategyConfig = { domains: ['example.com', 'my.domain.org', 'page.de'], useSubdomains: false } + Config.set({ ...options, urlStrategy }) + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const values = iterateThrough(getBaseUrlsIterator([] as any)) // passing endpoints needed only for backward compatibility + + expect(values.length).toBe(3) + expect(values[0]).toEqual({ app: 'example.com', gdpr: 'example.com' }) + expect(values[1]).toEqual({ app: 'my.domain.org', gdpr: 'my.domain.org' }) + expect(values[2]).toEqual({ app: 'page.de', gdpr: 'page.de' }) + }) + }) + + describe('Logs deprecation warnings', () => { + it('logs a warning when customUrl is set', () => { + Config.set({ ...options, customUrl: 'my.domain.org' }) + + iterateThrough(getBaseUrlsIterator(testEndpoints)) + + expect(Logger.default.warn).toHaveBeenCalledWith('customUrl is deprecated, use urlStrategy instead') + }) + + it.each([ + DataResidency.EU, + DataResidency.US, + DataResidency.TR, + ])('logs a warning when dataResidency is set', dr => { + Config.set({ ...options, dataResidency: dr }) + + iterateThrough(getBaseUrlsIterator(testEndpoints)) + + expect(Logger.default.warn).toHaveBeenCalledWith('dataResidency is deprecated, use urlStrategy instead') + }) + + it.each([ + UrlStrategy.China, + UrlStrategy.India + ])('logs a warning when urlStrategy set with a string', urlStrategy => { + Config.set({ ...options, urlStrategy: urlStrategy }) + + iterateThrough(getBaseUrlsIterator(testEndpoints)) + + expect(Logger.default.warn).toHaveBeenCalledWith('urlStrategy string literals (\'china\' and \'india\') are deprected, use UrlStartegyConfig instead') }) }) }) diff --git a/src/sdk/__tests__/utilities.spec.js b/src/sdk/__tests__/utilities.spec.js index 50c86550..aebe164e 100644 --- a/src/sdk/__tests__/utilities.spec.js +++ b/src/sdk/__tests__/utilities.spec.js @@ -107,7 +107,7 @@ describe('test for utility methods', () => { id: 2, text: 'smor' }, { id: 3, text: 'trt' - }], 'id', 4)).toEqual(-1) + }], 'id', 4)).toBe(-1) }) }) diff --git a/src/sdk/activity-state.js b/src/sdk/activity-state.js index 7e35d2ee..c3482130 100644 --- a/src/sdk/activity-state.js +++ b/src/sdk/activity-state.js @@ -5,11 +5,13 @@ import { type AttributionMapT, type CommonRequestParams } from './types' -import {SECOND} from './constants' -import {timePassed} from './time' -import {isRequest} from './utilities' +import { SECOND } from './constants' +import { timePassed } from './time' +import { isRequest } from './utilities' import Config from './config' import Logger from './logger' +import { subscribe } from './pub-sub' +import { PUB_SUB_EVENTS } from './constants' /** * Reference to the activity state @@ -41,8 +43,8 @@ let _active: boolean = false * * @returns {Object} */ -function currentGetter (): ActivityStateMapT { - return _started ? {..._activityState} : {} +function currentGetter(): ActivityStateMapT { + return _started ? { ..._activityState } : {} } /** @@ -50,8 +52,8 @@ function currentGetter (): ActivityStateMapT { * * @param {Object} params */ -function currentSetter (params: ActivityStateMapT = {}) { - _activityState = _started ? {...params} : {} +function currentSetter(params: ActivityStateMapT = {}) { + _activityState = _started ? { ...params } : {} } /** @@ -59,7 +61,7 @@ function currentSetter (params: ActivityStateMapT = {}) { * * @param {Object} params */ -function init (params: ActivityStateMapT) { +function init(params: ActivityStateMapT) { _started = true currentSetter(params) } @@ -69,7 +71,7 @@ function init (params: ActivityStateMapT) { * * @returns {boolean} */ -function isStarted () { +function isStarted() { return _started } @@ -78,7 +80,7 @@ function isStarted () { * * @private */ -function updateLastActive (): void { +function updateLastActive(): void { if (!_started) { return } @@ -93,21 +95,21 @@ function updateLastActive (): void { * @param {Object} params * @private */ -function _update (params: ActivityStateMapT): void { - _activityState = {..._activityState, ...params} +function _update(params: ActivityStateMapT): void { + _activityState = { ..._activityState, ...params } } /** * Set active flag to true when going foreground */ -function toForeground (): void { +function toForeground(): void { _active = true } /** * Set active flag to false when going background */ -function toBackground (): void { +function toBackground(): void { _active = false } @@ -117,7 +119,7 @@ function toBackground (): void { * @returns {number} * @private */ -function _getOffset (): number { +function _getOffset(): number { const lastActive = _activityState.lastActive return Math.round(timePassed(lastActive, Date.now()) / SECOND) } @@ -128,7 +130,7 @@ function _getOffset (): number { * @returns {number} * @private */ -function _getTimeSpent (): number { +function _getTimeSpent(): number { return (_activityState.timeSpent || 0) + (_active ? _getOffset() : 0) } @@ -138,7 +140,7 @@ function _getTimeSpent (): number { * @returns {number} * @private */ -function _getSessionLength (): number { +function _getSessionLength(): number { const lastActive = _activityState.lastActive const withinWindow = timePassed(lastActive, Date.now()) < Config.sessionWindow const withOffset = _active || !_active && withinWindow @@ -152,7 +154,7 @@ function _getSessionLength (): number { * @returns {number} * @private */ -function _getSessionCount (): number { +function _getSessionCount(): number { return _activityState.sessionCount || 0 } @@ -162,7 +164,7 @@ function _getSessionCount (): number { * @returns {number} * @private */ -function _getEventCount (): number { +function _getEventCount(): number { return _activityState.eventCount || 0 } @@ -172,7 +174,7 @@ function _getEventCount (): number { * @returns {number} * @private */ -function _getLastInterval (): number { +function _getLastInterval(): number { const lastActive = _activityState.lastActive if (lastActive) { @@ -185,7 +187,7 @@ function _getLastInterval (): number { /** * Initiate session params and go to foreground */ -function initParams (): void { +function initParams(): void { updateSessionOffset() toForeground() } @@ -195,7 +197,7 @@ function initParams (): void { * * @returns {Object} */ -function getParams (url?: UrlT): ?CommonRequestParams { +function getParams(url?: UrlT): ?CommonRequestParams { if (!_started) { return null } @@ -222,7 +224,7 @@ function getParams (url?: UrlT): ?CommonRequestParams { * @param {string} url * @param {boolean=false} auto */ -function updateParams (url: string, auto?: boolean): void { +function updateParams(url: string, auto?: boolean): void { if (!_started) { return } @@ -249,7 +251,7 @@ function updateParams (url: string, auto?: boolean): void { /** * Update installed flag - first session has been finished */ -function updateInstalled (): void { +function updateInstalled(): void { if (!_started) { return } @@ -258,13 +260,13 @@ function updateInstalled (): void { return } - _update({installed: true}) + _update({ installed: true }) } /** * Update session params which depend on the time offset since last measure point */ -function updateSessionOffset (): void { +function updateSessionOffset(): void { if (!_started) { return } @@ -272,45 +274,45 @@ function updateSessionOffset (): void { const timeSpent = _getTimeSpent() const sessionLength = _getSessionLength() - _update({timeSpent, sessionLength}) + _update({ timeSpent, sessionLength }) updateLastActive() } /** * Update session length */ -function updateSessionLength (): void { +function updateSessionLength(): void { if (!_started) { return } const sessionLength = _getSessionLength() - _update({sessionLength}) + _update({ sessionLength }) updateLastActive() } /** * Reset time spent and session length to zero */ -function resetSessionOffset (): void { +function resetSessionOffset(): void { if (!_started) { return } - _update({timeSpent: 0, sessionLength: 0}) + _update({ timeSpent: 0, sessionLength: 0 }) } /** * Destroy current activity state */ -function destroy (): void { +function destroy(): void { _activityState = {} _started = false _active = false } -function getAttribution (): AttributionMapT | null { +function getAttribution(): AttributionMapT | null { if (!_started) { return null } @@ -323,7 +325,17 @@ function getAttribution (): AttributionMapT | null { return _activityState.attribution } -function getWebUUID (): string { +function waitForAttribution(): Promise { + if (_activityState.attribution) { + return Promise.resolve(_activityState.attribution) + } + + return new Promise(resolve => + subscribe(PUB_SUB_EVENTS.ATTRIBUTION_RECEIVED, (_name: string, attribution: AttributionMapT) => resolve(attribution)) + ) +} + +function getWebUUID(): string { if (!_started) { return null } @@ -331,9 +343,19 @@ function getWebUUID (): string { return _activityState.uuid } +function waitForWebUUID(): Promise { + if (_activityState.uuid) { + return Promise.resolve(_activityState.uuid) + } + + return new Promise(resolve => + subscribe(PUB_SUB_EVENTS.WEB_UUID_CREATED, (_name: string, webUuid: string) => resolve(webUuid)) + ) +} + const ActivityState = { - get current () { return currentGetter() }, - set current (value) { currentSetter(value) }, + get current() { return currentGetter() }, + set current(value) { currentSetter(value) }, init, isStarted, toForeground, @@ -348,7 +370,9 @@ const ActivityState = { updateLastActive, destroy, getAttribution, - getWebUUID + getWebUUID, + waitForAttribution, + waitForWebUUID } export default ActivityState diff --git a/src/sdk/attribution.js b/src/sdk/attribution.js index 63d85fa4..9ef83d07 100644 --- a/src/sdk/attribution.js +++ b/src/sdk/attribution.js @@ -15,6 +15,7 @@ import {persist} from './identity' import ActivityState from './activity-state' import Logger from './logger' import Request from './request' +import { PUB_SUB_EVENTS } from './constants' /** * Http request instance @@ -92,6 +93,7 @@ function _setAttribution (result: HttpSuccessResponseT): Promise { publish('attribution:change', attribution) + publish(PUB_SUB_EVENTS.ATTRIBUTION_RECEIVED, attribution) Logger.info('Attribution has been updated') return {state: 'changed'} }) diff --git a/src/sdk/config.js b/src/sdk/config.js deleted file mode 100644 index 1c0f610c..00000000 --- a/src/sdk/config.js +++ /dev/null @@ -1,176 +0,0 @@ -// @flow -import { - type BaseParamsT, - type CustomConfigT, - type InitOptionsT, - type BaseParamsListT, - type BaseParamsMandatoryListT, - type CustomConfigListT -} from './types' -import {MINUTE, SECOND, DAY} from './constants' -import {buildList, reducer} from './utilities' -import Logger from './logger' - -/** - * Base parameters set by client - * - app token - * - environment - * - default tracker - * - external device ID - * - * @type {Object} - * @private - */ -let _baseParams: BaseParamsT = {} - -/** - * Custom config set by client - * - url override - * - event deduplication list limit - * - * @type {Object} - * @private - */ -let _customConfig: CustomConfigT = {} - -/** - * Mandatory fields to set for sdk initialization - * - * @type {string[]} - * @private - */ -const _mandatory: BaseParamsMandatoryListT = [ - 'appToken', - 'environment' -] - -/** - * Allowed params to be sent with each request - * - * @type {string[]} - * @private - */ -const _allowedParams: BaseParamsListT = [ - ..._mandatory, - 'defaultTracker', - 'externalDeviceId' -] - -/** - * Allowed configuration overrides - * - * @type {string[]} - * @private - */ -const _allowedConfig: CustomConfigListT = [ - 'customUrl', - 'dataResidency', - 'urlStrategy', - 'eventDeduplicationListLimit', - 'namespace' -] - -/** - * Global configuration object used across the sdk - * - * @type {{ - * namespace: string, - * version: string, - * sessionWindow: number, - * sessionTimerWindow: number, - * requestValidityWindow: number - * }} - */ -const _baseConfig = { - sessionWindow: 30 * MINUTE, - sessionTimerWindow: 60 * SECOND, - requestValidityWindow: 28 * DAY -} - -/** - * Check of configuration has been initialized - * - * @returns {boolean} - */ -function isInitialised (): boolean { - return _mandatory.reduce((acc, key) => acc && !!_baseParams[key], true) -} - -/** - * Get base params set by client - * - * @returns {Object} - */ -function getBaseParams (): BaseParamsT { - return {..._baseParams} -} - -/** - * Set base params and custom config for the sdk to run - * - * @param {Object} options - */ -function set (options: InitOptionsT): void { - if (hasMissing(options)) { - return - } - - const filteredParams = [..._allowedParams, ..._allowedConfig] - .filter(key => !!options[key]) - .map(key => [key, options[key]]) - - _baseParams = filteredParams - .filter(([key]) => _allowedParams.indexOf(key) !== -1) - .reduce(reducer, {}) - - _customConfig = filteredParams - .filter(([key]) => _allowedConfig.indexOf(key) !== -1) - .reduce(reducer, {}) -} - -/** - * Get custom config set by client - * - * @returns {Object} - */ -function getCustomConfig (): CustomConfigT { - return {..._customConfig} -} - -/** - * Check if there are missing mandatory parameters - * - * @param {Object} params - * @returns {boolean} - * @private - */ -function hasMissing (params: BaseParamsT): boolean { - const missing = _mandatory.filter(value => !params[value]) - - if (missing.length) { - Logger.error(`You must define ${buildList(missing)}`) - return true - } - - return false -} - -/** - * Restore config to its default state - */ -function destroy (): void { - _baseParams = {} - _customConfig = {} -} - -const Config = { - ..._baseConfig, - set, - getBaseParams, - getCustomConfig, - isInitialised, - hasMissing, - destroy -} - -export default Config diff --git a/src/sdk/config.ts b/src/sdk/config.ts new file mode 100644 index 00000000..b6dc410f --- /dev/null +++ b/src/sdk/config.ts @@ -0,0 +1,134 @@ +import { type Attribution } from './ts-types' +import { MINUTE, SECOND, DAY } from './constants' +import { buildList, reducer } from './utilities' +import Logger from './logger' + +type MandatoryParams = { + appToken: string, + environment: 'production' | 'sandbox', +} + +/** Base parameters set by client */ +type BaseParams = MandatoryParams & { + defaultTracker?: string, + externalDeviceId?: string +} + +/** Custom config set by client */ +type CustomConfig = { + customUrl?: string, + urlStrategy?: 'india' | 'china', + dataResidency?: 'EU' | 'TR' | 'US', + eventDeduplicationListLimit?: number, + namespace?: string +} + +export type InitOptions = BaseParams & CustomConfig & { + attributionCallback: (eventName: string, attribution: Attribution) => unknown +} + +let _baseParams: BaseParams | null = null + +let _customConfig: CustomConfig | null = null + +/** Mandatory fields to set for sdk initialization */ +const _mandatory: Array<(keyof MandatoryParams)> = [ + 'appToken', + 'environment' +] + +/** Allowed params to be sent with each request */ +const _allowedParams: Array<(keyof BaseParams)> = [ + ..._mandatory, + 'defaultTracker', + 'externalDeviceId' +] + +/** Allowed configuration overrides */ +const _allowedConfig: Array<(keyof CustomConfig)> = [ + 'customUrl', + 'dataResidency', + 'urlStrategy', + 'eventDeduplicationListLimit', + 'namespace' +] + +/** + * Check of configuration has been initialized + */ +function isInitialised(): boolean { + return _mandatory.reduce((acc, key) => acc && (!!_baseParams && !!_baseParams[key]), true) +} + +/** + * Set base params and custom config for the sdk to run + */ +function set(options: InitOptions): void { + if (hasMissing(options)) { + return + } + + _baseParams = _allowedParams + .filter(key => !!options[key]) + .map(key => [key, options[key]] as [keyof MandatoryParams, string]) + .reduce((acc, item) => reducer(acc, item) as BaseParams, {} as BaseParams) + + _customConfig = _allowedConfig + .filter(key => !!options[key]) + .map(key => [key, options[key]] as [string, string]) + .reduce(reducer, {} as CustomConfig) +} + +/** + * Get base params set by client + */ +function getBaseParams(): Partial { + return _baseParams + ? { ..._baseParams } // intentionally returns a copy + : {} +} + +/** + * Get custom config set by client + */ +function getCustomConfig(): CustomConfig { + return _customConfig + ? { ..._customConfig } // intentionally returns a copy + : {} +} + +/** + * Check if there are missing mandatory parameters + */ +function hasMissing(params: BaseParams): boolean { + const missing = _mandatory.filter(value => !params[value]) + + if (missing.length) { + Logger.error(`You must define ${buildList(missing)}`) + return true + } + + return false +} + +/** + * Restore config to its default state + */ +function destroy(): void { + _baseParams = null + _customConfig = null +} + +const Config = { + sessionWindow: 30 * MINUTE, + sessionTimerWindow: 60 * SECOND, + requestValidityWindow: 28 * DAY, + set, + getBaseParams, + getCustomConfig, + isInitialised, + hasMissing, + destroy +} + +export default Config diff --git a/src/sdk/constants.js b/src/sdk/constants.js deleted file mode 100644 index b67b86e9..00000000 --- a/src/sdk/constants.js +++ /dev/null @@ -1,54 +0,0 @@ -export const SECOND = 1000 -export const MINUTE = SECOND * 60 -export const HOUR = MINUTE * 60 -export const DAY = HOUR * 24 -export const REASON_GENERAL = 'general' -export const REASON_GDPR = 'gdpr' -export const HTTP_ERRORS = { - 'TRANSACTION_ERROR': 'XHR transaction failed due to an error', - 'SERVER_MALFORMED_RESPONSE': 'Response from server is malformed', - 'SERVER_INTERNAL_ERROR': 'Internal error occurred on the server', - 'SERVER_CANNOT_PROCESS': 'Server was not able to process the request, probably due to error coming from the client', - 'NO_CONNECTION': 'No internet connectivity', - 'SKIP': 'Skipping slower attempt', - 'MISSING_URL': 'Url is not provided' -} - -export const STORAGE_TYPES = { - NO_STORAGE: 'noStorage', - INDEXED_DB: 'indexedDB', - LOCAL_STORAGE: 'localStorage' -} - -export const ENDPOINTS = { - default: { - endpointName: 'Default', - app: 'https://app.adjust.com', - gdpr: 'https://gdpr.adjust.com' - }, - india: { - endpointName: 'Indian', - app: 'https://app.adjust.net.in', - gdpr: 'https://gdpr.adjust.net.in' - }, - china: { - endpointName: 'Chinese', - app: 'https://app.adjust.world', - gdpr: 'https://gdpr.adjust.world' - }, - EU: { - endpointName: 'EU', - app: 'https://app.eu.adjust.com', - gdpr: 'https://gdpr.eu.adjust.com' - }, - TR: { - endpointName: 'TR', - app: 'https://app.tr.adjust.com', - gdpr: 'https://gdpr.tr.adjust.com' - }, - US: { - endpointName: 'US', - app: 'https://app.us.adjust.com', - gdpr: 'https://gdpr.us.adjust.com' - } -} diff --git a/src/sdk/constants.ts b/src/sdk/constants.ts new file mode 100644 index 00000000..75621492 --- /dev/null +++ b/src/sdk/constants.ts @@ -0,0 +1,44 @@ +export const SECOND = 1000 +export const MINUTE = SECOND * 60 +export const HOUR = MINUTE * 60 +export const DAY = HOUR * 24 + +export enum DISABLE_REASONS { + REASON_GENERAL = 'general', + REASON_GDPR = 'gdpr' +} + +export const HTTP_ERRORS = { + 'TRANSACTION_ERROR': 'XHR transaction failed due to an error', + 'SERVER_MALFORMED_RESPONSE': 'Response from server is malformed', + 'SERVER_INTERNAL_ERROR': 'Internal error occurred on the server', + 'SERVER_CANNOT_PROCESS': 'Server was not able to process the request, probably due to error coming from the client', + 'NO_CONNECTION': 'No internet connectivity', + 'SKIP': 'Skipping slower attempt', + 'MISSING_URL': 'Url is not provided' +} + +export enum STORAGE_TYPES { + NO_STORAGE = 'noStorage', + INDEXED_DB = 'indexedDB', + LOCAL_STORAGE = 'localStorage' +} + +export const ENDPOINTS = { + default: 'adjust.com', + india: 'adjust.net.in', + china: 'adjust.world', + world: 'adjust.world', + EU: 'eu.adjust.com', + TR: 'tr.adjust.com', + US: 'us.adjust.com', +} + +export const BASE_URL_PREFIX = 'https://app.'; +export const GDPR_URL_PREFIX = 'https://gdpr.'; +export const BASE_URL_NO_SUB_DOMAIN_PREFIX = 'https://'; + +export const PUB_SUB_EVENTS = { + WEB_UUID_CREATED: 'activity:web_uuid', + ATTRIBUTION_RECEIVED: 'activity:attribution', +} diff --git a/src/sdk/disable.js b/src/sdk/disable.ts similarity index 55% rename from src/sdk/disable.js rename to src/sdk/disable.ts index 19953e01..6fa39099 100644 --- a/src/sdk/disable.js +++ b/src/sdk/disable.ts @@ -1,15 +1,13 @@ -// @flow -import {REASON_GDPR, REASON_GENERAL} from './constants' -import {getDisabled, setDisabled} from './preferences' +import { DISABLE_REASONS } from './constants' +import { getDisabled, setDisabled } from './preferences' import Logger from './logger' type StatusT = 'on' | 'off' | 'paused' -type ReasonT = REASON_GDPR | REASON_GENERAL -type PendingT = boolean -type ReasonMapT = {| - reason: ReasonT, - pending: PendingT -|} +type ReasonT = DISABLE_REASONS +type ReasonMapT = { + reason?: ReasonT, + pending: boolean +} /** * Get the disable action name depending on the reason @@ -18,7 +16,7 @@ type ReasonMapT = {| * @returns {string} * @private */ -const _disableReason = (reason: ReasonT) => reason === REASON_GDPR ? 'GDPR disable' : 'disable' +const _disableReason = (reason?: ReasonT) => reason === DISABLE_REASONS.REASON_GDPR ? 'GDPR disable' : 'disable' /** * Get log messages depending on the disable reason @@ -27,7 +25,7 @@ const _disableReason = (reason: ReasonT) => reason === REASON_GDPR ? 'GDPR disab * @returns {Object} * @private */ -const _logMessages = (reason: ReasonT) => ({ +const _logMessages = (reason?: ReasonT) => ({ start: { inProgress: `Adjust SDK ${_disableReason(reason)} process has already started`, done: `Adjust SDK ${_disableReason(reason)} process is now started` @@ -47,21 +45,21 @@ const _logMessages = (reason: ReasonT) => ({ * @returns {boolean} * @private */ -function _disable ({reason, pending}: ReasonMapT, expectedAction: 'start' | 'finish'): boolean { - const disabled = getDisabled() || {} - const action = expectedAction === 'start' && disabled.pending ? 'start': 'finish' - const shouldNotStart = expectedAction === 'start' && disabled.reason - const shouldNotFinish = expectedAction === 'finish' && disabled.reason && !disabled.pending +function _disable({ reason, pending }: ReasonMapT, expectedAction: 'start' | 'finish'): boolean { + const { reason: savedReason, pending: savedPending } = getDisabled() || {} + const action = expectedAction === 'start' && savedPending ? 'start' : 'finish' + const shouldNotStart = expectedAction === 'start' && savedReason + const shouldNotFinish = expectedAction === 'finish' && savedReason && !savedPending if (shouldNotStart || shouldNotFinish) { - Logger.log(_logMessages(disabled.reason)[action].inProgress) + Logger.log(_logMessages(savedReason)[action].inProgress) return false } Logger.log(_logMessages(reason)[action].done) setDisabled({ - reason: reason || REASON_GENERAL, + reason: reason || DISABLE_REASONS.REASON_GENERAL, pending }) @@ -75,8 +73,8 @@ function _disable ({reason, pending}: ReasonMapT, expectedAction: 'start' | 'fin * @param {boolean} pending * @private */ -function disable (reason: ?ReasonT, pending: ?PendingT = false): boolean { - return _disable({reason, pending: pending || false}, 'start') +function disable(reason?: ReasonT, pending = false): boolean { + return _disable({ reason, pending }, 'start') } /** @@ -85,22 +83,22 @@ function disable (reason: ?ReasonT, pending: ?PendingT = false): boolean { * @param {string} reason * @returns {boolean} */ -function finish (reason: ReasonT): boolean { - return _disable({reason, pending: false}, 'finish') +function finish(reason: ReasonT): boolean { + return _disable({ reason, pending: false }, 'finish') } /** * Enable sdk if not GDPR forgotten */ -function restore (): boolean { - const disabled = getDisabled() || {} +function restore(): boolean { + const { reason } = getDisabled() || {} - if (disabled.reason === REASON_GDPR) { + if (reason === DISABLE_REASONS.REASON_GDPR) { Logger.log('Adjust SDK is disabled due to GDPR-Forget-Me request and it can not be re-enabled') return false } - if (!disabled.reason) { + if (!reason) { Logger.log('Adjust SDK is already enabled') return false } @@ -120,12 +118,12 @@ function restore (): boolean { * * @returns {string} */ -function status (): StatusT { - const disabled = getDisabled() || {} +function status(): StatusT { + const { reason, pending } = getDisabled() || {} - if (disabled.reason === REASON_GENERAL || disabled.reason === REASON_GDPR && !disabled.pending) { + if (reason === DISABLE_REASONS.REASON_GENERAL || reason === DISABLE_REASONS.REASON_GDPR && !pending) { return 'off' - } else if (disabled.reason === REASON_GDPR && disabled.pending) { + } else if (reason === DISABLE_REASONS.REASON_GDPR && pending) { return 'paused' } diff --git a/src/sdk/gdpr-forget-device.js b/src/sdk/gdpr-forget-device.ts similarity index 77% rename from src/sdk/gdpr-forget-device.js rename to src/sdk/gdpr-forget-device.ts index 18bd5ddd..e2cb7522 100644 --- a/src/sdk/gdpr-forget-device.js +++ b/src/sdk/gdpr-forget-device.ts @@ -1,11 +1,10 @@ -// @flow import Request from './request' import ActivityState from './activity-state' import Logger from './logger' import Config from './config' -import {publish} from './pub-sub' -import {status, disable as sdkDisable, finish as sdkDisableFinish} from './disable' -import {REASON_GDPR} from './constants' +import { publish } from './pub-sub' +import { status, disable as sdkDisable, finish as sdkDisableFinish } from './disable' +import { DISABLE_REASONS } from './constants' /** * Http request instance @@ -38,7 +37,7 @@ const _logMessages = { * @param {boolean} force * @returns {boolean} */ -function forget (force?: boolean): boolean { +function forget(force?: boolean): boolean { const sdkStatus = status() if (!force && sdkStatus !== 'on') { @@ -52,7 +51,7 @@ function forget (force?: boolean): boolean { } _request.send({ - params: {...ActivityState.getParams()} + params: { ...ActivityState.getParams() } }).then(() => { publish('sdk:gdpr-forget-me') }) @@ -65,8 +64,8 @@ function forget (force?: boolean): boolean { * * @returns {boolean} */ -function disable () { - return sdkDisable(REASON_GDPR, true) +function disable() { + return sdkDisable(DISABLE_REASONS.REASON_GDPR, true) } /** @@ -74,14 +73,14 @@ function disable () { * * @returns {boolean} */ -function finish () { - return sdkDisableFinish(REASON_GDPR) +function finish() { + return sdkDisableFinish(DISABLE_REASONS.REASON_GDPR) } /** * Check if there is pending GDPR-Forget-Me request */ -function check (): void { +function check(): void { if (status() === 'paused') { Logger.log(_logMessages.running) forget(true) @@ -91,7 +90,7 @@ function check (): void { /** * Destroy by clearing running request */ -function destroy (): void { +function destroy(): void { _request.clear() } diff --git a/src/sdk/http.js b/src/sdk/http.js index 7f32e5d4..8db5ae0c 100644 --- a/src/sdk/http.js +++ b/src/sdk/http.js @@ -88,6 +88,10 @@ function _encodeParam ([key, value]: [string, $Values]): st encodedValue = encodeURIComponent(JSON.stringify(value) || '') } + if (key === 'granular_third_party_sharing_options' || key === 'partner_sharing_settings') { + return [encodedKey, encodedValue].join(encodeURIComponent('=')) + } + return [encodedKey, encodedValue].join('=') } @@ -263,7 +267,6 @@ function _interceptSuccess (result: HttpSuccessResponseT, url): HttpSuccessRespo const isGdprRequest = isRequest(url, 'gdpr_forget_device') const isAttributionRequest = isRequest(url, 'attribution') const isSessionRequest = isRequest(url, 'session') - const isThirdPartySharingOptOutRequest = isRequest(url, 'disable_third_party_sharing') const optedOut = result.tracking_state === 'opted_out' if (!isGdprRequest && optedOut) { @@ -279,11 +282,6 @@ function _interceptSuccess (result: HttpSuccessResponseT, url): HttpSuccessRespo publish('session:finished', result) } - if (isThirdPartySharingOptOutRequest) { - publish('sdk:third-party-sharing-opt-out') - return result - } - return result } diff --git a/src/sdk/identity.js b/src/sdk/identity.ts similarity index 58% rename from src/sdk/identity.js rename to src/sdk/identity.ts index 10e190b5..7d7d6ed6 100644 --- a/src/sdk/identity.js +++ b/src/sdk/identity.ts @@ -1,80 +1,57 @@ -// @flow -import {type ActivityStateMapT} from './types' +import { type ActivityStateMapT } from './types' import Storage from './storage/storage' import ActivityState from './activity-state' -import {reload as reloadPreferences} from './preferences' -import {REASON_GDPR} from './constants' -import {isEmpty} from './utilities' -import {disable, status} from './disable' -import {publish} from './pub-sub' - -type InterceptT = {| +import { reload as reloadPreferences } from './preferences' +import { PUB_SUB_EVENTS, DISABLE_REASONS } from './constants' +import { isEmpty } from './utilities' +import { disable, status } from './disable' +import { publish } from './pub-sub' +import { StoreName } from './storage/scheme' + +type InterceptT = { exists: boolean, - stored?: ?ActivityStateMapT -|} + stored?: ActivityStateMapT +} -/** - * Name of the store used by activityState - * - * @type {string} - * @private - */ -const _storeName = 'activityState' +/** Name of the store used by activityState */ +const _storeName = StoreName.ActivityState -/** - * Boolean used in start in order to avoid duplicated activity state - * - * @type {boolean} - * @private - */ +/** Boolean used in start in order to avoid duplicated activity state */ let _starting: boolean = false -/** - * Generate random uuid v4 - * - * @returns {string} - * @private - */ -function _generateUuid (): string { +/** Generate random uuid v4 */ +function _generateUuid(): string { let seed = Date.now() return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => { const r = (seed + Math.random() * 16) % 16 | 0 seed = Math.floor(seed / 16) - return (c === 'x' ? r : r & (0x3|0x8)).toString(16) + return (c === 'x' ? r : r & (0x3 | 0x8)).toString(16) }) } -/** - * Inspect stored activity state and check if disable needs to be repeated - * - * @param {Object=} stored - * @returns {Object} - * @private - */ -function _intercept (stored: ActivityStateMapT): InterceptT { +/** Inspect stored activity state and check if disable needs to be repeated */ +function _intercept(stored: ActivityStateMapT): InterceptT { if (!stored) { - return {exists: false} + return { exists: false } } if (stored.uuid === 'unknown') { - disable({reason: REASON_GDPR}) + disable(DISABLE_REASONS.REASON_GDPR) ActivityState.destroy() - return {exists: true, stored: null} + return { exists: true, stored: null } } ActivityState.init(stored) - return {exists: true, stored: stored} + return { exists: true, stored: stored } } /** * Cache stored activity state into running memory - * - * @returns {Promise} */ -function start (): Promise { +function start(): Promise { if (_starting) { - return Promise.reject({interrupted: true, message: 'Adjust SDK start already in progress'}) + return Promise.reject({ interrupted: true, message: 'Adjust SDK start already in progress' }) } _starting = true @@ -87,7 +64,7 @@ function start (): Promise { } const activityState = isEmpty(ActivityState.current) - ? {uuid: _generateUuid()} + ? { uuid: _generateUuid() } : ActivityState.current return Storage.addItem(_storeName, activityState) @@ -98,29 +75,30 @@ function start (): Promise { return activityState }) }) + .then((activityState: ActivityStateMapT | null) => { + if (activityState) { + publish(PUB_SUB_EVENTS.WEB_UUID_CREATED, activityState.uuid) + } else { + publish(PUB_SUB_EVENTS.WEB_UUID_CREATED, 'gdpr_forgotten') + } + return activityState + }) } -/** - * Check if sdk is running at all (totally disabled or inactive activity state) - * - * @returns {boolean} - * @private - */ -function _isLive () { +/** Check if sdk is running at all (totally disabled or inactive activity state) */ +function _isLive() { return status() !== 'off' && ActivityState.isStarted() } /** * Persist changes made directly in activity state and update lastActive flag - * - * @returns {Promise} */ -function persist (): Promise { +function persist(): Promise { if (!_isLive()) { return Promise.resolve(null) } - const activityState = {...ActivityState.current, lastActive: Date.now()} + const activityState = { ...ActivityState.current, lastActive: Date.now() } return Storage.updateItem(_storeName, activityState) .then(() => ActivityState.current = activityState) } @@ -128,10 +106,8 @@ function persist (): Promise { /** * Sync in-memory activityState with the one from store * - should be used when change from another tab is possible and critical - * - * @returns {Promise} */ -function sync (): Promise { +function sync(): Promise { return Storage.getFirst(_storeName) .then((activityState: ActivityStateMapT) => { const current = ActivityState.current @@ -157,8 +133,8 @@ function sync (): Promise { /** * Clear activity state store - set uuid to be unknown */ -function clear (): void { - const newActivityState = {uuid: 'unknown'} +function clear(): Promise { + const newActivityState = { uuid: 'unknown' } ActivityState.current = newActivityState @@ -169,7 +145,7 @@ function clear (): void { /** * Destroy current activity state */ -function destroy (): void { +function destroy(): void { ActivityState.destroy() } diff --git a/src/sdk/main.js b/src/sdk/main.js index 3aba3e06..5d68f578 100644 --- a/src/sdk/main.js +++ b/src/sdk/main.js @@ -6,38 +6,36 @@ import { type GlobalParamsT, type CustomErrorT, type ActivityStateMapT, - type SmartBannerOptionsT, type AttributionMapT } from './types' import Config from './config' import Storage from './storage/storage' import Logger from './logger' -import {run as queueRun, setOffline, clear as queueClear, destroy as queueDestroy} from './queue' -import {subscribe, unsubscribe, destroy as pubSubDestroy} from './pub-sub' -import {watch as sessionWatch, destroy as sessionDestroy} from './session' -import {start, clear as identityClear, destroy as identityDestroy} from './identity' -import {add, remove, removeAll, clear as globalParamsClear} from './global-params' -import {check as attributionCheck, destroy as attributionDestroy} from './attribution' -import {disable, restore, status} from './disable' -import {check as gdprForgetCheck, forget, disable as gdprDisable, finish as gdprDisableFinish, destroy as gdprForgetDestroy} from './gdpr-forget-device' -import {check as sharingDisableCheck, optOut as sharingOptOut, disable as sharingDisable, finish as sharingDisableFinish} from './third-party-sharing' -import {register as listenersRegister, destroy as listenersDestroy} from './listeners' -import {delay, flush, destroy as schedulerDestroy} from './scheduler' +import { run as queueRun, setOffline, clear as queueClear, destroy as queueDestroy } from './queue' +import { subscribe, unsubscribe, destroy as pubSubDestroy } from './pub-sub' +import { watch as sessionWatch, destroy as sessionDestroy } from './session' +import { start, clear as identityClear, destroy as identityDestroy } from './identity' +import { add, remove, removeAll, clear as globalParamsClear } from './global-params' +import { check as attributionCheck, destroy as attributionDestroy } from './attribution' +import { disable, restore, status } from './disable' +import { check as gdprForgetCheck, forget, disable as gdprDisable, finish as gdprDisableFinish, destroy as gdprForgetDestroy } from './gdpr-forget-device' +import { trackThirdPartySharing as trackTPS, ThirdPartySharing } from './track-third-party-sharing' +import { register as listenersRegister, destroy as listenersDestroy } from './listeners' +import { delay, flush, destroy as schedulerDestroy } from './scheduler' import event from './event' import sdkClick from './sdk-click' import ActivityState from './activity-state' import { STORAGE_TYPES } from './constants' -import { SmartBanner } from './smart-banner/smart-banner' -type InitConfigT = $ReadOnly<{|...InitOptionsT, ...LogOptionsT|}> +type InitConfigT = $ReadOnly<{|...InitOptionsT, ...LogOptionsT |}> -/** - * In-memory parameters to be used if restarting - * - * @type {Object} - * @private - */ -let _options: ?InitOptionsT = null + /** + * In-memory parameters to be used if restarting + * + * @type {Object} + * @private + */ + let _options: ? InitOptionsT = null /** * Flag to mark id sdk is in starting process @@ -63,13 +61,6 @@ let _isStarted: boolean = false */ let _isInstalled: boolean = false -/** - * SmartBanner instance - * - * @private - */ -let _smartBanner: ?SmartBanner = null - /** * Initiate the instance with parameters * @@ -77,7 +68,7 @@ let _smartBanner: ?SmartBanner = null * @param {string} logLevel * @param {string} logOutput */ -function initSdk ({logLevel, logOutput, ...options}: InitConfigT = {}): void { +function initSdk({ logLevel, logOutput, ...options }: InitConfigT = {}): void { Logger.setLogLevel(logLevel, logOutput) if (_isInitialised()) { @@ -111,21 +102,39 @@ function initSdk ({logLevel, logOutput, ...options}: InitConfigT = {}): void { * Get user's current attribution information * * @returns {AttributionMapT|undefined} current attribution information if available or `undefined` otherwise + * + * @deprecated Use {@link waitForAttribution} instead */ -function getAttribution (): ?AttributionMapT { +function getAttribution(): ?AttributionMapT { return _preCheck('get attribution', () => ActivityState.getAttribution()) } +/** + * Returns a promise which resolves when current attribution information becomes available + */ +function waitForAttribution(): Promise { + return _preCheck('get attribution', () => ActivityState.waitForAttribution(), {schedule: false}) +} + /** * Get `web_uuid` - a unique ID of user generated per subdomain and per browser * * @returns {string|undefined} `web_uuid` if available or `undefined` otherwise + * + * @deprecated Use {@link waitForWebUUID} instead */ -function getWebUUID (): ?string { +function getWebUUID(): ?string { return _preCheck('get web_uuid', () => ActivityState.getWebUUID()) } -function setReferrer (referrer: string) { +/** + * Returns a promise which resolves when `web_uuid` becomes available + */ +function waitForWebUUID(): Promise { + return _preCheck('get web_uuid', () => ActivityState.waitForWebUUID(), {schedule: false}) +} + +function setReferrer(referrer: string) { if (!referrer || typeof referrer !== 'string') { Logger.error('You must provide a string referrer') return @@ -143,7 +152,7 @@ function setReferrer (referrer: string) { * * @param {Object} params */ -function trackEvent (params: EventParamsT): Promise { +function trackEvent(params: EventParamsT): Promise { return _internalTrackEvent(params) } @@ -152,7 +161,7 @@ function trackEvent (params: EventParamsT): Promise { * * @param {Array} params */ -function addGlobalCallbackParameters (params: Array): void { +function addGlobalCallbackParameters(params: Array): void { _preCheck('add global callback parameters', () => add(params, 'callback')) } @@ -161,7 +170,7 @@ function addGlobalCallbackParameters (params: Array): void { * * @param {Array} params */ -function addGlobalPartnerParameters (params: Array): void { +function addGlobalPartnerParameters(params: Array): void { _preCheck('add global partner parameters', () => add(params, 'partner')) } @@ -170,7 +179,7 @@ function addGlobalPartnerParameters (params: Array): void { * * @param {string} key */ -function removeGlobalCallbackParameter (key: string): void { +function removeGlobalCallbackParameter(key: string): void { _preCheck('remove global callback parameter', () => remove(key, 'callback')) } @@ -179,42 +188,42 @@ function removeGlobalCallbackParameter (key: string): void { * * @param {string} key */ -function removeGlobalPartnerParameter (key: string): void { +function removeGlobalPartnerParameter(key: string): void { _preCheck('remove global partner parameter', () => remove(key, 'partner')) } /** * Remove all global callback parameters */ -function clearGlobalCallbackParameters (): void { +function clearGlobalCallbackParameters(): void { _preCheck('remove all global callback parameters', () => removeAll('callback')) } /** * Remove all global partner parameters */ -function clearGlobalPartnerParameters (): void { +function clearGlobalPartnerParameters(): void { _preCheck('remove all global partner parameters', () => removeAll('partner')) } /** * Switch offline mode */ -function switchToOfflineMode (): void { +function switchToOfflineMode(): void { _preCheck('set offline mode', () => setOffline(true)) } /** * Switch online mode */ -function switchBackToOnlineMode (): void { +function switchBackToOnlineMode(): void { _preCheck('set online mode', () => setOffline(false)) } /** * Stop SDK */ -function stop (): void { +function stop(): void { const done = disable() if (done && Config.isInitialised()) { @@ -225,7 +234,7 @@ function stop (): void { /** * Restart sdk if not GDPR forgotten */ -function restart (): void { +function restart(): void { const done = restore() if (done && _options) { @@ -236,7 +245,7 @@ function restart (): void { /** * Disable sdk and send GDPR-Forget-Me request */ -function gdprForgetMe (): void { +function gdprForgetMe(): void { let done = forget() if (!done) { @@ -252,53 +261,39 @@ function gdprForgetMe (): void { /** * Disable third party sharing + * + * @deprecated Use {@link trackThirdPartySharing} instead */ -function disableThirdPartySharing (): void { - _preCheck('disable third-party sharing', _handleDisableThirdPartySharing, { - schedule: true - }) +function disableThirdPartySharing(): void { + trackThirdPartySharing({isEnabled: false}) } -function initSmartBanner (options: SmartBannerOptionsT): void { - if (_smartBanner) { - Logger.error('Smart Banner already initialised') - return - } +/** + * Track third party sharing + */ +function trackThirdPartySharing(adjustThirdPartySharing: ThirdPartySharingOptions): void { + const callback = () => ActivityState.waitForWebUUID() // ensure we have web_uuid to be sent with request + .then(() => trackTPS(adjustThirdPartySharing)) - _smartBanner = new SmartBanner(options) + _preCheck('third-party sharing', callback, { + schedule: false, + optionalInit: true + }) } -function showSmartBanner (): void { - if (!_smartBanner) { - Logger.error('Smart Banner is not initialised yet') - return - } - - _smartBanner.show() +/** @deprecated */ +function initSmartBanner (): void { + Logger.error('function `initSmartBanner` is deprecated'); } -function hideSmartBanner (): void { - if (!_smartBanner) { - Logger.error('Smart Banner is not initialised yet') - return - } - - _smartBanner.hide() +/** @deprecated */ +function showSmartBanner (): void { + Logger.error('function `showSmartBanner` is deprecated'); } -/** - * Handle third party sharing disable - * - * @private - */ -function _handleDisableThirdPartySharing (): void { - let done = sharingOptOut() - - if (!done) { - return - } - - sharingDisable() +/** @deprecated */ +function hideSmartBanner (): void { + Logger.error('function `hideSmartBanner` is deprecated'); } /** @@ -306,7 +301,7 @@ function _handleDisableThirdPartySharing (): void { * * @private */ -function _handleGdprForgetMe (): void { +function _handleGdprForgetMe(): void { if (status() !== 'paused') { return } @@ -326,7 +321,7 @@ function _handleGdprForgetMe (): void { * * @private */ -function _isInitialised (): boolean { +function _isInitialised(): boolean { return _isInitialising || Config.isInitialised() } @@ -338,7 +333,7 @@ function _isInitialised (): boolean { * * @private */ -function _pause (): void { +function _pause(): void { _isInitialising = false _isStarted = false @@ -352,7 +347,7 @@ function _pause (): void { * Shutdown all dependencies * @private */ -function _shutdown (async): void { +function _shutdown(async): void { if (async) { Logger.log('Adjust SDK has been shutdown due to asynchronous disable') } @@ -371,7 +366,7 @@ function _shutdown (async): void { * * @private */ -function _destroy (): void { +function _destroy(): void { _isInstalled = false _shutdown() @@ -389,35 +384,31 @@ function _destroy (): void { * @returns {Promise|boolean} * @private */ -function _continue (activityState: ActivityStateMapT): Promise { +function _continue(activityState: ActivityStateMapT): Promise { Logger.log(`Adjust SDK is starting with web_uuid set to ${activityState.uuid}`) const isInstalled = ActivityState.current.installed gdprForgetCheck() - if (!isInstalled) { - sharingDisableCheck() - } - const sdkStatus = status() let message = (rest) => `Adjust SDK start has been interrupted ${rest}` if (sdkStatus === 'off') { _shutdown() - return Promise.reject({interrupted: true, message: message('due to complete async disable')}) + return Promise.reject({ interrupted: true, message: message('due to complete async disable') }) } if (sdkStatus === 'paused') { _pause() - return Promise.reject({interrupted: true, message: message('due to partial async disable')}) + return Promise.reject({ interrupted: true, message: message('due to partial async disable') }) } if (_isStarted) { - return Promise.reject({interrupted: true, message: message('due to multiple synchronous start attempt')}) + return Promise.reject({ interrupted: true, message: message('due to multiple synchronous start attempt') }) } - queueRun({cleanUp: true}) + queueRun({ cleanUp: true }) return sessionWatch() .then(() => { @@ -426,7 +417,6 @@ function _continue (activityState: ActivityStateMapT): Promise { if (isInstalled) { _handleSdkInstalled() - sharingDisableCheck() } }) } @@ -434,7 +424,7 @@ function _continue (activityState: ActivityStateMapT): Promise { /** * Handles SDK installed and runs delayed tasks */ -function _handleSdkInstalled () { +function _handleSdkInstalled() { _isInstalled = true flush() @@ -448,7 +438,7 @@ function _handleSdkInstalled () { * @param {Object|Error} error * @private */ -function _error (error: CustomErrorT | Error) { +function _error(error: CustomErrorT | Error) { if (error.interrupted) { Logger.log(error.message) return @@ -483,7 +473,7 @@ function _error (error: CustomErrorT | Error) { * @param {Function=} options.attributionCallback * @private */ -function _start (options: InitOptionsT): void { +function _start(options: InitOptionsT): void { if (status() === 'off') { Logger.log('Adjust SDK is disabled, can not start the sdk') return @@ -496,7 +486,6 @@ function _start (options: InitOptionsT): void { subscribe('sdk:installed', _handleSdkInstalled) subscribe('sdk:shutdown', () => _shutdown(true)) subscribe('sdk:gdpr-forget-me', _handleGdprForgetMe) - subscribe('sdk:third-party-sharing-opt-out', sharingDisableFinish) subscribe('attribution:check', (e, result) => attributionCheck(result)) if (typeof options.attributionCallback === 'function') { @@ -509,7 +498,7 @@ function _start (options: InitOptionsT): void { .catch(_error) } -function _internalTrackEvent (params: EventParamsT) { +function _internalTrackEvent(params: EventParamsT) { if (Storage.getType() === STORAGE_TYPES.NO_STORAGE) { const reason = 'Adjust SDK can not track event, no storage available' Logger.log(reason) @@ -548,7 +537,7 @@ function _internalTrackEvent (params: EventParamsT) { * @param {boolean=false} schedule * @private */ -function _preCheck (description: string, callback: () => mixed, {schedule, waitForInitFinished, optionalInit}: {schedule?: boolean, waitForInitFinished?: boolean, optionalInit?: boolean} = {}): mixed { +function _preCheck(description: string, callback: () => mixed, { schedule, waitForInitFinished, optionalInit }: { schedule?: boolean, waitForInitFinished?: boolean, optionalInit?: boolean } = {}): mixed { if (Storage.getType() === STORAGE_TYPES.NO_STORAGE) { Logger.log(`Adjust SDK can not ${description}, no storage available`) return @@ -574,11 +563,11 @@ function _preCheck (description: string, callback: () => mixed, {schedule, waitF } } -function _clearDatabase () { +function _clearDatabase() { return Storage.deleteDatabase() } -function _restartAfterAsyncEnable () { +function _restartAfterAsyncEnable() { Logger.log('Adjust SDK has been restarted due to asynchronous enable') if (_options) { @@ -590,6 +579,8 @@ const Adjust = { initSdk, getAttribution, getWebUUID, + waitForAttribution, + waitForWebUUID, setReferrer, trackEvent, addGlobalCallbackParameters, @@ -604,6 +595,8 @@ const Adjust = { restart, gdprForgetMe, disableThirdPartySharing, + trackThirdPartySharing, + ThirdPartySharing, initSmartBanner, showSmartBanner, hideSmartBanner, diff --git a/src/sdk/preferences.js b/src/sdk/preferences.js deleted file mode 100644 index 6e9ab423..00000000 --- a/src/sdk/preferences.js +++ /dev/null @@ -1,143 +0,0 @@ -// @flow -import {publish} from './pub-sub' -import QuickStorage from './storage/quick-storage' -import Scheme from './storage/scheme' -import {REASON_GDPR, REASON_GENERAL} from './constants' - -type SdkDisabledT = {| - reason: REASON_GENERAL | REASON_GDPR, - pending: boolean -|} - -type ThirdPartySharingDisabledT = {| - reason: REASON_GENERAL, - pending: boolean -|} - -type PreferencesT = {| - thirdPartySharingDisabled?: ?ThirdPartySharingDisabledT, - sdkDisabled?: ?SdkDisabledT -|} - -/** - * Name of the store used by preferences - * - * @type {string} - * @private - */ -let _storeName: string = Scheme.preferences.name - -/** - * Local reference to be used for recovering preserved state - * - * @type {Object} - * @private - */ -let _preferences: ?PreferencesT = _getPreferences() - -/** - * Get preferences stored in the localStorage - * - * @returns {Object} - * @private - */ -function _getPreferences (): ?PreferencesT { - if (!_preferences) { - _setPreferences() - } - - return _preferences ? {..._preferences} : null -} - -/** - * Set local reference of the preserved preferences - * - * @private - */ -function _setPreferences (): void { - _preferences = QuickStorage.stores[_storeName] -} - -/** - * Get current disabled state - * - * @returns {Object|null} - */ -function getDisabled (): ?SdkDisabledT { - const preferences = _getPreferences() - - return preferences ? preferences.sdkDisabled : null -} - -/** - * Set current disabled state - * - * @param {Object|null} value - */ -function setDisabled (value: ?SdkDisabledT): void { - const sdkDisabled = value ? {...value} : null - - QuickStorage.stores[_storeName] = {..._getPreferences(), sdkDisabled} - - _setPreferences() -} - -/** - * Get current third-party-sharing disabled state - * - * @returns {Object} - * @private - */ -function getThirdPartySharing (): ?ThirdPartySharingDisabledT { - const preferences = _getPreferences() - - return preferences ? preferences.thirdPartySharingDisabled : null -} - -/** - * Set current third-party-sharing disabled state - * - * @param {Object=} value - * @private - */ -function setThirdPartySharing (value: ?ThirdPartySharingDisabledT): void { - const thirdPartySharingDisabled = value ? {...value} : null - - QuickStorage.stores[_storeName] = {..._getPreferences(), thirdPartySharingDisabled} - - _setPreferences() -} - -/** - * Reload current preferences from localStorage if changed outside of current scope (e.g. tab) - */ -function reload (): void { - const stored: PreferencesT = QuickStorage.stores[_storeName] || {} - const sdkDisabled: ?SdkDisabledT = (_preferences || {}).sdkDisabled || null - - if (stored.sdkDisabled && !sdkDisabled) { - publish('sdk:shutdown') - } - - _setPreferences() -} - -/** - * Recover preferences from memory if storage was lost - */ -function recover (): void { - const stored: ?PreferencesT = QuickStorage.stores[_storeName] - - if (!stored) { - QuickStorage.stores[_storeName] = {..._preferences} - } -} - -export { - getDisabled, - setDisabled, - getThirdPartySharing, - setThirdPartySharing, - reload, - recover -} diff --git a/src/sdk/preferences.ts b/src/sdk/preferences.ts new file mode 100644 index 00000000..54b1f61d --- /dev/null +++ b/src/sdk/preferences.ts @@ -0,0 +1,103 @@ +import { publish } from './pub-sub' +import QuickStorage from './storage/quick-storage' +import Scheme from './storage/scheme' +import { DISABLE_REASONS } from './constants' + +type SdkDisabledT = { + reason: DISABLE_REASONS, + pending: boolean +} + +type PreferencesT = { + sdkDisabled?: SdkDisabledT +} + +/** + * Name of the store used by preferences + */ +const _storeName: string = Scheme.preferences.name + +/** + * Local reference to be used for recovering preserved state + */ +let _preferences: PreferencesT | null = null +_preferences = _getPreferences() + +/** + * Get preferences stored in the localStorage + * + * @returns {Object} + * @private + */ +function _getPreferences(): PreferencesT | null { + if (!_preferences) { + _setPreferences() + } + + return _preferences ? { ..._preferences } : null +} + +/** + * Set local reference of the preserved preferences + * + * @private + */ +function _setPreferences(): void { + _preferences = QuickStorage.stores[_storeName] +} + +/** + * Get current disabled state + * + * @returns {Object|null} + */ +function getDisabled(): SdkDisabledT | null { + const preferences = _getPreferences() + + return preferences && preferences.sdkDisabled || null +} + +/** + * Set current disabled state + * + * @param {Object|null} value + */ +function setDisabled(value: SdkDisabledT | null): void { + const sdkDisabled = value ? { ...value } : null + + QuickStorage.stores[_storeName] = { ..._getPreferences(), sdkDisabled } + + _setPreferences() +} + +/** + * Reload current preferences from localStorage if changed outside of current scope (e.g. tab) + */ +function reload(): void { + const stored: PreferencesT = QuickStorage.stores[_storeName] || {} + const sdkDisabled: SdkDisabledT | null = (_preferences || {}).sdkDisabled || null + + if (stored.sdkDisabled && !sdkDisabled) { + publish('sdk:shutdown') + } + + _setPreferences() +} + +/** + * Recover preferences from memory if storage was lost + */ +function recover(): void { + const stored: PreferencesT = QuickStorage.stores[_storeName] + + if (!stored) { + QuickStorage.stores[_storeName] = { ..._preferences } + } +} + +export { + getDisabled, + setDisabled, + reload, + recover +} diff --git a/src/sdk/pub-sub.js b/src/sdk/pub-sub.ts similarity index 54% rename from src/sdk/pub-sub.js rename to src/sdk/pub-sub.ts index fced6afa..37d379a9 100644 --- a/src/sdk/pub-sub.js +++ b/src/sdk/pub-sub.ts @@ -1,47 +1,33 @@ -// @flow -import {entries} from './utilities' +import { entries } from './utilities' -type CallbackT = {| +type CallbackWithId = { id: string, - cb: (string, T) => mixed -|} + cb: (name: string, arg?: T) => unknown +} /** * List of events with subscribed callbacks - * - * @type {Object} - * @private */ -let _list = {} +let _list: Record> = {} /** * Reference to timeout ids so they can be cleared on destroy - * - * @type {Array} - * @private */ -let _timeoutIds = [] +let _timeoutIds: Array> = [] /** * Get unique id for the callback to use for unsubscribe - * - * @returns {string} - * @private */ -function _getId (): string { - return 'id' + Math.random().toString(36).substr(2, 16) +function _getId(): string { + return 'id' + Math.random().toString(36).substring(2, 16) } /** * Subscribe to a certain event - * - * @param {string} name - * @param {Function} cb - * @returns {string} */ -function subscribe (name: string, cb: $PropertyType, 'cb'>): string { +function subscribe(name: string, cb: (name: string, arg: T) => unknown): string { const id = _getId() - const callback: CallbackT = {id, cb} + const callback: CallbackWithId = { id, cb } if (!_list[name]) { _list[name] = [] @@ -54,38 +40,31 @@ function subscribe (name: string, cb: $PropertyType, 'cb'>): str /** * Unsubscribe particular callback from an event - * - * @param {string} id */ -function unsubscribe (id: string): void { +function unsubscribe(id: string) { if (!id) { return } entries(_list) .some(([, callbacks]) => callbacks - .some((callback: CallbackT, i: number) => { + .some((callback: CallbackWithId, i: number) => { if (callback.id === id) { callbacks.splice(i, 1) - return true } })) } /** * Publish certain event with optional arguments - * - * @param {string} name - * @param {*} args - * @returns {Array} */ -function publish (name: string, args: T): void { +function publish(name: string, args?: T): void { if (!_list[name]) { return } _list[name] - .forEach((item: CallbackT) => { + .forEach((item: CallbackWithId) => { if (typeof item.cb === 'function') { _timeoutIds.push(setTimeout(() => item.cb(name, args))) } @@ -95,7 +74,7 @@ function publish (name: string, args: T): void { /** * Destroy all registered events with their callbacks */ -function destroy (): void { +function destroy(): void { _timeoutIds.forEach(clearTimeout) _timeoutIds = [] _list = {} diff --git a/src/sdk/smart-banner/api.ts b/src/sdk/smart-banner/api.ts deleted file mode 100644 index 4fe0f936..00000000 --- a/src/sdk/smart-banner/api.ts +++ /dev/null @@ -1,80 +0,0 @@ -import Logger from '../logger' -import { DeviceOS } from './detect-os' -import { Network } from './network/network' - -export enum Position { - Top = 'top', - Bottom = 'bottom' -} - -export interface SmartBannerData { - appId: string; - appName: string; - position: Position; - imageUrl?: string; - header: string; - description: string; - buttonText: string; - dismissInterval: number; - trackerToken: string; - deeplinkPath?: string; -} - -interface SmartBannerResponse { - app: { - name: string; - default_store_app_id: string; - }; - platform: DeviceOS; - position: Position; - tracker_token: string; - deeplink_path: string; - title: string; - description: string; - button_label: string; - image_url?: string; -} - -/** - * Ensures response contains general info: title, description, button_label and tracker_token and converts response - * to SmartBannerData - */ -function validate(response: Partial): SmartBannerData | null { - const { title, description, button_label, tracker_token } = response - - if (title && description && button_label && tracker_token) { - return { - appId: response.app?.default_store_app_id || '', - appName: response.app?.name || '', - position: response.position || Position.Bottom, - imageUrl: response.image_url, - header: title, - description: description, - buttonText: button_label, - trackerToken: tracker_token, - deeplinkPath: response.deeplink_path, - dismissInterval: 24 * 60 * 60 * 1000, // 1 day in millis before show banner next time - } - } - - return null -} - -export function fetchSmartBannerData(webToken: string, deviceOs: DeviceOS, network: Network): Promise { - const path = '/smart_banner' - - return network.request[]>(path, { 'app_web_token': webToken }) - .then(banners => { - const banner = banners.find(item => item.platform === deviceOs) - - if (!banner) { - return null - } - - return validate(banner) - }) - .catch(error => { - Logger.error('Network error occurred during loading Smart Banner: ' + JSON.stringify(error)) - return null - }) -} diff --git a/src/sdk/smart-banner/assets/styles.module.scss b/src/sdk/smart-banner/assets/styles.module.scss deleted file mode 100644 index b0a4200a..00000000 --- a/src/sdk/smart-banner/assets/styles.module.scss +++ /dev/null @@ -1,142 +0,0 @@ -$buttonColor: #6e7492; -$textColor: #353a52; -$shadowColor: #e0e2ec; -$fontsFamily: ArialMt, Arial, sans-serif; -$bannerMaxWidth: 428px; - -@function encodeColor($colour) { - @return "%23" + str-slice("#{$colour}", 2, -1); -} - -.bannerContainer { - height: 76px; - - @media (min-width: $bannerMaxWidth) { - & { - height: 0; - } - } -} - -.banner { - position: fixed; - left: 0; - right: 0; - z-index: 10000000; - - &.stickyToTop { - top: 0; - } - - &.stickyToBottom { - bottom: 0; - } - - .bannerBody { - margin: 0 auto; - max-width: $bannerMaxWidth; - background: white; - - .content { - display: flex; - align-items: center; - padding: 10px 8px 10px 4px; - - .dismiss { - width: 32px; - height: 32px; - border: none; - background: url("data:image/svg+xml;utf8,\ - \ - \ - \ - "); - background-repeat: no-repeat; - background-position: center center; - background-size: 8px 8px, auto; - cursor: pointer; - } - - .appIcon { - width: 56px; - height: 56px; - overflow: hidden; - background-color: $buttonColor; - border-radius: 8px; - - & .placeholder { - display: flex; - align-items: center; - justify-content: center; - width: 100%; - height: 100%; - color: $textColor; - font-weight: bold; - font-size: 23px; - font-family: $fontsFamily; - line-height: 32px; - background-color: $shadowColor; - } - - & .image { - width: 100%; - } - } - - .textContainer { - flex: 1 1 0%; - min-height: 0; - min-width: 0; - margin: 0 12px; - } - - .bannerText { - overflow: hidden; - text-overflow: ellipsis; - } - - h4 { - margin: 5px 0 8px; - color: $textColor; - font-family: Arial-BoldMT, $fontsFamily; - font-size: 12px; - font-weight: bold; - line-height: 16px; - white-space: nowrap; - } - - p { - margin: 8px 0 7px; - color: $textColor; - font-family: $fontsFamily; - font-size: 9px; - line-height: 11px; - max-height: 22px; - - // See https://css-tricks.com/line-clampin/ - display: -webkit-box; - -webkit-line-clamp: 2; - -webkit-box-orient: vertical; - } - - .action { - color: $buttonColor; - background: rgb(249, 250, 252); - border: 1px solid rgb(205, 208, 224); - border-radius: 4px; - border-color: $buttonColor; - box-shadow: inset 0px -1px 0px 0px $shadowColor; - padding: 4px 6.5px; - display: inline-block; - vertical-align: middle; - text-align: center; - font-family: $fontsFamily; - font-size: 12px; - font-weight: 500; - line-height: 16px; - cursor: pointer; - text-decoration: none; - } - } - } -} diff --git a/src/sdk/smart-banner/assets/template.ts b/src/sdk/smart-banner/assets/template.ts deleted file mode 100644 index 6ec81233..00000000 --- a/src/sdk/smart-banner/assets/template.ts +++ /dev/null @@ -1,19 +0,0 @@ -import styles from './styles.module.scss' - -export default (positionStyle: string, header: string, description: string, buttonText: string, href: string) => ` -
` diff --git a/src/sdk/smart-banner/detect-os.ts b/src/sdk/smart-banner/detect-os.ts deleted file mode 100644 index 9c423224..00000000 --- a/src/sdk/smart-banner/detect-os.ts +++ /dev/null @@ -1,40 +0,0 @@ -/** - * Operation systems - */ -export enum DeviceOS { - Android = 'android', - iOS = 'ios', - WindowsPC = 'windows', - WindowsPhone = 'windows-phone', -} - -/** - * Returns one of android, ios, windows, windows-phone or undefined for another OS. - */ -export function getDeviceOS(): Maybe { - const userAgent = navigator?.userAgent?.toLowerCase() - - if (!userAgent || userAgent.length < 1) { - return undefined - } - - if(/ipad|iphone|ipod/.test(userAgent)) { - return DeviceOS.iOS - } - - // Checking Windows first because Lumia devices could have for example - // "Mozilla/5.0 (Windows Mobile 10; Android 8.0.0; Microsoft; Lumia 950XL) ..." user agent - if (userAgent.includes('windows')) { - if (/phone|mobile/.test(userAgent)) { - return DeviceOS.WindowsPhone - } - - return DeviceOS.WindowsPC - } - - if (userAgent.includes('android')) { - return DeviceOS.Android - } - - return undefined -} diff --git a/src/sdk/smart-banner/network/errors.ts b/src/sdk/smart-banner/network/errors.ts deleted file mode 100644 index fca73b0a..00000000 --- a/src/sdk/smart-banner/network/errors.ts +++ /dev/null @@ -1,9 +0,0 @@ -export interface NetworkError { - status: number; - message: string; -} - -export const NoConnectionError: NetworkError = { - status: 0, - message: 'No internet connectivity' -} diff --git a/src/sdk/smart-banner/network/network.ts b/src/sdk/smart-banner/network/network.ts deleted file mode 100644 index 0fff4805..00000000 --- a/src/sdk/smart-banner/network/network.ts +++ /dev/null @@ -1,20 +0,0 @@ -export interface Network { - endpoint: string; - request: (path: string, params?: Record) => Promise; -} - -export class NetworkDecorator implements Network { - constructor(protected network: Network) { } - - public get endpoint(): string { - return this.network.endpoint - } - - public set endpoint(value: string) { - this.network.endpoint = value - } - - request(path: string, params?: Record): Promise { - return this.network.request(path, params) - } -} diff --git a/src/sdk/smart-banner/network/url-startegy-network.ts b/src/sdk/smart-banner/network/url-startegy-network.ts deleted file mode 100644 index e6f0a3a0..00000000 --- a/src/sdk/smart-banner/network/url-startegy-network.ts +++ /dev/null @@ -1,58 +0,0 @@ -import { ENDPOINTS } from '../../constants' -import { NetworkDecorator, Network } from '../network/network' -import { UrlStrategy } from './url-strategy/url-strategy' -import { UrlStrategyFactory, UrlStrategyConfig } from './url-strategy/url-strategy-factory' -import { NetworkError } from './errors' - -export class NetworkWithUrlStrategy extends NetworkDecorator { - private static readonly DEFAULT_ENDPOINT = ENDPOINTS.default.app - private lastSuccessfulEndpoint: string | undefined - private urlStrategy: UrlStrategy - - constructor(network: Network, { urlStrategy, urlStrategyConfig }: NetworkWithUrlStrategy.UrlStrategyParameters) { - super(network) - - this.urlStrategy = urlStrategy || UrlStrategyFactory.create(urlStrategyConfig) - } - - /** - * Returns last succesfull endpoint or default (`https://app.adjust.com`) one - */ - public get endpoint(): string { - return this.lastSuccessfulEndpoint || NetworkWithUrlStrategy.DEFAULT_ENDPOINT - } - - /** - * Sends a request to provided path choosing origin with UrlStrategy and caches used origin if it was successfully - * reached - * - * @param path - * @param params non-encoded parameters of the request - */ - public request(path: string, params?: Record): Promise { - - return this.urlStrategy.retries((baseUrlsMap) => { - this.network.endpoint = baseUrlsMap.app - - return this.network.request(path, params) - .then((result: T) => { - this.lastSuccessfulEndpoint = baseUrlsMap.app - return result - }) - .catch((err: NetworkError) => { - this.lastSuccessfulEndpoint = undefined - throw err - }) - }) - } -} - -namespace NetworkWithUrlStrategy { - export type UrlStrategyParameters = { - urlStrategy: UrlStrategy; - urlStrategyConfig?: never; - } | { - urlStrategy?: never; - urlStrategyConfig: UrlStrategyConfig; - } -} diff --git a/src/sdk/smart-banner/network/url-strategy/blocked-url-bypass.ts b/src/sdk/smart-banner/network/url-strategy/blocked-url-bypass.ts deleted file mode 100644 index f432f570..00000000 --- a/src/sdk/smart-banner/network/url-strategy/blocked-url-bypass.ts +++ /dev/null @@ -1,43 +0,0 @@ -import { BaseUrlsMap } from './url-strategy' -import { ENDPOINTS } from '../../../constants' - -export namespace BlockedUrlBypass { - export const Default = 'default' - export const India = 'india' - export const China = 'china' - - export type Strategy = typeof Default | typeof India | typeof China - - const endpoints: Record = { - [BlockedUrlBypass.Default]: ENDPOINTS.default, - [BlockedUrlBypass.India]: ENDPOINTS.india, - [BlockedUrlBypass.China]: ENDPOINTS.china, - } - - const getPreferredUrlsWithOption = (endpoints: Record, option?: BlockedUrlBypass.Strategy) => { - - if (option === BlockedUrlBypass.India) { - return [ - endpoints[BlockedUrlBypass.India], - endpoints[BlockedUrlBypass.Default] - ] - } - - if (option === BlockedUrlBypass.China) { - return [ - endpoints[BlockedUrlBypass.China], - endpoints[BlockedUrlBypass.Default] - ] - } - - return [ - endpoints[BlockedUrlBypass.Default], - endpoints[BlockedUrlBypass.India], - endpoints[BlockedUrlBypass.China] - ] - } - - export function preferredUrlsGetter(option?: BlockedUrlBypass.Strategy, endpointsMap: Record = endpoints) { - return () => getPreferredUrlsWithOption(endpointsMap, option) - } -} diff --git a/src/sdk/smart-banner/network/url-strategy/custom-url.ts b/src/sdk/smart-banner/network/url-strategy/custom-url.ts deleted file mode 100644 index abef7f88..00000000 --- a/src/sdk/smart-banner/network/url-strategy/custom-url.ts +++ /dev/null @@ -1,14 +0,0 @@ -export namespace CustomUrl { - const getPreferredUrlsWithOption = (customUrl: string) => { - - return [{ - endpointName: `Custom (${customUrl})`, - app: customUrl, - gdpr: customUrl - }] - } - - export function preferredUrlsGetter(customUrl: string) { - return () => getPreferredUrlsWithOption(customUrl) - } -} diff --git a/src/sdk/smart-banner/network/url-strategy/data-residency.ts b/src/sdk/smart-banner/network/url-strategy/data-residency.ts deleted file mode 100644 index 0ce32096..00000000 --- a/src/sdk/smart-banner/network/url-strategy/data-residency.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { BaseUrlsMap } from './url-strategy' -import { ENDPOINTS } from '../../../constants' - -export namespace DataResidency { - export const EU = 'EU' - export const TR = 'TR' - export const US = 'US' - - export type Region = typeof EU | typeof TR | typeof US - - const endpoints: Record = { - [DataResidency.EU]: ENDPOINTS.EU, - [DataResidency.TR]: ENDPOINTS.TR, - [DataResidency.US]: ENDPOINTS.US, - } - - const getPreferredUrlsWithOption = (endpoints: Record, option: DataResidency.Region) => { - return [endpoints[option]] - } - - export function preferredUrlsGetter(option: DataResidency.Region, endpointsMap: Record = endpoints) { - return () => getPreferredUrlsWithOption(endpointsMap, option) - } -} diff --git a/src/sdk/smart-banner/network/url-strategy/url-strategy-factory.ts b/src/sdk/smart-banner/network/url-strategy/url-strategy-factory.ts deleted file mode 100644 index 3815df8d..00000000 --- a/src/sdk/smart-banner/network/url-strategy/url-strategy-factory.ts +++ /dev/null @@ -1,45 +0,0 @@ -import Logger from '../../../logger' -import { UrlStrategy } from './url-strategy' -import { BlockedUrlBypass } from './blocked-url-bypass' -import { CustomUrl } from './custom-url' -import { DataResidency } from './data-residency' - -export type UrlStrategyConfig = { - customUrl: string; - urlStrategy?: never; - dataResidency?: never; -} | { - customUrl?: never; - dataResidency: DataResidency.Region; - urlStrategy?: never; -} | { - customUrl?: never; - dataResidency?: never; - urlStrategy?: BlockedUrlBypass.Strategy; -} - -export namespace UrlStrategyFactory { - const incorrectOptionIgnoredMessage = (higherPriority: string, lowerPriority: string) => { - Logger.warn(`Both ${higherPriority} and ${lowerPriority} are set in config, ${lowerPriority} will be ignored`) - } - - export function create(config: UrlStrategyConfig): UrlStrategy { - const { customUrl, dataResidency, urlStrategy } = config - - if (customUrl) { - if (dataResidency || urlStrategy) { - incorrectOptionIgnoredMessage('customUrl', dataResidency ? 'dataResidency' : 'urlStrategy') - } - - return new UrlStrategy(CustomUrl.preferredUrlsGetter(customUrl)) - } else if (dataResidency) { - if (urlStrategy) { - incorrectOptionIgnoredMessage('dataResidency', 'urlStrategy') - } - - return new UrlStrategy(DataResidency.preferredUrlsGetter(dataResidency)) - } else { - return new UrlStrategy(BlockedUrlBypass.preferredUrlsGetter(urlStrategy)) - } - } -} diff --git a/src/sdk/smart-banner/network/url-strategy/url-strategy.ts b/src/sdk/smart-banner/network/url-strategy/url-strategy.ts deleted file mode 100644 index be109ec7..00000000 --- a/src/sdk/smart-banner/network/url-strategy/url-strategy.ts +++ /dev/null @@ -1,51 +0,0 @@ -import Logger from '../../../logger' -import { NetworkError, NoConnectionError } from '../errors' - -export type BaseUrlsMap = { - endpointName: string; - app: string; - gdpr: string; -} - -export class UrlStrategy { - static NoPreferredUrlsDefinedError = new ReferenceError('UrlStrategy: No preferred URL defined') - - constructor(private preferredUrls: () => BaseUrlsMap[]) { } - - /** - * Gets the list of preferred endpoints and wraps `sendRequest` function with iterative retries until available - * endpoint found or another error occurred. - */ - public retries(sendRequest: (urls: BaseUrlsMap) => Promise): Promise { - let attempt = 0 - - const trySendRequest = (): Promise => { - const preferredUrls = this.preferredUrls() - - if (!preferredUrls || preferredUrls.length === 0) { - Logger.error(UrlStrategy.NoPreferredUrlsDefinedError.message) - throw UrlStrategy.NoPreferredUrlsDefinedError - } - - const urlsMap = preferredUrls[attempt++] - - return sendRequest(urlsMap) - .catch((reason: NetworkError) => { - if (reason === NoConnectionError) { - Logger.log(`Failed to connect ${urlsMap.endpointName} endpoint`) - - if (attempt < preferredUrls.length) { - Logger.log(`Trying ${preferredUrls[attempt].endpointName} one`) - - return trySendRequest() // Trying next endpoint - } - } - - // Another error occurred or we ran out of attempts, re-throw - throw reason - }) - } - - return trySendRequest() - } -} diff --git a/src/sdk/smart-banner/network/xhr-network.ts b/src/sdk/smart-banner/network/xhr-network.ts deleted file mode 100644 index e6b2a64f..00000000 --- a/src/sdk/smart-banner/network/xhr-network.ts +++ /dev/null @@ -1,78 +0,0 @@ -import Globals from '../../globals' -import { Network } from './network' -import { parseJson } from '../utilities' -import { NetworkError, NoConnectionError } from './errors' - -type Primitive = string | number | boolean - -/** Sends HTTP GET request using XMLHttpRequest */ -export class XhrNetwork implements Network { - constructor(public origin?: string) { } - - public get endpoint(): string { - if (!this.origin) { - throw Error('XhrNetwork: Origin not defined') - } - - return this.origin - } - - public set endpoint(value: string) { - this.origin = value - } - - /** - * Creates an XMLHttpRequest object and sends a GET request with provided encoded URL - * @param url encoded URL - */ - private xhr(url: string): Promise { - return new Promise((resolve, reject: (err: NetworkError) => void) => { - const xhr = new XMLHttpRequest() - xhr.open('GET', url) - - const headers = [ - ['Client-SDK', `js${Globals.version}`], - ['Content-Type', 'application/json'] - ] - - headers.forEach(([key, value]) => { - xhr.setRequestHeader(key, value) - }) - - xhr.onerror = () => reject(NoConnectionError) - - xhr.onreadystatechange = () => { - if (xhr.readyState !== 4) { - return - } - - const okStatus = xhr.status >= 200 && xhr.status < 300 - const json = parseJson(xhr.responseText) - - if (xhr.status === 0) { - reject(NoConnectionError) - } else { - if (okStatus) { - resolve(json) - } else { - reject({ status: xhr.status, message: json || xhr.responseText || '' }) - } - } - } - - xhr.send() - }) - } - - private encodeParams(params: Record): string { - return Object.keys(params) - .map(key => [encodeURIComponent(key), encodeURIComponent(params[key])].join('=')) - .join('&') - } - - public request(path: string, params?: Record): Promise { - const encodedParams = params ? `?${this.encodeParams(params)}` : '' - - return this.xhr(`${this.endpoint}${path}${encodedParams}`) - } -} diff --git a/src/sdk/smart-banner/smart-banner.ts b/src/sdk/smart-banner/smart-banner.ts deleted file mode 100644 index 922f8be1..00000000 --- a/src/sdk/smart-banner/smart-banner.ts +++ /dev/null @@ -1,216 +0,0 @@ -import Logger from '../logger' -import { getDeviceOS } from './detect-os' -import { Storage, StorageFactory } from './storage/factory' -import { fetchSmartBannerData, SmartBannerData } from './api' -import { SmartBannerView } from './view/smart-banner-view' -import { Network } from './network/network' -import { XhrNetwork } from './network/xhr-network' -import { NetworkWithUrlStrategy } from './network/url-startegy-network' -import { DataResidency } from './network/url-strategy/data-residency' - -type LogLevel = 'none' | 'error' | 'warning' | 'info' | 'verbose' - -type Callback = () => any; - -interface SmartBannerOptions { - webToken: string; - logLevel?: LogLevel; - dataResidency?: DataResidency.Region; - onCreated?: Callback; - onDismissed?: Callback; -} - -/** - * Adjust Web SDK Smart Banner - */ -export class SmartBanner { - private readonly STORAGE_KEY_DISMISSED = 'closed' - private network: Network - private storage: Storage - private timer: ReturnType | null = null - private dataFetchPromise: Promise | null - private banner: SmartBannerView | null - private onCreated?: Callback - private onDismissed?: Callback - - constructor({ webToken, logLevel = 'error', dataResidency, onCreated, onDismissed }: SmartBannerOptions, network?: Network) { - this.onCreated = onCreated - this.onDismissed = onDismissed - - Logger.setLogLevel(logLevel) - - const config = dataResidency ? { dataResidency } : {} - this.network = network || new NetworkWithUrlStrategy(new XhrNetwork(), { urlStrategyConfig: config }) - - this.storage = StorageFactory.createStorage() - - this.init(webToken) - } - - /** - * Initiate Smart Banner - * - * @param webToken token used to get data from backend - */ - init(webToken: string) { - if (this.banner) { - Logger.error('Smart Banner already exists') - return - } - - if (this.dataFetchPromise) { - Logger.error('Smart Banner is initialising already') - return - } - - const deviceOs = getDeviceOS() - if (!deviceOs) { - Logger.log('This platform is not one of the targeting ones, Smart Banner will not be shown') - return - } - - this.dataFetchPromise = fetchSmartBannerData(webToken, deviceOs, this.network) - - this.dataFetchPromise.then(bannerData => { - this.dataFetchPromise = null - - if (!bannerData) { - Logger.log(`No Smart Banners for ${deviceOs} platform found`) - return - } - - const whenToShow = this.getDateToShowAgain(bannerData.dismissInterval) - if (Date.now() < whenToShow) { - Logger.log('Smart Banner was dismissed') - this.scheduleCreation(webToken, whenToShow) - return - } - - Logger.log('Creating Smart Banner') - - this.banner = new SmartBannerView( - bannerData, - () => this.dismiss(webToken, bannerData.dismissInterval), - this.network.endpoint - ) - - Logger.log('Smart Banner created') - - if (this.onCreated) { - this.onCreated() - } - }) - } - - /** - * Show Smart Banner - */ - show(): void { - if (this.banner) { - this.banner.show() - return - } - - if (this.dataFetchPromise) { - Logger.log('Smart Banner will be shown after initialisation finished') - - this.dataFetchPromise - .then(() => { - Logger.log('Initialisation finished, showing Smart Banner') - this.show() - }) - - return - } - - Logger.error('There is no Smart Banner to show, have you called initialisation?') - } - - /** - * Hide Smart Banner - */ - hide(): void { - if (this.banner) { - this.banner.hide() - return - } - - if (this.dataFetchPromise) { - Logger.log('Smart Banner will be hidden after initialisation finished') - - this.dataFetchPromise - .then(() => { - Logger.log('Initialisation finished, hiding Smart Banner') - this.hide() - }) - - return - } - - Logger.error('There is no Smart Banner to hide, have you called initialisation?') - } - - /** - * Removes Smart Banner from DOM - */ - private destroy() { - if (this.banner) { - this.banner.destroy() - this.banner = null - Logger.log('Smart Banner removed') - } else { - Logger.error('There is no Smart Banner to remove') - } - } - - /** - * Schedules next Smart Banner show and removes banner from DOM - */ - private dismiss(webToken: string, dismissInterval: number) { - Logger.log('Smart Banner dismissed') - - this.storage.setItem(this.STORAGE_KEY_DISMISSED, Date.now()) - const whenToShow = this.getDateToShowAgain(dismissInterval) - this.scheduleCreation(webToken, whenToShow) - - this.destroy() - - if (this.onDismissed) { - this.onDismissed() - } - } - - /** - * Sets a timeout to schedule next Smart Banner show - */ - private scheduleCreation(webToken: string, when: number) { - if (this.timer) { - Logger.log('Clearing previously scheduled creation of Smart Banner') - clearTimeout(this.timer) - this.timer = null - } - - const delay = when - Date.now() - this.timer = setTimeout( - () => { - this.timer = null - this.init(webToken) - }, - delay) - - Logger.log('Smart Banner creation scheduled on ' + new Date(when)) - } - - /** - * Returns date when Smart Banner should be shown again - */ - private getDateToShowAgain(dismissInterval: number): number { - const dismissedDate = this.storage.getItem(this.STORAGE_KEY_DISMISSED) - - if (!dismissedDate || typeof dismissedDate !== 'number') { - return Date.now() - } - - return dismissedDate + dismissInterval - } -} diff --git a/src/sdk/smart-banner/storage/factory.ts b/src/sdk/smart-banner/storage/factory.ts deleted file mode 100644 index 0cdfe8c1..00000000 --- a/src/sdk/smart-banner/storage/factory.ts +++ /dev/null @@ -1,31 +0,0 @@ -import { LocalStorage } from './local-storage' -import { InMemoryStorage } from './in-memory-storage' -import { Storage } from './storage' - -class StorageFactory { - private static isLocalStorageSupported(): boolean { - try { - const uid = (new Date).toString() - const storage = window.localStorage - storage.setItem(uid, uid) - const result = storage.getItem(uid) === uid - storage.removeItem(uid) - const support = !!(result && storage) - - return support - - } catch (e) { - return false - } - } - - public static createStorage(): Storage { - if (this.isLocalStorageSupported()) { - return new LocalStorage() - } - - return new InMemoryStorage() - } -} - -export { Storage, StorageFactory } diff --git a/src/sdk/smart-banner/storage/in-memory-storage.ts b/src/sdk/smart-banner/storage/in-memory-storage.ts deleted file mode 100644 index 5ef85ae3..00000000 --- a/src/sdk/smart-banner/storage/in-memory-storage.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { Storage } from './storage' - -export class InMemoryStorage implements Storage { - private items: Record = {} - - public setItem(key: string, value: any): void { - this.items[key] = value - } - - public getItem(key: string): any | null { - return Object.prototype.hasOwnProperty.call(this.items, key) ? this.items[key] : null - } - - public removeItem(key: string): void { - delete this.items[key] - } -} diff --git a/src/sdk/smart-banner/storage/local-storage.ts b/src/sdk/smart-banner/storage/local-storage.ts deleted file mode 100644 index 94609f9e..00000000 --- a/src/sdk/smart-banner/storage/local-storage.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { parseJson } from '../utilities' -import { Storage } from './storage' - -export class LocalStorage implements Storage { - constructor(private storageName: string = 'adjust-smart-banner') {} - - setItem(key: string, value: any): void { - localStorage.setItem(`${this.storageName}.${key}`, JSON.stringify(value)) - } - - getItem(key: string): any | null { - const value = localStorage.getItem(`${this.storageName}.${key}`) - return parseJson(value) - } - - removeItem(key: string): void { - localStorage.removeItem(`${this.storageName}.${key}`) - } -} diff --git a/src/sdk/smart-banner/storage/storage.ts b/src/sdk/smart-banner/storage/storage.ts deleted file mode 100644 index 48f9d21f..00000000 --- a/src/sdk/smart-banner/storage/storage.ts +++ /dev/null @@ -1,5 +0,0 @@ -export interface Storage { - setItem(key: string, value: any): void; - getItem(key: string): any | null; - removeItem(key: string): void; -} diff --git a/src/sdk/smart-banner/typings.d.ts b/src/sdk/smart-banner/typings.d.ts deleted file mode 100644 index f8e975fa..00000000 --- a/src/sdk/smart-banner/typings.d.ts +++ /dev/null @@ -1,4 +0,0 @@ -declare module '*.scss' { - const content: Record - export default content -} diff --git a/src/sdk/smart-banner/utilities.ts b/src/sdk/smart-banner/utilities.ts deleted file mode 100644 index c40f0e55..00000000 --- a/src/sdk/smart-banner/utilities.ts +++ /dev/null @@ -1,15 +0,0 @@ -/** - * Wraps JSON.parse() with try-catch. - * Returns parsed object if successfully parsed and null otherwise. - */ -export function parseJson(str?: string | null): any { - if (!str) { - return null - } - - try { - return JSON.parse(str) - } catch (error) { - return null - } -} diff --git a/src/sdk/smart-banner/view/app-icon.ts b/src/sdk/smart-banner/view/app-icon.ts deleted file mode 100644 index 6ef1cace..00000000 --- a/src/sdk/smart-banner/view/app-icon.ts +++ /dev/null @@ -1,55 +0,0 @@ -import { SmartBannerData } from '../api' - -type AppIconData = Pick - -export class AppIcon { - private appTraceUrl = (appId: string) => `https://www.apptrace.com/api/app/${appId}/artwork_url_small` - - private appName: string - - private image: HTMLImageElement - private placeholder: HTMLElement - - constructor(bannerData: AppIconData, image: HTMLImageElement, placeholder: HTMLElement) { - this.image = image - this.placeholder = placeholder - this.appName = bannerData.appName - - const sources = this.getSources(bannerData) - this.showImage(sources) - } - - private getSources(bannerData: AppIconData): string[] { - const sourcesArray: string[] = [] - - if (bannerData.imageUrl) { - sourcesArray.push(bannerData.imageUrl) - } - sourcesArray.push(this.appTraceUrl(bannerData.appId)) - - return sourcesArray - } - - private showImage(sources: string[]): Promise { - const imageLoadingPromise = sources.reduce((acc, url) => { - return acc.catch(() => this.loadImage(url, this.image)) - }, Promise.reject()) - - return imageLoadingPromise - .then(() => { - this.placeholder.remove() - }) - .catch(() => { - this.image.remove() - this.placeholder.innerText = this.appName.length ? this.appName[0].toUpperCase() : '' - }) - } - - private loadImage(url: string, image: HTMLImageElement) { - return new Promise((resolve, reject) => { - image.onload = resolve - image.onerror = reject - image.src = url - }) - } -} diff --git a/src/sdk/smart-banner/view/smart-banner-view.ts b/src/sdk/smart-banner/view/smart-banner-view.ts deleted file mode 100644 index a1609be0..00000000 --- a/src/sdk/smart-banner/view/smart-banner-view.ts +++ /dev/null @@ -1,75 +0,0 @@ -import styles from '../assets/styles.module.scss' -import render from '../assets/template' -import { Position, SmartBannerData } from '../api' -import { AppIcon } from './app-icon' - -export class SmartBannerView { - private parent: HTMLElement = document.body - private banner: HTMLElement - private dismissButton: Element | null = null - - private onDismiss: (() => void) - - constructor(data: SmartBannerData, onDismiss: () => void, endpoint: string) { - this.onDismiss = onDismiss - - this.render(data, endpoint) - } - - private render(bannerData: SmartBannerData, endpoint: string) { - this.banner = document.createElement('div') - this.banner.setAttribute('class', styles.bannerContainer) - - const positionStyle = bannerData.position === Position.Top ? styles.stickyToTop : styles.stickyToBottom - const query = bannerData.deeplinkPath ? `?deeplink=${encodeURIComponent(bannerData.deeplinkPath)}` : '' - const href = `${endpoint}/${bannerData.trackerToken}${query}` - this.banner.innerHTML = render(positionStyle, bannerData.header, bannerData.description, bannerData.buttonText, href) - - if (bannerData.position === Position.Top) { - this.parent.insertBefore(this.banner, this.parent.firstChild) - } else { - this.parent.appendChild(this.banner) - } - - this.dismissButton = this.getElemByClass(styles.dismiss) - if (this.dismissButton) { - this.dismissButton.addEventListener('click', this.onDismiss) - } - - const appIconPlaceholder = this.getElemByClass(styles.placeholder) - const appIconImage = this.getElemByClass(styles.image) - - if (appIconImage && appIconPlaceholder) { - new AppIcon(bannerData, appIconImage, appIconPlaceholder) - } - } - - public show() { - this.banner.hidden = false - } - - public hide() { - this.banner.hidden = true - } - - public destroy() { - this.removeDismissButtonHandler() - this.banner.remove() - } - - private removeDismissButtonHandler() { - if (this.dismissButton && this.onDismiss) { - this.dismissButton.removeEventListener('click', this.onDismiss) - this.dismissButton = null - } - } - - private getElemByClass(classNames: string): T | null { - if (this.banner) { - const elements = this.banner.getElementsByClassName(classNames) - return elements.length > 0 ? elements[0] as T : null - } - - return null - } -} diff --git a/src/sdk/storage/scheme.ts b/src/sdk/storage/scheme.ts index 29570c1f..2953c48a 100644 --- a/src/sdk/storage/scheme.ts +++ b/src/sdk/storage/scheme.ts @@ -1,4 +1,4 @@ -import { REASON_GDPR, REASON_GENERAL } from '../constants' +import { DISABLE_REASONS } from '../constants' import { StoredValue } from './types' /** @@ -208,7 +208,7 @@ const _preferencesScheme: StoreOptionsOptionalKey = { reason: { key: 'r', values: { - [REASON_GENERAL]: 1 + [DISABLE_REASONS.REASON_GENERAL]: 1 } }, pending: { @@ -226,8 +226,8 @@ const _preferencesScheme: StoreOptionsOptionalKey = { reason: { key: 'r', values: { - [REASON_GENERAL]: 1, - [REASON_GDPR]: 2 + [DISABLE_REASONS.REASON_GENERAL]: 1, + [DISABLE_REASONS.REASON_GDPR]: 2 } }, pending: { diff --git a/src/sdk/storage/types.ts b/src/sdk/storage/types.ts index 47fa49d4..4b0030cb 100644 --- a/src/sdk/storage/types.ts +++ b/src/sdk/storage/types.ts @@ -36,4 +36,4 @@ function valueIsRecord(value: StoredValue | Record): value is R return isObject(value) } -export { IStorage, KeyRangeCondition, StoredValue, StoredRecord, StoredRecordId, Error, valueIsRecord } +export { IStorage, KeyRangeCondition, StoredValue, StoredRecord, StoredRecordId, type Error, valueIsRecord } diff --git a/src/sdk/third-party-sharing.js b/src/sdk/third-party-sharing.js deleted file mode 100644 index 383a259e..00000000 --- a/src/sdk/third-party-sharing.js +++ /dev/null @@ -1,136 +0,0 @@ -// @flow -import {push} from './queue' -import {getThirdPartySharing, setThirdPartySharing} from './preferences' -import Config from './config' -import Logger from './logger' -import {REASON_GENERAL} from './constants' - -type ThirdPartySharingStatusT = 'pending' | 'on' | 'off' - -/** - * Log messages used in different scenarios - * - * @type {Object} - * @private - */ -const _logMessages = { - running: 'Adjust SDK is running pending third-party sharing opt-out request', - delayed: 'Adjust SDK will run third-party sharing opt-out request after initialisation', - pending: 'Adjust SDK already queued third-party sharing opt-out request', - off: 'Third-party sharing opt-out is already done', - start: { - inProgress: 'Third-party sharing opt-out has already started', - done: 'Third-party sharing opt-out is now started' - }, - finish: { - inProgress: 'Third-party sharing opt-out has already finished', - done: 'Third-party sharing opt-out is now finished' - } -} - -/** - * Get the status of the third-party sharing - * - * @returns {string} - * @private - */ -function _status (): ThirdPartySharingStatusT { - const disabled = getThirdPartySharing() || {} - - if (disabled.reason) { - return disabled.pending ? 'pending' : 'off' - } - - return 'on' -} - -/** - * Request third-party sharing opt-out request - * - * @param {boolean} force - * @returns {boolean} - */ -function optOut (force?: boolean) { - let status = _status() - - if (!force && status !== 'on') { - Logger.log(_logMessages[status]) - return false - } - - if (!Config.isInitialised()) { - Logger.log(_logMessages.delayed) - return true - } - - push({ - url: '/disable_third_party_sharing', - method: 'POST' - }) - - return true -} - -/** - * Start or finish thrid-party sharing disable process - * - * @param {boolean} pending - * @param {string} expectedAction - * @returns {boolean} - * @private - */ -function _disable (pending: boolean, expectedAction: 'start' | 'finish'): boolean { - const disabled = getThirdPartySharing() || {} - const action = expectedAction === 'start' && pending ? 'start': 'finish' - const shouldNotStart = expectedAction === 'start' && disabled.reason - const shouldNotFinish = expectedAction === 'finish' && disabled.reason && !disabled.pending - - if (shouldNotStart || shouldNotFinish) { - Logger.log(_logMessages[action].inProgress) - return false - } - - Logger.log(_logMessages[action].done) - - setThirdPartySharing({ - reason: REASON_GENERAL, - pending - }) - - return true -} - -/** - * Start the third-party sharing disable process - * - * @returns {boolean} - */ -function disable (): boolean { - return _disable(true, 'start') -} - -/** - * Finalize the third-party sharing process - * - * @returns {boolean} - */ -function finish () { - return _disable(false, 'finish') -} - -/** - * Check if there s pending third-party sharing opt-out request - */ -function check (): void { - if (_status() === 'pending') { - Logger.log(_logMessages.running) - optOut(true) - } -} - -export { - optOut, - disable, - finish, - check -} diff --git a/src/sdk/track-third-party-sharing.ts b/src/sdk/track-third-party-sharing.ts new file mode 100644 index 00000000..91aacb86 --- /dev/null +++ b/src/sdk/track-third-party-sharing.ts @@ -0,0 +1,82 @@ +import Logger from './logger' +import { push } from './queue' + +export interface ThirdPartySharingOptions { + isEnabled: boolean; + granularOptions: Record>; + partnerSharingSettings: Record>; +} + +export class ThirdPartySharing implements ThirdPartySharingOptions { + private _isEnabled: boolean; + private _granularOptions: Record> = {}; + private _partnerSharingSettings: Record> = {}; + + constructor(isEnabled: boolean) { + if (typeof isEnabled !== 'boolean') { + Logger.warn(`isEnabled should be boolean, converting ${isEnabled} results ${!!isEnabled}`); + } + this._isEnabled = !!isEnabled + } + + get isEnabled(): boolean { + return this._isEnabled + } + + get granularOptions(): Record> { + return this._granularOptions + } + + get partnerSharingSettings(): Record> { + return this._partnerSharingSettings + } + + public addGranularOption(partnerName: string, key: string, value: string) { + if (!partnerName || !key || value === undefined) { + Logger.error('Cannot add granular option, partnerName, key and value are mandatory'); + return; + } + + const pair = { [key]: value }; + + if (this.granularOptions[partnerName]) { + this.granularOptions[partnerName] = { ...this.granularOptions[partnerName], ...pair }; + } else { + this.granularOptions[partnerName] = pair; + } + } + + public addPartnerSharingSetting(partnerName: string, key: string, value: boolean) { + if (!partnerName || !key || value === undefined) { + Logger.error('Cannot add partner sharing setting, partnerName, key and value are mandatory'); + return; + } + + const pair = { [key]: value }; + + if (this.partnerSharingSettings[partnerName]) { + this.partnerSharingSettings[partnerName] = { ...this.partnerSharingSettings[partnerName], ...pair }; + } else { + this.partnerSharingSettings[partnerName] = pair; + } + } +} + +export function trackThirdPartySharing(adjustThirdPartySharing: ThirdPartySharingOptions) { + if (!adjustThirdPartySharing || adjustThirdPartySharing.isEnabled === undefined) { + Logger.error('Can not track third-party sharing without parameters') + return + } + + const params = { + sharing: adjustThirdPartySharing.isEnabled ? 'enable' : 'disable', + granularThirdPartySharingOptions: adjustThirdPartySharing.granularOptions, + partnerSharingSettings: adjustThirdPartySharing.partnerSharingSettings + } + + push({ + url: '/third_party_sharing', + method: 'POST', + params + }) +} diff --git a/src/sdk/ts-types.ts b/src/sdk/ts-types.ts new file mode 100644 index 00000000..b8f53068 --- /dev/null +++ b/src/sdk/ts-types.ts @@ -0,0 +1,34 @@ +/** + * Type of attribution object received from the backend. + * + * @public + */ +export interface Attribution { + + /** Adjust device identifier */ + adid: string, + + /** Tracker token */ + tracker_token: string, + + /** Tracker name */ + tracker_name: string, + + /** Network grouping level */ + network?: string, + + /** Campaign grouping level */ + campaign?: string, + + /** Ad group grouping level */ + adgroup?: string, + + /** Creative grouping level */ + creative?: string, + + /** Click label */ + click_label?: string, + + /** Attribution state, for example 'installed' or 'reattributed' */ + state: string +} diff --git a/src/sdk/types.js b/src/sdk/types.js index 0ce6d6f5..0bbad7f4 100644 --- a/src/sdk/types.js +++ b/src/sdk/types.js @@ -186,12 +186,6 @@ export type InitOptionsT = $ReadOnly<$Shape<{| attributionCallback: (string, Object) => mixed |}>> -export type BaseParamsListT = $ReadOnlyArray<$Keys> - -export type BaseParamsMandatoryListT = $ReadOnlyArray<'appToken' | 'environment'> - -export type CustomConfigListT = $ReadOnlyArray<$Keys> - export type CustomErrorT = {| name: string, message: string, @@ -231,12 +225,6 @@ export type QueueSizeT = {| queueSize: number |} -export type SmartBannerOptionsT = {| - webToken: string, - logLevel: 'none' | 'error' | 'warning' | 'info' | 'verbose', - dataResidency: 'EU' | 'TR' | 'US', -|} - export type DefaultParamsT = {| ...CreatedAtT, ...SentAtT, diff --git a/src/sdk/url-strategy.ts b/src/sdk/url-strategy.ts index bc984b43..b5907b59 100644 --- a/src/sdk/url-strategy.ts +++ b/src/sdk/url-strategy.ts @@ -1,6 +1,30 @@ import Config from './config' import Logger from './logger' -import { ENDPOINTS } from './constants' +import { + ENDPOINTS, + BASE_URL_PREFIX, + GDPR_URL_PREFIX, + BASE_URL_NO_SUB_DOMAIN_PREFIX +} from './constants' + +export interface UrlStrategyConfig { + /** The country or countries of data residence, or the endpoints to which you want to send SDK traffic. */ + domains: Array; + + /** Whether the source should prefix a subdomain. */ + useSubdomains: boolean; + + /** Whether the domain should be used for data residency. */ + isDataResidency?: boolean; +} + +function getDefaultUrlStrategyConfig(endpoints: Record) { + return { + domains: [endpoints.default, endpoints.world], + useSubdomains: true, + isDataResidency: false + } +} enum UrlStrategy { Default = 'default', @@ -14,8 +38,6 @@ enum DataResidency { US = 'US' } -type EndpointName = UrlStrategy | DataResidency - type BaseUrlsMap = { app: string; gdpr: string; @@ -26,17 +48,20 @@ function incorrectOptionIgnoredMessage(higherPriority: string, lowerPriority: st } /** - * Returns a map of base URLs or a list of endpoint names depending on SDK configuration + * In case if deprecated parameters or no urlStrategy provided returns the most appropriate UrlStrategyConfig, + * and `null` otherwise */ -function getEndpointPreference(): BaseUrlsMap | EndpointName[] { +function transfromDeprecatedParamsToUrlStrategyConfig(endpoints: Record): UrlStrategyConfig | null { const { customUrl, urlStrategy, dataResidency } = Config.getCustomConfig() if (customUrl) { // If custom URL is set then send all requests there + Logger.warn('customUrl is deprecated, use urlStrategy instead') + if (dataResidency || urlStrategy) { incorrectOptionIgnoredMessage('customUrl', dataResidency ? 'dataResidency' : 'urlStrategy') } - return { app: customUrl, gdpr: customUrl } + return { domains: [customUrl], useSubdomains: false, isDataResidency: false } } if (dataResidency && urlStrategy) { @@ -44,48 +69,94 @@ function getEndpointPreference(): BaseUrlsMap | EndpointName[] { } if (dataResidency) { - return [dataResidency] + Logger.warn('dataResidency is deprecated, use urlStrategy instead') + + return { domains: [endpoints[dataResidency]], useSubdomains: true, isDataResidency: true } } - if (urlStrategy === UrlStrategy.India) { - return [UrlStrategy.India, UrlStrategy.Default] + if (typeof urlStrategy === 'string') { + Logger.warn('urlStrategy string literals (\'china\' and \'india\') are deprected, use UrlStartegyConfig instead') + + if (urlStrategy === UrlStrategy.India) { + return { + domains: [endpoints.india, endpoints.default], + useSubdomains: true, + isDataResidency: false + } + } + + if (urlStrategy === UrlStrategy.China) { + return { + domains: [endpoints.china, endpoints.default], + useSubdomains: true, + isDataResidency: false + } + } } - if (urlStrategy === UrlStrategy.China) { - return [UrlStrategy.China, UrlStrategy.Default] + if (!urlStrategy) { + return getDefaultUrlStrategyConfig(endpoints) } - return [UrlStrategy.Default, UrlStrategy.India, UrlStrategy.China] + return null } -const endpointMap: Record = { - [UrlStrategy.Default]: ENDPOINTS.default, - [UrlStrategy.India]: ENDPOINTS.india, - [UrlStrategy.China]: ENDPOINTS.china, - [DataResidency.EU]: ENDPOINTS.EU, - [DataResidency.TR]: ENDPOINTS.TR, - [DataResidency.US]: ENDPOINTS.US +/** + * Checks if passed UrlStrategyConfig is valid and returns it, returns `DEFAULT_URL_STRATEGY_CONFIG` otherwise + */ +function validateUrlStrategyConfig(endpoints: Record): UrlStrategyConfig { + const { urlStrategy } = Config.getCustomConfig() + + if (urlStrategy && typeof urlStrategy === 'object') { + const config = urlStrategy as UrlStrategyConfig; + + if (!config.domains || !Array.isArray(config.domains) || config.domains.length < 1) { + Logger.warn('Invalid urlStartegy: `domains` should be a non-empty array') + + return getDefaultUrlStrategyConfig(endpoints) + } + + return { domains: config.domains, useSubdomains: !!config.useSubdomains, isDataResidency: !!config.isDataResidency } + } + + return getDefaultUrlStrategyConfig(endpoints) +} + +function getUrlStrategyConfig(endpoints: Record): UrlStrategyConfig { + return transfromDeprecatedParamsToUrlStrategyConfig(endpoints) || validateUrlStrategyConfig(endpoints) } interface BaseUrlsIterator extends Iterator { reset: () => void; } -function getPreferredUrls(endpoints: Partial>): BaseUrlsMap[] { - const preference = getEndpointPreference() +function getPreferredUrls(endpoints: Record): BaseUrlsMap[] { + const urlStrategyConfig: UrlStrategyConfig = getUrlStrategyConfig(endpoints) + + const urls = [] as BaseUrlsMap[] - if (!Array.isArray(preference)) { - return [preference] - } else { - const res = preference - .map(strategy => endpoints[strategy] || null) - .filter((i): i is BaseUrlsMap => !!i) + //if (urlStrategyConfig.isDataResidency) { } - return res + for (const domain of urlStrategyConfig.domains) { + const map = urlStrategyConfig.useSubdomains + ? { + app: `${BASE_URL_PREFIX}${domain}`, + gdpr: `${GDPR_URL_PREFIX}${domain}` + } + : { + app: `${BASE_URL_NO_SUB_DOMAIN_PREFIX}${domain}`, + gdpr: `${BASE_URL_NO_SUB_DOMAIN_PREFIX}${domain}` + } + + urls.push(map) } + + return urls; } -function getBaseUrlsIterator(endpoints: Partial> = endpointMap): BaseUrlsIterator { +type Endpoints = keyof typeof ENDPOINTS + +function getBaseUrlsIterator(endpoints: Record = ENDPOINTS): BaseUrlsIterator { const _urls = getPreferredUrls(endpoints) let _counter = 0 diff --git a/src/snippet.js b/src/snippet.js index 5de70770..08402e05 100644 --- a/src/snippet.js +++ b/src/snippet.js @@ -1,15 +1,44 @@ /* eslint-disable */ -(function (window, document, tag, url, corsMode, integrity, sdkName, methods, placeholder, script, first) { +(function (window, document, tag, url, corsMode, integrity, sdkName, methods, classes, instanceAlias, placeholder, script, first) { var queueName = sdkName + '_q'; + var callsQueueName = sdkName + '_c'; window[sdkName] = window[sdkName] || {}; window[queueName] = window[queueName] || []; + window[callsQueueName] = window[callsQueueName] || []; - for (var i = 0; i < methods.length; i++) { + // creating wrappers for SDK functions + for (let i = 0; i < methods.length; i++) { placeholder(window[sdkName], window[queueName], methods[i]); } + // creating wrappers for SDK classes and their methods + for (let i = 0; i < classes.length; i++) { + var ctor = classes[i][0] + var classMethods = classes[i][1] + + var pretender + window[sdkName][ctor] = function (...args) { + pretender = this + window[callsQueueName].push(function () { + // calling real constructor + pretender[instanceAlias] = new window[sdkName][ctor](...args) + }) + return pretender + } + + for (let j = 0; j < classMethods.length; j++) { + const methodName = classMethods[j] + window[sdkName][ctor].prototype[methodName] = function (...args) { + window[callsQueueName].push(function () { + // calling real method + pretender[instanceAlias][methodName](...args) + }) + } + } + } + script = document.createElement(tag); first = document.getElementsByTagName(tag)[0]; script.async = true; @@ -21,8 +50,21 @@ } script.onload = function () { + // create all real objects and call their methods + for (var i = 0; i < window[callsQueueName].length; i++) { + window[callsQueueName][i]() + } + window[callsQueueName] = []; + + // create all real SDK functions for (var i = 0; i < window[queueName].length; i++) { - window[sdkName][window[queueName][i][0]].apply(window[sdkName], window[queueName][i][1]); + if (window[queueName][i][1][0][instanceAlias]) { + // if argument was an instance of some class, call function with real instance + // TODO: this doesn't support SDK functions with multiple parameters when some of them is a class instance + window[sdkName][window[queueName][i][0]](window[queueName][i][1][0][instanceAlias]) + } else { + window[sdkName][window[queueName][i][0]].apply(window[sdkName], window[queueName][i][1]); + } } window[queueName] = []; } @@ -39,6 +81,8 @@ 'initSdk', 'getAttribution', 'getWebUUID', + 'waitForAttribution', + 'waitForWebUUID', 'setReferrer', 'trackEvent', 'addGlobalCallbackParameters', @@ -53,10 +97,13 @@ 'restart', 'gdprForgetMe', 'disableThirdPartySharing', + 'trackThirdPartySharing', 'initSmartBanner', 'showSmartBanner', 'hideSmartBanner', ], + [['ThirdPartySharing', ['addGranularOption', 'addPartnerSharingSetting']]], + '__realObj', function (context, queue, methodName) { context[methodName] = function () { queue.push([methodName, arguments]); diff --git a/webpack.demo.config.js b/webpack.demo.config.js index 6be284c0..c7914d26 100644 --- a/webpack.demo.config.js +++ b/webpack.demo.config.js @@ -57,20 +57,6 @@ module.exports = () => ({ { loader: 'css-loader', }, { loader: 'sass-loader' } ] - }, { - test: /\.module\.s?css$/, - use: [ - MiniCssExtractPlugin.loader, - { - loader: 'css-loader', - options: { - modules: { - localIdentName: 'adjust-smart-banner-[local]__[hash:base64:5]', - } - } - }, - { loader: 'sass-loader' } - ] }, { test: /\.html$/, use: [ diff --git a/webpack.sdk.config.js b/webpack.sdk.config.js index a009fc20..d811c4ac 100644 --- a/webpack.sdk.config.js +++ b/webpack.sdk.config.js @@ -44,20 +44,6 @@ module.exports = () => ({ use: 'babel-loader', test: /\.(js|ts)$/, exclude: /node_modules/ - }, { - test: /\.module\.s?css$/, - use: [ - { loader: 'style-loader' }, - { - loader: 'css-loader', - options: { - modules: { - localIdentName: 'adjust-smart-banner__[hash:base64]', - } - }, - }, - { loader: 'sass-loader' } - ] }] } })