@newrelic/browser-agent 0.1.231 → 1.232.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 (256) hide show
  1. package/README.md +2 -2
  2. package/dist/cjs/common/config/state/configurable.js +27 -21
  3. package/dist/cjs/common/config/state/init.js +8 -0
  4. package/dist/cjs/common/config/state/runtime.js +24 -26
  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/context/shared-context.js +2 -1
  8. package/dist/cjs/common/event-emitter/contextual-ee.test.js +2 -2
  9. package/dist/cjs/common/event-emitter/register-handler.test.js +1 -1
  10. package/dist/cjs/common/event-listener/event-listener-opts.js +4 -2
  11. package/dist/cjs/common/harvest/harvest-scheduler.js +14 -11
  12. package/dist/cjs/common/harvest/harvest.js +3 -1
  13. package/dist/cjs/common/session/constants.js +12 -0
  14. package/dist/cjs/common/session/session-entity.js +278 -0
  15. package/dist/cjs/common/session/session-entity.test.js +436 -0
  16. package/dist/cjs/common/storage/first-party-cookies.js +35 -0
  17. package/dist/cjs/common/storage/local-memory.js +35 -0
  18. package/dist/cjs/common/storage/local-memory.test.js +20 -0
  19. package/dist/cjs/common/storage/local-storage.js +33 -0
  20. package/dist/cjs/common/storage/local-storage.test.js +14 -0
  21. package/dist/cjs/common/timer/interaction-timer.js +78 -0
  22. package/dist/cjs/common/timer/interaction-timer.test.js +216 -0
  23. package/dist/cjs/common/timer/timer.js +32 -0
  24. package/dist/cjs/common/timer/timer.test.js +105 -0
  25. package/dist/cjs/common/unload/eol.js +2 -2
  26. package/dist/cjs/common/url/canonicalize-url.js +32 -0
  27. package/dist/cjs/common/url/canonicalize-url.test.js +42 -0
  28. package/dist/cjs/common/url/clean-url.js +10 -3
  29. package/dist/cjs/common/util/data-size.js +6 -0
  30. package/dist/cjs/common/util/data-size.test.js +47 -0
  31. package/dist/cjs/common/util/global-scope.js +4 -2
  32. package/dist/cjs/common/util/invoke.js +73 -0
  33. package/dist/cjs/common/util/invoke.test.js +49 -0
  34. package/dist/cjs/common/util/obfuscate.js +0 -4
  35. package/dist/cjs/common/window/page-visibility.js +3 -1
  36. package/dist/cjs/common/wrap/wrap-fetch.js +1 -3
  37. package/dist/cjs/common/wrap/wrap-function.js +1 -3
  38. package/dist/cjs/common/wrap/wrap-timer.js +1 -1
  39. package/dist/cjs/features/ajax/aggregate/index.js +2 -2
  40. package/dist/cjs/features/ajax/instrument/index.js +1 -1
  41. package/dist/cjs/features/jserrors/aggregate/canonical-function-name.js +12 -4
  42. package/dist/cjs/features/jserrors/aggregate/compute-stack-trace.js +93 -10
  43. package/dist/cjs/features/jserrors/aggregate/compute-stack-trace.test.js +164 -38
  44. package/dist/cjs/features/jserrors/aggregate/index.js +25 -46
  45. package/dist/cjs/features/jserrors/instrument/index.js +0 -2
  46. package/dist/cjs/features/metrics/aggregate/index.js +13 -2
  47. package/dist/cjs/features/page_action/aggregate/index.js +2 -2
  48. package/dist/cjs/features/page_view_event/aggregate/index.js +6 -3
  49. package/dist/cjs/features/page_view_timing/aggregate/index.js +6 -6
  50. package/dist/cjs/features/session_trace/aggregate/index.js +3 -5
  51. package/dist/cjs/features/spa/aggregate/index.js +6 -5
  52. package/dist/cjs/features/utils/agent-session.js +73 -0
  53. package/dist/cjs/features/utils/feature-base.js +1 -1
  54. package/dist/cjs/features/utils/instrument-base.js +7 -2
  55. package/dist/cjs/features/utils/lazy-loader.js +1 -1
  56. package/dist/cjs/loaders/agent.js +1 -1
  57. package/dist/cjs/loaders/api/api.js +1 -4
  58. package/dist/cjs/loaders/api/apiAsync.js +3 -2
  59. package/dist/cjs/loaders/configure/configure.js +0 -6
  60. package/dist/esm/common/config/state/configurable.js +26 -20
  61. package/dist/esm/common/config/state/init.js +8 -0
  62. package/dist/esm/common/config/state/runtime.js +24 -26
  63. package/dist/esm/common/constants/env.cdn.js +1 -1
  64. package/dist/esm/common/constants/env.npm.js +1 -1
  65. package/dist/esm/common/context/shared-context.js +2 -1
  66. package/dist/esm/common/event-emitter/contextual-ee.test.js +2 -2
  67. package/dist/esm/common/event-emitter/register-handler.test.js +1 -1
  68. package/dist/esm/common/event-listener/event-listener-opts.js +4 -2
  69. package/dist/esm/common/harvest/harvest-scheduler.js +14 -11
  70. package/dist/esm/common/harvest/harvest.js +3 -1
  71. package/dist/esm/common/session/constants.js +3 -0
  72. package/dist/esm/common/session/session-entity.js +271 -0
  73. package/dist/esm/common/session/session-entity.test.js +434 -0
  74. package/dist/esm/common/storage/first-party-cookies.js +28 -0
  75. package/dist/esm/common/storage/local-memory.js +28 -0
  76. package/dist/esm/common/storage/local-memory.test.js +18 -0
  77. package/dist/esm/common/storage/local-storage.js +26 -0
  78. package/dist/esm/common/storage/local-storage.test.js +12 -0
  79. package/dist/esm/common/timer/interaction-timer.js +71 -0
  80. package/dist/esm/common/timer/interaction-timer.test.js +214 -0
  81. package/dist/esm/common/timer/timer.js +25 -0
  82. package/dist/esm/common/timer/timer.test.js +103 -0
  83. package/dist/esm/common/unload/eol.js +1 -1
  84. package/dist/esm/common/url/canonicalize-url.js +27 -0
  85. package/dist/esm/common/url/canonicalize-url.test.js +38 -0
  86. package/dist/esm/common/url/clean-url.js +10 -3
  87. package/dist/esm/common/util/data-size.js +7 -0
  88. package/dist/esm/common/util/data-size.test.js +45 -0
  89. package/dist/esm/common/util/global-scope.js +1 -0
  90. package/dist/esm/common/util/invoke.js +66 -0
  91. package/dist/esm/common/util/invoke.test.js +47 -0
  92. package/dist/esm/common/util/obfuscate.js +0 -4
  93. package/dist/esm/common/window/page-visibility.js +3 -1
  94. package/dist/esm/common/wrap/wrap-fetch.js +1 -2
  95. package/dist/esm/common/wrap/wrap-function.js +1 -2
  96. package/dist/esm/common/wrap/wrap-timer.js +1 -1
  97. package/dist/esm/features/ajax/aggregate/index.js +2 -2
  98. package/dist/esm/features/ajax/instrument/index.js +1 -1
  99. package/dist/esm/features/jserrors/aggregate/canonical-function-name.js +12 -4
  100. package/dist/esm/features/jserrors/aggregate/compute-stack-trace.js +93 -10
  101. package/dist/esm/features/jserrors/aggregate/compute-stack-trace.test.js +149 -25
  102. package/dist/esm/features/jserrors/aggregate/index.js +26 -46
  103. package/dist/esm/features/jserrors/instrument/index.js +0 -1
  104. package/dist/esm/features/metrics/aggregate/index.js +14 -3
  105. package/dist/esm/features/page_action/aggregate/index.js +2 -2
  106. package/dist/esm/features/page_view_event/aggregate/index.js +6 -3
  107. package/dist/esm/features/page_view_timing/aggregate/index.js +6 -6
  108. package/dist/esm/features/session_trace/aggregate/index.js +3 -4
  109. package/dist/esm/features/spa/aggregate/index.js +6 -5
  110. package/dist/esm/features/utils/agent-session.js +67 -0
  111. package/dist/esm/features/utils/feature-base.js +1 -1
  112. package/dist/esm/features/utils/instrument-base.js +7 -2
  113. package/dist/esm/features/utils/lazy-loader.js +1 -1
  114. package/dist/esm/loaders/agent.js +1 -1
  115. package/dist/esm/loaders/api/api.js +2 -5
  116. package/dist/esm/loaders/api/apiAsync.js +2 -1
  117. package/dist/esm/loaders/configure/configure.js +2 -8
  118. package/dist/types/common/config/state/configurable.d.ts.map +1 -1
  119. package/dist/types/common/config/state/init.d.ts.map +1 -1
  120. package/dist/types/common/config/state/runtime.d.ts.map +1 -1
  121. package/dist/types/common/context/shared-context.d.ts.map +1 -1
  122. package/dist/types/common/event-listener/event-listener-opts.d.ts +2 -2
  123. package/dist/types/common/event-listener/event-listener-opts.d.ts.map +1 -1
  124. package/dist/types/common/harvest/harvest-scheduler.d.ts +1 -0
  125. package/dist/types/common/harvest/harvest-scheduler.d.ts.map +1 -1
  126. package/dist/types/common/harvest/harvest.d.ts.map +1 -1
  127. package/dist/types/common/session/constants.d.ts +4 -0
  128. package/dist/types/common/session/constants.d.ts.map +1 -0
  129. package/dist/types/common/session/session-entity.d.ts +72 -0
  130. package/dist/types/common/session/session-entity.d.ts.map +1 -0
  131. package/dist/types/common/storage/first-party-cookies.d.ts +8 -0
  132. package/dist/types/common/storage/first-party-cookies.d.ts.map +1 -0
  133. package/dist/types/common/storage/local-memory.d.ts +8 -0
  134. package/dist/types/common/storage/local-memory.d.ts.map +1 -0
  135. package/dist/types/common/storage/local-storage.d.ts +6 -0
  136. package/dist/types/common/storage/local-storage.d.ts.map +1 -0
  137. package/dist/types/common/timer/interaction-timer.d.ts +11 -0
  138. package/dist/types/common/timer/interaction-timer.d.ts.map +1 -0
  139. package/dist/types/common/timer/timer.d.ts +12 -0
  140. package/dist/types/common/timer/timer.d.ts.map +1 -0
  141. package/dist/types/common/url/canonicalize-url.d.ts +9 -0
  142. package/dist/types/common/url/canonicalize-url.d.ts.map +1 -0
  143. package/dist/types/common/url/clean-url.d.ts +7 -1
  144. package/dist/types/common/url/clean-url.d.ts.map +1 -1
  145. package/dist/types/common/util/data-size.d.ts +7 -1
  146. package/dist/types/common/util/data-size.d.ts.map +1 -1
  147. package/dist/types/common/util/global-scope.d.ts +1 -0
  148. package/dist/types/common/util/global-scope.d.ts.map +1 -1
  149. package/dist/types/common/util/invoke.d.ts +35 -0
  150. package/dist/types/common/util/invoke.d.ts.map +1 -0
  151. package/dist/types/common/util/obfuscate.d.ts.map +1 -1
  152. package/dist/types/common/window/page-visibility.d.ts +1 -1
  153. package/dist/types/common/window/page-visibility.d.ts.map +1 -1
  154. package/dist/types/common/wrap/wrap-fetch.d.ts.map +1 -1
  155. package/dist/types/common/wrap/wrap-function.d.ts.map +1 -1
  156. package/dist/types/features/ajax/aggregate/index.d.ts +2 -2
  157. package/dist/types/features/ajax/aggregate/index.d.ts.map +1 -1
  158. package/dist/types/features/jserrors/aggregate/canonical-function-name.d.ts +8 -1
  159. package/dist/types/features/jserrors/aggregate/canonical-function-name.d.ts.map +1 -1
  160. package/dist/types/features/jserrors/aggregate/compute-stack-trace.d.ts +48 -19
  161. package/dist/types/features/jserrors/aggregate/compute-stack-trace.d.ts.map +1 -1
  162. package/dist/types/features/jserrors/aggregate/index.d.ts +14 -5
  163. package/dist/types/features/jserrors/aggregate/index.d.ts.map +1 -1
  164. package/dist/types/features/jserrors/instrument/index.d.ts.map +1 -1
  165. package/dist/types/features/metrics/aggregate/index.d.ts +2 -2
  166. package/dist/types/features/metrics/aggregate/index.d.ts.map +1 -1
  167. package/dist/types/features/page_action/aggregate/index.d.ts +3 -3
  168. package/dist/types/features/page_action/aggregate/index.d.ts.map +1 -1
  169. package/dist/types/features/page_view_event/aggregate/index.d.ts +2 -2
  170. package/dist/types/features/page_view_event/aggregate/index.d.ts.map +1 -1
  171. package/dist/types/features/page_view_timing/aggregate/index.d.ts +2 -2
  172. package/dist/types/features/page_view_timing/aggregate/index.d.ts.map +1 -1
  173. package/dist/types/features/session_trace/aggregate/index.d.ts +2 -2
  174. package/dist/types/features/session_trace/aggregate/index.d.ts.map +1 -1
  175. package/dist/types/features/spa/aggregate/index.d.ts +2 -2
  176. package/dist/types/features/spa/aggregate/index.d.ts.map +1 -1
  177. package/dist/types/features/utils/agent-session.d.ts +2 -0
  178. package/dist/types/features/utils/agent-session.d.ts.map +1 -0
  179. package/dist/types/features/utils/instrument-base.d.ts.map +1 -1
  180. package/dist/types/features/utils/lazy-loader.d.ts +2 -2
  181. package/dist/types/features/utils/lazy-loader.d.ts.map +1 -1
  182. package/dist/types/loaders/api/api.d.ts.map +1 -1
  183. package/dist/types/loaders/api/apiAsync.d.ts.map +1 -1
  184. package/dist/types/loaders/configure/configure.d.ts.map +1 -1
  185. package/package.json +9 -8
  186. package/src/common/config/state/configurable.js +26 -19
  187. package/src/common/config/state/init.js +7 -0
  188. package/src/common/config/state/runtime.js +22 -27
  189. package/src/common/context/shared-context.js +2 -1
  190. package/src/common/event-emitter/contextual-ee.test.js +2 -2
  191. package/src/common/event-emitter/register-handler.test.js +1 -1
  192. package/src/common/event-listener/event-listener-opts.js +4 -4
  193. package/src/common/harvest/harvest-scheduler.js +12 -8
  194. package/src/common/harvest/harvest.js +3 -1
  195. package/src/common/session/constants.js +3 -0
  196. package/src/common/session/session-entity.js +271 -0
  197. package/src/common/session/session-entity.test.js +317 -0
  198. package/src/common/storage/first-party-cookies.js +31 -0
  199. package/src/common/storage/local-memory.js +30 -0
  200. package/src/common/storage/local-memory.test.js +19 -0
  201. package/src/common/storage/local-storage.js +28 -0
  202. package/src/common/storage/local-storage.test.js +17 -0
  203. package/src/common/timer/interaction-timer.js +75 -0
  204. package/src/common/timer/interaction-timer.test.js +167 -0
  205. package/src/common/timer/timer.js +31 -0
  206. package/src/common/timer/timer.test.js +100 -0
  207. package/src/common/unload/eol.js +1 -1
  208. package/src/common/url/canonicalize-url.js +28 -0
  209. package/src/common/url/canonicalize-url.test.js +34 -0
  210. package/src/common/url/clean-url.js +10 -3
  211. package/src/common/util/data-size.js +6 -0
  212. package/src/common/util/data-size.test.js +50 -0
  213. package/src/common/util/global-scope.js +2 -0
  214. package/src/common/util/invoke.js +55 -0
  215. package/src/common/util/invoke.test.js +65 -0
  216. package/src/common/util/obfuscate.js +0 -4
  217. package/src/common/window/page-visibility.js +2 -2
  218. package/src/common/wrap/wrap-fetch.js +1 -2
  219. package/src/common/wrap/wrap-function.js +1 -2
  220. package/src/common/wrap/wrap-timer.js +1 -1
  221. package/src/features/ajax/aggregate/index.js +2 -2
  222. package/src/features/ajax/instrument/index.js +1 -1
  223. package/src/features/jserrors/aggregate/canonical-function-name.js +12 -4
  224. package/src/features/jserrors/aggregate/compute-stack-trace.js +85 -11
  225. package/src/features/jserrors/aggregate/compute-stack-trace.test.js +141 -24
  226. package/src/features/jserrors/aggregate/index.js +24 -50
  227. package/src/features/jserrors/instrument/index.js +0 -1
  228. package/src/features/metrics/aggregate/index.js +18 -3
  229. package/src/features/page_action/aggregate/index.js +2 -2
  230. package/src/features/page_view_event/aggregate/index.js +6 -3
  231. package/src/features/page_view_timing/aggregate/index.js +6 -6
  232. package/src/features/session_trace/aggregate/index.js +3 -4
  233. package/src/features/spa/aggregate/index.js +5 -5
  234. package/src/features/utils/agent-session.js +68 -0
  235. package/src/features/utils/feature-base.js +1 -1
  236. package/src/features/utils/instrument-base.js +5 -2
  237. package/src/features/utils/lazy-loader.js +1 -1
  238. package/src/loaders/agent.js +1 -1
  239. package/src/loaders/api/api.js +2 -5
  240. package/src/loaders/api/apiAsync.js +2 -1
  241. package/src/loaders/configure/configure.js +2 -7
  242. package/dist/cjs/common/util/single.js +0 -23
  243. package/dist/cjs/common/window/session-storage.js +0 -87
  244. package/dist/cjs/features/utils/aggregate-base.js +0 -13
  245. package/dist/esm/common/util/single.js +0 -16
  246. package/dist/esm/common/window/session-storage.js +0 -77
  247. package/dist/esm/features/utils/aggregate-base.js +0 -6
  248. package/dist/types/common/util/single.d.ts +0 -2
  249. package/dist/types/common/util/single.d.ts.map +0 -1
  250. package/dist/types/common/window/session-storage.d.ts +0 -18
  251. package/dist/types/common/window/session-storage.d.ts.map +0 -1
  252. package/dist/types/features/utils/aggregate-base.d.ts +0 -4
  253. package/dist/types/features/utils/aggregate-base.d.ts.map +0 -1
  254. package/src/common/util/single.js +0 -18
  255. package/src/common/window/session-storage.js +0 -75
  256. package/src/features/utils/aggregate-base.js +0 -7
@@ -25,7 +25,7 @@ const TIMER_FNS = [SET_TIMEOUT, 'setImmediate', SET_INTERVAL, CLEAR_TIMEOUT, 'cl
25
25
  * @param {Object} sharedEE - The shared event emitter on which a new scoped event emitter will be based.
26
26
  * @returns {Object} Scoped event emitter with a debug ID of `timer`.
27
27
  */
28
- //eslint-disable-next-line
28
+ // eslint-disable-next-line
29
29
  export function wrapTimer(sharedEE) {
30
30
  const ee = scopedEE(sharedEE);
31
31
 
@@ -9,12 +9,12 @@ import { handle } from '../../../common/event-emitter/handle';
9
9
  import { getConfigurationValue, getInfo } from '../../../common/config/config';
10
10
  import { HarvestScheduler } from '../../../common/harvest/harvest-scheduler';
11
11
  import { setDenyList, shouldCollectEvent } from '../../../common/deny-list/deny-list';
12
- import { AggregateBase } from '../../utils/aggregate-base';
13
12
  import { FEATURE_NAME } from '../constants';
14
13
  import { drain } from '../../../common/drain/drain';
15
14
  import { FEATURE_NAMES } from '../../../loaders/features/features';
16
15
  import { SUPPORTABILITY_METRIC_CHANNEL } from '../../metrics/constants';
17
- export class Aggregate extends AggregateBase {
16
+ import { FeatureBase } from '../../utils/feature-base';
17
+ export class Aggregate extends FeatureBase {
18
18
  static featureName = FEATURE_NAME;
19
19
  constructor(agentIdentifier, aggregator) {
20
20
  super(agentIdentifier, aggregator, FEATURE_NAME);
@@ -91,7 +91,7 @@ function subscribeToEvents(agentIdentifier, ee, handler, dt) {
91
91
  }
92
92
  function onOpenXhrEnd(args, xhr) {
93
93
  var loader_config = getLoaderConfig(agentIdentifier);
94
- if ('xpid' in loader_config && this.sameOrigin) {
94
+ if (loader_config.xpid && this.sameOrigin) {
95
95
  xhr.setRequestHeader('X-NewRelic-ID', loader_config.xpid);
96
96
  }
97
97
  var payload = dt.generateTracePayload(this.parsedOrigin);
@@ -3,10 +3,18 @@
3
3
  * SPDX-License-Identifier: Apache-2.0
4
4
  */
5
5
 
6
- var canonicalFunctionNameRe = /([a-z0-9]+)$/i;
7
- export function canonicalFunctionName(orig) {
8
- if (!orig) return;
9
- var match = orig.match(canonicalFunctionNameRe);
6
+ const canonicalFunctionNameRe = /([a-z0-9]+)$/i;
7
+
8
+ /**
9
+ * Given a function name string, extracts only an alphanumeric segment at the end of the string (if one exists).
10
+ * This is useful for stack traces, where functions might not be named (e.g., anonymous, computed).
11
+ *
12
+ * @param {string} functionNameString - The original function name string.
13
+ * @returns {string|undefined} The canonical function name, or undefined if the input is falsy or no alphanumeric segments are found.
14
+ */
15
+ export function canonicalFunctionName(functionNameString) {
16
+ if (!functionNameString) return;
17
+ const match = functionNameString.match(canonicalFunctionNameRe);
10
18
  if (match) return match[1];
11
19
  return;
12
20
  }
@@ -57,12 +57,31 @@
57
57
  // ex.message = ...
58
58
  // ex.name = ReferenceError
59
59
  import { formatStackTrace } from './format-stack-trace';
60
+ import { canonicalizeUrl } from '../../../common/url/canonicalize-url';
60
61
  var debug = false;
61
62
  var classNameRegex = /function (.+?)\s*\(/;
62
63
  var chrome = /^\s*at (?:((?:\[object object\])?(?:[^(]*\([^)]*\))*[^()]*(?: \[as \S+\])?) )?\(?((?:file|http|https|chrome-extension):.*?)?:(\d+)(?::(\d+))?\)?\s*$/i;
63
64
  var gecko = /^\s*(?:(\S*|global code)(?:\(.*?\))?@)?((?:file|http|https|chrome|safari-extension).*?):(\d+)(?::(\d+))?\s*$/i;
64
65
  var chrome_eval = /^\s*at .+ \(eval at \S+ \((?:(?:file|http|https):[^)]+)?\)(?:, [^:]*:\d+:\d+)?\)$/i;
65
66
  var ie_eval = /^\s*at Function code \(Function code:\d+:\d+\)\s*/i;
67
+
68
+ /**
69
+ * Represents an error with a stack trace.
70
+ * @typedef {Object} StackInfo
71
+ * @property {string} name - The name of the error (e.g. 'TypeError').
72
+ * @property {string} message - The error message.
73
+ * @property {string} stackString - The stack trace as a string.
74
+ * @property {Array<Object>} frames - An array of frames in the stack trace.
75
+ * @property {string} frames.url - The URL of the file containing the code for the frame.
76
+ * @property {string} frames.func - The name of the function associated with the frame.
77
+ * @property {number} frames.line - The line number of the code in the frame.
78
+ */
79
+
80
+ /**
81
+ * Attempts to compute a stack trace for the given exception.
82
+ * @param {Error} ex - The exception for which to compute the stack trace.
83
+ * @returns {StackInfo} A stack trace object containing information about the frames on the stack.
84
+ */
66
85
  export function computeStackTrace(ex) {
67
86
  var stack = null;
68
87
  try {
@@ -103,9 +122,9 @@ export function computeStackTrace(ex) {
103
122
  }
104
123
 
105
124
  /**
106
- * Computes stack trace information from the stack property.
107
- * Chrome and Gecko use this property.
108
- * @param {Error} ex
125
+ * Computes stack trace information from the stack property. Chrome and Gecko use this property.
126
+ *
127
+ * @param {Error} ex - The error object to compute the stack trace for.
109
128
  * @return {?Object.<string, *>} Stack trace information.
110
129
  */
111
130
  function computeStackTraceFromStackProp(ex) {
@@ -126,17 +145,48 @@ function computeStackTraceFromStackProp(ex) {
126
145
  frames: errorInfo.frames
127
146
  };
128
147
  }
148
+
149
+ /**
150
+ * Parses a line from a JavaScript error stack trace and adds it to the given `info` object.
151
+ * Ignores all stack entries thrown from one of our wrapper functions.
152
+ *
153
+ * @param {object} info - The `info` object to add the parsed line to.
154
+ * @param {string} line - The line to parse.
155
+ * @returns {object} The `info` object with the parsed line added.
156
+ */
129
157
  function parseStackProp(info, line) {
130
- var element = getElement(line);
158
+ let element = getStackElement(line);
159
+
160
+ // This catches lines that aren't frames (like the first line stating the error).
131
161
  if (!element) {
132
162
  info.stackLines.push(line);
133
163
  return info;
134
164
  }
135
- if (isWrapper(element.func)) info.wrapperSeen = true;else info.stackLines.push(line);
136
- if (!info.wrapperSeen) info.frames.push(element);
165
+
166
+ // Once we've seen a wrapper, ignore all subsequent stack entries.
167
+ if (isNrWrapper(element.func)) info.wrapperSeen = true;
168
+ if (!info.wrapperSeen) {
169
+ // Query strings and fragments should be removed, and URLs matching the loader's origin should be "<inline>".
170
+ let canonicalUrl = canonicalizeUrl(element.url);
171
+ if (canonicalUrl !== element.url) {
172
+ line = line.replace(element.url, canonicalUrl);
173
+ element.url = canonicalUrl;
174
+ }
175
+ info.stackLines.push(line);
176
+ info.frames.push(element);
177
+ }
137
178
  return info;
138
179
  }
139
- function getElement(line) {
180
+
181
+ /**
182
+ * Parses a line from a JavaScript error stack trace to extract information about a stack trace element, such as the
183
+ * URL, function name, line number, and column number.
184
+ *
185
+ * @param {string} line - A single line from a JavaScript error stack trace.
186
+ * @returns {object} An object containing information about the stack trace element, including the URL, function
187
+ * name, line number, and column number (if available).
188
+ */
189
+ function getStackElement(line) {
140
190
  var parts = line.match(gecko);
141
191
  if (!parts) parts = line.match(chrome);
142
192
  if (parts) {
@@ -153,6 +203,15 @@ function getElement(line) {
153
203
  };
154
204
  }
155
205
  }
206
+
207
+ /**
208
+ * Computes a stack trace object from an error object, by extracting the source and line number from the error object,
209
+ * and using them to create a single stack frame.
210
+ *
211
+ * @param {Error} ex - The error object to compute the stack trace for.
212
+ * @returns {Object|null} - An object representing the computed stack trace, or null if the
213
+ * input error object does not contain a line number.
214
+ */
156
215
  function computeStackTraceBySourceAndLine(ex) {
157
216
  if (!('line' in ex)) return null;
158
217
  var className = ex.name || getClassName(ex);
@@ -169,7 +228,10 @@ function computeStackTraceBySourceAndLine(ex) {
169
228
  }]
170
229
  };
171
230
  }
172
- var stackString = className + ': ' + ex.message + '\n at ' + ex.sourceURL;
231
+
232
+ // Remove any query string and fragment
233
+ var canonicalUrl = canonicalizeUrl(ex.sourceURL);
234
+ var stackString = className + ': ' + ex.message + '\n at ' + canonicalUrl;
173
235
  if (ex.line) {
174
236
  stackString += ':' + ex.line;
175
237
  if (ex.column) {
@@ -182,12 +244,19 @@ function computeStackTraceBySourceAndLine(ex) {
182
244
  message: ex.message,
183
245
  stackString: stackString,
184
246
  frames: [{
185
- url: ex.sourceURL,
247
+ url: canonicalUrl,
186
248
  line: ex.line,
187
249
  column: ex.column
188
250
  }]
189
251
  };
190
252
  }
253
+
254
+ /**
255
+ * For exceptions with no stack and only a message, derives a stack trace by extracting the class name and message.
256
+ *
257
+ * @param {Error} ex - The exception for which to compute the stack trace.
258
+ * @returns {StackTrace} A stack trace object containing the name and message of the exception.
259
+ */
191
260
  function computeStackTraceWithMessageOnly(ex) {
192
261
  var className = ex.name || getClassName(ex);
193
262
  if (!className) return null;
@@ -199,10 +268,24 @@ function computeStackTraceWithMessageOnly(ex) {
199
268
  frames: []
200
269
  };
201
270
  }
271
+
272
+ /**
273
+ * Attempts to extract the name of the constructor function (the class) of the given object.
274
+ *
275
+ * @param {Object} obj - The object for which to extract the constructor function name.
276
+ * @returns {string} The name of the constructor function, or 'unknown' if the name cannot be determined.
277
+ */
202
278
  function getClassName(obj) {
203
279
  var results = classNameRegex.exec(String(obj.constructor));
204
280
  return results && results.length > 1 ? results[1] : 'unknown';
205
281
  }
206
- function isWrapper(functionName) {
282
+
283
+ /**
284
+ * Checks whether the given function name is a New Relic wrapper function.
285
+ *
286
+ * @param {string} functionName - The name of the function to check.
287
+ * @returns {boolean} True if the function name includes the string 'nrWrapper', false otherwise.
288
+ */
289
+ function isNrWrapper(functionName) {
207
290
  return functionName && functionName.indexOf('nrWrapper') >= 0;
208
291
  }
@@ -1,14 +1,27 @@
1
1
  import { faker } from '@faker-js/faker';
2
2
  import { browserErrorUtils } from '../../../../tools/testing-utils';
3
- import { computeStackTrace } from './compute-stack-trace';
3
+ const globalScopeLocation = 'https://example.com/';
4
+ const mockGlobalScopeLocation = url => {
5
+ jest.doMock('../../../common/util/global-scope', () => ({
6
+ initialLocation: url || globalScopeLocation
7
+ }));
8
+ };
9
+ afterEach(() => {
10
+ jest.resetModules();
11
+ jest.clearAllMocks();
12
+ });
4
13
  const baseMockError = {
5
14
  toString: 'RangeError: Invalid array length',
6
15
  name: 'RangeError',
7
16
  constructor: 'function RangeError() { [native code] }',
8
17
  message: 'Invalid array length',
9
- stack: 'RangeError: Invalid array length\n at errorTest (http://bam-test-1.nr-local.net:3334/tests/assets/instrumented.html?loader=spa:74:16)\n at captureError (http://bam-test-1.nr-local.net:3334/tests/assets/instrumented.html?loader=spa:17:9)\n at onload (http://bam-test-1.nr-local.net:3334/tests/assets/instrumented.html?loader=spa:70:5)'
18
+ stack: 'RangeError: Invalid array length\n' + ' at errorTest (' + globalScopeLocation + '?loader=spa#hello:74:16)\n' + ' at captureError (' + globalScopeLocation + 'js/script.js?loader=spa:17:9)\n' + ' at onload (' + globalScopeLocation + 'js/script.js?loader=spa:70:5)'
10
19
  };
11
- test('parsing should return a failure for a null error object', () => {
20
+ test('parsing should return a failure for a null error object', async () => {
21
+ mockGlobalScopeLocation();
22
+ const {
23
+ computeStackTrace
24
+ } = await import('./compute-stack-trace');
12
25
  const result = computeStackTrace(null);
13
26
  expect(result).toEqual(expect.objectContaining({
14
27
  mode: 'failed',
@@ -17,24 +30,45 @@ test('parsing should return a failure for a null error object', () => {
17
30
  }));
18
31
  });
19
32
  describe('errors with stack property', () => {
20
- test('parsed name should be unknown when name and constructor are missing', () => {
33
+ test('should show <inline> for same-page stack string URLs but not sub-paths', async () => {
34
+ const mockError = browserErrorUtils.constructError({
35
+ ...baseMockError
36
+ });
37
+ mockGlobalScopeLocation();
38
+ const {
39
+ computeStackTrace
40
+ } = await import('./compute-stack-trace');
41
+ const result = computeStackTrace(mockError);
42
+ expect(result).toEqual(expect.objectContaining({
43
+ stackString:
44
+ // canonicalized
45
+ 'RangeError: Invalid array length\n' + ' at errorTest (<inline>:74:16)\n' + ' at captureError (' + globalScopeLocation + 'js/script.js:17:9)\n' + ' at onload (' + globalScopeLocation + 'js/script.js:70:5)'
46
+ }));
47
+ });
48
+ test('parsed name should be unknown when name and constructor are missing', async () => {
21
49
  const mockError = browserErrorUtils.constructError({
22
50
  ...baseMockError,
23
51
  name: null,
24
52
  constructor: null
25
53
  });
54
+ mockGlobalScopeLocation();
55
+ const {
56
+ computeStackTrace
57
+ } = await import('./compute-stack-trace');
26
58
  const result = computeStackTrace(mockError);
27
59
  expect(result).toEqual(expect.objectContaining({
28
60
  mode: 'stack',
29
- name: 'unknown',
30
- message: mockError.message,
31
- stackString: mockError.stack
61
+ name: 'unknown'
32
62
  }));
33
63
  });
34
- test('parsed stack should not contain nrWrapper', () => {
64
+ test('parsed stack should not contain nrWrapper', async () => {
35
65
  const alteredError = baseMockError;
36
- alteredError.stack += '\n at nrWrapper (http://bam-test-1.nr-local.net:3334/tests/assets/instrumented.html?loader=spa:60:17)';
66
+ alteredError.stack += '\n at nrWrapper (' + globalScopeLocation + '?loader=spa:60:17)';
37
67
  const mockError = browserErrorUtils.constructError(alteredError);
68
+ mockGlobalScopeLocation();
69
+ const {
70
+ computeStackTrace
71
+ } = await import('./compute-stack-trace');
38
72
  const result = computeStackTrace(mockError);
39
73
  expect(result).toEqual(expect.objectContaining({
40
74
  mode: 'stack',
@@ -46,17 +80,21 @@ describe('errors with stack property', () => {
46
80
  func: 'nrWrapper'
47
81
  }));
48
82
  });
49
- test('stack should still parse when column numbers are missing', () => {
83
+ test('stack should still parse when column numbers are missing', async () => {
50
84
  const mockError = browserErrorUtils.constructError({
51
85
  ...baseMockError,
52
- stack: 'Error: Blocked a frame with origin "http://bam-test-1.nr-local.net:3334" from accessing a cross-origin frame.\n at errorTest (http://bam-test-1.nr-local.net:3334/tests/assets/instrumented.html?loader=spa:60)\n at captureError (http://bam-test-1.nr-local.net:3334/tests/assets/instrumented.html?loader=spa:17)\n at onload (http://bam-test-1.nr-local.net:3334/tests/assets/instrumented.html?loader=spa:57)'
86
+ stack: 'RangeError: Invalid array length\n' + 'Error: Blocked a frame with origin "http://bam-test-1.nr-local.net:3334" from accessing a cross-origin frame.\n' + ' at errorTest (' + globalScopeLocation + '?loader=spa:60)\n' + ' at captureError (' + globalScopeLocation + '?loader=spa:17)\n' + ' at onload (' + globalScopeLocation + '?loader=spa:57)'
53
87
  });
88
+ mockGlobalScopeLocation();
89
+ const {
90
+ computeStackTrace
91
+ } = await import('./compute-stack-trace');
54
92
  const result = computeStackTrace(mockError);
55
93
  expect(result).toEqual(expect.objectContaining({
56
94
  mode: 'stack',
57
95
  name: mockError.name,
58
96
  message: mockError.message,
59
- stackString: mockError.stack
97
+ stackString: 'RangeError: Invalid array length\n' + 'Error: Blocked a frame with origin "http://bam-test-1.nr-local.net:3334" from accessing a cross-origin frame.\n' + ' at errorTest (<inline>:60)\n' + ' at captureError (<inline>:17)\n' + ' at onload (<inline>:57)'
60
98
  }));
61
99
  expect(result.frames.length).toEqual(3);
62
100
  expect(result.frames).toContainEqual(expect.objectContaining({
@@ -72,11 +110,15 @@ describe('errors with stack property', () => {
72
110
  column: null
73
111
  }));
74
112
  });
75
- test('parser can handle chrome eval stack', () => {
113
+ test('parser can handle chrome eval stack', async () => {
76
114
  const mockError = browserErrorUtils.constructError({
77
115
  ...baseMockError,
78
- stack: ' at foobar (eval at foobar (http://bam-test-1.nr-local.net:3334/tests/assets/instrumented.html))'
116
+ stack: ' at foobar (eval at foobar (' + globalScopeLocation + '))'
79
117
  });
118
+ mockGlobalScopeLocation();
119
+ const {
120
+ computeStackTrace
121
+ } = await import('./compute-stack-trace');
80
122
  const result = computeStackTrace(mockError);
81
123
  expect(result).toEqual(expect.objectContaining({
82
124
  mode: 'stack',
@@ -89,7 +131,7 @@ describe('errors with stack property', () => {
89
131
  func: 'evaluated code'
90
132
  }));
91
133
  });
92
- test('parser can handle ie eval stack', () => {
134
+ test('parser can handle ie eval stack', async () => {
93
135
  const mockError = browserErrorUtils.constructError({
94
136
  toString: 'TypeError: Permission denied',
95
137
  name: 'TypeError',
@@ -97,6 +139,10 @@ describe('errors with stack property', () => {
97
139
  message: 'Permission denied',
98
140
  stack: ' at Function code (Function code:23:23)'
99
141
  });
142
+ mockGlobalScopeLocation();
143
+ const {
144
+ computeStackTrace
145
+ } = await import('./compute-stack-trace');
100
146
  const result = computeStackTrace(mockError);
101
147
  expect(result).toEqual(expect.objectContaining({
102
148
  mode: 'stack',
@@ -109,11 +155,15 @@ describe('errors with stack property', () => {
109
155
  func: 'evaluated code'
110
156
  }));
111
157
  });
112
- test('parser can handle stack with anonymous function', () => {
158
+ test('parser can handle stack with anonymous function', async () => {
113
159
  const mockError = browserErrorUtils.constructError({
114
160
  ...baseMockError,
115
161
  stack: 'anonymous'
116
162
  });
163
+ mockGlobalScopeLocation();
164
+ const {
165
+ computeStackTrace
166
+ } = await import('./compute-stack-trace');
117
167
  const result = computeStackTrace(mockError);
118
168
  expect(result).toEqual(expect.objectContaining({
119
169
  mode: 'stack',
@@ -131,7 +181,7 @@ describe('errors without stack property and with line property', () => {
131
181
  /**
132
182
  * @deprecated sourceURL is no longer present in errors for any browsers we support
133
183
  */
134
- test('parsed stack should contain sourceURL and line number', () => {
184
+ test('parsed stack should contain sourceURL and line number', async () => {
135
185
  const sourceURL = faker.internet.url();
136
186
  const mockError = browserErrorUtils.constructError({
137
187
  ...baseMockError,
@@ -139,6 +189,10 @@ describe('errors without stack property and with line property', () => {
139
189
  line: 100,
140
190
  sourceURL
141
191
  });
192
+ mockGlobalScopeLocation();
193
+ const {
194
+ computeStackTrace
195
+ } = await import('./compute-stack-trace');
142
196
  const result = computeStackTrace(mockError);
143
197
  expect(result).toEqual(expect.objectContaining({
144
198
  mode: 'sourceline',
@@ -156,7 +210,7 @@ describe('errors without stack property and with line property', () => {
156
210
  /**
157
211
  * @deprecated sourceURL is no longer present in errors for any browsers we support
158
212
  */
159
- test('parsed stack should contain sourceURL, line number, and column number', () => {
213
+ test('parsed stack should contain sourceURL, line number, and column number', async () => {
160
214
  const sourceURL = faker.internet.url();
161
215
  const mockError = browserErrorUtils.constructError({
162
216
  ...baseMockError,
@@ -165,6 +219,10 @@ describe('errors without stack property and with line property', () => {
165
219
  stack: undefined,
166
220
  sourceURL
167
221
  });
222
+ mockGlobalScopeLocation();
223
+ const {
224
+ computeStackTrace
225
+ } = await import('./compute-stack-trace');
168
226
  const result = computeStackTrace(mockError);
169
227
  expect(result).toEqual(expect.objectContaining({
170
228
  mode: 'sourceline',
@@ -179,13 +237,17 @@ describe('errors without stack property and with line property', () => {
179
237
  column: mockError.column
180
238
  }));
181
239
  });
182
- test('parsed stack should contain "evaluated code" if sourceURL property is not present', () => {
240
+ test('parsed stack should contain "evaluated code" if sourceURL property is not present', async () => {
183
241
  const mockError = browserErrorUtils.constructError({
184
242
  ...baseMockError,
185
243
  line: 100,
186
244
  column: 200,
187
245
  stack: undefined
188
246
  });
247
+ mockGlobalScopeLocation();
248
+ const {
249
+ computeStackTrace
250
+ } = await import('./compute-stack-trace');
189
251
  const result = computeStackTrace(mockError);
190
252
  expect(result).toEqual(expect.objectContaining({
191
253
  mode: 'sourceline',
@@ -199,6 +261,58 @@ describe('errors without stack property and with line property', () => {
199
261
  }));
200
262
  });
201
263
 
264
+ /**
265
+ * @deprecated sourceURL is no longer present in errors for any browsers we support
266
+ */
267
+ test('should show <inline> for same-page URLs', async () => {
268
+ const pageLocation = faker.internet.url();
269
+ const sourceURL = pageLocation + '?abc=123';
270
+ const mockError = browserErrorUtils.constructError({
271
+ ...baseMockError,
272
+ line: 100,
273
+ column: 200,
274
+ stack: undefined,
275
+ sourceURL: sourceURL
276
+ });
277
+ mockGlobalScopeLocation(pageLocation);
278
+ const {
279
+ computeStackTrace
280
+ } = await import('./compute-stack-trace');
281
+ const result = computeStackTrace(mockError);
282
+ expect(result).toEqual(expect.objectContaining({
283
+ stackString: "".concat(mockError.name, ": ").concat(mockError.message, "\n at <inline>:").concat(mockError.line, ":").concat(mockError.column)
284
+ }));
285
+ expect(result.frames).toContainEqual(expect.objectContaining({
286
+ url: '<inline>'
287
+ }));
288
+ });
289
+
290
+ /**
291
+ * @deprecated sourceURL is no longer present in errors for any browsers we support
292
+ */
293
+ test('should NOT show <inline> for same-domain URLs with a sub-path', async () => {
294
+ const pageLocation = faker.internet.url();
295
+ const sourceURL = pageLocation + '/path/to/script.js';
296
+ const mockError = browserErrorUtils.constructError({
297
+ ...baseMockError,
298
+ line: 100,
299
+ column: 200,
300
+ stack: undefined,
301
+ sourceURL
302
+ });
303
+ mockGlobalScopeLocation(pageLocation);
304
+ const {
305
+ computeStackTrace
306
+ } = await import('./compute-stack-trace');
307
+ const result = computeStackTrace(mockError);
308
+ expect(result).toEqual(expect.objectContaining({
309
+ stackString: "".concat(mockError.name, ": ").concat(mockError.message, "\n at ").concat(sourceURL, ":").concat(mockError.line, ":").concat(mockError.column)
310
+ }));
311
+ expect(result.frames).toContainEqual(expect.objectContaining({
312
+ url: sourceURL
313
+ }));
314
+ });
315
+
202
316
  // TODO: computeStackTraceBySourceAndLine does not respect firefox lineNumber and columnNumber properties when stack is empty
203
317
  });
204
318
 
@@ -207,11 +321,15 @@ describe('errors without stack property and with line property', () => {
207
321
  * error, including primitives.
208
322
  */
209
323
  describe('errors that are messages only or primitives', () => {
210
- test('parser should get error name from constructor', () => {
324
+ test('parser should get error name from constructor', async () => {
211
325
  const mockError = browserErrorUtils.constructError({
212
326
  toString: '0',
213
327
  constructor: 'function Number() { [native code] }'
214
328
  });
329
+ mockGlobalScopeLocation();
330
+ const {
331
+ computeStackTrace
332
+ } = await import('./compute-stack-trace');
215
333
  const result = computeStackTrace(mockError);
216
334
  expect(result).toEqual(expect.objectContaining({
217
335
  mode: 'nameonly',
@@ -220,12 +338,16 @@ describe('errors that are messages only or primitives', () => {
220
338
  frames: []
221
339
  }));
222
340
  });
223
- test('parser should get error name from name property', () => {
341
+ test('parser should get error name from name property', async () => {
224
342
  const mockError = browserErrorUtils.constructError({
225
343
  toString: '0',
226
344
  name: faker.datatype.uuid(),
227
345
  constructor: 'function Number() { [native code] }'
228
346
  });
347
+ mockGlobalScopeLocation();
348
+ const {
349
+ computeStackTrace
350
+ } = await import('./compute-stack-trace');
229
351
  const result = computeStackTrace(mockError);
230
352
  expect(result).toEqual(expect.objectContaining({
231
353
  mode: 'nameonly',
@@ -234,13 +356,17 @@ describe('errors that are messages only or primitives', () => {
234
356
  frames: []
235
357
  }));
236
358
  });
237
- test('parser should include the message property', () => {
359
+ test('parser should include the message property', async () => {
238
360
  const mockError = browserErrorUtils.constructError({
239
361
  toString: '0',
240
362
  name: faker.datatype.uuid(),
241
363
  message: faker.datatype.uuid(),
242
364
  constructor: 'function Number() { [native code] }'
243
365
  });
366
+ mockGlobalScopeLocation();
367
+ const {
368
+ computeStackTrace
369
+ } = await import('./compute-stack-trace');
244
370
  const result = computeStackTrace(mockError);
245
371
  expect(result).toEqual(expect.objectContaining({
246
372
  mode: 'nameonly',
@@ -250,6 +376,4 @@ describe('errors that are messages only or primitives', () => {
250
376
  frames: []
251
377
  }));
252
378
  });
253
- });
254
-
255
- // describe('')
379
+ });