@newrelic/browser-agent 1.285.0 → 1.287.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 (180) hide show
  1. package/CHANGELOG.md +22 -0
  2. package/README.md +6 -0
  3. package/dist/cjs/common/config/init-types.js +96 -0
  4. package/dist/cjs/common/config/init.js +9 -79
  5. package/dist/cjs/common/config/runtime.js +7 -6
  6. package/dist/cjs/common/constants/env.cdn.js +1 -1
  7. package/dist/cjs/common/constants/env.npm.js +1 -1
  8. package/dist/cjs/common/dom/selector-path.js +1 -1
  9. package/dist/cjs/common/harvest/harvester.js +3 -3
  10. package/dist/cjs/common/util/feature-flags.js +6 -6
  11. package/dist/cjs/common/util/target.js +34 -0
  12. package/dist/cjs/features/ajax/aggregate/index.js +2 -1
  13. package/dist/cjs/features/generic_events/aggregate/index.js +10 -6
  14. package/dist/cjs/features/jserrors/aggregate/index.js +44 -22
  15. package/dist/cjs/features/logging/aggregate/index.js +17 -11
  16. package/dist/cjs/features/logging/shared/utils.js +3 -2
  17. package/dist/cjs/features/metrics/aggregate/index.js +6 -4
  18. package/dist/cjs/features/page_view_event/aggregate/index.js +60 -11
  19. package/dist/cjs/features/page_view_event/instrument/index.js +4 -0
  20. package/dist/cjs/features/session_replay/aggregate/index.js +8 -6
  21. package/dist/cjs/features/session_replay/instrument/index.js +1 -1
  22. package/dist/cjs/features/session_replay/shared/recorder-events.js +4 -2
  23. package/dist/cjs/features/session_replay/shared/recorder.js +17 -11
  24. package/dist/cjs/features/session_trace/aggregate/trace/storage.js +4 -3
  25. package/dist/cjs/features/soft_navigations/aggregate/index.js +12 -2
  26. package/dist/cjs/features/spa/aggregate/index.js +3 -3
  27. package/dist/cjs/features/spa/aggregate/interaction.js +6 -9
  28. package/dist/cjs/features/utils/aggregate-base.js +51 -24
  29. package/dist/cjs/features/utils/entity-manager.js +47 -0
  30. package/dist/cjs/features/utils/event-store-manager.js +79 -54
  31. package/dist/cjs/features/utils/nr1-debugger.js +1 -1
  32. package/dist/cjs/interfaces/registered-entity.js +114 -0
  33. package/dist/cjs/loaders/agent-base.js +1 -1
  34. package/dist/cjs/loaders/agent.js +3 -1
  35. package/dist/cjs/loaders/api/api-methods.js +1 -1
  36. package/dist/cjs/loaders/api/api.js +97 -69
  37. package/dist/cjs/loaders/api/apiAsync.js +19 -22
  38. package/dist/cjs/loaders/api/register-api-types.js +35 -0
  39. package/dist/cjs/loaders/api/register-api.js +165 -0
  40. package/dist/cjs/loaders/configure/configure.js +12 -15
  41. package/dist/cjs/loaders/micro-agent-base.js +17 -1
  42. package/dist/cjs/loaders/micro-agent.js +4 -1
  43. package/dist/esm/common/config/init-types.js +92 -0
  44. package/dist/esm/common/config/init.js +9 -79
  45. package/dist/esm/common/config/runtime.js +7 -6
  46. package/dist/esm/common/constants/env.cdn.js +1 -1
  47. package/dist/esm/common/constants/env.npm.js +1 -1
  48. package/dist/esm/common/dom/selector-path.js +1 -1
  49. package/dist/esm/common/harvest/harvester.js +3 -3
  50. package/dist/esm/common/util/feature-flags.js +6 -6
  51. package/dist/esm/common/util/target.js +27 -0
  52. package/dist/esm/features/ajax/aggregate/index.js +2 -1
  53. package/dist/esm/features/generic_events/aggregate/index.js +10 -6
  54. package/dist/esm/features/jserrors/aggregate/index.js +44 -22
  55. package/dist/esm/features/logging/aggregate/index.js +17 -11
  56. package/dist/esm/features/logging/shared/utils.js +3 -2
  57. package/dist/esm/features/metrics/aggregate/index.js +6 -4
  58. package/dist/esm/features/page_view_event/aggregate/index.js +60 -11
  59. package/dist/esm/features/page_view_event/instrument/index.js +4 -0
  60. package/dist/esm/features/session_replay/aggregate/index.js +8 -6
  61. package/dist/esm/features/session_replay/instrument/index.js +1 -1
  62. package/dist/esm/features/session_replay/shared/recorder-events.js +4 -2
  63. package/dist/esm/features/session_replay/shared/recorder.js +17 -11
  64. package/dist/esm/features/session_trace/aggregate/trace/storage.js +4 -3
  65. package/dist/esm/features/soft_navigations/aggregate/index.js +12 -2
  66. package/dist/esm/features/spa/aggregate/index.js +3 -3
  67. package/dist/esm/features/spa/aggregate/interaction.js +6 -9
  68. package/dist/esm/features/utils/aggregate-base.js +51 -24
  69. package/dist/esm/features/utils/entity-manager.js +40 -0
  70. package/dist/esm/features/utils/event-store-manager.js +79 -54
  71. package/dist/esm/features/utils/nr1-debugger.js +1 -1
  72. package/dist/esm/interfaces/registered-entity.js +107 -0
  73. package/dist/esm/loaders/agent-base.js +1 -1
  74. package/dist/esm/loaders/agent.js +3 -1
  75. package/dist/esm/loaders/api/api-methods.js +1 -1
  76. package/dist/esm/loaders/api/api.js +95 -67
  77. package/dist/esm/loaders/api/apiAsync.js +14 -17
  78. package/dist/esm/loaders/api/register-api-types.js +33 -0
  79. package/dist/esm/loaders/api/register-api.js +159 -0
  80. package/dist/esm/loaders/configure/configure.js +13 -16
  81. package/dist/esm/loaders/micro-agent-base.js +17 -1
  82. package/dist/esm/loaders/micro-agent.js +4 -1
  83. package/dist/tsconfig.tsbuildinfo +1 -1
  84. package/dist/types/common/config/init-types.d.ts +275 -0
  85. package/dist/types/common/config/init-types.d.ts.map +1 -0
  86. package/dist/types/common/config/init.d.ts +1 -262
  87. package/dist/types/common/config/init.d.ts.map +1 -1
  88. package/dist/types/common/config/runtime.d.ts.map +1 -1
  89. package/dist/types/common/dom/selector-path.d.ts.map +1 -1
  90. package/dist/types/common/util/feature-flags.d.ts +1 -1
  91. package/dist/types/common/util/feature-flags.d.ts.map +1 -1
  92. package/dist/types/common/util/target.d.ts +18 -0
  93. package/dist/types/common/util/target.d.ts.map +1 -0
  94. package/dist/types/features/ajax/aggregate/index.d.ts +1 -1
  95. package/dist/types/features/ajax/aggregate/index.d.ts.map +1 -1
  96. package/dist/types/features/generic_events/aggregate/index.d.ts +2 -1
  97. package/dist/types/features/generic_events/aggregate/index.d.ts.map +1 -1
  98. package/dist/types/features/jserrors/aggregate/index.d.ts +9 -1
  99. package/dist/types/features/jserrors/aggregate/index.d.ts.map +1 -1
  100. package/dist/types/features/logging/aggregate/index.d.ts +3 -3
  101. package/dist/types/features/logging/aggregate/index.d.ts.map +1 -1
  102. package/dist/types/features/logging/shared/utils.d.ts +2 -1
  103. package/dist/types/features/logging/shared/utils.d.ts.map +1 -1
  104. package/dist/types/features/metrics/aggregate/index.d.ts +1 -0
  105. package/dist/types/features/metrics/aggregate/index.d.ts.map +1 -1
  106. package/dist/types/features/page_view_event/aggregate/index.d.ts +10 -2
  107. package/dist/types/features/page_view_event/aggregate/index.d.ts.map +1 -1
  108. package/dist/types/features/page_view_event/instrument/index.d.ts.map +1 -1
  109. package/dist/types/features/session_replay/aggregate/index.d.ts +2 -11
  110. package/dist/types/features/session_replay/aggregate/index.d.ts.map +1 -1
  111. package/dist/types/features/session_replay/shared/recorder-events.d.ts +1 -0
  112. package/dist/types/features/session_replay/shared/recorder-events.d.ts.map +1 -1
  113. package/dist/types/features/session_replay/shared/recorder.d.ts +4 -4
  114. package/dist/types/features/session_replay/shared/recorder.d.ts.map +1 -1
  115. package/dist/types/features/soft_navigations/aggregate/index.d.ts.map +1 -1
  116. package/dist/types/features/spa/aggregate/interaction.d.ts +3 -4
  117. package/dist/types/features/spa/aggregate/interaction.d.ts.map +1 -1
  118. package/dist/types/features/utils/aggregate-base.d.ts +22 -5
  119. package/dist/types/features/utils/aggregate-base.d.ts.map +1 -1
  120. package/dist/types/features/utils/entity-manager.d.ts +15 -0
  121. package/dist/types/features/utils/entity-manager.d.ts.map +1 -0
  122. package/dist/types/features/utils/event-store-manager.d.ts +48 -24
  123. package/dist/types/features/utils/event-store-manager.d.ts.map +1 -1
  124. package/dist/types/interfaces/registered-entity.d.ts +72 -0
  125. package/dist/types/interfaces/registered-entity.d.ts.map +1 -0
  126. package/dist/types/loaders/agent.d.ts +2 -1
  127. package/dist/types/loaders/agent.d.ts.map +1 -1
  128. package/dist/types/loaders/api/api.d.ts +1 -20
  129. package/dist/types/loaders/api/api.d.ts.map +1 -1
  130. package/dist/types/loaders/api/apiAsync.d.ts +1 -1
  131. package/dist/types/loaders/api/apiAsync.d.ts.map +1 -1
  132. package/dist/types/loaders/api/register-api-types.d.ts +56 -0
  133. package/dist/types/loaders/api/register-api-types.d.ts.map +1 -0
  134. package/dist/types/loaders/api/register-api.d.ts +14 -0
  135. package/dist/types/loaders/api/register-api.d.ts.map +1 -0
  136. package/dist/types/loaders/configure/configure.d.ts +1 -0
  137. package/dist/types/loaders/configure/configure.d.ts.map +1 -1
  138. package/dist/types/loaders/micro-agent-base.d.ts +17 -0
  139. package/dist/types/loaders/micro-agent-base.d.ts.map +1 -1
  140. package/dist/types/loaders/micro-agent.d.ts +2 -0
  141. package/dist/types/loaders/micro-agent.d.ts.map +1 -1
  142. package/package.json +10 -1
  143. package/src/common/config/init-types.js +92 -0
  144. package/src/common/config/init.js +9 -79
  145. package/src/common/config/runtime.js +7 -6
  146. package/src/common/dom/selector-path.js +1 -3
  147. package/src/common/harvest/harvester.js +3 -3
  148. package/src/common/util/feature-flags.js +6 -6
  149. package/src/common/util/target.js +27 -0
  150. package/src/features/ajax/aggregate/index.js +2 -1
  151. package/src/features/generic_events/aggregate/index.js +8 -6
  152. package/src/features/jserrors/aggregate/index.js +42 -20
  153. package/src/features/logging/aggregate/index.js +14 -10
  154. package/src/features/logging/shared/utils.js +3 -2
  155. package/src/features/metrics/aggregate/index.js +7 -5
  156. package/src/features/page_view_event/aggregate/index.js +50 -8
  157. package/src/features/page_view_event/instrument/index.js +4 -0
  158. package/src/features/session_replay/aggregate/index.js +6 -3
  159. package/src/features/session_replay/instrument/index.js +1 -1
  160. package/src/features/session_replay/shared/recorder-events.js +5 -2
  161. package/src/features/session_replay/shared/recorder.js +17 -11
  162. package/src/features/session_trace/aggregate/trace/storage.js +3 -3
  163. package/src/features/soft_navigations/aggregate/index.js +11 -2
  164. package/src/features/spa/aggregate/index.js +3 -3
  165. package/src/features/spa/aggregate/interaction.js +6 -9
  166. package/src/features/utils/aggregate-base.js +56 -24
  167. package/src/features/utils/entity-manager.js +45 -0
  168. package/src/features/utils/event-store-manager.js +72 -49
  169. package/src/features/utils/nr1-debugger.js +1 -1
  170. package/src/interfaces/registered-entity.js +107 -0
  171. package/src/loaders/agent-base.js +2 -2
  172. package/src/loaders/agent.js +4 -1
  173. package/src/loaders/api/api-methods.js +1 -1
  174. package/src/loaders/api/api.js +94 -62
  175. package/src/loaders/api/apiAsync.js +14 -18
  176. package/src/loaders/api/register-api-types.js +33 -0
  177. package/src/loaders/api/register-api.js +152 -0
  178. package/src/loaders/configure/configure.js +12 -12
  179. package/src/loaders/micro-agent-base.js +18 -2
  180. package/src/loaders/micro-agent.js +5 -1
@@ -12,16 +12,42 @@ import { Obfuscator } from '../../common/util/obfuscate';
12
12
  import { FEATURE_NAMES } from '../../loaders/features/features';
13
13
  import { EventStoreManager } from './event-store-manager';
14
14
  import { Harvester } from '../../common/harvest/harvester';
15
+ import { warn } from '../../common/util/console';
16
+ import { EntityManager } from './entity-manager';
17
+ import { EventBuffer } from './event-buffer';
15
18
  import { handle } from '../../common/event-emitter/handle';
16
19
  import { SUPPORTABILITY_METRIC_CHANNEL } from '../metrics/constants';
20
+ import { EventAggregator } from '../../common/aggregate/event-aggregator';
17
21
  export class AggregateBase extends FeatureBase {
22
+ /**
23
+ * Create an AggregateBase instance.
24
+ * @param {Object} agentRef The reference to the agent instance.
25
+ * @param {string} featureName The name of the feature creating the instance.
26
+ */
18
27
  constructor(agentRef, featureName) {
19
28
  super(agentRef.agentIdentifier, featureName);
20
29
  this.agentRef = agentRef;
21
30
  this.checkConfiguration(agentRef);
22
31
  this.doOnceForAllAggregate(agentRef);
32
+ this.harvestOpts = {}; // features aggregate classes can define custom opts for when their harvest is called
33
+
34
+ const agentEntityGuid = this.agentRef?.runtime?.appMetadata?.agents?.[0]?.entityGuid;
35
+ if (agentEntityGuid) {
36
+ this.#setupEventStore(agentEntityGuid); // if there's no entity guid, wont dont anything, and will wait for rum flags
37
+ } else {
38
+ this.ee.on('entity-added', entity => {
39
+ this.#setupEventStore(entity.entityGuid);
40
+ });
41
+ }
42
+ }
23
43
 
24
- // This switch needs to be after doOnceForAllAggregate which may new sharedAggregator and reset mainAppKey.
44
+ /**
45
+ * sets up the event store for the feature. It must wait for the entity guid to be available before setting up the event store. This is called once the rum response is received with an entityGuid.
46
+ * @param {string} entityGuid
47
+ * @returns {void}
48
+ */
49
+ #setupEventStore(entityGuid) {
50
+ if (this.events || !entityGuid) return;
25
51
  switch (this.featureName) {
26
52
  // SessionTrace + Replay have their own storage mechanisms.
27
53
  case FEATURE_NAMES.sessionTrace:
@@ -30,16 +56,15 @@ export class AggregateBase extends FeatureBase {
30
56
  // Jserror and Metric features uses a singleton EventAggregator instead of a regular EventBuffer.
31
57
  case FEATURE_NAMES.jserrors:
32
58
  case FEATURE_NAMES.metrics:
33
- this.events = agentRef.sharedAggregator;
59
+ this.events = this.agentRef.sharedAggregator ??= new EventStoreManager(this.agentRef, EventAggregator, entityGuid, 'shared_aggregator');
34
60
  break;
35
61
  /** All other features get EventBuffer in the ESM by default. Note: PVE is included here, but event buffer will always be empty so future harvests will still not happen by interval or EOL.
36
62
  This was necessary to prevent race cond. issues where the event buffer was checked before the feature could "block" itself.
37
63
  Its easier to just keep an empty event buffer in place. */
38
64
  default:
39
- this.events = new EventStoreManager(agentRef.mainAppKey, 1, agentRef.agentIdentifier, this.featureName);
65
+ this.events = new EventStoreManager(this.agentRef, EventBuffer, entityGuid, this.featureName);
40
66
  break;
41
67
  }
42
- this.harvestOpts = {}; // features aggregate classes can define custom opts for when their harvest is called
43
68
  }
44
69
 
45
70
  /**
@@ -69,6 +94,10 @@ export class AggregateBase extends FeatureBase {
69
94
  this.deregisterDrain();
70
95
  });
71
96
  }
97
+
98
+ /**
99
+ * Stages the feature to be drained
100
+ */
72
101
  drain() {
73
102
  drain(this.agentIdentifier, this.featureName);
74
103
  this.drained = true;
@@ -80,28 +109,29 @@ export class AggregateBase extends FeatureBase {
80
109
  /**
81
110
  * Return harvest payload. A "serializer" function can be defined on a derived class to format the payload.
82
111
  * @param {Boolean} shouldRetryOnFail - harvester flag to backup payload for retry later if harvest request fails; this should be moved to harvester logic
83
- * @param {object|undefined} opts.target - the target app passed onto the event store manager to determine which app's data to return; if none provided, all apps data will be returned
112
+ * @param {object|undefined} opts - opts passed from the harvester to help form the payload
113
+ * @param {string} opts.targetEntityGuid - the entity guid of the target app
84
114
  * @returns {Array} Final payload tagged with their targeting browser app. The value of `payload` can be undefined if there are no pending events for an app. This should be a minimum length of 1.
85
115
  */
86
116
  makeHarvestPayload(shouldRetryOnFail = false, opts = {}) {
87
- if (this.events.isEmpty(this.harvestOpts, opts.target)) return;
117
+ if (!this.events || this.events.isEmpty(this.harvestOpts, opts.targetEntityGuid)) return;
88
118
  // Other conditions and things to do when preparing harvest that is required.
89
119
  if (this.preHarvestChecks && !this.preHarvestChecks(opts)) return;
90
- if (shouldRetryOnFail) this.events.save(this.harvestOpts, opts.target);
91
- const returnedDataArr = this.events.get(this.harvestOpts, opts.target);
92
- if (!returnedDataArr.length) throw new Error('Unexpected problem encountered. There should be at least one app for harvest!');
93
- this.events.clear(this.harvestOpts, opts.target);
120
+ if (shouldRetryOnFail) this.events.save(this.harvestOpts, opts.targetEntityGuid);
121
+ const returnedDataArr = this.events.get(this.harvestOpts, opts.targetEntityGuid);
122
+ if (!returnedDataArr.length) return warn(52);
123
+ this.events.clear(this.harvestOpts, opts.targetEntityGuid);
94
124
  return returnedDataArr.map(({
95
125
  targetApp,
96
126
  data
97
127
  }) => {
98
128
  // A serializer or formatter assists in creating the payload `body` from stored events on harvest when defined by derived feature class.
99
- const body = this.serializer ? this.serializer(data) : data;
129
+ const body = this.serializer ? this.serializer(data, targetApp?.entityGuid) : data;
100
130
  const payload = {
101
131
  body
102
132
  };
103
133
  // Constructs the payload `qs` for relevant features on harvest.
104
- if (this.queryStringsBuilder) payload.qs = this.queryStringsBuilder(data);
134
+ if (this.queryStringsBuilder) payload.qs = this.queryStringsBuilder(data, targetApp?.entityGuid);
105
135
  return {
106
136
  targetApp,
107
137
  payload
@@ -112,11 +142,15 @@ export class AggregateBase extends FeatureBase {
112
142
  /**
113
143
  * Cleanup task after a harvest.
114
144
  * @param {object} result - the cbResult object from the harvester's send method
145
+ * @param {object=} result.targetApp - the target app object that was used to point the harvest to the correct app
146
+ * @param {string=} result.targetApp.entityGuid - the entity guid of the target app
147
+ * @param {boolean=} result.sent - whether the harvest was sent successfully
148
+ * @param {boolean=} result.retry - whether the harvest should be retried
115
149
  */
116
150
  postHarvestCleanup(result = {}) {
117
151
  const harvestFailed = result.sent && result.retry;
118
- if (harvestFailed) this.events.reloadSave(this.harvestOpts, result.targetApp);
119
- this.events.clearSave(this.harvestOpts, result.targetApp);
152
+ if (harvestFailed) this.events.reloadSave(this.harvestOpts, result.targetApp?.entityGuid);
153
+ this.events.clearSave(this.harvestOpts, result.targetApp?.entityGuid);
120
154
  }
121
155
 
122
156
  /**
@@ -138,16 +172,14 @@ export class AggregateBase extends FeatureBase {
138
172
  } catch (err) {
139
173
  // do nothing
140
174
  }
141
- configure({
142
- agentIdentifier: this.agentIdentifier
143
- }, {
175
+ configure(existingAgent, {
144
176
  ...cdn,
145
177
  info: {
146
178
  ...cdn.info,
147
179
  jsAttributes
148
180
  },
149
181
  runtime: existingAgent.runtime
150
- });
182
+ }, existingAgent.runtime.loaderType);
151
183
  }
152
184
  }
153
185
 
@@ -158,12 +190,7 @@ export class AggregateBase extends FeatureBase {
158
190
  doOnceForAllAggregate(agentRef) {
159
191
  if (!agentRef.runtime.obfuscator) agentRef.runtime.obfuscator = new Obfuscator(agentRef);
160
192
  this.obfuscator = agentRef.runtime.obfuscator;
161
- if (!agentRef.mainAppKey) agentRef.mainAppKey = {
162
- licenseKey: agentRef.info.licenseKey,
163
- appId: agentRef.info.applicationID
164
- };
165
- // Create a single Aggregator for this agent if DNE yet; to be used by jserror endpoint features.
166
- if (!agentRef.sharedAggregator) agentRef.sharedAggregator = new EventStoreManager(agentRef.mainAppKey, 2, agentRef.agentIdentifier, 'shared_aggregator');
193
+ if (!agentRef.runtime.entityManager) agentRef.runtime.entityManager = new EntityManager(this.agentRef);
167
194
  if (!agentRef.runtime.harvester) agentRef.runtime.harvester = new Harvester(agentRef);
168
195
  }
169
196
 
@@ -0,0 +1,40 @@
1
+ /**
2
+ * Copyright 2020-2025 New Relic, Inc. All rights reserved.
3
+ * SPDX-License-Identifier: Apache-2.0
4
+ */
5
+
6
+ export class EntityManager {
7
+ #entities = new Map();
8
+ #entityGuidLookup = {};
9
+ #defaultEntity = null;
10
+ constructor(agentRef) {
11
+ this.agentRef = agentRef;
12
+ this.#defaultEntity = {
13
+ licenseKey: agentRef.info.licenseKey,
14
+ applicationID: agentRef.info.applicationID
15
+ };
16
+ }
17
+ get(entityGuid) {
18
+ if (!entityGuid) return this.#defaultEntity;
19
+ return this.#entities.get(entityGuid);
20
+ }
21
+ getEntityGuidFor(licenseKey, applicationID) {
22
+ if (!this.#entityGuidLookup[licenseKey] || !this.#entityGuidLookup[applicationID]) return;
23
+ return this.#entityGuidLookup[licenseKey].filter(x => this.#entityGuidLookup[applicationID].includes(x))[0];
24
+ }
25
+ set(entityGuid, entity) {
26
+ if (this.#entities.has(entityGuid)) return;
27
+ this.#entities.set(entityGuid, entity);
28
+ this.#entityGuidLookup[entity.licenseKey] ??= [];
29
+ this.#entityGuidLookup[entity.licenseKey].push(entityGuid);
30
+ this.#entityGuidLookup[entity.applicationID] ??= [];
31
+ this.#entityGuidLookup[entity.applicationID].push(entityGuid);
32
+ this.agentRef.ee.emit('entity-added', [entity]);
33
+ }
34
+ clear() {
35
+ this.#entities.clear();
36
+ }
37
+ setDefaultEntity(entity) {
38
+ this.#defaultEntity = entity;
39
+ }
40
+ }
@@ -2,10 +2,8 @@
2
2
  * Copyright 2020-2025 New Relic, Inc. All rights reserved.
3
3
  * SPDX-License-Identifier: Apache-2.0
4
4
  */
5
- import { EventAggregator } from '../../common/aggregate/event-aggregator';
6
5
  import { dispatchGlobalEvent } from '../../common/dispatch/global-event';
7
6
  import { activatedFeatures } from '../../common/util/feature-flags';
8
- import { EventBuffer } from './event-buffer';
9
7
 
10
8
  /**
11
9
  * This layer allows multiple browser entity apps, or "target", to each have their own segregated storage instance.
@@ -13,31 +11,39 @@ import { EventBuffer } from './event-buffer';
13
11
  */
14
12
  export class EventStoreManager {
15
13
  /**
16
- * @param {object} defaultTarget - should contain licenseKey and appId of the main app from NREUM.info at startup
17
- * @param {1|2} storageChoice - the type of storage to use in this manager; 'EventBuffer' (1), 'EventAggregator' (2)
18
- * @param {string} agentIdentifier - agent identifier used in inspection events
19
- * @param {string} featureName - feature name used in inspection events for non-shared aggregators
14
+ * @param {object} agentRef - reference to base agent class
15
+ * @param {EventBuffer|EventAggregator} storageClass - the type of storage to use in this manager; 'EventBuffer' (1), 'EventAggregator' (2)
20
16
  */
21
- constructor(defaultTarget, storageChoice, agentIdentifier, featureName) {
22
- this.mainApp = defaultTarget;
23
- this.StorageClass = storageChoice === 1 ? EventBuffer : EventAggregator;
17
+ constructor(agentRef, storageClass, defaultEntityGuid, featureName) {
18
+ this.agentRef = agentRef;
19
+ this.entityManager = agentRef.runtime.entityManager;
20
+ this.StorageClass = storageClass;
24
21
  this.appStorageMap = new Map();
25
- this.appStorageMap.set(defaultTarget, new this.StorageClass());
26
- this.agentIdentifier = agentIdentifier;
22
+ this.defaultEntity = this.#getEventStore(defaultEntityGuid);
27
23
  this.featureName = featureName;
28
24
  }
29
25
 
26
+ /**
27
+ * Always returns a storage instance. Creates one if one does not exist. If a lookup is not provided, uses the DEFAULT namespace
28
+ * @param {string=} targetEntityGuid the lookup
29
+ * @returns {*} ALWAYS returns a storage instance
30
+ */
31
+ #getEventStore(targetEntityGuid) {
32
+ if (!targetEntityGuid) return this.defaultEntity;
33
+ if (!this.appStorageMap.has(targetEntityGuid)) this.appStorageMap.set(targetEntityGuid, new this.StorageClass());
34
+ return this.appStorageMap.get(targetEntityGuid);
35
+ }
36
+
30
37
  // This class must contain an union of all methods from all supported storage classes and conceptualize away the target app argument.
31
38
 
32
39
  /**
40
+ * Calls the isEmpty method on the underlying storage class. If target is provided, runs just for the target, otherwise runs for all apps.
33
41
  * @param {object} optsIfPresent - exists if called during harvest interval, @see AggregateBase.makeHarvestPayload
34
42
  * @param {object} target - specific app's storage to check; if not provided, this method takes into account all apps recorded by this manager
35
43
  * @returns {boolean} True if the target's storage is empty, or target does not exist in map (defaults to all storages)
36
44
  */
37
- isEmpty(optsIfPresent, target) {
38
- if (target) {
39
- if (!this.appStorageMap.has(target)) return true;else return this.appStorageMap.get(target).isEmpty(optsIfPresent);
40
- }
45
+ isEmpty(optsIfPresent, targetEntityGuid) {
46
+ if (targetEntityGuid) return this.#getEventStore(targetEntityGuid).isEmpty(optsIfPresent);
41
47
  for (const eventStore of this.appStorageMap.values()) {
42
48
  if (!eventStore.isEmpty(optsIfPresent)) return false;
43
49
  }
@@ -45,77 +51,96 @@ export class EventStoreManager {
45
51
  }
46
52
 
47
53
  /**
54
+ * Calls the add method on the underlying storage class.
48
55
  * @param {string} event - the event element to store
49
- * @param {object} target - the app to store event under; if not provided, this method adds to the main app from NREUM.info
56
+ * @param {object} targetEntityGuid - the entity guid lookup to store event under; if not provided, this method adds to the default
50
57
  * @returns {boolean} True if the event was successfully added
51
58
  */
52
- add(event, target) {
59
+ add(event, targetEntityGuid) {
53
60
  dispatchGlobalEvent({
54
- agentIdentifier: this.agentIdentifier,
55
- loaded: !!activatedFeatures?.[this.agentIdentifier],
61
+ agentIdentifier: this.agentRef.agentIdentifier,
62
+ drained: !!activatedFeatures?.[this.agentRef.agentIdentifier],
56
63
  type: 'data',
57
64
  name: 'buffer',
58
65
  feature: this.featureName,
59
66
  data: event
60
67
  });
61
- if (target && !this.appStorageMap.has(target)) this.appStorageMap.set(target, new this.StorageClass());
62
- return this.appStorageMap.get(target || this.mainApp).add(event);
68
+ return this.#getEventStore(targetEntityGuid).add(event);
63
69
  }
64
70
 
65
71
  /** This is only used by the Metrics feature which has no need to add metric under a different app atm. */
66
72
  addMetric(type, name, params, value) {
67
- return this.appStorageMap.get(this.mainApp).addMetric(type, name, params, value);
73
+ return this.#getEventStore().addMetric(type, name, params, value);
68
74
  }
69
75
 
70
76
  /**
71
- * @param {object} optsIfPresent - exists if called during harvest interval, @see AggregateBase.makeHarvestPayload
72
- * @param {object} target - specific app to fetch; if not provided, this method fetches from all apps
77
+ * Calls the get method on the underlying storage class. If target is provided, runs just for the target, otherwise runs for all apps.
78
+ * @param {object=} opts - exists if called during harvest interval, @see AggregateBase.makeHarvestPayload
79
+ * @param {object=} target - specific app to fetch; if not provided, this method fetches from all apps
73
80
  * @returns {Array} Objects of `data` labeled with their respective `target` app to be sent to
74
81
  */
75
- get(optsIfPresent, target) {
76
- if (target) return [{
77
- targetApp: target,
78
- data: this.appStorageMap.get(target)?.get(optsIfPresent)
82
+ get(opts, targetEntityGuid) {
83
+ if (targetEntityGuid) return [{
84
+ targetApp: this.entityManager.get(targetEntityGuid),
85
+ data: this.#getEventStore(targetEntityGuid).get(opts)
79
86
  }];
80
87
  const allPayloads = [];
81
- this.appStorageMap.forEach((eventStore, recordedTarget) => {
82
- allPayloads.push({
83
- targetApp: recordedTarget,
84
- data: eventStore.get(optsIfPresent)
88
+ this.appStorageMap.forEach((eventStore, targetEntityGuid) => {
89
+ const targetApp = this.entityManager.get(targetEntityGuid);
90
+ if (targetApp) allPayloads.push({
91
+ targetApp,
92
+ data: eventStore.get(opts)
85
93
  });
86
94
  });
87
95
  return allPayloads;
88
96
  }
89
- byteSize(target) {
90
- return this.appStorageMap.get(target || this.mainApp).byteSize();
97
+
98
+ /**
99
+ * Calls the byteSize method on the underlying storage class
100
+ * @param {*} targetEntityGuid
101
+ * @returns
102
+ */
103
+ byteSize(targetEntityGuid) {
104
+ return this.#getEventStore(targetEntityGuid).byteSize();
91
105
  }
92
- wouldExceedMaxSize(incomingSize, target) {
93
- return this.appStorageMap.get(target || this.mainApp).wouldExceedMaxSize(incomingSize);
106
+
107
+ /**
108
+ * Calls the wouldExceedMaxSize method on the underlying storage class
109
+ * @param {*} incomingSize
110
+ * @param {*} targetEntityGuid
111
+ * @returns
112
+ */
113
+ wouldExceedMaxSize(incomingSize, targetEntityGuid) {
114
+ return this.#getEventStore(targetEntityGuid).wouldExceedMaxSize(incomingSize);
94
115
  }
95
- save(optsIfPresent, target) {
96
- if (target) return this.appStorageMap.get(target)?.save(optsIfPresent);
116
+
117
+ /**
118
+ * Calls the save method on the underlying storage class. If target is provided, runs just for the target, otherwise runs for all apps.
119
+ * @param {*} optsIfPresent
120
+ * @param {*} targetEntityGuid
121
+ * @returns
122
+ */
123
+ save(optsIfPresent, targetEntityGuid) {
124
+ if (targetEntityGuid) return this.#getEventStore(targetEntityGuid).save(optsIfPresent);
97
125
  this.appStorageMap.forEach(eventStore => eventStore.save(optsIfPresent));
98
126
  }
99
- clear(optsIfPresent, target) {
100
- if (target) return this.appStorageMap.get(target)?.clear(optsIfPresent);
127
+
128
+ /**
129
+ * Calls the clear method on the underlying storage class. If target is provided, runs just for the target, otherwise runs for all apps.
130
+ * @param {*} optsIfPresent
131
+ * @param {*} targetEntityGuid
132
+ * @returns
133
+ */
134
+ clear(optsIfPresent, targetEntityGuid) {
135
+ if (targetEntityGuid) return this.#getEventStore(targetEntityGuid).clear(optsIfPresent);
101
136
  this.appStorageMap.forEach(eventStore => eventStore.clear(optsIfPresent));
102
137
  }
103
138
 
104
139
  // Unlike the methods above, the following will have a target as they are called by AggregateBase.postHarvestCleanup callback on harvest finish after getting & sending the data.
105
- reloadSave(optsIfPresent, target) {
106
- if (!target) {
107
- // -- remove this block once the old harvest.js & harvest-schedule.js are deleted!
108
- this.appStorageMap.forEach(eventStore => eventStore.reloadSave(optsIfPresent));
109
- return;
110
- }
111
- return this.appStorageMap.get(target)?.reloadSave(optsIfPresent);
140
+ reloadSave(optsIfPresent, targetEntityGuid) {
141
+ return this.#getEventStore(targetEntityGuid).reloadSave(optsIfPresent);
112
142
  }
113
- clearSave(optsIfPresent, target) {
114
- if (!target) {
115
- // -- remove this block once the old harvest.js & harvest-schedule.js are deleted!
116
- this.appStorageMap.forEach(eventStore => eventStore.clearSave(optsIfPresent));
117
- return;
118
- }
119
- return this.appStorageMap.get(target)?.clearSave(optsIfPresent);
143
+ clearSave(optsIfPresent, targetEntityGuid) {
144
+ return this.#getEventStore(targetEntityGuid).clearSave(optsIfPresent);
120
145
  }
121
146
  }
@@ -6,7 +6,7 @@ import { gosCDN } from '../../common/window/nreum';
6
6
  const debugId = 1;
7
7
  const newrelic = gosCDN();
8
8
  export function debugNR1(agentIdentifier, location, event, otherprops = {}, debugName = 'SR') {
9
- const api = agentIdentifier ? newrelic.initializedAgents[agentIdentifier].api.addPageAction : newrelic.addPageAction;
9
+ const api = agentIdentifier ? newrelic.initializedAgents[agentIdentifier].addPageAction : newrelic.addPageAction;
10
10
  let url;
11
11
  try {
12
12
  const locURL = new URL(window.location);
@@ -0,0 +1,107 @@
1
+ /**
2
+ * Copyright 2020-2025 New Relic, Inc. All rights reserved.
3
+ * SPDX-License-Identifier: Apache-2.0
4
+ */
5
+ import { warn } from '../common/util/console';
6
+
7
+ /**
8
+ * @typedef {import('../loaders/api/register-api-types').RegisterAPI} RegisterAPI
9
+ * @typedef {import('../loaders/api/register-api-types').RegisterAPIMetadata} RegisterAPIMetadata
10
+ * @typedef {import('../loaders/api/register-api-types').RegisterAPIConstructor} RegisterAPIConstructor
11
+ */
12
+
13
+ /**
14
+ * @experimental
15
+ * IMPORTANT: This feature is being developed for use internally and is not in a public-facing production-ready state.
16
+ * It is not recommended for use in production environments and will not receive support for issues.
17
+ *
18
+ * An interface for registering an external caller to report through the base agent to a different target than the base agent.
19
+ */
20
+ export class RegisteredEntity {
21
+ /** @type {RegisterAPIMetadata} */
22
+ metadata = {
23
+ target: {},
24
+ customAttributes: {}
25
+ };
26
+
27
+ /**
28
+ *
29
+ * @param {RegisterAPIConstructor} opts The options for setting up the registered entity.
30
+ */
31
+ constructor(opts) {
32
+ try {
33
+ if (!window?.newrelic) return warn(51);
34
+ Object.assign(this, window?.newrelic?.register(opts) || {});
35
+ } catch (err) {
36
+ warn(50, err);
37
+ }
38
+ }
39
+
40
+ /**
41
+ * Reports a browser PageAction event along with a name and optional attributes to the registered target.
42
+ * {@link https://docs.newrelic.com/docs/browser/new-relic-browser/browser-apis/addpageaction/}
43
+ * @param {string} name Name or category of the action. Reported as the actionName attribute.
44
+ * @param {object} [attributes] JSON object with one or more key/value pairs. For example: {key:"value"}. The key is reported as its own PageAction attribute with the specified values.
45
+ */
46
+ addPageAction(name, attributes) {
47
+ /** this method will be overset once register is successful */
48
+ warn(35, 'addPageAction');
49
+ }
50
+
51
+ /**
52
+ * Adds a user-defined attribute name and value to subsequent events on the page for the registered target. Note -- the persist flag does not work with the register API.
53
+ * {@link https://docs.newrelic.com/docs/browser/new-relic-browser/browser-apis/setcustomattribute/}
54
+ * @param {string} name Name of the attribute. Appears as column in the PageView event. It will also appear as a column in the PageAction event if you are using it.
55
+ * @param {string|number|boolean|null} value Value of the attribute. Appears as the value in the named attribute column in the PageView event. It will appear as a column in the PageAction event if you are using it. Custom attribute values cannot be complex objects, only simple types such as Strings, Integers and Booleans. Passing a null value unsets any existing attribute of the same name.
56
+ * @param {boolean} [persist] Default false. If set to true, the name-value pair will also be set into the browser's storage API. Then on the following instrumented pages that load within the same session, the pair will be re-applied as a custom attribute.
57
+ */
58
+ setCustomAttribute(name, value, persist) {
59
+ /** this method will be overset once register is successful */
60
+ warn(35, 'setCustomAttribute');
61
+ }
62
+
63
+ /**
64
+ * Identifies a browser error without disrupting your app's operations for the registered target.
65
+ * {@link https://docs.newrelic.com/docs/browser/new-relic-browser/browser-apis/noticeerror/}
66
+ * @param {Error|string} error Provide a meaningful error message that you can use when analyzing data on browser's JavaScript errors page.
67
+ * @param {object} [customAttributes] An object containing name/value pairs representing custom attributes.
68
+ */
69
+ noticeError(error, customAttributes) {
70
+ /** this method will be overset once register is successful */
71
+ warn(35, 'noticeError');
72
+ }
73
+
74
+ /**
75
+ * Adds a user-defined identifier string to subsequent events on the page for the registered taret.
76
+ * {@link https://docs.newrelic.com/docs/browser/new-relic-browser/browser-apis/setuserid/}
77
+ * @param {string|null} value A string identifier for the end-user, useful for tying all browser events to specific users. The value parameter does not have to be unique. If IDs should be unique, the caller is responsible for that validation. Passing a null value unsets any existing user ID.
78
+ */
79
+ setUserId(value) {
80
+ /** this method will be overset once register is successful */
81
+ warn(35, 'setUserId');
82
+ }
83
+
84
+ /**
85
+ * Adds a user-defined application version string to subsequent events on the page for the registered target.
86
+ * This decorates all payloads with an attribute of `application.version` which is queryable in NR1.
87
+ * {@link https://docs.newrelic.com/docs/browser/new-relic-browser/browser-apis/setapplicationversion/}
88
+ * @param {string|null} value A string identifier for the application version, useful for
89
+ * tying all browser events to a specific release tag. The value parameter does not
90
+ * have to be unique. Passing a null value unsets any existing value.
91
+ */
92
+ setApplicationVersion(value) {
93
+ /** this method will be overset once register is successful */
94
+ warn(35, 'setApplicationVersion');
95
+ }
96
+
97
+ /**
98
+ * Capture a single log for the registered target.
99
+ * {@link https://docs.newrelic.com/docs/browser/new-relic-browser/browser-apis/log/}
100
+ * @param {string} message String to be captured as log message
101
+ * @param {{customAttributes?: object, level?: 'ERROR'|'TRACE'|'DEBUG'|'INFO'|'WARN'}} [options] customAttributes defaults to `{}` if not assigned, level defaults to `info` if not assigned.
102
+ */
103
+ log(message, options) {
104
+ /** this method will be overset once register is successful */
105
+ warn(35, 'setCustomAttribute');
106
+ }
107
+ }
@@ -20,7 +20,7 @@ export class AgentBase extends MicroAgentBase {
20
20
  * @param {...any} args
21
21
  */
22
22
  #callMethod(methodName, ...args) {
23
- if (typeof this.api?.[methodName] !== 'function') warn(35, methodName);else return this.api[methodName](...args);
23
+ if (this[methodName] === AgentBase.prototype[methodName] || this[methodName] === MicroAgentBase.prototype[methodName]) warn(35, methodName);else return this[methodName](...args);
24
24
  }
25
25
 
26
26
  /**
@@ -67,6 +67,9 @@ export class Agent extends AgentBase {
67
67
  runtime: this.runtime
68
68
  };
69
69
  }
70
+ get api() {
71
+ return this;
72
+ }
70
73
  run() {
71
74
  // Attempt to initialize all the requested features (sequentially in prio order & synchronously), with any failure aborting the whole process.
72
75
  try {
@@ -94,7 +97,6 @@ export class Agent extends AgentBase {
94
97
  this.features[featName].abortHandler?.();
95
98
  }
96
99
  const newrelic = gosNREUM();
97
- delete newrelic.initializedAgents[this.agentIdentifier]?.api; // prevent further calls to agent-specific APIs (see "configure.js")
98
100
  delete newrelic.initializedAgents[this.agentIdentifier]?.features; // GC mem used internally by features
99
101
  delete this.sharedAggregator;
100
102
  // Keep the initialized agent object with its configs for troubleshooting purposes.
@@ -3,5 +3,5 @@
3
3
  * SPDX-License-Identifier: Apache-2.0
4
4
  */
5
5
  import { SR_EVENT_EMITTER_TYPES } from '../../features/session_replay/constants';
6
- export const apiMethods = ['setErrorHandler', 'finished', 'addToTrace', 'addRelease', 'recordCustomEvent', 'addPageAction', 'setCurrentRouteName', 'setPageViewName', 'setCustomAttribute', 'interaction', 'noticeError', 'setUserId', 'setApplicationVersion', 'start', SR_EVENT_EMITTER_TYPES.RECORD, SR_EVENT_EMITTER_TYPES.PAUSE, 'log', 'wrapLogger'];
6
+ export const apiMethods = ['setErrorHandler', 'finished', 'addToTrace', 'addRelease', 'recordCustomEvent', 'addPageAction', 'setCurrentRouteName', 'setPageViewName', 'setCustomAttribute', 'interaction', 'noticeError', 'setUserId', 'setApplicationVersion', 'start', SR_EVENT_EMITTER_TYPES.RECORD, SR_EVENT_EMITTER_TYPES.PAUSE, 'log', 'wrapLogger', 'register'];
7
7
  export const asyncApiMethods = ['setErrorHandler', 'finished', 'addToTrace', 'addRelease'];