@newrelic/browser-agent 1.258.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 (58) hide show
  1. package/CHANGELOG.md +11 -0
  2. package/dist/cjs/common/constants/env.cdn.js +1 -1
  3. package/dist/cjs/common/constants/env.npm.js +1 -1
  4. package/dist/cjs/common/deny-list/deny-list.js +6 -8
  5. package/dist/cjs/common/vitals/time-to-first-byte.js +9 -1
  6. package/dist/cjs/features/ajax/instrument/index.js +11 -8
  7. package/dist/cjs/features/jserrors/aggregate/index.js +6 -2
  8. package/dist/cjs/features/jserrors/instrument/index.js +4 -87
  9. package/dist/cjs/features/jserrors/shared/cast-error.js +66 -0
  10. package/dist/cjs/features/jserrors/{instrument → shared}/uncaught-error.js +4 -2
  11. package/dist/cjs/features/session_trace/aggregate/index.js +6 -2
  12. package/dist/cjs/features/spa/aggregate/index.js +11 -1
  13. package/dist/cjs/features/spa/instrument/index.js +9 -0
  14. package/dist/cjs/features/utils/instrument-base.js +1 -1
  15. package/dist/cjs/loaders/api/api.js +6 -14
  16. package/dist/esm/common/constants/env.cdn.js +1 -1
  17. package/dist/esm/common/constants/env.npm.js +1 -1
  18. package/dist/esm/common/deny-list/deny-list.js +5 -8
  19. package/dist/esm/common/vitals/time-to-first-byte.js +9 -1
  20. package/dist/esm/features/ajax/instrument/index.js +11 -8
  21. package/dist/esm/features/jserrors/aggregate/index.js +6 -2
  22. package/dist/esm/features/jserrors/instrument/index.js +4 -87
  23. package/dist/esm/features/jserrors/shared/cast-error.js +59 -0
  24. package/dist/esm/features/jserrors/{instrument → shared}/uncaught-error.js +5 -2
  25. package/dist/esm/features/session_trace/aggregate/index.js +6 -2
  26. package/dist/esm/features/spa/aggregate/index.js +11 -1
  27. package/dist/esm/features/spa/instrument/index.js +9 -0
  28. package/dist/esm/features/utils/instrument-base.js +1 -1
  29. package/dist/esm/loaders/api/api.js +6 -14
  30. package/dist/types/common/deny-list/deny-list.d.ts +1 -0
  31. package/dist/types/common/deny-list/deny-list.d.ts.map +1 -1
  32. package/dist/types/features/ajax/instrument/index.d.ts.map +1 -1
  33. package/dist/types/features/jserrors/aggregate/index.d.ts.map +1 -1
  34. package/dist/types/features/jserrors/instrument/index.d.ts.map +1 -1
  35. package/dist/types/features/jserrors/shared/cast-error.d.ts +21 -0
  36. package/dist/types/features/jserrors/shared/cast-error.d.ts.map +1 -0
  37. package/dist/types/features/jserrors/{instrument → shared}/uncaught-error.d.ts +3 -2
  38. package/dist/types/features/jserrors/shared/uncaught-error.d.ts.map +1 -0
  39. package/dist/types/features/session_trace/aggregate/index.d.ts +1 -0
  40. package/dist/types/features/session_trace/aggregate/index.d.ts.map +1 -1
  41. package/dist/types/features/spa/aggregate/index.d.ts.map +1 -1
  42. package/dist/types/features/spa/instrument/index.d.ts.map +1 -1
  43. package/dist/types/loaders/api/api.d.ts +1 -1
  44. package/dist/types/loaders/api/api.d.ts.map +1 -1
  45. package/package.json +1 -1
  46. package/src/common/deny-list/deny-list.js +6 -7
  47. package/src/common/vitals/time-to-first-byte.js +8 -1
  48. package/src/features/ajax/instrument/index.js +11 -9
  49. package/src/features/jserrors/aggregate/index.js +7 -2
  50. package/src/features/jserrors/instrument/index.js +4 -99
  51. package/src/features/jserrors/shared/cast-error.js +69 -0
  52. package/src/features/jserrors/{instrument → shared}/uncaught-error.js +5 -2
  53. package/src/features/session_trace/aggregate/index.js +6 -2
  54. package/src/features/spa/aggregate/index.js +10 -1
  55. package/src/features/spa/instrument/index.js +3 -0
  56. package/src/features/utils/instrument-base.js +1 -1
  57. package/src/loaders/api/api.js +6 -15
  58. package/dist/types/features/jserrors/instrument/uncaught-error.d.ts.map +0 -1
@@ -17,6 +17,7 @@ import { FEATURE_NAME } from '../constants'
17
17
  import { FEATURE_NAMES } from '../../../loaders/features/features'
18
18
  import { SUPPORTABILITY_METRIC } from '../../metrics/constants'
19
19
  import { now } from '../../../common/timing/now'
20
+ import { hasUndefinedHostname } from '../../../common/deny-list/deny-list'
20
21
 
21
22
  var handlers = ['load', 'error', 'abort', 'timeout']
22
23
  var handlersLen = handlers.length
@@ -338,18 +339,18 @@ function subscribeToEvents (agentIdentifier, ee, handler, dt) {
338
339
  // eslint-disable-next-line handle-callback-err
339
340
  function onFetchDone (_, res) {
340
341
  this.endTime = now()
341
- if (!this.params) {
342
- this.params = {}
343
- }
342
+ if (!this.params) this.params = {}
343
+ if (hasUndefinedHostname(this.params)) return // don't bother with fetch to url with no hostname
344
+
344
345
  this.params.status = res ? res.status : 0
345
346
 
346
347
  // convert rxSize to a number
347
- var responseSize
348
+ let responseSize
348
349
  if (typeof this.rxSize === 'string' && this.rxSize.length > 0) {
349
350
  responseSize = +this.rxSize
350
351
  }
351
352
 
352
- var metrics = {
353
+ const metrics = {
353
354
  txSize: this.txSize,
354
355
  rxSize: responseSize,
355
356
  duration: now() - this.startTime
@@ -360,17 +361,18 @@ function subscribeToEvents (agentIdentifier, ee, handler, dt) {
360
361
 
361
362
  // Create report for XHR request that has finished
362
363
  function end (xhr) {
363
- var params = this.params
364
- var metrics = this.metrics
365
-
364
+ const params = this.params
365
+ const metrics = this.metrics
366
366
  if (this.ended) return
367
367
  this.ended = true
368
368
 
369
- for (var i = 0; i < handlersLen; i++) {
369
+ for (let i = 0; i < handlersLen; i++) {
370
370
  xhr.removeEventListener(handlers[i], this.listener, false)
371
371
  }
372
372
 
373
373
  if (params.aborted) return
374
+ if (hasUndefinedHostname(params)) return // don't bother with XHR of url with no hostname
375
+
374
376
  metrics.duration = now() - this.startTime
375
377
  if (!this.loadCaptureCalled && xhr.readyState === 4) {
376
378
  captureXhrData(this, xhr)
@@ -202,15 +202,20 @@ export class Aggregate extends AggregateBase {
202
202
 
203
203
  // Trace sends the error in its payload, and both trace & replay simply listens for any error to occur.
204
204
  const jsErrorEvent = [type, bucketHash, params, newMetrics, customAttributes]
205
- handle('errorAgg', jsErrorEvent, undefined, FEATURE_NAMES.sessionTrace, this.ee)
205
+ handle('trace-jserror', jsErrorEvent, undefined, FEATURE_NAMES.sessionTrace, this.ee)
206
206
  // still send EE events for other features such as above, but stop this one from aggregating internal data
207
207
  if (this.blocked) return
208
208
 
209
+ if (err.__newrelic?.[this.agentIdentifier]) {
210
+ params._interactionId = err.__newrelic[this.agentIdentifier].interactionId
211
+ params._interactionNodeId = err.__newrelic[this.agentIdentifier].interactionNodeId
212
+ }
213
+
209
214
  const softNavInUse = Boolean(getNREUMInitializedAgent(this.agentIdentifier)?.features[FEATURE_NAMES.softNav])
210
215
  // Note: the following are subject to potential race cond wherein if the other feature aren't fully initialized, it'll be treated as there being no associated interaction.
211
216
  // They each will also tack on their respective properties to the params object as part of the decision flow.
212
217
  if (softNavInUse) handle('jserror', [params, time], undefined, FEATURE_NAMES.softNav, this.ee)
213
- else handle('errorAgg', jsErrorEvent, undefined, FEATURE_NAMES.spa, this.ee)
218
+ else handle('spa-jserror', jsErrorEvent, undefined, FEATURE_NAMES.spa, this.ee)
214
219
 
215
220
  if (params.browserInteractionId && !params._softNavFinished) { // hold onto the error until the in-progress interaction is done, eithered saved or discarded
216
221
  this.bufferedErrorsUnderSpa[params.browserInteractionId] ??= []
@@ -9,15 +9,13 @@ 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
 
17
16
  export class Instrument extends InstrumentBase {
18
17
  static featureName = FEATURE_NAME
19
18
 
20
- #seenErrors = new Set()
21
19
  #replayRunning = false
22
20
 
23
21
  constructor (agentIdentifier, aggregator, auto = true) {
@@ -28,17 +26,9 @@ export class Instrument extends InstrumentBase {
28
26
  this.removeOnAbort = new AbortController()
29
27
  } catch (e) {}
30
28
 
31
- // Capture function errors early in case the spa feature is loaded
32
- this.ee.on('fn-err', (args, obj, error) => {
33
- if (!this.abortHandler || this.#seenErrors.has(error)) return
34
- this.#seenErrors.add(error)
35
-
36
- handle('err', [this.#castError(error), now()], undefined, FEATURE_NAMES.jserrors, this.ee)
37
- })
38
-
39
29
  this.ee.on('internal-error', (error) => {
40
30
  if (!this.abortHandler) return
41
- handle('ierr', [this.#castError(error), now(), true, {}, this.#replayRunning], undefined, FEATURE_NAMES.jserrors, this.ee)
31
+ handle('ierr', [castError(error), now(), true, {}, this.#replayRunning], undefined, FEATURE_NAMES.jserrors, this.ee)
42
32
  })
43
33
 
44
34
  this.ee.on(SR_EVENT_EMITTER_TYPES.REPLAY_RUNNING, (isRunning) => {
@@ -47,22 +37,12 @@ export class Instrument extends InstrumentBase {
47
37
 
48
38
  globalScope.addEventListener('unhandledrejection', (promiseRejectionEvent) => {
49
39
  if (!this.abortHandler) return
50
- handle('err', [this.#castPromiseRejectionEvent(promiseRejectionEvent), now(), false, { unhandledPromiseRejection: 1 }, this.#replayRunning], undefined, FEATURE_NAMES.jserrors, this.ee)
40
+ handle('err', [castPromiseRejectionEvent(promiseRejectionEvent), now(), false, { unhandledPromiseRejection: 1 }, this.#replayRunning], undefined, FEATURE_NAMES.jserrors, this.ee)
51
41
  }, eventListenerOpts(false, this.removeOnAbort?.signal))
52
42
 
53
43
  globalScope.addEventListener('error', (errorEvent) => {
54
44
  if (!this.abortHandler) return
55
-
56
- /**
57
- * If the spa feature is loaded, errors may already have been captured in the `fn-err` listener above.
58
- * This ensures those errors are not captured twice.
59
- */
60
- if (this.#seenErrors.has(errorEvent.error)) {
61
- this.#seenErrors.delete(errorEvent.error)
62
- return
63
- }
64
-
65
- handle('err', [this.#castErrorEvent(errorEvent), now(), false, {}, this.#replayRunning], undefined, FEATURE_NAMES.jserrors, this.ee)
45
+ handle('err', [castErrorEvent(errorEvent), now(), false, {}, this.#replayRunning], undefined, FEATURE_NAMES.jserrors, this.ee)
66
46
  }, eventListenerOpts(false, this.removeOnAbort?.signal))
67
47
 
68
48
  this.abortHandler = this.#abort // we also use this as a flag to denote that the feature is active or on and handling errors
@@ -72,81 +52,6 @@ export class Instrument extends InstrumentBase {
72
52
  /** Restoration and resource release tasks to be done if JS error loader is being aborted. Unwind changes to globals. */
73
53
  #abort () {
74
54
  this.removeOnAbort?.abort()
75
- this.#seenErrors.clear()
76
55
  this.abortHandler = undefined // weakly allow this abort op to run only once
77
56
  }
78
-
79
- /**
80
- * Any value can be used with the `throw` keyword. This function ensures that the value is
81
- * either a proper Error instance or attempts to convert it to an UncaughtError instance.
82
- * @param {any} error The value thrown
83
- * @returns {Error|UncaughtError} The converted error instance
84
- */
85
- #castError (error) {
86
- if (error instanceof Error) {
87
- return error
88
- }
89
-
90
- /**
91
- * The thrown value may contain a message property. If it does, try to treat the thrown
92
- * value as an Error-like object.
93
- */
94
- if (typeof error?.message !== 'undefined') {
95
- return new UncaughtError(
96
- error.message,
97
- error.filename || error.sourceURL,
98
- error.lineno || error.line,
99
- error.colno || error.col
100
- )
101
- }
102
-
103
- return new UncaughtError(typeof error === 'string' ? error : stringify(error))
104
- }
105
-
106
- /**
107
- * Attempts to convert a PromiseRejectionEvent object to an Error object
108
- * @param {PromiseRejectionEvent} unhandledRejectionEvent The unhandled promise rejection event
109
- * @returns {Error} An Error object with the message as the casted reason
110
- */
111
- #castPromiseRejectionEvent (promiseRejectionEvent) {
112
- let prefix = 'Unhandled Promise Rejection: '
113
-
114
- if (promiseRejectionEvent?.reason instanceof Error) {
115
- try {
116
- promiseRejectionEvent.reason.message = prefix + promiseRejectionEvent.reason.message
117
- return promiseRejectionEvent.reason
118
- } catch (e) {
119
- return promiseRejectionEvent.reason
120
- }
121
- }
122
-
123
- if (typeof promiseRejectionEvent.reason === 'undefined') return new UncaughtError(prefix)
124
-
125
- const error = this.#castError(promiseRejectionEvent.reason)
126
- error.message = prefix + error.message
127
- return error
128
- }
129
-
130
- /**
131
- * Attempts to convert an ErrorEvent object to an Error object
132
- * @param {ErrorEvent} errorEvent The error event
133
- * @returns {Error|UncaughtError} The error event converted to an Error object
134
- */
135
- #castErrorEvent (errorEvent) {
136
- if (errorEvent.error instanceof SyntaxError && !/:\d+$/.test(errorEvent.error.stack?.trim())) {
137
- const error = new UncaughtError(errorEvent.message, errorEvent.filename, errorEvent.lineno, errorEvent.colno)
138
- error.name = SyntaxError.name
139
- return error
140
- }
141
-
142
- if (errorEvent.error instanceof Error) {
143
- return errorEvent.error
144
- }
145
-
146
- /**
147
- * Older browsers do not contain the `error` property on the ErrorEvent instance.
148
- * https://caniuse.com/mdn-api_errorevent_error
149
- */
150
- return new UncaughtError(errorEvent.message, errorEvent.filename, errorEvent.lineno, errorEvent.colno)
151
- }
152
57
  }
@@ -0,0 +1,69 @@
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(
20
+ error?.message !== undefined ? error.message : error,
21
+ error?.filename || error?.sourceURL,
22
+ error?.lineno || error?.line,
23
+ error?.colno || error?.col,
24
+ error?.__newrelic
25
+ )
26
+ }
27
+
28
+ /**
29
+ * Attempts to convert a PromiseRejectionEvent object to an Error object
30
+ * @param {PromiseRejectionEvent} unhandledRejectionEvent The unhandled promise rejection event
31
+ * @returns {Error} An Error object with the message as the casted reason
32
+ */
33
+ export function castPromiseRejectionEvent (promiseRejectionEvent) {
34
+ let prefix = 'Unhandled Promise Rejection'
35
+
36
+ if (canTrustError(promiseRejectionEvent?.reason)) {
37
+ try {
38
+ promiseRejectionEvent.reason.message = prefix + ': ' + promiseRejectionEvent.reason.message
39
+ return castError(promiseRejectionEvent.reason)
40
+ } catch (e) {
41
+ return castError(promiseRejectionEvent.reason)
42
+ }
43
+ }
44
+
45
+ if (typeof promiseRejectionEvent.reason === 'undefined') return castError(prefix)
46
+
47
+ const error = castError(promiseRejectionEvent.reason)
48
+ error.message = prefix + ': ' + error?.message
49
+ return error
50
+ }
51
+
52
+ /**
53
+ * Attempts to convert an ErrorEvent object to an Error object
54
+ * @param {ErrorEvent} errorEvent The error event
55
+ * @returns {Error|UncaughtError} The error event converted to an Error object
56
+ */
57
+ export function castErrorEvent (errorEvent) {
58
+ if (errorEvent.error instanceof SyntaxError && !/:\d+$/.test(errorEvent.error.stack?.trim())) {
59
+ const error = new UncaughtError(errorEvent.message, errorEvent.filename, errorEvent.lineno, errorEvent.colno, errorEvent.error.__newrelic)
60
+ error.name = SyntaxError.name
61
+ return error
62
+ }
63
+ if (canTrustError(errorEvent.error)) return errorEvent.error
64
+ return castError(errorEvent)
65
+ }
66
+
67
+ function canTrustError (error) {
68
+ return error instanceof Error && !!error.stack
69
+ }
@@ -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
  }
@@ -44,6 +44,7 @@ export class Aggregate extends AggregateBase {
44
44
  this.trace = {}
45
45
  this.nodeCount = 0
46
46
  this.sentTrace = null
47
+ this.prevStoredEvents = new Set()
47
48
  this.harvestTimeSeconds = getConfigurationValue(agentIdentifier, 'session_trace.harvestTimeSeconds') || 10
48
49
  this.maxNodesPerHarvest = getConfigurationValue(agentIdentifier, 'session_trace.maxNodesPerHarvest') || 1000
49
50
  /**
@@ -108,7 +109,7 @@ export class Aggregate extends AggregateBase {
108
109
  this.isStandalone = true
109
110
  this.waitForFlags((['stn'])).then(([on]) => controlTraceOp(on), this.featureName, this.ee)
110
111
  } else {
111
- registerHandler('errorAgg', () => {
112
+ registerHandler('trace-jserror', () => {
112
113
  seenAnError = true
113
114
  switchToFull()
114
115
  }, this.featureName, this.ee)
@@ -169,7 +170,7 @@ export class Aggregate extends AggregateBase {
169
170
  registerHandler('bstHist', (...args) => operationalGate.settle(() => this.storeHist(...args)), this.featureName, this.ee)
170
171
  registerHandler('bstXhrAgg', (...args) => operationalGate.settle(() => this.storeXhrAgg(...args)), this.featureName, this.ee)
171
172
  registerHandler('bstApi', (...args) => operationalGate.settle(() => this.storeSTN(...args)), this.featureName, this.ee)
172
- registerHandler('errorAgg', (...args) => operationalGate.settle(() => this.storeErrorAgg(...args)), this.featureName, this.ee)
173
+ registerHandler('trace-jserror', (...args) => operationalGate.settle(() => this.storeErrorAgg(...args)), this.featureName, this.ee)
173
174
  registerHandler('pvtAdded', (...args) => operationalGate.settle(() => this.processPVT(...args)), this.featureName, this.ee)
174
175
  this.drain()
175
176
  }
@@ -208,6 +209,7 @@ export class Aggregate extends AggregateBase {
208
209
  }
209
210
 
210
211
  #prepareHarvest (options) {
212
+ this.prevStoredEvents.clear() // release references to past events for GC
211
213
  if (this.isStandalone) {
212
214
  if (this.ptid && now() >= MAX_TRACE_DURATION) {
213
215
  // Perform a final harvest once we hit or exceed the max session trace time
@@ -272,6 +274,8 @@ export class Aggregate extends AggregateBase {
272
274
  // Tracks the events and their listener's duration on objects wrapped by wrap-events.
273
275
  storeEvent (currentEvent, target, start, end) {
274
276
  if (this.shouldIgnoreEvent(currentEvent, target)) return
277
+ 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.
278
+ this.prevStoredEvents.add(currentEvent)
275
279
 
276
280
  const evt = {
277
281
  n: this.evtName(currentEvent.type),
@@ -698,7 +698,7 @@ export class Aggregate extends AggregateBase {
698
698
  }
699
699
  }
700
700
 
701
- baseEE.on('errorAgg', function (type, name, params, metrics) {
701
+ baseEE.on('spa-jserror', function (type, name, params, metrics) {
702
702
  if (!state.currentNode) return
703
703
  params._interactionId = state.currentNode.interaction.id
704
704
  // do not capture parentNodeId when in root node
@@ -707,6 +707,15 @@ export class Aggregate extends AggregateBase {
707
707
  }
708
708
  })
709
709
 
710
+ register('function-err', function (args, obj, error) {
711
+ if (!state.currentNode) return
712
+ error.__newrelic ??= {}
713
+ error.__newrelic[agentIdentifier] = { interactionId: state.currentNode.interaction.id }
714
+ if (state.currentNode.type && state.currentNode.type !== 'interaction') {
715
+ error.__newrelic[agentIdentifier].interactionNodeId = state.currentNode.id
716
+ }
717
+ }, this.featureName, baseEE)
718
+
710
719
  baseEE.on('interaction', saveInteraction)
711
720
 
712
721
  function getActionText (node) {
@@ -10,6 +10,7 @@ import { InstrumentBase } from '../../utils/instrument-base'
10
10
  import * as CONSTANTS from '../constants'
11
11
  import { isBrowserScope } from '../../../common/constants/runtime'
12
12
  import { now } from '../../../common/timing/now'
13
+ import { handle } from '../../../common/event-emitter/handle'
13
14
 
14
15
  const {
15
16
  FEATURE_NAME, START, END, BODY, CB_END, JS_TIME, FETCH, FN_START, CB_START, FN_END
@@ -46,6 +47,8 @@ export class Instrument extends InstrumentBase {
46
47
  promiseEE.on(CB_END, endTimestamp)
47
48
  jsonpEE.on(CB_END, endTimestamp)
48
49
 
50
+ this.ee.on('fn-err', (...args) => { if (!args[2]?.__newrelic?.[agentIdentifier]) handle('function-err', [...args], undefined, this.featureName, this.ee) })
51
+
49
52
  this.ee.buffer([FN_START, FN_END, 'xhr-resolved'], this.featureName)
50
53
  eventsEE.buffer([FN_START], this.featureName)
51
54
  timerEE.buffer(['setTimeout' + END, 'clearTimeout' + START, FN_START], this.featureName)
@@ -52,7 +52,7 @@ export class InstrumentBase extends FeatureBase {
52
52
  /** if the feature requires opt-in (!auto-start), it will get registered once the api has been called */
53
53
  if (this.auto) registerDrain(agentIdentifier, featureName)
54
54
  else {
55
- this.ee.on(`${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)
@@ -117,20 +117,10 @@ export function setAPI (agentIdentifier, forceDrain, runSoftNavOverSpa = false)
117
117
  return appendJsAttribute('application.version', value, 'setApplicationVersion', false)
118
118
  }
119
119
 
120
- apiInterface.start = (features) => {
120
+ apiInterface.start = () => {
121
121
  try {
122
- const smTag = !features ? 'undefined' : 'defined'
123
- handle(SUPPORTABILITY_METRIC_CHANNEL, [`API/start/${smTag}/called`], undefined, FEATURE_NAMES.metrics, instanceEE)
124
- const featNames = Object.values(FEATURE_NAMES)
125
- if (features === undefined) features = featNames
126
- else {
127
- features = Array.isArray(features) && features.length ? features : [features]
128
- if (features.some(f => !featNames.includes(f))) return warn(`Invalid feature name supplied. Acceptable feature names are: ${featNames}`)
129
- if (!features.includes(FEATURE_NAMES.pageViewEvent)) features.push(FEATURE_NAMES.pageViewEvent)
130
- }
131
- features.forEach(feature => {
132
- instanceEE.emit(`${feature}-opt-in`)
133
- })
122
+ handle(SUPPORTABILITY_METRIC_CHANNEL, ['API/start/called'], undefined, FEATURE_NAMES.metrics, instanceEE)
123
+ instanceEE.emit('manual-start-all')
134
124
  } catch (err) {
135
125
  warn('An unexpected issue occurred', err)
136
126
  }
@@ -166,9 +156,10 @@ export function setAPI (agentIdentifier, forceDrain, runSoftNavOverSpa = false)
166
156
  try {
167
157
  return cb.apply(this, arguments)
168
158
  } catch (err) {
169
- tracerEE.emit('fn-err', [arguments, this, err], contextStore)
159
+ const error = typeof err === 'string' ? new Error(err) : err
160
+ tracerEE.emit('fn-err', [arguments, this, error], contextStore)
170
161
  // the error came from outside the agent, so don't swallow
171
- throw err
162
+ throw error
172
163
  } finally {
173
164
  tracerEE.emit('fn-end', [now()], contextStore)
174
165
  }
@@ -1 +0,0 @@
1
- {"version":3,"file":"uncaught-error.d.ts","sourceRoot":"","sources":["../../../../../src/features/jserrors/instrument/uncaught-error.js"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH;IACE,kEAMC;IALC,aAA2B;IAC3B,aAAsB;IACtB,eAAyB;IACzB,UAAkB;IAClB,YAAmB;CAEtB"}