|
14 | 14 | * log RUM if part of the sample. |
15 | 15 | * @param {string} checkpoint identifies the checkpoint in funnel |
16 | 16 | * @param {Object} data additional data for RUM sample |
| 17 | + * @param {string} data.source DOM node that is the source of a checkpoint event, |
| 18 | + * identified by #id or .classname |
| 19 | + * @param {string} data.target subject of the checkpoint event, |
| 20 | + * for instance the href of a link, or a search term |
17 | 21 | */ |
18 | | - |
19 | 22 | export function sampleRUM(checkpoint, data = {}) { |
| 23 | + const SESSION_STORAGE_KEY = 'aem-rum'; |
| 24 | + sampleRUM.baseURL = sampleRUM.baseURL || new URL(window.RUM_BASE == null ? 'https://rum.hlx.page' : window.RUM_BASE, window.location); |
| 25 | + sampleRUM.defer = sampleRUM.defer || []; |
| 26 | + const defer = (fnname) => { |
| 27 | + sampleRUM[fnname] = sampleRUM[fnname] |
| 28 | + || ((...args) => sampleRUM.defer.push({ fnname, args })); |
| 29 | + }; |
| 30 | + sampleRUM.drain = sampleRUM.drain |
| 31 | + || ((dfnname, fn) => { |
| 32 | + sampleRUM[dfnname] = fn; |
| 33 | + sampleRUM.defer |
| 34 | + .filter(({ fnname }) => dfnname === fnname) |
| 35 | + .forEach(({ fnname, args }) => sampleRUM[fnname](...args)); |
| 36 | + }); |
| 37 | + sampleRUM.always = sampleRUM.always || []; |
| 38 | + sampleRUM.always.on = (chkpnt, fn) => { |
| 39 | + sampleRUM.always[chkpnt] = fn; |
| 40 | + }; |
| 41 | + sampleRUM.on = (chkpnt, fn) => { |
| 42 | + sampleRUM.cases[chkpnt] = fn; |
| 43 | + }; |
| 44 | + defer('observe'); |
| 45 | + defer('cwv'); |
20 | 46 | try { |
21 | 47 | window.hlx = window.hlx || {}; |
22 | 48 | if (!window.hlx.rum) { |
23 | 49 | const usp = new URLSearchParams(window.location.search); |
24 | 50 | const weight = (usp.get('rum') === 'on') ? 1 : 100; // with parameter, weight is 1. Defaults to 100. |
25 | | - // eslint-disable-next-line no-bitwise |
26 | | - const hashCode = (s) => s.split('').reduce((a, b) => (((a << 5) - a) + b.charCodeAt(0)) | 0, 0); |
27 | | - const id = `${hashCode(window.location.href)}-${new Date().getTime()}-${Math.random().toString(16).substr(2, 14)}`; |
| 51 | + const id = Array.from({ length: 75 }, (_, i) => String.fromCharCode(48 + i)).filter((a) => /\d|[A-Z]/i.test(a)).filter(() => Math.random() * 75 > 70).join(''); |
28 | 52 | const random = Math.random(); |
29 | 53 | const isSelected = (random * weight < 1); |
30 | | - // eslint-disable-next-line object-curly-newline |
31 | | - window.hlx.rum = { weight, id, random, isSelected }; |
| 54 | + const firstReadTime = window.performance ? window.performance.timeOrigin : Date.now(); |
| 55 | + const urlSanitizers = { |
| 56 | + full: () => window.location.href, |
| 57 | + origin: () => window.location.origin, |
| 58 | + path: () => window.location.href.replace(/\?.*$/, ''), |
| 59 | + }; |
| 60 | + // eslint-disable-next-line max-len |
| 61 | + const rumSessionStorage = sessionStorage.getItem(SESSION_STORAGE_KEY) ? JSON.parse(sessionStorage.getItem(SESSION_STORAGE_KEY)) : {}; |
| 62 | + // eslint-disable-next-line max-len |
| 63 | + rumSessionStorage.pages = (rumSessionStorage.pages ? rumSessionStorage.pages : 0) + 1 + /* noise */ (Math.floor(Math.random() * 20) - 10); |
| 64 | + sessionStorage.setItem(SESSION_STORAGE_KEY, JSON.stringify(rumSessionStorage)); |
| 65 | + // eslint-disable-next-line object-curly-newline, max-len |
| 66 | + window.hlx.rum = { weight, id, random, isSelected, firstReadTime, sampleRUM, sanitizeURL: urlSanitizers[window.hlx.RUM_MASK_URL || 'path'], rumSessionStorage }; |
32 | 67 | } |
33 | | - const { random, weight, id } = window.hlx.rum; |
34 | | - if (random && (random * weight < 1)) { |
35 | | - const sendPing = () => { |
| 68 | + |
| 69 | + const { weight, id, firstReadTime } = window.hlx.rum; |
| 70 | + if (window.hlx && window.hlx.rum && window.hlx.rum.isSelected) { |
| 71 | + const knownProperties = ['weight', 'id', 'referer', 'checkpoint', 't', 'source', 'target', 'cwv', 'CLS', 'FID', 'LCP', 'INP', 'TTFB']; |
| 72 | + const sendPing = (pdata = data) => { |
| 73 | + // eslint-disable-next-line max-len |
| 74 | + const t = Math.round(window.performance ? window.performance.now() : (Date.now() - firstReadTime)); |
36 | 75 | // eslint-disable-next-line object-curly-newline, max-len, no-use-before-define |
37 | | - const body = JSON.stringify({ weight, id, referer: window.location.href, generation: RUM_GENERATION, checkpoint, ...data }); |
38 | | - const url = `https://rum.hlx.page/.rum/${weight}`; |
39 | | - // eslint-disable-next-line no-unused-expressions |
| 76 | + const body = JSON.stringify({ weight, id, referer: window.hlx.rum.sanitizeURL(), checkpoint, t, ...data }, knownProperties); |
| 77 | + const url = new URL(`.rum/${weight}`, sampleRUM.baseURL).href; |
40 | 78 | navigator.sendBeacon(url, body); |
| 79 | + // eslint-disable-next-line no-console |
| 80 | + console.debug(`ping:${checkpoint}`, pdata); |
41 | 81 | }; |
42 | | - sendPing(); |
43 | | - // special case CWV |
44 | | - if (checkpoint === 'cwv') { |
45 | | - // eslint-disable-next-line import/no-unresolved |
46 | | - import('./web-vitals-module-2-1-2.js').then((mod) => { |
47 | | - const storeCWV = (measurement) => { |
48 | | - data.cwv = {}; |
49 | | - data.cwv[measurement.name] = measurement.value; |
50 | | - sendPing(); |
51 | | - }; |
52 | | - mod.getCLS(storeCWV); |
53 | | - mod.getFID(storeCWV); |
54 | | - mod.getLCP(storeCWV); |
55 | | - }); |
| 82 | + sampleRUM.cases = sampleRUM.cases || { |
| 83 | + load: () => sampleRUM('pagesviewed', { source: window.hlx.rum.rumSessionStorage.pages }) || true, |
| 84 | + cwv: () => sampleRUM.cwv(data) || true, |
| 85 | + lazy: () => { |
| 86 | + // use classic script to avoid CORS issues |
| 87 | + const script = document.createElement('script'); |
| 88 | + script.src = new URL('.rum/@adobe/helix-rum-enhancer@^1/src/index.js', sampleRUM.baseURL).href; |
| 89 | + document.head.appendChild(script); |
| 90 | + return true; |
| 91 | + }, |
| 92 | + }; |
| 93 | + sendPing(data); |
| 94 | + if (sampleRUM.cases[checkpoint]) { |
| 95 | + sampleRUM.cases[checkpoint](); |
56 | 96 | } |
57 | 97 | } |
58 | | - } catch (e) { |
| 98 | + if (sampleRUM.always[checkpoint]) { |
| 99 | + sampleRUM.always[checkpoint](data); |
| 100 | + } |
| 101 | + } catch (error) { |
59 | 102 | // something went wrong |
60 | 103 | } |
61 | 104 | } |
@@ -670,6 +713,10 @@ async function loadLazy(doc) { |
670 | 713 |
|
671 | 714 | loadCSS(`${window.hlx.codeBasePath}/styles/lazy-styles.css`); |
672 | 715 | addFavIcon(`${window.hlx.codeBasePath}/icon.svg`); |
| 716 | + |
| 717 | + sampleRUM('lazy'); |
| 718 | + sampleRUM.observe(main.querySelectorAll('div[data-block-name]')); |
| 719 | + sampleRUM.observe(main.querySelectorAll('picture > img')); |
673 | 720 | } |
674 | 721 |
|
675 | 722 | /** |
|
0 commit comments