@newrelic/browser-agent 1.232.1 → 1.233.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 (233) hide show
  1. package/dist/cjs/cdn/polyfills.js +5 -2
  2. package/dist/cjs/common/config/state/configurable.js +15 -26
  3. package/dist/cjs/common/config/state/info.js +1 -1
  4. package/dist/cjs/common/config/state/init.js +101 -56
  5. package/dist/cjs/common/config/state/loader-config.js +1 -1
  6. package/dist/cjs/common/config/state/runtime.js +1 -5
  7. package/dist/cjs/common/constants/env.cdn.js +1 -1
  8. package/dist/cjs/common/constants/env.npm.js +1 -1
  9. package/dist/cjs/common/drain/drain.js +1 -1
  10. package/dist/cjs/common/harvest/harvest-scheduler.js +30 -10
  11. package/dist/cjs/common/harvest/harvest.js +119 -55
  12. package/dist/cjs/common/session/session-entity.js +35 -22
  13. package/dist/cjs/common/session/session-entity.test.js +73 -49
  14. package/dist/cjs/common/timer/interaction-timer.js +9 -12
  15. package/dist/cjs/common/url/protocol.test.js +0 -1
  16. package/dist/cjs/common/util/feature-flags.js +2 -1
  17. package/dist/cjs/common/util/submit-data.js +57 -18
  18. package/dist/cjs/common/wrap/wrap-fetch.js +1 -1
  19. package/dist/cjs/common/wrap/wrap-function.js +1 -1
  20. package/dist/cjs/common/wrap/wrap-promise.js +1 -1
  21. package/dist/cjs/features/ajax/aggregate/index.js +2 -2
  22. package/dist/cjs/features/jserrors/aggregate/index.js +7 -5
  23. package/dist/cjs/features/metrics/aggregate/framework-detection.js +67 -0
  24. package/dist/cjs/features/metrics/aggregate/framework-detection.test.js +137 -0
  25. package/dist/cjs/features/metrics/aggregate/index.js +7 -3
  26. package/dist/cjs/features/metrics/aggregate/polyfill-detection.es5.js +14 -0
  27. package/dist/cjs/features/metrics/aggregate/polyfill-detection.es5.test.js +17 -0
  28. package/dist/cjs/features/metrics/aggregate/polyfill-detection.js +53 -0
  29. package/dist/cjs/features/metrics/aggregate/polyfill-detection.test.js +165 -0
  30. package/dist/cjs/features/page_action/aggregate/index.js +2 -2
  31. package/dist/cjs/features/page_view_event/aggregate/index.js +6 -3
  32. package/dist/cjs/features/page_view_timing/aggregate/index.js +2 -2
  33. package/dist/cjs/features/session_replay/aggregate/index.js +333 -0
  34. package/dist/cjs/features/session_replay/constants.js +9 -0
  35. package/dist/cjs/features/session_replay/index.js +12 -0
  36. package/dist/cjs/features/session_replay/instrument/index.js +29 -0
  37. package/dist/cjs/features/session_trace/aggregate/index.js +163 -162
  38. package/dist/cjs/features/session_trace/constants.js +2 -9
  39. package/dist/cjs/features/session_trace/instrument/index.js +24 -66
  40. package/dist/cjs/features/spa/aggregate/index.js +2 -2
  41. package/dist/cjs/features/utils/agent-session.js +1 -2
  42. package/dist/cjs/features/utils/aggregate-base.js +64 -0
  43. package/dist/cjs/features/utils/feature-base.js +0 -31
  44. package/dist/cjs/features/utils/handler-cache.js +3 -4
  45. package/dist/cjs/features/utils/instrument-base.js +42 -10
  46. package/dist/cjs/features/utils/{lazy-loader.js → lazy-feature-loader.js} +4 -2
  47. package/dist/cjs/loaders/agent.js +1 -1
  48. package/dist/cjs/loaders/api/apiAsync.js +3 -1
  49. package/dist/cjs/loaders/configure/configure.js +3 -3
  50. package/dist/cjs/loaders/features/featureDependencies.js +0 -12
  51. package/dist/cjs/loaders/features/features.js +3 -1
  52. package/dist/cjs/loaders/micro-agent.js +6 -6
  53. package/dist/esm/cdn/polyfills.js +5 -2
  54. package/dist/esm/common/config/state/configurable.js +14 -24
  55. package/dist/esm/common/config/state/info.js +2 -2
  56. package/dist/esm/common/config/state/init.js +102 -57
  57. package/dist/esm/common/config/state/loader-config.js +2 -2
  58. package/dist/esm/common/config/state/runtime.js +2 -4
  59. package/dist/esm/common/constants/env.cdn.js +1 -1
  60. package/dist/esm/common/constants/env.npm.js +1 -1
  61. package/dist/esm/common/drain/drain.js +1 -1
  62. package/dist/esm/common/harvest/harvest-scheduler.js +30 -10
  63. package/dist/esm/common/harvest/harvest.js +121 -56
  64. package/dist/esm/common/session/session-entity.js +35 -22
  65. package/dist/esm/common/session/session-entity.test.js +73 -49
  66. package/dist/esm/common/timer/interaction-timer.js +9 -12
  67. package/dist/esm/common/url/protocol.test.js +0 -1
  68. package/dist/esm/common/util/feature-flags.js +2 -1
  69. package/dist/esm/common/util/submit-data.js +57 -18
  70. package/dist/esm/common/wrap/wrap-fetch.js +1 -1
  71. package/dist/esm/common/wrap/wrap-function.js +1 -1
  72. package/dist/esm/common/wrap/wrap-promise.js +1 -1
  73. package/dist/esm/features/ajax/aggregate/index.js +2 -2
  74. package/dist/esm/features/jserrors/aggregate/index.js +7 -5
  75. package/dist/esm/features/metrics/aggregate/framework-detection.js +61 -0
  76. package/dist/esm/features/metrics/aggregate/framework-detection.test.js +133 -0
  77. package/dist/esm/features/metrics/aggregate/index.js +7 -3
  78. package/dist/esm/features/metrics/aggregate/polyfill-detection.es5.js +8 -0
  79. package/dist/esm/features/metrics/aggregate/polyfill-detection.es5.test.js +15 -0
  80. package/dist/esm/features/metrics/aggregate/polyfill-detection.js +47 -0
  81. package/dist/esm/features/metrics/aggregate/polyfill-detection.test.js +163 -0
  82. package/dist/esm/features/page_action/aggregate/index.js +2 -2
  83. package/dist/esm/features/page_view_event/aggregate/index.js +6 -3
  84. package/dist/esm/features/page_view_timing/aggregate/index.js +2 -2
  85. package/dist/esm/features/session_replay/aggregate/index.js +327 -0
  86. package/dist/esm/features/session_replay/constants.js +2 -0
  87. package/dist/esm/features/session_replay/index.js +12 -0
  88. package/dist/esm/features/session_replay/instrument/index.js +21 -0
  89. package/dist/esm/features/session_trace/aggregate/index.js +163 -162
  90. package/dist/esm/features/session_trace/constants.js +1 -5
  91. package/dist/esm/features/session_trace/instrument/index.js +24 -66
  92. package/dist/esm/features/spa/aggregate/index.js +2 -2
  93. package/dist/esm/features/utils/agent-session.js +1 -2
  94. package/dist/esm/features/utils/aggregate-base.js +57 -0
  95. package/dist/esm/features/utils/feature-base.js +1 -32
  96. package/dist/esm/features/utils/handler-cache.js +3 -4
  97. package/dist/esm/features/utils/instrument-base.js +42 -10
  98. package/dist/esm/features/utils/{lazy-loader.js → lazy-feature-loader.js} +3 -1
  99. package/dist/esm/loaders/agent.js +1 -1
  100. package/dist/esm/loaders/api/apiAsync.js +3 -1
  101. package/dist/esm/loaders/configure/configure.js +3 -3
  102. package/dist/esm/loaders/features/featureDependencies.js +0 -11
  103. package/dist/esm/loaders/features/features.js +3 -1
  104. package/dist/esm/loaders/micro-agent.js +6 -6
  105. package/dist/types/common/config/state/configurable.d.ts +1 -3
  106. package/dist/types/common/config/state/configurable.d.ts.map +1 -1
  107. package/dist/types/common/config/state/init.d.ts.map +1 -1
  108. package/dist/types/common/config/state/runtime.d.ts.map +1 -1
  109. package/dist/types/common/harvest/harvest-scheduler.d.ts.map +1 -1
  110. package/dist/types/common/harvest/harvest.d.ts +37 -34
  111. package/dist/types/common/harvest/harvest.d.ts.map +1 -1
  112. package/dist/types/common/session/session-entity.d.ts +6 -3
  113. package/dist/types/common/session/session-entity.d.ts.map +1 -1
  114. package/dist/types/common/timer/interaction-timer.d.ts +2 -1
  115. package/dist/types/common/timer/interaction-timer.d.ts.map +1 -1
  116. package/dist/types/common/util/feature-flags.d.ts.map +1 -1
  117. package/dist/types/common/util/submit-data.d.ts +40 -14
  118. package/dist/types/common/util/submit-data.d.ts.map +1 -1
  119. package/dist/types/features/ajax/aggregate/index.d.ts +2 -2
  120. package/dist/types/features/ajax/aggregate/index.d.ts.map +1 -1
  121. package/dist/types/features/jserrors/aggregate/index.d.ts +2 -2
  122. package/dist/types/features/jserrors/aggregate/index.d.ts.map +1 -1
  123. package/dist/types/features/metrics/aggregate/framework-detection.d.ts.map +1 -0
  124. package/dist/types/features/metrics/aggregate/index.d.ts +2 -2
  125. package/dist/types/features/metrics/aggregate/index.d.ts.map +1 -1
  126. package/dist/types/features/metrics/aggregate/polyfill-detection.d.ts +6 -0
  127. package/dist/types/features/metrics/aggregate/polyfill-detection.d.ts.map +1 -0
  128. package/dist/types/features/metrics/aggregate/polyfill-detection.es5.d.ts +7 -0
  129. package/dist/types/features/metrics/aggregate/polyfill-detection.es5.d.ts.map +1 -0
  130. package/dist/types/features/page_action/aggregate/index.d.ts +2 -2
  131. package/dist/types/features/page_action/aggregate/index.d.ts.map +1 -1
  132. package/dist/types/features/page_view_event/aggregate/index.d.ts +2 -2
  133. package/dist/types/features/page_view_event/aggregate/index.d.ts.map +1 -1
  134. package/dist/types/features/page_view_timing/aggregate/index.d.ts +2 -2
  135. package/dist/types/features/page_view_timing/aggregate/index.d.ts.map +1 -1
  136. package/dist/types/features/session_replay/aggregate/index.d.ts +96 -0
  137. package/dist/types/features/session_replay/aggregate/index.d.ts.map +1 -0
  138. package/dist/types/features/session_replay/constants.d.ts +2 -0
  139. package/dist/types/features/session_replay/constants.d.ts.map +1 -0
  140. package/dist/types/features/session_replay/index.d.ts +2 -0
  141. package/dist/types/features/session_replay/index.d.ts.map +1 -0
  142. package/dist/types/features/session_replay/instrument/index.d.ts +6 -0
  143. package/dist/types/features/session_replay/instrument/index.d.ts.map +1 -0
  144. package/dist/types/features/session_trace/aggregate/index.d.ts +8 -57
  145. package/dist/types/features/session_trace/aggregate/index.d.ts.map +1 -1
  146. package/dist/types/features/session_trace/constants.d.ts +0 -3
  147. package/dist/types/features/session_trace/constants.d.ts.map +1 -1
  148. package/dist/types/features/session_trace/instrument/index.d.ts +1 -3
  149. package/dist/types/features/session_trace/instrument/index.d.ts.map +1 -1
  150. package/dist/types/features/spa/aggregate/index.d.ts +2 -2
  151. package/dist/types/features/spa/aggregate/index.d.ts.map +1 -1
  152. package/dist/types/features/utils/agent-session.d.ts.map +1 -1
  153. package/dist/types/features/utils/aggregate-base.d.ts +11 -0
  154. package/dist/types/features/utils/aggregate-base.d.ts.map +1 -0
  155. package/dist/types/features/utils/feature-base.d.ts +0 -5
  156. package/dist/types/features/utils/feature-base.d.ts.map +1 -1
  157. package/dist/types/features/utils/handler-cache.d.ts.map +1 -1
  158. package/dist/types/features/utils/instrument-base.d.ts +3 -1
  159. package/dist/types/features/utils/instrument-base.d.ts.map +1 -1
  160. package/dist/types/features/utils/{lazy-loader.d.ts → lazy-feature-loader.d.ts} +2 -2
  161. package/dist/types/features/utils/lazy-feature-loader.d.ts.map +1 -0
  162. package/dist/types/loaders/configure/configure.d.ts.map +1 -1
  163. package/dist/types/loaders/features/featureDependencies.d.ts +0 -1
  164. package/dist/types/loaders/features/featureDependencies.d.ts.map +1 -1
  165. package/dist/types/loaders/features/features.d.ts +1 -0
  166. package/dist/types/loaders/features/features.d.ts.map +1 -1
  167. package/package.json +28 -19
  168. package/src/cdn/polyfills.js +4 -1
  169. package/src/common/config/state/configurable.js +18 -24
  170. package/src/common/config/state/info.js +2 -2
  171. package/src/common/config/state/init.js +62 -28
  172. package/src/common/config/state/loader-config.js +2 -2
  173. package/src/common/config/state/runtime.js +2 -4
  174. package/src/common/drain/drain.js +1 -1
  175. package/src/common/harvest/harvest-scheduler.js +35 -10
  176. package/src/common/harvest/harvest.js +73 -50
  177. package/src/common/session/session-entity.js +34 -23
  178. package/src/common/session/session-entity.test.js +57 -51
  179. package/src/common/timer/interaction-timer.js +9 -12
  180. package/src/common/url/protocol.test.js +0 -1
  181. package/src/common/util/feature-flags.js +2 -2
  182. package/src/common/util/submit-data.js +28 -17
  183. package/src/common/wrap/wrap-fetch.js +1 -1
  184. package/src/common/wrap/wrap-function.js +1 -1
  185. package/src/common/wrap/wrap-promise.js +1 -1
  186. package/src/features/ajax/aggregate/index.js +2 -2
  187. package/src/features/jserrors/aggregate/index.js +7 -5
  188. package/src/features/metrics/aggregate/framework-detection.js +73 -0
  189. package/src/features/metrics/aggregate/framework-detection.test.js +201 -0
  190. package/src/features/metrics/aggregate/index.js +8 -3
  191. package/src/features/metrics/aggregate/polyfill-detection.es5.js +9 -0
  192. package/src/features/metrics/aggregate/polyfill-detection.es5.test.js +16 -0
  193. package/src/features/metrics/aggregate/polyfill-detection.js +48 -0
  194. package/src/features/metrics/aggregate/polyfill-detection.test.js +163 -0
  195. package/src/features/page_action/aggregate/index.js +2 -2
  196. package/src/features/page_view_event/aggregate/index.js +5 -5
  197. package/src/features/page_view_timing/aggregate/index.js +2 -2
  198. package/src/features/session_replay/aggregate/index.js +314 -0
  199. package/src/features/session_replay/constants.js +3 -0
  200. package/src/features/session_replay/index.js +12 -0
  201. package/src/features/session_replay/instrument/index.js +22 -0
  202. package/src/features/session_trace/aggregate/index.js +148 -187
  203. package/src/features/session_trace/constants.js +0 -4
  204. package/src/features/session_trace/instrument/index.js +17 -69
  205. package/src/features/spa/aggregate/index.js +2 -2
  206. package/src/features/utils/agent-session.js +1 -2
  207. package/src/features/utils/aggregate-base.js +51 -0
  208. package/src/features/utils/feature-base.js +1 -31
  209. package/src/features/utils/handler-cache.js +3 -4
  210. package/src/features/utils/instrument-base.js +40 -8
  211. package/src/features/utils/{lazy-loader.js → lazy-feature-loader.js} +3 -1
  212. package/src/loaders/agent.js +1 -1
  213. package/src/loaders/api/apiAsync.js +1 -1
  214. package/src/loaders/configure/configure.js +4 -3
  215. package/src/loaders/features/featureDependencies.js +0 -12
  216. package/src/loaders/features/features.js +3 -1
  217. package/src/loaders/micro-agent.js +4 -4
  218. package/dist/cjs/common/metrics/framework-detection.js +0 -72
  219. package/dist/cjs/common/util/user-agent.js +0 -57
  220. package/dist/cjs/common/window/supports-performance-observer.js +0 -15
  221. package/dist/esm/common/metrics/framework-detection.js +0 -66
  222. package/dist/esm/common/util/user-agent.js +0 -48
  223. package/dist/esm/common/window/supports-performance-observer.js +0 -9
  224. package/dist/types/common/metrics/framework-detection.d.ts.map +0 -1
  225. package/dist/types/common/util/user-agent.d.ts +0 -5
  226. package/dist/types/common/util/user-agent.d.ts.map +0 -1
  227. package/dist/types/common/window/supports-performance-observer.d.ts +0 -2
  228. package/dist/types/common/window/supports-performance-observer.d.ts.map +0 -1
  229. package/dist/types/features/utils/lazy-loader.d.ts.map +0 -1
  230. package/src/common/metrics/framework-detection.js +0 -71
  231. package/src/common/util/user-agent.js +0 -56
  232. package/src/common/window/supports-performance-observer.js +0 -10
  233. /package/dist/types/{common/metrics → features/metrics/aggregate}/framework-detection.d.ts +0 -0
@@ -20,13 +20,13 @@ import { globalScope } from '../../../common/util/global-scope'
20
20
  import { FEATURE_NAME } from '../constants'
21
21
  import { drain } from '../../../common/drain/drain'
22
22
  import { FEATURE_NAMES } from '../../../loaders/features/features'
23
- import { FeatureBase } from '../../utils/feature-base'
23
+ import { AggregateBase } from '../../utils/aggregate-base'
24
24
 
25
25
  /**
26
26
  * @typedef {import('./compute-stack-trace.js').StackInfo} StackInfo
27
27
  */
28
28
 
29
- export class Aggregate extends FeatureBase {
29
+ export class Aggregate extends AggregateBase {
30
30
  static featureName = FEATURE_NAME
31
31
  constructor (agentIdentifier, aggregator) {
32
32
  super(agentIdentifier, aggregator, FEATURE_NAME)
@@ -189,10 +189,12 @@ export class Aggregate extends FeatureBase {
189
189
  var type = internal ? 'ierr' : 'err'
190
190
  var newMetrics = { time: time }
191
191
 
192
- // stn and spa aggregators listen to this event - stn sends the error in its payload,
192
+ // sr, stn and spa aggregators listen to this event - stn sends the error in its payload,
193
193
  // and spa annotates the error with interaction info
194
- handle('errorAgg', [type, bucketHash, params, newMetrics], undefined, FEATURE_NAMES.sessionTrace, this.ee)
195
- handle('errorAgg', [type, bucketHash, params, newMetrics], undefined, FEATURE_NAMES.spa, this.ee)
194
+ const msg = [type, bucketHash, params, newMetrics]
195
+ handle('errorAgg', msg, undefined, FEATURE_NAMES.sessionTrace, this.ee)
196
+ handle('errorAgg', msg, undefined, FEATURE_NAMES.spa, this.ee)
197
+ handle('errorAgg', msg, undefined, FEATURE_NAMES.sessionReplay, this.ee)
196
198
 
197
199
  // still send EE events for other features such as above, but stop this one from aggregating internal data
198
200
  if (this.blocked) return
@@ -0,0 +1,73 @@
1
+ import { isBrowserScope } from '../../../common/util/global-scope'
2
+
3
+ const FRAMEWORKS = {
4
+ REACT: 'React',
5
+ ANGULAR: 'Angular',
6
+ ANGULARJS: 'AngularJS',
7
+ BACKBONE: 'Backbone',
8
+ EMBER: 'Ember',
9
+ VUE: 'Vue',
10
+ METEOR: 'Meteor',
11
+ ZEPTO: 'Zepto',
12
+ JQUERY: 'Jquery',
13
+ MOOTOOLS: 'MooTools'
14
+ }
15
+
16
+ export function getFrameworks () {
17
+ if (!isBrowserScope) return [] // don't bother detecting frameworks if not in the main window context
18
+
19
+ const frameworks = []
20
+ try {
21
+ if (detectReact()) frameworks.push(FRAMEWORKS.REACT)
22
+ if (detectAngularJs()) frameworks.push(FRAMEWORKS.ANGULARJS)
23
+ if (detectAngular()) frameworks.push(FRAMEWORKS.ANGULAR)
24
+ if (Object.prototype.hasOwnProperty.call(window, 'Backbone')) frameworks.push(FRAMEWORKS.BACKBONE)
25
+ if (Object.prototype.hasOwnProperty.call(window, 'Ember')) frameworks.push(FRAMEWORKS.EMBER)
26
+ if (Object.prototype.hasOwnProperty.call(window, 'Vue')) frameworks.push(FRAMEWORKS.VUE)
27
+ if (Object.prototype.hasOwnProperty.call(window, 'Meteor')) frameworks.push(FRAMEWORKS.METEOR)
28
+ if (Object.prototype.hasOwnProperty.call(window, 'Zepto')) frameworks.push(FRAMEWORKS.ZEPTO)
29
+ if (Object.prototype.hasOwnProperty.call(window, 'jQuery')) frameworks.push(FRAMEWORKS.JQUERY)
30
+ if (Object.prototype.hasOwnProperty.call(window, 'MooTools')) frameworks.push(FRAMEWORKS.MOOTOOLS)
31
+ } catch (err) {
32
+ // Possibly not supported
33
+ }
34
+ return frameworks
35
+ }
36
+
37
+ function detectReact () {
38
+ try {
39
+ return Object.prototype.hasOwnProperty.call(window, 'React') ||
40
+ Object.prototype.hasOwnProperty.call(window, 'ReactDOM') ||
41
+ Object.prototype.hasOwnProperty.call(window, 'ReactRedux') ||
42
+ document.querySelector('[data-reactroot], [data-reactid]') ||
43
+ (() => {
44
+ const divs = document.querySelectorAll('body > div')
45
+ for (let i = 0; i < divs.length; i++) {
46
+ if (Object.prototype.hasOwnProperty.call(divs[i], '_reactRootContainer')) {
47
+ return true
48
+ }
49
+ }
50
+ })()
51
+ } catch (err) {
52
+ return false
53
+ }
54
+ }
55
+
56
+ function detectAngularJs () {
57
+ try {
58
+ return Object.prototype.hasOwnProperty.call(window, 'angular') ||
59
+ document.querySelector('.ng-binding, [ng-app], [data-ng-app], [ng-controller], [data-ng-controller], [ng-repeat], [data-ng-repeat]') ||
60
+ document.querySelector('script[src*="angular.js"], script[src*="angular.min.js"]')
61
+ } catch (err) {
62
+ return false
63
+ }
64
+ }
65
+
66
+ function detectAngular () {
67
+ try {
68
+ return Object.prototype.hasOwnProperty.call(window, 'ng') ||
69
+ document.querySelector('[ng-version]')
70
+ } catch (err) {
71
+ return false
72
+ }
73
+ }
@@ -0,0 +1,201 @@
1
+ import { faker } from '@faker-js/faker'
2
+ import { getFrameworks } from './framework-detection'
3
+
4
+ jest.mock('../../../common/util/global-scope', () => ({
5
+ isBrowserScope: true
6
+ }))
7
+
8
+ afterEach(() => {
9
+ document.body.innerHTML = ''
10
+ })
11
+
12
+ test('framework detection should not happen in non-browser scope', async () => {
13
+ global.React = {}
14
+
15
+ jest.resetModules()
16
+ jest.doMock('../../../common/util/global-scope', () => ({
17
+ isBrowserScope: false
18
+ }))
19
+ const frameworkDetector = await import('./framework-detection')
20
+
21
+ expect(frameworkDetector.getFrameworks()).toEqual([])
22
+
23
+ delete global.React
24
+ })
25
+
26
+ test('should detect react from global React property', () => {
27
+ global.React = {}
28
+
29
+ expect(getFrameworks()).toEqual(['React'])
30
+
31
+ delete global.React
32
+ })
33
+
34
+ test('should detect react from global ReactDOM property', () => {
35
+ global.ReactDOM = {}
36
+
37
+ expect(getFrameworks()).toEqual(['React'])
38
+
39
+ delete global.ReactDOM
40
+ })
41
+
42
+ test('should detect react from global ReactRedux property', () => {
43
+ global.ReactRedux = {}
44
+
45
+ expect(getFrameworks()).toEqual(['React'])
46
+
47
+ delete global.ReactRedux
48
+ })
49
+
50
+ test('should detect react from html [data-reactroot] property', () => {
51
+ document.body.innerHTML = '<div data-reactroot=""></div>'
52
+
53
+ expect(getFrameworks()).toEqual(['React'])
54
+ })
55
+
56
+ test('should detect react from html [data-reactid] property', () => {
57
+ document.body.innerHTML = '<div data-reactid=""></div>'
58
+
59
+ expect(getFrameworks()).toEqual(['React'])
60
+ })
61
+
62
+ test('should detect react from element _reactRootContainer property', () => {
63
+ const element = document.createElement('div')
64
+ element._reactRootContainer = {}
65
+ document.body.innerHTML = '<html><body></body></html>'
66
+ document.body.appendChild(element)
67
+
68
+ expect(getFrameworks()).toEqual(['React'])
69
+ })
70
+
71
+ test('should detect angularjs from global angular property', () => {
72
+ global.angular = {}
73
+
74
+ expect(getFrameworks()).toEqual(['AngularJS'])
75
+
76
+ delete global.angular
77
+ })
78
+
79
+ test('should detect angularjs from html .ng-binding property', () => {
80
+ document.body.innerHTML = '<div class="ng-binding"></div>'
81
+
82
+ expect(getFrameworks()).toEqual(['AngularJS'])
83
+ })
84
+
85
+ test('should detect angularjs from html [ng-app] property', () => {
86
+ document.body.innerHTML = '<div ng-app=""></div>'
87
+
88
+ expect(getFrameworks()).toEqual(['AngularJS'])
89
+ })
90
+
91
+ test('should detect angularjs from html [data-ng-app] property', () => {
92
+ document.body.innerHTML = '<div data-ng-app=""></div>'
93
+
94
+ expect(getFrameworks()).toEqual(['AngularJS'])
95
+ })
96
+
97
+ test('should detect angularjs from html [ng-controller] property', () => {
98
+ document.body.innerHTML = '<div ng-controller=""></div>'
99
+
100
+ expect(getFrameworks()).toEqual(['AngularJS'])
101
+ })
102
+
103
+ test('should detect angularjs from html [data-ng-controller] property', () => {
104
+ document.body.innerHTML = '<div data-ng-controller=""></div>'
105
+
106
+ expect(getFrameworks()).toEqual(['AngularJS'])
107
+ })
108
+
109
+ test('should detect angularjs from html [ng-repeat] property', () => {
110
+ document.body.innerHTML = '<div ng-repeat=""></div>'
111
+
112
+ expect(getFrameworks()).toEqual(['AngularJS'])
113
+ })
114
+
115
+ test('should detect angularjs from html [data-ng-repeat] property', () => {
116
+ document.body.innerHTML = '<div data-ng-repeat=""></div>'
117
+
118
+ expect(getFrameworks()).toEqual(['AngularJS'])
119
+ })
120
+
121
+ test('should detect angularjs from angular.js script element', () => {
122
+ document.body.innerHTML = `<script src="${faker.internet.url()}/angular.js"></script>`
123
+
124
+ expect(getFrameworks()).toEqual(['AngularJS'])
125
+ })
126
+
127
+ test('should detect angularjs from angular.min.js script element', () => {
128
+ document.body.innerHTML = `<script src="${faker.internet.url()}/angular.min.js"></script>`
129
+
130
+ expect(getFrameworks()).toEqual(['AngularJS'])
131
+ })
132
+
133
+ test('should detect angular from global ng property', () => {
134
+ global.ng = {}
135
+
136
+ expect(getFrameworks()).toEqual(['Angular'])
137
+
138
+ delete global.ng
139
+ })
140
+
141
+ test('should detect angular from html [ng-version] property', () => {
142
+ document.body.innerHTML = '<div ng-version=""></div>'
143
+
144
+ expect(getFrameworks()).toEqual(['Angular'])
145
+ })
146
+
147
+ test('should detect backbone from global Backbone property', () => {
148
+ global.Backbone = {}
149
+
150
+ expect(getFrameworks()).toEqual(['Backbone'])
151
+
152
+ delete global.Backbone
153
+ })
154
+
155
+ test('should detect ember from global Ember property', () => {
156
+ global.Ember = {}
157
+
158
+ expect(getFrameworks()).toEqual(['Ember'])
159
+
160
+ delete global.Ember
161
+ })
162
+
163
+ test('should detect vue from global Vue property', () => {
164
+ global.Vue = {}
165
+
166
+ expect(getFrameworks()).toEqual(['Vue'])
167
+
168
+ delete global.Vue
169
+ })
170
+
171
+ test('should detect meteor from global Meteor property', () => {
172
+ global.Meteor = {}
173
+
174
+ expect(getFrameworks()).toEqual(['Meteor'])
175
+
176
+ delete global.Meteor
177
+ })
178
+
179
+ test('should detect zepto from global Zepto property', () => {
180
+ global.Zepto = {}
181
+
182
+ expect(getFrameworks()).toEqual(['Zepto'])
183
+
184
+ delete global.Zepto
185
+ })
186
+
187
+ test('should detect jquery from global jQuery property', () => {
188
+ global.jQuery = {}
189
+
190
+ expect(getFrameworks()).toEqual(['Jquery'])
191
+
192
+ delete global.jQuery
193
+ })
194
+
195
+ test('should detect mootools from global MooTools property', () => {
196
+ global.MooTools = {}
197
+
198
+ expect(getFrameworks()).toEqual(['MooTools'])
199
+
200
+ delete global.MooTools
201
+ })
@@ -3,17 +3,18 @@ import { registerHandler } from '../../../common/event-emitter/register-handler'
3
3
  import { HarvestScheduler } from '../../../common/harvest/harvest-scheduler'
4
4
  import { FEATURE_NAME, SUPPORTABILITY_METRIC, CUSTOM_METRIC, SUPPORTABILITY_METRIC_CHANNEL, CUSTOM_METRIC_CHANNEL } from '../constants'
5
5
  import { drain } from '../../../common/drain/drain'
6
- import { getFrameworks } from '../../../common/metrics/framework-detection'
6
+ import { getFrameworks } from './framework-detection'
7
+ import { getPolyfills } from './polyfill-detection'
7
8
  import { isFileProtocol } from '../../../common/url/protocol'
8
9
  import { getRules, validateRules } from '../../../common/util/obfuscate'
9
10
  import { VERSION } from '../../../common/constants/env'
10
11
  import { onDOMContentLoaded } from '../../../common/window/load'
11
12
  import { windowAddEventListener } from '../../../common/event-listener/event-listener-opts'
12
13
  import { isBrowserScope } from '../../../common/util/global-scope'
13
- import { FeatureBase } from '../../utils/feature-base'
14
+ import { AggregateBase } from '../../utils/aggregate-base'
14
15
  import { stringify } from '../../../common/util/stringify'
15
16
 
16
- export class Aggregate extends FeatureBase {
17
+ export class Aggregate extends AggregateBase {
17
18
  static featureName = FEATURE_NAME
18
19
  constructor (agentIdentifier, aggregator) {
19
20
  super(agentIdentifier, aggregator, FEATURE_NAME)
@@ -71,6 +72,10 @@ export class Aggregate extends FeatureBase {
71
72
  })
72
73
  }
73
74
 
75
+ getPolyfills().forEach(polyfill => {
76
+ this.storeSupportabilityMetrics('Polyfill/' + polyfill + '/Detected')
77
+ })
78
+
74
79
  // file protocol detection
75
80
  if (isFileProtocol()) {
76
81
  this.storeSupportabilityMetrics('Generic/FileProtocol/Detected')
@@ -0,0 +1,9 @@
1
+
2
+ /**
3
+ * This file is used as a replacement for the polyfill-detection.es5.js
4
+ * file when running the polyfills webpack build.
5
+ * @returns {string[]} an empty array
6
+ */
7
+ export function getPolyfills () {
8
+ return []
9
+ }
@@ -0,0 +1,16 @@
1
+ import { getPolyfills } from './polyfill-detection.es5'
2
+
3
+ afterEach(() => {
4
+ jest.resetAllMocks()
5
+ })
6
+
7
+ test('should always return an empty array', () => {
8
+ const originalFn = Array.from
9
+ Array.from = jest.fn(function (...args) {
10
+ return originalFn.apply(this, args)
11
+ })
12
+
13
+ expect(getPolyfills()).toEqual([])
14
+
15
+ Array.from = originalFn
16
+ })
@@ -0,0 +1,48 @@
1
+ const detectionRegex = /\{ \[native code\] \}/
2
+ const POLYFILLS = {
3
+ FUNCTION_BIND: 'Function.bind',
4
+ FUNCTION_APPLY: 'Function.apply',
5
+ FUNCTION_CALL: 'Function.call',
6
+ ARRAY_INCLUDES: 'Array.includes',
7
+ ARRAY_FROM: 'Array.from',
8
+ ARRAY_FIND: 'Array.find',
9
+ ARRAY_FLAT: 'Array.flat',
10
+ ARRAY_FLATMAP: 'Array.flatMap',
11
+ OBJECT_ASSIGN: 'Object.assign',
12
+ OBJECT_ENTRIES: 'Object.entries',
13
+ OBJECT_VALUES: 'Object.values',
14
+ MAP: 'Map',
15
+ SET: 'Set',
16
+ WEAK_MAP: 'WeakMap',
17
+ WEAK_SET: 'WeakSet'
18
+ }
19
+
20
+ /**
21
+ * Checks for native globals to see if they have been polyfilled by customer code.
22
+ * @returns {string[]} Array of methods that were detected to have been polyfilled
23
+ */
24
+ export function getPolyfills () {
25
+ const polyfills = []
26
+ const mockFunction = function () { /* noop */ }
27
+ try {
28
+ if (typeof mockFunction.bind === 'function' && !detectionRegex.test(mockFunction.bind.toString())) polyfills.push(POLYFILLS.FUNCTION_BIND)
29
+ if (typeof mockFunction.apply === 'function' && !detectionRegex.test(mockFunction.apply.toString())) polyfills.push(POLYFILLS.FUNCTION_APPLY)
30
+ if (typeof mockFunction.call === 'function' && !detectionRegex.test(mockFunction.call.toString())) polyfills.push(POLYFILLS.FUNCTION_CALL)
31
+ if (typeof [].includes === 'function' && !detectionRegex.test([].includes.toString())) polyfills.push(POLYFILLS.ARRAY_INCLUDES)
32
+ if (typeof Array.from === 'function' && !detectionRegex.test(Array.from.toString())) polyfills.push(POLYFILLS.ARRAY_FROM)
33
+ if (typeof [].find === 'function' && !detectionRegex.test([].find.toString())) polyfills.push(POLYFILLS.ARRAY_FIND)
34
+ if (typeof [].flat === 'function' && !detectionRegex.test([].flat.toString())) polyfills.push(POLYFILLS.ARRAY_FLAT)
35
+ if (typeof [].flatMap === 'function' && !detectionRegex.test([].flatMap.toString())) polyfills.push(POLYFILLS.ARRAY_FLATMAP)
36
+ if (typeof Object.assign === 'function' && !detectionRegex.test(Object.assign.toString())) polyfills.push(POLYFILLS.OBJECT_ASSIGN)
37
+ if (typeof Object.entries === 'function' && !detectionRegex.test(Object.entries.toString())) polyfills.push(POLYFILLS.OBJECT_ENTRIES)
38
+ if (typeof Object.values === 'function' && !detectionRegex.test(Object.values.toString())) polyfills.push(POLYFILLS.OBJECT_VALUES)
39
+ if (typeof Map === 'function' && !detectionRegex.test(Map.toString())) polyfills.push(POLYFILLS.MAP)
40
+ if (typeof Set === 'function' && !detectionRegex.test(Set.toString())) polyfills.push(POLYFILLS.SET)
41
+ if (typeof WeakMap === 'function' && !detectionRegex.test(WeakMap.toString())) polyfills.push(POLYFILLS.WEAK_MAP)
42
+ if (typeof WeakSet === 'function' && !detectionRegex.test(WeakSet.toString())) polyfills.push(POLYFILLS.WEAK_SET)
43
+ } catch (err) {
44
+ // Possibly not supported
45
+ }
46
+
47
+ return polyfills
48
+ }
@@ -0,0 +1,163 @@
1
+ /* eslint-disable no-global-assign */
2
+ /* eslint-disable no-extend-native */
3
+ import { getPolyfills } from './polyfill-detection'
4
+
5
+ // DO NOT test Function.prototype.apply, it causes recursion in Jest
6
+
7
+ test('should return an empty array when nothing is polyfilled', () => {
8
+ expect(getPolyfills()).toEqual([])
9
+ })
10
+
11
+ test('should indicate Function.prototype.bind is polyfilled', () => {
12
+ const originalFn = Function.prototype.bind
13
+ Function.prototype.bind = jest.fn(function (...args) {
14
+ return originalFn.apply(this, args)
15
+ })
16
+
17
+ expect(getPolyfills()).toEqual(['Function.bind'])
18
+
19
+ Function.prototype.bind = originalFn
20
+ })
21
+
22
+ test('should indicate Function.prototype.call is polyfilled', () => {
23
+ const originalFn = Function.prototype.call
24
+ Function.prototype.call = jest.fn(function (...args) {
25
+ return originalFn.apply(this, args)
26
+ })
27
+
28
+ expect(getPolyfills()).toEqual(['Function.call'])
29
+
30
+ Function.prototype.call = originalFn
31
+ })
32
+
33
+ test('should indicate Array.prototype.includes is polyfilled', () => {
34
+ const originalFn = Array.prototype.includes
35
+ Array.prototype.includes = jest.fn(function (...args) {
36
+ return originalFn.apply(this, args)
37
+ })
38
+
39
+ expect(getPolyfills()).toEqual(['Array.includes'])
40
+
41
+ Array.prototype.includes = originalFn
42
+ })
43
+
44
+ test('should indicate Array.from is polyfilled', () => {
45
+ const originalFn = Array.from
46
+ Array.from = jest.fn(function (...args) {
47
+ return originalFn.apply(this, args)
48
+ })
49
+
50
+ expect(getPolyfills()).toEqual(['Array.from'])
51
+
52
+ Array.from = originalFn
53
+ })
54
+
55
+ test('should indicate Array.prototype.find is polyfilled', () => {
56
+ const originalFn = Array.prototype.find
57
+ Array.prototype.find = jest.fn(function (...args) {
58
+ return originalFn.apply(this, args)
59
+ })
60
+
61
+ expect(getPolyfills()).toEqual(['Array.find'])
62
+
63
+ Array.prototype.find = originalFn
64
+ })
65
+
66
+ test('should indicate Array.prototype.flat is polyfilled', () => {
67
+ const originalFn = Array.prototype.flat
68
+ Array.prototype.flat = jest.fn(function (...args) {
69
+ return originalFn.apply(this, args)
70
+ })
71
+
72
+ expect(getPolyfills()).toEqual(['Array.flat'])
73
+
74
+ Array.prototype.flat = originalFn
75
+ })
76
+
77
+ test('should indicate Array.prototype.flatMap is polyfilled', () => {
78
+ const originalFn = Array.prototype.flatMap
79
+ Array.prototype.flatMap = jest.fn(function (...args) {
80
+ return originalFn.apply(this, args)
81
+ })
82
+
83
+ expect(getPolyfills()).toEqual(['Array.flatMap'])
84
+
85
+ Array.prototype.flatMap = originalFn
86
+ })
87
+
88
+ test('should indicate Object.assign is polyfilled', () => {
89
+ const originalFn = Object.assign
90
+ Object.assign = jest.fn(function (...args) {
91
+ return originalFn.apply(this, args)
92
+ })
93
+
94
+ expect(getPolyfills()).toEqual(['Object.assign'])
95
+
96
+ Object.assign = originalFn
97
+ })
98
+
99
+ test('should indicate Object.entries is polyfilled', () => {
100
+ const originalFn = Object.entries
101
+ Object.entries = jest.fn(function (...args) {
102
+ return originalFn.apply(this, args)
103
+ })
104
+
105
+ expect(getPolyfills()).toEqual(['Object.entries'])
106
+
107
+ Object.entries = originalFn
108
+ })
109
+
110
+ test('should indicate Object.values is polyfilled', () => {
111
+ const originalFn = Object.values
112
+ Object.values = jest.fn(function (...args) {
113
+ return originalFn.apply(this, args)
114
+ })
115
+
116
+ expect(getPolyfills()).toEqual(['Object.values'])
117
+
118
+ Object.values = originalFn
119
+ })
120
+
121
+ test('should indicate Map constructor is polyfilled', () => {
122
+ const originalFn = Map
123
+ Map = jest.fn(function (...args) {
124
+ return originalFn.apply(this, args)
125
+ })
126
+
127
+ expect(getPolyfills()).toEqual(['Map'])
128
+
129
+ Map = originalFn
130
+ })
131
+
132
+ test('should indicate Set constructor is polyfilled', () => {
133
+ const originalFn = Set
134
+ Set = jest.fn(function (...args) {
135
+ return originalFn.apply(this, args)
136
+ })
137
+
138
+ expect(getPolyfills()).toEqual(['Set'])
139
+
140
+ Set = originalFn
141
+ })
142
+
143
+ test('should indicate WeakMap constructor is polyfilled', () => {
144
+ const originalFn = WeakMap
145
+ WeakMap = jest.fn(function (...args) {
146
+ return originalFn.apply(this, args)
147
+ })
148
+
149
+ expect(getPolyfills()).toEqual(['WeakMap'])
150
+
151
+ WeakMap = originalFn
152
+ })
153
+
154
+ test('should indicate WeakSet constructor is polyfilled', () => {
155
+ const originalFn = WeakSet
156
+ WeakSet = jest.fn(function (...args) {
157
+ return originalFn.apply(this, args)
158
+ })
159
+
160
+ expect(getPolyfills()).toEqual(['WeakSet'])
161
+
162
+ WeakSet = originalFn
163
+ })
@@ -12,9 +12,9 @@ import { getConfigurationValue, getInfo, getRuntime } from '../../../common/conf
12
12
  import { FEATURE_NAME } from '../constants'
13
13
  import { drain } from '../../../common/drain/drain'
14
14
  import { isBrowserScope } from '../../../common/util/global-scope'
15
- import { FeatureBase } from '../../utils/feature-base'
15
+ import { AggregateBase } from '../../utils/aggregate-base'
16
16
 
17
- export class Aggregate extends FeatureBase {
17
+ export class Aggregate extends AggregateBase {
18
18
  static featureName = FEATURE_NAME
19
19
  constructor (agentIdentifier, aggregator) {
20
20
  super(agentIdentifier, aggregator, FEATURE_NAME)
@@ -14,11 +14,11 @@ import * as CONSTANTS from '../constants'
14
14
  import { getActivatedFeaturesFlags } from './initialized-features'
15
15
  import { globalScope, isBrowserScope } from '../../../common/util/global-scope'
16
16
  import { drain } from '../../../common/drain/drain'
17
- import { FeatureBase } from '../../utils/feature-base'
17
+ import { AggregateBase } from '../../utils/aggregate-base'
18
18
 
19
19
  const jsonp = 'NREUM.setToken'
20
20
 
21
- export class Aggregate extends FeatureBase {
21
+ export class Aggregate extends AggregateBase {
22
22
  static featureName = CONSTANTS.FEATURE_NAME
23
23
  constructor (agentIdentifier, aggregator) {
24
24
  super(agentIdentifier, aggregator, CONSTANTS.FEATURE_NAME)
@@ -135,10 +135,10 @@ export class Aggregate extends FeatureBase {
135
135
  // Capture query bytes sent to RUM call endpoint (currently `1`) as a supportability metric. See metrics aggregator (on unload).
136
136
  agentRuntime.queryBytesSent[protocol] = (agentRuntime.queryBytesSent[protocol] || 0) + queryString?.length || 0
137
137
 
138
- const isValidJsonp = submitData.jsonp(
139
- this.getScheme() + '://' + info.beacon + '/' + protocol + '/' + info.licenseKey + queryString,
138
+ const isValidJsonp = submitData.jsonp({
139
+ url: this.getScheme() + '://' + info.beacon + '/' + protocol + '/' + info.licenseKey + '?' + queryString,
140
140
  jsonp
141
- )
141
+ })
142
142
  // Usually `drain` is invoked automatically after processing feature flags contained in the JSONP callback from
143
143
  // ingest (see `activateFeatures`), so when JSONP cannot execute (as with module workers), we drain manually.
144
144
  if (!isValidJsonp) drain(this.agentIdentifier, CONSTANTS.FEATURE_NAME)
@@ -17,9 +17,9 @@ import { getInfo, getConfigurationValue, getRuntime } from '../../../common/conf
17
17
  import { FEATURE_NAME } from '../constants'
18
18
  import { drain } from '../../../common/drain/drain'
19
19
  import { FEATURE_NAMES } from '../../../loaders/features/features'
20
- import { FeatureBase } from '../../utils/feature-base'
20
+ import { AggregateBase } from '../../utils/aggregate-base'
21
21
 
22
- export class Aggregate extends FeatureBase {
22
+ export class Aggregate extends AggregateBase {
23
23
  static featureName = FEATURE_NAME
24
24
  constructor (agentIdentifier, aggregator) {
25
25
  super(agentIdentifier, aggregator, FEATURE_NAME)