Skip to content

Commit d9f6711

Browse files
feat: agentIdentifier removal (#1712)
1 parent 847b6a7 commit d9f6711

51 files changed

Lines changed: 390 additions & 315 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

src/common/config/configurable.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/**
2-
* Copyright 2020-2025 New Relic, Inc. All rights reserved.
2+
* Copyright 2020-2026 New Relic, Inc. All rights reserved.
33
* SPDX-License-Identifier: Apache-2.0
44
*/
55
import { warn } from '../util/console'
@@ -20,6 +20,7 @@ export function getModeledObject (obj, model) {
2020
if (obj[key] === null) { output[key] = null; continue }
2121

2222
if (Array.isArray(obj[key]) && Array.isArray(model[key])) output[key] = Array.from(new Set([...obj[key], ...model[key]]))
23+
else if (obj[key] instanceof Map || obj[key] instanceof Set || obj[key] instanceof Date || obj[key] instanceof RegExp) output[key] = obj[key]
2324
else if (typeof obj[key] === 'object' && typeof model[key] === 'object') output[key] = getModeledObject(obj[key], model[key])
2425
else output[key] = obj[key]
2526
} catch (e) {

src/common/config/runtime.js

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/**
2-
* Copyright 2020-2025 New Relic, Inc. All rights reserved.
2+
* Copyright 2020-2026 New Relic, Inc. All rights reserved.
33
* SPDX-License-Identifier: Apache-2.0
44
*/
55
import { getModeledObject } from './configurable'
@@ -24,17 +24,23 @@ const hiddenState = {
2424
}
2525

2626
const RuntimeModel = {
27+
/** @type {{[key: string]: number} | undefined} */
28+
activatedFeatures: undefined,
2729
/** Agent-specific metadata found in the RUM call response. ex. entityGuid */
2830
appMetadata: {},
31+
/** @type {boolean} */
32+
configured: false,
2933
get consented () {
3034
return this.session?.state?.consent || hiddenState.consented
3135
},
3236
set consented (value) {
3337
hiddenState.consented = value
3438
},
3539
customTransaction: undefined,
36-
denyList: undefined,
40+
denyList: [],
3741
disabled: false,
42+
/** @type {Map<string, {staged: boolean, priority: number}>} */
43+
drainRegistry: new Map(),
3844
harvester: undefined,
3945
isolatedBacklog: false,
4046
isRecording: false, // true when actively recording, false when paused or stopped
Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
/**
2-
* Copyright 2020-2025 New Relic, Inc. All rights reserved.
2+
* Copyright 2020-2026 New Relic, Inc. All rights reserved.
33
* SPDX-License-Identifier: Apache-2.0
44
*/
55
export const drain = jest.fn()
66
export const registerDrain = jest.fn()
7+
export const deregisterDrain = jest.fn()

src/common/drain/drain.js

Lines changed: 27 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -4,81 +4,71 @@
44
*/
55

66
import { dispatchGlobalEvent } from '../../common/dispatch/global-event'
7-
import { ee } from '../event-emitter/contextual-ee'
87
import { registerHandler as defaultRegister } from '../event-emitter/register-handler'
98
import { featurePriority } from '../../loaders/features/features'
109
import { EventContext } from '../event-emitter/event-context'
1110

12-
const registry = {}
13-
1411
/**
1512
* Adds an entry to the centralized drain registry specifying that a particular agent has events of a particular named
1613
* event-group "bucket" that should be drained at the time the agent drains all its buffered events. Buffered events
1714
* accumulate because instrumentation begins as soon as possible, before the agent has finished lazy-loading the code
1815
* responsible for aggregating and reporting captured data.
19-
* @param {string} agentIdentifier - A 16 character string uniquely identifying the agent.
16+
* @param {Object} agentRef - The agent reference object.
2017
* @param {string} group - The named "bucket" for the events this feature will be bucketing for later collection.
2118
*/
22-
export function registerDrain (agentIdentifier, group) {
19+
export function registerDrain (agentRef, group) {
20+
if (!agentRef) return
2321
// Here `item` captures the registered properties of a feature-group: whether it is ready for its buffered events
2422
// to be drained (`staged`) and the `priority` order in which it should be drained relative to other feature groups.
2523
const item = { staged: false, priority: featurePriority[group] || 0 }
26-
curateRegistry(agentIdentifier)
27-
if (!registry[agentIdentifier].get(group)) registry[agentIdentifier].set(group, item)
24+
if (!agentRef.runtime.drainRegistry.get(group)) agentRef.runtime.drainRegistry.set(group, item)
2825
}
2926

3027
/**
3128
* Removes an item from the registry and immediately re-checks if the registry is ready to "drain all"
32-
* @param {*} agentIdentifier - A 16 character string uniquely identifying the agent.
29+
* @param {Object} agentRef - The agent reference object.
3330
* @param {*} group - The named "bucket" to be removed from the registry
3431
*/
35-
export function deregisterDrain (agentIdentifier, group) {
36-
if (!agentIdentifier || !registry[agentIdentifier]) return
37-
if (registry[agentIdentifier].get(group)) registry[agentIdentifier].delete(group)
38-
drainGroup(agentIdentifier, group, false)
39-
if (registry[agentIdentifier].size) checkCanDrainAll(agentIdentifier)
40-
}
41-
42-
/**
43-
* Registers the specified agent with the centralized event buffer registry if it is not already registered.
44-
* Agents without an identifier (as in the case of some tests) will be excluded from the registry.
45-
* @param {string} agentIdentifier - A 16 character string uniquely identifying an agent.
46-
*/
47-
function curateRegistry (agentIdentifier) {
48-
if (!agentIdentifier) throw new Error('agentIdentifier required')
49-
if (!registry[agentIdentifier]) registry[agentIdentifier] = new Map()
32+
export function deregisterDrain (agentRef, group) {
33+
if (!agentRef) return
34+
const drainRegistry = agentRef.runtime.drainRegistry
35+
if (!drainRegistry) return
36+
if (drainRegistry.get(group)) drainRegistry.delete(group)
37+
drainGroup(agentRef, group, false)
38+
if (drainRegistry.size) checkCanDrainAll(agentRef)
5039
}
5140

5241
/**
5342
* Drain buffered events out of the event emitter. Each feature should have its events bucketed by "group" and drain
5443
* its own named group explicitly, when ready.
55-
* @param {string} agentIdentifier - A unique 16 character ID corresponding to an instantiated agent.
44+
* @param {Object} agentRef - The agent reference object.
5645
* @param {string} featureName - A named group into which the feature's buffered events are bucketed.
5746
* @param {boolean} force - Whether to force the drain to occur immediately, bypassing the registry and staging logic.
5847
*/
59-
export function drain (agentIdentifier = '', featureName = 'feature', force = false) {
60-
curateRegistry(agentIdentifier)
48+
export function drain (agentRef, featureName = 'feature', force = false) {
49+
if (!agentRef) return
6150
// If the feature for the specified agent is not in the registry, that means the instrument file was bypassed.
6251
// This could happen in tests, or loaders that directly import the aggregator. In these cases it is safe to
6352
// drain the feature group immediately rather than waiting to drain all at once.
64-
if (!agentIdentifier || !registry[agentIdentifier].get(featureName) || force) return drainGroup(agentIdentifier, featureName)
53+
if (!agentRef.runtime.drainRegistry.get(featureName) || force) return drainGroup(agentRef, featureName)
6554

6655
// When `drain` is called, this feature is ready to drain (staged).
67-
registry[agentIdentifier].get(featureName).staged = true
56+
agentRef.runtime.drainRegistry.get(featureName).staged = true
6857

69-
checkCanDrainAll(agentIdentifier)
58+
checkCanDrainAll(agentRef)
7059
}
7160

7261
/** Checks all items in the registry to see if they have been "staged". If ALL items are staged, it will drain all registry items (drainGroup). It not, nothing will happen */
73-
function checkCanDrainAll (agentIdentifier) {
62+
function checkCanDrainAll (agentRef) {
63+
if (!agentRef) return
7464
// Only when the event-groups for all features are ready to drain (staged) do we execute the drain. This has the effect
7565
// that the last feature to call drain triggers drain for all features.
76-
const items = Array.from(registry[agentIdentifier])
66+
const items = Array.from(agentRef.runtime.drainRegistry)
7767
if (items.every(([key, values]) => values.staged)) {
7868
items.sort((a, b) => a[1].priority - b[1].priority)
7969
items.forEach(([group]) => {
80-
registry[agentIdentifier].delete(group)
81-
drainGroup(agentIdentifier, group)
70+
agentRef.runtime.drainRegistry.delete(group)
71+
drainGroup(agentRef, group)
8272
})
8373
}
8474
}
@@ -88,13 +78,13 @@ function checkCanDrainAll (agentIdentifier) {
8878
* the subscribed handlers for the group.
8979
* @param {*} group - The name of a particular feature's event "bucket".
9080
*/
91-
function drainGroup (agentIdentifier, group, activateGroup = true) {
92-
const baseEE = agentIdentifier ? ee.get(agentIdentifier) : ee
81+
function drainGroup (agentRef, group, activateGroup = true) {
82+
if (!agentRef) return
83+
const baseEE = agentRef.ee
9384
const handlers = defaultRegister.handlers // other storage in registerHandler
94-
if (baseEE.aborted || !baseEE.backlog || !handlers) return
85+
if (!baseEE || baseEE.aborted || !baseEE.backlog || !handlers) return
9586

9687
dispatchGlobalEvent({
97-
agentIdentifier,
9888
type: 'lifecycle',
9989
name: 'drain',
10090
feature: group

src/common/harvest/harvester.js

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@ import { obj, param } from '../url/encode'
1515
import { warn } from '../util/console'
1616
import { stringify } from '../util/stringify'
1717
import { getSubmitMethod, xhr as xhrMethod, xhrFetch as fetchMethod } from '../util/submit-data'
18-
import { activatedFeatures } from '../util/feature-flags'
1918
import { dispatchGlobalEvent } from '../dispatch/global-event'
2019

2120
const RETRY = 'Harvester/Retry/'
@@ -198,8 +197,7 @@ export function send (agentRef, { endpoint, payload, localOpts = {}, submitMetho
198197
}
199198

200199
dispatchGlobalEvent({
201-
agentIdentifier: agentRef.agentIdentifier,
202-
drained: !!activatedFeatures?.[agentRef.agentIdentifier],
200+
drained: !!agentRef.runtime?.activatedFeatures,
203201
type: 'data',
204202
name: 'harvest',
205203
feature: featureName,

src/common/session/session-entity.js

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
import { generateRandomHexString } from '../ids/unique-id'
66
import { warn } from '../util/console'
77
import { stringify } from '../util/stringify'
8-
import { ee } from '../event-emitter/contextual-ee'
98
import { Timer } from '../timer/timer'
109
import { isBrowserScope } from '../constants/runtime'
1110
import { DEFAULT_EXPIRES_MS, DEFAULT_INACTIVE_MS, MODE, PREFIX, SESSION_EVENTS, SESSION_EVENT_TYPES } from './constants'
@@ -39,25 +38,25 @@ const model = {
3938

4039
export class SessionEntity {
4140
/**
42-
* Create a self-managing Session Entity. This entity is scoped to the agent identifier which triggered it, allowing for multiple simultaneous session objects to exist.
41+
* Create a self-managing Session Entity. This entity is scoped to the agent which triggered it, allowing for multiple simultaneous session objects to exist.
4342
* There is one "namespace" an agent can store data in LS -- NRBA_{key}. If there are two agents on one page, and they both use the same key, they could overwrite each other since they would both use the same namespace in LS by default.
4443
* The value can be overridden in the constructor, but will default to a unique 16 character hex string
4544
* expiresMs and inactiveMs are used to "expire" the session, but can be overridden in the constructor. Pass 0 to disable expiration timers.
4645
*/
4746
constructor (opts) {
48-
const { agentIdentifier, key, storage } = opts
47+
const { agentRef, key, storage } = opts
4948

50-
if (!agentIdentifier || !key || !storage) {
51-
throw new Error(`Missing required field(s):${!agentIdentifier ? ' agentID' : ''}${!key ? ' key' : ''}${!storage ? ' storage' : ''}`)
49+
if (!agentRef || !key || !storage) {
50+
throw new Error(`Missing required field(s):${!agentRef ? ' agentRef' : ''}${!key ? ' key' : ''}${!storage ? ' storage' : ''}`)
5251
}
53-
this.agentIdentifier = agentIdentifier
52+
this.agentRef = agentRef
5453
this.storage = storage
5554
this.state = {}
5655

5756
// key is intended to act as the k=v pair
5857
this.key = key
5958

60-
this.ee = ee.get(agentIdentifier)
59+
this.ee = agentRef.ee
6160
wrapEvents(this.ee)
6261
this.setup(opts)
6362

@@ -243,7 +242,7 @@ export class SessionEntity {
243242
delete this.isNew
244243

245244
this.setup({
246-
agentIdentifier: this.agentIdentifier,
245+
agentRef: this.agentRef,
247246
key: this.key,
248247
storage: this.storage,
249248
expiresMs: this.expiresMs,

src/common/util/console.js

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/**
2-
* Copyright 2020-2025 New Relic, Inc. All rights reserved.
2+
* Copyright 2020-2026 New Relic, Inc. All rights reserved.
33
* SPDX-License-Identifier: Apache-2.0
44
*/
55

@@ -17,7 +17,6 @@ export function warn (code, secondary) {
1717
if (typeof console.debug !== 'function') return
1818
console.debug(`New Relic Warning: https://github.com/newrelic/newrelic-browser-agent/blob/main/docs/warning-codes.md#${code}`, secondary)
1919
dispatchGlobalEvent({
20-
agentIdentifier: null,
2120
drained: null,
2221
type: 'data',
2322
name: 'warn',

src/common/util/feature-flags.js

Lines changed: 5 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,35 +1,23 @@
11
/**
2-
* Copyright 2020-2025 New Relic, Inc. All rights reserved.
2+
* Copyright 2020-2026 New Relic, Inc. All rights reserved.
33
* SPDX-License-Identifier: Apache-2.0
44
*/
55
import { dispatchGlobalEvent } from '../dispatch/global-event'
66

7-
const sentIds = new Set()
8-
9-
/** A map of feature flags and their values as provided by the rum call -- scoped by agent ID */
10-
export const activatedFeatures = {}
11-
127
/**
13-
* Sets the activatedFeatures object, dispatches the global loaded event,
8+
* Sets the activatedFeatures on the agentRef, dispatches the global loaded event,
149
* and emits the rumresp flag to features
1510
* @param {{[key:string]:number}} flags key-val pair of flag names and numeric
16-
* @param {string} agentIdentifier agent instance identifier
11+
* @param {Object} agentRef agent reference
1712
* @returns {void}
1813
*/
1914
export function activateFeatures (flags, agentRef) {
20-
const agentIdentifier = agentRef.agentIdentifier
21-
activatedFeatures[agentIdentifier] ??= {}
22-
if (!flags || typeof flags !== 'object') return
23-
if (sentIds.has(agentIdentifier)) return
24-
15+
if (!flags || typeof flags !== 'object' || !!agentRef.runtime.activatedFeatures) return
2516
agentRef.ee.emit('rumresp', [flags])
26-
activatedFeatures[agentIdentifier] = flags
27-
28-
sentIds.add(agentIdentifier)
17+
agentRef.runtime.activatedFeatures = flags
2918

3019
// let any window level subscribers know that the agent is running, per install docs
3120
dispatchGlobalEvent({
32-
agentIdentifier,
3321
loaded: true,
3422
drained: true,
3523
type: 'lifecycle',

src/features/jserrors/aggregate/index.js

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -187,10 +187,6 @@ export class Aggregate extends AggregateBase {
187187
// still send EE events for other features such as above, but stop this one from aggregating internal data
188188
if (this.blocked) return
189189

190-
if (err.__newrelic?.[this.agentIdentifier]) {
191-
params._interactionId = err.__newrelic[this.agentIdentifier].interactionId
192-
params._interactionNodeId = err.__newrelic[this.agentIdentifier].interactionNodeId
193-
}
194190
if (err.__newrelic?.socketId) {
195191
customAttributes.socketId = err.__newrelic.socketId
196192
}

src/features/page_view_event/aggregate/index.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ export class Aggregate extends AggregateBase {
7979
us: info.user,
8080
ac: info.account,
8181
pr: info.product,
82-
af: getActivatedFeaturesFlags(this.agentIdentifier).join(','),
82+
af: getActivatedFeaturesFlags(this.agentRef).join(','),
8383
...measures,
8484
xx: info.extra,
8585
ua: info.userAttributes,

0 commit comments

Comments
 (0)