@newrelic/browser-agent 1.255.0 → 1.256.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +22 -0
- package/dist/cjs/common/constants/env.cdn.js +2 -2
- package/dist/cjs/common/constants/env.npm.js +2 -2
- package/dist/cjs/common/constants/runtime.js +1 -1
- package/dist/cjs/common/session/session-entity.js +2 -1
- package/dist/cjs/common/timer/interaction-timer.js +16 -2
- package/dist/cjs/common/timing/time-keeper.js +1 -2
- package/dist/cjs/features/jserrors/aggregate/index.js +16 -6
- package/dist/cjs/features/jserrors/instrument/index.js +8 -3
- package/dist/cjs/features/session_replay/aggregate/index.js +48 -29
- package/dist/cjs/features/session_replay/constants.js +2 -1
- package/dist/cjs/features/session_replay/instrument/index.js +9 -2
- package/dist/cjs/features/session_replay/shared/recorder-events.js +1 -9
- package/dist/cjs/features/session_replay/shared/recorder.js +22 -50
- package/dist/cjs/features/session_replay/shared/utils.js +12 -0
- package/dist/cjs/loaders/api/api.js +7 -1
- package/dist/esm/common/constants/env.cdn.js +2 -2
- package/dist/esm/common/constants/env.npm.js +2 -2
- package/dist/esm/common/constants/runtime.js +1 -1
- package/dist/esm/common/session/session-entity.js +2 -1
- package/dist/esm/common/timer/interaction-timer.js +16 -2
- package/dist/esm/common/timing/time-keeper.js +1 -3
- package/dist/esm/features/jserrors/aggregate/index.js +16 -6
- package/dist/esm/features/jserrors/instrument/index.js +8 -3
- package/dist/esm/features/session_replay/aggregate/index.js +48 -29
- package/dist/esm/features/session_replay/constants.js +2 -1
- package/dist/esm/features/session_replay/instrument/index.js +9 -2
- package/dist/esm/features/session_replay/shared/recorder-events.js +1 -9
- package/dist/esm/features/session_replay/shared/recorder.js +23 -51
- package/dist/esm/features/session_replay/shared/utils.js +11 -0
- package/dist/esm/loaders/api/api.js +7 -1
- package/dist/types/common/constants/runtime.d.ts.map +1 -1
- package/dist/types/common/session/session-entity.d.ts.map +1 -1
- package/dist/types/common/timer/interaction-timer.d.ts +2 -0
- package/dist/types/common/timer/interaction-timer.d.ts.map +1 -1
- package/dist/types/common/timing/time-keeper.d.ts.map +1 -1
- package/dist/types/features/jserrors/aggregate/index.d.ts +2 -1
- package/dist/types/features/jserrors/aggregate/index.d.ts.map +1 -1
- package/dist/types/features/jserrors/instrument/index.d.ts.map +1 -1
- package/dist/types/features/session_replay/aggregate/index.d.ts +5 -2
- package/dist/types/features/session_replay/aggregate/index.d.ts.map +1 -1
- package/dist/types/features/session_replay/constants.d.ts +1 -0
- package/dist/types/features/session_replay/constants.d.ts.map +1 -1
- package/dist/types/features/session_replay/instrument/index.d.ts +1 -0
- package/dist/types/features/session_replay/instrument/index.d.ts.map +1 -1
- package/dist/types/features/session_replay/shared/recorder-events.d.ts +0 -8
- package/dist/types/features/session_replay/shared/recorder-events.d.ts.map +1 -1
- package/dist/types/features/session_replay/shared/recorder.d.ts +1 -17
- package/dist/types/features/session_replay/shared/recorder.d.ts.map +1 -1
- package/dist/types/features/session_replay/shared/utils.d.ts +8 -0
- package/dist/types/features/session_replay/shared/utils.d.ts.map +1 -1
- package/dist/types/loaders/api/api.d.ts.map +1 -1
- package/package.json +2 -2
- package/src/common/constants/runtime.js +1 -1
- package/src/common/session/session-entity.js +2 -1
- package/src/common/timer/interaction-timer.js +17 -2
- package/src/common/timing/time-keeper.js +1 -3
- package/src/features/jserrors/aggregate/index.js +15 -6
- package/src/features/jserrors/instrument/index.js +9 -4
- package/src/features/session_replay/aggregate/index.js +43 -25
- package/src/features/session_replay/constants.js +2 -1
- package/src/features/session_replay/instrument/index.js +7 -2
- package/src/features/session_replay/shared/recorder-events.js +1 -6
- package/src/features/session_replay/shared/recorder.js +21 -27
- package/src/features/session_replay/shared/utils.js +12 -0
- package/src/loaders/api/api.js +10 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { record as recorder } from 'rrweb'
|
|
2
2
|
import { stringify } from '../../../common/util/stringify'
|
|
3
|
-
import { AVG_COMPRESSION, CHECKOUT_MS, IDEAL_PAYLOAD_SIZE, QUERY_PARAM_PADDING, RRWEB_EVENT_TYPES } from '../constants'
|
|
3
|
+
import { AVG_COMPRESSION, CHECKOUT_MS, IDEAL_PAYLOAD_SIZE, QUERY_PARAM_PADDING, RRWEB_EVENT_TYPES, SR_EVENT_EMITTER_TYPES } from '../constants'
|
|
4
4
|
import { getConfigurationValue } from '../../../common/config/config'
|
|
5
5
|
import { RecorderEvents } from './recorder-events'
|
|
6
6
|
import { MODE } from '../../../common/session/constants'
|
|
@@ -8,6 +8,7 @@ import { stylesheetEvaluator } from './stylesheet-evaluator'
|
|
|
8
8
|
import { handle } from '../../../common/event-emitter/handle'
|
|
9
9
|
import { SUPPORTABILITY_METRIC_CHANNEL } from '../../metrics/constants'
|
|
10
10
|
import { FEATURE_NAMES } from '../../../loaders/features/features'
|
|
11
|
+
import { buildNRMetaNode } from './utils'
|
|
11
12
|
|
|
12
13
|
export class Recorder {
|
|
13
14
|
/** Each page mutation or event will be stored (raw) in this array. This array will be cleared on each harvest */
|
|
@@ -20,9 +21,9 @@ export class Recorder {
|
|
|
20
21
|
#fixing = false
|
|
21
22
|
|
|
22
23
|
constructor (parent) {
|
|
23
|
-
this.#events = new RecorderEvents(
|
|
24
|
-
this.#backloggedEvents = new RecorderEvents(
|
|
25
|
-
this.#preloaded = [new RecorderEvents(
|
|
24
|
+
this.#events = new RecorderEvents()
|
|
25
|
+
this.#backloggedEvents = new RecorderEvents()
|
|
26
|
+
this.#preloaded = [new RecorderEvents()]
|
|
26
27
|
/** True when actively recording, false when paused or stopped */
|
|
27
28
|
this.recording = false
|
|
28
29
|
/** The pointer to the current bucket holding rrweb events */
|
|
@@ -40,14 +41,9 @@ export class Recorder {
|
|
|
40
41
|
}
|
|
41
42
|
|
|
42
43
|
getEvents () {
|
|
43
|
-
if (this.#preloaded[0]?.events.length) {
|
|
44
|
-
const preloadedEvents = this.returnCorrectTimestamps(this.#preloaded[0])
|
|
45
|
-
return { ...this.#preloaded[0], events: preloadedEvents, type: 'preloaded' }
|
|
46
|
-
}
|
|
47
|
-
const backloggedEvents = this.returnCorrectTimestamps(this.#backloggedEvents)
|
|
48
|
-
const events = this.returnCorrectTimestamps(this.#events)
|
|
44
|
+
if (this.#preloaded[0]?.events.length) return { ...this.#preloaded[0], type: 'preloaded' }
|
|
49
45
|
return {
|
|
50
|
-
events: [...backloggedEvents, ...events].filter(x => x),
|
|
46
|
+
events: [...this.#backloggedEvents.events, ...this.#events.events].filter(x => x),
|
|
51
47
|
type: 'standard',
|
|
52
48
|
cycleTimestamp: Math.min(this.#backloggedEvents.cycleTimestamp, this.#events.cycleTimestamp),
|
|
53
49
|
payloadBytesEstimation: this.#backloggedEvents.payloadBytesEstimation + this.#events.payloadBytesEstimation,
|
|
@@ -58,30 +54,22 @@ export class Recorder {
|
|
|
58
54
|
}
|
|
59
55
|
}
|
|
60
56
|
|
|
61
|
-
/**
|
|
62
|
-
* Returns time-corrected events. If the events were correctable from the beginning, this correction will have already been applied.
|
|
63
|
-
* @param {SessionReplayEvent[]} events The array of buffered SR nodes
|
|
64
|
-
* @returns {CorrectedSessionReplayEvent[]}
|
|
65
|
-
*/
|
|
66
|
-
returnCorrectTimestamps (events) {
|
|
67
|
-
if (!this.parent.timeKeeper?.ready) return events.events
|
|
68
|
-
return events.canCorrectTimestamps
|
|
69
|
-
? events.events
|
|
70
|
-
: events.events.map(({ __serialized, timestamp, ...e }) => ({ timestamp: this.parent.timeKeeper.correctAbsoluteTimestamp(timestamp), ...e }))
|
|
71
|
-
}
|
|
72
|
-
|
|
73
57
|
/** Clears the buffer (this.#events), and resets all payload metadata properties */
|
|
74
58
|
clearBuffer () {
|
|
75
59
|
if (this.#preloaded[0]?.events.length) this.#preloaded.shift()
|
|
76
60
|
else if (this.parent.mode === MODE.ERROR) this.#backloggedEvents = this.#events
|
|
77
|
-
else this.#backloggedEvents = new RecorderEvents(
|
|
78
|
-
this.#events = new RecorderEvents(
|
|
61
|
+
else this.#backloggedEvents = new RecorderEvents()
|
|
62
|
+
this.#events = new RecorderEvents()
|
|
79
63
|
}
|
|
80
64
|
|
|
81
65
|
/** Begin recording using configured recording lib */
|
|
82
66
|
startRecording () {
|
|
83
67
|
this.recording = true
|
|
84
68
|
const { block_class, ignore_class, mask_text_class, block_selector, mask_input_options, mask_text_selector, mask_all_inputs, inline_stylesheet, inline_images, collect_fonts } = getConfigurationValue(this.parent.agentIdentifier, 'session_replay')
|
|
69
|
+
const customMasker = (text, element) => {
|
|
70
|
+
if (element?.type?.toLowerCase() !== 'password' && (element?.dataset.nrUnmask !== undefined || element?.classList.contains('nr-unmask'))) return text
|
|
71
|
+
return '*'.repeat(text.length)
|
|
72
|
+
}
|
|
85
73
|
// set up rrweb configurations for maximum privacy --
|
|
86
74
|
// https://newrelic.atlassian.net/wiki/spaces/O11Y/pages/2792293280/2023+02+28+Browser+-+Session+Replay#Configuration-options
|
|
87
75
|
const stop = recorder({
|
|
@@ -92,15 +80,20 @@ export class Recorder {
|
|
|
92
80
|
blockSelector: block_selector,
|
|
93
81
|
maskInputOptions: mask_input_options,
|
|
94
82
|
maskTextSelector: mask_text_selector,
|
|
83
|
+
maskTextFn: customMasker,
|
|
95
84
|
maskAllInputs: mask_all_inputs,
|
|
85
|
+
maskInputFn: customMasker,
|
|
96
86
|
inlineStylesheet: inline_stylesheet,
|
|
97
87
|
inlineImages: inline_images,
|
|
98
88
|
collectFonts: collect_fonts,
|
|
99
89
|
checkoutEveryNms: CHECKOUT_MS[this.parent.mode]
|
|
100
90
|
})
|
|
101
91
|
|
|
92
|
+
this.parent.ee.emit(SR_EVENT_EMITTER_TYPES.REPLAY_RUNNING, [true, this.parent.mode])
|
|
93
|
+
|
|
102
94
|
this.stopRecording = () => {
|
|
103
95
|
this.recording = false
|
|
96
|
+
this.parent.ee.emit(SR_EVENT_EMITTER_TYPES.REPLAY_RUNNING, [false, this.parent.mode])
|
|
104
97
|
stop()
|
|
105
98
|
}
|
|
106
99
|
}
|
|
@@ -148,7 +141,8 @@ export class Recorder {
|
|
|
148
141
|
|
|
149
142
|
if (this.parent.blocked) return
|
|
150
143
|
|
|
151
|
-
if (this.
|
|
144
|
+
if (this.parent.timeKeeper?.ready && !event.__newrelic) {
|
|
145
|
+
event.__newrelic = buildNRMetaNode(event.timestamp, this.parent.timeKeeper)
|
|
152
146
|
event.timestamp = this.parent.timeKeeper.correctAbsoluteTimestamp(event.timestamp)
|
|
153
147
|
}
|
|
154
148
|
event.__serialized = stringify(event)
|
|
@@ -184,7 +178,7 @@ export class Recorder {
|
|
|
184
178
|
this.parent.scheduler.runHarvest()
|
|
185
179
|
} else {
|
|
186
180
|
// we are still in "preload" and it triggered a "stop point". Make a new set, which will get pointed at on next cycle
|
|
187
|
-
this.#preloaded.push(new RecorderEvents(
|
|
181
|
+
this.#preloaded.push(new RecorderEvents())
|
|
188
182
|
}
|
|
189
183
|
}
|
|
190
184
|
}
|
|
@@ -17,3 +17,15 @@ export function canImportReplayAgg (agentId, sessionMgr) {
|
|
|
17
17
|
if (!hasReplayPrerequisite(agentId)) return false
|
|
18
18
|
return !!sessionMgr?.isNew || !!sessionMgr?.state.sessionReplayMode // Session Replay should only try to run if already running from a previous page, or at the beginning of a session
|
|
19
19
|
}
|
|
20
|
+
|
|
21
|
+
export function buildNRMetaNode (timestamp, timeKeeper) {
|
|
22
|
+
const correctedTimestamp = timeKeeper.correctAbsoluteTimestamp(timestamp)
|
|
23
|
+
return {
|
|
24
|
+
originalTimestamp: timestamp,
|
|
25
|
+
correctedTimestamp,
|
|
26
|
+
timestampDiff: timestamp - correctedTimestamp,
|
|
27
|
+
timeKeeperOriginTime: timeKeeper.originTime,
|
|
28
|
+
timeKeeperCorrectedOriginTime: timeKeeper.correctedOriginTime,
|
|
29
|
+
timeKeeperDiff: Math.floor(timeKeeper.originTime - timeKeeper.correctedOriginTime)
|
|
30
|
+
}
|
|
31
|
+
}
|
package/src/loaders/api/api.js
CHANGED
|
@@ -15,6 +15,7 @@ import { gosCDN } from '../../common/window/nreum'
|
|
|
15
15
|
import { apiMethods, asyncApiMethods } from './api-methods'
|
|
16
16
|
import { SR_EVENT_EMITTER_TYPES } from '../../features/session_replay/constants'
|
|
17
17
|
import { now } from '../../common/timing/now'
|
|
18
|
+
import { MODE } from '../../common/session/constants'
|
|
18
19
|
|
|
19
20
|
export function setTopLevelCallers () {
|
|
20
21
|
const nr = gosCDN()
|
|
@@ -33,12 +34,20 @@ export function setTopLevelCallers () {
|
|
|
33
34
|
}
|
|
34
35
|
}
|
|
35
36
|
|
|
37
|
+
const replayRunning = {}
|
|
38
|
+
|
|
36
39
|
export function setAPI (agentIdentifier, forceDrain, runSoftNavOverSpa = false) {
|
|
37
40
|
if (!forceDrain) registerDrain(agentIdentifier, 'api')
|
|
38
41
|
const apiInterface = {}
|
|
39
42
|
var instanceEE = ee.get(agentIdentifier)
|
|
40
43
|
var tracerEE = instanceEE.get('tracer')
|
|
41
44
|
|
|
45
|
+
replayRunning[agentIdentifier] = MODE.OFF
|
|
46
|
+
|
|
47
|
+
instanceEE.on(SR_EVENT_EMITTER_TYPES.REPLAY_RUNNING, (isRunning) => {
|
|
48
|
+
replayRunning[agentIdentifier] = isRunning
|
|
49
|
+
})
|
|
50
|
+
|
|
42
51
|
var prefix = 'api-'
|
|
43
52
|
var spaPrefix = prefix + 'ixn-'
|
|
44
53
|
|
|
@@ -184,7 +193,7 @@ export function setAPI (agentIdentifier, forceDrain, runSoftNavOverSpa = false)
|
|
|
184
193
|
apiInterface.noticeError = function (err, customAttributes) {
|
|
185
194
|
if (typeof err === 'string') err = new Error(err)
|
|
186
195
|
handle(SUPPORTABILITY_METRIC_CHANNEL, ['API/noticeError/called'], undefined, FEATURE_NAMES.metrics, instanceEE)
|
|
187
|
-
handle('err', [err, now(), false, customAttributes], undefined, FEATURE_NAMES.jserrors, instanceEE)
|
|
196
|
+
handle('err', [err, now(), false, customAttributes, !!replayRunning[agentIdentifier]], undefined, FEATURE_NAMES.jserrors, instanceEE)
|
|
188
197
|
}
|
|
189
198
|
|
|
190
199
|
// theres no window.load event on non-browser scopes, lazy load immediately
|