@newrelic/browser-agent 1.280.0 → 1.282.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 (80) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/dist/cjs/common/config/init.js +2 -1
  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/dom/selector-path.js +20 -3
  6. package/dist/cjs/common/wrap/wrap-function.js +1 -0
  7. package/dist/cjs/common/wrap/wrap-websocket.js +23 -39
  8. package/dist/cjs/features/generic_events/aggregate/index.js +21 -14
  9. package/dist/cjs/features/generic_events/aggregate/user-actions/aggregated-user-action.js +2 -1
  10. package/dist/cjs/features/generic_events/aggregate/user-actions/user-actions-aggregator.js +20 -6
  11. package/dist/cjs/features/metrics/aggregate/index.js +7 -9
  12. package/dist/cjs/features/metrics/constants.js +3 -5
  13. package/dist/cjs/features/metrics/instrument/index.js +8 -11
  14. package/dist/esm/common/config/init.js +2 -1
  15. package/dist/esm/common/constants/env.cdn.js +1 -1
  16. package/dist/esm/common/constants/env.npm.js +1 -1
  17. package/dist/esm/common/dom/selector-path.js +20 -3
  18. package/dist/esm/common/wrap/wrap-function.js +1 -1
  19. package/dist/esm/common/wrap/wrap-websocket.js +23 -39
  20. package/dist/esm/features/generic_events/aggregate/index.js +21 -14
  21. package/dist/esm/features/generic_events/aggregate/user-actions/aggregated-user-action.js +2 -1
  22. package/dist/esm/features/generic_events/aggregate/user-actions/user-actions-aggregator.js +20 -6
  23. package/dist/esm/features/metrics/aggregate/index.js +8 -10
  24. package/dist/esm/features/metrics/constants.js +2 -3
  25. package/dist/esm/features/metrics/instrument/index.js +9 -11
  26. package/dist/tsconfig.tsbuildinfo +1 -0
  27. package/dist/types/common/aggregate/event-aggregator.d.ts +1 -3
  28. package/dist/types/common/aggregate/event-aggregator.d.ts.map +1 -1
  29. package/dist/types/common/constants/agent-constants.d.ts.map +1 -1
  30. package/dist/types/common/constants/env.cdn.d.ts.map +1 -1
  31. package/dist/types/common/constants/env.d.ts.map +1 -1
  32. package/dist/types/common/constants/env.npm.d.ts.map +1 -1
  33. package/dist/types/common/dom/selector-path.d.ts +1 -1
  34. package/dist/types/common/dom/selector-path.d.ts.map +1 -1
  35. package/dist/types/common/session/constants.d.ts.map +1 -1
  36. package/dist/types/common/url/clean-url.d.ts +1 -1
  37. package/dist/types/common/url/clean-url.d.ts.map +1 -1
  38. package/dist/types/common/util/traverse.d.ts +1 -1
  39. package/dist/types/common/util/traverse.d.ts.map +1 -1
  40. package/dist/types/common/window/page-visibility.d.ts +1 -1
  41. package/dist/types/common/window/page-visibility.d.ts.map +1 -1
  42. package/dist/types/common/wrap/wrap-function.d.ts +11 -1
  43. package/dist/types/common/wrap/wrap-function.d.ts.map +1 -1
  44. package/dist/types/common/wrap/wrap-websocket.d.ts.map +1 -1
  45. package/dist/types/features/generic_events/aggregate/index.d.ts.map +1 -1
  46. package/dist/types/features/generic_events/aggregate/user-actions/aggregated-user-action.d.ts +2 -1
  47. package/dist/types/features/generic_events/aggregate/user-actions/aggregated-user-action.d.ts.map +1 -1
  48. package/dist/types/features/generic_events/aggregate/user-actions/user-actions-aggregator.d.ts +1 -1
  49. package/dist/types/features/generic_events/aggregate/user-actions/user-actions-aggregator.d.ts.map +1 -1
  50. package/dist/types/features/generic_events/constants.d.ts.map +1 -1
  51. package/dist/types/features/logging/constants.d.ts.map +1 -1
  52. package/dist/types/features/metrics/constants.d.ts +1 -0
  53. package/dist/types/features/metrics/constants.d.ts.map +1 -1
  54. package/dist/types/features/session_replay/constants.d.ts.map +1 -1
  55. package/dist/types/features/session_trace/constants.d.ts.map +1 -1
  56. package/dist/types/features/soft_navigations/constants.d.ts.map +1 -1
  57. package/dist/types/features/spa/constants.d.ts.map +1 -1
  58. package/dist/types/features/utils/feature-base.d.ts.map +1 -1
  59. package/dist/types/features/utils/instrument-base.d.ts +2 -2
  60. package/dist/types/features/utils/instrument-base.d.ts.map +1 -1
  61. package/dist/types/loaders/agent-base.d.ts +2 -2
  62. package/dist/types/loaders/agent-base.d.ts.map +1 -1
  63. package/dist/types/loaders/api/api.d.ts +0 -10
  64. package/dist/types/loaders/api/api.d.ts.map +1 -1
  65. package/dist/types/loaders/features/features.d.ts.map +1 -1
  66. package/dist/types/loaders/micro-agent-base.d.ts +6 -6
  67. package/dist/types/loaders/micro-agent-base.d.ts.map +1 -1
  68. package/dist/types/loaders/micro-agent.d.ts +1 -1
  69. package/dist/types/loaders/micro-agent.d.ts.map +1 -1
  70. package/package.json +2 -2
  71. package/src/common/config/init.js +1 -1
  72. package/src/common/dom/selector-path.js +15 -3
  73. package/src/common/wrap/wrap-function.js +1 -1
  74. package/src/common/wrap/wrap-websocket.js +24 -40
  75. package/src/features/generic_events/aggregate/index.js +21 -6
  76. package/src/features/generic_events/aggregate/user-actions/aggregated-user-action.js +2 -1
  77. package/src/features/generic_events/aggregate/user-actions/user-actions-aggregator.js +11 -7
  78. package/src/features/metrics/aggregate/index.js +8 -8
  79. package/src/features/metrics/constants.js +2 -2
  80. package/src/features/metrics/instrument/index.js +9 -9
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.282.0](https://github.com/newrelic/newrelic-browser-agent/compare/v1.281.0...v1.282.0) (2025-02-13)
7
+
8
+
9
+ ### Features
10
+
11
+ * Re-implement wrap-websocket into agent ([#1342](https://github.com/newrelic/newrelic-browser-agent/issues/1342)) ([9b2756f](https://github.com/newrelic/newrelic-browser-agent/commit/9b2756f520cb946192e72d138e14fb4fca75a7b8))
12
+
13
+ ## [1.281.0](https://github.com/newrelic/newrelic-browser-agent/compare/v1.280.0...v1.281.0) (2025-02-04)
14
+
15
+
16
+ ### Features
17
+
18
+ * Capture Nearest UserAction Fields ([#1267](https://github.com/newrelic/newrelic-browser-agent/issues/1267)) ([d410937](https://github.com/newrelic/newrelic-browser-agent/commit/d410937983545a6a6aa39c52c3762f621acf1110))
19
+
6
20
  ## [1.280.0](https://github.com/newrelic/newrelic-browser-agent/compare/v1.279.1...v1.280.0) (2025-01-31)
7
21
 
8
22
 
@@ -211,7 +211,8 @@ const model = () => {
211
211
  },
212
212
  ssl: undefined,
213
213
  user_actions: {
214
- enabled: true
214
+ enabled: true,
215
+ elementAttributes: ['id', 'className', 'tagName', 'type']
215
216
  }
216
217
  };
217
218
  };
@@ -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.280.0";
20
+ const VERSION = exports.VERSION = "1.282.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.280.0";
20
+ const VERSION = exports.VERSION = "1.282.0";
21
21
 
22
22
  /**
23
23
  * Exposes the build type of the agent
@@ -16,8 +16,11 @@ exports.generateSelectorPath = void 0;
16
16
  * @param {boolean} includeClass
17
17
  * @returns {string|undefined}
18
18
  */
19
- const generateSelectorPath = elem => {
20
- if (!elem) return;
19
+ const generateSelectorPath = (elem, targetFields = []) => {
20
+ if (!elem) return {
21
+ path: undefined,
22
+ nearestFields: {}
23
+ };
21
24
  const getNthOfTypeIndex = node => {
22
25
  try {
23
26
  let i = 1;
@@ -35,12 +38,16 @@ const generateSelectorPath = elem => {
35
38
  };
36
39
  let pathSelector = '';
37
40
  let index = getNthOfTypeIndex(elem);
41
+ const nearestFields = {};
38
42
  try {
39
43
  while (elem?.tagName) {
40
44
  const {
41
45
  id,
42
46
  localName
43
47
  } = elem;
48
+ targetFields.forEach(field => {
49
+ nearestFields[nearestAttrName(field)] ||= elem[field];
50
+ });
44
51
  const selector = [localName, id ? "#".concat(id) : '', pathSelector ? ">".concat(pathSelector) : ''].join('');
45
52
  pathSelector = selector;
46
53
  elem = elem.parentNode;
@@ -48,6 +55,16 @@ const generateSelectorPath = elem => {
48
55
  } catch (err) {
49
56
  // do nothing for now
50
57
  }
51
- return pathSelector ? index ? "".concat(pathSelector, ":nth-of-type(").concat(index, ")") : pathSelector : undefined;
58
+ const path = pathSelector ? index ? "".concat(pathSelector, ":nth-of-type(").concat(index, ")") : pathSelector : undefined;
59
+ return {
60
+ path,
61
+ nearestFields
62
+ };
63
+ function nearestAttrName(originalFieldName) {
64
+ /** preserve original renaming structure for pre-existing field maps */
65
+ if (originalFieldName === 'tagName') originalFieldName = 'tag';
66
+ if (originalFieldName === 'className') originalFieldName = 'class';
67
+ return "nearest".concat(originalFieldName.charAt(0).toUpperCase() + originalFieldName.slice(1));
68
+ }
52
69
  };
53
70
  exports.generateSelectorPath = generateSelectorPath;
@@ -3,6 +3,7 @@
3
3
  Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
+ exports.copy = copy;
6
7
  exports.createWrapperWithEmitter = createWrapperWithEmitter;
7
8
  exports.flag = exports.default = void 0;
8
9
  var _contextualEe = require("../event-emitter/contextual-ee");
@@ -30,49 +30,33 @@ function wrapWebSocket(sharedEE) {
30
30
  sharedEE.emit(WEBSOCKET_TAG + message, [timestamp, timestamp - createdAt, isLoaded, socketId, ...data]);
31
31
  };
32
32
  }
33
- Object.defineProperty(WrappedWebSocket, 'name', {
34
- value: 'WebSocket'
35
- });
36
- function WrappedWebSocket() {
37
- const ws = new originals.WS(...arguments);
38
- const socketId = (0, _uniqueId.generateRandomHexString)(6);
39
- const report = reporter(socketId);
40
- report('new');
41
- const events = ['message', 'error', 'open', 'close'];
42
- /** add event listeners */
43
- events.forEach(evt => {
44
- ws.addEventListener(evt, function (e) {
45
- report(ADD_EVENT_LISTENER_TAG, {
46
- eventType: evt,
47
- event: e
33
+ class WrappedWebSocket extends WebSocket {
34
+ static name = 'WebSocket';
35
+ constructor(...args) {
36
+ super(...args);
37
+ const socketId = (0, _uniqueId.generateRandomHexString)(6);
38
+ this.report = reporter(socketId);
39
+ this.report('new');
40
+ const events = ['message', 'error', 'open', 'close'];
41
+ /** add event listeners */
42
+ events.forEach(evt => {
43
+ this.addEventListener(evt, function (e) {
44
+ this.report(ADD_EVENT_LISTENER_TAG, {
45
+ eventType: evt,
46
+ event: e
47
+ });
48
48
  });
49
49
  });
50
- })
51
-
52
- /** could also observe the on-events for runtime processing, but not implemented yet */
53
-
54
- /** observe the static method send, but noteably not close, as that is currently observed with the event listener */;
55
- ['send'].forEach(wrapStaticProperty);
56
- function wrapStaticProperty(prop) {
57
- const originalProp = ws[prop];
58
- if (originalProp) {
59
- Object.defineProperty(proxiedProp, 'name', {
60
- value: prop
61
- });
62
- function proxiedProp() {
63
- report(prop, ...arguments);
64
- try {
65
- return originalProp.apply(this, arguments);
66
- } catch (err) {
67
- report(prop + '-err', ...arguments);
68
- // rethrow error so we don't effect execution by observing.
69
- throw err;
70
- }
71
- }
72
- ws[prop] = proxiedProp;
50
+ }
51
+ send(...args) {
52
+ this.report('send', ...args);
53
+ try {
54
+ return super.send(...args);
55
+ } catch (err) {
56
+ this.report('send-err', ...args);
57
+ throw err;
73
58
  }
74
59
  }
75
- return ws;
76
60
  }
77
61
  _runtime.globalScope.WebSocket = WrappedWebSocket;
78
62
  return sharedEE;
@@ -61,7 +61,7 @@ class Aggregate extends _aggregateBase.AggregateBase {
61
61
  });
62
62
  }, this.featureName, this.ee);
63
63
  }
64
- let addUserAction;
64
+ let addUserAction = () => {/** no-op */};
65
65
  if (_runtime.isBrowserScope && agentRef.init.user_actions.enabled) {
66
66
  this.userActionAggregator = new _userActionsAggregator.UserActionsAggregator();
67
67
  this.harvestOpts.beforeUnload = () => addUserAction?.(this.userActionAggregator.aggregationEvent);
@@ -87,20 +87,27 @@ class Aggregate extends _aggregateBase.AggregateBase {
87
87
  ...((0, _iframe.isIFrameWindow)(window) && {
88
88
  iframe: true
89
89
  }),
90
- ...(canTrustTargetAttribute('id') && {
91
- targetId: target.id
92
- }),
93
- ...(canTrustTargetAttribute('tagName') && {
94
- targetTag: target.tagName
95
- }),
96
- ...(canTrustTargetAttribute('type') && {
97
- targetType: target.type
98
- }),
99
- ...(canTrustTargetAttribute('className') && {
100
- targetClass: target.className
101
- })
90
+ ...this.agentRef.init.user_actions.elementAttributes.reduce((acc, field) => {
91
+ /** prevent us from capturing an obscenely long value */
92
+ if (canTrustTargetAttribute(field)) acc[targetAttrName(field)] = String(target[field]).trim().slice(0, 128);
93
+ return acc;
94
+ }, {}),
95
+ ...aggregatedUserAction.nearestTargetFields
102
96
  });
103
97
 
98
+ /**
99
+ * Returns the original target field name with `target` prepended and camelCased
100
+ * @param {string} originalFieldName
101
+ * @returns {string} the target field name
102
+ */
103
+ function targetAttrName(originalFieldName) {
104
+ /** preserve original renaming structure for pre-existing field maps */
105
+ if (originalFieldName === 'tagName') originalFieldName = 'tag';
106
+ if (originalFieldName === 'className') originalFieldName = 'class';
107
+ /** return the original field name, cap'd and prepended with target to match formatting */
108
+ return "target".concat(originalFieldName.charAt(0).toUpperCase() + originalFieldName.slice(1));
109
+ }
110
+
104
111
  /**
105
112
  * Only trust attributes that exist on HTML element targets, which excludes the window and the document targets
106
113
  * @param {string} attribute The attribute to check for on the target element
@@ -116,7 +123,7 @@ class Aggregate extends _aggregateBase.AggregateBase {
116
123
  };
117
124
  (0, _registerHandler.registerHandler)('ua', evt => {
118
125
  /** the processor will return the previously aggregated event if it has been completed by processing the current event */
119
- addUserAction(this.userActionAggregator.process(evt));
126
+ addUserAction(this.userActionAggregator.process(evt, this.agentRef.init.user_actions.elementAttributes));
120
127
  }, this.featureName, this.ee);
121
128
  }
122
129
 
@@ -11,13 +11,14 @@ var _constants = require("../../constants");
11
11
  */
12
12
 
13
13
  class AggregatedUserAction {
14
- constructor(evt, selectorPath) {
14
+ constructor(evt, selectorPath, nearestTargetFields) {
15
15
  this.event = evt;
16
16
  this.count = 1;
17
17
  this.originMs = Math.floor(evt.timeStamp);
18
18
  this.relativeMs = [0];
19
19
  this.selectorPath = selectorPath;
20
20
  this.rageClick = undefined;
21
+ this.nearestTargetFields = nearestTargetFields;
21
22
  }
22
23
 
23
24
  /**
@@ -31,9 +31,12 @@ class UserActionsAggregator {
31
31
  * @param {Event} evt The event supplied by the addEventListener callback
32
32
  * @returns {AggregatedUserAction|undefined} The previous aggregation set if it has been completed by processing the current event
33
33
  */
34
- process(evt) {
34
+ process(evt, targetFields) {
35
35
  if (!evt) return;
36
- const selectorPath = getSelectorPath(evt);
36
+ const {
37
+ selectorPath,
38
+ nearestTargetFields
39
+ } = getSelectorPath(evt, targetFields);
37
40
  const aggregationKey = getAggregationKey(evt, selectorPath);
38
41
  if (!!aggregationKey && aggregationKey === this.#aggregationKey) {
39
42
  // an aggregation exists already, so lets just continue to increment
@@ -43,7 +46,7 @@ class UserActionsAggregator {
43
46
  const finishedEvent = this.#aggregationEvent;
44
47
  // then set as this new event aggregation
45
48
  this.#aggregationKey = aggregationKey;
46
- this.#aggregationEvent = new _aggregatedUserAction.AggregatedUserAction(evt, selectorPath);
49
+ this.#aggregationEvent = new _aggregatedUserAction.AggregatedUserAction(evt, selectorPath, nearestTargetFields);
47
50
  return finishedEvent;
48
51
  }
49
52
  }
@@ -56,13 +59,24 @@ class UserActionsAggregator {
56
59
  * @returns {string}
57
60
  */
58
61
  exports.UserActionsAggregator = UserActionsAggregator;
59
- function getSelectorPath(evt) {
62
+ function getSelectorPath(evt, targetFields) {
60
63
  let selectorPath;
64
+ let nearestTargetFields = {};
61
65
  if (_constants.OBSERVED_WINDOW_EVENTS.includes(evt.type) || evt.target === window) selectorPath = 'window';else if (evt.target === document) selectorPath = 'document';
62
66
  // if still no selectorPath, generate one from target tree that includes elem ids
63
- else selectorPath = (0, _selectorPath.generateSelectorPath)(evt.target);
67
+ else {
68
+ const {
69
+ path,
70
+ nearestFields
71
+ } = (0, _selectorPath.generateSelectorPath)(evt.target, targetFields);
72
+ selectorPath = path;
73
+ nearestTargetFields = nearestFields;
74
+ }
64
75
  // if STILL no selectorPath, it will return undefined which will skip aggregation for this event
65
- return selectorPath;
76
+ return {
77
+ selectorPath,
78
+ nearestTargetFields
79
+ };
66
80
  }
67
81
 
68
82
  /**
@@ -13,14 +13,13 @@ var _eventListenerOpts = require("../../../common/event-listener/event-listener-
13
13
  var _runtime = require("../../../common/constants/runtime");
14
14
  var _aggregateBase = require("../../utils/aggregate-base");
15
15
  var _iframe = require("../../../common/dom/iframe");
16
+ var _wrapWebsocket = require("../../../common/wrap/wrap-websocket");
17
+ var _websocketDetection = require("./websocket-detection");
16
18
  /**
17
19
  * Copyright 2020-2025 New Relic, Inc. All rights reserved.
18
20
  * SPDX-License-Identifier: Apache-2.0
19
21
  */
20
22
 
21
- // import { WEBSOCKET_TAG } from '../../../common/wrap/wrap-websocket'
22
- // import { handleWebsocketEvents } from './websocket-detection'
23
-
24
23
  class Aggregate extends _aggregateBase.AggregateBase {
25
24
  static featureName = _constants.FEATURE_NAME;
26
25
  constructor(agentRef) {
@@ -134,12 +133,11 @@ class Aggregate extends _aggregateBase.AggregateBase {
134
133
  subtree: true
135
134
  });
136
135
  }
137
-
138
- // WATCHABLE_WEB_SOCKET_EVENTS.forEach(tag => {
139
- // registerHandler('buffered-' + WEBSOCKET_TAG + tag, (...args) => {
140
- // handleWebsocketEvents(this.storeSupportabilityMetrics.bind(this), tag, ...args)
141
- // }, this.featureName, this.ee)
142
- // })
136
+ _constants.WATCHABLE_WEB_SOCKET_EVENTS.forEach(tag => {
137
+ (0, _registerHandler.registerHandler)('buffered-' + _wrapWebsocket.WEBSOCKET_TAG + tag, (...args) => {
138
+ (0, _websocketDetection.handleWebsocketEvents)(this.storeSupportabilityMetrics.bind(this), tag, ...args);
139
+ }, this.featureName, this.ee);
140
+ });
143
141
  }
144
142
  eachSessionChecks() {
145
143
  if (!_runtime.isBrowserScope) return;
@@ -3,19 +3,17 @@
3
3
  Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
- exports.SUPPORTABILITY_METRIC_CHANNEL = exports.SUPPORTABILITY_METRIC = exports.FEATURE_NAME = exports.CUSTOM_METRIC_CHANNEL = exports.CUSTOM_METRIC = void 0;
6
+ exports.WATCHABLE_WEB_SOCKET_EVENTS = exports.SUPPORTABILITY_METRIC_CHANNEL = exports.SUPPORTABILITY_METRIC = exports.FEATURE_NAME = exports.CUSTOM_METRIC_CHANNEL = exports.CUSTOM_METRIC = void 0;
7
+ var _wrapWebsocket = require("../../common/wrap/wrap-websocket");
7
8
  var _features = require("../../loaders/features/features");
8
9
  /**
9
10
  * Copyright 2020-2025 New Relic, Inc. All rights reserved.
10
11
  * SPDX-License-Identifier: Apache-2.0
11
12
  */
12
13
 
13
- // import { ADD_EVENT_LISTENER_TAG } from '../../common/wrap/wrap-websocket'
14
-
15
14
  const FEATURE_NAME = exports.FEATURE_NAME = _features.FEATURE_NAMES.metrics;
16
15
  const SUPPORTABILITY_METRIC = exports.SUPPORTABILITY_METRIC = 'sm';
17
16
  const CUSTOM_METRIC = exports.CUSTOM_METRIC = 'cm';
18
17
  const SUPPORTABILITY_METRIC_CHANNEL = exports.SUPPORTABILITY_METRIC_CHANNEL = 'storeSupportabilityMetrics';
19
18
  const CUSTOM_METRIC_CHANNEL = exports.CUSTOM_METRIC_CHANNEL = 'storeEventMetrics';
20
-
21
- // export const WATCHABLE_WEB_SOCKET_EVENTS = ['new', 'send', 'close', ADD_EVENT_LISTENER_TAG]
19
+ const WATCHABLE_WEB_SOCKET_EVENTS = exports.WATCHABLE_WEB_SOCKET_EVENTS = ['new', 'send', 'close', _wrapWebsocket.ADD_EVENT_LISTENER_TAG];
@@ -4,6 +4,8 @@ Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
6
  exports.Metrics = exports.Instrument = void 0;
7
+ var _handle = require("../../../common/event-emitter/handle");
8
+ var _wrapWebsocket = require("../../../common/wrap/wrap-websocket");
7
9
  var _instrumentBase = require("../../utils/instrument-base");
8
10
  var _constants = require("../constants");
9
11
  /**
@@ -11,21 +13,16 @@ var _constants = require("../constants");
11
13
  * SPDX-License-Identifier: Apache-2.0
12
14
  */
13
15
 
14
- // import { handle } from '../../../common/event-emitter/handle'
15
- // import { WEBSOCKET_TAG, wrapWebSocket } from '../../../common/wrap/wrap-websocket'
16
-
17
16
  class Instrument extends _instrumentBase.InstrumentBase {
18
17
  static featureName = _constants.FEATURE_NAME;
19
18
  constructor(agentRef, auto = true) {
20
19
  super(agentRef, _constants.FEATURE_NAME, auto);
21
- // wrapWebSocket(this.ee)
22
-
23
- // WATCHABLE_WEB_SOCKET_EVENTS.forEach((suffix) => {
24
- // this.ee.on(WEBSOCKET_TAG + suffix, (...args) => {
25
- // handle('buffered-' + WEBSOCKET_TAG + suffix, [...args], undefined, this.featureName, this.ee)
26
- // })
27
- // })
28
-
20
+ (0, _wrapWebsocket.wrapWebSocket)(this.ee);
21
+ _constants.WATCHABLE_WEB_SOCKET_EVENTS.forEach(suffix => {
22
+ this.ee.on(_wrapWebsocket.WEBSOCKET_TAG + suffix, (...args) => {
23
+ (0, _handle.handle)('buffered-' + _wrapWebsocket.WEBSOCKET_TAG + suffix, [...args], undefined, this.featureName, this.ee);
24
+ });
25
+ });
29
26
  this.importAggregator(agentRef);
30
27
  }
31
28
  }
@@ -202,7 +202,8 @@ const model = () => {
202
202
  },
203
203
  ssl: undefined,
204
204
  user_actions: {
205
- enabled: true
205
+ enabled: true,
206
+ elementAttributes: ['id', 'className', 'tagName', 'type']
206
207
  }
207
208
  };
208
209
  };
@@ -11,7 +11,7 @@
11
11
  /**
12
12
  * Exposes the version of the agent
13
13
  */
14
- export const VERSION = "1.280.0";
14
+ export const VERSION = "1.282.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.280.0";
14
+ export const VERSION = "1.282.0";
15
15
 
16
16
  /**
17
17
  * Exposes the build type of the agent
@@ -10,8 +10,11 @@
10
10
  * @param {boolean} includeClass
11
11
  * @returns {string|undefined}
12
12
  */
13
- export const generateSelectorPath = elem => {
14
- if (!elem) return;
13
+ export const generateSelectorPath = (elem, targetFields = []) => {
14
+ if (!elem) return {
15
+ path: undefined,
16
+ nearestFields: {}
17
+ };
15
18
  const getNthOfTypeIndex = node => {
16
19
  try {
17
20
  let i = 1;
@@ -29,12 +32,16 @@ export const generateSelectorPath = elem => {
29
32
  };
30
33
  let pathSelector = '';
31
34
  let index = getNthOfTypeIndex(elem);
35
+ const nearestFields = {};
32
36
  try {
33
37
  while (elem?.tagName) {
34
38
  const {
35
39
  id,
36
40
  localName
37
41
  } = elem;
42
+ targetFields.forEach(field => {
43
+ nearestFields[nearestAttrName(field)] ||= elem[field];
44
+ });
38
45
  const selector = [localName, id ? "#".concat(id) : '', pathSelector ? ">".concat(pathSelector) : ''].join('');
39
46
  pathSelector = selector;
40
47
  elem = elem.parentNode;
@@ -42,5 +49,15 @@ export const generateSelectorPath = elem => {
42
49
  } catch (err) {
43
50
  // do nothing for now
44
51
  }
45
- return pathSelector ? index ? "".concat(pathSelector, ":nth-of-type(").concat(index, ")") : pathSelector : undefined;
52
+ const path = pathSelector ? index ? "".concat(pathSelector, ":nth-of-type(").concat(index, ")") : pathSelector : undefined;
53
+ return {
54
+ path,
55
+ nearestFields
56
+ };
57
+ function nearestAttrName(originalFieldName) {
58
+ /** preserve original renaming structure for pre-existing field maps */
59
+ if (originalFieldName === 'tagName') originalFieldName = 'tag';
60
+ if (originalFieldName === 'className') originalFieldName = 'class';
61
+ return "nearest".concat(originalFieldName.charAt(0).toUpperCase() + originalFieldName.slice(1));
62
+ }
46
63
  };
@@ -173,7 +173,7 @@ function report(args, emitter) {
173
173
  * Defaults to the global event emitter.
174
174
  * @returns {object} - The destination founction or object with copied properties.
175
175
  */
176
- function copy(from, to, emitter) {
176
+ export function copy(from, to, emitter) {
177
177
  if (Object.defineProperty && Object.keys) {
178
178
  // Create accessors that proxy to actual function
179
179
  try {
@@ -22,49 +22,33 @@ export function wrapWebSocket(sharedEE) {
22
22
  sharedEE.emit(WEBSOCKET_TAG + message, [timestamp, timestamp - createdAt, isLoaded, socketId, ...data]);
23
23
  };
24
24
  }
25
- Object.defineProperty(WrappedWebSocket, 'name', {
26
- value: 'WebSocket'
27
- });
28
- function WrappedWebSocket() {
29
- const ws = new originals.WS(...arguments);
30
- const socketId = generateRandomHexString(6);
31
- const report = reporter(socketId);
32
- report('new');
33
- const events = ['message', 'error', 'open', 'close'];
34
- /** add event listeners */
35
- events.forEach(evt => {
36
- ws.addEventListener(evt, function (e) {
37
- report(ADD_EVENT_LISTENER_TAG, {
38
- eventType: evt,
39
- event: e
25
+ class WrappedWebSocket extends WebSocket {
26
+ static name = 'WebSocket';
27
+ constructor(...args) {
28
+ super(...args);
29
+ const socketId = generateRandomHexString(6);
30
+ this.report = reporter(socketId);
31
+ this.report('new');
32
+ const events = ['message', 'error', 'open', 'close'];
33
+ /** add event listeners */
34
+ events.forEach(evt => {
35
+ this.addEventListener(evt, function (e) {
36
+ this.report(ADD_EVENT_LISTENER_TAG, {
37
+ eventType: evt,
38
+ event: e
39
+ });
40
40
  });
41
41
  });
42
- })
43
-
44
- /** could also observe the on-events for runtime processing, but not implemented yet */
45
-
46
- /** observe the static method send, but noteably not close, as that is currently observed with the event listener */;
47
- ['send'].forEach(wrapStaticProperty);
48
- function wrapStaticProperty(prop) {
49
- const originalProp = ws[prop];
50
- if (originalProp) {
51
- Object.defineProperty(proxiedProp, 'name', {
52
- value: prop
53
- });
54
- function proxiedProp() {
55
- report(prop, ...arguments);
56
- try {
57
- return originalProp.apply(this, arguments);
58
- } catch (err) {
59
- report(prop + '-err', ...arguments);
60
- // rethrow error so we don't effect execution by observing.
61
- throw err;
62
- }
63
- }
64
- ws[prop] = proxiedProp;
42
+ }
43
+ send(...args) {
44
+ this.report('send', ...args);
45
+ try {
46
+ return super.send(...args);
47
+ } catch (err) {
48
+ this.report('send-err', ...args);
49
+ throw err;
65
50
  }
66
51
  }
67
- return ws;
68
52
  }
69
53
  globalScope.WebSocket = WrappedWebSocket;
70
54
  return sharedEE;
@@ -54,7 +54,7 @@ export class Aggregate extends AggregateBase {
54
54
  });
55
55
  }, this.featureName, this.ee);
56
56
  }
57
- let addUserAction;
57
+ let addUserAction = () => {/** no-op */};
58
58
  if (isBrowserScope && agentRef.init.user_actions.enabled) {
59
59
  this.userActionAggregator = new UserActionsAggregator();
60
60
  this.harvestOpts.beforeUnload = () => addUserAction?.(this.userActionAggregator.aggregationEvent);
@@ -80,20 +80,27 @@ export class Aggregate extends AggregateBase {
80
80
  ...(isIFrameWindow(window) && {
81
81
  iframe: true
82
82
  }),
83
- ...(canTrustTargetAttribute('id') && {
84
- targetId: target.id
85
- }),
86
- ...(canTrustTargetAttribute('tagName') && {
87
- targetTag: target.tagName
88
- }),
89
- ...(canTrustTargetAttribute('type') && {
90
- targetType: target.type
91
- }),
92
- ...(canTrustTargetAttribute('className') && {
93
- targetClass: target.className
94
- })
83
+ ...this.agentRef.init.user_actions.elementAttributes.reduce((acc, field) => {
84
+ /** prevent us from capturing an obscenely long value */
85
+ if (canTrustTargetAttribute(field)) acc[targetAttrName(field)] = String(target[field]).trim().slice(0, 128);
86
+ return acc;
87
+ }, {}),
88
+ ...aggregatedUserAction.nearestTargetFields
95
89
  });
96
90
 
91
+ /**
92
+ * Returns the original target field name with `target` prepended and camelCased
93
+ * @param {string} originalFieldName
94
+ * @returns {string} the target field name
95
+ */
96
+ function targetAttrName(originalFieldName) {
97
+ /** preserve original renaming structure for pre-existing field maps */
98
+ if (originalFieldName === 'tagName') originalFieldName = 'tag';
99
+ if (originalFieldName === 'className') originalFieldName = 'class';
100
+ /** return the original field name, cap'd and prepended with target to match formatting */
101
+ return "target".concat(originalFieldName.charAt(0).toUpperCase() + originalFieldName.slice(1));
102
+ }
103
+
97
104
  /**
98
105
  * Only trust attributes that exist on HTML element targets, which excludes the window and the document targets
99
106
  * @param {string} attribute The attribute to check for on the target element
@@ -109,7 +116,7 @@ export class Aggregate extends AggregateBase {
109
116
  };
110
117
  registerHandler('ua', evt => {
111
118
  /** the processor will return the previously aggregated event if it has been completed by processing the current event */
112
- addUserAction(this.userActionAggregator.process(evt));
119
+ addUserAction(this.userActionAggregator.process(evt, this.agentRef.init.user_actions.elementAttributes));
113
120
  }, this.featureName, this.ee);
114
121
  }
115
122
 
@@ -4,13 +4,14 @@
4
4
  */
5
5
  import { RAGE_CLICK_THRESHOLD_EVENTS, RAGE_CLICK_THRESHOLD_MS } from '../../constants';
6
6
  export class AggregatedUserAction {
7
- constructor(evt, selectorPath) {
7
+ constructor(evt, selectorPath, nearestTargetFields) {
8
8
  this.event = evt;
9
9
  this.count = 1;
10
10
  this.originMs = Math.floor(evt.timeStamp);
11
11
  this.relativeMs = [0];
12
12
  this.selectorPath = selectorPath;
13
13
  this.rageClick = undefined;
14
+ this.nearestTargetFields = nearestTargetFields;
14
15
  }
15
16
 
16
17
  /**