@newrelic/browser-agent 0.0.9 → 0.1.229

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 (339) hide show
  1. package/README.md +84 -207
  2. package/dist/cjs/cdn/lite.js +14 -0
  3. package/dist/cjs/cdn/polyfills/lite.js +4 -0
  4. package/dist/cjs/cdn/polyfills/pro.js +4 -0
  5. package/dist/cjs/cdn/polyfills/spa.js +4 -0
  6. package/dist/cjs/cdn/polyfills.js +14 -0
  7. package/dist/cjs/cdn/pro.js +19 -0
  8. package/dist/cjs/cdn/spa.js +19 -0
  9. package/dist/cjs/cdn/worker.js +16 -0
  10. package/dist/cjs/common/aggregate/aggregator.js +168 -0
  11. package/dist/cjs/common/browser-version/firefox-version.js +17 -0
  12. package/dist/cjs/common/browser-version/ios-version.js +18 -0
  13. package/dist/cjs/common/config/config.js +76 -0
  14. package/dist/cjs/common/config/state/configurable.js +32 -0
  15. package/dist/cjs/common/config/state/info.js +50 -0
  16. package/dist/cjs/common/config/state/init.js +86 -0
  17. package/dist/cjs/common/config/state/loader-config.js +28 -0
  18. package/dist/cjs/common/config/state/originals.js +9 -0
  19. package/dist/cjs/common/config/state/runtime.js +50 -0
  20. package/dist/cjs/common/constants/environment-variables.js +20 -0
  21. package/dist/cjs/common/context/shared-context.js +25 -0
  22. package/dist/cjs/common/deny-list/deny-list.js +108 -0
  23. package/dist/cjs/common/drain/drain.js +126 -0
  24. package/dist/cjs/common/event-emitter/contextual-ee.js +149 -0
  25. package/dist/cjs/common/event-emitter/handle.js +24 -0
  26. package/dist/cjs/common/event-emitter/register-handler.js +24 -0
  27. package/dist/cjs/common/event-listener/event-listener-opts.js +46 -0
  28. package/dist/cjs/common/harvest/harvest-scheduler.js +111 -0
  29. package/dist/cjs/common/harvest/harvest.js +236 -0
  30. package/dist/cjs/common/ids/id.js +30 -0
  31. package/dist/cjs/common/ids/unique-id.js +84 -0
  32. package/dist/cjs/common/metrics/framework-detection.js +72 -0
  33. package/dist/cjs/common/metrics/paint-metrics.js +13 -0
  34. package/dist/cjs/common/serialize/bel-serializer.js +89 -0
  35. package/dist/cjs/common/timing/nav-timing.js +77 -0
  36. package/dist/cjs/common/timing/now.js +15 -0
  37. package/dist/cjs/common/unload/eol.js +69 -0
  38. package/dist/cjs/common/url/clean-url.js +16 -0
  39. package/dist/cjs/common/url/encode.js +79 -0
  40. package/dist/cjs/common/url/location.js +14 -0
  41. package/dist/cjs/common/url/parse-url.js +66 -0
  42. package/dist/cjs/common/url/protocol.js +25 -0
  43. package/dist/cjs/common/util/console.js +17 -0
  44. package/dist/cjs/common/util/data-size.js +25 -0
  45. package/dist/cjs/common/util/feature-flags.js +42 -0
  46. package/dist/cjs/common/util/get-or-set.js +39 -0
  47. package/dist/cjs/common/util/global-scope.js +56 -0
  48. package/dist/cjs/common/util/map-own.js +24 -0
  49. package/dist/cjs/common/util/obfuscate.js +76 -0
  50. package/dist/cjs/common/util/reduce.js +22 -0
  51. package/dist/cjs/common/util/s-hash.js +19 -0
  52. package/dist/cjs/common/util/single.js +23 -0
  53. package/dist/cjs/common/util/stringify.js +47 -0
  54. package/dist/cjs/common/util/submit-data.js +99 -0
  55. package/dist/cjs/common/util/traverse.js +41 -0
  56. package/dist/cjs/common/util/user-agent.js +57 -0
  57. package/dist/cjs/common/window/load.js +19 -0
  58. package/dist/cjs/common/window/nreum.js +107 -0
  59. package/dist/cjs/common/window/page-visibility.js +28 -0
  60. package/dist/cjs/common/window/session-storage.js +42 -0
  61. package/dist/cjs/common/window/supports-performance-observer.js +15 -0
  62. package/dist/cjs/common/window/top-level-callers.js +23 -0
  63. package/dist/cjs/common/wrap/index.js +68 -0
  64. package/dist/cjs/common/wrap/wrap-events.js +105 -0
  65. package/dist/cjs/common/wrap/wrap-fetch.js +114 -0
  66. package/dist/cjs/common/wrap/wrap-function.js +269 -0
  67. package/dist/cjs/common/wrap/wrap-history.js +56 -0
  68. package/dist/cjs/common/wrap/wrap-jsonp.js +129 -0
  69. package/dist/cjs/common/wrap/wrap-mutation.js +61 -0
  70. package/dist/cjs/common/wrap/wrap-promise.js +160 -0
  71. package/dist/cjs/common/wrap/wrap-raf.js +55 -0
  72. package/dist/cjs/common/wrap/wrap-timer.js +70 -0
  73. package/dist/cjs/common/wrap/wrap-xhr.js +206 -0
  74. package/dist/cjs/features/ajax/aggregate/index.js +226 -0
  75. package/dist/cjs/features/ajax/constants.js +9 -0
  76. package/dist/cjs/features/ajax/index.js +12 -0
  77. package/dist/cjs/features/ajax/instrument/distributed-tracing.js +145 -0
  78. package/dist/cjs/features/ajax/instrument/index.js +338 -0
  79. package/dist/cjs/features/ajax/instrument/response-size.js +26 -0
  80. package/dist/cjs/features/jserrors/aggregate/canonical-function-name.js +18 -0
  81. package/dist/cjs/features/jserrors/aggregate/canonical-function-name.test.js +30 -0
  82. package/dist/cjs/features/jserrors/aggregate/compute-stack-trace.js +216 -0
  83. package/dist/cjs/features/jserrors/aggregate/compute-stack-trace.test.js +257 -0
  84. package/dist/cjs/features/jserrors/aggregate/format-stack-trace.js +36 -0
  85. package/dist/cjs/features/jserrors/aggregate/format-stack-trace.test.js +39 -0
  86. package/dist/cjs/features/jserrors/aggregate/index.js +267 -0
  87. package/dist/cjs/features/jserrors/aggregate/string-hash-code.js +23 -0
  88. package/dist/cjs/features/jserrors/aggregate/string-hash-code.test.js +26 -0
  89. package/dist/cjs/features/jserrors/constants.js +11 -0
  90. package/dist/cjs/features/jserrors/index.js +12 -0
  91. package/dist/cjs/features/jserrors/instrument/debug.js +40 -0
  92. package/dist/cjs/features/jserrors/instrument/index.js +158 -0
  93. package/dist/cjs/features/metrics/aggregate/index.js +136 -0
  94. package/dist/cjs/features/metrics/constants.js +17 -0
  95. package/dist/cjs/features/metrics/index.js +12 -0
  96. package/dist/cjs/features/metrics/instrument/index.js +20 -0
  97. package/dist/cjs/features/metrics/instrument/workers-helper.js +121 -0
  98. package/dist/cjs/features/page_action/aggregate/index.js +112 -0
  99. package/dist/cjs/features/page_action/constants.js +9 -0
  100. package/dist/cjs/features/page_action/index.js +12 -0
  101. package/dist/cjs/features/page_action/instrument/index.js +21 -0
  102. package/dist/cjs/features/page_view_event/aggregate/index.js +133 -0
  103. package/dist/cjs/features/page_view_event/aggregate/initialized-features.js +39 -0
  104. package/dist/cjs/features/page_view_event/constants.js +15 -0
  105. package/dist/cjs/features/page_view_event/index.js +12 -0
  106. package/dist/cjs/features/page_view_event/instrument/index.js +38 -0
  107. package/dist/cjs/features/page_view_timing/aggregate/index.js +265 -0
  108. package/dist/cjs/features/page_view_timing/constants.js +9 -0
  109. package/dist/cjs/features/page_view_timing/first-paint.js +50 -0
  110. package/dist/cjs/features/page_view_timing/index.js +12 -0
  111. package/dist/cjs/features/page_view_timing/instrument/index.js +36 -0
  112. package/dist/cjs/features/page_view_timing/long-tasks.js +75 -0
  113. package/dist/cjs/features/session_trace/aggregate/index.js +375 -0
  114. package/dist/cjs/features/session_trace/constants.js +32 -0
  115. package/dist/cjs/features/session_trace/index.js +12 -0
  116. package/dist/cjs/features/session_trace/instrument/index.js +133 -0
  117. package/dist/cjs/features/spa/aggregate/index.js +684 -0
  118. package/dist/cjs/features/spa/aggregate/interaction-node.js +84 -0
  119. package/dist/cjs/features/spa/aggregate/interaction-node.test.js +16 -0
  120. package/dist/cjs/features/spa/aggregate/interaction.js +98 -0
  121. package/dist/cjs/features/spa/aggregate/serializer.js +147 -0
  122. package/dist/cjs/features/spa/constants.js +53 -0
  123. package/dist/cjs/features/spa/index.js +12 -0
  124. package/dist/cjs/features/spa/instrument/index.js +114 -0
  125. package/dist/cjs/features/utils/aggregate-base.js +13 -0
  126. package/dist/cjs/features/utils/feature-base.js +58 -0
  127. package/dist/cjs/features/utils/handler-cache.js +64 -0
  128. package/dist/cjs/features/utils/instrument-base.js +71 -0
  129. package/dist/cjs/features/utils/lazy-loader.js +44 -0
  130. package/dist/cjs/index.js +81 -58
  131. package/dist/cjs/loaders/agent.js +86 -0
  132. package/dist/cjs/loaders/api/api.js +109 -0
  133. package/dist/cjs/loaders/api/apiAsync.js +94 -0
  134. package/dist/cjs/loaders/browser-agent.js +29 -0
  135. package/dist/cjs/loaders/configure/configure.js +47 -0
  136. package/dist/cjs/loaders/features/enabled-features.js +19 -0
  137. package/dist/cjs/loaders/features/featureDependencies.js +32 -0
  138. package/dist/cjs/loaders/features/features.js +33 -0
  139. package/dist/cjs/loaders/micro-agent.js +93 -0
  140. package/dist/cjs/loaders/worker-agent.js +24 -0
  141. package/dist/esm/cdn/lite.js +12 -0
  142. package/dist/esm/cdn/polyfills/lite.js +7 -0
  143. package/dist/esm/cdn/polyfills/pro.js +7 -0
  144. package/dist/esm/cdn/polyfills/spa.js +7 -0
  145. package/dist/esm/cdn/polyfills.js +17 -0
  146. package/dist/esm/cdn/pro.js +17 -0
  147. package/dist/esm/cdn/spa.js +17 -0
  148. package/dist/esm/cdn/worker.js +14 -0
  149. package/dist/esm/common/aggregate/aggregator.js +161 -0
  150. package/dist/esm/common/browser-version/firefox-version.js +10 -0
  151. package/dist/esm/common/browser-version/ios-version.js +10 -0
  152. package/dist/esm/common/config/config.js +11 -0
  153. package/dist/esm/common/config/state/configurable.js +25 -0
  154. package/dist/esm/common/config/state/info.js +42 -0
  155. package/dist/esm/common/config/state/init.js +78 -0
  156. package/dist/esm/common/config/state/loader-config.js +21 -0
  157. package/dist/esm/common/config/state/originals.js +2 -0
  158. package/dist/esm/common/config/state/runtime.js +41 -0
  159. package/dist/esm/common/constants/environment-variables.js +11 -0
  160. package/dist/esm/common/context/shared-context.js +18 -0
  161. package/dist/esm/common/deny-list/deny-list.js +101 -0
  162. package/dist/esm/common/drain/drain.js +119 -0
  163. package/dist/esm/common/event-emitter/contextual-ee.js +142 -0
  164. package/dist/esm/common/event-emitter/handle.js +16 -0
  165. package/dist/esm/common/event-emitter/register-handler.js +19 -0
  166. package/dist/esm/common/event-listener/event-listener-opts.js +39 -0
  167. package/dist/esm/common/harvest/harvest-scheduler.js +105 -0
  168. package/dist/esm/common/harvest/harvest.js +228 -0
  169. package/dist/esm/common/ids/id.js +23 -0
  170. package/dist/esm/common/ids/unique-id.js +75 -0
  171. package/dist/esm/common/metrics/framework-detection.js +66 -0
  172. package/dist/esm/common/metrics/paint-metrics.js +6 -0
  173. package/dist/esm/common/serialize/bel-serializer.js +80 -0
  174. package/dist/esm/common/timing/nav-timing.js +67 -0
  175. package/dist/esm/common/timing/now.js +9 -0
  176. package/dist/esm/common/unload/eol.js +62 -0
  177. package/dist/esm/common/url/clean-url.js +10 -0
  178. package/dist/esm/common/url/encode.js +71 -0
  179. package/dist/esm/common/url/location.js +8 -0
  180. package/dist/esm/common/url/parse-url.js +60 -0
  181. package/dist/esm/common/url/protocol.js +17 -0
  182. package/dist/esm/common/util/console.js +11 -0
  183. package/dist/esm/common/util/data-size.js +19 -0
  184. package/dist/esm/common/util/feature-flags.js +33 -0
  185. package/dist/esm/common/util/get-or-set.js +33 -0
  186. package/dist/esm/common/util/global-scope.js +44 -0
  187. package/dist/esm/common/util/map-own.js +18 -0
  188. package/dist/esm/common/util/obfuscate.js +67 -0
  189. package/dist/esm/common/util/reduce.js +16 -0
  190. package/dist/esm/common/util/s-hash.js +13 -0
  191. package/dist/esm/common/util/single.js +16 -0
  192. package/dist/esm/common/util/stringify.js +42 -0
  193. package/dist/esm/common/util/submit-data.js +91 -0
  194. package/dist/esm/common/util/traverse.js +35 -0
  195. package/dist/esm/common/util/user-agent.js +48 -0
  196. package/dist/esm/common/window/load.js +12 -0
  197. package/dist/esm/common/window/nreum.js +91 -0
  198. package/dist/esm/common/window/page-visibility.js +23 -0
  199. package/dist/esm/common/window/session-storage.js +36 -0
  200. package/dist/esm/common/window/supports-performance-observer.js +9 -0
  201. package/dist/esm/common/window/top-level-callers.js +17 -0
  202. package/dist/esm/common/wrap/index.js +14 -0
  203. package/dist/esm/common/wrap/wrap-events.js +97 -0
  204. package/dist/esm/common/wrap/wrap-fetch.js +105 -0
  205. package/dist/esm/common/wrap/wrap-function.js +257 -0
  206. package/dist/esm/common/wrap/wrap-history.js +48 -0
  207. package/dist/esm/common/wrap/wrap-jsonp.js +122 -0
  208. package/dist/esm/common/wrap/wrap-mutation.js +54 -0
  209. package/dist/esm/common/wrap/wrap-promise.js +153 -0
  210. package/dist/esm/common/wrap/wrap-raf.js +48 -0
  211. package/dist/esm/common/wrap/wrap-timer.js +63 -0
  212. package/dist/esm/common/wrap/wrap-xhr.js +199 -0
  213. package/dist/esm/features/ajax/aggregate/index.js +218 -0
  214. package/dist/esm/features/ajax/constants.js +2 -0
  215. package/dist/esm/features/ajax/index.js +1 -0
  216. package/dist/esm/features/ajax/instrument/distributed-tracing.js +137 -0
  217. package/dist/esm/features/ajax/instrument/index.js +330 -0
  218. package/dist/esm/features/ajax/instrument/response-size.js +19 -0
  219. package/dist/esm/features/jserrors/aggregate/canonical-function-name.js +12 -0
  220. package/dist/esm/features/jserrors/aggregate/canonical-function-name.test.js +28 -0
  221. package/dist/esm/features/jserrors/aggregate/compute-stack-trace.js +209 -0
  222. package/dist/esm/features/jserrors/aggregate/compute-stack-trace.test.js +255 -0
  223. package/dist/esm/features/jserrors/aggregate/format-stack-trace.js +29 -0
  224. package/dist/esm/features/jserrors/aggregate/format-stack-trace.test.js +37 -0
  225. package/dist/esm/features/jserrors/aggregate/index.js +260 -0
  226. package/dist/esm/features/jserrors/aggregate/string-hash-code.js +17 -0
  227. package/dist/esm/features/jserrors/aggregate/string-hash-code.test.js +24 -0
  228. package/dist/esm/features/jserrors/constants.js +3 -0
  229. package/dist/esm/features/jserrors/index.js +1 -0
  230. package/dist/esm/features/jserrors/instrument/debug.js +38 -0
  231. package/dist/esm/features/jserrors/instrument/index.js +150 -0
  232. package/dist/esm/features/metrics/aggregate/index.js +129 -0
  233. package/dist/esm/features/metrics/constants.js +6 -0
  234. package/dist/esm/features/metrics/index.js +1 -0
  235. package/dist/esm/features/metrics/instrument/index.js +13 -0
  236. package/dist/esm/features/metrics/instrument/workers-helper.js +116 -0
  237. package/dist/esm/features/page_action/aggregate/index.js +105 -0
  238. package/dist/esm/features/page_action/constants.js +2 -0
  239. package/dist/esm/features/page_action/index.js +1 -0
  240. package/dist/esm/features/page_action/instrument/index.js +14 -0
  241. package/dist/esm/features/page_view_event/aggregate/index.js +124 -0
  242. package/dist/esm/features/page_view_event/aggregate/initialized-features.js +34 -0
  243. package/dist/esm/features/page_view_event/constants.js +5 -0
  244. package/dist/esm/features/page_view_event/index.js +1 -0
  245. package/dist/esm/features/page_view_event/instrument/index.js +29 -0
  246. package/dist/esm/features/page_view_timing/aggregate/index.js +258 -0
  247. package/dist/esm/features/page_view_timing/constants.js +2 -0
  248. package/dist/esm/features/page_view_timing/first-paint.js +43 -0
  249. package/dist/esm/features/page_view_timing/index.js +1 -0
  250. package/dist/esm/features/page_view_timing/instrument/index.js +28 -0
  251. package/dist/esm/features/page_view_timing/long-tasks.js +69 -0
  252. package/dist/esm/features/session_trace/aggregate/index.js +366 -0
  253. package/dist/esm/features/session_trace/constants.js +14 -0
  254. package/dist/esm/features/session_trace/index.js +1 -0
  255. package/dist/esm/features/session_trace/instrument/index.js +123 -0
  256. package/dist/esm/features/spa/aggregate/index.js +674 -0
  257. package/dist/esm/features/spa/aggregate/interaction-node.js +78 -0
  258. package/dist/esm/features/spa/aggregate/interaction-node.test.js +14 -0
  259. package/dist/esm/features/spa/aggregate/interaction.js +92 -0
  260. package/dist/esm/features/spa/aggregate/serializer.js +139 -0
  261. package/dist/esm/features/spa/constants.js +25 -0
  262. package/dist/esm/features/spa/index.js +1 -0
  263. package/dist/esm/features/spa/instrument/index.js +104 -0
  264. package/dist/esm/features/utils/aggregate-base.js +6 -0
  265. package/dist/esm/features/utils/feature-base.js +51 -0
  266. package/dist/esm/features/utils/handler-cache.js +57 -0
  267. package/dist/esm/features/utils/instrument-base.js +69 -0
  268. package/dist/esm/features/utils/lazy-loader.js +37 -0
  269. package/dist/esm/index.js +15 -0
  270. package/dist/esm/loaders/agent.js +77 -0
  271. package/dist/esm/loaders/api/api.js +104 -0
  272. package/dist/esm/loaders/api/apiAsync.js +88 -0
  273. package/dist/esm/loaders/browser-agent.js +23 -0
  274. package/dist/esm/loaders/configure/configure.js +41 -0
  275. package/dist/esm/loaders/features/enabled-features.js +13 -0
  276. package/dist/esm/loaders/features/featureDependencies.js +25 -0
  277. package/dist/esm/loaders/features/features.js +25 -0
  278. package/dist/esm/loaders/micro-agent.js +86 -0
  279. package/dist/esm/loaders/worker-agent.js +18 -0
  280. package/package.json +204 -71
  281. package/types.ts +221 -0
  282. package/dist/bundled/es5/index.js +0 -2
  283. package/dist/bundled/es5/index.js.map +0 -1
  284. package/dist/bundled/es6/index.js +0 -2
  285. package/dist/bundled/es6/index.js.map +0 -1
  286. package/dist/cjs/index.d.ts +0 -19
  287. package/dist/cjs/index.js.map +0 -1
  288. package/dist/cjs/types.d.ts +0 -94
  289. package/dist/cjs/types.js +0 -28
  290. package/dist/cjs/types.js.map +0 -1
  291. package/dist/cjs/utils/api/api.d.ts +0 -10
  292. package/dist/cjs/utils/api/api.js +0 -40
  293. package/dist/cjs/utils/api/api.js.map +0 -1
  294. package/dist/cjs/utils/config/build-configs.d.ts +0 -6
  295. package/dist/cjs/utils/config/build-configs.js +0 -68
  296. package/dist/cjs/utils/config/build-configs.js.map +0 -1
  297. package/dist/cjs/utils/features/features.d.ts +0 -5
  298. package/dist/cjs/utils/features/features.js +0 -14
  299. package/dist/cjs/utils/features/features.js.map +0 -1
  300. package/dist/cjs/utils/features/initialize.d.ts +0 -5
  301. package/dist/cjs/utils/features/initialize.js +0 -51
  302. package/dist/cjs/utils/features/initialize.js.map +0 -1
  303. package/dist/es/index.d.ts +0 -19
  304. package/dist/es/index.js +0 -55
  305. package/dist/es/index.js.map +0 -1
  306. package/dist/es/types.d.ts +0 -94
  307. package/dist/es/types.js +0 -24
  308. package/dist/es/types.js.map +0 -1
  309. package/dist/es/utils/api/api.d.ts +0 -10
  310. package/dist/es/utils/api/api.js +0 -36
  311. package/dist/es/utils/api/api.js.map +0 -1
  312. package/dist/es/utils/config/build-configs.d.ts +0 -6
  313. package/dist/es/utils/config/build-configs.js +0 -64
  314. package/dist/es/utils/config/build-configs.js.map +0 -1
  315. package/dist/es/utils/features/features.d.ts +0 -5
  316. package/dist/es/utils/features/features.js +0 -10
  317. package/dist/es/utils/features/features.js.map +0 -1
  318. package/dist/es/utils/features/initialize.d.ts +0 -5
  319. package/dist/es/utils/features/initialize.js +0 -24
  320. package/dist/es/utils/features/initialize.js.map +0 -1
  321. package/dist/umd/index.d.ts +0 -19
  322. package/dist/umd/index.js +0 -69
  323. package/dist/umd/index.js.map +0 -1
  324. package/dist/umd/types.d.ts +0 -94
  325. package/dist/umd/types.js +0 -38
  326. package/dist/umd/types.js.map +0 -1
  327. package/dist/umd/utils/api/api.d.ts +0 -10
  328. package/dist/umd/utils/api/api.js +0 -50
  329. package/dist/umd/utils/api/api.js.map +0 -1
  330. package/dist/umd/utils/config/build-configs.d.ts +0 -6
  331. package/dist/umd/utils/config/build-configs.js +0 -78
  332. package/dist/umd/utils/config/build-configs.js.map +0 -1
  333. package/dist/umd/utils/features/features.d.ts +0 -5
  334. package/dist/umd/utils/features/features.js +0 -24
  335. package/dist/umd/utils/features/features.js.map +0 -1
  336. package/dist/umd/utils/features/initialize.d.ts +0 -5
  337. package/dist/umd/utils/features/initialize.js +0 -63
  338. package/dist/umd/utils/features/initialize.js.map +0 -1
  339. package/dist/webpack-analysis.html +0 -38
@@ -0,0 +1,674 @@
1
+ /*
2
+ * Copyright 2020 New Relic Corporation. All rights reserved.
3
+ * SPDX-License-Identifier: Apache-2.0
4
+ */
5
+ /*eslint no-undef: "error"*/
6
+ import { registerHandler as register } from '../../../common/event-emitter/register-handler';
7
+ import { parseUrl } from '../../../common/url/parse-url';
8
+ import { shouldCollectEvent } from '../../../common/deny-list/deny-list';
9
+ import { mapOwn } from '../../../common/util/map-own';
10
+ import { navTimingValues as navTiming } from '../../../common/timing/nav-timing';
11
+ import { generateUuid } from '../../../common/ids/unique-id';
12
+ import { paintMetrics } from '../../../common/metrics/paint-metrics';
13
+ import { Interaction } from './interaction';
14
+ import { getConfigurationValue, getRuntime } from '../../../common/config/config';
15
+ import { eventListenerOpts } from '../../../common/event-listener/event-listener-opts';
16
+ import { AggregateBase } from '../../utils/aggregate-base';
17
+ import { HarvestScheduler } from '../../../common/harvest/harvest-scheduler';
18
+ import { Serializer } from './serializer';
19
+ import { ee } from '../../../common/event-emitter/contextual-ee';
20
+ import * as CONSTANTS from '../constants';
21
+ import { drain } from '../../../common/drain/drain';
22
+ import { FEATURE_NAMES } from '../../../loaders/features/features';
23
+ const {
24
+ FEATURE_NAME,
25
+ INTERACTION_EVENTS,
26
+ MAX_TIMER_BUDGET,
27
+ FN_START,
28
+ FN_END,
29
+ CB_START,
30
+ INTERACTION_API,
31
+ REMAINING,
32
+ INTERACTION,
33
+ SPA_NODE,
34
+ JSONP_NODE,
35
+ FETCH_START,
36
+ FETCH_DONE,
37
+ FETCH_BODY,
38
+ JSONP_END,
39
+ originalSetTimeout
40
+ } = CONSTANTS;
41
+ export class Aggregate extends AggregateBase {
42
+ static featureName = FEATURE_NAME;
43
+ constructor(agentIdentifier, aggregator) {
44
+ super(agentIdentifier, aggregator, FEATURE_NAME);
45
+ this.state = {
46
+ initialPageURL: getRuntime(agentIdentifier).origin,
47
+ lastSeenUrl: getRuntime(agentIdentifier).origin,
48
+ lastSeenRouteName: null,
49
+ timerMap: {},
50
+ timerBudget: MAX_TIMER_BUDGET,
51
+ currentNode: null,
52
+ prevNode: null,
53
+ nodeOnLastHashUpdate: null,
54
+ initialPageLoad: null,
55
+ pageLoaded: false,
56
+ childTime: 0,
57
+ depth: 0,
58
+ harvestTimeSeconds: getConfigurationValue(agentIdentifier, 'spa.harvestTimeSeconds') || 10,
59
+ interactionsToHarvest: [],
60
+ interactionsSent: []
61
+ };
62
+ this.serializer = new Serializer(this);
63
+ const {
64
+ state,
65
+ serializer
66
+ } = this;
67
+ let {
68
+ blocked
69
+ } = this;
70
+ const baseEE = ee.get(agentIdentifier); // <-- parent baseEE
71
+ const mutationEE = baseEE.get('mutation');
72
+ const promiseEE = baseEE.get('promise');
73
+ const historyEE = baseEE.get('history');
74
+ const eventsEE = baseEE.get('events'); // ajax --> ee(123).emit() ee()
75
+ const timerEE = baseEE.get('timer');
76
+ const fetchEE = baseEE.get('fetch');
77
+ const jsonpEE = baseEE.get('jsonp');
78
+ const xhrEE = baseEE.get('xhr');
79
+ const tracerEE = baseEE.get('tracer');
80
+ const scheduler = new HarvestScheduler('events', {
81
+ onFinished: onHarvestFinished,
82
+ retryDelay: state.harvestTimeSeconds
83
+ }, {
84
+ agentIdentifier
85
+ });
86
+ scheduler.harvest.on('events', onHarvestStarted);
87
+
88
+ // childTime is used when calculating exclusive time for a cb duration.
89
+ //
90
+ // Exclusive time will be different than the total time for either callbacks
91
+ // which synchronously invoke a customTracer callback or, trigger a synchronous
92
+ // event (eg. onreadystate=1 or popstate).
93
+ //
94
+ // At fn-end, childTime will contain the total time of all timed callbacks and
95
+ // event handlers which executed as a child of the current callback. At the
96
+ // begining of every callback, childTime is saved to the event context (which at
97
+ // that time contains the sum of its preceeding siblings) and is reset to 0. The
98
+ // callback is then executed, and its children may increase childTime. At the
99
+ // end of the callback, it reports its exclusive time as its
100
+ // execution time - exlcuded. childTime is then reset to its previous
101
+ // value, and the totalTime of the callback that just finished executing is
102
+ // added to the childTime time.
103
+ // | clock | childTime | ctx.ct | totalTime | exclusive |
104
+ // click fn-start | 0 | 0 | 0 | | |
105
+ // | click begining: | 5 | 0 | 0 | | |
106
+ // | | custom-1 fn-start | 10 | 0 | 0 | | |
107
+ // | | | custom-1 begining | 15 | 0 | 0 | | |
108
+ // | | | | custom-2 fn-start | 20 | 0 | 0 | | |
109
+ // | | | | | custom-2 | 25 | 0 | 0 | | |
110
+ // | | | | custom-2 fn-end | 30 | 10 | 0 | 10 | 10 |
111
+ // | | | custom-1 middle | 35 | 10 | 0 | | |
112
+ // | | | | custom-3 fn-start | 40 | 0 | 10 | | |
113
+ // | | | | | custom-3 | 45 | 0 | 10 | | |
114
+ // | | | | custom-3 fn-end | 50 | 20 | 0 | 10 | 10 |
115
+ // | | | custom-1 ending | 55 | 20 | 0 | | |
116
+ // | custom-1 fn-end | 60 | 50 | 0 | 50 | 30 |
117
+ // | click ending: | 65 | 50 | | | |
118
+ // click fn-end | 70 | 0 | 0 | 70 | 20 |
119
+
120
+ // if rum response determines that customer lacks entitlements for spa endpoint, block it
121
+ register('block-spa', () => {
122
+ blocked = true;
123
+ scheduler.stopTimer(true);
124
+ }, this.featureName, baseEE);
125
+ if (!isEnabled()) return;
126
+ state.initialPageLoad = new Interaction('initialPageLoad', 0, state.lastSeenUrl, state.lastSeenRouteName, onInteractionFinished, agentIdentifier);
127
+ state.initialPageLoad.save = true;
128
+ state.prevInteraction = state.initialPageLoad;
129
+ state.currentNode = state.initialPageLoad.root; // hint
130
+ // ensure that checkFinish calls are safe during initialPageLoad
131
+ state.initialPageLoad[REMAINING]++;
132
+ register(FN_START, callbackStart, this.featureName, baseEE);
133
+ register(CB_START, callbackStart, this.featureName, promiseEE);
134
+
135
+ // register plugins
136
+ var pluginApi = {
137
+ getCurrentNode: getCurrentNode,
138
+ setCurrentNode: setCurrentNode
139
+ };
140
+ register('spa-register', function (init) {
141
+ if (typeof init === 'function') {
142
+ init(pluginApi);
143
+ }
144
+ }, FEATURE_NAMES.spa, baseEE);
145
+ function callbackStart() {
146
+ state.depth++;
147
+ this.prevNode = state.currentNode;
148
+ this.ct = state.childTime;
149
+ state.childTime = 0;
150
+ state.timerBudget = MAX_TIMER_BUDGET;
151
+ }
152
+ register(FN_END, callbackEnd, this.featureName, baseEE);
153
+ register('cb-end', callbackEnd, this.featureName, promiseEE);
154
+ function callbackEnd() {
155
+ state.depth--;
156
+ var totalTime = this.jsTime || 0;
157
+ var exclusiveTime = totalTime - state.childTime;
158
+ state.childTime = this.ct + totalTime;
159
+ if (state.currentNode) {
160
+ // transfer accumulated callback time to the active interaction node
161
+ // run even if jsTime is 0 to update jsEnd
162
+ state.currentNode.callback(exclusiveTime, this[FN_END]);
163
+ if (this.isTraced) {
164
+ state.currentNode.attrs.tracedTime = exclusiveTime;
165
+ }
166
+ }
167
+ this.jsTime = state.currentNode ? 0 : exclusiveTime;
168
+ setCurrentNode(this.prevNode);
169
+ this.prevNode = null;
170
+ state.timerBudget = MAX_TIMER_BUDGET;
171
+ }
172
+ register(FN_START, function (args, eventSource) {
173
+ var ev = args[0];
174
+ var evName = ev.type;
175
+ var eventNode = ev.__nrNode;
176
+ if (!state.pageLoaded && evName === 'load' && eventSource === window) {
177
+ state.pageLoaded = true;
178
+ // set to null so prevNode is set correctly
179
+ this.prevNode = state.currentNode = null;
180
+ if (state.initialPageLoad) {
181
+ eventNode = state.initialPageLoad.root;
182
+
183
+ // Even if initialPageLoad has remaining callbacks, force the initialPageLoad
184
+ // interaction to complete when the page load event occurs.
185
+ state.initialPageLoad[REMAINING] = 0;
186
+ originalSetTimeout(function () {
187
+ INTERACTION_EVENTS.push('popstate');
188
+ });
189
+ }
190
+ }
191
+ if (eventNode) {
192
+ // If we've already seen a previous handler for this specific event object,
193
+ // just restore that. We want multiple handlers for the same event to share
194
+ // a node.
195
+ setCurrentNode(eventNode);
196
+ } else if (evName === 'hashchange') {
197
+ setCurrentNode(state.nodeOnLastHashUpdate);
198
+ state.nodeOnLastHashUpdate = null;
199
+ } else if (eventSource instanceof XMLHttpRequest) {
200
+ // If this event was emitted by an XHR, restore the node ID associated with
201
+ // that XHR.
202
+ setCurrentNode(baseEE.context(eventSource).spaNode);
203
+ } else if (!state.currentNode) {
204
+ // Otherwise, if no interaction is currently active, create a new node ID,
205
+ // and let the aggregator know that we entered a new event handler callback
206
+ // so that it has a chance to possibly start an interaction.
207
+ if (INTERACTION_EVENTS.indexOf(evName) !== -1) {
208
+ var ixn = new Interaction(evName, this[FN_START], state.lastSeenUrl, state.lastSeenRouteName, onInteractionFinished, agentIdentifier);
209
+
210
+ // Store the interaction as prevInteraction in case it is prematurely discarded
211
+ state.prevInteraction = ixn;
212
+ setCurrentNode(ixn.root);
213
+ if (evName === 'click') {
214
+ var value = getActionText(ev.target);
215
+ if (value) {
216
+ state.currentNode.attrs.custom['actionText'] = value;
217
+ }
218
+ }
219
+ }
220
+ }
221
+ ev.__nrNode = state.currentNode;
222
+ }, this.featureName, eventsEE);
223
+
224
+ /**
225
+ * *** TIMERS ***
226
+ * setTimeout call needs to keep the interaction active in case a node is started
227
+ * in its callback.
228
+ */
229
+
230
+ // The context supplied to this callback will be shared with the fn-start/fn-end
231
+ // callbacks that fire around the callback passed to setTimeout originally.
232
+ register('setTimeout-end', function saveId(args, obj, timerId) {
233
+ if (!state.currentNode || state.timerBudget - this.timerDuration < 0) return;
234
+ if (args && !(args[0] instanceof Function)) return;
235
+ state.currentNode[INTERACTION][REMAINING]++;
236
+ this.timerId = timerId;
237
+ state.timerMap[timerId] = state.currentNode;
238
+ this.timerBudget = state.timerBudget - 50;
239
+ }, this.featureName, timerEE);
240
+ register('clearTimeout-start', function clear(args) {
241
+ var timerId = args[0];
242
+ var node = state.timerMap[timerId];
243
+ if (node) {
244
+ var interaction = node[INTERACTION];
245
+ interaction[REMAINING]--;
246
+ interaction.checkFinish();
247
+ delete state.timerMap[timerId];
248
+ }
249
+ }, this.featureName, timerEE);
250
+ register(FN_START, function () {
251
+ state.timerBudget = this.timerBudget || MAX_TIMER_BUDGET;
252
+ var id = this.timerId;
253
+ var node = state.timerMap[id];
254
+ setCurrentNode(node);
255
+ delete state.timerMap[id];
256
+ if (node) node[INTERACTION][REMAINING]--;
257
+ }, this.featureName, timerEE);
258
+
259
+ /**
260
+ * *** XHR ***
261
+ * - `new-xhr` event is fired when new instance of XHR is created. Here we create
262
+ * a new node and store it on the XHR object.
263
+ * - When the send method is called (`send-xhr-start` event), we tell the interaction
264
+ * to wait for this XHR to complete.
265
+ * - When any direct event handlers are invoked (`fn-start` on the `xhr` emitter),
266
+ * we restore the node in case other child nodes are started here.
267
+ * - Callbacks attached using `addEventListener` are handled using `fn-start` on the
268
+ * `events` emitter.
269
+ * - When `xhr-resolved` is emitted, we end the node. The node.finish() call also
270
+ * instructs the interaction to stop waiting for this node.
271
+ */
272
+
273
+ // context is shared with new-xhr event, and is stored on the xhr iteself.
274
+ register(FN_START, function () {
275
+ setCurrentNode(this[SPA_NODE]);
276
+ }, this.featureName, xhrEE);
277
+
278
+ // context is stored on the xhr and is shared with all callbacks associated
279
+ // with the new xhr
280
+ register('new-xhr', function () {
281
+ if (!state.currentNode && state.prevInteraction && !state.prevInteraction.ignored) {
282
+ /*
283
+ * The previous interaction was discarded before a route change. Restore the interaction
284
+ * in case this XHR is associated with a route change.
285
+ */
286
+ const interaction = state.prevInteraction;
287
+ state.currentNode = interaction.root;
288
+ interaction.root.end = null;
289
+ }
290
+ if (state.currentNode) {
291
+ this[SPA_NODE] = state.currentNode.child('ajax', null, null, true);
292
+ }
293
+ }, this.featureName, xhrEE);
294
+ register('send-xhr-start', function () {
295
+ var node = this[SPA_NODE];
296
+ if (node && !this.sent) {
297
+ this.sent = true;
298
+ node.dt = this.dt;
299
+ node.jsEnd = node.start = this.startTime;
300
+ node[INTERACTION][REMAINING]++;
301
+ }
302
+ }, this.featureName, xhrEE);
303
+ register('xhr-resolved', function () {
304
+ var node = this[SPA_NODE];
305
+ if (node) {
306
+ if (!shouldCollectEvent(this.params)) {
307
+ node.cancel();
308
+ return;
309
+ }
310
+ var attrs = node.attrs;
311
+ attrs.params = this.params;
312
+ attrs.metrics = this.metrics;
313
+ node.finish(this.endTime);
314
+ if (!!this.currentNode && !!this.currentNode.interaction) this.currentNode.interaction.checkFinish();
315
+ }
316
+ }, this.featureName, baseEE);
317
+
318
+ /**
319
+ * *** JSONP ***
320
+ *
321
+ */
322
+
323
+ register('new-jsonp', function (url) {
324
+ if (state.currentNode) {
325
+ var node = this[JSONP_NODE] = state.currentNode.child('ajax', this[FETCH_START]);
326
+ node.start = this['new-jsonp'];
327
+ this.url = url;
328
+ this.status = null;
329
+ }
330
+ }, this.featureName, jsonpEE);
331
+ register('cb-start', function (args) {
332
+ var node = this[JSONP_NODE];
333
+ if (node) {
334
+ setCurrentNode(node);
335
+ this.status = 200;
336
+ }
337
+ }, this.featureName, jsonpEE);
338
+ register('jsonp-error', function () {
339
+ var node = this[JSONP_NODE];
340
+ if (node) {
341
+ setCurrentNode(node);
342
+ this.status = 0;
343
+ }
344
+ }, this.featureName, jsonpEE);
345
+ register(JSONP_END, function () {
346
+ var node = this[JSONP_NODE];
347
+ if (node) {
348
+ // if no status is set then cb never fired - so it's not a valid JSONP
349
+ if (this.status === null) {
350
+ node.cancel();
351
+ return;
352
+ }
353
+ var attrs = node.attrs;
354
+ var params = attrs.params = {};
355
+ var parsed = parseUrl(this.url);
356
+ params.method = 'GET';
357
+ params.pathname = parsed.pathname;
358
+ params.host = parsed.hostname + ':' + parsed.port;
359
+ params.status = this.status;
360
+ attrs.metrics = {
361
+ txSize: 0,
362
+ rxSize: 0
363
+ };
364
+ attrs.isJSONP = true;
365
+ node.jsEnd = this[JSONP_END];
366
+ node.jsTime = this[CB_START] ? this[JSONP_END] - this[CB_START] : 0;
367
+ node.finish(node.jsEnd);
368
+ }
369
+ }, this.featureName, jsonpEE);
370
+ register(FETCH_START, function (fetchArguments, dtPayload) {
371
+ if (fetchArguments) {
372
+ if (!state.currentNode && state.prevInteraction && !state.prevInteraction.ignored) {
373
+ /*
374
+ * The previous interaction was discarded before a route change. Restore the interaction
375
+ * in case this XHR is associated with a route change.
376
+ */
377
+ const interaction = state.prevInteraction;
378
+ state.currentNode = interaction.root;
379
+ interaction.root.end = null;
380
+ }
381
+ if (state.currentNode) {
382
+ this[SPA_NODE] = state.currentNode.child('ajax', this[FETCH_START]);
383
+ if (dtPayload && this[SPA_NODE]) this[SPA_NODE].dt = dtPayload;
384
+ }
385
+ }
386
+ }, this.featureName, fetchEE);
387
+ register(FETCH_BODY + 'start', function (args) {
388
+ if (state.currentNode) {
389
+ this[SPA_NODE] = state.currentNode;
390
+ state.currentNode[INTERACTION][REMAINING]++;
391
+ }
392
+ }, this.featureName, fetchEE);
393
+ register(FETCH_BODY + 'end', function (args, ctx, bodyPromise) {
394
+ var node = this[SPA_NODE];
395
+ if (node) node[INTERACTION][REMAINING]--;
396
+ }, this.featureName, fetchEE);
397
+ register(FETCH_DONE, function (err, res) {
398
+ var node = this[SPA_NODE];
399
+ if (node) {
400
+ if (err) {
401
+ node.cancel();
402
+ return;
403
+ }
404
+ var attrs = node.attrs;
405
+ attrs.params = this.params;
406
+ attrs.metrics = {
407
+ txSize: this.txSize,
408
+ rxSize: this.rxSize
409
+ };
410
+ attrs.isFetch = true;
411
+ node.finish(this[FETCH_DONE]);
412
+ }
413
+ }, this.featureName, fetchEE);
414
+ register('newURL', function (url, hashChangedDuringCb) {
415
+ if (state.currentNode) {
416
+ state.currentNode[INTERACTION].setNewURL(url);
417
+ } else if (state.prevInteraction && !state.prevInteraction.ignored) {
418
+ /*
419
+ * The previous interaction was discarded before the route was changed. This can happen in SPA
420
+ * frameworks when using lazy loading. We have also seen this in version 11+ of Nextjs where
421
+ * some route changes re-use cached resolved promises.
422
+ */
423
+ const interaction = state.prevInteraction;
424
+ interaction.setNewURL(url);
425
+ interaction.root.end = null;
426
+ setCurrentNode(interaction.root);
427
+ }
428
+ if (state.currentNode) {
429
+ if (state.lastSeenUrl !== url) {
430
+ state.currentNode[INTERACTION].routeChange = true;
431
+ }
432
+ if (hashChangedDuringCb) {
433
+ state.nodeOnLastHashUpdate = state.currentNode;
434
+ }
435
+ }
436
+ state.lastSeenUrl = url;
437
+ }, this.featureName, historyEE);
438
+
439
+ /**
440
+ * SCRIPTS
441
+ * This is only needed to keep the interaction open while external scripts are being loaded.
442
+ * The script that is loaded could continue the interaction by making additional AJAX
443
+ * calls or changing the URL. The interaction context (currentNode) needs to be
444
+ * restored somehow, but this differs based on the specific customer code. In some cases, we
445
+ * could wrap a JSONP callback, in other cases we could wrap a higher-level API, and in
446
+ * some cases we may not be able to restore context automatically (customer would need
447
+ * to instrument their code manually).
448
+ *
449
+ * - We do not restore the original context in the load/error callbacks. This would not
450
+ * work for the scripts themselves because by the time the load event fires, the
451
+ * script content has already been executed.
452
+ */
453
+
454
+ // dom-start is emitted when appendChild or replaceChild are called. If the element being
455
+ // inserted is script and we are inside an interaction, we will keep the interaction open
456
+ // until the script is loaded.
457
+ jsonpEE.on('dom-start', function (args) {
458
+ if (!state.currentNode) return;
459
+ var el = args[0];
460
+ var isScript = el && el.nodeName === 'SCRIPT' && el.src !== '';
461
+ var interaction = state.currentNode.interaction;
462
+ if (isScript) {
463
+ // increase remaining count to keep the interaction open
464
+ interaction[REMAINING]++;
465
+ el.addEventListener('load', onload, eventListenerOpts(false));
466
+ el.addEventListener('error', onerror, eventListenerOpts(false));
467
+ }
468
+ function onload() {
469
+ // decrease remaining to allow interaction to finish
470
+ interaction[REMAINING]--;
471
+
472
+ // checkFinish is what initiates closing interaction, but is only called
473
+ // when setCurrentNode is called. Since we are not restoring a node here,
474
+ // we need to initiate the check manually.
475
+ // The reason we are not restoring the node here is because 1) this is not
476
+ // where the code of the external script runs (by the time the load event
477
+ // fires, it has already executed), and 2) it would require storing the context
478
+ // probably on the DOM node and restoring in all callbacks, which is a different
479
+ // use case than lazy loading.
480
+ interaction.checkFinish();
481
+ }
482
+ function onerror() {
483
+ interaction[REMAINING]--;
484
+ interaction.checkFinish();
485
+ }
486
+ });
487
+ register(FN_START, function () {
488
+ setCurrentNode(state.prevNode);
489
+ }, this.featureName, mutationEE);
490
+ register('resolve-start', resolvePromise, this.featureName, promiseEE);
491
+ register('executor-err', resolvePromise, this.featureName, promiseEE);
492
+ register('propagate', saveNode, this.featureName, promiseEE);
493
+ register(CB_START, function () {
494
+ var ctx = this.getCtx ? this.getCtx() : this;
495
+ setCurrentNode(ctx[SPA_NODE]);
496
+ }, this.featureName, promiseEE);
497
+ register(INTERACTION_API + 'get', function (t) {
498
+ var interaction;
499
+ if (state?.currentNode?.[INTERACTION]) interaction = this.ixn = state.currentNode[INTERACTION];else if (state?.prevNode?.end === null && state?.prevNode?.[INTERACTION]?.root?.[INTERACTION]?.eventName != 'initialPageLoad') interaction = this.ixn = state.prevNode[INTERACTION];else interaction = this.ixn = new Interaction('api', t, state.lastSeenUrl, state.lastSeenRouteName, onInteractionFinished, agentIdentifier);
500
+ if (!state.currentNode) {
501
+ interaction.checkFinish();
502
+ if (state.depth) setCurrentNode(interaction.root);
503
+ }
504
+ }, this.featureName, baseEE);
505
+ register(INTERACTION_API + 'actionText', function (t, actionText) {
506
+ var customAttrs = this.ixn.root.attrs.custom;
507
+ if (actionText) customAttrs.actionText = actionText;
508
+ }, this.featureName, baseEE);
509
+ register(INTERACTION_API + 'setName', function (t, name, trigger) {
510
+ var attrs = this.ixn.root.attrs;
511
+ if (name) attrs.customName = name;
512
+ if (trigger) attrs.trigger = trigger;
513
+ }, this.featureName, baseEE);
514
+ register(INTERACTION_API + 'setAttribute', function (t, name, value) {
515
+ this.ixn.root.attrs.custom[name] = value;
516
+ }, this.featureName, baseEE);
517
+ register(INTERACTION_API + 'end', function (timestamp) {
518
+ var interaction = this.ixn;
519
+ var node = activeNodeFor(interaction);
520
+ setCurrentNode(null);
521
+ node.child('customEnd', timestamp).finish(timestamp);
522
+ interaction.finish();
523
+ }, this.featureName, baseEE);
524
+ register(INTERACTION_API + 'ignore', function (t) {
525
+ this.ixn.ignored = true;
526
+ }, this.featureName, baseEE);
527
+ register(INTERACTION_API + 'save', function (t) {
528
+ this.ixn.save = true;
529
+ }, this.featureName, baseEE);
530
+ register(INTERACTION_API + 'tracer', function (timestamp, name, store) {
531
+ var interaction = this.ixn;
532
+ var parent = activeNodeFor(interaction);
533
+ var ctx = baseEE.context(store);
534
+ if (!name) {
535
+ ctx.inc = ++interaction[REMAINING];
536
+ return ctx[SPA_NODE] = parent;
537
+ }
538
+ ctx[SPA_NODE] = parent.child('customTracer', timestamp, name);
539
+ }, this.featureName, baseEE);
540
+ register(FN_START, tracerDone, this.featureName, tracerEE);
541
+ register('no-' + FN_START, tracerDone, this.featureName, tracerEE);
542
+ function tracerDone(timestamp, interactionContext, hasCb) {
543
+ var node = this[SPA_NODE];
544
+ if (!node) return;
545
+ var interaction = node[INTERACTION];
546
+ var inc = this.inc;
547
+ this.isTraced = true;
548
+ if (inc) {
549
+ interaction[REMAINING]--;
550
+ } else if (node) {
551
+ node.finish(timestamp);
552
+ }
553
+ hasCb ? setCurrentNode(node) : interaction.checkFinish();
554
+ }
555
+ register(INTERACTION_API + 'getContext', function (t, cb) {
556
+ var store = this.ixn.root.attrs.store;
557
+ setTimeout(function () {
558
+ cb(store);
559
+ }, 0);
560
+ }, this.featureName, baseEE);
561
+ register(INTERACTION_API + 'onEnd', function (t, cb) {
562
+ this.ixn.handlers.push(cb);
563
+ }, this.featureName, baseEE);
564
+ register('api-routeName', function (t, currentRouteName) {
565
+ state.lastSeenRouteName = currentRouteName;
566
+ if (state.currentNode) state.currentNode[INTERACTION].setNewRoute(currentRouteName);
567
+ }, this.featureName, baseEE);
568
+ function activeNodeFor(interaction) {
569
+ return state.currentNode && state.currentNode[INTERACTION] === interaction ? state.currentNode : interaction.root;
570
+ }
571
+ function saveNode(val, overwrite) {
572
+ if (overwrite || !this[SPA_NODE]) this[SPA_NODE] = state.currentNode;
573
+ }
574
+ function resolvePromise() {
575
+ if (!this.resolved) {
576
+ this.resolved = true;
577
+ this[SPA_NODE] = state.currentNode;
578
+ }
579
+ }
580
+ function getCurrentNode() {
581
+ return state.currentNode;
582
+ }
583
+ function setCurrentNode(newNode) {
584
+ if (!state.pageLoaded && !newNode && state.initialPageLoad) newNode = state.initialPageLoad.root;
585
+ if (state.currentNode) {
586
+ state.currentNode[INTERACTION].checkFinish();
587
+ }
588
+ state.prevNode = state.currentNode;
589
+ state.currentNode = newNode && !newNode[INTERACTION].root.end ? newNode : null;
590
+ }
591
+ function onInteractionFinished(interaction) {
592
+ if (interaction === state.initialPageLoad) state.initialPageLoad = null;
593
+ var root = interaction.root;
594
+ var attrs = root.attrs;
595
+
596
+ // make sure that newrelic[INTERACTION]() works in end handler
597
+ state.currentNode = root;
598
+ mapOwn(interaction.handlers, function (i, cb) {
599
+ cb(attrs.store);
600
+ });
601
+ setCurrentNode(null);
602
+ }
603
+ function onHarvestStarted(options) {
604
+ if (state.interactionsToHarvest.length === 0 || blocked) return {};
605
+ var payload = serializer.serializeMultiple(state.interactionsToHarvest, 0, navTiming);
606
+ if (options.retry) {
607
+ state.interactionsToHarvest.forEach(function (interaction) {
608
+ state.interactionsSent.push(interaction);
609
+ });
610
+ }
611
+ state.interactionsToHarvest = [];
612
+ return {
613
+ body: {
614
+ e: payload
615
+ }
616
+ };
617
+ }
618
+ function onHarvestFinished(result) {
619
+ if (result.sent && result.retry && state.interactionsSent.length > 0) {
620
+ state.interactionsSent.forEach(function (interaction) {
621
+ state.interactionsToHarvest.push(interaction);
622
+ });
623
+ state.interactionsSent = [];
624
+ }
625
+ }
626
+ baseEE.on('errorAgg', function (type, name, params, metrics) {
627
+ if (!state.currentNode) return;
628
+ params._interactionId = state.currentNode.interaction.id;
629
+ // do not capture parentNodeId when in root node
630
+ if (state.currentNode.type && state.currentNode.type !== 'interaction') {
631
+ params._interactionNodeId = state.currentNode.id;
632
+ }
633
+ });
634
+ baseEE.on('interaction', saveInteraction);
635
+ function getActionText(node) {
636
+ var nodeType = node.tagName.toLowerCase();
637
+ var goodNodeTypes = ['a', 'button', 'input'];
638
+ var isGoodNode = goodNodeTypes.indexOf(nodeType) !== -1;
639
+ if (isGoodNode) {
640
+ return node.title || node.value || node.innerText;
641
+ }
642
+ }
643
+ function saveInteraction(interaction) {
644
+ if (interaction.ignored || !interaction.save && !interaction.routeChange) {
645
+ baseEE.emit('interactionDiscarded', [interaction]);
646
+ return;
647
+ }
648
+ if (state.prevInteraction === interaction) {
649
+ // If the interaction is being saved, remove it from prevInteraction variable
650
+ // to prevent the interaction from possibly being sent twice or causing an internal
651
+ // recursive loop issue.
652
+ state.prevInteraction = null;
653
+ }
654
+
655
+ // assign unique id, this is serialized and used to link interactions with errors
656
+ interaction.root.attrs.id = generateUuid();
657
+ if (interaction.root.attrs.trigger === 'initialPageLoad') {
658
+ interaction.root.attrs.firstPaint = paintMetrics['first-paint'];
659
+ interaction.root.attrs.firstContentfulPaint = paintMetrics['first-contentful-paint'];
660
+ }
661
+ baseEE.emit('interactionSaved', [interaction]);
662
+ state.interactionsToHarvest.push(interaction);
663
+ scheduler.scheduleHarvest(0);
664
+ }
665
+ function isEnabled() {
666
+ var enabled = getConfigurationValue(agentIdentifier, 'spa.enabled');
667
+ if (enabled === false) {
668
+ return false;
669
+ }
670
+ return true;
671
+ }
672
+ drain(this.agentIdentifier, this.featureName);
673
+ }
674
+ }