@newrelic/browser-agent 1.237.0 → 1.238.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/common/constants/env.cdn.js +1 -1
- package/dist/cjs/common/constants/env.npm.js +1 -1
- package/dist/cjs/common/deny-list/deny-list.js +1 -1
- package/dist/cjs/common/event-emitter/contextual-ee.js +18 -26
- package/dist/cjs/common/event-emitter/event-context.js +12 -0
- package/dist/cjs/common/harvest/harvest.js +4 -4
- package/dist/cjs/common/ids/bundle-id.js +19 -0
- package/dist/cjs/common/wrap/wrap-events.js +3 -2
- package/dist/cjs/common/wrap/wrap-fetch.js +1 -3
- package/dist/cjs/common/wrap/wrap-function.js +15 -46
- package/dist/cjs/common/wrap/wrap-mutation.js +1 -2
- package/dist/cjs/common/wrap/wrap-promise.js +2 -3
- package/dist/cjs/common/wrap/wrap-xhr.js +23 -27
- package/dist/cjs/features/ajax/instrument/distributed-tracing.js +0 -4
- package/dist/cjs/features/ajax/instrument/index.js +19 -9
- package/dist/cjs/features/metrics/aggregate/index.js +8 -0
- package/dist/cjs/features/session_replay/aggregate/index.js +2 -0
- package/dist/cjs/features/session_trace/aggregate/index.js +14 -8
- package/dist/cjs/features/spa/aggregate/index.js +1 -1
- package/dist/cjs/loaders/agent-base.js +12 -0
- package/dist/cjs/loaders/api/api.js +14 -1
- package/dist/cjs/loaders/api/interaction-types.js +11 -4
- package/dist/cjs/loaders/configure/configure.js +2 -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/deny-list/deny-list.js +1 -1
- package/dist/esm/common/event-emitter/contextual-ee.js +16 -23
- package/dist/esm/common/event-emitter/event-context.js +5 -0
- package/dist/esm/common/harvest/harvest.js +4 -4
- package/dist/esm/common/ids/bundle-id.js +13 -0
- package/dist/esm/common/wrap/wrap-events.js +4 -3
- package/dist/esm/common/wrap/wrap-fetch.js +2 -4
- package/dist/esm/common/wrap/wrap-function.js +15 -44
- package/dist/esm/common/wrap/wrap-mutation.js +2 -3
- package/dist/esm/common/wrap/wrap-promise.js +3 -4
- package/dist/esm/common/wrap/wrap-xhr.js +23 -27
- package/dist/esm/features/ajax/instrument/distributed-tracing.js +0 -4
- package/dist/esm/features/ajax/instrument/index.js +20 -10
- package/dist/esm/features/metrics/aggregate/index.js +8 -0
- package/dist/esm/features/session_replay/aggregate/index.js +2 -0
- package/dist/esm/features/session_trace/aggregate/index.js +14 -8
- package/dist/esm/features/spa/aggregate/index.js +1 -1
- package/dist/esm/loaders/agent-base.js +12 -0
- package/dist/esm/loaders/api/api.js +14 -1
- package/dist/esm/loaders/api/interaction-types.js +11 -4
- package/dist/esm/loaders/configure/configure.js +3 -2
- package/dist/types/common/event-emitter/contextual-ee.d.ts +22 -2
- package/dist/types/common/event-emitter/contextual-ee.d.ts.map +1 -1
- package/dist/types/common/event-emitter/event-context.d.ts +5 -0
- package/dist/types/common/event-emitter/event-context.d.ts.map +1 -0
- package/dist/types/common/event-emitter/register-handler.d.ts +1 -1
- package/dist/types/common/harvest/harvest.d.ts.map +1 -1
- package/dist/types/common/ids/bundle-id.d.ts +5 -0
- package/dist/types/common/ids/bundle-id.d.ts.map +1 -0
- package/dist/types/common/session/session-entity.d.ts +6 -6
- package/dist/types/common/window/nreum.d.ts +2 -2
- package/dist/types/common/wrap/wrap-events.d.ts.map +1 -1
- package/dist/types/common/wrap/wrap-fetch.d.ts.map +1 -1
- package/dist/types/common/wrap/wrap-function.d.ts +1 -19
- package/dist/types/common/wrap/wrap-function.d.ts.map +1 -1
- package/dist/types/common/wrap/wrap-mutation.d.ts.map +1 -1
- package/dist/types/common/wrap/wrap-promise.d.ts.map +1 -1
- package/dist/types/common/wrap/wrap-xhr.d.ts.map +1 -1
- package/dist/types/features/ajax/instrument/distributed-tracing.d.ts +1 -1
- package/dist/types/features/ajax/instrument/distributed-tracing.d.ts.map +1 -1
- package/dist/types/features/metrics/aggregate/endpoint-map.d.ts +5 -5
- package/dist/types/features/metrics/aggregate/index.d.ts.map +1 -1
- package/dist/types/features/session_replay/aggregate/index.d.ts.map +1 -1
- package/dist/types/features/session_trace/aggregate/index.d.ts +5 -0
- package/dist/types/features/session_trace/aggregate/index.d.ts.map +1 -1
- package/dist/types/loaders/agent-base.d.ts +9 -0
- package/dist/types/loaders/agent-base.d.ts.map +1 -1
- package/dist/types/loaders/api/api.d.ts +6 -0
- package/dist/types/loaders/api/api.d.ts.map +1 -1
- package/dist/types/loaders/api/interaction-types.d.ts +18 -7
- package/dist/types/loaders/api/interaction-types.d.ts.map +1 -1
- package/dist/types/loaders/configure/configure.d.ts +1 -0
- package/dist/types/loaders/configure/configure.d.ts.map +1 -1
- package/dist/types/loaders/features/features.d.ts +9 -9
- package/dist/types/loaders/micro-agent.d.ts +1 -1
- package/dist/types/loaders/micro-agent.d.ts.map +1 -1
- package/package.json +22 -27
- package/src/common/deny-list/deny-list.js +1 -1
- package/src/common/deny-list/deny-list.test.js +103 -30
- package/src/common/event-emitter/{contextual-ee.test.js → contextual-ee.component-test.js} +15 -32
- package/src/common/event-emitter/contextual-ee.js +20 -31
- package/src/common/event-emitter/event-context.js +5 -0
- package/src/common/harvest/harvest.js +4 -4
- package/src/common/harvest/harvest.test.js +1 -1
- package/src/common/ids/__mocks__/bundle-id.js +2 -0
- package/src/common/ids/__mocks__/unique-id.js +17 -0
- package/src/common/ids/bundle-id.js +13 -0
- package/src/common/url/__mocks__/parse-url.js +15 -0
- package/src/common/util/__mocks__/get-or-set.js +5 -0
- package/src/common/window/__mocks__/nreum.js +10 -0
- package/src/common/wrap/wrap-events.js +4 -3
- package/src/common/wrap/wrap-fetch.js +2 -4
- package/src/common/wrap/wrap-function.js +16 -44
- package/src/common/wrap/wrap-mutation.js +2 -3
- package/src/common/wrap/{wrap-promise.test.js → wrap-promise.component-test.js} +2 -32
- package/src/common/wrap/wrap-promise.js +3 -4
- package/src/common/wrap/wrap-xhr.js +24 -28
- package/src/features/ajax/instrument/distributed-tracing.js +0 -4
- package/src/features/ajax/instrument/distributed-tracing.test.js +375 -0
- package/src/features/ajax/instrument/index.js +22 -10
- package/src/features/metrics/aggregate/index.js +8 -0
- package/src/features/session_replay/aggregate/index.js +2 -0
- package/src/features/session_trace/aggregate/index.js +16 -7
- package/src/features/spa/aggregate/index.js +1 -1
- package/src/loaders/agent-base.js +12 -0
- package/src/loaders/api/api.component-test.js +45 -0
- package/src/loaders/api/api.js +14 -1
- package/src/loaders/api/interaction-types.js +11 -4
- package/src/loaders/configure/configure.js +11 -2
- /package/src/common/url/{encode.component-test.js → encode.test.js} +0 -0
- /package/src/common/url/{protocol.component-test.js → protocol.test.js} +0 -0
|
@@ -12,7 +12,7 @@ exports.unused = void 0;
|
|
|
12
12
|
* @property {getContext} getContext
|
|
13
13
|
* @property {ignore} ignore
|
|
14
14
|
* @property {onEnd} onEnd
|
|
15
|
-
* @property {
|
|
15
|
+
* @property {save} save
|
|
16
16
|
* @property {setAttribute} setAttribute
|
|
17
17
|
* @property {setName} setName
|
|
18
18
|
*/
|
|
@@ -30,8 +30,8 @@ exports.unused = void 0;
|
|
|
30
30
|
* {@link https://docs.newrelic.com/docs/browser/new-relic-browser/browser-apis/createtracer/}
|
|
31
31
|
* @callback createTracer
|
|
32
32
|
* @param {string} name This will be used as the name of the tracer.
|
|
33
|
-
* @param {
|
|
34
|
-
* @returns {
|
|
33
|
+
* @param {(...args: any[]) => any} [callback] A callback that contains the synchronous work to run at the end of the async work. To execute this callback, call the wrapper function returned using createTracer().
|
|
34
|
+
* @returns {(...args: any[]) => any} Returns a method that wraps the original callback. When this method is invoked, it calls the original callback and ends the async timing.
|
|
35
35
|
*/
|
|
36
36
|
|
|
37
37
|
/**
|
|
@@ -49,6 +49,13 @@ exports.unused = void 0;
|
|
|
49
49
|
* @returns {InteractionInstance} Returns the same interaction object allowing method chaining.
|
|
50
50
|
*/
|
|
51
51
|
|
|
52
|
+
/**
|
|
53
|
+
* Overrides other SPA save() calls; ignores an interaction so it is not saved or sent to New Relic.
|
|
54
|
+
* {@link https://docs.newrelic.com/docs/browser/new-relic-browser/browser-apis/ignore/}
|
|
55
|
+
* @callback ignore
|
|
56
|
+
* @returns {InteractionInstance} Returns the same interaction object allowing method chaining.
|
|
57
|
+
*/
|
|
58
|
+
|
|
52
59
|
/**
|
|
53
60
|
* Change the values associated with a SPA interaction before the interaction is saved.
|
|
54
61
|
* {@link https://docs.newrelic.com/docs/browser/new-relic-browser/browser-apis/onend/}
|
|
@@ -69,7 +76,7 @@ exports.unused = void 0;
|
|
|
69
76
|
* {@link https://docs.newrelic.com/docs/browser/new-relic-browser/browser-apis/setattribute/}
|
|
70
77
|
* @callback setAttribute
|
|
71
78
|
* @param {string} key Used as the attribute name on the BrowserInteraction event.
|
|
72
|
-
* @param {any}
|
|
79
|
+
* @param {any} value Used as the attribute value on the BrowserInteraction event. This can be a string, number, boolean, or object. If it is an object, New Relic serializes it to a JSON string.
|
|
73
80
|
* @returns {InteractionInstance} Returns the same interaction object allowing method chaining.
|
|
74
81
|
*/
|
|
75
82
|
|
|
@@ -36,7 +36,8 @@ function configure(agentIdentifier) {
|
|
|
36
36
|
info.jsAttributes.isWorker = true;
|
|
37
37
|
}
|
|
38
38
|
(0, _config.setInfo)(agentIdentifier, info);
|
|
39
|
-
|
|
39
|
+
const updatedInit = (0, _config.getConfiguration)(agentIdentifier);
|
|
40
|
+
runtime.denyList = [...(updatedInit.ajax?.deny_list || []), ...(updatedInit.ajax?.block_internal ? [info.beacon, info.errorBeacon] : [])];
|
|
40
41
|
(0, _config.setRuntime)(agentIdentifier, runtime);
|
|
41
42
|
(0, _api.setTopLevelCallers)();
|
|
42
43
|
const api = (0, _api.setAPI)(agentIdentifier, forceDrain);
|
|
@@ -5,21 +5,21 @@
|
|
|
5
5
|
|
|
6
6
|
import { gosNREUM } from '../window/nreum';
|
|
7
7
|
import { getOrSet } from '../util/get-or-set';
|
|
8
|
-
import { mapOwn } from '../util/map-own';
|
|
9
8
|
import { getRuntime } from '../config/config';
|
|
10
|
-
|
|
9
|
+
import { EventContext } from './event-context';
|
|
10
|
+
import { bundleId } from '../ids/bundle-id';
|
|
11
11
|
|
|
12
|
+
// create a unique id to store event context data for the current agent bundle
|
|
13
|
+
const contextId = "nr@context:".concat(bundleId);
|
|
12
14
|
// create global emitter instance that can be shared among bundles
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
if
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
globalInstance = ee(undefined, 'globalEE');
|
|
15
|
+
const globalInstance = ee(undefined, 'globalEE');
|
|
16
|
+
|
|
17
|
+
// Only override the exposed event emitter if one has not already been exposed
|
|
18
|
+
const nr = gosNREUM();
|
|
19
|
+
if (!nr.ee) {
|
|
19
20
|
nr.ee = globalInstance;
|
|
20
21
|
}
|
|
21
|
-
export { globalInstance as ee };
|
|
22
|
-
function EventContext() {}
|
|
22
|
+
export { globalInstance as ee, contextId };
|
|
23
23
|
function ee(old, debugId) {
|
|
24
24
|
var handlers = {};
|
|
25
25
|
var bufferGroupMap = {};
|
|
@@ -56,9 +56,9 @@ function ee(old, debugId) {
|
|
|
56
56
|
if (contextOrStore && contextOrStore instanceof EventContext) {
|
|
57
57
|
return contextOrStore;
|
|
58
58
|
} else if (contextOrStore) {
|
|
59
|
-
return getOrSet(contextOrStore,
|
|
59
|
+
return getOrSet(contextOrStore, contextId, () => new EventContext(contextId));
|
|
60
60
|
} else {
|
|
61
|
-
return
|
|
61
|
+
return new EventContext(contextId);
|
|
62
62
|
}
|
|
63
63
|
}
|
|
64
64
|
function emit(type, args, contextOrStore, force, bubble) {
|
|
@@ -105,12 +105,13 @@ function ee(old, debugId) {
|
|
|
105
105
|
return emitters[name] = emitters[name] || ee(emitter, name);
|
|
106
106
|
}
|
|
107
107
|
function bufferEventsByGroup(types, group) {
|
|
108
|
-
|
|
108
|
+
const eventBuffer = getBuffer();
|
|
109
|
+
group = group || 'feature';
|
|
109
110
|
|
|
110
111
|
// do not buffer events if agent has been aborted
|
|
111
112
|
if (emitter.aborted) return;
|
|
112
|
-
|
|
113
|
-
|
|
113
|
+
Object.entries(types || {}).forEach(_ref => {
|
|
114
|
+
let [_, type] = _ref;
|
|
114
115
|
bufferGroupMap[type] = group;
|
|
115
116
|
if (!(group in eventBuffer)) {
|
|
116
117
|
eventBuffer[group] = [];
|
|
@@ -128,14 +129,6 @@ function ee(old, debugId) {
|
|
|
128
129
|
return emitter.backlog;
|
|
129
130
|
}
|
|
130
131
|
}
|
|
131
|
-
|
|
132
|
-
// get context object from store object, or create if does not exist
|
|
133
|
-
export function getOrSetContext(obj) {
|
|
134
|
-
return getOrSet(obj, ctxId, getNewContext);
|
|
135
|
-
}
|
|
136
|
-
function getNewContext() {
|
|
137
|
-
return new EventContext();
|
|
138
|
-
}
|
|
139
132
|
function abort() {
|
|
140
133
|
globalInstance.aborted = true;
|
|
141
134
|
globalInstance.backlog = {};
|
|
@@ -48,7 +48,8 @@ export class Harvest extends SharedContext {
|
|
|
48
48
|
isFinalHarvest: spec.opts?.unload
|
|
49
49
|
});
|
|
50
50
|
const options = {
|
|
51
|
-
retry: !spec.opts?.unload && submitMethod === submitData.xhr
|
|
51
|
+
retry: !spec.opts?.unload && submitMethod === submitData.xhr,
|
|
52
|
+
isFinalHarvest: spec.opts?.unload === true
|
|
52
53
|
};
|
|
53
54
|
const payload = this.createPayload(spec.endpoint, options);
|
|
54
55
|
const caller = this.obfuscator.shouldObfuscate() ? this.obfuscateAndSend.bind(this) : this._send.bind(this);
|
|
@@ -250,9 +251,8 @@ export class Harvest extends SharedContext {
|
|
|
250
251
|
cleanPayload() {
|
|
251
252
|
let payload = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
|
|
252
253
|
const clean = input => {
|
|
253
|
-
if (typeof Uint8Array !== 'undefined' && input instanceof Uint8Array ||
|
|
254
|
-
|
|
255
|
-
}
|
|
254
|
+
if (typeof Uint8Array !== 'undefined' && input instanceof Uint8Array || Array.isArray(input)) return input;
|
|
255
|
+
if (typeof input === 'string') return input.length > 0 ? input : null;
|
|
256
256
|
return Object.entries(input || {}).reduce((accumulator, _ref2) => {
|
|
257
257
|
let [key, value] = _ref2;
|
|
258
258
|
if (typeof value === 'number' || typeof value === 'string' && value.length > 0 || typeof value === 'object' && Object.keys(value || {}).length > 0) {
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file Contains a unique identifier for the running agent bundle
|
|
3
|
+
* when loaded.
|
|
4
|
+
* @copyright 2023 New Relic Corporation. All rights reserved.
|
|
5
|
+
* @license Apache-2.0
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { generateUuid } from './unique-id';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Provides a unique id for the current agent bundle
|
|
12
|
+
*/
|
|
13
|
+
export const bundleId = generateUuid();
|
|
@@ -7,14 +7,15 @@
|
|
|
7
7
|
* This module is used directly by: session_trace.
|
|
8
8
|
* It is also called by -> wrapXhr <-, so see "wrap-xhr.js" for features that use this indirectly.
|
|
9
9
|
*/
|
|
10
|
-
import { ee as baseEE } from '../event-emitter/contextual-ee';
|
|
10
|
+
import { ee as baseEE, contextId } from '../event-emitter/contextual-ee';
|
|
11
11
|
import { createWrapperWithEmitter as wfn } from './wrap-function';
|
|
12
12
|
import { getOrSet } from '../util/get-or-set';
|
|
13
13
|
import { globalScope, isBrowserScope } from '../constants/runtime';
|
|
14
14
|
const wrapped = {};
|
|
15
|
-
const XHR = XMLHttpRequest;
|
|
15
|
+
const XHR = globalScope.XMLHttpRequest;
|
|
16
16
|
const ADD_EVENT_LISTENER = 'addEventListener';
|
|
17
17
|
const REMOVE_EVENT_LISTENER = 'removeEventListener';
|
|
18
|
+
const flag = "nr@wrapped:".concat(contextId);
|
|
18
19
|
|
|
19
20
|
/**
|
|
20
21
|
* Wraps `addEventListener` and `removeEventListener` on: global scope; the prototype of `XMLHttpRequest`, and
|
|
@@ -43,7 +44,7 @@ export function wrapEvents(sharedEE) {
|
|
|
43
44
|
if (originalListener === null || typeof originalListener !== 'function' && typeof originalListener !== 'object') {
|
|
44
45
|
return;
|
|
45
46
|
}
|
|
46
|
-
var wrapped = getOrSet(originalListener,
|
|
47
|
+
var wrapped = getOrSet(originalListener, flag, function () {
|
|
47
48
|
var listener = {
|
|
48
49
|
object: wrapHandleEvent,
|
|
49
50
|
function: originalListener
|
|
@@ -6,16 +6,14 @@
|
|
|
6
6
|
* @file Wraps `fetch` and related methods for instrumentation.
|
|
7
7
|
* This module is used by: ajax, spa.
|
|
8
8
|
*/
|
|
9
|
-
import { ee as baseEE } from '../event-emitter/contextual-ee';
|
|
9
|
+
import { ee as baseEE, contextId } from '../event-emitter/contextual-ee';
|
|
10
10
|
import { globalScope } from '../constants/runtime';
|
|
11
|
-
import { flag } from './wrap-function';
|
|
12
11
|
var prefix = 'fetch-';
|
|
13
12
|
var bodyPrefix = prefix + 'body-';
|
|
14
13
|
var bodyMethods = ['arrayBuffer', 'blob', 'json', 'text', 'formData'];
|
|
15
14
|
var Req = globalScope.Request;
|
|
16
15
|
var Res = globalScope.Response;
|
|
17
16
|
var proto = 'prototype';
|
|
18
|
-
var ctxId = 'nr@context';
|
|
19
17
|
const wrapped = {};
|
|
20
18
|
|
|
21
19
|
/**
|
|
@@ -72,7 +70,7 @@ export function wrapFetch(sharedEE) {
|
|
|
72
70
|
// we are wrapping args in an array so we can preserve the reference
|
|
73
71
|
ee.emit(prefix + 'before-start', [args], ctx);
|
|
74
72
|
var dtPayload;
|
|
75
|
-
if (ctx[
|
|
73
|
+
if (ctx[contextId] && ctx[contextId].dt) dtPayload = ctx[contextId].dt;
|
|
76
74
|
var origPromiseFromFetch = fn.apply(this, args);
|
|
77
75
|
ee.emit(prefix + 'start', [args, dtPayload], origPromiseFromFetch);
|
|
78
76
|
|
|
@@ -7,7 +7,8 @@
|
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
9
|
import { ee } from '../event-emitter/contextual-ee';
|
|
10
|
-
|
|
10
|
+
import { bundleId } from '../ids/bundle-id';
|
|
11
|
+
export const flag = "nr@original:".concat(bundleId);
|
|
11
12
|
|
|
12
13
|
/**
|
|
13
14
|
* A convenience alias of `hasOwnProperty`.
|
|
@@ -100,25 +101,23 @@ export function createWrapperWithEmitter(emitter, always) {
|
|
|
100
101
|
|
|
101
102
|
/**
|
|
102
103
|
* Creates wrapper functions around each of an array of methods on a specified object.
|
|
103
|
-
* @param {Object} obj
|
|
104
|
-
* @param {string[]} methods
|
|
105
|
-
* @param {string} [prefix='']
|
|
106
|
-
*
|
|
107
|
-
* @param {function|object} [getContext]
|
|
108
|
-
*
|
|
109
|
-
* @param {boolean} [bubble=false]
|
|
110
|
-
*
|
|
104
|
+
* @param {Object} obj The object to which the specified methods belong.
|
|
105
|
+
* @param {string[]} methods An array of method names to be wrapped.
|
|
106
|
+
* @param {string} [prefix=''] A prefix to add to the names of emitted events. If `-` is the first character, also
|
|
107
|
+
* adds the method name before the prefix.
|
|
108
|
+
* @param {function|object} [getContext] The function or object that will serve as the 'this' context for handlers
|
|
109
|
+
* of events emitted by this wrapper.
|
|
110
|
+
* @param {boolean} [bubble=false] If `true`, emitted events should also bubble up to the old emitter upon which
|
|
111
|
+
* the `emitter` in the current scope was based (if it defines one).
|
|
111
112
|
*/
|
|
112
113
|
function inPlace(obj, methods, prefix, getContext, bubble) {
|
|
113
114
|
if (!prefix) prefix = '';
|
|
115
|
+
|
|
114
116
|
// If prefix starts with '-' set this boolean to add the method name to the prefix before passing each one to wrap.
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
for (i = 0; i < methods.length; i++) {
|
|
120
|
-
method = methods[i];
|
|
121
|
-
fn = obj[method];
|
|
117
|
+
const prependMethodPrefix = prefix.charAt(0) === '-';
|
|
118
|
+
for (let i = 0; i < methods.length; i++) {
|
|
119
|
+
const method = methods[i];
|
|
120
|
+
const fn = obj[method];
|
|
122
121
|
|
|
123
122
|
// Unless fn is both wrappable and unwrapped, bail so we don't add extra properties with undefined values.
|
|
124
123
|
if (notWrappable(fn)) continue;
|
|
@@ -212,32 +211,4 @@ function copy(from, to, emitter) {
|
|
|
212
211
|
*/
|
|
213
212
|
function notWrappable(fn) {
|
|
214
213
|
return !(fn && fn instanceof Function && fn.apply && !fn[flag]);
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
/**
|
|
218
|
-
* Creates a wrapped version of a function using a specified wrapper function. The new wrapped function references the
|
|
219
|
-
* original function. Also copies the properties of the original function to the wrapped function.
|
|
220
|
-
* @param {function} fn - A function to be wrapped.
|
|
221
|
-
* @param {function} wrapper - A higher order function that returns a new function, which executes the function passed
|
|
222
|
-
* to the higher order function as an argument.
|
|
223
|
-
* @returns {function} A wrapped function with an internal reference to the original function.
|
|
224
|
-
*/
|
|
225
|
-
export function wrapFunction(fn, wrapper) {
|
|
226
|
-
var wrapped = wrapper(fn);
|
|
227
|
-
wrapped[flag] = fn;
|
|
228
|
-
copy(fn, wrapped, ee);
|
|
229
|
-
return wrapped;
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
/**
|
|
233
|
-
* Replaces a function with a wrapped version of itself. To preserve object references, rather than taking the function
|
|
234
|
-
* itself as an argument, takes the object on which the particular named function is a property.
|
|
235
|
-
* @param {Object} obj - The object on which the named function is a property.
|
|
236
|
-
* @param {string} fnName - The name of the function to be wrapped.
|
|
237
|
-
* @param {function} wrapper - A higher order function that returns a new function, which executes the function passed
|
|
238
|
-
* to the higher order function as an argument.
|
|
239
|
-
*/
|
|
240
|
-
export function wrapInPlace(obj, fnName, wrapper) {
|
|
241
|
-
var fn = obj[fnName];
|
|
242
|
-
obj[fnName] = wrapFunction(fn, wrapper);
|
|
243
214
|
}
|
|
@@ -9,8 +9,7 @@
|
|
|
9
9
|
|
|
10
10
|
import { ee as baseEE } from '../event-emitter/contextual-ee';
|
|
11
11
|
import { createWrapperWithEmitter as wfn } from './wrap-function';
|
|
12
|
-
import {
|
|
13
|
-
import { isBrowserScope } from '../constants/runtime';
|
|
12
|
+
import { globalScope, isBrowserScope } from '../constants/runtime';
|
|
14
13
|
const wrapped = {};
|
|
15
14
|
|
|
16
15
|
/**
|
|
@@ -29,7 +28,7 @@ export function wrapMutation(sharedEE) {
|
|
|
29
28
|
wrapped[ee.debugId] = true; // otherwise, first feature to wrap mutations
|
|
30
29
|
|
|
31
30
|
var wrapFn = wfn(ee);
|
|
32
|
-
var OriginalObserver =
|
|
31
|
+
var OriginalObserver = globalScope.MutationObserver;
|
|
33
32
|
if (OriginalObserver) {
|
|
34
33
|
window.MutationObserver = function WrappedMutationObserver(cb) {
|
|
35
34
|
if (this instanceof OriginalObserver) {
|
|
@@ -8,8 +8,7 @@
|
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
10
|
import { createWrapperWithEmitter as wrapFn, flag } from './wrap-function';
|
|
11
|
-
import { ee as baseEE
|
|
12
|
-
import { originals } from '../config/config';
|
|
11
|
+
import { ee as baseEE } from '../event-emitter/contextual-ee';
|
|
13
12
|
import { globalScope } from '../constants/runtime';
|
|
14
13
|
const wrapped = {};
|
|
15
14
|
|
|
@@ -28,9 +27,9 @@ export function wrapPromise(sharedEE) {
|
|
|
28
27
|
if (wrapped[promiseEE.debugId]) return promiseEE;
|
|
29
28
|
wrapped[promiseEE.debugId] = true; // otherwise, first feature to wrap promise
|
|
30
29
|
|
|
31
|
-
var getContext =
|
|
30
|
+
var getContext = promiseEE.context;
|
|
32
31
|
var promiseWrapper = wrapFn(promiseEE);
|
|
33
|
-
var prevPromiseObj =
|
|
32
|
+
var prevPromiseObj = globalScope.Promise;
|
|
34
33
|
if (prevPromiseObj) {
|
|
35
34
|
// ensure there's a Promise API (native or otherwise) to even wrap
|
|
36
35
|
wrap();
|
|
@@ -11,7 +11,6 @@ import { wrapEvents } from './wrap-events';
|
|
|
11
11
|
import { ee as contextualEE } from '../event-emitter/contextual-ee';
|
|
12
12
|
import { eventListenerOpts } from '../event-listener/event-listener-opts';
|
|
13
13
|
import { createWrapperWithEmitter as wfn } from './wrap-function';
|
|
14
|
-
import { originals } from '../config/config';
|
|
15
14
|
import { globalScope } from '../constants/runtime';
|
|
16
15
|
import { warn } from '../util/console';
|
|
17
16
|
const wrapped = {};
|
|
@@ -35,32 +34,28 @@ export function wrapXhr(sharedEE) {
|
|
|
35
34
|
|
|
36
35
|
wrapEvents(baseEE); // wrap-events patches XMLHttpRequest.prototype.addEventListener for us
|
|
37
36
|
var wrapFn = wfn(ee);
|
|
38
|
-
var OrigXHR =
|
|
39
|
-
var MutationObserver =
|
|
40
|
-
var Promise =
|
|
41
|
-
var setImmediate =
|
|
37
|
+
var OrigXHR = globalScope.XMLHttpRequest;
|
|
38
|
+
var MutationObserver = globalScope.MutationObserver;
|
|
39
|
+
var Promise = globalScope.Promise;
|
|
40
|
+
var setImmediate = globalScope.setInterval;
|
|
42
41
|
var READY_STATE_CHANGE = 'readystatechange';
|
|
43
42
|
var handlers = ['onload', 'onerror', 'onabort', 'onloadstart', 'onloadend', 'onprogress', 'ontimeout'];
|
|
44
43
|
var pendingXhrs = [];
|
|
45
|
-
var activeListeners = globalScope.XMLHttpRequest.listeners;
|
|
46
44
|
var XHR = globalScope.XMLHttpRequest = newXHR;
|
|
47
45
|
function newXHR(opts) {
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
46
|
+
const xhr = new OrigXHR(opts);
|
|
47
|
+
const context = ee.context(xhr);
|
|
48
|
+
try {
|
|
49
|
+
ee.emit('new-xhr', [xhr], context);
|
|
50
|
+
xhr.addEventListener(READY_STATE_CHANGE, wrapXHR(context), eventListenerOpts(false));
|
|
51
|
+
} catch (e) {
|
|
52
|
+
warn('An error occurred while intercepting XHR', e);
|
|
51
53
|
try {
|
|
52
|
-
ee.emit('
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
warn('An error occured while intercepting XHR', e);
|
|
56
|
-
try {
|
|
57
|
-
ee.emit('internal-error', [e]);
|
|
58
|
-
} catch (err) {
|
|
59
|
-
// do nothing
|
|
60
|
-
}
|
|
54
|
+
ee.emit('internal-error', [e]);
|
|
55
|
+
} catch (err) {
|
|
56
|
+
// do nothing
|
|
61
57
|
}
|
|
62
58
|
}
|
|
63
|
-
this.listeners.forEach(listener => listener());
|
|
64
59
|
return xhr;
|
|
65
60
|
}
|
|
66
61
|
copy(OrigXHR, XHR);
|
|
@@ -74,14 +69,15 @@ export function wrapXhr(sharedEE) {
|
|
|
74
69
|
function wrapOnreadystatechange(args, xhr) {
|
|
75
70
|
wrapFn.inPlace(xhr, ['onreadystatechange'], 'fn-', getObject);
|
|
76
71
|
}
|
|
77
|
-
function wrapXHR() {
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
72
|
+
function wrapXHR(ctx) {
|
|
73
|
+
return function () {
|
|
74
|
+
var xhr = this;
|
|
75
|
+
if (xhr.readyState > 3 && !ctx.resolved) {
|
|
76
|
+
ctx.resolved = true;
|
|
77
|
+
ee.emit('xhr-resolved', [], xhr);
|
|
78
|
+
}
|
|
79
|
+
wrapFn.inPlace(xhr, handlers, 'fn-', getObject);
|
|
80
|
+
};
|
|
85
81
|
}
|
|
86
82
|
|
|
87
83
|
// Wrapping the onreadystatechange property of XHRs takes some special tricks.
|
|
@@ -10,10 +10,6 @@ import { stringify } from '../../../common/util/stringify';
|
|
|
10
10
|
export class DT {
|
|
11
11
|
constructor(agentIdentifier) {
|
|
12
12
|
this.agentIdentifier = agentIdentifier;
|
|
13
|
-
// Binds this class instance context to the following fn used in an external module (exported);
|
|
14
|
-
// Alternatively, can make them class field arrow functions, but requires experimental features/plugin for eslint.
|
|
15
|
-
this.generateTracePayload = this.generateTracePayload.bind(this);
|
|
16
|
-
this.shouldGenerateTrace = this.shouldGenerateTrace.bind(this);
|
|
17
13
|
}
|
|
18
14
|
generateTracePayload(parsedOrigin) {
|
|
19
15
|
if (!this.shouldGenerateTrace(parsedOrigin)) {
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
import { originals, getLoaderConfig, getRuntime } from '../../../common/config/config';
|
|
6
6
|
import { handle } from '../../../common/event-emitter/handle';
|
|
7
7
|
import { id } from '../../../common/ids/id';
|
|
8
|
-
import { ffVersion, globalScope } from '../../../common/constants/runtime';
|
|
8
|
+
import { ffVersion, globalScope, isBrowserScope } from '../../../common/constants/runtime';
|
|
9
9
|
import { dataSize } from '../../../common/util/data-size';
|
|
10
10
|
import { eventListenerOpts } from '../../../common/event-listener/event-listener-opts';
|
|
11
11
|
import { now } from '../../../common/timing/now';
|
|
@@ -19,7 +19,7 @@ import { FEATURE_NAMES } from '../../../loaders/features/features';
|
|
|
19
19
|
var handlers = ['load', 'error', 'abort', 'timeout'];
|
|
20
20
|
var handlersLen = handlers.length;
|
|
21
21
|
var origRequest = originals.REQ;
|
|
22
|
-
var origXHR =
|
|
22
|
+
var origXHR = originals.XHR;
|
|
23
23
|
export class Instrument extends InstrumentBase {
|
|
24
24
|
static featureName = FEATURE_NAME;
|
|
25
25
|
constructor(agentIdentifier, aggregator) {
|
|
@@ -185,15 +185,25 @@ function subscribeToEvents(agentIdentifier, ee, handler, dt) {
|
|
|
185
185
|
function onFetchBeforeStart(args) {
|
|
186
186
|
var opts = args[1] || {};
|
|
187
187
|
var url;
|
|
188
|
-
// argument is USVString
|
|
189
188
|
if (typeof args[0] === 'string') {
|
|
189
|
+
// argument is USVString
|
|
190
190
|
url = args[0];
|
|
191
|
-
|
|
191
|
+
if (url.length === 0 && isBrowserScope) {
|
|
192
|
+
url = '' + globalScope.location.href;
|
|
193
|
+
}
|
|
192
194
|
} else if (args[0] && args[0].url) {
|
|
195
|
+
// argument is Request object
|
|
193
196
|
url = args[0].url;
|
|
194
|
-
// argument is URL object
|
|
195
197
|
} else if (globalScope?.URL && args[0] && args[0] instanceof URL) {
|
|
198
|
+
// argument is URL object
|
|
196
199
|
url = args[0].href;
|
|
200
|
+
} else if (typeof args[0].toString === 'function') {
|
|
201
|
+
url = args[0].toString();
|
|
202
|
+
}
|
|
203
|
+
if (typeof url !== 'string' || url.length === 0) {
|
|
204
|
+
// Short-circuit DT since we could not determine the URL of the fetch call
|
|
205
|
+
// this is very unlikely to happen
|
|
206
|
+
return;
|
|
197
207
|
}
|
|
198
208
|
if (url) {
|
|
199
209
|
this.parsedOrigin = parseUrl(url);
|
|
@@ -203,7 +213,11 @@ function subscribeToEvents(agentIdentifier, ee, handler, dt) {
|
|
|
203
213
|
if (!payload || !payload.newrelicHeader && !payload.traceContextParentHeader) {
|
|
204
214
|
return;
|
|
205
215
|
}
|
|
206
|
-
if (
|
|
216
|
+
if (args[0] && args[0].headers) {
|
|
217
|
+
if (addHeaders(args[0].headers, payload)) {
|
|
218
|
+
this.dt = payload;
|
|
219
|
+
}
|
|
220
|
+
} else {
|
|
207
221
|
var clone = {};
|
|
208
222
|
for (var key in opts) {
|
|
209
223
|
clone[key] = opts[key];
|
|
@@ -217,10 +231,6 @@ function subscribeToEvents(agentIdentifier, ee, handler, dt) {
|
|
|
217
231
|
} else {
|
|
218
232
|
args.push(clone);
|
|
219
233
|
}
|
|
220
|
-
} else if (args[0] && args[0].headers) {
|
|
221
|
-
if (addHeaders(args[0].headers, payload)) {
|
|
222
|
-
this.dt = payload;
|
|
223
|
-
}
|
|
224
234
|
}
|
|
225
235
|
function addHeaders(headersObj, payload) {
|
|
226
236
|
var added = false;
|
|
@@ -148,6 +148,14 @@ export class Aggregate extends AggregateBase {
|
|
|
148
148
|
// Capture metrics for size of custom attributes
|
|
149
149
|
const jsAttributes = stringify(info.jsAttributes);
|
|
150
150
|
this.storeSupportabilityMetrics('PageSession/Feature/CustomData/Bytes', jsAttributes === '{}' ? 0 : jsAttributes.length);
|
|
151
|
+
|
|
152
|
+
// Capture metrics for performance markers and measures
|
|
153
|
+
if (typeof performance !== 'undefined') {
|
|
154
|
+
const markers = performance.getEntriesByType('mark');
|
|
155
|
+
const measures = performance.getEntriesByType('measure');
|
|
156
|
+
this.storeSupportabilityMetrics('Generic/Performance/Mark/Seen', markers.length);
|
|
157
|
+
this.storeSupportabilityMetrics('Generic/Performance/Measure/Seen', measures.length);
|
|
158
|
+
}
|
|
151
159
|
} catch (e) {
|
|
152
160
|
// do nothing
|
|
153
161
|
}
|
|
@@ -174,11 +174,13 @@ export class Aggregate extends AggregateBase {
|
|
|
174
174
|
this.scheduler.startTimer(this.harvestTimeSeconds);
|
|
175
175
|
}
|
|
176
176
|
try {
|
|
177
|
+
// Do not change the webpackChunkName or it will break the webpack nrba-chunking plugin
|
|
177
178
|
recorder = (await import( /* webpackChunkName: "recorder" */'rrweb')).record;
|
|
178
179
|
} catch (err) {
|
|
179
180
|
return this.abort();
|
|
180
181
|
}
|
|
181
182
|
try {
|
|
183
|
+
// Do not change the webpackChunkName or it will break the webpack nrba-chunking plugin
|
|
182
184
|
const {
|
|
183
185
|
gzipSync,
|
|
184
186
|
strToU8
|
|
@@ -57,9 +57,13 @@ export class Aggregate extends AggregateBase {
|
|
|
57
57
|
this.sentTrace = null;
|
|
58
58
|
this.harvestTimeSeconds = getConfigurationValue(agentIdentifier, 'session_trace.harvestTimeSeconds') || 10;
|
|
59
59
|
this.maxNodesPerHarvest = getConfigurationValue(agentIdentifier, 'session_trace.maxNodesPerHarvest') || 1000;
|
|
60
|
+
/**
|
|
61
|
+
* Standalone (mode) refers to the legacy version of ST before the idea of 'session' or the Replay feature existed.
|
|
62
|
+
* It has some different behavior vs when used in tandem with replay. */
|
|
60
63
|
this.isStandalone = false;
|
|
61
64
|
const operationalGate = new HandlerCache(); // acts as a controller-intermediary that can enable or disable this feature's collection dynamically
|
|
62
65
|
const sessionEntity = this.agentRuntime.session;
|
|
66
|
+
this.operationalGate = operationalGate;
|
|
63
67
|
|
|
64
68
|
/* --- The following section deals with user sessions concept & contains non-trivial control flow. --- */
|
|
65
69
|
const controlTraceOp = traceMode => {
|
|
@@ -243,17 +247,16 @@ export class Aggregate extends AggregateBase {
|
|
|
243
247
|
}
|
|
244
248
|
}
|
|
245
249
|
#prepareHarvest(options) {
|
|
246
|
-
/* Standalone refers to the legacy version of ST before the idea of 'session' or the Replay feature existed.
|
|
247
|
-
It has a different behavior on returning a payload for harvest than when used in tandem with either of those concepts. */
|
|
248
250
|
if (this.isStandalone) {
|
|
249
|
-
if (now()
|
|
250
|
-
//
|
|
251
|
-
|
|
252
|
-
this.
|
|
251
|
+
if (this.ptid && now() >= MAX_TRACE_DURATION) {
|
|
252
|
+
// Perform a final harvest once we hit or exceed the max session trace time
|
|
253
|
+
options.isFinalHarvest = true;
|
|
254
|
+
this.operationalGate.permanentlyDecide(false);
|
|
255
|
+
this.#scheduler.stopTimer(true);
|
|
256
|
+
} else if (this.ptid && this.nodeCount <= REQ_THRESHOLD_TO_SEND && !options.isFinalHarvest) {
|
|
257
|
+
// Only harvest when more than some threshold of nodes are pending, after the very first harvest, with the exception of the last outgoing harvest.
|
|
253
258
|
return;
|
|
254
259
|
}
|
|
255
|
-
// Only harvest when more than some threshold of nodes are pending, after the very first harvest.
|
|
256
|
-
if (this.ptid && this.nodeCount <= REQ_THRESHOLD_TO_SEND) return;
|
|
257
260
|
} else {
|
|
258
261
|
// -- *cli May '26 - Update: Not rate limiting backgrounded pages either for now.
|
|
259
262
|
// if (this.ptid && document.visibilityState === 'hidden' && this.nodeCount <= REQ_THRESHOLD_TO_SEND) return
|
|
@@ -443,6 +446,9 @@ export class Aggregate extends AggregateBase {
|
|
|
443
446
|
const openedSpace = this.trimSTNs(ERROR_MODE_SECONDS_WINDOW); // but maybe we could make some space by discarding irrelevant nodes if we're in sessioned Error mode
|
|
444
447
|
if (openedSpace == 0) return;
|
|
445
448
|
}
|
|
449
|
+
if (this.isStandalone && now() >= MAX_TRACE_DURATION) {
|
|
450
|
+
return;
|
|
451
|
+
}
|
|
446
452
|
if (this.trace[stn.n]) this.trace[stn.n].push(stn);else this.trace[stn.n] = [stn];
|
|
447
453
|
this.nodeCount++;
|
|
448
454
|
}
|