@newrelic/browser-agent 1.301.0 → 1.302.0-rc.1

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 (142) hide show
  1. package/CHANGELOG.md +13 -0
  2. package/dist/cjs/common/config/init-types.js +1 -1
  3. package/dist/cjs/common/config/init.js +7 -1
  4. package/dist/cjs/common/config/runtime.js +1 -1
  5. package/dist/cjs/common/constants/agent-constants.js +11 -2
  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/drain/drain.js +1 -1
  9. package/dist/cjs/common/harvest/harvester.js +30 -39
  10. package/dist/cjs/common/util/mfe.js +45 -0
  11. package/dist/cjs/features/ajax/aggregate/index.js +5 -1
  12. package/dist/cjs/features/generic_events/aggregate/index.js +9 -8
  13. package/dist/cjs/features/generic_events/constants.js +3 -1
  14. package/dist/cjs/features/generic_events/instrument/index.js +38 -32
  15. package/dist/cjs/features/jserrors/aggregate/index.js +18 -17
  16. package/dist/cjs/features/logging/aggregate/index.js +19 -15
  17. package/dist/cjs/features/logging/shared/utils.js +3 -3
  18. package/dist/cjs/features/page_view_event/aggregate/index.js +3 -33
  19. package/dist/cjs/features/session_replay/aggregate/index.js +13 -13
  20. package/dist/cjs/features/session_replay/shared/recorder.js +3 -2
  21. package/dist/cjs/features/session_trace/aggregate/index.js +0 -2
  22. package/dist/cjs/features/soft_navigations/aggregate/index.js +1 -2
  23. package/dist/cjs/features/utils/aggregate-base.js +45 -47
  24. package/dist/cjs/loaders/api/addPageAction.js +2 -2
  25. package/dist/cjs/loaders/api/log.js +2 -2
  26. package/dist/cjs/loaders/api/noticeError.js +2 -2
  27. package/dist/cjs/loaders/api/register-api-types.js +5 -5
  28. package/dist/cjs/loaders/api/register.js +80 -97
  29. package/dist/esm/common/config/init-types.js +1 -1
  30. package/dist/esm/common/config/init.js +7 -1
  31. package/dist/esm/common/config/runtime.js +1 -1
  32. package/dist/esm/common/constants/agent-constants.js +9 -1
  33. package/dist/esm/common/constants/env.cdn.js +1 -1
  34. package/dist/esm/common/constants/env.npm.js +1 -1
  35. package/dist/esm/common/drain/drain.js +1 -1
  36. package/dist/esm/common/harvest/harvester.js +30 -39
  37. package/dist/esm/common/util/mfe.js +38 -0
  38. package/dist/esm/features/ajax/aggregate/index.js +5 -1
  39. package/dist/esm/features/generic_events/aggregate/index.js +9 -8
  40. package/dist/esm/features/generic_events/constants.js +3 -1
  41. package/dist/esm/features/generic_events/instrument/index.js +38 -32
  42. package/dist/esm/features/jserrors/aggregate/index.js +18 -17
  43. package/dist/esm/features/logging/aggregate/index.js +19 -15
  44. package/dist/esm/features/logging/shared/utils.js +3 -3
  45. package/dist/esm/features/page_view_event/aggregate/index.js +3 -33
  46. package/dist/esm/features/session_replay/aggregate/index.js +13 -13
  47. package/dist/esm/features/session_replay/shared/recorder.js +3 -2
  48. package/dist/esm/features/session_trace/aggregate/index.js +0 -2
  49. package/dist/esm/features/soft_navigations/aggregate/index.js +1 -2
  50. package/dist/esm/features/utils/aggregate-base.js +46 -48
  51. package/dist/esm/loaders/api/addPageAction.js +2 -2
  52. package/dist/esm/loaders/api/log.js +2 -2
  53. package/dist/esm/loaders/api/noticeError.js +2 -2
  54. package/dist/esm/loaders/api/register-api-types.js +5 -5
  55. package/dist/esm/loaders/api/register.js +80 -97
  56. package/dist/tsconfig.tsbuildinfo +1 -1
  57. package/dist/types/common/config/init-types.d.ts +4 -1
  58. package/dist/types/common/config/init-types.d.ts.map +1 -1
  59. package/dist/types/common/config/init.d.ts.map +1 -1
  60. package/dist/types/common/constants/agent-constants.d.ts +7 -4
  61. package/dist/types/common/constants/agent-constants.d.ts.map +1 -1
  62. package/dist/types/common/harvest/harvester.d.ts +2 -2
  63. package/dist/types/common/harvest/harvester.d.ts.map +1 -1
  64. package/dist/types/common/util/mfe.d.ts +20 -0
  65. package/dist/types/common/util/mfe.d.ts.map +1 -0
  66. package/dist/types/features/ajax/aggregate/index.d.ts.map +1 -1
  67. package/dist/types/features/generic_events/aggregate/index.d.ts +2 -2
  68. package/dist/types/features/generic_events/aggregate/index.d.ts.map +1 -1
  69. package/dist/types/features/generic_events/constants.d.ts +1 -0
  70. package/dist/types/features/generic_events/instrument/index.d.ts.map +1 -1
  71. package/dist/types/features/jserrors/aggregate/index.d.ts +3 -3
  72. package/dist/types/features/jserrors/aggregate/index.d.ts.map +1 -1
  73. package/dist/types/features/logging/aggregate/index.d.ts +3 -3
  74. package/dist/types/features/logging/aggregate/index.d.ts.map +1 -1
  75. package/dist/types/features/logging/shared/utils.d.ts +2 -2
  76. package/dist/types/features/logging/shared/utils.d.ts.map +1 -1
  77. package/dist/types/features/page_view_event/aggregate/index.d.ts +2 -4
  78. package/dist/types/features/page_view_event/aggregate/index.d.ts.map +1 -1
  79. package/dist/types/features/session_replay/aggregate/index.d.ts +13 -4
  80. package/dist/types/features/session_replay/aggregate/index.d.ts.map +1 -1
  81. package/dist/types/features/session_replay/shared/recorder.d.ts +1 -0
  82. package/dist/types/features/session_replay/shared/recorder.d.ts.map +1 -1
  83. package/dist/types/features/session_trace/aggregate/index.d.ts.map +1 -1
  84. package/dist/types/features/soft_navigations/aggregate/index.d.ts.map +1 -1
  85. package/dist/types/features/utils/aggregate-base.d.ts +13 -5
  86. package/dist/types/features/utils/aggregate-base.d.ts.map +1 -1
  87. package/dist/types/loaders/api/addPageAction.d.ts +1 -1
  88. package/dist/types/loaders/api/addPageAction.d.ts.map +1 -1
  89. package/dist/types/loaders/api/log.d.ts +1 -1
  90. package/dist/types/loaders/api/log.d.ts.map +1 -1
  91. package/dist/types/loaders/api/noticeError.d.ts +1 -1
  92. package/dist/types/loaders/api/noticeError.d.ts.map +1 -1
  93. package/dist/types/loaders/api/register-api-types.d.ts +4 -4
  94. package/dist/types/loaders/api/register-api-types.d.ts.map +1 -1
  95. package/dist/types/loaders/api/register.d.ts +8 -1
  96. package/dist/types/loaders/api/register.d.ts.map +1 -1
  97. package/package.json +2 -2
  98. package/src/common/config/init-types.js +1 -1
  99. package/src/common/config/init.js +3 -1
  100. package/src/common/config/runtime.js +1 -1
  101. package/src/common/constants/agent-constants.js +10 -0
  102. package/src/common/drain/drain.js +1 -1
  103. package/src/common/harvest/harvester.js +27 -32
  104. package/src/common/util/mfe.js +35 -0
  105. package/src/features/ajax/aggregate/index.js +7 -1
  106. package/src/features/generic_events/aggregate/index.js +9 -8
  107. package/src/features/generic_events/constants.js +3 -1
  108. package/src/features/generic_events/instrument/index.js +44 -35
  109. package/src/features/jserrors/aggregate/index.js +17 -17
  110. package/src/features/logging/aggregate/index.js +20 -13
  111. package/src/features/logging/shared/utils.js +3 -3
  112. package/src/features/page_view_event/aggregate/index.js +3 -28
  113. package/src/features/session_replay/aggregate/index.js +12 -10
  114. package/src/features/session_replay/shared/recorder.js +3 -2
  115. package/src/features/session_trace/aggregate/index.js +0 -2
  116. package/src/features/soft_navigations/aggregate/index.js +1 -2
  117. package/src/features/utils/aggregate-base.js +47 -42
  118. package/src/loaders/api/addPageAction.js +2 -2
  119. package/src/loaders/api/log.js +2 -2
  120. package/src/loaders/api/noticeError.js +2 -2
  121. package/src/loaders/api/register-api-types.js +5 -5
  122. package/src/loaders/api/register.js +62 -89
  123. package/dist/cjs/common/util/target.js +0 -34
  124. package/dist/cjs/features/utils/entity-manager.js +0 -46
  125. package/dist/cjs/features/utils/event-store-manager.js +0 -174
  126. package/dist/cjs/loaders/api/register-api.js +0 -165
  127. package/dist/esm/common/util/target.js +0 -27
  128. package/dist/esm/features/utils/entity-manager.js +0 -39
  129. package/dist/esm/features/utils/event-store-manager.js +0 -166
  130. package/dist/esm/loaders/api/register-api.js +0 -159
  131. package/dist/types/common/util/target.d.ts +0 -18
  132. package/dist/types/common/util/target.d.ts.map +0 -1
  133. package/dist/types/features/utils/entity-manager.d.ts +0 -11
  134. package/dist/types/features/utils/entity-manager.d.ts.map +0 -1
  135. package/dist/types/features/utils/event-store-manager.d.ts +0 -85
  136. package/dist/types/features/utils/event-store-manager.d.ts.map +0 -1
  137. package/dist/types/loaders/api/register-api.d.ts +0 -14
  138. package/dist/types/loaders/api/register-api.d.ts.map +0 -1
  139. package/src/common/util/target.js +0 -27
  140. package/src/features/utils/entity-manager.js +0 -45
  141. package/src/features/utils/event-store-manager.js +0 -165
  142. package/src/loaders/api/register-api.js +0 -152
@@ -10,15 +10,12 @@ import { drain } from '../../common/drain/drain'
10
10
  import { activatedFeatures } from '../../common/util/feature-flags'
11
11
  import { Obfuscator } from '../../common/util/obfuscate'
12
12
  import { FEATURE_NAMES } from '../../loaders/features/features'
13
- import { EventStoreManager } from './event-store-manager'
14
13
  import { Harvester } from '../../common/harvest/harvester'
15
- import { warn } from '../../common/util/console'
16
- import { EntityManager } from './entity-manager'
17
14
  import { EventBuffer } from './event-buffer'
18
15
  import { handle } from '../../common/event-emitter/handle'
19
16
  import { SUPPORTABILITY_METRIC_CHANNEL } from '../metrics/constants'
20
17
  import { EventAggregator } from '../../common/aggregate/event-aggregator'
21
- import { IDEAL_PAYLOAD_SIZE } from '../../common/constants/agent-constants'
18
+ import { MAX_PAYLOAD_SIZE, IDEAL_PAYLOAD_SIZE, SUPPORTS_REGISTERED_ENTITIES } from '../../common/constants/agent-constants'
22
19
 
23
20
  export class AggregateBase extends FeatureBase {
24
21
  /**
@@ -41,16 +38,9 @@ export class AggregateBase extends FeatureBase {
41
38
 
42
39
  this.harvestOpts = {} // features aggregate classes can define custom opts for when their harvest is called
43
40
 
44
- const agentEntityGuid = this.agentRef?.runtime?.appMetadata?.agents?.[0]?.entityGuid
45
- this.#setupEventStore(agentEntityGuid)
46
- if (!agentEntityGuid) {
47
- /** wait for the entity guid from the rum response and use to it to further configure things to set the default entity to share an indexed entity with entityGuid */
48
- this.ee.on('entity-added', entity => {
49
- // not all event managers have this fn, like ST and SR
50
- // this allows the lookup to work for the default and an entityGuid without creating two separate buffers
51
- this.events?.setEventStore?.(entity.entityGuid)
52
- })
53
- }
41
+ this.#setupEventStore()
42
+
43
+ this.waitForDrain()
54
44
  }
55
45
 
56
46
  /**
@@ -58,7 +48,7 @@ export class AggregateBase extends FeatureBase {
58
48
  * @param {string} entityGuid
59
49
  * @returns {void}
60
50
  */
61
- #setupEventStore (entityGuid) {
51
+ #setupEventStore () {
62
52
  if (this.events) return
63
53
  switch (this.featureName) {
64
54
  // SessionReplay has its own storage mechanisms.
@@ -67,17 +57,40 @@ export class AggregateBase extends FeatureBase {
67
57
  // Jserror and Metric features uses a singleton EventAggregator instead of a regular EventBuffer.
68
58
  case FEATURE_NAMES.jserrors:
69
59
  case FEATURE_NAMES.metrics:
70
- this.events = this.agentRef.sharedAggregator ??= new EventStoreManager(this.agentRef, EventAggregator, entityGuid, { featureName: 'shared_aggregator' })
60
+ this.events = this.agentRef.sharedAggregator ??= new EventAggregator()
71
61
  break
72
- /** 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.
62
+ /** All other features get EventBuffer 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.
73
63
  This was necessary to prevent race cond. issues where the event buffer was checked before the feature could "block" itself.
74
64
  Its easier to just keep an empty event buffer in place. */
75
65
  default:
76
- this.events = new EventStoreManager(this.agentRef, EventBuffer, entityGuid, this)
66
+ this.events = new EventBuffer(MAX_PAYLOAD_SIZE, this)
77
67
  break
78
68
  }
79
69
  }
80
70
 
71
+ /** @type {Boolean} indicates if the feature supports registered entities and the harvest requirements therein. Also read by getter "harvestEndpointVersion". Controlled by feature flag in pre-release phase. */
72
+ get supportsRegisteredEntities () {
73
+ return this.featureName in SUPPORTS_REGISTERED_ENTITIES && (SUPPORTS_REGISTERED_ENTITIES[this.featureName] || this.agentRef.init.feature_flags.includes('register.' + this.featureName))
74
+ }
75
+
76
+ /**
77
+ * the endpoint version the feature uses during harvests
78
+ * @type {number}
79
+ * @returns {boolean}
80
+ */
81
+ get harvestEndpointVersion () {
82
+ return (this.supportsRegisteredEntities && !!this.agentRef.runtime.registeredEntities.length) ? 2 : 1
83
+ }
84
+
85
+ waitForDrain () {
86
+ /** emitted when the feature successfully drains */
87
+ this.ee.on('drain-' + this.featureName, () => {
88
+ /** make an immediate harvest for all the features to help with harvestability for pre-load dervied data on short lived pages */
89
+ if (!this.drained) setTimeout(() => this.agentRef.runtime.harvester.triggerHarvestFor(this), 1)
90
+ this.drained = true
91
+ })
92
+ }
93
+
81
94
  /**
82
95
  * Evaluates whether a harvest should be made early by estimating the size of the current payload. Currently, this only happens if the event storage is EventBuffer, since that triggers this method directly.
83
96
  * If conditions are met, a new harvest will be triggered immediately.
@@ -125,7 +138,6 @@ export class AggregateBase extends FeatureBase {
125
138
  */
126
139
  drain () {
127
140
  drain(this.agentIdentifier, this.featureName)
128
- this.drained = true
129
141
  }
130
142
 
131
143
  preHarvestChecks (opts) {
@@ -136,44 +148,39 @@ export class AggregateBase extends FeatureBase {
136
148
  * Return harvest payload. A "serializer" function can be defined on a derived class to format the payload.
137
149
  * @param {Boolean} shouldRetryOnFail - harvester flag to backup payload for retry later if harvest request fails; this should be moved to harvester logic
138
150
  * @param {object|undefined} opts - opts passed from the harvester to help form the payload
139
- * @param {string} opts.targetEntityGuid - the entity guid of the target app
151
+ * @param {string} opts.target - the target app metadata
140
152
  * @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.
141
153
  */
142
154
  makeHarvestPayload (shouldRetryOnFail = false, opts = {}) {
143
- if (!this.events || this.events.isEmpty(this.harvestOpts, opts.targetEntityGuid)) return
155
+ if (!this.events || this.events.isEmpty(this.harvestOpts)) return
144
156
  // Other conditions and things to do when preparing harvest that is required.
145
157
  if (this.preHarvestChecks && !this.preHarvestChecks(opts)) return
146
158
 
147
- if (shouldRetryOnFail) this.events.save(this.harvestOpts, opts.targetEntityGuid)
148
- const returnedDataArr = this.events.get(this.harvestOpts, opts.targetEntityGuid)
149
- if (!returnedDataArr.length) return warn(52)
150
- this.events.clear(this.harvestOpts, opts.targetEntityGuid)
151
-
152
- return returnedDataArr.map(({ targetApp, data }) => {
153
- // A serializer or formatter assists in creating the payload `body` from stored events on harvest when defined by derived feature class.
154
- const body = this.serializer ? this.serializer(data, targetApp?.entityGuid) : data
155
- const payload = {
156
- body
157
- }
158
- // Constructs the payload `qs` for relevant features on harvest.
159
- if (this.queryStringsBuilder) payload.qs = this.queryStringsBuilder(data, targetApp?.entityGuid)
159
+ if (shouldRetryOnFail) this.events.save(this.harvestOpts)
160
+ const data = this.events.get(this.harvestOpts)
161
+ if (!data) return
162
+ this.events.clear(this.harvestOpts)
160
163
 
161
- return { targetApp, payload }
162
- })
164
+ // A serializer or formatter assists in creating the payload `body` from stored events on harvest when defined by derived feature class.
165
+ const body = this.serializer ? this.serializer(data) : data
166
+ const payload = {
167
+ body
168
+ }
169
+ // Constructs the payload `qs` for relevant features on harvest.
170
+ if (this.queryStringsBuilder) payload.qs = this.queryStringsBuilder(data)
171
+ return payload
163
172
  }
164
173
 
165
174
  /**
166
175
  * Cleanup task after a harvest.
167
176
  * @param {object} result - the cbResult object from the harvester's send method
168
- * @param {object=} result.targetApp - the target app object that was used to point the harvest to the correct app
169
- * @param {string=} result.targetApp.entityGuid - the entity guid of the target app
170
177
  * @param {boolean=} result.sent - whether the harvest was sent successfully
171
178
  * @param {boolean=} result.retry - whether the harvest should be retried
172
179
  */
173
180
  postHarvestCleanup (result = {}) {
174
181
  this.isRetrying = result.sent && result.retry
175
- if (this.isRetrying) this.events.reloadSave(this.harvestOpts, result.targetApp?.entityGuid)
176
- this.events.clearSave(this.harvestOpts, result.targetApp?.entityGuid)
182
+ if (this.isRetrying) this.events.reloadSave(this.harvestOpts)
183
+ this.events.clearSave(this.harvestOpts)
177
184
  }
178
185
 
179
186
  /**
@@ -212,8 +219,6 @@ export class AggregateBase extends FeatureBase {
212
219
  if (!agentRef.runtime.obfuscator) agentRef.runtime.obfuscator = new Obfuscator(agentRef)
213
220
  this.obfuscator = agentRef.runtime.obfuscator
214
221
 
215
- if (!agentRef.runtime.entityManager) agentRef.runtime.entityManager = new EntityManager(this.agentRef)
216
-
217
222
  if (!agentRef.runtime.harvester) agentRef.runtime.harvester = new Harvester(agentRef)
218
223
  }
219
224
 
@@ -12,6 +12,6 @@ export function setupAddPageActionAPI (agent) {
12
12
  setupAPI(ADD_PAGE_ACTION, (name, attributes) => addPageAction(name, attributes, agent), agent)
13
13
  }
14
14
 
15
- export function addPageAction (name, attributes, agentRef, targetEntityGuid, timestamp = now()) {
16
- handle(prefix + ADD_PAGE_ACTION, [timestamp, name, attributes, targetEntityGuid], undefined, FEATURE_NAMES.genericEvents, agentRef.ee)
15
+ export function addPageAction (name, attributes, agentRef, target, timestamp = now()) {
16
+ handle(prefix + ADD_PAGE_ACTION, [timestamp, name, attributes, target], undefined, FEATURE_NAMES.genericEvents, agentRef.ee)
17
17
  }
@@ -13,6 +13,6 @@ export function setupLogAPI (agent) {
13
13
  setupAPI(LOG, (message, options) => log(message, options, agent), agent)
14
14
  }
15
15
 
16
- export function log (message, { customAttributes = {}, level = LOG_LEVELS.INFO } = {}, agentRef, targetEntityGuid, timestamp = now()) {
17
- bufferLog(agentRef.ee, message, customAttributes, level, targetEntityGuid, timestamp)
16
+ export function log (message, { customAttributes = {}, level = LOG_LEVELS.INFO } = {}, agentRef, target, timestamp = now()) {
17
+ bufferLog(agentRef.ee, message, customAttributes, level, target, timestamp)
18
18
  }
@@ -12,8 +12,8 @@ export function setupNoticeErrorAPI (agent) {
12
12
  setupAPI(NOTICE_ERROR, (err, customAttributes) => noticeError(err, customAttributes, agent), agent)
13
13
  }
14
14
 
15
- export function noticeError (err, customAttributes, agentRef, targetEntityGuid, timestamp = now()) {
15
+ export function noticeError (err, customAttributes, agentRef, target, timestamp = now()) {
16
16
  if (typeof err === 'string') err = new Error(err)
17
- handle('err', [err, timestamp, false, customAttributes, agentRef.runtime.isRecording, undefined, targetEntityGuid], undefined, FEATURE_NAMES.jserrors, agentRef.ee)
17
+ handle('err', [err, timestamp, false, customAttributes, agentRef.runtime.isRecording, undefined, target], undefined, FEATURE_NAMES.jserrors, agentRef.ee)
18
18
  handle('uaErr', [], undefined, FEATURE_NAMES.genericEvents, agentRef.ee)
19
19
  }
@@ -17,17 +17,17 @@
17
17
  /**
18
18
  * @typedef {Object} RegisterAPIConstructor
19
19
  * @property {Object} opts - The options for the registered entity.
20
- * @property {string} opts.licenseKey - The license key for the registered entity.
21
- * @property {string} opts.applicationID - The application ID for the registered entity.
20
+ * @property {string} opts.id - The unique id for the registered entity. This will be assigned to any synthesized entities.
21
+ * @property {string} opts.name - The readable name for the registered entity. This will be assigned to any synthesized entities.
22
22
  */
23
23
 
24
24
  /**
25
25
  * @typedef {Object} RegisterAPIMetadata
26
26
  * @property {Object} customAttributes - The custom attributes for the registered entity.
27
27
  * @property {Object} target - The options for the registered entity.
28
- * @property {string} target.licenseKey - The license key for the registered entity.
29
- * @property {string} target.applicationID - The application ID for the registered entity.
30
- * @property {string} target.entityGuid - The entity guid returned for the registered entity.
28
+ * @property {string} target.licenseKey - The license key for the registered entity. If none was supplied, it will assume the license key from the main agent.
29
+ * @property {string} target.id - The ID for the registered entity.
30
+ * @property {string} target.name - The name returned for the registered entity.
31
31
  */
32
32
 
33
33
  export default {}
@@ -4,7 +4,7 @@
4
4
  */
5
5
  import { handle } from '../../common/event-emitter/handle'
6
6
  import { warn } from '../../common/util/console'
7
- import { isContainerAgentTarget, isValidTarget } from '../../common/util/target'
7
+ import { isValidMFETarget } from '../../common/util/mfe'
8
8
  import { FEATURE_NAMES } from '../features/features'
9
9
  import { now } from '../../common/timing/now'
10
10
  import { SUPPORTABILITY_METRIC_CHANNEL } from '../../features/metrics/constants'
@@ -13,6 +13,7 @@ import { REGISTER } from './constants'
13
13
  import { log } from './log'
14
14
  import { addPageAction } from './addPageAction'
15
15
  import { noticeError } from './noticeError'
16
+ import { single } from '../../common/util/invoke'
16
17
 
17
18
  /**
18
19
  * @typedef {import('./register-api-types').RegisterAPI} RegisterAPI
@@ -35,106 +36,80 @@ export function setupRegisterAPI (agent) {
35
36
  * @param {Object} agentRef the reference to the base agent instance
36
37
  * @param {Object} handlers the shared handlers to be used by both the base agent's API and the external target's API
37
38
  * @param {Object} target the target information to be used by the external target's API to send data to the correct location
39
+ * @param {string} [target.licenseKey] the license key of the target to report data to
40
+ * @param {string} target.id the entity ID of the target to report data to
41
+ * @param {string} target.name the entity name of the target to report data to
38
42
  * @returns {RegisterAPI} the api object to be returned from the register api method
39
43
  */
40
44
  export function buildRegisterApi (agentRef, target) {
41
45
  const attrs = {}
42
46
  warn(54, 'newrelic.register')
43
47
 
44
- /** @type {Function|undefined} a function that is set and reports when APIs are triggered -- warns the customer of the invalid state */
45
- let invalidApiResponse
48
+ target ||= {}
49
+ target.licenseKey ||= agentRef.info.licenseKey // will inherit the license key from the container agent if not provided for brevity. A future state may dictate that we need different license keys to do different things.
50
+ target.blocked = false
51
+
52
+ /** @type {Function} a function that is set and reports when APIs are triggered -- warns the customer of the invalid state */
53
+ let invalidApiResponse = () => {}
54
+ /** @type {Array} the array of registered target APIs */
55
+ const registeredEntities = agentRef.runtime.registeredEntities
56
+
57
+ /** if we have already registered this target, go ahead and re-use it */
58
+ const preregisteredEntity = registeredEntities.find(({ metadata: { target: { id, name } } }) => id === target.id)
59
+ if (preregisteredEntity) {
60
+ if (preregisteredEntity.metadata.target.name !== target.name) preregisteredEntity.metadata.target.name = target.name
61
+ return preregisteredEntity
62
+ }
46
63
 
47
64
  /**
48
- * A promise that indicates when all needed connections for the registered child to be ready to report data
49
- * 1. The main agent to be ready (made a RUM call and got its entity guid)
50
- * 2. The child to be registered with the main agent (made its own RUM call and got its entity guid)
51
- * @type {Promise<RegisterAPI>}
52
- */
53
- let _connected
54
- if (!agentRef.init.api.allow_registered_children) invalidApiResponse = () => warn(55)
55
- if (!target || !isValidTarget(target)) invalidApiResponse = () => warn(48, target)
65
+ * Block the API, and supply a warning function to display a message to end users
66
+ * @param {Function} warning
67
+ */
68
+ const block = (warning) => {
69
+ target.blocked = true
70
+ invalidApiResponse = warning
71
+ }
72
+
73
+ /** primary cases that can block the register API from working at init time */
74
+ if (!agentRef.init.api.allow_registered_children) block(single(() => warn(55)))
75
+ if (!isValidMFETarget(target)) block(single(() => warn(48, target)))
56
76
 
57
77
  /** @type {RegisterAPI} */
58
78
  const api = {
59
- addPageAction: (name, attributes = {}) => {
60
- report(addPageAction, [name, { ...attrs, ...attributes }, agentRef], target)
61
- },
62
- log: (message, options = {}) => {
63
- report(log, [message, { ...options, customAttributes: { ...attrs, ...(options.customAttributes || {}) } }, agentRef], target)
64
- },
65
- noticeError: (error, attributes = {}) => {
66
- report(noticeError, [error, { ...attrs, ...attributes }, agentRef], target)
67
- },
68
- setApplicationVersion: (value) => {
69
- attrs['application.version'] = value
70
- },
71
- setCustomAttribute: (key, value) => {
72
- attrs[key] = value
73
- },
74
- setUserId: (value) => {
75
- attrs['enduser.id'] = value
76
- },
79
+ addPageAction: (name, attributes = {}) => report(addPageAction, [name, { ...attrs, ...attributes }, agentRef], target),
80
+ log: (message, options = {}) => report(log, [message, { ...options, customAttributes: { ...attrs, ...(options.customAttributes || {}) } }, agentRef], target),
81
+ noticeError: (error, attributes = {}) => report(noticeError, [error, { ...attrs, ...attributes }, agentRef], target),
82
+ setApplicationVersion: (value) => setLocalValue('application.version', value),
83
+ setCustomAttribute: (key, value) => setLocalValue(key, value),
84
+ setUserId: (value) => setLocalValue('enduser.id', value),
77
85
  /** metadata */
78
86
  metadata: {
79
87
  customAttributes: attrs,
80
- target,
81
- /** set in a getter so that later access of the Promise is not polluted before customer is allowed to set a catch block */
82
- get connected () {
83
- return _connected || Promise.reject(new Error('Failed to connect'))
84
- }
88
+ target
85
89
  }
86
90
  }
87
91
 
88
- if (invalidApiResponse) {
89
- invalidApiResponse()
90
- } else {
91
- _connected = new Promise((resolve, reject) => {
92
- try {
93
- const entityManager = agentRef.runtime?.entityManager
94
- /** check if main agent already has main agent entity guid */
95
- let mainAgentReady = !!entityManager?.get().entityGuid
96
- /** check if registered target already has entity guid */
97
- let registeredEntityGuid = entityManager?.getEntityGuidFor(target.licenseKey, target.applicationID)
98
- let registrationReady = !!registeredEntityGuid
99
-
100
- /** check if we can just resolve immediately without making another connect call */
101
- if (mainAgentReady && registrationReady) {
102
- target.entityGuid = registeredEntityGuid
103
- resolve(api)
104
- } else {
105
- /** we need to make a new connection call since we dont already have a registered entity for this call */
106
-
107
- /** if the connect callback doesnt resolve in 15 seconds... reject */
108
- const timeout = setTimeout(() => reject(new Error('Failed to connect - Timeout')), 15000)
109
-
110
- // tell the main agent to send a rum call for this target
111
- // when the rum call resolves, it will emit an "entity-added" event, see below
112
- agentRef.ee.emit('api-send-rum', [attrs, target])
113
-
114
- // wait for entity events to emit to see when main agent and/or API registration is ready
115
- agentRef.ee.on('entity-added', entityEventHandler)
92
+ /**
93
+ * Check if the API is blocked and emit a warning message describing the blockage
94
+ * @returns {boolean}
95
+ */
96
+ const isBlocked = () => {
97
+ if (target.blocked) invalidApiResponse()
98
+ return target.blocked
99
+ }
116
100
 
117
- function entityEventHandler (entity) {
118
- if (isContainerAgentTarget(entity, agentRef)) mainAgentReady ||= true
119
- else {
120
- if (target.licenseKey === entity.licenseKey && target.applicationID === entity.applicationID) {
121
- registrationReady = true
122
- target.entityGuid = entity.entityGuid
123
- }
124
- }
101
+ /** only allow registered APIs to be tracked in the agent runtime */
102
+ if (!isBlocked()) registeredEntities.push(api)
125
103
 
126
- if (mainAgentReady && registrationReady) {
127
- clearTimeout(timeout)
128
- // unsubscribe from the event emitter
129
- agentRef.ee.removeEventListener('entity-added', entityEventHandler)
130
- resolve(api)
131
- }
132
- }
133
- }
134
- } catch (err) {
135
- reject(err)
136
- }
137
- })
104
+ /**
105
+ * Sets a value local to the registered API attrs. Will do nothing if APIs are deregistered.
106
+ * @param {string} key The attribute key
107
+ * @param {*} value the attribute value
108
+ * @returns {void}
109
+ */
110
+ const setLocalValue = (key, value) => {
111
+ if (isBlocked()) return
112
+ attrs[key] = value
138
113
  }
139
114
 
140
115
  /**
@@ -142,23 +117,21 @@ export function buildRegisterApi (agentRef, target) {
142
117
  * If the api.duplicate_registered_data configuration value is set to true, the data will be reported to BOTH the container and the external target
143
118
  * @param {*} methodToCall the container agent's API method to call
144
119
  * @param {*} args the arguments to supply to the container agent's API method
145
- * @param {string} targetEntityGuid the target entity guid, which looks up the target to report the data to from the entity manager. If undefined, will report to the container agent's target.
120
+ * @param {string} target the target to report the data to. If undefined, will report to the container agent's target.
146
121
  * @returns
147
122
  */
148
- const report = async (methodToCall, args, target) => {
149
- if (invalidApiResponse) return invalidApiResponse()
123
+ const report = (methodToCall, args, target) => {
124
+ if (isBlocked()) return
150
125
  /** set the timestamp before the async part of waiting for the rum response for better accuracy */
151
126
  const timestamp = now()
152
127
  handle(SUPPORTABILITY_METRIC_CHANNEL, [`API/register/${methodToCall.name}/called`], undefined, FEATURE_NAMES.metrics, agentRef.ee)
153
128
  try {
154
- await _connected
155
- // target should be decorated with entityGuid by the rum resp at this point
156
129
  const shouldDuplicate = agentRef.init.api.duplicate_registered_data
157
- if (shouldDuplicate === true || (Array.isArray(shouldDuplicate) && shouldDuplicate.includes(target.entityGuid))) {
130
+ if (shouldDuplicate === true || Array.isArray(shouldDuplicate)) {
158
131
  // also report to container by providing undefined target
159
132
  methodToCall(...args, undefined, timestamp)
160
133
  }
161
- methodToCall(...args, target.entityGuid, timestamp) // always report to target
134
+ return methodToCall(...args, target, timestamp) // always report to target
162
135
  } catch (err) {
163
136
  warn(50, err)
164
137
  }
@@ -1,34 +0,0 @@
1
- "use strict";
2
-
3
- Object.defineProperty(exports, "__esModule", {
4
- value: true
5
- });
6
- exports.isContainerAgentTarget = isContainerAgentTarget;
7
- exports.isValidTarget = isValidTarget;
8
- /**
9
- * Copyright 2020-2025 New Relic, Inc. All rights reserved.
10
- * SPDX-License-Identifier: Apache-2.0
11
- */
12
-
13
- /**
14
- * @param {Object} [target] - the target to be validated
15
- * @param {boolean} [allowUndefined=true]
16
- * @returns {boolean}
17
- */
18
- function isValidTarget(target) {
19
- /** target can be undefined to support legacy/default behaviors - main agent does not supply a target */
20
- if (!target) return true;
21
- /** if not undefined, we require specific values */
22
- return !!(target.licenseKey && target.applicationID);
23
- }
24
-
25
- /**
26
- * Checks if the target matches the container agent target
27
- * @param {*} target the target to be validated
28
- * @param {*} agentRef the agent reference to be validated
29
- * @returns {boolean}
30
- */
31
- function isContainerAgentTarget(target, agentRef) {
32
- if (!target) return true;
33
- return target.licenseKey === agentRef.info.licenseKey && target.applicationID === agentRef.info.applicationID;
34
- }
@@ -1,46 +0,0 @@
1
- "use strict";
2
-
3
- Object.defineProperty(exports, "__esModule", {
4
- value: true
5
- });
6
- exports.EntityManager = void 0;
7
- var _agentConstants = require("../../common/constants/agent-constants");
8
- /**
9
- * Copyright 2020-2025 New Relic, Inc. All rights reserved.
10
- * SPDX-License-Identifier: Apache-2.0
11
- */
12
-
13
- class EntityManager {
14
- #entities = new Map();
15
- #entityGuidLookup = {};
16
- constructor(agentRef) {
17
- this.agentRef = agentRef;
18
- this.#entities.set(_agentConstants.DEFAULT_KEY, {
19
- licenseKey: agentRef.info.licenseKey,
20
- applicationID: agentRef.info.applicationID
21
- });
22
- }
23
- get(entityGuid = _agentConstants.DEFAULT_KEY) {
24
- return this.#entities.get(entityGuid);
25
- }
26
- getEntityGuidFor(licenseKey, applicationID) {
27
- if (!this.#entityGuidLookup[licenseKey] || !this.#entityGuidLookup[applicationID]) return;
28
- return this.#entityGuidLookup[licenseKey].filter(x => this.#entityGuidLookup[applicationID].includes(x))[0];
29
- }
30
- set(entityGuid, entity) {
31
- if (this.#entities.has(entityGuid)) return;
32
- this.#entities.set(entityGuid, entity);
33
- this.#entityGuidLookup[entity.licenseKey] ??= [];
34
- this.#entityGuidLookup[entity.licenseKey].push(entityGuid);
35
- this.#entityGuidLookup[entity.applicationID] ??= [];
36
- this.#entityGuidLookup[entity.applicationID].push(entityGuid);
37
- this.agentRef.ee.emit('entity-added', [entity]);
38
- }
39
- clear() {
40
- this.#entities.clear();
41
- }
42
- setDefaultEntity(entity) {
43
- this.#entities.set(_agentConstants.DEFAULT_KEY, entity);
44
- }
45
- }
46
- exports.EntityManager = EntityManager;