@newrelic/browser-agent 1.239.1 → 1.241.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 +25 -17
- 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 +17 -7
- 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 +36 -147
- package/dist/cjs/features/page_view_timing/instrument/index.js +0 -3
- package/dist/cjs/features/session_replay/aggregate/index.js +81 -35
- 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/browser-agent.js +2 -1
- 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 +25 -17
- 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 +18 -8
- 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 +37 -148
- package/dist/esm/features/page_view_timing/instrument/index.js +0 -3
- package/dist/esm/features/session_replay/aggregate/index.js +81 -35
- 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/browser-agent.js +2 -1
- 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_replay/aggregate/index.d.ts +16 -4
- package/dist/types/features/session_replay/aggregate/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/browser-agent.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 +2 -3
- package/src/cdn/pro.js +2 -0
- package/src/cdn/spa.js +2 -0
- package/src/common/config/state/init.js +21 -17
- package/src/common/constants/runtime.js +7 -3
- package/src/common/constants/runtime.test.js +8 -0
- package/src/common/harvest/harvest-scheduler.test.js +2 -2
- 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 +11 -4
- 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 +31 -108
- package/src/features/page_view_timing/instrument/index.js +0 -3
- package/src/features/session_replay/aggregate/index.component-test.js +10 -10
- package/src/features/session_replay/aggregate/index.js +62 -29
- 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/browser-agent.js +3 -1
- 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
|
@@ -20,6 +20,7 @@ import { AggregateBase } from '../../utils/aggregate-base'
|
|
|
20
20
|
import { sharedChannel } from '../../../common/constants/shared-channel'
|
|
21
21
|
import { obj as encodeObj } from '../../../common/url/encode'
|
|
22
22
|
import { warn } from '../../../common/util/console'
|
|
23
|
+
import { globalScope } from '../../../common/constants/runtime'
|
|
23
24
|
|
|
24
25
|
// would be better to get this dynamically in some way
|
|
25
26
|
export const RRWEB_VERSION = '2.0.0-alpha.8'
|
|
@@ -63,15 +64,22 @@ export class Aggregate extends AggregateBase {
|
|
|
63
64
|
* -- When visibility changes from "hidden" -> "visible", it must capture a full snapshot for the replay to work correctly across tabs
|
|
64
65
|
*/
|
|
65
66
|
this.hasSnapshot = false
|
|
67
|
+
/** Payload metadata -- Should indicate that the payload being sent has a meta node. The meta node should always precede a snapshot node. */
|
|
68
|
+
this.hasMeta = false
|
|
66
69
|
/** Payload metadata -- Should indicate that the payload being sent contains an error. Used for query/filter purposes in UI */
|
|
67
70
|
this.hasError = false
|
|
68
71
|
|
|
69
|
-
/** Payload metadata -- Should indicate when a replay blob started recording. Resets each time a harvest occurs.
|
|
70
|
-
|
|
72
|
+
/** Payload metadata -- Should indicate when a replay blob started recording. Resets each time a harvest occurs.
|
|
73
|
+
* cycle timestamps are used as fallbacks if event timestamps cannot be used
|
|
74
|
+
*/
|
|
75
|
+
this.timestamp = { event: { first: undefined, last: undefined }, cycle: { first: undefined, last: undefined } }
|
|
71
76
|
|
|
72
77
|
/** A value which increments with every new mutation node reported. Resets after a harvest is sent */
|
|
73
78
|
this.payloadBytesEstimation = 0
|
|
74
79
|
|
|
80
|
+
/** Hold on to the last meta node, so that it can be re-inserted if the meta and snapshot nodes are broken up due to harvesting */
|
|
81
|
+
this.lastMeta = undefined
|
|
82
|
+
|
|
75
83
|
const shouldSetup = (
|
|
76
84
|
getConfigurationValue(agentIdentifier, 'privacy.cookies_enabled') === true &&
|
|
77
85
|
getConfigurationValue(agentIdentifier, 'session_trace.enabled') === true
|
|
@@ -123,8 +131,8 @@ export class Aggregate extends AggregateBase {
|
|
|
123
131
|
|
|
124
132
|
this.waitForFlags(['sr']).then(([flagOn]) => this.initializeRecording(
|
|
125
133
|
flagOn,
|
|
126
|
-
Math.random() < getConfigurationValue(this.agentIdentifier, 'session_replay.
|
|
127
|
-
Math.random() < getConfigurationValue(this.agentIdentifier, 'session_replay.
|
|
134
|
+
(Math.random() * 100) < getConfigurationValue(this.agentIdentifier, 'session_replay.error_sampling_rate'),
|
|
135
|
+
(Math.random() * 100) < getConfigurationValue(this.agentIdentifier, 'session_replay.sampling_rate')
|
|
128
136
|
)).then(() => sharedChannel.onReplayReady(this.mode)) // notify watchers that replay started with the mode
|
|
129
137
|
|
|
130
138
|
this.drain()
|
|
@@ -164,6 +172,13 @@ export class Aggregate extends AggregateBase {
|
|
|
164
172
|
this.mode = MODE.FULL
|
|
165
173
|
}
|
|
166
174
|
|
|
175
|
+
try {
|
|
176
|
+
// Do not change the webpackChunkName or it will break the webpack nrba-chunking plugin
|
|
177
|
+
recorder = (await import(/* webpackChunkName: "recorder" */'rrweb')).record
|
|
178
|
+
} catch (err) {
|
|
179
|
+
return this.abort()
|
|
180
|
+
}
|
|
181
|
+
|
|
167
182
|
// FULL mode records AND reports from the beginning, while ERROR mode only records (but does not report).
|
|
168
183
|
// ERROR mode will do this until an error is thrown, and then switch into FULL mode.
|
|
169
184
|
// If an error happened in ERROR mode before we've gotten to this stage, it will have already set the mode to FULL
|
|
@@ -172,13 +187,6 @@ export class Aggregate extends AggregateBase {
|
|
|
172
187
|
this.scheduler.startTimer(this.harvestTimeSeconds)
|
|
173
188
|
}
|
|
174
189
|
|
|
175
|
-
try {
|
|
176
|
-
// Do not change the webpackChunkName or it will break the webpack nrba-chunking plugin
|
|
177
|
-
recorder = (await import(/* webpackChunkName: "recorder" */'rrweb')).record
|
|
178
|
-
} catch (err) {
|
|
179
|
-
return this.abort()
|
|
180
|
-
}
|
|
181
|
-
|
|
182
190
|
try {
|
|
183
191
|
// Do not change the webpackChunkName or it will break the webpack nrba-chunking plugin
|
|
184
192
|
const { gzipSync, strToU8 } = await import(/* webpackChunkName: "compressor" */'fflate')
|
|
@@ -213,6 +221,8 @@ export class Aggregate extends AggregateBase {
|
|
|
213
221
|
getHarvestContents () {
|
|
214
222
|
const agentRuntime = getRuntime(this.agentIdentifier)
|
|
215
223
|
const info = getInfo(this.agentIdentifier)
|
|
224
|
+
const firstTimestamp = this.timestamp.event.first || this.timestamp.cycle.first
|
|
225
|
+
const lastTimestamp = this.timestamp.event.last || this.timestamp.cycle.last
|
|
216
226
|
return {
|
|
217
227
|
qs: {
|
|
218
228
|
browser_monitoring_key: info.licenseKey,
|
|
@@ -221,11 +231,12 @@ export class Aggregate extends AggregateBase {
|
|
|
221
231
|
protocol_version: '0',
|
|
222
232
|
attributes: encodeObj({
|
|
223
233
|
...(this.shouldCompress && { content_encoding: 'gzip' }),
|
|
224
|
-
'replay.firstTimestamp':
|
|
225
|
-
'replay.lastTimestamp':
|
|
226
|
-
'replay.durationMs':
|
|
234
|
+
'replay.firstTimestamp': firstTimestamp,
|
|
235
|
+
'replay.lastTimestamp': lastTimestamp,
|
|
236
|
+
'replay.durationMs': lastTimestamp - firstTimestamp,
|
|
227
237
|
agentVersion: agentRuntime.version,
|
|
228
238
|
session: agentRuntime.session.state.value,
|
|
239
|
+
hasMeta: this.hasMeta,
|
|
229
240
|
hasSnapshot: this.hasSnapshot,
|
|
230
241
|
hasError: this.hasError,
|
|
231
242
|
isFirstChunk: this.isFirstChunk,
|
|
@@ -251,6 +262,7 @@ export class Aggregate extends AggregateBase {
|
|
|
251
262
|
this.events = []
|
|
252
263
|
this.isFirstChunk = false
|
|
253
264
|
this.hasSnapshot = false
|
|
265
|
+
this.hasMeta = false
|
|
254
266
|
this.hasError = false
|
|
255
267
|
this.payloadBytesEstimation = 0
|
|
256
268
|
this.clearTimestamps()
|
|
@@ -262,19 +274,22 @@ export class Aggregate extends AggregateBase {
|
|
|
262
274
|
warn('Recording library was never imported')
|
|
263
275
|
return this.abort()
|
|
264
276
|
}
|
|
277
|
+
this.clearTimestamps()
|
|
278
|
+
// set the fallbacks as early as possible
|
|
279
|
+
this.setTimestamps()
|
|
265
280
|
this.recording = true
|
|
266
|
-
const {
|
|
281
|
+
const { block_class, ignore_class, mask_text_class, block_selector, mask_input_options, mask_text_selector, mask_all_inputs } = getConfigurationValue(this.agentIdentifier, 'session_replay')
|
|
267
282
|
// set up rrweb configurations for maximum privacy --
|
|
268
283
|
// https://newrelic.atlassian.net/wiki/spaces/O11Y/pages/2792293280/2023+02+28+Browser+-+Session+Replay#Configuration-options
|
|
269
284
|
const stop = recorder({
|
|
270
285
|
emit: this.store.bind(this),
|
|
271
|
-
blockClass,
|
|
272
|
-
ignoreClass,
|
|
273
|
-
maskTextClass,
|
|
274
|
-
blockSelector,
|
|
275
|
-
maskInputOptions,
|
|
276
|
-
maskTextSelector,
|
|
277
|
-
maskAllInputs,
|
|
286
|
+
blockClass: block_class,
|
|
287
|
+
ignoreClass: ignore_class,
|
|
288
|
+
maskTextClass: mask_text_class,
|
|
289
|
+
blockSelector: block_selector,
|
|
290
|
+
maskInputOptions: mask_input_options,
|
|
291
|
+
maskTextSelector: mask_text_selector,
|
|
292
|
+
maskAllInputs: mask_all_inputs,
|
|
278
293
|
checkoutEveryNms: CHECKOUT_MS[this.mode]
|
|
279
294
|
})
|
|
280
295
|
|
|
@@ -286,6 +301,7 @@ export class Aggregate extends AggregateBase {
|
|
|
286
301
|
|
|
287
302
|
/** Store a payload in the buffer (this.events). This should be the callback to the recording lib noticing a mutation */
|
|
288
303
|
store (event, isCheckout) {
|
|
304
|
+
this.setTimestamps(event)
|
|
289
305
|
if (this.blocked) return
|
|
290
306
|
const eventBytes = stringify(event).length
|
|
291
307
|
/** The estimated size of the payload after compression */
|
|
@@ -303,8 +319,21 @@ export class Aggregate extends AggregateBase {
|
|
|
303
319
|
this.clearBuffer()
|
|
304
320
|
}
|
|
305
321
|
|
|
306
|
-
|
|
307
|
-
if (event.type ===
|
|
322
|
+
// meta event
|
|
323
|
+
if (event.type === 4) {
|
|
324
|
+
this.hasMeta = true
|
|
325
|
+
this.lastMeta = event
|
|
326
|
+
}
|
|
327
|
+
// snapshot event
|
|
328
|
+
if (event.type === 2) {
|
|
329
|
+
this.hasSnapshot = true
|
|
330
|
+
// small chance that the meta event got separated from its matching snapshot across payload harvests
|
|
331
|
+
// it needs to precede the snapshot, so shove it in first.
|
|
332
|
+
if (!this.hasMeta) {
|
|
333
|
+
this.events.push(this.lastMeta)
|
|
334
|
+
this.hasMeta = true
|
|
335
|
+
}
|
|
336
|
+
}
|
|
308
337
|
|
|
309
338
|
this.events.push(event)
|
|
310
339
|
this.payloadBytesEstimation += eventBytes
|
|
@@ -323,14 +352,18 @@ export class Aggregate extends AggregateBase {
|
|
|
323
352
|
recorder.takeFullSnapshot()
|
|
324
353
|
}
|
|
325
354
|
|
|
326
|
-
setTimestamps (
|
|
327
|
-
if
|
|
328
|
-
|
|
329
|
-
this.timestamp.
|
|
355
|
+
setTimestamps (event) {
|
|
356
|
+
// fallbacks if timestamps cannot be derived from rrweb events
|
|
357
|
+
this.timestamp.cycle.last = getRuntime(this.agentIdentifier).offset + globalScope.performance.now()
|
|
358
|
+
if (!this.timestamp.cycle.first) this.timestamp.cycle.first = this.timestamp.cycle.last
|
|
359
|
+
// timestamps based on rrweb events
|
|
360
|
+
if (!event || !event.timestamp) return
|
|
361
|
+
if (!this.timestamp.event.first) this.timestamp.event.first = event.timestamp
|
|
362
|
+
this.timestamp.event.last = event.timestamp
|
|
330
363
|
}
|
|
331
364
|
|
|
332
365
|
clearTimestamps () {
|
|
333
|
-
this.timestamp = { first: undefined, last: undefined }
|
|
366
|
+
this.timestamp = { event: { first: undefined, last: undefined }, cycle: { first: undefined, last: undefined } }
|
|
334
367
|
}
|
|
335
368
|
|
|
336
369
|
/** Estimate the payload size */
|
|
@@ -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 + '&'
|
|
@@ -8,6 +8,7 @@ import { Instrument as InstrumentXhr } from '../features/ajax/instrument'
|
|
|
8
8
|
import { Instrument as InstrumentSessionTrace } from '../features/session_trace/instrument'
|
|
9
9
|
import { Instrument as InstrumentSpa } from '../features/spa/instrument'
|
|
10
10
|
import { Instrument as InstrumentPageAction } from '../features/page_action/instrument'
|
|
11
|
+
import { Instrument as InstrumentSessionReplay } from '../features/session_replay/instrument'
|
|
11
12
|
|
|
12
13
|
/**
|
|
13
14
|
* An agent class with all feature modules available. Features may be disabled and enabled via runtime configuration.
|
|
@@ -25,7 +26,8 @@ export class BrowserAgent extends Agent {
|
|
|
25
26
|
InstrumentMetrics,
|
|
26
27
|
InstrumentPageAction,
|
|
27
28
|
InstrumentErrors,
|
|
28
|
-
InstrumentSpa
|
|
29
|
+
InstrumentSpa,
|
|
30
|
+
InstrumentSessionReplay
|
|
29
31
|
],
|
|
30
32
|
loaderType: 'browser-agent'
|
|
31
33
|
})
|
|
@@ -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
|
-
}
|