UNPKG

@snowplow/browser-tracker

Version:
1,209 lines (1,185 loc) 222 kB
/*! * Browser tracker for Snowplow v4.6.8 (http://bit.ly/sp-js) * Copyright 2022 Snowplow Analytics Ltd, 2010 Anthon Pang * Licensed under BSD-3-Clause */ (function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) : typeof define === 'function' && define.amd ? define(['exports'], factory) : (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.snowplowBrowserTracking = {})); })(this, (function (exports) { 'use strict'; /****************************************************************************** Copyright (c) Microsoft Corporation. Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ***************************************************************************** */ var __assign = function() { __assign = Object.assign || function __assign(t) { for (var s, i = 1, n = arguments.length; i < n; i++) { s = arguments[i]; for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p]; } return t; }; return __assign.apply(this, arguments); }; function __awaiter(thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); } function __generator(thisArg, body) { var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype); return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; function verb(n) { return function (v) { return step([n, v]); }; } function step(op) { if (f) throw new TypeError("Generator is already executing."); while (g && (g = 0, op[0] && (_ = 0)), _) try { if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; if (y = 0, t) op = [op[0] & 2, t.value]; switch (op[0]) { case 0: case 1: t = op; break; case 4: _.label++; return { value: op[1], done: false }; case 5: _.label++; y = op[1]; op = [0]; continue; case 7: op = _.ops.pop(); _.trys.pop(); continue; default: if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } if (t[2]) _.ops.pop(); _.trys.pop(); continue; } op = body.call(thisArg, _); } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; } } function __spreadArray(to, from, pack) { if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) { if (ar || !(i in from)) { if (!ar) ar = Array.prototype.slice.call(from, 0, i); ar[i] = from[i]; } } return to.concat(ar || Array.prototype.slice.call(from)); } typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) { var e = new Error(message); return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e; }; /** * Convert array of 16 byte values to UUID string format of the form: * XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX */ var byteToHex = []; for (var i = 0; i < 256; ++i) { byteToHex.push((i + 0x100).toString(16).slice(1)); } function unsafeStringify(arr, offset = 0) { // Note: Be careful editing this code! It's been tuned for performance // and works in ways you may not expect. See https://github.com/uuidjs/uuid/pull/434 // // Note to future-self: No, you can't remove the `toLowerCase()` call. // REF: https://github.com/uuidjs/uuid/pull/677#issuecomment-1757351351 return (byteToHex[arr[offset + 0]] + byteToHex[arr[offset + 1]] + byteToHex[arr[offset + 2]] + byteToHex[arr[offset + 3]] + '-' + byteToHex[arr[offset + 4]] + byteToHex[arr[offset + 5]] + '-' + byteToHex[arr[offset + 6]] + byteToHex[arr[offset + 7]] + '-' + byteToHex[arr[offset + 8]] + byteToHex[arr[offset + 9]] + '-' + byteToHex[arr[offset + 10]] + byteToHex[arr[offset + 11]] + byteToHex[arr[offset + 12]] + byteToHex[arr[offset + 13]] + byteToHex[arr[offset + 14]] + byteToHex[arr[offset + 15]]).toLowerCase(); } // Unique ID creation requires a high quality random # generator. In the browser we therefore // require the crypto API and do not support built-in fallback to lower quality random number // generators (like Math.random()). var getRandomValues; var rnds8 = new Uint8Array(16); function rng() { // lazy load so that environments that need to polyfill have a chance to do so if (!getRandomValues) { // getRandomValues needs to be invoked in a context where "this" is a Crypto implementation. getRandomValues = typeof crypto !== 'undefined' && crypto.getRandomValues && crypto.getRandomValues.bind(crypto); if (!getRandomValues) { throw new Error('crypto.getRandomValues() not supported. See https://github.com/uuidjs/uuid#getrandomvalues-not-supported'); } } return getRandomValues(rnds8); } var randomUUID = typeof crypto !== 'undefined' && crypto.randomUUID && crypto.randomUUID.bind(crypto); var native = { randomUUID }; function v4(options, buf, offset) { if (native.randomUUID && !buf && !options) { return native.randomUUID(); } options = options || {}; var rnds = options.random || (options.rng || rng)(); // Per 4.4, set bits for version and `clock_seq_hi_and_reserved` rnds[6] = rnds[6] & 0x0f | 0x40; rnds[8] = rnds[8] & 0x3f | 0x80; // Copy bytes to buffer, if provided if (buf) { offset = offset || 0; for (var i = 0; i < 16; ++i) { buf[offset + i] = rnds[i]; } return buf; } return unsafeStringify(rnds); } /*! * Core functionality for Snowplow JavaScript trackers v4.6.8 (http://bit.ly/sp-js) * Copyright 2022 Snowplow Analytics Ltd, 2010 Anthon Pang * Licensed under BSD-3-Clause */ var version$1 = "4.6.8"; /* * Copyright (c) 2013 Kevin van Zonneveld (http://kvz.io) * and Contributors (http://phpjs.org/authors) * * Permission is hereby granted, free of charge, to any person obtaining a copy of * this software and associated documentation files (the "Software"), to deal in * the Software without restriction, including without limitation the rights to * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is furnished to do * so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ /** * Decodes a url safe Base 64 encoded string * @remarks See: {@link http://tools.ietf.org/html/rfc4648#page-7} * @param data - String to decode * @returns The decoded string */ function base64urldecode(data) { if (!data) { return data; } var padding = 4 - (data.length % 4); switch (padding) { case 2: data += '=='; break; case 3: data += '='; break; } var b64Data = data.replace(/-/g, '+').replace(/_/g, '/'); return base64decode(b64Data); } /** * Encodes a string into a url safe Base 64 encoded string * @remarks See: {@link http://tools.ietf.org/html/rfc4648#page-7} * @param data - String to encode * @returns The url safe Base 64 string */ function base64urlencode(data) { if (!data) { return data; } var enc = base64encode(data); return enc.replace(/=/g, '').replace(/\+/g, '-').replace(/\//g, '_'); } var b64 = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/='; /** * Encode string as base64. * Any type can be passed, but will be stringified * * @param data - string to encode * @returns base64-encoded string */ function base64encode(data) { // discuss at: http://phpjs.org/functions/base64_encode/ // original by: Tyler Akins (http://rumkin.com) // improved by: Bayron Guevara // improved by: Thunder.m // improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net) // improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net) // improved by: Rafał Kukawski (http://kukawski.pl) // bugfixed by: Pellentesque Malesuada // example 1: base64_encode('Kevin van Zonneveld'); // returns 1: 'S2V2aW4gdmFuIFpvbm5ldmVsZA==' // example 2: base64_encode('a'); // returns 2: 'YQ==' // example 3: base64_encode('✓ à la mode'); // returns 3: '4pyTIMOgIGxhIG1vZGU=' var o1, o2, o3, h1, h2, h3, h4, bits, i = 0, ac = 0; var tmp_arr = []; if (!data) { return data; } data = unescape(encodeURIComponent(data)); do { // pack three octets into four hexets o1 = data.charCodeAt(i++); o2 = data.charCodeAt(i++); o3 = data.charCodeAt(i++); bits = (o1 << 16) | (o2 << 8) | o3; h1 = (bits >> 18) & 0x3f; h2 = (bits >> 12) & 0x3f; h3 = (bits >> 6) & 0x3f; h4 = bits & 0x3f; // use hexets to index into b64, and append result to encoded string tmp_arr[ac++] = b64.charAt(h1) + b64.charAt(h2) + b64.charAt(h3) + b64.charAt(h4); } while (i < data.length); var enc = tmp_arr.join(''); var r = data.length % 3; return (r ? enc.slice(0, r - 3) : enc) + '==='.slice(r || 3); } /** * Decode base64 to string * * @param data - base64 to string * @returns decoded string */ function base64decode(encodedData) { // discuss at: http://locutus.io/php/base64_decode/ // original by: Tyler Akins (http://rumkin.com) // improved by: Thunder.m // improved by: Kevin van Zonneveld (http://kvz.io) // improved by: Kevin van Zonneveld (http://kvz.io) // input by: Aman Gupta // input by: Brett Zamir (http://brett-zamir.me) // bugfixed by: Onno Marsman (https://twitter.com/onnomarsman) // bugfixed by: Pellentesque Malesuada // bugfixed by: Kevin van Zonneveld (http://kvz.io) // improved by: Indigo744 // example 1: base64_decode('S2V2aW4gdmFuIFpvbm5ldmVsZA==') // returns 1: 'Kevin van Zonneveld' // example 2: base64_decode('YQ==') // returns 2: 'a' // example 3: base64_decode('4pyTIMOgIGxhIG1vZGU=') // returns 3: '✓ à la mode' // decodeUTF8string() // Internal function to decode properly UTF8 string // Adapted from Solution #1 at https://developer.mozilla.org/en-US/docs/Web/API/WindowBase64/Base64_encoding_and_decoding var decodeUTF8string = function (str) { // Going backwards: from bytestream, to percent-encoding, to original string. return decodeURIComponent(str .split('') .map(function (c) { return '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2); }) .join('')); }; var o1, o2, o3, h1, h2, h3, h4, bits, i = 0, ac = 0, dec = ''; var tmpArr = []; if (!encodedData) { return encodedData; } encodedData += ''; do { // unpack four hexets into three octets using index points in b64 h1 = b64.indexOf(encodedData.charAt(i++)); h2 = b64.indexOf(encodedData.charAt(i++)); h3 = b64.indexOf(encodedData.charAt(i++)); h4 = b64.indexOf(encodedData.charAt(i++)); bits = (h1 << 18) | (h2 << 12) | (h3 << 6) | h4; o1 = (bits >> 16) & 0xff; o2 = (bits >> 8) & 0xff; o3 = bits & 0xff; if (h3 === 64) { tmpArr[ac++] = String.fromCharCode(o1); } else if (h4 === 64) { tmpArr[ac++] = String.fromCharCode(o1, o2); } else { tmpArr[ac++] = String.fromCharCode(o1, o2, o3); } } while (i < encodedData.length); dec = tmpArr.join(''); return decodeUTF8string(dec.replace(/\0+$/, '')); } /* * Copyright (c) 2022 Snowplow Analytics Ltd, 2010 Anthon Pang * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, this * list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ function payloadBuilder() { var dict = {}, allJson = [], jsonForProcessing = [], contextEntitiesForProcessing = []; var processor; var add = function (key, value) { if (value != null && value !== '') { // null also checks undefined dict[key] = value; } }; var addDict = function (dict) { for (var key in dict) { if (Object.prototype.hasOwnProperty.call(dict, key)) { add(key, dict[key]); } } }; var addJson = function (keyIfEncoded, keyIfNotEncoded, json) { if (json && isNonEmptyJson(json)) { var jsonWithKeys = { keyIfEncoded: keyIfEncoded, keyIfNotEncoded: keyIfNotEncoded, json: json }; jsonForProcessing.push(jsonWithKeys); allJson.push(jsonWithKeys); } }; var addContextEntity = function (entity) { contextEntitiesForProcessing.push(entity); }; return { add: add, addDict: addDict, addJson: addJson, addContextEntity: addContextEntity, getPayload: function () { return dict; }, getJson: function () { return allJson; }, withJsonProcessor: function (jsonProcessor) { processor = jsonProcessor; }, build: function () { processor === null || processor === void 0 ? void 0 : processor(this, jsonForProcessing, contextEntitiesForProcessing); return dict; }, }; } /** * A helper to build a Snowplow request from a set of name-value pairs, provided using the add methods. * Will base64 encode JSON, if desired, on build * * @returns The request builder, with add and build methods */ function payloadJsonProcessor(encodeBase64) { return function (payloadBuilder, jsonForProcessing, contextEntitiesForProcessing) { var add = function (json, keyIfEncoded, keyIfNotEncoded) { var str = JSON.stringify(json); if (encodeBase64) { payloadBuilder.add(keyIfEncoded, base64urlencode(str)); } else { payloadBuilder.add(keyIfNotEncoded, str); } }; var getContextFromPayload = function () { var payload = payloadBuilder.getPayload(); if (encodeBase64 ? payload.cx : payload.co) { return JSON.parse(encodeBase64 ? base64urldecode(payload.cx) : payload.co); } return undefined; }; var combineContexts = function (originalContext, newContext) { var context = originalContext || getContextFromPayload(); if (context) { context.data = context.data.concat(newContext.data); } else { context = newContext; } return context; }; var context = undefined; for (var _i = 0, jsonForProcessing_1 = jsonForProcessing; _i < jsonForProcessing_1.length; _i++) { var json = jsonForProcessing_1[_i]; if (json.keyIfEncoded === 'cx') { context = combineContexts(context, json.json); } else { add(json.json, json.keyIfEncoded, json.keyIfNotEncoded); } } jsonForProcessing.length = 0; if (contextEntitiesForProcessing.length) { var newContext = { schema: 'iglu:com.snowplowanalytics.snowplow/contexts/jsonschema/1-0-0', data: __spreadArray([], contextEntitiesForProcessing, true), }; context = combineContexts(context, newContext); contextEntitiesForProcessing.length = 0; } if (context) { add(context, 'cx', 'co'); } }; } /** * Is property a non-empty JSON? * @param property - Checks if object is non-empty json */ function isNonEmptyJson(property) { if (!isJson(property)) { return false; } for (var key in property) { if (Object.prototype.hasOwnProperty.call(property, key)) { return true; } } return false; } /** * Is property a JSON? * @param property - Checks if object is json */ function isJson(property) { return (typeof property !== 'undefined' && property !== null && (property.constructor === {}.constructor || property.constructor === [].constructor)); } /* * Copyright (c) 2022 Snowplow Analytics Ltd, 2010 Anthon Pang * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, this * list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ var label = 'Snowplow: '; var LOG_LEVEL; (function (LOG_LEVEL) { LOG_LEVEL[LOG_LEVEL["none"] = 0] = "none"; LOG_LEVEL[LOG_LEVEL["error"] = 1] = "error"; LOG_LEVEL[LOG_LEVEL["warn"] = 2] = "warn"; LOG_LEVEL[LOG_LEVEL["debug"] = 3] = "debug"; LOG_LEVEL[LOG_LEVEL["info"] = 4] = "info"; })(LOG_LEVEL || (LOG_LEVEL = {})); var LOG = logger(); function logger(logLevel) { if (logLevel === void 0) { logLevel = LOG_LEVEL.warn; } function setLogLevel(level) { if (LOG_LEVEL[level]) { logLevel = level; } else { logLevel = LOG_LEVEL.warn; } } /** * Log errors, with or without error object */ function error(message, error) { var extraParams = []; for (var _i = 2; _i < arguments.length; _i++) { extraParams[_i - 2] = arguments[_i]; } if (logLevel >= LOG_LEVEL.error && typeof console !== 'undefined') { var logMsg = label + message + '\n'; if (error) { console.error.apply(console, __spreadArray([logMsg + '\n', error], extraParams, false)); } else { console.error.apply(console, __spreadArray([logMsg], extraParams, false)); } } } /** * Log warnings, with or without error object */ function warn(message, error) { var extraParams = []; for (var _i = 2; _i < arguments.length; _i++) { extraParams[_i - 2] = arguments[_i]; } if (logLevel >= LOG_LEVEL.warn && typeof console !== 'undefined') { var logMsg = label + message; if (error) { console.warn.apply(console, __spreadArray([logMsg + '\n', error], extraParams, false)); } else { console.warn.apply(console, __spreadArray([logMsg], extraParams, false)); } } } /** * Log debug messages */ function debug(message) { var extraParams = []; for (var _i = 1; _i < arguments.length; _i++) { extraParams[_i - 1] = arguments[_i]; } if (logLevel >= LOG_LEVEL.debug && typeof console !== 'undefined') { console.debug.apply(console, __spreadArray([label + message], extraParams, false)); } } /** * Log info messages */ function info(message) { var extraParams = []; for (var _i = 1; _i < arguments.length; _i++) { extraParams[_i - 1] = arguments[_i]; } if (logLevel >= LOG_LEVEL.info && typeof console !== 'undefined') { console.info.apply(console, __spreadArray([label + message], extraParams, false)); } } return { setLogLevel: setLogLevel, warn: warn, error: error, debug: debug, info: info }; } /* * Copyright (c) 2022 Snowplow Analytics Ltd, 2010 Anthon Pang * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, this * list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /** * Contains helper functions to aid in the addition and removal of Global Contexts */ function globalContexts() { var globalPrimitives = []; var conditionalProviders = []; var namedPrimitives = {}; var namedConditionalProviders = {}; /** * Returns all applicable global contexts for a specified event * @param event - The event to check for applicable global contexts for * @returns An array of contexts */ var assembleAllContexts = function (event) { var eventSchema = getUsefulSchema(event); var eventType = getEventType(event); var contexts = []; var generatedPrimitives = generatePrimitives(globalPrimitives.concat(Object.values(namedPrimitives)), event, eventType, eventSchema); contexts.push.apply(contexts, generatedPrimitives); var generatedConditionals = generateConditionals(conditionalProviders.concat(Object.values(namedConditionalProviders)), event, eventType, eventSchema); contexts.push.apply(contexts, generatedConditionals); return contexts; }; return { getGlobalPrimitives: function () { return globalPrimitives.concat(Object.values(namedPrimitives)); }, getConditionalProviders: function () { return conditionalProviders.concat(Object.values(namedConditionalProviders)); }, addGlobalContexts: function (contexts) { if (Array.isArray(contexts)) { var acceptedConditionalContexts = []; var acceptedContextPrimitives = []; for (var _i = 0, contexts_1 = contexts; _i < contexts_1.length; _i++) { var context = contexts_1[_i]; if (isConditionalContextProvider(context)) { acceptedConditionalContexts.push(context); } else if (isContextPrimitive(context)) { acceptedContextPrimitives.push(context); } } globalPrimitives = globalPrimitives.concat(acceptedContextPrimitives); conditionalProviders = conditionalProviders.concat(acceptedConditionalContexts); } else { for (var _a = 0, _b = Object.entries(contexts); _a < _b.length; _a++) { var _c = _b[_a], name_1 = _c[0], context = _c[1]; if (isConditionalContextProvider(context)) { namedConditionalProviders[name_1] = context; } else if (isContextPrimitive(context)) { namedPrimitives[name_1] = context; } } } }, clearGlobalContexts: function () { conditionalProviders = []; globalPrimitives = []; namedConditionalProviders = {}; namedPrimitives = {}; }, removeGlobalContexts: function (contexts) { var _loop_1 = function (context) { if (typeof context === 'string') { delete namedConditionalProviders[context]; delete namedPrimitives[context]; } else if (isConditionalContextProvider(context)) { conditionalProviders = conditionalProviders.filter(function (item) { return !compareProvider(context, item); }); } else if (isContextPrimitive(context)) { globalPrimitives = globalPrimitives.filter(function (item) { return !compareProvider(context, item); }); } }; for (var _i = 0, contexts_2 = contexts; _i < contexts_2.length; _i++) { var context = contexts_2[_i]; _loop_1(context); } }, getApplicableContexts: function (event) { return assembleAllContexts(event); }, }; } function pluginContexts(plugins) { /** * Add common contexts to every event * * @param array - additionalContexts List of user-defined contexts * @returns userContexts combined with commonContexts */ return { addPluginContexts: function (additionalContexts) { var combinedContexts = additionalContexts ? __spreadArray([], additionalContexts, true) : []; plugins.forEach(function (plugin) { try { if (plugin.contexts) { combinedContexts.push.apply(combinedContexts, plugin.contexts()); } } catch (ex) { LOG.error('Error adding plugin contexts', ex); } }); return combinedContexts; }, }; } /** * Slices a schema into its composite parts. Useful for ruleset filtering. * @param input - A schema string * @returns The vendor, schema name, major, minor and patch information of a schema string */ function getSchemaParts(input) { var re = new RegExp('^iglu:([a-zA-Z0-9-_.]+)/([a-zA-Z0-9-_]+)/jsonschema/([1-9][0-9]*)-(0|[1-9][0-9]*)-(0|[1-9][0-9]*)$'); var matches = re.exec(input); if (matches !== null) return matches.slice(1, 6); return undefined; } /** * Validates the vendor section of a schema string contains allowed wildcard values * @param parts - Array of parts from a schema string * @returns Whether the vendor validation parts are a valid combination */ function validateVendorParts(parts) { if (parts[0] === '*' || parts[1] === '*') { return false; // no wildcard in first or second part } if (parts.slice(2).length > 0) { var asterisk = false; for (var _i = 0, _a = parts.slice(2); _i < _a.length; _i++) { var part = _a[_i]; if (part === '*') // mark when we've found a wildcard asterisk = true; else if (asterisk) // invalid if alpha parts come after wildcard return false; } return true; } else if (parts.length == 2) return true; return false; } /** * Validates the vendor part of a schema string is valid for a rule set * @param input - Vendor part of a schema string * @returns Whether the vendor validation string is valid */ function validateVendor(input) { var parts = input.split('.'); if (parts && parts.length > 1) return validateVendorParts(parts); return false; } /** * Checks for validity of input and returns all the sections of a schema string that are used to match rules in a ruleset * @param input - A Schema string * @returns The sections of a schema string that are used to match rules in a ruleset */ function getRuleParts(input) { var re = new RegExp('^iglu:((?:(?:[a-zA-Z0-9-_]+|\\*).)+(?:[a-zA-Z0-9-_]+|\\*))/([a-zA-Z0-9-_.]+|\\*)/jsonschema/([1-9][0-9]*|\\*)-(0|[1-9][0-9]*|\\*)-(0|[1-9][0-9]*|\\*)$'); var matches = re.exec(input); if (matches !== null && validateVendor(matches[1])) return matches.slice(1, 6); return undefined; } /** * Ensures the rules specified in a schema string of a ruleset are valid * @param input - A Schema string * @returns if there rule is valid */ function isValidRule(input) { var ruleParts = getRuleParts(input); if (ruleParts) { var vendor = ruleParts[0]; return ruleParts.length === 5 && validateVendor(vendor); } return false; } /** * Check if a variable is an Array containing only strings * @param input - The variable to validate * @returns True if the input is an array containing only strings */ function isStringArray(input) { return (Array.isArray(input) && input.every(function (x) { return typeof x === 'string'; })); } /** * Validates whether a rule set is an array of valid ruleset strings * @param input - The Array of rule set arguments * @returns True is the input is an array of valid rules */ function isValidRuleSetArg(input) { if (isStringArray(input)) return input.every(function (x) { return isValidRule(x); }); else if (typeof input === 'string') return isValidRule(input); return false; } /** * Check if a variable is a valid, non-empty Self Describing JSON * @param input - The variable to validate * @returns True if a valid Self Describing JSON */ function isSelfDescribingJson(input) { var sdj = input; if (isNonEmptyJson(sdj)) if ('schema' in sdj && 'data' in sdj) return typeof sdj.schema === 'string' && typeof sdj.data === 'object'; return false; } /** * Validates if the input object contains the expected properties of a ruleset * @param input - The object containing a rule set * @returns True if a valid rule set */ function isRuleSet(input) { var ruleSet = input; var ruleCount = 0; if (input != null && typeof input === 'object' && !Array.isArray(input)) { if (Object.prototype.hasOwnProperty.call(ruleSet, 'accept')) { if (isValidRuleSetArg(ruleSet['accept'])) { ruleCount += 1; } else { return false; } } if (Object.prototype.hasOwnProperty.call(ruleSet, 'reject')) { if (isValidRuleSetArg(ruleSet['reject'])) { ruleCount += 1; } else { return false; } } // if either 'reject' or 'accept' or both exists, // we have a valid ruleset return ruleCount > 0 && ruleCount <= 2; } return false; } /** * Validates if the function can be a valid context generator function * @param input - The function to be validated */ function isContextCallbackFunction(input) { return typeof input === 'function' && input.length <= 1; } /** * Validates if the function can be a valid context primitive function or self describing json * @param input - The function or orbject to be validated * @returns True if either a Context Generator or Self Describing JSON */ function isContextPrimitive(input) { return isContextCallbackFunction(input) || isSelfDescribingJson(input); } /** * Validates if an array is a valid shape to be a Filter Provider * @param input - The Array of Context filter callbacks */ function isFilterProvider(input) { if (Array.isArray(input)) { if (input.length === 2) { if (Array.isArray(input[1])) { return isContextCallbackFunction(input[0]) && input[1].every(isContextPrimitive); } return isContextCallbackFunction(input[0]) && isContextPrimitive(input[1]); } } return false; } /** * Validates if an array is a valid shape to be an array of rule sets * @param input - The Array of Rule Sets */ function isRuleSetProvider(input) { if (Array.isArray(input) && input.length === 2) { if (!isRuleSet(input[0])) return false; if (Array.isArray(input[1])) return input[1].every(isContextPrimitive); return isContextPrimitive(input[1]); } return false; } /** * Checks if an input array is either a filter provider or a rule set provider * @param input - An array of filter providers or rule set providers * @returns Whether the array is a valid {@link ConditionalContextProvider} */ function isConditionalContextProvider(input) { return isFilterProvider(input) || isRuleSetProvider(input); } /** * Checks if a given schema matches any rules within the provided rule set * @param ruleSet - The rule set containing rules to match schema against * @param schema - The schema to be matched against the rule set */ function matchSchemaAgainstRuleSet(ruleSet, schema) { var rejectCount = 0; var acceptCount = 0; var acceptRules = ruleSet['accept']; if (Array.isArray(acceptRules)) { if (ruleSet.accept.some(function (rule) { return matchSchemaAgainstRule(rule, schema); })) { acceptCount++; } } else if (typeof acceptRules === 'string') { if (matchSchemaAgainstRule(acceptRules, schema)) { acceptCount++; } } var rejectRules = ruleSet['reject']; if (Array.isArray(rejectRules)) { if (ruleSet.reject.some(function (rule) { return matchSchemaAgainstRule(rule, schema); })) { rejectCount++; } } else if (typeof rejectRules === 'string') { if (matchSchemaAgainstRule(rejectRules, schema)) { rejectCount++; } } if (acceptCount > 0 && rejectCount === 0) { return true; } else if (acceptCount === 0 && rejectCount > 0) { return false; } return false; } /** * Checks if a given schema matches a specific rule from a rule set * @param rule - The rule to match schema against * @param schema - The schema to be matched against the rule */ function matchSchemaAgainstRule(rule, schema) { if (!isValidRule(rule)) return false; var ruleParts = getRuleParts(rule); var schemaParts = getSchemaParts(schema); if (ruleParts && schemaParts) { if (!matchVendor(ruleParts[0], schemaParts[0])) return false; for (var i = 1; i < 5; i++) { if (!matchPart(ruleParts[i], schemaParts[i])) return false; } return true; // if it hasn't failed, it passes } return false; } function matchVendor(rule, vendor) { // rule and vendor must have same number of elements var vendorParts = vendor.split('.'); var ruleParts = rule.split('.'); if (vendorParts && ruleParts) { if (vendorParts.length !== ruleParts.length) return false; for (var i = 0; i < ruleParts.length; i++) { if (!matchPart(vendorParts[i], ruleParts[i])) return false; } return true; } return false; } function matchPart(rule, schema) { // parts should be the string nested between slashes in the URI: /example/ return (rule && schema && rule === '*') || rule === schema; } // Returns the "useful" schema, i.e. what would someone want to use to identify events. // For some events this is the 'e' property but for unstructured events, this is the // 'schema' from the 'ue_px' field. function getUsefulSchema(sb) { var eventJson = sb.getJson(); for (var _i = 0, eventJson_1 = eventJson; _i < eventJson_1.length; _i++) { var json = eventJson_1[_i]; if (json.keyIfEncoded === 'ue_px' && typeof json.json['data'] === 'object') { var schema = json.json['data']['schema']; if (typeof schema == 'string') { return schema; } } } return ''; } function getEventType(payloadBuilder) { var eventType = payloadBuilder.getPayload()['e']; return typeof eventType === 'string' ? eventType : ''; } function buildGenerator(generator, event, eventType, eventSchema) { var contextGeneratorResult = undefined; try { // try to evaluate context generator var args = { event: event.getPayload(), eventType: eventType, eventSchema: eventSchema, }; contextGeneratorResult = generator(args); // determine if the produced result is a valid SDJ if (Array.isArray(contextGeneratorResult) && contextGeneratorResult.every(isSelfDescribingJson)) { return contextGeneratorResult; } else if (isSelfDescribingJson(contextGeneratorResult)) { return contextGeneratorResult; } else { return undefined; } } catch (error) { contextGeneratorResult = undefined; } return contextGeneratorResult; } function normalizeToArray(input) { if (Array.isArray(input)) { return input; } return Array.of(input); } function generatePrimitives(contextPrimitives, event, eventType, eventSchema) { var _a; var normalizedInputs = normalizeToArray(contextPrimitives); var partialEvaluate = function (primitive) { var result = evaluatePrimitive(primitive, event, eventType, eventSchema); if (result && result.length !== 0) { return result; } return undefined; }; var generatedContexts = normalizedInputs.map(partialEvaluate); return (_a = []).concat.apply(_a, generatedContexts.filter(function (c) { return c != null && c.filter(Boolean); })); } function evaluatePrimitive(contextPrimitive, event, eventType, eventSchema) { if (isSelfDescribingJson(contextPrimitive)) { return [contextPrimitive]; } else if (isContextCallbackFunction(contextPrimitive)) { var generatorOutput = buildGenerator(contextPrimitive, event, eventType, eventSchema); if (isSelfDescribingJson(generatorOutput)) { return [generatorOutput]; } else if (Array.isArray(generatorOutput)) { return generatorOutput; } } return undefined; } function evaluateProvider(provider, event, eventType, eventSchema) { if (isFilterProvider(provider)) { var filter = provider[0]; var filterResult = false; try { var args = { event: event.getPayload(), eventType: eventType, eventSchema: eventSchema, }; filterResult = filter(args); } catch (error) { filterResult = false; } if (filterResult === true) { return generatePrimitives(provider[1], event, eventType, eventSchema); } } else if (isRuleSetProvider(provider)) { if (matchSchemaAgainstRuleSet(provider[0], eventSchema)) { return generatePrimitives(provider[1], event, eventType, eventSchema); } } return []; } function compareProviderPart(a, b) { if (typeof a === 'function') return a === b; return JSON.stringify(a) === JSON.stringify(b); } function compareProvider(a, b) { if (isConditionalContextProvider(a)) { if (!isConditionalContextProvider(b)) return false; var ruleA = a[0], primitivesA = a[1]; var ruleB = b[0], primitivesB_1 = b[1]; if (!compareProviderPart(ruleA, ruleB)) return false; if (Array.isArray(primitivesA)) { if (!Array.isArray(primitivesB_1)) return false; if (primitivesA.length !== primitivesB_1.length) return false; return primitivesA.reduce(function (matches, a, i) { return matches && compareProviderPart(a, primitivesB_1[i]); }, true); } else { if (Array.isArray(primitivesB_1)) return false; return compareProviderPart(primitivesA, primitivesB_1); } } else if (isContextPrimitive(a)) { if (!isContextPrimitive(b)) return false; return compareProviderPart(a, b); } return false; } function generateConditionals(providers, event, eventType, eventSchema) { var _a; var normalizedInput = normalizeToArray(providers); var partialEvaluate = function (provider) { var result = evaluateProvider(provider, event, eventType, eventSchema); if (result && result.length !== 0) { return result; } return undefined; }; var generatedContexts = normalizedInput.map(partialEvaluate); return (_a = []).concat.apply(_a, generatedContexts.filter(function (c) { return c != null && c.filter(Boolean); })); } /** * Create a new EventStorePayload */ function newEventStorePayload(_a) { var payload = _a.payload, _b = _a.svrAnon, svrAnon = _b === void 0 ? false : _b; return { payload: payload, svrAnon: svrAnon, }; } /* * Copyright (c) 2022 Snowplow Analytics Ltd, 2010 Anthon Pang * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, this * list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /** * Transform optional/old-behavior number timestamp into`Timestamp` ADT * * @param timestamp - optional number or timestamp object * @returns correct timestamp object */ function getTimestamp(