UNPKG

@embrace-io/web-sdk

Version:
160 lines (159 loc) 9.5 kB
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" }); const require_constants_attributes = require("../../../constants/attributes.cjs"); const require_utils_performanceObserver_performanceObserver = require("../../../utils/performanceObserver/performanceObserver.cjs"); const require_instrumentations_soft_navigation_performance_SoftNavigationPerformanceInstrumentation_constants = require("./constants.cjs"); const require_instrumentations_EmbraceInstrumentationBase_EmbraceInstrumentationBase = require("../../EmbraceInstrumentationBase/EmbraceInstrumentationBase.cjs"); //#region src/instrumentations/soft-navigation-performance/SoftNavigationPerformanceInstrumentation/SoftNavigationPerformanceInstrumentation.ts /** * Returns the click entry whose processing window contains the given navigation * timestamp, or null if no match is found. * * For pushState navigations, currententrychange fires synchronously during the * click handler, so the timestamp falls within the click entry's * [startTime, startTime + duration] window. */ function getNavigationEventTrigger(navigationTimestamp, entry) { return entry.startTime <= navigationTimestamp && navigationTimestamp <= entry.startTime + entry.duration ? entry : null; } const PENDING_NAVIGATION_TTL_MS = 6e4; var SoftNavigationPerformanceInstrumentation = class extends require_instrumentations_EmbraceInstrumentationBase_EmbraceInstrumentationBase.EmbraceInstrumentationBase { _observer = null; _eventObserver = null; _pendingNavigations = []; _usePolyfill = false; _navigationHost; _signalBuffer; _handleCurrentEntryChange = (event) => { const url = this._navigationHost.navigation?.currentEntry?.url; if (!url) { this._diag.debug("currententrychange fired with no URL, skipping"); return; } this._pendingNavigations.push({ timestamp: event.timeStamp, url }); }; constructor({ diag, perf, limitManager, navigationHost = window, signalBuffer } = {}) { super({ instrumentationName: "SoftNavigationPerformanceInstrumentation", instrumentationVersion: "1.0.0", diag, perf, limitManager, config: {} }); this._navigationHost = navigationHost; this._signalBuffer = signalBuffer; if (this._config.enabled) this.enable(); } enable() { if (require_utils_performanceObserver_performanceObserver.isEntryTypeSupported("soft-navigation")) { this._usePolyfill = false; super.enable(); } else if (this._navigationHost.navigation && require_utils_performanceObserver_performanceObserver.isEntryTypeSupported("event")) { this._usePolyfill = true; super.enable(); } else this._diag.debug("soft-navigation and navigation API not supported, skipping"); } onEnable() { if (this._usePolyfill) this._enablePolyfill(); else this._enableNative(); } _enableNative() { if (this._observer) this._observer.disconnect(); this._observer = require_utils_performanceObserver_performanceObserver.createPerformanceObserver("soft-navigation", (entry) => this._processEntry(entry), { diag: this._diag }); if (!this._observer) { this._isEnabled = false; this._diag.error("failed to enable"); return; } } _enablePolyfill() { if (this._eventObserver) this._eventObserver.disconnect(); const nav = this._navigationHost.navigation; nav.addEventListener("currententrychange", this._handleCurrentEntryChange); this._eventObserver = require_utils_performanceObserver_performanceObserver.createPerformanceObserver("event", (entry) => this._processClickEntry(entry), { diag: this._diag, durationThreshold: 16 }); if (!this._eventObserver) { nav.removeEventListener("currententrychange", this._handleCurrentEntryChange); this._isEnabled = false; this._diag.error("failed to enable polyfill"); return; } } onDisable() { if (this._observer) { this._observer.disconnect(); this._observer = null; } if (this._eventObserver) { this._eventObserver.disconnect(); this._eventObserver = null; } this._navigationHost.navigation?.removeEventListener("currententrychange", this._handleCurrentEntryChange); this._pendingNavigations.length = 0; } _correlationAttributes(startTimeEpochMillis, endTimeEpochMillis) { if (!this._signalBuffer) return {}; const { spanIds, spanTypes, logIds, logTypes } = this._signalBuffer.collectWindow(startTimeEpochMillis, endTimeEpochMillis); return { [require_instrumentations_soft_navigation_performance_SoftNavigationPerformanceInstrumentation_constants.KEY_EMB_SOFT_NAVIGATION_SPAN_IDS]: spanIds, [require_instrumentations_soft_navigation_performance_SoftNavigationPerformanceInstrumentation_constants.KEY_EMB_SOFT_NAVIGATION_SPAN_ID_TYPES]: spanTypes, [require_instrumentations_soft_navigation_performance_SoftNavigationPerformanceInstrumentation_constants.KEY_EMB_SOFT_NAVIGATION_LOG_IDS]: logIds, [require_instrumentations_soft_navigation_performance_SoftNavigationPerformanceInstrumentation_constants.KEY_EMB_SOFT_NAVIGATION_LOG_ID_TYPES]: logTypes }; } _processEntry(entry) { if (!this._isEnabled) return; if (this.limitManager?.limitSoftNavigationEntry()) return; const startTimeEpochMillis = this.perf.epochMillisFromOrigin(entry.startTime); const endTimeEpochMillis = this.perf.epochMillisFromOrigin(entry.startTime + entry.duration); this.tracer.startSpan(require_instrumentations_soft_navigation_performance_SoftNavigationPerformanceInstrumentation_constants.SOFT_NAVIGATION_SPAN_NAME, { startTime: startTimeEpochMillis, attributes: { [require_constants_attributes.KEY_BROWSER_URL_FULL]: entry.name, [require_instrumentations_soft_navigation_performance_SoftNavigationPerformanceInstrumentation_constants.KEY_EMB_SOFT_NAVIGATION_SOURCE]: require_instrumentations_soft_navigation_performance_SoftNavigationPerformanceInstrumentation_constants.SOFT_NAVIGATION_SOURCES.performanceObserver, [require_instrumentations_soft_navigation_performance_SoftNavigationPerformanceInstrumentation_constants.KEY_EMB_SOFT_NAVIGATION_NAVIGATION_ID]: entry.navigationId, [require_instrumentations_soft_navigation_performance_SoftNavigationPerformanceInstrumentation_constants.KEY_EMB_SOFT_NAVIGATION_INTERACTION_ID]: entry.interactionId, [require_instrumentations_soft_navigation_performance_SoftNavigationPerformanceInstrumentation_constants.KEY_EMB_SOFT_NAVIGATION_START_TIME]: this.perf.millisFromZeroTime(entry.startTime), [require_instrumentations_soft_navigation_performance_SoftNavigationPerformanceInstrumentation_constants.KEY_EMB_SOFT_NAVIGATION_DURATION]: entry.duration, [require_instrumentations_soft_navigation_performance_SoftNavigationPerformanceInstrumentation_constants.KEY_EMB_SOFT_NAVIGATION_PAINT_TIME]: entry.paintTime != null ? this.perf.millisFromZeroTime(entry.paintTime) : void 0, [require_instrumentations_soft_navigation_performance_SoftNavigationPerformanceInstrumentation_constants.KEY_EMB_SOFT_NAVIGATION_PRESENTATION_TIME]: entry.presentationTime != null ? this.perf.millisFromZeroTime(entry.presentationTime) : void 0, ...this._correlationAttributes(startTimeEpochMillis, endTimeEpochMillis) } }).end(endTimeEpochMillis); } _processClickEntry(entry) { if (!this._isEnabled) return; if (entry.name !== "click") return; this._pendingNavigations = this._pendingNavigations.filter(({ timestamp }) => entry.startTime - timestamp < PENDING_NAVIGATION_TTL_MS); const index = this._pendingNavigations.findIndex(({ timestamp }) => getNavigationEventTrigger(timestamp, entry) !== null); if (index === -1) return; const [matched] = this._pendingNavigations.splice(index, 1); this._emitPolyfillSpan(entry, matched.timestamp, matched.url); } _emitPolyfillSpan(clickEntry, navigationTimestamp, url) { if (!this._isEnabled) return; if (this.limitManager?.limitSoftNavigationEntry()) return; const startTimeEpochMillis = this.perf.epochMillisFromOrigin(clickEntry.startTime); const endTimeEpochMillis = this.perf.epochMillisFromOrigin(navigationTimestamp); this.tracer.startSpan(require_instrumentations_soft_navigation_performance_SoftNavigationPerformanceInstrumentation_constants.SOFT_NAVIGATION_SPAN_NAME, { startTime: startTimeEpochMillis, attributes: { [require_constants_attributes.KEY_BROWSER_URL_FULL]: url, [require_instrumentations_soft_navigation_performance_SoftNavigationPerformanceInstrumentation_constants.KEY_EMB_SOFT_NAVIGATION_SOURCE]: require_instrumentations_soft_navigation_performance_SoftNavigationPerformanceInstrumentation_constants.SOFT_NAVIGATION_SOURCES.polyfill, [require_instrumentations_soft_navigation_performance_SoftNavigationPerformanceInstrumentation_constants.KEY_EMB_SOFT_NAVIGATION_START_TIME]: this.perf.millisFromZeroTime(clickEntry.startTime), [require_instrumentations_soft_navigation_performance_SoftNavigationPerformanceInstrumentation_constants.KEY_EMB_SOFT_NAVIGATION_DURATION]: navigationTimestamp - clickEntry.startTime, [require_instrumentations_soft_navigation_performance_SoftNavigationPerformanceInstrumentation_constants.KEY_EMB_SOFT_NAVIGATION_INTERACTION_ID]: clickEntry.interactionId !== 0 ? clickEntry.interactionId : void 0, ...this._correlationAttributes(startTimeEpochMillis, endTimeEpochMillis) } }).end(endTimeEpochMillis); } }; //#endregion exports.SoftNavigationPerformanceInstrumentation = SoftNavigationPerformanceInstrumentation; exports.getNavigationEventTrigger = getNavigationEventTrigger; //# sourceMappingURL=SoftNavigationPerformanceInstrumentation.cjs.map