@newrelic/browser-agent 1.286.0 → 1.288.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 (162) hide show
  1. package/CHANGELOG.md +21 -0
  2. package/dist/cjs/common/config/init-types.js +96 -0
  3. package/dist/cjs/common/config/init.js +9 -79
  4. package/dist/cjs/common/config/runtime.js +7 -6
  5. package/dist/cjs/common/constants/env.cdn.js +1 -1
  6. package/dist/cjs/common/constants/env.npm.js +1 -1
  7. package/dist/cjs/common/harvest/harvester.js +2 -2
  8. package/dist/cjs/common/session/session-entity.js +10 -3
  9. package/dist/cjs/common/util/feature-flags.js +4 -5
  10. package/dist/cjs/common/util/target.js +34 -0
  11. package/dist/cjs/features/ajax/aggregate/index.js +2 -1
  12. package/dist/cjs/features/generic_events/aggregate/index.js +10 -6
  13. package/dist/cjs/features/jserrors/aggregate/index.js +44 -22
  14. package/dist/cjs/features/logging/aggregate/index.js +21 -13
  15. package/dist/cjs/features/logging/shared/utils.js +3 -2
  16. package/dist/cjs/features/metrics/aggregate/index.js +6 -4
  17. package/dist/cjs/features/page_view_event/aggregate/index.js +60 -11
  18. package/dist/cjs/features/page_view_event/instrument/index.js +4 -0
  19. package/dist/cjs/features/session_replay/aggregate/index.js +9 -7
  20. package/dist/cjs/features/session_replay/instrument/index.js +1 -1
  21. package/dist/cjs/features/session_replay/shared/recorder-events.js +4 -2
  22. package/dist/cjs/features/session_replay/shared/recorder.js +17 -11
  23. package/dist/cjs/features/session_trace/aggregate/trace/storage.js +8 -1
  24. package/dist/cjs/features/soft_navigations/aggregate/index.js +13 -2
  25. package/dist/cjs/features/spa/aggregate/index.js +4 -3
  26. package/dist/cjs/features/spa/aggregate/interaction.js +6 -9
  27. package/dist/cjs/features/utils/aggregate-base.js +49 -20
  28. package/dist/cjs/features/utils/entity-manager.js +47 -0
  29. package/dist/cjs/features/utils/event-store-manager.js +79 -54
  30. package/dist/cjs/interfaces/registered-entity.js +114 -0
  31. package/dist/cjs/loaders/api/api-methods.js +1 -1
  32. package/dist/cjs/loaders/api/api.js +50 -18
  33. package/dist/cjs/loaders/api/register-api-types.js +35 -0
  34. package/dist/cjs/loaders/api/register-api.js +165 -0
  35. package/dist/cjs/loaders/micro-agent-base.js +16 -0
  36. package/dist/cjs/loaders/micro-agent.js +1 -0
  37. package/dist/esm/common/config/init-types.js +92 -0
  38. package/dist/esm/common/config/init.js +9 -79
  39. package/dist/esm/common/config/runtime.js +7 -6
  40. package/dist/esm/common/constants/env.cdn.js +1 -1
  41. package/dist/esm/common/constants/env.npm.js +1 -1
  42. package/dist/esm/common/harvest/harvester.js +2 -2
  43. package/dist/esm/common/session/session-entity.js +10 -3
  44. package/dist/esm/common/util/feature-flags.js +4 -5
  45. package/dist/esm/common/util/target.js +27 -0
  46. package/dist/esm/features/ajax/aggregate/index.js +2 -1
  47. package/dist/esm/features/generic_events/aggregate/index.js +10 -6
  48. package/dist/esm/features/jserrors/aggregate/index.js +44 -22
  49. package/dist/esm/features/logging/aggregate/index.js +21 -13
  50. package/dist/esm/features/logging/shared/utils.js +3 -2
  51. package/dist/esm/features/metrics/aggregate/index.js +6 -4
  52. package/dist/esm/features/page_view_event/aggregate/index.js +60 -11
  53. package/dist/esm/features/page_view_event/instrument/index.js +4 -0
  54. package/dist/esm/features/session_replay/aggregate/index.js +9 -7
  55. package/dist/esm/features/session_replay/instrument/index.js +1 -1
  56. package/dist/esm/features/session_replay/shared/recorder-events.js +4 -2
  57. package/dist/esm/features/session_replay/shared/recorder.js +17 -11
  58. package/dist/esm/features/session_trace/aggregate/trace/storage.js +8 -1
  59. package/dist/esm/features/soft_navigations/aggregate/index.js +13 -2
  60. package/dist/esm/features/spa/aggregate/index.js +4 -3
  61. package/dist/esm/features/spa/aggregate/interaction.js +6 -9
  62. package/dist/esm/features/utils/aggregate-base.js +49 -20
  63. package/dist/esm/features/utils/entity-manager.js +40 -0
  64. package/dist/esm/features/utils/event-store-manager.js +79 -54
  65. package/dist/esm/interfaces/registered-entity.js +107 -0
  66. package/dist/esm/loaders/api/api-methods.js +1 -1
  67. package/dist/esm/loaders/api/api.js +50 -18
  68. package/dist/esm/loaders/api/register-api-types.js +33 -0
  69. package/dist/esm/loaders/api/register-api.js +159 -0
  70. package/dist/esm/loaders/micro-agent-base.js +16 -0
  71. package/dist/esm/loaders/micro-agent.js +1 -0
  72. package/dist/tsconfig.tsbuildinfo +1 -1
  73. package/dist/types/common/config/init-types.d.ts +275 -0
  74. package/dist/types/common/config/init-types.d.ts.map +1 -0
  75. package/dist/types/common/config/init.d.ts +1 -262
  76. package/dist/types/common/config/init.d.ts.map +1 -1
  77. package/dist/types/common/config/runtime.d.ts.map +1 -1
  78. package/dist/types/common/session/session-entity.d.ts +3 -1
  79. package/dist/types/common/session/session-entity.d.ts.map +1 -1
  80. package/dist/types/common/util/feature-flags.d.ts +1 -1
  81. package/dist/types/common/util/feature-flags.d.ts.map +1 -1
  82. package/dist/types/common/util/target.d.ts +18 -0
  83. package/dist/types/common/util/target.d.ts.map +1 -0
  84. package/dist/types/features/ajax/aggregate/index.d.ts +1 -1
  85. package/dist/types/features/ajax/aggregate/index.d.ts.map +1 -1
  86. package/dist/types/features/generic_events/aggregate/index.d.ts +2 -1
  87. package/dist/types/features/generic_events/aggregate/index.d.ts.map +1 -1
  88. package/dist/types/features/jserrors/aggregate/index.d.ts +9 -1
  89. package/dist/types/features/jserrors/aggregate/index.d.ts.map +1 -1
  90. package/dist/types/features/logging/aggregate/index.d.ts +3 -3
  91. package/dist/types/features/logging/aggregate/index.d.ts.map +1 -1
  92. package/dist/types/features/logging/shared/utils.d.ts +2 -1
  93. package/dist/types/features/logging/shared/utils.d.ts.map +1 -1
  94. package/dist/types/features/metrics/aggregate/index.d.ts +1 -0
  95. package/dist/types/features/metrics/aggregate/index.d.ts.map +1 -1
  96. package/dist/types/features/page_view_event/aggregate/index.d.ts +10 -2
  97. package/dist/types/features/page_view_event/aggregate/index.d.ts.map +1 -1
  98. package/dist/types/features/page_view_event/instrument/index.d.ts.map +1 -1
  99. package/dist/types/features/session_replay/aggregate/index.d.ts +3 -12
  100. package/dist/types/features/session_replay/aggregate/index.d.ts.map +1 -1
  101. package/dist/types/features/session_replay/shared/recorder-events.d.ts +1 -0
  102. package/dist/types/features/session_replay/shared/recorder-events.d.ts.map +1 -1
  103. package/dist/types/features/session_replay/shared/recorder.d.ts +5 -5
  104. package/dist/types/features/session_replay/shared/recorder.d.ts.map +1 -1
  105. package/dist/types/features/session_trace/aggregate/trace/storage.d.ts +1 -0
  106. package/dist/types/features/session_trace/aggregate/trace/storage.d.ts.map +1 -1
  107. package/dist/types/features/soft_navigations/aggregate/index.d.ts.map +1 -1
  108. package/dist/types/features/spa/aggregate/index.d.ts.map +1 -1
  109. package/dist/types/features/spa/aggregate/interaction.d.ts +3 -4
  110. package/dist/types/features/spa/aggregate/interaction.d.ts.map +1 -1
  111. package/dist/types/features/utils/aggregate-base.d.ts +22 -5
  112. package/dist/types/features/utils/aggregate-base.d.ts.map +1 -1
  113. package/dist/types/features/utils/entity-manager.d.ts +15 -0
  114. package/dist/types/features/utils/entity-manager.d.ts.map +1 -0
  115. package/dist/types/features/utils/event-store-manager.d.ts +48 -24
  116. package/dist/types/features/utils/event-store-manager.d.ts.map +1 -1
  117. package/dist/types/interfaces/registered-entity.d.ts +72 -0
  118. package/dist/types/interfaces/registered-entity.d.ts.map +1 -0
  119. package/dist/types/loaders/agent.d.ts +1 -1
  120. package/dist/types/loaders/api/api.d.ts.map +1 -1
  121. package/dist/types/loaders/api/register-api-types.d.ts +56 -0
  122. package/dist/types/loaders/api/register-api-types.d.ts.map +1 -0
  123. package/dist/types/loaders/api/register-api.d.ts +14 -0
  124. package/dist/types/loaders/api/register-api.d.ts.map +1 -0
  125. package/dist/types/loaders/micro-agent-base.d.ts +17 -0
  126. package/dist/types/loaders/micro-agent-base.d.ts.map +1 -1
  127. package/dist/types/loaders/micro-agent.d.ts +1 -0
  128. package/dist/types/loaders/micro-agent.d.ts.map +1 -1
  129. package/package.json +10 -1
  130. package/src/common/config/init-types.js +92 -0
  131. package/src/common/config/init.js +9 -79
  132. package/src/common/config/runtime.js +7 -6
  133. package/src/common/harvest/harvester.js +2 -2
  134. package/src/common/session/session-entity.js +10 -3
  135. package/src/common/util/feature-flags.js +4 -5
  136. package/src/common/util/target.js +27 -0
  137. package/src/features/ajax/aggregate/index.js +2 -1
  138. package/src/features/generic_events/aggregate/index.js +8 -6
  139. package/src/features/jserrors/aggregate/index.js +42 -20
  140. package/src/features/logging/aggregate/index.js +18 -12
  141. package/src/features/logging/shared/utils.js +3 -2
  142. package/src/features/metrics/aggregate/index.js +7 -5
  143. package/src/features/page_view_event/aggregate/index.js +50 -8
  144. package/src/features/page_view_event/instrument/index.js +4 -0
  145. package/src/features/session_replay/aggregate/index.js +7 -4
  146. package/src/features/session_replay/instrument/index.js +1 -1
  147. package/src/features/session_replay/shared/recorder-events.js +5 -2
  148. package/src/features/session_replay/shared/recorder.js +17 -11
  149. package/src/features/session_trace/aggregate/trace/storage.js +9 -1
  150. package/src/features/soft_navigations/aggregate/index.js +12 -2
  151. package/src/features/spa/aggregate/index.js +4 -3
  152. package/src/features/spa/aggregate/interaction.js +6 -9
  153. package/src/features/utils/aggregate-base.js +54 -22
  154. package/src/features/utils/entity-manager.js +45 -0
  155. package/src/features/utils/event-store-manager.js +72 -49
  156. package/src/interfaces/registered-entity.js +107 -0
  157. package/src/loaders/api/api-methods.js +1 -1
  158. package/src/loaders/api/api.js +49 -13
  159. package/src/loaders/api/register-api-types.js +33 -0
  160. package/src/loaders/api/register-api.js +152 -0
  161. package/src/loaders/micro-agent-base.js +16 -0
  162. package/src/loaders/micro-agent.js +1 -0
@@ -13,6 +13,7 @@ var _log = require("../shared/log");
13
13
  var _utils = require("../shared/utils");
14
14
  var _traverse = require("../../../common/util/traverse");
15
15
  var _agentConstants = require("../../../common/constants/agent-constants");
16
+ var _target = require("../../../common/util/target");
16
17
  var _constants2 = require("../../../common/session/constants");
17
18
  var _constants3 = require("../../session_replay/constants");
18
19
  var _featureGates = require("../../utils/feature-gates");
@@ -62,7 +63,8 @@ class Aggregate extends _aggregateBase.AggregateBase {
62
63
  loggingMode: this.loggingMode
63
64
  });
64
65
  }
65
- handleLog(timestamp, message, attributes = {}, level = _constants.LOG_LEVELS.INFO) {
66
+ handleLog(timestamp, message, attributes = {}, level = _constants.LOG_LEVELS.INFO, targetEntityGuid) {
67
+ if (!this.agentRef.runtime.entityManager.get(targetEntityGuid)) return (0, _console.warn)(56, this.featureName);
66
68
  if (this.blocked || !this.loggingMode) return;
67
69
  if (!attributes || typeof attributes !== 'object') attributes = {};
68
70
  if (typeof level === 'string') level = level.toUpperCase();
@@ -97,11 +99,13 @@ class Aggregate extends _aggregateBase.AggregateBase {
97
99
  (0, _console.warn)(31, log.message.slice(0, 25) + '...');
98
100
  return;
99
101
  }
100
- if (this.events.wouldExceedMaxSize(logBytes)) {
102
+ if (this.events.wouldExceedMaxSize(logBytes, targetEntityGuid)) {
101
103
  this.reportSupportabilityMetric('Logging/Harvest/Early/Seen', this.events.byteSize() + logBytes);
102
- this.agentRef.runtime.harvester.triggerHarvestFor(this); // force a harvest synchronously to try adding again
104
+ this.agentRef.runtime.harvester.triggerHarvestFor(this, {
105
+ targetEntityGuid
106
+ }); // force a harvest synchronously to try adding again
103
107
  }
104
- if (!this.events.add(log)) {
108
+ if (!this.events.add(log, targetEntityGuid)) {
105
109
  // still failed after a harvest attempt despite not being too large would mean harvest failed with options.retry
106
110
  this.reportSupportabilityMetric(failToHarvestMessage, logBytes);
107
111
  (0, _console.warn)(31, log.message.slice(0, 25) + '...');
@@ -109,24 +113,25 @@ class Aggregate extends _aggregateBase.AggregateBase {
109
113
  this.reportSupportabilityMetric('Logging/Event/Added/Seen');
110
114
  }
111
115
  }
112
- serializer(eventBuffer) {
116
+ serializer(eventBuffer, targetEntityGuid) {
117
+ const target = this.agentRef.runtime.entityManager.get(targetEntityGuid);
113
118
  const sessionEntity = this.agentRef.runtime.session;
114
119
  return [{
115
120
  common: {
116
121
  /** Attributes in the `common` section are added to `all` logs generated in the payload */
117
122
  attributes: {
118
- 'entity.guid': this.agentRef.runtime.appMetadata?.agents?.[0]?.entityGuid,
119
- // browser entity guid as provided from RUM response
123
+ 'entity.guid': target.entityGuid,
124
+ // browser entity guid as provided API target OR the default from RUM response if not supplied
120
125
  ...(sessionEntity && {
121
126
  session: sessionEntity.state.value || '0',
122
127
  // The session ID that we generate and keep across page loads
123
- hasReplay: sessionEntity.state.sessionReplayMode === 1,
128
+ hasReplay: sessionEntity.state.sessionReplayMode === 1 && (0, _target.isContainerAgentTarget)(target, this.agentRef),
124
129
  // True if a session replay recording is running
125
130
  hasTrace: sessionEntity.state.sessionTraceMode === 1 // True if a session trace recording is running
126
131
  }),
127
132
  ptid: this.agentRef.runtime.ptid,
128
133
  // page trace id
129
- appId: this.agentRef.info.applicationID,
134
+ appId: target.applicationID || this.agentRef.info.applicationID,
130
135
  // Application ID from info object,
131
136
  standalone: Boolean(this.agentRef.info.sa),
132
137
  // copy paste (true) vs APM (false)
@@ -144,9 +149,10 @@ class Aggregate extends _aggregateBase.AggregateBase {
144
149
  logs: (0, _traverse.applyFnToProps)(eventBuffer, this.obfuscator.obfuscateString.bind(this.obfuscator), 'string')
145
150
  }];
146
151
  }
147
- queryStringsBuilder() {
152
+ queryStringsBuilder(_, targetEntityGuid) {
153
+ const target = this.agentRef.runtime.entityManager.get(targetEntityGuid);
148
154
  return {
149
- browser_monitoring_key: this.agentRef.info.licenseKey
155
+ browser_monitoring_key: target.licenseKey
150
156
  };
151
157
  }
152
158
 
@@ -154,8 +160,10 @@ class Aggregate extends _aggregateBase.AggregateBase {
154
160
  abort(reason = {}) {
155
161
  this.reportSupportabilityMetric("Logging/Abort/".concat(reason.sm));
156
162
  this.blocked = true;
157
- this.events.clear();
158
- this.events.clearSave();
163
+ if (this.events) {
164
+ this.events.clear();
165
+ this.events.clearSave();
166
+ }
159
167
  this.updateLoggingMode(_constants.LOGGING_MODE.OFF);
160
168
  this.deregisterDrain();
161
169
  }
@@ -20,10 +20,11 @@ var _constants2 = require("../constants");
20
20
  * @param {string} message - the log message string
21
21
  * @param {{[key: string]: *}} customAttributes - The log's custom attributes if any
22
22
  * @param {enum} level - the log level enum
23
+ * @param {object=} targetEntityGuid - the optional target entity guid provided by an api call
23
24
  */
24
- function bufferLog(ee, message, customAttributes = {}, level = _constants2.LOG_LEVELS.INFO) {
25
+ function bufferLog(ee, message, customAttributes = {}, level = _constants2.LOG_LEVELS.INFO, targetEntityGuid, timestamp = (0, _now.now)()) {
25
26
  (0, _handle.handle)(_constants.SUPPORTABILITY_METRIC_CHANNEL, ["API/logging/".concat(level.toLowerCase(), "/called")], undefined, _features.FEATURE_NAMES.metrics, ee);
26
- (0, _handle.handle)(_constants2.LOGGING_EVENT_EMITTER_CHANNEL, [(0, _now.now)(), message, customAttributes, level], undefined, _features.FEATURE_NAMES.logging, ee);
27
+ (0, _handle.handle)(_constants2.LOGGING_EVENT_EMITTER_CHANNEL, [timestamp, message, customAttributes, level, targetEntityGuid], undefined, _features.FEATURE_NAMES.logging, ee);
27
28
  }
28
29
 
29
30
  /**
@@ -28,8 +28,13 @@ class Aggregate extends _aggregateBase.AggregateBase {
28
28
  this.harvestOpts.aggregatorTypes = ['cm', 'sm']; // the types in EventAggregator this feature cares about
29
29
  // This feature only harvests once per potential EoL of the page, which is handled by the central harvester.
30
30
 
31
+ // this must be read/stored synchronously, as the currentScript is removed from the DOM after this script is executed and this lookup will be void
32
+ // its used to report a SM later in the lifecycle
33
+ this.agentNonce = _runtime.isBrowserScope && document.currentScript?.nonce;
31
34
  this.waitForFlags(['err']).then(([errFlag]) => {
32
35
  if (errFlag) {
36
+ this.singleChecks(); // checks that are run only one time, at script load
37
+ this.eachSessionChecks(); // the start of every time user engages with page
33
38
  this.drain();
34
39
  } else {
35
40
  this.blocked = true; // if rum response determines that customer lacks entitlements for spa endpoint, this feature shouldn't harvest
@@ -40,8 +45,6 @@ class Aggregate extends _aggregateBase.AggregateBase {
40
45
  // Allow features external to the metrics feature to capture SMs and CMs through the event emitter
41
46
  (0, _registerHandler.registerHandler)(_constants.SUPPORTABILITY_METRIC_CHANNEL, this.storeSupportabilityMetrics.bind(this), this.featureName, this.ee);
42
47
  (0, _registerHandler.registerHandler)(_constants.CUSTOM_METRIC_CHANNEL, this.storeEventMetrics.bind(this), this.featureName, this.ee);
43
- this.singleChecks(); // checks that are run only one time, at script load
44
- this.eachSessionChecks(); // the start of every time user engages with page
45
48
  }
46
49
  preHarvestChecks(opts) {
47
50
  return this.drained && opts.isFinalHarvest;
@@ -77,8 +80,7 @@ class Aggregate extends _aggregateBase.AggregateBase {
77
80
  if (distMethod) this.storeSupportabilityMetrics("Generic/DistMethod/".concat(distMethod, "/Detected"));
78
81
  if (_runtime.isBrowserScope) {
79
82
  this.storeSupportabilityMetrics('Generic/Runtime/Browser/Detected');
80
- const nonce = document?.currentScript?.nonce;
81
- if (nonce && nonce !== '') {
83
+ if (this.agentNonce && this.agentNonce !== '') {
82
84
  this.storeSupportabilityMetrics('Generic/Runtime/Nonce/Detected');
83
85
  }
84
86
 
@@ -19,6 +19,8 @@ var _timeToFirstByte = require("../../../common/vitals/time-to-first-byte");
19
19
  var _now = require("../../../common/timing/now");
20
20
  var _timeKeeper = require("../../../common/timing/time-keeper");
21
21
  var _traverse = require("../../../common/util/traverse");
22
+ var _registerHandler = require("../../../common/event-emitter/register-handler");
23
+ var _target = require("../../../common/util/target");
22
24
  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); }
23
25
  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 && {}.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; }
24
26
  /**
@@ -34,6 +36,9 @@ class Aggregate extends _aggregateBase.AggregateBase {
34
36
  this.firstByteToWindowLoad = 0; // our "frontend" duration
35
37
  this.firstByteToDomContent = 0; // our "dom processing" duration
36
38
 
39
+ (0, _registerHandler.registerHandler)('send-rum', (customAttibutes, target) => {
40
+ this.sendRum(customAttibutes, target);
41
+ }, this.featureName, this.ee);
37
42
  if (!(0, _info.isValid)(agentRef.agentIdentifier)) {
38
43
  this.ee.abort();
39
44
  return (0, _console.warn)(43);
@@ -56,7 +61,17 @@ class Aggregate extends _aggregateBase.AggregateBase {
56
61
  this.sendRum();
57
62
  }
58
63
  }
59
- sendRum() {
64
+
65
+ /**
66
+ *
67
+ * @param {Function} cb A function to run once the RUM call has finished - Defaults to activateFeatures
68
+ * @param {*} customAttributes custom attributes to attach to the RUM call - Defaults to info.js
69
+ * @param {*} target The target to harvest to - Since we will not know the entityGuid before harvesting, this must be an object directly supplied from the info object or API, not an entityGuid string for lookup with the entityManager - Defaults to { licenseKey: this.agentRef.info.licenseKey, applicationID: this.agentRef.info.applicationID }
70
+ */
71
+ sendRum(customAttributes = this.agentRef.info.jsAttributes, target = {
72
+ licenseKey: this.agentRef.info.licenseKey,
73
+ applicationID: this.agentRef.info.applicationID
74
+ }) {
60
75
  const info = this.agentRef.info;
61
76
  const measures = {};
62
77
  if (info.queueTime) measures.qt = info.queueTime;
@@ -82,9 +97,9 @@ class Aggregate extends _aggregateBase.AggregateBase {
82
97
  if (this.agentRef.runtime.session) queryParameters.fsh = Number(this.agentRef.runtime.session.isNew); // "first session harvest" aka RUM request or PageView event of a session
83
98
 
84
99
  let body;
85
- if (typeof info.jsAttributes === 'object' && Object.keys(info.jsAttributes).length > 0) {
100
+ if (typeof customAttributes === 'object' && Object.keys(customAttributes).length > 0) {
86
101
  body = (0, _traverse.applyFnToProps)({
87
- ja: info.jsAttributes
102
+ ja: customAttributes
88
103
  }, this.obfuscator.obfuscateString.bind(this.obfuscator), 'string');
89
104
  }
90
105
  if (_runtime.globalScope.performance) {
@@ -114,7 +129,7 @@ class Aggregate extends _aggregateBase.AggregateBase {
114
129
  this.rumStartTime = (0, _now.now)();
115
130
  this.agentRef.runtime.harvester.triggerHarvestFor(this, {
116
131
  directSend: {
117
- targetApp: this.agentRef.mainAppKey,
132
+ targetApp: target,
118
133
  payload: {
119
134
  qs: queryParameters,
120
135
  body
@@ -127,20 +142,32 @@ class Aggregate extends _aggregateBase.AggregateBase {
127
142
  postHarvestCleanup({
128
143
  status,
129
144
  responseText,
130
- xhr
145
+ xhr,
146
+ targetApp
131
147
  }) {
132
148
  const rumEndTime = (0, _now.now)();
149
+ let app, flags;
150
+ try {
151
+ ({
152
+ app,
153
+ ...flags
154
+ } = JSON.parse(responseText));
155
+ this.processEntities(app.agents, targetApp);
156
+ } catch (error) {
157
+ // wont set entity stuff here, if main agent will later abort, if registered agent, nothing will happen
158
+ (0, _console.warn)(53, error);
159
+ }
160
+
161
+ /** Only run agent-wide side-effects if the harvest was for the main agent */
162
+ if (!(0, _target.isContainerAgentTarget)(targetApp, this.agentRef)) return;
133
163
  if (status >= 400 || status === 0) {
134
164
  (0, _console.warn)(18, status);
135
165
  // Adding retry logic for the rum call will be a separate change; this.blocked will need to be changed since that prevents another triggerHarvestFor()
136
166
  this.ee.abort();
137
167
  return;
138
168
  }
139
- const {
140
- app,
141
- ...flags
142
- } = JSON.parse(responseText);
143
169
  try {
170
+ // will do nothing if already done
144
171
  this.agentRef.runtime.timeKeeper.processRumRequest(xhr, this.rumStartTime, rumEndTime, app.nrServerTime);
145
172
  if (!this.agentRef.runtime.timeKeeper.ready) throw new Error('TimeKeeper not ready');
146
173
  } catch (error) {
@@ -148,10 +175,32 @@ class Aggregate extends _aggregateBase.AggregateBase {
148
175
  (0, _console.warn)(17, error);
149
176
  return;
150
177
  }
151
- this.agentRef.runtime.appMetadata = app;
152
- (0, _featureFlags.activateFeatures)(flags, this.agentIdentifier);
178
+
179
+ // set the agent runtime objects that require the rum response or entity guid
180
+ if (!Object.keys(this.agentRef.runtime.appMetadata).length) this.agentRef.runtime.appMetadata = app;
153
181
  this.drain();
154
182
  this.agentRef.runtime.harvester.startTimer();
183
+ (0, _featureFlags.activateFeatures)(flags, this.agentRef);
184
+ }
185
+ processEntities(entities, targetApp) {
186
+ if (!entities || !targetApp) return;
187
+ entities.forEach(agent => {
188
+ const entityManager = this.agentRef.runtime.entityManager;
189
+ const entityGuid = agent.entityGuid;
190
+ const entity = entityManager.get(entityGuid);
191
+ if (entity) return; // already processed
192
+
193
+ if ((0, _target.isContainerAgentTarget)(targetApp, this.agentRef)) {
194
+ entityManager.setDefaultEntity({
195
+ ...targetApp,
196
+ entityGuid
197
+ });
198
+ }
199
+ entityManager.set(agent.entityGuid, {
200
+ ...targetApp,
201
+ entityGuid
202
+ });
203
+ });
155
204
  }
156
205
  }
157
206
  exports.Aggregate = Aggregate;
@@ -4,6 +4,7 @@ Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
6
  exports.PageViewEvent = exports.Instrument = void 0;
7
+ var _handle = require("../../../common/event-emitter/handle");
7
8
  var _instrumentBase = require("../../utils/instrument-base");
8
9
  var CONSTANTS = _interopRequireWildcard(require("../constants"));
9
10
  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); }
@@ -17,6 +18,9 @@ class Instrument extends _instrumentBase.InstrumentBase {
17
18
  static featureName = CONSTANTS.FEATURE_NAME;
18
19
  constructor(agentRef, auto = true) {
19
20
  super(agentRef, CONSTANTS.FEATURE_NAME, auto);
21
+
22
+ /** messages from the register API that can trigger a new RUM call */
23
+ this.ee.on('api-send-rum', (attrs, target) => (0, _handle.handle)('send-rum', [attrs, target], undefined, this.featureName, this.ee));
20
24
  this.importAggregator(agentRef);
21
25
  }
22
26
  }
@@ -50,7 +50,7 @@ class Aggregate extends _aggregateBase.AggregateBase {
50
50
  this.recorder = args?.recorder;
51
51
  this.errorNoticed = args?.errorNoticed || false;
52
52
  this.harvestOpts.raw = true;
53
- this.isSessionTrackingEnabled = (0, _featureGates.canEnableSessionTracking)(this.agentIdentifier) && this.agentRef.runtime.session;
53
+ this.isSessionTrackingEnabled = (0, _featureGates.canEnableSessionTracking)(this.agentIdentifier) && !!this.agentRef.runtime.session;
54
54
  this.reportSupportabilityMetric('Config/SessionReplay/Enabled');
55
55
 
56
56
  // 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.
@@ -223,6 +223,10 @@ class Aggregate extends _aggregateBase.AggregateBase {
223
223
  }
224
224
  }
225
225
  makeHarvestPayload(shouldRetryOnFail) {
226
+ const payloadOutput = {
227
+ targetApp: undefined,
228
+ payload: undefined
229
+ };
226
230
  if (this.mode !== _constants2.MODE.FULL || this.blocked) return;
227
231
  if (!this.recorder || !this.timeKeeper?.ready || !this.recorder.hasSeenSnapshot) return;
228
232
  const recorderEvents = this.recorder.getEvents();
@@ -231,7 +235,7 @@ class Aggregate extends _aggregateBase.AggregateBase {
231
235
  const payload = this.getHarvestContents(recorderEvents);
232
236
  if (!payload.body.length) {
233
237
  this.recorder.clearBuffer();
234
- return;
238
+ return [payloadOutput];
235
239
  }
236
240
  this.reportSupportabilityMetric('SessionReplay/Harvest/Attempts');
237
241
  let len = 0;
@@ -268,7 +272,7 @@ class Aggregate extends _aggregateBase.AggregateBase {
268
272
  }
269
273
  if (len > _agentConstants.MAX_PAYLOAD_SIZE) {
270
274
  this.abort(_constants.ABORT_REASONS.TOO_BIG, len);
271
- return;
275
+ return [payloadOutput];
272
276
  }
273
277
  // TODO -- Gracefully handle the buffer for retries.
274
278
  if (!this.agentRef.runtime.session.state.sessionReplaySentFirstChunk) this.syncWithSessionManager({
@@ -276,10 +280,8 @@ class Aggregate extends _aggregateBase.AggregateBase {
276
280
  });
277
281
  this.recorder.clearBuffer();
278
282
  if (recorderEvents.type === 'preloaded') this.agentRef.runtime.harvester.triggerHarvestFor(this);
279
- return [{
280
- targetApp: undefined,
281
- payload
282
- }]; // SR doesn't need a targetApp as it only works for the main, but format needs to make AggregateBase
283
+ payloadOutput.payload = payload;
284
+ return [payloadOutput];
283
285
  }
284
286
  getCorrectedTimestamp(node) {
285
287
  if (!node?.timestamp) return;
@@ -89,7 +89,7 @@ class Instrument extends _instrumentBase.InstrumentBase {
89
89
  });
90
90
  this.recorder.startRecording();
91
91
  this.abortHandler = this.recorder.stopRecording;
92
- } catch (e) {}
92
+ } catch (e) {} // TODO add internal error handling
93
93
  this.importAggregator(this.#agentRef, {
94
94
  recorder: this.recorder,
95
95
  errorNoticed: this.errorNoticed
@@ -26,8 +26,10 @@ class RecorderEvents {
26
26
  hasMeta = false;
27
27
  /** Payload metadata -- Should indicate that the payload being sent contains an error. Used for query/filter purposes in UI */
28
28
  hasError = false;
29
- /** Payload metadata -- Denotes whether all stylesheet elements were able to be inlined */
30
- inlinedAllStylesheets = true;
29
+ constructor(shouldInlineStylesheets = true) {
30
+ /** Payload metadata -- Denotes whether all stylesheet elements were able to be inlined */
31
+ this.inlinedAllStylesheets = shouldInlineStylesheets;
32
+ }
31
33
  add(event) {
32
34
  this.#events.add(event);
33
35
  }
@@ -35,9 +35,14 @@ class Recorder {
35
35
  #warnCSSOnce = (0, _invoke.single)(() => (0, _console.warn)(47)); // notifies user of potential replayer issue if fix_stylesheets is off
36
36
 
37
37
  constructor(parent) {
38
- this.#events = new _recorderEvents.RecorderEvents();
39
- this.#backloggedEvents = new _recorderEvents.RecorderEvents();
40
- this.#preloaded = [new _recorderEvents.RecorderEvents()];
38
+ /** The parent class that instantiated the recorder */
39
+ this.parent = parent;
40
+ /** A flag that can be set to false by failing conversions to stop the fetching process */
41
+ this.shouldFix = this.parent.agentRef.init.session_replay.fix_stylesheets;
42
+ /** Event Buffers */
43
+ this.#events = new _recorderEvents.RecorderEvents(this.shouldFix);
44
+ this.#backloggedEvents = new _recorderEvents.RecorderEvents(this.shouldFix);
45
+ this.#preloaded = [new _recorderEvents.RecorderEvents(this.shouldFix)];
41
46
  /** True when actively recording, false when paused or stopped */
42
47
  this.recording = false;
43
48
  /** The pointer to the current bucket holding rrweb events */
@@ -46,10 +51,6 @@ class Recorder {
46
51
  this.hasSeenSnapshot = false;
47
52
  /** Hold on to the last meta node, so that it can be re-inserted if the meta and snapshot nodes are broken up due to harvesting */
48
53
  this.lastMeta = false;
49
- /** The parent class that instantiated the recorder */
50
- this.parent = parent;
51
- /** A flag that can be set to false by failing conversions to stop the fetching process */
52
- this.shouldFix = this.parent.agentRef.init.session_replay.fix_stylesheets;
53
54
  /** The method to stop recording. This defaults to a noop, but is overwritten once the recording library is imported and initialized */
54
55
  this.stopRecording = () => {/* no-op until set by rrweb initializer */};
55
56
  }
@@ -76,8 +77,8 @@ class Recorder {
76
77
 
77
78
  /** Clears the buffer (this.#events), and resets all payload metadata properties */
78
79
  clearBuffer() {
79
- if (this.#preloaded[0]?.events.length) this.#preloaded.shift();else if (this.parent.mode === _constants2.MODE.ERROR) this.#backloggedEvents = this.#events;else this.#backloggedEvents = new _recorderEvents.RecorderEvents();
80
- this.#events = new _recorderEvents.RecorderEvents();
80
+ if (this.#preloaded[0]?.events.length) this.#preloaded.shift();else if (this.parent.mode === _constants2.MODE.ERROR) this.#backloggedEvents = this.#events;else this.#backloggedEvents = new _recorderEvents.RecorderEvents(this.shouldFix);
81
+ this.#events = new _recorderEvents.RecorderEvents(this.shouldFix);
81
82
  }
82
83
 
83
84
  /** Begin recording using configured recording lib */
@@ -116,6 +117,7 @@ class Recorder {
116
117
  });
117
118
  this.stopRecording = () => {
118
119
  this.recording = false;
120
+ this.notified = false;
119
121
  this.parent.ee.emit(_constants.SR_EVENT_EMITTER_TYPES.REPLAY_RUNNING, [false, this.parent.mode]);
120
122
  stop?.();
121
123
  };
@@ -129,7 +131,7 @@ class Recorder {
129
131
  */
130
132
  audit(event, isCheckout) {
131
133
  /** An count of stylesheet objects that were blocked from accessing contents via JS */
132
- const incompletes = _stylesheetEvaluator.stylesheetEvaluator.evaluate();
134
+ const incompletes = this.parent.agentRef.init.session_replay.fix_stylesheets ? _stylesheetEvaluator.stylesheetEvaluator.evaluate() : 0;
133
135
  const missingInlineSMTag = 'SessionReplay/Payload/Missing-Inline-Css/';
134
136
  /** only run the full fixing behavior (more costly) if fix_stylesheets is configured as on (default behavior) */
135
137
  if (!this.shouldFix) {
@@ -163,6 +165,10 @@ class Recorder {
163
165
  /** Store a payload in the buffer (this.#events). This should be the callback to the recording lib noticing a mutation */
164
166
  store(event, isCheckout) {
165
167
  if (!event) return;
168
+ if (this.parent.agentRef.runtime?.session?.isAfterSessionExpiry(event.timestamp)) {
169
+ this.parent.reportSupportabilityMetric('Session/Expired/SessionReplay/Seen');
170
+ return;
171
+ }
166
172
  if (!(this.parent instanceof _aggregateBase.AggregateBase) && this.#preloaded.length) this.currentBufferTarget = this.#preloaded[this.#preloaded.length - 1];else this.currentBufferTarget = this.#events;
167
173
  if (this.parent.blocked) return;
168
174
  if (!this.notified) {
@@ -205,7 +211,7 @@ class Recorder {
205
211
  this.parent.agentRef.runtime.harvester.triggerHarvestFor(this.parent);
206
212
  } else {
207
213
  // we are still in "preload" and it triggered a "stop point". Make a new set, which will get pointed at on next cycle
208
- this.#preloaded.push(new _recorderEvents.RecorderEvents());
214
+ this.#preloaded.push(new _recorderEvents.RecorderEvents(this.shouldFix));
209
215
  }
210
216
  }
211
217
  }
@@ -53,6 +53,9 @@ class TraceStorage {
53
53
  constructor(parent) {
54
54
  this.parent = parent;
55
55
  }
56
+ isAfterSessionExpiry(entryTimestamp) {
57
+ return this.parent.agentRef.runtime?.session?.isAfterSessionExpiry((this.parent.timeKeeper?.ready && this.parent.timeKeeper.convertRelativeTimestamp(entryTimestamp)) ?? undefined);
58
+ }
56
59
 
57
60
  /** Central function called by all the other store__ & addToTrace API to append a trace node. */
58
61
  storeSTN(stn) {
@@ -63,6 +66,10 @@ class TraceStorage {
63
66
  const openedSpace = this.trimSTNs(ERROR_MODE_SECONDS_WINDOW); // but maybe we could make some space by discarding irrelevant nodes if we're in sessioned Error mode
64
67
  if (openedSpace === 0) return;
65
68
  }
69
+ if (this.isAfterSessionExpiry(stn.s)) {
70
+ this.parent.reportSupportabilityMetric('Session/Expired/SessionTrace/Seen');
71
+ return;
72
+ }
66
73
  if (this.trace[stn.n]) this.trace[stn.n].push(stn);else this.trace[stn.n] = [stn];
67
74
  if (stn.s < this.earliestTimeStamp) this.earliestTimeStamp = stn.s;
68
75
  if (stn.s > this.latestTimeStamp) this.latestTimeStamp = stn.s;
@@ -267,7 +274,7 @@ class TraceStorage {
267
274
  }
268
275
  get() {
269
276
  return [{
270
- targetApp: this.parent.agentRef.mainAppKey,
277
+ targetApp: this.parent.agentRef.runtime.entityManager.get(),
271
278
  data: this.takeSTNs()
272
279
  }];
273
280
  }
@@ -30,8 +30,17 @@ class Aggregate extends _aggregateBase.AggregateBase {
30
30
  this.initialPageLoadInteraction = new _initialPageLoadInteraction.InitialPageLoadInteraction(agentRef.agentIdentifier);
31
31
  this.initialPageLoadInteraction.onDone.push(() => {
32
32
  // this ensures the .end() method also works with iPL
33
+ if (agentRef.runtime.session?.isNew) this.initialPageLoadInteraction.customAttributes.isFirstOfSession = true; // mark the hard page load as first of its session
33
34
  this.initialPageLoadInteraction.forceSave = true; // unless forcibly ignored, iPL always finish by default
34
- this.interactionsToHarvest.add(this.initialPageLoadInteraction);
35
+ const ixn = this.initialPageLoadInteraction;
36
+ /** this.events (ixns to harvest) has already been set up, use it immediately */
37
+ if (this.interactionsToHarvest) this.interactionsToHarvest.add(ixn);else {
38
+ /** this.events (ixns to harvest) hasnt been initialized yet... wait for it */
39
+ this.ee.on('entity-added', () => {
40
+ this.interactionsToHarvest = this.events;
41
+ this.interactionsToHarvest.add(ixn);
42
+ });
43
+ }
35
44
  this.initialPageLoadInteraction = null;
36
45
  });
37
46
  _timeToFirstByte.timeToFirstByte.subscribe(({
@@ -132,7 +141,9 @@ class Aggregate extends _aggregateBase.AggregateBase {
132
141
  */
133
142
  if (this.interactionInProgress?.isActiveDuring(timestamp)) return this.interactionInProgress;
134
143
  let saveIxn;
135
- const interactionsBuffer = this.interactionsToHarvest.get(this.agentRef.mainAppKey)[0].data;
144
+ const [{
145
+ data: interactionsBuffer
146
+ }] = this.interactionsToHarvest.get();
136
147
  for (let idx = interactionsBuffer.length - 1; idx >= 0; idx--) {
137
148
  // reverse search for the latest completed interaction for efficiency
138
149
  const finishedInteraction = interactionsBuffer[idx];
@@ -125,8 +125,9 @@ class Aggregate extends _aggregateBase.AggregateBase {
125
125
  }
126
126
  });
127
127
  if (agentRef.init.spa.enabled !== true) return;
128
- state.initialPageLoad = new _interaction.Interaction('initialPageLoad', 0, state.lastSeenUrl, state.lastSeenRouteName, onInteractionFinished, agentRef.agentIdentifier);
128
+ state.initialPageLoad = new _interaction.Interaction('initialPageLoad', 0, state.lastSeenUrl, state.lastSeenRouteName, onInteractionFinished, agentRef);
129
129
  state.initialPageLoad.save = true;
130
+ if (agentRef.runtime.session?.isNew) state.initialPageLoad.root.attrs.custom.isFirstOfSession = true; // mark the hard page load as first of its session
130
131
  state.prevInteraction = state.initialPageLoad;
131
132
  state.currentNode = state.initialPageLoad.root; // hint
132
133
  // ensure that checkFinish calls are safe during initialPageLoad
@@ -206,7 +207,7 @@ class Aggregate extends _aggregateBase.AggregateBase {
206
207
  // Otherwise, if no interaction is currently active, create a new node ID,
207
208
  // and let the aggregator know that we entered a new event handler callback
208
209
  // so that it has a chance to possibly start an interaction.
209
- var ixn = new _interaction.Interaction(evName, this[FN_START], state.lastSeenUrl, state.lastSeenRouteName, onInteractionFinished, agentRef.agentIdentifier);
210
+ var ixn = new _interaction.Interaction(evName, this[FN_START], state.lastSeenUrl, state.lastSeenRouteName, onInteractionFinished, agentRef);
210
211
 
211
212
  // Store the interaction as prevInteraction in case it is prematurely discarded
212
213
  state.prevInteraction = ixn;
@@ -504,7 +505,7 @@ class Aggregate extends _aggregateBase.AggregateBase {
504
505
  }, this.featureName, promiseEE);
505
506
  (0, _registerHandler.registerHandler)(INTERACTION_API + 'get', function (t) {
506
507
  var interaction;
507
- if (state?.currentNode?.[INTERACTION]) interaction = this.ixn = state.currentNode[INTERACTION];else if (state?.prevNode?.end === null && state?.prevNode?.[INTERACTION]?.root?.[INTERACTION]?.eventName !== 'initialPageLoad') interaction = this.ixn = state.prevNode[INTERACTION];else interaction = this.ixn = new _interaction.Interaction('api', t, state.lastSeenUrl, state.lastSeenRouteName, onInteractionFinished, agentRef.agentIdentifier);
508
+ if (state?.currentNode?.[INTERACTION]) interaction = this.ixn = state.currentNode[INTERACTION];else if (state?.prevNode?.end === null && state?.prevNode?.[INTERACTION]?.root?.[INTERACTION]?.eventName !== 'initialPageLoad') interaction = this.ixn = state.prevNode[INTERACTION];else interaction = this.ixn = new _interaction.Interaction('api', t, state.lastSeenUrl, state.lastSeenRouteName, onInteractionFinished, agentRef);
508
509
  if (!state.currentNode) {
509
510
  interaction.checkFinish();
510
511
  if (state.depth) setCurrentNode(interaction.root);
@@ -4,10 +4,8 @@ Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
6
  exports.Interaction = Interaction;
7
- var _info = require("../../../common/config/info");
8
7
  var _runtime = require("../../../common/constants/runtime");
9
8
  var _nreum = require("../../../common/window/nreum");
10
- var _contextualEe = require("../../../common/event-emitter/contextual-ee");
11
9
  var _interactionNode = require("./interaction-node");
12
10
  /**
13
11
  * Copyright 2020-2025 New Relic, Inc. All rights reserved.
@@ -17,11 +15,10 @@ var _interactionNode = require("./interaction-node");
17
15
  var originalSetTimeout = (0, _nreum.gosNREUMOriginals)().o.ST;
18
16
  var originalClearTimeout = (0, _nreum.gosNREUMOriginals)().o.CT;
19
17
  var lastId = {};
20
- function Interaction(eventName, timestamp, url, routeName, onFinished, agentIdentifier) {
21
- this.agentIdentifier = agentIdentifier;
22
- this.ee = _contextualEe.ee.get(agentIdentifier);
23
- lastId[agentIdentifier] = 0;
24
- this.id = ++lastId[agentIdentifier];
18
+ function Interaction(eventName, timestamp, url, routeName, onFinished, agentRef) {
19
+ this.agentRef = agentRef;
20
+ lastId[agentRef.agentIdentifier] = 0;
21
+ this.id = ++lastId[agentRef.agentIdentifier];
25
22
  this.eventName = eventName;
26
23
  this.nodes = 0;
27
24
  this.remaining = 0;
@@ -89,9 +86,9 @@ InteractionPrototype.finish = function finishInteraction() {
89
86
  if (this.onFinished) {
90
87
  this.onFinished(this);
91
88
  }
92
- Object.entries((0, _info.getInfo)(interaction.agentIdentifier).jsAttributes || {}).forEach(([attr, value]) => {
89
+ Object.entries(interaction.agentRef.info?.jsAttributes || {}).forEach(([attr, value]) => {
93
90
  if (!(attr in customAttrs)) customAttrs[attr] = value;
94
91
  });
95
92
  root.end = endTimestamp;
96
- interaction.ee.emit('interaction', [this]);
93
+ interaction.agentRef.ee.emit('interaction', [this]);
97
94
  };