@newrelic/browser-agent 1.244.0 → 1.246.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 +23 -0
- package/dist/cjs/cdn/polyfills.js +5 -1
- package/dist/cjs/common/config/state/configurable.js +1 -1
- package/dist/cjs/common/config/state/init.js +1 -0
- package/dist/cjs/common/constants/env.cdn.js +1 -1
- package/dist/cjs/common/constants/env.npm.js +1 -1
- package/dist/cjs/common/session/session-entity.js +3 -2
- package/dist/cjs/common/url/parse-url.js +21 -44
- package/dist/cjs/common/util/type-check.js +14 -0
- package/dist/cjs/common/vitals/first-input-delay.js +1 -2
- package/dist/cjs/common/vitals/largest-contentful-paint.js +1 -2
- package/dist/cjs/common/vitals/vital-metric.js +2 -12
- package/dist/cjs/features/ajax/aggregate/gql.js +94 -0
- package/dist/cjs/features/ajax/aggregate/index.js +13 -1
- package/dist/cjs/features/ajax/instrument/index.js +2 -0
- package/dist/cjs/features/jserrors/aggregate/index.js +1 -1
- package/dist/cjs/features/page_view_event/aggregate/index.js +2 -0
- package/dist/cjs/features/page_view_timing/aggregate/index.js +10 -1
- package/dist/cjs/features/session_replay/aggregate/index.js +6 -6
- package/dist/cjs/features/session_trace/aggregate/index.js +14 -3
- package/dist/cjs/features/spa/aggregate/index.js +5 -3
- package/dist/cjs/features/spa/aggregate/serializer.js +7 -0
- package/dist/cjs/features/utils/instrument-base.js +1 -1
- package/dist/cjs/index.js +0 -7
- package/dist/cjs/loaders/api/api.js +2 -2
- package/dist/esm/cdn/polyfills.js +5 -1
- package/dist/esm/common/config/state/configurable.js +1 -1
- package/dist/esm/common/config/state/init.js +1 -0
- package/dist/esm/common/constants/env.cdn.js +1 -1
- package/dist/esm/common/constants/env.npm.js +1 -1
- package/dist/esm/common/session/session-entity.js +3 -2
- package/dist/esm/common/url/parse-url.js +22 -45
- package/dist/esm/common/util/type-check.js +8 -0
- package/dist/esm/common/vitals/first-input-delay.js +1 -2
- package/dist/esm/common/vitals/largest-contentful-paint.js +1 -2
- package/dist/esm/common/vitals/vital-metric.js +1 -11
- package/dist/esm/features/ajax/aggregate/gql.js +89 -0
- package/dist/esm/features/ajax/aggregate/index.js +13 -1
- package/dist/esm/features/ajax/instrument/index.js +2 -0
- package/dist/esm/features/jserrors/aggregate/index.js +1 -1
- package/dist/esm/features/page_view_event/aggregate/index.js +2 -0
- package/dist/esm/features/page_view_timing/aggregate/index.js +9 -0
- package/dist/esm/features/session_replay/aggregate/index.js +6 -6
- package/dist/esm/features/session_trace/aggregate/index.js +14 -3
- package/dist/esm/features/spa/aggregate/index.js +5 -3
- package/dist/esm/features/spa/aggregate/serializer.js +7 -0
- package/dist/esm/features/utils/instrument-base.js +1 -1
- package/dist/esm/index.js +0 -1
- package/dist/esm/loaders/api/api.js +2 -2
- package/dist/types/common/config/state/configurable.d.ts.map +1 -1
- package/dist/types/common/config/state/init.d.ts.map +1 -1
- package/dist/types/common/session/session-entity.d.ts.map +1 -1
- package/dist/types/common/url/parse-url.d.ts +12 -1
- package/dist/types/common/url/parse-url.d.ts.map +1 -1
- package/dist/types/common/util/type-check.d.ts +7 -0
- package/dist/types/common/util/type-check.d.ts.map +1 -0
- package/dist/types/common/vitals/vital-metric.d.ts +1 -2
- package/dist/types/common/vitals/vital-metric.d.ts.map +1 -1
- package/dist/types/features/ajax/aggregate/gql.d.ts +29 -0
- package/dist/types/features/ajax/aggregate/gql.d.ts.map +1 -0
- package/dist/types/features/ajax/aggregate/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_timing/aggregate/index.d.ts.map +1 -1
- package/dist/types/features/session_trace/aggregate/index.d.ts +1 -0
- package/dist/types/features/session_trace/aggregate/index.d.ts.map +1 -1
- package/dist/types/features/spa/aggregate/index.d.ts +1 -0
- package/dist/types/features/spa/aggregate/index.d.ts.map +1 -1
- package/dist/types/features/spa/aggregate/serializer.d.ts.map +1 -1
- package/dist/types/index.d.ts +0 -1
- package/dist/types/loaders/configure/public-path.npm.d.ts.map +1 -1
- package/package.json +1 -9
- package/src/cdn/polyfills.js +4 -0
- package/src/common/config/state/configurable.js +2 -1
- package/src/common/config/state/init.js +1 -0
- package/src/common/session/session-entity.js +3 -2
- package/src/common/url/parse-url.js +22 -50
- package/src/common/util/type-check.js +8 -0
- package/src/common/vitals/first-input-delay.js +1 -2
- package/src/common/vitals/largest-contentful-paint.js +1 -2
- package/src/common/vitals/vital-metric.js +2 -12
- package/src/features/ajax/aggregate/gql.js +95 -0
- package/src/features/ajax/aggregate/index.js +11 -1
- package/src/features/ajax/instrument/index.js +3 -0
- package/src/features/jserrors/aggregate/index.js +1 -1
- package/src/features/page_view_event/aggregate/index.js +2 -0
- package/src/features/page_view_timing/aggregate/index.js +11 -0
- package/src/features/session_replay/aggregate/index.js +6 -6
- package/src/features/session_trace/aggregate/index.js +10 -2
- package/src/features/spa/aggregate/index.js +5 -3
- package/src/features/spa/aggregate/serializer.js +7 -1
- package/src/features/utils/instrument-base.js +1 -1
- package/src/index.js +0 -1
- package/src/loaders/api/api.js +2 -2
- package/src/loaders/configure/public-path.npm.js +0 -1
- package/dist/cjs/cdn/worker.js +0 -16
- package/dist/cjs/loaders/worker-agent.js +0 -24
- package/dist/esm/cdn/worker.js +0 -14
- package/dist/esm/loaders/worker-agent.js +0 -18
- package/dist/types/cdn/worker.d.ts +0 -2
- package/dist/types/cdn/worker.d.ts.map +0 -1
- package/dist/types/loaders/worker-agent.d.ts +0 -8
- package/dist/types/loaders/worker-agent.d.ts.map +0 -1
- package/src/cdn/worker.js +0 -21
- package/src/loaders/worker-agent.js +0 -24
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,29 @@
|
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
|
4
4
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
|
5
5
|
|
|
6
|
+
## [1.246.0](https://github.com/newrelic/newrelic-browser-agent/compare/v1.245.0...v1.246.0) (2023-10-23)
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
### Features
|
|
10
|
+
|
|
11
|
+
* Add network info to all page view timing events ([#768](https://github.com/newrelic/newrelic-browser-agent/issues/768)) ([757cf19](https://github.com/newrelic/newrelic-browser-agent/commit/757cf1953af471118d809414cd41297a87c89a34))
|
|
12
|
+
* Replace url parsing with URL class ([#781](https://github.com/newrelic/newrelic-browser-agent/issues/781)) ([4206263](https://github.com/newrelic/newrelic-browser-agent/commit/42062638850b4b410ac75eb008120ec4a82583c1))
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
### Bug Fixes
|
|
16
|
+
|
|
17
|
+
* Add feature flag support for Browser Interactions ([#779](https://github.com/newrelic/newrelic-browser-agent/issues/779)) ([aa39c6c](https://github.com/newrelic/newrelic-browser-agent/commit/aa39c6cd2aeefaecd803aeb0736ad7aef8477bc4))
|
|
18
|
+
* Add first harvest of session flags to RUM and Trace ([#765](https://github.com/newrelic/newrelic-browser-agent/issues/765)) ([ab2e9dd](https://github.com/newrelic/newrelic-browser-agent/commit/ab2e9dd2252143635b67ea9da4e07867ec68cd0f))
|
|
19
|
+
|
|
20
|
+
## [1.245.0](https://github.com/newrelic/newrelic-browser-agent/compare/v1.244.0...v1.245.0) (2023-10-18)
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
### Features
|
|
24
|
+
|
|
25
|
+
* Allow boolean values in setCustomAttribute ([#776](https://github.com/newrelic/newrelic-browser-agent/issues/776)) ([d44f033](https://github.com/newrelic/newrelic-browser-agent/commit/d44f03384655f47c5f8a63db02f7eaac58585a86))
|
|
26
|
+
* Detect GraphQL operation names and types in AJAX calls ([#764](https://github.com/newrelic/newrelic-browser-agent/issues/764)) ([8587afc](https://github.com/newrelic/newrelic-browser-agent/commit/8587afc9dbc18a52048f467c77e5ededc225eb2a))
|
|
27
|
+
* Removing worker build ([#762](https://github.com/newrelic/newrelic-browser-agent/issues/762)) ([15f801b](https://github.com/newrelic/newrelic-browser-agent/commit/15f801b1a48c6e60f8f50f349aa382c77a073480))
|
|
28
|
+
|
|
6
29
|
## [1.244.0](https://github.com/newrelic/newrelic-browser-agent/compare/v1.243.1...v1.244.0) (2023-10-10)
|
|
7
30
|
|
|
8
31
|
|
|
@@ -10,8 +10,12 @@ require("core-js/stable/array/some");
|
|
|
10
10
|
require("core-js/stable/object/assign");
|
|
11
11
|
require("core-js/stable/object/entries");
|
|
12
12
|
require("core-js/stable/object/values");
|
|
13
|
+
require("core-js/stable/object/from-entries");
|
|
13
14
|
require("core-js/stable/map");
|
|
14
15
|
require("core-js/stable/reflect");
|
|
15
16
|
require("core-js/stable/set");
|
|
16
17
|
require("core-js/stable/weak-set");
|
|
17
|
-
require("core-js/stable/object/get-own-property-descriptors");
|
|
18
|
+
require("core-js/stable/object/get-own-property-descriptors");
|
|
19
|
+
require("core-js/stable/url");
|
|
20
|
+
require("core-js/stable/url-search-params");
|
|
21
|
+
require("core-js/stable/string/starts-with");
|
|
@@ -15,7 +15,7 @@ function getModeledObject(obj, model) {
|
|
|
15
15
|
for (let key in target) {
|
|
16
16
|
if (obj[key] !== undefined) {
|
|
17
17
|
try {
|
|
18
|
-
if (typeof obj[key] === 'object' && typeof model[key] === 'object') output[key] = getModeledObject(obj[key], model[key]);else output[key] = obj[key];
|
|
18
|
+
if (Array.isArray(obj[key]) && Array.isArray(model[key])) output[key] = Array.from(new Set([...obj[key], ...model[key]]));else if (typeof obj[key] === 'object' && typeof model[key] === 'object') output[key] = getModeledObject(obj[key], model[key]);else output[key] = obj[key];
|
|
19
19
|
} catch (e) {
|
|
20
20
|
(0, _console.warn)('An error occurred while setting a property of a Configurable', e);
|
|
21
21
|
}
|
|
@@ -31,9 +31,10 @@ const model = {
|
|
|
31
31
|
inactiveAt: 0,
|
|
32
32
|
expiresAt: 0,
|
|
33
33
|
updatedAt: Date.now(),
|
|
34
|
-
|
|
34
|
+
sessionReplayMode: MODE.OFF,
|
|
35
35
|
sessionReplaySentFirstChunk: false,
|
|
36
36
|
sessionTraceMode: MODE.OFF,
|
|
37
|
+
traceHarvestStarted: false,
|
|
37
38
|
custom: {}
|
|
38
39
|
};
|
|
39
40
|
const SESSION_EVENTS = {
|
|
@@ -152,7 +153,7 @@ class SessionEntity {
|
|
|
152
153
|
|
|
153
154
|
// The fact that the session is "new" or pre-existing is used in some places in the agent. Session Replay and Trace
|
|
154
155
|
// can use this info to inform whether to trust a new sampling decision vs continue a previous tracking effort.
|
|
155
|
-
|
|
156
|
+
this.isNew = !Object.keys(initialRead).length;
|
|
156
157
|
// if its a "new" session, we write to storage API with the default values. These values may change over the lifespan of the agent run.
|
|
157
158
|
// we can use a modeled object here to help us know and manage what values are being used. -- see "model" above
|
|
158
159
|
if (this.isNew) this.write((0, _configurable.getModeledObject)(this.state, model), true);else this.sync(initialRead);
|
|
@@ -10,57 +10,34 @@ var _runtime = require("../constants/runtime");
|
|
|
10
10
|
* SPDX-License-Identifier: Apache-2.0
|
|
11
11
|
*/
|
|
12
12
|
|
|
13
|
-
var stringsToParsedUrls = {};
|
|
14
13
|
function parseUrl(url) {
|
|
15
|
-
if (url in stringsToParsedUrls) {
|
|
16
|
-
return stringsToParsedUrls[url];
|
|
17
|
-
}
|
|
18
|
-
|
|
19
14
|
// Return if URL is a data URL, parseUrl assumes urls are http/https
|
|
20
15
|
if ((url || '').indexOf('data:') === 0) {
|
|
21
16
|
return {
|
|
22
17
|
protocol: 'data'
|
|
23
18
|
};
|
|
24
19
|
}
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
20
|
+
try {
|
|
21
|
+
const parsedUrl = new URL(url, location.href);
|
|
22
|
+
const returnVal = {
|
|
23
|
+
port: parsedUrl.port,
|
|
24
|
+
hostname: parsedUrl.hostname,
|
|
25
|
+
pathname: parsedUrl.pathname,
|
|
26
|
+
search: parsedUrl.search,
|
|
27
|
+
protocol: parsedUrl.protocol.slice(0, parsedUrl.protocol.indexOf(':')),
|
|
28
|
+
sameOrigin: parsedUrl.protocol === _runtime.globalScope?.location?.protocol && parsedUrl.host === _runtime.globalScope?.location?.host
|
|
29
|
+
};
|
|
30
|
+
if (!returnVal.port || returnVal.port === '') {
|
|
31
|
+
if (parsedUrl.protocol === 'http:') returnVal.port = '80';
|
|
32
|
+
if (parsedUrl.protocol === 'https:') returnVal.port = '443';
|
|
37
33
|
}
|
|
34
|
+
if (!returnVal.pathname || returnVal.pathname === '') {
|
|
35
|
+
returnVal.pathname = '/';
|
|
36
|
+
} else if (!returnVal.pathname.startsWith('/')) {
|
|
37
|
+
returnVal.pathname = "/".concat(returnVal.pathname);
|
|
38
|
+
}
|
|
39
|
+
return returnVal;
|
|
40
|
+
} catch (err) {
|
|
41
|
+
return {};
|
|
38
42
|
}
|
|
39
|
-
ret.port = urlEl.port;
|
|
40
|
-
var firstSplit = urlEl.href.split('://');
|
|
41
|
-
if (!ret.port && firstSplit[1]) {
|
|
42
|
-
ret.port = firstSplit[1].split('/')[0].split('@').pop().split(':')[1];
|
|
43
|
-
}
|
|
44
|
-
if (!ret.port || ret.port === '0') ret.port = firstSplit[0] === 'https' ? '443' : '80';
|
|
45
|
-
|
|
46
|
-
// Host not provided in IE for relative urls
|
|
47
|
-
ret.hostname = urlEl.hostname || location.hostname;
|
|
48
|
-
ret.pathname = urlEl.pathname;
|
|
49
|
-
ret.protocol = firstSplit[0];
|
|
50
|
-
|
|
51
|
-
// Pathname sometimes doesn't have leading slash (IE 8 and 9)
|
|
52
|
-
if (ret.pathname.charAt(0) !== '/') ret.pathname = '/' + ret.pathname;
|
|
53
|
-
|
|
54
|
-
// urlEl.protocol is ':' in old ie when protocol is not specified
|
|
55
|
-
var sameProtocol = !urlEl.protocol || urlEl.protocol === ':' || urlEl.protocol === location.protocol;
|
|
56
|
-
var sameDomain = urlEl.hostname === location.hostname && urlEl.port === location.port;
|
|
57
|
-
|
|
58
|
-
// urlEl.hostname is not provided by IE for relative urls, but relative urls are also same-origin
|
|
59
|
-
ret.sameOrigin = sameProtocol && (!urlEl.hostname || sameDomain);
|
|
60
|
-
|
|
61
|
-
// Only cache if url doesn't have a path
|
|
62
|
-
if (ret.pathname === '/') {
|
|
63
|
-
stringsToParsedUrls[url] = ret;
|
|
64
|
-
}
|
|
65
|
-
return ret;
|
|
66
43
|
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.isPureObject = isPureObject;
|
|
7
|
+
/**
|
|
8
|
+
* Tests a passed object to see if it is a pure object or not. All non-primatives in JS
|
|
9
|
+
* are technically objects and would pass a `typeof` check.
|
|
10
|
+
* @param {*} obj Input object to be tested
|
|
11
|
+
**/
|
|
12
|
+
function isPureObject(obj) {
|
|
13
|
+
return obj?.constructor === {}.constructor;
|
|
14
|
+
}
|
|
@@ -16,8 +16,7 @@ class VitalMetric {
|
|
|
16
16
|
let {
|
|
17
17
|
value,
|
|
18
18
|
entries = [],
|
|
19
|
-
attrs = {}
|
|
20
|
-
shouldAddConnectionAttributes = false
|
|
19
|
+
attrs = {}
|
|
21
20
|
} = _ref;
|
|
22
21
|
if (value < 0) return;
|
|
23
22
|
const state = {
|
|
@@ -26,7 +25,6 @@ class VitalMetric {
|
|
|
26
25
|
entries,
|
|
27
26
|
attrs
|
|
28
27
|
};
|
|
29
|
-
if (shouldAddConnectionAttributes) addConnectionAttributes(state.attrs);
|
|
30
28
|
this.history.push(state);
|
|
31
29
|
this.#subscribers.forEach(cb => {
|
|
32
30
|
try {
|
|
@@ -60,12 +58,4 @@ class VitalMetric {
|
|
|
60
58
|
};
|
|
61
59
|
}
|
|
62
60
|
}
|
|
63
|
-
exports.VitalMetric = VitalMetric;
|
|
64
|
-
function addConnectionAttributes(obj) {
|
|
65
|
-
var connection = navigator.connection || navigator.mozConnection || navigator.webkitConnection; // to date, both window & worker shares the same support for connection
|
|
66
|
-
if (!connection) return;
|
|
67
|
-
if (connection.type) obj['net-type'] = connection.type;
|
|
68
|
-
if (connection.effectiveType) obj['net-etype'] = connection.effectiveType;
|
|
69
|
-
if (connection.rtt) obj['net-rtt'] = connection.rtt;
|
|
70
|
-
if (connection.downlink) obj['net-dlink'] = connection.downlink;
|
|
71
|
-
}
|
|
61
|
+
exports.VitalMetric = VitalMetric;
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.parseGQL = parseGQL;
|
|
7
|
+
var _typeCheck = require("../../../common/util/type-check");
|
|
8
|
+
/**
|
|
9
|
+
* @typedef {object} GQLMetadata
|
|
10
|
+
* @property {string} operationName Name of the operation
|
|
11
|
+
* @property {string} operationType Type of the operation
|
|
12
|
+
* @property {string} operationFramework Framework responsible for the operation
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Parses and returns the graphql metadata from a network request. If the network
|
|
17
|
+
* request is not a graphql call, undefined will be returned.
|
|
18
|
+
* @param {object|string} body Ajax request body
|
|
19
|
+
* @param {string} query Ajax request query param string
|
|
20
|
+
* @returns {GQLMetadata | undefined}
|
|
21
|
+
*/
|
|
22
|
+
function parseGQL() {
|
|
23
|
+
let {
|
|
24
|
+
body,
|
|
25
|
+
query
|
|
26
|
+
} = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
|
|
27
|
+
if (!body && !query) return;
|
|
28
|
+
try {
|
|
29
|
+
const gqlBody = parseBatchGQL(parseGQLContents(body));
|
|
30
|
+
if (gqlBody) return gqlBody;
|
|
31
|
+
const gqlQuery = parseSingleGQL(parseGQLQueryString(query));
|
|
32
|
+
if (gqlQuery) return gqlQuery;
|
|
33
|
+
} catch (err) {
|
|
34
|
+
// parsing failed, return undefined
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* @param {string|Object} gql The GraphQL object body sent to a GQL server
|
|
40
|
+
* @returns {GQLMetadata}
|
|
41
|
+
*/
|
|
42
|
+
function parseSingleGQL(contents) {
|
|
43
|
+
if (typeof contents !== 'object' || !contents.query || typeof contents.query !== 'string') return;
|
|
44
|
+
|
|
45
|
+
/** parses gql query string and returns [fullmatch, type match, name match] */
|
|
46
|
+
const matches = contents.query.trim().match(/^(query|mutation|subscription)\s?(\w*)/);
|
|
47
|
+
const operationType = matches?.[1];
|
|
48
|
+
if (!operationType) return;
|
|
49
|
+
const operationName = contents.operationName || matches?.[2] || 'Anonymous';
|
|
50
|
+
return {
|
|
51
|
+
operationName,
|
|
52
|
+
// the operation name of the indiv query
|
|
53
|
+
operationType,
|
|
54
|
+
// query, mutation, or subscription,
|
|
55
|
+
operationFramework: 'GraphQL'
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
function parseBatchGQL(contents) {
|
|
59
|
+
if (!contents) return;
|
|
60
|
+
if (!Array.isArray(contents)) contents = [contents];
|
|
61
|
+
const opNames = [];
|
|
62
|
+
const opTypes = [];
|
|
63
|
+
for (let content of contents) {
|
|
64
|
+
const operation = parseSingleGQL(content);
|
|
65
|
+
if (!operation) continue;
|
|
66
|
+
opNames.push(operation.operationName);
|
|
67
|
+
opTypes.push(operation.operationType);
|
|
68
|
+
}
|
|
69
|
+
if (!opTypes.length) return;
|
|
70
|
+
return {
|
|
71
|
+
operationName: opNames.join(','),
|
|
72
|
+
// the operation name of the indiv query -- joined by ',' for batched results
|
|
73
|
+
operationType: opTypes.join(','),
|
|
74
|
+
// query, mutation, or subscription -- joined by ',' for batched results
|
|
75
|
+
operationFramework: 'GraphQL'
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
function parseGQLContents(gqlContents) {
|
|
79
|
+
let contents;
|
|
80
|
+
if (!gqlContents || typeof gqlContents !== 'string' && typeof gqlContents !== 'object') return;else if (typeof gqlContents === 'string') contents = JSON.parse(gqlContents);else contents = gqlContents;
|
|
81
|
+
if (!(0, _typeCheck.isPureObject)(contents) && !Array.isArray(contents)) return;
|
|
82
|
+
let isValid = false;
|
|
83
|
+
if (Array.isArray(contents)) isValid = contents.some(x => validateGQLObject(x));else isValid = validateGQLObject(contents);
|
|
84
|
+
if (!isValid) return;
|
|
85
|
+
return contents;
|
|
86
|
+
}
|
|
87
|
+
function parseGQLQueryString(gqlQueryString) {
|
|
88
|
+
if (!gqlQueryString || typeof gqlQueryString !== 'string') return;
|
|
89
|
+
const params = new URLSearchParams(gqlQueryString);
|
|
90
|
+
return parseGQLContents(Object.fromEntries(params));
|
|
91
|
+
}
|
|
92
|
+
function validateGQLObject(obj) {
|
|
93
|
+
return !(typeof obj !== 'object' || !obj.query || typeof obj.query !== 'string');
|
|
94
|
+
}
|
|
@@ -15,6 +15,7 @@ var _constants = require("../constants");
|
|
|
15
15
|
var _features = require("../../../loaders/features/features");
|
|
16
16
|
var _constants2 = require("../../metrics/constants");
|
|
17
17
|
var _aggregateBase = require("../../utils/aggregate-base");
|
|
18
|
+
var _gql = require("./gql");
|
|
18
19
|
/*
|
|
19
20
|
* Copyright 2020 New Relic Corporation. All rights reserved.
|
|
20
21
|
* SPDX-License-Identifier: Apache-2.0
|
|
@@ -116,6 +117,13 @@ class Aggregate extends _aggregateBase.AggregateBase {
|
|
|
116
117
|
event.spanTimestamp = xhrContext.dt.timestamp;
|
|
117
118
|
}
|
|
118
119
|
|
|
120
|
+
// parsed from the AJAX body, looking for operationName param & parsing query for operationType
|
|
121
|
+
event.gql = params.gql = (0, _gql.parseGQL)({
|
|
122
|
+
body: this.body,
|
|
123
|
+
query: this?.parsedOrigin?.search
|
|
124
|
+
});
|
|
125
|
+
if (event.gql) (0, _handle.handle)(_constants2.SUPPORTABILITY_METRIC_CHANNEL, ['Ajax/Events/GraphQL/Bytes-Added', (0, _stringify.stringify)(event.gql).length], undefined, _features.FEATURE_NAMES.metrics, ee);
|
|
126
|
+
|
|
119
127
|
// if the ajax happened inside an interaction, hold it until the interaction finishes
|
|
120
128
|
if (this.spaNode) {
|
|
121
129
|
var interactionId = this.spaNode.interaction.id;
|
|
@@ -205,7 +213,11 @@ class Aggregate extends _aggregateBase.AggregateBase {
|
|
|
205
213
|
var insert = '2,';
|
|
206
214
|
|
|
207
215
|
// add custom attributes
|
|
208
|
-
|
|
216
|
+
// gql decorators are added as custom attributes to alleviate need for new BEL schema
|
|
217
|
+
var attrParts = (0, _belSerializer.addCustomAttributes)({
|
|
218
|
+
...((0, _config.getInfo)(agentIdentifier).jsAttributes || {}),
|
|
219
|
+
...(event.gql || {})
|
|
220
|
+
}, this.addString);
|
|
209
221
|
fields.unshift((0, _belSerializer.numeric)(attrParts.length));
|
|
210
222
|
insert += fields.join(',');
|
|
211
223
|
if (attrParts && attrParts.length > 0) {
|
|
@@ -161,6 +161,7 @@ function subscribeToEvents(agentIdentifier, ee, handler, dt) {
|
|
|
161
161
|
if (size) metrics.txSize = size;
|
|
162
162
|
}
|
|
163
163
|
this.startTime = (0, _now.now)();
|
|
164
|
+
this.body = data;
|
|
164
165
|
this.listener = function (evt) {
|
|
165
166
|
try {
|
|
166
167
|
if (evt.type === 'abort' && !context.loadCaptureCalled) {
|
|
@@ -308,6 +309,7 @@ function subscribeToEvents(agentIdentifier, ee, handler, dt) {
|
|
|
308
309
|
addUrl(this, url);
|
|
309
310
|
var method = ('' + (target && target instanceof origRequest && target.method || opts.method || 'GET')).toUpperCase();
|
|
310
311
|
this.params.method = method;
|
|
312
|
+
this.body = opts.body;
|
|
311
313
|
this.txSize = (0, _dataSize.dataSize)(opts.body) || 0;
|
|
312
314
|
}
|
|
313
315
|
|
|
@@ -187,7 +187,7 @@ class Aggregate extends _aggregateBase.AggregateBase {
|
|
|
187
187
|
params.pageview = 1;
|
|
188
188
|
this.pageviewReported[bucketHash] = true;
|
|
189
189
|
}
|
|
190
|
-
if (agentRuntime?.session?.state?.
|
|
190
|
+
if (agentRuntime?.session?.state?.sessionReplayMode) params.hasReplay = true;
|
|
191
191
|
params.firstOccurrenceTimestamp = this.observedAt[bucketHash];
|
|
192
192
|
var type = internal ? 'ierr' : 'err';
|
|
193
193
|
var newMetrics = {
|
|
@@ -84,6 +84,8 @@ class Aggregate extends _aggregateBase.AggregateBase {
|
|
|
84
84
|
ua: info.userAttributes,
|
|
85
85
|
at: info.atts
|
|
86
86
|
};
|
|
87
|
+
if (agentRuntime.session) queryParameters.fsh = Number(agentRuntime.session.isNew); // "first session harvest" aka RUM request or PageView event of a session
|
|
88
|
+
|
|
87
89
|
let body;
|
|
88
90
|
if (typeof info.jsAttributes === 'object' && Object.keys(info.jsAttributes).length > 0) {
|
|
89
91
|
body = {
|
|
@@ -106,6 +106,7 @@ class Aggregate extends _aggregateBase.AggregateBase {
|
|
|
106
106
|
}
|
|
107
107
|
addTiming(name, value, attrs) {
|
|
108
108
|
attrs = attrs || {};
|
|
109
|
+
addConnectionAttributes(attrs); // network conditions may differ from the actual for VitalMetrics when they were captured
|
|
109
110
|
|
|
110
111
|
// If cls was set to another value by `onCLS`, then it's supported and is attached onto any timing but is omitted until such time.
|
|
111
112
|
/*
|
|
@@ -177,4 +178,12 @@ class Aggregate extends _aggregateBase.AggregateBase {
|
|
|
177
178
|
return payload;
|
|
178
179
|
}
|
|
179
180
|
}
|
|
180
|
-
exports.Aggregate = Aggregate;
|
|
181
|
+
exports.Aggregate = Aggregate;
|
|
182
|
+
function addConnectionAttributes(obj) {
|
|
183
|
+
var connection = navigator.connection || navigator.mozConnection || navigator.webkitConnection; // to date, both window & worker shares the same support for connection
|
|
184
|
+
if (!connection) return;
|
|
185
|
+
if (connection.type) obj['net-type'] = connection.type;
|
|
186
|
+
if (connection.effectiveType) obj['net-etype'] = connection.effectiveType;
|
|
187
|
+
if (connection.rtt) obj['net-rtt'] = connection.rtt;
|
|
188
|
+
if (connection.downlink) obj['net-dlink'] = connection.downlink;
|
|
189
|
+
}
|
|
@@ -140,13 +140,13 @@ class Aggregate extends _aggregateBase.AggregateBase {
|
|
|
140
140
|
const {
|
|
141
141
|
session
|
|
142
142
|
} = (0, _config.getRuntime)(this.agentIdentifier);
|
|
143
|
-
this.mode = session.state.
|
|
143
|
+
this.mode = session.state.sessionReplayMode;
|
|
144
144
|
if (!this.initialized || this.mode === _sessionEntity.MODE.OFF) return;
|
|
145
145
|
this.startRecording();
|
|
146
146
|
});
|
|
147
147
|
this.ee.on(_sessionEntity.SESSION_EVENTS.UPDATE, (type, data) => {
|
|
148
148
|
if (!this.initialized || this.blocked || type !== _sessionEntity.SESSION_EVENT_TYPES.CROSS_TAB) return;
|
|
149
|
-
if (this.mode !== _sessionEntity.MODE.OFF && data.
|
|
149
|
+
if (this.mode !== _sessionEntity.MODE.OFF && data.sessionReplayMode === _sessionEntity.MODE.OFF) this.abort(ABORT_REASONS.CROSS_TAB);
|
|
150
150
|
this.mode = data.sessionReplay;
|
|
151
151
|
});
|
|
152
152
|
|
|
@@ -172,7 +172,7 @@ class Aggregate extends _aggregateBase.AggregateBase {
|
|
|
172
172
|
this.startRecording();
|
|
173
173
|
this.scheduler.startTimer(this.harvestTimeSeconds);
|
|
174
174
|
this.syncWithSessionManager({
|
|
175
|
-
|
|
175
|
+
sessionReplayMode: this.mode
|
|
176
176
|
});
|
|
177
177
|
}
|
|
178
178
|
}
|
|
@@ -207,7 +207,7 @@ class Aggregate extends _aggregateBase.AggregateBase {
|
|
|
207
207
|
// session replays can continue if already in progress
|
|
208
208
|
if (!session.isNew) {
|
|
209
209
|
// inherit the mode of the existing session
|
|
210
|
-
this.mode = session.state.
|
|
210
|
+
this.mode = session.state.sessionReplayMode;
|
|
211
211
|
} else {
|
|
212
212
|
// The session is new... determine the mode the new session should start in
|
|
213
213
|
if (fullSample) this.mode = _sessionEntity.MODE.FULL; // full mode has precedence over error mode
|
|
@@ -248,7 +248,7 @@ class Aggregate extends _aggregateBase.AggregateBase {
|
|
|
248
248
|
}
|
|
249
249
|
this.startRecording();
|
|
250
250
|
this.syncWithSessionManager({
|
|
251
|
-
|
|
251
|
+
sessionReplayMode: this.mode
|
|
252
252
|
});
|
|
253
253
|
}
|
|
254
254
|
prepareHarvest() {
|
|
@@ -457,7 +457,7 @@ class Aggregate extends _aggregateBase.AggregateBase {
|
|
|
457
457
|
this.mode = _sessionEntity.MODE.OFF;
|
|
458
458
|
this.stopRecording();
|
|
459
459
|
this.syncWithSessionManager({
|
|
460
|
-
|
|
460
|
+
sessionReplayMode: this.mode
|
|
461
461
|
});
|
|
462
462
|
this.clearTimestamps();
|
|
463
463
|
this.ee.emit('REPLAY_ABORTED');
|
|
@@ -148,7 +148,7 @@ class Aggregate extends _aggregateBase.AggregateBase {
|
|
|
148
148
|
});
|
|
149
149
|
if (!sessionEntity.isNew) {
|
|
150
150
|
// inherit the same mode as existing session's Trace
|
|
151
|
-
if (sessionEntity.state.
|
|
151
|
+
if (sessionEntity.state.sessionReplayMode === _sessionEntity.MODE.OFF) this.isStandalone = true;
|
|
152
152
|
controlTraceOp(mostRecentModeKnown = sessionEntity.state.sessionTraceMode);
|
|
153
153
|
} else {
|
|
154
154
|
// for new sessions, see the truth table associated with NEWRELIC-8662 wrt the new Trace behavior under session management
|
|
@@ -510,6 +510,16 @@ class Aggregate extends _aggregateBase.AggregateBase {
|
|
|
510
510
|
}
|
|
511
511
|
this.trace = {};
|
|
512
512
|
this.nodeCount = 0;
|
|
513
|
+
let firstHarvestOfSession;
|
|
514
|
+
if (this.agentRuntime.session) {
|
|
515
|
+
const isFirstPayload = !this.agentRuntime.session.state.traceHarvestStarted;
|
|
516
|
+
firstHarvestOfSession = {
|
|
517
|
+
fsh: Number(isFirstPayload)
|
|
518
|
+
}; // converted to '0' | '1'
|
|
519
|
+
if (isFirstPayload) this.agentRuntime.session.write({
|
|
520
|
+
traceHarvestStarted: true
|
|
521
|
+
});
|
|
522
|
+
}
|
|
513
523
|
return {
|
|
514
524
|
qs: {
|
|
515
525
|
st: this.agentRuntime.offset,
|
|
@@ -520,9 +530,10 @@ class Aggregate extends _aggregateBase.AggregateBase {
|
|
|
520
530
|
* so that blob parsing doesn't need to happen to support UI/API functions */
|
|
521
531
|
fts: this.agentRuntime.offset + earliestTimeStamp,
|
|
522
532
|
/** 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 */
|
|
523
|
-
n: stns.length
|
|
533
|
+
n: stns.length,
|
|
534
|
+
// node count
|
|
535
|
+
...firstHarvestOfSession
|
|
524
536
|
},
|
|
525
|
-
|
|
526
537
|
body: {
|
|
527
538
|
res: stns
|
|
528
539
|
}
|
|
@@ -67,7 +67,9 @@ class Aggregate extends _aggregateBase.AggregateBase {
|
|
|
67
67
|
depth: 0,
|
|
68
68
|
harvestTimeSeconds: (0, _config.getConfigurationValue)(agentIdentifier, 'spa.harvestTimeSeconds') || 10,
|
|
69
69
|
interactionsToHarvest: [],
|
|
70
|
-
interactionsSent: []
|
|
70
|
+
interactionsSent: [],
|
|
71
|
+
// The below feature flag is used to disable the SPA ajax fix for specific customers, see https://new-relic.atlassian.net/browse/NR-172169
|
|
72
|
+
disableSpaFix: ((0, _config.getConfigurationValue)(agentIdentifier, 'feature_flags') || []).indexOf('disable-spa-fix') > -1
|
|
71
73
|
};
|
|
72
74
|
this.serializer = new _serializer.Serializer(this);
|
|
73
75
|
const {
|
|
@@ -287,7 +289,7 @@ class Aggregate extends _aggregateBase.AggregateBase {
|
|
|
287
289
|
// context is stored on the xhr and is shared with all callbacks associated
|
|
288
290
|
// with the new xhr
|
|
289
291
|
(0, _registerHandler.registerHandler)('new-xhr', function () {
|
|
290
|
-
if (!state.currentNode && state.prevInteraction && !state.prevInteraction.ignored) {
|
|
292
|
+
if (!state.disableSpaFix && !state.currentNode && state.prevInteraction && !state.prevInteraction.ignored) {
|
|
291
293
|
/*
|
|
292
294
|
* The previous interaction was discarded before a route change. Restore the interaction
|
|
293
295
|
* in case this XHR is associated with a route change.
|
|
@@ -378,7 +380,7 @@ class Aggregate extends _aggregateBase.AggregateBase {
|
|
|
378
380
|
}, this.featureName, jsonpEE);
|
|
379
381
|
(0, _registerHandler.registerHandler)(FETCH_START, function (fetchArguments, dtPayload) {
|
|
380
382
|
if (fetchArguments) {
|
|
381
|
-
if (!state.currentNode && state.prevInteraction && !state.prevInteraction.ignored) {
|
|
383
|
+
if (!state.disableSpaFix && !state.currentNode && state.prevInteraction && !state.prevInteraction.ignored) {
|
|
382
384
|
/*
|
|
383
385
|
* The previous interaction was discarded before a route change. Restore the interaction
|
|
384
386
|
* in case this XHR is associated with a route change.
|
|
@@ -91,6 +91,13 @@ class Serializer extends _sharedContext.SharedContext {
|
|
|
91
91
|
break;
|
|
92
92
|
case 2:
|
|
93
93
|
fields.push(addString(params.method), (0, _belSerializer.numeric)(params.status), addString(params.host), addString(params.pathname), (0, _belSerializer.numeric)(metrics.txSize), (0, _belSerializer.numeric)(metrics.rxSize), attrs.isFetch ? 1 : attrs.isJSONP ? 2 : '', addString(node.id), (0, _belSerializer.nullable)(node.dt && node.dt.spanId, addString, true) + (0, _belSerializer.nullable)(node.dt && node.dt.traceId, addString, true) + (0, _belSerializer.nullable)(node.dt && node.dt.timestamp, _belSerializer.numeric, false));
|
|
94
|
+
|
|
95
|
+
// add params.gql here
|
|
96
|
+
if (Object.keys(params?.gql || {}).length) {
|
|
97
|
+
var ajaxAttrParts = (0, _belSerializer.addCustomAttributes)(params.gql, addString);
|
|
98
|
+
children = children.concat(ajaxAttrParts);
|
|
99
|
+
attrCount = ajaxAttrParts.length;
|
|
100
|
+
}
|
|
94
101
|
break;
|
|
95
102
|
case 4:
|
|
96
103
|
var tracedTime = attrs.tracedTime;
|
|
@@ -139,7 +139,7 @@ class InstrumentBase extends _featureBase.FeatureBase {
|
|
|
139
139
|
if (featureName === _features.FEATURE_NAMES.sessionReplay) {
|
|
140
140
|
if (!_config.originals.MO) return false; // Session Replay cannot work without Mutation Observer
|
|
141
141
|
if ((0, _config.getConfigurationValue)(this.agentIdentifier, 'session_trace.enabled') === false) return false; // Session Replay as of now is tightly coupled with Session Trace in the UI
|
|
142
|
-
return !!session?.isNew || !!session?.state.
|
|
142
|
+
return !!session?.isNew || !!session?.state.sessionReplayMode; // Session Replay should only try to run if already running from a previous page, or at the beginning of a session
|
|
143
143
|
}
|
|
144
144
|
|
|
145
145
|
return true;
|
package/dist/cjs/index.js
CHANGED
|
@@ -69,15 +69,8 @@ Object.defineProperty(exports, "Spa", {
|
|
|
69
69
|
return _spa.Spa;
|
|
70
70
|
}
|
|
71
71
|
});
|
|
72
|
-
Object.defineProperty(exports, "WorkerAgent", {
|
|
73
|
-
enumerable: true,
|
|
74
|
-
get: function () {
|
|
75
|
-
return _workerAgent.WorkerAgent;
|
|
76
|
-
}
|
|
77
|
-
});
|
|
78
72
|
var _agent = require("./loaders/agent");
|
|
79
73
|
var _browserAgent = require("./loaders/browser-agent");
|
|
80
|
-
var _workerAgent = require("./loaders/worker-agent");
|
|
81
74
|
var _microAgent = require("./loaders/micro-agent");
|
|
82
75
|
var _ajax = require("./features/ajax");
|
|
83
76
|
var _jserrors = require("./features/jserrors");
|
|
@@ -99,8 +99,8 @@ function setAPI(agentIdentifier, forceDrain) {
|
|
|
99
99
|
(0, _console.warn)("Failed to execute setCustomAttribute.\nName must be a string type, but a type of <".concat(typeof name, "> was provided."));
|
|
100
100
|
return;
|
|
101
101
|
}
|
|
102
|
-
if (!(['string', 'number'].includes(typeof value) || value === null)) {
|
|
103
|
-
(0, _console.warn)("Failed to execute setCustomAttribute.\nNon-null value must be a string or
|
|
102
|
+
if (!(['string', 'number', 'boolean'].includes(typeof value) || value === null)) {
|
|
103
|
+
(0, _console.warn)("Failed to execute setCustomAttribute.\nNon-null value must be a string, number or boolean type, but a type of <".concat(typeof value, "> was provided."));
|
|
104
104
|
return;
|
|
105
105
|
}
|
|
106
106
|
return appendJsAttribute(name, value, 'setCustomAttribute', persistAttribute);
|
|
@@ -13,8 +13,12 @@ import 'core-js/stable/array/some';
|
|
|
13
13
|
import 'core-js/stable/object/assign';
|
|
14
14
|
import 'core-js/stable/object/entries';
|
|
15
15
|
import 'core-js/stable/object/values';
|
|
16
|
+
import 'core-js/stable/object/from-entries';
|
|
16
17
|
import 'core-js/stable/map';
|
|
17
18
|
import 'core-js/stable/reflect';
|
|
18
19
|
import 'core-js/stable/set';
|
|
19
20
|
import 'core-js/stable/weak-set';
|
|
20
|
-
import 'core-js/stable/object/get-own-property-descriptors';
|
|
21
|
+
import 'core-js/stable/object/get-own-property-descriptors';
|
|
22
|
+
import 'core-js/stable/url';
|
|
23
|
+
import 'core-js/stable/url-search-params';
|
|
24
|
+
import 'core-js/stable/string/starts-with';
|
|
@@ -9,7 +9,7 @@ export function getModeledObject(obj, model) {
|
|
|
9
9
|
for (let key in target) {
|
|
10
10
|
if (obj[key] !== undefined) {
|
|
11
11
|
try {
|
|
12
|
-
if (typeof obj[key] === 'object' && typeof model[key] === 'object') output[key] = getModeledObject(obj[key], model[key]);else output[key] = obj[key];
|
|
12
|
+
if (Array.isArray(obj[key]) && Array.isArray(model[key])) output[key] = Array.from(new Set([...obj[key], ...model[key]]));else if (typeof obj[key] === 'object' && typeof model[key] === 'object') output[key] = getModeledObject(obj[key], model[key]);else output[key] = obj[key];
|
|
13
13
|
} catch (e) {
|
|
14
14
|
warn('An error occurred while setting a property of a Configurable', e);
|
|
15
15
|
}
|