UNPKG

@grafana/faro-web-sdk

Version:

Faro instrumentations, metas, transports for web.

149 lines 8.64 kB
"use strict"; 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