@grafana/faro-web-sdk
Version:
Faro instrumentations, metas, transports for web.
149 lines • 8.64 kB
JavaScript
;
var __assign = (this && this.__assign) || function () {
__assign = Object.assign || function(t) {
for (var s, i = 1, n = arguments.length; i < n; i++) {
s = arguments[i];
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
t[p] = s[p];
}
return t;
};
return __assign.apply(this, arguments);
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.getSpanContextFromServerTiming = getSpanContextFromServerTiming;
exports.performanceObserverSupported = performanceObserverSupported;
exports.onDocumentReady = onDocumentReady;
exports.includePerformanceEntry = includePerformanceEntry;
exports.createFaroResourceTiming = createFaroResourceTiming;
exports.createFaroNavigationTiming = createFaroNavigationTiming;
var faro_core_1 = require("@grafana/faro-core");
var w3cTraceparentFormat = /^00-[a-f0-9]{32}-[a-f0-9]{16}-[0-9]{1,2}$/;
// Extract traceparent from serverTiming, if present
function getSpanContextFromServerTiming(serverTimings) {
if (serverTimings === void 0) { serverTimings = []; }
for (var _i = 0, serverTimings_1 = serverTimings; _i < serverTimings_1.length; _i++) {
var serverEntry = serverTimings_1[_i];
if (serverEntry.name === 'traceparent') {
if (!w3cTraceparentFormat.test(serverEntry.description)) {
continue;
}
var _a = serverEntry.description.split('-'), traceId = _a[1], spanId = _a[2];
if (traceId != null && spanId != null) {
return { traceId: traceId, spanId: spanId };
}
break;
}
}
return undefined;
}
function performanceObserverSupported() {
return 'PerformanceObserver' in window;
}
function onDocumentReady(handleReady) {
if (document.readyState === 'complete') {
handleReady();
}
else {
var readyStateCompleteHandler_1 = function () {
if (document.readyState === 'complete') {
handleReady();
document.removeEventListener('readystatechange', readyStateCompleteHandler_1);
}
};
document.addEventListener('readystatechange', readyStateCompleteHandler_1);
}
}
function includePerformanceEntry(performanceEntryJSON, allowProps) {
if (allowProps === void 0) { allowProps = {}; }
for (var _i = 0, _a = Object.entries(allowProps); _i < _a.length; _i++) {
var _b = _a[_i], allowPropKey = _b[0], allowPropValue = _b[1];
var perfEntryPropVal = performanceEntryJSON[allowPropKey];
if (perfEntryPropVal == null) {
return false;
}
if ((0, faro_core_1.isArray)(allowPropValue)) {
return allowPropValue.includes(perfEntryPropVal);
}
return perfEntryPropVal === allowPropValue;
}
// empty object allows all
return true;
}
function createFaroResourceTiming(resourceEntryRaw) {
var connectEnd = resourceEntryRaw.connectEnd, connectStart = resourceEntryRaw.connectStart, decodedBodySize = resourceEntryRaw.decodedBodySize, domainLookupEnd = resourceEntryRaw.domainLookupEnd, domainLookupStart = resourceEntryRaw.domainLookupStart, duration = resourceEntryRaw.duration, encodedBodySize = resourceEntryRaw.encodedBodySize, fetchStart = resourceEntryRaw.fetchStart, initiatorType = resourceEntryRaw.initiatorType, name = resourceEntryRaw.name, nextHopProtocol = resourceEntryRaw.nextHopProtocol, redirectEnd = resourceEntryRaw.redirectEnd, redirectStart = resourceEntryRaw.redirectStart,
// @ts-expect-error the renderBlockingStatus property is not available in all browsers
rbs = resourceEntryRaw.renderBlockingStatus, requestStart = resourceEntryRaw.requestStart, responseEnd = resourceEntryRaw.responseEnd, responseStart = resourceEntryRaw.responseStart, responseStatus = resourceEntryRaw.responseStatus, secureConnectionStart = resourceEntryRaw.secureConnectionStart, transferSize = resourceEntryRaw.transferSize, workerStart = resourceEntryRaw.workerStart;
return {
name: name,
duration: toFaroPerformanceTimingString(duration),
tcpHandshakeTime: toFaroPerformanceTimingString(connectEnd - connectStart),
dnsLookupTime: toFaroPerformanceTimingString(domainLookupEnd - domainLookupStart),
tlsNegotiationTime: toFaroPerformanceTimingString(connectEnd - secureConnectionStart),
responseStatus: toFaroPerformanceTimingString(responseStatus),
redirectTime: toFaroPerformanceTimingString(redirectEnd - redirectStart),
requestTime: toFaroPerformanceTimingString(responseStart - requestStart),
responseTime: toFaroPerformanceTimingString(responseEnd - responseStart),
fetchTime: toFaroPerformanceTimingString(responseEnd - fetchStart),
serviceWorkerTime: toFaroPerformanceTimingString(fetchStart - workerStart),
decodedBodySize: toFaroPerformanceTimingString(decodedBodySize),
encodedBodySize: toFaroPerformanceTimingString(encodedBodySize),
cacheHitStatus: getCacheType(),
renderBlockingStatus: toFaroPerformanceTimingString(rbs),
protocol: nextHopProtocol,
initiatorType: initiatorType,
visibilityState: document.visibilityState,
ttfb: toFaroPerformanceTimingString(responseStart - requestStart),
transferSize: toFaroPerformanceTimingString(transferSize),
// TODO: add in future iteration, ideally after nested objects are supported by the collector.
// serverTiming: resourceEntryRaw.serverTiming,
};
function getCacheType() {
var cacheType = 'fullLoad';
if (transferSize === 0) {
if (decodedBodySize > 0) {
cacheType = 'cache';
}
}
else {
if (responseStatus != null) {
if (responseStatus === 304) {
cacheType = 'conditionalFetch';
}
}
else if (encodedBodySize > 0 && transferSize < encodedBodySize) {
cacheType = 'conditionalFetch';
}
}
return cacheType;
}
}
function createFaroNavigationTiming(navigationEntryRaw) {
var activationStart = navigationEntryRaw.activationStart, domComplete = navigationEntryRaw.domComplete, domContentLoadedEventEnd = navigationEntryRaw.domContentLoadedEventEnd, domContentLoadedEventStart = navigationEntryRaw.domContentLoadedEventStart, domInteractive = navigationEntryRaw.domInteractive, fetchStart = navigationEntryRaw.fetchStart, loadEventEnd = navigationEntryRaw.loadEventEnd, loadEventStart = navigationEntryRaw.loadEventStart, responseStart = navigationEntryRaw.responseStart, type = navigationEntryRaw.type;
var parserStart = getDocumentParsingTime();
return __assign(__assign({}, createFaroResourceTiming(navigationEntryRaw)), { pageLoadTime: toFaroPerformanceTimingString(domComplete - fetchStart), documentParsingTime: toFaroPerformanceTimingString(parserStart ? domInteractive - parserStart : null), domProcessingTime: toFaroPerformanceTimingString(domComplete - domInteractive), domContentLoadHandlerTime: toFaroPerformanceTimingString(domContentLoadedEventEnd - domContentLoadedEventStart), onLoadTime: toFaroPerformanceTimingString(loadEventEnd - loadEventStart),
// For navigation entries we can calculate the TTFB based on activationStart. We overwrite the TTFB value coming with the resource entry.
// For more accuracy on prerendered pages page we calculate relative top the activationStart instead of the start of the navigation.
// clamp to 0 if activationStart occurs after first byte is received.
ttfb: toFaroPerformanceTimingString(Math.max(responseStart - (activationStart !== null && activationStart !== void 0 ? activationStart : 0), 0)), type: type });
}
function getDocumentParsingTime() {
var _a;
if (((_a = performance.timing) === null || _a === void 0 ? void 0 : _a.domLoading) != null) {
// the browser is about to start parsing the first received bytes of the HTML document.
// This property is deprecated but there isn't a really good alternative atm.
// For now we stick with domLoading and keep researching a better alternative.
return performance.timing.domLoading - performance.timeOrigin;
}
return null;
}
function toFaroPerformanceTimingString(v) {
if (v == null) {
return faro_core_1.unknownString;
}
if (typeof v === 'number') {
return Math.round(v > 0 ? v : 0).toString();
}
return v.toString();
}
//# sourceMappingURL=performanceUtils.js.map