@newrelic/browser-agent 1.312.1 → 1.313.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (104) hide show
  1. package/CHANGELOG.md +16 -0
  2. package/dist/cjs/common/config/init-types.js +3 -2
  3. package/dist/cjs/common/config/init.js +10 -8
  4. package/dist/cjs/common/constants/env.cdn.js +1 -1
  5. package/dist/cjs/common/constants/env.npm.js +1 -1
  6. package/dist/cjs/common/timing/time-keeper.js +28 -1
  7. package/dist/cjs/common/util/short-circuit.js +13 -0
  8. package/dist/cjs/common/util/submit-data.js +2 -9
  9. package/dist/cjs/common/v2/script-correlation.js +50 -0
  10. package/dist/cjs/common/v2/script-tracker.js +278 -0
  11. package/dist/cjs/common/{util/v2.js → v2/utils.js} +4 -4
  12. package/dist/cjs/common/wrap/wrap-fetch.js +2 -2
  13. package/dist/cjs/common/wrap/wrap-function.js +2 -2
  14. package/dist/cjs/common/wrap/wrap-xhr.js +2 -2
  15. package/dist/cjs/features/ajax/aggregate/index.js +4 -4
  16. package/dist/cjs/features/generic_events/aggregate/index.js +21 -2
  17. package/dist/cjs/features/generic_events/instrument/index.js +24 -21
  18. package/dist/cjs/features/jserrors/aggregate/index.js +206 -40
  19. package/dist/cjs/features/logging/aggregate/index.js +4 -4
  20. package/dist/cjs/features/metrics/instrument/index.js +1 -8
  21. package/dist/cjs/features/session_trace/aggregate/index.js +3 -4
  22. package/dist/cjs/features/session_trace/aggregate/trace/storage.js +2 -2
  23. package/dist/cjs/interfaces/registered-entity.js +7 -20
  24. package/dist/cjs/loaders/api/register-api-types.js +8 -8
  25. package/dist/cjs/loaders/api/register.js +49 -43
  26. package/dist/esm/common/config/init-types.js +3 -2
  27. package/dist/esm/common/config/init.js +10 -8
  28. package/dist/esm/common/constants/env.cdn.js +1 -1
  29. package/dist/esm/common/constants/env.npm.js +1 -1
  30. package/dist/esm/common/timing/time-keeper.js +28 -1
  31. package/dist/esm/common/util/short-circuit.js +6 -0
  32. package/dist/esm/common/util/submit-data.js +2 -9
  33. package/dist/esm/common/v2/script-correlation.js +43 -0
  34. package/dist/esm/common/v2/script-tracker.js +270 -0
  35. package/dist/esm/common/{util/v2.js → v2/utils.js} +4 -4
  36. package/dist/esm/common/wrap/wrap-fetch.js +1 -1
  37. package/dist/esm/common/wrap/wrap-function.js +1 -1
  38. package/dist/esm/common/wrap/wrap-xhr.js +1 -1
  39. package/dist/esm/features/ajax/aggregate/index.js +1 -1
  40. package/dist/esm/features/generic_events/aggregate/index.js +20 -1
  41. package/dist/esm/features/generic_events/instrument/index.js +24 -21
  42. package/dist/esm/features/jserrors/aggregate/index.js +205 -39
  43. package/dist/esm/features/logging/aggregate/index.js +1 -1
  44. package/dist/esm/features/metrics/instrument/index.js +2 -9
  45. package/dist/esm/features/session_trace/aggregate/index.js +4 -5
  46. package/dist/esm/features/session_trace/aggregate/trace/storage.js +2 -2
  47. package/dist/esm/interfaces/registered-entity.js +7 -20
  48. package/dist/esm/loaders/api/register-api-types.js +8 -8
  49. package/dist/esm/loaders/api/register.js +46 -41
  50. package/dist/tsconfig.tsbuildinfo +1 -1
  51. package/dist/types/common/config/init-types.d.ts +10 -8
  52. package/dist/types/common/config/init-types.d.ts.map +1 -1
  53. package/dist/types/common/config/init.d.ts.map +1 -1
  54. package/dist/types/common/timing/time-keeper.d.ts.map +1 -1
  55. package/dist/types/common/util/short-circuit.d.ts +8 -0
  56. package/dist/types/common/util/short-circuit.d.ts.map +1 -0
  57. package/dist/types/common/util/submit-data.d.ts.map +1 -1
  58. package/dist/types/common/v2/script-correlation.d.ts +38 -0
  59. package/dist/types/common/v2/script-correlation.d.ts.map +1 -0
  60. package/dist/types/common/{util → v2}/script-tracker.d.ts +3 -0
  61. package/dist/types/common/v2/script-tracker.d.ts.map +1 -0
  62. package/dist/types/common/{util/v2.d.ts → v2/utils.d.ts} +1 -1
  63. package/dist/types/common/v2/utils.d.ts.map +1 -0
  64. package/dist/types/features/generic_events/aggregate/index.d.ts.map +1 -1
  65. package/dist/types/features/generic_events/instrument/index.d.ts +1 -1
  66. package/dist/types/features/generic_events/instrument/index.d.ts.map +1 -1
  67. package/dist/types/features/jserrors/aggregate/index.d.ts +25 -16
  68. package/dist/types/features/jserrors/aggregate/index.d.ts.map +1 -1
  69. package/dist/types/features/metrics/instrument/index.d.ts.map +1 -1
  70. package/dist/types/features/session_trace/aggregate/index.d.ts.map +1 -1
  71. package/dist/types/interfaces/registered-entity.d.ts +0 -10
  72. package/dist/types/interfaces/registered-entity.d.ts.map +1 -1
  73. package/dist/types/loaders/api/register-api-types.d.ts +15 -15
  74. package/dist/types/loaders/api/register-api-types.d.ts.map +1 -1
  75. package/dist/types/loaders/api/register.d.ts +6 -0
  76. package/dist/types/loaders/api/register.d.ts.map +1 -1
  77. package/package.json +1 -1
  78. package/src/common/config/init-types.js +3 -2
  79. package/src/common/config/init.js +6 -4
  80. package/src/common/timing/time-keeper.js +30 -1
  81. package/src/common/util/short-circuit.js +6 -0
  82. package/src/common/util/submit-data.js +2 -8
  83. package/src/common/v2/script-correlation.js +44 -0
  84. package/src/common/v2/script-tracker.js +260 -0
  85. package/src/common/{util/v2.js → v2/utils.js} +4 -4
  86. package/src/common/wrap/wrap-fetch.js +1 -1
  87. package/src/common/wrap/wrap-function.js +1 -1
  88. package/src/common/wrap/wrap-xhr.js +1 -1
  89. package/src/features/ajax/aggregate/index.js +1 -1
  90. package/src/features/generic_events/aggregate/index.js +20 -1
  91. package/src/features/generic_events/instrument/index.js +25 -22
  92. package/src/features/jserrors/aggregate/index.js +200 -43
  93. package/src/features/logging/aggregate/index.js +1 -1
  94. package/src/features/metrics/instrument/index.js +2 -13
  95. package/src/features/session_trace/aggregate/index.js +4 -6
  96. package/src/features/session_trace/aggregate/trace/storage.js +2 -2
  97. package/src/interfaces/registered-entity.js +8 -20
  98. package/src/loaders/api/register-api-types.js +8 -8
  99. package/src/loaders/api/register.js +42 -34
  100. package/dist/cjs/common/util/script-tracker.js +0 -204
  101. package/dist/esm/common/util/script-tracker.js +0 -196
  102. package/dist/types/common/util/script-tracker.d.ts.map +0 -1
  103. package/dist/types/common/util/v2.d.ts.map +0 -1
  104. package/src/common/util/script-tracker.js +0 -189
@@ -1,189 +0,0 @@
1
- /**
2
- * Copyright 2020-2026 New Relic, Inc. All rights reserved.
3
- * SPDX-License-Identifier: Apache-2.0
4
- */
5
-
6
- import { globalScope } from '../constants/runtime'
7
- import { now } from '../timing/now'
8
- import { cleanURL } from '../url/clean-url'
9
- import { chrome, chromeEval, gecko } from './browser-stack-matchers'
10
-
11
- /**
12
- * @typedef {import('./register-api-types').RegisterAPITimings} RegisterAPITimings
13
- */
14
-
15
- /** export for testing purposes */
16
- export let thisFile
17
- try {
18
- thisFile = extractUrlsFromStack(getDeepStackTrace())[0]
19
- } catch (err) {
20
- thisFile = extractUrlsFromStack(err)[0]
21
- }
22
-
23
- /** @type {(entry: PerformanceEntry) => boolean} - A shared function to determine if a performance entry is a valid script or link resource for evaluation */
24
- const validEntryCriteria = entry => entry.initiatorType === 'script' || (['link', 'fetch'].includes(entry.initiatorType) && entry.name.endsWith('.js'))
25
-
26
- /** @type {Set<PerformanceResourceTiming>} - A set of resource timing objects that are "valid" -- see "validEntryCriteria" */
27
- const scripts = new Set()
28
- /** @type {Array<{ test: (entry: PerformanceEntry) => boolean, addedAt: number }>} an array of PerformanceObserver subscribers to check for late emissions */
29
- let poSubscribers = []
30
-
31
- if (globalScope.PerformanceObserver?.supportedEntryTypes.includes('resource')) {
32
- /** We must track the script assets this way, because the performance buffer can fill up and when it does that
33
- * it stops accepting new entries (instead of dropping old entries), which means if the register API is called
34
- * after the buffer fills up we won't be able to get the script timing information from the resource timing API
35
- */
36
- const scriptObserver = new PerformanceObserver((list) => {
37
- list.getEntries().forEach((entry) => {
38
- if (validEntryCriteria(entry)) {
39
- if (scripts.size > 250) scripts.delete(scripts.values().next().value) // keep the set from growing indefinitely, we only need to check recent entries against the stack of the register API caller, so we can drop old entries as new ones come in
40
- scripts.add(entry)
41
- const canClear = []
42
- poSubscribers.forEach(({ test, addedAt }, idx) => {
43
- if (test(entry) || now() - addedAt > 10000) canClear.push(idx) // Clear subscribers that have resolved or have been pending for more than 10 seconds
44
- })
45
- poSubscribers = poSubscribers.filter((_, idx) => !canClear.includes(idx))
46
- }
47
- })
48
- })
49
- scriptObserver.observe({ type: 'resource', buffered: true })
50
- }
51
-
52
- /**
53
- * Extracts URLs from stack traces using the same logic as compute-stack-trace.js
54
- * @param {string} stack The error stack trace
55
- * @returns {string[]} Array of cleaned URLs found in the stack trace
56
- */
57
- export function extractUrlsFromStack (stack) {
58
- if (!stack || typeof stack !== 'string') return []
59
-
60
- const urls = new Set()
61
- const lines = stack.split('\n')
62
-
63
- for (const line of lines) {
64
- // Try gecko format first, then chrome
65
- const parts = line.match(gecko) || line.match(chrome) || line.match(chromeEval)
66
- if (parts && parts[2]) {
67
- urls.add(cleanURL(parts[2]))
68
- } else {
69
- // Fallback: match URLs using a generic .js pattern (non-greedy to handle ports and query params)
70
- const fallbackMatch = line.match(/\(([^)]+\.js):\d+:\d+\)/) || line.match(/^\s+at\s+([^\s(]+\.js):\d+:\d+/)
71
- if (fallbackMatch && fallbackMatch[1]) {
72
- urls.add(cleanURL(fallbackMatch[1]))
73
- }
74
- }
75
- }
76
- return [...urls]
77
- }
78
-
79
- /**
80
- * Returns a deep stack trace by temporarily increasing the stack trace limit.
81
- * @returns {Error.stack | undefined}
82
- */
83
- export function getDeepStackTrace () {
84
- let stack
85
- try {
86
- const originalStackLimit = Error.stackTraceLimit
87
- Error.stackTraceLimit = 50
88
- stack = new Error().stack
89
- Error.stackTraceLimit = originalStackLimit
90
- } catch (e) {
91
- stack = new Error().stack
92
- }
93
- return stack
94
- }
95
-
96
- /**
97
- * Indicates whether the provided URL matches any script preload link tags in the document.
98
- * @param {string} targetUrl The URL to match against preload tags
99
- * @returns {boolean} True if a matching preload link is found, false otherwise
100
- */
101
- function wasPreloaded (targetUrl) {
102
- if (!targetUrl || !globalScope.document) return false
103
-
104
- try {
105
- const linkTags = globalScope.document.querySelectorAll('link[rel="preload"][as="script"]')
106
- for (const link of linkTags) {
107
- // link.href is resolved to an absolute URL by the browser (even if supplied as relative), so we can match exactly against the cleaned target URL
108
- if (cleanURL(link.href) === targetUrl) return true
109
- }
110
- } catch (error) {
111
- // Don't let DOM parsing errors break anything
112
- }
113
- return false
114
- }
115
-
116
- /**
117
- * Uses the stack of the initiator function, returns script timing information if a script can be found with the resource timing API matching the URL found in the stack.
118
- * @returns {RegisterAPITimings} Object containing script fetch start and end times, and the asset URL if found
119
- */
120
- export function findScriptTimings () {
121
- const timings = { registeredAt: now(), reportedAt: undefined, fetchStart: 0, fetchEnd: 0, asset: undefined, type: 'unknown' }
122
- const stack = getDeepStackTrace()
123
- if (!stack) return timings
124
- const navUrl = globalScope.performance?.getEntriesByType('navigation')?.[0]?.name || ''
125
-
126
- try {
127
- const urls = extractUrlsFromStack(stack)
128
- /** if there is exactly one url, this means the MFE script is running in the same file as the agent. Otherwise, lets strip away the known agent file from any other file lines */
129
- const mfeScriptUrl = (urls.length > 1 ? urls.filter(line => (!thisFile.endsWith(line) && !line.endsWith(thisFile))) : urls)[0]
130
- if (!mfeScriptUrl) return timings
131
-
132
- if (navUrl.includes(mfeScriptUrl)) {
133
- // this means the stack is indicating that the registration came from an inline script or eval, so we won't find a matching script resource - return early with just the URL
134
- timings.asset = cleanURL(navUrl)
135
- timings.type = 'inline'
136
- return timings
137
- }
138
-
139
- // try to find it in the static list, which updates faster than the PO. This cant be solely trusted, since the buffer can fill up and stop accepting entries,
140
- // but it can still help in cases where the check is made before the asset is emitted by the performance observer and the buffer is not full. Fallback to checking the PO
141
- // entries that have been buffered as seen in the PO callback if its not found in the static list.
142
- const match = performance.getEntriesByType('resource').find(entryMatchesMfe) || [...scripts].find(entryMatchesMfe)
143
-
144
- if (match) { setMatchedAttributes(match) } else {
145
- // We didnt find a match with the PO, nor a static lookup... check if its preloaded and if so, set basic fallbacks and try to associate with a later script entry if possible, this can happen if the preload is reported late in the PO observer callback
146
- if (wasPreloaded(mfeScriptUrl)) {
147
- timings.asset = mfeScriptUrl
148
- timings.type = 'preload'
149
-
150
- // wait for a late PO callback... The timings object can be mutated after the fact since we return a pointer and not a cloned object
151
- poSubscribers.push({
152
- addedAt: now(),
153
- test: (entry) => {
154
- if (entryMatchesMfe(entry)) {
155
- setMatchedAttributes(entry)
156
- return true // return true so that we know to clear this callback from the pending list since we found our match, otherwise it will stay in the list and be called for future entries which is unnecessary after we found our match and can cause performance issues if there are a lot of future entries and pending callbacks
157
- }
158
- return false
159
- }
160
- })
161
- }
162
- }
163
-
164
- /**
165
- * A matcher function to determine if a performance entry corresponds to the MFE script based on URL matching. It checks if either the entry URL ends with the MFE script URL or vice versa, to account for potential differences in how URLs are represented in the stack trace versus the resource timing entries.
166
- * @param {PerformanceResourceTiming} entry - The resource timing entry to compare to the MFE script
167
- * @returns {boolean} True if we think the entry matches the MFE script, false otherwise
168
- */
169
- function entryMatchesMfe (entry) {
170
- const entryUrl = cleanURL(entry.name)
171
- return entryUrl.endsWith(mfeScriptUrl) || mfeScriptUrl.endsWith(entryUrl)
172
- }
173
-
174
- /**
175
- * A helper function to set the matched timing attributes on the timings object based on a performance entry. This is called when we have identified a resource timing entry that we believe corresponds to the MFE script, and we want to extract the fetch start and end times, as well as the asset URL and type.
176
- * @param {PerformanceResourceTiming} entry - The resource timing entry to base values off of
177
- */
178
- function setMatchedAttributes (entry) {
179
- timings.fetchStart = Math.floor(entry.startTime)
180
- timings.fetchEnd = Math.floor(entry.responseEnd)
181
- timings.asset = entry.name
182
- timings.type = entry.initiatorType
183
- }
184
- } catch (error) {
185
- // Don't let stack parsing errors break anything
186
- }
187
-
188
- return timings
189
- }