@newrelic/browser-agent 1.252.0 → 1.253.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 (217) hide show
  1. package/CHANGELOG.md +18 -0
  2. package/README.md +6 -6
  3. package/dist/cjs/cdn/experimental.js +6 -2
  4. package/dist/cjs/cdn/spa.js +5 -3
  5. package/dist/cjs/common/aggregate/aggregator.js +1 -8
  6. package/dist/cjs/common/config/state/init.js +7 -0
  7. package/dist/cjs/common/constants/env.cdn.js +1 -1
  8. package/dist/cjs/common/constants/env.npm.js +1 -1
  9. package/dist/cjs/common/context/observation-context-manager.js +56 -0
  10. package/dist/cjs/common/event-emitter/contextual-ee.js +12 -9
  11. package/dist/cjs/common/session/constants.js +2 -1
  12. package/dist/cjs/common/session/session-entity.js +3 -1
  13. package/dist/cjs/common/timing/nav-timing.js +8 -3
  14. package/dist/cjs/common/timing/now.js +1 -1
  15. package/dist/cjs/common/util/feature-flags.js +1 -1
  16. package/dist/cjs/common/wrap/index.js +0 -7
  17. package/dist/cjs/common/wrap/wrap-events.js +2 -2
  18. package/dist/cjs/common/wrap/wrap-fetch.js +2 -1
  19. package/dist/cjs/common/wrap/wrap-function.js +5 -7
  20. package/dist/cjs/common/wrap/wrap-promise.js +2 -1
  21. package/dist/cjs/features/ajax/aggregate/index.js +34 -16
  22. package/dist/cjs/features/jserrors/aggregate/index.js +77 -66
  23. package/dist/cjs/features/page_view_event/aggregate/index.js +1 -1
  24. package/dist/cjs/features/page_view_event/aggregate/initialized-features.js +1 -0
  25. package/dist/cjs/features/session_replay/aggregate/index.js +96 -94
  26. package/dist/cjs/features/session_replay/constants.js +5 -1
  27. package/dist/cjs/features/session_replay/instrument/index.js +24 -8
  28. package/dist/cjs/features/session_replay/shared/recorder.js +5 -4
  29. package/dist/cjs/features/session_replay/shared/stylesheet-evaluator.js +8 -7
  30. package/dist/cjs/features/session_replay/shared/utils.js +26 -0
  31. package/dist/cjs/features/soft_navigations/aggregate/ajax-node.js +50 -0
  32. package/dist/cjs/features/soft_navigations/aggregate/bel-node.js +29 -0
  33. package/dist/cjs/features/soft_navigations/aggregate/index.js +263 -0
  34. package/dist/cjs/features/soft_navigations/aggregate/initial-page-load-interaction.js +62 -0
  35. package/dist/cjs/features/soft_navigations/aggregate/interaction.js +146 -0
  36. package/dist/cjs/features/soft_navigations/constants.js +31 -0
  37. package/dist/cjs/features/soft_navigations/index.js +12 -0
  38. package/dist/cjs/features/soft_navigations/instrument/index.js +79 -0
  39. package/dist/cjs/features/spa/aggregate/index.js +4 -4
  40. package/dist/cjs/features/utils/agent-session.js +2 -1
  41. package/dist/cjs/features/utils/instrument-base.js +6 -9
  42. package/dist/cjs/features/utils/lazy-feature-loader.js +2 -0
  43. package/dist/cjs/loaders/agent-base.js +18 -3
  44. package/dist/cjs/loaders/agent.js +15 -18
  45. package/dist/cjs/loaders/api/api-methods.js +9 -0
  46. package/dist/cjs/loaders/api/api.js +17 -18
  47. package/dist/cjs/loaders/configure/configure.js +5 -2
  48. package/dist/cjs/loaders/features/enabled-features.js +1 -1
  49. package/dist/cjs/loaders/features/features.js +3 -1
  50. package/dist/esm/cdn/experimental.js +5 -2
  51. package/dist/esm/cdn/spa.js +3 -1
  52. package/dist/esm/common/aggregate/aggregator.js +1 -8
  53. package/dist/esm/common/config/state/init.js +7 -0
  54. package/dist/esm/common/constants/env.cdn.js +1 -1
  55. package/dist/esm/common/constants/env.npm.js +1 -1
  56. package/dist/esm/common/context/observation-context-manager.js +49 -0
  57. package/dist/esm/common/event-emitter/contextual-ee.js +12 -9
  58. package/dist/esm/common/session/constants.js +1 -0
  59. package/dist/esm/common/session/session-entity.js +3 -1
  60. package/dist/esm/common/timing/nav-timing.js +8 -3
  61. package/dist/esm/common/timing/now.js +1 -1
  62. package/dist/esm/common/util/feature-flags.js +1 -1
  63. package/dist/esm/common/wrap/index.js +1 -2
  64. package/dist/esm/common/wrap/wrap-events.js +3 -3
  65. package/dist/esm/common/wrap/wrap-fetch.js +3 -2
  66. package/dist/esm/common/wrap/wrap-function.js +4 -5
  67. package/dist/esm/common/wrap/wrap-promise.js +3 -2
  68. package/dist/esm/features/ajax/aggregate/index.js +36 -18
  69. package/dist/esm/features/jserrors/aggregate/index.js +77 -66
  70. package/dist/esm/features/page_view_event/aggregate/index.js +1 -1
  71. package/dist/esm/features/page_view_event/aggregate/initialized-features.js +1 -0
  72. package/dist/esm/features/session_replay/aggregate/index.js +97 -95
  73. package/dist/esm/features/session_replay/constants.js +4 -0
  74. package/dist/esm/features/session_replay/instrument/index.js +25 -9
  75. package/dist/esm/features/session_replay/shared/recorder.js +5 -4
  76. package/dist/esm/features/session_replay/shared/stylesheet-evaluator.js +8 -7
  77. package/dist/esm/features/session_replay/shared/utils.js +17 -0
  78. package/dist/esm/features/soft_navigations/aggregate/ajax-node.js +43 -0
  79. package/dist/esm/features/soft_navigations/aggregate/bel-node.js +22 -0
  80. package/dist/esm/features/soft_navigations/aggregate/index.js +256 -0
  81. package/dist/esm/features/soft_navigations/aggregate/initial-page-load-interaction.js +55 -0
  82. package/dist/esm/features/soft_navigations/aggregate/interaction.js +140 -0
  83. package/dist/esm/features/soft_navigations/constants.js +25 -0
  84. package/dist/esm/features/soft_navigations/index.js +1 -0
  85. package/dist/esm/features/soft_navigations/instrument/index.js +73 -0
  86. package/dist/esm/features/spa/aggregate/index.js +4 -4
  87. package/dist/esm/features/utils/agent-session.js +2 -1
  88. package/dist/esm/features/utils/instrument-base.js +7 -10
  89. package/dist/esm/features/utils/lazy-feature-loader.js +2 -0
  90. package/dist/esm/loaders/agent-base.js +18 -3
  91. package/dist/esm/loaders/agent.js +15 -18
  92. package/dist/esm/loaders/api/api-methods.js +3 -0
  93. package/dist/esm/loaders/api/api.js +17 -17
  94. package/dist/esm/loaders/configure/configure.js +5 -2
  95. package/dist/esm/loaders/features/enabled-features.js +1 -1
  96. package/dist/esm/loaders/features/features.js +3 -1
  97. package/dist/types/common/aggregate/aggregator.d.ts.map +1 -1
  98. package/dist/types/common/config/state/init.d.ts.map +1 -1
  99. package/dist/types/common/context/event-context.d.ts.map +1 -0
  100. package/dist/types/common/context/observation-context-manager.d.ts +28 -0
  101. package/dist/types/common/context/observation-context-manager.d.ts.map +1 -0
  102. package/dist/types/common/event-emitter/contextual-ee.d.ts +2 -2
  103. package/dist/types/common/event-emitter/contextual-ee.d.ts.map +1 -1
  104. package/dist/types/common/session/constants.d.ts +1 -0
  105. package/dist/types/common/session/constants.d.ts.map +1 -1
  106. package/dist/types/common/session/session-entity.d.ts +0 -1
  107. package/dist/types/common/session/session-entity.d.ts.map +1 -1
  108. package/dist/types/common/timing/nav-timing.d.ts.map +1 -1
  109. package/dist/types/common/wrap/index.d.ts +1 -2
  110. package/dist/types/common/wrap/index.d.ts.map +1 -1
  111. package/dist/types/common/wrap/wrap-fetch.d.ts.map +1 -1
  112. package/dist/types/common/wrap/wrap-function.d.ts +0 -1
  113. package/dist/types/common/wrap/wrap-function.d.ts.map +1 -1
  114. package/dist/types/common/wrap/wrap-promise.d.ts.map +1 -1
  115. package/dist/types/features/ajax/aggregate/index.d.ts.map +1 -1
  116. package/dist/types/features/jserrors/aggregate/index.d.ts +4 -3
  117. package/dist/types/features/jserrors/aggregate/index.d.ts.map +1 -1
  118. package/dist/types/features/page_view_event/aggregate/initialized-features.d.ts.map +1 -1
  119. package/dist/types/features/session_replay/aggregate/index.d.ts +1 -1
  120. package/dist/types/features/session_replay/aggregate/index.d.ts.map +1 -1
  121. package/dist/types/features/session_replay/constants.d.ts +4 -0
  122. package/dist/types/features/session_replay/constants.d.ts.map +1 -1
  123. package/dist/types/features/session_replay/instrument/index.d.ts.map +1 -1
  124. package/dist/types/features/session_replay/shared/recorder.d.ts.map +1 -1
  125. package/dist/types/features/session_replay/shared/stylesheet-evaluator.d.ts.map +1 -1
  126. package/dist/types/features/session_replay/shared/utils.d.ts +4 -0
  127. package/dist/types/features/session_replay/shared/utils.d.ts.map +1 -0
  128. package/dist/types/features/soft_navigations/aggregate/ajax-node.d.ts +19 -0
  129. package/dist/types/features/soft_navigations/aggregate/ajax-node.d.ts.map +1 -0
  130. package/dist/types/features/soft_navigations/aggregate/bel-node.d.ts +16 -0
  131. package/dist/types/features/soft_navigations/aggregate/bel-node.d.ts.map +1 -0
  132. package/dist/types/features/soft_navigations/aggregate/index.d.ts +36 -0
  133. package/dist/types/features/soft_navigations/aggregate/index.d.ts.map +1 -0
  134. package/dist/types/features/soft_navigations/aggregate/initial-page-load-interaction.d.ts +12 -0
  135. package/dist/types/features/soft_navigations/aggregate/initial-page-load-interaction.d.ts.map +1 -0
  136. package/dist/types/features/soft_navigations/aggregate/interaction.d.ts +50 -0
  137. package/dist/types/features/soft_navigations/aggregate/interaction.d.ts.map +1 -0
  138. package/dist/types/features/soft_navigations/constants.d.ts +20 -0
  139. package/dist/types/features/soft_navigations/constants.d.ts.map +1 -0
  140. package/dist/types/features/soft_navigations/index.d.ts +2 -0
  141. package/dist/types/features/soft_navigations/index.d.ts.map +1 -0
  142. package/dist/types/features/soft_navigations/instrument/index.d.ts +7 -0
  143. package/dist/types/features/soft_navigations/instrument/index.d.ts.map +1 -0
  144. package/dist/types/features/spa/aggregate/index.d.ts.map +1 -1
  145. package/dist/types/features/utils/agent-session.d.ts.map +1 -1
  146. package/dist/types/features/utils/instrument-base.d.ts +1 -7
  147. package/dist/types/features/utils/instrument-base.d.ts.map +1 -1
  148. package/dist/types/features/utils/lazy-feature-loader.d.ts.map +1 -1
  149. package/dist/types/loaders/agent-base.d.ts +5 -1
  150. package/dist/types/loaders/agent-base.d.ts.map +1 -1
  151. package/dist/types/loaders/agent.d.ts +2 -2
  152. package/dist/types/loaders/agent.d.ts.map +1 -1
  153. package/dist/types/loaders/api/api-methods.d.ts +3 -0
  154. package/dist/types/loaders/api/api-methods.d.ts.map +1 -0
  155. package/dist/types/loaders/api/api.d.ts +3 -6
  156. package/dist/types/loaders/api/api.d.ts.map +1 -1
  157. package/dist/types/loaders/configure/configure.d.ts.map +1 -1
  158. package/dist/types/loaders/features/features.d.ts +1 -0
  159. package/dist/types/loaders/features/features.d.ts.map +1 -1
  160. package/dist/types/loaders/micro-agent.d.ts +0 -1
  161. package/dist/types/loaders/micro-agent.d.ts.map +1 -1
  162. package/package.json +1 -1
  163. package/src/cdn/experimental.js +4 -2
  164. package/src/cdn/spa.js +3 -1
  165. package/src/common/aggregate/aggregator.js +2 -11
  166. package/src/common/config/state/init.js +3 -1
  167. package/src/common/context/observation-context-manager.js +55 -0
  168. package/src/common/event-emitter/contextual-ee.js +20 -10
  169. package/src/common/session/constants.js +1 -0
  170. package/src/common/session/session-entity.js +3 -1
  171. package/src/common/timing/nav-timing.js +7 -3
  172. package/src/common/timing/now.js +1 -1
  173. package/src/common/util/feature-flags.js +1 -1
  174. package/src/common/wrap/index.js +1 -2
  175. package/src/common/wrap/wrap-events.js +3 -3
  176. package/src/common/wrap/wrap-fetch.js +3 -2
  177. package/src/common/wrap/wrap-function.js +4 -6
  178. package/src/common/wrap/wrap-promise.js +3 -2
  179. package/src/features/ajax/aggregate/index.js +36 -18
  180. package/src/features/jserrors/aggregate/index.js +70 -73
  181. package/src/features/page_view_event/aggregate/index.js +1 -1
  182. package/src/features/page_view_event/aggregate/initialized-features.js +1 -0
  183. package/src/features/session_replay/aggregate/index.js +92 -95
  184. package/src/features/session_replay/constants.js +5 -0
  185. package/src/features/session_replay/instrument/index.js +24 -9
  186. package/src/features/session_replay/shared/recorder.js +5 -4
  187. package/src/features/session_replay/shared/stylesheet-evaluator.js +8 -7
  188. package/src/features/session_replay/shared/utils.js +19 -0
  189. package/src/features/soft_navigations/aggregate/ajax-node.js +57 -0
  190. package/src/features/soft_navigations/aggregate/bel-node.js +26 -0
  191. package/src/features/soft_navigations/aggregate/index.js +254 -0
  192. package/src/features/soft_navigations/aggregate/initial-page-load-interaction.js +53 -0
  193. package/src/features/soft_navigations/aggregate/interaction.js +159 -0
  194. package/src/features/soft_navigations/constants.js +29 -0
  195. package/src/features/soft_navigations/index.js +1 -0
  196. package/src/features/soft_navigations/instrument/index.js +67 -0
  197. package/src/features/spa/aggregate/index.js +5 -4
  198. package/src/features/utils/agent-session.js +2 -1
  199. package/src/features/utils/instrument-base.js +7 -10
  200. package/src/features/utils/lazy-feature-loader.js +2 -0
  201. package/src/loaders/agent-base.js +18 -3
  202. package/src/loaders/agent.js +18 -17
  203. package/src/loaders/api/api-methods.js +12 -0
  204. package/src/loaders/api/api.js +17 -28
  205. package/src/loaders/configure/configure.js +4 -1
  206. package/src/loaders/features/enabled-features.js +1 -1
  207. package/src/loaders/features/features.js +3 -1
  208. package/dist/cjs/common/wrap/wrap-raf.js +0 -55
  209. package/dist/esm/common/wrap/wrap-raf.js +0 -48
  210. package/dist/types/common/event-emitter/event-context.d.ts.map +0 -1
  211. package/dist/types/common/wrap/wrap-raf.d.ts +0 -16
  212. package/dist/types/common/wrap/wrap-raf.d.ts.map +0 -1
  213. package/src/common/wrap/wrap-raf.js +0 -52
  214. /package/dist/cjs/common/{event-emitter → context}/event-context.js +0 -0
  215. /package/dist/esm/common/{event-emitter → context}/event-context.js +0 -0
  216. /package/dist/types/common/{event-emitter → context}/event-context.d.ts +0 -0
  217. /package/src/common/{event-emitter → context}/event-context.js +0 -0
@@ -19,6 +19,7 @@ var _runtime = require("../../../common/constants/runtime");
19
19
  var _constants = require("../constants");
20
20
  var _features = require("../../../loaders/features/features");
21
21
  var _aggregateBase = require("../../utils/aggregate-base");
22
+ var _nreum = require("../../../common/window/nreum");
22
23
  /*
23
24
  * Copyright 2020 New Relic Corporation. All rights reserved.
24
25
  * SPDX-License-Identifier: Apache-2.0
@@ -37,21 +38,20 @@ class Aggregate extends _aggregateBase.AggregateBase {
37
38
  this.stackReported = {};
38
39
  this.observedAt = {};
39
40
  this.pageviewReported = {};
40
- this.errorCache = {};
41
+ this.bufferedErrorsUnderSpa = {};
41
42
  this.currentBody = undefined;
42
43
  this.errorOnPage = false;
43
44
 
44
45
  // this will need to change to match whatever ee we use in the instrument
45
- this.ee.on('interactionSaved', interaction => this.onInteractionSaved(interaction));
46
-
47
- // this will need to change to match whatever ee we use in the instrument
48
- this.ee.on('interactionDiscarded', interaction => this.onInteractionDiscarded(interaction));
46
+ this.ee.on('interactionDone', (interaction, wasSaved) => this.onInteractionDone(interaction, wasSaved));
49
47
  (0, _registerHandler.registerHandler)('err', function () {
50
48
  return _this.storeError(...arguments);
51
49
  }, this.featureName, this.ee);
52
50
  (0, _registerHandler.registerHandler)('ierr', function () {
53
51
  return _this.storeError(...arguments);
54
52
  }, this.featureName, this.ee);
53
+ (0, _registerHandler.registerHandler)('softNavFlush', (interactionId, wasFinished, softNavAttrs) => this.onSoftNavNotification(interactionId, wasFinished, softNavAttrs), this.featureName, this.ee); // when an ixn is done or cancelled
54
+
55
55
  const harvestTimeSeconds = (0, _config.getConfigurationValue)(this.agentIdentifier, 'jserrors.harvestTimeSeconds') || 10;
56
56
  const scheduler = new _harvestScheduler.HarvestScheduler('jserrors', {
57
57
  onFinished: function () {
@@ -193,82 +193,93 @@ class Aggregate extends _aggregateBase.AggregateBase {
193
193
  time
194
194
  };
195
195
 
196
- // sr, stn and spa aggregators listen to this event - stn sends the error in its payload,
197
- // and spa annotates the error with interaction info
198
- const msg = [type, bucketHash, params, newMetrics];
199
- (0, _handle.handle)('errorAgg', msg, undefined, _features.FEATURE_NAMES.sessionTrace, this.ee);
200
- (0, _handle.handle)('errorAgg', msg, undefined, _features.FEATURE_NAMES.spa, this.ee);
201
- (0, _handle.handle)('errorAgg', msg, undefined, _features.FEATURE_NAMES.sessionReplay, this.ee);
202
-
196
+ // Trace sends the error in its payload, and both trace & replay simply listens for any error to occur.
197
+ const jsErrorEvent = [type, bucketHash, params, newMetrics, customAttributes];
198
+ (0, _handle.handle)('errorAgg', jsErrorEvent, undefined, _features.FEATURE_NAMES.sessionTrace, this.ee);
199
+ (0, _handle.handle)('errorAgg', jsErrorEvent, undefined, _features.FEATURE_NAMES.sessionReplay, this.ee);
203
200
  // still send EE events for other features such as above, but stop this one from aggregating internal data
204
201
  if (this.blocked) return;
205
- var att = (0, _config.getInfo)(this.agentIdentifier).jsAttributes;
206
- if (params._interactionId != null) {
207
- // hold on to the error until the interaction finishes
208
- this.errorCache[params._interactionId] = this.errorCache[params._interactionId] || [];
209
- this.errorCache[params._interactionId].push([type, bucketHash, params, newMetrics, att, customAttributes]);
202
+ const softNavInUse = Boolean((0, _nreum.getNREUMInitializedAgent)(this.agentIdentifier)?.features[_features.FEATURE_NAMES.softNav]);
203
+ // Note: the following are subject to potential race cond wherein if the other feature aren't fully initialized, it'll be treated as there being no associated interaction.
204
+ // They each will also tack on their respective properties to the params object as part of the decision flow.
205
+ if (softNavInUse) (0, _handle.handle)('jserror', [params, time], undefined, _features.FEATURE_NAMES.softNav, this.ee);else (0, _handle.handle)('errorAgg', jsErrorEvent, undefined, _features.FEATURE_NAMES.spa, this.ee);
206
+ if (params.browserInteractionId && !params._softNavFinished) {
207
+ // hold onto the error until the in-progress interaction is done, eithered saved or discarded
208
+ this.bufferedErrorsUnderSpa[params.browserInteractionId] ??= [];
209
+ this.bufferedErrorsUnderSpa[params.browserInteractionId].push(jsErrorEvent);
210
+ } else if (params._interactionId != null) {
211
+ // same as above, except tailored for the way old spa does it
212
+ this.bufferedErrorsUnderSpa[params._interactionId] = this.bufferedErrorsUnderSpa[params._interactionId] || [];
213
+ this.bufferedErrorsUnderSpa[params._interactionId].push(jsErrorEvent);
210
214
  } else {
211
- // store custom attributes
212
- var customParams = {};
213
- (0, _mapOwn.mapOwn)(att, setCustom);
214
- if (customAttributes) {
215
- (0, _mapOwn.mapOwn)(customAttributes, setCustom);
216
- }
217
- var jsAttributesHash = (0, _stringHashCode.stringHashCode)((0, _stringify.stringify)(customParams));
218
- var aggregateHash = bucketHash + ':' + jsAttributesHash;
219
- this.aggregator.store(type, aggregateHash, params, newMetrics, customParams);
215
+ // Either there is no interaction (then all these params properties will be undefined) OR there's a related soft navigation that's already completed.
216
+ // The old spa does not look up completed interactions at all, so there's no need to consider it.
217
+ this.#storeJserrorForHarvest(jsErrorEvent, params.browserInteractionId !== undefined, params._softNavAttributes);
220
218
  }
219
+ }
220
+ #storeJserrorForHarvest(errorInfoArr, softNavOccurredFinished) {
221
+ let softNavCustomAttrs = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
222
+ let [type, bucketHash, params, newMetrics, localAttrs] = errorInfoArr;
223
+ const allCustomAttrs = {};
224
+ if (softNavOccurredFinished) {
225
+ Object.entries(softNavCustomAttrs).forEach(_ref => {
226
+ let [k, v] = _ref;
227
+ return setCustom(k, v);
228
+ }); // when an ixn finishes, it'll include stuff in jsAttributes + attrs specific to the ixn
229
+ bucketHash += params.browserInteractionId;
230
+ delete params._softNavAttributes; // cleanup temp properties from synchronous evaluation; this is harmless when async from soft nav (properties DNE)
231
+ delete params._softNavFinished;
232
+ } else {
233
+ // interaction was cancelled -> error should not be associated OR there was no interaction
234
+ Object.entries((0, _config.getInfo)(this.agentIdentifier).jsAttributes).forEach(_ref2 => {
235
+ let [k, v] = _ref2;
236
+ return setCustom(k, v);
237
+ });
238
+ delete params.browserInteractionId;
239
+ }
240
+ if (localAttrs) Object.entries(localAttrs).forEach(_ref3 => {
241
+ let [k, v] = _ref3;
242
+ return setCustom(k, v);
243
+ }); // local custom attrs are applied in either case with the highest precedence
244
+
245
+ const jsAttributesHash = (0, _stringHashCode.stringHashCode)((0, _stringify.stringify)(allCustomAttrs));
246
+ const aggregateHash = bucketHash + ':' + jsAttributesHash;
247
+ this.aggregator.store(type, aggregateHash, params, newMetrics, allCustomAttrs);
221
248
  function setCustom(key, val) {
222
- customParams[key] = val && typeof val === 'object' ? (0, _stringify.stringify)(val) : val;
249
+ allCustomAttrs[key] = val && typeof val === 'object' ? (0, _stringify.stringify)(val) : val;
223
250
  }
224
251
  }
225
- onInteractionSaved(interaction) {
226
- if (!this.errorCache[interaction.id] || this.blocked) return;
227
- this.errorCache[interaction.id].forEach(item => {
228
- var customParams = {};
229
- var globalCustomParams = item[4];
230
- var localCustomParams = item[5];
231
- (0, _mapOwn.mapOwn)(globalCustomParams, setCustom);
232
- (0, _mapOwn.mapOwn)(interaction.root.attrs.custom, setCustom);
233
- (0, _mapOwn.mapOwn)(localCustomParams, setCustom);
252
+
253
+ // TO-DO: Remove this function when old spa is taken out. #storeJserrorForHarvest handles the work with the softnav feature.
254
+ onInteractionDone(interaction, wasSaved) {
255
+ if (!this.bufferedErrorsUnderSpa[interaction.id] || this.blocked) return;
256
+ this.bufferedErrorsUnderSpa[interaction.id].forEach(item => {
257
+ var allCustomAttrs = {};
258
+ const localCustomAttrs = item[4];
259
+ (0, _mapOwn.mapOwn)(interaction.root.attrs.custom, setCustom); // tack on custom attrs from the interaction
260
+ (0, _mapOwn.mapOwn)(localCustomAttrs, setCustom);
234
261
  var params = item[2];
235
- params.browserInteractionId = interaction.root.attrs.id;
236
- delete params._interactionId;
237
- if (params._interactionNodeId) {
238
- params.parentNodeId = params._interactionNodeId.toString();
239
- delete params._interactionNodeId;
240
- }
241
- var hash = item[1] + interaction.root.attrs.id;
242
- var jsAttributesHash = (0, _stringHashCode.stringHashCode)((0, _stringify.stringify)(customParams));
243
- var aggregateHash = hash + ':' + jsAttributesHash;
244
- this.aggregator.store(item[0], aggregateHash, params, item[3], customParams);
245
- function setCustom(key, val) {
246
- customParams[key] = val && typeof val === 'object' ? (0, _stringify.stringify)(val) : val;
262
+ if (wasSaved) {
263
+ params.browserInteractionId = interaction.root.attrs.id;
264
+ if (params._interactionNodeId) params.parentNodeId = params._interactionNodeId.toString();
247
265
  }
248
- });
249
- delete this.errorCache[interaction.id];
250
- }
251
- onInteractionDiscarded(interaction) {
252
- if (!this.errorCache || !this.errorCache[interaction.id] || this.blocked) return;
253
- this.errorCache[interaction.id].forEach(item => {
254
- var customParams = {};
255
- var globalCustomParams = item[4];
256
- var localCustomParams = item[5];
257
- (0, _mapOwn.mapOwn)(globalCustomParams, setCustom);
258
- (0, _mapOwn.mapOwn)(interaction.root.attrs.custom, setCustom);
259
- (0, _mapOwn.mapOwn)(localCustomParams, setCustom);
260
- var params = item[2];
261
266
  delete params._interactionId;
262
267
  delete params._interactionNodeId;
263
- var hash = item[1];
264
- var jsAttributesHash = (0, _stringHashCode.stringHashCode)((0, _stringify.stringify)(customParams));
268
+ var hash = wasSaved ? item[1] + interaction.root.attrs.id : item[1];
269
+ var jsAttributesHash = (0, _stringHashCode.stringHashCode)((0, _stringify.stringify)(allCustomAttrs));
265
270
  var aggregateHash = hash + ':' + jsAttributesHash;
266
- this.aggregator.store(item[0], aggregateHash, item[2], item[3], customParams);
271
+ this.aggregator.store(item[0], aggregateHash, params, item[3], allCustomAttrs);
267
272
  function setCustom(key, val) {
268
- customParams[key] = val && typeof val === 'object' ? (0, _stringify.stringify)(val) : val;
273
+ allCustomAttrs[key] = val && typeof val === 'object' ? (0, _stringify.stringify)(val) : val;
269
274
  }
270
275
  });
271
- delete this.errorCache[interaction.id];
276
+ delete this.bufferedErrorsUnderSpa[interaction.id];
277
+ }
278
+ onSoftNavNotification(interactionId, wasFinished, softNavAttrs) {
279
+ if (this.blocked) return;
280
+ this.bufferedErrorsUnderSpa[interactionId]?.forEach(jsErrorEvent => this.#storeJserrorForHarvest(jsErrorEvent, wasFinished, softNavAttrs) // this should not modify the re-used softNavAttrs contents
281
+ );
282
+ delete this.bufferedErrorsUnderSpa[interactionId]; // wipe the list of jserrors so they aren't duplicated by another call to the same id
272
283
  }
273
284
  }
274
285
  exports.Aggregate = Aggregate;
@@ -137,7 +137,7 @@ class Aggregate extends _aggregateBase.AggregateBase {
137
137
  this.drain();
138
138
  } catch (err) {
139
139
  this.ee.abort();
140
- (0, _console.warn)('RUM call failed. Agent shutting down.');
140
+ (0, _console.warn)('RUM call failed. Agent shutting down.', err);
141
141
  }
142
142
  }
143
143
  });
@@ -30,6 +30,7 @@ function getActivatedFeaturesFlags(agentId) {
30
30
  case _features.FEATURE_NAMES.sessionTrace:
31
31
  flagArr.push('stn');
32
32
  break;
33
+ case _features.FEATURE_NAMES.softNav:
33
34
  case _features.FEATURE_NAMES.spa:
34
35
  flagArr.push('spa');
35
36
  break;
@@ -58,103 +58,100 @@ class Aggregate extends _aggregateBase.AggregateBase {
58
58
  this.recorder = args?.recorder;
59
59
  if (this.recorder) this.recorder.parent = this;
60
60
  (0, _handle.handle)(_constants2.SUPPORTABILITY_METRIC_CHANNEL, ['Config/SessionReplay/Enabled'], undefined, _features.FEATURE_NAMES.metrics, this.ee);
61
- const shouldSetup = (0, _config.getConfigurationValue)(agentIdentifier, 'privacy.cookies_enabled') === true && (0, _config.getConfigurationValue)(agentIdentifier, 'session_trace.enabled') === true;
62
- if (shouldSetup) {
63
- // 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
- this.ee.on(_constants3.SESSION_EVENTS.RESET, () => {
65
- this.scheduler.runHarvest();
66
- this.abort(_constants.ABORT_REASONS.RESET);
67
- });
68
-
69
- // The SessionEntity class can emit a message indicating the session was paused (visibility change). This feature must stop recording if that occurs.
70
- this.ee.on(_constants3.SESSION_EVENTS.PAUSE, () => {
71
- this.recorder?.stopRecording();
72
- });
73
- // The SessionEntity class can emit a message indicating the session was resumed (visibility change). This feature must start running again (if already running) if that occurs.
74
- this.ee.on(_constants3.SESSION_EVENTS.RESUME, () => {
75
- if (!this.recorder) return;
76
- // if the mode changed on a different tab, it needs to update this instance to match
77
- const {
78
- session
79
- } = (0, _config.getRuntime)(this.agentIdentifier);
80
- this.mode = session.state.sessionReplayMode;
81
- if (!this.initialized || this.mode === _constants3.MODE.OFF) return;
82
- this.recorder?.startRecording();
83
- });
84
- this.ee.on(_constants3.SESSION_EVENTS.UPDATE, (type, data) => {
85
- if (!this.recorder || !this.initialized || this.blocked || type !== _constants3.SESSION_EVENT_TYPES.CROSS_TAB) return;
86
- if (this.mode !== _constants3.MODE.OFF && data.sessionReplayMode === _constants3.MODE.OFF) this.abort(_constants.ABORT_REASONS.CROSS_TAB);
87
- this.mode = data.sessionReplay;
88
- });
89
61
 
90
- // Bespoke logic for blobs endpoint.
91
- this.scheduler = new _harvestScheduler.HarvestScheduler('browser/blobs', {
92
- onFinished: this.onHarvestFinished.bind(this),
93
- retryDelay: this.harvestTimeSeconds,
94
- getPayload: this.prepareHarvest.bind(this),
95
- raw: true
96
- }, this);
97
- if (this.recorder?.getEvents().type === 'preloaded') {
98
- this.prepUtils().then(() => {
99
- this.scheduler.runHarvest();
100
- });
101
- }
102
- (0, _registerHandler.registerHandler)('recordReplay', () => {
103
- // if it has aborted or BCS returned bad entitlements, do not allow
104
- if (this.blocked || !this.entitled) return;
105
- // if it isnt already (fully) initialized... initialize it
106
- if (!this.recorder) this.initializeRecording(false, true, true);
107
- // its been initialized and imported the recorder but its not recording (mode === off || error)
108
- else if (this.mode !== _constants3.MODE.FULL) this.switchToFull();
109
- // if it gets all the way to here, that means a full session is already recording... do nothing
110
- }, this.featureName, this.ee);
111
- (0, _registerHandler.registerHandler)('pauseReplay', () => {
112
- this.forceStop(this.mode !== _constants3.MODE.ERROR);
113
- }, this.featureName, this.ee);
62
+ // 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.
63
+ this.ee.on(_constants3.SESSION_EVENTS.RESET, () => {
64
+ this.scheduler.runHarvest();
65
+ this.abort(_constants.ABORT_REASONS.RESET);
66
+ });
114
67
 
115
- // Wait for an error to be reported. This currently is wrapped around the "Error" feature. This is a feature-feature dependency.
116
- // This was to ensure that all errors, including those on the page before load and those handled with "noticeError" are accounted for. Needs evalulation
117
- (0, _registerHandler.registerHandler)('errorAgg', e => {
118
- this.errorNoticed = true;
119
- if (this.recorder) this.recorder.currentBufferTarget.hasError = true;
120
- // run once
121
- if (this.mode === _constants3.MODE.ERROR && _runtime.globalScope?.document.visibilityState === 'visible') {
122
- this.switchToFull();
123
- }
124
- }, this.featureName, this.ee);
68
+ // The SessionEntity class can emit a message indicating the session was paused (visibility change). This feature must stop recording if that occurs.
69
+ this.ee.on(_constants3.SESSION_EVENTS.PAUSE, () => {
70
+ this.recorder?.stopRecording();
71
+ });
72
+ // The SessionEntity class can emit a message indicating the session was resumed (visibility change). This feature must start running again (if already running) if that occurs.
73
+ this.ee.on(_constants3.SESSION_EVENTS.RESUME, () => {
74
+ if (!this.recorder) return;
75
+ // if the mode changed on a different tab, it needs to update this instance to match
125
76
  const {
126
- error_sampling_rate,
127
- sampling_rate,
128
- autoStart,
129
- block_selector,
130
- mask_text_selector,
131
- mask_all_inputs,
132
- inline_stylesheet,
133
- inline_images,
134
- collect_fonts
135
- } = (0, _config.getConfigurationValue)(this.agentIdentifier, 'session_replay');
136
- this.waitForFlags(['sr']).then(_ref => {
137
- let [flagOn] = _ref;
138
- this.entitled = flagOn;
139
- if (!this.entitled && this.recorder?.recording) {
140
- this.recorder.abort(_constants.ABORT_REASONS.ENTITLEMENTS);
141
- (0, _handle.handle)(_constants2.SUPPORTABILITY_METRIC_CHANNEL, ['SessionReplay/EnabledNotEntitled/Detected'], undefined, _features.FEATURE_NAMES.metrics, this.ee);
142
- }
143
- this.initializeRecording(Math.random() * 100 < error_sampling_rate, Math.random() * 100 < sampling_rate);
144
- }).then(() => _sharedChannel.sharedChannel.onReplayReady(this.mode)); // notify watchers that replay started with the mode
77
+ session
78
+ } = (0, _config.getRuntime)(this.agentIdentifier);
79
+ this.mode = session.state.sessionReplayMode;
80
+ if (!this.initialized || this.mode === _constants3.MODE.OFF) return;
81
+ this.recorder?.startRecording();
82
+ });
83
+ this.ee.on(_constants3.SESSION_EVENTS.UPDATE, (type, data) => {
84
+ if (!this.recorder || !this.initialized || this.blocked || type !== _constants3.SESSION_EVENT_TYPES.CROSS_TAB) return;
85
+ if (this.mode !== _constants3.MODE.OFF && data.sessionReplayMode === _constants3.MODE.OFF) this.abort(_constants.ABORT_REASONS.CROSS_TAB);
86
+ this.mode = data.sessionReplay;
87
+ });
145
88
 
146
- /** Detect if the default configs have been altered and report a SM. This is useful to evaluate what the reasonable defaults are across a customer base over time */
147
- if (!autoStart) (0, _handle.handle)(_constants2.SUPPORTABILITY_METRIC_CHANNEL, ['Config/SessionReplay/AutoStart/Modified'], undefined, _features.FEATURE_NAMES.metrics, this.ee);
148
- if (collect_fonts === true) (0, _handle.handle)(_constants2.SUPPORTABILITY_METRIC_CHANNEL, ['Config/SessionReplay/CollectFonts/Modified'], undefined, _features.FEATURE_NAMES.metrics, this.ee);
149
- if (inline_stylesheet !== true) (0, _handle.handle)(_constants2.SUPPORTABILITY_METRIC_CHANNEL, ['Config/SessionReplay/InlineStylesheet/Modified'], undefined, _features.FEATURE_NAMES.metrics, this.ee);
150
- if (inline_images === true) (0, _handle.handle)(_constants2.SUPPORTABILITY_METRIC_CHANNEL, ['Config/SessionReplay/InlineImages/Modifed'], undefined, _features.FEATURE_NAMES.metrics, this.ee);
151
- if (mask_all_inputs !== true) (0, _handle.handle)(_constants2.SUPPORTABILITY_METRIC_CHANNEL, ['Config/SessionReplay/MaskAllInputs/Modified'], undefined, _features.FEATURE_NAMES.metrics, this.ee);
152
- if (block_selector !== '[data-nr-block]') (0, _handle.handle)(_constants2.SUPPORTABILITY_METRIC_CHANNEL, ['Config/SessionReplay/BlockSelector/Modified'], undefined, _features.FEATURE_NAMES.metrics, this.ee);
153
- if (mask_text_selector !== '*') (0, _handle.handle)(_constants2.SUPPORTABILITY_METRIC_CHANNEL, ['Config/SessionReplay/MaskTextSelector/Modified'], undefined, _features.FEATURE_NAMES.metrics, this.ee);
154
- (0, _handle.handle)(_constants2.SUPPORTABILITY_METRIC_CHANNEL, ['Config/SessionReplay/SamplingRate/Value', sampling_rate], undefined, _features.FEATURE_NAMES.metrics, this.ee);
155
- (0, _handle.handle)(_constants2.SUPPORTABILITY_METRIC_CHANNEL, ['Config/SessionReplay/ErrorSamplingRate/Value', error_sampling_rate], undefined, _features.FEATURE_NAMES.metrics, this.ee);
156
- this.drain();
157
- }
89
+ // Bespoke logic for blobs endpoint.
90
+ this.scheduler = new _harvestScheduler.HarvestScheduler('browser/blobs', {
91
+ onFinished: this.onHarvestFinished.bind(this),
92
+ retryDelay: this.harvestTimeSeconds,
93
+ getPayload: this.prepareHarvest.bind(this),
94
+ raw: true
95
+ }, this);
96
+ (0, _registerHandler.registerHandler)(_constants.SR_EVENT_EMITTER_TYPES.RECORD, () => {
97
+ // if it has aborted or BCS returned bad entitlements, do not allow
98
+ if (this.blocked || !this.entitled) return;
99
+ // if it isnt already (fully) initialized... initialize it
100
+ if (!this.recorder) this.initializeRecording(false, true, true);
101
+ // its been initialized and imported the recorder but its not recording (mode === off || error)
102
+ else if (this.mode !== _constants3.MODE.FULL) this.switchToFull();
103
+ // if it gets all the way to here, that means a full session is already recording... do nothing
104
+ }, this.featureName, this.ee);
105
+ (0, _registerHandler.registerHandler)(_constants.SR_EVENT_EMITTER_TYPES.PAUSE, () => {
106
+ this.forceStop(this.mode !== _constants3.MODE.ERROR);
107
+ }, this.featureName, this.ee);
108
+
109
+ // Wait for an error to be reported. This currently is wrapped around the "Error" feature. This is a feature-feature dependency.
110
+ // This was to ensure that all errors, including those on the page before load and those handled with "noticeError" are accounted for. Needs evalulation
111
+ (0, _registerHandler.registerHandler)('errorAgg', e => {
112
+ this.errorNoticed = true;
113
+ if (this.recorder) this.recorder.currentBufferTarget.hasError = true;
114
+ // run once
115
+ if (this.mode === _constants3.MODE.ERROR && _runtime.globalScope?.document.visibilityState === 'visible') {
116
+ this.switchToFull();
117
+ }
118
+ }, this.featureName, this.ee);
119
+ const {
120
+ error_sampling_rate,
121
+ sampling_rate,
122
+ autoStart,
123
+ block_selector,
124
+ mask_text_selector,
125
+ mask_all_inputs,
126
+ inline_stylesheet,
127
+ inline_images,
128
+ collect_fonts
129
+ } = (0, _config.getConfigurationValue)(this.agentIdentifier, 'session_replay');
130
+ this.waitForFlags(['sr']).then(_ref => {
131
+ let [flagOn] = _ref;
132
+ this.entitled = flagOn;
133
+ if (!this.entitled && this.recorder?.recording) {
134
+ this.abort(_constants.ABORT_REASONS.ENTITLEMENTS);
135
+ (0, _handle.handle)(_constants2.SUPPORTABILITY_METRIC_CHANNEL, ['SessionReplay/EnabledNotEntitled/Detected'], undefined, _features.FEATURE_NAMES.metrics, this.ee);
136
+ return;
137
+ }
138
+ this.initializeRecording(Math.random() * 100 < error_sampling_rate, Math.random() * 100 < sampling_rate);
139
+ }).then(() => {
140
+ if (this.mode === _constants3.MODE.OFF) args?.recorder?.stopRecording(); // stop any conservative preload recording launched by instrument
141
+ _sharedChannel.sharedChannel.onReplayReady(this.mode); // notify watchers that replay started with the mode
142
+ });
143
+
144
+ /** Detect if the default configs have been altered and report a SM. This is useful to evaluate what the reasonable defaults are across a customer base over time */
145
+ if (!autoStart) (0, _handle.handle)(_constants2.SUPPORTABILITY_METRIC_CHANNEL, ['Config/SessionReplay/AutoStart/Modified'], undefined, _features.FEATURE_NAMES.metrics, this.ee);
146
+ if (collect_fonts === true) (0, _handle.handle)(_constants2.SUPPORTABILITY_METRIC_CHANNEL, ['Config/SessionReplay/CollectFonts/Modified'], undefined, _features.FEATURE_NAMES.metrics, this.ee);
147
+ if (inline_stylesheet !== true) (0, _handle.handle)(_constants2.SUPPORTABILITY_METRIC_CHANNEL, ['Config/SessionReplay/InlineStylesheet/Modified'], undefined, _features.FEATURE_NAMES.metrics, this.ee);
148
+ if (inline_images === true) (0, _handle.handle)(_constants2.SUPPORTABILITY_METRIC_CHANNEL, ['Config/SessionReplay/InlineImages/Modifed'], undefined, _features.FEATURE_NAMES.metrics, this.ee);
149
+ if (mask_all_inputs !== true) (0, _handle.handle)(_constants2.SUPPORTABILITY_METRIC_CHANNEL, ['Config/SessionReplay/MaskAllInputs/Modified'], undefined, _features.FEATURE_NAMES.metrics, this.ee);
150
+ if (block_selector !== '[data-nr-block]') (0, _handle.handle)(_constants2.SUPPORTABILITY_METRIC_CHANNEL, ['Config/SessionReplay/BlockSelector/Modified'], undefined, _features.FEATURE_NAMES.metrics, this.ee);
151
+ if (mask_text_selector !== '*') (0, _handle.handle)(_constants2.SUPPORTABILITY_METRIC_CHANNEL, ['Config/SessionReplay/MaskTextSelector/Modified'], undefined, _features.FEATURE_NAMES.metrics, this.ee);
152
+ (0, _handle.handle)(_constants2.SUPPORTABILITY_METRIC_CHANNEL, ['Config/SessionReplay/SamplingRate/Value', sampling_rate], undefined, _features.FEATURE_NAMES.metrics, this.ee);
153
+ (0, _handle.handle)(_constants2.SUPPORTABILITY_METRIC_CHANNEL, ['Config/SessionReplay/ErrorSamplingRate/Value', error_sampling_rate], undefined, _features.FEATURE_NAMES.metrics, this.ee);
154
+ this.drain();
158
155
  }
159
156
  switchToFull() {
160
157
  this.mode = _constants3.MODE.FULL;
@@ -202,6 +199,11 @@ class Aggregate extends _aggregateBase.AggregateBase {
202
199
  return;
203
200
  }
204
201
  }
202
+ if (this.recorder?.getEvents().type === 'preloaded') {
203
+ this.prepUtils().then(() => {
204
+ this.scheduler.runHarvest();
205
+ });
206
+ }
205
207
  if (!this.recorder) {
206
208
  try {
207
209
  // Do not change the webpackChunkName or it will break the webpack nrba-chunking plugin
@@ -394,7 +396,7 @@ class Aggregate extends _aggregateBase.AggregateBase {
394
396
  });
395
397
  this.recorder?.clearTimestamps?.();
396
398
  this.ee.emit('REPLAY_ABORTED');
397
- this.recorder?.clearBuffer?.();
399
+ while (this.recorder?.getEvents().events.length) this.recorder?.clearBuffer?.();
398
400
  }
399
401
  syncWithSessionManager() {
400
402
  let state = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
@@ -3,10 +3,14 @@
3
3
  Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
- exports.RRWEB_EVENT_TYPES = exports.QUERY_PARAM_PADDING = exports.MAX_PAYLOAD_SIZE = exports.IDEAL_PAYLOAD_SIZE = exports.FEATURE_NAME = exports.CHECKOUT_MS = exports.AVG_COMPRESSION = exports.ABORT_REASONS = void 0;
6
+ exports.SR_EVENT_EMITTER_TYPES = exports.RRWEB_EVENT_TYPES = exports.QUERY_PARAM_PADDING = exports.MAX_PAYLOAD_SIZE = exports.IDEAL_PAYLOAD_SIZE = exports.FEATURE_NAME = exports.CHECKOUT_MS = exports.AVG_COMPRESSION = exports.ABORT_REASONS = void 0;
7
7
  var _constants = require("../../common/session/constants");
8
8
  var _features = require("../../loaders/features/features");
9
9
  const FEATURE_NAME = exports.FEATURE_NAME = _features.FEATURE_NAMES.sessionReplay;
10
+ const SR_EVENT_EMITTER_TYPES = exports.SR_EVENT_EMITTER_TYPES = {
11
+ RECORD: 'recordReplay',
12
+ PAUSE: 'pauseReplay'
13
+ };
10
14
  const AVG_COMPRESSION = exports.AVG_COMPRESSION = 0.12;
11
15
  const RRWEB_EVENT_TYPES = exports.RRWEB_EVENT_TYPES = {
12
16
  DomContentLoaded: 0,
@@ -7,6 +7,7 @@ exports.Instrument = void 0;
7
7
  var _constants = require("../../../common/session/constants");
8
8
  var _instrumentBase = require("../../utils/instrument-base");
9
9
  var _constants2 = require("../constants");
10
+ var _utils = require("../shared/utils");
10
11
  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); }
11
12
  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; } /*
12
13
  * Copyright 2023 New Relic Corporation. All rights reserved.
@@ -23,15 +24,29 @@ class Instrument extends _instrumentBase.InstrumentBase {
23
24
  constructor(agentIdentifier, aggregator) {
24
25
  let auto = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : true;
25
26
  super(agentIdentifier, aggregator, _constants2.FEATURE_NAME, auto);
27
+ let session;
26
28
  try {
27
- const session = JSON.parse(localStorage.getItem('NRBA_SESSION'));
28
- if (session.sessionReplayMode !== _constants.MODE.OFF) {
29
- this.#startRecording(session.sessionReplayMode);
30
- } else {
31
- this.importAggregator({});
32
- }
33
- } catch (err) {
34
- this.importAggregator({});
29
+ session = JSON.parse(localStorage.getItem("".concat(_constants.PREFIX, "_").concat(_constants.DEFAULT_KEY)));
30
+ } catch (err) {}
31
+ if (this.#canPreloadRecorder(session)) {
32
+ this.#startRecording(session?.sessionReplayMode);
33
+ } else {
34
+ this.importAggregator();
35
+ }
36
+ }
37
+
38
+ // At this point wherein session state exists already but we haven't init SessionEntity aka verify timers.
39
+ #canPreloadRecorder(session) {
40
+ if (!session) {
41
+ // this might be a new session if entity initializes: conservatively start recording if first-time config allows
42
+ // Note: users with SR enabled, as well as these other configs enabled by-default, will be penalized by the recorder overhead EVEN IF they don't actually have or get
43
+ // entitlement or sampling decision, or otherwise intentionally opted-in for the feature.
44
+ return (0, _utils.isPreloadAllowed)(this.agentIdentifier);
45
+ } else if (session.sessionReplayMode === _constants.MODE.FULL || session.sessionReplayMode === _constants.MODE.ERROR) {
46
+ return true; // existing sessions get to continue recording, regardless of this page's configs or if it has expired (conservatively)
47
+ } else {
48
+ // SR mode was OFF but may potentially be turned on if session resets and configs allows the new session to have replay...
49
+ return (0, _utils.isPreloadAllowed)(this.agentIdentifier);
35
50
  }
36
51
  }
37
52
  async #startRecording(mode) {
@@ -43,6 +58,7 @@ class Instrument extends _instrumentBase.InstrumentBase {
43
58
  agentIdentifier: this.agentIdentifier
44
59
  });
45
60
  this.recorder.startRecording();
61
+ this.abortHandler = this.recorder.stopRecording;
46
62
  this.importAggregator({
47
63
  recorder: this.recorder
48
64
  });
@@ -115,14 +115,15 @@ class Recorder {
115
115
  const incompletes = _stylesheetEvaluator.stylesheetEvaluator.evaluate();
116
116
  /** Only stop ignoring data if already ignoring and a new valid snapshap is taking place (0 incompletes and we get a meta node for the snap) */
117
117
  if (!incompletes && this.#fixing && event.type === _constants.RRWEB_EVENT_TYPES.Meta) this.#fixing = false;
118
- if (incompletes) {
118
+ if (incompletes > 0) {
119
119
  /** wait for the evaluator to download/replace the incompletes' src code and then take a new snap */
120
120
  _stylesheetEvaluator.stylesheetEvaluator.fix().then(failedToFix => {
121
- if (failedToFix) {
121
+ if (failedToFix > 0) {
122
122
  this.currentBufferTarget.inlinedAllStylesheets = false;
123
123
  this.shouldFix = false;
124
- (0, _handle.handle)(_constants3.SUPPORTABILITY_METRIC_CHANNEL, ['SessionReplay/Payload/Missing-Inline-Css/Failed', failedToFix], undefined, _features.FEATURE_NAMES.metrics, this.parent.ee);
125
- } else (0, _handle.handle)(_constants3.SUPPORTABILITY_METRIC_CHANNEL, ['SessionReplay/Payload/Missing-Inline-Css/Fixed', incompletes - failedToFix], undefined, _features.FEATURE_NAMES.metrics, this.parent.ee);
124
+ }
125
+ (0, _handle.handle)(_constants3.SUPPORTABILITY_METRIC_CHANNEL, ['SessionReplay/Payload/Missing-Inline-Css/Failed', failedToFix], undefined, _features.FEATURE_NAMES.metrics, this.parent.ee);
126
+ (0, _handle.handle)(_constants3.SUPPORTABILITY_METRIC_CHANNEL, ['SessionReplay/Payload/Missing-Inline-Css/Fixed', incompletes - failedToFix], undefined, _features.FEATURE_NAMES.metrics, this.parent.ee);
126
127
  this.takeFullSnapshot();
127
128
  });
128
129
  /** Only start ignoring data if got a faulty snapshot */
@@ -24,15 +24,15 @@ class StylesheetEvaluator {
24
24
  let incompletes = 0;
25
25
  if (_runtime.isBrowserScope) {
26
26
  for (let i = 0; i < Object.keys(document.styleSheets).length; i++) {
27
- const ss = document.styleSheets[i];
28
- if (!this.#evaluated.has(ss)) {
29
- this.#evaluated.add(ss);
27
+ if (!this.#evaluated.has(document.styleSheets[i])) {
28
+ this.#evaluated.add(document.styleSheets[i]);
30
29
  try {
31
30
  // eslint-disable-next-line
32
- const temp = ss.cssRules;
31
+ const temp = document.styleSheets[i].cssRules;
33
32
  } catch (err) {
33
+ if (!document.styleSheets[i].href) return;
34
34
  incompletes++;
35
- this.#fetchProms.push(this.#fetchAndOverride(document.styleSheets[i], ss.href));
35
+ this.#fetchProms.push(this.#fetchAndOverride(document.styleSheets[i]));
36
36
  }
37
37
  }
38
38
  }
@@ -59,9 +59,10 @@ class StylesheetEvaluator {
59
59
  * @param {*} href - The asset href to fetch
60
60
  * @returns {Promise}
61
61
  */
62
- async #fetchAndOverride(target, href) {
62
+ async #fetchAndOverride(target) {
63
+ if (!target?.href) return;
63
64
  try {
64
- const stylesheetContents = await _config.originals.FETCH.bind(window)(href);
65
+ const stylesheetContents = await _config.originals.FETCH.bind(window)(target.href);
65
66
  if (!stylesheetContents.ok) {
66
67
  this.failedToFix++;
67
68
  return;
@@ -0,0 +1,26 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.canImportReplayAgg = canImportReplayAgg;
7
+ exports.enableSessionTracking = void 0;
8
+ exports.isPreloadAllowed = isPreloadAllowed;
9
+ var _config = require("../../../common/config/config");
10
+ var _runtime = require("../../../common/constants/runtime");
11
+ const enableSessionTracking = agentId => _runtime.isBrowserScope && (0, _config.getConfigurationValue)(agentId, 'privacy.cookies_enabled') === true;
12
+ exports.enableSessionTracking = enableSessionTracking;
13
+ function hasReplayPrerequisite(agentId) {
14
+ return _config.originals.MO &&
15
+ // Session Replay cannot work without Mutation Observer
16
+ enableSessionTracking &&
17
+ // requires session tracking to be running (hence "session" replay...)
18
+ (0, _config.getConfigurationValue)(agentId, 'session_trace.enabled') === true; // Session Replay as of now is tightly coupled with Session Trace in the UI
19
+ }
20
+ function isPreloadAllowed(agentId) {
21
+ return (0, _config.getConfigurationValue)(agentId, 'session_replay.preload') === true && hasReplayPrerequisite(agentId);
22
+ }
23
+ function canImportReplayAgg(agentId, sessionMgr) {
24
+ if (!hasReplayPrerequisite(agentId)) return false;
25
+ 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
26
+ }