@newrelic/browser-agent 1.257.0 → 1.258.1

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 (127) hide show
  1. package/CHANGELOG.md +27 -0
  2. package/dist/cjs/common/config/state/configurable.js +8 -5
  3. package/dist/cjs/common/config/state/init.js +0 -2
  4. package/dist/cjs/common/config/state/runtime.js +10 -8
  5. package/dist/cjs/common/constants/env.cdn.js +1 -1
  6. package/dist/cjs/common/constants/env.npm.js +1 -1
  7. package/dist/cjs/common/constants/runtime.js +8 -2
  8. package/dist/cjs/common/deny-list/deny-list.js +6 -8
  9. package/dist/cjs/common/session/constants.js +1 -0
  10. package/dist/cjs/common/session/session-entity.js +3 -0
  11. package/dist/cjs/common/timing/time-keeper.js +45 -9
  12. package/dist/cjs/common/vitals/time-to-first-byte.js +10 -2
  13. package/dist/cjs/common/vitals/vital-metric.js +1 -1
  14. package/dist/cjs/features/ajax/aggregate/chunk.js +50 -0
  15. package/dist/cjs/features/ajax/aggregate/index.js +131 -191
  16. package/dist/cjs/features/ajax/instrument/index.js +11 -11
  17. package/dist/cjs/features/jserrors/aggregate/index.js +7 -3
  18. package/dist/cjs/features/jserrors/instrument/index.js +4 -87
  19. package/dist/cjs/features/jserrors/shared/cast-error.js +66 -0
  20. package/dist/cjs/features/jserrors/{instrument → shared}/uncaught-error.js +4 -2
  21. package/dist/cjs/features/page_view_event/aggregate/index.js +3 -3
  22. package/dist/cjs/features/session_replay/aggregate/index.js +1 -0
  23. package/dist/cjs/features/session_replay/shared/utils.js +3 -3
  24. package/dist/cjs/features/session_trace/aggregate/index.js +9 -7
  25. package/dist/cjs/features/spa/aggregate/index.js +11 -1
  26. package/dist/cjs/features/spa/instrument/index.js +9 -2
  27. package/dist/cjs/features/utils/agent-session.js +1 -5
  28. package/dist/cjs/features/utils/instrument-base.js +1 -1
  29. package/dist/cjs/loaders/api/api.js +6 -14
  30. package/dist/cjs/loaders/api/apiAsync.js +5 -4
  31. package/dist/esm/common/config/state/configurable.js +8 -5
  32. package/dist/esm/common/config/state/init.js +0 -2
  33. package/dist/esm/common/config/state/runtime.js +11 -9
  34. package/dist/esm/common/constants/env.cdn.js +1 -1
  35. package/dist/esm/common/constants/env.npm.js +1 -1
  36. package/dist/esm/common/constants/runtime.js +7 -1
  37. package/dist/esm/common/deny-list/deny-list.js +5 -8
  38. package/dist/esm/common/session/constants.js +1 -0
  39. package/dist/esm/common/session/session-entity.js +3 -0
  40. package/dist/esm/common/timing/time-keeper.js +46 -9
  41. package/dist/esm/common/vitals/time-to-first-byte.js +11 -3
  42. package/dist/esm/common/vitals/vital-metric.js +1 -1
  43. package/dist/esm/features/ajax/aggregate/chunk.js +43 -0
  44. package/dist/esm/features/ajax/aggregate/index.js +130 -191
  45. package/dist/esm/features/ajax/instrument/index.js +12 -12
  46. package/dist/esm/features/jserrors/aggregate/index.js +7 -3
  47. package/dist/esm/features/jserrors/instrument/index.js +4 -87
  48. package/dist/esm/features/jserrors/shared/cast-error.js +59 -0
  49. package/dist/esm/features/jserrors/{instrument → shared}/uncaught-error.js +5 -2
  50. package/dist/esm/features/page_view_event/aggregate/index.js +4 -4
  51. package/dist/esm/features/session_replay/aggregate/index.js +1 -0
  52. package/dist/esm/features/session_replay/shared/utils.js +4 -4
  53. package/dist/esm/features/session_trace/aggregate/index.js +9 -7
  54. package/dist/esm/features/spa/aggregate/index.js +11 -1
  55. package/dist/esm/features/spa/instrument/index.js +9 -2
  56. package/dist/esm/features/utils/agent-session.js +1 -5
  57. package/dist/esm/features/utils/instrument-base.js +1 -1
  58. package/dist/esm/loaders/api/api.js +6 -14
  59. package/dist/esm/loaders/api/apiAsync.js +5 -4
  60. package/dist/types/common/config/state/configurable.d.ts.map +1 -1
  61. package/dist/types/common/config/state/init.d.ts.map +1 -1
  62. package/dist/types/common/config/state/runtime.d.ts.map +1 -1
  63. package/dist/types/common/constants/runtime.d.ts +6 -1
  64. package/dist/types/common/constants/runtime.d.ts.map +1 -1
  65. package/dist/types/common/deny-list/deny-list.d.ts +1 -0
  66. package/dist/types/common/deny-list/deny-list.d.ts.map +1 -1
  67. package/dist/types/common/session/constants.d.ts +1 -0
  68. package/dist/types/common/session/session-entity.d.ts.map +1 -1
  69. package/dist/types/common/timing/time-keeper.d.ts +1 -1
  70. package/dist/types/common/timing/time-keeper.d.ts.map +1 -1
  71. package/dist/types/features/ajax/aggregate/chunk.d.ts +8 -0
  72. package/dist/types/features/ajax/aggregate/chunk.d.ts.map +1 -0
  73. package/dist/types/features/ajax/aggregate/index.d.ts +8 -6
  74. package/dist/types/features/ajax/aggregate/index.d.ts.map +1 -1
  75. package/dist/types/features/ajax/instrument/index.d.ts +2 -2
  76. package/dist/types/features/ajax/instrument/index.d.ts.map +1 -1
  77. package/dist/types/features/jserrors/aggregate/index.d.ts.map +1 -1
  78. package/dist/types/features/jserrors/instrument/index.d.ts.map +1 -1
  79. package/dist/types/features/jserrors/shared/cast-error.d.ts +21 -0
  80. package/dist/types/features/jserrors/shared/cast-error.d.ts.map +1 -0
  81. package/dist/types/features/jserrors/{instrument → shared}/uncaught-error.d.ts +3 -2
  82. package/dist/types/features/jserrors/shared/uncaught-error.d.ts.map +1 -0
  83. package/dist/types/features/session_replay/aggregate/index.d.ts.map +1 -1
  84. package/dist/types/features/session_replay/shared/utils.d.ts +3 -3
  85. package/dist/types/features/session_trace/aggregate/index.d.ts +9 -8
  86. package/dist/types/features/session_trace/aggregate/index.d.ts.map +1 -1
  87. package/dist/types/features/spa/aggregate/index.d.ts.map +1 -1
  88. package/dist/types/features/spa/instrument/index.d.ts.map +1 -1
  89. package/dist/types/features/utils/agent-session.d.ts.map +1 -1
  90. package/dist/types/loaders/api/api.d.ts +1 -1
  91. package/dist/types/loaders/api/api.d.ts.map +1 -1
  92. package/dist/types/loaders/api/apiAsync.d.ts.map +1 -1
  93. package/package.json +1 -1
  94. package/src/common/config/state/configurable.js +9 -8
  95. package/src/common/config/state/init.js +0 -1
  96. package/src/common/config/state/runtime.js +12 -9
  97. package/src/common/constants/__mocks__/runtime.js +2 -0
  98. package/src/common/constants/runtime.js +6 -1
  99. package/src/common/deny-list/deny-list.js +6 -7
  100. package/src/common/session/constants.js +1 -0
  101. package/src/common/session/session-entity.js +2 -0
  102. package/src/common/timing/time-keeper.js +44 -10
  103. package/src/common/vitals/time-to-first-byte.js +10 -3
  104. package/src/common/vitals/vital-metric.js +1 -1
  105. package/src/features/ajax/aggregate/chunk.js +51 -0
  106. package/src/features/ajax/aggregate/index.js +128 -200
  107. package/src/features/ajax/instrument/index.js +12 -13
  108. package/src/features/jserrors/aggregate/index.js +9 -3
  109. package/src/features/jserrors/instrument/index.js +4 -99
  110. package/src/features/jserrors/shared/cast-error.js +69 -0
  111. package/src/features/jserrors/{instrument → shared}/uncaught-error.js +5 -2
  112. package/src/features/page_view_event/aggregate/index.js +4 -4
  113. package/src/features/session_replay/aggregate/index.js +2 -0
  114. package/src/features/session_replay/shared/utils.js +4 -4
  115. package/src/features/session_trace/aggregate/index.js +9 -8
  116. package/src/features/spa/aggregate/index.js +10 -1
  117. package/src/features/spa/instrument/index.js +3 -3
  118. package/src/features/utils/agent-session.js +1 -7
  119. package/src/features/utils/instrument-base.js +1 -1
  120. package/src/loaders/api/api.js +6 -15
  121. package/src/loaders/api/apiAsync.js +5 -4
  122. package/dist/cjs/common/storage/first-party-cookies.js +0 -36
  123. package/dist/esm/common/storage/first-party-cookies.js +0 -29
  124. package/dist/types/common/storage/first-party-cookies.d.ts +0 -8
  125. package/dist/types/common/storage/first-party-cookies.d.ts.map +0 -1
  126. package/dist/types/features/jserrors/instrument/uncaught-error.d.ts.map +0 -1
  127. package/src/common/storage/first-party-cookies.js +0 -32
@@ -9,13 +9,11 @@ import { FEATURE_NAME } from '../constants';
9
9
  import { FEATURE_NAMES } from '../../../loaders/features/features';
10
10
  import { globalScope } from '../../../common/constants/runtime';
11
11
  import { eventListenerOpts } from '../../../common/event-listener/event-listener-opts';
12
- import { stringify } from '../../../common/util/stringify';
13
- import { UncaughtError } from './uncaught-error';
14
12
  import { now } from '../../../common/timing/now';
15
13
  import { SR_EVENT_EMITTER_TYPES } from '../../session_replay/constants';
14
+ import { castError, castErrorEvent, castPromiseRejectionEvent } from '../shared/cast-error';
16
15
  export class Instrument extends InstrumentBase {
17
16
  static featureName = FEATURE_NAME;
18
- #seenErrors = new Set();
19
17
  #replayRunning = false;
20
18
  constructor(agentIdentifier, aggregator) {
21
19
  let auto = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : true;
@@ -24,38 +22,22 @@ export class Instrument extends InstrumentBase {
24
22
  // this try-catch can be removed when IE11 is completely unsupported & gone
25
23
  this.removeOnAbort = new AbortController();
26
24
  } catch (e) {}
27
-
28
- // Capture function errors early in case the spa feature is loaded
29
- this.ee.on('fn-err', (args, obj, error) => {
30
- if (!this.abortHandler || this.#seenErrors.has(error)) return;
31
- this.#seenErrors.add(error);
32
- handle('err', [this.#castError(error), now()], undefined, FEATURE_NAMES.jserrors, this.ee);
33
- });
34
25
  this.ee.on('internal-error', error => {
35
26
  if (!this.abortHandler) return;
36
- handle('ierr', [this.#castError(error), now(), true, {}, this.#replayRunning], undefined, FEATURE_NAMES.jserrors, this.ee);
27
+ handle('ierr', [castError(error), now(), true, {}, this.#replayRunning], undefined, FEATURE_NAMES.jserrors, this.ee);
37
28
  });
38
29
  this.ee.on(SR_EVENT_EMITTER_TYPES.REPLAY_RUNNING, isRunning => {
39
30
  this.#replayRunning = isRunning;
40
31
  });
41
32
  globalScope.addEventListener('unhandledrejection', promiseRejectionEvent => {
42
33
  if (!this.abortHandler) return;
43
- handle('err', [this.#castPromiseRejectionEvent(promiseRejectionEvent), now(), false, {
34
+ handle('err', [castPromiseRejectionEvent(promiseRejectionEvent), now(), false, {
44
35
  unhandledPromiseRejection: 1
45
36
  }, this.#replayRunning], undefined, FEATURE_NAMES.jserrors, this.ee);
46
37
  }, eventListenerOpts(false, this.removeOnAbort?.signal));
47
38
  globalScope.addEventListener('error', errorEvent => {
48
39
  if (!this.abortHandler) return;
49
-
50
- /**
51
- * If the spa feature is loaded, errors may already have been captured in the `fn-err` listener above.
52
- * This ensures those errors are not captured twice.
53
- */
54
- if (this.#seenErrors.has(errorEvent.error)) {
55
- this.#seenErrors.delete(errorEvent.error);
56
- return;
57
- }
58
- handle('err', [this.#castErrorEvent(errorEvent), now(), false, {}, this.#replayRunning], undefined, FEATURE_NAMES.jserrors, this.ee);
40
+ handle('err', [castErrorEvent(errorEvent), now(), false, {}, this.#replayRunning], undefined, FEATURE_NAMES.jserrors, this.ee);
59
41
  }, eventListenerOpts(false, this.removeOnAbort?.signal));
60
42
  this.abortHandler = this.#abort; // we also use this as a flag to denote that the feature is active or on and handling errors
61
43
  this.importAggregator();
@@ -64,71 +46,6 @@ export class Instrument extends InstrumentBase {
64
46
  /** Restoration and resource release tasks to be done if JS error loader is being aborted. Unwind changes to globals. */
65
47
  #abort() {
66
48
  this.removeOnAbort?.abort();
67
- this.#seenErrors.clear();
68
49
  this.abortHandler = undefined; // weakly allow this abort op to run only once
69
50
  }
70
-
71
- /**
72
- * Any value can be used with the `throw` keyword. This function ensures that the value is
73
- * either a proper Error instance or attempts to convert it to an UncaughtError instance.
74
- * @param {any} error The value thrown
75
- * @returns {Error|UncaughtError} The converted error instance
76
- */
77
- #castError(error) {
78
- if (error instanceof Error) {
79
- return error;
80
- }
81
-
82
- /**
83
- * The thrown value may contain a message property. If it does, try to treat the thrown
84
- * value as an Error-like object.
85
- */
86
- if (typeof error?.message !== 'undefined') {
87
- return new UncaughtError(error.message, error.filename || error.sourceURL, error.lineno || error.line, error.colno || error.col);
88
- }
89
- return new UncaughtError(typeof error === 'string' ? error : stringify(error));
90
- }
91
-
92
- /**
93
- * Attempts to convert a PromiseRejectionEvent object to an Error object
94
- * @param {PromiseRejectionEvent} unhandledRejectionEvent The unhandled promise rejection event
95
- * @returns {Error} An Error object with the message as the casted reason
96
- */
97
- #castPromiseRejectionEvent(promiseRejectionEvent) {
98
- let prefix = 'Unhandled Promise Rejection: ';
99
- if (promiseRejectionEvent?.reason instanceof Error) {
100
- try {
101
- promiseRejectionEvent.reason.message = prefix + promiseRejectionEvent.reason.message;
102
- return promiseRejectionEvent.reason;
103
- } catch (e) {
104
- return promiseRejectionEvent.reason;
105
- }
106
- }
107
- if (typeof promiseRejectionEvent.reason === 'undefined') return new UncaughtError(prefix);
108
- const error = this.#castError(promiseRejectionEvent.reason);
109
- error.message = prefix + error.message;
110
- return error;
111
- }
112
-
113
- /**
114
- * Attempts to convert an ErrorEvent object to an Error object
115
- * @param {ErrorEvent} errorEvent The error event
116
- * @returns {Error|UncaughtError} The error event converted to an Error object
117
- */
118
- #castErrorEvent(errorEvent) {
119
- if (errorEvent.error instanceof SyntaxError && !/:\d+$/.test(errorEvent.error.stack?.trim())) {
120
- const error = new UncaughtError(errorEvent.message, errorEvent.filename, errorEvent.lineno, errorEvent.colno);
121
- error.name = SyntaxError.name;
122
- return error;
123
- }
124
- if (errorEvent.error instanceof Error) {
125
- return errorEvent.error;
126
- }
127
-
128
- /**
129
- * Older browsers do not contain the `error` property on the ErrorEvent instance.
130
- * https://caniuse.com/mdn-api_errorevent_error
131
- */
132
- return new UncaughtError(errorEvent.message, errorEvent.filename, errorEvent.lineno, errorEvent.colno);
133
- }
134
51
  }
@@ -0,0 +1,59 @@
1
+ import { UncaughtError } from './uncaught-error';
2
+
3
+ /**
4
+ * Any value can be used with the `throw` keyword. This function ensures that the value is
5
+ * either a proper Error instance or attempts to convert it to an UncaughtError instance.
6
+ * @param {any} error The value thrown
7
+ * @returns {Error|UncaughtError} The converted error instance
8
+ */
9
+ export function castError(error) {
10
+ /** Sometimes a browser can emit an error object with no stack */
11
+ if (canTrustError(error)) {
12
+ return error;
13
+ }
14
+
15
+ /**
16
+ * The thrown value may contain a message property. If it does, try to treat the thrown
17
+ * value as an Error-like object.
18
+ */
19
+ return new UncaughtError(error?.message !== undefined ? error.message : error, error?.filename || error?.sourceURL, error?.lineno || error?.line, error?.colno || error?.col, error?.__newrelic);
20
+ }
21
+
22
+ /**
23
+ * Attempts to convert a PromiseRejectionEvent object to an Error object
24
+ * @param {PromiseRejectionEvent} unhandledRejectionEvent The unhandled promise rejection event
25
+ * @returns {Error} An Error object with the message as the casted reason
26
+ */
27
+ export function castPromiseRejectionEvent(promiseRejectionEvent) {
28
+ let prefix = 'Unhandled Promise Rejection';
29
+ if (canTrustError(promiseRejectionEvent?.reason)) {
30
+ try {
31
+ promiseRejectionEvent.reason.message = prefix + ': ' + promiseRejectionEvent.reason.message;
32
+ return castError(promiseRejectionEvent.reason);
33
+ } catch (e) {
34
+ return castError(promiseRejectionEvent.reason);
35
+ }
36
+ }
37
+ if (typeof promiseRejectionEvent.reason === 'undefined') return castError(prefix);
38
+ const error = castError(promiseRejectionEvent.reason);
39
+ error.message = prefix + ': ' + error?.message;
40
+ return error;
41
+ }
42
+
43
+ /**
44
+ * Attempts to convert an ErrorEvent object to an Error object
45
+ * @param {ErrorEvent} errorEvent The error event
46
+ * @returns {Error|UncaughtError} The error event converted to an Error object
47
+ */
48
+ export function castErrorEvent(errorEvent) {
49
+ if (errorEvent.error instanceof SyntaxError && !/:\d+$/.test(errorEvent.error.stack?.trim())) {
50
+ const error = new UncaughtError(errorEvent.message, errorEvent.filename, errorEvent.lineno, errorEvent.colno, errorEvent.error.__newrelic);
51
+ error.name = SyntaxError.name;
52
+ return error;
53
+ }
54
+ if (canTrustError(errorEvent.error)) return errorEvent.error;
55
+ return castError(errorEvent);
56
+ }
57
+ function canTrustError(error) {
58
+ return error instanceof Error && !!error.stack;
59
+ }
@@ -1,3 +1,5 @@
1
+ import { stringify } from '../../../common/util/stringify';
2
+
1
3
  /**
2
4
  * Represents an uncaught non Error type error. This class does
3
5
  * not extend the Error class to prevent an invalid stack trace
@@ -5,11 +7,12 @@
5
7
  * do not use the Error class (strings, etc) to an object.
6
8
  */
7
9
  export class UncaughtError {
8
- constructor(message, filename, lineno, colno) {
10
+ constructor(message, filename, lineno, colno, newrelic) {
9
11
  this.name = 'UncaughtError';
10
- this.message = message;
12
+ this.message = typeof message === 'string' ? message : stringify(message);
11
13
  this.sourceURL = filename;
12
14
  this.line = lineno;
13
15
  this.column = colno;
16
+ this.__newrelic = newrelic;
14
17
  }
15
18
  }
@@ -1,4 +1,4 @@
1
- import { globalScope, isBrowserScope } from '../../../common/constants/runtime';
1
+ import { globalScope, isBrowserScope, originTime } from '../../../common/constants/runtime';
2
2
  import { addPT, addPN } from '../../../common/timing/nav-timing';
3
3
  import { stringify } from '../../../common/util/stringify';
4
4
  import { getInfo, getRuntime } from '../../../common/config/config';
@@ -95,14 +95,14 @@ export class Aggregate extends AggregateBase {
95
95
  // Navigation Timing level 2 API that replaced PerformanceTiming & PerformanceNavigation
96
96
  const navTimingEntry = globalScope?.performance?.getEntriesByType('navigation')?.[0];
97
97
  const perf = {
98
- timing: addPT(agentRuntime.offset, navTimingEntry, {}),
98
+ timing: addPT(originTime, navTimingEntry, {}),
99
99
  navigation: addPN(navTimingEntry, {})
100
100
  };
101
101
  queryParameters.perf = stringify(perf);
102
102
  } else if (typeof PerformanceTiming !== 'undefined') {
103
103
  // Safari pre-15 did not support level 2 timing
104
104
  const perf = {
105
- timing: addPT(agentRuntime.offset, globalScope.performance.timing, {}, true),
105
+ timing: addPT(originTime, globalScope.performance.timing, {}, true),
106
106
  navigation: addPN(globalScope.performance.navigation, {})
107
107
  };
108
108
  queryParameters.perf = stringify(perf);
@@ -134,7 +134,7 @@ export class Aggregate extends AggregateBase {
134
134
  return;
135
135
  }
136
136
  try {
137
- const timeKeeper = new TimeKeeper();
137
+ const timeKeeper = new TimeKeeper(this.agentIdentifier);
138
138
  timeKeeper.processRumRequest(xhr, rumStartTime, rumEndTime);
139
139
  if (!timeKeeper.ready) throw new Error('TimeKeeper not ready');
140
140
  agentRuntime.timeKeeper = timeKeeper;
@@ -368,6 +368,7 @@ export class Aggregate extends AggregateBase {
368
368
  ...(agentMetadata.entityGuid && {
369
369
  entityGuid: agentMetadata.entityGuid
370
370
  }),
371
+ harvestId: [agentRuntime.session?.state.value, agentRuntime.ptid, agentRuntime.harvestCount].filter(x => x).join('_'),
371
372
  'replay.firstTimestamp': firstTimestamp,
372
373
  'replay.lastTimestamp': lastTimestamp,
373
374
  'replay.nodes': events.length,
@@ -1,5 +1,5 @@
1
1
  import { getConfigurationValue, originals } from '../../../common/config/config';
2
- import { isBrowserScope } from '../../../common/constants/runtime';
2
+ import { isBrowserScope, originTime } from '../../../common/constants/runtime';
3
3
  export function enableSessionTracking(agentId) {
4
4
  return isBrowserScope && getConfigurationValue(agentId, 'privacy.cookies_enabled') === true;
5
5
  }
@@ -23,8 +23,8 @@ export function buildNRMetaNode(timestamp, timeKeeper) {
23
23
  originalTimestamp: timestamp,
24
24
  correctedTimestamp,
25
25
  timestampDiff: timestamp - correctedTimestamp,
26
- timeKeeperOriginTime: timeKeeper.originTime,
27
- timeKeeperCorrectedOriginTime: timeKeeper.correctedOriginTime,
28
- timeKeeperDiff: Math.floor(timeKeeper.originTime - timeKeeper.correctedOriginTime)
26
+ originTime,
27
+ correctedOriginTime: timeKeeper.correctedOriginTime,
28
+ originTimeDiff: Math.floor(originTime - timeKeeper.correctedOriginTime)
29
29
  };
30
30
  }
@@ -12,6 +12,7 @@ import { getSessionReplayMode } from '../../session_replay/shared/replay-mode';
12
12
  import { AggregateBase } from '../../utils/aggregate-base';
13
13
  import { MODE, SESSION_EVENTS } from '../../../common/session/constants';
14
14
  import { now } from '../../../common/timing/now';
15
+ import { originTime } from '../../../common/constants/runtime';
15
16
  const ignoredEvents = {
16
17
  // we find that certain events make the data too noisy to be useful
17
18
  global: {
@@ -46,14 +47,12 @@ export class Aggregate extends AggregateBase {
46
47
  super(agentIdentifier, aggregator, FEATURE_NAME);
47
48
  _this = this;
48
49
  this.agentRuntime = getRuntime(agentIdentifier);
49
-
50
- // Very unlikely, but in case the existing XMLHttpRequest.prototype object on the page couldn't be wrapped.
51
- if (!this.agentRuntime.xhrWrappable) return;
52
50
  this.resourceObserver = argsObj?.resourceObserver; // undefined if observer couldn't be created
53
51
  this.ptid = '';
54
52
  this.trace = {};
55
53
  this.nodeCount = 0;
56
54
  this.sentTrace = null;
55
+ this.prevStoredEvents = new Set();
57
56
  this.harvestTimeSeconds = getConfigurationValue(agentIdentifier, 'session_trace.harvestTimeSeconds') || 10;
58
57
  this.maxNodesPerHarvest = getConfigurationValue(agentIdentifier, 'session_trace.maxNodesPerHarvest') || 1000;
59
58
  /**
@@ -121,7 +120,7 @@ export class Aggregate extends AggregateBase {
121
120
  return controlTraceOp(on);
122
121
  }, this.featureName, this.ee);
123
122
  } else {
124
- registerHandler('errorAgg', () => {
123
+ registerHandler('trace-jserror', () => {
125
124
  seenAnError = true;
126
125
  switchToFull();
127
126
  }, this.featureName, this.ee);
@@ -216,7 +215,7 @@ export class Aggregate extends AggregateBase {
216
215
  }
217
216
  return operationalGate.settle(() => _this.storeSTN(...args));
218
217
  }, this.featureName, this.ee);
219
- registerHandler('errorAgg', function () {
218
+ registerHandler('trace-jserror', function () {
220
219
  for (var _len6 = arguments.length, args = new Array(_len6), _key6 = 0; _key6 < _len6; _key6++) {
221
220
  args[_key6] = arguments[_key6];
222
221
  }
@@ -265,6 +264,7 @@ export class Aggregate extends AggregateBase {
265
264
  }
266
265
  }
267
266
  #prepareHarvest(options) {
267
+ this.prevStoredEvents.clear(); // release references to past events for GC
268
268
  if (this.isStandalone) {
269
269
  if (this.ptid && now() >= MAX_TRACE_DURATION) {
270
270
  // Perform a final harvest once we hit or exceed the max session trace time
@@ -331,6 +331,8 @@ export class Aggregate extends AggregateBase {
331
331
  // Tracks the events and their listener's duration on objects wrapped by wrap-events.
332
332
  storeEvent(currentEvent, target, start, end) {
333
333
  if (this.shouldIgnoreEvent(currentEvent, target)) return;
334
+ if (this.prevStoredEvents.has(currentEvent)) return; // prevent multiple listeners of an event from creating duplicate trace nodes per occurrence. Cleared every harvest. near-zero chance for re-duplication after clearing per harvest since the timestamps of the event are considered for uniqueness.
335
+ this.prevStoredEvents.add(currentEvent);
334
336
  const evt = {
335
337
  n: this.evtName(currentEvent.type),
336
338
  s: start,
@@ -531,13 +533,13 @@ export class Aggregate extends AggregateBase {
531
533
  }
532
534
  return {
533
535
  qs: {
534
- st: this.agentRuntime.offset,
536
+ st: originTime,
535
537
  /** hr === "hasReplay" in NR1, standalone is always checked and processed before harvesting
536
538
  * so a race condition between ST and SR states should not be a concern if implemented here */
537
539
  hr: Number(!this.isStandalone),
538
540
  /** fts === "firstTimestamp" in NR1, indicates what the earliest NODE timestamp was
539
541
  * so that blob parsing doesn't need to happen to support UI/API functions */
540
- fts: this.agentRuntime.offset + earliestTimeStamp,
542
+ fts: originTime + earliestTimeStamp,
541
543
  /** n === "nodeCount" in NR1, a count of nodes in the ST payload, so that blob parsing doesn't need to happen to support UI/API functions */
542
544
  n: stns.length,
543
545
  // node count
@@ -642,7 +642,7 @@ export class Aggregate extends AggregateBase {
642
642
  state.interactionsSent = [];
643
643
  }
644
644
  }
645
- baseEE.on('errorAgg', function (type, name, params, metrics) {
645
+ baseEE.on('spa-jserror', function (type, name, params, metrics) {
646
646
  if (!state.currentNode) return;
647
647
  params._interactionId = state.currentNode.interaction.id;
648
648
  // do not capture parentNodeId when in root node
@@ -650,6 +650,16 @@ export class Aggregate extends AggregateBase {
650
650
  params._interactionNodeId = state.currentNode.id;
651
651
  }
652
652
  });
653
+ register('function-err', function (args, obj, error) {
654
+ if (!state.currentNode) return;
655
+ error.__newrelic ??= {};
656
+ error.__newrelic[agentIdentifier] = {
657
+ interactionId: state.currentNode.interaction.id
658
+ };
659
+ if (state.currentNode.type && state.currentNode.type !== 'interaction') {
660
+ error.__newrelic[agentIdentifier].interactionNodeId = state.currentNode.id;
661
+ }
662
+ }, this.featureName, baseEE);
653
663
  baseEE.on('interaction', saveInteraction);
654
664
  function getActionText(node) {
655
665
  var nodeType = node.tagName.toLowerCase();
@@ -5,10 +5,10 @@
5
5
  import { wrapMutation, wrapPromise, wrapHistory, wrapTimer, wrapFetch, wrapXhr, wrapJsonP } from '../../../common/wrap';
6
6
  import { eventListenerOpts } from '../../../common/event-listener/event-listener-opts';
7
7
  import { InstrumentBase } from '../../utils/instrument-base';
8
- import { getRuntime } from '../../../common/config/config';
9
8
  import * as CONSTANTS from '../constants';
10
9
  import { isBrowserScope } from '../../../common/constants/runtime';
11
10
  import { now } from '../../../common/timing/now';
11
+ import { handle } from '../../../common/event-emitter/handle';
12
12
  const {
13
13
  FEATURE_NAME,
14
14
  START,
@@ -24,11 +24,12 @@ const {
24
24
  export class Instrument extends InstrumentBase {
25
25
  static featureName = FEATURE_NAME;
26
26
  constructor(agentIdentifier, aggregator) {
27
+ var _this;
27
28
  let auto = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : true;
28
29
  super(agentIdentifier, aggregator, FEATURE_NAME, auto);
30
+ _this = this;
29
31
  if (!isBrowserScope) return; // SPA not supported outside web env
30
32
 
31
- if (!getRuntime(agentIdentifier).xhrWrappable) return;
32
33
  try {
33
34
  this.removeOnAbort = new AbortController();
34
35
  } catch (e) {}
@@ -49,6 +50,12 @@ export class Instrument extends InstrumentBase {
49
50
  this.ee.on(FN_END, endTimestamp);
50
51
  promiseEE.on(CB_END, endTimestamp);
51
52
  jsonpEE.on(CB_END, endTimestamp);
53
+ this.ee.on('fn-err', function () {
54
+ for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
55
+ args[_key] = arguments[_key];
56
+ }
57
+ if (!args[2]?.__newrelic?.[agentIdentifier]) handle('function-err', [...args], undefined, _this.featureName, _this.ee);
58
+ });
52
59
  this.ee.buffer([FN_START, FN_END, 'xhr-resolved'], this.featureName);
53
60
  eventsEE.buffer([FN_START], this.featureName);
54
61
  timerEE.buffer(['setTimeout' + END, 'clearTimeout' + START, FN_START], this.featureName);
@@ -4,20 +4,16 @@ import { ee } from '../../common/event-emitter/contextual-ee';
4
4
  import { registerHandler } from '../../common/event-emitter/register-handler';
5
5
  import { SessionEntity } from '../../common/session/session-entity';
6
6
  import { LocalStorage } from '../../common/storage/local-storage.js';
7
- import { FirstPartyCookies } from '../../common/storage/first-party-cookies';
8
7
  import { DEFAULT_KEY } from '../../common/session/constants';
9
8
  let ranOnce = 0;
10
9
  export function setupAgentSession(agentIdentifier) {
11
10
  const agentRuntime = getRuntime(agentIdentifier);
12
11
  if (ranOnce++) return agentRuntime.session;
13
12
  const sessionInit = getConfiguration(agentIdentifier).session;
14
- /* Domain is a string that can be specified by customer. The only way to keep the session object across subdomains is using first party cookies.
15
- This determines which storage wrapper the session manager will use to keep state. */
16
- const storageTypeInst = sessionInit?.domain ? new FirstPartyCookies(sessionInit.domain) : new LocalStorage();
17
13
  agentRuntime.session = new SessionEntity({
18
14
  agentIdentifier,
19
15
  key: DEFAULT_KEY,
20
- storage: storageTypeInst,
16
+ storage: new LocalStorage(),
21
17
  expiresMs: sessionInit?.expiresMs,
22
18
  inactiveMs: sessionInit?.inactiveMs
23
19
  });
@@ -52,7 +52,7 @@ export class InstrumentBase extends FeatureBase {
52
52
  if (getConfigurationValue(this.agentIdentifier, "".concat(this.featureName, ".autoStart")) === false) this.auto = false;
53
53
  /** if the feature requires opt-in (!auto-start), it will get registered once the api has been called */
54
54
  if (this.auto) registerDrain(agentIdentifier, featureName);else {
55
- this.ee.on("".concat(this.featureName, "-opt-in"), single(() => {
55
+ this.ee.on('manual-start-all', single(() => {
56
56
  // register the feature to drain only once the API has been called, it will drain when importAggregator finishes for all the features
57
57
  // called by the api in that cycle
58
58
  registerDrain(this.agentIdentifier, this.featureName);
@@ -125,19 +125,10 @@ export function setAPI(agentIdentifier, forceDrain) {
125
125
  }
126
126
  return appendJsAttribute('application.version', value, 'setApplicationVersion', false);
127
127
  };
128
- apiInterface.start = features => {
128
+ apiInterface.start = () => {
129
129
  try {
130
- const smTag = !features ? 'undefined' : 'defined';
131
- handle(SUPPORTABILITY_METRIC_CHANNEL, ["API/start/".concat(smTag, "/called")], undefined, FEATURE_NAMES.metrics, instanceEE);
132
- const featNames = Object.values(FEATURE_NAMES);
133
- if (features === undefined) features = featNames;else {
134
- features = Array.isArray(features) && features.length ? features : [features];
135
- if (features.some(f => !featNames.includes(f))) return warn("Invalid feature name supplied. Acceptable feature names are: ".concat(featNames));
136
- if (!features.includes(FEATURE_NAMES.pageViewEvent)) features.push(FEATURE_NAMES.pageViewEvent);
137
- }
138
- features.forEach(feature => {
139
- instanceEE.emit("".concat(feature, "-opt-in"));
140
- });
130
+ handle(SUPPORTABILITY_METRIC_CHANNEL, ['API/start/called'], undefined, FEATURE_NAMES.metrics, instanceEE);
131
+ instanceEE.emit('manual-start-all');
141
132
  } catch (err) {
142
133
  warn('An unexpected issue occurred', err);
143
134
  }
@@ -168,9 +159,10 @@ export function setAPI(agentIdentifier, forceDrain) {
168
159
  try {
169
160
  return cb.apply(this, arguments);
170
161
  } catch (err) {
171
- tracerEE.emit('fn-err', [arguments, this, err], contextStore);
162
+ const error = typeof err === 'string' ? new Error(err) : err;
163
+ tracerEE.emit('fn-err', [arguments, this, error], contextStore);
172
164
  // the error came from outside the agent, so don't swallow
173
- throw err;
165
+ throw error;
174
166
  } finally {
175
167
  tracerEE.emit('fn-end', [now()], contextStore);
176
168
  }
@@ -5,6 +5,7 @@ import { handle } from '../../common/event-emitter/handle';
5
5
  import { registerHandler } from '../../common/event-emitter/register-handler';
6
6
  import { single } from '../../common/util/invoke';
7
7
  import { CUSTOM_METRIC_CHANNEL } from '../../features/metrics/constants';
8
+ import { originTime } from '../../common/constants/runtime';
8
9
  export function setAPI(agentIdentifier) {
9
10
  var instanceEE = ee.get(agentIdentifier);
10
11
  var api = {
@@ -24,13 +25,13 @@ export function setAPI(agentIdentifier) {
24
25
  // first parameter. These functions can be called asynchronously.
25
26
 
26
27
  function finished(t, providedTime) {
27
- var time = providedTime ? providedTime - getRuntime(agentIdentifier).offset : t;
28
+ var time = providedTime ? providedTime - originTime : t;
28
29
  handle(CUSTOM_METRIC_CHANNEL, ['finished', {
29
30
  time
30
31
  }], undefined, FEATURE_NAMES.metrics, instanceEE);
31
32
  addToTrace(t, {
32
33
  name: 'finished',
33
- start: time + getRuntime(agentIdentifier).offset,
34
+ start: time + originTime,
34
35
  origin: 'nr'
35
36
  });
36
37
  handle('api-addPageAction', [time, 'finished'], undefined, FEATURE_NAMES.pageAction, instanceEE);
@@ -39,8 +40,8 @@ export function setAPI(agentIdentifier) {
39
40
  if (!(evt && typeof evt === 'object' && evt.name && evt.start)) return;
40
41
  var report = {
41
42
  n: evt.name,
42
- s: evt.start - getRuntime(agentIdentifier).offset,
43
- e: (evt.end || evt.start) - getRuntime(agentIdentifier).offset,
43
+ s: evt.start - originTime,
44
+ e: (evt.end || evt.start) - originTime,
44
45
  o: evt.origin || '',
45
46
  t: 'api'
46
47
  };
@@ -1 +1 @@
1
- {"version":3,"file":"configurable.d.ts","sourceRoot":"","sources":["../../../../../src/common/config/state/configurable.js"],"names":[],"mappings":"AAEA,4DAyBC"}
1
+ {"version":3,"file":"configurable.d.ts","sourceRoot":"","sources":["../../../../../src/common/config/state/configurable.js"],"names":[],"mappings":"AAEA,4DA0BC"}
@@ -1 +1 @@
1
- {"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../../../../../src/common/config/state/init.js"],"names":[],"mappings":"AA+GA,+CAIC;AAED,0DAKC;AAED,+DAYC"}
1
+ {"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../../../../../src/common/config/state/init.js"],"names":[],"mappings":"AA8GA,+CAIC;AAED,0DAKC;AAED,+DAYC"}
@@ -1 +1 @@
1
- {"version":3,"file":"runtime.d.ts","sourceRoot":"","sources":["../../../../../src/common/config/state/runtime.js"],"names":[],"mappings":"AAgCA,yCAIC;AAED,oDAKC"}
1
+ {"version":3,"file":"runtime.d.ts","sourceRoot":"","sources":["../../../../../src/common/config/state/runtime.js"],"names":[],"mappings":"AAgCA,yCAIC;AAED,oDAQC"}
@@ -28,5 +28,10 @@ export const iOSBelow16: boolean;
28
28
  export const ffVersion: number;
29
29
  export const isIE: boolean;
30
30
  export const supportsSendBeacon: boolean;
31
- export const offset: number;
31
+ /**
32
+ * Represents the absolute timestamp in milliseconds that the page was loaded
33
+ * according to the browser's local clock.
34
+ * @type {number}
35
+ */
36
+ export const originTime: number;
32
37
  //# sourceMappingURL=runtime.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"runtime.d.ts","sourceRoot":"","sources":["../../../../src/common/constants/runtime.js"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH;;GAEG;AACH,qCAEqB;AAErB;;GAEG;AACH,oCAeK;AAEL,oDAUI;AAEJ,oDAA6F;AAE7F,sCAA2F;AAE3F,qCAAyD;AAEzD,4BAA8E;AAE9E;;;;;;GAMG;AACH,iCAAwE;AAExE,+BAOI;AAEJ,2BAA2E;AAE3E,yCAAqE;AAErE,4BAAgE"}
1
+ {"version":3,"file":"runtime.d.ts","sourceRoot":"","sources":["../../../../src/common/constants/runtime.js"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH;;GAEG;AACH,qCAEqB;AAErB;;GAEG;AACH,oCAeK;AAEL,oDAUI;AAEJ,oDAA6F;AAE7F,sCAA2F;AAE3F,qCAAyD;AAEzD,4BAA8E;AAE9E;;;;;;GAMG;AACH,iCAAwE;AAExE,+BAOI;AAEJ,2BAA2E;AAE3E,yCAAqE;AAErE;;;;GAIG;AACH,yBAFU,MAAM,CAEoD"}
@@ -4,6 +4,7 @@
4
4
  * @returns {boolean} `true` if request does not match any entries of {@link denyList|deny list}; else `false`
5
5
  */
6
6
  export function shouldCollectEvent(params: Object): boolean;
7
+ export function hasUndefinedHostname(params: any): boolean;
7
8
  /**
8
9
  * Initializes the {@link denyList|XHR deny list} by extracting hostname and pathname from an array of filter strings.
9
10
  * @param {string[]} denyListConfig - array of URL filters to identify XHR requests to be excluded from collection
@@ -1 +1 @@
1
- {"version":3,"file":"deny-list.d.ts","sourceRoot":"","sources":["../../../../src/common/deny-list/deny-list.js"],"names":[],"mappings":"AAMA;;;;GAIG;AACH,2CAHW,MAAM,GACJ,OAAO,CA0BnB;AAED;;;GAGG;AACH,4CAFW,MAAM,EAAE,QAgClB"}
1
+ {"version":3,"file":"deny-list.d.ts","sourceRoot":"","sources":["../../../../src/common/deny-list/deny-list.js"],"names":[],"mappings":"AAMA;;;;GAIG;AACH,2CAHW,MAAM,GACJ,OAAO,CAqBnB;AAED,2DAEC;AAED;;;GAGG;AACH,4CAFW,MAAM,EAAE,QAgClB"}
@@ -3,6 +3,7 @@ export const DEFAULT_KEY: "SESSION";
3
3
  export const DEFAULT_EXPIRES_MS: 14400000;
4
4
  export const DEFAULT_INACTIVE_MS: 1800000;
5
5
  export namespace SESSION_EVENTS {
6
+ let STARTED: string;
6
7
  let PAUSE: string;
7
8
  let RESET: string;
8
9
  let RESUME: string;
@@ -1 +1 @@
1
- {"version":3,"file":"session-entity.d.ts","sourceRoot":"","sources":["../../../../src/common/session/session-entity.js"],"names":[],"mappings":"AA6BA;IACE;;;;;OAKG;IACH,uBA0BC;IApBC,qBAAsC;IACtC,aAAsB;IACtB,UAAe;IAGf,SAAc;IAEd,QAAiC;IAenC;;;;aAyEC;IAlEC,8BAA0B;IAC1B,+BAA4B;IAc1B,gCAOqC;IAUrC,4CAkBsC;IAexC,iCAAuB;IAIzB,wBAEC;IAED,sBAEC;IAED;;;OAGG;IACH,QAFa,MAAM,CA6BlB;IAED;;;;;;OAMG;IACH,YAHW,MAAM,GACJ,MAAM,CAkBlB;IAED,gBAuBC;IAED;;OAEG;IACH,gBAIC;IAED;;;OAGG;IACH,qBAHW,MAAM,GACJ,OAAO,CAInB;IAED;;;OAGG;IACH,gBAHW,MAAM,GACJ,OAAO,CAKnB;IAED,yDAUC;IAED,6DAIC;IAED;;;OAGG;IACH,6BAHW,MAAM,GACJ,MAAM,CAIlB;IAED,gDAaC;IAHG,YAAuD;CAI5D;sBArSqB,gBAAgB;iCAGL,4BAA4B"}
1
+ {"version":3,"file":"session-entity.d.ts","sourceRoot":"","sources":["../../../../src/common/session/session-entity.js"],"names":[],"mappings":"AA8BA;IACE;;;;;OAKG;IACH,uBA0BC;IApBC,qBAAsC;IACtC,aAAsB;IACtB,UAAe;IAGf,SAAc;IAEd,QAAiC;IAenC;;;;aA0EC;IAnEC,8BAA0B;IAC1B,+BAA4B;IAc1B,gCAOqC;IAUrC,4CAkBsC;IAexC,iCAAuB;IAKzB,wBAEC;IAED,sBAEC;IAED;;;OAGG;IACH,QAFa,MAAM,CA6BlB;IAED;;;;;;OAMG;IACH,YAHW,MAAM,GACJ,MAAM,CAkBlB;IAED,gBAuBC;IAED;;OAEG;IACH,gBAIC;IAED;;;OAGG;IACH,qBAHW,MAAM,GACJ,OAAO,CAInB;IAED;;;OAGG;IACH,gBAHW,MAAM,GACJ,OAAO,CAKnB;IAED,yDAUC;IAED,6DAIC;IAED;;;OAGG;IACH,6BAHW,MAAM,GACJ,MAAM,CAIlB;IAED,gDAaC;IAHG,YAAuD;CAI5D;sBAvSqB,gBAAgB;iCAGL,4BAA4B"}
@@ -4,8 +4,8 @@
4
4
  * to the harvested data event offset time.
5
5
  */
6
6
  export class TimeKeeper {
7
+ constructor(agentIdentifier: any);
7
8
  get ready(): number;
8
- get originTime(): number;
9
9
  get correctedOriginTime(): number;
10
10
  /**
11
11
  * Process a rum request to calculate NR server time.
@@ -1 +1 @@
1
- {"version":3,"file":"time-keeper.d.ts","sourceRoot":"","sources":["../../../../src/common/timing/time-keeper.js"],"names":[],"mappings":"AAAA;;;;GAIG;AACH;IA+BE,oBAEC;IAED,yBAEC;IAED,kCAEC;IAED;;;;;OAKG;IACH,8BAJsB,cAAc,aACf,MAAM,WACR,MAAM,QAoBxB;IAED;;;;;OAKG;IACH,uCAHwB,MAAM,GACjB,MAAM,CAIlB;IAED;;;;OAIG;IACH,oCAHqB,MAAM,GACf,MAAM,CAIjB;;CACF"}
1
+ {"version":3,"file":"time-keeper.d.ts","sourceRoot":"","sources":["../../../../src/common/timing/time-keeper.js"],"names":[],"mappings":"AAKA;;;;GAIG;AACH;IA2BE,kCAaC;IAED,oBAEC;IAED,kCAEC;IAED;;;;;OAKG;IACH,8BAJsB,cAAc,aACf,MAAM,WACR,MAAM,QAuBxB;IAED;;;;;OAKG;IACH,uCAHwB,MAAM,GACjB,MAAM,CAIlB;IAED;;;;OAIG;IACH,oCAHqB,MAAM,GACf,MAAM,CAIjB;;CAoBF"}
@@ -0,0 +1,8 @@
1
+ export default class Chunk {
2
+ constructor(events: any, aggregateInstance: any);
3
+ addString: (str: any) => string;
4
+ events: any;
5
+ payload: string;
6
+ tooBig: boolean;
7
+ }
8
+ //# sourceMappingURL=chunk.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"chunk.d.ts","sourceRoot":"","sources":["../../../../../src/features/ajax/aggregate/chunk.js"],"names":[],"mappings":"AAGA;IACE,iDA6CC;IA5CC,gCAAuE;IACvE,YAAoB;IACpB,gBAAuB;IAyCvB,gBAA0E;CAE7E"}
@@ -1,16 +1,18 @@
1
1
  export class Aggregate extends AggregateBase {
2
2
  static featureName: string;
3
3
  constructor(agentIdentifier: any, aggregator: any);
4
- storeXhr: (params: any, metrics: any, startTime: any, endTime: any, type: any) => void;
5
- prepareHarvest: (options: any) => {
4
+ MAX_PAYLOAD_SIZE: any;
5
+ ajaxEvents: any[];
6
+ spaAjaxEvents: {};
7
+ sentAjaxEvents: any[];
8
+ storeXhr(params: any, metrics: any, startTime: any, endTime: any, type: any, ctx: any): void;
9
+ prepareHarvest(options: any): {
6
10
  body: {
7
11
  e: any;
8
12
  };
9
13
  }[] | null;
10
- getStoredEvents: () => {
11
- ajaxEvents: any[];
12
- spaAjaxEvents: {};
13
- };
14
+ onEventsHarvestFinished(result: any): void;
15
+ #private;
14
16
  }
15
17
  import { AggregateBase } from '../../utils/aggregate-base';
16
18
  //# sourceMappingURL=index.d.ts.map