UNPKG

@grafana/faro-web-sdk

Version:

Faro instrumentations, metas, transports for web.

143 lines 7.21 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.getSpanContextFromServerTiming = getSpanContextFromServerTiming; exports.performanceObserverSupported = performanceObserverSupported; exports.onDocumentReady = onDocumentReady; exports.includePerformanceEntry = includePerformanceEntry; exports.createFaroResourceTiming = createFaroResourceTiming; exports.createFaroNavigationTiming = createFaroNavigationTiming; const faro_core_1 = require("@grafana/faro-core"); const w3cTraceparentFormat = /^00-[a-f0-9]{32}-[a-f0-9]{16}-[0-9]{1,2}$/; // Extract traceparent from serverTiming, if present function getSpanContextFromServerTiming(serverTimings = []) { for (const serverEntry of serverTimings) { if (serverEntry.name === 'traceparent') { if (!w3cTraceparentFormat.test(serverEntry.description)) { continue; } const [, traceId, spanId] = serverEntry.description.split('-'); if (traceId != null && spanId != null) { return { traceId, spanId }; } break; } } return undefined; } function performanceObserverSupported() { return 'PerformanceObserver' in window; } function onDocumentReady(handleReady) { if (document.readyState === 'complete') { handleReady(); } else { const readyStateCompleteHandler = () => { if (document.readyState === 'complete') { handleReady(); document.removeEventListener('readystatechange', readyStateCompleteHandler); } }; document.addEventListener('readystatechange', readyStateCompleteHandler); } } function includePerformanceEntry(performanceEntryJSON, allowProps = {}) { for (const [allowPropKey, allowPropValue] of Object.entries(allowProps)) { const 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) { const { connectEnd, connectStart, decodedBodySize, domainLookupEnd, domainLookupStart, duration, encodedBodySize, fetchStart, initiatorType, name, nextHopProtocol, redirectEnd, redirectStart, // @ts-expect-error the renderBlockingStatus property is not available in all browsers renderBlockingStatus: rbs, requestStart, responseEnd, responseStart, responseStatus, secureConnectionStart, transferSize, workerStart, } = resourceEntryRaw; return { name: name, httpHost: getHostFromUrl(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() { let 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) { const { activationStart, domComplete, domContentLoadedEventEnd, domContentLoadedEventStart, domInteractive, fetchStart, loadEventEnd, loadEventStart, responseStart, type, } = navigationEntryRaw; const parserStart = getDocumentParsingTime(); return Object.assign(Object.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 getHostFromUrl(url) { try { return new URL(url).host || faro_core_1.unknownString; } catch (_a) { return faro_core_1.unknownString; } } 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