{
"version": 3,
"sources": ["../../../node_modules/@citizensadvice/design-system/lib/header/header.js", "../../../node_modules/@citizensadvice/design-system/lib/header/index.js", "../../../node_modules/@citizensadvice/design-system/lib/navigation/navigation.js", "../../../node_modules/@citizensadvice/design-system/lib/navigation/index.js", "../../../node_modules/@citizensadvice/design-system/lib/greedy-nav/GreedyNav.js", "../../../node_modules/@citizensadvice/design-system/lib/greedy-nav/index.js", "../../javascript/application.js", "../../javascript/modules/greedy-nav.js", "../../javascript/modules/supplier-table.js", "../../../node_modules/@datadog/browser-core/src/tools/display.ts", "../../../node_modules/@datadog/browser-core/src/tools/catchUserErrors.ts", "../../../node_modules/@datadog/browser-core/src/tools/utils/numberUtils.ts", "../../../node_modules/@datadog/browser-core/src/tools/utils/timeUtils.ts", "../../../node_modules/@datadog/browser-core/src/tools/utils/byteUtils.ts", "../../../node_modules/@datadog/browser-core/src/tools/utils/objectUtils.ts", "../../../node_modules/@datadog/browser-core/src/tools/getGlobalObject.ts", "../../../node_modules/@datadog/browser-core/src/tools/getZoneJsOriginalValue.ts", "../../../node_modules/@datadog/browser-core/src/tools/monitor.ts", "../../../node_modules/@datadog/browser-core/src/tools/timer.ts", "../../../node_modules/@datadog/browser-core/src/tools/observable.ts", "../../../node_modules/@datadog/browser-core/src/tools/utils/functionUtils.ts", "../../../node_modules/@datadog/browser-core/src/tools/utils/stringUtils.ts", "../../../node_modules/@datadog/browser-core/src/tools/utils/browserDetection.ts", "../../../node_modules/@datadog/browser-core/src/browser/cookie.ts", "../../../node_modules/@datadog/browser-core/src/domain/session/storeStrategies/sessionStoreStrategy.ts", "../../../node_modules/@datadog/browser-core/src/tools/utils/polyfills.ts", "../../../node_modules/@datadog/browser-core/src/domain/session/sessionConstants.ts", "../../../node_modules/@datadog/browser-core/src/domain/session/sessionStateValidation.ts", "../../../node_modules/@datadog/browser-core/src/domain/session/sessionState.ts", "../../../node_modules/@datadog/browser-core/src/domain/session/oldCookiesMigration.ts", "../../../node_modules/@datadog/browser-core/src/domain/session/storeStrategies/sessionInCookie.ts", "../../../node_modules/@datadog/browser-core/src/domain/session/storeStrategies/sessionInLocalStorage.ts", "../../../node_modules/@datadog/browser-core/src/domain/session/sessionStoreOperations.ts", "../../../node_modules/@datadog/browser-core/src/domain/session/sessionStore.ts", "../../../node_modules/@datadog/browser-core/src/domain/trackingConsent.ts", "../../../node_modules/@datadog/browser-core/src/tools/serialisation/jsonStringify.ts", "../../../node_modules/@datadog/browser-core/src/tools/utils/urlPolyfill.ts", "../../../node_modules/@datadog/browser-core/src/domain/configuration/intakeSites.ts", "../../../node_modules/@datadog/browser-core/src/domain/configuration/endpointBuilder.ts", "../../../node_modules/@datadog/browser-core/src/domain/configuration/tags.ts", "../../../node_modules/@datadog/browser-core/src/domain/configuration/transportConfiguration.ts", "../../../node_modules/@datadog/browser-core/src/domain/configuration/configuration.ts", "../../../node_modules/@datadog/browser-core/src/tools/experimentalFeatures.ts", "../../../node_modules/@datadog/browser-core/src/tools/stackTrace/computeStackTrace.ts", "../../../node_modules/@datadog/browser-core/src/tools/stackTrace/handlingStack.ts", "../../../node_modules/@datadog/browser-core/src/tools/instrumentMethod.ts", "../../../node_modules/@datadog/browser-core/src/tools/serialisation/sanitize.ts", "../../../node_modules/@datadog/browser-core/src/domain/error/error.ts", "../../../node_modules/@datadog/browser-core/src/domain/error/error.types.ts", "../../../node_modules/@datadog/browser-core/src/domain/error/trackRuntimeError.ts", "../../../node_modules/@datadog/browser-core/src/boot/init.ts", "../../../node_modules/@datadog/browser-core/src/boot/displayAlreadyInitializedError.ts", "../../../node_modules/@datadog/browser-core/src/browser/addEventListener.ts", "../../../node_modules/@datadog/browser-core/src/domain/report/reportObservable.ts", "../../../node_modules/@datadog/browser-core/src/tools/sendToExtension.ts", "../../../node_modules/@datadog/browser-core/src/tools/utils/typeUtils.ts", "../../../node_modules/@datadog/browser-core/src/tools/mergeInto.ts", "../../../node_modules/@datadog/browser-core/src/domain/connectivity/connectivity.ts", "../../../node_modules/@datadog/browser-core/src/tools/utils/arrayUtils.ts", "../../../node_modules/@datadog/browser-core/src/tools/boundedBuffer.ts", "../../../node_modules/@datadog/browser-core/src/domain/telemetry/rawTelemetryEvent.types.ts", "../../../node_modules/@datadog/browser-core/src/domain/telemetry/telemetry.ts", "../../../node_modules/@datadog/browser-core/src/tools/valueHistory.ts", "../../../node_modules/@datadog/browser-core/src/domain/session/sessionManager.ts", "../../../node_modules/@datadog/browser-core/src/tools/utils/responseUtils.ts", "../../../node_modules/@datadog/browser-core/src/transport/sendWithRetryStrategy.ts", "../../../node_modules/@datadog/browser-core/src/transport/httpRequest.ts", "../../../node_modules/@datadog/browser-core/src/transport/eventBridge.ts", "../../../node_modules/@datadog/browser-core/src/browser/pageExitObservable.ts", "../../../node_modules/@datadog/browser-core/src/transport/batch.ts", "../../../node_modules/@datadog/browser-core/src/transport/flushController.ts", "../../../node_modules/@datadog/browser-core/src/transport/startBatchWithReplica.ts", "../../../node_modules/@datadog/browser-core/src/tools/encoder.ts", "../../../node_modules/@datadog/browser-core/src/tools/abstractLifeCycle.ts", "../../../node_modules/@datadog/browser-core/src/domain/eventRateLimiter/createEventRateLimiter.ts", "../../../node_modules/@datadog/browser-core/src/browser/xhrObservable.ts", "../../../node_modules/@datadog/browser-core/src/browser/fetchObservable.ts", "../../../node_modules/@datadog/browser-core/src/domain/console/consoleObservable.ts", "../../../node_modules/@datadog/browser-core/src/domain/context/contextManager.ts", "../../../node_modules/@datadog/browser-core/src/domain/context/storeContextManager.ts", "../../../node_modules/@datadog/browser-core/src/domain/context/customerDataTracker.ts", "../../../node_modules/@datadog/browser-core/src/tools/readBytesFromStream.ts", "../../../node_modules/@datadog/browser-core/src/domain/synthetics/syntheticsWorkerValues.ts", "../../../node_modules/@datadog/browser-core/src/domain/user/user.ts", "../../../node_modules/@datadog/browser-logs/src/domain/logger/isAuthorized.ts", "../../../node_modules/@datadog/browser-logs/src/domain/createErrorFieldFromRawError.ts", "../../../node_modules/@datadog/browser-logs/src/domain/logger.ts", "../../../node_modules/@datadog/browser-logs/src/domain/contexts/commonContext.ts", "../../../node_modules/@datadog/browser-logs/src/domain/configuration.ts", "../../../node_modules/@datadog/browser-logs/src/boot/preStartLogs.ts", "../../../node_modules/@datadog/browser-logs/src/boot/logsPublicApi.ts", "../../../node_modules/@datadog/browser-logs/src/domain/logsSessionManager.ts", "../../../node_modules/@datadog/browser-logs/src/domain/contexts/rumInternalContext.ts", "../../../node_modules/@datadog/browser-logs/src/domain/assembly.ts", "../../../node_modules/@datadog/browser-logs/src/domain/console/consoleCollection.ts", "../../../node_modules/@datadog/browser-logs/src/domain/report/reportCollection.ts", "../../../node_modules/@datadog/browser-logs/src/domain/networkError/networkErrorCollection.ts", "../../../node_modules/@datadog/browser-logs/src/domain/runtimeError/runtimeErrorCollection.ts", "../../../node_modules/@datadog/browser-logs/src/domain/lifeCycle.ts", "../../../node_modules/@datadog/browser-logs/src/domain/logger/loggerCollection.ts", "../../../node_modules/@datadog/browser-logs/src/transport/startLogsBatch.ts", "../../../node_modules/@datadog/browser-logs/src/transport/startLogsBridge.ts", "../../../node_modules/@datadog/browser-logs/src/domain/contexts/internalContext.ts", "../../../node_modules/@datadog/browser-logs/src/domain/reportError.ts", "../../../node_modules/@datadog/browser-logs/src/domain/logsTelemetry.ts", "../../../node_modules/@datadog/browser-logs/src/boot/startLogs.ts", "../../../node_modules/@datadog/browser-logs/src/entries/main.ts", "../../javascript/modules/datadog.js"],
"sourcesContent": ["\"use strict\";\nObject.defineProperty(exports, \"__esModule\", { value: true });\nvar initHeader = function () {\n var SELECTOR = '.js-cads-search-reveal';\n var SHOW_SEARCH_CLASS_NAME = 'cads-header--show-search';\n var header = document.querySelector('.cads-header');\n var controlButton = header && header.querySelector(SELECTOR);\n if (controlButton) {\n var setClosed_1 = function () {\n header.classList.remove(SHOW_SEARCH_CLASS_NAME);\n controlButton.setAttribute('aria-expanded', 'false');\n var showLabel = controlButton.getAttribute('data-descriptive-label-show');\n if (showLabel) {\n controlButton.setAttribute('aria-label', showLabel);\n }\n };\n var setOpen_1 = function () {\n header.classList.add(SHOW_SEARCH_CLASS_NAME);\n controlButton.setAttribute('aria-expanded', 'true');\n var hideLabel = controlButton.getAttribute('data-descriptive-label-hide');\n if (hideLabel) {\n controlButton.setAttribute('aria-label', hideLabel);\n }\n };\n // Set initial control state on init\n setClosed_1();\n controlButton.addEventListener('click', function () {\n if (header.classList.contains(SHOW_SEARCH_CLASS_NAME)) {\n setClosed_1();\n }\n else {\n setOpen_1();\n }\n });\n }\n};\nexports.default = initHeader;\n", "\"use strict\";\nvar __importDefault = (this && this.__importDefault) || function (mod) {\n return (mod && mod.__esModule) ? mod : { \"default\": mod };\n};\nObject.defineProperty(exports, \"__esModule\", { value: true });\nexports.initHeader = void 0;\nvar header_1 = __importDefault(require(\"./header\"));\nexports.initHeader = header_1.default;\nexports.default = header_1.default;\n", "\"use strict\";\nObject.defineProperty(exports, \"__esModule\", { value: true });\nexports.default = initNavigation;\n/**\n * The global navigation uses a \"greedy\" navigation pattern to allow as many\n * links to be visible as possible. As the width of the viewport is restricted,\n * navigation links fall into a collapsible section. On smaller viewports any links\n * that appear in the header links section in the header are also copied into the navigation.\n *\n * import { initNavigation } from '@citizensadvice/design-system/lib';\n * initNavigation();\n *\n * There is no configuration for the component. Labels are translated through\n * data attributes on the the container element. Translations are provided by\n * the ViewComponent and bundled as part of the Rails engine.\n *\n * This component uses an inline SVG icon for the toggle icon which a\n * version of the ArrowDown component HTML. The icon is rotated in CSS when\n * active rather than inlining two dedicated icons.\n */\nfunction isExpanded(toggle) {\n var ariaExpanded = toggle.getAttribute('aria-expanded');\n return ariaExpanded === 'true';\n}\nfunction getToggleEl(containerEl) {\n return containerEl.querySelector(\"button[aria-controls='cads-greedy-nav-dropdown']\");\n}\nfunction getWrapperEl(containerEl) {\n return containerEl.querySelector('.cads-greedy-nav');\n}\nfunction getDropdownEl(containerEl) {\n // The dropdown has a unique ID but query against the container for consistency\n return containerEl.querySelector('#cads-greedy-nav-dropdown');\n}\nfunction getMainNavEl(containerEl) {\n // The menu is just the first element child of the container\n return containerEl.firstElementChild;\n}\nfunction extractDataAttributes(containerEl) {\n var getData = function (name) {\n return containerEl.getAttribute(\"data-dropdown-\".concat(name));\n };\n return {\n label: getData('label') || 'More',\n labelClose: getData('label-close') || 'Close',\n ariaLabel: getData('aria-label') || 'More navigation options',\n ariaLabelClose: getData('aria-label-close') || 'Close navigation',\n };\n}\nfunction buildToggle(containerEl, dropdownId) {\n var data = extractDataAttributes(containerEl);\n var toggleId = 'cads-greedy-nav-toggle';\n return \"\");\n}\nfunction buildNavDropdown(dropdownId) {\n var navDropdown = document.createElement('ul');\n navDropdown.setAttribute('id', dropdownId);\n navDropdown.setAttribute('data-testid', dropdownId);\n navDropdown.classList.add('cads-greedy-nav__dropdown');\n var headerLinks = document.querySelector('.js-cads-copy-into-nav');\n if (headerLinks) {\n // Clone header links into navigation\n var headerLinksClone = headerLinks.cloneNode(true);\n var headerLinksContainer = document.createElement('li');\n headerLinksContainer.className = 'cads-greedy-nav__header-links';\n headerLinksContainer.appendChild(headerLinksClone);\n navDropdown.appendChild(headerLinksContainer);\n }\n return navDropdown;\n}\nfunction prepareHtml(containerEl) {\n var mainNavEl = getMainNavEl(containerEl);\n var toggleWrapper = document.createElement('div');\n toggleWrapper.classList.add('cads-greedy-nav');\n var dropdownId = 'cads-greedy-nav-dropdown';\n toggleWrapper.insertAdjacentHTML('beforeend', buildToggle(containerEl, dropdownId));\n toggleWrapper.appendChild(buildNavDropdown(dropdownId));\n mainNavEl.insertAdjacentElement('afterend', toggleWrapper);\n}\nfunction setToggleVisibility(containerEl, breaks) {\n var _a, _b;\n var toggleEl = getToggleEl(containerEl);\n if (breaks.length < 1) {\n (_a = toggleEl.parentElement) === null || _a === void 0 ? void 0 : _a.setAttribute('aria-haspopup', 'false');\n }\n else {\n (_b = toggleEl.parentElement) === null || _b === void 0 ? void 0 : _b.setAttribute('aria-haspopup', 'true');\n }\n}\nfunction openDropDown(containerEl) {\n var data = extractDataAttributes(containerEl);\n var toggleEl = getToggleEl(containerEl);\n var dropdownEl = getDropdownEl(containerEl);\n toggleEl.setAttribute('aria-expanded', 'true');\n dropdownEl.setAttribute('aria-hidden', 'false');\n toggleEl.firstElementChild.textContent = data.labelClose;\n toggleEl.setAttribute('aria-label', data.ariaLabelClose);\n}\nfunction closeDropDown(containerEl) {\n var data = extractDataAttributes(containerEl);\n var toggleEl = getToggleEl(containerEl);\n var dropdownEl = getDropdownEl(containerEl);\n toggleEl.setAttribute('aria-expanded', 'false');\n dropdownEl.setAttribute('aria-hidden', 'true');\n toggleEl.firstElementChild.textContent = data.label;\n toggleEl.setAttribute('aria-label', data.ariaLabel);\n}\nfunction toDropdown(containerEl) {\n var navDropdown = getDropdownEl(containerEl);\n var mainNav = getMainNavEl(containerEl);\n if (mainNav && mainNav.children.length > 0 && mainNav.lastElementChild) {\n if (navDropdown.firstChild) {\n navDropdown.insertBefore(mainNav.lastElementChild, navDropdown.firstChild);\n }\n else {\n navDropdown.appendChild(mainNav.lastElementChild);\n }\n }\n}\nfunction toMenu(containerEl) {\n var navDropdown = getDropdownEl(containerEl);\n var mainNav = getMainNavEl(containerEl);\n if (mainNav &&\n navDropdown &&\n navDropdown.children.length > 0 &&\n navDropdown.firstElementChild) {\n mainNav.appendChild(navDropdown.firstElementChild);\n }\n}\nfunction computeContentWidth(containerEl) {\n var styles = window.getComputedStyle(containerEl);\n var padding = parseFloat(styles.paddingLeft) + parseFloat(styles.paddingRight);\n return containerEl.clientWidth - padding;\n}\nfunction computeChildrenOffsetWidth(containerEl) {\n var children = containerEl.childNodes;\n var sum = 0;\n for (var i = 0; i < children.length; i++) {\n var child = children[i];\n if (child.nodeType !== 3 && !Number.isNaN(child.offsetWidth)) {\n sum += child.offsetWidth;\n }\n }\n return sum;\n}\nfunction doesItFit(containerEl, breaks) {\n var mainNav = getMainNavEl(containerEl);\n var currentContentWidth = computeContentWidth(containerEl);\n var currentChildrenOffsetWidth = computeChildrenOffsetWidth(containerEl);\n while (currentContentWidth <= currentChildrenOffsetWidth &&\n mainNav.children.length > 0) {\n toDropdown(containerEl);\n breaks.push(currentChildrenOffsetWidth);\n setToggleVisibility(containerEl, breaks);\n currentContentWidth = computeContentWidth(containerEl);\n currentChildrenOffsetWidth = computeChildrenOffsetWidth(containerEl);\n }\n while (currentContentWidth >= breaks[breaks.length - 1]) {\n toMenu(containerEl);\n breaks.pop();\n setToggleVisibility(containerEl, breaks);\n }\n setToggleVisibility(containerEl, breaks);\n}\nfunction addResizeObserver(containerEl) {\n var isInitialised = false;\n var breaks = [];\n var timer;\n var observer = new ResizeObserver(function () {\n clearTimeout(timer);\n timer = window.setTimeout(function () {\n doesItFit(containerEl, breaks);\n // Only add initialised class once we've actually\n // moved some items into the navigation for the\n // first time.\n if (!isInitialised) {\n document.body.classList.add('cads-has-greedy-nav');\n isInitialised = true;\n }\n }, 50);\n });\n // This will fire when observed, which is desirable.\n // Use this to set up the initial state of the dropdown.\n observer.observe(containerEl);\n}\nfunction addToggleHandler(containerEl) {\n var toggleEl = getToggleEl(containerEl);\n toggleEl.addEventListener('click', function (event) {\n if (isExpanded(toggleEl)) {\n closeDropDown(containerEl);\n }\n else {\n openDropDown(containerEl);\n }\n });\n}\nfunction addFocusHandler(containerEl) {\n var wrapperEl = getWrapperEl(containerEl);\n wrapperEl.addEventListener('focusout', function (event) {\n var target = event.relatedTarget || document.activeElement;\n if (wrapperEl.contains(target)) {\n return;\n }\n closeDropDown(containerEl);\n });\n}\nfunction addClickOutsideHandler(containerEl) {\n var toggleEl = getToggleEl(containerEl);\n var navDropdownEl = getDropdownEl(containerEl);\n document.addEventListener('click', function (event) {\n if ('composedPath' in event) {\n var withinBoundaries = event.composedPath().includes(toggleEl) ||\n event.composedPath().includes(navDropdownEl);\n if (!withinBoundaries) {\n closeDropDown(containerEl);\n }\n }\n });\n}\nfunction addEscapeKeyHandler(containerEl) {\n document.addEventListener('keydown', function (event) {\n if (event.key === 'Escape') {\n closeDropDown(containerEl);\n }\n });\n}\nfunction initNavigation() {\n var containerEl = document.querySelector('.js-cads-greedy-nav');\n if (containerEl) {\n prepareHtml(containerEl);\n addResizeObserver(containerEl);\n addToggleHandler(containerEl);\n addFocusHandler(containerEl);\n addClickOutsideHandler(containerEl);\n addEscapeKeyHandler(containerEl);\n }\n}\n", "\"use strict\";\nvar __importDefault = (this && this.__importDefault) || function (mod) {\n return (mod && mod.__esModule) ? mod : { \"default\": mod };\n};\nObject.defineProperty(exports, \"__esModule\", { value: true });\nexports.initNavigation = void 0;\nvar navigation_1 = __importDefault(require(\"./navigation\"));\nexports.initNavigation = navigation_1.default;\nexports.default = navigation_1.default;\n", "\"use strict\";\nvar __importDefault = (this && this.__importDefault) || function (mod) {\n return (mod && mod.__esModule) ? mod : { \"default\": mod };\n};\nObject.defineProperty(exports, \"__esModule\", { value: true });\nvar navigation_1 = __importDefault(require(\"../navigation\"));\n// Delegate to new navigation script for backwards compatibility\nvar GreedyNav = {\n init: function (_options) {\n if (_options === void 0) { _options = {}; }\n console.warn('Deprecated: use initNavigation instead');\n (0, navigation_1.default)();\n },\n};\nexports.default = GreedyNav;\n", "\"use strict\";\nvar __importDefault = (this && this.__importDefault) || function (mod) {\n return (mod && mod.__esModule) ? mod : { \"default\": mod };\n};\nObject.defineProperty(exports, \"__esModule\", { value: true });\nexports.GreedyNav = void 0;\nexports.initGreedyNav = initGreedyNav;\nvar GreedyNav_1 = __importDefault(require(\"./GreedyNav\"));\nexports.GreedyNav = GreedyNav_1.default;\n// Delegate to old init script for backwards compatibility\nfunction initGreedyNav(options) {\n if (options === void 0) { options = {}; }\n GreedyNav_1.default.init(options);\n}\nexports.default = GreedyNav_1.default;\n", "// Entry point for the build script in your package.json\nimport initHeader from '@citizensadvice/design-system/lib/header';\nimport initGreedyNav from \"./modules/greedy-nav\";\nimport initSupplierTableButton from \"./modules/supplier-table\";\nimport initDatadog from \"./modules/datadog\";\n\ntry {\n // Initialise datadog monitoring\n initDatadog();\n\n // Initialise design-system modules\n initHeader();\n initGreedyNav();\n\n // Initialise application modules\n initSupplierTableButton();\n} catch (error) {\n document.querySelector(\"html\").classList.add(\"no-js\");\n throw error;\n}\n", "import greedyNav from '@citizensadvice/design-system/lib/greedy-nav';\n\nexport default function initGreedyNav() {\n greedyNav.init({\n mainNavWrapper: '.cads-navigation',\n initClass: 'cads-greedy-nav-init',\n });\n}\n", "const selectors = {\n table: \".js-supplier-table\",\n allRows: \".js-supplier-table tbody tr\",\n rowSixUp: \".js-supplier-table tbody tr:nth-child(n + 6)\",\n showMoreButton: \".js-show-more-suppliers\",\n showFewerButton: \".js-show-fewer-suppliers\",\n countText: \".js-supplier-table-count-text\",\n};\n\nconst totalRows = document.querySelectorAll(selectors.allRows).length;\n\nconst hideRows = () => {\n document.querySelectorAll(selectors.rowSixUp).forEach((row) => {\n row.classList.add(\"supplier-table__row--hidden\");\n });\n};\n\nconst showRows = () => {\n document.querySelectorAll(selectors.rowSixUp).forEach((row) => {\n row.classList.remove(\"supplier-table__row--hidden\");\n });\n};\n\nconst updateCountText = (showing) => {\n const countText = document.querySelector(selectors.countText);\n countText.innerHTML = `Showing ${showing} of ${totalRows} suppliers`;\n};\n\nconst showMoreSuppliers = () => {\n const tableContainer = document.querySelector(\".supplier-table--show-more\");\n tableContainer.classList.remove(\"supplier-table--show-more\");\n tableContainer.classList.add(\"supplier-table--show-fewer\");\n\n updateCountText(totalRows);\n showRows();\n const showFewer = document.querySelector(selectors.showFewerButton);\n showFewer.focus({ focusVisible: true });\n};\n\nconst showFewerSuppliers = () => {\n const tableContainer = document.querySelector(\".supplier-table--show-fewer\");\n tableContainer.classList.remove(\"supplier-table--show-fewer\");\n tableContainer.classList.add(\"supplier-table--show-more\");\n\n updateCountText(5);\n hideRows();\n const showMore = document.querySelector(selectors.showMoreButton);\n showMore.focus({ focusVisible: true });\n};\n\nconst addButtonEventHandlers = () => {\n document\n .querySelector(selectors.showMoreButton)\n .addEventListener(\"click\", showMoreSuppliers);\n\n document\n .querySelector(selectors.showFewerButton)\n .addEventListener(\"click\", showFewerSuppliers);\n};\n\nexport default () => {\n hideRows();\n addButtonEventHandlers();\n};\n", "/* eslint-disable local-rules/disallow-side-effects */\n/**\n * Keep references on console methods to avoid triggering patched behaviors\n *\n * NB: in some setup, console could already be patched by another SDK.\n * In this case, some display messages can be sent by the other SDK\n * but we should be safe from infinite loop nonetheless.\n */\n\nexport const ConsoleApiName = {\n log: 'log',\n debug: 'debug',\n info: 'info',\n warn: 'warn',\n error: 'error',\n} as const\n\nexport type ConsoleApiName = (typeof ConsoleApiName)[keyof typeof ConsoleApiName]\n\ninterface Display {\n debug: typeof console.debug\n log: typeof console.log\n info: typeof console.info\n warn: typeof console.warn\n error: typeof console.error\n}\n\n/**\n * When building JS bundles, some users might use a plugin[1] or configuration[2] to remove\n * \"console.*\" references. This causes some issue as we expect `console.*` to be defined.\n * As a workaround, let's use a variable alias, so those expressions won't be taken into account by\n * simple static analysis.\n *\n * [1]: https://babeljs.io/docs/babel-plugin-transform-remove-console/\n * [2]: https://github.com/terser/terser#compress-options (look for drop_console)\n */\nexport const globalConsole = console\n\nexport const originalConsoleMethods = {} as Display\nObject.keys(ConsoleApiName).forEach((name) => {\n originalConsoleMethods[name as ConsoleApiName] = globalConsole[name as ConsoleApiName]\n})\n\nconst PREFIX = 'Datadog Browser SDK:'\n\nexport const display: Display = {\n debug: originalConsoleMethods.debug.bind(globalConsole, PREFIX),\n log: originalConsoleMethods.log.bind(globalConsole, PREFIX),\n info: originalConsoleMethods.info.bind(globalConsole, PREFIX),\n warn: originalConsoleMethods.warn.bind(globalConsole, PREFIX),\n error: originalConsoleMethods.error.bind(globalConsole, PREFIX),\n}\n\nexport const DOCS_ORIGIN = 'https://docs.datadoghq.com'\nexport const DOCS_TROUBLESHOOTING = `${DOCS_ORIGIN}/real_user_monitoring/browser/troubleshooting`\nexport const MORE_DETAILS = 'More details:'\n", "import { display } from './display'\n\nexport function catchUserErrors(fn: (...args: Args) => R, errorMsg: string) {\n return (...args: Args) => {\n try {\n return fn(...args)\n } catch (err) {\n display.error(errorMsg, err)\n }\n }\n}\n", "/**\n * Return true if the draw is successful\n * @param threshold between 0 and 100\n */\nexport function performDraw(threshold: number): boolean {\n return threshold !== 0 && Math.random() * 100 <= threshold\n}\n\nexport function round(num: number, decimals: 0 | 1 | 2 | 3 | 4) {\n return +num.toFixed(decimals)\n}\n\nexport function isPercentage(value: unknown) {\n return isNumber(value) && value >= 0 && value <= 100\n}\n\nexport function isNumber(value: unknown): value is number {\n return typeof value === 'number'\n}\n", "import { isNumber, round } from './numberUtils'\n\nexport const ONE_SECOND = 1000\nexport const ONE_MINUTE = 60 * ONE_SECOND\nexport const ONE_HOUR = 60 * ONE_MINUTE\nexport const ONE_DAY = 24 * ONE_HOUR\nexport const ONE_YEAR = 365 * ONE_DAY\n\nexport type Duration = number & { d: 'Duration in ms' }\nexport type ServerDuration = number & { s: 'Duration in ns' }\nexport type TimeStamp = number & { t: 'Epoch time' }\nexport type RelativeTime = number & { r: 'Time relative to navigation start' } & { d: 'Duration in ms' }\nexport type ClocksState = { relative: RelativeTime; timeStamp: TimeStamp }\n\nexport function relativeToClocks(relative: RelativeTime) {\n return { relative, timeStamp: getCorrectedTimeStamp(relative) }\n}\n\nexport function timeStampToClocks(timeStamp: TimeStamp) {\n return { relative: getRelativeTime(timeStamp), timeStamp }\n}\n\nfunction getCorrectedTimeStamp(relativeTime: RelativeTime) {\n const correctedOrigin = (dateNow() - performance.now()) as TimeStamp\n // apply correction only for positive drift\n if (correctedOrigin > getNavigationStart()) {\n return Math.round(addDuration(correctedOrigin, relativeTime)) as TimeStamp\n }\n return getTimeStamp(relativeTime)\n}\n\nexport function currentDrift() {\n return Math.round(dateNow() - addDuration(getNavigationStart(), performance.now() as Duration))\n}\n\nexport function toServerDuration(duration: Duration): ServerDuration\nexport function toServerDuration(duration: Duration | undefined): ServerDuration | undefined\nexport function toServerDuration(duration: Duration | undefined) {\n if (!isNumber(duration)) {\n return duration\n }\n return round(duration * 1e6, 0) as ServerDuration\n}\n\nexport function dateNow() {\n // Do not use `Date.now` because sometimes websites are wrongly \"polyfilling\" it. For example, we\n // had some users using a very old version of `datejs`, which patched `Date.now` to return a Date\n // instance instead of a timestamp[1]. Those users are unlikely to fix this, so let's handle this\n // case ourselves.\n // [1]: https://github.com/datejs/Datejs/blob/97f5c7c58c5bc5accdab8aa7602b6ac56462d778/src/core-debug.js#L14-L16\n return new Date().getTime()\n}\n\nexport function timeStampNow() {\n return dateNow() as TimeStamp\n}\n\nexport function relativeNow() {\n return performance.now() as RelativeTime\n}\n\nexport function clocksNow() {\n return { relative: relativeNow(), timeStamp: timeStampNow() }\n}\n\nexport function clocksOrigin() {\n return { relative: 0 as RelativeTime, timeStamp: getNavigationStart() }\n}\n\nexport function elapsed(start: TimeStamp, end: TimeStamp): Duration\nexport function elapsed(start: RelativeTime, end: RelativeTime): Duration\nexport function elapsed(start: number, end: number) {\n return (end - start) as Duration\n}\n\nexport function addDuration(a: TimeStamp, b: Duration): TimeStamp\nexport function addDuration(a: RelativeTime, b: Duration): RelativeTime\nexport function addDuration(a: Duration, b: Duration): Duration\nexport function addDuration(a: number, b: number) {\n return a + b\n}\n\n// Get the time since the navigation was started.\nexport function getRelativeTime(timestamp: TimeStamp) {\n return (timestamp - getNavigationStart()) as RelativeTime\n}\n\nexport function getTimeStamp(relativeTime: RelativeTime) {\n return Math.round(addDuration(getNavigationStart(), relativeTime)) as TimeStamp\n}\n\nexport function looksLikeRelativeTime(time: RelativeTime | TimeStamp): time is RelativeTime {\n return time < ONE_YEAR\n}\n\n/**\n * Navigation start slightly change on some rare cases\n */\nlet navigationStart: TimeStamp | undefined\n\n/**\n * Notes: this does not use `performance.timeOrigin` because:\n * - It doesn't seem to reflect the actual time on which the navigation has started: it may be much farther in the past,\n * at least in Firefox 71. (see: https://bugzilla.mozilla.org/show_bug.cgi?id=1429926)\n * - It is not supported in Safari <15\n */\nfunction getNavigationStart() {\n if (navigationStart === undefined) {\n navigationStart = performance.timing.navigationStart as TimeStamp\n }\n return navigationStart\n}\n", "export const ONE_KIBI_BYTE = 1024\nexport const ONE_MEBI_BYTE = 1024 * ONE_KIBI_BYTE\n\n// eslint-disable-next-line no-control-regex\nconst HAS_MULTI_BYTES_CHARACTERS = /[^\\u0000-\\u007F]/\n\nexport function computeBytesCount(candidate: string): number {\n // Accurate bytes count computations can degrade performances when there is a lot of events to process\n if (!HAS_MULTI_BYTES_CHARACTERS.test(candidate)) {\n return candidate.length\n }\n\n if (window.TextEncoder !== undefined) {\n return new TextEncoder().encode(candidate).length\n }\n\n return new Blob([candidate]).size\n}\n\nexport function concatBuffers(buffers: Uint8Array[]): Uint8Array {\n const length = buffers.reduce((total, buffer) => total + buffer.length, 0)\n const result: Uint8Array = new Uint8Array(length)\n let offset = 0\n for (const buffer of buffers) {\n result.set(buffer, offset)\n offset += buffer.length\n }\n return result\n}\n", "export function shallowClone(object: T): T & Record {\n return { ...object } as T & Record\n}\n\nexport function objectHasValue(object: T, value: unknown): value is T[keyof T] {\n return Object.keys(object).some((key) => object[key] === value)\n}\n\nexport function isEmptyObject(object: object) {\n return Object.keys(object).length === 0\n}\n\nexport function mapValues(object: { [key: string]: A }, fn: (arg: A) => B) {\n const newObject: { [key: string]: B } = {}\n for (const key of Object.keys(object)) {\n newObject[key] = fn(object[key])\n }\n return newObject\n}\n", "/**\n * inspired by https://mathiasbynens.be/notes/globalthis\n */\n\nexport function getGlobalObject(): T {\n if (typeof globalThis === 'object') {\n return globalThis as unknown as T\n }\n Object.defineProperty(Object.prototype, '_dd_temp_', {\n get() {\n return this as object\n },\n configurable: true,\n })\n // @ts-ignore _dd_temp is defined using defineProperty\n let globalObject: unknown = _dd_temp_\n // @ts-ignore _dd_temp is defined using defineProperty\n delete Object.prototype._dd_temp_\n if (typeof globalObject !== 'object') {\n // on safari _dd_temp_ is available on window but not globally\n // fallback on other browser globals check\n if (typeof self === 'object') {\n globalObject = self\n } else if (typeof window === 'object') {\n globalObject = window\n } else {\n globalObject = {}\n }\n }\n return globalObject as T\n}\n", "import { getGlobalObject } from './getGlobalObject'\n\nexport interface BrowserWindowWithZoneJs extends Window {\n Zone?: {\n // All Zone.js versions expose the __symbol__ method, but we observed that some website have a\n // 'Zone' global variable unrelated to Zone.js, so let's consider this method optional\n // nonetheless.\n __symbol__?: (name: string) => string\n }\n}\n\n/**\n * Gets the original value for a DOM API that was potentially patched by Zone.js.\n *\n * Zone.js[1] is a library that patches a bunch of JS and DOM APIs. It usually stores the original\n * value of the patched functions/constructors/methods in a hidden property prefixed by\n * __zone_symbol__.\n *\n * In multiple occasions, we observed that Zone.js is the culprit of important issues leading to\n * browser resource exhaustion (memory leak, high CPU usage). This method is used as a workaround to\n * use the original DOM API instead of the one patched by Zone.js.\n *\n * [1]: https://github.com/angular/angular/tree/main/packages/zone.js\n */\nexport function getZoneJsOriginalValue(\n target: Target,\n name: Name\n): Target[Name] {\n const browserWindow = getGlobalObject()\n let original: Target[Name] | undefined\n if (browserWindow.Zone && typeof browserWindow.Zone.__symbol__ === 'function') {\n original = (target as any)[browserWindow.Zone.__symbol__(name)]\n }\n if (!original) {\n original = target[name]\n }\n return original\n}\n", "import { display } from './display'\n\nlet onMonitorErrorCollected: undefined | ((error: unknown) => void)\nlet debugMode = false\n\nexport function startMonitorErrorCollection(newOnMonitorErrorCollected: (error: unknown) => void) {\n onMonitorErrorCollected = newOnMonitorErrorCollected\n}\n\nexport function setDebugMode(newDebugMode: boolean) {\n debugMode = newDebugMode\n}\n\nexport function resetMonitor() {\n onMonitorErrorCollected = undefined\n debugMode = false\n}\n\nexport function monitored unknown>(\n _: any,\n __: string,\n descriptor: TypedPropertyDescriptor\n) {\n const originalMethod = descriptor.value!\n descriptor.value = function (this: any, ...args: Parameters) {\n const decorated = onMonitorErrorCollected ? monitor(originalMethod) : originalMethod\n return decorated.apply(this, args) as ReturnType\n } as T\n}\n\nexport function monitor any>(fn: T): T {\n return function (this: any) {\n // eslint-disable-next-line @typescript-eslint/no-unsafe-return\n return callMonitored(fn, this, arguments as unknown as Parameters)\n } as unknown as T // consider output type has input type\n}\n\nexport function callMonitored any>(\n fn: T,\n context: ThisParameterType,\n args: Parameters\n): ReturnType | undefined\nexport function callMonitored any>(fn: T): ReturnType | undefined\nexport function callMonitored any>(\n fn: T,\n context?: any,\n args?: any\n): ReturnType | undefined {\n try {\n // eslint-disable-next-line @typescript-eslint/no-unsafe-return\n return fn.apply(context, args)\n } catch (e) {\n monitorError(e)\n }\n}\n\nexport function monitorError(e: unknown) {\n displayIfDebugEnabled(e)\n if (onMonitorErrorCollected) {\n try {\n onMonitorErrorCollected(e)\n } catch (e) {\n displayIfDebugEnabled(e)\n }\n }\n}\n\nexport function displayIfDebugEnabled(...args: any[]) {\n if (debugMode) {\n display.error('[MONITOR]', ...args)\n }\n}\n", "import { getZoneJsOriginalValue } from './getZoneJsOriginalValue'\nimport { monitor } from './monitor'\nimport { getGlobalObject } from './getGlobalObject'\n\nexport type TimeoutId = ReturnType\n\nexport function setTimeout(callback: () => void, delay?: number): TimeoutId {\n return getZoneJsOriginalValue(getGlobalObject(), 'setTimeout')(monitor(callback), delay)\n}\n\nexport function clearTimeout(timeoutId: TimeoutId | undefined) {\n getZoneJsOriginalValue(getGlobalObject(), 'clearTimeout')(timeoutId)\n}\n\nexport function setInterval(callback: () => void, delay?: number): TimeoutId {\n return getZoneJsOriginalValue(getGlobalObject(), 'setInterval')(monitor(callback), delay)\n}\n\nexport function clearInterval(timeoutId: TimeoutId | undefined) {\n getZoneJsOriginalValue(getGlobalObject(), 'clearInterval')(timeoutId)\n}\n", "export interface Subscription {\n unsubscribe: () => void\n}\n\n// eslint-disable-next-line no-restricted-syntax\nexport class Observable {\n private observers: Array<(data: T) => void> = []\n private onLastUnsubscribe?: () => void\n\n constructor(private onFirstSubscribe?: (observable: Observable) => (() => void) | void) {}\n\n subscribe(f: (data: T) => void): Subscription {\n this.observers.push(f)\n if (this.observers.length === 1 && this.onFirstSubscribe) {\n this.onLastUnsubscribe = this.onFirstSubscribe(this) || undefined\n }\n return {\n unsubscribe: () => {\n this.observers = this.observers.filter((other) => f !== other)\n if (!this.observers.length && this.onLastUnsubscribe) {\n this.onLastUnsubscribe()\n }\n },\n }\n }\n\n notify(data: T) {\n this.observers.forEach((observer) => observer(data))\n }\n}\n\nexport function mergeObservables(...observables: Array>) {\n return new Observable((globalObservable) => {\n const subscriptions: Subscription[] = observables.map((observable) =>\n observable.subscribe((data) => globalObservable.notify(data))\n )\n return () => subscriptions.forEach((subscription) => subscription.unsubscribe())\n })\n}\n", "import type { TimeoutId } from '../timer'\nimport { setTimeout, clearTimeout } from '../timer'\n\n// use lodash API\nexport function throttle void>(\n fn: T,\n wait: number,\n options?: { leading?: boolean; trailing?: boolean }\n) {\n const needLeadingExecution = options && options.leading !== undefined ? options.leading : true\n const needTrailingExecution = options && options.trailing !== undefined ? options.trailing : true\n let inWaitPeriod = false\n let pendingExecutionWithParameters: Parameters | undefined\n let pendingTimeoutId: TimeoutId\n\n return {\n throttled: (...parameters: Parameters) => {\n if (inWaitPeriod) {\n pendingExecutionWithParameters = parameters\n return\n }\n if (needLeadingExecution) {\n fn(...parameters)\n } else {\n pendingExecutionWithParameters = parameters\n }\n inWaitPeriod = true\n pendingTimeoutId = setTimeout(() => {\n if (needTrailingExecution && pendingExecutionWithParameters) {\n fn(...pendingExecutionWithParameters)\n }\n inWaitPeriod = false\n pendingExecutionWithParameters = undefined\n }, wait)\n },\n cancel: () => {\n clearTimeout(pendingTimeoutId)\n inWaitPeriod = false\n pendingExecutionWithParameters = undefined\n },\n }\n}\n\n// eslint-disable-next-line @typescript-eslint/no-empty-function\nexport function noop() {}\n", "/**\n * UUID v4\n * from https://gist.github.com/jed/982883\n */\nexport function generateUUID(placeholder?: string): string {\n return placeholder\n ? // eslint-disable-next-line no-bitwise\n (parseInt(placeholder, 10) ^ ((Math.random() * 16) >> (parseInt(placeholder, 10) / 4))).toString(16)\n : `${1e7}-${1e3}-${4e3}-${8e3}-${1e11}`.replace(/[018]/g, generateUUID)\n}\n\nconst COMMA_SEPARATED_KEY_VALUE = /([\\w-]+)\\s*=\\s*([^;]+)/g\n\nexport function findCommaSeparatedValue(rawString: string, name: string): string | undefined {\n COMMA_SEPARATED_KEY_VALUE.lastIndex = 0\n while (true) {\n const match = COMMA_SEPARATED_KEY_VALUE.exec(rawString)\n if (match) {\n if (match[1] === name) {\n return match[2]\n }\n } else {\n break\n }\n }\n}\n\nexport function findCommaSeparatedValues(rawString: string): Map {\n const result = new Map()\n COMMA_SEPARATED_KEY_VALUE.lastIndex = 0\n while (true) {\n const match = COMMA_SEPARATED_KEY_VALUE.exec(rawString)\n if (match) {\n result.set(match[1], match[2])\n } else {\n break\n }\n }\n return result\n}\n\nexport function safeTruncate(candidate: string, length: number, suffix = '') {\n const lastChar = candidate.charCodeAt(length - 1)\n const isLastCharSurrogatePair = lastChar >= 0xd800 && lastChar <= 0xdbff\n const correctedLength = isLastCharSurrogatePair ? length + 1 : length\n\n if (candidate.length <= correctedLength) {\n return candidate\n }\n\n return `${candidate.slice(0, correctedLength)}${suffix}`\n}\n", "// Exported only for tests\nexport const enum Browser {\n CHROMIUM,\n SAFARI,\n OTHER,\n}\n\nexport function isChromium() {\n return detectBrowserCached() === Browser.CHROMIUM\n}\n\nexport function isSafari() {\n return detectBrowserCached() === Browser.SAFARI\n}\n\nlet browserCache: Browser | undefined\nfunction detectBrowserCached() {\n return browserCache ?? (browserCache = detectBrowser())\n}\n\n// Exported only for tests\nexport function detectBrowser(browserWindow: Window = window) {\n const userAgent = browserWindow.navigator.userAgent\n if ((browserWindow as any).chrome || /HeadlessChrome/.test(userAgent)) {\n return Browser.CHROMIUM\n }\n\n if (\n // navigator.vendor is deprecated, but it is the most resilient way we found to detect\n // \"Apple maintained browsers\" (AKA Safari). If one day it gets removed, we still have the\n // useragent test as a semi-working fallback.\n browserWindow.navigator.vendor?.indexOf('Apple') === 0 ||\n (/safari/i.test(userAgent) && !/chrome|android/i.test(userAgent))\n ) {\n return Browser.SAFARI\n }\n\n return Browser.OTHER\n}\n", "import { display } from '../tools/display'\nimport { ONE_MINUTE, ONE_SECOND } from '../tools/utils/timeUtils'\nimport { findCommaSeparatedValue, findCommaSeparatedValues, generateUUID } from '../tools/utils/stringUtils'\n\nexport interface CookieOptions {\n secure?: boolean\n crossSite?: boolean\n partitioned?: boolean\n domain?: string\n}\n\nexport function setCookie(name: string, value: string, expireDelay: number = 0, options?: CookieOptions) {\n const date = new Date()\n date.setTime(date.getTime() + expireDelay)\n const expires = `expires=${date.toUTCString()}`\n const sameSite = options && options.crossSite ? 'none' : 'strict'\n const domain = options && options.domain ? `;domain=${options.domain}` : ''\n const secure = options && options.secure ? ';secure' : ''\n const partitioned = options && options.partitioned ? ';partitioned' : ''\n document.cookie = `${name}=${value};${expires};path=/;samesite=${sameSite}${domain}${secure}${partitioned}`\n}\n\nexport function getCookie(name: string) {\n return findCommaSeparatedValue(document.cookie, name)\n}\n\nlet initCookieParsed: Map | undefined\n\n/**\n * Returns a cached value of the cookie. Use this during SDK initialization (and whenever possible)\n * to avoid accessing document.cookie multiple times.\n */\nexport function getInitCookie(name: string) {\n if (!initCookieParsed) {\n initCookieParsed = findCommaSeparatedValues(document.cookie)\n }\n return initCookieParsed.get(name)\n}\n\nexport function resetInitCookies() {\n initCookieParsed = undefined\n}\n\nexport function deleteCookie(name: string, options?: CookieOptions) {\n setCookie(name, '', 0, options)\n}\n\nexport function areCookiesAuthorized(options: CookieOptions): boolean {\n if (document.cookie === undefined || document.cookie === null) {\n return false\n }\n try {\n // Use a unique cookie name to avoid issues when the SDK is initialized multiple times during\n // the test cookie lifetime\n const testCookieName = `dd_cookie_test_${generateUUID()}`\n const testCookieValue = 'test'\n setCookie(testCookieName, testCookieValue, ONE_MINUTE, options)\n const isCookieCorrectlySet = getCookie(testCookieName) === testCookieValue\n deleteCookie(testCookieName, options)\n return isCookieCorrectlySet\n } catch (error) {\n display.error(error)\n return false\n }\n}\n\n/**\n * No API to retrieve it, number of levels for subdomain and suffix are unknown\n * strategy: find the minimal domain on which cookies are allowed to be set\n * https://web.dev/same-site-same-origin/#site\n */\nlet getCurrentSiteCache: string | undefined\nexport function getCurrentSite() {\n if (getCurrentSiteCache === undefined) {\n // Use a unique cookie name to avoid issues when the SDK is initialized multiple times during\n // the test cookie lifetime\n const testCookieName = `dd_site_test_${generateUUID()}`\n const testCookieValue = 'test'\n\n const domainLevels = window.location.hostname.split('.')\n let candidateDomain = domainLevels.pop()!\n while (domainLevels.length && !getCookie(testCookieName)) {\n candidateDomain = `${domainLevels.pop()!}.${candidateDomain}`\n setCookie(testCookieName, testCookieValue, ONE_SECOND, { domain: candidateDomain })\n }\n deleteCookie(testCookieName, { domain: candidateDomain })\n getCurrentSiteCache = candidateDomain\n }\n return getCurrentSiteCache\n}\n", "import type { CookieOptions } from '../../../browser/cookie'\nimport type { SessionPersistence } from '../sessionConstants'\nimport type { SessionState } from '../sessionState'\n\nexport const SESSION_STORE_KEY = '_dd_s'\n\nexport type SessionStoreStrategyType =\n | { type: typeof SessionPersistence.COOKIE; cookieOptions: CookieOptions }\n | { type: typeof SessionPersistence.LOCAL_STORAGE }\n\nexport interface SessionStoreStrategy {\n isLockEnabled: boolean\n persistSession: (session: SessionState) => void\n retrieveSession: () => SessionState\n expireSession: (previousSessionState: SessionState) => void\n}\n", "export function findLast(\n array: T[],\n predicate: (item: T, index: number, array: T[]) => item is S\n): S | undefined {\n for (let i = array.length - 1; i >= 0; i -= 1) {\n const item = array[i]\n if (predicate(item, i, array)) {\n return item\n }\n }\n return undefined\n}\n\n// Keep the following wrapper functions as it can be mangled and will result in smaller bundle size that using\n// the native Object.values and Object.entries directly\n\nexport function objectValues(object: { [key: string]: T }) {\n return Object.values(object)\n}\n\nexport function objectEntries(object: { [key: string]: T }): Array<[string, T]> {\n return Object.entries(object)\n}\n", "import { ONE_HOUR, ONE_MINUTE, ONE_YEAR } from '../../tools/utils/timeUtils'\n\nexport const SESSION_TIME_OUT_DELAY = 4 * ONE_HOUR\nexport const SESSION_EXPIRATION_DELAY = 15 * ONE_MINUTE\nexport const SESSION_COOKIE_EXPIRATION_DELAY = ONE_YEAR\n\nexport const SessionPersistence = {\n COOKIE: 'cookie',\n LOCAL_STORAGE: 'local-storage',\n} as const\nexport type SessionPersistence = (typeof SessionPersistence)[keyof typeof SessionPersistence]\n", "export const SESSION_ENTRY_REGEXP = /^([a-zA-Z]+)=([a-z0-9-]+)$/\nexport const SESSION_ENTRY_SEPARATOR = '&'\n\nexport function isValidSessionString(sessionString: string | undefined | null): sessionString is string {\n return (\n !!sessionString &&\n (sessionString.indexOf(SESSION_ENTRY_SEPARATOR) !== -1 || SESSION_ENTRY_REGEXP.test(sessionString))\n )\n}\n", "import { isEmptyObject } from '../../tools/utils/objectUtils'\nimport { objectEntries } from '../../tools/utils/polyfills'\nimport { dateNow } from '../../tools/utils/timeUtils'\nimport { generateUUID } from '../../tools/utils/stringUtils'\nimport type { Configuration } from '../configuration'\nimport { SESSION_EXPIRATION_DELAY, SESSION_TIME_OUT_DELAY } from './sessionConstants'\nimport { isValidSessionString, SESSION_ENTRY_REGEXP, SESSION_ENTRY_SEPARATOR } from './sessionStateValidation'\nexport const EXPIRED = '1'\n\nexport interface SessionState {\n id?: string\n created?: string\n expire?: string\n isExpired?: typeof EXPIRED\n\n [key: string]: string | undefined\n}\n\nexport function getExpiredSessionState(\n previousSessionState: SessionState | undefined,\n configuration: Configuration\n): SessionState {\n const expiredSessionState: SessionState = {\n isExpired: EXPIRED,\n }\n if (configuration.trackAnonymousUser) {\n if (previousSessionState?.anonymousId) {\n expiredSessionState.anonymousId = previousSessionState?.anonymousId\n } else {\n expiredSessionState.anonymousId = generateUUID()\n }\n }\n return expiredSessionState\n}\n\nexport function isSessionInNotStartedState(session: SessionState) {\n return isEmptyObject(session)\n}\n\nexport function isSessionStarted(session: SessionState) {\n return !isSessionInNotStartedState(session)\n}\n\nexport function isSessionInExpiredState(session: SessionState) {\n return session.isExpired !== undefined || !isActiveSession(session)\n}\n\n// An active session is a session in either `Tracked` or `NotTracked` state\nfunction isActiveSession(sessionState: SessionState) {\n // created and expire can be undefined for versions which was not storing them\n // these checks could be removed when older versions will not be available/live anymore\n return (\n (sessionState.created === undefined || dateNow() - Number(sessionState.created) < SESSION_TIME_OUT_DELAY) &&\n (sessionState.expire === undefined || dateNow() < Number(sessionState.expire))\n )\n}\n\nexport function expandSessionState(session: SessionState) {\n session.expire = String(dateNow() + SESSION_EXPIRATION_DELAY)\n}\n\nexport function toSessionString(session: SessionState) {\n return (\n objectEntries(session)\n // we use `aid` as a key for anonymousId\n .map(([key, value]) => (key === 'anonymousId' ? `aid=${value}` : `${key}=${value}`))\n .join(SESSION_ENTRY_SEPARATOR)\n )\n}\n\nexport function toSessionState(sessionString: string | undefined | null) {\n const session: SessionState = {}\n if (isValidSessionString(sessionString)) {\n sessionString.split(SESSION_ENTRY_SEPARATOR).forEach((entry) => {\n const matches = SESSION_ENTRY_REGEXP.exec(entry)\n if (matches !== null) {\n const [, key, value] = matches\n if (key === 'aid') {\n // we use `aid` as a key for anonymousId\n session.anonymousId = value\n } else {\n session[key] = value\n }\n }\n })\n }\n return session\n}\n", "import { getInitCookie } from '../../browser/cookie'\nimport type { SessionStoreStrategy } from './storeStrategies/sessionStoreStrategy'\nimport { SESSION_STORE_KEY } from './storeStrategies/sessionStoreStrategy'\nimport type { SessionState } from './sessionState'\nimport { expandSessionState, isSessionStarted } from './sessionState'\n\nexport const OLD_SESSION_COOKIE_NAME = '_dd'\nexport const OLD_RUM_COOKIE_NAME = '_dd_r'\nexport const OLD_LOGS_COOKIE_NAME = '_dd_l'\n\n// duplicate values to avoid dependency issues\nexport const RUM_SESSION_KEY = 'rum'\nexport const LOGS_SESSION_KEY = 'logs'\n\n/**\n * This migration should remain in the codebase as long as older versions are available/live\n * to allow older sdk versions to be upgraded to newer versions without compatibility issues.\n */\nexport function tryOldCookiesMigration(cookieStoreStrategy: SessionStoreStrategy) {\n const sessionString = getInitCookie(SESSION_STORE_KEY)\n if (!sessionString) {\n const oldSessionId = getInitCookie(OLD_SESSION_COOKIE_NAME)\n const oldRumType = getInitCookie(OLD_RUM_COOKIE_NAME)\n const oldLogsType = getInitCookie(OLD_LOGS_COOKIE_NAME)\n const session: SessionState = {}\n\n if (oldSessionId) {\n session.id = oldSessionId\n }\n if (oldLogsType && /^[01]$/.test(oldLogsType)) {\n session[LOGS_SESSION_KEY] = oldLogsType\n }\n if (oldRumType && /^[012]$/.test(oldRumType)) {\n session[RUM_SESSION_KEY] = oldRumType\n }\n\n if (isSessionStarted(session)) {\n expandSessionState(session)\n cookieStoreStrategy.persistSession(session)\n }\n }\n}\n", "import { isChromium } from '../../../tools/utils/browserDetection'\nimport type { CookieOptions } from '../../../browser/cookie'\nimport { getCurrentSite, areCookiesAuthorized, getCookie, setCookie } from '../../../browser/cookie'\nimport type { InitConfiguration, Configuration } from '../../configuration'\nimport { tryOldCookiesMigration } from '../oldCookiesMigration'\nimport {\n SESSION_COOKIE_EXPIRATION_DELAY,\n SESSION_EXPIRATION_DELAY,\n SESSION_TIME_OUT_DELAY,\n SessionPersistence,\n} from '../sessionConstants'\nimport type { SessionState } from '../sessionState'\nimport { toSessionString, toSessionState, getExpiredSessionState } from '../sessionState'\nimport type { SessionStoreStrategy, SessionStoreStrategyType } from './sessionStoreStrategy'\nimport { SESSION_STORE_KEY } from './sessionStoreStrategy'\n\nexport function selectCookieStrategy(initConfiguration: InitConfiguration): SessionStoreStrategyType | undefined {\n const cookieOptions = buildCookieOptions(initConfiguration)\n return areCookiesAuthorized(cookieOptions) ? { type: SessionPersistence.COOKIE, cookieOptions } : undefined\n}\n\nexport function initCookieStrategy(configuration: Configuration, cookieOptions: CookieOptions): SessionStoreStrategy {\n const cookieStore = {\n /**\n * Lock strategy allows mitigating issues due to concurrent access to cookie.\n * This issue concerns only chromium browsers and enabling this on firefox increases cookie write failures.\n */\n isLockEnabled: isChromium(),\n persistSession: persistSessionCookie(cookieOptions),\n retrieveSession: retrieveSessionCookie,\n expireSession: (sessionState: SessionState) => expireSessionCookie(cookieOptions, sessionState, configuration),\n }\n\n tryOldCookiesMigration(cookieStore)\n\n return cookieStore\n}\n\nfunction persistSessionCookie(options: CookieOptions) {\n return (session: SessionState) => {\n setCookie(SESSION_STORE_KEY, toSessionString(session), SESSION_EXPIRATION_DELAY, options)\n }\n}\n\nfunction expireSessionCookie(options: CookieOptions, sessionState: SessionState, configuration: Configuration) {\n const expiredSessionState = getExpiredSessionState(sessionState, configuration)\n // we do not extend cookie expiration date\n setCookie(\n SESSION_STORE_KEY,\n toSessionString(expiredSessionState),\n configuration.trackAnonymousUser ? SESSION_COOKIE_EXPIRATION_DELAY : SESSION_TIME_OUT_DELAY,\n options\n )\n}\n\nfunction retrieveSessionCookie(): SessionState {\n const sessionString = getCookie(SESSION_STORE_KEY)\n const sessionState = toSessionState(sessionString)\n return sessionState\n}\n\nexport function buildCookieOptions(initConfiguration: InitConfiguration) {\n const cookieOptions: CookieOptions = {}\n\n cookieOptions.secure =\n !!initConfiguration.useSecureSessionCookie || !!initConfiguration.usePartitionedCrossSiteSessionCookie\n cookieOptions.crossSite = !!initConfiguration.usePartitionedCrossSiteSessionCookie\n cookieOptions.partitioned = !!initConfiguration.usePartitionedCrossSiteSessionCookie\n\n if (initConfiguration.trackSessionAcrossSubdomains) {\n cookieOptions.domain = getCurrentSite()\n }\n\n return cookieOptions\n}\n", "import { generateUUID } from '../../../tools/utils/stringUtils'\nimport type { Configuration } from '../../configuration'\nimport { SessionPersistence } from '../sessionConstants'\nimport type { SessionState } from '../sessionState'\nimport { toSessionString, toSessionState, getExpiredSessionState } from '../sessionState'\nimport type { SessionStoreStrategy, SessionStoreStrategyType } from './sessionStoreStrategy'\nimport { SESSION_STORE_KEY } from './sessionStoreStrategy'\n\nconst LOCAL_STORAGE_TEST_KEY = '_dd_test_'\n\nexport function selectLocalStorageStrategy(): SessionStoreStrategyType | undefined {\n try {\n const id = generateUUID()\n const testKey = `${LOCAL_STORAGE_TEST_KEY}${id}`\n localStorage.setItem(testKey, id)\n const retrievedId = localStorage.getItem(testKey)\n localStorage.removeItem(testKey)\n return id === retrievedId ? { type: SessionPersistence.LOCAL_STORAGE } : undefined\n } catch {\n return undefined\n }\n}\n\nexport function initLocalStorageStrategy(configuration: Configuration): SessionStoreStrategy {\n return {\n isLockEnabled: false,\n persistSession: persistInLocalStorage,\n retrieveSession: retrieveSessionFromLocalStorage,\n expireSession: (sessionState: SessionState) => expireSessionFromLocalStorage(sessionState, configuration),\n }\n}\n\nfunction persistInLocalStorage(sessionState: SessionState) {\n localStorage.setItem(SESSION_STORE_KEY, toSessionString(sessionState))\n}\n\nfunction retrieveSessionFromLocalStorage(): SessionState {\n const sessionString = localStorage.getItem(SESSION_STORE_KEY)\n return toSessionState(sessionString)\n}\n\nfunction expireSessionFromLocalStorage(previousSessionState: SessionState, configuration: Configuration) {\n persistInLocalStorage(getExpiredSessionState(previousSessionState, configuration))\n}\n", "import { setTimeout } from '../../tools/timer'\nimport { generateUUID } from '../../tools/utils/stringUtils'\nimport type { SessionStoreStrategy } from './storeStrategies/sessionStoreStrategy'\nimport type { SessionState } from './sessionState'\nimport { expandSessionState, isSessionInExpiredState } from './sessionState'\n\ntype Operations = {\n process: (sessionState: SessionState) => SessionState | undefined\n after?: (sessionState: SessionState) => void\n}\n\nexport const LOCK_RETRY_DELAY = 10\nexport const LOCK_MAX_TRIES = 100\nconst bufferedOperations: Operations[] = []\nlet ongoingOperations: Operations | undefined\n\nexport function processSessionStoreOperations(\n operations: Operations,\n sessionStoreStrategy: SessionStoreStrategy,\n numberOfRetries = 0\n) {\n const { isLockEnabled, persistSession, expireSession } = sessionStoreStrategy\n const persistWithLock = (session: SessionState) => persistSession({ ...session, lock: currentLock })\n const retrieveStore = () => {\n const session = sessionStoreStrategy.retrieveSession()\n const lock = session.lock\n\n if (session.lock) {\n delete session.lock\n }\n\n return {\n session,\n lock,\n }\n }\n\n if (!ongoingOperations) {\n ongoingOperations = operations\n }\n if (operations !== ongoingOperations) {\n bufferedOperations.push(operations)\n return\n }\n if (isLockEnabled && numberOfRetries >= LOCK_MAX_TRIES) {\n next(sessionStoreStrategy)\n return\n }\n let currentLock: string\n let currentStore = retrieveStore()\n if (isLockEnabled) {\n // if someone has lock, retry later\n if (currentStore.lock) {\n retryLater(operations, sessionStoreStrategy, numberOfRetries)\n return\n }\n // acquire lock\n currentLock = generateUUID()\n persistWithLock(currentStore.session)\n // if lock is not acquired, retry later\n currentStore = retrieveStore()\n if (currentStore.lock !== currentLock) {\n retryLater(operations, sessionStoreStrategy, numberOfRetries)\n return\n }\n }\n let processedSession = operations.process(currentStore.session)\n if (isLockEnabled) {\n // if lock corrupted after process, retry later\n currentStore = retrieveStore()\n if (currentStore.lock !== currentLock!) {\n retryLater(operations, sessionStoreStrategy, numberOfRetries)\n return\n }\n }\n if (processedSession) {\n if (isSessionInExpiredState(processedSession)) {\n expireSession(processedSession)\n } else {\n expandSessionState(processedSession)\n if (isLockEnabled) {\n persistWithLock(processedSession)\n } else {\n persistSession(processedSession)\n }\n }\n }\n if (isLockEnabled) {\n // correctly handle lock around expiration would require to handle this case properly at several levels\n // since we don't have evidence of lock issues around expiration, let's just not do the corruption check for it\n if (!(processedSession && isSessionInExpiredState(processedSession))) {\n // if lock corrupted after persist, retry later\n currentStore = retrieveStore()\n if (currentStore.lock !== currentLock!) {\n retryLater(operations, sessionStoreStrategy, numberOfRetries)\n return\n }\n persistSession(currentStore.session)\n processedSession = currentStore.session\n }\n }\n // call after even if session is not persisted in order to perform operations on\n // up-to-date session state value => the value could have been modified by another tab\n operations.after?.(processedSession || currentStore.session)\n next(sessionStoreStrategy)\n}\n\nfunction retryLater(operations: Operations, sessionStore: SessionStoreStrategy, currentNumberOfRetries: number) {\n setTimeout(() => {\n processSessionStoreOperations(operations, sessionStore, currentNumberOfRetries + 1)\n }, LOCK_RETRY_DELAY)\n}\n\nfunction next(sessionStore: SessionStoreStrategy) {\n ongoingOperations = undefined\n const nextOperations = bufferedOperations.shift()\n if (nextOperations) {\n processSessionStoreOperations(nextOperations, sessionStore)\n }\n}\n", "import { clearInterval, setInterval } from '../../tools/timer'\nimport { Observable } from '../../tools/observable'\nimport { ONE_SECOND, dateNow } from '../../tools/utils/timeUtils'\nimport { throttle } from '../../tools/utils/functionUtils'\nimport { generateUUID } from '../../tools/utils/stringUtils'\nimport type { InitConfiguration, Configuration } from '../configuration'\nimport { display } from '../../tools/display'\nimport { selectCookieStrategy, initCookieStrategy } from './storeStrategies/sessionInCookie'\nimport type { SessionStoreStrategyType } from './storeStrategies/sessionStoreStrategy'\nimport {\n getExpiredSessionState,\n isSessionInExpiredState,\n isSessionInNotStartedState,\n isSessionStarted,\n} from './sessionState'\nimport type { SessionState } from './sessionState'\nimport { initLocalStorageStrategy, selectLocalStorageStrategy } from './storeStrategies/sessionInLocalStorage'\nimport { processSessionStoreOperations } from './sessionStoreOperations'\nimport { SessionPersistence } from './sessionConstants'\n\nexport interface SessionStore {\n expandOrRenewSession: () => void\n expandSession: () => void\n getSession: () => SessionState\n restartSession: () => void\n renewObservable: Observable\n expireObservable: Observable\n sessionStateUpdateObservable: Observable<{ previousState: SessionState; newState: SessionState }>\n expire: () => void\n stop: () => void\n updateSessionState: (state: Partial) => void\n}\n\n/**\n * Every second, the storage will be polled to check for any change that can occur\n * to the session state in another browser tab, or another window.\n * This value has been determined from our previous cookie-only implementation.\n */\nexport const STORAGE_POLL_DELAY = ONE_SECOND\n\n/**\n * Selects the correct session store strategy type based on the configuration and storage\n * availability.\n */\nexport function selectSessionStoreStrategyType(\n initConfiguration: InitConfiguration\n): SessionStoreStrategyType | undefined {\n switch (initConfiguration.sessionPersistence) {\n case SessionPersistence.COOKIE:\n return selectCookieStrategy(initConfiguration)\n\n case SessionPersistence.LOCAL_STORAGE:\n return selectLocalStorageStrategy()\n\n case undefined: {\n let sessionStoreStrategyType = selectCookieStrategy(initConfiguration)\n if (!sessionStoreStrategyType && initConfiguration.allowFallbackToLocalStorage) {\n sessionStoreStrategyType = selectLocalStorageStrategy()\n }\n return sessionStoreStrategyType\n }\n\n default:\n display.error(`Invalid session persistence '${String(initConfiguration.sessionPersistence)}'`)\n }\n}\n\n/**\n * Different session concepts:\n * - tracked, the session has an id and is updated along the user navigation\n * - not tracked, the session does not have an id but it is updated along the user navigation\n * - inactive, no session in store or session expired, waiting for a renew session\n */\nexport function startSessionStore(\n sessionStoreStrategyType: SessionStoreStrategyType,\n configuration: Configuration,\n productKey: string,\n computeSessionState: (rawTrackingType?: string) => { trackingType: TrackingType; isTracked: boolean }\n): SessionStore {\n const renewObservable = new Observable()\n const expireObservable = new Observable()\n const sessionStateUpdateObservable = new Observable<{ previousState: SessionState; newState: SessionState }>()\n\n const sessionStoreStrategy =\n sessionStoreStrategyType.type === SessionPersistence.COOKIE\n ? initCookieStrategy(configuration, sessionStoreStrategyType.cookieOptions)\n : initLocalStorageStrategy(configuration)\n const { expireSession } = sessionStoreStrategy\n\n const watchSessionTimeoutId = setInterval(watchSession, STORAGE_POLL_DELAY)\n let sessionCache: SessionState\n\n startSession()\n\n const { throttled: throttledExpandOrRenewSession, cancel: cancelExpandOrRenewSession } = throttle(() => {\n processSessionStoreOperations(\n {\n process: (sessionState) => {\n if (isSessionInNotStartedState(sessionState)) {\n return\n }\n\n const synchronizedSession = synchronizeSession(sessionState)\n expandOrRenewSessionState(synchronizedSession)\n return synchronizedSession\n },\n after: (sessionState) => {\n if (isSessionStarted(sessionState) && !hasSessionInCache()) {\n renewSessionInCache(sessionState)\n }\n sessionCache = sessionState\n },\n },\n sessionStoreStrategy\n )\n }, STORAGE_POLL_DELAY)\n\n function expandSession() {\n processSessionStoreOperations(\n {\n process: (sessionState) => (hasSessionInCache() ? synchronizeSession(sessionState) : undefined),\n },\n sessionStoreStrategy\n )\n }\n\n /**\n * allows two behaviors:\n * - if the session is active, synchronize the session cache without updating the session store\n * - if the session is not active, clear the session store and expire the session cache\n */\n function watchSession() {\n processSessionStoreOperations(\n {\n process: (sessionState) =>\n isSessionInExpiredState(sessionState) ? getExpiredSessionState(sessionState, configuration) : undefined,\n after: synchronizeSession,\n },\n sessionStoreStrategy\n )\n }\n\n function synchronizeSession(sessionState: SessionState) {\n if (isSessionInExpiredState(sessionState)) {\n sessionState = getExpiredSessionState(sessionState, configuration)\n }\n if (hasSessionInCache()) {\n if (isSessionInCacheOutdated(sessionState)) {\n expireSessionInCache()\n } else {\n sessionStateUpdateObservable.notify({ previousState: sessionCache, newState: sessionState })\n sessionCache = sessionState\n }\n }\n return sessionState\n }\n\n function startSession() {\n processSessionStoreOperations(\n {\n process: (sessionState) => {\n if (isSessionInNotStartedState(sessionState)) {\n return getExpiredSessionState(sessionState, configuration)\n }\n },\n after: (sessionState) => {\n sessionCache = sessionState\n },\n },\n sessionStoreStrategy\n )\n }\n\n function expandOrRenewSessionState(sessionState: SessionState) {\n if (isSessionInNotStartedState(sessionState)) {\n return false\n }\n\n const { trackingType, isTracked } = computeSessionState(sessionState[productKey])\n sessionState[productKey] = trackingType\n delete sessionState.isExpired\n if (isTracked && !sessionState.id) {\n sessionState.id = generateUUID()\n sessionState.created = String(dateNow())\n }\n }\n\n function hasSessionInCache() {\n return sessionCache[productKey] !== undefined\n }\n\n function isSessionInCacheOutdated(sessionState: SessionState) {\n return sessionCache.id !== sessionState.id || sessionCache[productKey] !== sessionState[productKey]\n }\n\n function expireSessionInCache() {\n sessionCache = getExpiredSessionState(sessionCache, configuration)\n expireObservable.notify()\n }\n\n function renewSessionInCache(sessionState: SessionState) {\n sessionCache = sessionState\n renewObservable.notify()\n }\n\n function updateSessionState(partialSessionState: Partial) {\n processSessionStoreOperations(\n {\n process: (sessionState) => ({ ...sessionState, ...partialSessionState }),\n after: synchronizeSession,\n },\n sessionStoreStrategy\n )\n }\n\n return {\n expandOrRenewSession: throttledExpandOrRenewSession,\n expandSession,\n getSession: () => sessionCache,\n renewObservable,\n expireObservable,\n sessionStateUpdateObservable,\n restartSession: startSession,\n expire: () => {\n cancelExpandOrRenewSession()\n expireSession(sessionCache)\n synchronizeSession(getExpiredSessionState(sessionCache, configuration))\n },\n stop: () => {\n clearInterval(watchSessionTimeoutId)\n },\n updateSessionState,\n }\n}\n", "import { Observable } from '../tools/observable'\n\nexport const TrackingConsent = {\n GRANTED: 'granted',\n NOT_GRANTED: 'not-granted',\n} as const\nexport type TrackingConsent = (typeof TrackingConsent)[keyof typeof TrackingConsent]\n\nexport interface TrackingConsentState {\n tryToInit: (trackingConsent: TrackingConsent) => void\n update: (trackingConsent: TrackingConsent) => void\n isGranted: () => boolean\n observable: Observable\n}\n\nexport function createTrackingConsentState(currentConsent?: TrackingConsent): TrackingConsentState {\n const observable = new Observable()\n\n return {\n tryToInit(trackingConsent: TrackingConsent) {\n if (!currentConsent) {\n currentConsent = trackingConsent\n }\n },\n update(trackingConsent: TrackingConsent) {\n currentConsent = trackingConsent\n observable.notify()\n },\n isGranted() {\n return currentConsent === TrackingConsent.GRANTED\n },\n observable,\n }\n}\n", "import { noop } from '../utils/functionUtils'\n\n/**\n * Custom implementation of JSON.stringify that ignores some toJSON methods. We need to do that\n * because some sites badly override toJSON on certain objects. Removing all toJSON methods from\n * nested values would be too costly, so we just detach them from the root value, and native classes\n * used to build JSON values (Array and Object).\n *\n * Note: this still assumes that JSON.stringify is correct.\n */\nexport function jsonStringify(\n value: unknown,\n replacer?: Array,\n space?: string | number\n): string | undefined {\n if (typeof value !== 'object' || value === null) {\n return JSON.stringify(value)\n }\n\n // Note: The order matter here. We need to detach toJSON methods on parent classes before their\n // subclasses.\n const restoreObjectPrototypeToJson = detachToJsonMethod(Object.prototype)\n const restoreArrayPrototypeToJson = detachToJsonMethod(Array.prototype)\n const restoreValuePrototypeToJson = detachToJsonMethod(Object.getPrototypeOf(value))\n const restoreValueToJson = detachToJsonMethod(value)\n\n try {\n return JSON.stringify(value, replacer, space)\n } catch {\n return ''\n } finally {\n restoreObjectPrototypeToJson()\n restoreArrayPrototypeToJson()\n restoreValuePrototypeToJson()\n restoreValueToJson()\n }\n}\n\nexport interface ObjectWithToJsonMethod {\n toJSON?: () => unknown\n}\n\nexport function detachToJsonMethod(value: object) {\n const object = value as ObjectWithToJsonMethod\n const objectToJson = object.toJSON\n if (objectToJson) {\n delete object.toJSON\n return () => {\n object.toJSON = objectToJson\n }\n }\n return noop\n}\n", "import { jsonStringify } from '../serialisation/jsonStringify'\n\nexport function normalizeUrl(url: string) {\n return buildUrl(url, location.href).href\n}\n\nexport function isValidUrl(url: string) {\n try {\n return !!buildUrl(url)\n } catch {\n return false\n }\n}\n\nexport function getPathName(url: string) {\n const pathname = buildUrl(url).pathname\n return pathname[0] === '/' ? pathname : `/${pathname}`\n}\n\nexport function buildUrl(url: string, base?: string) {\n const supportedURL = getSupportedUrl()\n if (supportedURL) {\n try {\n return base !== undefined ? new supportedURL(url, base) : new supportedURL(url)\n } catch (error) {\n throw new Error(`Failed to construct URL: ${String(error)} ${jsonStringify({ url, base })!}`)\n }\n }\n if (base === undefined && !/:/.test(url)) {\n throw new Error(`Invalid URL: '${url}'`)\n }\n let doc = document\n const anchorElement = doc.createElement('a')\n if (base !== undefined) {\n doc = document.implementation.createHTMLDocument('')\n const baseElement = doc.createElement('base')\n baseElement.href = base\n doc.head.appendChild(baseElement)\n doc.body.appendChild(anchorElement)\n }\n anchorElement.href = url\n return anchorElement\n}\n\nconst originalURL = URL\nlet isURLSupported: boolean | undefined\nfunction getSupportedUrl(): typeof URL | undefined {\n if (isURLSupported === undefined) {\n try {\n const url = new originalURL('http://test/path')\n isURLSupported = url.href === 'http://test/path'\n } catch {\n isURLSupported = false\n }\n }\n return isURLSupported ? originalURL : undefined\n}\n", "export type Site =\n | 'datadoghq.com'\n | 'us3.datadoghq.com'\n | 'us5.datadoghq.com'\n | 'datadoghq.eu'\n | 'ddog-gov.com'\n | 'ap1.datadoghq.com'\n\nexport const INTAKE_SITE_STAGING: Site = 'datad0g.com' as Site\nexport const INTAKE_SITE_FED_STAGING: Site = 'dd0g-gov.com' as Site\nexport const INTAKE_SITE_US1: Site = 'datadoghq.com'\nexport const INTAKE_SITE_EU1: Site = 'datadoghq.eu'\nexport const INTAKE_SITE_US1_FED: Site = 'ddog-gov.com'\n\nexport const PCI_INTAKE_HOST_US1 = 'pci.browser-intake-datadoghq.com'\nexport const INTAKE_URL_PARAMETERS = ['ddsource', 'ddtags']\n", "import type { Payload } from '../../transport'\nimport { timeStampNow } from '../../tools/utils/timeUtils'\nimport { normalizeUrl } from '../../tools/utils/urlPolyfill'\nimport { generateUUID } from '../../tools/utils/stringUtils'\nimport type { InitConfiguration } from './configuration'\nimport { INTAKE_SITE_US1, INTAKE_SITE_FED_STAGING, PCI_INTAKE_HOST_US1 } from './intakeSites'\n\n// replaced at build time\ndeclare const __BUILD_ENV__SDK_VERSION__: string\n\nexport type TrackType = 'logs' | 'rum' | 'replay'\nexport type ApiType =\n | 'xhr'\n | 'fetch'\n | 'beacon'\n // 'manual' reflects that the request have been sent manually, outside of the SDK (ex: via curl or\n // a Node.js script).\n | 'manual'\n\nexport type EndpointBuilder = ReturnType\n\nexport function createEndpointBuilder(\n initConfiguration: InitConfiguration,\n trackType: TrackType,\n configurationTags: string[]\n) {\n const buildUrlWithParameters = createEndpointUrlWithParametersBuilder(initConfiguration, trackType)\n\n return {\n build(api: ApiType, payload: Payload) {\n const parameters = buildEndpointParameters(initConfiguration, trackType, configurationTags, api, payload)\n return buildUrlWithParameters(parameters)\n },\n urlPrefix: buildUrlWithParameters(''),\n trackType,\n }\n}\n\n/**\n * Create a function used to build a full endpoint url from provided parameters. The goal of this\n * function is to pre-compute some parts of the URL to avoid re-computing everything on every\n * request, as only parameters are changing.\n */\nfunction createEndpointUrlWithParametersBuilder(\n initConfiguration: InitConfiguration,\n trackType: TrackType\n): (parameters: string) => string {\n const path = `/api/v2/${trackType}`\n const proxy = initConfiguration.proxy\n if (typeof proxy === 'string') {\n const normalizedProxyUrl = normalizeUrl(proxy)\n return (parameters) => `${normalizedProxyUrl}?ddforward=${encodeURIComponent(`${path}?${parameters}`)}`\n }\n if (typeof proxy === 'function') {\n return (parameters) => proxy({ path, parameters })\n }\n const host = buildEndpointHost(trackType, initConfiguration)\n return (parameters) => `https://${host}${path}?${parameters}`\n}\n\nexport function buildEndpointHost(\n trackType: TrackType,\n initConfiguration: InitConfiguration & { usePciIntake?: boolean }\n) {\n const { site = INTAKE_SITE_US1, internalAnalyticsSubdomain } = initConfiguration\n\n if (trackType === 'logs' && initConfiguration.usePciIntake && site === INTAKE_SITE_US1) {\n return PCI_INTAKE_HOST_US1\n }\n\n if (internalAnalyticsSubdomain && site === INTAKE_SITE_US1) {\n return `${internalAnalyticsSubdomain}.${INTAKE_SITE_US1}`\n }\n\n if (site === INTAKE_SITE_FED_STAGING) {\n return `http-intake.logs.${site}`\n }\n\n const domainParts = site.split('.')\n const extension = domainParts.pop()\n return `browser-intake-${domainParts.join('-')}.${extension!}`\n}\n\n/**\n * Build parameters to be used for an intake request. Parameters should be re-built for each\n * request, as they change randomly.\n */\nfunction buildEndpointParameters(\n { clientToken, internalAnalyticsSubdomain }: InitConfiguration,\n trackType: TrackType,\n configurationTags: string[],\n api: ApiType,\n { retry, encoding }: Payload\n) {\n const tags = [`sdk_version:${__BUILD_ENV__SDK_VERSION__}`, `api:${api}`].concat(configurationTags)\n if (retry) {\n tags.push(`retry_count:${retry.count}`, `retry_after:${retry.lastFailureStatus}`)\n }\n\n const parameters = [\n 'ddsource=browser',\n `ddtags=${encodeURIComponent(tags.join(','))}`,\n `dd-api-key=${clientToken}`,\n `dd-evp-origin-version=${encodeURIComponent(__BUILD_ENV__SDK_VERSION__)}`,\n 'dd-evp-origin=browser',\n `dd-request-id=${generateUUID()}`,\n ]\n\n if (encoding) {\n parameters.push(`dd-evp-encoding=${encoding}`)\n }\n\n if (trackType === 'rum') {\n parameters.push(`batch_time=${timeStampNow()}`)\n }\n\n if (internalAnalyticsSubdomain) {\n parameters.reverse()\n }\n\n return parameters.join('&')\n}\n", "import { DOCS_ORIGIN, MORE_DETAILS, display } from '../../tools/display'\nimport type { InitConfiguration } from './configuration'\n\nexport const TAG_SIZE_LIMIT = 200\n\nexport function buildTags(configuration: InitConfiguration): string[] {\n const { env, service, version, datacenter } = configuration\n const tags = []\n\n if (env) {\n tags.push(buildTag('env', env))\n }\n if (service) {\n tags.push(buildTag('service', service))\n }\n if (version) {\n tags.push(buildTag('version', version))\n }\n if (datacenter) {\n tags.push(buildTag('datacenter', datacenter))\n }\n\n return tags\n}\n\nexport function buildTag(key: string, rawValue: string) {\n // See https://docs.datadoghq.com/getting_started/tagging/#defining-tags for tags syntax. Note\n // that the backend may not follow the exact same rules, so we only want to display an informal\n // warning.\n const valueSizeLimit = TAG_SIZE_LIMIT - key.length - 1\n\n if (rawValue.length > valueSizeLimit || hasForbiddenCharacters(rawValue)) {\n display.warn(\n `${key} value doesn't meet tag requirements and will be sanitized. ${MORE_DETAILS} ${DOCS_ORIGIN}/getting_started/tagging/#defining-tags`\n )\n }\n\n // Let the backend do most of the sanitization, but still make sure multiple tags can't be crafted\n // by forging a value containing commas.\n const sanitizedValue = rawValue.replace(/,/g, '_')\n\n return `${key}:${sanitizedValue}`\n}\n\nfunction hasForbiddenCharacters(rawValue: string) {\n // Unicode property escapes is not supported in all browsers, so we use a try/catch.\n // Todo: Remove the try/catch when dropping support for Chrome 63 and Firefox 67\n // see: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Regular_expressions/Unicode_character_class_escape#browser_compatibility\n if (!supportUnicodePropertyEscapes()) {\n return false\n }\n\n // We use the Unicode property escapes to match any character that is a letter including other languages like Chinese, Japanese, etc.\n // p{Ll} matches a lowercase letter.\n // p{Lo} matches a letter that is neither uppercase nor lowercase (ex: Japanese characters).\n // See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Regular_expressions/Unicode_character_class_escape#unicode_property_escapes_vs._character_classes\n return new RegExp('[^\\\\p{Ll}\\\\p{Lo}0-9_:./-]', 'u').test(rawValue)\n}\n\nexport function supportUnicodePropertyEscapes() {\n try {\n new RegExp('[\\\\p{Ll}]', 'u')\n return true\n } catch {\n return false\n }\n}\n", "import type { InitConfiguration } from './configuration'\nimport type { EndpointBuilder } from './endpointBuilder'\nimport { createEndpointBuilder } from './endpointBuilder'\nimport { buildTags } from './tags'\nimport type { Site } from './intakeSites'\nimport { INTAKE_SITE_US1, INTAKE_URL_PARAMETERS } from './intakeSites'\n\nexport interface TransportConfiguration {\n logsEndpointBuilder: EndpointBuilder\n rumEndpointBuilder: EndpointBuilder\n sessionReplayEndpointBuilder: EndpointBuilder\n replica?: ReplicaConfiguration\n site: Site\n}\n\nexport interface ReplicaConfiguration {\n applicationId?: string\n logsEndpointBuilder: EndpointBuilder\n rumEndpointBuilder: EndpointBuilder\n}\n\nexport function computeTransportConfiguration(initConfiguration: InitConfiguration): TransportConfiguration {\n const site = initConfiguration.site || INTAKE_SITE_US1\n\n const tags = buildTags(initConfiguration)\n\n const endpointBuilders = computeEndpointBuilders(initConfiguration, tags)\n const replicaConfiguration = computeReplicaConfiguration(initConfiguration, tags)\n\n return {\n replica: replicaConfiguration,\n site,\n ...endpointBuilders,\n }\n}\n\nfunction computeEndpointBuilders(initConfiguration: InitConfiguration, tags: string[]) {\n return {\n logsEndpointBuilder: createEndpointBuilder(initConfiguration, 'logs', tags),\n rumEndpointBuilder: createEndpointBuilder(initConfiguration, 'rum', tags),\n sessionReplayEndpointBuilder: createEndpointBuilder(initConfiguration, 'replay', tags),\n }\n}\n\nfunction computeReplicaConfiguration(\n initConfiguration: InitConfiguration,\n tags: string[]\n): ReplicaConfiguration | undefined {\n if (!initConfiguration.replica) {\n return\n }\n\n const replicaConfiguration: InitConfiguration = {\n ...initConfiguration,\n site: INTAKE_SITE_US1,\n clientToken: initConfiguration.replica.clientToken,\n }\n\n const replicaEndpointBuilders = {\n logsEndpointBuilder: createEndpointBuilder(replicaConfiguration, 'logs', tags),\n rumEndpointBuilder: createEndpointBuilder(replicaConfiguration, 'rum', tags),\n }\n\n return { applicationId: initConfiguration.replica.applicationId, ...replicaEndpointBuilders }\n}\n\nexport function isIntakeUrl(url: string): boolean {\n // check if tags is present in the query string\n return INTAKE_URL_PARAMETERS.every((param) => url.includes(param))\n}\n", "import { catchUserErrors } from '../../tools/catchUserErrors'\nimport { DOCS_ORIGIN, MORE_DETAILS, display } from '../../tools/display'\nimport type { RawTelemetryConfiguration } from '../telemetry'\nimport type { Duration } from '../../tools/utils/timeUtils'\nimport { ONE_SECOND } from '../../tools/utils/timeUtils'\nimport { isPercentage } from '../../tools/utils/numberUtils'\nimport { ONE_KIBI_BYTE } from '../../tools/utils/byteUtils'\nimport { objectHasValue } from '../../tools/utils/objectUtils'\nimport { selectSessionStoreStrategyType } from '../session/sessionStore'\nimport type { SessionStoreStrategyType } from '../session/storeStrategies/sessionStoreStrategy'\nimport { TrackingConsent } from '../trackingConsent'\nimport type { SessionPersistence } from '../session/sessionConstants'\nimport type { TransportConfiguration } from './transportConfiguration'\nimport { computeTransportConfiguration } from './transportConfiguration'\nimport type { Site } from './intakeSites'\n\nexport const DefaultPrivacyLevel = {\n ALLOW: 'allow',\n MASK: 'mask',\n MASK_USER_INPUT: 'mask-user-input',\n} as const\nexport type DefaultPrivacyLevel = (typeof DefaultPrivacyLevel)[keyof typeof DefaultPrivacyLevel]\n\nexport const TraceContextInjection = {\n ALL: 'all',\n SAMPLED: 'sampled',\n} as const\n\nexport type TraceContextInjection = (typeof TraceContextInjection)[keyof typeof TraceContextInjection]\n\nexport interface InitConfiguration {\n /**\n * The client token for Datadog. Required for authenticating your application with Datadog.\n */\n clientToken: string\n beforeSend?: GenericBeforeSendCallback | undefined\n /**\n * The percentage of sessions tracked. A value between 0 and 100.\n * @default 100\n */\n sessionSampleRate?: number | undefined\n /**\n * The percentage of telemetry events sent. A value between 0 and 100.\n * @default 20\n */\n telemetrySampleRate?: number | undefined\n /**\n * Initialization fails silently if the RUM Browser SDK is already initialized on the page.\n * @default false\n */\n silentMultipleInit?: boolean | undefined\n\n /**\n * Which storage strategy to use for persisting sessions. Can be either 'cookie' or 'local-storage'.\n * @default \"cookie\"\n */\n sessionPersistence?: SessionPersistence | undefined\n\n /**\n * Allows the use of localStorage when cookies cannot be set. This enables the RUM Browser SDK to run in environments that do not provide cookie support.\n * See [Monitor Electron Applications Using the Browser SDK](https://docs.datadoghq.com/real_user_monitoring/guide/monitor-electron-applications-using-browser-sdk) for further information.\n * @deprecated use `sessionPersistence: local-storage` where you want to use localStorage instead\n */\n allowFallbackToLocalStorage?: boolean | undefined\n\n /**\n * Allow listening to DOM events dispatched programmatically ([untrusted events](https://developer.mozilla.org/en-US/docs/Web/API/Event/isTrusted)). Enabling this option can be useful if you heavily rely on programmatic events, such as in an automated UI test environment.\n * @default false\n */\n allowUntrustedEvents?: boolean | undefined\n /**\n * Store global context and user context in localStorage to preserve them along the user navigation.\n * See [Contexts life cycle](https://docs.datadoghq.com/real_user_monitoring/browser/advanced_configuration/?tab=npm#contexts-life-cycle) for further information.\n * @default false\n */\n storeContextsAcrossPages?: boolean | undefined\n /**\n * Set the initial user tracking consent state.\n * See [User tracking consent](https://docs.datadoghq.com/real_user_monitoring/browser/advanced_configuration/?tab=npm#user-tracking-consent) for further information.\n * @default granted\n */\n trackingConsent?: TrackingConsent | undefined\n\n // transport options\n /**\n * Optional proxy URL, for example: https://www.proxy.com/path.\n * See [Proxy Your Browser RUM Data](https://docs.datadoghq.com/real_user_monitoring/guide/proxy-rum-data) for further information.\n */\n proxy?: string | ProxyFn | undefined\n /**\n * The Datadog [site](https://docs.datadoghq.com/getting_started/site) parameter of your organization.\n * @default datadoghq.com\n */\n site?: Site | undefined\n\n // tag and context options\n /**\n * The service name for your application. Follows the [tag syntax requirements](https://docs.datadoghq.com/getting_started/tagging/#define-tags).\n */\n service?: string | undefined | null\n /**\n * The application\u2019s environment, for example: prod, pre-prod, and staging. Follows the [tag syntax requirements](https://docs.datadoghq.com/getting_started/tagging/#define-tags).\n */\n env?: string | undefined | null\n /**\n * The application\u2019s version, for example: 1.2.3, 6c44da20, and 2020.02.13. Follows the [tag syntax requirements](https://docs.datadoghq.com/getting_started/tagging/#define-tags).\n */\n version?: string | undefined | null\n\n // cookie options\n /**\n * Use a partitioned secure cross-site session cookie. This allows the RUM Browser SDK to run when the site is loaded from another one (iframe). Implies `useSecureSessionCookie`.\n * @default false\n */\n usePartitionedCrossSiteSessionCookie?: boolean | undefined\n /**\n * Use a secure session cookie. This disables RUM events sent on insecure (non-HTTPS) connections.\n * @default false\n */\n useSecureSessionCookie?: boolean | undefined\n /**\n * Preserve the session across subdomains for the same site.\n * @default false\n */\n trackSessionAcrossSubdomains?: boolean | undefined\n /**\n * Track anonymous user for the same site and extend cookie expiration date\n * @default true\n */\n trackAnonymousUser?: boolean | undefined\n // internal options\n /**\n * [Internal option] Enable experimental features\n */\n enableExperimentalFeatures?: string[] | undefined\n /**\n * [Internal option] Configure the dual chipping to another datacenter\n */\n replica?: ReplicaUserConfiguration | undefined\n /**\n * [Internal option] Set the datacenter from where the data is dual chipped\n */\n datacenter?: string\n /**\n * [Internal option] Datadog internal analytics subdomain\n */\n // TODO next major: remove this option and replace usages by proxyFn\n internalAnalyticsSubdomain?: string\n /**\n * [Internal option] The percentage of telemetry configuration sent. A value between 0 and 100.\n * @default 5\n */\n telemetryConfigurationSampleRate?: number\n /**\n * [Internal option] The percentage of telemetry usage sent. A value between 0 and 100.\n * @default 5\n */\n telemetryUsageSampleRate?: number\n}\n\n// This type is only used to build the core configuration. Logs and RUM SDKs are using a proper type\n// for this option.\ntype GenericBeforeSendCallback = (event: any, context?: any) => unknown\n\n/**\n * path: /api/vX/product\n * parameters: xxx=yyy&zzz=aaa\n */\ntype ProxyFn = (options: { path: string; parameters: string }) => string\n\nexport interface ReplicaUserConfiguration {\n applicationId?: string\n clientToken: string\n}\n\nexport interface Configuration extends TransportConfiguration {\n // Built from init configuration\n beforeSend: GenericBeforeSendCallback | undefined\n sessionStoreStrategyType: SessionStoreStrategyType | undefined\n sessionSampleRate: number\n telemetrySampleRate: number\n telemetryConfigurationSampleRate: number\n telemetryUsageSampleRate: number\n service: string | undefined\n silentMultipleInit: boolean\n allowUntrustedEvents: boolean\n trackingConsent: TrackingConsent\n storeContextsAcrossPages: boolean\n trackAnonymousUser?: boolean\n\n // Event limits\n eventRateLimiterThreshold: number // Limit the maximum number of actions, errors and logs per minutes\n maxTelemetryEventsPerPage: number\n\n // Batch configuration\n batchBytesLimit: number\n flushTimeout: Duration\n batchMessagesLimit: number\n messageBytesLimit: number\n}\n\nfunction isString(tag: unknown, tagName: string): tag is string | undefined | null {\n if (tag !== undefined && tag !== null && typeof tag !== 'string') {\n display.error(`${tagName} must be defined as a string`)\n return false\n }\n return true\n}\n\nfunction isDatadogSite(site: unknown) {\n if (site && typeof site === 'string' && !/(datadog|ddog|datad0g|dd0g)/.test(site)) {\n display.error(`Site should be a valid Datadog site. ${MORE_DETAILS} ${DOCS_ORIGIN}/getting_started/site/.`)\n return false\n }\n return true\n}\n\nexport function isSampleRate(sampleRate: unknown, name: string) {\n if (sampleRate !== undefined && !isPercentage(sampleRate)) {\n display.error(`${name} Sample Rate should be a number between 0 and 100`)\n return false\n }\n return true\n}\n\nexport function validateAndBuildConfiguration(initConfiguration: InitConfiguration): Configuration | undefined {\n if (!initConfiguration || !initConfiguration.clientToken) {\n display.error('Client Token is not configured, we will not send any data.')\n return\n }\n\n if (\n !isDatadogSite(initConfiguration.site) ||\n !isSampleRate(initConfiguration.sessionSampleRate, 'Session') ||\n !isSampleRate(initConfiguration.telemetrySampleRate, 'Telemetry') ||\n !isSampleRate(initConfiguration.telemetryConfigurationSampleRate, 'Telemetry Configuration') ||\n !isSampleRate(initConfiguration.telemetryUsageSampleRate, 'Telemetry Usage') ||\n !isString(initConfiguration.version, 'Version') ||\n !isString(initConfiguration.env, 'Env') ||\n !isString(initConfiguration.service, 'Service')\n ) {\n return\n }\n\n if (\n initConfiguration.trackingConsent !== undefined &&\n !objectHasValue(TrackingConsent, initConfiguration.trackingConsent)\n ) {\n display.error('Tracking Consent should be either \"granted\" or \"not-granted\"')\n return\n }\n\n return {\n beforeSend:\n initConfiguration.beforeSend && catchUserErrors(initConfiguration.beforeSend, 'beforeSend threw an error:'),\n sessionStoreStrategyType: selectSessionStoreStrategyType(initConfiguration),\n sessionSampleRate: initConfiguration.sessionSampleRate ?? 100,\n telemetrySampleRate: initConfiguration.telemetrySampleRate ?? 20,\n telemetryConfigurationSampleRate: initConfiguration.telemetryConfigurationSampleRate ?? 5,\n telemetryUsageSampleRate: initConfiguration.telemetryUsageSampleRate ?? 5,\n service: initConfiguration.service || undefined,\n silentMultipleInit: !!initConfiguration.silentMultipleInit,\n allowUntrustedEvents: !!initConfiguration.allowUntrustedEvents,\n trackingConsent: initConfiguration.trackingConsent ?? TrackingConsent.GRANTED,\n trackAnonymousUser: initConfiguration.trackAnonymousUser ?? true,\n storeContextsAcrossPages: !!initConfiguration.storeContextsAcrossPages,\n /**\n * beacon payload max queue size implementation is 64kb\n * ensure that we leave room for logs, rum and potential other users\n */\n batchBytesLimit: 16 * ONE_KIBI_BYTE,\n\n eventRateLimiterThreshold: 3000,\n maxTelemetryEventsPerPage: 15,\n\n /**\n * flush automatically, aim to be lower than ALB connection timeout\n * to maximize connection reuse.\n */\n flushTimeout: (30 * ONE_SECOND) as Duration,\n\n /**\n * Logs intake limit\n */\n batchMessagesLimit: 50,\n messageBytesLimit: 256 * ONE_KIBI_BYTE,\n ...computeTransportConfiguration(initConfiguration),\n }\n}\n\nexport function serializeConfiguration(initConfiguration: InitConfiguration) {\n return {\n session_sample_rate: initConfiguration.sessionSampleRate,\n telemetry_sample_rate: initConfiguration.telemetrySampleRate,\n telemetry_configuration_sample_rate: initConfiguration.telemetryConfigurationSampleRate,\n telemetry_usage_sample_rate: initConfiguration.telemetryUsageSampleRate,\n use_before_send: !!initConfiguration.beforeSend,\n use_partitioned_cross_site_session_cookie: initConfiguration.usePartitionedCrossSiteSessionCookie,\n use_secure_session_cookie: initConfiguration.useSecureSessionCookie,\n use_proxy: !!initConfiguration.proxy,\n silent_multiple_init: initConfiguration.silentMultipleInit,\n track_session_across_subdomains: initConfiguration.trackSessionAcrossSubdomains,\n track_anonymous_user: initConfiguration.trackAnonymousUser,\n session_persistence: initConfiguration.sessionPersistence,\n allow_fallback_to_local_storage: !!initConfiguration.allowFallbackToLocalStorage,\n store_contexts_across_pages: !!initConfiguration.storeContextsAcrossPages,\n allow_untrusted_events: !!initConfiguration.allowUntrustedEvents,\n tracking_consent: initConfiguration.trackingConsent,\n } satisfies RawTelemetryConfiguration\n}\n", "/**\n * LIMITATION:\n * For NPM setup, this feature flag singleton is shared between RUM and Logs product.\n * This means that an experimental flag set on the RUM product will be set on the Logs product.\n * So keep in mind that in certain configurations, your experimental feature flag may affect other products.\n *\n * FORMAT:\n * All feature flags should be snake_cased\n */\n// We want to use a real enum (i.e. not a const enum) here, to be able to check whether an arbitrary\n// string is an expected feature flag\n\nimport { objectHasValue } from './utils/objectUtils'\n\n// eslint-disable-next-line no-restricted-syntax\nexport enum ExperimentalFeature {\n WRITABLE_RESOURCE_GRAPHQL = 'writable_resource_graphql',\n CONSISTENT_TRACE_SAMPLING = 'consistent_trace_sampling',\n MISSING_URL_CONTEXT_TELEMETRY = 'missing_url_context_telemetry',\n}\n\nconst enabledExperimentalFeatures: Set = new Set()\n\nexport function initFeatureFlags(enableExperimentalFeatures: string[] | undefined) {\n if (Array.isArray(enableExperimentalFeatures)) {\n addExperimentalFeatures(\n enableExperimentalFeatures.filter((flag): flag is ExperimentalFeature =>\n objectHasValue(ExperimentalFeature, flag)\n )\n )\n }\n}\n\nexport function addExperimentalFeatures(enabledFeatures: ExperimentalFeature[]): void {\n enabledFeatures.forEach((flag) => {\n enabledExperimentalFeatures.add(flag)\n })\n}\n\nexport function isExperimentalFeatureEnabled(featureName: ExperimentalFeature): boolean {\n return enabledExperimentalFeatures.has(featureName)\n}\n\nexport function resetExperimentalFeatures(): void {\n enabledExperimentalFeatures.clear()\n}\n\nexport function getExperimentalFeatures(): Set {\n return enabledExperimentalFeatures\n}\n", "/**\n * Cross-browser stack trace computation.\n *\n * Reference implementation: https://github.com/csnover/TraceKit/blob/04530298073c3823de72deb0b97e7b38ca7bcb59/tracekit.js\n */\n\nexport interface StackFrame {\n url?: string\n func?: string\n /** The arguments passed to the function, if known. */\n args?: string[]\n line?: number\n column?: number\n /** An array of source code lines; the middle element corresponds to the correct line. */\n context?: string[]\n}\n\nexport interface StackTrace {\n name?: string\n message?: string\n url?: string\n stack: StackFrame[]\n incomplete?: boolean\n partial?: boolean\n}\n\nconst UNKNOWN_FUNCTION = '?'\n\nexport function computeStackTrace(ex: unknown): StackTrace {\n const stack: StackFrame[] = []\n\n let stackProperty = tryToGetString(ex, 'stack')\n const exString = String(ex)\n if (stackProperty && stackProperty.startsWith(exString)) {\n stackProperty = stackProperty.slice(exString.length)\n }\n if (stackProperty) {\n stackProperty.split('\\n').forEach((line) => {\n const stackFrame =\n parseChromeLine(line) || parseChromeAnonymousLine(line) || parseWinLine(line) || parseGeckoLine(line)\n if (stackFrame) {\n if (!stackFrame.func && stackFrame.line) {\n stackFrame.func = UNKNOWN_FUNCTION\n }\n\n stack.push(stackFrame)\n }\n })\n }\n\n return {\n message: tryToGetString(ex, 'message'),\n name: tryToGetString(ex, 'name'),\n stack,\n }\n}\nconst fileUrl =\n '((?:file|https?|blob|chrome-extension|electron|native|eval|webpack|snippet||\\\\w+\\\\.|\\\\/).*?)'\nconst filePosition = '(?::(\\\\d+))'\nconst CHROME_LINE_RE = new RegExp(`^\\\\s*at (.*?) ?\\\\(${fileUrl}${filePosition}?${filePosition}?\\\\)?\\\\s*$`, 'i')\n\nconst CHROME_EVAL_RE = new RegExp(`\\\\((\\\\S*)${filePosition}${filePosition}\\\\)`)\n\nfunction parseChromeLine(line: string): StackFrame | undefined {\n const parts = CHROME_LINE_RE.exec(line)\n\n if (!parts) {\n return\n }\n\n const isNative = parts[2] && parts[2].indexOf('native') === 0 // start of line\n const isEval = parts[2] && parts[2].indexOf('eval') === 0 // start of line\n const submatch = CHROME_EVAL_RE.exec(parts[2])\n\n if (isEval && submatch) {\n // throw out eval line/column and use top-most line/column number\n parts[2] = submatch[1] // url\n parts[3] = submatch[2] // line\n parts[4] = submatch[3] // column\n }\n\n return {\n args: isNative ? [parts[2]] : [],\n column: parts[4] ? +parts[4] : undefined,\n func: parts[1] || UNKNOWN_FUNCTION,\n line: parts[3] ? +parts[3] : undefined,\n url: !isNative ? parts[2] : undefined,\n }\n}\n\nconst CHROME_ANONYMOUS_FUNCTION_RE = new RegExp(`^\\\\s*at ?${fileUrl}${filePosition}?${filePosition}??\\\\s*$`, 'i')\n\nfunction parseChromeAnonymousLine(line: string): StackFrame | undefined {\n const parts = CHROME_ANONYMOUS_FUNCTION_RE.exec(line)\n\n if (!parts) {\n return\n }\n\n return {\n args: [],\n column: parts[3] ? +parts[3] : undefined,\n func: UNKNOWN_FUNCTION,\n line: parts[2] ? +parts[2] : undefined,\n url: parts[1],\n }\n}\n\nconst WINJS_LINE_RE =\n /^\\s*at (?:((?:\\[object object\\])?.+) )?\\(?((?:file|ms-appx|https?|webpack|blob):.*?):(\\d+)(?::(\\d+))?\\)?\\s*$/i\n\nfunction parseWinLine(line: string): StackFrame | undefined {\n const parts = WINJS_LINE_RE.exec(line)\n if (!parts) {\n return\n }\n\n return {\n args: [],\n column: parts[4] ? +parts[4] : undefined,\n func: parts[1] || UNKNOWN_FUNCTION,\n line: +parts[3],\n url: parts[2],\n }\n}\n\nconst GECKO_LINE_RE =\n /^\\s*(.*?)(?:\\((.*?)\\))?(?:^|@)((?:file|https?|blob|chrome|webpack|resource|capacitor|\\[native).*?|[^@]*bundle)(?::(\\d+))?(?::(\\d+))?\\s*$/i\nconst GECKO_EVAL_RE = /(\\S+) line (\\d+)(?: > eval line \\d+)* > eval/i\n\nfunction parseGeckoLine(line: string): StackFrame | undefined {\n const parts = GECKO_LINE_RE.exec(line)\n if (!parts) {\n return\n }\n\n const isEval = parts[3] && parts[3].indexOf(' > eval') > -1\n const submatch = GECKO_EVAL_RE.exec(parts[3])\n\n if (isEval && submatch) {\n // throw out eval line/column and use top-most line number\n parts[3] = submatch[1]\n parts[4] = submatch[2]\n parts[5] = undefined! // no column when eval\n }\n\n return {\n args: parts[2] ? parts[2].split(',') : [],\n column: parts[5] ? +parts[5] : undefined,\n func: parts[1] || UNKNOWN_FUNCTION,\n line: parts[4] ? +parts[4] : undefined,\n url: parts[3],\n }\n}\n\nfunction tryToGetString(candidate: unknown, property: string) {\n if (typeof candidate !== 'object' || !candidate || !(property in candidate)) {\n return undefined\n }\n const value = (candidate as { [k: string]: unknown })[property]\n return typeof value === 'string' ? value : undefined\n}\n\nexport function computeStackTraceFromOnErrorMessage(messageObj: unknown, url?: string, line?: number, column?: number) {\n const stack = [{ url, column, line }]\n const { name, message } = tryToParseMessage(messageObj)\n return {\n name,\n message,\n stack,\n }\n}\n\n// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error#Error_types\nconst ERROR_TYPES_RE =\n /^(?:[Uu]ncaught (?:exception: )?)?(?:((?:Eval|Internal|Range|Reference|Syntax|Type|URI|)Error): )?([\\s\\S]*)$/\n\nfunction tryToParseMessage(messageObj: unknown) {\n let name\n let message\n if ({}.toString.call(messageObj) === '[object String]') {\n ;[, name, message] = ERROR_TYPES_RE.exec(messageObj as string)!\n }\n return { name, message }\n}\n", "import { callMonitored } from '../monitor'\nimport type { StackTrace } from './computeStackTrace'\nimport { computeStackTrace } from './computeStackTrace'\n\n/**\n * Creates a stacktrace without SDK internal frames.\n * Constraints:\n * - Has to be called at the utmost position of the call stack.\n * - No monitored function should encapsulate it, that is why we need to use callMonitored inside it.\n */\nexport function createHandlingStack(): string {\n /**\n * Skip the two internal frames:\n * - SDK API (console.error, ...)\n * - this function\n * in order to keep only the user calls\n */\n const internalFramesToSkip = 2\n const error = new Error()\n let formattedStack: string\n\n callMonitored(() => {\n const stackTrace = computeStackTrace(error)\n stackTrace.stack = stackTrace.stack.slice(internalFramesToSkip)\n formattedStack = toStackTraceString(stackTrace)\n })\n\n return formattedStack!\n}\n\nexport function toStackTraceString(stack: StackTrace) {\n let result = formatErrorMessage(stack)\n stack.stack.forEach((frame) => {\n const func = frame.func === '?' ? '' : frame.func\n const args = frame.args && frame.args.length > 0 ? `(${frame.args.join(', ')})` : ''\n const line = frame.line ? `:${frame.line}` : ''\n const column = frame.line && frame.column ? `:${frame.column}` : ''\n result += `\\n at ${func!}${args} @ ${frame.url!}${line}${column}`\n })\n return result\n}\n\nexport function formatErrorMessage(stack: StackTrace) {\n return `${stack.name || 'Error'}: ${stack.message!}`\n}\n", "import { setTimeout } from './timer'\nimport { callMonitored } from './monitor'\nimport { noop } from './utils/functionUtils'\nimport { createHandlingStack } from './stackTrace/handlingStack'\n\n/**\n * Object passed to the callback of an instrumented method call. See `instrumentMethod` for more\n * info.\n */\nexport type InstrumentedMethodCall = {\n /**\n * The target object on which the method was called.\n */\n target: TARGET\n\n /**\n * The parameters with which the method was called.\n *\n * Note: if needed, parameters can be mutated by the instrumentation\n */\n parameters: Parameters\n\n /**\n * Registers a callback that will be called after the original method is called, with the method\n * result passed as argument.\n */\n onPostCall: (callback: PostCallCallback) => void\n\n /**\n * The stack trace of the method call.\n */\n handlingStack?: string\n}\n\ntype PostCallCallback = (\n result: ReturnType\n) => void\n\n/**\n * Instruments a method on a object, calling the given callback before the original method is\n * invoked. The callback receives an object with information about the method call.\n *\n * This function makes sure that we are \"good citizens\" regarding third party instrumentations: when\n * removing the instrumentation, the original method is usually restored, but if a third party\n * instrumentation was set after ours, we keep it in place and just replace our instrumentation with\n * a noop.\n *\n * Note: it is generally better to instrument methods that are \"owned\" by the object instead of ones\n * that are inherited from the prototype chain. Example:\n * * do: `instrumentMethod(Array.prototype, 'push', ...)`\n * * don't: `instrumentMethod([], 'push', ...)`\n *\n * This method is also used to set event handler properties (ex: window.onerror = ...), as it has\n * the same requirements as instrumenting a method:\n * * if the event handler is already set by a third party, we need to call it and not just blindly\n * override it.\n * * if the event handler is set by a third party after us, we need to keep it in place when\n * removing ours.\n *\n * @example\n *\n * instrumentMethod(window, 'fetch', ({ target, parameters, onPostCall }) => {\n * console.log('Before calling fetch on', target, 'with parameters', parameters)\n *\n * onPostCall((result) => {\n * console.log('After fetch calling on', target, 'with parameters', parameters, 'and result', result)\n * })\n * })\n */\nexport function instrumentMethod(\n targetPrototype: TARGET,\n method: METHOD,\n onPreCall: (this: null, callInfos: InstrumentedMethodCall) => void,\n { computeHandlingStack }: { computeHandlingStack?: boolean } = {}\n) {\n let original = targetPrototype[method]\n\n if (typeof original !== 'function') {\n if (method in targetPrototype && method.startsWith('on')) {\n original = noop as TARGET[METHOD]\n } else {\n return { stop: noop }\n }\n }\n\n let stopped = false\n\n const instrumentation = function (this: TARGET): ReturnType | undefined {\n if (stopped) {\n // eslint-disable-next-line @typescript-eslint/no-unsafe-return, @typescript-eslint/no-unsafe-call\n return original.apply(this, arguments as unknown as Parameters)\n }\n\n const parameters = Array.from(arguments) as Parameters\n\n let postCallCallback: PostCallCallback | undefined\n\n callMonitored(onPreCall, null, [\n {\n target: this,\n parameters,\n onPostCall: (callback) => {\n postCallCallback = callback\n },\n handlingStack: computeHandlingStack ? createHandlingStack() : undefined,\n },\n ])\n\n // eslint-disable-next-line @typescript-eslint/no-unsafe-call\n const result = original.apply(this, parameters)\n\n if (postCallCallback) {\n callMonitored(postCallCallback, null, [result])\n }\n\n // eslint-disable-next-line @typescript-eslint/no-unsafe-return\n return result\n }\n\n targetPrototype[method] = instrumentation as TARGET[METHOD]\n\n return {\n stop: () => {\n stopped = true\n // If the instrumentation has been removed by a third party, keep the last one\n if (targetPrototype[method] === instrumentation) {\n targetPrototype[method] = original\n }\n },\n }\n}\n\nexport function instrumentSetter(\n targetPrototype: TARGET,\n property: PROPERTY,\n after: (target: TARGET, value: TARGET[PROPERTY]) => void\n) {\n const originalDescriptor = Object.getOwnPropertyDescriptor(targetPrototype, property)\n if (!originalDescriptor || !originalDescriptor.set || !originalDescriptor.configurable) {\n return { stop: noop }\n }\n\n const stoppedInstrumentation = noop\n let instrumentation = (target: TARGET, value: TARGET[PROPERTY]) => {\n // put hooked setter into event loop to avoid of set latency\n setTimeout(() => {\n if (instrumentation !== stoppedInstrumentation) {\n after(target, value)\n }\n }, 0)\n }\n\n const instrumentationWrapper = function (this: TARGET, value: TARGET[PROPERTY]) {\n originalDescriptor.set!.call(this, value)\n instrumentation(this, value)\n }\n\n Object.defineProperty(targetPrototype, property, {\n set: instrumentationWrapper,\n })\n\n return {\n stop: () => {\n if (Object.getOwnPropertyDescriptor(targetPrototype, property)?.set === instrumentationWrapper) {\n Object.defineProperty(targetPrototype, property, originalDescriptor)\n }\n instrumentation = stoppedInstrumentation\n },\n }\n}\n", "import { display } from '../display'\nimport { ONE_KIBI_BYTE } from '../utils/byteUtils'\nimport type { Context, ContextArray, ContextValue } from './context'\nimport type { ObjectWithToJsonMethod } from './jsonStringify'\nimport { detachToJsonMethod } from './jsonStringify'\n\n// eslint-disable-next-line @typescript-eslint/no-unsafe-function-type\ntype PrimitivesAndFunctions = string | number | boolean | undefined | null | symbol | bigint | Function\ntype ExtendedContextValue = PrimitivesAndFunctions | object | ExtendedContext | ExtendedContextArray\ntype ExtendedContext = { [key: string]: ExtendedContextValue }\ntype ExtendedContextArray = ExtendedContextValue[]\n\ntype ContainerElementToProcess = {\n source: ExtendedContextArray | ExtendedContext\n target: ContextArray | Context\n path: string\n}\n\ntype SanitizedEvent = {\n type: string\n isTrusted: boolean\n currentTarget: string | null | undefined\n target: string | null | undefined\n}\n\n// The maximum size of a single event is 256KiB. By default, we ensure that user-provided data\n// going through sanitize fits inside our events, while leaving room for other contexts, metadata, ...\nconst SANITIZE_DEFAULT_MAX_CHARACTER_COUNT = 220 * ONE_KIBI_BYTE\n\n// Symbol for the root element of the JSONPath used for visited objects\nconst JSON_PATH_ROOT_ELEMENT = '$'\n\n// When serializing (using JSON.stringify) a key of an object, { key: 42 } gets wrapped in quotes as \"key\".\n// With the separator (:), we need to add 3 characters to the count.\nconst KEY_DECORATION_LENGTH = 3\n\n/**\n * Ensures user-provided data is 'safe' for the SDK\n * - Deep clones data\n * - Removes cyclic references\n * - Transforms unserializable types to a string representation\n *\n * LIMITATIONS:\n * - Size is in characters, not byte count (may differ according to character encoding)\n * - Size does not take into account indentation that can be applied to JSON.stringify\n * - Non-numerical properties of Arrays are ignored. Same behavior as JSON.stringify\n *\n * @param source User-provided data meant to be serialized using JSON.stringify\n * @param maxCharacterCount Maximum number of characters allowed in serialized form\n */\nexport function sanitize(source: string, maxCharacterCount?: number): string | undefined\nexport function sanitize(source: Context, maxCharacterCount?: number): Context\nexport function sanitize(source: unknown, maxCharacterCount?: number): ContextValue\nexport function sanitize(source: unknown, maxCharacterCount = SANITIZE_DEFAULT_MAX_CHARACTER_COUNT) {\n // Unbind any toJSON function we may have on [] or {} prototypes\n const restoreObjectPrototypeToJson = detachToJsonMethod(Object.prototype)\n const restoreArrayPrototypeToJson = detachToJsonMethod(Array.prototype)\n\n // Initial call to sanitizeProcessor - will populate containerQueue if source is an Array or a plain Object\n const containerQueue: ContainerElementToProcess[] = []\n const visitedObjectsWithPath = new WeakMap