@newrelic/browser-agent 1.233.1 → 1.234.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (97) 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/event-emitter/contextual-ee.test.js +10 -10
  4. package/dist/cjs/common/harvest/harvest-scheduler.test.js +2 -2
  5. package/dist/cjs/common/harvest/harvest.js +4 -11
  6. package/dist/cjs/common/harvest/harvest.test.js +224 -0
  7. package/dist/cjs/common/url/encode.js +2 -2
  8. package/dist/cjs/common/util/console.test.js +30 -0
  9. package/dist/cjs/common/util/get-or-set.js +8 -1
  10. package/dist/cjs/common/util/get-or-set.test.js +47 -0
  11. package/dist/cjs/common/util/stringify.test.js +48 -0
  12. package/dist/cjs/common/util/submit-data.js +15 -15
  13. package/dist/cjs/common/util/submit-data.test.js +221 -0
  14. package/dist/cjs/common/util/traverse.js +19 -27
  15. package/dist/cjs/common/util/traverse.test.js +44 -0
  16. package/dist/cjs/features/metrics/aggregate/endpoint-map.js +14 -0
  17. package/dist/cjs/features/metrics/aggregate/index.js +3 -2
  18. package/dist/cjs/features/metrics/instrument/index.js +0 -2
  19. package/dist/cjs/features/page_view_event/aggregate/index.js +58 -44
  20. package/dist/cjs/loaders/configure/configure.js +0 -1
  21. package/dist/esm/common/constants/env.cdn.js +1 -1
  22. package/dist/esm/common/constants/env.npm.js +1 -1
  23. package/dist/esm/common/event-emitter/contextual-ee.test.js +10 -10
  24. package/dist/esm/common/harvest/harvest-scheduler.test.js +2 -2
  25. package/dist/esm/common/harvest/harvest.js +4 -11
  26. package/dist/esm/common/harvest/harvest.test.js +222 -0
  27. package/dist/esm/common/url/encode.js +2 -2
  28. package/dist/esm/common/util/console.test.js +28 -0
  29. package/dist/esm/common/util/get-or-set.js +8 -1
  30. package/dist/esm/common/util/get-or-set.test.js +45 -0
  31. package/dist/esm/common/util/stringify.test.js +46 -0
  32. package/dist/esm/common/util/submit-data.js +15 -15
  33. package/dist/esm/common/util/submit-data.test.js +219 -0
  34. package/dist/esm/common/util/traverse.js +19 -27
  35. package/dist/esm/common/util/traverse.test.js +42 -0
  36. package/dist/esm/features/metrics/aggregate/endpoint-map.js +7 -0
  37. package/dist/esm/features/metrics/aggregate/index.js +3 -2
  38. package/dist/esm/features/metrics/instrument/index.js +0 -2
  39. package/dist/esm/features/page_view_event/aggregate/index.js +58 -44
  40. package/dist/esm/loaders/configure/configure.js +0 -1
  41. package/dist/types/common/config/state/init.d.ts.map +1 -1
  42. package/dist/types/common/context/shared-context.d.ts.map +1 -1
  43. package/dist/types/common/harvest/harvest.d.ts.map +1 -1
  44. package/dist/types/common/timer/timer.d.ts.map +1 -1
  45. package/dist/types/common/util/get-or-set.d.ts +9 -1
  46. package/dist/types/common/util/get-or-set.d.ts.map +1 -1
  47. package/dist/types/common/util/submit-data.d.ts +14 -10
  48. package/dist/types/common/util/submit-data.d.ts.map +1 -1
  49. package/dist/types/common/util/traverse.d.ts +10 -1
  50. package/dist/types/common/util/traverse.d.ts.map +1 -1
  51. package/dist/types/common/window/nreum.d.ts.map +1 -1
  52. package/dist/types/features/metrics/aggregate/endpoint-map.d.ts +8 -0
  53. package/dist/types/features/metrics/aggregate/endpoint-map.d.ts.map +1 -0
  54. package/dist/types/features/metrics/aggregate/index.d.ts.map +1 -1
  55. package/dist/types/features/metrics/aggregate/polyfill-detection.es5.d.ts.map +1 -1
  56. package/dist/types/features/metrics/instrument/index.d.ts.map +1 -1
  57. package/dist/types/features/page_view_event/aggregate/index.d.ts.map +1 -1
  58. package/dist/types/features/page_view_event/instrument/index.d.ts.map +1 -1
  59. package/dist/types/features/utils/handler-cache.d.ts.map +1 -1
  60. package/dist/types/loaders/configure/configure.d.ts.map +1 -1
  61. package/package.json +6 -2
  62. package/src/common/config/state/init.js +0 -1
  63. package/src/common/context/shared-context.js +0 -1
  64. package/src/common/event-emitter/contextual-ee.test.js +10 -10
  65. package/src/common/harvest/harvest-scheduler.test.js +2 -2
  66. package/src/common/harvest/harvest.js +5 -9
  67. package/src/common/harvest/harvest.test.js +169 -0
  68. package/src/common/session/session-entity.test.js +0 -1
  69. package/src/common/timer/timer.js +0 -1
  70. package/src/common/url/encode.js +2 -2
  71. package/src/common/util/console.test.js +34 -0
  72. package/src/common/util/get-or-set.js +8 -1
  73. package/src/common/util/get-or-set.test.js +58 -0
  74. package/src/common/util/stringify.test.js +49 -0
  75. package/src/common/util/submit-data.js +15 -16
  76. package/src/common/util/submit-data.test.js +218 -0
  77. package/src/common/util/traverse.js +18 -27
  78. package/src/common/util/traverse.test.js +50 -0
  79. package/src/common/window/nreum.js +0 -1
  80. package/src/features/metrics/aggregate/endpoint-map.js +7 -0
  81. package/src/features/metrics/aggregate/index.js +3 -2
  82. package/src/features/metrics/aggregate/polyfill-detection.es5.js +0 -1
  83. package/src/features/metrics/instrument/index.js +0 -2
  84. package/src/features/page_view_event/aggregate/index.js +48 -51
  85. package/src/features/page_view_event/instrument/index.js +0 -1
  86. package/src/features/utils/handler-cache.js +0 -1
  87. package/src/loaders/configure/configure.js +0 -1
  88. package/dist/cjs/common/util/s-hash.js +0 -19
  89. package/dist/cjs/features/metrics/instrument/workers-helper.js +0 -124
  90. package/dist/esm/common/util/s-hash.js +0 -13
  91. package/dist/esm/features/metrics/instrument/workers-helper.js +0 -119
  92. package/dist/types/common/util/s-hash.d.ts +0 -2
  93. package/dist/types/common/util/s-hash.d.ts.map +0 -1
  94. package/dist/types/features/metrics/instrument/workers-helper.d.ts +0 -7
  95. package/dist/types/features/metrics/instrument/workers-helper.d.ts.map +0 -1
  96. package/src/common/util/s-hash.js +0 -14
  97. package/src/features/metrics/instrument/workers-helper.js +0 -113
@@ -0,0 +1,218 @@
1
+ /**
2
+ * @jest-environment jsdom
3
+ * @jest-environment-options {"html": "<html><head><script></script></head><body></body></html>", "url": "https://example.com/"}
4
+ */
5
+
6
+ import { submitData } from './submit-data'
7
+
8
+ const mockWorkerScope = jest.fn().mockImplementation(() => false)
9
+ jest.mock('./global-scope', () => ({
10
+ __esModule: true,
11
+ get isWorkerScope () {
12
+ return mockWorkerScope()
13
+ }
14
+ }))
15
+
16
+ const url = 'https://example.com/api'
17
+
18
+ beforeEach(() => {
19
+ jest.restoreAllMocks()
20
+ mockWorkerScope.mockReturnValue(false)
21
+ })
22
+
23
+ describe('submitData.jsonp', () => {
24
+ // This test requires a script tag to exist in the html set by this file's jest-environment-options header block.
25
+ test('should return an HTMLScriptElement when called from a web window environment', () => {
26
+ mockWorkerScope.mockReturnValue(false)
27
+
28
+ const jsonp = 'callback'
29
+
30
+ const result = submitData.jsonp({ url, jsonp })
31
+
32
+ expect(result).toBeInstanceOf(HTMLScriptElement)
33
+ expect(result.type).toBe('text/javascript')
34
+ expect(result.src).toBe(url + '&jsonp=' + jsonp)
35
+ })
36
+
37
+ test('should try to use importScripts when called from a worker scope', () => {
38
+ mockWorkerScope.mockReturnValueOnce(true)
39
+
40
+ const jsonp = 'callback'
41
+
42
+ global.importScripts = jest.fn()
43
+
44
+ submitData.jsonp({ url, jsonp })
45
+
46
+ expect(importScripts).toHaveBeenCalledWith(url + '&jsonp=' + jsonp)
47
+
48
+ delete global.importScripts
49
+ })
50
+
51
+ test('should fall back to an xhrGet call and return false when called from a worker scope', () => {
52
+ mockWorkerScope.mockReturnValueOnce(true)
53
+
54
+ const jsonp = 'callback'
55
+
56
+ jest.spyOn(submitData, 'xhrGet').mockImplementation(jest.fn())
57
+
58
+ const result = submitData.jsonp({ url, jsonp })
59
+
60
+ expect(result).toBe(false)
61
+ expect(submitData.xhrGet).toHaveBeenCalledTimes(1)
62
+ })
63
+
64
+ test('should not throw an error if any error occurs during execution', () => {
65
+ jest.spyOn(document, 'createElement').mockImplementation(() => { throw new Error('message') })
66
+
67
+ const jsonp = 'callback'
68
+
69
+ expect(() => {
70
+ submitData.jsonp({ url, jsonp })
71
+ }).not.toThrow()
72
+ })
73
+ })
74
+
75
+ describe('submitData.xhrGet', () => {
76
+ test('should return an XMLHttpRequest object', () => {
77
+ const result = submitData.xhrGet({ url })
78
+ expect(result).toBeInstanceOf(XMLHttpRequest)
79
+ })
80
+
81
+ test('should not throw an error if URL is not provided', () => {
82
+ expect(() => {
83
+ submitData.xhrGet({})
84
+ }).not.toThrow()
85
+ })
86
+
87
+ test('should not throw an error if an invalid URL is provided', () => {
88
+ expect(() => {
89
+ submitData.xhrGet({ url: 'invalid url' })
90
+ }).not.toThrow()
91
+ })
92
+ })
93
+
94
+ describe('submitData.xhr', () => {
95
+ test('should return an XMLHttpRequest object', () => {
96
+ const result = submitData.xhrGet({ url })
97
+ expect(result).toBeInstanceOf(XMLHttpRequest)
98
+ })
99
+
100
+ test('should not throw an error if URL is not provided', () => {
101
+ expect(() => {
102
+ submitData.xhr({})
103
+ }).not.toThrow()
104
+ })
105
+
106
+ test('should not throw an error if an invalid URL is provided', () => {
107
+ expect(() => {
108
+ submitData.xhr({ url: 'invalid url' })
109
+ }).not.toThrow()
110
+ })
111
+
112
+ test('should send a POST request by default', () => {
113
+ jest.spyOn(XMLHttpRequest.prototype, 'open')
114
+
115
+ submitData.xhr({ url })
116
+
117
+ expect(XMLHttpRequest.prototype.open).toHaveBeenCalledWith('POST', url, true)
118
+ })
119
+
120
+ test('should send a GET request if specified', () => {
121
+ jest.spyOn(XMLHttpRequest.prototype, 'open')
122
+
123
+ submitData.xhr({ url, method: 'GET' })
124
+
125
+ expect(XMLHttpRequest.prototype.open).toHaveBeenCalledWith('GET', url, true)
126
+ })
127
+
128
+ // This test requires a same-origin url to be set by this file's jest-environment-options header block.
129
+ test('should send a request synchronously if specified', () => {
130
+ jest.spyOn(XMLHttpRequest.prototype, 'open')
131
+
132
+ submitData.xhr({ url, sync: true })
133
+
134
+ expect(XMLHttpRequest.prototype.open).toHaveBeenCalledWith('POST', url, false)
135
+ })
136
+
137
+ test('should set custom headers if provided', () => {
138
+ const headers = [{ key: 'Content-Type', value: 'application/json' }]
139
+
140
+ jest.spyOn(XMLHttpRequest.prototype, 'setRequestHeader')
141
+
142
+ submitData.xhr({ url, headers })
143
+
144
+ headers.forEach((header) => {
145
+ expect(XMLHttpRequest.prototype.setRequestHeader).toHaveBeenCalledWith(header.key, header.value)
146
+ })
147
+ })
148
+
149
+ test('should send a request with the specified body', () => {
150
+ const body = JSON.stringify({ key: 'value' })
151
+
152
+ jest.spyOn(XMLHttpRequest.prototype, 'send')
153
+
154
+ submitData.xhr({ url, body })
155
+
156
+ expect(XMLHttpRequest.prototype.send).toHaveBeenCalledWith(body)
157
+ })
158
+ })
159
+
160
+ describe('submitData.img', () => {
161
+ test('should return an HTMLImageElement', () => {
162
+ const imageUrl = 'https://example.com/image.png'
163
+
164
+ const result = submitData.img({ url: imageUrl })
165
+
166
+ expect(result).toBeInstanceOf(HTMLImageElement)
167
+ })
168
+
169
+ test('should not throw an error if URL is not provided', () => {
170
+ expect(() => {
171
+ submitData.img({})
172
+ }).not.toThrow()
173
+ })
174
+
175
+ test('should set the src attribute of the image element to the provided URL', () => {
176
+ const imageUrl = 'https://example.com/image.png'
177
+
178
+ const result = submitData.img({ url: imageUrl })
179
+
180
+ expect(result.src).toBe(imageUrl)
181
+ })
182
+ })
183
+
184
+ describe('submitData.beacon', () => {
185
+ test('should return true when beacon request succeeds', () => {
186
+ const body = JSON.stringify({ key: 'value' })
187
+
188
+ window.navigator.sendBeacon = {
189
+ bind: jest.fn(() => () => true)
190
+ }
191
+
192
+ const result = submitData.beacon({ url, body })
193
+
194
+ expect(result).toBe(true)
195
+ })
196
+
197
+ test('should return false when beacon request fails', () => {
198
+ const body = JSON.stringify({ key: 'value' })
199
+
200
+ window.navigator.sendBeacon = {
201
+ bind: jest.fn(() => () => { throw new Error('message') })
202
+ }
203
+
204
+ const result = submitData.beacon({ url, body })
205
+
206
+ expect(result).toBe(false)
207
+ })
208
+
209
+ test('should error if sendBeacon is not supported', () => {
210
+ const body = JSON.stringify({ key: 'value' })
211
+
212
+ window.navigator.sendBeacon = undefined
213
+
214
+ expect(() => {
215
+ submitData.beacon({ url, body })
216
+ }).toThrow()
217
+ })
218
+ })
@@ -3,34 +3,25 @@
3
3
  * SPDX-License-Identifier: Apache-2.0
4
4
  */
5
5
 
6
- // traverses an object and applies a fn to property values of a certain type
7
- export function applyFnToProps (obj, fn, type, ignoreKeys) {
6
+ /**
7
+ * Applies a function to properties of a specified type in an object, recursively.
8
+ *
9
+ * @param {Object} obj - The object to apply the function to.
10
+ * @param {Function} fn - The function to apply to matching properties.
11
+ * @param {string} [type='string'] - The type of properties to apply the function to.
12
+ * @param {Array<string>} [ignoreKeys=[]] - The keys of properties to ignore and not modify.
13
+ * @returns {Object} - The object with function recursively applied.
14
+ */
15
+ export function applyFnToProps (obj, fn, type = 'string', ignoreKeys = []) {
8
16
  if (!obj || typeof obj !== 'object') return obj
9
- type = type || 'string'
10
- ignoreKeys = ignoreKeys || []
11
- return traverse(obj)
12
- function traverse (obj) {
13
- for (var property in obj) {
14
- // eslint-disable-next-line
15
- if (obj.hasOwnProperty(property)) {
16
- if (typeof obj[property] === 'object') {
17
- traverse(obj[property])
18
- } else {
19
- if (typeof obj[property] === type && !shouldIgnore(property)) obj[property] = fn(obj[property])
20
- }
21
- }
22
- }
23
- return obj
24
- }
25
17
 
26
- function shouldIgnore (key) {
27
- var ignore = false
28
- for (var i = 0; i < ignoreKeys.length; i++) {
29
- if (ignoreKeys[i] === key) {
30
- ignore = true
31
- break
32
- }
18
+ Object.keys(obj).forEach(property => {
19
+ if (typeof obj[property] === 'object') {
20
+ applyFnToProps(obj[property], fn, type, ignoreKeys)
21
+ } else {
22
+ if (typeof obj[property] === type && !ignoreKeys.includes(property)) obj[property] = fn(obj[property])
33
23
  }
34
- return ignore
35
- }
24
+ })
25
+
26
+ return obj
36
27
  }
@@ -0,0 +1,50 @@
1
+ import { applyFnToProps } from './traverse'
2
+
3
+ test.each(['not an object', null, undefined])('should return input unchanged when input is %p', (input) => {
4
+ const result = applyFnToProps(input, jest.fn())
5
+ expect(result).toEqual(input)
6
+ })
7
+
8
+ test('should apply the provided function only to properties of the specified type', () => {
9
+ const obj = {
10
+ stringProp: 'Hello',
11
+ numberProp: 42,
12
+ arrayProp: [1, 2, 3],
13
+ objectProp: { foo: 'bar' }
14
+ }
15
+
16
+ const fn = jest.fn((value) => value.toUpperCase())
17
+
18
+ const result = applyFnToProps(obj, fn, 'string')
19
+
20
+ expect(result.stringProp).toBe('HELLO')
21
+ expect(result.numberProp).toBe(42)
22
+ expect(result.arrayProp).toEqual([1, 2, 3])
23
+ expect(result.objectProp).toEqual({ foo: 'BAR' })
24
+
25
+ expect(fn).toHaveBeenCalledTimes(2)
26
+ expect(fn).toHaveBeenCalledWith('Hello')
27
+ expect(fn).toHaveBeenCalledWith('bar')
28
+ })
29
+
30
+ test('should ignore properties specified in ignoreKeys', () => {
31
+ const obj = {
32
+ a: 1,
33
+ b: 2,
34
+ c: 3
35
+ }
36
+
37
+ const fn = jest.fn((value) => value + 1)
38
+
39
+ const ignoreKeys = ['c']
40
+
41
+ const result = applyFnToProps(obj, fn, 'number', ignoreKeys)
42
+
43
+ expect(result.a).toBe(2)
44
+ expect(result.b).toBe(3)
45
+ expect(result.c).toBe(3)
46
+
47
+ expect(fn).toHaveBeenCalledTimes(2)
48
+ expect(fn).toHaveBeenCalledWith(1)
49
+ expect(fn).toHaveBeenCalledWith(2)
50
+ })
@@ -1,4 +1,3 @@
1
-
2
1
  import { now } from '../timing/now'
3
2
  import { globalScope } from '../util/global-scope'
4
3
 
@@ -0,0 +1,7 @@
1
+ export const endpointMap = {
2
+ rum: '1',
3
+ events: 'Events',
4
+ ins: 'Ins',
5
+ jserrors: 'Jserrors',
6
+ resources: 'Resources'
7
+ }
@@ -13,6 +13,7 @@ import { windowAddEventListener } from '../../../common/event-listener/event-lis
13
13
  import { isBrowserScope } from '../../../common/util/global-scope'
14
14
  import { AggregateBase } from '../../utils/aggregate-base'
15
15
  import { stringify } from '../../../common/util/stringify'
16
+ import { endpointMap } from './endpoint-map'
16
17
 
17
18
  export class Aggregate extends AggregateBase {
18
19
  static featureName = FEATURE_NAME
@@ -125,7 +126,7 @@ export class Aggregate extends AggregateBase {
125
126
  // Capture per-agent bytes sent for each endpoint (see harvest) and RUM call (see page_view_event aggregator).
126
127
  Object.keys(agentRuntime.bytesSent).forEach(endpoint => {
127
128
  this.storeSupportabilityMetrics(
128
- `PageSession/Endpoint/${endpoint.charAt(0).toUpperCase() + endpoint.slice(1)}/BytesSent`,
129
+ `PageSession/Endpoint/${endpointMap[endpoint]}/BytesSent`,
129
130
  agentRuntime.bytesSent[endpoint]
130
131
  )
131
132
  })
@@ -133,7 +134,7 @@ export class Aggregate extends AggregateBase {
133
134
  // Capture per-agent query bytes sent for each endpoint (see harvest) and RUM call (see page_view_event aggregator).
134
135
  Object.keys(agentRuntime.bytesSent).forEach(endpoint => {
135
136
  this.storeSupportabilityMetrics(
136
- `PageSession/Endpoint/${endpoint.charAt(0).toUpperCase() + endpoint.slice(1)}/QueryBytesSent`,
137
+ `PageSession/Endpoint/${endpointMap[endpoint]}/QueryBytesSent`,
137
138
  agentRuntime.queryBytesSent[endpoint]
138
139
  )
139
140
  })
@@ -1,4 +1,3 @@
1
-
2
1
  /**
3
2
  * This file is used as a replacement for the polyfill-detection.es5.js
4
3
  * file when running the polyfills webpack build.
@@ -1,5 +1,4 @@
1
1
  import { InstrumentBase } from '../../utils/instrument-base'
2
- import { insertSupportMetrics } from './workers-helper'
3
2
  import { FEATURE_NAME, SUPPORTABILITY_METRIC_CHANNEL } from '../constants'
4
3
  import { handle } from '../../../common/event-emitter/handle'
5
4
  import { FEATURE_NAMES } from '../../../loaders/features/features'
@@ -8,7 +7,6 @@ export class Instrument extends InstrumentBase {
8
7
  static featureName = FEATURE_NAME
9
8
  constructor (agentIdentifier, aggregator, auto = true) {
10
9
  super(agentIdentifier, aggregator, FEATURE_NAME, auto)
11
- insertSupportMetrics(tag => handle(SUPPORTABILITY_METRIC_CHANNEL, [tag], undefined, FEATURE_NAMES.metrics, this.ee))
12
10
  this.importAggregator()
13
11
  }
14
12
  }
@@ -2,22 +2,19 @@ import { handle } from '../../../common/event-emitter/handle'
2
2
  import { FEATURE_NAMES } from '../../../loaders/features/features'
3
3
  import { isiOS } from '../../../common/browser-version/ios-version'
4
4
  import { onTTFB } from 'web-vitals'
5
- import { mapOwn } from '../../../common/util/map-own'
6
- import { param, fromArray } from '../../../common/url/encode'
7
5
  import { addPT, addPN } from '../../../common/timing/nav-timing'
8
6
  import { stringify } from '../../../common/util/stringify'
9
7
  import { paintMetrics } from '../../../common/metrics/paint-metrics'
10
- import { submitData } from '../../../common/util/submit-data'
11
8
  import { getConfigurationValue, getInfo, getRuntime } from '../../../common/config/config'
12
- import { HarvestScheduler } from '../../../common/harvest/harvest-scheduler'
9
+ import { Harvest } from '../../../common/harvest/harvest'
13
10
  import * as CONSTANTS from '../constants'
14
11
  import { getActivatedFeaturesFlags } from './initialized-features'
15
12
  import { globalScope, isBrowserScope } from '../../../common/util/global-scope'
16
13
  import { drain } from '../../../common/drain/drain'
14
+ import { activateFeatures } from '../../../common/util/feature-flags'
15
+ import { warn } from '../../../common/util/console'
17
16
  import { AggregateBase } from '../../utils/aggregate-base'
18
17
 
19
- const jsonp = 'NREUM.setToken'
20
-
21
18
  export class Aggregate extends AggregateBase {
22
19
  static featureName = CONSTANTS.FEATURE_NAME
23
20
  constructor (agentIdentifier, aggregator) {
@@ -55,10 +52,12 @@ export class Aggregate extends AggregateBase {
55
52
 
56
53
  sendRum () {
57
54
  const info = getInfo(this.agentIdentifier)
55
+ const agentRuntime = getRuntime(this.agentIdentifier)
56
+ const harvester = new Harvest(this)
57
+
58
58
  if (!info.beacon) return
59
59
  if (info.queueTime) this.aggregator.store('measures', 'qt', { value: info.queueTime })
60
60
  if (info.applicationTime) this.aggregator.store('measures', 'ap', { value: info.applicationTime })
61
- const agentRuntime = getRuntime(this.agentIdentifier)
62
61
 
63
62
  // These 3 values should've been recorded after load and before this func runs. They are part of the minimum required for PageView events to be created.
64
63
  // Following PR #428, which demands that all agents send RUM call, these need to be sent even outside of the main window context where PerformanceTiming
@@ -67,27 +66,27 @@ export class Aggregate extends AggregateBase {
67
66
  this.aggregator.store('measures', 'fe', { value: isBrowserScope ? agentRuntime[CONSTANTS.FBTWL] : 0 })
68
67
  this.aggregator.store('measures', 'dc', { value: isBrowserScope ? agentRuntime[CONSTANTS.FBTDC] : 0 })
69
68
 
70
- var measuresMetrics = this.aggregator.get('measures')
71
-
72
- var measuresQueryString = mapOwn(measuresMetrics, function (metricName, measure) {
73
- return '&' + metricName + '=' + measure.params.value
74
- }).join('')
75
-
76
- // currently we only have one version of our protocol
77
- // in the future we may add more
78
- var protocol = '1'
79
-
80
- var scheduler = new HarvestScheduler('page_view_event', {}, this)
81
-
82
- var chunksForQueryString = [scheduler.harvest.baseQueryString()]
83
-
84
- chunksForQueryString.push(measuresQueryString)
69
+ const queryParameters = {
70
+ tt: info.ttGuid,
71
+ us: info.user,
72
+ ac: info.account,
73
+ pr: info.product,
74
+ af: getActivatedFeaturesFlags(this.agentIdentifier).join(','),
75
+ ...(
76
+ Object.entries(this.aggregator.get('measures') || {}).reduce((aggregator, [metricName, measure]) => {
77
+ aggregator[metricName] = measure.params?.value
78
+ return aggregator
79
+ }, {})
80
+ ),
81
+ xx: info.extra,
82
+ ua: info.userAttributes,
83
+ at: info.atts
84
+ }
85
85
 
86
- chunksForQueryString.push(param('tt', info.ttGuid))
87
- chunksForQueryString.push(param('us', info.user))
88
- chunksForQueryString.push(param('ac', info.account))
89
- chunksForQueryString.push(param('pr', info.product))
90
- chunksForQueryString.push(param('af', getActivatedFeaturesFlags(this.agentIdentifier).join(',')))
86
+ let body
87
+ if (typeof info.jsAttributes === 'object' && Object.keys(info.jsAttributes).length > 0) {
88
+ body = { ja: info.jsAttributes }
89
+ }
91
90
 
92
91
  if (globalScope.performance) {
93
92
  if (typeof PerformanceNavigationTiming !== 'undefined') { // Navigation Timing level 2 API that replaced PerformanceTiming & PerformanceNavigation
@@ -96,13 +95,13 @@ export class Aggregate extends AggregateBase {
96
95
  timing: addPT(agentRuntime.offset, navTimingEntry, {}),
97
96
  navigation: addPN(navTimingEntry, {})
98
97
  })
99
- chunksForQueryString.push(param('perf', stringify(perf)))
98
+ queryParameters.perf = stringify(perf)
100
99
  } else if (typeof PerformanceTiming !== 'undefined') { // Safari pre-15 did not support level 2 timing
101
100
  const perf = ({
102
101
  timing: addPT(agentRuntime.offset, globalScope.performance.timing, {}, true),
103
102
  navigation: addPN(globalScope.performance.navigation, {})
104
103
  })
105
- chunksForQueryString.push(param('perf', stringify(perf)))
104
+ queryParameters.perf = stringify(perf)
106
105
  }
107
106
  }
108
107
 
@@ -112,35 +111,33 @@ export class Aggregate extends AggregateBase {
112
111
  if (!entry.startTime || entry.startTime <= 0) return
113
112
 
114
113
  if (entry.name === 'first-paint') {
115
- chunksForQueryString.push(param('fp', String(Math.floor(entry.startTime))))
114
+ queryParameters.fp = String(Math.floor(entry.startTime))
116
115
  } else if (entry.name === 'first-contentful-paint') {
117
- chunksForQueryString.push(param('fcp', String(Math.floor(entry.startTime))))
116
+ queryParameters.fcp = String(Math.floor(entry.startTime))
118
117
  }
119
118
  paintMetrics[entry.name] = Math.floor(entry.startTime) // this is consumed by Spa module
120
119
  })
121
120
  } catch (e) {}
122
121
 
123
- chunksForQueryString.push(param('xx', info.extra))
124
- chunksForQueryString.push(param('ua', info.userAttributes))
125
- chunksForQueryString.push(param('at', info.atts))
126
-
127
- var customJsAttributes = stringify(info.jsAttributes)
128
- chunksForQueryString.push(param('ja', customJsAttributes === '{}' ? null : customJsAttributes))
129
-
130
- var queryString = fromArray(chunksForQueryString, agentRuntime.maxBytes)
131
-
132
- // Capture bytes sent to RUM call endpoint (currently `1`) as a supportability metric. See metrics aggregator (on unload).
133
- agentRuntime.bytesSent[protocol] = 0 // Set to zero for now until RUM is moved to POST
134
-
135
- // Capture query bytes sent to RUM call endpoint (currently `1`) as a supportability metric. See metrics aggregator (on unload).
136
- agentRuntime.queryBytesSent[protocol] = (agentRuntime.queryBytesSent[protocol] || 0) + queryString?.length || 0
122
+ harvester.send({
123
+ endpoint: 'rum',
124
+ payload: { qs: queryParameters, body },
125
+ opts: { needResponse: true, sendEmptyBody: true },
126
+ cbFinished: ({ status, responseText }) => {
127
+ if (status >= 400) {
128
+ // Adding retry logic for the rum call will be a separate change
129
+ this.ee.abort()
130
+ return
131
+ }
137
132
 
138
- const isValidJsonp = submitData.jsonp({
139
- url: this.getScheme() + '://' + info.beacon + '/' + protocol + '/' + info.licenseKey + '?' + queryString,
140
- jsonp
133
+ try {
134
+ activateFeatures(JSON.parse(responseText), this.agentIdentifier)
135
+ drain(this.agentIdentifier, this.featureName)
136
+ } catch (err) {
137
+ this.ee.abort()
138
+ warn('RUM call failed. Agent shutting down.')
139
+ }
140
+ }
141
141
  })
142
- // Usually `drain` is invoked automatically after processing feature flags contained in the JSONP callback from
143
- // ingest (see `activateFeatures`), so when JSONP cannot execute (as with module workers), we drain manually.
144
- if (!isValidJsonp) drain(this.agentIdentifier, CONSTANTS.FEATURE_NAME)
145
142
  }
146
143
  }
@@ -1,4 +1,3 @@
1
-
2
1
  import { handle } from '../../../common/event-emitter/handle'
3
2
  import { isiOS } from '../../../common/browser-version/ios-version'
4
3
  import { InstrumentBase } from '../../utils/instrument-base'
@@ -1,4 +1,3 @@
1
-
2
1
  /**
3
2
  * A class to defer callback execution until a decision can be reached
4
3
  */
@@ -29,7 +29,6 @@ export function configure (agentIdentifier, opts = {}, loaderType, forceDrain) {
29
29
  gosNREUMInitializedAgents(agentIdentifier, api, 'api')
30
30
  gosNREUMInitializedAgents(agentIdentifier, exposed, 'exposed')
31
31
  addToNREUM('activatedFeatures', activatedFeatures)
32
- addToNREUM('setToken', (flags) => activateFeatures(flags, agentIdentifier))
33
32
 
34
33
  return api
35
34
  }
@@ -1,19 +0,0 @@
1
- "use strict";
2
-
3
- Object.defineProperty(exports, "__esModule", {
4
- value: true
5
- });
6
- exports.sHash = sHash;
7
- /*
8
- * Copyright 2020 New Relic Corporation. All rights reserved.
9
- * SPDX-License-Identifier: Apache-2.0
10
- */
11
-
12
- function sHash(s) {
13
- var i;
14
- var h = 0;
15
- for (i = 0; i < s.length; i++) {
16
- h += (i + 1) * s.charCodeAt(i);
17
- }
18
- return Math.abs(h);
19
- }