@newrelic/browser-agent 1.233.0 → 1.234.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/dist/cjs/cdn/experimental.js +27 -0
- package/dist/cjs/common/config/state/init.js +1 -1
- package/dist/cjs/common/constants/env.cdn.js +1 -1
- package/dist/cjs/common/constants/env.npm.js +1 -1
- package/dist/cjs/common/event-emitter/contextual-ee.test.js +10 -10
- package/dist/cjs/common/harvest/harvest-scheduler.js +18 -3
- package/dist/cjs/common/harvest/harvest-scheduler.test.js +39 -0
- package/dist/cjs/common/harvest/harvest.js +14 -34
- package/dist/cjs/common/harvest/harvest.test.js +224 -0
- package/dist/cjs/common/url/encode.js +2 -2
- package/dist/cjs/common/util/console.test.js +30 -0
- package/dist/cjs/common/util/get-or-set.js +8 -1
- package/dist/cjs/common/util/get-or-set.test.js +47 -0
- package/dist/cjs/common/util/stringify.test.js +48 -0
- package/dist/cjs/common/util/submit-data.js +15 -15
- package/dist/cjs/common/util/submit-data.test.js +221 -0
- package/dist/cjs/common/util/traverse.js +19 -27
- package/dist/cjs/common/util/traverse.test.js +44 -0
- package/dist/cjs/features/metrics/aggregate/endpoint-map.js +14 -0
- package/dist/cjs/features/metrics/aggregate/index.js +3 -2
- package/dist/cjs/features/metrics/instrument/index.js +0 -2
- package/dist/cjs/features/page_view_event/aggregate/index.js +58 -44
- package/dist/cjs/features/session_replay/aggregate/index.js +10 -7
- package/dist/cjs/loaders/configure/configure.js +0 -1
- package/dist/esm/cdn/experimental.js +24 -0
- package/dist/esm/common/config/state/init.js +1 -1
- package/dist/esm/common/constants/env.cdn.js +1 -1
- package/dist/esm/common/constants/env.npm.js +1 -1
- package/dist/esm/common/event-emitter/contextual-ee.test.js +10 -10
- package/dist/esm/common/harvest/harvest-scheduler.js +18 -3
- package/dist/esm/common/harvest/harvest-scheduler.test.js +37 -0
- package/dist/esm/common/harvest/harvest.js +14 -34
- package/dist/esm/common/harvest/harvest.test.js +222 -0
- package/dist/esm/common/url/encode.js +2 -2
- package/dist/esm/common/util/console.test.js +28 -0
- package/dist/esm/common/util/get-or-set.js +8 -1
- package/dist/esm/common/util/get-or-set.test.js +45 -0
- package/dist/esm/common/util/stringify.test.js +46 -0
- package/dist/esm/common/util/submit-data.js +15 -15
- package/dist/esm/common/util/submit-data.test.js +219 -0
- package/dist/esm/common/util/traverse.js +19 -27
- package/dist/esm/common/util/traverse.test.js +42 -0
- package/dist/esm/features/metrics/aggregate/endpoint-map.js +7 -0
- package/dist/esm/features/metrics/aggregate/index.js +3 -2
- package/dist/esm/features/metrics/instrument/index.js +0 -2
- package/dist/esm/features/page_view_event/aggregate/index.js +58 -44
- package/dist/esm/features/session_replay/aggregate/index.js +10 -7
- package/dist/esm/loaders/configure/configure.js +0 -1
- package/dist/types/cdn/experimental.d.ts +2 -0
- package/dist/types/cdn/experimental.d.ts.map +1 -0
- package/dist/types/common/config/state/init.d.ts.map +1 -1
- package/dist/types/common/context/shared-context.d.ts.map +1 -1
- package/dist/types/common/harvest/harvest-scheduler.d.ts +26 -3
- package/dist/types/common/harvest/harvest-scheduler.d.ts.map +1 -1
- package/dist/types/common/harvest/harvest.d.ts +2 -2
- package/dist/types/common/harvest/harvest.d.ts.map +1 -1
- package/dist/types/common/timer/timer.d.ts.map +1 -1
- package/dist/types/common/util/get-or-set.d.ts +9 -1
- package/dist/types/common/util/get-or-set.d.ts.map +1 -1
- package/dist/types/common/util/submit-data.d.ts +14 -10
- package/dist/types/common/util/submit-data.d.ts.map +1 -1
- package/dist/types/common/util/traverse.d.ts +10 -1
- package/dist/types/common/util/traverse.d.ts.map +1 -1
- package/dist/types/common/window/nreum.d.ts.map +1 -1
- package/dist/types/features/metrics/aggregate/endpoint-map.d.ts +8 -0
- package/dist/types/features/metrics/aggregate/endpoint-map.d.ts.map +1 -0
- package/dist/types/features/metrics/aggregate/index.d.ts.map +1 -1
- package/dist/types/features/metrics/aggregate/polyfill-detection.es5.d.ts.map +1 -1
- package/dist/types/features/metrics/instrument/index.d.ts.map +1 -1
- package/dist/types/features/page_view_event/aggregate/index.d.ts.map +1 -1
- package/dist/types/features/page_view_event/instrument/index.d.ts.map +1 -1
- package/dist/types/features/session_replay/aggregate/index.d.ts +4 -0
- package/dist/types/features/session_replay/aggregate/index.d.ts.map +1 -1
- package/dist/types/features/utils/handler-cache.d.ts.map +1 -1
- package/dist/types/loaders/configure/configure.d.ts.map +1 -1
- package/package.json +8 -3
- package/src/cdn/experimental.js +36 -0
- package/src/common/config/state/init.js +1 -2
- package/src/common/context/shared-context.js +0 -1
- package/src/common/event-emitter/contextual-ee.test.js +10 -10
- package/src/common/harvest/harvest-scheduler.js +18 -3
- package/src/common/harvest/harvest-scheduler.test.js +25 -0
- package/src/common/harvest/harvest.js +15 -20
- package/src/common/harvest/harvest.test.js +169 -0
- package/src/common/session/session-entity.test.js +0 -1
- package/src/common/timer/timer.js +0 -1
- package/src/common/url/encode.js +2 -2
- package/src/common/util/console.test.js +34 -0
- package/src/common/util/get-or-set.js +8 -1
- package/src/common/util/get-or-set.test.js +58 -0
- package/src/common/util/stringify.test.js +49 -0
- package/src/common/util/submit-data.js +15 -16
- package/src/common/util/submit-data.test.js +218 -0
- package/src/common/util/traverse.js +18 -27
- package/src/common/util/traverse.test.js +50 -0
- package/src/common/window/nreum.js +0 -1
- package/src/features/metrics/aggregate/endpoint-map.js +7 -0
- package/src/features/metrics/aggregate/index.js +3 -2
- package/src/features/metrics/aggregate/polyfill-detection.es5.js +0 -1
- package/src/features/metrics/instrument/index.js +0 -2
- package/src/features/page_view_event/aggregate/index.js +48 -51
- package/src/features/page_view_event/instrument/index.js +0 -1
- package/src/features/session_replay/aggregate/index.js +12 -7
- package/src/features/utils/handler-cache.js +0 -1
- package/src/loaders/configure/configure.js +0 -1
- package/dist/cjs/common/util/s-hash.js +0 -19
- package/dist/cjs/features/metrics/instrument/workers-helper.js +0 -124
- package/dist/esm/common/util/s-hash.js +0 -13
- package/dist/esm/features/metrics/instrument/workers-helper.js +0 -119
- package/dist/types/common/util/s-hash.d.ts +0 -2
- package/dist/types/common/util/s-hash.d.ts.map +0 -1
- package/dist/types/features/metrics/instrument/workers-helper.d.ts +0 -7
- package/dist/types/features/metrics/instrument/workers-helper.d.ts.map +0 -1
- package/src/common/util/s-hash.js +0 -14
- package/src/features/metrics/instrument/workers-helper.js +0 -113
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
var _stringify = require("./stringify");
|
|
4
|
+
var mockEmit = jest.fn();
|
|
5
|
+
jest.mock('../event-emitter/contextual-ee', () => ({
|
|
6
|
+
__esModule: true,
|
|
7
|
+
get ee() {
|
|
8
|
+
return {
|
|
9
|
+
emit: mockEmit
|
|
10
|
+
};
|
|
11
|
+
}
|
|
12
|
+
}));
|
|
13
|
+
test('should return a JSON string representation of the value', () => {
|
|
14
|
+
const obj = {
|
|
15
|
+
a: 1,
|
|
16
|
+
b: {
|
|
17
|
+
nested: true
|
|
18
|
+
}
|
|
19
|
+
};
|
|
20
|
+
const expected = '{"a":1,"b":{"nested":true}}';
|
|
21
|
+
const result = (0, _stringify.stringify)(obj);
|
|
22
|
+
expect(result).toBe(expected);
|
|
23
|
+
});
|
|
24
|
+
test('should handle circular references and exclude them from the JSON output', () => {
|
|
25
|
+
const obj = {
|
|
26
|
+
a: 1
|
|
27
|
+
};
|
|
28
|
+
obj.b = obj; // Create a circular reference
|
|
29
|
+
const expected = '{"a":1}';
|
|
30
|
+
const result = (0, _stringify.stringify)(obj);
|
|
31
|
+
expect(result).toBe(expected);
|
|
32
|
+
});
|
|
33
|
+
test('should handle non-object values and return their string representation', () => {
|
|
34
|
+
const value = 42;
|
|
35
|
+
const expected = '42';
|
|
36
|
+
const result = (0, _stringify.stringify)(value);
|
|
37
|
+
expect(result).toBe(expected);
|
|
38
|
+
});
|
|
39
|
+
test('should emit an "internal-error" event if an error occurs during JSON.stringify', () => {
|
|
40
|
+
const obj = {
|
|
41
|
+
a: 1
|
|
42
|
+
};
|
|
43
|
+
jest.spyOn(JSON, 'stringify').mockImplementation(() => {
|
|
44
|
+
throw new Error('message');
|
|
45
|
+
});
|
|
46
|
+
(0, _stringify.stringify)(obj);
|
|
47
|
+
expect(mockEmit).toHaveBeenCalledWith('internal-error', expect.any(Array));
|
|
48
|
+
});
|
|
@@ -50,6 +50,14 @@ submitData.jsonp = function jsonp(_ref) {
|
|
|
50
50
|
// do nothing
|
|
51
51
|
}
|
|
52
52
|
};
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Performs an asynchronous GET request using XMLHttpRequest.
|
|
56
|
+
*
|
|
57
|
+
* @param {Object} args - An object containing a `url` property.
|
|
58
|
+
* @param {string} args.url - The URL to send the GET request to.
|
|
59
|
+
* @returns {XMLHttpRequest} - An XMLHttpRequest object.
|
|
60
|
+
*/
|
|
53
61
|
submitData.xhrGet = function xhrGet(_ref2) {
|
|
54
62
|
let {
|
|
55
63
|
url
|
|
@@ -63,18 +71,18 @@ submitData.xhrGet = function xhrGet(_ref2) {
|
|
|
63
71
|
|
|
64
72
|
/**
|
|
65
73
|
* Send via XHR
|
|
66
|
-
* @param {Object} args - The args
|
|
67
|
-
* @param {string} args.url - The URL to send to
|
|
68
|
-
* @param {string=} args.body - The Stringified body
|
|
69
|
-
* @param {boolean=} args.sync - Run XHR
|
|
70
|
-
* @param {string=} [args.method=POST] - The XHR method to use
|
|
71
|
-
* @param {{key: string, value: string}[]} [args.headers] - The headers to attach
|
|
74
|
+
* @param {Object} args - The args.
|
|
75
|
+
* @param {string} args.url - The URL to send to.
|
|
76
|
+
* @param {string=} args.body - The Stringified body. Default null to prevent IE11 from breaking.
|
|
77
|
+
* @param {boolean=} args.sync - Run XHR synchronously.
|
|
78
|
+
* @param {string=} [args.method=POST] - The XHR method to use.
|
|
79
|
+
* @param {{key: string, value: string}[]} [args.headers] - The headers to attach.
|
|
72
80
|
* @returns {XMLHttpRequest}
|
|
73
81
|
*/
|
|
74
82
|
submitData.xhr = function xhr(_ref3) {
|
|
75
83
|
let {
|
|
76
84
|
url,
|
|
77
|
-
body,
|
|
85
|
+
body = null,
|
|
78
86
|
sync,
|
|
79
87
|
method = 'POST',
|
|
80
88
|
headers = [{
|
|
@@ -97,13 +105,6 @@ submitData.xhr = function xhr(_ref3) {
|
|
|
97
105
|
return request;
|
|
98
106
|
};
|
|
99
107
|
|
|
100
|
-
/**
|
|
101
|
-
* Unused at the moment -- DEPRECATED
|
|
102
|
-
*/
|
|
103
|
-
// submitData.xhrSync = function xhrSync (url, body) {
|
|
104
|
-
// return submitData.xhr(url, body, true)
|
|
105
|
-
// }
|
|
106
|
-
|
|
107
108
|
/**
|
|
108
109
|
* Send by appending an <img> element to the page. Do NOT call this function outside of a guaranteed web window environment.
|
|
109
110
|
* @param {Object} args - The args
|
|
@@ -114,7 +115,6 @@ submitData.img = function img(_ref4) {
|
|
|
114
115
|
let {
|
|
115
116
|
url
|
|
116
117
|
} = _ref4;
|
|
117
|
-
console.log('img url', url);
|
|
118
118
|
var element = new Image();
|
|
119
119
|
element.src = url;
|
|
120
120
|
return element;
|
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
var _submitData = require("./submit-data");
|
|
4
|
+
/**
|
|
5
|
+
* @jest-environment jsdom
|
|
6
|
+
* @jest-environment-options {"html": "<html><head><script></script></head><body></body></html>", "url": "https://example.com/"}
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
const mockWorkerScope = jest.fn().mockImplementation(() => false);
|
|
10
|
+
jest.mock('./global-scope', () => ({
|
|
11
|
+
__esModule: true,
|
|
12
|
+
get isWorkerScope() {
|
|
13
|
+
return mockWorkerScope();
|
|
14
|
+
}
|
|
15
|
+
}));
|
|
16
|
+
const url = 'https://example.com/api';
|
|
17
|
+
beforeEach(() => {
|
|
18
|
+
jest.restoreAllMocks();
|
|
19
|
+
mockWorkerScope.mockReturnValue(false);
|
|
20
|
+
});
|
|
21
|
+
describe('submitData.jsonp', () => {
|
|
22
|
+
// This test requires a script tag to exist in the html set by this file's jest-environment-options header block.
|
|
23
|
+
test('should return an HTMLScriptElement when called from a web window environment', () => {
|
|
24
|
+
mockWorkerScope.mockReturnValue(false);
|
|
25
|
+
const jsonp = 'callback';
|
|
26
|
+
const result = _submitData.submitData.jsonp({
|
|
27
|
+
url,
|
|
28
|
+
jsonp
|
|
29
|
+
});
|
|
30
|
+
expect(result).toBeInstanceOf(HTMLScriptElement);
|
|
31
|
+
expect(result.type).toBe('text/javascript');
|
|
32
|
+
expect(result.src).toBe(url + '&jsonp=' + jsonp);
|
|
33
|
+
});
|
|
34
|
+
test('should try to use importScripts when called from a worker scope', () => {
|
|
35
|
+
mockWorkerScope.mockReturnValueOnce(true);
|
|
36
|
+
const jsonp = 'callback';
|
|
37
|
+
global.importScripts = jest.fn();
|
|
38
|
+
_submitData.submitData.jsonp({
|
|
39
|
+
url,
|
|
40
|
+
jsonp
|
|
41
|
+
});
|
|
42
|
+
expect(importScripts).toHaveBeenCalledWith(url + '&jsonp=' + jsonp);
|
|
43
|
+
delete global.importScripts;
|
|
44
|
+
});
|
|
45
|
+
test('should fall back to an xhrGet call and return false when called from a worker scope', () => {
|
|
46
|
+
mockWorkerScope.mockReturnValueOnce(true);
|
|
47
|
+
const jsonp = 'callback';
|
|
48
|
+
jest.spyOn(_submitData.submitData, 'xhrGet').mockImplementation(jest.fn());
|
|
49
|
+
const result = _submitData.submitData.jsonp({
|
|
50
|
+
url,
|
|
51
|
+
jsonp
|
|
52
|
+
});
|
|
53
|
+
expect(result).toBe(false);
|
|
54
|
+
expect(_submitData.submitData.xhrGet).toHaveBeenCalledTimes(1);
|
|
55
|
+
});
|
|
56
|
+
test('should not throw an error if any error occurs during execution', () => {
|
|
57
|
+
jest.spyOn(document, 'createElement').mockImplementation(() => {
|
|
58
|
+
throw new Error('message');
|
|
59
|
+
});
|
|
60
|
+
const jsonp = 'callback';
|
|
61
|
+
expect(() => {
|
|
62
|
+
_submitData.submitData.jsonp({
|
|
63
|
+
url,
|
|
64
|
+
jsonp
|
|
65
|
+
});
|
|
66
|
+
}).not.toThrow();
|
|
67
|
+
});
|
|
68
|
+
});
|
|
69
|
+
describe('submitData.xhrGet', () => {
|
|
70
|
+
test('should return an XMLHttpRequest object', () => {
|
|
71
|
+
const result = _submitData.submitData.xhrGet({
|
|
72
|
+
url
|
|
73
|
+
});
|
|
74
|
+
expect(result).toBeInstanceOf(XMLHttpRequest);
|
|
75
|
+
});
|
|
76
|
+
test('should not throw an error if URL is not provided', () => {
|
|
77
|
+
expect(() => {
|
|
78
|
+
_submitData.submitData.xhrGet({});
|
|
79
|
+
}).not.toThrow();
|
|
80
|
+
});
|
|
81
|
+
test('should not throw an error if an invalid URL is provided', () => {
|
|
82
|
+
expect(() => {
|
|
83
|
+
_submitData.submitData.xhrGet({
|
|
84
|
+
url: 'invalid url'
|
|
85
|
+
});
|
|
86
|
+
}).not.toThrow();
|
|
87
|
+
});
|
|
88
|
+
});
|
|
89
|
+
describe('submitData.xhr', () => {
|
|
90
|
+
test('should return an XMLHttpRequest object', () => {
|
|
91
|
+
const result = _submitData.submitData.xhrGet({
|
|
92
|
+
url
|
|
93
|
+
});
|
|
94
|
+
expect(result).toBeInstanceOf(XMLHttpRequest);
|
|
95
|
+
});
|
|
96
|
+
test('should not throw an error if URL is not provided', () => {
|
|
97
|
+
expect(() => {
|
|
98
|
+
_submitData.submitData.xhr({});
|
|
99
|
+
}).not.toThrow();
|
|
100
|
+
});
|
|
101
|
+
test('should not throw an error if an invalid URL is provided', () => {
|
|
102
|
+
expect(() => {
|
|
103
|
+
_submitData.submitData.xhr({
|
|
104
|
+
url: 'invalid url'
|
|
105
|
+
});
|
|
106
|
+
}).not.toThrow();
|
|
107
|
+
});
|
|
108
|
+
test('should send a POST request by default', () => {
|
|
109
|
+
jest.spyOn(XMLHttpRequest.prototype, 'open');
|
|
110
|
+
_submitData.submitData.xhr({
|
|
111
|
+
url
|
|
112
|
+
});
|
|
113
|
+
expect(XMLHttpRequest.prototype.open).toHaveBeenCalledWith('POST', url, true);
|
|
114
|
+
});
|
|
115
|
+
test('should send a GET request if specified', () => {
|
|
116
|
+
jest.spyOn(XMLHttpRequest.prototype, 'open');
|
|
117
|
+
_submitData.submitData.xhr({
|
|
118
|
+
url,
|
|
119
|
+
method: 'GET'
|
|
120
|
+
});
|
|
121
|
+
expect(XMLHttpRequest.prototype.open).toHaveBeenCalledWith('GET', url, true);
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
// This test requires a same-origin url to be set by this file's jest-environment-options header block.
|
|
125
|
+
test('should send a request synchronously if specified', () => {
|
|
126
|
+
jest.spyOn(XMLHttpRequest.prototype, 'open');
|
|
127
|
+
_submitData.submitData.xhr({
|
|
128
|
+
url,
|
|
129
|
+
sync: true
|
|
130
|
+
});
|
|
131
|
+
expect(XMLHttpRequest.prototype.open).toHaveBeenCalledWith('POST', url, false);
|
|
132
|
+
});
|
|
133
|
+
test('should set custom headers if provided', () => {
|
|
134
|
+
const headers = [{
|
|
135
|
+
key: 'Content-Type',
|
|
136
|
+
value: 'application/json'
|
|
137
|
+
}];
|
|
138
|
+
jest.spyOn(XMLHttpRequest.prototype, 'setRequestHeader');
|
|
139
|
+
_submitData.submitData.xhr({
|
|
140
|
+
url,
|
|
141
|
+
headers
|
|
142
|
+
});
|
|
143
|
+
headers.forEach(header => {
|
|
144
|
+
expect(XMLHttpRequest.prototype.setRequestHeader).toHaveBeenCalledWith(header.key, header.value);
|
|
145
|
+
});
|
|
146
|
+
});
|
|
147
|
+
test('should send a request with the specified body', () => {
|
|
148
|
+
const body = JSON.stringify({
|
|
149
|
+
key: 'value'
|
|
150
|
+
});
|
|
151
|
+
jest.spyOn(XMLHttpRequest.prototype, 'send');
|
|
152
|
+
_submitData.submitData.xhr({
|
|
153
|
+
url,
|
|
154
|
+
body
|
|
155
|
+
});
|
|
156
|
+
expect(XMLHttpRequest.prototype.send).toHaveBeenCalledWith(body);
|
|
157
|
+
});
|
|
158
|
+
});
|
|
159
|
+
describe('submitData.img', () => {
|
|
160
|
+
test('should return an HTMLImageElement', () => {
|
|
161
|
+
const imageUrl = 'https://example.com/image.png';
|
|
162
|
+
const result = _submitData.submitData.img({
|
|
163
|
+
url: imageUrl
|
|
164
|
+
});
|
|
165
|
+
expect(result).toBeInstanceOf(HTMLImageElement);
|
|
166
|
+
});
|
|
167
|
+
test('should not throw an error if URL is not provided', () => {
|
|
168
|
+
expect(() => {
|
|
169
|
+
_submitData.submitData.img({});
|
|
170
|
+
}).not.toThrow();
|
|
171
|
+
});
|
|
172
|
+
test('should set the src attribute of the image element to the provided URL', () => {
|
|
173
|
+
const imageUrl = 'https://example.com/image.png';
|
|
174
|
+
const result = _submitData.submitData.img({
|
|
175
|
+
url: imageUrl
|
|
176
|
+
});
|
|
177
|
+
expect(result.src).toBe(imageUrl);
|
|
178
|
+
});
|
|
179
|
+
});
|
|
180
|
+
describe('submitData.beacon', () => {
|
|
181
|
+
test('should return true when beacon request succeeds', () => {
|
|
182
|
+
const body = JSON.stringify({
|
|
183
|
+
key: 'value'
|
|
184
|
+
});
|
|
185
|
+
window.navigator.sendBeacon = {
|
|
186
|
+
bind: jest.fn(() => () => true)
|
|
187
|
+
};
|
|
188
|
+
const result = _submitData.submitData.beacon({
|
|
189
|
+
url,
|
|
190
|
+
body
|
|
191
|
+
});
|
|
192
|
+
expect(result).toBe(true);
|
|
193
|
+
});
|
|
194
|
+
test('should return false when beacon request fails', () => {
|
|
195
|
+
const body = JSON.stringify({
|
|
196
|
+
key: 'value'
|
|
197
|
+
});
|
|
198
|
+
window.navigator.sendBeacon = {
|
|
199
|
+
bind: jest.fn(() => () => {
|
|
200
|
+
throw new Error('message');
|
|
201
|
+
})
|
|
202
|
+
};
|
|
203
|
+
const result = _submitData.submitData.beacon({
|
|
204
|
+
url,
|
|
205
|
+
body
|
|
206
|
+
});
|
|
207
|
+
expect(result).toBe(false);
|
|
208
|
+
});
|
|
209
|
+
test('should error if sendBeacon is not supported', () => {
|
|
210
|
+
const body = JSON.stringify({
|
|
211
|
+
key: 'value'
|
|
212
|
+
});
|
|
213
|
+
window.navigator.sendBeacon = undefined;
|
|
214
|
+
expect(() => {
|
|
215
|
+
_submitData.submitData.beacon({
|
|
216
|
+
url,
|
|
217
|
+
body
|
|
218
|
+
});
|
|
219
|
+
}).toThrow();
|
|
220
|
+
});
|
|
221
|
+
});
|
|
@@ -9,33 +9,25 @@ exports.applyFnToProps = applyFnToProps;
|
|
|
9
9
|
* SPDX-License-Identifier: Apache-2.0
|
|
10
10
|
*/
|
|
11
11
|
|
|
12
|
-
|
|
13
|
-
function
|
|
12
|
+
/**
|
|
13
|
+
* Applies a function to properties of a specified type in an object, recursively.
|
|
14
|
+
*
|
|
15
|
+
* @param {Object} obj - The object to apply the function to.
|
|
16
|
+
* @param {Function} fn - The function to apply to matching properties.
|
|
17
|
+
* @param {string} [type='string'] - The type of properties to apply the function to.
|
|
18
|
+
* @param {Array<string>} [ignoreKeys=[]] - The keys of properties to ignore and not modify.
|
|
19
|
+
* @returns {Object} - The object with function recursively applied.
|
|
20
|
+
*/
|
|
21
|
+
function applyFnToProps(obj, fn) {
|
|
22
|
+
let type = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 'string';
|
|
23
|
+
let ignoreKeys = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : [];
|
|
14
24
|
if (!obj || typeof obj !== 'object') return obj;
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
// eslint-disable-next-line
|
|
21
|
-
if (obj.hasOwnProperty(property)) {
|
|
22
|
-
if (typeof obj[property] === 'object') {
|
|
23
|
-
traverse(obj[property]);
|
|
24
|
-
} else {
|
|
25
|
-
if (typeof obj[property] === type && !shouldIgnore(property)) obj[property] = fn(obj[property]);
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
return obj;
|
|
30
|
-
}
|
|
31
|
-
function shouldIgnore(key) {
|
|
32
|
-
var ignore = false;
|
|
33
|
-
for (var i = 0; i < ignoreKeys.length; i++) {
|
|
34
|
-
if (ignoreKeys[i] === key) {
|
|
35
|
-
ignore = true;
|
|
36
|
-
break;
|
|
37
|
-
}
|
|
25
|
+
Object.keys(obj).forEach(property => {
|
|
26
|
+
if (typeof obj[property] === 'object') {
|
|
27
|
+
applyFnToProps(obj[property], fn, type, ignoreKeys);
|
|
28
|
+
} else {
|
|
29
|
+
if (typeof obj[property] === type && !ignoreKeys.includes(property)) obj[property] = fn(obj[property]);
|
|
38
30
|
}
|
|
39
|
-
|
|
40
|
-
|
|
31
|
+
});
|
|
32
|
+
return obj;
|
|
41
33
|
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
var _traverse = require("./traverse");
|
|
4
|
+
test.each(['not an object', null, undefined])('should return input unchanged when input is %p', input => {
|
|
5
|
+
const result = (0, _traverse.applyFnToProps)(input, jest.fn());
|
|
6
|
+
expect(result).toEqual(input);
|
|
7
|
+
});
|
|
8
|
+
test('should apply the provided function only to properties of the specified type', () => {
|
|
9
|
+
const obj = {
|
|
10
|
+
stringProp: 'Hello',
|
|
11
|
+
numberProp: 42,
|
|
12
|
+
arrayProp: [1, 2, 3],
|
|
13
|
+
objectProp: {
|
|
14
|
+
foo: 'bar'
|
|
15
|
+
}
|
|
16
|
+
};
|
|
17
|
+
const fn = jest.fn(value => value.toUpperCase());
|
|
18
|
+
const result = (0, _traverse.applyFnToProps)(obj, fn, 'string');
|
|
19
|
+
expect(result.stringProp).toBe('HELLO');
|
|
20
|
+
expect(result.numberProp).toBe(42);
|
|
21
|
+
expect(result.arrayProp).toEqual([1, 2, 3]);
|
|
22
|
+
expect(result.objectProp).toEqual({
|
|
23
|
+
foo: 'BAR'
|
|
24
|
+
});
|
|
25
|
+
expect(fn).toHaveBeenCalledTimes(2);
|
|
26
|
+
expect(fn).toHaveBeenCalledWith('Hello');
|
|
27
|
+
expect(fn).toHaveBeenCalledWith('bar');
|
|
28
|
+
});
|
|
29
|
+
test('should ignore properties specified in ignoreKeys', () => {
|
|
30
|
+
const obj = {
|
|
31
|
+
a: 1,
|
|
32
|
+
b: 2,
|
|
33
|
+
c: 3
|
|
34
|
+
};
|
|
35
|
+
const fn = jest.fn(value => value + 1);
|
|
36
|
+
const ignoreKeys = ['c'];
|
|
37
|
+
const result = (0, _traverse.applyFnToProps)(obj, fn, 'number', ignoreKeys);
|
|
38
|
+
expect(result.a).toBe(2);
|
|
39
|
+
expect(result.b).toBe(3);
|
|
40
|
+
expect(result.c).toBe(3);
|
|
41
|
+
expect(fn).toHaveBeenCalledTimes(2);
|
|
42
|
+
expect(fn).toHaveBeenCalledWith(1);
|
|
43
|
+
expect(fn).toHaveBeenCalledWith(2);
|
|
44
|
+
});
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.endpointMap = void 0;
|
|
7
|
+
const endpointMap = {
|
|
8
|
+
rum: '1',
|
|
9
|
+
events: 'Events',
|
|
10
|
+
ins: 'Ins',
|
|
11
|
+
jserrors: 'Jserrors',
|
|
12
|
+
resources: 'Resources'
|
|
13
|
+
};
|
|
14
|
+
exports.endpointMap = endpointMap;
|
|
@@ -19,6 +19,7 @@ var _eventListenerOpts = require("../../../common/event-listener/event-listener-
|
|
|
19
19
|
var _globalScope = require("../../../common/util/global-scope");
|
|
20
20
|
var _aggregateBase = require("../../utils/aggregate-base");
|
|
21
21
|
var _stringify = require("../../../common/util/stringify");
|
|
22
|
+
var _endpointMap = require("./endpoint-map");
|
|
22
23
|
class Aggregate extends _aggregateBase.AggregateBase {
|
|
23
24
|
static featureName = _constants.FEATURE_NAME;
|
|
24
25
|
constructor(agentIdentifier, aggregator) {
|
|
@@ -137,12 +138,12 @@ class Aggregate extends _aggregateBase.AggregateBase {
|
|
|
137
138
|
|
|
138
139
|
// Capture per-agent bytes sent for each endpoint (see harvest) and RUM call (see page_view_event aggregator).
|
|
139
140
|
Object.keys(agentRuntime.bytesSent).forEach(endpoint => {
|
|
140
|
-
this.storeSupportabilityMetrics("PageSession/Endpoint/".concat(
|
|
141
|
+
this.storeSupportabilityMetrics("PageSession/Endpoint/".concat(_endpointMap.endpointMap[endpoint], "/BytesSent"), agentRuntime.bytesSent[endpoint]);
|
|
141
142
|
});
|
|
142
143
|
|
|
143
144
|
// Capture per-agent query bytes sent for each endpoint (see harvest) and RUM call (see page_view_event aggregator).
|
|
144
145
|
Object.keys(agentRuntime.bytesSent).forEach(endpoint => {
|
|
145
|
-
this.storeSupportabilityMetrics("PageSession/Endpoint/".concat(
|
|
146
|
+
this.storeSupportabilityMetrics("PageSession/Endpoint/".concat(_endpointMap.endpointMap[endpoint], "/QueryBytesSent"), agentRuntime.queryBytesSent[endpoint]);
|
|
146
147
|
});
|
|
147
148
|
|
|
148
149
|
// Capture metrics for session trace if active (`ptid` is set when returned by replay ingest).
|
|
@@ -5,7 +5,6 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
5
5
|
});
|
|
6
6
|
exports.Instrument = void 0;
|
|
7
7
|
var _instrumentBase = require("../../utils/instrument-base");
|
|
8
|
-
var _workersHelper = require("./workers-helper");
|
|
9
8
|
var _constants = require("../constants");
|
|
10
9
|
var _handle = require("../../../common/event-emitter/handle");
|
|
11
10
|
var _features = require("../../../loaders/features/features");
|
|
@@ -14,7 +13,6 @@ class Instrument extends _instrumentBase.InstrumentBase {
|
|
|
14
13
|
constructor(agentIdentifier, aggregator) {
|
|
15
14
|
let auto = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : true;
|
|
16
15
|
super(agentIdentifier, aggregator, _constants.FEATURE_NAME, auto);
|
|
17
|
-
(0, _workersHelper.insertSupportMetrics)(tag => (0, _handle.handle)(_constants.SUPPORTABILITY_METRIC_CHANNEL, [tag], undefined, _features.FEATURE_NAMES.metrics, this.ee));
|
|
18
16
|
this.importAggregator();
|
|
19
17
|
}
|
|
20
18
|
}
|
|
@@ -8,22 +8,20 @@ var _handle = require("../../../common/event-emitter/handle");
|
|
|
8
8
|
var _features = require("../../../loaders/features/features");
|
|
9
9
|
var _iosVersion = require("../../../common/browser-version/ios-version");
|
|
10
10
|
var _webVitals = require("web-vitals");
|
|
11
|
-
var _mapOwn = require("../../../common/util/map-own");
|
|
12
|
-
var _encode = require("../../../common/url/encode");
|
|
13
11
|
var _navTiming = require("../../../common/timing/nav-timing");
|
|
14
12
|
var _stringify = require("../../../common/util/stringify");
|
|
15
13
|
var _paintMetrics = require("../../../common/metrics/paint-metrics");
|
|
16
|
-
var _submitData = require("../../../common/util/submit-data");
|
|
17
14
|
var _config = require("../../../common/config/config");
|
|
18
|
-
var
|
|
15
|
+
var _harvest = require("../../../common/harvest/harvest");
|
|
19
16
|
var CONSTANTS = _interopRequireWildcard(require("../constants"));
|
|
20
17
|
var _initializedFeatures = require("./initialized-features");
|
|
21
18
|
var _globalScope = require("../../../common/util/global-scope");
|
|
22
19
|
var _drain = require("../../../common/drain/drain");
|
|
20
|
+
var _featureFlags = require("../../../common/util/feature-flags");
|
|
21
|
+
var _console = require("../../../common/util/console");
|
|
23
22
|
var _aggregateBase = require("../../utils/aggregate-base");
|
|
24
23
|
function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function (nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); }
|
|
25
24
|
function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; }
|
|
26
|
-
const jsonp = 'NREUM.setToken';
|
|
27
25
|
class Aggregate extends _aggregateBase.AggregateBase {
|
|
28
26
|
static featureName = CONSTANTS.FEATURE_NAME;
|
|
29
27
|
constructor(agentIdentifier, aggregator) {
|
|
@@ -62,6 +60,8 @@ class Aggregate extends _aggregateBase.AggregateBase {
|
|
|
62
60
|
}
|
|
63
61
|
sendRum() {
|
|
64
62
|
const info = (0, _config.getInfo)(this.agentIdentifier);
|
|
63
|
+
const agentRuntime = (0, _config.getRuntime)(this.agentIdentifier);
|
|
64
|
+
const harvester = new _harvest.Harvest(this);
|
|
65
65
|
if (!info.beacon) return;
|
|
66
66
|
if (info.queueTime) this.aggregator.store('measures', 'qt', {
|
|
67
67
|
value: info.queueTime
|
|
@@ -69,7 +69,6 @@ class Aggregate extends _aggregateBase.AggregateBase {
|
|
|
69
69
|
if (info.applicationTime) this.aggregator.store('measures', 'ap', {
|
|
70
70
|
value: info.applicationTime
|
|
71
71
|
});
|
|
72
|
-
const agentRuntime = (0, _config.getRuntime)(this.agentIdentifier);
|
|
73
72
|
|
|
74
73
|
// These 3 values should've been recorded after load and before this func runs. They are part of the minimum required for PageView events to be created.
|
|
75
74
|
// Following PR #428, which demands that all agents send RUM call, these need to be sent even outside of the main window context where PerformanceTiming
|
|
@@ -83,22 +82,27 @@ class Aggregate extends _aggregateBase.AggregateBase {
|
|
|
83
82
|
this.aggregator.store('measures', 'dc', {
|
|
84
83
|
value: _globalScope.isBrowserScope ? agentRuntime[CONSTANTS.FBTDC] : 0
|
|
85
84
|
});
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
85
|
+
const queryParameters = {
|
|
86
|
+
tt: info.ttGuid,
|
|
87
|
+
us: info.user,
|
|
88
|
+
ac: info.account,
|
|
89
|
+
pr: info.product,
|
|
90
|
+
af: (0, _initializedFeatures.getActivatedFeaturesFlags)(this.agentIdentifier).join(','),
|
|
91
|
+
...Object.entries(this.aggregator.get('measures') || {}).reduce((aggregator, _ref2) => {
|
|
92
|
+
let [metricName, measure] = _ref2;
|
|
93
|
+
aggregator[metricName] = measure.params?.value;
|
|
94
|
+
return aggregator;
|
|
95
|
+
}, {}),
|
|
96
|
+
xx: info.extra,
|
|
97
|
+
ua: info.userAttributes,
|
|
98
|
+
at: info.atts
|
|
99
|
+
};
|
|
100
|
+
let body;
|
|
101
|
+
if (typeof info.jsAttributes === 'object' && Object.keys(info.jsAttributes).length > 0) {
|
|
102
|
+
body = {
|
|
103
|
+
ja: info.jsAttributes
|
|
104
|
+
};
|
|
105
|
+
}
|
|
102
106
|
if (_globalScope.globalScope.performance) {
|
|
103
107
|
if (typeof PerformanceNavigationTiming !== 'undefined') {
|
|
104
108
|
// Navigation Timing level 2 API that replaced PerformanceTiming & PerformanceNavigation
|
|
@@ -107,14 +111,14 @@ class Aggregate extends _aggregateBase.AggregateBase {
|
|
|
107
111
|
timing: (0, _navTiming.addPT)(agentRuntime.offset, navTimingEntry, {}),
|
|
108
112
|
navigation: (0, _navTiming.addPN)(navTimingEntry, {})
|
|
109
113
|
};
|
|
110
|
-
|
|
114
|
+
queryParameters.perf = (0, _stringify.stringify)(perf);
|
|
111
115
|
} else if (typeof PerformanceTiming !== 'undefined') {
|
|
112
116
|
// Safari pre-15 did not support level 2 timing
|
|
113
117
|
const perf = {
|
|
114
118
|
timing: (0, _navTiming.addPT)(agentRuntime.offset, _globalScope.globalScope.performance.timing, {}, true),
|
|
115
119
|
navigation: (0, _navTiming.addPN)(_globalScope.globalScope.performance.navigation, {})
|
|
116
120
|
};
|
|
117
|
-
|
|
121
|
+
queryParameters.perf = (0, _stringify.stringify)(perf);
|
|
118
122
|
}
|
|
119
123
|
}
|
|
120
124
|
try {
|
|
@@ -123,32 +127,42 @@ class Aggregate extends _aggregateBase.AggregateBase {
|
|
|
123
127
|
entries.forEach(function (entry) {
|
|
124
128
|
if (!entry.startTime || entry.startTime <= 0) return;
|
|
125
129
|
if (entry.name === 'first-paint') {
|
|
126
|
-
|
|
130
|
+
queryParameters.fp = String(Math.floor(entry.startTime));
|
|
127
131
|
} else if (entry.name === 'first-contentful-paint') {
|
|
128
|
-
|
|
132
|
+
queryParameters.fcp = String(Math.floor(entry.startTime));
|
|
129
133
|
}
|
|
130
134
|
_paintMetrics.paintMetrics[entry.name] = Math.floor(entry.startTime); // this is consumed by Spa module
|
|
131
135
|
});
|
|
132
136
|
} catch (e) {}
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
137
|
+
harvester.send({
|
|
138
|
+
endpoint: 'rum',
|
|
139
|
+
payload: {
|
|
140
|
+
qs: queryParameters,
|
|
141
|
+
body
|
|
142
|
+
},
|
|
143
|
+
opts: {
|
|
144
|
+
needResponse: true,
|
|
145
|
+
sendEmptyBody: true
|
|
146
|
+
},
|
|
147
|
+
cbFinished: _ref3 => {
|
|
148
|
+
let {
|
|
149
|
+
status,
|
|
150
|
+
responseText
|
|
151
|
+
} = _ref3;
|
|
152
|
+
if (status >= 400) {
|
|
153
|
+
// Adding retry logic for the rum call will be a separate change
|
|
154
|
+
this.ee.abort();
|
|
155
|
+
return;
|
|
156
|
+
}
|
|
157
|
+
try {
|
|
158
|
+
(0, _featureFlags.activateFeatures)(JSON.parse(responseText), this.agentIdentifier);
|
|
159
|
+
(0, _drain.drain)(this.agentIdentifier, this.featureName);
|
|
160
|
+
} catch (err) {
|
|
161
|
+
this.ee.abort();
|
|
162
|
+
(0, _console.warn)('RUM call failed. Agent shutting down.');
|
|
163
|
+
}
|
|
164
|
+
}
|
|
148
165
|
});
|
|
149
|
-
// Usually `drain` is invoked automatically after processing feature flags contained in the JSONP callback from
|
|
150
|
-
// ingest (see `activateFeatures`), so when JSONP cannot execute (as with module workers), we drain manually.
|
|
151
|
-
if (!isValidJsonp) (0, _drain.drain)(this.agentIdentifier, CONSTANTS.FEATURE_NAME);
|
|
152
166
|
}
|
|
153
167
|
}
|
|
154
168
|
exports.Aggregate = Aggregate;
|