UNPKG

@dash0hq/sdk-web

Version:

Dash0's Web SDK to collect telemetry from end-users' web browsers

1 lines 155 kB
{"version":3,"file":"dash0.umd.cjs","sources":["modules/utils/fn.js","modules/utils/debug.js","modules/utils/obj.js","modules/utils/globals.js","modules/utils/id.js","modules/utils/listeners.js","modules/utils/local-storage.js","modules/utils/time.js","modules/utils/timers.js","modules/utils/performance.js","modules/utils/constants.js","modules/utils/math.js","modules/utils/url.js","modules/utils/origin.js","modules/utils/wrap.js","modules/vars.js","modules/semantic-conventions.js","modules/api/session.js","../node_modules/.pnpm/web-vitals@4.2.4/node_modules/web-vitals/dist/web-vitals.js","modules/utils/on-last-chance.js","modules/transport/batcher.js","modules/transport/fetch.js","modules/transport/index.js","modules/utils/rate-limit.js","modules/utils/otel/attributes.js","modules/utils/otel/trace-context.js","modules/utils/otel/span.js","modules/utils/session-storage.js","modules/utils/tab-id.js","modules/attributes/common.js","modules/attributes/url.js","modules/instrumentations/web-vitals.js","modules/utils/ignore-rules.js","modules/instrumentations/errors/unhandled-error.js","modules/instrumentations/errors/unhandled-promise-rejection.js","modules/instrumentations/errors/async-function-wrapping.js","modules/instrumentations/errors/event-handlers.js","modules/instrumentations/errors/timers.js","modules/instrumentations/errors/index.js","modules/instrumentations/http/utils.js","modules/instrumentations/http/fetch.js","modules/utils/otel/http.js","modules/utils/otel/error.js","modules/instrumentations/navigation/event.js","modules/instrumentations/navigation/page-load.js","modules/instrumentations/navigation/page-transition.js","modules/instrumentations/navigation/index.js","../node_modules/.pnpm/ts-deepmerge@7.0.3/node_modules/ts-deepmerge/esm/index.js","modules/api/init.js","modules/utils/pick.js","modules/api/attributes.js","modules/api/debug.js","modules/api/identify.js","modules/entrypoint/npm-package.js","modules/api/report-error.js","modules/api/events.js"],"sourcesContent":["export function noop() {\n // This function is intentionally empty.\n}\n","import { noop } from \"./fn\";\nexport const log = createLogger(\"log\");\nexport const info = createLogger(\"info\");\nexport const warn = createLogger(\"warn\");\nexport const error = createLogger(\"error\");\nexport const debug = createLogger(\"debug\");\nfunction createLogger(method) {\n if (typeof console === \"undefined\" || typeof console.log !== \"function\" || typeof console.log.apply !== \"function\") {\n return noop;\n }\n if (console[method] && typeof console[method].apply === \"function\") {\n return function () {\n console[method].apply(console, arguments);\n };\n }\n return function () {\n console.log.apply(console, arguments);\n };\n}\n","const globalHasOwnProperty = Object.prototype.hasOwnProperty;\n/**\n * protection against hasOwnProperty overrides.\n */\nexport function hasOwnProperty(obj, key) {\n return globalHasOwnProperty.call(obj, key);\n}\nexport function hasKey(obj, key) {\n return key in obj;\n}\n","// aliasing globals for improved minification\n// Avoid blowing up in an ssr context. It is important to check via typeof here because window might not even be declared when imported in ssr.\nexport const win = typeof window !== \"undefined\" ? window : undefined;\nexport const doc = win?.document;\nexport const nav = win?.navigator;\nexport const loc = typeof location !== \"undefined\" ? location : undefined;\nexport const perf = win?.performance || win?.webkitPerformance || win?.msPerformance || win?.mozPerformance;\nexport const encodeURIComponent = win?.encodeURIComponent;\nexport const fetch = win?.fetch;\nexport const localStorage = (function () {\n try {\n return win?.localStorage ?? null;\n }\n catch {\n // localStorage access is not permitted in certain security modes, e.g.\n // when cookies are completely disabled in web browsers.\n return null;\n }\n})();\nexport const sessionStorage = (function () {\n try {\n return win?.sessionStorage ?? null;\n }\n catch {\n // sessionStorage access is not permitted in certain security modes, e.g.\n // when cookies are completely disabled in web browsers.\n return null;\n }\n})();\n/**\n * Exposed via this module to enable testing.\n */\nexport function sendBeacon(url, data) {\n return nav?.sendBeacon(url, data) ?? false;\n}\n/* eslint-enable no-restricted-globals */\n","export const SPAN_ID_BYTES = 8;\nexport const TRACE_ID_BYTES = 16;\nexport const PAGE_LOAD_ID_BYTES = TRACE_ID_BYTES;\nexport const SESSION_ID_BYTES = TRACE_ID_BYTES;\nexport const TAB_ID_BYTES = SPAN_ID_BYTES;\nconst SHARED_CHAR_CODES_ARRAY = Array(32);\nexport function generateUniqueId(byteCount) {\n for (let i = 0; i < byteCount * 2; i++) {\n SHARED_CHAR_CODES_ARRAY[i] = Math.floor(Math.random() * 16) + 48;\n // valid hex characters in the range 48-57 and 97-102\n if (SHARED_CHAR_CODES_ARRAY[i] >= 58) {\n SHARED_CHAR_CODES_ARRAY[i] += 39;\n }\n }\n return String.fromCharCode.apply(null, SHARED_CHAR_CODES_ARRAY.slice(0, byteCount * 2));\n}\n","// aliasing the global function for improved minification and\n// protection against hasOwnProperty overrides.\nexport function addEventListener(target, eventType, callback) {\n if (target.addEventListener) {\n target.addEventListener(eventType, callback, false);\n }\n else if (target.attachEvent) {\n target.attachEvent(\"on\" + eventType, callback);\n }\n}\nexport function removeEventListener(target, eventType, callback) {\n if (target.removeEventListener) {\n target.removeEventListener(eventType, callback, false);\n }\n else if (target.detachEvent) {\n target.detachEvent(\"on\" + eventType, callback);\n }\n}\n","// localStorage API re-exposed to allow testing.\nimport { localStorage } from \"./globals\";\nexport const isSupported = localStorage != null && typeof localStorage.getItem === \"function\" && typeof localStorage.setItem === \"function\";\nexport function getItem(k) {\n if (isSupported && localStorage) {\n return localStorage.getItem(k);\n }\n return null;\n}\nexport function setItem(k, v) {\n if (isSupported && localStorage) {\n localStorage.setItem(k, v);\n }\n}\nexport function removeItem(k) {\n if (isSupported && localStorage) {\n localStorage.removeItem(k);\n }\n}\n","import { perf } from \"./globals\";\nexport function now() {\n return new Date().getTime();\n}\nexport function nowNanos() {\n const timeOrigin = getTimeOrigin();\n if (timeOrigin) {\n return String((perf.now() + timeOrigin) * 1000000);\n }\n return toNanosTs(new Date());\n}\nexport function toNanosTs(time) {\n if (typeof time === \"object\") {\n return toNanosTs(time.getTime());\n }\n // We don't multiply, because we want to keep number precision\n return String(time) + \"000000\";\n}\nexport function getTimeOrigin() {\n let timeOrigin = perf?.timeOrigin;\n if (typeof timeOrigin !== \"number\") {\n timeOrigin = perf?.timing?.fetchStart;\n }\n return timeOrigin;\n}\nexport function domHRTimestampToNanos(ts) {\n return String(Math.round((ts + getTimeOrigin()) * 1000000));\n}\n","import { warn, debug } from \"./debug\";\nimport { win } from \"./globals\";\n// This module contains wrappers around the standard timer API. These wrappers can be used to\n// ensure that execution of timers happens outside of any Angular specific zones. This in turn\n// means that this script will never disturb Angular's stabilization phase.\n// https://angular.io/api/core/ApplicationRef#isStable\n// Please note that it may sometimes be necessary to deliberately execute code inside of\n// Angular's Zones. Always take care to make a deliberate decision when to use and when not to\n// use these wrappers.\n// We take a copy of all globals to ensure that no other script will change them all of a sudden.\n// This ensures that when we register a timeout/interval on one global, that we will be able to\n// de-register it again in all cases.\nconst globals = {\n setTimeout: win?.setTimeout,\n clearTimeout: win?.clearTimeout,\n setInterval: win?.setInterval,\n clearInterval: win?.clearInterval,\n};\n// If the globals don't exist at execution time of this file, then we know that the globals stored\n// above are not wrapped by Zone.js. This in turn can mean better performance for Angular users.\nexport const isRunningZoneJs = win != null && win[\"Zone\"] != null && win[\"Zone\"][\"root\"] != null && typeof win[\"Zone\"][\"root\"][\"run\"] === \"function\";\nif (isRunningZoneJs) {\n debug(\"Discovered Zone.js globals. Will attempt to register all timers inside the root Zone.\");\n}\nexport function setTimeout(..._) {\n return executeGlobally.apply(\"setTimeout\", arguments);\n}\nexport function clearTimeout(..._) {\n return executeGlobally.apply(\"clearTimeout\", arguments);\n}\nexport function setInterval(..._) {\n return executeGlobally.apply(\"setInterval\", arguments);\n}\nexport function clearInterval(..._) {\n return executeGlobally.apply(\"clearInterval\", arguments);\n}\nfunction executeGlobally() {\n // We don't want to incur a performance penalty for all users just because some\n // users are relying on zone.js. This API looks quite ridiculous, but it\n // allows for concise and efficient code, e.g. arguments does not need to be\n // translated into an array.\n // eslint-disable-next-line @typescript-eslint/no-this-alias\n const globalFunctionName = this;\n if (isRunningZoneJs) {\n try {\n // Incurr a performance overhead for Zone.js users that we just cannot avoid:\n // Copy the arguments passed in here so that we can use them inside the root\n // zone.\n const args = Array.prototype.slice.apply(arguments);\n // @ts-expect-error necessary Zone.js types not added\n return win[\"Zone\"][\"root\"][\"run\"](globals[globalFunctionName], win, args);\n }\n catch (e) {\n warn(\"Failed to execute %s inside of zone (via Zone.js). Falling back to execution inside currently \" +\n \"active zone.\", globalFunctionName, e);\n // failure – maybe zone js not properly initialized? Fall back to execution\n // outside of Zone.js as a last resort (outside of try/catch and if)\n }\n }\n // Note: Explicitly passing win as 'this' even though we are getting the function from 'globals'\n return globals[globalFunctionName]?.apply(win, arguments);\n}\n","import { doc, perf, win } from \"./globals\";\nimport { now } from \"./time\";\nimport { noop } from \"./fn\";\nimport { setTimeout } from \"./timers\";\nimport { addEventListener, removeEventListener } from \"./listeners\";\nconst TEN_MINUTES_IN_MILLIS = 1000 * 60 * 10;\nconst ONE_DAY_IN_MILLIS = 1000 * 60 * 60 * 24;\nexport const isResourceTimingAvailable = !!(perf && perf.getEntriesByType);\nexport const isPerformanceObserverAvailable = perf && typeof win[\"PerformanceObserver\"] === \"function\" && typeof perf[\"now\"] === \"function\";\nexport const PerformanceTimingNames = Object.freeze({\n CONNECT_END: \"connectEnd\",\n CONNECT_START: \"connectStart\",\n DECODED_BODY_SIZE: \"decodedBodySize\",\n DOM_COMPLETE: \"domComplete\",\n DOM_CONTENT_LOADED_EVENT_END: \"domContentLoadedEventEnd\",\n DOM_CONTENT_LOADED_EVENT_START: \"domContentLoadedEventStart\",\n DOM_INTERACTIVE: \"domInteractive\",\n DOMAIN_LOOKUP_END: \"domainLookupEnd\",\n DOMAIN_LOOKUP_START: \"domainLookupStart\",\n ENCODED_BODY_SIZE: \"encodedBodySize\",\n FETCH_START: \"fetchStart\",\n LOAD_EVENT_END: \"loadEventEnd\",\n LOAD_EVENT_START: \"loadEventStart\",\n NAVIGATION_START: \"navigationStart\",\n REDIRECT_END: \"redirectEnd\",\n REDIRECT_START: \"redirectStart\",\n REQUEST_START: \"requestStart\",\n RESPONSE_END: \"responseEnd\",\n RESPONSE_START: \"responseStart\",\n SECURE_CONNECTION_START: \"secureConnectionStart\",\n START_TIME: \"startTime\",\n UNLOAD_EVENT_END: \"unloadEventEnd\",\n UNLOAD_EVENT_START: \"unloadEventStart\",\n});\nexport function observeResourcePerformance(opts) {\n if (!isPerformanceObserverAvailable)\n return observeWithoutPerformanceObserverSupport(opts.onEnd);\n // Used to calculate the duration when no resource was found.\n let startTime;\n let endTime;\n let resource;\n // Global resources that will need to be disposed\n let observer;\n let fallbackNoResourceFoundTimerHandle;\n let fallbackEndNeverCalledTimerHandle;\n return {\n start: onStart,\n end: onEnd,\n cancel: disposeGlobalResources,\n };\n function onStart() {\n startTime = perf.now();\n try {\n const PerformanceObserver = win?.PerformanceObserver;\n if (PerformanceObserver) {\n observer = new PerformanceObserver(onResourceFound);\n observer?.observe({ type: \"resource\" });\n }\n }\n catch (_e) {\n // Some browsers may not support the passed entryTypes and decide to throw an error.\n // This would then result in an error with a message like:\n //\n // entryTypes only contained unsupported types\n //\n // Swallow and ignore the error. Treat it like unavailable performance observer data.\n }\n fallbackEndNeverCalledTimerHandle = setTimeout(disposeGlobalResources, TEN_MINUTES_IN_MILLIS);\n }\n function onEnd() {\n endTime = perf.now();\n cancelFallbackEndNeverCalledTimer();\n if (resource || !isWaitingAcceptable()) {\n end();\n }\n else {\n addEventListener(doc, \"visibilitychange\", onVisibilityChanged);\n fallbackNoResourceFoundTimerHandle = setTimeout(end, opts.maxWaitForResourceMillis);\n }\n }\n function end() {\n disposeGlobalResources();\n // In some old web browsers, e.g. Chrome 31, the value provided as the duration\n // can be very wrong. We have seen cases where this value is measured in years.\n // If this does seem be the case, then we will ignore the duration property and\n // instead prefer our approximation.\n if (resource?.duration && resource.duration < ONE_DAY_IN_MILLIS) {\n opts.onEnd({ resource, duration: Math.round(resource.duration) });\n }\n else {\n opts.onEnd({ resource, duration: Math.round(endTime - startTime) });\n }\n }\n function onResourceFound(list) {\n const r = list.getEntriesByType(\"resource\").find((e) => {\n // This polymorphism is not properly represented in the api types. The cast is safe since we're only accessing the resource timings.\n const entry = e;\n return (entry.startTime >= startTime &&\n (!endTime || endTime + opts.maxToleranceForResourceTimingsMillis >= entry.responseEnd) &&\n opts.resourceMatcher(entry));\n });\n if (!r)\n return;\n resource = r;\n if (endTime)\n end();\n }\n function onVisibilityChanged() {\n if (!isWaitingAcceptable())\n end();\n }\n function disposeGlobalResources() {\n disconnectResourceObserver();\n cancelFallbackNoResourceFoundTimer();\n cancelFallbackEndNeverCalledTimer();\n stopVisibilityObservation();\n }\n function disconnectResourceObserver() {\n if (observer) {\n try {\n observer?.disconnect();\n }\n catch (_e) {\n // Observer disconnect may throw when connect attempt wasn't successful. Ignore this.\n }\n observer = undefined;\n }\n }\n function cancelFallbackNoResourceFoundTimer() {\n if (fallbackNoResourceFoundTimerHandle) {\n clearTimeout(fallbackNoResourceFoundTimerHandle);\n fallbackNoResourceFoundTimerHandle = undefined;\n }\n }\n function cancelFallbackEndNeverCalledTimer() {\n if (fallbackEndNeverCalledTimerHandle) {\n clearTimeout(fallbackEndNeverCalledTimerHandle);\n fallbackEndNeverCalledTimerHandle = undefined;\n }\n }\n function stopVisibilityObservation() {\n if (!doc)\n return;\n removeEventListener(doc, \"visibilitychange\", onVisibilityChanged);\n }\n}\n// We may only wait for resource data to arrive as long as the document is visible or in the process\n// of becoming visible. In all other cases we might lose data when waiting, e.g. when the document\n// is in the process of being disposed.\nfunction isWaitingAcceptable() {\n return doc?.visibilityState === \"visible\" || doc?.visibilityState === \"prerender\";\n}\nfunction observeWithoutPerformanceObserverSupport(onEnd) {\n let start = 0;\n return {\n start: () => {\n start = now();\n },\n end: () => onEnd({ duration: now() - start }),\n cancel: noop,\n };\n}\n","export const NO_VALUE_FALLBACK = \"undefined\";\nexport const INIT_MESSAGE = \"Initializing Dash0 Web SDK\";\n","export function roundToTwoDecimals(n) {\n return Math.round(n * 100) / 100;\n}\n","import { doc, loc } from \"./globals\";\n/**\n * Parses url using URL constructor. Supports URL objects as passthrough input to simplify implementations\n * May throw if parsing fails\n * @param url\n */\nexport function parseUrl(url) {\n if (typeof url !== \"string\")\n return url;\n return new URL(url, doc?.baseURI ?? loc?.href);\n}\n","import { parseUrl } from \"./url\";\n/** Returns the origin if present (if in browser context). */\nfunction getOrigin() {\n return typeof location !== \"undefined\" ? location.origin : undefined;\n}\nexport function isSameOrigin(url) {\n try {\n const parsedUrl = parseUrl(url);\n return parsedUrl.origin === getOrigin();\n }\n catch (_e) {\n return false;\n }\n}\n","import { debug } from \"./debug\";\nconst INSTRUMENTED_BY_DASH0_SYMBOL = Symbol.for(\"INSTRUMENTED_BY_DASH0\");\nfunction isAlreadyInstrumented(objOrFunction) {\n // @ts-expect-error -- typescript does not know about this hidden marker and we're not going to tell it 🤫\n return objOrFunction[INSTRUMENTED_BY_DASH0_SYMBOL] === true;\n}\nfunction markAsInstrumented(objOrFunction) {\n // @ts-expect-error -- typescript does not know about this hidden marker and we're not going to tell it 🤫\n objOrFunction[INSTRUMENTED_BY_DASH0_SYMBOL] = true;\n}\nexport function wrap(module, target, wrapper) {\n const original = module[target];\n if (!original) {\n debug(`${String(target)} is not defined, unable to instrument`);\n return;\n }\n if (isAlreadyInstrumented(original)) {\n debug(`${String(target)} has already been instrumented, skipping`);\n }\n markAsInstrumented(original);\n module[target] = wrapper(original);\n}\n","// TODO use OTel types here?\nexport const vars = {\n endpoints: [],\n resource: {\n attributes: [],\n },\n scope: {\n name: \"dash0-web-sdk\",\n version: __sdkVersion,\n attributes: [],\n },\n signalAttributes: [],\n ignoreUrls: [],\n ignoreErrorMessages: [],\n wrapEventHandlers: true,\n wrapTimers: true,\n propagateTraceHeadersCorsURLs: [],\n maxWaitForResourceTimingsMillis: 10000,\n maxToleranceForResourceTimingsMillis: 3000,\n headersToCapture: [],\n pageViewInstrumentation: {\n trackVirtualPageViews: true,\n includeParts: [],\n },\n};\n","// Resource Attribute Keys\nexport const SERVICE_NAME = \"service.name\";\nexport const SERVICE_VERSION = \"service.version\";\nexport const DEPLOYMENT_ENVIRONMENT_NAME = \"deployment.environment.name\";\nexport const DEPLOYMENT_NAME = \"deployment.name\";\nexport const DEPLOYMENT_ID = \"deployment.id\";\n// Misc Signal Attribute Keys\nexport const EVENT_NAME = \"event.name\";\nexport const EVENT_TITLE = \"dash0.event.title\";\nexport const PAGE_LOAD_ID = \"page.load.id\";\nexport const SESSION_ID = \"session.id\";\nexport const USER_AGENT = \"user_agent.original\";\nexport const BROWSER_TAB_ID = \"browser.tab.id\";\nexport const WINDOW_WIDTH = \"browser.window.width\";\nexport const WINDOW_HEIGHT = \"browser.window.height\";\nexport const NETWORK_CONNECTION_TYPE = \"network.connection.subtype\";\nexport const EXCEPTION_COMPONENT_STACK = \"exception.component_stack\";\n// User Attribute Keys\nexport const USER_ID = \"user.id\";\nexport const USER_NAME = \"user.name\";\nexport const USER_FULL_NAME = \"user.full_name\";\nexport const USER_EMAIL = \"user.email\";\nexport const USER_HASH = \"user.hash\";\nexport const USER_ROLES = \"user.roles\";\n// Exception Attribute Keys\nexport const EXCEPTION_MESSAGE = \"exception.message\";\nexport const EXCEPTION_TYPE = \"exception.type\";\nexport const EXCEPTION_STACKTRACE = \"exception.stacktrace\";\n// URL Attribute Keys\nexport const URL_DOMAIN = \"url.domain\";\nexport const URL_FRAGMENT = \"url.fragment\";\nexport const URL_FULL = \"url.full\";\nexport const URL_PATH = \"url.path\";\nexport const URL_QUERY = \"url.query\";\nexport const URL_SCHEME = \"url.scheme\";\n// Http Attribute Keys\nexport const HTTP_REQUEST_METHOD = \"http.request.method\";\nexport const HTTP_REQUEST_METHOD_ORIGINAL = \"http.request.method_original\";\nexport const HTTP_REQUEST_HEADER = \"http.request.header\";\nexport const HTTP_RESPONSE_STATUS_CODE = \"http.response.status_code\";\nexport const HTTP_RESPONSE_HEADER = \"http.response.header\";\nexport const HTTP_RESPONSE_BODY_SIZE = \"http.response.body.size\";\n// Event Names\nexport const EVENT_NAMES = {\n PAGE_VIEW: \"browser.page_view\",\n NAVIGATION_TIMING: \"browser.navigation_timing\",\n WEB_VITAL: \"browser.web_vital\",\n ERROR: \"browser.error\",\n};\nexport const SPAN_EVENT_NAME_EXCEPTION = \"exception\";\n// Log Severities\nexport const LOG_SEVERITIES = {\n UNSPECIFIED: 0,\n TRACE: 1,\n DEBUG: 5,\n INFO: 9,\n WARN: 13,\n ERROR: 17,\n FATAL: 21,\n};\n// Page View Event Attributes\n// SEE: https://github.com/open-telemetry/semantic-conventions/pull/1910/files\nexport const PAGE_VIEW_TYPE = \"type\";\nexport const PAGE_VIEW_TYPE_VALUES = {\n INITIAL: 0,\n VIRTUAL: 1,\n};\nexport const PAGE_VIEW_CHANGE_STATE = \"change_state\";\nexport const PAGE_VIEW_CHANGE_STATE_VALUES = {\n PUSH: \"pushState\",\n REPLACE: \"replaceState\",\n};\n// Span Status\nexport const SPAN_STATUS_UNSET = 0;\nexport const SPAN_STATUS_OK = 1; // This is here for completion, status ok is reserved for use by application developers\nexport const SPAN_STATUS_ERROR = 2;\n// Span Kind\n// See: https://github.com/open-telemetry/opentelemetry-proto/blob/ac3242b03157295e4ee9e616af53b81517b06559/opentelemetry/proto/trace/v1/trace.proto#L143-L169\nexport const SPAN_KIND_CLIENT = 3;\n","import { isSupported, getItem, setItem, removeItem, SESSION_ID_BYTES } from \"../utils\";\nimport { debug, generateUniqueId, now } from \"../utils\";\nimport { info, warn } from \"../utils\";\nconst SESSION_STORAGE_KEY = \"d0_session\";\nconst STORAGE_SEPARATOR_KEY = \"#\";\nconst DEFAULT_SESSION_INACTIVITY_TIMEOUT_MILLIS = 1000 * 60 * 60 * 3;\nconst DEFAULT_SESSION_TERMINATION_TIMEOUT_MILLIS = 1000 * 60 * 60 * 6;\nconst MAX_ALLOWED_SESSION_TIMEOUT_MILLIS = 1000 * 60 * 60 * 24;\nexport let sessionId = null;\nexport function trackSessions(sessionInactivityTimeoutMillis, sessionTerminationTimeoutMillis) {\n if (!isSupported) {\n debug(\"Storage API is not available and session tracking is therefore not supported.\");\n return;\n }\n if (!sessionInactivityTimeoutMillis) {\n sessionInactivityTimeoutMillis = DEFAULT_SESSION_INACTIVITY_TIMEOUT_MILLIS;\n }\n if (!sessionTerminationTimeoutMillis) {\n sessionTerminationTimeoutMillis = DEFAULT_SESSION_TERMINATION_TIMEOUT_MILLIS;\n }\n sessionInactivityTimeoutMillis = Math.min(sessionInactivityTimeoutMillis, MAX_ALLOWED_SESSION_TIMEOUT_MILLIS);\n sessionTerminationTimeoutMillis = Math.min(sessionTerminationTimeoutMillis, MAX_ALLOWED_SESSION_TIMEOUT_MILLIS);\n try {\n const storedValue = getItem(SESSION_STORAGE_KEY);\n let session = parseSession(storedValue);\n if (session && !isSessionValid(session, sessionInactivityTimeoutMillis, sessionTerminationTimeoutMillis)) {\n session = null;\n }\n if (session) {\n session.lastActivityTime = now();\n }\n else {\n session = {\n id: generateUniqueId(SESSION_ID_BYTES),\n startTime: now(),\n lastActivityTime: now(),\n };\n }\n setItem(SESSION_STORAGE_KEY, serializeSession(session));\n sessionId = session.id;\n }\n catch (e) {\n warn(\"Failed to record session information\", e);\n }\n}\nexport function terminateSession() {\n sessionId = null;\n if (!isSupported) {\n return;\n }\n try {\n removeItem(SESSION_STORAGE_KEY);\n }\n catch (e) {\n info(\"Failed to terminate session\", e);\n }\n}\nfunction parseSession(storedValue) {\n if (!storedValue) {\n return null;\n }\n const values = storedValue.split(STORAGE_SEPARATOR_KEY);\n if (values.length < 3) {\n return null;\n }\n const id = values[0];\n const startTime = parseInt(values[1], 10);\n const lastActivityTime = parseInt(values[2], 10);\n if (!id || isNaN(startTime) || isNaN(lastActivityTime)) {\n return null;\n }\n return {\n id,\n startTime,\n lastActivityTime,\n };\n}\nfunction serializeSession(session) {\n return session.id + STORAGE_SEPARATOR_KEY + session.startTime + STORAGE_SEPARATOR_KEY + session.lastActivityTime;\n}\nfunction isSessionValid(session, sessionInactivityTimeoutMillis, sessionTerminationTimeoutMillis) {\n const minAllowedLastActivityTime = now() - sessionInactivityTimeoutMillis;\n if (session.lastActivityTime < minAllowedLastActivityTime) {\n return false;\n }\n const minAllowedStartTime = now() - sessionTerminationTimeoutMillis;\n return session.startTime >= minAllowedStartTime;\n}\n","var e,n,t,r,i,o=-1,a=function(e){addEventListener(\"pageshow\",(function(n){n.persisted&&(o=n.timeStamp,e(n))}),!0)},c=function(){var e=self.performance&&performance.getEntriesByType&&performance.getEntriesByType(\"navigation\")[0];if(e&&e.responseStart>0&&e.responseStart<performance.now())return e},u=function(){var e=c();return e&&e.activationStart||0},f=function(e,n){var t=c(),r=\"navigate\";o>=0?r=\"back-forward-cache\":t&&(document.prerendering||u()>0?r=\"prerender\":document.wasDiscarded?r=\"restore\":t.type&&(r=t.type.replace(/_/g,\"-\")));return{name:e,value:void 0===n?-1:n,rating:\"good\",delta:0,entries:[],id:\"v4-\".concat(Date.now(),\"-\").concat(Math.floor(8999999999999*Math.random())+1e12),navigationType:r}},s=function(e,n,t){try{if(PerformanceObserver.supportedEntryTypes.includes(e)){var r=new PerformanceObserver((function(e){Promise.resolve().then((function(){n(e.getEntries())}))}));return r.observe(Object.assign({type:e,buffered:!0},t||{})),r}}catch(e){}},d=function(e,n,t,r){var i,o;return function(a){n.value>=0&&(a||r)&&((o=n.value-(i||0))||void 0===i)&&(i=n.value,n.delta=o,n.rating=function(e,n){return e>n[1]?\"poor\":e>n[0]?\"needs-improvement\":\"good\"}(n.value,t),e(n))}},l=function(e){requestAnimationFrame((function(){return requestAnimationFrame((function(){return e()}))}))},p=function(e){document.addEventListener(\"visibilitychange\",(function(){\"hidden\"===document.visibilityState&&e()}))},v=function(e){var n=!1;return function(){n||(e(),n=!0)}},m=-1,h=function(){return\"hidden\"!==document.visibilityState||document.prerendering?1/0:0},g=function(e){\"hidden\"===document.visibilityState&&m>-1&&(m=\"visibilitychange\"===e.type?e.timeStamp:0,T())},y=function(){addEventListener(\"visibilitychange\",g,!0),addEventListener(\"prerenderingchange\",g,!0)},T=function(){removeEventListener(\"visibilitychange\",g,!0),removeEventListener(\"prerenderingchange\",g,!0)},E=function(){return m<0&&(m=h(),y(),a((function(){setTimeout((function(){m=h(),y()}),0)}))),{get firstHiddenTime(){return m}}},C=function(e){document.prerendering?addEventListener(\"prerenderingchange\",(function(){return e()}),!0):e()},b=[1800,3e3],S=function(e,n){n=n||{},C((function(){var t,r=E(),i=f(\"FCP\"),o=s(\"paint\",(function(e){e.forEach((function(e){\"first-contentful-paint\"===e.name&&(o.disconnect(),e.startTime<r.firstHiddenTime&&(i.value=Math.max(e.startTime-u(),0),i.entries.push(e),t(!0)))}))}));o&&(t=d(e,i,b,n.reportAllChanges),a((function(r){i=f(\"FCP\"),t=d(e,i,b,n.reportAllChanges),l((function(){i.value=performance.now()-r.timeStamp,t(!0)}))})))}))},L=[.1,.25],w=function(e,n){n=n||{},S(v((function(){var t,r=f(\"CLS\",0),i=0,o=[],c=function(e){e.forEach((function(e){if(!e.hadRecentInput){var n=o[0],t=o[o.length-1];i&&e.startTime-t.startTime<1e3&&e.startTime-n.startTime<5e3?(i+=e.value,o.push(e)):(i=e.value,o=[e])}})),i>r.value&&(r.value=i,r.entries=o,t())},u=s(\"layout-shift\",c);u&&(t=d(e,r,L,n.reportAllChanges),p((function(){c(u.takeRecords()),t(!0)})),a((function(){i=0,r=f(\"CLS\",0),t=d(e,r,L,n.reportAllChanges),l((function(){return t()}))})),setTimeout(t,0))})))},A=0,I=1/0,P=0,M=function(e){e.forEach((function(e){e.interactionId&&(I=Math.min(I,e.interactionId),P=Math.max(P,e.interactionId),A=P?(P-I)/7+1:0)}))},k=function(){return e?A:performance.interactionCount||0},F=function(){\"interactionCount\"in performance||e||(e=s(\"event\",M,{type:\"event\",buffered:!0,durationThreshold:0}))},D=[],x=new Map,R=0,B=function(){var e=Math.min(D.length-1,Math.floor((k()-R)/50));return D[e]},H=[],q=function(e){if(H.forEach((function(n){return n(e)})),e.interactionId||\"first-input\"===e.entryType){var n=D[D.length-1],t=x.get(e.interactionId);if(t||D.length<10||e.duration>n.latency){if(t)e.duration>t.latency?(t.entries=[e],t.latency=e.duration):e.duration===t.latency&&e.startTime===t.entries[0].startTime&&t.entries.push(e);else{var r={id:e.interactionId,latency:e.duration,entries:[e]};x.set(r.id,r),D.push(r)}D.sort((function(e,n){return n.latency-e.latency})),D.length>10&&D.splice(10).forEach((function(e){return x.delete(e.id)}))}}},O=function(e){var n=self.requestIdleCallback||self.setTimeout,t=-1;return e=v(e),\"hidden\"===document.visibilityState?e():(t=n(e),p(e)),t},N=[200,500],j=function(e,n){\"PerformanceEventTiming\"in self&&\"interactionId\"in PerformanceEventTiming.prototype&&(n=n||{},C((function(){var t;F();var r,i=f(\"INP\"),o=function(e){O((function(){e.forEach(q);var n=B();n&&n.latency!==i.value&&(i.value=n.latency,i.entries=n.entries,r())}))},c=s(\"event\",o,{durationThreshold:null!==(t=n.durationThreshold)&&void 0!==t?t:40});r=d(e,i,N,n.reportAllChanges),c&&(c.observe({type:\"first-input\",buffered:!0}),p((function(){o(c.takeRecords()),r(!0)})),a((function(){R=k(),D.length=0,x.clear(),i=f(\"INP\"),r=d(e,i,N,n.reportAllChanges)})))})))},_=[2500,4e3],z={},G=function(e,n){n=n||{},C((function(){var t,r=E(),i=f(\"LCP\"),o=function(e){n.reportAllChanges||(e=e.slice(-1)),e.forEach((function(e){e.startTime<r.firstHiddenTime&&(i.value=Math.max(e.startTime-u(),0),i.entries=[e],t())}))},c=s(\"largest-contentful-paint\",o);if(c){t=d(e,i,_,n.reportAllChanges);var m=v((function(){z[i.id]||(o(c.takeRecords()),c.disconnect(),z[i.id]=!0,t(!0))}));[\"keydown\",\"click\"].forEach((function(e){addEventListener(e,(function(){return O(m)}),{once:!0,capture:!0})})),p(m),a((function(r){i=f(\"LCP\"),t=d(e,i,_,n.reportAllChanges),l((function(){i.value=performance.now()-r.timeStamp,z[i.id]=!0,t(!0)}))}))}}))},J=[800,1800],K=function e(n){document.prerendering?C((function(){return e(n)})):\"complete\"!==document.readyState?addEventListener(\"load\",(function(){return e(n)}),!0):setTimeout(n,0)},Q=function(e,n){n=n||{};var t=f(\"TTFB\"),r=d(e,t,J,n.reportAllChanges);K((function(){var i=c();i&&(t.value=Math.max(i.responseStart-u(),0),t.entries=[i],r(!0),a((function(){t=f(\"TTFB\",0),(r=d(e,t,J,n.reportAllChanges))(!0)})))}))},U={passive:!0,capture:!0},V=new Date,W=function(e,i){n||(n=i,t=e,r=new Date,Z(removeEventListener),X())},X=function(){if(t>=0&&t<r-V){var e={entryType:\"first-input\",name:n.type,target:n.target,cancelable:n.cancelable,startTime:n.timeStamp,processingStart:n.timeStamp+t};i.forEach((function(n){n(e)})),i=[]}},Y=function(e){if(e.cancelable){var n=(e.timeStamp>1e12?new Date:performance.now())-e.timeStamp;\"pointerdown\"==e.type?function(e,n){var t=function(){W(e,n),i()},r=function(){i()},i=function(){removeEventListener(\"pointerup\",t,U),removeEventListener(\"pointercancel\",r,U)};addEventListener(\"pointerup\",t,U),addEventListener(\"pointercancel\",r,U)}(n,e):W(n,e)}},Z=function(e){[\"mousedown\",\"keydown\",\"touchstart\",\"pointerdown\"].forEach((function(n){return e(n,Y,U)}))},$=[100,300],ee=function(e,r){r=r||{},C((function(){var o,c=E(),u=f(\"FID\"),l=function(e){e.startTime<c.firstHiddenTime&&(u.value=e.processingStart-e.startTime,u.entries.push(e),o(!0))},m=function(e){e.forEach(l)},h=s(\"first-input\",m);o=d(e,u,$,r.reportAllChanges),h&&(p(v((function(){m(h.takeRecords()),h.disconnect()}))),a((function(){var a;u=f(\"FID\"),o=d(e,u,$,r.reportAllChanges),i=[],t=-1,n=null,Z(addEventListener),a=l,i.push(a),X()})))}))};export{L as CLSThresholds,b as FCPThresholds,$ as FIDThresholds,N as INPThresholds,_ as LCPThresholds,J as TTFBThresholds,w as onCLS,S as onFCP,ee as onFID,j as onINP,G as onLCP,Q as onTTFB};\n","import { addEventListener } from \"./listeners\";\nimport { doc, win } from \"./globals\";\nlet isUnloading = false;\n/**\n * Triggers the `fn` when the HTML document is getting unloaded\n * (or when it looks like it might be). Useful to flush batch activities.\n */\nexport function onLastChance(fn) {\n if (isUnloading) {\n fn();\n }\n if (!doc || !win)\n return;\n addEventListener(doc, \"visibilitychange\", function () {\n if (doc.visibilityState !== \"visible\") {\n fn();\n }\n });\n addEventListener(win, \"pagehide\", function () {\n isUnloading = true;\n fn();\n });\n // According to the spec visibilitychange should be a replacement for\n // beforeunload, but the reality is different (as of 2019-04-17). Chrome will\n // close tabs without firing visibilitychange. beforeunload on the other hand\n // is fired.\n addEventListener(win, \"beforeunload\", function () {\n isUnloading = true;\n fn();\n });\n}\n","import { onLastChance } from \"../utils/on-last-chance\";\nimport { doc } from \"../utils\";\nimport { setTimeout } from \"../utils/timers\";\nconst SCHEDULE_DELAY_MILLIS = 1000;\nconst MAX_QUEUE_SIZE = 15;\nexport function newBatcher(sendInternal) {\n const queuedItems = [];\n let pendingFlushTimeout;\n // We attempt batching of messages to be more efficient on the client, network and\n // server-side. While the connection is either a persistent HTTP 2 connection or\n // an HTTP 1.1 connection with keep-alive, there is still some overhead involved\n // in having many small messages.\n //\n // For this reason we attempt batching. When batching we must be careful to\n // force a transmission when the document is unloaded.\n onLastChance(flush);\n return {\n send(item) {\n if (isWindowHidden()) {\n // We cannot guarantee that we will ever get time to transmit data in a batched\n // format when the window is hidden, as this might occur while the document is\n // being unloaded. Immediately force a transmission in these cases.\n sendInternal([item]);\n return;\n }\n queuedItems.push(item);\n if (queuedItems.length >= MAX_QUEUE_SIZE) {\n flush();\n }\n else if (pendingFlushTimeout == null) {\n pendingFlushTimeout = setTimeout(flush, SCHEDULE_DELAY_MILLIS);\n }\n },\n };\n function flush() {\n if (pendingFlushTimeout != null) {\n clearTimeout(pendingFlushTimeout);\n pendingFlushTimeout = null;\n }\n if (queuedItems.length > 0) {\n sendInternal(queuedItems.slice());\n queuedItems.length = 0;\n }\n }\n}\nfunction isWindowHidden() {\n return doc?.visibilityState !== \"visible\";\n}\n","import { vars } from \"../vars\";\nimport { noop, warn, fetch } from \"../utils\";\nconst BEACON_BODY_SIZE_LIMIT = 60000;\nexport async function send(path, body) {\n const jsonString = JSON.stringify(body);\n let requestBody = jsonString;\n let byteLength = jsonString.length;\n let isCompressed = false;\n // Try to compress if supported\n if (typeof CompressionStream !== \"undefined\") {\n requestBody = await compressWithGzip(jsonString);\n byteLength = requestBody.byteLength;\n isCompressed = true;\n }\n // Send to all endpoints in parallel\n await Promise.all(vars.endpoints.map(async (endpoint) => {\n try {\n const url = new URL(endpoint[\"url\"]);\n url.pathname = url.pathname + (url.pathname.endsWith(\"/\") ? path.substring(1) : path);\n const headers = {\n \"Content-Type\": \"application/json\",\n Authorization: `Bearer ${endpoint[\"authToken\"]}`,\n };\n if (endpoint.dataset) {\n headers[\"Dash0-Dataset\"] = endpoint[\"dataset\"];\n }\n // Try to compress if supported\n if (isCompressed) {\n headers[\"Content-Encoding\"] = \"gzip\";\n }\n if (!fetch) {\n warn(\"Unable to send telemetry, fetch is not defined\");\n return;\n }\n const response = await fetch(url, {\n method: \"POST\",\n headers,\n body: requestBody,\n // The keepalive flag is related to the window.sendBeacon API. This in turn has size limitations.\n keepalive: byteLength <= BEACON_BODY_SIZE_LIMIT,\n });\n // read the body so the connection can be closed\n response.text().catch(noop);\n if (!response.ok) {\n warn(`Failed to send telemetry to ${url}: ${response.status} ${response.statusText}`);\n }\n }\n catch (error) {\n warn(`Error sending telemetry to ${endpoint.url}${path}:`, error);\n }\n }));\n}\nasync function compressWithGzip(data) {\n const blob = new Blob([data]);\n const stream = blob.stream();\n const compressedStream = stream.pipeThrough(new CompressionStream(\"gzip\"));\n return new Response(compressedStream).arrayBuffer();\n}\n","import { newBatcher } from \"./batcher\";\nimport { send } from \"./fetch\";\nimport { vars } from \"../vars\";\nimport { createRateLimiter } from \"../utils/rate-limit\";\nimport { debug, error } from \"../utils\";\nconst logBatcher = newBatcher(sendLogs);\nconst spanBatcher = newBatcher(sendSpans);\nlet rateLimiter;\nfunction isRateLimited() {\n if (!rateLimiter) {\n rateLimiter = createRateLimiter({\n maxCalls: 8096,\n maxCallsPerTenMinutes: 4096,\n maxCallsPerTenSeconds: 128,\n });\n }\n return rateLimiter();\n}\nexport function sendLog(log) {\n if (isRateLimited()) {\n debug(\"Transport rate limit. Will not send item.\", log);\n return;\n }\n logBatcher.send(log);\n}\nfunction sendLogs(logs) {\n send(\"/v1/logs\", {\n resourceLogs: [\n {\n resource: vars.resource,\n scopeLogs: [\n {\n scope: vars.scope,\n logRecords: logs,\n },\n ],\n },\n ],\n }).catch((err) => {\n error(\"Failed to transmit logs\", err);\n });\n}\nexport function sendSpan(span) {\n if (isRateLimited()) {\n debug(\"Transport rate limit. Will not send item.\", span);\n return;\n }\n spanBatcher.send(span);\n}\nfunction sendSpans(spans) {\n send(\"/v1/traces\", {\n resourceSpans: [\n {\n resource: vars.resource,\n scopeSpans: [\n {\n scope: vars.scope,\n spans: spans,\n },\n ],\n },\n ],\n }).catch((err) => {\n error(\"Failed to transmit spans\", err);\n });\n}\n","import { setInterval } from \"./timers\";\nexport function createRateLimiter(opts) {\n const maxCalls = opts.maxCalls || 4096;\n const maxCallsPerTenMinutes = opts.maxCallsPerTenMinutes || 128;\n const maxCallsPerTenSeconds = opts.maxCallsPerTenSeconds || 32;\n let totalCalls = 0;\n let totalCallsInLastTenMinutes = 0;\n let totalCallsInLastTenSeconds = 0;\n setInterval(function () {\n totalCallsInLastTenMinutes = 0;\n }, 1000 * 60 * 10);\n setInterval(function () {\n totalCallsInLastTenSeconds = 0;\n }, 1000 * 10);\n return function isExcessiveUsage() {\n return (++totalCalls > maxCalls ||\n ++totalCallsInLastTenMinutes > maxCallsPerTenMinutes ||\n ++totalCallsInLastTenSeconds > maxCallsPerTenSeconds);\n };\n}\n","const ANY_VALUE_KEYS = [\n \"stringValue\",\n \"boolValue\",\n \"intValue\",\n \"doubleValue\",\n \"arrayValue\",\n \"kvlistValue\",\n \"bytesValue\",\n];\nfunction isAnyValue(value) {\n if (value == null || typeof value !== \"object\")\n return false;\n const keys = Object.keys(value);\n return keys.length === 1 && ANY_VALUE_KEYS.includes(keys[0]);\n}\nexport function toAnyValue(value) {\n if (value == null) {\n return undefined;\n }\n let anyValue = {};\n if (Array.isArray(value)) {\n anyValue[\"arrayValue\"] = { values: value.map((e) => toAnyValue(e)) };\n }\n else if (typeof value === \"string\") {\n anyValue[\"stringValue\"] = value;\n }\n else if (typeof value === \"number\") {\n anyValue[\"doubleValue\"] = value;\n }\n else if (typeof value === \"boolean\") {\n anyValue[\"boolValue\"] = value;\n }\n else if (isAnyValue(value)) {\n anyValue = value;\n }\n else if (typeof value === \"object\") {\n anyValue[\"kvlistValue\"] = { values: Object.entries(value).map(([key, value]) => toKeyValue(key, value)) };\n }\n return anyValue;\n}\nexport function toKeyValue(key, value) {\n return {\n key: key,\n value: toAnyValue(value),\n };\n}\nexport function addAttribute(attributes, key, value) {\n attributes.push(toKeyValue(key, value));\n}\nexport function removeAttribute(attributes, key) {\n const index = attributes.findIndex((attr) => attr[\"key\"] === key);\n if (index !== -1) {\n attributes.splice(index, 1);\n }\n}\n","import { doc } from \"../globals\";\nimport { perf } from \"../globals\";\nconst TRACE_PARENT_HEADER = \"traceparent\";\n/**\n * See https://www.w3.org/TR/trace-context/#traceparent-header\n */\nconst w3cTraceparentFormat = /^00-([a-f0-9]{32})-([a-f0-9]{16})-[0-9]{1,2}$/;\nexport function getTraceContextForPageLoad() {\n const match = getTraceparentFromMetaElement().match(w3cTraceparentFormat) ||\n getTraceparentFromNavigationTiming().match(w3cTraceparentFormat);\n if (match) {\n return {\n traceId: match[1],\n spanId: match[2],\n };\n }\n return undefined;\n}\nfunction getTraceparentFromMetaElement() {\n return (Array.from(doc?.getElementsByTagName(\"meta\") ?? [])\n .find((e) => e.getAttribute(\"name\")?.toLowerCase() === TRACE_PARENT_HEADER)\n ?.content.trim() || \"\");\n}\nfunction getTraceparentFromNavigationTiming() {\n const nt = perf.getEntriesByType(\"navigation\")[0];\n if (!nt) {\n return \"\";\n }\n if (!nt.serverTiming) {\n return \"\";\n }\n return getTraceparentFromServerTiming(nt.serverTiming);\n}\nfunction getTraceparentFromServerTiming(serverTimings) {\n for (const serverEntry of serverTimings) {\n if (serverEntry.name === TRACE_PARENT_HEADER) {\n return serverEntry.description.trim();\n }\n }\n return \"\";\n}\nexport function addTraceContextHttpHeaders(fn, ctx, span) {\n /**\n * W3C Traceparent header.\n * General format is ${version}-${trace-id}-${parent-id}-${trace-flags}\n *\n * The only spec'd version is currently 00\n * Trace flags are an 8 bit field of bit flags:\n * Sampled: 00000001 - Should only be set if a definite decision to record the trace was made.\n * If set downstream processors should also record the trace\n * Random Trace ID: 00000010 - IF set the component guarantees that the seven right most bytes of the trace-id\n * are randomly generated. Downstream processors are then able to rely on this for technical things like shard keys.\n *\n * References:\n * https://www.w3.org/TR/trace-context-2/#traceparent-header\n * https://www.w3.org/TR/trace-context-2/#trace-flags\n * https://www.w3.org/TR/trace-context-2/#random-trace-id-flag\n */\n fn.call(ctx, \"traceparent\", `00-${span.traceId}-${span.spanId}-03`);\n}\n","import { nowNanos } from \"../time\";\nimport { generateUniqueId, SPAN_ID_BYTES, TRACE_ID_BYTES } from \"../id\";\nimport { SPAN_KIND_CLIENT, SPAN_STATUS_UNSET } from \"../../semantic-conventions\";\nexport function startSpan(name) {\n return {\n traceId: generateUniqueId(TRACE_ID_BYTES),\n spanId: generateUniqueId(SPAN_ID_BYTES),\n name,\n // Always CLIENT for now https://github.com/open-telemetry/opentelemetry-proto/blob/ac3242b03157295e4ee9e616af53b81517b06559/opentelemetry/proto/trace/v1/trace.proto#L143-L169\n // Note: we directly define otlp here, this differs from the values used by oteljs internally.\n kind: SPAN_KIND_CLIENT,\n startTimeUnixNano: nowNanos(),\n attributes: [],\n events: [],\n links: [],\n status: { code: SPAN_STATUS_UNSET },\n };\n}\nexport function endSpan(span, status, durationNano) {\n // We cast here to avoid having to instantiate a copy of the span\n const s = span;\n if (status) {\n s.status = status;\n }\n s.endTimeUnixNano =\n durationNano != null ? String(Math.round(parseInt(s.startTimeUnixNano) + durationNano)) : nowNanos();\n return s;\n}\n/**\n * Adds an event to a span. Can optionally accept the events timestamp and attributes for the event.\n * The timestamp needs to be specified as nanoseconds since unix epoch in string format.\n */\nexport function addSpanEvent(span, name, attributesOrTs, attributes) {\n let ts = undefined;\n let attr = undefined;\n if (typeof attributesOrTs === \"string\") {\n ts = attributesOrTs;\n }\n else if (Array.isArray(attributesOrTs)) {\n attr = attributesOrTs;\n }\n if (attributes) {\n attr = attributes;\n }\n span.events.push({\n name,\n timeUnixNano: ts != null ? ts : nowNanos(),\n attributes: attr ?? [],\n });\n}\nexport function setSpanStatus(span, code, message) {\n span.status = {\n code,\n message,\n };\n}\n","// sessionStorage API re-exposed to allow testing.\nimport { sessionStorage } from \"./globals\";\nexport const isSupported = sessionStorage != null &&\n typeof sessionStorage.getItem === \"function\" &&\n typeof sessionStorage.setItem === \"function\";\nexport function getItem(k) {\n if (isSupported && sessionStorage) {\n return sessionStorage.getItem(k);\n }\n return null;\n}\nexport function setItem(k, v) {\n if (isSupported && sessionStorage) {\n sessionStorage.setItem(k, v);\n }\n}\nexport function removeItem(k) {\n if (isSupported && sessionStorage) {\n sessionStorage.removeItem(k);\n }\n}\n","import { getItem, isSupported, setItem } from \"./session-storage\";\nimport { debug, warn } from \"./debug\";\nimport { generateUniqueId, TAB_ID_BYTES } from \"./id\";\nconst TAB_ID_STORAGE_KEY = \"d0_tab\";\nexport let tabId = null;\n/**\n * We want to be able to identify what browser tab the user is looking at. We do this by identifying tabs through\n * random-generated IDs that are kept in session storage. To quote MDN:\n *\n * > sessionStorage is partitioned by both origin and browser tabs (top-level browsing contexts)\n */\nexport function initializeTabId() {\n if (!isSupported) {\n debug(\"Storage API is not available and tab tracking is therefore not supported.\");\n return;\n }\n try {\n const storedValue = getItem(TAB_ID_STORAGE_KEY);\n if (storedValue) {\n tabId = storedValue;\n return;\n }\n tabId = generateUniqueId(TAB_ID_BYTES);\n setItem(TAB_ID_STORAGE_KEY, tabId);\n }\n catch (e) {\n warn(\"Failed to record tab ID information\", e);\n }\n}\n","import { nav, NO_VALUE_FALLBACK, win } from \"../utils\";\nimport { BROWSER_TAB_ID, NETWORK_CONNECTION_TYPE, SESSION_ID, WINDOW_HEIGHT, WINDOW_WIDTH, } from \"../semantic-conventions\";\nimport { sessionId } from \"../api/session\";\nimport { vars } from \"../vars\";\nimport { a