@newrelic/browser-agent 1.284.1 → 1.285.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (49) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/dist/cjs/common/constants/env.cdn.js +1 -1
  3. package/dist/cjs/common/constants/env.npm.js +1 -1
  4. package/dist/cjs/common/harvest/harvester.js +29 -2
  5. package/dist/cjs/common/util/feature-flags.js +6 -1
  6. package/dist/cjs/features/logging/aggregate/index.js +1 -1
  7. package/dist/cjs/features/session_replay/shared/recorder.js +3 -10
  8. package/dist/cjs/features/session_replay/shared/utils.js +12 -0
  9. package/dist/cjs/features/utils/aggregate-base.js +2 -2
  10. package/dist/cjs/features/utils/event-store-manager.js +15 -1
  11. package/dist/cjs/loaders/api/api.js +13 -0
  12. package/dist/cjs/loaders/configure/configure.js +16 -0
  13. package/dist/cjs/loaders/features/features.js +4 -3
  14. package/dist/esm/common/constants/env.cdn.js +1 -1
  15. package/dist/esm/common/constants/env.npm.js +1 -1
  16. package/dist/esm/common/harvest/harvester.js +30 -3
  17. package/dist/esm/common/util/feature-flags.js +6 -1
  18. package/dist/esm/features/logging/aggregate/index.js +1 -1
  19. package/dist/esm/features/session_replay/shared/recorder.js +2 -9
  20. package/dist/esm/features/session_replay/shared/utils.js +11 -0
  21. package/dist/esm/features/utils/aggregate-base.js +2 -2
  22. package/dist/esm/features/utils/event-store-manager.js +15 -1
  23. package/dist/esm/loaders/api/api.js +13 -0
  24. package/dist/esm/loaders/configure/configure.js +16 -0
  25. package/dist/esm/loaders/features/features.js +3 -2
  26. package/dist/types/common/harvest/harvester.d.ts.map +1 -1
  27. package/dist/types/common/util/feature-flags.d.ts.map +1 -1
  28. package/dist/types/features/logging/aggregate/index.d.ts +0 -1
  29. package/dist/types/features/logging/aggregate/index.d.ts.map +1 -1
  30. package/dist/types/features/session_replay/shared/recorder.d.ts.map +1 -1
  31. package/dist/types/features/session_replay/shared/utils.d.ts +1 -0
  32. package/dist/types/features/session_replay/shared/utils.d.ts.map +1 -1
  33. package/dist/types/features/utils/event-store-manager.d.ts +5 -1
  34. package/dist/types/features/utils/event-store-manager.d.ts.map +1 -1
  35. package/dist/types/loaders/api/api.d.ts.map +1 -1
  36. package/dist/types/loaders/configure/configure.d.ts.map +1 -1
  37. package/dist/types/loaders/features/features.d.ts +2 -0
  38. package/dist/types/loaders/features/features.d.ts.map +1 -1
  39. package/package.json +1 -1
  40. package/src/common/harvest/harvester.js +33 -3
  41. package/src/common/util/feature-flags.js +8 -1
  42. package/src/features/logging/aggregate/index.js +2 -2
  43. package/src/features/session_replay/shared/recorder.js +2 -9
  44. package/src/features/session_replay/shared/utils.js +12 -0
  45. package/src/features/utils/aggregate-base.js +2 -2
  46. package/src/features/utils/event-store-manager.js +15 -1
  47. package/src/loaders/api/api.js +10 -0
  48. package/src/loaders/configure/configure.js +13 -0
  49. package/src/loaders/features/features.js +3 -2
package/CHANGELOG.md CHANGED
@@ -3,6 +3,20 @@
3
3
  All notable changes to this project will be documented in this file.
4
4
  See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
5
5
 
6
+ ## [1.285.0](https://github.com/newrelic/newrelic-browser-agent/compare/v1.284.1...v1.285.0) (2025-03-18)
7
+
8
+
9
+ ### Features
10
+
11
+ * Decorate harvest requests with ht (hasTrace) param ([#1409](https://github.com/newrelic/newrelic-browser-agent/issues/1409)) ([b8ed2b0](https://github.com/newrelic/newrelic-browser-agent/commit/b8ed2b0aeef8b8db651ddb8001171f28785c7673))
12
+ * Inspection events ([#1413](https://github.com/newrelic/newrelic-browser-agent/issues/1413)) ([1832562](https://github.com/newrelic/newrelic-browser-agent/commit/1832562f52c1e2e26c49c2855ad1996d6251b803))
13
+
14
+
15
+ ### Bug Fixes
16
+
17
+ * Logging mode on session update ([#1417](https://github.com/newrelic/newrelic-browser-agent/issues/1417)) ([3f59afe](https://github.com/newrelic/newrelic-browser-agent/commit/3f59afef8a53848d80f7f32d28302b6b0bcf7d2f))
18
+ * Session Replay text masking for whitespace ([#1416](https://github.com/newrelic/newrelic-browser-agent/issues/1416)) ([97bf326](https://github.com/newrelic/newrelic-browser-agent/commit/97bf32655d6e608ea7248acc680d983b1d1e0eea))
19
+
6
20
  ## [1.284.1](https://github.com/newrelic/newrelic-browser-agent/compare/v1.284.0...v1.284.1) (2025-03-11)
7
21
 
8
22
 
@@ -17,7 +17,7 @@ exports.VERSION = exports.RRWEB_VERSION = exports.DIST_METHOD = exports.BUILD_EN
17
17
  /**
18
18
  * Exposes the version of the agent
19
19
  */
20
- const VERSION = exports.VERSION = "1.284.1";
20
+ const VERSION = exports.VERSION = "1.285.0";
21
21
 
22
22
  /**
23
23
  * Exposes the build type of the agent
@@ -17,7 +17,7 @@ exports.VERSION = exports.RRWEB_VERSION = exports.DIST_METHOD = exports.BUILD_EN
17
17
  /**
18
18
  * Exposes the version of the agent
19
19
  */
20
- const VERSION = exports.VERSION = "1.284.1";
20
+ const VERSION = exports.VERSION = "1.285.0";
21
21
 
22
22
  /**
23
23
  * Exposes the build type of the agent
@@ -18,6 +18,8 @@ var _encode = require("../url/encode");
18
18
  var _console = require("../util/console");
19
19
  var _stringify = require("../util/stringify");
20
20
  var _submitData = require("../util/submit-data");
21
+ var _featureFlags = require("../util/feature-flags");
22
+ var _globalEvent = require("../dispatch/global-event");
21
23
  /**
22
24
  * Copyright 2020-2025 New Relic, Inc. All rights reserved.
23
25
  * SPDX-License-Identifier: Apache-2.0
@@ -88,7 +90,8 @@ class Harvester {
88
90
  localOpts,
89
91
  submitMethod,
90
92
  cbFinished,
91
- raw: aggregateInst.harvestOpts.raw
93
+ raw: aggregateInst.harvestOpts.raw,
94
+ featureName: aggregateInst.featureName
92
95
  });
93
96
  ranSend = true;
94
97
  });
@@ -130,7 +133,8 @@ function send(agentRef, {
130
133
  localOpts = {},
131
134
  submitMethod,
132
135
  cbFinished,
133
- raw
136
+ raw,
137
+ featureName
134
138
  }) {
135
139
  if (!agentRef.info.errorBeacon) return false;
136
140
  let {
@@ -211,6 +215,22 @@ function send(agentRef, {
211
215
  });
212
216
  }
213
217
  }
218
+ (0, _globalEvent.dispatchGlobalEvent)({
219
+ agentIdentifier: agentRef.agentIdentifier,
220
+ loaded: !!_featureFlags.activatedFeatures?.[agentRef.agentIdentifier],
221
+ type: 'data',
222
+ name: 'harvest',
223
+ feature: featureName,
224
+ data: {
225
+ endpoint,
226
+ headers,
227
+ targetApp,
228
+ payload,
229
+ submitMethod: getSubmitMethodName(),
230
+ raw,
231
+ synchronousXhr: !!(localOpts.isFinalHarvest && _runtime.isWorkerScope)
232
+ }
233
+ });
214
234
  return true;
215
235
  function shouldRetry(status) {
216
236
  switch (status) {
@@ -221,6 +241,11 @@ function send(agentRef, {
221
241
  }
222
242
  return status >= 502 && status <= 504 || status >= 512 && status <= 530;
223
243
  }
244
+ function getSubmitMethodName() {
245
+ if (submitMethod === _submitData.xhr) return 'xhr';
246
+ if (submitMethod === _submitData.xhrFetch) return 'fetch';
247
+ return 'beacon';
248
+ }
224
249
  }
225
250
 
226
251
  /**
@@ -252,12 +277,14 @@ function cleanPayload(payload = {}) {
252
277
  function baseQueryString(agentRef, qs, endpoint, applicationID) {
253
278
  const ref = agentRef.runtime.obfuscator.obfuscateString((0, _cleanUrl.cleanURL)('' + _runtime.globalScope.location));
254
279
  const hr = agentRef.runtime.session?.state.sessionReplayMode === 1 && endpoint !== _features.JSERRORS;
280
+ const ht = agentRef.runtime.session?.state.sessionTraceMode === 1 && ![_features.LOGS, _features.BLOBS].includes(endpoint);
255
281
  const qps = ['a=' + applicationID, (0, _encode.param)('sa', agentRef.info.sa ? '' + agentRef.info.sa : ''), (0, _encode.param)('v', _env.VERSION), transactionNameParam(), (0, _encode.param)('ct', agentRef.runtime.customTransaction), '&rst=' + (0, _now.now)(), '&ck=0',
256
282
  // ck param DEPRECATED - still expected by backend
257
283
  '&s=' + (agentRef.runtime.session?.state.value || '0'),
258
284
  // the 0 id encaps all untrackable and default traffic
259
285
  (0, _encode.param)('ref', ref), (0, _encode.param)('ptid', agentRef.runtime.ptid ? '' + agentRef.runtime.ptid : '')];
260
286
  if (hr) qps.push((0, _encode.param)('hr', '1', qs));
287
+ if (ht) qps.push((0, _encode.param)('ht', '1', qs));
261
288
  return qps.join('');
262
289
 
263
290
  // Constructs the transaction name param for the beacon URL.
@@ -35,6 +35,11 @@ function activateFeatures(flags, agentIdentifier) {
35
35
 
36
36
  // let any window level subscribers know that the agent is running
37
37
  (0, _globalEvent.dispatchGlobalEvent)({
38
- loaded: true
38
+ agentIdentifier,
39
+ loaded: true,
40
+ type: 'lifecycle',
41
+ name: 'load',
42
+ feature: undefined,
43
+ data: flags
39
44
  });
40
45
  }
@@ -33,7 +33,7 @@ class Aggregate extends _aggregateBase.AggregateBase {
33
33
  });
34
34
  this.ee.on(_constants2.SESSION_EVENTS.UPDATE, (type, data) => {
35
35
  if (this.blocked || type !== _constants2.SESSION_EVENT_TYPES.CROSS_TAB) return;
36
- if (this.mode !== _constants.LOGGING_MODE.OFF && data.loggingMode === _constants.LOGGING_MODE.OFF) this.abort(_constants3.ABORT_REASONS.CROSS_TAB);else this.mode = data.loggingMode;
36
+ if (this.loggingMode !== _constants.LOGGING_MODE.OFF && data.loggingMode === _constants.LOGGING_MODE.OFF) this.abort(_constants3.ABORT_REASONS.CROSS_TAB);else this.loggingMode = data.loggingMode;
37
37
  });
38
38
  this.harvestOpts.raw = true;
39
39
  this.waitForFlags(['log']).then(([loggingMode]) => {
@@ -90,14 +90,7 @@ class Recorder {
90
90
  inline_images,
91
91
  collect_fonts
92
92
  } = this.parent.agentRef.init.session_replay;
93
- const customMasker = (text, element) => {
94
- try {
95
- if (typeof element?.type === 'string' && element.type.toLowerCase() !== 'password' && (element?.dataset?.nrUnmask !== undefined || element?.classList?.contains('nr-unmask'))) return text;
96
- } catch (err) {
97
- // likely an element was passed to this handler that was invalid and was missing attributes or methods
98
- }
99
- return '*'.repeat(text?.length || 0);
100
- };
93
+
101
94
  // set up rrweb configurations for maximum privacy --
102
95
  // https://newrelic.atlassian.net/wiki/spaces/O11Y/pages/2792293280/2023+02+28+Browser+-+Session+Replay#Configuration-options
103
96
  const stop = (0, _rrweb.record)({
@@ -108,9 +101,9 @@ class Recorder {
108
101
  blockSelector: block_selector,
109
102
  maskInputOptions: mask_input_options,
110
103
  maskTextSelector: mask_text_selector,
111
- maskTextFn: customMasker,
104
+ maskTextFn: _utils.customMasker,
112
105
  maskAllInputs: mask_all_inputs,
113
- maskInputFn: customMasker,
106
+ maskInputFn: _utils.customMasker,
114
107
  inlineStylesheet: true,
115
108
  inlineImages: inline_images,
116
109
  collectFonts: collect_fonts,
@@ -4,6 +4,7 @@ Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
6
  exports.buildNRMetaNode = buildNRMetaNode;
7
+ exports.customMasker = customMasker;
7
8
  exports.hasReplayPrerequisite = hasReplayPrerequisite;
8
9
  exports.isPreloadAllowed = isPreloadAllowed;
9
10
  var _nreum = require("../../../common/window/nreum");
@@ -35,4 +36,15 @@ function buildNRMetaNode(timestamp, timeKeeper) {
35
36
  correctedOriginTime: timeKeeper.correctedOriginTime,
36
37
  originTimeDiff: Math.floor(_runtime.originTime - timeKeeper.correctedOriginTime)
37
38
  };
39
+ }
40
+ function customMasker(text, element) {
41
+ try {
42
+ if (typeof element?.type === 'string') {
43
+ if (element.type.toLowerCase() === 'password') return '*'.repeat(text?.length || 0);
44
+ if (element?.dataset?.nrUnmask !== undefined || element?.classList?.contains('nr-unmask')) return text;
45
+ }
46
+ } catch (err) {
47
+ // likely an element was passed to this handler that was invalid and was missing attributes or methods
48
+ }
49
+ return typeof text === 'string' ? text.replace(/[\S]/g, '*') : '*'.repeat(text?.length || 0);
38
50
  }
@@ -43,7 +43,7 @@ class AggregateBase extends _featureBase.FeatureBase {
43
43
  This was necessary to prevent race cond. issues where the event buffer was checked before the feature could "block" itself.
44
44
  Its easier to just keep an empty event buffer in place. */
45
45
  default:
46
- this.events = new _eventStoreManager.EventStoreManager(agentRef.mainAppKey, 1);
46
+ this.events = new _eventStoreManager.EventStoreManager(agentRef.mainAppKey, 1, agentRef.agentIdentifier, this.featureName);
47
47
  break;
48
48
  }
49
49
  this.harvestOpts = {}; // features aggregate classes can define custom opts for when their harvest is called
@@ -170,7 +170,7 @@ class AggregateBase extends _featureBase.FeatureBase {
170
170
  appId: agentRef.info.applicationID
171
171
  };
172
172
  // Create a single Aggregator for this agent if DNE yet; to be used by jserror endpoint features.
173
- if (!agentRef.sharedAggregator) agentRef.sharedAggregator = new _eventStoreManager.EventStoreManager(agentRef.mainAppKey, 2);
173
+ if (!agentRef.sharedAggregator) agentRef.sharedAggregator = new _eventStoreManager.EventStoreManager(agentRef.mainAppKey, 2, agentRef.agentIdentifier, 'shared_aggregator');
174
174
  if (!agentRef.runtime.harvester) agentRef.runtime.harvester = new _harvester.Harvester(agentRef);
175
175
  }
176
176
 
@@ -5,6 +5,8 @@ Object.defineProperty(exports, "__esModule", {
5
5
  });
6
6
  exports.EventStoreManager = void 0;
7
7
  var _eventAggregator = require("../../common/aggregate/event-aggregator");
8
+ var _globalEvent = require("../../common/dispatch/global-event");
9
+ var _featureFlags = require("../../common/util/feature-flags");
8
10
  var _eventBuffer = require("./event-buffer");
9
11
  /**
10
12
  * Copyright 2020-2025 New Relic, Inc. All rights reserved.
@@ -19,12 +21,16 @@ class EventStoreManager {
19
21
  /**
20
22
  * @param {object} defaultTarget - should contain licenseKey and appId of the main app from NREUM.info at startup
21
23
  * @param {1|2} storageChoice - the type of storage to use in this manager; 'EventBuffer' (1), 'EventAggregator' (2)
24
+ * @param {string} agentIdentifier - agent identifier used in inspection events
25
+ * @param {string} featureName - feature name used in inspection events for non-shared aggregators
22
26
  */
23
- constructor(defaultTarget, storageChoice) {
27
+ constructor(defaultTarget, storageChoice, agentIdentifier, featureName) {
24
28
  this.mainApp = defaultTarget;
25
29
  this.StorageClass = storageChoice === 1 ? _eventBuffer.EventBuffer : _eventAggregator.EventAggregator;
26
30
  this.appStorageMap = new Map();
27
31
  this.appStorageMap.set(defaultTarget, new this.StorageClass());
32
+ this.agentIdentifier = agentIdentifier;
33
+ this.featureName = featureName;
28
34
  }
29
35
 
30
36
  // This class must contain an union of all methods from all supported storage classes and conceptualize away the target app argument.
@@ -50,6 +56,14 @@ class EventStoreManager {
50
56
  * @returns {boolean} True if the event was successfully added
51
57
  */
52
58
  add(event, target) {
59
+ (0, _globalEvent.dispatchGlobalEvent)({
60
+ agentIdentifier: this.agentIdentifier,
61
+ loaded: !!_featureFlags.activatedFeatures?.[this.agentIdentifier],
62
+ type: 'data',
63
+ name: 'buffer',
64
+ feature: this.featureName,
65
+ data: event
66
+ });
53
67
  if (target && !this.appStorageMap.has(target)) this.appStorageMap.set(target, new this.StorageClass());
54
68
  return this.appStorageMap.get(target || this.mainApp).add(event);
55
69
  }
@@ -23,6 +23,8 @@ var _constants3 = require("../../common/session/constants");
23
23
  var _constants4 = require("../../features/logging/constants");
24
24
  var _utils = require("../../features/logging/shared/utils");
25
25
  var _wrapLogger = require("../../common/wrap/wrap-logger");
26
+ var _globalEvent = require("../../common/dispatch/global-event");
27
+ var _featureFlags = require("../../common/util/feature-flags");
26
28
  function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); }
27
29
  function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && {}.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; } /**
28
30
  * Copyright 2020-2025 New Relic, Inc. All rights reserved.
@@ -199,6 +201,17 @@ function setAPI(agentIdentifier, forceDrain, runSoftNavOverSpa = false) {
199
201
  function apiCall(prefix, name, notSpa, bufferGroup) {
200
202
  return function () {
201
203
  (0, _handle.handle)(_constants.SUPPORTABILITY_METRIC_CHANNEL, ['API/' + name + '/called'], undefined, _features.FEATURE_NAMES.metrics, instanceEE);
204
+ (0, _globalEvent.dispatchGlobalEvent)({
205
+ agentIdentifier,
206
+ loaded: !!_featureFlags.activatedFeatures?.[agentIdentifier],
207
+ type: 'data',
208
+ name: 'api',
209
+ feature: prefix + name,
210
+ data: {
211
+ notSpa,
212
+ bufferGroup
213
+ }
214
+ });
202
215
  if (bufferGroup) (0, _handle.handle)(prefix + name, [notSpa ? (0, _now.now)() : performance.now(), ...arguments], notSpa ? null : this, bufferGroup, instanceEE); // no bufferGroup means only the SM is emitted
203
216
  return notSpa ? undefined : this; // returns the InteractionHandle which allows these methods to be chained
204
217
  };
@@ -14,6 +14,7 @@ var _featureFlags = require("../../common/util/feature-flags");
14
14
  var _runtime2 = require("../../common/constants/runtime");
15
15
  var _publicPath = require("./public-path");
16
16
  var _contextualEe = require("../../common/event-emitter/contextual-ee");
17
+ var _globalEvent = require("../../common/dispatch/global-event");
17
18
  /**
18
19
  * Copyright 2020-2025 New Relic, Inc. All rights reserved.
19
20
  * SPDX-License-Identifier: Apache-2.0
@@ -70,5 +71,20 @@ function configure(agent, opts = {}, loaderType, forceDrain) {
70
71
  agent.ee = _contextualEe.ee.get(agent.agentIdentifier);
71
72
  if (agent.api === undefined) agent.api = (0, _api.setAPI)(agent.agentIdentifier, forceDrain, agent.runSoftNavOverSpa);
72
73
  if (agent.exposed === undefined) agent.exposed = exposed;
74
+ if (!alreadySetOnce) {
75
+ (0, _globalEvent.dispatchGlobalEvent)({
76
+ agentIdentifier: agent.agentIdentifier,
77
+ loaded: !!_featureFlags.activatedFeatures?.[agent.agentIdentifier],
78
+ type: 'lifecycle',
79
+ name: 'initialize',
80
+ feature: undefined,
81
+ data: {
82
+ init: updatedInit,
83
+ info,
84
+ loader_config,
85
+ runtime
86
+ }
87
+ });
88
+ }
73
89
  alreadySetOnce = true;
74
90
  }
@@ -3,7 +3,7 @@
3
3
  Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
- exports.featurePriority = exports.RUM = exports.JSERRORS = exports.FEATURE_TO_ENDPOINT = exports.FEATURE_NAMES = exports.EVENTS = void 0;
6
+ exports.featurePriority = exports.RUM = exports.LOGS = exports.JSERRORS = exports.FEATURE_TO_ENDPOINT = exports.FEATURE_NAMES = exports.EVENTS = exports.BLOBS = void 0;
7
7
  /**
8
8
  * Copyright 2020-2025 New Relic, Inc. All rights reserved.
9
9
  * SPDX-License-Identifier: Apache-2.0
@@ -12,8 +12,9 @@ exports.featurePriority = exports.RUM = exports.JSERRORS = exports.FEATURE_TO_EN
12
12
  // To reduce build size a bit:
13
13
  const EVENTS = exports.EVENTS = 'events';
14
14
  const JSERRORS = exports.JSERRORS = 'jserrors';
15
- const BLOBS = 'browser/blobs';
15
+ const BLOBS = exports.BLOBS = 'browser/blobs';
16
16
  const RUM = exports.RUM = 'rum';
17
+ const LOGS = exports.LOGS = 'browser/logs';
17
18
  const FEATURE_NAMES = exports.FEATURE_NAMES = {
18
19
  ajax: 'ajax',
19
20
  genericEvents: 'generic_events',
@@ -59,6 +60,6 @@ const FEATURE_TO_ENDPOINT = exports.FEATURE_TO_ENDPOINT = {
59
60
  [FEATURE_NAMES.jserrors]: JSERRORS,
60
61
  [FEATURE_NAMES.sessionTrace]: BLOBS,
61
62
  [FEATURE_NAMES.sessionReplay]: BLOBS,
62
- [FEATURE_NAMES.logging]: 'browser/logs',
63
+ [FEATURE_NAMES.logging]: LOGS,
63
64
  [FEATURE_NAMES.genericEvents]: 'ins'
64
65
  };
@@ -11,7 +11,7 @@
11
11
  /**
12
12
  * Exposes the version of the agent
13
13
  */
14
- export const VERSION = "1.284.1";
14
+ export const VERSION = "1.285.0";
15
15
 
16
16
  /**
17
17
  * Exposes the build type of the agent
@@ -11,7 +11,7 @@
11
11
  /**
12
12
  * Exposes the version of the agent
13
13
  */
14
- export const VERSION = "1.284.1";
14
+ export const VERSION = "1.285.0";
15
15
 
16
16
  /**
17
17
  * Exposes the build type of the agent
@@ -3,7 +3,7 @@
3
3
  * SPDX-License-Identifier: Apache-2.0
4
4
  */
5
5
  import { SUPPORTABILITY_METRIC_CHANNEL } from '../../features/metrics/constants';
6
- import { FEATURE_TO_ENDPOINT, JSERRORS, RUM, EVENTS, FEATURE_NAMES } from '../../loaders/features/features';
6
+ import { FEATURE_TO_ENDPOINT, JSERRORS, RUM, EVENTS, FEATURE_NAMES, BLOBS, LOGS } from '../../loaders/features/features';
7
7
  import { VERSION } from "../constants/env.npm";
8
8
  import { globalScope, isWorkerScope } from '../constants/runtime';
9
9
  import { handle } from '../event-emitter/handle';
@@ -16,6 +16,8 @@ import { obj, param } from '../url/encode';
16
16
  import { warn } from '../util/console';
17
17
  import { stringify } from '../util/stringify';
18
18
  import { getSubmitMethod, xhr as xhrMethod, xhrFetch as fetchMethod } from '../util/submit-data';
19
+ import { activatedFeatures } from '../util/feature-flags';
20
+ import { dispatchGlobalEvent } from '../dispatch/global-event';
19
21
  const RETRY_FAILED = 'Harvester/Retry/Failed/';
20
22
  const RETRY_SUCCEEDED = 'Harvester/Retry/Succeeded/';
21
23
  export class Harvester {
@@ -81,7 +83,8 @@ export class Harvester {
81
83
  localOpts,
82
84
  submitMethod,
83
85
  cbFinished,
84
- raw: aggregateInst.harvestOpts.raw
86
+ raw: aggregateInst.harvestOpts.raw,
87
+ featureName: aggregateInst.featureName
85
88
  });
86
89
  ranSend = true;
87
90
  });
@@ -123,7 +126,8 @@ function send(agentRef, {
123
126
  localOpts = {},
124
127
  submitMethod,
125
128
  cbFinished,
126
- raw
129
+ raw,
130
+ featureName
127
131
  }) {
128
132
  if (!agentRef.info.errorBeacon) return false;
129
133
  let {
@@ -204,6 +208,22 @@ function send(agentRef, {
204
208
  });
205
209
  }
206
210
  }
211
+ dispatchGlobalEvent({
212
+ agentIdentifier: agentRef.agentIdentifier,
213
+ loaded: !!activatedFeatures?.[agentRef.agentIdentifier],
214
+ type: 'data',
215
+ name: 'harvest',
216
+ feature: featureName,
217
+ data: {
218
+ endpoint,
219
+ headers,
220
+ targetApp,
221
+ payload,
222
+ submitMethod: getSubmitMethodName(),
223
+ raw,
224
+ synchronousXhr: !!(localOpts.isFinalHarvest && isWorkerScope)
225
+ }
226
+ });
207
227
  return true;
208
228
  function shouldRetry(status) {
209
229
  switch (status) {
@@ -214,6 +234,11 @@ function send(agentRef, {
214
234
  }
215
235
  return status >= 502 && status <= 504 || status >= 512 && status <= 530;
216
236
  }
237
+ function getSubmitMethodName() {
238
+ if (submitMethod === xhrMethod) return 'xhr';
239
+ if (submitMethod === fetchMethod) return 'fetch';
240
+ return 'beacon';
241
+ }
217
242
  }
218
243
 
219
244
  /**
@@ -245,12 +270,14 @@ function cleanPayload(payload = {}) {
245
270
  function baseQueryString(agentRef, qs, endpoint, applicationID) {
246
271
  const ref = agentRef.runtime.obfuscator.obfuscateString(cleanURL('' + globalScope.location));
247
272
  const hr = agentRef.runtime.session?.state.sessionReplayMode === 1 && endpoint !== JSERRORS;
273
+ const ht = agentRef.runtime.session?.state.sessionTraceMode === 1 && ![LOGS, BLOBS].includes(endpoint);
248
274
  const qps = ['a=' + applicationID, param('sa', agentRef.info.sa ? '' + agentRef.info.sa : ''), param('v', VERSION), transactionNameParam(), param('ct', agentRef.runtime.customTransaction), '&rst=' + now(), '&ck=0',
249
275
  // ck param DEPRECATED - still expected by backend
250
276
  '&s=' + (agentRef.runtime.session?.state.value || '0'),
251
277
  // the 0 id encaps all untrackable and default traffic
252
278
  param('ref', ref), param('ptid', agentRef.runtime.ptid ? '' + agentRef.runtime.ptid : '')];
253
279
  if (hr) qps.push(param('hr', '1', qs));
280
+ if (ht) qps.push(param('ht', '1', qs));
254
281
  return qps.join('');
255
282
 
256
283
  // Constructs the transaction name param for the beacon URL.
@@ -27,6 +27,11 @@ export function activateFeatures(flags, agentIdentifier) {
27
27
 
28
28
  // let any window level subscribers know that the agent is running
29
29
  dispatchGlobalEvent({
30
- loaded: true
30
+ agentIdentifier,
31
+ loaded: true,
32
+ type: 'lifecycle',
33
+ name: 'load',
34
+ feature: undefined,
35
+ data: flags
31
36
  });
32
37
  }
@@ -26,7 +26,7 @@ export class Aggregate extends AggregateBase {
26
26
  });
27
27
  this.ee.on(SESSION_EVENTS.UPDATE, (type, data) => {
28
28
  if (this.blocked || type !== SESSION_EVENT_TYPES.CROSS_TAB) return;
29
- if (this.mode !== LOGGING_MODE.OFF && data.loggingMode === LOGGING_MODE.OFF) this.abort(ABORT_REASONS.CROSS_TAB);else this.mode = data.loggingMode;
29
+ if (this.loggingMode !== LOGGING_MODE.OFF && data.loggingMode === LOGGING_MODE.OFF) this.abort(ABORT_REASONS.CROSS_TAB);else this.loggingMode = data.loggingMode;
30
30
  });
31
31
  this.harvestOpts.raw = true;
32
32
  this.waitForFlags(['log']).then(([loggingMode]) => {
@@ -11,7 +11,7 @@ import { stylesheetEvaluator } from './stylesheet-evaluator';
11
11
  import { handle } from '../../../common/event-emitter/handle';
12
12
  import { SUPPORTABILITY_METRIC_CHANNEL } from '../../metrics/constants';
13
13
  import { FEATURE_NAMES } from '../../../loaders/features/features';
14
- import { buildNRMetaNode } from './utils';
14
+ import { buildNRMetaNode, customMasker } from './utils';
15
15
  import { IDEAL_PAYLOAD_SIZE } from '../../../common/constants/agent-constants';
16
16
  import { AggregateBase } from '../../utils/aggregate-base';
17
17
  export class Recorder {
@@ -83,14 +83,7 @@ export class Recorder {
83
83
  inline_images,
84
84
  collect_fonts
85
85
  } = this.parent.agentRef.init.session_replay;
86
- const customMasker = (text, element) => {
87
- try {
88
- if (typeof element?.type === 'string' && element.type.toLowerCase() !== 'password' && (element?.dataset?.nrUnmask !== undefined || element?.classList?.contains('nr-unmask'))) return text;
89
- } catch (err) {
90
- // likely an element was passed to this handler that was invalid and was missing attributes or methods
91
- }
92
- return '*'.repeat(text?.length || 0);
93
- };
86
+
94
87
  // set up rrweb configurations for maximum privacy --
95
88
  // https://newrelic.atlassian.net/wiki/spaces/O11Y/pages/2792293280/2023+02+28+Browser+-+Session+Replay#Configuration-options
96
89
  const stop = recorder({
@@ -26,4 +26,15 @@ export function buildNRMetaNode(timestamp, timeKeeper) {
26
26
  correctedOriginTime: timeKeeper.correctedOriginTime,
27
27
  originTimeDiff: Math.floor(originTime - timeKeeper.correctedOriginTime)
28
28
  };
29
+ }
30
+ export function customMasker(text, element) {
31
+ try {
32
+ if (typeof element?.type === 'string') {
33
+ if (element.type.toLowerCase() === 'password') return '*'.repeat(text?.length || 0);
34
+ if (element?.dataset?.nrUnmask !== undefined || element?.classList?.contains('nr-unmask')) return text;
35
+ }
36
+ } catch (err) {
37
+ // likely an element was passed to this handler that was invalid and was missing attributes or methods
38
+ }
39
+ return typeof text === 'string' ? text.replace(/[\S]/g, '*') : '*'.repeat(text?.length || 0);
29
40
  }
@@ -36,7 +36,7 @@ export class AggregateBase extends FeatureBase {
36
36
  This was necessary to prevent race cond. issues where the event buffer was checked before the feature could "block" itself.
37
37
  Its easier to just keep an empty event buffer in place. */
38
38
  default:
39
- this.events = new EventStoreManager(agentRef.mainAppKey, 1);
39
+ this.events = new EventStoreManager(agentRef.mainAppKey, 1, agentRef.agentIdentifier, this.featureName);
40
40
  break;
41
41
  }
42
42
  this.harvestOpts = {}; // features aggregate classes can define custom opts for when their harvest is called
@@ -163,7 +163,7 @@ export class AggregateBase extends FeatureBase {
163
163
  appId: agentRef.info.applicationID
164
164
  };
165
165
  // Create a single Aggregator for this agent if DNE yet; to be used by jserror endpoint features.
166
- if (!agentRef.sharedAggregator) agentRef.sharedAggregator = new EventStoreManager(agentRef.mainAppKey, 2);
166
+ if (!agentRef.sharedAggregator) agentRef.sharedAggregator = new EventStoreManager(agentRef.mainAppKey, 2, agentRef.agentIdentifier, 'shared_aggregator');
167
167
  if (!agentRef.runtime.harvester) agentRef.runtime.harvester = new Harvester(agentRef);
168
168
  }
169
169
 
@@ -3,6 +3,8 @@
3
3
  * SPDX-License-Identifier: Apache-2.0
4
4
  */
5
5
  import { EventAggregator } from '../../common/aggregate/event-aggregator';
6
+ import { dispatchGlobalEvent } from '../../common/dispatch/global-event';
7
+ import { activatedFeatures } from '../../common/util/feature-flags';
6
8
  import { EventBuffer } from './event-buffer';
7
9
 
8
10
  /**
@@ -13,12 +15,16 @@ export class EventStoreManager {
13
15
  /**
14
16
  * @param {object} defaultTarget - should contain licenseKey and appId of the main app from NREUM.info at startup
15
17
  * @param {1|2} storageChoice - the type of storage to use in this manager; 'EventBuffer' (1), 'EventAggregator' (2)
18
+ * @param {string} agentIdentifier - agent identifier used in inspection events
19
+ * @param {string} featureName - feature name used in inspection events for non-shared aggregators
16
20
  */
17
- constructor(defaultTarget, storageChoice) {
21
+ constructor(defaultTarget, storageChoice, agentIdentifier, featureName) {
18
22
  this.mainApp = defaultTarget;
19
23
  this.StorageClass = storageChoice === 1 ? EventBuffer : EventAggregator;
20
24
  this.appStorageMap = new Map();
21
25
  this.appStorageMap.set(defaultTarget, new this.StorageClass());
26
+ this.agentIdentifier = agentIdentifier;
27
+ this.featureName = featureName;
22
28
  }
23
29
 
24
30
  // This class must contain an union of all methods from all supported storage classes and conceptualize away the target app argument.
@@ -44,6 +50,14 @@ export class EventStoreManager {
44
50
  * @returns {boolean} True if the event was successfully added
45
51
  */
46
52
  add(event, target) {
53
+ dispatchGlobalEvent({
54
+ agentIdentifier: this.agentIdentifier,
55
+ loaded: !!activatedFeatures?.[this.agentIdentifier],
56
+ type: 'data',
57
+ name: 'buffer',
58
+ feature: this.featureName,
59
+ data: event
60
+ });
47
61
  if (target && !this.appStorageMap.has(target)) this.appStorageMap.set(target, new this.StorageClass());
48
62
  return this.appStorageMap.get(target || this.mainApp).add(event);
49
63
  }
@@ -20,6 +20,8 @@ import { MODE } from '../../common/session/constants';
20
20
  import { LOG_LEVELS } from '../../features/logging/constants';
21
21
  import { bufferLog } from '../../features/logging/shared/utils';
22
22
  import { wrapLogger } from '../../common/wrap/wrap-logger';
23
+ import { dispatchGlobalEvent } from '../../common/dispatch/global-event';
24
+ import { activatedFeatures } from '../../common/util/feature-flags';
23
25
  export function setTopLevelCallers() {
24
26
  const nr = gosCDN();
25
27
  apiMethods.forEach(f => {
@@ -191,6 +193,17 @@ export function setAPI(agentIdentifier, forceDrain, runSoftNavOverSpa = false) {
191
193
  function apiCall(prefix, name, notSpa, bufferGroup) {
192
194
  return function () {
193
195
  handle(SUPPORTABILITY_METRIC_CHANNEL, ['API/' + name + '/called'], undefined, FEATURE_NAMES.metrics, instanceEE);
196
+ dispatchGlobalEvent({
197
+ agentIdentifier,
198
+ loaded: !!activatedFeatures?.[agentIdentifier],
199
+ type: 'data',
200
+ name: 'api',
201
+ feature: prefix + name,
202
+ data: {
203
+ notSpa,
204
+ bufferGroup
205
+ }
206
+ });
194
207
  if (bufferGroup) handle(prefix + name, [notSpa ? now() : performance.now(), ...arguments], notSpa ? null : this, bufferGroup, instanceEE); // no bufferGroup means only the SM is emitted
195
208
  return notSpa ? undefined : this; // returns the InteractionHandle which allows these methods to be chained
196
209
  };
@@ -12,6 +12,7 @@ import { activatedFeatures } from '../../common/util/feature-flags';
12
12
  import { isWorkerScope } from '../../common/constants/runtime';
13
13
  import { redefinePublicPath } from './public-path';
14
14
  import { ee } from '../../common/event-emitter/contextual-ee';
15
+ import { dispatchGlobalEvent } from '../../common/dispatch/global-event';
15
16
  let alreadySetOnce = false; // the configure() function can run multiple times in agent lifecycle
16
17
 
17
18
  /**
@@ -63,5 +64,20 @@ export function configure(agent, opts = {}, loaderType, forceDrain) {
63
64
  agent.ee = ee.get(agent.agentIdentifier);
64
65
  if (agent.api === undefined) agent.api = setAPI(agent.agentIdentifier, forceDrain, agent.runSoftNavOverSpa);
65
66
  if (agent.exposed === undefined) agent.exposed = exposed;
67
+ if (!alreadySetOnce) {
68
+ dispatchGlobalEvent({
69
+ agentIdentifier: agent.agentIdentifier,
70
+ loaded: !!activatedFeatures?.[agent.agentIdentifier],
71
+ type: 'lifecycle',
72
+ name: 'initialize',
73
+ feature: undefined,
74
+ data: {
75
+ init: updatedInit,
76
+ info,
77
+ loader_config,
78
+ runtime
79
+ }
80
+ });
81
+ }
66
82
  alreadySetOnce = true;
67
83
  }
@@ -6,8 +6,9 @@
6
6
  // To reduce build size a bit:
7
7
  export const EVENTS = 'events';
8
8
  export const JSERRORS = 'jserrors';
9
- const BLOBS = 'browser/blobs';
9
+ export const BLOBS = 'browser/blobs';
10
10
  export const RUM = 'rum';
11
+ export const LOGS = 'browser/logs';
11
12
  export const FEATURE_NAMES = {
12
13
  ajax: 'ajax',
13
14
  genericEvents: 'generic_events',
@@ -53,6 +54,6 @@ export const FEATURE_TO_ENDPOINT = {
53
54
  [FEATURE_NAMES.jserrors]: JSERRORS,
54
55
  [FEATURE_NAMES.sessionTrace]: BLOBS,
55
56
  [FEATURE_NAMES.sessionReplay]: BLOBS,
56
- [FEATURE_NAMES.logging]: 'browser/logs',
57
+ [FEATURE_NAMES.logging]: LOGS,
57
58
  [FEATURE_NAMES.genericEvents]: 'ins'
58
59
  };
@@ -1 +1 @@
1
- {"version":3,"file":"harvester.d.ts","sourceRoot":"","sources":["../../../../src/common/harvest/harvester.js"],"names":[],"mappings":"AAsBA;IAIE,2BAcC;IAhBD,6BAA0B;IAGxB,cAAwB;IAe1B,wCASC;IAED;;;;;OAKG;IACH,iCAJW,MAAM,cACN,MAAM,GACJ,OAAO,CA8CnB;;CACF;8BAGY,OAAO,YAAY,EAAE,eAAe"}
1
+ {"version":3,"file":"harvester.d.ts","sourceRoot":"","sources":["../../../../src/common/harvest/harvester.js"],"names":[],"mappings":"AAwBA;IAIE,2BAcC;IAhBD,6BAA0B;IAGxB,cAAwB;IAe1B,wCASC;IAED;;;;;OAKG;IACH,iCAJW,MAAM,cACN,MAAM,GACJ,OAAO,CA+CnB;;CACF;8BAGY,OAAO,YAAY,EAAE,eAAe"}
@@ -1 +1 @@
1
- {"version":3,"file":"feature-flags.d.ts","sourceRoot":"","sources":["../../../../src/common/util/feature-flags.js"],"names":[],"mappings":"AAYA;;;;;;GAMG;AACH,wCAJW;IAAC,CAAC,GAAG,EAAC,MAAM,GAAE,MAAM,CAAA;CAAC,mBACrB,MAAM,GACJ,IAAI,CAehB;AAvBD,gGAAgG;AAChG,mCAAmC"}
1
+ {"version":3,"file":"feature-flags.d.ts","sourceRoot":"","sources":["../../../../src/common/util/feature-flags.js"],"names":[],"mappings":"AAYA;;;;;;GAMG;AACH,wCAJW;IAAC,CAAC,GAAG,EAAC,MAAM,GAAE,MAAM,CAAA;CAAC,mBACrB,MAAM,GACJ,IAAI,CAsBhB;AA9BD,gGAAgG;AAChG,mCAAmC"}
@@ -2,7 +2,6 @@ export class Aggregate extends AggregateBase {
2
2
  static featureName: string;
3
3
  constructor(agentRef: any);
4
4
  isSessionTrackingEnabled: any;
5
- mode: any;
6
5
  loggingMode: any;
7
6
  updateLoggingMode(loggingMode: any): void;
8
7
  handleLog(timestamp: any, message: any, attributes?: {}, level?: string): void;
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/features/logging/aggregate/index.js"],"names":[],"mappings":"AAiBA;IACE,2BAAiC;IACjC,2BAmCC;IAjCC,8BAA+G;IAUxG,UAA4B;IAc/B,iBAA4C;IAWlD,0CAKC;IAED,+EAuDC;IAED;;YAIM,0FAA0F;;;QAoB5F,0DAA0D;;QAM7D;IAED;;MAEC;IAED,yDAAyD;IACzD,yBAOC;IAED,yCAIC;CACF;8BApK6B,4BAA4B"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/features/logging/aggregate/index.js"],"names":[],"mappings":"AAiBA;IACE,2BAAiC;IACjC,2BAmCC;IAjCC,8BAA+G;IAUxG,iBAAmC;IAyB5C,0CAKC;IAED,+EAuDC;IAED;;YAIM,0FAA0F;;;QAoB5F,0DAA0D;;QAM7D;IAED;;MAEC;IAED,yDAAyD;IACzD,yBAOC;IAED,yCAIC;CACF;8BApK6B,4BAA4B"}
@@ -1 +1 @@
1
- {"version":3,"file":"recorder.d.ts","sourceRoot":"","sources":["../../../../../src/features/session_replay/shared/recorder.js"],"names":[],"mappings":"AAiBA;IAUE,yBAkBC;IAdC,iEAAiE;IACjE,mBAAsB;IACtB,6DAA6D;IAC7D,oCAAuC;IACvC,+IAA+I;IAC/I,yBAA4B;IAC5B,kIAAkI;IAClI,kBAAqB;IACrB,sDAAsD;IACtD,YAAoB;IACpB,0FAA0F;IAC1F,eAAyE;IACzE,uIAAuI;IACvI,0BAAyE;IAG3E;;;;;;;;;MAmBC;IAED,mFAAmF;IACnF,oBAKC;IAED,qDAAqD;IACrD,uBAoCC;IAED;;;;;OAKG;IACH,aAHW,GAAC,cACD,GAAC,QAgCX;IAED,0HAA0H;IAC1H,yCAoDC;IA1CG,8BAAoB;IA4CxB,0HAA0H;IAC1H,yBAOC;IAED,wBAEC;IAED,gCAAgC;IAChC,uCAGC;IAED;;;SAGK;IACL,oCAGC;;CACF;+BArO8B,mBAAmB"}
1
+ {"version":3,"file":"recorder.d.ts","sourceRoot":"","sources":["../../../../../src/features/session_replay/shared/recorder.js"],"names":[],"mappings":"AAiBA;IAUE,yBAkBC;IAdC,iEAAiE;IACjE,mBAAsB;IACtB,6DAA6D;IAC7D,oCAAuC;IACvC,+IAA+I;IAC/I,yBAA4B;IAC5B,kIAAkI;IAClI,kBAAqB;IACrB,sDAAsD;IACtD,YAAoB;IACpB,0FAA0F;IAC1F,eAAyE;IACzE,uIAAuI;IACvI,0BAAyE;IAG3E;;;;;;;;;MAmBC;IAED,mFAAmF;IACnF,oBAKC;IAED,qDAAqD;IACrD,uBA6BC;IAED;;;;;OAKG;IACH,aAHW,GAAC,cACD,GAAC,QAgCX;IAED,0HAA0H;IAC1H,yCAoDC;IA1CG,8BAAoB;IA4CxB,0HAA0H;IAC1H,yBAOC;IAED,wBAEC;IAED,gCAAgC;IAChC,uCAGC;IAED;;;SAGK;IACL,oCAGC;;CACF;+BA9N8B,mBAAmB"}
@@ -8,4 +8,5 @@ export function buildNRMetaNode(timestamp: any, timeKeeper: any): {
8
8
  correctedOriginTime: any;
9
9
  originTimeDiff: number;
10
10
  };
11
+ export function customMasker(text: any, element: any): any;
11
12
  //# sourceMappingURL=utils.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../../../../src/features/session_replay/shared/utils.js"],"names":[],"mappings":"AASA,6DAIC;AAED,wDAEC;AAED;;;;;;;EAUC"}
1
+ {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../../../../src/features/session_replay/shared/utils.js"],"names":[],"mappings":"AASA,6DAIC;AAED,wDAEC;AAED;;;;;;;EAUC;AAED,2DAUC"}
@@ -6,11 +6,15 @@ export class EventStoreManager {
6
6
  /**
7
7
  * @param {object} defaultTarget - should contain licenseKey and appId of the main app from NREUM.info at startup
8
8
  * @param {1|2} storageChoice - the type of storage to use in this manager; 'EventBuffer' (1), 'EventAggregator' (2)
9
+ * @param {string} agentIdentifier - agent identifier used in inspection events
10
+ * @param {string} featureName - feature name used in inspection events for non-shared aggregators
9
11
  */
10
- constructor(defaultTarget: object, storageChoice: 1 | 2);
12
+ constructor(defaultTarget: object, storageChoice: 1 | 2, agentIdentifier: string, featureName: string);
11
13
  mainApp: object;
12
14
  StorageClass: typeof EventAggregator | typeof EventBuffer;
13
15
  appStorageMap: Map<any, any>;
16
+ agentIdentifier: string;
17
+ featureName: string;
14
18
  /**
15
19
  * @param {object} optsIfPresent - exists if called during harvest interval, @see AggregateBase.makeHarvestPayload
16
20
  * @param {object} target - specific app's storage to check; if not provided, this method takes into account all apps recorded by this manager
@@ -1 +1 @@
1
- {"version":3,"file":"event-store-manager.d.ts","sourceRoot":"","sources":["../../../../src/features/utils/event-store-manager.js"],"names":[],"mappings":"AAOA;;;GAGG;AACH;IACE;;;OAGG;IACH,2BAHW,MAAM,iBACN,CAAC,GAAC,CAAC,EAOb;IAJC,gBAA4B;IAC5B,0DAAuE;IACvE,6BAA8B;IAMhC;;;;OAIG;IACH,uBAJW,MAAM,UACN,MAAM,GACJ,OAAO,CAWnB;IAED;;;;OAIG;IACH,WAJW,MAAM,UACN,MAAM,GACJ,OAAO,CAKnB;IAED,0GAA0G;IAC1G,8DAEC;IAED;;;;OAIG;IACH,mBAJW,MAAM,UACN,MAAM,SAUhB;IAED,2BAEC;IAED,wDAEC;IAED,2CAGC;IAED,4CAGC;IAGD,iDAMC;IAED,gDAMC;CACF;gCApG+B,yCAAyC;4BAC7C,gBAAgB"}
1
+ {"version":3,"file":"event-store-manager.d.ts","sourceRoot":"","sources":["../../../../src/features/utils/event-store-manager.js"],"names":[],"mappings":"AASA;;;GAGG;AACH;IACE;;;;;OAKG;IACH,2BALW,MAAM,iBACN,CAAC,GAAC,CAAC,mBACH,MAAM,eACN,MAAM,EAShB;IANC,gBAA4B;IAC5B,0DAAuE;IACvE,6BAA8B;IAE9B,wBAAsC;IACtC,oBAA8B;IAKhC;;;;OAIG;IACH,uBAJW,MAAM,UACN,MAAM,GACJ,OAAO,CAWnB;IAED;;;;OAIG;IACH,WAJW,MAAM,UACN,MAAM,GACJ,OAAO,CAanB;IAED,0GAA0G;IAC1G,8DAEC;IAED;;;;OAIG;IACH,mBAJW,MAAM,UACN,MAAM,SAUhB;IAED,2BAEC;IAED,wDAEC;IAED,2CAGC;IAED,4CAGC;IAGD,iDAMC;IAED,gDAMC;CACF;gCAlH+B,yCAAyC;4BAG7C,gBAAgB"}
@@ -1 +1 @@
1
- {"version":3,"file":"api.d.ts","sourceRoot":"","sources":["../../../../src/loaders/api/api.js"],"names":[],"mappings":"AAuBA,2CAiBC;AAID;;;;;;;;;;;;;qBAqEa,MAAM;iCAaN,MAAM,GAAC,IAAI;;;;;EAiGvB"}
1
+ {"version":3,"file":"api.d.ts","sourceRoot":"","sources":["../../../../src/loaders/api/api.js"],"names":[],"mappings":"AAyBA,2CAiBC;AAID;;;;;;;;;;;;;qBAqEa,MAAM;iCAaN,MAAM,GAAC,IAAI;;;;;EAyGvB"}
@@ -1 +1 @@
1
- {"version":3,"file":"configure.d.ts","sourceRoot":"","sources":["../../../../src/loaders/configure/configure.js"],"names":[],"mappings":"AAiBA;;GAEG;AACH,oGAmDC"}
1
+ {"version":3,"file":"configure.d.ts","sourceRoot":"","sources":["../../../../src/loaders/configure/configure.js"],"names":[],"mappings":"AAkBA;;GAEG;AACH,oGA+DC"}
@@ -4,7 +4,9 @@
4
4
  */
5
5
  export const EVENTS: "events";
6
6
  export const JSERRORS: "jserrors";
7
+ export const BLOBS: "browser/blobs";
7
8
  export const RUM: "rum";
9
+ export const LOGS: "browser/logs";
8
10
  export namespace FEATURE_NAMES {
9
11
  export let ajax: string;
10
12
  export let genericEvents: string;
@@ -1 +1 @@
1
- {"version":3,"file":"features.d.ts","sourceRoot":"","sources":["../../../../src/loaders/features/features.js"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,qBAAsB,QAAQ,CAAA;AAC9B,uBAAwB,UAAU,CAAA;AAElC,kBAAmB,KAAK,CAAA;;;;;;;;;;;;;;;AAoBxB;;;GAGG;AACH;;EAYC;AAED;;EAYC"}
1
+ {"version":3,"file":"features.d.ts","sourceRoot":"","sources":["../../../../src/loaders/features/features.js"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,qBAAsB,QAAQ,CAAA;AAC9B,uBAAwB,UAAU,CAAA;AAClC,oBAAqB,eAAe,CAAA;AACpC,kBAAmB,KAAK,CAAA;AACxB,mBAAoB,cAAc,CAAA;;;;;;;;;;;;;;;AAoBlC;;;GAGG;AACH;;EAYC;AAED;;EAYC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@newrelic/browser-agent",
3
- "version": "1.284.1",
3
+ "version": "1.285.0",
4
4
  "private": false,
5
5
  "author": "New Relic Browser Agent Team <browser-agent@newrelic.com>",
6
6
  "description": "New Relic Browser Agent",
@@ -3,7 +3,7 @@
3
3
  * SPDX-License-Identifier: Apache-2.0
4
4
  */
5
5
  import { SUPPORTABILITY_METRIC_CHANNEL } from '../../features/metrics/constants'
6
- import { FEATURE_TO_ENDPOINT, JSERRORS, RUM, EVENTS, FEATURE_NAMES } from '../../loaders/features/features'
6
+ import { FEATURE_TO_ENDPOINT, JSERRORS, RUM, EVENTS, FEATURE_NAMES, BLOBS, LOGS } from '../../loaders/features/features'
7
7
  import { VERSION } from '../constants/env'
8
8
  import { globalScope, isWorkerScope } from '../constants/runtime'
9
9
  import { handle } from '../event-emitter/handle'
@@ -16,6 +16,8 @@ import { obj, param } from '../url/encode'
16
16
  import { warn } from '../util/console'
17
17
  import { stringify } from '../util/stringify'
18
18
  import { getSubmitMethod, xhr as xhrMethod, xhrFetch as fetchMethod } from '../util/submit-data'
19
+ import { activatedFeatures } from '../util/feature-flags'
20
+ import { dispatchGlobalEvent } from '../dispatch/global-event'
19
21
 
20
22
  const RETRY_FAILED = 'Harvester/Retry/Failed/'
21
23
  const RETRY_SUCCEEDED = 'Harvester/Retry/Succeeded/'
@@ -80,7 +82,8 @@ export class Harvester {
80
82
  localOpts,
81
83
  submitMethod,
82
84
  cbFinished,
83
- raw: aggregateInst.harvestOpts.raw
85
+ raw: aggregateInst.harvestOpts.raw,
86
+ featureName: aggregateInst.featureName
84
87
  })
85
88
  ranSend = true
86
89
  })
@@ -114,7 +117,7 @@ const warnings = {}
114
117
  * @param {NetworkSendSpec} param0 Specification for sending data
115
118
  * @returns {boolean} True if a network call was made. Note that this does not mean or guarantee that it was successful.
116
119
  */
117
- function send (agentRef, { endpoint, targetApp, payload, localOpts = {}, submitMethod, cbFinished, raw }) {
120
+ function send (agentRef, { endpoint, targetApp, payload, localOpts = {}, submitMethod, cbFinished, raw, featureName }) {
118
121
  if (!agentRef.info.errorBeacon) return false
119
122
 
120
123
  let { body, qs } = cleanPayload(payload)
@@ -172,6 +175,24 @@ function send (agentRef, { endpoint, targetApp, payload, localOpts = {}, submitM
172
175
  })
173
176
  }
174
177
  }
178
+
179
+ dispatchGlobalEvent({
180
+ agentIdentifier: agentRef.agentIdentifier,
181
+ loaded: !!activatedFeatures?.[agentRef.agentIdentifier],
182
+ type: 'data',
183
+ name: 'harvest',
184
+ feature: featureName,
185
+ data: {
186
+ endpoint,
187
+ headers,
188
+ targetApp,
189
+ payload,
190
+ submitMethod: getSubmitMethodName(),
191
+ raw,
192
+ synchronousXhr: !!(localOpts.isFinalHarvest && isWorkerScope)
193
+ }
194
+ })
195
+
175
196
  return true
176
197
 
177
198
  function shouldRetry (status) {
@@ -183,6 +204,12 @@ function send (agentRef, { endpoint, targetApp, payload, localOpts = {}, submitM
183
204
  }
184
205
  return (status >= 502 && status <= 504) || (status >= 512 && status <= 530)
185
206
  }
207
+
208
+ function getSubmitMethodName () {
209
+ if (submitMethod === xhrMethod) return 'xhr'
210
+ if (submitMethod === fetchMethod) return 'fetch'
211
+ return 'beacon'
212
+ }
186
213
  }
187
214
 
188
215
  /**
@@ -218,6 +245,7 @@ function cleanPayload (payload = {}) {
218
245
  function baseQueryString (agentRef, qs, endpoint, applicationID) {
219
246
  const ref = agentRef.runtime.obfuscator.obfuscateString(cleanURL('' + globalScope.location))
220
247
  const hr = agentRef.runtime.session?.state.sessionReplayMode === 1 && endpoint !== JSERRORS
248
+ const ht = agentRef.runtime.session?.state.sessionTraceMode === 1 && ![LOGS, BLOBS].includes(endpoint)
221
249
 
222
250
  const qps = [
223
251
  'a=' + applicationID,
@@ -232,6 +260,8 @@ function baseQueryString (agentRef, qs, endpoint, applicationID) {
232
260
  param('ptid', (agentRef.runtime.ptid ? '' + agentRef.runtime.ptid : ''))
233
261
  ]
234
262
  if (hr) qps.push(param('hr', '1', qs))
263
+ if (ht) qps.push(param('ht', '1', qs))
264
+
235
265
  return qps.join('')
236
266
 
237
267
  // Constructs the transaction name param for the beacon URL.
@@ -29,5 +29,12 @@ export function activateFeatures (flags, agentIdentifier) {
29
29
  sentIds.add(agentIdentifier)
30
30
 
31
31
  // let any window level subscribers know that the agent is running
32
- dispatchGlobalEvent({ loaded: true })
32
+ dispatchGlobalEvent({
33
+ agentIdentifier,
34
+ loaded: true,
35
+ type: 'lifecycle',
36
+ name: 'load',
37
+ feature: undefined,
38
+ data: flags
39
+ })
33
40
  }
@@ -28,8 +28,8 @@ export class Aggregate extends AggregateBase {
28
28
 
29
29
  this.ee.on(SESSION_EVENTS.UPDATE, (type, data) => {
30
30
  if (this.blocked || type !== SESSION_EVENT_TYPES.CROSS_TAB) return
31
- if (this.mode !== LOGGING_MODE.OFF && data.loggingMode === LOGGING_MODE.OFF) this.abort(ABORT_REASONS.CROSS_TAB)
32
- else this.mode = data.loggingMode
31
+ if (this.loggingMode !== LOGGING_MODE.OFF && data.loggingMode === LOGGING_MODE.OFF) this.abort(ABORT_REASONS.CROSS_TAB)
32
+ else this.loggingMode = data.loggingMode
33
33
  })
34
34
 
35
35
  this.harvestOpts.raw = true
@@ -11,7 +11,7 @@ import { stylesheetEvaluator } from './stylesheet-evaluator'
11
11
  import { handle } from '../../../common/event-emitter/handle'
12
12
  import { SUPPORTABILITY_METRIC_CHANNEL } from '../../metrics/constants'
13
13
  import { FEATURE_NAMES } from '../../../loaders/features/features'
14
- import { buildNRMetaNode } from './utils'
14
+ import { buildNRMetaNode, customMasker } from './utils'
15
15
  import { IDEAL_PAYLOAD_SIZE } from '../../../common/constants/agent-constants'
16
16
  import { AggregateBase } from '../../utils/aggregate-base'
17
17
 
@@ -78,14 +78,7 @@ export class Recorder {
78
78
  startRecording () {
79
79
  this.recording = true
80
80
  const { block_class, ignore_class, mask_text_class, block_selector, mask_input_options, mask_text_selector, mask_all_inputs, inline_images, collect_fonts } = this.parent.agentRef.init.session_replay
81
- const customMasker = (text, element) => {
82
- try {
83
- if (typeof element?.type === 'string' && element.type.toLowerCase() !== 'password' && (element?.dataset?.nrUnmask !== undefined || element?.classList?.contains('nr-unmask'))) return text
84
- } catch (err) {
85
- // likely an element was passed to this handler that was invalid and was missing attributes or methods
86
- }
87
- return '*'.repeat(text?.length || 0)
88
- }
81
+
89
82
  // set up rrweb configurations for maximum privacy --
90
83
  // https://newrelic.atlassian.net/wiki/spaces/O11Y/pages/2792293280/2023+02+28+Browser+-+Session+Replay#Configuration-options
91
84
  const stop = recorder({
@@ -28,3 +28,15 @@ export function buildNRMetaNode (timestamp, timeKeeper) {
28
28
  originTimeDiff: Math.floor(originTime - timeKeeper.correctedOriginTime)
29
29
  }
30
30
  }
31
+
32
+ export function customMasker (text, element) {
33
+ try {
34
+ if (typeof element?.type === 'string') {
35
+ if (element.type.toLowerCase() === 'password') return '*'.repeat(text?.length || 0)
36
+ if (element?.dataset?.nrUnmask !== undefined || element?.classList?.contains('nr-unmask')) return text
37
+ }
38
+ } catch (err) {
39
+ // likely an element was passed to this handler that was invalid and was missing attributes or methods
40
+ }
41
+ return typeof text === 'string' ? text.replace(/[\S]/g, '*') : '*'.repeat(text?.length || 0)
42
+ }
@@ -37,7 +37,7 @@ export class AggregateBase extends FeatureBase {
37
37
  This was necessary to prevent race cond. issues where the event buffer was checked before the feature could "block" itself.
38
38
  Its easier to just keep an empty event buffer in place. */
39
39
  default:
40
- this.events = new EventStoreManager(agentRef.mainAppKey, 1)
40
+ this.events = new EventStoreManager(agentRef.mainAppKey, 1, agentRef.agentIdentifier, this.featureName)
41
41
  break
42
42
  }
43
43
  this.harvestOpts = {} // features aggregate classes can define custom opts for when their harvest is called
@@ -157,7 +157,7 @@ export class AggregateBase extends FeatureBase {
157
157
 
158
158
  if (!agentRef.mainAppKey) agentRef.mainAppKey = { licenseKey: agentRef.info.licenseKey, appId: agentRef.info.applicationID }
159
159
  // Create a single Aggregator for this agent if DNE yet; to be used by jserror endpoint features.
160
- if (!agentRef.sharedAggregator) agentRef.sharedAggregator = new EventStoreManager(agentRef.mainAppKey, 2)
160
+ if (!agentRef.sharedAggregator) agentRef.sharedAggregator = new EventStoreManager(agentRef.mainAppKey, 2, agentRef.agentIdentifier, 'shared_aggregator')
161
161
 
162
162
  if (!agentRef.runtime.harvester) agentRef.runtime.harvester = new Harvester(agentRef)
163
163
  }
@@ -3,6 +3,8 @@
3
3
  * SPDX-License-Identifier: Apache-2.0
4
4
  */
5
5
  import { EventAggregator } from '../../common/aggregate/event-aggregator'
6
+ import { dispatchGlobalEvent } from '../../common/dispatch/global-event'
7
+ import { activatedFeatures } from '../../common/util/feature-flags'
6
8
  import { EventBuffer } from './event-buffer'
7
9
 
8
10
  /**
@@ -13,12 +15,16 @@ export class EventStoreManager {
13
15
  /**
14
16
  * @param {object} defaultTarget - should contain licenseKey and appId of the main app from NREUM.info at startup
15
17
  * @param {1|2} storageChoice - the type of storage to use in this manager; 'EventBuffer' (1), 'EventAggregator' (2)
18
+ * @param {string} agentIdentifier - agent identifier used in inspection events
19
+ * @param {string} featureName - feature name used in inspection events for non-shared aggregators
16
20
  */
17
- constructor (defaultTarget, storageChoice) {
21
+ constructor (defaultTarget, storageChoice, agentIdentifier, featureName) {
18
22
  this.mainApp = defaultTarget
19
23
  this.StorageClass = storageChoice === 1 ? EventBuffer : EventAggregator
20
24
  this.appStorageMap = new Map()
21
25
  this.appStorageMap.set(defaultTarget, new this.StorageClass())
26
+ this.agentIdentifier = agentIdentifier
27
+ this.featureName = featureName
22
28
  }
23
29
 
24
30
  // This class must contain an union of all methods from all supported storage classes and conceptualize away the target app argument.
@@ -45,6 +51,14 @@ export class EventStoreManager {
45
51
  * @returns {boolean} True if the event was successfully added
46
52
  */
47
53
  add (event, target) {
54
+ dispatchGlobalEvent({
55
+ agentIdentifier: this.agentIdentifier,
56
+ loaded: !!activatedFeatures?.[this.agentIdentifier],
57
+ type: 'data',
58
+ name: 'buffer',
59
+ feature: this.featureName,
60
+ data: event
61
+ })
48
62
  if (target && !this.appStorageMap.has(target)) this.appStorageMap.set(target, new this.StorageClass())
49
63
  return this.appStorageMap.get(target || this.mainApp).add(event)
50
64
  }
@@ -20,6 +20,8 @@ import { MODE } from '../../common/session/constants'
20
20
  import { LOG_LEVELS } from '../../features/logging/constants'
21
21
  import { bufferLog } from '../../features/logging/shared/utils'
22
22
  import { wrapLogger } from '../../common/wrap/wrap-logger'
23
+ import { dispatchGlobalEvent } from '../../common/dispatch/global-event'
24
+ import { activatedFeatures } from '../../common/util/feature-flags'
23
25
 
24
26
  export function setTopLevelCallers () {
25
27
  const nr = gosCDN()
@@ -194,6 +196,14 @@ export function setAPI (agentIdentifier, forceDrain, runSoftNavOverSpa = false)
194
196
  function apiCall (prefix, name, notSpa, bufferGroup) {
195
197
  return function () {
196
198
  handle(SUPPORTABILITY_METRIC_CHANNEL, ['API/' + name + '/called'], undefined, FEATURE_NAMES.metrics, instanceEE)
199
+ dispatchGlobalEvent({
200
+ agentIdentifier,
201
+ loaded: !!activatedFeatures?.[agentIdentifier],
202
+ type: 'data',
203
+ name: 'api',
204
+ feature: prefix + name,
205
+ data: { notSpa, bufferGroup }
206
+ })
197
207
  if (bufferGroup) handle(prefix + name, [notSpa ? now() : performance.now(), ...arguments], notSpa ? null : this, bufferGroup, instanceEE) // no bufferGroup means only the SM is emitted
198
208
  return notSpa ? undefined : this // returns the InteractionHandle which allows these methods to be chained
199
209
  }
@@ -12,6 +12,7 @@ import { activatedFeatures } from '../../common/util/feature-flags'
12
12
  import { isWorkerScope } from '../../common/constants/runtime'
13
13
  import { redefinePublicPath } from './public-path'
14
14
  import { ee } from '../../common/event-emitter/contextual-ee'
15
+ import { dispatchGlobalEvent } from '../../common/dispatch/global-event'
15
16
 
16
17
  let alreadySetOnce = false // the configure() function can run multiple times in agent lifecycle
17
18
 
@@ -68,5 +69,17 @@ export function configure (agent, opts = {}, loaderType, forceDrain) {
68
69
 
69
70
  if (agent.api === undefined) agent.api = setAPI(agent.agentIdentifier, forceDrain, agent.runSoftNavOverSpa)
70
71
  if (agent.exposed === undefined) agent.exposed = exposed
72
+
73
+ if (!alreadySetOnce) {
74
+ dispatchGlobalEvent({
75
+ agentIdentifier: agent.agentIdentifier,
76
+ loaded: !!activatedFeatures?.[agent.agentIdentifier],
77
+ type: 'lifecycle',
78
+ name: 'initialize',
79
+ feature: undefined,
80
+ data: { init: updatedInit, info, loader_config, runtime }
81
+ })
82
+ }
83
+
71
84
  alreadySetOnce = true
72
85
  }
@@ -6,8 +6,9 @@
6
6
  // To reduce build size a bit:
7
7
  export const EVENTS = 'events'
8
8
  export const JSERRORS = 'jserrors'
9
- const BLOBS = 'browser/blobs'
9
+ export const BLOBS = 'browser/blobs'
10
10
  export const RUM = 'rum'
11
+ export const LOGS = 'browser/logs'
11
12
 
12
13
  export const FEATURE_NAMES = {
13
14
  ajax: 'ajax',
@@ -55,6 +56,6 @@ export const FEATURE_TO_ENDPOINT = {
55
56
  [FEATURE_NAMES.jserrors]: JSERRORS,
56
57
  [FEATURE_NAMES.sessionTrace]: BLOBS,
57
58
  [FEATURE_NAMES.sessionReplay]: BLOBS,
58
- [FEATURE_NAMES.logging]: 'browser/logs',
59
+ [FEATURE_NAMES.logging]: LOGS,
59
60
  [FEATURE_NAMES.genericEvents]: 'ins'
60
61
  }