@newrelic/browser-agent 1.232.0 → 1.232.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/common/constants/env.cdn.js +1 -1
- package/dist/cjs/common/constants/env.npm.js +1 -1
- package/dist/cjs/common/url/canonicalize-url.js +32 -0
- package/dist/cjs/common/url/canonicalize-url.test.js +42 -0
- package/dist/cjs/common/url/clean-url.js +10 -3
- package/dist/cjs/common/util/global-scope.js +4 -2
- package/dist/cjs/common/wrap/wrap-fetch.js +1 -3
- package/dist/cjs/common/wrap/wrap-function.js +1 -3
- package/dist/cjs/features/ajax/instrument/index.js +1 -1
- package/dist/cjs/features/jserrors/aggregate/canonical-function-name.js +12 -4
- package/dist/cjs/features/jserrors/aggregate/compute-stack-trace.js +93 -10
- package/dist/cjs/features/jserrors/aggregate/compute-stack-trace.test.js +164 -38
- package/dist/cjs/features/jserrors/aggregate/index.js +22 -43
- package/dist/cjs/features/jserrors/instrument/index.js +0 -2
- package/dist/cjs/features/session_trace/aggregate/index.js +1 -3
- package/dist/esm/common/constants/env.cdn.js +1 -1
- package/dist/esm/common/constants/env.npm.js +1 -1
- package/dist/esm/common/url/canonicalize-url.js +27 -0
- package/dist/esm/common/url/canonicalize-url.test.js +38 -0
- package/dist/esm/common/url/clean-url.js +10 -3
- package/dist/esm/common/util/global-scope.js +1 -0
- package/dist/esm/common/wrap/wrap-fetch.js +1 -2
- package/dist/esm/common/wrap/wrap-function.js +1 -2
- package/dist/esm/features/ajax/instrument/index.js +1 -1
- package/dist/esm/features/jserrors/aggregate/canonical-function-name.js +12 -4
- package/dist/esm/features/jserrors/aggregate/compute-stack-trace.js +93 -10
- package/dist/esm/features/jserrors/aggregate/compute-stack-trace.test.js +149 -25
- package/dist/esm/features/jserrors/aggregate/index.js +23 -43
- package/dist/esm/features/jserrors/instrument/index.js +0 -1
- package/dist/esm/features/session_trace/aggregate/index.js +1 -2
- package/dist/types/common/url/canonicalize-url.d.ts +9 -0
- package/dist/types/common/url/canonicalize-url.d.ts.map +1 -0
- package/dist/types/common/url/clean-url.d.ts +7 -1
- package/dist/types/common/url/clean-url.d.ts.map +1 -1
- package/dist/types/common/util/global-scope.d.ts +1 -0
- package/dist/types/common/util/global-scope.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.map +1 -1
- package/dist/types/features/jserrors/aggregate/canonical-function-name.d.ts +8 -1
- package/dist/types/features/jserrors/aggregate/canonical-function-name.d.ts.map +1 -1
- package/dist/types/features/jserrors/aggregate/compute-stack-trace.d.ts +48 -19
- package/dist/types/features/jserrors/aggregate/compute-stack-trace.d.ts.map +1 -1
- package/dist/types/features/jserrors/aggregate/index.d.ts +12 -3
- package/dist/types/features/jserrors/aggregate/index.d.ts.map +1 -1
- package/dist/types/features/jserrors/instrument/index.d.ts.map +1 -1
- package/dist/types/features/page_action/aggregate/index.d.ts +1 -1
- package/dist/types/features/page_action/aggregate/index.d.ts.map +1 -1
- package/dist/types/features/session_trace/aggregate/index.d.ts.map +1 -1
- package/package.json +4 -4
- package/src/common/url/canonicalize-url.js +28 -0
- package/src/common/url/canonicalize-url.test.js +34 -0
- package/src/common/url/clean-url.js +10 -3
- package/src/common/util/global-scope.js +2 -0
- package/src/common/wrap/wrap-fetch.js +1 -2
- package/src/common/wrap/wrap-function.js +1 -2
- package/src/features/ajax/instrument/index.js +1 -1
- package/src/features/jserrors/aggregate/canonical-function-name.js +12 -4
- package/src/features/jserrors/aggregate/compute-stack-trace.js +85 -11
- package/src/features/jserrors/aggregate/compute-stack-trace.test.js +141 -24
- package/src/features/jserrors/aggregate/index.js +21 -47
- package/src/features/jserrors/instrument/index.js +0 -1
- package/src/features/session_trace/aggregate/index.js +1 -2
|
@@ -8,7 +8,6 @@ import { mapOwn } from '../../../common/util/map-own';
|
|
|
8
8
|
import { stringify } from '../../../common/util/stringify';
|
|
9
9
|
import { parseUrl } from '../../../common/url/parse-url';
|
|
10
10
|
import { supportsPerformanceObserver } from '../../../common/window/supports-performance-observer';
|
|
11
|
-
import slice from 'lodash._slice';
|
|
12
11
|
import { getConfigurationValue, getInfo, getRuntime } from '../../../common/config/config';
|
|
13
12
|
import { now } from '../../../common/timing/now';
|
|
14
13
|
import { FEATURE_NAME } from '../constants';
|
|
@@ -252,7 +251,7 @@ export class Aggregate extends FeatureBase {
|
|
|
252
251
|
} else if (t && typeof t.tagName === 'string') {
|
|
253
252
|
origin = t.tagName.toLowerCase();
|
|
254
253
|
if (t.id) origin += '#' + t.id;
|
|
255
|
-
if (t.className) origin += '.' +
|
|
254
|
+
if (t.className) origin += '.' + Array.from(t.classList).join('.');
|
|
256
255
|
}
|
|
257
256
|
if (origin === 'unknown') {
|
|
258
257
|
if (typeof target === 'string') origin = target;else if (target === document) origin = 'document';else if (target === window) origin = 'window';else if (target instanceof FileReader) origin = 'FileReader';
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Converts a URL to its basic form without a query string or fragment. If the resulting URL is the same as the
|
|
3
|
+
* loader's origin URL, returns '<inline>'.
|
|
4
|
+
* @param {string} url - The URL to be canonicalized.
|
|
5
|
+
* @param {string} loaderOriginUrl - The origin URL of the agent loader, used for inline detection.
|
|
6
|
+
* @returns {string} The canonicalized URL, or '<inline>' if the URL matches the loader origin URL.
|
|
7
|
+
*/
|
|
8
|
+
export function canonicalizeUrl(url: string): string;
|
|
9
|
+
//# sourceMappingURL=canonicalize-url.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"canonicalize-url.d.ts","sourceRoot":"","sources":["../../../../src/common/url/canonicalize-url.js"],"names":[],"mappings":"AAQA;;;;;;GAMG;AACH,qCAJW,MAAM,GAEJ,MAAM,CAclB"}
|
|
@@ -1,2 +1,8 @@
|
|
|
1
|
-
|
|
1
|
+
/**
|
|
2
|
+
* Cleans a URL by removing the query string and fragment (hash portion).
|
|
3
|
+
* @param {string} url - The original URL to be cleaned.
|
|
4
|
+
* @param {boolean} [keepHash=false] - Whether to preserve the hash portion of the URL.
|
|
5
|
+
* @returns {string} The cleaned URL.
|
|
6
|
+
*/
|
|
7
|
+
export function cleanURL(url: string, keepHash?: boolean | undefined): string;
|
|
2
8
|
//# sourceMappingURL=clean-url.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"clean-url.d.ts","sourceRoot":"","sources":["../../../../src/common/url/clean-url.js"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"clean-url.d.ts","sourceRoot":"","sources":["../../../../src/common/url/clean-url.js"],"names":[],"mappings":"AAQA;;;;;GAKG;AACH,8BAJW,MAAM,mCAEJ,MAAM,CAIlB"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"global-scope.d.ts","sourceRoot":"","sources":["../../../../src/common/util/global-scope.js"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"global-scope.d.ts","sourceRoot":"","sources":["../../../../src/common/util/global-scope.js"],"names":[],"mappings":"AAwBA;;;;;GAKG;AACH,yCAEC;AACD,mCAYC;AACD,oDAEC;AA9CD,qCAC2D;AAE3D,oCACgG;AAEhG,0CAYI;AAEJ,qCAAwD"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"wrap-fetch.d.ts","sourceRoot":"","sources":["../../../../src/common/wrap/wrap-fetch.js"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"wrap-fetch.d.ts","sourceRoot":"","sources":["../../../../src/common/wrap/wrap-fetch.js"],"names":[],"mappings":"AAsBA;;;;;;;GAOG;AACH,oCAJW,MAAM,GAEJ,MAAM,CAqElB;AAED;;;;;;GAMG;AACH,mCAJW,MAAM,GAEJ,MAAM,CAIlB"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"wrap-function.d.ts","sourceRoot":"","sources":["../../../../src/common/wrap/wrap-function.js"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"wrap-function.d.ts","sourceRoot":"","sources":["../../../../src/common/wrap/wrap-function.js"],"names":[],"mappings":"AA0BA;;;;;GAKG;AACH,+EAHW,OAAO,YAkIjB;AA6DD;;;;;;;GAOG;AACH,wEAKC;AAED;;;;;;;GAOG;AACH,iCALW,MAAM,UACN,MAAM,2BAOhB;AA7OD,iCAAiC"}
|
|
@@ -1,2 +1,9 @@
|
|
|
1
|
-
|
|
1
|
+
/**
|
|
2
|
+
* Given a function name string, extracts only an alphanumeric segment at the end of the string (if one exists).
|
|
3
|
+
* This is useful for stack traces, where functions might not be named (e.g., anonymous, computed).
|
|
4
|
+
*
|
|
5
|
+
* @param {string} functionNameString - The original function name string.
|
|
6
|
+
* @returns {string|undefined} The canonical function name, or undefined if the input is falsy or no alphanumeric segments are found.
|
|
7
|
+
*/
|
|
8
|
+
export function canonicalFunctionName(functionNameString: string): string | undefined;
|
|
2
9
|
//# sourceMappingURL=canonical-function-name.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"canonical-function-name.d.ts","sourceRoot":"","sources":["../../../../../src/features/jserrors/aggregate/canonical-function-name.js"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"canonical-function-name.d.ts","sourceRoot":"","sources":["../../../../../src/features/jserrors/aggregate/canonical-function-name.js"],"names":[],"mappings":"AAOA;;;;;;GAMG;AACH,0DAHW,MAAM,GACJ,MAAM,GAAC,SAAS,CAS5B"}
|
|
@@ -1,22 +1,51 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
1
|
+
/**
|
|
2
|
+
* Represents an error with a stack trace.
|
|
3
|
+
* @typedef {Object} StackInfo
|
|
4
|
+
* @property {string} name - The name of the error (e.g. 'TypeError').
|
|
5
|
+
* @property {string} message - The error message.
|
|
6
|
+
* @property {string} stackString - The stack trace as a string.
|
|
7
|
+
* @property {Array<Object>} frames - An array of frames in the stack trace.
|
|
8
|
+
* @property {string} frames.url - The URL of the file containing the code for the frame.
|
|
9
|
+
* @property {string} frames.func - The name of the function associated with the frame.
|
|
10
|
+
* @property {number} frames.line - The line number of the code in the frame.
|
|
11
|
+
*/
|
|
12
|
+
/**
|
|
13
|
+
* Attempts to compute a stack trace for the given exception.
|
|
14
|
+
* @param {Error} ex - The exception for which to compute the stack trace.
|
|
15
|
+
* @returns {StackInfo} A stack trace object containing information about the frames on the stack.
|
|
16
|
+
*/
|
|
17
|
+
export function computeStackTrace(ex: Error): StackInfo;
|
|
18
|
+
/**
|
|
19
|
+
* Represents an error with a stack trace.
|
|
20
|
+
*/
|
|
21
|
+
export type StackInfo = {
|
|
22
|
+
/**
|
|
23
|
+
* - The name of the error (e.g. 'TypeError').
|
|
24
|
+
*/
|
|
25
|
+
name: string;
|
|
26
|
+
/**
|
|
27
|
+
* - The error message.
|
|
28
|
+
*/
|
|
29
|
+
message: string;
|
|
30
|
+
/**
|
|
31
|
+
* - The stack trace as a string.
|
|
32
|
+
*/
|
|
7
33
|
stackString: string;
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
34
|
+
/**
|
|
35
|
+
* - An array of frames in the stack trace.
|
|
36
|
+
*/
|
|
37
|
+
frames: Array<Object>;
|
|
38
|
+
/**
|
|
39
|
+
* - The URL of the file containing the code for the frame.
|
|
40
|
+
*/
|
|
41
|
+
url: string;
|
|
42
|
+
/**
|
|
43
|
+
* - The name of the function associated with the frame.
|
|
44
|
+
*/
|
|
45
|
+
func: string;
|
|
46
|
+
/**
|
|
47
|
+
* - The line number of the code in the frame.
|
|
48
|
+
*/
|
|
49
|
+
line: number;
|
|
21
50
|
};
|
|
22
51
|
//# sourceMappingURL=compute-stack-trace.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"compute-stack-trace.d.ts","sourceRoot":"","sources":["../../../../../src/features/jserrors/aggregate/compute-stack-trace.js"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"compute-stack-trace.d.ts","sourceRoot":"","sources":["../../../../../src/features/jserrors/aggregate/compute-stack-trace.js"],"names":[],"mappings":"AAqEA;;;;;;;;;;GAUG;AAEH;;;;GAIG;AACH,sCAHW,KAAK,GACH,SAAS,CA2CrB;;;;;;;;UAvDa,MAAM;;;;aACN,MAAM;;;;iBACN,MAAM;;;;YACN,MAAM,MAAM,CAAC;;;;SACb,MAAM;;;;UACN,MAAM;;;;UACN,MAAM"}
|
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @typedef {import('./compute-stack-trace.js').StackInfo} StackInfo
|
|
3
|
+
*/
|
|
1
4
|
export class Aggregate extends FeatureBase {
|
|
2
5
|
static featureName: string;
|
|
3
6
|
constructor(agentIdentifier: any, aggregator: any);
|
|
@@ -13,12 +16,18 @@ export class Aggregate extends FeatureBase {
|
|
|
13
16
|
onHarvestFinished(result: any): void;
|
|
14
17
|
nameHash(params: any): number;
|
|
15
18
|
getBucketName(params: any, customParams: any): string;
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
+
/**
|
|
20
|
+
* Builds a standardized stack trace string from the frames in the given `stackInfo` object, with each frame separated
|
|
21
|
+
* by a newline character. Lines take the form `<functionName>@<url>:<lineNumber>`.
|
|
22
|
+
*
|
|
23
|
+
* @param {StackInfo} stackInfo - An object specifying a stack string and individual frames.
|
|
24
|
+
* @returns {string} A canonical stack string built from the URLs and function names in the given `stackInfo` object.
|
|
25
|
+
*/
|
|
26
|
+
buildCanonicalStackString(stackInfo: StackInfo): string;
|
|
19
27
|
storeError(err: any, time: any, internal: any, customAttributes: any): void;
|
|
20
28
|
onInteractionSaved(interaction: any): void;
|
|
21
29
|
onInteractionDiscarded(interaction: any): void;
|
|
22
30
|
}
|
|
31
|
+
export type StackInfo = import('./compute-stack-trace.js').StackInfo;
|
|
23
32
|
import { FeatureBase } from '../../utils/feature-base';
|
|
24
33
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/features/jserrors/aggregate/index.js"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/features/jserrors/aggregate/index.js"],"names":[],"mappings":"AAwBA;;GAEG;AAEH;IACE,2BAAiC;IACjC,mDAmCC;IAhCC,kBAAuB;IACvB,qBAA0B;IAC1B,eAAoB;IAEpB,qBAAwB;IA8B1B;;;MAoBC;IAfG,iBAAuB;IAiB3B,qCAWC;IAED,8BAEC;IAED,sDAEC;IAED;;;;;;OAMG;IACH,qCAHW,SAAS,GACP,MAAM,CAgBlB;IAED,4EAsFC;IAED,2CAgCC;IAED,+CA2BC;CACF;wBAlQY,OAAO,0BAA0B,EAAE,SAAS;4BAH7B,0BAA0B"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/features/jserrors/instrument/index.js"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/features/jserrors/instrument/index.js"],"names":[],"mappings":"AAkBA;IACE,2BAAiC;IACjC,mEAgDC;IA5CC,iBAAiB;IAGf,2CAA0C;IAyB5C,iCAAsC;IActC,yBAA+B;IAUjC;;;;;;;;;OASG;IACH,wBAPW,MAAM,YACN,MAAM,UACN,MAAM,UACN,MAAM,YACN,KAAK,MAAI,2BAiBnB;;CACF;+BA3F8B,6BAA6B"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/features/page_action/aggregate/index.js"],"names":[],"mappings":"AAgBA;IACE,2BAAiC;IACjC,mDA2BC;IAzBC,wBAA0B;IAC1B,wBAAsL;IACtL,yBAA2E;IAI3E,cAAgB;IAEhB,SAAqD;IAEZ,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/features/page_action/aggregate/index.js"],"names":[],"mappings":"AAgBA;IACE,2BAAiC;IACjC,mDA2BC;IAzBC,wBAA0B;IAC1B,wBAAsL;IACtL,yBAA2E;IAI3E,cAAgB;IAEhB,SAAqD;IAEZ,gCAA8C;IAiBzF;;;;;;;;MAkBC;IALG,wCAAgC;IAOpC,qCAKC;IAGD,wDAoCC;CACF;4BAlG2B,0BAA0B"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/features/session_trace/aggregate/index.js"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/features/session_trace/aggregate/index.js"],"names":[],"mappings":"AAiBA;IACE,2BAAiC;IACjC,mDAoHC;IA7GC,yBAAc;IACd;;;;;;;;;;;;kBAOC;IACD;;;;;kBAKC;IACD;;;;;;;;;;;;;;;;;;;;;;;;kBAwBC;IAED,sBAAe;IACf,8BAAkB;IAClB,iCAAqB;IACrB,wBAA0G;IAC1G,wBAA4G;IAE5G,8BAAkB;IAgEpB,oDAKC;IAED,2BAuBC;IAED,+DAaC;IAED,oFAmBC;IAED,wBAQC;IAED,uCAqBC;IAED,gDAUC;IAED,qCAoBC;IAED,qEAUC;IAED,mEAUC;IAED,yBASC;IAED,sCASC;IAED,yBAmCC;IAED,gCAEC;IAED,+DAyBC;IAED,+BAEC;IAED,6BAEC;IAED,uCAEC;IAED,4BAIC;IAED,oDAMC;CACF;4BA5Y2B,0BAA0B"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@newrelic/browser-agent",
|
|
3
|
-
"version": "1.232.
|
|
3
|
+
"version": "1.232.1",
|
|
4
4
|
"private": false,
|
|
5
5
|
"author": "New Relic Browser Agent Team <browser-agent@newrelic.com>",
|
|
6
6
|
"description": "Tests for the New Relic JavaScript agent",
|
|
@@ -116,8 +116,8 @@
|
|
|
116
116
|
"cdn:watch:extension": "jung -r ./src -F '.*\\.test\\.js' --run -- npm run cdn:build:extension",
|
|
117
117
|
"cdn:webpack": "npx webpack --progress --config ./webpack.config.js",
|
|
118
118
|
"packages:bundle": "webpack -c tools/jil/webpack.modular.js",
|
|
119
|
-
"test-server": "node ./tools/
|
|
120
|
-
"sauce:connect": "node ./tools/
|
|
119
|
+
"test-server": "node ./tools/wdio/bin/server",
|
|
120
|
+
"sauce:connect": "node ./tools/saucelabs/bin.mjs",
|
|
121
121
|
"sauce:get-browsers": "node ./tools/browsers-lists/sauce-browsers.mjs",
|
|
122
122
|
"tools:test-builds": "npm --prefix ./tools/test-builds run build-all",
|
|
123
123
|
"third-party-updates": "oss third-party manifest --includeOptDeps && oss third-party notices --includeOptDeps",
|
|
@@ -136,7 +136,6 @@
|
|
|
136
136
|
},
|
|
137
137
|
"dependencies": {
|
|
138
138
|
"core-js": "^3.26.0",
|
|
139
|
-
"lodash._slice": "^2.4.1",
|
|
140
139
|
"web-vitals": "^3.1.0"
|
|
141
140
|
},
|
|
142
141
|
"devDependencies": {
|
|
@@ -146,6 +145,7 @@
|
|
|
146
145
|
"@babel/eslint-parser": "^7.19.1",
|
|
147
146
|
"@babel/plugin-syntax-import-assertions": "^7.20.0",
|
|
148
147
|
"@babel/preset-env": "^7.20.2",
|
|
148
|
+
"@babel/register": "^7.21.0",
|
|
149
149
|
"@faker-js/faker": "^7.6.0",
|
|
150
150
|
"@fastify/cors": "^8.2.0",
|
|
151
151
|
"@fastify/multipart": "^7.3.0",
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright 2020 New Relic Corporation. All rights reserved.
|
|
3
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { initialLocation } from '../util/global-scope'
|
|
7
|
+
import { cleanURL } from './clean-url'
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Converts a URL to its basic form without a query string or fragment. If the resulting URL is the same as the
|
|
11
|
+
* loader's origin URL, returns '<inline>'.
|
|
12
|
+
* @param {string} url - The URL to be canonicalized.
|
|
13
|
+
* @param {string} loaderOriginUrl - The origin URL of the agent loader, used for inline detection.
|
|
14
|
+
* @returns {string} The canonicalized URL, or '<inline>' if the URL matches the loader origin URL.
|
|
15
|
+
*/
|
|
16
|
+
export function canonicalizeUrl (url) {
|
|
17
|
+
if (typeof url !== 'string') return ''
|
|
18
|
+
|
|
19
|
+
const cleanedUrl = cleanURL(url)
|
|
20
|
+
const cleanedGlobalScopeUrl = cleanURL(initialLocation)
|
|
21
|
+
|
|
22
|
+
// If the URL matches the origin URL of the loader, we assume it originated within an inline script.
|
|
23
|
+
if (cleanedUrl === cleanedGlobalScopeUrl) {
|
|
24
|
+
return '<inline>'
|
|
25
|
+
} else {
|
|
26
|
+
return cleanedUrl
|
|
27
|
+
}
|
|
28
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
afterEach(() => {
|
|
2
|
+
jest.resetModules()
|
|
3
|
+
})
|
|
4
|
+
|
|
5
|
+
test.each([null, undefined, 34])('returns empty string when url argument is %s', async (url) => {
|
|
6
|
+
const { canonicalizeUrl } = await import('./canonicalize-url')
|
|
7
|
+
expect(canonicalizeUrl(url)).toEqual('')
|
|
8
|
+
})
|
|
9
|
+
|
|
10
|
+
test('strips URLs of query strings and fragments', async () => {
|
|
11
|
+
jest.doMock('../util/global-scope', () => ({
|
|
12
|
+
initialLocation: 'http://different-domain.com/'
|
|
13
|
+
}))
|
|
14
|
+
const { canonicalizeUrl } = await import('./canonicalize-url')
|
|
15
|
+
expect(canonicalizeUrl('http://example.com/path?query=string#fragment')).toBe('http://example.com/path')
|
|
16
|
+
expect(canonicalizeUrl('https://www.example.com/path/to/file.html?param=value')).toBe('https://www.example.com/path/to/file.html')
|
|
17
|
+
expect(canonicalizeUrl('https://www.example.com/?param=value#fragment')).toBe('https://www.example.com/')
|
|
18
|
+
})
|
|
19
|
+
|
|
20
|
+
test('returns <inline> when matching the page URL of the loader', async () => {
|
|
21
|
+
jest.doMock('../util/global-scope', () => ({
|
|
22
|
+
initialLocation: 'http://example.com/'
|
|
23
|
+
}))
|
|
24
|
+
const { canonicalizeUrl } = await import('./canonicalize-url')
|
|
25
|
+
expect(canonicalizeUrl('http://example.com/')).toEqual('<inline>')
|
|
26
|
+
})
|
|
27
|
+
|
|
28
|
+
test('does not identify sub-paths of the loader origin as <inline>', async () => {
|
|
29
|
+
jest.doMock('../util/global-scope', () => ({
|
|
30
|
+
initialLocation: 'http://example.com/'
|
|
31
|
+
}))
|
|
32
|
+
const { canonicalizeUrl } = await import('./canonicalize-url')
|
|
33
|
+
expect(canonicalizeUrl('http://example.com/path/to/script.js')).not.toEqual('<inline>')
|
|
34
|
+
})
|
|
@@ -3,8 +3,15 @@
|
|
|
3
3
|
* SPDX-License-Identifier: Apache-2.0
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
var
|
|
7
|
-
var
|
|
6
|
+
var patternWithHash = /([^?#]*)[^#]*(#[^?]*|$).*/
|
|
7
|
+
var patternWithoutHash = /([^?#]*)().*/
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Cleans a URL by removing the query string and fragment (hash portion).
|
|
11
|
+
* @param {string} url - The original URL to be cleaned.
|
|
12
|
+
* @param {boolean} [keepHash=false] - Whether to preserve the hash portion of the URL.
|
|
13
|
+
* @returns {string} The cleaned URL.
|
|
14
|
+
*/
|
|
8
15
|
export function cleanURL (url, keepHash) {
|
|
9
|
-
return url.replace(keepHash ?
|
|
16
|
+
return url.replace(keepHash ? patternWithHash : patternWithoutHash, '$1$2')
|
|
10
17
|
}
|
|
@@ -20,6 +20,8 @@ export let globalScope = (() => {
|
|
|
20
20
|
throw new Error('New Relic browser agent shutting down due to error: Unable to locate global scope. This is possibly due to code redefining browser global variables like "self" and "window".')
|
|
21
21
|
})()
|
|
22
22
|
|
|
23
|
+
export const initialLocation = '' + globalScope.location
|
|
24
|
+
|
|
23
25
|
/**
|
|
24
26
|
* The below methods are only used for testing and should be removed once the
|
|
25
27
|
* reliant tests are moved to Jest.
|
|
@@ -7,7 +7,6 @@
|
|
|
7
7
|
* This module is used by: ajax, spa.
|
|
8
8
|
*/
|
|
9
9
|
import { ee as baseEE } from '../event-emitter/contextual-ee'
|
|
10
|
-
import slice from 'lodash._slice'
|
|
11
10
|
import { globalScope } from '../util/global-scope'
|
|
12
11
|
import { flag } from './wrap-function'
|
|
13
12
|
|
|
@@ -71,7 +70,7 @@ export function wrapFetch (sharedEE) {
|
|
|
71
70
|
var fn = target[name]
|
|
72
71
|
if (typeof fn === 'function') {
|
|
73
72
|
target[name] = function () {
|
|
74
|
-
var args =
|
|
73
|
+
var args = Array.from(arguments)
|
|
75
74
|
|
|
76
75
|
var ctx = {}
|
|
77
76
|
// we are wrapping args in an array so we can preserve the reference
|
|
@@ -7,7 +7,6 @@
|
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
9
|
import { ee } from '../event-emitter/contextual-ee'
|
|
10
|
-
import slice from 'lodash._slice'
|
|
11
10
|
export const flag = 'nr@original'
|
|
12
11
|
|
|
13
12
|
/**
|
|
@@ -77,7 +76,7 @@ export function createWrapperWithEmitter (emitter, always) {
|
|
|
77
76
|
|
|
78
77
|
try {
|
|
79
78
|
originalThis = this
|
|
80
|
-
args =
|
|
79
|
+
args = Array.from(arguments)
|
|
81
80
|
|
|
82
81
|
if (typeof getContext === 'function') {
|
|
83
82
|
ctx = getContext(args, originalThis)
|
|
@@ -99,7 +99,7 @@ function subscribeToEvents (agentIdentifier, ee, handler, dt) {
|
|
|
99
99
|
|
|
100
100
|
function onOpenXhrEnd (args, xhr) {
|
|
101
101
|
var loader_config = getLoaderConfig(agentIdentifier)
|
|
102
|
-
if (
|
|
102
|
+
if (loader_config.xpid && this.sameOrigin) {
|
|
103
103
|
xhr.setRequestHeader('X-NewRelic-ID', loader_config.xpid)
|
|
104
104
|
}
|
|
105
105
|
|
|
@@ -3,11 +3,19 @@
|
|
|
3
3
|
* SPDX-License-Identifier: Apache-2.0
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
|
|
7
|
-
export function canonicalFunctionName (orig) {
|
|
8
|
-
if (!orig) return
|
|
6
|
+
const canonicalFunctionNameRe = /([a-z0-9]+)$/i
|
|
9
7
|
|
|
10
|
-
|
|
8
|
+
/**
|
|
9
|
+
* Given a function name string, extracts only an alphanumeric segment at the end of the string (if one exists).
|
|
10
|
+
* This is useful for stack traces, where functions might not be named (e.g., anonymous, computed).
|
|
11
|
+
*
|
|
12
|
+
* @param {string} functionNameString - The original function name string.
|
|
13
|
+
* @returns {string|undefined} The canonical function name, or undefined if the input is falsy or no alphanumeric segments are found.
|
|
14
|
+
*/
|
|
15
|
+
export function canonicalFunctionName (functionNameString) {
|
|
16
|
+
if (!functionNameString) return
|
|
17
|
+
|
|
18
|
+
const match = functionNameString.match(canonicalFunctionNameRe)
|
|
11
19
|
if (match) return match[1]
|
|
12
20
|
|
|
13
21
|
return
|
|
@@ -57,6 +57,7 @@
|
|
|
57
57
|
// ex.message = ...
|
|
58
58
|
// ex.name = ReferenceError
|
|
59
59
|
import { formatStackTrace } from './format-stack-trace'
|
|
60
|
+
import { canonicalizeUrl } from '../../../common/url/canonicalize-url'
|
|
60
61
|
|
|
61
62
|
var debug = false
|
|
62
63
|
|
|
@@ -66,6 +67,23 @@ var gecko = /^\s*(?:(\S*|global code)(?:\(.*?\))?@)?((?:file|http|https|chrome|s
|
|
|
66
67
|
var chrome_eval = /^\s*at .+ \(eval at \S+ \((?:(?:file|http|https):[^)]+)?\)(?:, [^:]*:\d+:\d+)?\)$/i
|
|
67
68
|
var ie_eval = /^\s*at Function code \(Function code:\d+:\d+\)\s*/i
|
|
68
69
|
|
|
70
|
+
/**
|
|
71
|
+
* Represents an error with a stack trace.
|
|
72
|
+
* @typedef {Object} StackInfo
|
|
73
|
+
* @property {string} name - The name of the error (e.g. 'TypeError').
|
|
74
|
+
* @property {string} message - The error message.
|
|
75
|
+
* @property {string} stackString - The stack trace as a string.
|
|
76
|
+
* @property {Array<Object>} frames - An array of frames in the stack trace.
|
|
77
|
+
* @property {string} frames.url - The URL of the file containing the code for the frame.
|
|
78
|
+
* @property {string} frames.func - The name of the function associated with the frame.
|
|
79
|
+
* @property {number} frames.line - The line number of the code in the frame.
|
|
80
|
+
*/
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Attempts to compute a stack trace for the given exception.
|
|
84
|
+
* @param {Error} ex - The exception for which to compute the stack trace.
|
|
85
|
+
* @returns {StackInfo} A stack trace object containing information about the frames on the stack.
|
|
86
|
+
*/
|
|
69
87
|
export function computeStackTrace (ex) {
|
|
70
88
|
var stack = null
|
|
71
89
|
|
|
@@ -110,9 +128,9 @@ export function computeStackTrace (ex) {
|
|
|
110
128
|
}
|
|
111
129
|
|
|
112
130
|
/**
|
|
113
|
-
* Computes stack trace information from the stack property.
|
|
114
|
-
*
|
|
115
|
-
* @param {Error} ex
|
|
131
|
+
* Computes stack trace information from the stack property. Chrome and Gecko use this property.
|
|
132
|
+
*
|
|
133
|
+
* @param {Error} ex - The error object to compute the stack trace for.
|
|
116
134
|
* @return {?Object.<string, *>} Stack trace information.
|
|
117
135
|
*/
|
|
118
136
|
function computeStackTraceFromStackProp (ex) {
|
|
@@ -136,22 +154,49 @@ function computeStackTraceFromStackProp (ex) {
|
|
|
136
154
|
}
|
|
137
155
|
}
|
|
138
156
|
|
|
157
|
+
/**
|
|
158
|
+
* Parses a line from a JavaScript error stack trace and adds it to the given `info` object.
|
|
159
|
+
* Ignores all stack entries thrown from one of our wrapper functions.
|
|
160
|
+
*
|
|
161
|
+
* @param {object} info - The `info` object to add the parsed line to.
|
|
162
|
+
* @param {string} line - The line to parse.
|
|
163
|
+
* @returns {object} The `info` object with the parsed line added.
|
|
164
|
+
*/
|
|
139
165
|
function parseStackProp (info, line) {
|
|
140
|
-
|
|
166
|
+
let element = getStackElement(line)
|
|
141
167
|
|
|
168
|
+
// This catches lines that aren't frames (like the first line stating the error).
|
|
142
169
|
if (!element) {
|
|
143
170
|
info.stackLines.push(line)
|
|
144
171
|
return info
|
|
145
172
|
}
|
|
146
173
|
|
|
147
|
-
|
|
148
|
-
|
|
174
|
+
// Once we've seen a wrapper, ignore all subsequent stack entries.
|
|
175
|
+
if (isNrWrapper(element.func)) info.wrapperSeen = true
|
|
176
|
+
if (!info.wrapperSeen) {
|
|
177
|
+
// Query strings and fragments should be removed, and URLs matching the loader's origin should be "<inline>".
|
|
178
|
+
let canonicalUrl = canonicalizeUrl(element.url)
|
|
179
|
+
if (canonicalUrl !== element.url) {
|
|
180
|
+
line = line.replace(element.url, canonicalUrl)
|
|
181
|
+
element.url = canonicalUrl
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
info.stackLines.push(line)
|
|
185
|
+
info.frames.push(element)
|
|
186
|
+
}
|
|
149
187
|
|
|
150
|
-
if (!info.wrapperSeen) info.frames.push(element)
|
|
151
188
|
return info
|
|
152
189
|
}
|
|
153
190
|
|
|
154
|
-
|
|
191
|
+
/**
|
|
192
|
+
* Parses a line from a JavaScript error stack trace to extract information about a stack trace element, such as the
|
|
193
|
+
* URL, function name, line number, and column number.
|
|
194
|
+
*
|
|
195
|
+
* @param {string} line - A single line from a JavaScript error stack trace.
|
|
196
|
+
* @returns {object} An object containing information about the stack trace element, including the URL, function
|
|
197
|
+
* name, line number, and column number (if available).
|
|
198
|
+
*/
|
|
199
|
+
function getStackElement (line) {
|
|
155
200
|
var parts = line.match(gecko)
|
|
156
201
|
if (!parts) parts = line.match(chrome)
|
|
157
202
|
|
|
@@ -169,6 +214,14 @@ function getElement (line) {
|
|
|
169
214
|
}
|
|
170
215
|
}
|
|
171
216
|
|
|
217
|
+
/**
|
|
218
|
+
* Computes a stack trace object from an error object, by extracting the source and line number from the error object,
|
|
219
|
+
* and using them to create a single stack frame.
|
|
220
|
+
*
|
|
221
|
+
* @param {Error} ex - The error object to compute the stack trace for.
|
|
222
|
+
* @returns {Object|null} - An object representing the computed stack trace, or null if the
|
|
223
|
+
* input error object does not contain a line number.
|
|
224
|
+
*/
|
|
172
225
|
function computeStackTraceBySourceAndLine (ex) {
|
|
173
226
|
if (!('line' in ex)) return null
|
|
174
227
|
|
|
@@ -187,7 +240,10 @@ function computeStackTraceBySourceAndLine (ex) {
|
|
|
187
240
|
})
|
|
188
241
|
}
|
|
189
242
|
|
|
190
|
-
|
|
243
|
+
// Remove any query string and fragment
|
|
244
|
+
var canonicalUrl = canonicalizeUrl(ex.sourceURL)
|
|
245
|
+
|
|
246
|
+
var stackString = className + ': ' + ex.message + '\n at ' + canonicalUrl
|
|
191
247
|
if (ex.line) {
|
|
192
248
|
stackString += ':' + ex.line
|
|
193
249
|
if (ex.column) {
|
|
@@ -201,13 +257,19 @@ function computeStackTraceBySourceAndLine (ex) {
|
|
|
201
257
|
message: ex.message,
|
|
202
258
|
stackString: stackString,
|
|
203
259
|
frames: [{
|
|
204
|
-
url:
|
|
260
|
+
url: canonicalUrl,
|
|
205
261
|
line: ex.line,
|
|
206
262
|
column: ex.column
|
|
207
263
|
}]
|
|
208
264
|
})
|
|
209
265
|
}
|
|
210
266
|
|
|
267
|
+
/**
|
|
268
|
+
* For exceptions with no stack and only a message, derives a stack trace by extracting the class name and message.
|
|
269
|
+
*
|
|
270
|
+
* @param {Error} ex - The exception for which to compute the stack trace.
|
|
271
|
+
* @returns {StackTrace} A stack trace object containing the name and message of the exception.
|
|
272
|
+
*/
|
|
211
273
|
function computeStackTraceWithMessageOnly (ex) {
|
|
212
274
|
var className = ex.name || getClassName(ex)
|
|
213
275
|
if (!className) return null
|
|
@@ -221,11 +283,23 @@ function computeStackTraceWithMessageOnly (ex) {
|
|
|
221
283
|
})
|
|
222
284
|
}
|
|
223
285
|
|
|
286
|
+
/**
|
|
287
|
+
* Attempts to extract the name of the constructor function (the class) of the given object.
|
|
288
|
+
*
|
|
289
|
+
* @param {Object} obj - The object for which to extract the constructor function name.
|
|
290
|
+
* @returns {string} The name of the constructor function, or 'unknown' if the name cannot be determined.
|
|
291
|
+
*/
|
|
224
292
|
function getClassName (obj) {
|
|
225
293
|
var results = classNameRegex.exec(String(obj.constructor))
|
|
226
294
|
return (results && results.length > 1) ? results[1] : 'unknown'
|
|
227
295
|
}
|
|
228
296
|
|
|
229
|
-
|
|
297
|
+
/**
|
|
298
|
+
* Checks whether the given function name is a New Relic wrapper function.
|
|
299
|
+
*
|
|
300
|
+
* @param {string} functionName - The name of the function to check.
|
|
301
|
+
* @returns {boolean} True if the function name includes the string 'nrWrapper', false otherwise.
|
|
302
|
+
*/
|
|
303
|
+
function isNrWrapper (functionName) {
|
|
230
304
|
return (functionName && functionName.indexOf('nrWrapper') >= 0)
|
|
231
305
|
}
|