@newrelic/browser-agent 1.239.1 → 1.240.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 (167) hide show
  1. package/README.md +4 -0
  2. package/dist/cjs/cdn/pro.js +3 -2
  3. package/dist/cjs/cdn/spa.js +4 -3
  4. package/dist/cjs/common/config/state/init.js +6 -0
  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 +9 -5
  8. package/dist/cjs/common/harvest/harvest.js +5 -3
  9. package/dist/cjs/common/vitals/constants.js +17 -0
  10. package/dist/cjs/common/vitals/cumulative-layout-shift.js +27 -0
  11. package/dist/cjs/common/vitals/first-contentful-paint.js +49 -0
  12. package/dist/cjs/common/vitals/first-input-delay.js +32 -0
  13. package/dist/{esm/features/page_view_timing → cjs/common/vitals}/first-paint.js +19 -17
  14. package/dist/cjs/common/vitals/interaction-to-next-paint.js +29 -0
  15. package/dist/cjs/common/vitals/largest-contentful-paint.js +41 -0
  16. package/dist/cjs/common/vitals/long-task.js +64 -0
  17. package/dist/cjs/common/vitals/time-to-first-byte.js +36 -0
  18. package/dist/cjs/common/vitals/vital-metric.js +71 -0
  19. package/dist/cjs/features/ajax/aggregate/index.js +4 -1
  20. package/dist/cjs/features/metrics/aggregate/index.js +7 -0
  21. package/dist/cjs/features/page_view_event/aggregate/index.js +18 -40
  22. package/dist/cjs/features/page_view_event/constants.js +2 -8
  23. package/dist/cjs/features/page_view_event/instrument/index.js +0 -22
  24. package/dist/cjs/features/page_view_timing/aggregate/index.js +27 -138
  25. package/dist/cjs/features/page_view_timing/instrument/index.js +0 -3
  26. package/dist/cjs/features/session_trace/aggregate/index.js +13 -1
  27. package/dist/cjs/features/spa/aggregate/index.js +4 -3
  28. package/dist/cjs/loaders/agent.js +3 -0
  29. package/dist/cjs/loaders/api/api.js +2 -0
  30. package/dist/cjs/loaders/api/apiAsync.js +4 -2
  31. package/dist/cjs/loaders/configure/configure.js +13 -1
  32. package/dist/cjs/loaders/configure/public-path.js +13 -0
  33. package/dist/cjs/loaders/configure/public-path.npm.js +10 -0
  34. package/dist/esm/cdn/pro.js +2 -1
  35. package/dist/esm/cdn/spa.js +2 -1
  36. package/dist/esm/common/config/state/init.js +6 -0
  37. package/dist/esm/common/constants/env.cdn.js +1 -1
  38. package/dist/esm/common/constants/env.npm.js +1 -1
  39. package/dist/esm/common/constants/runtime.js +5 -3
  40. package/dist/esm/common/harvest/harvest.js +6 -4
  41. package/dist/esm/common/vitals/constants.js +10 -0
  42. package/dist/esm/common/vitals/cumulative-layout-shift.js +20 -0
  43. package/dist/esm/common/vitals/first-contentful-paint.js +41 -0
  44. package/dist/esm/common/vitals/first-input-delay.js +25 -0
  45. package/dist/{cjs/features/page_view_timing → esm/common/vitals}/first-paint.js +12 -24
  46. package/dist/esm/common/vitals/interaction-to-next-paint.js +22 -0
  47. package/dist/esm/common/vitals/largest-contentful-paint.js +34 -0
  48. package/dist/esm/common/vitals/long-task.js +57 -0
  49. package/dist/esm/common/vitals/time-to-first-byte.js +29 -0
  50. package/dist/esm/common/vitals/vital-metric.js +64 -0
  51. package/dist/esm/features/ajax/aggregate/index.js +4 -1
  52. package/dist/esm/features/metrics/aggregate/index.js +8 -1
  53. package/dist/esm/features/page_view_event/aggregate/index.js +20 -42
  54. package/dist/esm/features/page_view_event/constants.js +1 -4
  55. package/dist/esm/features/page_view_event/instrument/index.js +0 -22
  56. package/dist/esm/features/page_view_timing/aggregate/index.js +28 -139
  57. package/dist/esm/features/page_view_timing/instrument/index.js +0 -3
  58. package/dist/esm/features/session_trace/aggregate/index.js +13 -1
  59. package/dist/esm/features/spa/aggregate/index.js +4 -3
  60. package/dist/esm/loaders/agent.js +2 -0
  61. package/dist/esm/loaders/api/api.js +2 -0
  62. package/dist/esm/loaders/api/apiAsync.js +5 -3
  63. package/dist/esm/loaders/configure/configure.js +13 -1
  64. package/dist/esm/loaders/configure/public-path.js +6 -0
  65. package/dist/esm/loaders/configure/public-path.npm.js +3 -0
  66. package/dist/types/common/config/state/init.d.ts.map +1 -1
  67. package/dist/types/common/constants/runtime.d.ts +3 -1
  68. package/dist/types/common/constants/runtime.d.ts.map +1 -1
  69. package/dist/types/common/harvest/harvest.d.ts +0 -1
  70. package/dist/types/common/harvest/harvest.d.ts.map +1 -1
  71. package/dist/types/common/vitals/constants.d.ts +11 -0
  72. package/dist/types/common/vitals/constants.d.ts.map +1 -0
  73. package/dist/types/common/vitals/cumulative-layout-shift.d.ts +3 -0
  74. package/dist/types/common/vitals/cumulative-layout-shift.d.ts.map +1 -0
  75. package/dist/types/common/vitals/first-contentful-paint.d.ts +3 -0
  76. package/dist/types/common/vitals/first-contentful-paint.d.ts.map +1 -0
  77. package/dist/types/common/vitals/first-input-delay.d.ts +3 -0
  78. package/dist/types/common/vitals/first-input-delay.d.ts.map +1 -0
  79. package/dist/types/common/vitals/first-paint.d.ts +3 -0
  80. package/dist/types/common/vitals/first-paint.d.ts.map +1 -0
  81. package/dist/types/common/vitals/interaction-to-next-paint.d.ts +3 -0
  82. package/dist/types/common/vitals/interaction-to-next-paint.d.ts.map +1 -0
  83. package/dist/types/common/vitals/largest-contentful-paint.d.ts +3 -0
  84. package/dist/types/common/vitals/largest-contentful-paint.d.ts.map +1 -0
  85. package/dist/types/common/vitals/long-task.d.ts +3 -0
  86. package/dist/types/common/vitals/long-task.d.ts.map +1 -0
  87. package/dist/types/common/vitals/time-to-first-byte.d.ts +3 -0
  88. package/dist/types/common/vitals/time-to-first-byte.d.ts.map +1 -0
  89. package/dist/types/common/vitals/vital-metric.d.ts +18 -0
  90. package/dist/types/common/vitals/vital-metric.d.ts.map +1 -0
  91. package/dist/types/features/ajax/aggregate/index.d.ts.map +1 -1
  92. package/dist/types/features/metrics/aggregate/index.d.ts.map +1 -1
  93. package/dist/types/features/page_view_event/aggregate/index.d.ts +3 -2
  94. package/dist/types/features/page_view_event/aggregate/index.d.ts.map +1 -1
  95. package/dist/types/features/page_view_event/constants.d.ts +0 -3
  96. package/dist/types/features/page_view_event/constants.d.ts.map +1 -1
  97. package/dist/types/features/page_view_event/instrument/index.d.ts.map +1 -1
  98. package/dist/types/features/page_view_timing/aggregate/index.d.ts +1 -3
  99. package/dist/types/features/page_view_timing/aggregate/index.d.ts.map +1 -1
  100. package/dist/types/features/page_view_timing/instrument/index.d.ts.map +1 -1
  101. package/dist/types/features/session_trace/aggregate/index.d.ts +9 -1
  102. package/dist/types/features/session_trace/aggregate/index.d.ts.map +1 -1
  103. package/dist/types/features/spa/aggregate/index.d.ts.map +1 -1
  104. package/dist/types/loaders/agent.d.ts.map +1 -1
  105. package/dist/types/loaders/api/api.d.ts.map +1 -1
  106. package/dist/types/loaders/api/apiAsync.d.ts.map +1 -1
  107. package/dist/types/loaders/configure/configure.d.ts.map +1 -1
  108. package/dist/types/loaders/configure/public-path.d.ts +2 -0
  109. package/dist/types/loaders/configure/public-path.d.ts.map +1 -0
  110. package/dist/types/loaders/configure/public-path.npm.d.ts +2 -0
  111. package/dist/types/loaders/configure/public-path.npm.d.ts.map +1 -0
  112. package/package.json +1 -1
  113. package/src/cdn/pro.js +2 -0
  114. package/src/cdn/spa.js +2 -0
  115. package/src/common/config/state/init.js +4 -0
  116. package/src/common/constants/runtime.js +7 -3
  117. package/src/common/constants/runtime.test.js +8 -0
  118. package/src/common/harvest/harvest.js +6 -4
  119. package/src/common/harvest/harvest.test.js +17 -0
  120. package/src/common/vitals/__mocks__/web-vitals.js +19 -0
  121. package/src/common/vitals/constants.js +10 -0
  122. package/src/common/vitals/cumulative-layout-shift.js +13 -0
  123. package/src/common/vitals/cumulative-layout-shift.test.js +71 -0
  124. package/src/common/vitals/first-contentful-paint.js +31 -0
  125. package/src/common/vitals/first-contentful-paint.test.js +124 -0
  126. package/src/common/vitals/first-input-delay.js +20 -0
  127. package/src/common/vitals/first-input-delay.test.js +88 -0
  128. package/src/{features/page_view_timing → common/vitals}/first-paint.js +11 -17
  129. package/src/common/vitals/first-paint.test.js +127 -0
  130. package/src/common/vitals/interaction-to-next-paint.js +13 -0
  131. package/src/common/vitals/interaction-to-next-paint.test.js +74 -0
  132. package/src/common/vitals/largest-contentful-paint.js +29 -0
  133. package/src/common/vitals/largest-contentful-paint.test.js +94 -0
  134. package/src/common/vitals/long-task.js +52 -0
  135. package/src/common/vitals/long-task.test.js +122 -0
  136. package/src/common/vitals/time-to-first-byte.js +21 -0
  137. package/src/common/vitals/time-to-first-byte.test.js +147 -0
  138. package/src/common/vitals/vital-metric.js +60 -0
  139. package/src/common/vitals/vital-metric.test.js +171 -0
  140. package/src/features/ajax/aggregate/index.js +5 -1
  141. package/src/features/metrics/aggregate/index.js +6 -1
  142. package/src/features/page_view_event/aggregate/index.js +20 -43
  143. package/src/features/page_view_event/constants.js +0 -3
  144. package/src/features/page_view_event/instrument/index.js +0 -21
  145. package/src/features/page_view_timing/aggregate/index.component-test.js +86 -0
  146. package/src/features/page_view_timing/aggregate/index.js +24 -102
  147. package/src/features/page_view_timing/instrument/index.js +0 -3
  148. package/src/features/session_trace/aggregate/index.js +15 -1
  149. package/src/features/spa/aggregate/index.js +4 -3
  150. package/src/loaders/agent.js +2 -0
  151. package/src/loaders/api/api.js +2 -0
  152. package/src/loaders/api/apiAsync.js +5 -4
  153. package/src/loaders/configure/configure.js +15 -7
  154. package/src/loaders/configure/public-path.js +6 -0
  155. package/src/loaders/configure/public-path.npm.js +4 -0
  156. package/dist/cjs/common/metrics/paint-metrics.js +0 -13
  157. package/dist/cjs/features/page_view_timing/long-tasks.js +0 -75
  158. package/dist/esm/common/metrics/paint-metrics.js +0 -6
  159. package/dist/esm/features/page_view_timing/long-tasks.js +0 -69
  160. package/dist/types/common/metrics/paint-metrics.d.ts +0 -2
  161. package/dist/types/common/metrics/paint-metrics.d.ts.map +0 -1
  162. package/dist/types/features/page_view_timing/first-paint.d.ts +0 -2
  163. package/dist/types/features/page_view_timing/first-paint.d.ts.map +0 -1
  164. package/dist/types/features/page_view_timing/long-tasks.d.ts +0 -2
  165. package/dist/types/features/page_view_timing/long-tasks.d.ts.map +0 -1
  166. package/src/common/metrics/paint-metrics.js +0 -6
  167. package/src/features/page_view_timing/long-tasks.js +0 -60
@@ -3,113 +3,48 @@
3
3
  * SPDX-License-Identifier: Apache-2.0
4
4
  */
5
5
 
6
- import { onFCP, onFID, onLCP, onCLS, onINP } from 'web-vitals'
7
- import { onFirstPaint } from '../first-paint'
8
- import { onLongTask } from '../long-tasks'
9
- import { iOSBelow16 } from '../../../common/constants/runtime'
10
6
  import { nullable, numeric, getAddStringContext, addCustomAttributes } from '../../../common/serialize/bel-serializer'
11
7
  import { mapOwn } from '../../../common/util/map-own'
12
8
  import { HarvestScheduler } from '../../../common/harvest/harvest-scheduler'
13
9
  import { registerHandler } from '../../../common/event-emitter/register-handler'
14
- import { cleanURL } from '../../../common/url/clean-url'
15
10
  import { handle } from '../../../common/event-emitter/handle'
16
- import { getInfo, getConfigurationValue, getRuntime } from '../../../common/config/config'
11
+ import { getInfo, getConfigurationValue } from '../../../common/config/config'
17
12
  import { FEATURE_NAME } from '../constants'
18
13
  import { FEATURE_NAMES } from '../../../loaders/features/features'
19
14
  import { AggregateBase } from '../../utils/aggregate-base'
15
+ import { cumulativeLayoutShift } from '../../../common/vitals/cumulative-layout-shift'
16
+ import { firstContentfulPaint } from '../../../common/vitals/first-contentful-paint'
17
+ import { firstInputDelay } from '../../../common/vitals/first-input-delay'
18
+ import { firstPaint } from '../../../common/vitals/first-paint'
19
+ import { interactionToNextPaint } from '../../../common/vitals/interaction-to-next-paint'
20
+ import { largestContentfulPaint } from '../../../common/vitals/largest-contentful-paint'
21
+ import { timeToFirstByte } from '../../../common/vitals/time-to-first-byte'
22
+ import { longTask } from '../../../common/vitals/long-task'
20
23
 
21
24
  export class Aggregate extends AggregateBase {
22
25
  static featureName = FEATURE_NAME
26
+
27
+ #handleVitalMetric = ({ name, value, attrs }) => {
28
+ this.addTiming(name, value, attrs)
29
+ }
30
+
23
31
  constructor (agentIdentifier, aggregator) {
24
32
  super(agentIdentifier, aggregator, FEATURE_NAME)
25
33
 
26
34
  this.timings = []
27
35
  this.timingsSent = []
28
36
  this.curSessEndRecorded = false
29
- this.cls = null // this should be null unless set to a numeric value by web-vitals so that we differentiate if CLS is supported
30
37
 
31
- /* ! This is the section that used to be in the loader portion: ! */
32
- /* ------------------------------------------------------------ */
33
- const pageStartedHidden = getRuntime(agentIdentifier).initHidden // our attempt at recapturing initial vis state since this code runs post-load time
34
- this.alreadySent = new Set() // since we don't support timings on BFCache restores, this tracks and helps cap metrics that web-vitals report more than once
35
-
36
- /* PerformancePaintTiming API - BFC is not yet supported. */
37
- onFirstPaint(({ name, value }) => {
38
- if (pageStartedHidden) return
39
- this.addTiming(name.toLowerCase(), Math.floor(value))
38
+ firstPaint.subscribe(this.#handleVitalMetric)
39
+ firstContentfulPaint.subscribe(this.#handleVitalMetric)
40
+ firstInputDelay.subscribe(this.#handleVitalMetric)
41
+ largestContentfulPaint.subscribe(this.#handleVitalMetric)
42
+ interactionToNextPaint.subscribe(this.#handleVitalMetric)
43
+ timeToFirstByte.subscribe(({ entries }) => {
44
+ this.addTiming('load', Math.round(entries[0].loadEventEnd))
40
45
  })
41
46
 
42
- /* First Contentful Paint - As of WV v3, it still imperfectly tries to detect document vis state asap and isn't supposed to report if page starts hidden. */
43
- if (iOSBelow16) {
44
- try {
45
- if (!pageStartedHidden) { // see ios-version.js for detail on this following bug case; tldr: buffered flag doesn't work but getEntriesByType does
46
- const paintEntries = performance.getEntriesByType('paint')
47
- paintEntries.forEach(entry => {
48
- if (entry.name === 'first-contentful-paint') {
49
- this.addTiming('fcp', Math.floor(entry.startTime))
50
- }
51
- })
52
- }
53
- } catch (e) {}
54
- } else {
55
- onFCP(({ name, value }) => {
56
- if (pageStartedHidden || this.alreadySent.has(name)) return
57
- this.alreadySent.add(name)
58
- this.addTiming(name.toLowerCase(), value)
59
- })
60
- }
61
-
62
- /* First Input Delay (+"First Interaction") - As of WV v3, it still imperfectly tries to detect document vis state asap and isn't supposed to report if page starts hidden. */
63
- onFID(({ name, value, entries }) => {
64
- if (pageStartedHidden || this.alreadySent.has(name) || entries.length === 0) return
65
- this.alreadySent.add(name)
66
-
67
- // CWV will only report one (THE) first-input entry to us; fid isn't reported if there are no user interactions occurs before the *first* page hiding.
68
- const fiEntry = entries[0]
69
- const attributes = {
70
- type: fiEntry.name,
71
- fid: Math.round(value)
72
- }
73
- this.addConnectionAttributes(attributes)
74
- this.addTiming('fi', Math.round(fiEntry.startTime), attributes)
75
- })
76
-
77
- /* Largest Contentful Paint - As of WV v3, it still imperfectly tries to detect document vis state asap and isn't supposed to report if page starts hidden. */
78
- onLCP(({ name, value, entries }) => {
79
- if (pageStartedHidden || this.alreadySent.has(name)) return
80
- this.alreadySent.add(name)
81
-
82
- const attributes = {}
83
- if (entries.length > 0) {
84
- // CWV will only ever report one (THE) lcp entry to us; lcp is also only reported *once* on earlier(user interaction, page hidden).
85
- const lcpEntry = entries[entries.length - 1] // this looks weird if we only expect one, but this is how cwv-attribution gets it so to be sure...
86
- attributes.size = lcpEntry.size
87
- attributes.eid = lcpEntry.id
88
-
89
- if (lcpEntry.url) {
90
- attributes.elUrl = cleanURL(lcpEntry.url)
91
- }
92
- if (lcpEntry.element?.tagName) {
93
- attributes.elTag = lcpEntry.element.tagName
94
- }
95
- }
96
-
97
- this.addConnectionAttributes(attributes)
98
- this.addTiming(name.toLowerCase(), value, attributes)
99
- })
100
-
101
- /* Cumulative Layout Shift - We don't have to limit this callback since cls is stored as a state and only sent as attribute on other timings.
102
- reportAllChanges ensures our tracked cls has the most recent rolling value to attach to 'unload' and 'pagehide'. */
103
- onCLS(({ value }) => { this.cls = value }, { reportAllChanges: true })
104
-
105
- /* Interaction-to-Next-Paint */
106
- onINP(({ name, value, id }) => this.addTiming(name.toLowerCase(), value, { metricId: id }))
107
-
108
- /* PerformanceLongTaskTiming API */
109
- if (getConfigurationValue(this.agentIdentifier, 'page_view_timing.long_task') === true) {
110
- onLongTask(({ name, value, info }) => this.addTiming(name.toLowerCase(), value, info)) // lt context is passed as 'info'=attrs in the timing node
111
- }
112
- /* ------------------------------------End of ex-loader section */
47
+ if (getConfigurationValue(this.agentIdentifier, 'page_view_timing.long_task') === true) longTask.subscribe(this.#handleVitalMetric)
113
48
 
114
49
  /* It's important that CWV api, like "onLCP", is called before this scheduler is initialized. The reason is because they listen to the same
115
50
  on vis change or pagehide events, and we'd want ex. onLCP to record the timing (win the race) before we try to send "final harvest". */
@@ -130,19 +65,6 @@ export class Aggregate extends AggregateBase {
130
65
  this.drain()
131
66
  }
132
67
 
133
- // takes an attributes object and appends connection attributes if available
134
- addConnectionAttributes (attributes) {
135
- var connection = navigator.connection || navigator.mozConnection || navigator.webkitConnection // to date, both window & worker shares the same support for connection
136
- if (!connection) return
137
-
138
- if (connection.type) attributes['net-type'] = connection.type
139
- if (connection.effectiveType) attributes['net-etype'] = connection.effectiveType
140
- if (connection.rtt) attributes['net-rtt'] = connection.rtt
141
- if (connection.downlink) attributes['net-dlink'] = connection.downlink
142
-
143
- return attributes
144
- }
145
-
146
68
  /**
147
69
  * Add the time of _document visibilitychange to hidden_ to the next PVT harvest == NRDB pageHide attr.
148
70
  * @param {number} timestamp
@@ -179,8 +101,8 @@ export class Aggregate extends AggregateBase {
179
101
  Mitigation: We've set initial CLS to null so that it's omitted from timings like 'pageHide' in that edge case. It should only be included if onCLS callback was executed at least once.
180
102
  Future: onCLS value changes should be reported directly & CLS separated into its own timing node so it's not beholden to 'pageHide' firing. It'd also be possible to report the real final CLS.
181
103
  */
182
- if (this.cls !== null) {
183
- attrs.cls = this.cls
104
+ if (cumulativeLayoutShift.current.value >= 0) {
105
+ attrs.cls = cumulativeLayoutShift.current.value
184
106
  }
185
107
 
186
108
  this.timings.push({
@@ -3,7 +3,6 @@
3
3
  * SPDX-License-Identifier: Apache-2.0
4
4
  */
5
5
  import { handle } from '../../../common/event-emitter/handle'
6
- import { getRuntime } from '../../../common/config/config'
7
6
  import { subscribeToVisibilityChange } from '../../../common/window/page-visibility'
8
7
  import { windowAddEventListener } from '../../../common/event-listener/event-listener-opts'
9
8
  import { now } from '../../../common/timing/now'
@@ -17,9 +16,7 @@ export class Instrument extends InstrumentBase {
17
16
  super(agentIdentifier, aggregator, FEATURE_NAME, auto)
18
17
  if (!isBrowserScope) return // CWV is irrelevant outside web context
19
18
 
20
- // Document visibility state becomes hidden; this should run as soon as possible in page life.
21
19
  // While we try to replicate web-vital's visibilitywatcher logic in an effort to defer that library to post-pageload, this isn't perfect and doesn't consider prerendering.
22
- getRuntime(agentIdentifier).initHidden = Boolean(document.visibilityState === 'hidden')
23
20
  subscribeToVisibilityChange(() => handle('docHidden', [now()], undefined, FEATURE_NAME, this.ee), true)
24
21
 
25
22
  // Window fires its pagehide event (typically on navigation--this occurrence is a *subset* of vis change); don't defer this unless it's guarantee it cannot happen before load(?)
@@ -443,7 +443,11 @@ export class Aggregate extends AggregateBase {
443
443
  this.storeResources(window.performance.getEntriesByType('resource'))
444
444
  }
445
445
 
446
+ let earliestTimeStamp = Infinity
446
447
  const stns = Object.entries(this.trace).flatMap(([name, listOfSTNodes]) => { // basically take the "this.trace" map-obj and concat all the list-type values
448
+ const oldestNodeTS = listOfSTNodes.reduce((acc, next) => (!acc || next.s < acc) ? next.s : acc, undefined)
449
+ if (oldestNodeTS < earliestTimeStamp) earliestTimeStamp = oldestNodeTS
450
+
447
451
  if (!(name in toAggregate)) return listOfSTNodes
448
452
  // Special processing for event nodes dealing with user inputs:
449
453
  const reindexByOriginFn = this.smearEvtsByOrigin(name)
@@ -459,7 +463,17 @@ export class Aggregate extends AggregateBase {
459
463
  this.nodeCount = 0
460
464
 
461
465
  return {
462
- qs: { st: String(getRuntime(this.agentIdentifier).offset) },
466
+ qs: {
467
+ st: this.agentRuntime.offset,
468
+ /** hr === "hasReplay" in NR1, standalone is always checked and processed before harvesting
469
+ * so a race condition between ST and SR states should not be a concern if implemented here */
470
+ hr: Number(!this.isStandalone),
471
+ /** fts === "firstTimestamp" in NR1, indicates what the earliest NODE timestamp was
472
+ * so that blob parsing doesn't need to happen to support UI/API functions */
473
+ fts: this.agentRuntime.offset + earliestTimeStamp,
474
+ /** 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 */
475
+ n: stns.length // node count
476
+ },
463
477
  body: { res: stns }
464
478
  }
465
479
  }
@@ -9,7 +9,6 @@ import { shouldCollectEvent } from '../../../common/deny-list/deny-list'
9
9
  import { mapOwn } from '../../../common/util/map-own'
10
10
  import { navTimingValues as navTiming } from '../../../common/timing/nav-timing'
11
11
  import { generateUuid } from '../../../common/ids/unique-id'
12
- import { paintMetrics } from '../../../common/metrics/paint-metrics'
13
12
  import { Interaction } from './interaction'
14
13
  import { getConfigurationValue, getRuntime } from '../../../common/config/config'
15
14
  import { eventListenerOpts } from '../../../common/event-listener/event-listener-opts'
@@ -19,6 +18,8 @@ import { ee } from '../../../common/event-emitter/contextual-ee'
19
18
  import * as CONSTANTS from '../constants'
20
19
  import { FEATURE_NAMES } from '../../../loaders/features/features'
21
20
  import { AggregateBase } from '../../utils/aggregate-base'
21
+ import { firstContentfulPaint } from '../../../common/vitals/first-contentful-paint'
22
+ import { firstPaint } from '../../../common/vitals/first-paint'
22
23
 
23
24
  const {
24
25
  FEATURE_NAME, INTERACTION_EVENTS, MAX_TIMER_BUDGET, FN_START, FN_END, CB_START, INTERACTION_API, REMAINING,
@@ -714,8 +715,8 @@ export class Aggregate extends AggregateBase {
714
715
  interaction.root.attrs.id = generateUuid()
715
716
 
716
717
  if (interaction.root.attrs.trigger === 'initialPageLoad') {
717
- interaction.root.attrs.firstPaint = paintMetrics['first-paint']
718
- interaction.root.attrs.firstContentfulPaint = paintMetrics['first-contentful-paint']
718
+ interaction.root.attrs.firstPaint = firstPaint.current.value
719
+ interaction.root.attrs.firstContentfulPaint = firstContentfulPaint.current.value
719
720
  }
720
721
  baseEE.emit('interactionSaved', [interaction])
721
722
  state.interactionsToHarvest.push(interaction)
@@ -1,3 +1,5 @@
1
+ // important side effects
2
+ import './configure/public-path'
1
3
  // loader files
2
4
  import { AgentBase } from './agent-base'
3
5
  import { getEnabledFeatures } from './features/enabled-features'
@@ -124,6 +124,8 @@ export function setAPI (agentIdentifier, forceDrain) {
124
124
 
125
125
  apiInterface.start = (features) => {
126
126
  try {
127
+ const smTag = !features ? 'undefined' : 'defined'
128
+ handle(SUPPORTABILITY_METRIC_CHANNEL, [`API/start/${smTag}/called`], undefined, FEATURE_NAMES.metrics, instanceEE)
127
129
  const featNames = Object.values(FEATURE_NAMES)
128
130
  if (features === undefined) features = featNames
129
131
  else {
@@ -1,5 +1,5 @@
1
1
  import { FEATURE_NAMES } from '../features/features'
2
- import { getConfigurationValue, getInfo, getRuntime } from '../../common/config/config'
2
+ import { getConfiguration, getInfo, getRuntime } from '../../common/config/config'
3
3
  import { ee } from '../../common/event-emitter/contextual-ee'
4
4
  import { handle } from '../../common/event-emitter/handle'
5
5
  import { registerHandler } from '../../common/event-emitter/register-handler'
@@ -12,8 +12,6 @@ export function setAPI (agentIdentifier) {
12
12
  var instanceEE = ee.get(agentIdentifier)
13
13
  var cycle = 0
14
14
 
15
- var scheme = (getConfigurationValue(agentIdentifier, 'ssl') === false) ? 'http' : 'https'
16
-
17
15
  var api = {
18
16
  finished: single(finished),
19
17
  setErrorHandler,
@@ -66,7 +64,10 @@ export function setAPI (agentIdentifier) {
66
64
  const agentInfo = getInfo(agentIdentifier)
67
65
  if (!agentInfo.beacon) return
68
66
 
69
- var url = scheme + '://' + agentInfo.beacon + '/1/' + agentInfo.licenseKey
67
+ const agentInit = getConfiguration(agentIdentifier)
68
+ const scheme = agentInit.ssl === false ? 'http' : 'https'
69
+ const beacon = agentInit.proxy.beacon || agentInfo.beacon
70
+ let url = `${scheme}://${beacon}/1/${agentInfo.licenseKey}`
70
71
 
71
72
  url += '?a=' + agentInfo.applicationID + '&'
72
73
  url += 't=' + requestName + '&'
@@ -3,6 +3,9 @@ import { addToNREUM, gosCDN, gosNREUMInitializedAgents } from '../../common/wind
3
3
  import { getConfiguration, setConfiguration, setInfo, setLoaderConfig, setRuntime } from '../../common/config/config'
4
4
  import { activatedFeatures } from '../../common/util/feature-flags'
5
5
  import { isWorkerScope } from '../../common/constants/runtime'
6
+ import { redefinePublicPath } from './public-path'
7
+
8
+ let alreadySetOnce = false // the configure() function can run multiple times in agent lifecycle
6
9
 
7
10
  export function configure (agentIdentifier, opts = {}, loaderType, forceDrain) {
8
11
  // eslint-disable-next-line camelcase
@@ -26,14 +29,19 @@ export function configure (agentIdentifier, opts = {}, loaderType, forceDrain) {
26
29
  setInfo(agentIdentifier, info)
27
30
 
28
31
  const updatedInit = getConfiguration(agentIdentifier)
32
+ const internalTrafficList = [info.beacon, info.errorBeacon]
33
+ if (!alreadySetOnce) {
34
+ alreadySetOnce = true
35
+ if (updatedInit.proxy.assets) {
36
+ redefinePublicPath(updatedInit.proxy.assets + '/') // much like the info.beacon & init.proxy.beacon, this input should not end in a slash, but one is needed for webpack concat
37
+ internalTrafficList.push(updatedInit.proxy.assets)
38
+ }
39
+ if (updatedInit.proxy.beacon) internalTrafficList.push(updatedInit.proxy.beacon)
40
+ }
41
+
29
42
  runtime.denyList = [
30
- ...(updatedInit.ajax?.deny_list || []),
31
- ...(updatedInit.ajax?.block_internal
32
- ? [
33
- info.beacon,
34
- info.errorBeacon
35
- ]
36
- : [])
43
+ ...(updatedInit.ajax.deny_list || []),
44
+ ...(updatedInit.ajax.block_internal ? internalTrafficList : [])
37
45
  ]
38
46
  setRuntime(agentIdentifier, runtime)
39
47
 
@@ -0,0 +1,6 @@
1
+ // Set the default CDN or remote for fetching the assets; NPM shouldn't change this var.
2
+
3
+ export const redefinePublicPath = (url) => {
4
+ // There's no URL validation here, so caller should check arg if need be.
5
+ __webpack_public_path__ = url // eslint-disable-line
6
+ }
@@ -0,0 +1,4 @@
1
+
2
+ export const redefinePublicPath = () => {
3
+ // We don't support setting public path in webpack via NPM build.
4
+ }
@@ -1,13 +0,0 @@
1
- "use strict";
2
-
3
- Object.defineProperty(exports, "__esModule", {
4
- value: true
5
- });
6
- exports.paintMetrics = void 0;
7
- /*
8
- * Copyright 2020 New Relic Corporation. All rights reserved.
9
- * SPDX-License-Identifier: Apache-2.0
10
- */
11
-
12
- const paintMetrics = {};
13
- exports.paintMetrics = paintMetrics;
@@ -1,75 +0,0 @@
1
- "use strict";
2
-
3
- Object.defineProperty(exports, "__esModule", {
4
- value: true
5
- });
6
- exports.onLongTask = void 0;
7
- var _eol = require("../../common/unload/eol");
8
- /**
9
- * Calls the `onReport` function for every entry reported by the PerformanceLongTaskTiming API.
10
- * The reported value is a `DOMHighResTimeStamp`.
11
- *
12
- * The callback is always called when the page's visibility state changes to hidden.
13
- * As a result, the `onReport` function might be called multiple times during the same page load.
14
- *
15
- * @param {Function} onReport - callback that accepts a `metric` object as the single parameter
16
- */
17
- const onLongTask = onReport => {
18
- const handleEntries = entries => {
19
- entries.forEach(entry => {
20
- const metric = {
21
- name: 'LT',
22
- value: entry.duration,
23
- info: {
24
- // this property deviates from CWV std interface but will hold the custom context to send to NRDB
25
- ltFrame: entry.name,
26
- // MDN: the browsing context or frame that can be attributed to the long task
27
- ltStart: entry.startTime,
28
- // MDN: a double representing the time (millisec) when the task started
29
- ltCtr: entry.attribution[0].containerType // MDN: type of frame container: 'iframe', 'embed', or 'object' ... but this can also be 'window'
30
- }
31
- };
32
-
33
- if (metric.info.ltCtr !== 'window') {
34
- // the following properties are only of relevance & appended for html elements
35
- Object.assign(metric.info, {
36
- ltCtrSrc: entry.attribution[0].containerSrc,
37
- // MDN: container's 'src' attribute
38
- ltCtrId: entry.attribution[0].containerId,
39
- // MDN: container's 'id' attribute
40
- ltCtrName: entry.attribution[0].containerName // MDN: container's 'name' attribute
41
- });
42
- }
43
-
44
- onReport(metric); // report every long task observed unconditionally
45
- });
46
- };
47
-
48
- let observer;
49
- try {
50
- if (PerformanceObserver.supportedEntryTypes.includes('longtask')) {
51
- observer = new PerformanceObserver(list => {
52
- // Delay by a microtask to workaround a bug in Safari where the
53
- // callback is invoked immediately, rather than in a separate task.
54
- // See: https://github.com/GoogleChrome/web-vitals/issues/277
55
- Promise.resolve().then(() => {
56
- handleEntries(list.getEntries());
57
- });
58
- });
59
- observer.observe({
60
- type: 'longtask',
61
- buffered: true
62
- });
63
- }
64
- } catch (e) {
65
- // Do nothing.
66
- }
67
- if (observer) {
68
- (0, _eol.subscribeToEOL)(() => {
69
- handleEntries(observer.takeRecords());
70
- }, true); // this bool is a temp arg under staged BFCache work that runs the func under the new page session logic -- tb removed w/ the feature flag later
71
-
72
- /* No work needed on BFCache restore for long task. */
73
- }
74
- };
75
- exports.onLongTask = onLongTask;
@@ -1,6 +0,0 @@
1
- /*
2
- * Copyright 2020 New Relic Corporation. All rights reserved.
3
- * SPDX-License-Identifier: Apache-2.0
4
- */
5
-
6
- export const paintMetrics = {};
@@ -1,69 +0,0 @@
1
- import { subscribeToEOL } from '../../common/unload/eol';
2
-
3
- /**
4
- * Calls the `onReport` function for every entry reported by the PerformanceLongTaskTiming API.
5
- * The reported value is a `DOMHighResTimeStamp`.
6
- *
7
- * The callback is always called when the page's visibility state changes to hidden.
8
- * As a result, the `onReport` function might be called multiple times during the same page load.
9
- *
10
- * @param {Function} onReport - callback that accepts a `metric` object as the single parameter
11
- */
12
- export const onLongTask = onReport => {
13
- const handleEntries = entries => {
14
- entries.forEach(entry => {
15
- const metric = {
16
- name: 'LT',
17
- value: entry.duration,
18
- info: {
19
- // this property deviates from CWV std interface but will hold the custom context to send to NRDB
20
- ltFrame: entry.name,
21
- // MDN: the browsing context or frame that can be attributed to the long task
22
- ltStart: entry.startTime,
23
- // MDN: a double representing the time (millisec) when the task started
24
- ltCtr: entry.attribution[0].containerType // MDN: type of frame container: 'iframe', 'embed', or 'object' ... but this can also be 'window'
25
- }
26
- };
27
-
28
- if (metric.info.ltCtr !== 'window') {
29
- // the following properties are only of relevance & appended for html elements
30
- Object.assign(metric.info, {
31
- ltCtrSrc: entry.attribution[0].containerSrc,
32
- // MDN: container's 'src' attribute
33
- ltCtrId: entry.attribution[0].containerId,
34
- // MDN: container's 'id' attribute
35
- ltCtrName: entry.attribution[0].containerName // MDN: container's 'name' attribute
36
- });
37
- }
38
-
39
- onReport(metric); // report every long task observed unconditionally
40
- });
41
- };
42
-
43
- let observer;
44
- try {
45
- if (PerformanceObserver.supportedEntryTypes.includes('longtask')) {
46
- observer = new PerformanceObserver(list => {
47
- // Delay by a microtask to workaround a bug in Safari where the
48
- // callback is invoked immediately, rather than in a separate task.
49
- // See: https://github.com/GoogleChrome/web-vitals/issues/277
50
- Promise.resolve().then(() => {
51
- handleEntries(list.getEntries());
52
- });
53
- });
54
- observer.observe({
55
- type: 'longtask',
56
- buffered: true
57
- });
58
- }
59
- } catch (e) {
60
- // Do nothing.
61
- }
62
- if (observer) {
63
- subscribeToEOL(() => {
64
- handleEntries(observer.takeRecords());
65
- }, true); // this bool is a temp arg under staged BFCache work that runs the func under the new page session logic -- tb removed w/ the feature flag later
66
-
67
- /* No work needed on BFCache restore for long task. */
68
- }
69
- };
@@ -1,2 +0,0 @@
1
- export const paintMetrics: {};
2
- //# sourceMappingURL=paint-metrics.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"paint-metrics.d.ts","sourceRoot":"","sources":["../../../../src/common/metrics/paint-metrics.js"],"names":[],"mappings":"AAKA,8BAA8B"}
@@ -1,2 +0,0 @@
1
- export function onFirstPaint(onReport: Function): void;
2
- //# sourceMappingURL=first-paint.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"first-paint.d.ts","sourceRoot":"","sources":["../../../../src/features/page_view_timing/first-paint.js"],"names":[],"mappings":"AAMO,uDAkCN"}
@@ -1,2 +0,0 @@
1
- export function onLongTask(onReport: Function): void;
2
- //# sourceMappingURL=long-tasks.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"long-tasks.d.ts","sourceRoot":"","sources":["../../../../src/features/page_view_timing/long-tasks.js"],"names":[],"mappings":"AAWO,qDAgDN"}
@@ -1,6 +0,0 @@
1
- /*
2
- * Copyright 2020 New Relic Corporation. All rights reserved.
3
- * SPDX-License-Identifier: Apache-2.0
4
- */
5
-
6
- export const paintMetrics = {}
@@ -1,60 +0,0 @@
1
- import { subscribeToEOL } from '../../common/unload/eol'
2
-
3
- /**
4
- * Calls the `onReport` function for every entry reported by the PerformanceLongTaskTiming API.
5
- * The reported value is a `DOMHighResTimeStamp`.
6
- *
7
- * The callback is always called when the page's visibility state changes to hidden.
8
- * As a result, the `onReport` function might be called multiple times during the same page load.
9
- *
10
- * @param {Function} onReport - callback that accepts a `metric` object as the single parameter
11
- */
12
- export const onLongTask = (onReport) => {
13
- const handleEntries = (entries) => {
14
- entries.forEach(entry => {
15
- const metric = {
16
- name: 'LT',
17
- value: entry.duration,
18
- info: { // this property deviates from CWV std interface but will hold the custom context to send to NRDB
19
- ltFrame: entry.name, // MDN: the browsing context or frame that can be attributed to the long task
20
- ltStart: entry.startTime, // MDN: a double representing the time (millisec) when the task started
21
- ltCtr: entry.attribution[0].containerType // MDN: type of frame container: 'iframe', 'embed', or 'object' ... but this can also be 'window'
22
- }
23
- }
24
- if (metric.info.ltCtr !== 'window') { // the following properties are only of relevance & appended for html elements
25
- Object.assign(metric.info, {
26
- ltCtrSrc: entry.attribution[0].containerSrc, // MDN: container's 'src' attribute
27
- ltCtrId: entry.attribution[0].containerId, // MDN: container's 'id' attribute
28
- ltCtrName: entry.attribution[0].containerName // MDN: container's 'name' attribute
29
- })
30
- }
31
-
32
- onReport(metric) // report every long task observed unconditionally
33
- })
34
- }
35
-
36
- let observer
37
- try {
38
- if (PerformanceObserver.supportedEntryTypes.includes('longtask')) {
39
- observer = new PerformanceObserver((list) => {
40
- // Delay by a microtask to workaround a bug in Safari where the
41
- // callback is invoked immediately, rather than in a separate task.
42
- // See: https://github.com/GoogleChrome/web-vitals/issues/277
43
- Promise.resolve().then(() => {
44
- handleEntries(list.getEntries())
45
- })
46
- })
47
- observer.observe({ type: 'longtask', buffered: true })
48
- }
49
- } catch (e) {
50
- // Do nothing.
51
- }
52
-
53
- if (observer) {
54
- subscribeToEOL(() => {
55
- handleEntries(observer.takeRecords())
56
- }, true) // this bool is a temp arg under staged BFCache work that runs the func under the new page session logic -- tb removed w/ the feature flag later
57
-
58
- /* No work needed on BFCache restore for long task. */
59
- }
60
- }