@newrelic/browser-agent 1.302.0-rc.6 → 1.302.0-rc.8

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 (64) hide show
  1. package/dist/cjs/common/config/init-types.js +2 -0
  2. package/dist/cjs/common/config/init.js +3 -0
  3. package/dist/cjs/common/constants/env.cdn.js +1 -1
  4. package/dist/cjs/common/constants/env.npm.js +1 -1
  5. package/dist/cjs/common/harvest/harvester.js +13 -9
  6. package/dist/cjs/common/harvest/types.js +0 -1
  7. package/dist/cjs/common/session/session-entity.js +4 -2
  8. package/dist/cjs/features/page_view_event/aggregate/index.js +36 -22
  9. package/dist/cjs/features/page_view_event/instrument/index.js +0 -4
  10. package/dist/cjs/features/utils/agent-session.js +13 -0
  11. package/dist/cjs/features/utils/instrument-base.js +7 -8
  12. package/dist/cjs/loaders/agent.js +2 -0
  13. package/dist/cjs/loaders/api/consent.js +24 -0
  14. package/dist/cjs/loaders/api/constants.js +3 -2
  15. package/dist/cjs/loaders/api-base.js +10 -0
  16. package/dist/esm/common/config/init-types.js +2 -0
  17. package/dist/esm/common/config/init.js +3 -0
  18. package/dist/esm/common/constants/env.cdn.js +1 -1
  19. package/dist/esm/common/constants/env.npm.js +1 -1
  20. package/dist/esm/common/harvest/harvester.js +13 -9
  21. package/dist/esm/common/harvest/types.js +0 -1
  22. package/dist/esm/common/session/session-entity.js +4 -2
  23. package/dist/esm/features/page_view_event/aggregate/index.js +36 -22
  24. package/dist/esm/features/page_view_event/instrument/index.js +0 -4
  25. package/dist/esm/features/utils/agent-session.js +13 -0
  26. package/dist/esm/features/utils/instrument-base.js +7 -8
  27. package/dist/esm/loaders/agent.js +2 -0
  28. package/dist/esm/loaders/api/consent.js +17 -0
  29. package/dist/esm/loaders/api/constants.js +2 -1
  30. package/dist/esm/loaders/api-base.js +11 -1
  31. package/dist/tsconfig.tsbuildinfo +1 -1
  32. package/dist/types/common/config/init-types.d.ts +6 -0
  33. package/dist/types/common/config/init.d.ts.map +1 -1
  34. package/dist/types/common/harvest/harvester.d.ts.map +1 -1
  35. package/dist/types/common/harvest/types.d.ts +0 -2
  36. package/dist/types/common/harvest/types.d.ts.map +1 -1
  37. package/dist/types/common/session/session-entity.d.ts.map +1 -1
  38. package/dist/types/features/page_view_event/aggregate/index.d.ts +22 -3
  39. package/dist/types/features/page_view_event/aggregate/index.d.ts.map +1 -1
  40. package/dist/types/features/page_view_event/instrument/index.d.ts.map +1 -1
  41. package/dist/types/features/utils/agent-session.d.ts.map +1 -1
  42. package/dist/types/features/utils/instrument-base.d.ts +1 -0
  43. package/dist/types/features/utils/instrument-base.d.ts.map +1 -1
  44. package/dist/types/loaders/agent.d.ts.map +1 -1
  45. package/dist/types/loaders/api/consent.d.ts +2 -0
  46. package/dist/types/loaders/api/consent.d.ts.map +1 -0
  47. package/dist/types/loaders/api/constants.d.ts +1 -0
  48. package/dist/types/loaders/api/constants.d.ts.map +1 -1
  49. package/dist/types/loaders/api-base.d.ts +7 -0
  50. package/dist/types/loaders/api-base.d.ts.map +1 -1
  51. package/package.json +1 -1
  52. package/src/common/config/init-types.js +2 -0
  53. package/src/common/config/init.js +1 -0
  54. package/src/common/harvest/harvester.js +11 -8
  55. package/src/common/harvest/types.js +0 -1
  56. package/src/common/session/session-entity.js +6 -2
  57. package/src/features/page_view_event/aggregate/index.js +29 -15
  58. package/src/features/page_view_event/instrument/index.js +0 -4
  59. package/src/features/utils/agent-session.js +12 -0
  60. package/src/features/utils/instrument-base.js +7 -9
  61. package/src/loaders/agent.js +2 -0
  62. package/src/loaders/api/consent.js +18 -0
  63. package/src/loaders/api/constants.js +1 -0
  64. package/src/loaders/api-base.js +11 -1
@@ -23,12 +23,16 @@ import { getSubmitMethod } from '../../../common/util/submit-data'
23
23
 
24
24
  export class Aggregate extends AggregateBase {
25
25
  static featureName = CONSTANTS.FEATURE_NAME
26
+
26
27
  constructor (agentRef) {
27
28
  super(agentRef, CONSTANTS.FEATURE_NAME)
28
29
 
30
+ this.sentRum = false // flag to facilitate calling sendRum() once externally (by the consent API in agent-session.js)
31
+
29
32
  this.timeToFirstByte = 0
30
33
  this.firstByteToWindowLoad = 0 // our "frontend" duration
31
34
  this.firstByteToDomContent = 0 // our "dom processing" duration
35
+ this.retries = 0
32
36
 
33
37
  if (!isValid(agentRef.info)) {
34
38
  this.ee.abort()
@@ -55,9 +59,8 @@ export class Aggregate extends AggregateBase {
55
59
  *
56
60
  * @param {Function} cb A function to run once the RUM call has finished - Defaults to activateFeatures
57
61
  * @param {*} customAttributes custom attributes to attach to the RUM call - Defaults to info.js
58
- * @param {*} target The target to harvest to
59
62
  */
60
- sendRum (customAttributes = this.agentRef.info.jsAttributes, target = { licenseKey: this.agentRef.info.licenseKey, applicationID: this.agentRef.info.applicationID }) {
63
+ sendRum (customAttributes = this.agentRef.info.jsAttributes) {
61
64
  const info = this.agentRef.info
62
65
  const measures = {}
63
66
 
@@ -110,24 +113,26 @@ export class Aggregate extends AggregateBase {
110
113
  queryParameters.fp = firstPaint.current.value
111
114
  queryParameters.fcp = firstContentfulPaint.current.value
112
115
 
113
- const timeKeeper = this.agentRef.runtime.timeKeeper
114
- if (timeKeeper?.ready) {
115
- queryParameters.timestamp = Math.floor(timeKeeper.correctRelativeTimestamp(now()))
116
+ this.queryStringsBuilder = () => { // this will be called by AggregateBase.makeHarvestPayload every time harvest is triggered to be qs
117
+ this.rumStartTime = now() // this should be reset at the beginning of each RUM call for proper timeKeeper calculation in coordination with postHarvestCleanup
118
+ const timeKeeper = this.agentRef.runtime.timeKeeper
119
+ if (timeKeeper?.ready) {
120
+ queryParameters.timestamp = Math.floor(timeKeeper.correctRelativeTimestamp(this.rumStartTime))
121
+ }
122
+ return queryParameters
116
123
  }
124
+ this.events.add(body)
117
125
 
118
- this.rumStartTime = now()
119
-
120
- this.agentRef.runtime.harvester.triggerHarvestFor(this, {
121
- directSend: {
122
- target,
123
- payload: { qs: queryParameters, body }
124
- },
125
- needResponse: true,
126
+ if (this.agentRef.runtime.harvester.triggerHarvestFor(this, {
126
127
  sendEmptyBody: true
127
- })
128
+ }).ranSend) this.sentRum = true
128
129
  }
129
130
 
130
- postHarvestCleanup ({ status, responseText, xhr }) {
131
+ serializer (eventBuffer) { // this is necessary because PVE sends a single item rather than an array; in the case of undefined, this prevents sending [null] as body
132
+ return eventBuffer[0]
133
+ }
134
+
135
+ postHarvestCleanup ({ sent, status, responseText, xhr, retry }) {
131
136
  const rumEndTime = now()
132
137
  let app, flags
133
138
  try {
@@ -137,8 +142,16 @@ export class Aggregate extends AggregateBase {
137
142
  warn(53, error)
138
143
  }
139
144
 
145
+ super.postHarvestCleanup({ sent, retry }) // this will set isRetrying & re-buffer the body if request is to be retried
146
+ if (this.isRetrying && this.retries++ < 1) { // Only retry once
147
+ setTimeout(() => this.agentRef.runtime.harvester.triggerHarvestFor(this, {
148
+ sendEmptyBody: true
149
+ }), 5000) // Retry sending the RUM event after 5 seconds
150
+ return
151
+ }
140
152
  if (status >= 400 || status === 0) {
141
153
  warn(18, status)
154
+ this.blocked = true
142
155
 
143
156
  // Get estimated payload size of our backlog
144
157
  const textEncoder = new TextEncoder()
@@ -205,6 +218,7 @@ export class Aggregate extends AggregateBase {
205
218
  }
206
219
  } catch (error) {
207
220
  this.ee.abort()
221
+ this.blocked = true
208
222
  warn(17, error)
209
223
  return
210
224
  }
@@ -2,7 +2,6 @@
2
2
  * Copyright 2020-2025 New Relic, Inc. All rights reserved.
3
3
  * SPDX-License-Identifier: Apache-2.0
4
4
  */
5
- import { handle } from '../../../common/event-emitter/handle'
6
5
  import { setupSetPageViewNameAPI } from '../../../loaders/api/setPageViewName'
7
6
  import { InstrumentBase } from '../../utils/instrument-base'
8
7
  import * as CONSTANTS from '../constants'
@@ -21,9 +20,6 @@ export class Instrument extends InstrumentBase {
21
20
  /** feature specific APIs */
22
21
  setupSetPageViewNameAPI(agentRef)
23
22
 
24
- /** messages from the register API that can trigger a new RUM call */
25
- this.ee.on('api-send-rum', (attrs, target) => handle('send-rum', [attrs, target], undefined, this.featureName, this.ee))
26
-
27
23
  this.importAggregator(agentRef, () => import(/* webpackChunkName: "page_view_event-aggregate" */ '../aggregate'))
28
24
  }
29
25
 
@@ -56,6 +56,18 @@ export function setupAgentSession (agentRef) {
56
56
  agentRef.runtime.session.syncCustomAttribute(key, value)
57
57
  }, 'session', sharedEE)
58
58
 
59
+ registerHandler('api-consent', (accept) => {
60
+ agentRef.runtime.session.write({ consent: accept === undefined ? true : accept })
61
+
62
+ // call sendRum if it wasn't called yet
63
+ agentRef.features.page_view_event.onAggregateImported.then((loaded) => {
64
+ const pveAgg = agentRef.features.page_view_event.featAggregate
65
+ if (loaded && !pveAgg.sentRum) {
66
+ pveAgg.sendRum()
67
+ }
68
+ })
69
+ }, 'session', sharedEE)
70
+
59
71
  drain(agentRef.agentIdentifier, 'session')
60
72
 
61
73
  return agentRef.runtime.session
@@ -49,7 +49,10 @@ export class InstrumentBase extends FeatureBase {
49
49
  * @type {Promise} Assigned immediately after @see importAggregator runs. Serves as a signal for when the inner async fn finishes execution. Useful for features to await
50
50
  * one another if there are inter-features dependencies.
51
51
  */
52
- this.onAggregateImported = undefined
52
+ this.loadedSuccessfully = undefined
53
+ this.onAggregateImported = new Promise(resolve => {
54
+ this.loadedSuccessfully = resolve
55
+ })
53
56
 
54
57
  /**
55
58
  * used in conjunction with newrelic.start() to defer harvesting in features
@@ -83,11 +86,6 @@ export class InstrumentBase extends FeatureBase {
83
86
  importAggregator (agentRef, fetchAggregator, argsObjFromInstrument = {}) {
84
87
  if (this.featAggregate) return
85
88
 
86
- let loadedSuccessfully
87
- this.onAggregateImported = new Promise(resolve => {
88
- loadedSuccessfully = resolve
89
- })
90
-
91
89
  const importLater = async () => {
92
90
  // wait for the deferred promise to resolve before proceeding
93
91
  // this will resolve immediately if the feature is auto-started,
@@ -113,20 +111,20 @@ export class InstrumentBase extends FeatureBase {
113
111
  try {
114
112
  if (!this.#shouldImportAgg(this.featureName, session, agentRef.init)) {
115
113
  drain(this.agentIdentifier, this.featureName)
116
- loadedSuccessfully(false) // aggregate module isn't loaded at all
114
+ this.loadedSuccessfully(false) // aggregate module isn't loaded at all
117
115
  return
118
116
  }
119
117
  const { Aggregate } = await fetchAggregator()
120
118
  this.featAggregate = new Aggregate(agentRef, argsObjFromInstrument)
121
119
 
122
120
  agentRef.runtime.harvester.initializedAggregates.push(this.featAggregate) // "subscribe" the feature to future harvest intervals (PVE will start the timer)
123
- loadedSuccessfully(true)
121
+ this.loadedSuccessfully(true)
124
122
  } catch (e) {
125
123
  warn(34, e)
126
124
  this.abortHandler?.() // undo any important alterations made to the page
127
125
  // not supported yet but nice to do: "abort" this agent's EE for this feature specifically
128
126
  drain(this.agentIdentifier, this.featureName, true)
129
- loadedSuccessfully(false)
127
+ this.loadedSuccessfully(false)
130
128
  if (this.ee) this.ee.abort()
131
129
  }
132
130
  }
@@ -23,6 +23,7 @@ import { setupSetCustomAttributeAPI } from './api/setCustomAttribute'
23
23
  import { setupSetUserIdAPI } from './api/setUserId'
24
24
  import { setupSetApplicationVersionAPI } from './api/setApplicationVersion'
25
25
  import { setupStartAPI } from './api/start'
26
+ import { setupConsentAPI } from './api/consent'
26
27
 
27
28
  /**
28
29
  * @typedef {Object} AgentOptions
@@ -70,6 +71,7 @@ export class Agent extends AgentBase {
70
71
  setupSetUserIdAPI(this)
71
72
  setupSetApplicationVersionAPI(this)
72
73
  setupStartAPI(this)
74
+ setupConsentAPI(this)
73
75
 
74
76
  this.run()
75
77
  }
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Copyright 2020-2025 New Relic, Inc. All rights reserved.
3
+ * SPDX-License-Identifier: Apache-2.0
4
+ */
5
+ import { prefix, CONSENT } from './constants'
6
+ import { setupAPI } from './sharedHandlers'
7
+ import { handle } from '../../common/event-emitter/handle'
8
+ import { warn } from '../../common/util/console'
9
+
10
+ export function setupConsentAPI (agent) {
11
+ setupAPI(CONSENT, function (accept) {
12
+ if (accept !== undefined && typeof accept !== 'boolean') {
13
+ warn(65, typeof accept)
14
+ return
15
+ }
16
+ handle(prefix + CONSENT, [accept], undefined, 'session', agent.ee)
17
+ }, agent)
18
+ }
@@ -25,3 +25,4 @@ export const SET_USER_ID = 'setUserId'
25
25
  export const START = 'start'
26
26
  export const WRAP_LOGGER = 'wrapLogger'
27
27
  export const MEASURE = 'measure'
28
+ export const CONSENT = 'consent'
@@ -3,7 +3,7 @@
3
3
  * SPDX-License-Identifier: Apache-2.0
4
4
  */
5
5
  import { warn } from '../common/util/console'
6
- import { ADD_PAGE_ACTION, ADD_RELEASE, ADD_TO_TRACE, FINISHED, INTERACTION, LOG, NOTICE_ERROR, PAUSE_REPLAY, RECORD_CUSTOM_EVENT, RECORD_REPLAY, REGISTER, SET_APPLICATION_VERSION, SET_CURRENT_ROUTE_NAME, SET_CUSTOM_ATTRIBUTE, SET_ERROR_HANDLER, SET_PAGE_VIEW_NAME, SET_USER_ID, START, WRAP_LOGGER, MEASURE } from './api/constants'
6
+ import { ADD_PAGE_ACTION, ADD_RELEASE, ADD_TO_TRACE, CONSENT, FINISHED, INTERACTION, LOG, NOTICE_ERROR, PAUSE_REPLAY, RECORD_CUSTOM_EVENT, RECORD_REPLAY, REGISTER, SET_APPLICATION_VERSION, SET_CURRENT_ROUTE_NAME, SET_CUSTOM_ATTRIBUTE, SET_ERROR_HANDLER, SET_PAGE_VIEW_NAME, SET_USER_ID, START, WRAP_LOGGER, MEASURE } from './api/constants'
7
7
 
8
8
  /**
9
9
  * @typedef {import('./api/interaction-types').InteractionInstance} InteractionInstance
@@ -225,4 +225,14 @@ export class ApiBase {
225
225
  measure (name, options) {
226
226
  return this.#callMethod(MEASURE, name, options)
227
227
  }
228
+
229
+ /**
230
+ * Accepts or rejects consent when the agent is configured to require consent before harvesting.
231
+ * The consent state is stored in session storage inside the NRBA_SESSION object.
232
+ * {@link https://docs.newrelic.com/docs/browser/new-relic-browser/browser-apis/consent/}
233
+ * @param {boolean?} accept Whether to accept or reject consent. Defaults to true (accept) if left undefined.
234
+ */
235
+ consent (accept) {
236
+ return this.#callMethod(CONSENT, accept)
237
+ }
228
238
  }