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

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 (133) hide show
  1. package/dist/cjs/common/config/init-types.js +1 -1
  2. package/dist/cjs/common/config/init.js +7 -1
  3. package/dist/cjs/common/config/runtime.js +1 -1
  4. package/dist/cjs/common/constants/agent-constants.js +11 -2
  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/drain/drain.js +1 -1
  8. package/dist/cjs/common/harvest/harvester.js +30 -39
  9. package/dist/cjs/common/util/mfe.js +45 -0
  10. package/dist/cjs/features/generic_events/aggregate/index.js +9 -8
  11. package/dist/cjs/features/generic_events/constants.js +3 -1
  12. package/dist/cjs/features/jserrors/aggregate/index.js +18 -17
  13. package/dist/cjs/features/logging/aggregate/index.js +19 -15
  14. package/dist/cjs/features/logging/shared/utils.js +3 -3
  15. package/dist/cjs/features/page_view_event/aggregate/index.js +3 -33
  16. package/dist/cjs/features/session_replay/aggregate/index.js +13 -13
  17. package/dist/cjs/features/session_replay/shared/recorder.js +3 -2
  18. package/dist/cjs/features/session_trace/aggregate/index.js +0 -2
  19. package/dist/cjs/features/soft_navigations/aggregate/index.js +1 -2
  20. package/dist/cjs/features/utils/aggregate-base.js +45 -47
  21. package/dist/cjs/loaders/api/addPageAction.js +2 -2
  22. package/dist/cjs/loaders/api/log.js +2 -2
  23. package/dist/cjs/loaders/api/noticeError.js +2 -2
  24. package/dist/cjs/loaders/api/register-api-types.js +5 -5
  25. package/dist/cjs/loaders/api/register.js +80 -97
  26. package/dist/esm/common/config/init-types.js +1 -1
  27. package/dist/esm/common/config/init.js +7 -1
  28. package/dist/esm/common/config/runtime.js +1 -1
  29. package/dist/esm/common/constants/agent-constants.js +9 -1
  30. package/dist/esm/common/constants/env.cdn.js +1 -1
  31. package/dist/esm/common/constants/env.npm.js +1 -1
  32. package/dist/esm/common/drain/drain.js +1 -1
  33. package/dist/esm/common/harvest/harvester.js +30 -39
  34. package/dist/esm/common/util/mfe.js +38 -0
  35. package/dist/esm/features/generic_events/aggregate/index.js +9 -8
  36. package/dist/esm/features/generic_events/constants.js +3 -1
  37. package/dist/esm/features/jserrors/aggregate/index.js +18 -17
  38. package/dist/esm/features/logging/aggregate/index.js +19 -15
  39. package/dist/esm/features/logging/shared/utils.js +3 -3
  40. package/dist/esm/features/page_view_event/aggregate/index.js +3 -33
  41. package/dist/esm/features/session_replay/aggregate/index.js +13 -13
  42. package/dist/esm/features/session_replay/shared/recorder.js +3 -2
  43. package/dist/esm/features/session_trace/aggregate/index.js +0 -2
  44. package/dist/esm/features/soft_navigations/aggregate/index.js +1 -2
  45. package/dist/esm/features/utils/aggregate-base.js +46 -48
  46. package/dist/esm/loaders/api/addPageAction.js +2 -2
  47. package/dist/esm/loaders/api/log.js +2 -2
  48. package/dist/esm/loaders/api/noticeError.js +2 -2
  49. package/dist/esm/loaders/api/register-api-types.js +5 -5
  50. package/dist/esm/loaders/api/register.js +80 -97
  51. package/dist/tsconfig.tsbuildinfo +1 -1
  52. package/dist/types/common/config/init-types.d.ts +4 -1
  53. package/dist/types/common/config/init-types.d.ts.map +1 -1
  54. package/dist/types/common/config/init.d.ts.map +1 -1
  55. package/dist/types/common/constants/agent-constants.d.ts +7 -4
  56. package/dist/types/common/constants/agent-constants.d.ts.map +1 -1
  57. package/dist/types/common/harvest/harvester.d.ts +2 -2
  58. package/dist/types/common/harvest/harvester.d.ts.map +1 -1
  59. package/dist/types/common/util/mfe.d.ts +20 -0
  60. package/dist/types/common/util/mfe.d.ts.map +1 -0
  61. package/dist/types/features/generic_events/aggregate/index.d.ts +2 -2
  62. package/dist/types/features/generic_events/aggregate/index.d.ts.map +1 -1
  63. package/dist/types/features/generic_events/constants.d.ts +1 -0
  64. package/dist/types/features/jserrors/aggregate/index.d.ts +3 -3
  65. package/dist/types/features/jserrors/aggregate/index.d.ts.map +1 -1
  66. package/dist/types/features/logging/aggregate/index.d.ts +3 -3
  67. package/dist/types/features/logging/aggregate/index.d.ts.map +1 -1
  68. package/dist/types/features/logging/shared/utils.d.ts +2 -2
  69. package/dist/types/features/logging/shared/utils.d.ts.map +1 -1
  70. package/dist/types/features/page_view_event/aggregate/index.d.ts +2 -4
  71. package/dist/types/features/page_view_event/aggregate/index.d.ts.map +1 -1
  72. package/dist/types/features/session_replay/aggregate/index.d.ts +13 -4
  73. package/dist/types/features/session_replay/aggregate/index.d.ts.map +1 -1
  74. package/dist/types/features/session_replay/shared/recorder.d.ts +1 -0
  75. package/dist/types/features/session_replay/shared/recorder.d.ts.map +1 -1
  76. package/dist/types/features/session_trace/aggregate/index.d.ts.map +1 -1
  77. package/dist/types/features/soft_navigations/aggregate/index.d.ts.map +1 -1
  78. package/dist/types/features/utils/aggregate-base.d.ts +13 -5
  79. package/dist/types/features/utils/aggregate-base.d.ts.map +1 -1
  80. package/dist/types/loaders/api/addPageAction.d.ts +1 -1
  81. package/dist/types/loaders/api/addPageAction.d.ts.map +1 -1
  82. package/dist/types/loaders/api/log.d.ts +1 -1
  83. package/dist/types/loaders/api/log.d.ts.map +1 -1
  84. package/dist/types/loaders/api/noticeError.d.ts +1 -1
  85. package/dist/types/loaders/api/noticeError.d.ts.map +1 -1
  86. package/dist/types/loaders/api/register-api-types.d.ts +4 -4
  87. package/dist/types/loaders/api/register-api-types.d.ts.map +1 -1
  88. package/dist/types/loaders/api/register.d.ts +8 -1
  89. package/dist/types/loaders/api/register.d.ts.map +1 -1
  90. package/package.json +1 -1
  91. package/src/common/config/init-types.js +1 -1
  92. package/src/common/config/init.js +3 -1
  93. package/src/common/config/runtime.js +1 -1
  94. package/src/common/constants/agent-constants.js +10 -0
  95. package/src/common/drain/drain.js +1 -1
  96. package/src/common/harvest/harvester.js +27 -32
  97. package/src/common/util/mfe.js +35 -0
  98. package/src/features/generic_events/aggregate/index.js +9 -8
  99. package/src/features/generic_events/constants.js +3 -1
  100. package/src/features/jserrors/aggregate/index.js +17 -17
  101. package/src/features/logging/aggregate/index.js +20 -13
  102. package/src/features/logging/shared/utils.js +3 -3
  103. package/src/features/page_view_event/aggregate/index.js +3 -28
  104. package/src/features/session_replay/aggregate/index.js +12 -10
  105. package/src/features/session_replay/shared/recorder.js +3 -2
  106. package/src/features/session_trace/aggregate/index.js +0 -2
  107. package/src/features/soft_navigations/aggregate/index.js +1 -2
  108. package/src/features/utils/aggregate-base.js +47 -42
  109. package/src/loaders/api/addPageAction.js +2 -2
  110. package/src/loaders/api/log.js +2 -2
  111. package/src/loaders/api/noticeError.js +2 -2
  112. package/src/loaders/api/register-api-types.js +5 -5
  113. package/src/loaders/api/register.js +62 -89
  114. package/dist/cjs/common/util/target.js +0 -34
  115. package/dist/cjs/features/utils/entity-manager.js +0 -46
  116. package/dist/cjs/features/utils/event-store-manager.js +0 -174
  117. package/dist/cjs/loaders/api/register-api.js +0 -165
  118. package/dist/esm/common/util/target.js +0 -27
  119. package/dist/esm/features/utils/entity-manager.js +0 -39
  120. package/dist/esm/features/utils/event-store-manager.js +0 -166
  121. package/dist/esm/loaders/api/register-api.js +0 -159
  122. package/dist/types/common/util/target.d.ts +0 -18
  123. package/dist/types/common/util/target.d.ts.map +0 -1
  124. package/dist/types/features/utils/entity-manager.d.ts +0 -11
  125. package/dist/types/features/utils/entity-manager.d.ts.map +0 -1
  126. package/dist/types/features/utils/event-store-manager.d.ts +0 -85
  127. package/dist/types/features/utils/event-store-manager.d.ts.map +0 -1
  128. package/dist/types/loaders/api/register-api.d.ts +0 -14
  129. package/dist/types/loaders/api/register-api.d.ts.map +0 -1
  130. package/src/common/util/target.js +0 -27
  131. package/src/features/utils/entity-manager.js +0 -45
  132. package/src/features/utils/event-store-manager.js +0 -165
  133. package/src/loaders/api/register-api.js +0 -152
@@ -10,10 +10,10 @@ import { FEATURE_NAME, LOGGING_EVENT_EMITTER_CHANNEL, LOG_LEVELS, LOGGING_MODE }
10
10
  import { Log } from '../shared/log'
11
11
  import { isValidLogLevel } from '../shared/utils'
12
12
  import { applyFnToProps } from '../../../common/util/traverse'
13
- import { isContainerAgentTarget } from '../../../common/util/target'
14
13
  import { SESSION_EVENT_TYPES, SESSION_EVENTS } from '../../../common/session/constants'
15
14
  import { ABORT_REASONS } from '../../session_replay/constants'
16
15
  import { canEnableSessionTracking } from '../../utils/feature-gates'
16
+ import { getVersion2Attributes } from '../../../common/util/mfe'
17
17
 
18
18
  export class Aggregate extends AggregateBase {
19
19
  static featureName = FEATURE_NAME
@@ -21,6 +21,8 @@ export class Aggregate extends AggregateBase {
21
21
  super(agentRef, FEATURE_NAME)
22
22
  this.isSessionTrackingEnabled = canEnableSessionTracking(agentRef.init) && agentRef.runtime.session
23
23
 
24
+ /** set up agg-level behaviors specific to this feature */
25
+ this.harvestOpts.raw = true
24
26
  super.customAttributesAreSeparate = true
25
27
 
26
28
  // 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.
@@ -34,7 +36,6 @@ export class Aggregate extends AggregateBase {
34
36
  else this.loggingMode = data.loggingMode
35
37
  })
36
38
 
37
- this.harvestOpts.raw = true
38
39
  this.waitForFlags(['log']).then(([loggingMode]) => {
39
40
  const session = this.agentRef.runtime.session ?? {}
40
41
  if (this.loggingMode === LOGGING_MODE.OFF || (session.isNew && loggingMode === LOGGING_MODE.OFF)) {
@@ -63,11 +64,17 @@ export class Aggregate extends AggregateBase {
63
64
  })
64
65
  }
65
66
 
66
- handleLog (timestamp, message, attributes = {}, level = LOG_LEVELS.INFO, targetEntityGuid) {
67
- if (!this.agentRef.runtime.entityManager.get(targetEntityGuid)) return warn(56, this.featureName)
67
+ handleLog (timestamp, message, attributes = {}, level = LOG_LEVELS.INFO, target) {
68
68
  if (this.blocked || !this.loggingMode) return
69
69
 
70
70
  if (!attributes || typeof attributes !== 'object') attributes = {}
71
+
72
+ attributes = {
73
+ ...attributes,
74
+ /** Specific attributes only supplied if harvesting to endpoint version 2 */
75
+ ...(getVersion2Attributes(target, this))
76
+ }
77
+
71
78
  if (typeof level === 'string') level = level.toUpperCase()
72
79
  if (!isValidLogLevel(level)) return warn(30, level)
73
80
  if (this.loggingMode < (LOGGING_MODE[level] || Infinity)) {
@@ -100,25 +107,26 @@ export class Aggregate extends AggregateBase {
100
107
  level
101
108
  )
102
109
 
103
- this.events.add(log, targetEntityGuid)
110
+ this.events.add(log)
104
111
  }
105
112
 
106
- serializer (eventBuffer, targetEntityGuid) {
107
- const target = this.agentRef.runtime.entityManager.get(targetEntityGuid)
113
+ serializer (eventBuffer) {
108
114
  const sessionEntity = this.agentRef.runtime.session
109
115
  return [{
110
116
  common: {
111
117
  /** Attributes in the `common` section are added to `all` logs generated in the payload */
112
118
  attributes: {
113
119
  ...this.agentRef.info.jsAttributes, // user-provided custom attributes
114
- 'entity.guid': target.entityGuid, // browser entity guid as provided API target OR the default from RUM response if not supplied
120
+ ...(this.harvestEndpointVersion === 1 && {
121
+ 'entity.guid': this.agentRef.runtime.appMetadata.agents[0].entityGuid,
122
+ appId: this.agentRef.info.applicationID
123
+ }),
115
124
  ...(sessionEntity && {
116
125
  session: sessionEntity.state.value || '0', // The session ID that we generate and keep across page loads
117
- hasReplay: sessionEntity.state.sessionReplayMode === 1 && isContainerAgentTarget(target, this.agentRef), // True if a session replay recording is running
126
+ hasReplay: sessionEntity.state.sessionReplayMode === 1, // True if a session replay recording is running
118
127
  hasTrace: sessionEntity.state.sessionTraceMode === 1 // True if a session trace recording is running
119
128
  }),
120
129
  ptid: this.agentRef.runtime.ptid, // page trace id
121
- appId: target.applicationID || this.agentRef.info.applicationID, // Application ID from info object,
122
130
  standalone: Boolean(this.agentRef.info.sa), // copy paste (true) vs APM (false)
123
131
  agentVersion: this.agentRef.runtime.version, // browser agent version
124
132
  // The following 3 attributes are evaluated and dropped at ingest processing time and do not get stored on NRDB:
@@ -135,9 +143,8 @@ export class Aggregate extends AggregateBase {
135
143
  }]
136
144
  }
137
145
 
138
- queryStringsBuilder (_, targetEntityGuid) {
139
- const target = this.agentRef.runtime.entityManager.get(targetEntityGuid)
140
- return { browser_monitoring_key: target.licenseKey }
146
+ queryStringsBuilder () {
147
+ return { browser_monitoring_key: this.agentRef.info.licenseKey }
141
148
  }
142
149
 
143
150
  /** Abort the feature, once aborted it will not resume */
@@ -13,11 +13,11 @@ import { LOGGING_EVENT_EMITTER_CHANNEL, LOG_LEVELS } from '../constants'
13
13
  * @param {string} message - the log message string
14
14
  * @param {{[key: string]: *}} customAttributes - The log's custom attributes if any
15
15
  * @param {enum} level - the log level enum
16
- * @param {object=} targetEntityGuid - the optional target entity guid provided by an api call
16
+ * @param {object=} target - the optional target provided by an api call
17
17
  */
18
- export function bufferLog (ee, message, customAttributes = {}, level = LOG_LEVELS.INFO, targetEntityGuid, timestamp = now()) {
18
+ export function bufferLog (ee, message, customAttributes = {}, level = LOG_LEVELS.INFO, target, timestamp = now()) {
19
19
  handle(SUPPORTABILITY_METRIC_CHANNEL, [`API/logging/${level.toLowerCase()}/called`], undefined, FEATURE_NAMES.metrics, ee)
20
- handle(LOGGING_EVENT_EMITTER_CHANNEL, [timestamp, message, customAttributes, level, targetEntityGuid], undefined, FEATURE_NAMES.logging, ee)
20
+ handle(LOGGING_EVENT_EMITTER_CHANNEL, [timestamp, message, customAttributes, level, target], undefined, FEATURE_NAMES.logging, ee)
21
21
  }
22
22
 
23
23
  /**
@@ -17,8 +17,6 @@ import { timeToFirstByte } from '../../../common/vitals/time-to-first-byte'
17
17
  import { now } from '../../../common/timing/now'
18
18
  import { TimeKeeper } from '../../../common/timing/time-keeper'
19
19
  import { applyFnToProps } from '../../../common/util/traverse'
20
- import { registerHandler } from '../../../common/event-emitter/register-handler'
21
- import { isContainerAgentTarget } from '../../../common/util/target'
22
20
 
23
21
  export class Aggregate extends AggregateBase {
24
22
  static featureName = CONSTANTS.FEATURE_NAME
@@ -29,10 +27,6 @@ export class Aggregate extends AggregateBase {
29
27
  this.firstByteToWindowLoad = 0 // our "frontend" duration
30
28
  this.firstByteToDomContent = 0 // our "dom processing" duration
31
29
 
32
- registerHandler('send-rum', (customAttibutes, target) => {
33
- this.sendRum(customAttibutes, target)
34
- }, this.featureName, this.ee)
35
-
36
30
  if (!isValid(agentRef.info)) {
37
31
  this.ee.abort()
38
32
  return warn(43)
@@ -58,7 +52,7 @@ export class Aggregate extends AggregateBase {
58
52
  *
59
53
  * @param {Function} cb A function to run once the RUM call has finished - Defaults to activateFeatures
60
54
  * @param {*} customAttributes custom attributes to attach to the RUM call - Defaults to info.js
61
- * @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 }
55
+ * @param {*} target The target to harvest to
62
56
  */
63
57
  sendRum (customAttributes = this.agentRef.info.jsAttributes, target = { licenseKey: this.agentRef.info.licenseKey, applicationID: this.agentRef.info.applicationID }) {
64
58
  const info = this.agentRef.info
@@ -122,7 +116,7 @@ export class Aggregate extends AggregateBase {
122
116
 
123
117
  this.agentRef.runtime.harvester.triggerHarvestFor(this, {
124
118
  directSend: {
125
- targetApp: target,
119
+ target,
126
120
  payload: { qs: queryParameters, body }
127
121
  },
128
122
  needResponse: true,
@@ -130,20 +124,16 @@ export class Aggregate extends AggregateBase {
130
124
  })
131
125
  }
132
126
 
133
- postHarvestCleanup ({ status, responseText, xhr, targetApp }) {
127
+ postHarvestCleanup ({ status, responseText, xhr }) {
134
128
  const rumEndTime = now()
135
129
  let app, flags
136
130
  try {
137
131
  ({ app, ...flags } = JSON.parse(responseText))
138
- this.processEntities(app.agents, targetApp)
139
132
  } catch (error) {
140
133
  // wont set entity stuff here, if main agent will later abort, if registered agent, nothing will happen
141
134
  warn(53, error)
142
135
  }
143
136
 
144
- /** Only run agent-wide side-effects if the harvest was for the main agent */
145
- if (!isContainerAgentTarget(targetApp, this.agentRef)) return
146
-
147
137
  if (status >= 400 || status === 0) {
148
138
  warn(18, status)
149
139
  // Adding retry logic for the rum call will be a separate change; this.blocked will need to be changed since that prevents another triggerHarvestFor()
@@ -176,19 +166,4 @@ export class Aggregate extends AggregateBase {
176
166
  this.agentRef.runtime.harvester.startTimer()
177
167
  activateFeatures(flags, this.agentRef)
178
168
  }
179
-
180
- processEntities (entities, targetApp) {
181
- if (!entities || !targetApp) return
182
- entities.forEach(agent => {
183
- const entityManager = this.agentRef.runtime.entityManager
184
- const entityGuid = agent.entityGuid
185
- const entity = entityManager.get(entityGuid)
186
- if (entity) return // already processed
187
-
188
- if (isContainerAgentTarget(targetApp, this.agentRef)) {
189
- entityManager.setDefaultEntity({ ...targetApp, entityGuid })
190
- }
191
- entityManager.set(agent.entityGuid, { ...targetApp, entityGuid })
192
- })
193
- }
194
169
  }
@@ -37,6 +37,8 @@ export class Aggregate extends AggregateBase {
37
37
  this.gzipper = undefined
38
38
  /** populated with the u8 string lib async */
39
39
  this.u8 = undefined
40
+ /** flips to false if the compressor libraries cannot import */
41
+ this.shouldCompress = true
40
42
 
41
43
  /** set by BCS response */
42
44
  this.entitled = false
@@ -200,14 +202,15 @@ export class Aggregate extends AggregateBase {
200
202
  this.gzipper = gzipSync
201
203
  this.u8 = strToU8
202
204
  } catch (err) {
203
- // compressor failed to load, but we can still record without compression as a last ditch effort
205
+ this.shouldCompress = false
206
+ // compressor failed to load, but we can still try to record without compression as a last ditch effort
204
207
  }
205
208
  }
206
209
 
207
- makeHarvestPayload (shouldRetryOnFail) {
208
- const payloadOutput = { targetApp: undefined, payload: undefined }
209
- if (this.mode !== MODE.FULL || this.blocked) return
210
- if (!this.recorder || !this.timeKeeper?.ready || !this.recorder.hasSeenSnapshot) return
210
+ makeHarvestPayload () {
211
+ if (this.mode !== MODE.FULL || this.blocked) return // harvests should only be made in FULL mode, and not if the feature is blocked
212
+ if (this.shouldCompress && !this.gzipper) return // if compression is enabled, but the libraries have not loaded, wait for them to load
213
+ if (!this.recorder || !this.timeKeeper?.ready || !(this.recorder.hasSeenSnapshot && this.recorder.hasSeenMeta)) return // if the recorder or the timekeeper is not ready, or the recorder has not yet seen a snapshot, do not harvest
211
214
 
212
215
  const recorderEvents = this.recorder.getEvents()
213
216
  // get the event type and use that to trigger another harvest if needed
@@ -216,7 +219,7 @@ export class Aggregate extends AggregateBase {
216
219
  const payload = this.getHarvestContents(recorderEvents)
217
220
  if (!payload.body.length) {
218
221
  this.recorder.clearBuffer()
219
- return [payloadOutput]
222
+ return
220
223
  }
221
224
 
222
225
  this.reportSupportabilityMetric('SessionReplay/Harvest/Attempts')
@@ -232,19 +235,18 @@ export class Aggregate extends AggregateBase {
232
235
 
233
236
  if (len > MAX_PAYLOAD_SIZE) {
234
237
  this.abort(ABORT_REASONS.TOO_BIG, len)
235
- return [payloadOutput]
238
+ return
236
239
  }
240
+
237
241
  // TODO -- Gracefully handle the buffer for retries.
238
242
  if (!this.agentRef.runtime.session.state.sessionReplaySentFirstChunk) this.syncWithSessionManager({ sessionReplaySentFirstChunk: true })
239
243
  this.recorder.clearBuffer()
240
- if (recorderEvents.type === 'preloaded') this.agentRef.runtime.harvester.triggerHarvestFor(this)
241
- payloadOutput.payload = payload
242
244
 
243
245
  if (!this.agentRef.runtime.session.state.traceHarvestStarted) {
244
246
  warn(59, JSON.stringify(this.agentRef.runtime.session.state))
245
247
  }
246
248
 
247
- return [payloadOutput]
249
+ return payload
248
250
  }
249
251
 
250
252
  /**
@@ -47,6 +47,7 @@ export class Recorder {
47
47
  this.backloggedEvents = new RecorderEvents(this.shouldFix)
48
48
  /** Only set to true once a snapshot node has been processed. Used to block harvests from sending before we know we have a snapshot */
49
49
  this.hasSeenSnapshot = false
50
+ this.hasSeenMeta = false
50
51
  /** 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 */
51
52
  this.lastMeta = false
52
53
  /** The method to stop recording. This defaults to a noop, but is overwritten once the recording library is imported and initialized */
@@ -191,9 +192,9 @@ export class Recorder {
191
192
  }
192
193
 
193
194
  // meta event
194
- this.events.hasMeta ||= event.type === RRWEB_EVENT_TYPES.Meta
195
+ this.hasSeenMeta ||= this.events.hasMeta ||= event.type === RRWEB_EVENT_TYPES.Meta
195
196
  // snapshot event
196
- this.events.hasSnapshot ||= this.hasSeenSnapshot ||= event.type === RRWEB_EVENT_TYPES.FullSnapshot
197
+ this.hasSeenSnapshot ||= this.events.hasSnapshot ||= event.type === RRWEB_EVENT_TYPES.FullSnapshot
197
198
 
198
199
  //* dont let the EventBuffer class double evaluate the event data size, it's a performance burden and we have special reasons to do it outside the event buffer */
199
200
  this.events.add(event, eventBytes)
@@ -97,8 +97,6 @@ export class Aggregate extends AggregateBase {
97
97
  }
98
98
  this.agentRef.runtime.session.write({ sessionTraceMode: this.mode })
99
99
  this.drain()
100
- /** try to harvest immediately. This will not send if the trace is not running in FULL mode due to the pre-harvest checks. */
101
- this.agentRef.runtime.harvester.triggerHarvestFor(this)
102
100
  }
103
101
 
104
102
  preHarvestChecks () {
@@ -46,7 +46,6 @@ export class Aggregate extends AggregateBase {
46
46
  this.waitForFlags(['spa']).then(([spaOn]) => {
47
47
  if (spaOn) {
48
48
  this.drain()
49
- setTimeout(() => agentRef.runtime.harvester.triggerHarvestFor(this), 0) // send the IPL ixn on next tick, giving some time for any ajax to finish; we may want to just remove this?
50
49
  } else {
51
50
  this.blocked = true // if rum response determines that customer lacks entitlements for spa endpoint, this feature shouldn't harvest
52
51
  this.deregisterDrain()
@@ -148,7 +147,7 @@ export class Aggregate extends AggregateBase {
148
147
  */
149
148
  if (this.interactionInProgress?.isActiveDuring(timestamp)) return this.interactionInProgress
150
149
  let saveIxn
151
- const interactionsBuffer = this.interactionsToHarvest.get()?.[0]?.data
150
+ const interactionsBuffer = this.interactionsToHarvest.get()
152
151
  if (!interactionsBuffer) return undefined // no interactions have been staged yet, so nothing to search through)
153
152
  for (let idx = interactionsBuffer.length - 1; idx >= 0; idx--) { // reverse search for the latest completed interaction for efficiency
154
153
  const finishedInteraction = interactionsBuffer[idx]
@@ -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 {}