@newrelic/browser-agent 1.254.1 → 1.256.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 (176) hide show
  1. package/CHANGELOG.md +25 -0
  2. package/dist/cjs/common/config/state/runtime.js +2 -1
  3. package/dist/cjs/common/constants/env.cdn.js +2 -2
  4. package/dist/cjs/common/constants/env.npm.js +2 -2
  5. package/dist/cjs/common/constants/runtime.js +1 -1
  6. package/dist/cjs/common/context/shared-context.js +1 -1
  7. package/dist/cjs/common/harvest/harvest.js +2 -1
  8. package/dist/cjs/common/session/session-entity.js +2 -1
  9. package/dist/cjs/common/timer/interaction-timer.js +16 -2
  10. package/dist/cjs/common/timing/time-keeper.js +23 -19
  11. package/dist/cjs/common/vitals/cumulative-layout-shift.js +12 -5
  12. package/dist/cjs/common/vitals/first-contentful-paint.js +10 -6
  13. package/dist/cjs/common/vitals/first-input-delay.js +12 -10
  14. package/dist/cjs/common/vitals/first-paint.js +1 -2
  15. package/dist/cjs/common/vitals/interaction-to-next-paint.js +11 -7
  16. package/dist/cjs/common/vitals/largest-contentful-paint.js +19 -17
  17. package/dist/cjs/common/vitals/long-task.js +0 -1
  18. package/dist/cjs/common/vitals/time-to-first-byte.js +11 -6
  19. package/dist/cjs/common/vitals/vital-metric.js +1 -4
  20. package/dist/cjs/common/window/nreum.js +1 -1
  21. package/dist/cjs/features/ajax/aggregate/index.js +3 -2
  22. package/dist/cjs/features/ajax/instrument/index.js +1 -1
  23. package/dist/cjs/features/jserrors/aggregate/index.js +19 -8
  24. package/dist/cjs/features/jserrors/instrument/index.js +9 -4
  25. package/dist/cjs/features/page_action/aggregate/index.js +3 -2
  26. package/dist/cjs/features/page_view_event/aggregate/index.js +10 -5
  27. package/dist/cjs/features/page_view_timing/aggregate/index.js +21 -7
  28. package/dist/cjs/features/page_view_timing/instrument/index.js +1 -1
  29. package/dist/cjs/features/session_replay/aggregate/index.js +59 -35
  30. package/dist/cjs/features/session_replay/constants.js +2 -1
  31. package/dist/cjs/features/session_replay/instrument/index.js +9 -2
  32. package/dist/cjs/features/session_replay/shared/recorder.js +20 -4
  33. package/dist/cjs/features/session_replay/shared/utils.js +12 -0
  34. package/dist/cjs/features/session_trace/aggregate/index.js +20 -23
  35. package/dist/cjs/features/session_trace/instrument/index.js +1 -1
  36. package/dist/cjs/features/soft_navigations/aggregate/index.js +2 -2
  37. package/dist/cjs/features/soft_navigations/instrument/index.js +1 -1
  38. package/dist/cjs/features/spa/aggregate/index.js +19 -10
  39. package/dist/cjs/features/spa/instrument/index.js +1 -1
  40. package/dist/cjs/features/utils/feature-base.js +0 -2
  41. package/dist/cjs/loaders/agent-base.js +0 -2
  42. package/dist/cjs/loaders/agent.js +1 -1
  43. package/dist/cjs/loaders/api/api.js +8 -2
  44. package/dist/cjs/loaders/configure/configure.js +1 -0
  45. package/dist/cjs/loaders/micro-agent.js +4 -7
  46. package/dist/esm/common/config/state/runtime.js +2 -1
  47. package/dist/esm/common/constants/env.cdn.js +2 -2
  48. package/dist/esm/common/constants/env.npm.js +2 -2
  49. package/dist/esm/common/constants/runtime.js +1 -1
  50. package/dist/esm/common/context/shared-context.js +1 -1
  51. package/dist/esm/common/harvest/harvest.js +2 -1
  52. package/dist/esm/common/session/session-entity.js +2 -1
  53. package/dist/esm/common/timer/interaction-timer.js +16 -2
  54. package/dist/esm/common/timing/time-keeper.js +23 -20
  55. package/dist/esm/common/vitals/cumulative-layout-shift.js +11 -4
  56. package/dist/esm/common/vitals/first-contentful-paint.js +9 -5
  57. package/dist/esm/common/vitals/first-input-delay.js +11 -9
  58. package/dist/esm/common/vitals/first-paint.js +1 -2
  59. package/dist/esm/common/vitals/interaction-to-next-paint.js +10 -6
  60. package/dist/esm/common/vitals/largest-contentful-paint.js +18 -16
  61. package/dist/esm/common/vitals/long-task.js +0 -1
  62. package/dist/esm/common/vitals/time-to-first-byte.js +10 -5
  63. package/dist/esm/common/vitals/vital-metric.js +1 -4
  64. package/dist/esm/common/window/nreum.js +1 -1
  65. package/dist/esm/features/ajax/aggregate/index.js +3 -2
  66. package/dist/esm/features/ajax/instrument/index.js +1 -1
  67. package/dist/esm/features/jserrors/aggregate/index.js +19 -8
  68. package/dist/esm/features/jserrors/instrument/index.js +9 -4
  69. package/dist/esm/features/page_action/aggregate/index.js +3 -2
  70. package/dist/esm/features/page_view_event/aggregate/index.js +10 -5
  71. package/dist/esm/features/page_view_timing/aggregate/index.js +21 -7
  72. package/dist/esm/features/page_view_timing/instrument/index.js +1 -1
  73. package/dist/esm/features/session_replay/aggregate/index.js +59 -35
  74. package/dist/esm/features/session_replay/constants.js +2 -1
  75. package/dist/esm/features/session_replay/instrument/index.js +9 -2
  76. package/dist/esm/features/session_replay/shared/recorder.js +21 -5
  77. package/dist/esm/features/session_replay/shared/utils.js +11 -0
  78. package/dist/esm/features/session_trace/aggregate/index.js +20 -23
  79. package/dist/esm/features/session_trace/instrument/index.js +1 -1
  80. package/dist/esm/features/soft_navigations/aggregate/index.js +2 -2
  81. package/dist/esm/features/soft_navigations/instrument/index.js +1 -1
  82. package/dist/esm/features/spa/aggregate/index.js +19 -10
  83. package/dist/esm/features/spa/instrument/index.js +1 -1
  84. package/dist/esm/features/utils/feature-base.js +0 -2
  85. package/dist/esm/loaders/agent-base.js +0 -2
  86. package/dist/esm/loaders/agent.js +1 -1
  87. package/dist/esm/loaders/api/api.js +8 -2
  88. package/dist/esm/loaders/configure/configure.js +1 -0
  89. package/dist/esm/loaders/micro-agent.js +4 -7
  90. package/dist/types/common/config/state/runtime.d.ts.map +1 -1
  91. package/dist/types/common/constants/runtime.d.ts.map +1 -1
  92. package/dist/types/common/harvest/harvest.d.ts.map +1 -1
  93. package/dist/types/common/session/session-entity.d.ts.map +1 -1
  94. package/dist/types/common/timer/interaction-timer.d.ts +2 -0
  95. package/dist/types/common/timer/interaction-timer.d.ts.map +1 -1
  96. package/dist/types/common/timing/time-keeper.d.ts +4 -9
  97. package/dist/types/common/timing/time-keeper.d.ts.map +1 -1
  98. package/dist/types/common/vitals/vital-metric.d.ts +1 -2
  99. package/dist/types/common/vitals/vital-metric.d.ts.map +1 -1
  100. package/dist/types/features/ajax/aggregate/index.d.ts.map +1 -1
  101. package/dist/types/features/ajax/instrument/index.d.ts.map +1 -1
  102. package/dist/types/features/jserrors/aggregate/index.d.ts +2 -1
  103. package/dist/types/features/jserrors/aggregate/index.d.ts.map +1 -1
  104. package/dist/types/features/jserrors/instrument/index.d.ts.map +1 -1
  105. package/dist/types/features/page_action/aggregate/index.d.ts.map +1 -1
  106. package/dist/types/features/page_view_event/aggregate/index.d.ts.map +1 -1
  107. package/dist/types/features/page_view_timing/aggregate/index.d.ts.map +1 -1
  108. package/dist/types/features/page_view_timing/instrument/index.d.ts.map +1 -1
  109. package/dist/types/features/session_replay/aggregate/index.d.ts +9 -2
  110. package/dist/types/features/session_replay/aggregate/index.d.ts.map +1 -1
  111. package/dist/types/features/session_replay/constants.d.ts +1 -0
  112. package/dist/types/features/session_replay/constants.d.ts.map +1 -1
  113. package/dist/types/features/session_replay/instrument/index.d.ts +1 -0
  114. package/dist/types/features/session_replay/instrument/index.d.ts.map +1 -1
  115. package/dist/types/features/session_replay/shared/recorder.d.ts.map +1 -1
  116. package/dist/types/features/session_replay/shared/utils.d.ts +8 -0
  117. package/dist/types/features/session_replay/shared/utils.d.ts.map +1 -1
  118. package/dist/types/features/session_trace/aggregate/index.d.ts +2 -1
  119. package/dist/types/features/session_trace/aggregate/index.d.ts.map +1 -1
  120. package/dist/types/features/session_trace/instrument/index.d.ts.map +1 -1
  121. package/dist/types/features/soft_navigations/instrument/index.d.ts.map +1 -1
  122. package/dist/types/features/spa/aggregate/index.d.ts +0 -2
  123. package/dist/types/features/spa/aggregate/index.d.ts.map +1 -1
  124. package/dist/types/features/utils/feature-base.d.ts +0 -1
  125. package/dist/types/features/utils/feature-base.d.ts.map +1 -1
  126. package/dist/types/loaders/agent-base.d.ts +0 -2
  127. package/dist/types/loaders/agent-base.d.ts.map +1 -1
  128. package/dist/types/loaders/api/api.d.ts.map +1 -1
  129. package/dist/types/loaders/configure/configure.d.ts.map +1 -1
  130. package/dist/types/loaders/micro-agent.d.ts.map +1 -1
  131. package/package.json +2 -2
  132. package/src/common/config/state/runtime.js +2 -1
  133. package/src/common/constants/runtime.js +1 -1
  134. package/src/common/context/__mocks__/shared-context.js +3 -0
  135. package/src/common/context/shared-context.js +1 -1
  136. package/src/common/harvest/harvest.js +2 -2
  137. package/src/common/session/session-entity.js +2 -1
  138. package/src/common/timer/interaction-timer.js +17 -2
  139. package/src/common/timing/__mocks__/time-keeper.js +2 -0
  140. package/src/common/timing/time-keeper.js +25 -22
  141. package/src/common/vitals/cumulative-layout-shift.js +10 -4
  142. package/src/common/vitals/first-contentful-paint.js +9 -4
  143. package/src/common/vitals/first-input-delay.js +11 -6
  144. package/src/common/vitals/first-paint.js +1 -1
  145. package/src/common/vitals/interaction-to-next-paint.js +10 -3
  146. package/src/common/vitals/largest-contentful-paint.js +19 -15
  147. package/src/common/vitals/long-task.js +0 -1
  148. package/src/common/vitals/time-to-first-byte.js +5 -4
  149. package/src/common/vitals/vital-metric.js +2 -4
  150. package/src/common/window/nreum.js +1 -1
  151. package/src/features/ajax/aggregate/index.js +3 -2
  152. package/src/features/ajax/instrument/index.js +1 -1
  153. package/src/features/jserrors/aggregate/index.js +18 -8
  154. package/src/features/jserrors/instrument/index.js +10 -5
  155. package/src/features/page_action/aggregate/index.js +3 -2
  156. package/src/features/page_view_event/aggregate/index.js +11 -5
  157. package/src/features/page_view_timing/aggregate/index.js +16 -6
  158. package/src/features/page_view_timing/instrument/index.js +1 -1
  159. package/src/features/session_replay/aggregate/index.js +53 -31
  160. package/src/features/session_replay/constants.js +2 -1
  161. package/src/features/session_replay/instrument/index.js +7 -2
  162. package/src/features/session_replay/shared/recorder.js +23 -5
  163. package/src/features/session_replay/shared/utils.js +12 -0
  164. package/src/features/session_trace/aggregate/index.js +19 -17
  165. package/src/features/session_trace/instrument/index.js +1 -1
  166. package/src/features/soft_navigations/aggregate/index.js +2 -2
  167. package/src/features/soft_navigations/instrument/index.js +1 -1
  168. package/src/features/spa/aggregate/index.js +19 -8
  169. package/src/features/spa/instrument/index.js +1 -1
  170. package/src/features/utils/feature-base.js +0 -3
  171. package/src/loaders/agent-base.js +0 -2
  172. package/src/loaders/agent.js +1 -1
  173. package/src/loaders/api/api.js +11 -2
  174. package/src/loaders/configure/configure.js +1 -0
  175. package/src/loaders/micro-agent.js +4 -6
  176. package/src/common/vitals/__mocks__/web-vitals.js +0 -19
@@ -14,13 +14,13 @@ var _stringify = require("../../../common/util/stringify");
14
14
  var _handle = require("../../../common/event-emitter/handle");
15
15
  var _mapOwn = require("../../../common/util/map-own");
16
16
  var _config = require("../../../common/config/config");
17
- var _now = require("../../../common/timing/now");
18
17
  var _runtime = require("../../../common/constants/runtime");
19
18
  var _constants = require("../constants");
20
19
  var _features = require("../../../loaders/features/features");
21
20
  var _aggregateBase = require("../../utils/aggregate-base");
22
21
  var _nreum = require("../../../common/window/nreum");
23
22
  var _drain = require("../../../common/drain/drain");
23
+ var _now = require("../../../common/timing/now");
24
24
  /*
25
25
  * Copyright 2020 New Relic Corporation. All rights reserved.
26
26
  * SPDX-License-Identifier: Apache-2.0
@@ -42,9 +42,13 @@ class Aggregate extends _aggregateBase.AggregateBase {
42
42
  this.bufferedErrorsUnderSpa = {};
43
43
  this.currentBody = undefined;
44
44
  this.errorOnPage = false;
45
+ this.replayAborted = false;
45
46
 
46
47
  // this will need to change to match whatever ee we use in the instrument
47
48
  this.ee.on('interactionDone', (interaction, wasSaved) => this.onInteractionDone(interaction, wasSaved));
49
+ this.ee.on('REPLAY_ABORTED', () => {
50
+ this.replayAborted = true;
51
+ });
48
52
  (0, _registerHandler.registerHandler)('err', function () {
49
53
  return _this.storeError(...arguments);
50
54
  }, this.featureName, this.ee);
@@ -89,9 +93,16 @@ class Aggregate extends _aggregateBase.AggregateBase {
89
93
  if (releaseIds !== '{}') {
90
94
  payload.qs.ri = releaseIds;
91
95
  }
92
- if (body && body.err && body.err.length && !this.errorOnPage) {
93
- payload.qs.pve = '1';
94
- this.errorOnPage = true;
96
+ if (body && body.err && body.err.length) {
97
+ if (this.replayAborted) {
98
+ body.err.forEach(e => {
99
+ delete e.params?.hasReplay;
100
+ });
101
+ }
102
+ if (!this.errorOnPage) {
103
+ payload.qs.pve = '1';
104
+ this.errorOnPage = true;
105
+ }
95
106
  }
96
107
  return payload;
97
108
  }
@@ -136,7 +147,7 @@ class Aggregate extends _aggregateBase.AggregateBase {
136
147
  }
137
148
  return canonicalStackString;
138
149
  }
139
- storeError(err, time, internal, customAttributes) {
150
+ storeError(err, time, internal, customAttributes, hasReplay) {
140
151
  // are we in an interaction
141
152
  time = time || (0, _now.now)();
142
153
  const agentRuntime = (0, _config.getRuntime)(this.agentIdentifier);
@@ -172,7 +183,7 @@ class Aggregate extends _aggregateBase.AggregateBase {
172
183
  if (!this.stackReported[bucketHash]) {
173
184
  this.stackReported[bucketHash] = true;
174
185
  params.stack_trace = (0, _formatStackTrace.truncateSize)(stackInfo.stackString);
175
- this.observedAt[bucketHash] = agentRuntime.offset + time;
186
+ this.observedAt[bucketHash] = agentRuntime.timeKeeper.convertRelativeTimestamp(time);
176
187
  } else {
177
188
  params.browser_stack_hash = (0, _stringHashCode.stringHashCode)(stackInfo.stackString);
178
189
  }
@@ -187,8 +198,9 @@ class Aggregate extends _aggregateBase.AggregateBase {
187
198
  params.pageview = 1;
188
199
  this.pageviewReported[bucketHash] = true;
189
200
  }
190
- if (agentRuntime?.session?.state?.sessionReplayMode) params.hasReplay = true;
201
+ if (hasReplay && !this.replayAborted) params.hasReplay = hasReplay;
191
202
  params.firstOccurrenceTimestamp = this.observedAt[bucketHash];
203
+ params.timestamp = this.observedAt[bucketHash];
192
204
  var type = internal ? 'ierr' : 'err';
193
205
  var newMetrics = {
194
206
  time
@@ -197,7 +209,6 @@ class Aggregate extends _aggregateBase.AggregateBase {
197
209
  // Trace sends the error in its payload, and both trace & replay simply listens for any error to occur.
198
210
  const jsErrorEvent = [type, bucketHash, params, newMetrics, customAttributes];
199
211
  (0, _handle.handle)('errorAgg', jsErrorEvent, undefined, _features.FEATURE_NAMES.sessionTrace, this.ee);
200
- (0, _handle.handle)('errorAgg', jsErrorEvent, undefined, _features.FEATURE_NAMES.sessionReplay, this.ee);
201
212
  // still send EE events for other features such as above, but stop this one from aggregating internal data
202
213
  if (this.blocked) return;
203
214
  const softNavInUse = Boolean((0, _nreum.getNREUMInitializedAgent)(this.agentIdentifier)?.features[_features.FEATURE_NAMES.softNav]);
@@ -5,7 +5,6 @@ Object.defineProperty(exports, "__esModule", {
5
5
  });
6
6
  exports.Instrument = void 0;
7
7
  var _handle = require("../../../common/event-emitter/handle");
8
- var _now = require("../../../common/timing/now");
9
8
  var _instrumentBase = require("../../utils/instrument-base");
10
9
  var _constants = require("../constants");
11
10
  var _features = require("../../../loaders/features/features");
@@ -13,6 +12,8 @@ var _runtime = require("../../../common/constants/runtime");
13
12
  var _eventListenerOpts = require("../../../common/event-listener/event-listener-opts");
14
13
  var _stringify = require("../../../common/util/stringify");
15
14
  var _uncaughtError = require("./uncaught-error");
15
+ var _now = require("../../../common/timing/now");
16
+ var _constants2 = require("../../session_replay/constants");
16
17
  /*
17
18
  * Copyright 2020 New Relic Corporation. All rights reserved.
18
19
  * SPDX-License-Identifier: Apache-2.0
@@ -21,6 +22,7 @@ var _uncaughtError = require("./uncaught-error");
21
22
  class Instrument extends _instrumentBase.InstrumentBase {
22
23
  static featureName = _constants.FEATURE_NAME;
23
24
  #seenErrors = new Set();
25
+ #replayRunning = false;
24
26
  constructor(agentIdentifier, aggregator) {
25
27
  let auto = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : true;
26
28
  super(agentIdentifier, aggregator, _constants.FEATURE_NAME, auto);
@@ -37,13 +39,16 @@ class Instrument extends _instrumentBase.InstrumentBase {
37
39
  });
38
40
  this.ee.on('internal-error', error => {
39
41
  if (!this.abortHandler) return;
40
- (0, _handle.handle)('ierr', [this.#castError(error), (0, _now.now)(), true], undefined, _features.FEATURE_NAMES.jserrors, this.ee);
42
+ (0, _handle.handle)('ierr', [this.#castError(error), (0, _now.now)(), true, {}, this.#replayRunning], undefined, _features.FEATURE_NAMES.jserrors, this.ee);
43
+ });
44
+ this.ee.on(_constants2.SR_EVENT_EMITTER_TYPES.REPLAY_RUNNING, isRunning => {
45
+ this.#replayRunning = isRunning;
41
46
  });
42
47
  _runtime.globalScope.addEventListener('unhandledrejection', promiseRejectionEvent => {
43
48
  if (!this.abortHandler) return;
44
49
  (0, _handle.handle)('err', [this.#castPromiseRejectionEvent(promiseRejectionEvent), (0, _now.now)(), false, {
45
50
  unhandledPromiseRejection: 1
46
- }], undefined, _features.FEATURE_NAMES.jserrors, this.ee);
51
+ }, this.#replayRunning], undefined, _features.FEATURE_NAMES.jserrors, this.ee);
47
52
  }, (0, _eventListenerOpts.eventListenerOpts)(false, this.removeOnAbort?.signal));
48
53
  _runtime.globalScope.addEventListener('error', errorEvent => {
49
54
  if (!this.abortHandler) return;
@@ -56,7 +61,7 @@ class Instrument extends _instrumentBase.InstrumentBase {
56
61
  this.#seenErrors.delete(errorEvent.error);
57
62
  return;
58
63
  }
59
- (0, _handle.handle)('err', [this.#castErrorEvent(errorEvent), (0, _now.now)()], undefined, _features.FEATURE_NAMES.jserrors, this.ee);
64
+ (0, _handle.handle)('err', [this.#castErrorEvent(errorEvent), (0, _now.now)(), false, {}, this.#replayRunning], undefined, _features.FEATURE_NAMES.jserrors, this.ee);
60
65
  }, (0, _eventListenerOpts.eventListenerOpts)(false, this.removeOnAbort?.signal));
61
66
  this.abortHandler = this.#abort; // we also use this as a flag to denote that the feature is active or on and handling errors
62
67
  this.importAggregator();
@@ -95,14 +95,15 @@ class Aggregate extends _aggregateBase.AggregateBase {
95
95
  width = window.document.documentElement.clientWidth;
96
96
  height = window.document.documentElement.clientHeight;
97
97
  }
98
+ const agentRuntime = (0, _config.getRuntime)(this.agentIdentifier);
98
99
  var defaults = {
99
- timestamp: t + (0, _config.getRuntime)(this.agentIdentifier).offset,
100
+ timestamp: agentRuntime.timeKeeper.convertRelativeTimestamp(t),
100
101
  timeSinceLoad: t / 1000,
101
102
  browserWidth: width,
102
103
  browserHeight: height,
103
104
  referrerUrl: this.referrerUrl,
104
105
  currentUrl: (0, _cleanUrl.cleanURL)('' + location),
105
- pageUrl: (0, _cleanUrl.cleanURL)((0, _config.getRuntime)(this.agentIdentifier).origin),
106
+ pageUrl: (0, _cleanUrl.cleanURL)(agentRuntime.origin),
106
107
  eventType: 'PageAction'
107
108
  };
108
109
  (0, _mapOwn.mapOwn)(defaults, set);
@@ -21,6 +21,8 @@ var _drain = require("../../../common/drain/drain");
21
21
  var _features = require("../../../loaders/features/features");
22
22
  var _handle = require("../../../common/event-emitter/handle");
23
23
  var _constants2 = require("../../metrics/constants");
24
+ var _now = require("../../../common/timing/now");
25
+ var _timeKeeper = require("../../../common/timing/time-keeper");
24
26
  function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); }
25
27
  function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && Object.prototype.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; }
26
28
  class Aggregate extends _aggregateBase.AggregateBase {
@@ -35,9 +37,9 @@ class Aggregate extends _aggregateBase.AggregateBase {
35
37
  _timeToFirstByte.timeToFirstByte.subscribe(_ref => {
36
38
  let {
37
39
  value,
38
- entries
40
+ attrs
39
41
  } = _ref;
40
- const navEntry = entries[0];
42
+ const navEntry = attrs.navigationEntry;
41
43
  this.timeToFirstByte = Math.max(value, this.timeToFirstByte);
42
44
  this.firstByteToWindowLoad = Math.max(Math.round(navEntry.loadEventEnd - this.timeToFirstByte), this.firstByteToWindowLoad); // our "frontend" duration
43
45
  this.firstByteToDomContent = Math.max(Math.round(navEntry.domContentLoadedEventEnd - this.timeToFirstByte), this.firstByteToDomContent); // our "dom processing" duration
@@ -116,7 +118,7 @@ class Aggregate extends _aggregateBase.AggregateBase {
116
118
  }
117
119
  queryParameters.fp = _firstPaint.firstPaint.current.value;
118
120
  queryParameters.fcp = _firstContentfulPaint.firstContentfulPaint.current.value;
119
- const rumStartTime = this.timeKeeper.now();
121
+ const rumStartTime = (0, _now.now)();
120
122
  harvester.send({
121
123
  endpoint: 'rum',
122
124
  payload: {
@@ -133,14 +135,17 @@ class Aggregate extends _aggregateBase.AggregateBase {
133
135
  responseText,
134
136
  xhr
135
137
  } = _ref3;
136
- const rumEndTime = this.timeKeeper.now();
138
+ const rumEndTime = (0, _now.now)();
137
139
  if (status >= 400 || status === 0) {
138
140
  // Adding retry logic for the rum call will be a separate change
139
141
  this.ee.abort();
140
142
  return;
141
143
  }
142
144
  try {
143
- this.timeKeeper.processRumRequest(xhr, rumStartTime, rumEndTime);
145
+ const timeKeeper = new _timeKeeper.TimeKeeper();
146
+ timeKeeper.processRumRequest(xhr, rumStartTime, rumEndTime);
147
+ if (!timeKeeper.ready) throw new Error('TimeKeeper not ready');
148
+ agentRuntime.timeKeeper = timeKeeper;
144
149
  } catch (error) {
145
150
  (0, _handle.handle)(_constants2.SUPPORTABILITY_METRIC_CHANNEL, ['PVE/NRTime/Calculation/Failed'], undefined, _features.FEATURE_NAMES.metrics, this.ee);
146
151
  (0, _drain.drain)(this.agentIdentifier, _features.FEATURE_NAMES.metrics, true);
@@ -21,6 +21,8 @@ var _interactionToNextPaint = require("../../../common/vitals/interaction-to-nex
21
21
  var _largestContentfulPaint = require("../../../common/vitals/largest-contentful-paint");
22
22
  var _timeToFirstByte = require("../../../common/vitals/time-to-first-byte");
23
23
  var _longTask = require("../../../common/vitals/long-task");
24
+ var _pageVisibility = require("../../../common/window/page-visibility");
25
+ var _constants2 = require("../../../common/vitals/constants");
24
26
  /*
25
27
  * Copyright 2020 New Relic Corporation. All rights reserved.
26
28
  * SPDX-License-Identifier: Apache-2.0
@@ -44,15 +46,13 @@ class Aggregate extends _aggregateBase.AggregateBase {
44
46
  this.timingsSent = [];
45
47
  this.curSessEndRecorded = false;
46
48
  if ((0, _config.getConfigurationValue)(this.agentIdentifier, 'page_view_timing.long_task') === true) _longTask.longTask.subscribe(this.#handleVitalMetric);
47
-
48
- /* It's important that CWV api, like "onLCP", is called before this scheduler is initialized. The reason is because they listen to the same
49
- on vis change or pagehide events, and we'd want ex. onLCP to record the timing (win the race) before we try to send "final harvest". */
50
-
51
49
  (0, _registerHandler.registerHandler)('docHidden', msTimestamp => this.endCurrentSession(msTimestamp), this.featureName, this.ee);
52
50
  (0, _registerHandler.registerHandler)('winPagehide', msTimestamp => this.recordPageUnload(msTimestamp), this.featureName, this.ee);
53
51
  const initialHarvestSeconds = (0, _config.getConfigurationValue)(this.agentIdentifier, 'page_view_timing.initialHarvestSeconds') || 10;
54
52
  const harvestTimeSeconds = (0, _config.getConfigurationValue)(this.agentIdentifier, 'page_view_timing.harvestTimeSeconds') || 30;
55
53
  this.waitForFlags([]).then(() => {
54
+ /* It's important that CWV api, like "onLCP", is called before the **scheduler** is initialized. The reason is because they listen to the same
55
+ on vis change or pagehide events, and we'd want ex. onLCP to record the timing (win the race) before we try to send "final harvest". */
56
56
  _firstPaint.firstPaint.subscribe(this.#handleVitalMetric);
57
57
  _firstContentfulPaint.firstContentfulPaint.subscribe(this.#handleVitalMetric);
58
58
  _firstInputDelay.firstInputDelay.subscribe(this.#handleVitalMetric);
@@ -60,10 +60,23 @@ class Aggregate extends _aggregateBase.AggregateBase {
60
60
  _interactionToNextPaint.interactionToNextPaint.subscribe(this.#handleVitalMetric);
61
61
  _timeToFirstByte.timeToFirstByte.subscribe(_ref2 => {
62
62
  let {
63
- entries
63
+ attrs
64
64
  } = _ref2;
65
- this.addTiming('load', Math.round(entries[0].loadEventEnd));
65
+ this.addTiming('load', Math.round(attrs.navigationEntry.loadEventEnd));
66
66
  });
67
+ (0, _pageVisibility.subscribeToVisibilityChange)(() => {
68
+ /* Downstream, the event consumer interprets all timing node value as ms-unit and converts it to seconds via division by 1000. CLS is unitless so this normally is a problem.
69
+ bel.6 schema also doesn't support decimal values, of which cls within [0,1). However, the two nicely cancels out, and we can multiply cls by 1000 to both negate the division
70
+ and send an integer > 1. We effectively lose some precision down to 3 decimal places for this workaround. E.g. (real) 0.749132... -> 749.132...-> 749 -> 0.749 (final) */
71
+ const {
72
+ name,
73
+ value,
74
+ attrs
75
+ } = _cumulativeLayoutShift.cumulativeLayoutShift.current;
76
+ if (value === undefined) return;
77
+ this.addTiming(name, value * 1000, attrs);
78
+ }, true); // CLS node should only reports on vis change rather than on every change
79
+
67
80
  const scheduler = new _harvestScheduler.HarvestScheduler('events', {
68
81
  onFinished: function () {
69
82
  return _this.onHarvestFinished(...arguments);
@@ -113,8 +126,9 @@ class Aggregate extends _aggregateBase.AggregateBase {
113
126
  Issue: Because NR 'pageHide' was only sent once with what is considered the "final" CLS value, in the case that 'pageHide' fires before 'load' happens, we incorrectly a final CLS of 0 for that page.
114
127
  Mitigation: We've set initial CLS to null so that it's omitted from timings like 'pageHide' in that edge case. It should only be included if onCLS callback was executed at least once.
115
128
  Future: onCLS value changes should be reported directly & CLS separated into its own timing node so it's not beholden to 'pageHide' firing. It'd also be possible to report the real final CLS.
129
+ *cli Mar'24 update: CLS now emitted as its own timing node in addition to as-property under other nodes. The 'cls' property is unnecessary for cls nodes.
116
130
  */
117
- if (_cumulativeLayoutShift.cumulativeLayoutShift.current.value >= 0) {
131
+ if (name !== _constants2.VITAL_NAMES.CUMULATIVE_LAYOUT_SHIFT && _cumulativeLayoutShift.cumulativeLayoutShift.current.value >= 0) {
118
132
  attrs.cls = _cumulativeLayoutShift.cumulativeLayoutShift.current.value;
119
133
  }
120
134
  this.timings.push({
@@ -7,10 +7,10 @@ exports.Instrument = void 0;
7
7
  var _handle = require("../../../common/event-emitter/handle");
8
8
  var _pageVisibility = require("../../../common/window/page-visibility");
9
9
  var _eventListenerOpts = require("../../../common/event-listener/event-listener-opts");
10
- var _now = require("../../../common/timing/now");
11
10
  var _instrumentBase = require("../../utils/instrument-base");
12
11
  var _constants = require("../constants");
13
12
  var _runtime = require("../../../common/constants/runtime");
13
+ var _now = require("../../../common/timing/now");
14
14
  /*
15
15
  * Copyright 2020 New Relic Corporation. All rights reserved.
16
16
  * SPDX-License-Identifier: Apache-2.0
@@ -17,11 +17,12 @@ var _constants2 = require("../../metrics/constants");
17
17
  var _handle = require("../../../common/event-emitter/handle");
18
18
  var _features = require("../../../loaders/features/features");
19
19
  var _env = require("../../../common/constants/env.npm");
20
- var _now = require("../../../common/timing/now");
21
20
  var _constants3 = require("../../../common/session/constants");
22
21
  var _stringify = require("../../../common/util/stringify");
23
22
  var _stylesheetEvaluator = require("../shared/stylesheet-evaluator");
24
23
  var _drain = require("../../../common/drain/drain");
24
+ var _now = require("../../../common/timing/now");
25
+ var _utils = require("../shared/utils");
25
26
  function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); }
26
27
  function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && Object.prototype.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; } /*
27
28
  * Copyright 2023 New Relic Corporation. All rights reserved.
@@ -35,6 +36,8 @@ function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e;
35
36
  */
36
37
  class Aggregate extends _aggregateBase.AggregateBase {
37
38
  static featureName = _constants.FEATURE_NAME;
39
+ mode = _constants3.MODE.OFF;
40
+
38
41
  // pass the recorder into the aggregator
39
42
  constructor(agentIdentifier, aggregator, args) {
40
43
  super(agentIdentifier, aggregator, _constants.FEATURE_NAME);
@@ -48,21 +51,18 @@ class Aggregate extends _aggregateBase.AggregateBase {
48
51
  this.gzipper = undefined;
49
52
  /** populated with the u8 string lib async */
50
53
  this.u8 = undefined;
51
- /** the mode to start in. Defaults to off */
52
- const {
53
- session
54
- } = (0, _config.getRuntime)(this.agentIdentifier);
55
- this.mode = session.state.sessionReplayMode || _constants3.MODE.OFF;
56
54
 
57
55
  /** set by BCS response */
58
56
  this.entitled = false;
57
+ /** set at BCS response, stored in runtime */
58
+ this.timeKeeper = undefined;
59
59
  this.recorder = args?.recorder;
60
- if (this.recorder) this.recorder.parent = this;
60
+ this.preloaded = !!this.recorder;
61
+ this.errorNoticed = args?.errorNoticed || false;
61
62
  (0, _handle.handle)(_constants2.SUPPORTABILITY_METRIC_CHANNEL, ['Config/SessionReplay/Enabled'], undefined, _features.FEATURE_NAMES.metrics, this.ee);
62
63
 
63
64
  // The SessionEntity class can emit a message indicating the session was cleared and reset (expiry, inactivity). This feature must abort and never resume if that occurs.
64
65
  this.ee.on(_constants3.SESSION_EVENTS.RESET, () => {
65
- this.scheduler.runHarvest();
66
66
  this.abort(_constants.ABORT_REASONS.RESET);
67
67
  });
68
68
 
@@ -106,17 +106,6 @@ class Aggregate extends _aggregateBase.AggregateBase {
106
106
  (0, _registerHandler.registerHandler)(_constants.SR_EVENT_EMITTER_TYPES.PAUSE, () => {
107
107
  this.forceStop(this.mode !== _constants3.MODE.ERROR);
108
108
  }, this.featureName, this.ee);
109
-
110
- // Wait for an error to be reported. This currently is wrapped around the "Error" feature. This is a feature-feature dependency.
111
- // This was to ensure that all errors, including those on the page before load and those handled with "noticeError" are accounted for. Needs evalulation
112
- (0, _registerHandler.registerHandler)('errorAgg', e => {
113
- this.errorNoticed = true;
114
- if (this.recorder) this.recorder.currentBufferTarget.hasError = true;
115
- // run once
116
- if (this.mode === _constants3.MODE.ERROR && _runtime.globalScope?.document.visibilityState === 'visible') {
117
- this.switchToFull();
118
- }
119
- }, this.featureName, this.ee);
120
109
  const {
121
110
  error_sampling_rate,
122
111
  sampling_rate,
@@ -157,6 +146,13 @@ class Aggregate extends _aggregateBase.AggregateBase {
157
146
  (0, _handle.handle)(_constants2.SUPPORTABILITY_METRIC_CHANNEL, ['Config/SessionReplay/SamplingRate/Value', sampling_rate], undefined, _features.FEATURE_NAMES.metrics, this.ee);
158
147
  (0, _handle.handle)(_constants2.SUPPORTABILITY_METRIC_CHANNEL, ['Config/SessionReplay/ErrorSamplingRate/Value', error_sampling_rate], undefined, _features.FEATURE_NAMES.metrics, this.ee);
159
148
  }
149
+ handleError(e) {
150
+ if (this.recorder) this.recorder.currentBufferTarget.hasError = true;
151
+ // run once
152
+ if (this.mode === _constants3.MODE.ERROR && _runtime.globalScope?.document.visibilityState === 'visible') {
153
+ this.switchToFull();
154
+ }
155
+ }
160
156
  switchToFull() {
161
157
  this.mode = _constants3.MODE.FULL;
162
158
  // if the error was noticed AFTER the recorder was already imported....
@@ -189,8 +185,10 @@ class Aggregate extends _aggregateBase.AggregateBase {
189
185
  // session replay samples can only be decided on the first load of a session
190
186
  // session replays can continue if already in progress
191
187
  const {
192
- session
188
+ session,
189
+ timeKeeper
193
190
  } = (0, _config.getRuntime)(this.agentIdentifier);
191
+ this.timeKeeper = timeKeeper;
194
192
  if (!session.isNew && !ignoreSession) {
195
193
  // inherit the mode of the existing session
196
194
  this.mode = session.state.sessionReplayMode;
@@ -219,12 +217,13 @@ class Aggregate extends _aggregateBase.AggregateBase {
219
217
  } catch (err) {
220
218
  return this.abort(_constants.ABORT_REASONS.IMPORT);
221
219
  }
220
+ } else {
221
+ this.recorder.parent = this;
222
222
  }
223
223
 
224
224
  // If an error was noticed before the mode could be set (like in the early lifecycle of the page), immediately set to FULL mode
225
- if (this.mode === _constants3.MODE.ERROR && this.errorNoticed) {
226
- this.mode = _constants3.MODE.FULL;
227
- }
225
+ if (this.mode === _constants3.MODE.ERROR && this.errorNoticed) this.mode = _constants3.MODE.FULL;
226
+ if (!this.preloaded) this.ee.on('err', e => this.handleError(e));
228
227
 
229
228
  // FULL mode records AND reports from the beginning, while ERROR mode only records (but does not report).
230
229
  // ERROR mode will do this until an error is thrown, and then switch into FULL mode.
@@ -256,7 +255,7 @@ class Aggregate extends _aggregateBase.AggregateBase {
256
255
  let {
257
256
  opts
258
257
  } = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
259
- if (!this.recorder) return;
258
+ if (!this.recorder || !this.timeKeeper?.ready) return;
260
259
  const recorderEvents = this.recorder.getEvents();
261
260
  // get the event type and use that to trigger another harvest if needed
262
261
  if (!recorderEvents.events.length || this.mode !== _constants3.MODE.FULL || this.blocked) return;
@@ -265,18 +264,39 @@ class Aggregate extends _aggregateBase.AggregateBase {
265
264
  this.recorder.clearBuffer();
266
265
  return;
267
266
  }
267
+ (0, _handle.handle)(_constants2.SUPPORTABILITY_METRIC_CHANNEL, ['SessionReplay/Harvest/Attempts'], undefined, _features.FEATURE_NAMES.metrics, this.ee);
268
268
  let len = 0;
269
269
  if (!!this.gzipper && !!this.u8) {
270
- payload.body = this.gzipper(this.u8("[".concat(payload.body.map(e => e.__serialized).join(','), "]")));
270
+ payload.body = this.gzipper(this.u8("[".concat(payload.body.map(_ref2 => {
271
+ let {
272
+ __serialized,
273
+ ...e
274
+ } = _ref2;
275
+ if (e.__newrelic && __serialized) return __serialized;
276
+ const output = {
277
+ ...e
278
+ };
279
+ if (!output.__newrelic) {
280
+ output.__newrelic = (0, _utils.buildNRMetaNode)(e.timestamp, this.timeKeeper);
281
+ output.timestamp = this.timeKeeper.correctAbsoluteTimestamp(e.timestamp);
282
+ }
283
+ return (0, _stringify.stringify)(output);
284
+ }).join(','), "]")));
271
285
  len = payload.body.length;
272
286
  this.scheduler.opts.gzip = true;
273
287
  } else {
274
- payload.body = payload.body.map(_ref2 => {
288
+ payload.body = payload.body.map(_ref3 => {
275
289
  let {
276
290
  __serialized,
277
291
  ...node
278
- } = _ref2;
279
- return node;
292
+ } = _ref3;
293
+ if (node.__newrelic) return node;
294
+ const output = {
295
+ ...node
296
+ };
297
+ output.__newrelic = (0, _utils.buildNRMetaNode)(node.timestamp, this.timeKeeper);
298
+ output.timestamp = this.timeKeeper.correctAbsoluteTimestamp(node.timestamp);
299
+ return output;
280
300
  });
281
301
  len = (0, _stringify.stringify)(payload.body).length;
282
302
  this.scheduler.opts.gzip = false;
@@ -296,6 +316,11 @@ class Aggregate extends _aggregateBase.AggregateBase {
296
316
  if (recorderEvents.type === 'preloaded') this.scheduler.runHarvest(opts);
297
317
  return [payload];
298
318
  }
319
+ getCorrectedTimestamp(node) {
320
+ if (!node.timestamp) return;
321
+ if (node.__newrelic) return node.timestamp;
322
+ return this.timeKeeper.correctAbsoluteTimestamp(node.timestamp);
323
+ }
299
324
  getHarvestContents(recorderEvents) {
300
325
  recorderEvents ??= this.recorder.getEvents();
301
326
  let events = recorderEvents.events;
@@ -320,12 +345,11 @@ class Aggregate extends _aggregateBase.AggregateBase {
320
345
  events = events.slice(0, events.length - 1);
321
346
  recorderEvents.hasMeta = !!events.find(x => x.type === _constants.RRWEB_EVENT_TYPES.Meta);
322
347
  }
323
- const agentOffset = (0, _config.getRuntime)(this.agentIdentifier).offset;
324
348
  const relativeNow = (0, _now.now)();
325
- const firstEventTimestamp = events[0]?.timestamp; // from rrweb node
326
- const lastEventTimestamp = events[events.length - 1]?.timestamp; // from rrweb node
327
- const firstTimestamp = firstEventTimestamp || recorderEvents.cycleTimestamp; // from rrweb node || from when the harvest cycle started
328
- const lastTimestamp = lastEventTimestamp || agentOffset + relativeNow;
349
+ const firstEventTimestamp = this.getCorrectedTimestamp(events[0]); // from rrweb node
350
+ const lastEventTimestamp = this.getCorrectedTimestamp(events[events.length - 1]); // from rrweb node
351
+ const firstTimestamp = firstEventTimestamp || this.timeKeeper.correctAbsoluteTimestamp(recorderEvents.cycleTimestamp); // from rrweb node || from when the harvest cycle started
352
+ const lastTimestamp = lastEventTimestamp || this.timeKeeper.convertRelativeTimestamp(relativeNow);
329
353
  const agentMetadata = agentRuntime.appMetadata?.agents?.[0] || {};
330
354
  return {
331
355
  qs: {
@@ -333,6 +357,7 @@ class Aggregate extends _aggregateBase.AggregateBase {
333
357
  type: 'SessionReplay',
334
358
  app_id: info.applicationID,
335
359
  protocol_version: '0',
360
+ timestamp: firstTimestamp,
336
361
  attributes: (0, _encode.obj)({
337
362
  // this section of attributes must be controllable and stay below the query param padding limit -- see QUERY_PARAM_PADDING
338
363
  // if not, data could be lost to truncation at time of sending, potentially breaking parsing / API behavior in NR1
@@ -343,9 +368,7 @@ class Aggregate extends _aggregateBase.AggregateBase {
343
368
  entityGuid: agentMetadata.entityGuid
344
369
  }),
345
370
  'replay.firstTimestamp': firstTimestamp,
346
- 'replay.firstTimestampOffset': firstTimestamp - agentOffset,
347
371
  'replay.lastTimestamp': lastTimestamp,
348
- 'replay.durationMs': lastTimestamp - firstTimestamp,
349
372
  'replay.nodes': events.length,
350
373
  'session.durationMs': agentRuntime.session.getDuration(),
351
374
  agentVersion: agentRuntime.version,
@@ -359,6 +382,7 @@ class Aggregate extends _aggregateBase.AggregateBase {
359
382
  invalidStylesheetsDetected: _stylesheetEvaluator.stylesheetEvaluator.invalidStylesheetsDetected,
360
383
  inlinedAllStylesheets: recorderEvents.inlinedAllStylesheets,
361
384
  'rrweb.version': _env.RRWEB_VERSION,
385
+ 'payload.type': recorderEvents.type,
362
386
  // customer-defined data should go last so that if it exceeds the query param padding limit it will be truncated instead of important attrs
363
387
  ...(endUserId && {
364
388
  'enduser.id': endUserId
@@ -9,7 +9,8 @@ var _features = require("../../loaders/features/features");
9
9
  const FEATURE_NAME = exports.FEATURE_NAME = _features.FEATURE_NAMES.sessionReplay;
10
10
  const SR_EVENT_EMITTER_TYPES = exports.SR_EVENT_EMITTER_TYPES = {
11
11
  RECORD: 'recordReplay',
12
- PAUSE: 'pauseReplay'
12
+ PAUSE: 'pauseReplay',
13
+ REPLAY_RUNNING: 'replayRunning'
13
14
  };
14
15
  const AVG_COMPRESSION = exports.AVG_COMPRESSION = 0.12;
15
16
  const RRWEB_EVENT_TYPES = exports.RRWEB_EVENT_TYPES = {
@@ -29,6 +29,11 @@ class Instrument extends _instrumentBase.InstrumentBase {
29
29
  session = JSON.parse(localStorage.getItem("".concat(_constants.PREFIX, "_").concat(_constants.DEFAULT_KEY)));
30
30
  } catch (err) {}
31
31
  if (this.#canPreloadRecorder(session)) {
32
+ /** If this is preloaded, set up a buffer, if not, later when sampling we will set up a .on for live events */
33
+ this.ee.on('err', e => {
34
+ this.errorNoticed = true;
35
+ if (this.featAggregate) this.featAggregate.handleError();
36
+ });
32
37
  this.#startRecording(session?.sessionReplayMode);
33
38
  } else {
34
39
  this.importAggregator();
@@ -55,12 +60,14 @@ class Instrument extends _instrumentBase.InstrumentBase {
55
60
  } = await Promise.resolve().then(() => _interopRequireWildcard(require( /* webpackChunkName: "recorder" */'../shared/recorder')));
56
61
  this.recorder = new Recorder({
57
62
  mode,
58
- agentIdentifier: this.agentIdentifier
63
+ agentIdentifier: this.agentIdentifier,
64
+ ee: this.ee
59
65
  });
60
66
  this.recorder.startRecording();
61
67
  this.abortHandler = this.recorder.stopRecording;
62
68
  this.importAggregator({
63
- recorder: this.recorder
69
+ recorder: this.recorder,
70
+ errorNoticed: this.errorNoticed
64
71
  });
65
72
  }
66
73
  }
@@ -14,16 +14,20 @@ var _stylesheetEvaluator = require("./stylesheet-evaluator");
14
14
  var _handle = require("../../../common/event-emitter/handle");
15
15
  var _constants3 = require("../../metrics/constants");
16
16
  var _features = require("../../../loaders/features/features");
17
+ var _utils = require("./utils");
17
18
  class Recorder {
18
19
  /** Each page mutation or event will be stored (raw) in this array. This array will be cleared on each harvest */
19
- #events = new _recorderEvents.RecorderEvents();
20
+ #events;
20
21
  /** Backlog used for a 2-part sliding window to guarantee a 15-30s buffer window */
21
- #backloggedEvents = new _recorderEvents.RecorderEvents();
22
+ #backloggedEvents;
22
23
  /** array of recorder events -- Will be filled only if forced harvest was triggered and harvester does not exist */
23
- #preloaded = [new _recorderEvents.RecorderEvents()];
24
+ #preloaded;
24
25
  /** flag that if true, blocks events from being "stored". Only set to true when a full snapshot has incomplete nodes (only stylesheets ATM) */
25
26
  #fixing = false;
26
27
  constructor(parent) {
28
+ this.#events = new _recorderEvents.RecorderEvents();
29
+ this.#backloggedEvents = new _recorderEvents.RecorderEvents();
30
+ this.#preloaded = [new _recorderEvents.RecorderEvents()];
27
31
  /** True when actively recording, false when paused or stopped */
28
32
  this.recording = false;
29
33
  /** The pointer to the current bucket holding rrweb events */
@@ -77,6 +81,10 @@ class Recorder {
77
81
  inline_images,
78
82
  collect_fonts
79
83
  } = (0, _config.getConfigurationValue)(this.parent.agentIdentifier, 'session_replay');
84
+ const customMasker = (text, element) => {
85
+ if (element?.type?.toLowerCase() !== 'password' && (element?.dataset.nrUnmask !== undefined || element?.classList.contains('nr-unmask'))) return text;
86
+ return '*'.repeat(text.length);
87
+ };
80
88
  // set up rrweb configurations for maximum privacy --
81
89
  // https://newrelic.atlassian.net/wiki/spaces/O11Y/pages/2792293280/2023+02+28+Browser+-+Session+Replay#Configuration-options
82
90
  const stop = (0, _rrweb.record)({
@@ -87,14 +95,18 @@ class Recorder {
87
95
  blockSelector: block_selector,
88
96
  maskInputOptions: mask_input_options,
89
97
  maskTextSelector: mask_text_selector,
98
+ maskTextFn: customMasker,
90
99
  maskAllInputs: mask_all_inputs,
100
+ maskInputFn: customMasker,
91
101
  inlineStylesheet: inline_stylesheet,
92
102
  inlineImages: inline_images,
93
103
  collectFonts: collect_fonts,
94
104
  checkoutEveryNms: _constants.CHECKOUT_MS[this.parent.mode]
95
105
  });
106
+ this.parent.ee.emit(_constants.SR_EVENT_EMITTER_TYPES.REPLAY_RUNNING, [true, this.parent.mode]);
96
107
  this.stopRecording = () => {
97
108
  this.recording = false;
109
+ this.parent.ee.emit(_constants.SR_EVENT_EMITTER_TYPES.REPLAY_RUNNING, [false, this.parent.mode]);
98
110
  stop();
99
111
  };
100
112
  }
@@ -136,9 +148,13 @@ class Recorder {
136
148
  /** Store a payload in the buffer (this.#events). This should be the callback to the recording lib noticing a mutation */
137
149
  store(event, isCheckout) {
138
150
  if (!event) return;
139
- event.__serialized = (0, _stringify.stringify)(event);
140
151
  if (!this.parent.scheduler && this.#preloaded.length) this.currentBufferTarget = this.#preloaded[this.#preloaded.length - 1];else this.currentBufferTarget = this.#events;
141
152
  if (this.parent.blocked) return;
153
+ if (this.parent.timeKeeper?.ready && !event.__newrelic) {
154
+ event.__newrelic = (0, _utils.buildNRMetaNode)(event.timestamp, this.parent.timeKeeper);
155
+ event.timestamp = this.parent.timeKeeper.correctAbsoluteTimestamp(event.timestamp);
156
+ }
157
+ event.__serialized = (0, _stringify.stringify)(event);
142
158
  const eventBytes = event.__serialized.length;
143
159
  /** The estimated size of the payload after compression */
144
160
  const payloadSize = this.getPayloadSize(eventBytes);
@@ -3,6 +3,7 @@
3
3
  Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
+ exports.buildNRMetaNode = buildNRMetaNode;
6
7
  exports.canImportReplayAgg = canImportReplayAgg;
7
8
  exports.enableSessionTracking = void 0;
8
9
  exports.isPreloadAllowed = isPreloadAllowed;
@@ -23,4 +24,15 @@ function isPreloadAllowed(agentId) {
23
24
  function canImportReplayAgg(agentId, sessionMgr) {
24
25
  if (!hasReplayPrerequisite(agentId)) return false;
25
26
  return !!sessionMgr?.isNew || !!sessionMgr?.state.sessionReplayMode; // Session Replay should only try to run if already running from a previous page, or at the beginning of a session
27
+ }
28
+ function buildNRMetaNode(timestamp, timeKeeper) {
29
+ const correctedTimestamp = timeKeeper.correctAbsoluteTimestamp(timestamp);
30
+ return {
31
+ originalTimestamp: timestamp,
32
+ correctedTimestamp,
33
+ timestampDiff: timestamp - correctedTimestamp,
34
+ timeKeeperOriginTime: timeKeeper.originTime,
35
+ timeKeeperCorrectedOriginTime: timeKeeper.correctedOriginTime,
36
+ timeKeeperDiff: Math.floor(timeKeeper.originTime - timeKeeper.correctedOriginTime)
37
+ };
26
38
  }