|
| 1 | +/* |
| 2 | + * Copyright 2024 Adobe. All rights reserved. |
| 3 | + * This file is licensed to you under the Apache License, Version 2.0 (the "License"); |
| 4 | + * you may not use this file except in compliance with the License. You may obtain a copy |
| 5 | + * of the License at http://www.apache.org/licenses/LICENSE-2.0 |
| 6 | + * |
| 7 | + * Unless required by applicable law or agreed to in writing, software distributed under |
| 8 | + * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS |
| 9 | + * OF ANY KIND, either express or implied. See the License for the specific language |
| 10 | + * governing permissions and limitations under the License. |
| 11 | + */ |
| 12 | + |
| 13 | +// /* eslint-disable max-classes-per-file */ |
| 14 | +(function () { |
| 15 | + 'use strict'; |
| 16 | + |
| 17 | + /* eslint-env browser */ |
| 18 | + function sampleRUM(checkpoint, data) { |
| 19 | + // eslint-disable-next-line max-len |
| 20 | + const timeShift = () => (window.performance ? window.performance.now() : Date.now() - window.hlx.rum.firstReadTime); |
| 21 | + try { |
| 22 | + window.hlx = window.hlx || {}; |
| 23 | + if (!window.hlx.rum || !window.hlx.rum.collector) { |
| 24 | + sampleRUM.enhance = () => {}; |
| 25 | + const param = new URLSearchParams(window.location.search).get('rum'); |
| 26 | + const weight = (param === 'on' && 1) |
| 27 | + || (window.SAMPLE_PAGEVIEWS_AT_RATE === 'high' && 10) |
| 28 | + || (window.SAMPLE_PAGEVIEWS_AT_RATE === 'low' && 1000) |
| 29 | + || 100; |
| 30 | + const id = (window.hlx.rum && window.hlx.rum.id) |
| 31 | + || crypto.randomUUID().slice(-9); |
| 32 | + const isSelected = (window.hlx.rum && window.hlx.rum.isSelected) || ((param !== 'off') && (Math.random() * weight < 1)); |
| 33 | + // eslint-disable-next-line object-curly-newline, max-len |
| 34 | + window.hlx.rum = { weight, id, isSelected, firstReadTime: window.performance ? window.performance.timeOrigin : Date.now(), sampleRUM, queue: [], collector: (...args) => window.hlx.rum.queue.push(args) }; |
| 35 | + if (isSelected) { |
| 36 | + const dataFromErrorObj = (error) => { |
| 37 | + const errData = { source: 'undefined error' }; |
| 38 | + try { |
| 39 | + errData.target = error.toString(); |
| 40 | + errData.source = error.stack.split('\n') |
| 41 | + .filter((line) => line.match(/https?:\/\//)).shift() |
| 42 | + .replace(/at ([^ ]+) \((.+)\)/, '$1@$2') |
| 43 | + .replace(/ at /, '@') |
| 44 | + .trim(); |
| 45 | + } catch (err) { /* error structure was not as expected */ } |
| 46 | + return errData; |
| 47 | + }; |
| 48 | + |
| 49 | + window.addEventListener('error', ({ error }) => { |
| 50 | + const errData = dataFromErrorObj(error); |
| 51 | + sampleRUM('error', errData); |
| 52 | + }); |
| 53 | + |
| 54 | + window.addEventListener('unhandledrejection', ({ reason }) => { |
| 55 | + let errData = { |
| 56 | + source: 'Unhandled Rejection', |
| 57 | + target: reason || 'Unknown', |
| 58 | + }; |
| 59 | + if (reason instanceof Error) { |
| 60 | + errData = dataFromErrorObj(reason); |
| 61 | + } |
| 62 | + sampleRUM('error', errData); |
| 63 | + }); |
| 64 | + |
| 65 | + sampleRUM.baseURL = sampleRUM.baseURL || new URL(window.RUM_BASE || '/', new URL('https://rum.hlx.page')); |
| 66 | + sampleRUM.collectBaseURL = sampleRUM.collectBaseURL || sampleRUM.baseURL; |
| 67 | + sampleRUM.sendPing = (ck, time, pingData = {}) => { |
| 68 | + // eslint-disable-next-line max-len, object-curly-newline |
| 69 | + const rumData = JSON.stringify({ weight, id, referer: window.location.href, checkpoint: ck, t: time, ...pingData }); |
| 70 | + const urlParams = window.RUM_PARAMS ? (new URLSearchParams(window.RUM_PARAMS).toString() || '') : ''; |
| 71 | + const { href: url, origin } = new URL(`.rum/${weight}${urlParams ? `?${urlParams}` : ''}`, sampleRUM.collectBaseURL); |
| 72 | + const body = origin === window.location.origin ? new Blob([rumData], { type: 'application/json' }) : rumData; |
| 73 | + navigator.sendBeacon(url, body); |
| 74 | + // eslint-disable-next-line no-console |
| 75 | + console.debug(`ping:${ck}`, pingData); |
| 76 | + }; |
| 77 | + sampleRUM.sendPing('top', timeShift()); |
| 78 | + |
| 79 | + sampleRUM.enhance = () => { |
| 80 | + // only enhance once |
| 81 | + if (document.querySelector('script[src*="rum-enhancer"]')) return; |
| 82 | + const { enhancerVersion, enhancerHash } = sampleRUM.enhancerContext || {}; |
| 83 | + const script = document.createElement('script'); |
| 84 | + if (enhancerHash) { |
| 85 | + script.integrity = enhancerHash; |
| 86 | + script.setAttribute('crossorigin', 'anonymous'); |
| 87 | + } |
| 88 | + script.src = new URL(`.rum/@adobe/helix-rum-enhancer@${enhancerVersion || '^2'}/src/index.js`, sampleRUM.baseURL).href; |
| 89 | + document.head.appendChild(script); |
| 90 | + }; |
| 91 | + if (!window.hlx.RUM_MANUAL_ENHANCE) { |
| 92 | + sampleRUM.enhance(); |
| 93 | + } |
| 94 | + } |
| 95 | + } |
| 96 | + if (window.hlx.rum && window.hlx.rum.isSelected && checkpoint) { |
| 97 | + window.hlx.rum.collector(checkpoint, data, timeShift()); |
| 98 | + } |
| 99 | + document.dispatchEvent(new CustomEvent('rum', { detail: { checkpoint, data } })); |
| 100 | + } catch (error) { |
| 101 | + // something went awry |
| 102 | + } |
| 103 | + } |
| 104 | + |
| 105 | + try { |
| 106 | + const scriptSrc = (document.currentScript && document.currentScript.src) |
| 107 | + ? new URL(document.currentScript.src, window.location.origin).origin : null; |
| 108 | + // eslint-disable-next-line max-len |
| 109 | + const dataAttrs = (document.currentScript && document.currentScript.dataset) ? document.currentScript.dataset : {}; |
| 110 | + const { |
| 111 | + postPath, status, enhancerVersion, enhancerHash, ...scriptParams |
| 112 | + } = dataAttrs; |
| 113 | + const base = scriptSrc && postPath ? new URL(postPath, scriptSrc) : scriptSrc; |
| 114 | + sampleRUM.enhancerContext = { enhancerVersion, enhancerHash }; |
| 115 | + window.RUM_BASE = window.RUM_BASE || base; |
| 116 | + window.RUM_PARAMS = window.RUM_PARAMS || scriptParams; |
| 117 | + |
| 118 | + const [navigation] = (window.performance && window.performance.getEntriesByType('navigation')) || []; |
| 119 | + const is404 = status === '404' || (navigation && navigation.name === window.location.href |
| 120 | + && navigation.responseStatus === 404); |
| 121 | + |
| 122 | + if (is404) { |
| 123 | + sampleRUM('404', { source: document.referrer }); |
| 124 | + } else { |
| 125 | + sampleRUM(); |
| 126 | + } |
| 127 | + } catch (error) { |
| 128 | + // something went wrong |
| 129 | + } |
| 130 | +}()); |
0 commit comments