@embrace-io/web-sdk
Version:
160 lines (159 loc) • 9.5 kB
JavaScript
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