@newrelic/browser-agent 1.239.1 → 1.241.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (178) hide show
  1. package/README.md +4 -0
  2. package/dist/cjs/cdn/pro.js +3 -2
  3. package/dist/cjs/cdn/spa.js +4 -3
  4. package/dist/cjs/common/config/state/init.js +25 -17
  5. package/dist/cjs/common/constants/env.cdn.js +1 -1
  6. package/dist/cjs/common/constants/env.npm.js +1 -1
  7. package/dist/cjs/common/constants/runtime.js +9 -5
  8. package/dist/cjs/common/harvest/harvest.js +5 -3
  9. package/dist/cjs/common/vitals/constants.js +17 -0
  10. package/dist/cjs/common/vitals/cumulative-layout-shift.js +27 -0
  11. package/dist/cjs/common/vitals/first-contentful-paint.js +49 -0
  12. package/dist/cjs/common/vitals/first-input-delay.js +32 -0
  13. package/dist/{esm/features/page_view_timing → cjs/common/vitals}/first-paint.js +19 -17
  14. package/dist/cjs/common/vitals/interaction-to-next-paint.js +29 -0
  15. package/dist/cjs/common/vitals/largest-contentful-paint.js +41 -0
  16. package/dist/cjs/common/vitals/long-task.js +64 -0
  17. package/dist/cjs/common/vitals/time-to-first-byte.js +36 -0
  18. package/dist/cjs/common/vitals/vital-metric.js +71 -0
  19. package/dist/cjs/features/ajax/aggregate/index.js +4 -1
  20. package/dist/cjs/features/metrics/aggregate/index.js +17 -7
  21. package/dist/cjs/features/page_view_event/aggregate/index.js +18 -40
  22. package/dist/cjs/features/page_view_event/constants.js +2 -8
  23. package/dist/cjs/features/page_view_event/instrument/index.js +0 -22
  24. package/dist/cjs/features/page_view_timing/aggregate/index.js +36 -147
  25. package/dist/cjs/features/page_view_timing/instrument/index.js +0 -3
  26. package/dist/cjs/features/session_replay/aggregate/index.js +81 -35
  27. package/dist/cjs/features/session_trace/aggregate/index.js +13 -1
  28. package/dist/cjs/features/spa/aggregate/index.js +4 -3
  29. package/dist/cjs/loaders/agent.js +3 -0
  30. package/dist/cjs/loaders/api/api.js +2 -0
  31. package/dist/cjs/loaders/api/apiAsync.js +4 -2
  32. package/dist/cjs/loaders/browser-agent.js +2 -1
  33. package/dist/cjs/loaders/configure/configure.js +13 -1
  34. package/dist/cjs/loaders/configure/public-path.js +13 -0
  35. package/dist/cjs/loaders/configure/public-path.npm.js +10 -0
  36. package/dist/esm/cdn/pro.js +2 -1
  37. package/dist/esm/cdn/spa.js +2 -1
  38. package/dist/esm/common/config/state/init.js +25 -17
  39. package/dist/esm/common/constants/env.cdn.js +1 -1
  40. package/dist/esm/common/constants/env.npm.js +1 -1
  41. package/dist/esm/common/constants/runtime.js +5 -3
  42. package/dist/esm/common/harvest/harvest.js +6 -4
  43. package/dist/esm/common/vitals/constants.js +10 -0
  44. package/dist/esm/common/vitals/cumulative-layout-shift.js +20 -0
  45. package/dist/esm/common/vitals/first-contentful-paint.js +41 -0
  46. package/dist/esm/common/vitals/first-input-delay.js +25 -0
  47. package/dist/{cjs/features/page_view_timing → esm/common/vitals}/first-paint.js +12 -24
  48. package/dist/esm/common/vitals/interaction-to-next-paint.js +22 -0
  49. package/dist/esm/common/vitals/largest-contentful-paint.js +34 -0
  50. package/dist/esm/common/vitals/long-task.js +57 -0
  51. package/dist/esm/common/vitals/time-to-first-byte.js +29 -0
  52. package/dist/esm/common/vitals/vital-metric.js +64 -0
  53. package/dist/esm/features/ajax/aggregate/index.js +4 -1
  54. package/dist/esm/features/metrics/aggregate/index.js +18 -8
  55. package/dist/esm/features/page_view_event/aggregate/index.js +20 -42
  56. package/dist/esm/features/page_view_event/constants.js +1 -4
  57. package/dist/esm/features/page_view_event/instrument/index.js +0 -22
  58. package/dist/esm/features/page_view_timing/aggregate/index.js +37 -148
  59. package/dist/esm/features/page_view_timing/instrument/index.js +0 -3
  60. package/dist/esm/features/session_replay/aggregate/index.js +81 -35
  61. package/dist/esm/features/session_trace/aggregate/index.js +13 -1
  62. package/dist/esm/features/spa/aggregate/index.js +4 -3
  63. package/dist/esm/loaders/agent.js +2 -0
  64. package/dist/esm/loaders/api/api.js +2 -0
  65. package/dist/esm/loaders/api/apiAsync.js +5 -3
  66. package/dist/esm/loaders/browser-agent.js +2 -1
  67. package/dist/esm/loaders/configure/configure.js +13 -1
  68. package/dist/esm/loaders/configure/public-path.js +6 -0
  69. package/dist/esm/loaders/configure/public-path.npm.js +3 -0
  70. package/dist/types/common/config/state/init.d.ts.map +1 -1
  71. package/dist/types/common/constants/runtime.d.ts +3 -1
  72. package/dist/types/common/constants/runtime.d.ts.map +1 -1
  73. package/dist/types/common/harvest/harvest.d.ts +0 -1
  74. package/dist/types/common/harvest/harvest.d.ts.map +1 -1
  75. package/dist/types/common/vitals/constants.d.ts +11 -0
  76. package/dist/types/common/vitals/constants.d.ts.map +1 -0
  77. package/dist/types/common/vitals/cumulative-layout-shift.d.ts +3 -0
  78. package/dist/types/common/vitals/cumulative-layout-shift.d.ts.map +1 -0
  79. package/dist/types/common/vitals/first-contentful-paint.d.ts +3 -0
  80. package/dist/types/common/vitals/first-contentful-paint.d.ts.map +1 -0
  81. package/dist/types/common/vitals/first-input-delay.d.ts +3 -0
  82. package/dist/types/common/vitals/first-input-delay.d.ts.map +1 -0
  83. package/dist/types/common/vitals/first-paint.d.ts +3 -0
  84. package/dist/types/common/vitals/first-paint.d.ts.map +1 -0
  85. package/dist/types/common/vitals/interaction-to-next-paint.d.ts +3 -0
  86. package/dist/types/common/vitals/interaction-to-next-paint.d.ts.map +1 -0
  87. package/dist/types/common/vitals/largest-contentful-paint.d.ts +3 -0
  88. package/dist/types/common/vitals/largest-contentful-paint.d.ts.map +1 -0
  89. package/dist/types/common/vitals/long-task.d.ts +3 -0
  90. package/dist/types/common/vitals/long-task.d.ts.map +1 -0
  91. package/dist/types/common/vitals/time-to-first-byte.d.ts +3 -0
  92. package/dist/types/common/vitals/time-to-first-byte.d.ts.map +1 -0
  93. package/dist/types/common/vitals/vital-metric.d.ts +18 -0
  94. package/dist/types/common/vitals/vital-metric.d.ts.map +1 -0
  95. package/dist/types/features/ajax/aggregate/index.d.ts.map +1 -1
  96. package/dist/types/features/metrics/aggregate/index.d.ts.map +1 -1
  97. package/dist/types/features/page_view_event/aggregate/index.d.ts +3 -2
  98. package/dist/types/features/page_view_event/aggregate/index.d.ts.map +1 -1
  99. package/dist/types/features/page_view_event/constants.d.ts +0 -3
  100. package/dist/types/features/page_view_event/constants.d.ts.map +1 -1
  101. package/dist/types/features/page_view_event/instrument/index.d.ts.map +1 -1
  102. package/dist/types/features/page_view_timing/aggregate/index.d.ts +1 -3
  103. package/dist/types/features/page_view_timing/aggregate/index.d.ts.map +1 -1
  104. package/dist/types/features/page_view_timing/instrument/index.d.ts.map +1 -1
  105. package/dist/types/features/session_replay/aggregate/index.d.ts +16 -4
  106. package/dist/types/features/session_replay/aggregate/index.d.ts.map +1 -1
  107. package/dist/types/features/session_trace/aggregate/index.d.ts +9 -1
  108. package/dist/types/features/session_trace/aggregate/index.d.ts.map +1 -1
  109. package/dist/types/features/spa/aggregate/index.d.ts.map +1 -1
  110. package/dist/types/loaders/agent.d.ts.map +1 -1
  111. package/dist/types/loaders/api/api.d.ts.map +1 -1
  112. package/dist/types/loaders/api/apiAsync.d.ts.map +1 -1
  113. package/dist/types/loaders/browser-agent.d.ts.map +1 -1
  114. package/dist/types/loaders/configure/configure.d.ts.map +1 -1
  115. package/dist/types/loaders/configure/public-path.d.ts +2 -0
  116. package/dist/types/loaders/configure/public-path.d.ts.map +1 -0
  117. package/dist/types/loaders/configure/public-path.npm.d.ts +2 -0
  118. package/dist/types/loaders/configure/public-path.npm.d.ts.map +1 -0
  119. package/package.json +2 -3
  120. package/src/cdn/pro.js +2 -0
  121. package/src/cdn/spa.js +2 -0
  122. package/src/common/config/state/init.js +21 -17
  123. package/src/common/constants/runtime.js +7 -3
  124. package/src/common/constants/runtime.test.js +8 -0
  125. package/src/common/harvest/harvest-scheduler.test.js +2 -2
  126. package/src/common/harvest/harvest.js +6 -4
  127. package/src/common/harvest/harvest.test.js +17 -0
  128. package/src/common/vitals/__mocks__/web-vitals.js +19 -0
  129. package/src/common/vitals/constants.js +10 -0
  130. package/src/common/vitals/cumulative-layout-shift.js +13 -0
  131. package/src/common/vitals/cumulative-layout-shift.test.js +71 -0
  132. package/src/common/vitals/first-contentful-paint.js +31 -0
  133. package/src/common/vitals/first-contentful-paint.test.js +124 -0
  134. package/src/common/vitals/first-input-delay.js +20 -0
  135. package/src/common/vitals/first-input-delay.test.js +88 -0
  136. package/src/{features/page_view_timing → common/vitals}/first-paint.js +11 -17
  137. package/src/common/vitals/first-paint.test.js +127 -0
  138. package/src/common/vitals/interaction-to-next-paint.js +13 -0
  139. package/src/common/vitals/interaction-to-next-paint.test.js +74 -0
  140. package/src/common/vitals/largest-contentful-paint.js +29 -0
  141. package/src/common/vitals/largest-contentful-paint.test.js +94 -0
  142. package/src/common/vitals/long-task.js +52 -0
  143. package/src/common/vitals/long-task.test.js +122 -0
  144. package/src/common/vitals/time-to-first-byte.js +21 -0
  145. package/src/common/vitals/time-to-first-byte.test.js +147 -0
  146. package/src/common/vitals/vital-metric.js +60 -0
  147. package/src/common/vitals/vital-metric.test.js +171 -0
  148. package/src/features/ajax/aggregate/index.js +5 -1
  149. package/src/features/metrics/aggregate/index.js +11 -4
  150. package/src/features/page_view_event/aggregate/index.js +20 -43
  151. package/src/features/page_view_event/constants.js +0 -3
  152. package/src/features/page_view_event/instrument/index.js +0 -21
  153. package/src/features/page_view_timing/aggregate/index.component-test.js +86 -0
  154. package/src/features/page_view_timing/aggregate/index.js +31 -108
  155. package/src/features/page_view_timing/instrument/index.js +0 -3
  156. package/src/features/session_replay/aggregate/index.component-test.js +10 -10
  157. package/src/features/session_replay/aggregate/index.js +62 -29
  158. package/src/features/session_trace/aggregate/index.js +15 -1
  159. package/src/features/spa/aggregate/index.js +4 -3
  160. package/src/loaders/agent.js +2 -0
  161. package/src/loaders/api/api.js +2 -0
  162. package/src/loaders/api/apiAsync.js +5 -4
  163. package/src/loaders/browser-agent.js +3 -1
  164. package/src/loaders/configure/configure.js +15 -7
  165. package/src/loaders/configure/public-path.js +6 -0
  166. package/src/loaders/configure/public-path.npm.js +4 -0
  167. package/dist/cjs/common/metrics/paint-metrics.js +0 -13
  168. package/dist/cjs/features/page_view_timing/long-tasks.js +0 -75
  169. package/dist/esm/common/metrics/paint-metrics.js +0 -6
  170. package/dist/esm/features/page_view_timing/long-tasks.js +0 -69
  171. package/dist/types/common/metrics/paint-metrics.d.ts +0 -2
  172. package/dist/types/common/metrics/paint-metrics.d.ts.map +0 -1
  173. package/dist/types/features/page_view_timing/first-paint.d.ts +0 -2
  174. package/dist/types/features/page_view_timing/first-paint.d.ts.map +0 -1
  175. package/dist/types/features/page_view_timing/long-tasks.d.ts +0 -2
  176. package/dist/types/features/page_view_timing/long-tasks.d.ts.map +0 -1
  177. package/src/common/metrics/paint-metrics.js +0 -6
  178. package/src/features/page_view_timing/long-tasks.js +0 -60
@@ -14,6 +14,7 @@ var _aggregateBase = require("../../utils/aggregate-base");
14
14
  var _sharedChannel = require("../../../common/constants/shared-channel");
15
15
  var _encode = require("../../../common/url/encode");
16
16
  var _console = require("../../../common/util/console");
17
+ var _runtime = require("../../../common/constants/runtime");
17
18
  function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function (nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); }
18
19
  function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; } /*
19
20
  * Copyright 2023 New Relic Corporation. All rights reserved.
@@ -72,17 +73,30 @@ class Aggregate extends _aggregateBase.AggregateBase {
72
73
  * -- When visibility changes from "hidden" -> "visible", it must capture a full snapshot for the replay to work correctly across tabs
73
74
  */
74
75
  this.hasSnapshot = false;
76
+ /** Payload metadata -- Should indicate that the payload being sent has a meta node. The meta node should always precede a snapshot node. */
77
+ this.hasMeta = false;
75
78
  /** Payload metadata -- Should indicate that the payload being sent contains an error. Used for query/filter purposes in UI */
76
79
  this.hasError = false;
77
80
 
78
- /** Payload metadata -- Should indicate when a replay blob started recording. Resets each time a harvest occurs. */
81
+ /** Payload metadata -- Should indicate when a replay blob started recording. Resets each time a harvest occurs.
82
+ * cycle timestamps are used as fallbacks if event timestamps cannot be used
83
+ */
79
84
  this.timestamp = {
80
- first: undefined,
81
- last: undefined
85
+ event: {
86
+ first: undefined,
87
+ last: undefined
88
+ },
89
+ cycle: {
90
+ first: undefined,
91
+ last: undefined
92
+ }
82
93
  };
83
94
 
84
95
  /** A value which increments with every new mutation node reported. Resets after a harvest is sent */
85
96
  this.payloadBytesEstimation = 0;
97
+
98
+ /** Hold on to the last meta node, so that it can be re-inserted if the meta and snapshot nodes are broken up due to harvesting */
99
+ this.lastMeta = undefined;
86
100
  const shouldSetup = (0, _config.getConfigurationValue)(agentIdentifier, 'privacy.cookies_enabled') === true && (0, _config.getConfigurationValue)(agentIdentifier, 'session_trace.enabled') === true;
87
101
 
88
102
  /** The method to stop recording. This defaults to a noop, but is overwritten once the recording library is imported and initialized */
@@ -132,7 +146,7 @@ class Aggregate extends _aggregateBase.AggregateBase {
132
146
  }, this.featureName, this.ee);
133
147
  this.waitForFlags(['sr']).then(_ref => {
134
148
  let [flagOn] = _ref;
135
- return this.initializeRecording(flagOn, Math.random() < (0, _config.getConfigurationValue)(this.agentIdentifier, 'session_replay.errorSampleRate'), Math.random() < (0, _config.getConfigurationValue)(this.agentIdentifier, 'session_replay.sampleRate'));
149
+ return this.initializeRecording(flagOn, Math.random() * 100 < (0, _config.getConfigurationValue)(this.agentIdentifier, 'session_replay.error_sampling_rate'), Math.random() * 100 < (0, _config.getConfigurationValue)(this.agentIdentifier, 'session_replay.sampling_rate'));
136
150
  }).then(() => _sharedChannel.sharedChannel.onReplayReady(this.mode)); // notify watchers that replay started with the mode
137
151
 
138
152
  this.drain();
@@ -173,6 +187,12 @@ class Aggregate extends _aggregateBase.AggregateBase {
173
187
  if (this.mode === _sessionEntity.MODE.ERROR && this.errorNoticed) {
174
188
  this.mode = _sessionEntity.MODE.FULL;
175
189
  }
190
+ try {
191
+ // Do not change the webpackChunkName or it will break the webpack nrba-chunking plugin
192
+ recorder = (await Promise.resolve().then(() => _interopRequireWildcard(require( /* webpackChunkName: "recorder" */'rrweb')))).record;
193
+ } catch (err) {
194
+ return this.abort();
195
+ }
176
196
 
177
197
  // FULL mode records AND reports from the beginning, while ERROR mode only records (but does not report).
178
198
  // ERROR mode will do this until an error is thrown, and then switch into FULL mode.
@@ -181,12 +201,6 @@ class Aggregate extends _aggregateBase.AggregateBase {
181
201
  // We only report (harvest) in FULL mode
182
202
  this.scheduler.startTimer(this.harvestTimeSeconds);
183
203
  }
184
- try {
185
- // Do not change the webpackChunkName or it will break the webpack nrba-chunking plugin
186
- recorder = (await Promise.resolve().then(() => _interopRequireWildcard(require( /* webpackChunkName: "recorder" */'rrweb')))).record;
187
- } catch (err) {
188
- return this.abort();
189
- }
190
204
  try {
191
205
  // Do not change the webpackChunkName or it will break the webpack nrba-chunking plugin
192
206
  const {
@@ -221,6 +235,8 @@ class Aggregate extends _aggregateBase.AggregateBase {
221
235
  getHarvestContents() {
222
236
  const agentRuntime = (0, _config.getRuntime)(this.agentIdentifier);
223
237
  const info = (0, _config.getInfo)(this.agentIdentifier);
238
+ const firstTimestamp = this.timestamp.event.first || this.timestamp.cycle.first;
239
+ const lastTimestamp = this.timestamp.event.last || this.timestamp.cycle.last;
224
240
  return {
225
241
  qs: {
226
242
  browser_monitoring_key: info.licenseKey,
@@ -231,11 +247,12 @@ class Aggregate extends _aggregateBase.AggregateBase {
231
247
  ...(this.shouldCompress && {
232
248
  content_encoding: 'gzip'
233
249
  }),
234
- 'replay.firstTimestamp': this.timestamp.first,
235
- 'replay.lastTimestamp': this.timestamp.last,
236
- 'replay.durationMs': this.timestamp.last - this.timestamp.first,
250
+ 'replay.firstTimestamp': firstTimestamp,
251
+ 'replay.lastTimestamp': lastTimestamp,
252
+ 'replay.durationMs': lastTimestamp - firstTimestamp,
237
253
  agentVersion: agentRuntime.version,
238
254
  session: agentRuntime.session.state.value,
255
+ hasMeta: this.hasMeta,
239
256
  hasSnapshot: this.hasSnapshot,
240
257
  hasError: this.hasError,
241
258
  isFirstChunk: this.isFirstChunk,
@@ -260,6 +277,7 @@ class Aggregate extends _aggregateBase.AggregateBase {
260
277
  this.events = [];
261
278
  this.isFirstChunk = false;
262
279
  this.hasSnapshot = false;
280
+ this.hasMeta = false;
263
281
  this.hasError = false;
264
282
  this.payloadBytesEstimation = 0;
265
283
  this.clearTimestamps();
@@ -271,27 +289,30 @@ class Aggregate extends _aggregateBase.AggregateBase {
271
289
  (0, _console.warn)('Recording library was never imported');
272
290
  return this.abort();
273
291
  }
292
+ this.clearTimestamps();
293
+ // set the fallbacks as early as possible
294
+ this.setTimestamps();
274
295
  this.recording = true;
275
296
  const {
276
- blockClass,
277
- ignoreClass,
278
- maskTextClass,
279
- blockSelector,
280
- maskInputOptions,
281
- maskTextSelector,
282
- maskAllInputs
297
+ block_class,
298
+ ignore_class,
299
+ mask_text_class,
300
+ block_selector,
301
+ mask_input_options,
302
+ mask_text_selector,
303
+ mask_all_inputs
283
304
  } = (0, _config.getConfigurationValue)(this.agentIdentifier, 'session_replay');
284
305
  // set up rrweb configurations for maximum privacy --
285
306
  // https://newrelic.atlassian.net/wiki/spaces/O11Y/pages/2792293280/2023+02+28+Browser+-+Session+Replay#Configuration-options
286
307
  const stop = recorder({
287
308
  emit: this.store.bind(this),
288
- blockClass,
289
- ignoreClass,
290
- maskTextClass,
291
- blockSelector,
292
- maskInputOptions,
293
- maskTextSelector,
294
- maskAllInputs,
309
+ blockClass: block_class,
310
+ ignoreClass: ignore_class,
311
+ maskTextClass: mask_text_class,
312
+ blockSelector: block_selector,
313
+ maskInputOptions: mask_input_options,
314
+ maskTextSelector: mask_text_selector,
315
+ maskAllInputs: mask_all_inputs,
295
316
  checkoutEveryNms: CHECKOUT_MS[this.mode]
296
317
  });
297
318
  this.stopRecording = () => {
@@ -302,6 +323,7 @@ class Aggregate extends _aggregateBase.AggregateBase {
302
323
 
303
324
  /** Store a payload in the buffer (this.events). This should be the callback to the recording lib noticing a mutation */
304
325
  store(event, isCheckout) {
326
+ this.setTimestamps(event);
305
327
  if (this.blocked) return;
306
328
  const eventBytes = (0, _stringify.stringify)(event).length;
307
329
  /** The estimated size of the payload after compression */
@@ -318,8 +340,22 @@ class Aggregate extends _aggregateBase.AggregateBase {
318
340
  // we are still waiting for an error to throw, so keep wiping the buffer over time
319
341
  this.clearBuffer();
320
342
  }
321
- this.setTimestamps(event);
322
- if (event.type === 2) this.hasSnapshot = true;
343
+
344
+ // meta event
345
+ if (event.type === 4) {
346
+ this.hasMeta = true;
347
+ this.lastMeta = event;
348
+ }
349
+ // snapshot event
350
+ if (event.type === 2) {
351
+ this.hasSnapshot = true;
352
+ // small chance that the meta event got separated from its matching snapshot across payload harvests
353
+ // it needs to precede the snapshot, so shove it in first.
354
+ if (!this.hasMeta) {
355
+ this.events.push(this.lastMeta);
356
+ this.hasMeta = true;
357
+ }
358
+ }
323
359
  this.events.push(event);
324
360
  this.payloadBytesEstimation += eventBytes;
325
361
 
@@ -336,15 +372,25 @@ class Aggregate extends _aggregateBase.AggregateBase {
336
372
  if (!recorder) return;
337
373
  recorder.takeFullSnapshot();
338
374
  }
339
- setTimestamps(rrwebEvent) {
340
- if (!rrwebEvent) return;
341
- if (!this.timestamp.first) this.timestamp.first = rrwebEvent.timestamp;
342
- this.timestamp.last = rrwebEvent.timestamp;
375
+ setTimestamps(event) {
376
+ // fallbacks if timestamps cannot be derived from rrweb events
377
+ this.timestamp.cycle.last = (0, _config.getRuntime)(this.agentIdentifier).offset + _runtime.globalScope.performance.now();
378
+ if (!this.timestamp.cycle.first) this.timestamp.cycle.first = this.timestamp.cycle.last;
379
+ // timestamps based on rrweb events
380
+ if (!event || !event.timestamp) return;
381
+ if (!this.timestamp.event.first) this.timestamp.event.first = event.timestamp;
382
+ this.timestamp.event.last = event.timestamp;
343
383
  }
344
384
  clearTimestamps() {
345
385
  this.timestamp = {
346
- first: undefined,
347
- last: undefined
386
+ event: {
387
+ first: undefined,
388
+ last: undefined
389
+ },
390
+ cycle: {
391
+ first: undefined,
392
+ last: undefined
393
+ }
348
394
  };
349
395
  }
350
396
 
@@ -492,9 +492,12 @@ class Aggregate extends _aggregateBase.AggregateBase {
492
492
  // if PO isn't supported, this checks resourcetiming buffer every harvest.
493
493
  this.storeResources(window.performance.getEntriesByType('resource'));
494
494
  }
495
+ let earliestTimeStamp = Infinity;
495
496
  const stns = Object.entries(this.trace).flatMap(_ref3 => {
496
497
  let [name, listOfSTNodes] = _ref3;
497
498
  // basically take the "this.trace" map-obj and concat all the list-type values
499
+ const oldestNodeTS = listOfSTNodes.reduce((acc, next) => !acc || next.s < acc ? next.s : acc, undefined);
500
+ if (oldestNodeTS < earliestTimeStamp) earliestTimeStamp = oldestNodeTS;
498
501
  if (!(name in toAggregate)) return listOfSTNodes;
499
502
  // Special processing for event nodes dealing with user inputs:
500
503
  const reindexByOriginFn = this.smearEvtsByOrigin(name);
@@ -509,8 +512,17 @@ class Aggregate extends _aggregateBase.AggregateBase {
509
512
  this.nodeCount = 0;
510
513
  return {
511
514
  qs: {
512
- st: String((0, _config.getRuntime)(this.agentIdentifier).offset)
515
+ st: this.agentRuntime.offset,
516
+ /** hr === "hasReplay" in NR1, standalone is always checked and processed before harvesting
517
+ * so a race condition between ST and SR states should not be a concern if implemented here */
518
+ hr: Number(!this.isStandalone),
519
+ /** fts === "firstTimestamp" in NR1, indicates what the earliest NODE timestamp was
520
+ * so that blob parsing doesn't need to happen to support UI/API functions */
521
+ fts: this.agentRuntime.offset + earliestTimeStamp,
522
+ /** n === "nodeCount" in NR1, a count of nodes in the ST payload, so that blob parsing doesn't need to happen to support UI/API functions */
523
+ n: stns.length // node count
513
524
  },
525
+
514
526
  body: {
515
527
  res: stns
516
528
  }
@@ -10,7 +10,6 @@ var _denyList = require("../../../common/deny-list/deny-list");
10
10
  var _mapOwn = require("../../../common/util/map-own");
11
11
  var _navTiming = require("../../../common/timing/nav-timing");
12
12
  var _uniqueId = require("../../../common/ids/unique-id");
13
- var _paintMetrics = require("../../../common/metrics/paint-metrics");
14
13
  var _interaction = require("./interaction");
15
14
  var _config = require("../../../common/config/config");
16
15
  var _eventListenerOpts = require("../../../common/event-listener/event-listener-opts");
@@ -20,6 +19,8 @@ var _contextualEe = require("../../../common/event-emitter/contextual-ee");
20
19
  var CONSTANTS = _interopRequireWildcard(require("../constants"));
21
20
  var _features = require("../../../loaders/features/features");
22
21
  var _aggregateBase = require("../../utils/aggregate-base");
22
+ var _firstContentfulPaint = require("../../../common/vitals/first-contentful-paint");
23
+ var _firstPaint = require("../../../common/vitals/first-paint");
23
24
  function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function (nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); }
24
25
  function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; }
25
26
  /*
@@ -662,8 +663,8 @@ class Aggregate extends _aggregateBase.AggregateBase {
662
663
  // assign unique id, this is serialized and used to link interactions with errors
663
664
  interaction.root.attrs.id = (0, _uniqueId.generateUuid)();
664
665
  if (interaction.root.attrs.trigger === 'initialPageLoad') {
665
- interaction.root.attrs.firstPaint = _paintMetrics.paintMetrics['first-paint'];
666
- interaction.root.attrs.firstContentfulPaint = _paintMetrics.paintMetrics['first-contentful-paint'];
666
+ interaction.root.attrs.firstPaint = _firstPaint.firstPaint.current.value;
667
+ interaction.root.attrs.firstContentfulPaint = _firstContentfulPaint.firstContentfulPaint.current.value;
667
668
  }
668
669
  baseEE.emit('interactionSaved', [interaction]);
669
670
  state.interactionsToHarvest.push(interaction);
@@ -4,6 +4,7 @@ Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
6
  exports.Agent = void 0;
7
+ require("./configure/public-path.npm");
7
8
  var _agentBase = require("./agent-base");
8
9
  var _enabledFeatures = require("./features/enabled-features");
9
10
  var _configure = require("./configure/configure");
@@ -17,6 +18,8 @@ var _config = require("../common/config/config");
17
18
  var _console = require("../common/util/console");
18
19
  var _stringify = require("../common/util/stringify");
19
20
  var _runtime = require("../common/constants/runtime");
21
+ // important side effects
22
+
20
23
  // loader files
21
24
 
22
25
  // required features
@@ -132,6 +132,8 @@ function setAPI(agentIdentifier, forceDrain) {
132
132
  };
133
133
  apiInterface.start = features => {
134
134
  try {
135
+ const smTag = !features ? 'undefined' : 'defined';
136
+ (0, _handle.handle)(_constants.SUPPORTABILITY_METRIC_CHANNEL, ["API/start/".concat(smTag, "/called")], undefined, _features.FEATURE_NAMES.metrics, instanceEE);
135
137
  const featNames = Object.values(_features.FEATURE_NAMES);
136
138
  if (features === undefined) features = featNames;else {
137
139
  features = Array.isArray(features) && features.length ? features : [features];
@@ -18,7 +18,6 @@ function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj &&
18
18
  function setAPI(agentIdentifier) {
19
19
  var instanceEE = _contextualEe.ee.get(agentIdentifier);
20
20
  var cycle = 0;
21
- var scheme = (0, _config.getConfigurationValue)(agentIdentifier, 'ssl') === false ? 'http' : 'https';
22
21
  var api = {
23
22
  finished: (0, _invoke.single)(finished),
24
23
  setErrorHandler,
@@ -74,7 +73,10 @@ function setAPI(agentIdentifier) {
74
73
  cycle += 1;
75
74
  const agentInfo = (0, _config.getInfo)(agentIdentifier);
76
75
  if (!agentInfo.beacon) return;
77
- var url = scheme + '://' + agentInfo.beacon + '/1/' + agentInfo.licenseKey;
76
+ const agentInit = (0, _config.getConfiguration)(agentIdentifier);
77
+ const scheme = agentInit.ssl === false ? 'http' : 'https';
78
+ const beacon = agentInit.proxy.beacon || agentInfo.beacon;
79
+ let url = "".concat(scheme, "://").concat(beacon, "/1/").concat(agentInfo.licenseKey);
78
80
  url += '?a=' + agentInfo.applicationID + '&';
79
81
  url += 't=' + requestName + '&';
80
82
  url += 'qt=' + ~~queueTime + '&';
@@ -13,6 +13,7 @@ var _instrument5 = require("../features/ajax/instrument");
13
13
  var _instrument6 = require("../features/session_trace/instrument");
14
14
  var _instrument7 = require("../features/spa/instrument");
15
15
  var _instrument8 = require("../features/page_action/instrument");
16
+ var _instrument9 = require("../features/session_replay/instrument");
16
17
  /**
17
18
  * An agent class with all feature modules available. Features may be disabled and enabled via runtime configuration.
18
19
  * The BrowserAgent class is the most convenient and reliable option for most use cases.
@@ -21,7 +22,7 @@ class BrowserAgent extends _agent.Agent {
21
22
  constructor(args) {
22
23
  super({
23
24
  ...args,
24
- features: [_instrument5.Instrument, _instrument.Instrument, _instrument2.Instrument, _instrument6.Instrument, _instrument3.Instrument, _instrument8.Instrument, _instrument4.Instrument, _instrument7.Instrument],
25
+ features: [_instrument5.Instrument, _instrument.Instrument, _instrument2.Instrument, _instrument6.Instrument, _instrument3.Instrument, _instrument8.Instrument, _instrument4.Instrument, _instrument7.Instrument, _instrument9.Instrument],
25
26
  loaderType: 'browser-agent'
26
27
  });
27
28
  }
@@ -9,6 +9,9 @@ var _nreum = require("../../common/window/nreum");
9
9
  var _config = require("../../common/config/config");
10
10
  var _featureFlags = require("../../common/util/feature-flags");
11
11
  var _runtime = require("../../common/constants/runtime");
12
+ var _publicPath = require("./public-path");
13
+ let alreadySetOnce = false; // the configure() function can run multiple times in agent lifecycle
14
+
12
15
  function configure(agentIdentifier) {
13
16
  let opts = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
14
17
  let loaderType = arguments.length > 2 ? arguments[2] : undefined;
@@ -40,7 +43,16 @@ function configure(agentIdentifier) {
40
43
  }
41
44
  (0, _config.setInfo)(agentIdentifier, info);
42
45
  const updatedInit = (0, _config.getConfiguration)(agentIdentifier);
43
- runtime.denyList = [...(updatedInit.ajax?.deny_list || []), ...(updatedInit.ajax?.block_internal ? [info.beacon, info.errorBeacon] : [])];
46
+ const internalTrafficList = [info.beacon, info.errorBeacon];
47
+ if (!alreadySetOnce) {
48
+ alreadySetOnce = true;
49
+ if (updatedInit.proxy.assets) {
50
+ (0, _publicPath.redefinePublicPath)(updatedInit.proxy.assets + '/'); // much like the info.beacon & init.proxy.beacon, this input should not end in a slash, but one is needed for webpack concat
51
+ internalTrafficList.push(updatedInit.proxy.assets);
52
+ }
53
+ if (updatedInit.proxy.beacon) internalTrafficList.push(updatedInit.proxy.beacon);
54
+ }
55
+ runtime.denyList = [...(updatedInit.ajax.deny_list || []), ...(updatedInit.ajax.block_internal ? internalTrafficList : [])];
44
56
  (0, _config.setRuntime)(agentIdentifier, runtime);
45
57
  (0, _api.setTopLevelCallers)();
46
58
  const api = (0, _api.setAPI)(agentIdentifier, forceDrain);
@@ -0,0 +1,13 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.redefinePublicPath = void 0;
7
+ // Set the default CDN or remote for fetching the assets; NPM shouldn't change this var.
8
+
9
+ const redefinePublicPath = url => {
10
+ // There's no URL validation here, so caller should check arg if need be.
11
+ __webpack_public_path__ = url; // eslint-disable-line
12
+ };
13
+ exports.redefinePublicPath = redefinePublicPath;
@@ -0,0 +1,10 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.redefinePublicPath = void 0;
7
+ const redefinePublicPath = () => {
8
+ // We don't support setting public path in webpack via NPM build.
9
+ };
10
+ exports.redefinePublicPath = redefinePublicPath;
@@ -10,8 +10,9 @@ import { Instrument as InstrumentMetrics } from '../features/metrics/instrument'
10
10
  import { Instrument as InstrumentErrors } from '../features/jserrors/instrument';
11
11
  import { Instrument as InstrumentXhr } from '../features/ajax/instrument';
12
12
  import { Instrument as InstrumentSessionTrace } from '../features/session_trace/instrument';
13
+ import { Instrument as InstrumentSessionReplay } from '../features/session_replay/instrument';
13
14
  import { Instrument as InstrumentPageAction } from '../features/page_action/instrument';
14
15
  new Agent({
15
- features: [InstrumentPageViewEvent, InstrumentPageViewTiming, InstrumentSessionTrace, InstrumentXhr, InstrumentMetrics, InstrumentPageAction, InstrumentErrors],
16
+ features: [InstrumentPageViewEvent, InstrumentPageViewTiming, InstrumentSessionTrace, InstrumentSessionReplay, InstrumentXhr, InstrumentMetrics, InstrumentPageAction, InstrumentErrors],
16
17
  loaderType: 'pro'
17
18
  });
@@ -9,9 +9,10 @@ import { Instrument as InstrumentMetrics } from '../features/metrics/instrument'
9
9
  import { Instrument as InstrumentErrors } from '../features/jserrors/instrument';
10
10
  import { Instrument as InstrumentXhr } from '../features/ajax/instrument';
11
11
  import { Instrument as InstrumentSessionTrace } from '../features/session_trace/instrument';
12
+ import { Instrument as InstrumentSessionReplay } from '../features/session_replay/instrument';
12
13
  import { Instrument as InstrumentSpa } from '../features/spa/instrument';
13
14
  import { Instrument as InstrumentPageAction } from '../features/page_action/instrument';
14
15
  new Agent({
15
- features: [InstrumentXhr, InstrumentPageViewEvent, InstrumentPageViewTiming, InstrumentSessionTrace, InstrumentMetrics, InstrumentPageAction, InstrumentErrors, InstrumentSpa],
16
+ features: [InstrumentXhr, InstrumentPageViewEvent, InstrumentPageViewTiming, InstrumentSessionTrace, InstrumentSessionReplay, InstrumentMetrics, InstrumentPageAction, InstrumentErrors, InstrumentSpa],
16
17
  loaderType: 'spa'
17
18
  });
@@ -3,12 +3,18 @@ import { gosNREUMInitializedAgents } from '../../window/nreum';
3
3
  import { getModeledObject } from './configurable';
4
4
  const model = () => {
5
5
  const hiddenState = {
6
- blockSelector: '[data-nr-block]',
7
- maskInputOptions: {
6
+ block_selector: '[data-nr-block]',
7
+ mask_input_options: {
8
8
  password: true
9
9
  }
10
10
  };
11
11
  return {
12
+ proxy: {
13
+ assets: undefined,
14
+ // if this value is set, it will be used to overwrite the webpack asset path used to fetch assets
15
+ beacon: undefined // likewise for the url to which we send analytics
16
+ },
17
+
12
18
  privacy: {
13
19
  cookies_enabled: true
14
20
  },
@@ -72,35 +78,37 @@ const model = () => {
72
78
  autoStart: true,
73
79
  enabled: false,
74
80
  harvestTimeSeconds: 60,
75
- sampleRate: 0.1,
76
- errorSampleRate: 0.1,
81
+ sampling_rate: 50,
82
+ // float from 0 - 100
83
+ error_sampling_rate: 50,
84
+ // float from 0 - 100
77
85
  // recording config settings
78
- maskTextSelector: '*',
79
- maskAllInputs: true,
86
+ mask_text_selector: '*',
87
+ mask_all_inputs: true,
80
88
  // these properties only have getters because they are enforcable constants and should error if someone tries to override them
81
- get blockClass() {
89
+ get block_class() {
82
90
  return 'nr-block';
83
91
  },
84
- get ignoreClass() {
92
+ get ignore_class() {
85
93
  return 'nr-ignore';
86
94
  },
87
- get maskTextClass() {
95
+ get mask_text_class() {
88
96
  return 'nr-mask';
89
97
  },
90
98
  // props with a getter and setter are used to extend enforcable constants with customer input
91
99
  // we must preserve data-nr-block no matter what else the customer sets
92
- get blockSelector() {
93
- return hiddenState.blockSelector;
100
+ get block_selector() {
101
+ return hiddenState.block_selector;
94
102
  },
95
- set blockSelector(val) {
96
- hiddenState.blockSelector += ",".concat(val);
103
+ set block_selector(val) {
104
+ hiddenState.block_selector += ",".concat(val);
97
105
  },
98
106
  // password: must always be present and true no matter what customer sets
99
- get maskInputOptions() {
100
- return hiddenState.maskInputOptions;
107
+ get mask_input_options() {
108
+ return hiddenState.mask_input_options;
101
109
  },
102
- set maskInputOptions(val) {
103
- hiddenState.maskInputOptions = {
110
+ set mask_input_options(val) {
111
+ hiddenState.mask_input_options = {
104
112
  ...val,
105
113
  password: true
106
114
  };
@@ -6,7 +6,7 @@
6
6
  /**
7
7
  * Exposes the version of the agent
8
8
  */
9
- export const VERSION = "1.239.1";
9
+ export const VERSION = "1.241.0";
10
10
 
11
11
  /**
12
12
  * Exposes the build type of the agent
@@ -6,7 +6,7 @@
6
6
  /**
7
7
  * Exposes the version of the agent
8
8
  */
9
- export const VERSION = "1.239.1";
9
+ export const VERSION = "1.241.0";
10
10
 
11
11
  /**
12
12
  * Exposes the build type of the agent
@@ -15,8 +15,9 @@ export const isBrowserScope = typeof window !== 'undefined' && !!window.document
15
15
  */
16
16
  export const isWorkerScope = typeof WorkerGlobalScope !== 'undefined' && (typeof self !== 'undefined' && self instanceof WorkerGlobalScope && self.navigator instanceof WorkerNavigator || typeof globalThis !== 'undefined' && globalThis instanceof WorkerGlobalScope && globalThis.navigator instanceof WorkerNavigator);
17
17
  export const globalScope = isBrowserScope ? window : typeof WorkerGlobalScope !== 'undefined' && (typeof self !== 'undefined' && self instanceof WorkerGlobalScope && self || typeof globalThis !== 'undefined' && globalThis instanceof WorkerGlobalScope && globalThis);
18
+ export const initiallyHidden = Boolean(globalScope?.document?.visibilityState === 'hidden');
18
19
  export const initialLocation = '' + globalScope?.location;
19
- export const isiOS = /iPad|iPhone|iPod/.test(navigator.userAgent);
20
+ export const isiOS = /iPad|iPhone|iPod/.test(globalScope.navigator?.userAgent);
20
21
 
21
22
  /**
22
23
  * Shared Web Workers introduced in iOS 16.0+ and n/a in 15.6-
@@ -27,7 +28,7 @@ export const isiOS = /iPad|iPhone|iPod/.test(navigator.userAgent);
27
28
  */
28
29
  export const iOSBelow16 = isiOS && typeof SharedWorker === 'undefined';
29
30
  export const ffVersion = (() => {
30
- const match = navigator.userAgent.match(/Firefox[/\s](\d+\.\d+)/);
31
+ const match = globalScope.navigator?.userAgent?.match(/Firefox[/\s](\d+\.\d+)/);
31
32
  if (Array.isArray(match) && match.length >= 2) {
32
33
  return +match[1];
33
34
  }
@@ -35,4 +36,5 @@ export const ffVersion = (() => {
35
36
  })();
36
37
  export const isIE = Boolean(isBrowserScope && window.document.documentMode); // deprecated property that only works in IE
37
38
 
38
- export const supportsSendBeacon = !!navigator.sendBeacon;
39
+ export const supportsSendBeacon = !!globalScope.navigator?.sendBeacon;
40
+ export const offset = Math.floor(globalScope?.performance?.timeOrigin || globalScope?.performance?.timing?.navigationStart || Date.now());
@@ -7,7 +7,7 @@ import { obj as encodeObj, param as encodeParam } from '../url/encode';
7
7
  import { stringify } from '../util/stringify';
8
8
  import * as submitData from '../util/submit-data';
9
9
  import { getLocation } from '../url/location';
10
- import { getInfo, getConfigurationValue, getRuntime } from '../config/config';
10
+ import { getInfo, getConfigurationValue, getRuntime, getConfiguration } from '../config/config';
11
11
  import { cleanURL } from '../url/clean-url';
12
12
  import { now } from '../timing/now';
13
13
  import { eventListenerOpts } from '../event-listener/event-listener-opts';
@@ -32,7 +32,6 @@ export class Harvest extends SharedContext {
32
32
 
33
33
  this.tooManyRequestsDelay = getConfigurationValue(this.sharedContext.agentIdentifier, 'harvest.tooManyRequestsDelay') || 60;
34
34
  this.obfuscator = new Obfuscator(this.sharedContext);
35
- this.getScheme = () => getConfigurationValue(this.sharedContext.agentIdentifier, 'ssl') === false ? 'http' : 'https';
36
35
  this._events = {};
37
36
  }
38
37
 
@@ -123,10 +122,13 @@ export class Harvest extends SharedContext {
123
122
  }
124
123
  return false;
125
124
  }
125
+ const init = getConfiguration(this.sharedContext.agentIdentifier);
126
+ const protocol = init.ssl === false ? 'http' : 'https';
127
+ const perceviedBeacon = init.proxy.beacon || info.errorBeacon;
126
128
  const endpointURLPart = endpoint !== 'rum' ? "/".concat(endpoint) : '';
127
- let url = "".concat(this.getScheme(), "://").concat(info.errorBeacon).concat(endpointURLPart, "/1/").concat(info.licenseKey);
129
+ let url = "".concat(protocol, "://").concat(perceviedBeacon).concat(endpointURLPart, "/1/").concat(info.licenseKey);
128
130
  if (customUrl) url = customUrl;
129
- if (raw) url = "".concat(this.getScheme(), "://").concat(info.errorBeacon, "/").concat(endpoint);
131
+ if (raw) url = "".concat(protocol, "://").concat(perceviedBeacon, "/").concat(endpoint);
130
132
  const baseParams = !raw && includeBaseParams ? this.baseQueryString() : '';
131
133
  let payloadParams = encodeObj(qs, agentRuntime.maxBytes);
132
134
  if (!submitMethod) {
@@ -0,0 +1,10 @@
1
+ export const VITAL_NAMES = {
2
+ FIRST_PAINT: 'fp',
3
+ FIRST_CONTENTFUL_PAINT: 'fcp',
4
+ FIRST_INPUT_DELAY: 'fi',
5
+ LARGEST_CONTENTFUL_PAINT: 'lcp',
6
+ CUMULATIVE_LAYOUT_SHIFT: 'cls',
7
+ INTERACTION_TO_NEXT_PAINT: 'inp',
8
+ LONG_TASK: 'lt',
9
+ TIME_TO_FIRST_BYTE: 'ttfb'
10
+ };
@@ -0,0 +1,20 @@
1
+ import { onCLS } from 'web-vitals';
2
+ import { VITAL_NAMES } from './constants';
3
+ import { VitalMetric } from './vital-metric';
4
+ import { isBrowserScope } from '../constants/runtime';
5
+ export const cumulativeLayoutShift = new VitalMetric(VITAL_NAMES.CUMULATIVE_LAYOUT_SHIFT, x => x);
6
+ if (isBrowserScope) {
7
+ onCLS(_ref => {
8
+ let {
9
+ value,
10
+ entries
11
+ } = _ref;
12
+ if (cumulativeLayoutShift.roundingMethod(value) === cumulativeLayoutShift.current.value) return;
13
+ cumulativeLayoutShift.update({
14
+ value,
15
+ entries
16
+ });
17
+ }, {
18
+ reportAllChanges: true
19
+ });
20
+ }