@newrelic/browser-agent 1.239.0 → 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.
- package/README.md +4 -0
- package/dist/cjs/cdn/pro.js +3 -2
- package/dist/cjs/cdn/spa.js +4 -3
- package/dist/cjs/common/config/state/init.js +6 -0
- package/dist/cjs/common/constants/env.cdn.js +1 -1
- package/dist/cjs/common/constants/env.npm.js +1 -1
- package/dist/cjs/common/constants/runtime.js +9 -5
- package/dist/cjs/common/harvest/harvest.js +5 -3
- package/dist/cjs/common/vitals/constants.js +17 -0
- package/dist/cjs/common/vitals/cumulative-layout-shift.js +27 -0
- package/dist/cjs/common/vitals/first-contentful-paint.js +49 -0
- package/dist/cjs/common/vitals/first-input-delay.js +32 -0
- package/dist/{esm/features/page_view_timing → cjs/common/vitals}/first-paint.js +19 -17
- package/dist/cjs/common/vitals/interaction-to-next-paint.js +29 -0
- package/dist/cjs/common/vitals/largest-contentful-paint.js +41 -0
- package/dist/cjs/common/vitals/long-task.js +64 -0
- package/dist/cjs/common/vitals/time-to-first-byte.js +36 -0
- package/dist/cjs/common/vitals/vital-metric.js +71 -0
- package/dist/cjs/features/ajax/aggregate/index.js +4 -1
- package/dist/cjs/features/metrics/aggregate/index.js +7 -0
- package/dist/cjs/features/page_view_event/aggregate/index.js +18 -40
- package/dist/cjs/features/page_view_event/constants.js +2 -8
- package/dist/cjs/features/page_view_event/instrument/index.js +0 -22
- package/dist/cjs/features/page_view_timing/aggregate/index.js +27 -138
- package/dist/cjs/features/page_view_timing/instrument/index.js +0 -3
- package/dist/cjs/features/session_trace/aggregate/index.js +13 -1
- package/dist/cjs/features/spa/aggregate/index.js +4 -3
- package/dist/cjs/loaders/agent.js +3 -0
- package/dist/cjs/loaders/api/api.js +2 -0
- package/dist/cjs/loaders/api/apiAsync.js +4 -2
- package/dist/cjs/loaders/configure/configure.js +13 -1
- package/dist/cjs/loaders/configure/public-path.js +13 -0
- package/dist/cjs/loaders/configure/public-path.npm.js +10 -0
- package/dist/esm/cdn/pro.js +2 -1
- package/dist/esm/cdn/spa.js +2 -1
- package/dist/esm/common/config/state/init.js +6 -0
- package/dist/esm/common/constants/env.cdn.js +1 -1
- package/dist/esm/common/constants/env.npm.js +1 -1
- package/dist/esm/common/constants/runtime.js +5 -3
- package/dist/esm/common/harvest/harvest.js +6 -4
- package/dist/esm/common/vitals/constants.js +10 -0
- package/dist/esm/common/vitals/cumulative-layout-shift.js +20 -0
- package/dist/esm/common/vitals/first-contentful-paint.js +41 -0
- package/dist/esm/common/vitals/first-input-delay.js +25 -0
- package/dist/{cjs/features/page_view_timing → esm/common/vitals}/first-paint.js +12 -24
- package/dist/esm/common/vitals/interaction-to-next-paint.js +22 -0
- package/dist/esm/common/vitals/largest-contentful-paint.js +34 -0
- package/dist/esm/common/vitals/long-task.js +57 -0
- package/dist/esm/common/vitals/time-to-first-byte.js +29 -0
- package/dist/esm/common/vitals/vital-metric.js +64 -0
- package/dist/esm/features/ajax/aggregate/index.js +4 -1
- package/dist/esm/features/metrics/aggregate/index.js +8 -1
- package/dist/esm/features/page_view_event/aggregate/index.js +20 -42
- package/dist/esm/features/page_view_event/constants.js +1 -4
- package/dist/esm/features/page_view_event/instrument/index.js +0 -22
- package/dist/esm/features/page_view_timing/aggregate/index.js +28 -139
- package/dist/esm/features/page_view_timing/instrument/index.js +0 -3
- package/dist/esm/features/session_trace/aggregate/index.js +13 -1
- package/dist/esm/features/spa/aggregate/index.js +4 -3
- package/dist/esm/loaders/agent.js +2 -0
- package/dist/esm/loaders/api/api.js +2 -0
- package/dist/esm/loaders/api/apiAsync.js +5 -3
- package/dist/esm/loaders/configure/configure.js +13 -1
- package/dist/esm/loaders/configure/public-path.js +6 -0
- package/dist/esm/loaders/configure/public-path.npm.js +3 -0
- package/dist/types/common/config/state/init.d.ts.map +1 -1
- package/dist/types/common/constants/runtime.d.ts +3 -1
- package/dist/types/common/constants/runtime.d.ts.map +1 -1
- package/dist/types/common/harvest/harvest.d.ts +0 -1
- package/dist/types/common/harvest/harvest.d.ts.map +1 -1
- package/dist/types/common/vitals/constants.d.ts +11 -0
- package/dist/types/common/vitals/constants.d.ts.map +1 -0
- package/dist/types/common/vitals/cumulative-layout-shift.d.ts +3 -0
- package/dist/types/common/vitals/cumulative-layout-shift.d.ts.map +1 -0
- package/dist/types/common/vitals/first-contentful-paint.d.ts +3 -0
- package/dist/types/common/vitals/first-contentful-paint.d.ts.map +1 -0
- package/dist/types/common/vitals/first-input-delay.d.ts +3 -0
- package/dist/types/common/vitals/first-input-delay.d.ts.map +1 -0
- package/dist/types/common/vitals/first-paint.d.ts +3 -0
- package/dist/types/common/vitals/first-paint.d.ts.map +1 -0
- package/dist/types/common/vitals/interaction-to-next-paint.d.ts +3 -0
- package/dist/types/common/vitals/interaction-to-next-paint.d.ts.map +1 -0
- package/dist/types/common/vitals/largest-contentful-paint.d.ts +3 -0
- package/dist/types/common/vitals/largest-contentful-paint.d.ts.map +1 -0
- package/dist/types/common/vitals/long-task.d.ts +3 -0
- package/dist/types/common/vitals/long-task.d.ts.map +1 -0
- package/dist/types/common/vitals/time-to-first-byte.d.ts +3 -0
- package/dist/types/common/vitals/time-to-first-byte.d.ts.map +1 -0
- package/dist/types/common/vitals/vital-metric.d.ts +18 -0
- package/dist/types/common/vitals/vital-metric.d.ts.map +1 -0
- package/dist/types/features/ajax/aggregate/index.d.ts.map +1 -1
- package/dist/types/features/metrics/aggregate/index.d.ts.map +1 -1
- package/dist/types/features/page_view_event/aggregate/index.d.ts +3 -2
- package/dist/types/features/page_view_event/aggregate/index.d.ts.map +1 -1
- package/dist/types/features/page_view_event/constants.d.ts +0 -3
- package/dist/types/features/page_view_event/constants.d.ts.map +1 -1
- package/dist/types/features/page_view_event/instrument/index.d.ts.map +1 -1
- package/dist/types/features/page_view_timing/aggregate/index.d.ts +1 -3
- package/dist/types/features/page_view_timing/aggregate/index.d.ts.map +1 -1
- package/dist/types/features/page_view_timing/instrument/index.d.ts.map +1 -1
- package/dist/types/features/session_trace/aggregate/index.d.ts +9 -1
- package/dist/types/features/session_trace/aggregate/index.d.ts.map +1 -1
- package/dist/types/features/spa/aggregate/index.d.ts.map +1 -1
- package/dist/types/loaders/agent.d.ts.map +1 -1
- package/dist/types/loaders/api/api.d.ts.map +1 -1
- package/dist/types/loaders/api/apiAsync.d.ts.map +1 -1
- package/dist/types/loaders/configure/configure.d.ts.map +1 -1
- package/dist/types/loaders/configure/public-path.d.ts +2 -0
- package/dist/types/loaders/configure/public-path.d.ts.map +1 -0
- package/dist/types/loaders/configure/public-path.npm.d.ts +2 -0
- package/dist/types/loaders/configure/public-path.npm.d.ts.map +1 -0
- package/package.json +1 -1
- package/src/cdn/pro.js +2 -0
- package/src/cdn/spa.js +2 -0
- package/src/common/config/state/init.js +4 -0
- package/src/common/constants/runtime.js +7 -3
- package/src/common/constants/runtime.test.js +8 -0
- package/src/common/harvest/harvest.js +6 -4
- package/src/common/harvest/harvest.test.js +17 -0
- package/src/common/vitals/__mocks__/web-vitals.js +19 -0
- package/src/common/vitals/constants.js +10 -0
- package/src/common/vitals/cumulative-layout-shift.js +13 -0
- package/src/common/vitals/cumulative-layout-shift.test.js +71 -0
- package/src/common/vitals/first-contentful-paint.js +31 -0
- package/src/common/vitals/first-contentful-paint.test.js +124 -0
- package/src/common/vitals/first-input-delay.js +20 -0
- package/src/common/vitals/first-input-delay.test.js +88 -0
- package/src/{features/page_view_timing → common/vitals}/first-paint.js +11 -17
- package/src/common/vitals/first-paint.test.js +127 -0
- package/src/common/vitals/interaction-to-next-paint.js +13 -0
- package/src/common/vitals/interaction-to-next-paint.test.js +74 -0
- package/src/common/vitals/largest-contentful-paint.js +29 -0
- package/src/common/vitals/largest-contentful-paint.test.js +94 -0
- package/src/common/vitals/long-task.js +52 -0
- package/src/common/vitals/long-task.test.js +122 -0
- package/src/common/vitals/time-to-first-byte.js +21 -0
- package/src/common/vitals/time-to-first-byte.test.js +147 -0
- package/src/common/vitals/vital-metric.js +60 -0
- package/src/common/vitals/vital-metric.test.js +171 -0
- package/src/features/ajax/aggregate/index.js +5 -1
- package/src/features/metrics/aggregate/index.js +6 -1
- package/src/features/page_view_event/aggregate/index.js +20 -43
- package/src/features/page_view_event/constants.js +0 -3
- package/src/features/page_view_event/instrument/index.js +0 -21
- package/src/features/page_view_timing/aggregate/index.component-test.js +86 -0
- package/src/features/page_view_timing/aggregate/index.js +24 -102
- package/src/features/page_view_timing/instrument/index.js +0 -3
- package/src/features/session_trace/aggregate/index.js +15 -1
- package/src/features/spa/aggregate/index.js +4 -3
- package/src/loaders/agent.js +2 -0
- package/src/loaders/api/api.js +2 -0
- package/src/loaders/api/apiAsync.js +5 -4
- package/src/loaders/configure/configure.js +15 -7
- package/src/loaders/configure/public-path.js +6 -0
- package/src/loaders/configure/public-path.npm.js +4 -0
- package/dist/cjs/common/metrics/paint-metrics.js +0 -13
- package/dist/cjs/features/page_view_timing/long-tasks.js +0 -75
- package/dist/esm/common/metrics/paint-metrics.js +0 -6
- package/dist/esm/features/page_view_timing/long-tasks.js +0 -69
- package/dist/types/common/metrics/paint-metrics.d.ts +0 -2
- package/dist/types/common/metrics/paint-metrics.d.ts.map +0 -1
- package/dist/types/features/page_view_timing/first-paint.d.ts +0 -2
- package/dist/types/features/page_view_timing/first-paint.d.ts.map +0 -1
- package/dist/types/features/page_view_timing/long-tasks.d.ts +0 -2
- package/dist/types/features/page_view_timing/long-tasks.d.ts.map +0 -1
- package/src/common/metrics/paint-metrics.js +0 -6
- 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
|
|
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
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
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
|
-
|
|
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 (
|
|
183
|
-
attrs.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: {
|
|
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 =
|
|
718
|
-
interaction.root.attrs.firstContentfulPaint =
|
|
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)
|
package/src/loaders/agent.js
CHANGED
package/src/loaders/api/api.js
CHANGED
|
@@ -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 {
|
|
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
|
-
|
|
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
|
|
31
|
-
...(updatedInit.ajax
|
|
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
|
+
}
|
|
@@ -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,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 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"paint-metrics.d.ts","sourceRoot":"","sources":["../../../../src/common/metrics/paint-metrics.js"],"names":[],"mappings":"AAKA,8BAA8B"}
|
|
@@ -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 +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,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
|
-
}
|