@newrelic/browser-agent 1.250.0 → 1.251.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/CHANGELOG.md +13 -0
- package/dist/cjs/common/config/state/init.js +0 -2
- package/dist/cjs/common/config/state/originals.js +1 -2
- package/dist/cjs/common/constants/env.cdn.js +4 -8
- package/dist/cjs/common/constants/env.js +4 -8
- package/dist/cjs/common/constants/env.npm.js +4 -8
- package/dist/cjs/common/constants/runtime.js +13 -24
- package/dist/cjs/common/constants/shared-channel.js +2 -3
- package/dist/cjs/common/event-emitter/contextual-ee.js +2 -4
- package/dist/cjs/common/event-emitter/handle.js +1 -2
- package/dist/cjs/common/harvest/harvest-scheduler.js +2 -2
- package/dist/cjs/common/harvest/harvest.js +2 -2
- package/dist/cjs/common/harvest/types.js +1 -2
- package/dist/cjs/common/ids/bundle-id.js +1 -2
- package/dist/cjs/common/session/constants.js +7 -13
- package/dist/cjs/common/timer/interaction-timer.js +0 -1
- package/dist/cjs/common/timing/nav-timing.js +1 -2
- package/dist/cjs/common/util/feature-flags.js +1 -2
- package/dist/cjs/common/vitals/constants.js +2 -3
- package/dist/cjs/common/vitals/cumulative-layout-shift.js +1 -2
- package/dist/cjs/common/vitals/first-contentful-paint.js +1 -2
- package/dist/cjs/common/vitals/first-input-delay.js +1 -2
- package/dist/cjs/common/vitals/first-paint.js +1 -2
- package/dist/cjs/common/vitals/interaction-to-next-paint.js +1 -2
- package/dist/cjs/common/vitals/largest-contentful-paint.js +1 -2
- package/dist/cjs/common/vitals/long-task.js +1 -3
- package/dist/cjs/common/vitals/time-to-first-byte.js +1 -2
- package/dist/cjs/common/window/nreum.js +1 -2
- package/dist/cjs/common/wrap/wrap-function.js +2 -4
- package/dist/cjs/features/ajax/aggregate/index.js +0 -2
- package/dist/cjs/features/ajax/constants.js +1 -2
- package/dist/cjs/features/jserrors/aggregate/index.js +0 -1
- package/dist/cjs/features/jserrors/aggregate/string-hash-code.js +0 -1
- package/dist/cjs/features/jserrors/constants.js +1 -2
- package/dist/cjs/features/metrics/constants.js +5 -10
- package/dist/cjs/features/page_action/constants.js +1 -2
- package/dist/cjs/features/page_view_event/aggregate/index.js +2 -2
- package/dist/cjs/features/page_view_event/constants.js +1 -2
- package/dist/cjs/features/page_view_event/instrument/index.js +2 -2
- package/dist/cjs/features/page_view_timing/constants.js +1 -2
- package/dist/cjs/features/session_replay/aggregate/index.js +14 -12
- package/dist/cjs/features/session_replay/constants.js +8 -16
- package/dist/cjs/features/session_replay/instrument/index.js +11 -11
- package/dist/cjs/features/session_replay/shared/recorder-events.js +2 -0
- package/dist/cjs/features/session_replay/shared/recorder.js +50 -4
- package/dist/cjs/features/session_replay/shared/stylesheet-evaluator.js +94 -0
- package/dist/cjs/features/session_trace/aggregate/index.js +0 -2
- package/dist/cjs/features/session_trace/constants.js +8 -16
- package/dist/cjs/features/session_trace/instrument/index.js +2 -2
- package/dist/cjs/features/spa/aggregate/index.js +2 -2
- package/dist/cjs/features/spa/aggregate/interaction-node.js +0 -1
- package/dist/cjs/features/spa/constants.js +22 -44
- package/dist/cjs/features/spa/instrument/index.js +2 -2
- package/dist/cjs/features/utils/instrument-base.js +6 -7
- package/dist/cjs/features/utils/lazy-feature-loader.js +2 -2
- package/dist/cjs/loaders/agent-base.js +61 -15
- package/dist/cjs/loaders/agent.js +0 -38
- package/dist/cjs/loaders/api/api.js +7 -7
- package/dist/cjs/loaders/api/interaction-types.js +1 -2
- package/dist/cjs/loaders/features/features.js +3 -5
- package/dist/cjs/loaders/micro-agent.js +2 -2
- package/dist/esm/common/config/state/init.js +0 -2
- package/dist/esm/common/constants/env.cdn.js +1 -1
- package/dist/esm/common/constants/env.npm.js +1 -1
- package/dist/esm/common/timer/interaction-timer.js +0 -1
- package/dist/esm/common/vitals/long-task.js +0 -1
- package/dist/esm/features/ajax/aggregate/index.js +0 -2
- package/dist/esm/features/jserrors/aggregate/index.js +0 -1
- package/dist/esm/features/jserrors/aggregate/string-hash-code.js +0 -1
- package/dist/esm/features/session_replay/aggregate/index.js +3 -1
- package/dist/esm/features/session_replay/shared/recorder-events.js +2 -0
- package/dist/esm/features/session_replay/shared/recorder.js +50 -4
- package/dist/esm/features/session_replay/shared/stylesheet-evaluator.js +88 -0
- package/dist/esm/features/session_trace/aggregate/index.js +0 -2
- package/dist/esm/features/spa/aggregate/interaction-node.js +0 -1
- package/dist/esm/features/utils/instrument-base.js +0 -1
- package/dist/esm/loaders/agent-base.js +62 -15
- package/dist/esm/loaders/agent.js +0 -38
- package/dist/types/features/session_replay/aggregate/index.d.ts.map +1 -1
- package/dist/types/features/session_replay/shared/recorder-events.d.ts +2 -0
- package/dist/types/features/session_replay/shared/recorder-events.d.ts.map +1 -1
- package/dist/types/features/session_replay/shared/recorder.d.ts +14 -1
- package/dist/types/features/session_replay/shared/recorder.d.ts.map +1 -1
- package/dist/types/features/session_replay/shared/stylesheet-evaluator.d.ts +22 -0
- package/dist/types/features/session_replay/shared/stylesheet-evaluator.d.ts.map +1 -0
- package/dist/types/loaders/agent-base.d.ts +48 -12
- package/dist/types/loaders/agent-base.d.ts.map +1 -1
- package/dist/types/loaders/agent.d.ts +0 -33
- package/dist/types/loaders/agent.d.ts.map +1 -1
- package/package.json +49 -49
- package/src/common/ids/__mocks__/bundle-id.js +1 -1
- package/src/common/ids/__mocks__/unique-id.js +2 -2
- package/src/features/session_replay/aggregate/index.js +3 -0
- package/src/features/session_replay/shared/recorder-events.js +2 -0
- package/src/features/session_replay/shared/recorder.js +50 -5
- package/src/features/session_replay/shared/stylesheet-evaluator.js +84 -0
- package/src/loaders/agent-base.js +59 -15
- package/src/loaders/agent.js +0 -38
|
@@ -5,8 +5,8 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
5
5
|
});
|
|
6
6
|
exports.lazyFeatureLoader = lazyFeatureLoader;
|
|
7
7
|
var _features = require("../../loaders/features/features");
|
|
8
|
-
function _getRequireWildcardCache(
|
|
9
|
-
function _interopRequireWildcard(
|
|
8
|
+
function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); }
|
|
9
|
+
function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && Object.prototype.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; }
|
|
10
10
|
/**
|
|
11
11
|
* Centralizes the lazy loading of agent feature aggregate and instrument sources.
|
|
12
12
|
*
|
|
@@ -7,10 +7,21 @@ exports.AgentBase = void 0;
|
|
|
7
7
|
var _console = require("../common/util/console");
|
|
8
8
|
/* eslint-disable n/handle-callback-err */
|
|
9
9
|
|
|
10
|
+
/**
|
|
11
|
+
* @typedef {import('./api/interaction-types').InteractionInstance} InteractionInstance
|
|
12
|
+
*/
|
|
13
|
+
|
|
10
14
|
class AgentBase {
|
|
11
|
-
/**
|
|
12
|
-
|
|
13
|
-
|
|
15
|
+
/**
|
|
16
|
+
* Tries to execute the api and generates a generic warning message with the api name injected if unsuccessful
|
|
17
|
+
* @param {string} methodName
|
|
18
|
+
* @param {...any} args
|
|
19
|
+
*/
|
|
20
|
+
#callMethod(methodName) {
|
|
21
|
+
for (var _len = arguments.length, args = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
|
|
22
|
+
args[_key - 1] = arguments[_key];
|
|
23
|
+
}
|
|
24
|
+
if (typeof this.api?.[methodName] !== 'function') (0, _console.warn)("Call to agent api ".concat(methodName, " failed. The API is not currently initialized."));else return this.api[methodName](...args);
|
|
14
25
|
}
|
|
15
26
|
|
|
16
27
|
/**
|
|
@@ -20,7 +31,7 @@ class AgentBase {
|
|
|
20
31
|
* @param {object} [attributes] JSON object with one or more key/value pairs. For example: {key:"value"}. The key is reported as its own PageAction attribute with the specified values.
|
|
21
32
|
*/
|
|
22
33
|
addPageAction(name, attributes) {
|
|
23
|
-
|
|
34
|
+
return this.#callMethod('addPageAction', name, attributes);
|
|
24
35
|
}
|
|
25
36
|
|
|
26
37
|
/**
|
|
@@ -30,7 +41,7 @@ class AgentBase {
|
|
|
30
41
|
* @param {string} [host] Default is http://custom.transaction. Typically set host to your site's domain URI.
|
|
31
42
|
*/
|
|
32
43
|
setPageViewName(name, host) {
|
|
33
|
-
|
|
44
|
+
return this.#callMethod('setPageViewName', name, host);
|
|
34
45
|
}
|
|
35
46
|
|
|
36
47
|
/**
|
|
@@ -41,7 +52,7 @@ class AgentBase {
|
|
|
41
52
|
* @param {boolean} [persist] Default false. f set to true, the name-value pair will also be set into the browser's storage API. Then on the following instrumented pages that load within the same session, the pair will be re-applied as a custom attribute.
|
|
42
53
|
*/
|
|
43
54
|
setCustomAttribute(name, value, persist) {
|
|
44
|
-
|
|
55
|
+
return this.#callMethod('setCustomAttribute', name, value, persist);
|
|
45
56
|
}
|
|
46
57
|
|
|
47
58
|
/**
|
|
@@ -51,7 +62,7 @@ class AgentBase {
|
|
|
51
62
|
* @param {object} [customAttributes] An object containing name/value pairs representing custom attributes.
|
|
52
63
|
*/
|
|
53
64
|
noticeError(error, customAttributes) {
|
|
54
|
-
|
|
65
|
+
return this.#callMethod('noticeError', error, customAttributes);
|
|
55
66
|
}
|
|
56
67
|
|
|
57
68
|
/**
|
|
@@ -60,7 +71,7 @@ class AgentBase {
|
|
|
60
71
|
* @param {string|null} value A string identifier for the end-user, useful for tying all browser events to specific users. The value parameter does not have to be unique. If IDs should be unique, the caller is responsible for that validation. Passing a null value unsets any existing user ID.
|
|
61
72
|
*/
|
|
62
73
|
setUserId(value) {
|
|
63
|
-
|
|
74
|
+
return this.#callMethod('setUserId', value);
|
|
64
75
|
}
|
|
65
76
|
|
|
66
77
|
/**
|
|
@@ -72,7 +83,7 @@ class AgentBase {
|
|
|
72
83
|
* have to be unique. Passing a null value unsets any existing value.
|
|
73
84
|
*/
|
|
74
85
|
setApplicationVersion(value) {
|
|
75
|
-
|
|
86
|
+
return this.#callMethod('setApplicationVersion', value);
|
|
76
87
|
}
|
|
77
88
|
|
|
78
89
|
/**
|
|
@@ -81,7 +92,7 @@ class AgentBase {
|
|
|
81
92
|
* @param {(error: Error|string) => boolean | { group: string }} callback When an error occurs, the callback is called with the error object as a parameter. The callback will be called with each error, so it is not specific to one error.
|
|
82
93
|
*/
|
|
83
94
|
setErrorHandler(callback) {
|
|
84
|
-
|
|
95
|
+
return this.#callMethod('setErrorHandler', callback);
|
|
85
96
|
}
|
|
86
97
|
|
|
87
98
|
/**
|
|
@@ -90,7 +101,7 @@ class AgentBase {
|
|
|
90
101
|
* @param {number} [timeStamp] Defaults to the current time of the call. If used, this marks the time that the page is "finished" according to your own criteria.
|
|
91
102
|
*/
|
|
92
103
|
finished(timeStamp) {
|
|
93
|
-
|
|
104
|
+
return this.#callMethod('finished', timeStamp);
|
|
94
105
|
}
|
|
95
106
|
|
|
96
107
|
/**
|
|
@@ -100,7 +111,7 @@ class AgentBase {
|
|
|
100
111
|
* @param {string} id The ID or version of this release; for example, a version number, build number from your CI environment, GitHub SHA, GUID, or a hash of the contents.
|
|
101
112
|
*/
|
|
102
113
|
addRelease(name, id) {
|
|
103
|
-
|
|
114
|
+
return this.#callMethod('addRelease', name, id);
|
|
104
115
|
}
|
|
105
116
|
|
|
106
117
|
/**
|
|
@@ -109,7 +120,7 @@ class AgentBase {
|
|
|
109
120
|
* @param {string|string[]} [featureNames] The name(s) of the features to start. If no name(s) are passed, all features will be started
|
|
110
121
|
*/
|
|
111
122
|
start(featureNames) {
|
|
112
|
-
|
|
123
|
+
return this.#callMethod('start', featureNames);
|
|
113
124
|
}
|
|
114
125
|
|
|
115
126
|
/**
|
|
@@ -118,7 +129,7 @@ class AgentBase {
|
|
|
118
129
|
* {@link https://docs.newrelic.com/docs/browser/new-relic-browser/browser-apis/recordReplay/}
|
|
119
130
|
*/
|
|
120
131
|
recordReplay() {
|
|
121
|
-
|
|
132
|
+
return this.#callMethod('recordReplay');
|
|
122
133
|
}
|
|
123
134
|
|
|
124
135
|
/**
|
|
@@ -128,7 +139,42 @@ class AgentBase {
|
|
|
128
139
|
* {@link https://docs.newrelic.com/docs/browser/new-relic-browser/browser-apis/recordReplay/}
|
|
129
140
|
*/
|
|
130
141
|
pauseReplay() {
|
|
131
|
-
|
|
142
|
+
return this.#callMethod('pauseReplay');
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Adds a JavaScript object with a custom name, start time, etc. to an in-progress session trace.
|
|
147
|
+
* {@link https://docs.newrelic.com/docs/browser/new-relic-browser/browser-apis/addtotrace/}
|
|
148
|
+
* @param {{name: string, start: number, end?: number, origin?: string, type?: string}} customAttributes Supply a JavaScript object with these required and optional name/value pairs:
|
|
149
|
+
*
|
|
150
|
+
* - Required name/value pairs: name, start
|
|
151
|
+
* - Optional name/value pairs: end, origin, type
|
|
152
|
+
* - Note: Does not apply to MicroAgent
|
|
153
|
+
*
|
|
154
|
+
* If you are sending the same event object to New Relic as a PageAction, omit the TYPE attribute. (type is a string to describe what type of event you are marking inside of a session trace.) If included, it will override the event type and cause the PageAction event to be sent incorrectly. Instead, use the name attribute for event information.
|
|
155
|
+
*/
|
|
156
|
+
addToTrace(customAttributes) {
|
|
157
|
+
return this.#callMethod('addToTrace', customAttributes);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* Gives SPA routes more accurate names than default names. Monitors specific routes rather than by default grouping.
|
|
162
|
+
* {@link https://docs.newrelic.com/docs/browser/new-relic-browser/browser-apis/setcurrentroutename/}
|
|
163
|
+
* @param {string} name Current route name for the page.
|
|
164
|
+
* - Note: Does not apply to MicroAgent
|
|
165
|
+
*/
|
|
166
|
+
setCurrentRouteName(name) {
|
|
167
|
+
return this.#callMethod('setCurrentRouteName', name);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* Returns a new API object that is bound to the current SPA interaction.
|
|
172
|
+
* {@link https://docs.newrelic.com/docs/browser/new-relic-browser/browser-apis/interaction/}
|
|
173
|
+
* @returns {InteractionInstance} An API object that is bound to a specific BrowserInteraction event. Each time this method is called for the same BrowserInteraction, a new object is created, but it still references the same interaction.
|
|
174
|
+
* - Note: Does not apply to MicroAgent
|
|
175
|
+
*/
|
|
176
|
+
interaction() {
|
|
177
|
+
return this.#callMethod('interaction');
|
|
132
178
|
}
|
|
133
179
|
}
|
|
134
180
|
exports.AgentBase = AgentBase;
|
|
@@ -27,10 +27,6 @@ var _runtime = require("../common/constants/runtime");
|
|
|
27
27
|
|
|
28
28
|
// common files
|
|
29
29
|
|
|
30
|
-
/**
|
|
31
|
-
* @typedef {import('./api/interaction-types').InteractionInstance} InteractionInstance
|
|
32
|
-
*/
|
|
33
|
-
|
|
34
30
|
/**
|
|
35
31
|
* A flexible class that may be used to compose an agent from a select subset of feature modules. In applications
|
|
36
32
|
* sensitive to network load, this may result in smaller builds with slightly lower performance impact.
|
|
@@ -100,39 +96,5 @@ class Agent extends _agentBase.AgentBase {
|
|
|
100
96
|
return false;
|
|
101
97
|
}
|
|
102
98
|
}
|
|
103
|
-
|
|
104
|
-
/* Below API methods are only available on a standard agent and not the micro agent */
|
|
105
|
-
|
|
106
|
-
/**
|
|
107
|
-
* Adds a JavaScript object with a custom name, start time, etc. to an in-progress session trace.
|
|
108
|
-
* {@link https://docs.newrelic.com/docs/browser/new-relic-browser/browser-apis/addtotrace/}
|
|
109
|
-
* @param {{name: string, start: number, end?: number, origin?: string, type?: string}} customAttributes Supply a JavaScript object with these required and optional name/value pairs:
|
|
110
|
-
*
|
|
111
|
-
* - Required name/value pairs: name, start
|
|
112
|
-
* - Optional name/value pairs: end, origin, type
|
|
113
|
-
*
|
|
114
|
-
* If you are sending the same event object to New Relic as a PageAction, omit the TYPE attribute. (type is a string to describe what type of event you are marking inside of a session trace.) If included, it will override the event type and cause the PageAction event to be sent incorrectly. Instead, use the name attribute for event information.
|
|
115
|
-
*/
|
|
116
|
-
addToTrace(customAttributes) {
|
|
117
|
-
(0, _console.warn)('Call to agent api addToTrace failed. The session trace feature is not currently initialized.');
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
/**
|
|
121
|
-
* Gives SPA routes more accurate names than default names. Monitors specific routes rather than by default grouping.
|
|
122
|
-
* {@link https://docs.newrelic.com/docs/browser/new-relic-browser/browser-apis/setcurrentroutename/}
|
|
123
|
-
* @param {string} name Current route name for the page.
|
|
124
|
-
*/
|
|
125
|
-
setCurrentRouteName(name) {
|
|
126
|
-
(0, _console.warn)('Call to agent api setCurrentRouteName failed. The spa feature is not currently initialized.');
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
/**
|
|
130
|
-
* Returns a new API object that is bound to the current SPA interaction.
|
|
131
|
-
* {@link https://docs.newrelic.com/docs/browser/new-relic-browser/browser-apis/interaction/}
|
|
132
|
-
* @returns {InteractionInstance} An API object that is bound to a specific BrowserInteraction event. Each time this method is called for the same BrowserInteraction, a new object is created, but it still references the same interaction.
|
|
133
|
-
*/
|
|
134
|
-
interaction() {
|
|
135
|
-
(0, _console.warn)('Call to agent api interaction failed. The spa feature is not currently initialized.');
|
|
136
|
-
}
|
|
137
99
|
}
|
|
138
100
|
exports.Agent = Agent;
|
|
@@ -17,13 +17,13 @@ var _runtime = require("../../common/constants/runtime");
|
|
|
17
17
|
var _console = require("../../common/util/console");
|
|
18
18
|
var _constants = require("../../features/metrics/constants");
|
|
19
19
|
var _nreum = require("../../common/window/nreum");
|
|
20
|
-
function _getRequireWildcardCache(
|
|
21
|
-
function _interopRequireWildcard(
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
const CUSTOM_ATTR_GROUP = 'CUSTOM/'; // the subgroup items should be stored under in storage API
|
|
26
|
-
|
|
20
|
+
function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); }
|
|
21
|
+
function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && Object.prototype.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; } /*
|
|
22
|
+
* Copyright 2020 New Relic Corporation. All rights reserved.
|
|
23
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
24
|
+
*/
|
|
25
|
+
const CUSTOM_ATTR_GROUP = exports.CUSTOM_ATTR_GROUP = 'CUSTOM/'; // the subgroup items should be stored under in storage API
|
|
26
|
+
|
|
27
27
|
function setTopLevelCallers() {
|
|
28
28
|
const nr = (0, _nreum.gosCDN)();
|
|
29
29
|
const funcs = ['setErrorHandler', 'finished', 'addToTrace', 'addRelease', 'addPageAction', 'setCurrentRouteName', 'setPageViewName', 'setCustomAttribute', 'interaction', 'noticeError', 'setUserId', 'setApplicationVersion', 'start', 'recordReplay', 'pauseReplay'];
|
|
@@ -4,7 +4,7 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
4
4
|
value: true
|
|
5
5
|
});
|
|
6
6
|
exports.featurePriority = exports.FEATURE_NAMES = void 0;
|
|
7
|
-
const FEATURE_NAMES = {
|
|
7
|
+
const FEATURE_NAMES = exports.FEATURE_NAMES = {
|
|
8
8
|
ajax: 'ajax',
|
|
9
9
|
jserrors: 'jserrors',
|
|
10
10
|
metrics: 'metrics',
|
|
@@ -20,8 +20,7 @@ const FEATURE_NAMES = {
|
|
|
20
20
|
* The order in which features will be instrumented. This is the traditional order. It's unclear if the order of
|
|
21
21
|
* wrapping events has any ramifications, so we are enforcing this order intentionally for now.
|
|
22
22
|
*/
|
|
23
|
-
exports.
|
|
24
|
-
const featurePriority = {
|
|
23
|
+
const featurePriority = exports.featurePriority = {
|
|
25
24
|
[FEATURE_NAMES.pageViewEvent]: 1,
|
|
26
25
|
[FEATURE_NAMES.pageViewTiming]: 2,
|
|
27
26
|
[FEATURE_NAMES.metrics]: 3,
|
|
@@ -31,5 +30,4 @@ const featurePriority = {
|
|
|
31
30
|
[FEATURE_NAMES.pageAction]: 7,
|
|
32
31
|
[FEATURE_NAMES.spa]: 8,
|
|
33
32
|
[FEATURE_NAMES.sessionReplay]: 9
|
|
34
|
-
};
|
|
35
|
-
exports.featurePriority = featurePriority;
|
|
33
|
+
};
|
|
@@ -15,8 +15,8 @@ var _features = require("./features/features");
|
|
|
15
15
|
var _console = require("../common/util/console");
|
|
16
16
|
var _load = require("../common/window/load");
|
|
17
17
|
var _agentBase = require("./agent-base");
|
|
18
|
-
function _getRequireWildcardCache(
|
|
19
|
-
function _interopRequireWildcard(
|
|
18
|
+
function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); }
|
|
19
|
+
function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && Object.prototype.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; } // loader files
|
|
20
20
|
// core files
|
|
21
21
|
const nonAutoFeatures = [_features.FEATURE_NAMES.jserrors, _features.FEATURE_NAMES.pageAction, _features.FEATURE_NAMES.metrics];
|
|
22
22
|
|
|
@@ -28,7 +28,6 @@ const model = () => {
|
|
|
28
28
|
password: true // This will be enforced to always be true in the setter
|
|
29
29
|
}
|
|
30
30
|
};
|
|
31
|
-
|
|
32
31
|
return {
|
|
33
32
|
feature_flags: [],
|
|
34
33
|
proxy: {
|
|
@@ -36,7 +35,6 @@ const model = () => {
|
|
|
36
35
|
// if this value is set, it will be used to overwrite the webpack asset path used to fetch assets
|
|
37
36
|
beacon: undefined // likewise for the url to which we send analytics
|
|
38
37
|
},
|
|
39
|
-
|
|
40
38
|
privacy: {
|
|
41
39
|
cookies_enabled: true
|
|
42
40
|
},
|
|
@@ -25,7 +25,6 @@ export class Aggregate extends AggregateBase {
|
|
|
25
25
|
this.drain();
|
|
26
26
|
return; // feature will only collect timeslice metrics & ajax trace nodes if it's not fully enabled
|
|
27
27
|
}
|
|
28
|
-
|
|
29
28
|
const denyList = getRuntime(agentIdentifier).denyList;
|
|
30
29
|
setDenyList(denyList);
|
|
31
30
|
let ajaxEvents = [];
|
|
@@ -202,7 +201,6 @@ export class Aggregate extends AggregateBase {
|
|
|
202
201
|
// traceId
|
|
203
202
|
nullable(event.spanTimestamp, numeric, false) // timestamp
|
|
204
203
|
];
|
|
205
|
-
|
|
206
204
|
var insert = '2,';
|
|
207
205
|
|
|
208
206
|
// add custom attributes
|
|
@@ -144,7 +144,6 @@ export class Aggregate extends AggregateBase {
|
|
|
144
144
|
}
|
|
145
145
|
// Again as with previous usage, all falsey values would include the error.
|
|
146
146
|
}
|
|
147
|
-
|
|
148
147
|
var stackInfo = computeStackTrace(err);
|
|
149
148
|
var canonicalStackString = this.buildCanonicalStackString(stackInfo);
|
|
150
149
|
const params = {
|
|
@@ -26,6 +26,7 @@ import { RRWEB_VERSION } from "../../../common/constants/env.npm";
|
|
|
26
26
|
import { now } from '../../../common/timing/now';
|
|
27
27
|
import { MODE, SESSION_EVENTS, SESSION_EVENT_TYPES } from '../../../common/session/constants';
|
|
28
28
|
import { stringify } from '../../../common/util/stringify';
|
|
29
|
+
import { stylesheetEvaluator } from '../shared/stylesheet-evaluator';
|
|
29
30
|
let gzipper, u8;
|
|
30
31
|
export class Aggregate extends AggregateBase {
|
|
31
32
|
static featureName = FEATURE_NAME;
|
|
@@ -304,6 +305,8 @@ export class Aggregate extends AggregateBase {
|
|
|
304
305
|
hasError: recorderEvents.hasError || false,
|
|
305
306
|
isFirstChunk: agentRuntime.session.state.sessionReplaySentFirstChunk === false,
|
|
306
307
|
decompressedBytes: recorderEvents.payloadBytesEstimation,
|
|
308
|
+
invalidStylesheetsDetected: stylesheetEvaluator.invalidStylesheetsDetected,
|
|
309
|
+
inlinedAllStylesheets: recorderEvents.inlinedAllStylesheets,
|
|
307
310
|
'rrweb.version': RRWEB_VERSION,
|
|
308
311
|
// customer-defined data should go last so that if it exceeds the query param padding limit it will be truncated instead of important attrs
|
|
309
312
|
...(endUserId && {
|
|
@@ -312,7 +315,6 @@ export class Aggregate extends AggregateBase {
|
|
|
312
315
|
// The Query Param is being arbitrarily limited in length here. It is also applied when estimating the size of the payload in getPayloadSize()
|
|
313
316
|
}, QUERY_PARAM_PADDING).substring(1) // remove the leading '&'
|
|
314
317
|
},
|
|
315
|
-
|
|
316
318
|
body: events
|
|
317
319
|
};
|
|
318
320
|
}
|
|
@@ -17,6 +17,8 @@ export class RecorderEvents {
|
|
|
17
17
|
this.hasMeta = false;
|
|
18
18
|
/** Payload metadata -- Should indicate that the payload being sent contains an error. Used for query/filter purposes in UI */
|
|
19
19
|
this.hasError = false;
|
|
20
|
+
/** Payload metadata -- Denotes whether all stylesheet elements were able to be inlined */
|
|
21
|
+
this.inlinedAllStylesheets = true;
|
|
20
22
|
}
|
|
21
23
|
add(event) {
|
|
22
24
|
this.events.push(event);
|
|
@@ -4,6 +4,10 @@ import { AVG_COMPRESSION, CHECKOUT_MS, IDEAL_PAYLOAD_SIZE, QUERY_PARAM_PADDING,
|
|
|
4
4
|
import { getConfigurationValue } from '../../../common/config/config';
|
|
5
5
|
import { RecorderEvents } from './recorder-events';
|
|
6
6
|
import { MODE } from '../../../common/session/constants';
|
|
7
|
+
import { stylesheetEvaluator } from './stylesheet-evaluator';
|
|
8
|
+
import { handle } from '../../../common/event-emitter/handle';
|
|
9
|
+
import { SUPPORTABILITY_METRIC_CHANNEL } from '../../metrics/constants';
|
|
10
|
+
import { FEATURE_NAMES } from '../../../loaders/features/features';
|
|
7
11
|
export class Recorder {
|
|
8
12
|
/** Each page mutation or event will be stored (raw) in this array. This array will be cleared on each harvest */
|
|
9
13
|
#events = new RecorderEvents();
|
|
@@ -11,14 +15,21 @@ export class Recorder {
|
|
|
11
15
|
#backloggedEvents = new RecorderEvents();
|
|
12
16
|
/** array of recorder events -- Will be filled only if forced harvest was triggered and harvester does not exist */
|
|
13
17
|
#preloaded = [new RecorderEvents()];
|
|
18
|
+
/** flag that if true, blocks events from being "stored". Only set to true when a full snapshot has incomplete nodes (only stylesheets ATM) */
|
|
19
|
+
#fixing = false;
|
|
14
20
|
constructor(parent) {
|
|
15
21
|
/** True when actively recording, false when paused or stopped */
|
|
16
22
|
this.recording = false;
|
|
23
|
+
/** The pointer to the current bucket holding rrweb events */
|
|
17
24
|
this.currentBufferTarget = this.#events;
|
|
18
25
|
/** 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 */
|
|
19
26
|
this.lastMeta = false;
|
|
27
|
+
/** The parent class that instantiated the recorder */
|
|
20
28
|
this.parent = parent;
|
|
21
|
-
|
|
29
|
+
/** Config to inform to inline stylesheet contents (true default) */
|
|
30
|
+
this.shouldInlineStylesheets = getConfigurationValue(this.parent.agentIdentifier, 'session_replay.inline_stylesheet');
|
|
31
|
+
/** A flag that can be set to false by failing conversions to stop the fetching process */
|
|
32
|
+
this.shouldFix = this.shouldInlineStylesheets;
|
|
22
33
|
/** The method to stop recording. This defaults to a noop, but is overwritten once the recording library is imported and initialized */
|
|
23
34
|
this.stopRecording = () => {/* no-op until set by rrweb initializer */};
|
|
24
35
|
}
|
|
@@ -34,7 +45,8 @@ export class Recorder {
|
|
|
34
45
|
payloadBytesEstimation: this.#backloggedEvents.payloadBytesEstimation + this.#events.payloadBytesEstimation,
|
|
35
46
|
hasError: this.#backloggedEvents.hasError || this.#events.hasError,
|
|
36
47
|
hasMeta: this.#backloggedEvents.hasMeta || this.#events.hasMeta,
|
|
37
|
-
hasSnapshot: this.#backloggedEvents.hasSnapshot || this.#events.hasSnapshot
|
|
48
|
+
hasSnapshot: this.#backloggedEvents.hasSnapshot || this.#events.hasSnapshot,
|
|
49
|
+
inlinedAllStylesheets: !!this.#backloggedEvents.events.length && this.#backloggedEvents.inlinedAllStylesheets || this.#events.inlinedAllStylesheets
|
|
38
50
|
};
|
|
39
51
|
}
|
|
40
52
|
|
|
@@ -62,7 +74,7 @@ export class Recorder {
|
|
|
62
74
|
// set up rrweb configurations for maximum privacy --
|
|
63
75
|
// https://newrelic.atlassian.net/wiki/spaces/O11Y/pages/2792293280/2023+02+28+Browser+-+Session+Replay#Configuration-options
|
|
64
76
|
const stop = recorder({
|
|
65
|
-
emit: this.
|
|
77
|
+
emit: this.audit.bind(this),
|
|
66
78
|
blockClass: block_class,
|
|
67
79
|
ignoreClass: ignore_class,
|
|
68
80
|
maskTextClass: mask_text_class,
|
|
@@ -81,10 +93,44 @@ export class Recorder {
|
|
|
81
93
|
};
|
|
82
94
|
}
|
|
83
95
|
|
|
96
|
+
/**
|
|
97
|
+
* audit - Checks if the event node payload is missing certain attributes
|
|
98
|
+
* will forward on to the "store" method if nothing needs async fixing
|
|
99
|
+
* @param {*} event - An RRWEB event node
|
|
100
|
+
* @param {*} isCheckout - Flag indicating if the payload was triggered as a checkout
|
|
101
|
+
*/
|
|
102
|
+
audit(event, isCheckout) {
|
|
103
|
+
/** only run the audit if inline_stylesheets is configured as on (default behavior) */
|
|
104
|
+
if (this.shouldInlineStylesheets === false || !this.shouldFix) {
|
|
105
|
+
this.currentBufferTarget.inlinedAllStylesheets = false;
|
|
106
|
+
return this.store(event, isCheckout);
|
|
107
|
+
}
|
|
108
|
+
/** An count of stylesheet objects that were blocked from accessing contents via JS */
|
|
109
|
+
const incompletes = stylesheetEvaluator.evaluate();
|
|
110
|
+
/** Only stop ignoring data if already ignoring and a new valid snapshap is taking place (0 incompletes and we get a meta node for the snap) */
|
|
111
|
+
if (!incompletes && this.#fixing && event.type === RRWEB_EVENT_TYPES.Meta) this.#fixing = false;
|
|
112
|
+
if (incompletes) {
|
|
113
|
+
handle(SUPPORTABILITY_METRIC_CHANNEL, ['SessionReplay/Payload/Missing-Inline-Css', incompletes], undefined, FEATURE_NAMES.metrics, this.parent.ee);
|
|
114
|
+
/** wait for the evaluator to download/replace the incompletes' src code and then take a new snap */
|
|
115
|
+
stylesheetEvaluator.fix().then(failedToFix => {
|
|
116
|
+
if (failedToFix) {
|
|
117
|
+
this.currentBufferTarget.inlinedAllStylesheets = false;
|
|
118
|
+
this.shouldFix = false;
|
|
119
|
+
}
|
|
120
|
+
this.takeFullSnapshot();
|
|
121
|
+
});
|
|
122
|
+
/** Only start ignoring data if got a faulty snapshot */
|
|
123
|
+
if (event.type === RRWEB_EVENT_TYPES.FullSnapshot || event.type === RRWEB_EVENT_TYPES.Meta) this.#fixing = true;
|
|
124
|
+
}
|
|
125
|
+
/** Only store the data if not being "fixed" (full snapshots that have broken css) */
|
|
126
|
+
if (!this.#fixing) this.store(event, isCheckout);
|
|
127
|
+
}
|
|
128
|
+
|
|
84
129
|
/** Store a payload in the buffer (this.#events). This should be the callback to the recording lib noticing a mutation */
|
|
85
130
|
store(event, isCheckout) {
|
|
131
|
+
if (!event) return;
|
|
86
132
|
event.__serialized = stringify(event);
|
|
87
|
-
if (!this.parent.scheduler) this.currentBufferTarget = this.#preloaded[this.#preloaded.length - 1];else this.currentBufferTarget = this.#events;
|
|
133
|
+
if (!this.parent.scheduler && this.#preloaded.length) this.currentBufferTarget = this.#preloaded[this.#preloaded.length - 1];else this.currentBufferTarget = this.#events;
|
|
88
134
|
if (this.parent.blocked) return;
|
|
89
135
|
const eventBytes = event.__serialized.length;
|
|
90
136
|
/** The estimated size of the payload after compression */
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { originals } from '../../../common/config/config';
|
|
2
|
+
import { isBrowserScope } from '../../../common/constants/runtime';
|
|
3
|
+
class StylesheetEvaluator {
|
|
4
|
+
#evaluated = new WeakSet();
|
|
5
|
+
#fetchProms = [];
|
|
6
|
+
/**
|
|
7
|
+
* Flipped to true if stylesheets that cannot be natively inlined are detected by the stylesheetEvaluator class
|
|
8
|
+
* Used at harvest time to denote that all subsequent payloads are subject to this and customers should be advised to handle crossorigin decoration
|
|
9
|
+
* */
|
|
10
|
+
invalidStylesheetsDetected = false;
|
|
11
|
+
failedToFix = false;
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* this works by checking (only ever once) each cssRules obj in the style sheets array. The try/catch will catch an error if the cssRules obj blocks access, triggering the module to try to "fix" the asset`. Returns the count of incomplete assets discovered.
|
|
15
|
+
* @returns {Number}
|
|
16
|
+
*/
|
|
17
|
+
evaluate() {
|
|
18
|
+
let incompletes = 0;
|
|
19
|
+
if (isBrowserScope) {
|
|
20
|
+
for (let i = 0; i < Object.keys(document.styleSheets).length; i++) {
|
|
21
|
+
const ss = document.styleSheets[i];
|
|
22
|
+
if (!this.#evaluated.has(ss)) {
|
|
23
|
+
this.#evaluated.add(ss);
|
|
24
|
+
try {
|
|
25
|
+
// eslint-disable-next-line
|
|
26
|
+
const temp = ss.cssRules;
|
|
27
|
+
} catch (err) {
|
|
28
|
+
incompletes++;
|
|
29
|
+
this.#fetchProms.push(this.#fetchAndOverride(document.styleSheets[i], ss.href));
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
if (incompletes) this.invalidStylesheetsDetected = true;
|
|
35
|
+
return incompletes;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Resolves promise once all stylesheets have been fetched and overridden
|
|
40
|
+
* @returns {Promise}
|
|
41
|
+
*/
|
|
42
|
+
async fix() {
|
|
43
|
+
await Promise.all(this.#fetchProms);
|
|
44
|
+
this.#fetchProms = [];
|
|
45
|
+
const failedToFix = this.failedToFix;
|
|
46
|
+
this.failedToFix = false;
|
|
47
|
+
return failedToFix;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Fetches stylesheet contents and overrides the target getters
|
|
52
|
+
* @param {*} target - The stylesheet object target - ex. document.styleSheets[0]
|
|
53
|
+
* @param {*} href - The asset href to fetch
|
|
54
|
+
* @returns {Promise}
|
|
55
|
+
*/
|
|
56
|
+
async #fetchAndOverride(target, href) {
|
|
57
|
+
const stylesheetContents = await originals.FETCH.bind(window)(href);
|
|
58
|
+
if (!stylesheetContents.ok) {
|
|
59
|
+
this.failedToFix = true;
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
const stylesheetText = await stylesheetContents.text();
|
|
63
|
+
try {
|
|
64
|
+
const cssSheet = new CSSStyleSheet();
|
|
65
|
+
await cssSheet.replace(stylesheetText);
|
|
66
|
+
Object.defineProperty(target, 'cssRules', {
|
|
67
|
+
get() {
|
|
68
|
+
return cssSheet.cssRules;
|
|
69
|
+
}
|
|
70
|
+
});
|
|
71
|
+
Object.defineProperty(target, 'rules', {
|
|
72
|
+
get() {
|
|
73
|
+
return cssSheet.rules;
|
|
74
|
+
}
|
|
75
|
+
});
|
|
76
|
+
} catch (err) {
|
|
77
|
+
// cant make new dynamic stylesheets, browser likely doesn't support `.replace()`...
|
|
78
|
+
// this is appended in prep of forking rrweb
|
|
79
|
+
Object.defineProperty(target, 'cssText', {
|
|
80
|
+
get() {
|
|
81
|
+
return stylesheetText;
|
|
82
|
+
}
|
|
83
|
+
});
|
|
84
|
+
this.failedToFix = true;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
export const stylesheetEvaluator = new StylesheetEvaluator();
|
|
@@ -244,7 +244,6 @@ export class Aggregate extends AggregateBase {
|
|
|
244
244
|
}); // sends first stn harvest immediately
|
|
245
245
|
startupBuffer.decide(true); // signal to ALLOW & process data in EE's buffer into internal nodes queued for next harvest
|
|
246
246
|
}
|
|
247
|
-
|
|
248
247
|
#onHarvestFinished(result) {
|
|
249
248
|
if (result.sent && result.responseText && !this.ptid) {
|
|
250
249
|
// continue interval harvest only if ptid was returned by server on the first
|
|
@@ -283,7 +282,6 @@ export class Aggregate extends AggregateBase {
|
|
|
283
282
|
if (currentMode === MODE.OFF && Object.keys(this.trace).length === 0) return;
|
|
284
283
|
if (currentMode === MODE.ERROR) return; // Trace in this mode should never be harvesting, even on unload
|
|
285
284
|
}
|
|
286
|
-
|
|
287
285
|
return this.takeSTNs(options.retry);
|
|
288
286
|
}
|
|
289
287
|
|