UNPKG

@dash0/sdk-web

Version:

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

73 lines (72 loc) 2.85 kB
import { debug, nowNanos, win, wrap } from "../../utils"; import { vars } from "../../vars"; import { transmitPageViewEvent } from "./event"; let currentLocation = {}; let shouldIncludeHashChanges = false; let shouldIncludeSearchChanges = false; /** * Tracks page transitions (virtual page views) as per this OTel spec: * https://github.com/open-telemetry/semantic-conventions/pull/1910/files * * Notable difference: The full URL is transmitted as a signal attribute. */ export function startPageTransitionInstrumentation() { if (!win || !win.history) { debug("Browser does not support history API, skipping instrumentation"); return; } if (!vars.pageViewInstrumentation.trackVirtualPageViews) { return; } shouldIncludeSearchChanges = vars.pageViewInstrumentation.includeParts?.includes("SEARCH") ?? false; shouldIncludeHashChanges = vars.pageViewInstrumentation.includeParts?.includes("HASH") ?? false; /** * Not wrapping history.go, history.backward and history.forward here, because their call signatures don't receive * url information. For these cases we can use the popstate and hashchange events. */ wrap(win.history, "replaceState", (original) => function (state, unused, url) { onUrlChange(url ? String(url) : undefined, true); return original.apply(this, [state, unused, url]); }); wrap(win.history, "pushState", (original) => function (state, unused, url) { onUrlChange(url ? String(url) : undefined); return original.apply(this, [state, unused, url]); }); win.addEventListener("hashchange", onHashChange); win.addEventListener("popstate", onPopState); try { updateCurrentLocation(new URL(win.location.href)); } catch { } } function onPopState() { // popState event is fired after the location entry has been updated, so the location should already contain the new url. onUrlChange(win?.location.href); } function onHashChange(event) { onUrlChange(event.newURL); } function onUrlChange(url, replaced) { if (!url) return; try { const parsedUrl = new URL(url, win?.location.href); if (isLocationChange(parsedUrl)) { updateCurrentLocation(parsedUrl); transmitPageViewEvent(nowNanos(), parsedUrl, true, Boolean(replaced)); } } catch (e) { debug("Failed to handle url change", e); } } function updateCurrentLocation(url) { currentLocation.path = url.pathname; currentLocation.search = url.search; currentLocation.hash = url.hash; } function isLocationChange(newUrl) { return (newUrl.pathname !== currentLocation.path || (shouldIncludeSearchChanges && newUrl.search !== currentLocation.search) || (shouldIncludeHashChanges && newUrl.hash !== currentLocation.hash)); }