@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.
Files changed (116) hide show
  1. package/dist/cjs/common/constants/env.cdn.js +1 -1
  2. package/dist/cjs/common/constants/env.npm.js +1 -1
  3. package/dist/cjs/common/deny-list/deny-list.js +1 -1
  4. package/dist/cjs/common/event-emitter/contextual-ee.js +18 -26
  5. package/dist/cjs/common/event-emitter/event-context.js +12 -0
  6. package/dist/cjs/common/harvest/harvest.js +4 -4
  7. package/dist/cjs/common/ids/bundle-id.js +19 -0
  8. package/dist/cjs/common/wrap/wrap-events.js +3 -2
  9. package/dist/cjs/common/wrap/wrap-fetch.js +1 -3
  10. package/dist/cjs/common/wrap/wrap-function.js +15 -46
  11. package/dist/cjs/common/wrap/wrap-mutation.js +1 -2
  12. package/dist/cjs/common/wrap/wrap-promise.js +2 -3
  13. package/dist/cjs/common/wrap/wrap-xhr.js +23 -27
  14. package/dist/cjs/features/ajax/instrument/distributed-tracing.js +0 -4
  15. package/dist/cjs/features/ajax/instrument/index.js +19 -9
  16. package/dist/cjs/features/metrics/aggregate/index.js +8 -0
  17. package/dist/cjs/features/session_replay/aggregate/index.js +2 -0
  18. package/dist/cjs/features/session_trace/aggregate/index.js +14 -8
  19. package/dist/cjs/features/spa/aggregate/index.js +1 -1
  20. package/dist/cjs/loaders/agent-base.js +12 -0
  21. package/dist/cjs/loaders/api/api.js +14 -1
  22. package/dist/cjs/loaders/api/interaction-types.js +11 -4
  23. package/dist/cjs/loaders/configure/configure.js +2 -1
  24. package/dist/esm/common/constants/env.cdn.js +1 -1
  25. package/dist/esm/common/constants/env.npm.js +1 -1
  26. package/dist/esm/common/deny-list/deny-list.js +1 -1
  27. package/dist/esm/common/event-emitter/contextual-ee.js +16 -23
  28. package/dist/esm/common/event-emitter/event-context.js +5 -0
  29. package/dist/esm/common/harvest/harvest.js +4 -4
  30. package/dist/esm/common/ids/bundle-id.js +13 -0
  31. package/dist/esm/common/wrap/wrap-events.js +4 -3
  32. package/dist/esm/common/wrap/wrap-fetch.js +2 -4
  33. package/dist/esm/common/wrap/wrap-function.js +15 -44
  34. package/dist/esm/common/wrap/wrap-mutation.js +2 -3
  35. package/dist/esm/common/wrap/wrap-promise.js +3 -4
  36. package/dist/esm/common/wrap/wrap-xhr.js +23 -27
  37. package/dist/esm/features/ajax/instrument/distributed-tracing.js +0 -4
  38. package/dist/esm/features/ajax/instrument/index.js +20 -10
  39. package/dist/esm/features/metrics/aggregate/index.js +8 -0
  40. package/dist/esm/features/session_replay/aggregate/index.js +2 -0
  41. package/dist/esm/features/session_trace/aggregate/index.js +14 -8
  42. package/dist/esm/features/spa/aggregate/index.js +1 -1
  43. package/dist/esm/loaders/agent-base.js +12 -0
  44. package/dist/esm/loaders/api/api.js +14 -1
  45. package/dist/esm/loaders/api/interaction-types.js +11 -4
  46. package/dist/esm/loaders/configure/configure.js +3 -2
  47. package/dist/types/common/event-emitter/contextual-ee.d.ts +22 -2
  48. package/dist/types/common/event-emitter/contextual-ee.d.ts.map +1 -1
  49. package/dist/types/common/event-emitter/event-context.d.ts +5 -0
  50. package/dist/types/common/event-emitter/event-context.d.ts.map +1 -0
  51. package/dist/types/common/event-emitter/register-handler.d.ts +1 -1
  52. package/dist/types/common/harvest/harvest.d.ts.map +1 -1
  53. package/dist/types/common/ids/bundle-id.d.ts +5 -0
  54. package/dist/types/common/ids/bundle-id.d.ts.map +1 -0
  55. package/dist/types/common/session/session-entity.d.ts +6 -6
  56. package/dist/types/common/window/nreum.d.ts +2 -2
  57. package/dist/types/common/wrap/wrap-events.d.ts.map +1 -1
  58. package/dist/types/common/wrap/wrap-fetch.d.ts.map +1 -1
  59. package/dist/types/common/wrap/wrap-function.d.ts +1 -19
  60. package/dist/types/common/wrap/wrap-function.d.ts.map +1 -1
  61. package/dist/types/common/wrap/wrap-mutation.d.ts.map +1 -1
  62. package/dist/types/common/wrap/wrap-promise.d.ts.map +1 -1
  63. package/dist/types/common/wrap/wrap-xhr.d.ts.map +1 -1
  64. package/dist/types/features/ajax/instrument/distributed-tracing.d.ts +1 -1
  65. package/dist/types/features/ajax/instrument/distributed-tracing.d.ts.map +1 -1
  66. package/dist/types/features/metrics/aggregate/endpoint-map.d.ts +5 -5
  67. package/dist/types/features/metrics/aggregate/index.d.ts.map +1 -1
  68. package/dist/types/features/session_replay/aggregate/index.d.ts.map +1 -1
  69. package/dist/types/features/session_trace/aggregate/index.d.ts +5 -0
  70. package/dist/types/features/session_trace/aggregate/index.d.ts.map +1 -1
  71. package/dist/types/loaders/agent-base.d.ts +9 -0
  72. package/dist/types/loaders/agent-base.d.ts.map +1 -1
  73. package/dist/types/loaders/api/api.d.ts +6 -0
  74. package/dist/types/loaders/api/api.d.ts.map +1 -1
  75. package/dist/types/loaders/api/interaction-types.d.ts +18 -7
  76. package/dist/types/loaders/api/interaction-types.d.ts.map +1 -1
  77. package/dist/types/loaders/configure/configure.d.ts +1 -0
  78. package/dist/types/loaders/configure/configure.d.ts.map +1 -1
  79. package/dist/types/loaders/features/features.d.ts +9 -9
  80. package/dist/types/loaders/micro-agent.d.ts +1 -1
  81. package/dist/types/loaders/micro-agent.d.ts.map +1 -1
  82. package/package.json +22 -27
  83. package/src/common/deny-list/deny-list.js +1 -1
  84. package/src/common/deny-list/deny-list.test.js +103 -30
  85. package/src/common/event-emitter/{contextual-ee.test.js → contextual-ee.component-test.js} +15 -32
  86. package/src/common/event-emitter/contextual-ee.js +20 -31
  87. package/src/common/event-emitter/event-context.js +5 -0
  88. package/src/common/harvest/harvest.js +4 -4
  89. package/src/common/harvest/harvest.test.js +1 -1
  90. package/src/common/ids/__mocks__/bundle-id.js +2 -0
  91. package/src/common/ids/__mocks__/unique-id.js +17 -0
  92. package/src/common/ids/bundle-id.js +13 -0
  93. package/src/common/url/__mocks__/parse-url.js +15 -0
  94. package/src/common/util/__mocks__/get-or-set.js +5 -0
  95. package/src/common/window/__mocks__/nreum.js +10 -0
  96. package/src/common/wrap/wrap-events.js +4 -3
  97. package/src/common/wrap/wrap-fetch.js +2 -4
  98. package/src/common/wrap/wrap-function.js +16 -44
  99. package/src/common/wrap/wrap-mutation.js +2 -3
  100. package/src/common/wrap/{wrap-promise.test.js → wrap-promise.component-test.js} +2 -32
  101. package/src/common/wrap/wrap-promise.js +3 -4
  102. package/src/common/wrap/wrap-xhr.js +24 -28
  103. package/src/features/ajax/instrument/distributed-tracing.js +0 -4
  104. package/src/features/ajax/instrument/distributed-tracing.test.js +375 -0
  105. package/src/features/ajax/instrument/index.js +22 -10
  106. package/src/features/metrics/aggregate/index.js +8 -0
  107. package/src/features/session_replay/aggregate/index.js +2 -0
  108. package/src/features/session_trace/aggregate/index.js +16 -7
  109. package/src/features/spa/aggregate/index.js +1 -1
  110. package/src/loaders/agent-base.js +12 -0
  111. package/src/loaders/api/api.component-test.js +45 -0
  112. package/src/loaders/api/api.js +14 -1
  113. package/src/loaders/api/interaction-types.js +11 -4
  114. package/src/loaders/configure/configure.js +11 -2
  115. /package/src/common/url/{encode.component-test.js → encode.test.js} +0 -0
  116. /package/src/common/url/{protocol.component-test.js → protocol.test.js} +0 -0
package/package.json CHANGED
@@ -1,9 +1,9 @@
1
1
  {
2
2
  "name": "@newrelic/browser-agent",
3
- "version": "1.237.0",
3
+ "version": "1.238.0",
4
4
  "private": false,
5
5
  "author": "New Relic Browser Agent Team <browser-agent@newrelic.com>",
6
- "description": "Tests for the New Relic JavaScript agent",
6
+ "description": "New Relic Browser Agent",
7
7
  "license": "Apache-2.0",
8
8
  "homepage": "https://docs.newrelic.com/docs/browser/browser-monitoring",
9
9
  "main": "./dist/cjs/index.js",
@@ -92,35 +92,30 @@
92
92
  "node": ">=12.17.0 < 13.0.0 || >=13.7.0"
93
93
  },
94
94
  "scripts": {
95
- "wdio": "node --max-old-space-size=8192 tools/wdio/bin/cli.js",
96
95
  "start": "npm-run-all --parallel cdn:watch test-server",
97
- "test:unit": "jest",
98
- "test:component": "jest --config jest.component-config.js",
96
+ "lint": "eslint -c .eslintrc.js --ext .js,.cjs,.mjs .",
97
+ "lint:fix": "npm run lint -- --fix",
98
+ "test": "jest",
99
+ "test:unit": "jest --selectProjects unit",
100
+ "test:component": "jest --selectProjects component",
101
+ "test:types": "tsd -f ./tests/dts/**/*.ts",
102
+ "wdio": "node --max-old-space-size=8192 tools/wdio/bin/cli.js",
103
+ "jil": "tools/jil/bin/cli.js",
99
104
  "build:all": "npm run cdn:build:local && npm run build:npm && npm run tools:test-builds",
100
105
  "build:npm": "npm run npm:build:esm && npm run npm:build:cjs && npm run npm:build:types && npm run npm:pack",
101
106
  "build:browser-tests": "npm --prefix ./tools/test-builds/browser-tests run build",
102
- "lint": "eslint -c .eslintrc.js --ext .js,.cjs,.mjs .",
103
- "lint:fix": "npm run lint -- --fix",
104
- "test-jil": "tools/jil/bin/cli.js",
105
- "ci-build": "npm ci --cache ./.npm",
106
- "build-tar": "npm run ci-build && tar czf ./build/browser-agent.tar.gz --exclude=.git --exclude=.npm --exclude=build/browser-agent.tar.gz .",
107
- "watch": "jung -D '\\.npm|.git|node_modules|build|tools|tests' --run -- npm run build:all",
107
+ "watch:browser-tests": "jung -r ./src -F '.*\\.test\\.js' --run -- npm run build:browser-tests",
108
108
  "cdn:build": "npm run cdn:build:prod",
109
- "cdn:build:extension": "PUBLISH=EXTENSION rm -rf ./build && npm run cdn:webpack && npm run cdn:cleanup",
110
- "cdn:build:local": "npm run cdn:webpack && npm run cdn:cleanup",
111
- "cdn:build:prod": "npm run cdn:fresh-build && PUBLISH=PROD npm run cdn:webpack && npm run cdn:cleanup && npm run cdn:clone",
112
- "cdn:build:dev": "npm run cdn:fresh-build && PUBLISH=DEV npm run cdn:webpack && npm run cdn:cleanup",
113
- "cdn:build:current": "npm run cdn:fresh-build && PUBLISH=CURRENT npm run cdn:webpack && npm run cdn:cleanup",
114
- "cdn:build:nr": "npm run cdn:fresh-build && PUBLISH=PROD SOURCEMAPS=false npm run cdn:webpack && npm run cdn:cleanup",
115
- "cdn:build:pr": "npm run cdn:fresh-build && PUBLISH=PR npm run cdn:webpack && npm run cdn:cleanup",
116
- "cdn:build:experiment": "npm run cdn:fresh-build && PUBLISH=EXPERIMENT npm run cdn:webpack && npm run cdn:cleanup",
117
- "cdn:cleanup": "node ./tools/scripts/post-build-cleanup",
118
- "cdn:clone": "node ./tools/scripts/clone",
119
- "cdn:fresh-build": "rm -rf ./build",
109
+ "cdn:build:local": "npm run cdn:webpack",
110
+ "cdn:build:prod": "npm run cdn:webpack -- --env mode=prod",
111
+ "postcdn:build:prod": "npm run cdn:clone",
112
+ "cdn:build:dev": "npm run cdn:webpack -- --env mode=dev",
113
+ "cdn:build:experiment": "npm run cdn:webpack -- --env mode=experiment",
114
+ "cdn:webpack": "npx webpack --progress --config ./tools/webpack/index.mjs",
115
+ "postcdn:webpack": "npm run cdn:cleanup",
120
116
  "cdn:watch": "jung -r ./src -F '.*\\.test\\.js' --run -- npm run cdn:build:local",
121
- "cdn:watch:extension": "jung -r ./src -F '.*\\.test\\.js' --run -- npm run cdn:build:extension",
122
- "cdn:webpack": "npx webpack --progress --config ./webpack.config.js",
123
- "packages:bundle": "webpack -c tools/jil/webpack.modular.js",
117
+ "cdn:cleanup": "node ./tools/webpack/scripts/cleanup.mjs",
118
+ "cdn:clone": "node ./tools/webpack/scripts/clone.mjs",
124
119
  "test-server": "node ./tools/wdio/bin/server",
125
120
  "sauce:connect": "node ./tools/saucelabs/bin.mjs",
126
121
  "sauce:get-browsers": "node ./tools/browsers-lists/sauce-browsers.mjs",
@@ -130,8 +125,7 @@
130
125
  "npm:build:esm": "npx babel --env-name npm-esm --out-dir dist/esm --out-file-extension .js ./src",
131
126
  "npm:build:cjs": "npx babel --env-name npm-cjs --out-dir dist/cjs --out-file-extension .js ./src",
132
127
  "npm:build:types": "npx tsc -b",
133
- "npm:pack": "mkdir -p temp && export PKG_NAME=$(npm pack --pack-destination temp) && echo ./temp/$PKG_NAME",
134
- "watch:browser-tests": "jung -r ./src -F '.*\\.test\\.js' --run -- npm run build:browser-tests"
128
+ "npm:pack": "mkdir -p temp && export PKG_NAME=$(npm pack --pack-destination temp) && echo ./temp/$PKG_NAME"
135
129
  },
136
130
  "config": {
137
131
  "unsafe-perm": true
@@ -228,6 +222,7 @@
228
222
  "tape": "^4.8.0",
229
223
  "through": "^2.3.8",
230
224
  "through2": "^2.0.1",
225
+ "tsd": "^0.28.1",
231
226
  "typescript": "^5.0.3",
232
227
  "uuid": "^9.0.0",
233
228
  "wait-on": "^7.0.1",
@@ -65,7 +65,7 @@ export function setDenyList (denyListConfig) {
65
65
  host = url
66
66
  pathname = ''
67
67
  }
68
- let [hostname, port] = host.split(':')
68
+ let [hostname] = host.split(':')
69
69
 
70
70
  denyList.push({ hostname, pathname })
71
71
  }
@@ -1,31 +1,104 @@
1
- describe('Setting deny list', () => {
2
- let DlMod
3
- beforeEach(() => {
4
- jest.isolateModules(() => import('./deny-list.js').then(m => DlMod = m)) // give every test its own denyList (sandbox)
5
- })
6
-
7
- it('respects path', () => {
8
- DlMod.setDenyList(['bam.nr-data.net/somepath'])
9
-
10
- expect(DlMod.shouldCollectEvent({ hostname: 'bam.nr-data.net', pathname: '/somepath' })).toBeFalsy() // shouldCollectEvent returns 'false' when there IS a match...
11
- expect(DlMod.shouldCollectEvent({ hostname: 'bam.nr-data.net', port: '7890', protocol: 'https', host: 'bam.nr-data.net:7890', pathname: '/somepath' })).toBeFalsy()
12
- expect(DlMod.shouldCollectEvent({ hostname: 'bam.nr-data.com', pathname: '' })).toBeTruthy()
13
- expect(DlMod.shouldCollectEvent({ hostname: 'bam.nr-data.com', pathname: '/someotherpath' })).toBeTruthy()
14
- })
15
-
16
- it('ignores port', () => {
17
- DlMod.setDenyList(['bam.nr-data.net:1234'])
18
-
19
- expect(DlMod.shouldCollectEvent({ hostname: 'bam.nr-data.net', pathname: '', port: '4321' })).toBeFalsy()
20
- expect(DlMod.shouldCollectEvent({ hostname: 'bam.nr-data.net', port: '7890', protocol: 'http', host: 'bam.nr-data.net:7890', pathname: '/somepath' })).toBeFalsy()
21
- expect(DlMod.shouldCollectEvent({ hostname: 'bam.nr-data.com', pathname: '' })).toBeTruthy()
22
- })
23
-
24
- it('ignores protocol', () => {
25
- DlMod.setDenyList(['http://bam.nr-data.net'])
26
-
27
- expect(DlMod.shouldCollectEvent({ hostname: 'bam.nr-data.net', pathname: '', protocol: 'https' })).toBeFalsy()
28
- expect(DlMod.shouldCollectEvent({ hostname: 'bam.nr-data.net', port: '7890', protocol: 'https', host: 'bam.nr-data.net:7890', pathname: '/somepath' })).toBeFalsy()
29
- expect(DlMod.shouldCollectEvent({ hostname: 'bam.nr-data.com', pathname: '', protocol: 'http' })).toBeTruthy()
30
- })
1
+ jest.enableAutomock()
2
+ jest.unmock('./deny-list')
3
+
4
+ let denyListModule
5
+
6
+ beforeEach(async () => {
7
+ denyListModule = await import('./deny-list')
8
+ })
9
+
10
+ afterEach(() => {
11
+ jest.resetModules()
12
+ })
13
+
14
+ test('domain-only blocks all subdomains and all paths', () => {
15
+ denyListModule.setDenyList(['foo.com'])
16
+
17
+ expect(denyListModule.shouldCollectEvent({ hostname: 'foo.com', pathname: '/' })).toBeFalsy()
18
+ expect(denyListModule.shouldCollectEvent({ hostname: 'foo.com', pathname: '/a' })).toBeFalsy()
19
+ expect(denyListModule.shouldCollectEvent({ hostname: 'foo.com', pathname: '/a/b' })).toBeFalsy()
20
+
21
+ expect(denyListModule.shouldCollectEvent({ hostname: 'www.foo.com', pathname: '/' })).toBeFalsy()
22
+ expect(denyListModule.shouldCollectEvent({ hostname: 'a.b.foo.com', pathname: '/' })).toBeFalsy()
23
+ expect(denyListModule.shouldCollectEvent({ hostname: 'a.b.foo.com', pathname: '/c/d' })).toBeFalsy()
24
+
25
+ expect(denyListModule.shouldCollectEvent({ hostname: 'oo.com', pathname: '/' })).toBeTruthy()
26
+ expect(denyListModule.shouldCollectEvent({ hostname: 'bar.com', pathname: '/' })).toBeTruthy()
27
+ })
28
+
29
+ test('subdomain blocks further subdomains, but not parent domain', () => {
30
+ denyListModule.setDenyList(['bar.foo.com'])
31
+
32
+ expect(denyListModule.shouldCollectEvent({ hostname: 'bar.foo.com', pathname: '/' })).toBeFalsy()
33
+ expect(denyListModule.shouldCollectEvent({ hostname: 'bar.foo.com', pathname: '/a' })).toBeFalsy()
34
+ expect(denyListModule.shouldCollectEvent({ hostname: 'bar.foo.com', pathname: '/a/b' })).toBeFalsy()
35
+
36
+ expect(denyListModule.shouldCollectEvent({ hostname: 'a.bar.foo.com', pathname: '/' })).toBeFalsy()
37
+ expect(denyListModule.shouldCollectEvent({ hostname: 'a.bar.foo.com', pathname: '/a' })).toBeFalsy()
38
+ expect(denyListModule.shouldCollectEvent({ hostname: 'a.bar.foo.com', pathname: '/a/b' })).toBeFalsy()
39
+
40
+ expect(denyListModule.shouldCollectEvent({ hostname: 'foo.com', pathname: '/' })).toBeTruthy()
41
+ expect(denyListModule.shouldCollectEvent({ hostname: 'foo.com', pathname: '/a' })).toBeTruthy()
42
+ expect(denyListModule.shouldCollectEvent({ hostname: 'foo.com', pathname: '/a/b' })).toBeTruthy()
43
+
44
+ expect(denyListModule.shouldCollectEvent({ hostname: 'oo.com', pathname: '/' })).toBeTruthy()
45
+ expect(denyListModule.shouldCollectEvent({ hostname: 'bar.com', pathname: '/' })).toBeTruthy()
46
+ })
47
+
48
+ test('* blocks all domains', () => {
49
+ denyListModule.setDenyList(['*'])
50
+
51
+ expect(denyListModule.shouldCollectEvent({ hostname: 'foo.com', pathname: '/' })).toBeFalsy()
52
+ expect(denyListModule.shouldCollectEvent({ hostname: 'www.foo.com', pathname: '/' })).toBeFalsy()
53
+ expect(denyListModule.shouldCollectEvent({ hostname: 'bar.com', pathname: '/' })).toBeFalsy()
54
+ expect(denyListModule.shouldCollectEvent({ hostname: 'www.bar.com', pathname: '/' })).toBeFalsy()
55
+ })
56
+
57
+ test('respects path', () => {
58
+ denyListModule.setDenyList(['bam.nr-data.net/somepath'])
59
+
60
+ expect(denyListModule.shouldCollectEvent({ hostname: 'bam.nr-data.net', pathname: '/somepath' })).toBeFalsy()
61
+ expect(denyListModule.shouldCollectEvent({ hostname: 'bam.nr-data.net', port: '7890', protocol: 'https', host: 'bam.nr-data.net:7890', pathname: '/somepath' })).toBeFalsy()
62
+
63
+ expect(denyListModule.shouldCollectEvent({ hostname: 'bam.nr-data.com', pathname: '' })).toBeTruthy()
64
+ expect(denyListModule.shouldCollectEvent({ hostname: 'bam.nr-data.com', pathname: '/someotherpath' })).toBeTruthy()
65
+ expect(denyListModule.shouldCollectEvent({ hostname: 'bam.nr-data.com', pathname: '/some/otherpath' })).toBeTruthy()
66
+ })
67
+
68
+ test('ignores port', () => {
69
+ denyListModule.setDenyList(['bam.nr-data.net:1234'])
70
+
71
+ expect(denyListModule.shouldCollectEvent({ hostname: 'bam.nr-data.net', pathname: '', port: '4321' })).toBeFalsy()
72
+ expect(denyListModule.shouldCollectEvent({ hostname: 'bam.nr-data.net', port: '7890', protocol: 'http', host: 'bam.nr-data.net:7890', pathname: '/somepath' })).toBeFalsy()
73
+
74
+ expect(denyListModule.shouldCollectEvent({ hostname: 'bam.nr-data.com', pathname: '' })).toBeTruthy()
75
+ })
76
+
77
+ test('ignores protocol', () => {
78
+ denyListModule.setDenyList(['http://bam.nr-data.net'])
79
+
80
+ expect(denyListModule.shouldCollectEvent({ hostname: 'bam.nr-data.net', pathname: '', protocol: 'https' })).toBeFalsy()
81
+ expect(denyListModule.shouldCollectEvent({ hostname: 'bam.nr-data.net', port: '7890', protocol: 'https', host: 'bam.nr-data.net:7890', pathname: '/somepath' })).toBeFalsy()
82
+
83
+ expect(denyListModule.shouldCollectEvent({ hostname: 'bam.nr-data.com', pathname: '', protocol: 'http' })).toBeTruthy()
84
+ })
85
+
86
+ test.each([
87
+ null,
88
+ undefined,
89
+ '!@$%^*',
90
+ 'https://example.com/http://foo.bar/'
91
+ ])('ignores invalid deny list value %s', (denyListValue) => {
92
+ denyListModule.setDenyList([denyListValue])
93
+
94
+ expect(denyListModule.shouldCollectEvent({ hostname: 'foo.com', pathname: '/' })).toBeTruthy()
95
+ expect(denyListModule.shouldCollectEvent({ hostname: 'foo.com', pathname: '/a' })).toBeTruthy()
96
+ expect(denyListModule.shouldCollectEvent({ hostname: 'foo.com', pathname: '/a/b' })).toBeTruthy()
97
+
98
+ expect(denyListModule.shouldCollectEvent({ hostname: 'www.foo.com', pathname: '/' })).toBeTruthy()
99
+ expect(denyListModule.shouldCollectEvent({ hostname: 'a.b.foo.com', pathname: '/' })).toBeTruthy()
100
+ expect(denyListModule.shouldCollectEvent({ hostname: 'a.b.foo.com', pathname: '/c/d' })).toBeTruthy()
101
+
102
+ expect(denyListModule.shouldCollectEvent({ hostname: 'oo.com', pathname: '/' })).toBeTruthy()
103
+ expect(denyListModule.shouldCollectEvent({ hostname: 'bar.com', pathname: '/' })).toBeTruthy()
31
104
  })
@@ -24,19 +24,18 @@ afterEach(() => {
24
24
  })
25
25
 
26
26
  describe('global event-emitter', () => {
27
- test('it returns the event-emitter defined on window.NREUM', async () => {
28
- const mockEE = {}
29
- mockNREUM.ee = mockEE
30
-
27
+ test('it sets the global event-emitter on window.NREUM when it does not already exist', async () => {
31
28
  const { ee } = await import('./contextual-ee')
32
29
 
33
- expect(ee).toEqual(mockEE)
30
+ expect(ee).toEqual(mockNREUM.ee)
34
31
  })
35
32
 
36
- test('it sets the global event-emitter on window.NREUM', async () => {
33
+ test('it does not set the global event-emitter on window.NREUM when it already exists', async () => {
34
+ mockNREUM.ee = {}
35
+
37
36
  const { ee } = await import('./contextual-ee')
38
37
 
39
- expect(ee).toEqual(mockNREUM.ee)
38
+ expect(ee).not.toEqual(mockNREUM.ee)
40
39
  })
41
40
  })
42
41
 
@@ -79,7 +78,9 @@ describe('event-emitter context', () => {
79
78
 
80
79
  const result = ee.context()
81
80
 
82
- expect(result).toEqual({})
81
+ expect(result).toEqual(expect.objectContaining({
82
+ contextId: expect.stringContaining('nr@context:')
83
+ }))
83
84
  })
84
85
 
85
86
  test('it returns the same context', async () => {
@@ -87,7 +88,7 @@ describe('event-emitter context', () => {
87
88
 
88
89
  const result = ee.context()
89
90
 
90
- expect(result).toEqual(ee.context(result))
91
+ expect(result).toBe(ee.context(result))
91
92
  })
92
93
 
93
94
  test('it adds the context to the provided object', async () => {
@@ -95,8 +96,9 @@ describe('event-emitter context', () => {
95
96
 
96
97
  const obj = {}
97
98
  const result = ee.context(obj)
99
+ const ctxKey = Object.getOwnPropertyNames(obj).find(k => k.startsWith('nr@context'))
98
100
 
99
- expect(result).toEqual(obj['nr@context'])
101
+ expect(result).toBe(obj[ctxKey])
100
102
  })
101
103
  })
102
104
 
@@ -281,30 +283,11 @@ describe('event-emitter emit', () => {
281
283
  scopeEE,
282
284
  eventType,
283
285
  eventArgs,
284
- {}
286
+ expect.objectContaining({
287
+ contextId: expect.stringContaining('nr@context:')
288
+ })
285
289
  ])
286
290
  ]))
287
291
  expect(ee.backlog.feature).toEqual(scopeEE.backlog.feature)
288
292
  })
289
293
  })
290
-
291
- describe('getOrSetContext', () => {
292
- test('it returns a new context', async () => {
293
- const { getOrSetContext } = await import('./contextual-ee')
294
-
295
- const obj = {}
296
- const result = getOrSetContext(obj)
297
-
298
- expect(result).toEqual({})
299
- expect(result).toEqual(obj['nr@context'])
300
- })
301
-
302
- test('it returns the same context', async () => {
303
- const { getOrSetContext } = await import('./contextual-ee')
304
-
305
- const obj = {}
306
- const result = getOrSetContext(obj)
307
-
308
- expect(getOrSetContext(obj)).toEqual(result)
309
- })
310
- })
@@ -5,25 +5,22 @@
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'
9
+ import { EventContext } from './event-context'
10
+ import { bundleId } from '../ids/bundle-id'
10
11
 
11
- var ctxId = 'nr@context'
12
-
12
+ // create a unique id to store event context data for the current agent bundle
13
+ const contextId = `nr@context:${bundleId}`
13
14
  // create global emitter instance that can be shared among bundles
14
- let nr = gosNREUM()
15
- var globalInstance
15
+ const globalInstance = ee(undefined, 'globalEE')
16
16
 
17
- if (nr.ee) {
18
- globalInstance = nr.ee
19
- } else {
20
- globalInstance = ee(undefined, 'globalEE')
17
+ // Only override the exposed event emitter if one has not already been exposed
18
+ const nr = gosNREUM()
19
+ if (!nr.ee) {
21
20
  nr.ee = globalInstance
22
21
  }
23
22
 
24
- export { globalInstance as ee }
25
-
26
- function EventContext () { }
23
+ export { globalInstance as ee, contextId }
27
24
 
28
25
  function ee (old, debugId) {
29
26
  var handlers = {}
@@ -65,9 +62,9 @@ function ee (old, debugId) {
65
62
  if (contextOrStore && contextOrStore instanceof EventContext) {
66
63
  return contextOrStore
67
64
  } else if (contextOrStore) {
68
- return getOrSet(contextOrStore, ctxId, getNewContext)
65
+ return getOrSet(contextOrStore, contextId, () => new EventContext(contextId))
69
66
  } else {
70
- return getNewContext()
67
+ return new EventContext(contextId)
71
68
  }
72
69
  }
73
70
 
@@ -119,17 +116,18 @@ function ee (old, debugId) {
119
116
  }
120
117
 
121
118
  function bufferEventsByGroup (types, group) {
122
- var eventBuffer = getBuffer()
119
+ const eventBuffer = getBuffer()
120
+ group = group || 'feature'
123
121
 
124
122
  // do not buffer events if agent has been aborted
125
123
  if (emitter.aborted) return
126
- mapOwn(types, function (i, type) {
127
- group = group || 'feature'
128
- bufferGroupMap[type] = group
129
- if (!(group in eventBuffer)) {
130
- eventBuffer[group] = []
131
- }
132
- })
124
+ Object.entries(types || {})
125
+ .forEach(([_, type]) => {
126
+ bufferGroupMap[type] = group
127
+ if (!(group in eventBuffer)) {
128
+ eventBuffer[group] = []
129
+ }
130
+ })
133
131
  }
134
132
 
135
133
  function isBuffering (type) {
@@ -144,15 +142,6 @@ function ee (old, debugId) {
144
142
  }
145
143
  }
146
144
 
147
- // get context object from store object, or create if does not exist
148
- export function getOrSetContext (obj) {
149
- return getOrSet(obj, ctxId, getNewContext)
150
- }
151
-
152
- function getNewContext () {
153
- return new EventContext()
154
- }
155
-
156
145
  function abort () {
157
146
  globalInstance.aborted = true
158
147
  globalInstance.backlog = {}
@@ -0,0 +1,5 @@
1
+ export class EventContext {
2
+ constructor (contextId) {
3
+ this.contextId = contextId
4
+ }
5
+ }
@@ -47,7 +47,8 @@ export class Harvest extends SharedContext {
47
47
  sendX (spec = {}) {
48
48
  const submitMethod = submitData.getSubmitMethod({ isFinalHarvest: spec.opts?.unload })
49
49
  const options = {
50
- retry: !spec.opts?.unload && submitMethod === submitData.xhr
50
+ retry: !spec.opts?.unload && submitMethod === submitData.xhr,
51
+ isFinalHarvest: spec.opts?.unload === true
51
52
  }
52
53
  const payload = this.createPayload(spec.endpoint, options)
53
54
  const caller = this.obfuscator.shouldObfuscate() ? this.obfuscateAndSend.bind(this) : this._send.bind(this)
@@ -230,9 +231,8 @@ export class Harvest extends SharedContext {
230
231
  */
231
232
  cleanPayload (payload = {}) {
232
233
  const clean = (input) => {
233
- if ((typeof Uint8Array !== 'undefined' && input instanceof Uint8Array) || typeof input === 'string') {
234
- return input.length > 0 ? input : null
235
- }
234
+ if ((typeof Uint8Array !== 'undefined' && input instanceof Uint8Array) || Array.isArray(input)) return input
235
+ if (typeof input === 'string') return input.length > 0 ? input : null
236
236
  return Object.entries(input || {})
237
237
  .reduce((accumulator, [key, value]) => {
238
238
  if ((typeof value === 'number') ||
@@ -53,7 +53,7 @@ describe('sendX', () => {
53
53
 
54
54
  harvestInstance.sendX(spec)
55
55
 
56
- expect(harvestInstance.createPayload).toHaveBeenCalledWith(spec.endpoint, { retry: true })
56
+ expect(harvestInstance.createPayload).toHaveBeenCalledWith(spec.endpoint, { retry: true, isFinalHarvest: false })
57
57
  })
58
58
 
59
59
  test('should not use obfuscateAndSend', async () => {
@@ -0,0 +1,2 @@
1
+ import { faker } from '@faker-js/faker'
2
+ export const bundleId = faker.datatype.uuid()
@@ -0,0 +1,17 @@
1
+ import { faker } from '@faker-js/faker'
2
+
3
+ export function generateUuid () {
4
+ return faker.datatype.uuid()
5
+ }
6
+
7
+ export function generateRandomHexString (length) {
8
+ return faker.datatype.hexadecimal({ length, prefix: '' })
9
+ }
10
+
11
+ export function generateSpanId () {
12
+ return generateRandomHexString(16)
13
+ }
14
+
15
+ export function generateTraceId () {
16
+ return generateRandomHexString(32)
17
+ }
@@ -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()
@@ -0,0 +1,15 @@
1
+ export function parseUrl (url) {
2
+ try {
3
+ const result = new URL(url)
4
+
5
+ return {
6
+ hostname: result.hostname,
7
+ port: result.port || result.protocol.startsWith('https') ? '443' : '80',
8
+ pathname: result.pathname,
9
+ protocol: result.protocol.replace(':', ''),
10
+ sameOrigin: false
11
+ }
12
+ } catch (err) {
13
+ return {}
14
+ }
15
+ }
@@ -0,0 +1,5 @@
1
+ export const getOrSet = jest.fn((obj, prop, getVal) => {
2
+ if (obj[prop]) return obj[prop]
3
+ obj[prop] = getVal()
4
+ return obj[prop]
5
+ })
@@ -0,0 +1,10 @@
1
+ export const defaults = {}
2
+ export const gosNREUM = jest.fn(() => ({}))
3
+ export const gosNREUMInfo = jest.fn(() => ({}))
4
+ export const gosNREUMLoaderConfig = jest.fn(() => ({}))
5
+ export const gosNREUMInit = jest.fn(() => ({}))
6
+ export const gosNREUMOriginals = jest.fn(() => ({}))
7
+ export const gosNREUMInitializedAgents = jest.fn(() => ({}))
8
+ export const addToNREUM = jest.fn(() => ({}))
9
+ export const NREUMinitialized = jest.fn(() => ({}))
10
+ export const gosCDN = jest.fn(() => ({}))
@@ -7,15 +7,16 @@
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
 
15
15
  const wrapped = {}
16
- const XHR = XMLHttpRequest
16
+ const XHR = globalScope.XMLHttpRequest
17
17
  const ADD_EVENT_LISTENER = 'addEventListener'
18
18
  const REMOVE_EVENT_LISTENER = 'removeEventListener'
19
+ const flag = `nr@wrapped:${contextId}`
19
20
 
20
21
  /**
21
22
  * Wraps `addEventListener` and `removeEventListener` on: global scope; the prototype of `XMLHttpRequest`, and
@@ -48,7 +49,7 @@ export function wrapEvents (sharedEE) {
48
49
  return
49
50
  }
50
51
 
51
- var wrapped = getOrSet(originalListener, 'nr@wrapped', function () {
52
+ var wrapped = getOrSet(originalListener, flag, function () {
52
53
  var listener = {
53
54
  object: wrapHandleEvent,
54
55
  function: originalListener
@@ -6,9 +6,8 @@
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
 
13
12
  var prefix = 'fetch-'
14
13
  var bodyPrefix = prefix + 'body-'
@@ -16,7 +15,6 @@ var bodyMethods = ['arrayBuffer', 'blob', 'json', 'text', 'formData']
16
15
  var Req = globalScope.Request
17
16
  var Res = globalScope.Response
18
17
  var proto = 'prototype'
19
- var ctxId = 'nr@context'
20
18
 
21
19
  const wrapped = {}
22
20
 
@@ -76,7 +74,7 @@ export function wrapFetch (sharedEE) {
76
74
  // we are wrapping args in an array so we can preserve the reference
77
75
  ee.emit(prefix + 'before-start', [args], ctx)
78
76
  var dtPayload
79
- if (ctx[ctxId] && ctx[ctxId].dt) dtPayload = ctx[ctxId].dt
77
+ if (ctx[contextId] && ctx[contextId].dt) dtPayload = ctx[contextId].dt
80
78
 
81
79
  var origPromiseFromFetch = fn.apply(this, args)
82
80