@newrelic/browser-agent 0.1.231 → 1.232.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (256) hide show
  1. package/README.md +2 -2
  2. package/dist/cjs/common/config/state/configurable.js +27 -21
  3. package/dist/cjs/common/config/state/init.js +8 -0
  4. package/dist/cjs/common/config/state/runtime.js +24 -26
  5. package/dist/cjs/common/constants/env.cdn.js +1 -1
  6. package/dist/cjs/common/constants/env.npm.js +1 -1
  7. package/dist/cjs/common/context/shared-context.js +2 -1
  8. package/dist/cjs/common/event-emitter/contextual-ee.test.js +2 -2
  9. package/dist/cjs/common/event-emitter/register-handler.test.js +1 -1
  10. package/dist/cjs/common/event-listener/event-listener-opts.js +4 -2
  11. package/dist/cjs/common/harvest/harvest-scheduler.js +14 -11
  12. package/dist/cjs/common/harvest/harvest.js +3 -1
  13. package/dist/cjs/common/session/constants.js +12 -0
  14. package/dist/cjs/common/session/session-entity.js +278 -0
  15. package/dist/cjs/common/session/session-entity.test.js +436 -0
  16. package/dist/cjs/common/storage/first-party-cookies.js +35 -0
  17. package/dist/cjs/common/storage/local-memory.js +35 -0
  18. package/dist/cjs/common/storage/local-memory.test.js +20 -0
  19. package/dist/cjs/common/storage/local-storage.js +33 -0
  20. package/dist/cjs/common/storage/local-storage.test.js +14 -0
  21. package/dist/cjs/common/timer/interaction-timer.js +78 -0
  22. package/dist/cjs/common/timer/interaction-timer.test.js +216 -0
  23. package/dist/cjs/common/timer/timer.js +32 -0
  24. package/dist/cjs/common/timer/timer.test.js +105 -0
  25. package/dist/cjs/common/unload/eol.js +2 -2
  26. package/dist/cjs/common/url/canonicalize-url.js +32 -0
  27. package/dist/cjs/common/url/canonicalize-url.test.js +42 -0
  28. package/dist/cjs/common/url/clean-url.js +10 -3
  29. package/dist/cjs/common/util/data-size.js +6 -0
  30. package/dist/cjs/common/util/data-size.test.js +47 -0
  31. package/dist/cjs/common/util/global-scope.js +4 -2
  32. package/dist/cjs/common/util/invoke.js +73 -0
  33. package/dist/cjs/common/util/invoke.test.js +49 -0
  34. package/dist/cjs/common/util/obfuscate.js +0 -4
  35. package/dist/cjs/common/window/page-visibility.js +3 -1
  36. package/dist/cjs/common/wrap/wrap-fetch.js +1 -3
  37. package/dist/cjs/common/wrap/wrap-function.js +1 -3
  38. package/dist/cjs/common/wrap/wrap-timer.js +1 -1
  39. package/dist/cjs/features/ajax/aggregate/index.js +2 -2
  40. package/dist/cjs/features/ajax/instrument/index.js +1 -1
  41. package/dist/cjs/features/jserrors/aggregate/canonical-function-name.js +12 -4
  42. package/dist/cjs/features/jserrors/aggregate/compute-stack-trace.js +93 -10
  43. package/dist/cjs/features/jserrors/aggregate/compute-stack-trace.test.js +164 -38
  44. package/dist/cjs/features/jserrors/aggregate/index.js +25 -46
  45. package/dist/cjs/features/jserrors/instrument/index.js +0 -2
  46. package/dist/cjs/features/metrics/aggregate/index.js +13 -2
  47. package/dist/cjs/features/page_action/aggregate/index.js +2 -2
  48. package/dist/cjs/features/page_view_event/aggregate/index.js +6 -3
  49. package/dist/cjs/features/page_view_timing/aggregate/index.js +6 -6
  50. package/dist/cjs/features/session_trace/aggregate/index.js +3 -5
  51. package/dist/cjs/features/spa/aggregate/index.js +6 -5
  52. package/dist/cjs/features/utils/agent-session.js +73 -0
  53. package/dist/cjs/features/utils/feature-base.js +1 -1
  54. package/dist/cjs/features/utils/instrument-base.js +7 -2
  55. package/dist/cjs/features/utils/lazy-loader.js +1 -1
  56. package/dist/cjs/loaders/agent.js +1 -1
  57. package/dist/cjs/loaders/api/api.js +1 -4
  58. package/dist/cjs/loaders/api/apiAsync.js +3 -2
  59. package/dist/cjs/loaders/configure/configure.js +0 -6
  60. package/dist/esm/common/config/state/configurable.js +26 -20
  61. package/dist/esm/common/config/state/init.js +8 -0
  62. package/dist/esm/common/config/state/runtime.js +24 -26
  63. package/dist/esm/common/constants/env.cdn.js +1 -1
  64. package/dist/esm/common/constants/env.npm.js +1 -1
  65. package/dist/esm/common/context/shared-context.js +2 -1
  66. package/dist/esm/common/event-emitter/contextual-ee.test.js +2 -2
  67. package/dist/esm/common/event-emitter/register-handler.test.js +1 -1
  68. package/dist/esm/common/event-listener/event-listener-opts.js +4 -2
  69. package/dist/esm/common/harvest/harvest-scheduler.js +14 -11
  70. package/dist/esm/common/harvest/harvest.js +3 -1
  71. package/dist/esm/common/session/constants.js +3 -0
  72. package/dist/esm/common/session/session-entity.js +271 -0
  73. package/dist/esm/common/session/session-entity.test.js +434 -0
  74. package/dist/esm/common/storage/first-party-cookies.js +28 -0
  75. package/dist/esm/common/storage/local-memory.js +28 -0
  76. package/dist/esm/common/storage/local-memory.test.js +18 -0
  77. package/dist/esm/common/storage/local-storage.js +26 -0
  78. package/dist/esm/common/storage/local-storage.test.js +12 -0
  79. package/dist/esm/common/timer/interaction-timer.js +71 -0
  80. package/dist/esm/common/timer/interaction-timer.test.js +214 -0
  81. package/dist/esm/common/timer/timer.js +25 -0
  82. package/dist/esm/common/timer/timer.test.js +103 -0
  83. package/dist/esm/common/unload/eol.js +1 -1
  84. package/dist/esm/common/url/canonicalize-url.js +27 -0
  85. package/dist/esm/common/url/canonicalize-url.test.js +38 -0
  86. package/dist/esm/common/url/clean-url.js +10 -3
  87. package/dist/esm/common/util/data-size.js +7 -0
  88. package/dist/esm/common/util/data-size.test.js +45 -0
  89. package/dist/esm/common/util/global-scope.js +1 -0
  90. package/dist/esm/common/util/invoke.js +66 -0
  91. package/dist/esm/common/util/invoke.test.js +47 -0
  92. package/dist/esm/common/util/obfuscate.js +0 -4
  93. package/dist/esm/common/window/page-visibility.js +3 -1
  94. package/dist/esm/common/wrap/wrap-fetch.js +1 -2
  95. package/dist/esm/common/wrap/wrap-function.js +1 -2
  96. package/dist/esm/common/wrap/wrap-timer.js +1 -1
  97. package/dist/esm/features/ajax/aggregate/index.js +2 -2
  98. package/dist/esm/features/ajax/instrument/index.js +1 -1
  99. package/dist/esm/features/jserrors/aggregate/canonical-function-name.js +12 -4
  100. package/dist/esm/features/jserrors/aggregate/compute-stack-trace.js +93 -10
  101. package/dist/esm/features/jserrors/aggregate/compute-stack-trace.test.js +149 -25
  102. package/dist/esm/features/jserrors/aggregate/index.js +26 -46
  103. package/dist/esm/features/jserrors/instrument/index.js +0 -1
  104. package/dist/esm/features/metrics/aggregate/index.js +14 -3
  105. package/dist/esm/features/page_action/aggregate/index.js +2 -2
  106. package/dist/esm/features/page_view_event/aggregate/index.js +6 -3
  107. package/dist/esm/features/page_view_timing/aggregate/index.js +6 -6
  108. package/dist/esm/features/session_trace/aggregate/index.js +3 -4
  109. package/dist/esm/features/spa/aggregate/index.js +6 -5
  110. package/dist/esm/features/utils/agent-session.js +67 -0
  111. package/dist/esm/features/utils/feature-base.js +1 -1
  112. package/dist/esm/features/utils/instrument-base.js +7 -2
  113. package/dist/esm/features/utils/lazy-loader.js +1 -1
  114. package/dist/esm/loaders/agent.js +1 -1
  115. package/dist/esm/loaders/api/api.js +2 -5
  116. package/dist/esm/loaders/api/apiAsync.js +2 -1
  117. package/dist/esm/loaders/configure/configure.js +2 -8
  118. package/dist/types/common/config/state/configurable.d.ts.map +1 -1
  119. package/dist/types/common/config/state/init.d.ts.map +1 -1
  120. package/dist/types/common/config/state/runtime.d.ts.map +1 -1
  121. package/dist/types/common/context/shared-context.d.ts.map +1 -1
  122. package/dist/types/common/event-listener/event-listener-opts.d.ts +2 -2
  123. package/dist/types/common/event-listener/event-listener-opts.d.ts.map +1 -1
  124. package/dist/types/common/harvest/harvest-scheduler.d.ts +1 -0
  125. package/dist/types/common/harvest/harvest-scheduler.d.ts.map +1 -1
  126. package/dist/types/common/harvest/harvest.d.ts.map +1 -1
  127. package/dist/types/common/session/constants.d.ts +4 -0
  128. package/dist/types/common/session/constants.d.ts.map +1 -0
  129. package/dist/types/common/session/session-entity.d.ts +72 -0
  130. package/dist/types/common/session/session-entity.d.ts.map +1 -0
  131. package/dist/types/common/storage/first-party-cookies.d.ts +8 -0
  132. package/dist/types/common/storage/first-party-cookies.d.ts.map +1 -0
  133. package/dist/types/common/storage/local-memory.d.ts +8 -0
  134. package/dist/types/common/storage/local-memory.d.ts.map +1 -0
  135. package/dist/types/common/storage/local-storage.d.ts +6 -0
  136. package/dist/types/common/storage/local-storage.d.ts.map +1 -0
  137. package/dist/types/common/timer/interaction-timer.d.ts +11 -0
  138. package/dist/types/common/timer/interaction-timer.d.ts.map +1 -0
  139. package/dist/types/common/timer/timer.d.ts +12 -0
  140. package/dist/types/common/timer/timer.d.ts.map +1 -0
  141. package/dist/types/common/url/canonicalize-url.d.ts +9 -0
  142. package/dist/types/common/url/canonicalize-url.d.ts.map +1 -0
  143. package/dist/types/common/url/clean-url.d.ts +7 -1
  144. package/dist/types/common/url/clean-url.d.ts.map +1 -1
  145. package/dist/types/common/util/data-size.d.ts +7 -1
  146. package/dist/types/common/util/data-size.d.ts.map +1 -1
  147. package/dist/types/common/util/global-scope.d.ts +1 -0
  148. package/dist/types/common/util/global-scope.d.ts.map +1 -1
  149. package/dist/types/common/util/invoke.d.ts +35 -0
  150. package/dist/types/common/util/invoke.d.ts.map +1 -0
  151. package/dist/types/common/util/obfuscate.d.ts.map +1 -1
  152. package/dist/types/common/window/page-visibility.d.ts +1 -1
  153. package/dist/types/common/window/page-visibility.d.ts.map +1 -1
  154. package/dist/types/common/wrap/wrap-fetch.d.ts.map +1 -1
  155. package/dist/types/common/wrap/wrap-function.d.ts.map +1 -1
  156. package/dist/types/features/ajax/aggregate/index.d.ts +2 -2
  157. package/dist/types/features/ajax/aggregate/index.d.ts.map +1 -1
  158. package/dist/types/features/jserrors/aggregate/canonical-function-name.d.ts +8 -1
  159. package/dist/types/features/jserrors/aggregate/canonical-function-name.d.ts.map +1 -1
  160. package/dist/types/features/jserrors/aggregate/compute-stack-trace.d.ts +48 -19
  161. package/dist/types/features/jserrors/aggregate/compute-stack-trace.d.ts.map +1 -1
  162. package/dist/types/features/jserrors/aggregate/index.d.ts +14 -5
  163. package/dist/types/features/jserrors/aggregate/index.d.ts.map +1 -1
  164. package/dist/types/features/jserrors/instrument/index.d.ts.map +1 -1
  165. package/dist/types/features/metrics/aggregate/index.d.ts +2 -2
  166. package/dist/types/features/metrics/aggregate/index.d.ts.map +1 -1
  167. package/dist/types/features/page_action/aggregate/index.d.ts +3 -3
  168. package/dist/types/features/page_action/aggregate/index.d.ts.map +1 -1
  169. package/dist/types/features/page_view_event/aggregate/index.d.ts +2 -2
  170. package/dist/types/features/page_view_event/aggregate/index.d.ts.map +1 -1
  171. package/dist/types/features/page_view_timing/aggregate/index.d.ts +2 -2
  172. package/dist/types/features/page_view_timing/aggregate/index.d.ts.map +1 -1
  173. package/dist/types/features/session_trace/aggregate/index.d.ts +2 -2
  174. package/dist/types/features/session_trace/aggregate/index.d.ts.map +1 -1
  175. package/dist/types/features/spa/aggregate/index.d.ts +2 -2
  176. package/dist/types/features/spa/aggregate/index.d.ts.map +1 -1
  177. package/dist/types/features/utils/agent-session.d.ts +2 -0
  178. package/dist/types/features/utils/agent-session.d.ts.map +1 -0
  179. package/dist/types/features/utils/instrument-base.d.ts.map +1 -1
  180. package/dist/types/features/utils/lazy-loader.d.ts +2 -2
  181. package/dist/types/features/utils/lazy-loader.d.ts.map +1 -1
  182. package/dist/types/loaders/api/api.d.ts.map +1 -1
  183. package/dist/types/loaders/api/apiAsync.d.ts.map +1 -1
  184. package/dist/types/loaders/configure/configure.d.ts.map +1 -1
  185. package/package.json +9 -8
  186. package/src/common/config/state/configurable.js +26 -19
  187. package/src/common/config/state/init.js +7 -0
  188. package/src/common/config/state/runtime.js +22 -27
  189. package/src/common/context/shared-context.js +2 -1
  190. package/src/common/event-emitter/contextual-ee.test.js +2 -2
  191. package/src/common/event-emitter/register-handler.test.js +1 -1
  192. package/src/common/event-listener/event-listener-opts.js +4 -4
  193. package/src/common/harvest/harvest-scheduler.js +12 -8
  194. package/src/common/harvest/harvest.js +3 -1
  195. package/src/common/session/constants.js +3 -0
  196. package/src/common/session/session-entity.js +271 -0
  197. package/src/common/session/session-entity.test.js +317 -0
  198. package/src/common/storage/first-party-cookies.js +31 -0
  199. package/src/common/storage/local-memory.js +30 -0
  200. package/src/common/storage/local-memory.test.js +19 -0
  201. package/src/common/storage/local-storage.js +28 -0
  202. package/src/common/storage/local-storage.test.js +17 -0
  203. package/src/common/timer/interaction-timer.js +75 -0
  204. package/src/common/timer/interaction-timer.test.js +167 -0
  205. package/src/common/timer/timer.js +31 -0
  206. package/src/common/timer/timer.test.js +100 -0
  207. package/src/common/unload/eol.js +1 -1
  208. package/src/common/url/canonicalize-url.js +28 -0
  209. package/src/common/url/canonicalize-url.test.js +34 -0
  210. package/src/common/url/clean-url.js +10 -3
  211. package/src/common/util/data-size.js +6 -0
  212. package/src/common/util/data-size.test.js +50 -0
  213. package/src/common/util/global-scope.js +2 -0
  214. package/src/common/util/invoke.js +55 -0
  215. package/src/common/util/invoke.test.js +65 -0
  216. package/src/common/util/obfuscate.js +0 -4
  217. package/src/common/window/page-visibility.js +2 -2
  218. package/src/common/wrap/wrap-fetch.js +1 -2
  219. package/src/common/wrap/wrap-function.js +1 -2
  220. package/src/common/wrap/wrap-timer.js +1 -1
  221. package/src/features/ajax/aggregate/index.js +2 -2
  222. package/src/features/ajax/instrument/index.js +1 -1
  223. package/src/features/jserrors/aggregate/canonical-function-name.js +12 -4
  224. package/src/features/jserrors/aggregate/compute-stack-trace.js +85 -11
  225. package/src/features/jserrors/aggregate/compute-stack-trace.test.js +141 -24
  226. package/src/features/jserrors/aggregate/index.js +24 -50
  227. package/src/features/jserrors/instrument/index.js +0 -1
  228. package/src/features/metrics/aggregate/index.js +18 -3
  229. package/src/features/page_action/aggregate/index.js +2 -2
  230. package/src/features/page_view_event/aggregate/index.js +6 -3
  231. package/src/features/page_view_timing/aggregate/index.js +6 -6
  232. package/src/features/session_trace/aggregate/index.js +3 -4
  233. package/src/features/spa/aggregate/index.js +5 -5
  234. package/src/features/utils/agent-session.js +68 -0
  235. package/src/features/utils/feature-base.js +1 -1
  236. package/src/features/utils/instrument-base.js +5 -2
  237. package/src/features/utils/lazy-loader.js +1 -1
  238. package/src/loaders/agent.js +1 -1
  239. package/src/loaders/api/api.js +2 -5
  240. package/src/loaders/api/apiAsync.js +2 -1
  241. package/src/loaders/configure/configure.js +2 -7
  242. package/dist/cjs/common/util/single.js +0 -23
  243. package/dist/cjs/common/window/session-storage.js +0 -87
  244. package/dist/cjs/features/utils/aggregate-base.js +0 -13
  245. package/dist/esm/common/util/single.js +0 -16
  246. package/dist/esm/common/window/session-storage.js +0 -77
  247. package/dist/esm/features/utils/aggregate-base.js +0 -6
  248. package/dist/types/common/util/single.d.ts +0 -2
  249. package/dist/types/common/util/single.d.ts.map +0 -1
  250. package/dist/types/common/window/session-storage.d.ts +0 -18
  251. package/dist/types/common/window/session-storage.d.ts.map +0 -1
  252. package/dist/types/features/utils/aggregate-base.d.ts +0 -4
  253. package/dist/types/features/utils/aggregate-base.d.ts.map +0 -1
  254. package/src/common/util/single.js +0 -18
  255. package/src/common/window/session-storage.js +0 -75
  256. package/src/features/utils/aggregate-base.js +0 -7
@@ -2,25 +2,31 @@ import { getFrozenAttributes } from '../../../loaders/features/featureDependenci
2
2
  import { warn } from '../../util/console';
3
3
  export class Configurable {
4
4
  constructor(obj, model) {
5
- try {
6
- if (!obj || typeof obj !== 'object') return warn('New setting a Configurable requires an object as input');
7
- if (!model || typeof model !== 'object') return warn('Setting a Configurable requires a model to set its initial properties');
8
- Object.assign(this, model);
9
- Object.entries(obj).forEach(_ref => {
10
- let [key, value] = _ref;
11
- const frozenAttrs = getFrozenAttributes(key);
12
- if (frozenAttrs.length && value && typeof value === 'object') {
13
- frozenAttrs.forEach(attr => {
14
- if (attr in value) {
15
- warn("\"".concat(attr, "\" is a protected attribute and can not be changed in feature ").concat(key, ". It will have no effect."));
16
- delete value[attr];
17
- }
18
- });
19
- }
20
- this[key] = value;
21
- });
22
- } catch (err) {
23
- warn('An error occured while setting a Configurable', err);
24
- }
5
+ Object.assign(this, setValues(obj, model));
6
+ }
7
+ }
8
+ function setValues(obj, model) {
9
+ const state = {};
10
+ try {
11
+ if (!obj || typeof obj !== 'object') return warn('New setting a Configurable requires an object as input');
12
+ if (!model || typeof model !== 'object') return warn('Setting a Configurable requires a model to set its initial properties');
13
+ Object.assign(state, model);
14
+ Object.entries(obj).forEach(_ref => {
15
+ let [key, value] = _ref;
16
+ if (!Object.keys(model).includes(key)) return;
17
+ const frozenAttrs = getFrozenAttributes(key);
18
+ if (frozenAttrs.length && value && typeof value === 'object') {
19
+ frozenAttrs.forEach(attr => {
20
+ if (attr in value) {
21
+ warn("\"".concat(attr, "\" is a protected attribute and can not be changed in feature ").concat(key, ". It will have no effect."));
22
+ delete value[attr];
23
+ }
24
+ });
25
+ }
26
+ state[key] = value;
27
+ });
28
+ return state;
29
+ } catch (err) {
30
+ warn('An error occured while setting a Configurable', err);
25
31
  }
26
32
  }
@@ -1,3 +1,5 @@
1
+ import { DEFAULT_EXPIRES_MS, DEFAULT_INACTIVE_MS } from '../../session/constants';
2
+ import { globalScope } from '../../util/global-scope';
1
3
  import { gosNREUMInitializedAgents } from '../../window/nreum';
2
4
  import { Configurable } from './configurable';
3
5
  const model = {
@@ -19,6 +21,12 @@ const model = {
19
21
  cors_use_tracecontext_headers: undefined,
20
22
  allowed_origins: undefined
21
23
  },
24
+ session: {
25
+ domain: undefined,
26
+ // used by first party cookies to set the top-level domain
27
+ expiresMs: DEFAULT_EXPIRES_MS,
28
+ inactiveMs: DEFAULT_INACTIVE_MS
29
+ },
22
30
  ssl: undefined,
23
31
  obfuscate: undefined,
24
32
  jserrors: {
@@ -1,33 +1,31 @@
1
1
  import * as userAgent from '../../util/user-agent';
2
2
  import { Configurable } from './configurable';
3
3
  import { gosNREUMInitializedAgents } from '../../window/nreum';
4
- import { getCurrentSessionIdOrMakeNew } from '../../window/session-storage';
5
- import { getConfigurationValue } from '../config';
6
4
  import { globalScope } from '../../util/global-scope';
7
5
  import { BUILD_ENV, DIST_METHOD, VERSION } from "../../constants/env.npm";
8
- const model = agentId => {
9
- return {
10
- buildEnv: BUILD_ENV,
11
- bytesSent: {},
12
- customTransaction: undefined,
13
- disabled: false,
14
- distMethod: DIST_METHOD,
15
- isolatedBacklog: false,
16
- loaderType: undefined,
17
- maxBytes: 30000,
18
- // The "timeOrigin" property is the new standard timestamp property shared across main frame and workers, but is not supported in some early Safari browsers (safari<15) + IE
19
- // ingest expects an integer value, and timeOrigin can return a float.
20
- offset: Math.floor(globalScope?.performance?.timeOrigin || globalScope?.performance?.timing?.navigationStart || Date.now()),
21
- onerror: undefined,
22
- origin: '' + globalScope.location,
23
- ptid: undefined,
24
- releaseIds: {},
25
- sessionId: getConfigurationValue(agentId, 'privacy.cookies_enabled') == true ? getCurrentSessionIdOrMakeNew() : null,
26
- // if cookies (now session tracking) is turned off or can't get session ID, this is null
27
- xhrWrappable: typeof globalScope.XMLHttpRequest?.prototype?.addEventListener === 'function',
28
- userAgent,
29
- version: VERSION
30
- };
6
+ const model = {
7
+ buildEnv: BUILD_ENV,
8
+ bytesSent: {},
9
+ // Used for SM to capture body bytes sent per endpoint
10
+ queryBytesSent: {},
11
+ // Used for SM to capture query parameter bytes sent per endpoint
12
+ customTransaction: undefined,
13
+ disabled: false,
14
+ distMethod: DIST_METHOD,
15
+ isolatedBacklog: false,
16
+ loaderType: undefined,
17
+ maxBytes: 30000,
18
+ // The "timeOrigin" property is the new standard timestamp property shared across main frame and workers, but is not supported in some early Safari browsers (safari<15) + IE
19
+ // ingest expects an integer value, and timeOrigin can return a float.
20
+ offset: Math.floor(globalScope?.performance?.timeOrigin || globalScope?.performance?.timing?.navigationStart || Date.now()),
21
+ onerror: undefined,
22
+ origin: '' + globalScope.location,
23
+ ptid: undefined,
24
+ releaseIds: {},
25
+ session: undefined,
26
+ xhrWrappable: typeof globalScope.XMLHttpRequest?.prototype?.addEventListener === 'function',
27
+ userAgent,
28
+ version: VERSION
31
29
  };
32
30
  const _cache = {};
33
31
  export function getRuntime(id) {
@@ -37,6 +35,6 @@ export function getRuntime(id) {
37
35
  }
38
36
  export function setRuntime(id, obj) {
39
37
  if (!id) throw new Error('All runtime objects require an agent identifier!');
40
- _cache[id] = new Configurable(obj, model(id));
38
+ _cache[id] = new Configurable(obj, model);
41
39
  gosNREUMInitializedAgents(id, _cache[id], 'runtime');
42
40
  }
@@ -6,7 +6,7 @@
6
6
  /**
7
7
  * Exposes the version of the agent
8
8
  */
9
- export const VERSION = "1.231.0";
9
+ export const VERSION = "1.232.1";
10
10
 
11
11
  /**
12
12
  * Exposes the build type of the agent
@@ -6,7 +6,7 @@
6
6
  /**
7
7
  * Exposes the version of the agent
8
8
  */
9
- export const VERSION = "1.231.0";
9
+ export const VERSION = "1.232.1";
10
10
 
11
11
  /**
12
12
  * Exposes the build type of the agent
@@ -1,6 +1,7 @@
1
1
  import { warn } from '../util/console';
2
2
  const model = {
3
- agentIdentifier: ''
3
+ agentIdentifier: '',
4
+ ee: undefined
4
5
  };
5
6
  export class SharedContext {
6
7
  constructor(context) {
@@ -253,8 +253,8 @@ describe('event-emitter emit', () => {
253
253
  scopeEE.emit(eventType, eventArgs, {}, false, false);
254
254
  expect(mockScopeListener).toHaveBeenCalledTimes(1);
255
255
  expect(mockListener).not.toHaveBeenCalled();
256
- expect(scopeEE.backlog['feature']).toEqual(expect.arrayContaining([expect.arrayContaining([scopeEE, eventType, eventArgs, {}])]));
257
- expect(ee.backlog['feature']).toEqual(scopeEE.backlog['feature']);
256
+ expect(scopeEE.backlog.feature).toEqual(expect.arrayContaining([expect.arrayContaining([scopeEE, eventType, eventArgs, {}])]));
257
+ expect(ee.backlog.feature).toEqual(scopeEE.backlog.feature);
258
258
  });
259
259
  });
260
260
  describe('getOrSetContext', () => {
@@ -17,7 +17,7 @@ test('should default group to "feature"', async () => {
17
17
  const eventType = faker.datatype.uuid();
18
18
  const eventHandler = jest.fn();
19
19
  registerHandler(eventType, eventHandler);
20
- expect(registerHandler.handlers['feature']).toEqual(expect.objectContaining({
20
+ expect(registerHandler.handlers.feature).toEqual(expect.objectContaining({
21
21
  [eventType]: [expect.arrayContaining([handleEE, eventHandler])]
22
22
  }));
23
23
  });
@@ -32,10 +32,12 @@ export function eventListenerOpts(useCapture, abortSignal) {
32
32
  /** Do not use this within the worker context. */
33
33
  export function windowAddEventListener(event, listener) {
34
34
  let capture = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;
35
- window.addEventListener(event, listener, eventListenerOpts(capture));
35
+ let abortSignal = arguments.length > 3 ? arguments[3] : undefined;
36
+ window.addEventListener(event, listener, eventListenerOpts(capture, abortSignal));
36
37
  }
37
38
  /** Do not use this within the worker context. */
38
39
  export function documentAddEventListener(event, listener) {
39
40
  let capture = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;
40
- document.addEventListener(event, listener, eventListenerOpts(capture));
41
+ let abortSignal = arguments.length > 3 ? arguments[3] : undefined;
42
+ document.addEventListener(event, listener, eventListenerOpts(capture, abortSignal));
41
43
  }
@@ -22,17 +22,21 @@ export class HarvestScheduler extends SharedContext {
22
22
  this.aborted = false; // this controls the per-interval and final harvests for the scheduler (currently per feature specific!)
23
23
 
24
24
  this.harvest = new Harvest(this.sharedContext);
25
- subscribeToEOL(() => {
26
- if (this.aborted) return;
27
25
 
28
- // If opts.onUnload is defined, these are special actions to execute before attempting to send the final payload.
29
- if (this.opts.onUnload) this.opts.onUnload();
30
- this.runHarvest({
31
- unload: true
32
- });
33
- }, getConfigurationValue(this.sharedContext.agentIdentifier, 'allow_bfcache')); // TO DO: remove feature flag after rls stable
34
- }
26
+ // unload if EOL mechanism fires
27
+ subscribeToEOL(this.unload.bind(this), getConfigurationValue(this.sharedContext.agentIdentifier, 'allow_bfcache')); // TO DO: remove feature flag after rls stable
35
28
 
29
+ // unload if session resets
30
+ this.sharedContext?.ee.on('session-reset', this.unload.bind(this));
31
+ }
32
+ unload() {
33
+ if (this.aborted) return;
34
+ // If opts.onUnload is defined, these are special actions to execute before attempting to send the final payload.
35
+ if (this.opts.onUnload) this.opts.onUnload();
36
+ this.runHarvest({
37
+ unload: true
38
+ });
39
+ }
36
40
  startTimer(interval, initialDelay) {
37
41
  this.interval = interval;
38
42
  this.started = true;
@@ -75,8 +79,7 @@ export class HarvestScheduler extends SharedContext {
75
79
  }
76
80
  }
77
81
  } else {
78
- const runAfterSending = opts?.unload ? undefined : onHarvestFinished; // don't bother running onFinish handler if this is the final harvest
79
- this.harvest.sendX(this.endpoint, opts, runAfterSending);
82
+ this.harvest.sendX(this.endpoint, opts, onHarvestFinished);
80
83
  }
81
84
  if (this.started) {
82
85
  this.scheduleHarvest();
@@ -120,6 +120,8 @@ export class Harvest extends SharedContext {
120
120
 
121
121
  // Get bytes harvested per endpoint as a supportability metric. See metrics aggregator (on unload).
122
122
  agentRuntime.bytesSent[endpoint] = (agentRuntime.bytesSent[endpoint] || 0) + body?.length || 0;
123
+ // Get query bytes harvested per endpoint as a supportability metric. See metrics aggregator (on unload).
124
+ agentRuntime.queryBytesSent[endpoint] = (agentRuntime.queryBytesSent[endpoint] || 0) + fullUrl.split('?').slice(-1)[0]?.length || 0;
123
125
 
124
126
  /* Since workers don't support sendBeacon right now, or Image(), they can only use XHR method.
125
127
  Because they still do permit synch XHR, the idea is that at final harvest time (worker is closing),
@@ -160,7 +162,7 @@ export class Harvest extends SharedContext {
160
162
  var ref = this.obfuscator.shouldObfuscate() ? this.obfuscator.obfuscateString(location) : location;
161
163
  return ['?a=' + info.applicationID, encodeParam('sa', info.sa ? '' + info.sa : ''), encodeParam('v', VERSION), transactionNameParam(info), encodeParam('ct', runtime.customTransaction), '&rst=' + now(), '&ck=0',
162
164
  // ck param DEPRECATED - still expected by backend
163
- '&s=' + (runtime.sessionId || '0'),
165
+ '&s=' + (runtime.session?.value || '0'),
164
166
  // the 0 id encaps all untrackable and default traffic
165
167
  encodeParam('ref', ref), encodeParam('ptid', runtime.ptid ? '' + runtime.ptid : '')].join('');
166
168
  }
@@ -0,0 +1,3 @@
1
+ export const PREFIX = 'NRBA';
2
+ export const DEFAULT_EXPIRES_MS = 14400000;
3
+ export const DEFAULT_INACTIVE_MS = 1800000;
@@ -0,0 +1,271 @@
1
+ import { generateRandomHexString } from '../ids/unique-id';
2
+ import { warn } from '../util/console';
3
+ import { stringify } from '../util/stringify';
4
+ import { ee } from '../event-emitter/contextual-ee';
5
+ import { Timer } from '../timer/timer';
6
+ import { isBrowserScope } from '../util/global-scope';
7
+ import { DEFAULT_EXPIRES_MS, DEFAULT_INACTIVE_MS, PREFIX } from './constants';
8
+ import { LocalMemory } from '../storage/local-memory';
9
+ import { InteractionTimer } from '../timer/interaction-timer';
10
+ import { wrapEvents } from '../wrap';
11
+ import { Configurable } from '../config/state/configurable';
12
+ import { handle } from '../event-emitter/handle';
13
+ import { SUPPORTABILITY_METRIC_CHANNEL } from '../../features/metrics/constants';
14
+ import { FEATURE_NAMES } from '../../loaders/features/features';
15
+ const model = {
16
+ value: '',
17
+ inactiveAt: 0,
18
+ expiresAt: 0,
19
+ updatedAt: Date.now(),
20
+ sessionReplayActive: false,
21
+ sessionTraceActive: false,
22
+ custom: {}
23
+ };
24
+ export class SessionEntity {
25
+ /**
26
+ * Create a self-managing Session Entity. This entity is scoped to the agent identifier which triggered it, allowing for multiple simultaneous session objects to exist.
27
+ * There is one "namespace" an agent can store data in LS -- NRBA_{key}. If there are two agents on one page, and they both use the same key, they could overwrite each other since they would both use the same namespace in LS by default.
28
+ * The value can be overridden in the constructor, but will default to a unique 16 character hex string
29
+ * expiresMs and inactiveMs are used to "expire" the session, but can be overridden in the constructor. Pass 0 to disable expiration timers.
30
+ */
31
+ constructor(opts) {
32
+ this.setup(opts);
33
+ }
34
+ setup(_ref) {
35
+ let {
36
+ agentIdentifier,
37
+ key,
38
+ value = generateRandomHexString(16),
39
+ expiresMs = DEFAULT_EXPIRES_MS,
40
+ inactiveMs = DEFAULT_INACTIVE_MS,
41
+ storageAPI = new LocalMemory()
42
+ } = _ref;
43
+ if (!agentIdentifier || !key) throw new Error('Missing Required Fields');
44
+ if (!isBrowserScope) this.storage = new LocalMemory();else this.storage = storageAPI;
45
+ this.sync(model);
46
+ this.agentIdentifier = agentIdentifier;
47
+
48
+ // key is intended to act as the k=v pair
49
+ this.key = key;
50
+ // value is intended to act as the primary value of the k=v pair
51
+ this.value = value;
52
+ this.expiresMs = expiresMs;
53
+ this.inactiveMs = inactiveMs;
54
+ this.ee = ee.get(agentIdentifier);
55
+ wrapEvents(this.ee);
56
+
57
+ // the first time the session entity class is instantiated, we check the storage API for an existing
58
+ // object. If it exists, the values inside the object are used to inform the timers that run locally.
59
+ // if the initial read is empty, it allows us to set a "fresh" "new" session immediately.
60
+ // the local timers are used after the session is running to "expire" the session, allowing for pausing timers etc.
61
+ // the timestamps stored in the storage API can be checked at initial run, and when the page is restored, otherwise we lean
62
+ // on the local timers to expire the session
63
+ const initialRead = this.read();
64
+
65
+ // the set-up of the timer used to expire the session "naturally" at a certain time
66
+ // this gets ignored if the value is falsy, allowing for session entities that do not expire
67
+ if (expiresMs) {
68
+ this.expiresAt = initialRead?.expiresAt || this.getFutureTimestamp(expiresMs);
69
+ this.expiresTimer = new Timer({
70
+ // When the inactive timer ends, collect a SM and reset the session
71
+ onEnd: () => {
72
+ this.collectSM('expired', this);
73
+ this.collectSM('duration', this);
74
+ this.reset();
75
+ }
76
+ }, this.expiresAt - Date.now());
77
+ } else {
78
+ this.expiresAt = Infinity;
79
+ }
80
+
81
+ // the set-up of the timer used to expire the session due to "inactivity" at a certain time
82
+ // this gets ignored if the value is falsy, allowing for session entities that do not expire
83
+ // this gets "refreshed" when "activity" is observed
84
+ if (inactiveMs) {
85
+ this.inactiveAt = initialRead?.inactiveAt || this.getFutureTimestamp(inactiveMs);
86
+ this.inactiveTimer = new InteractionTimer({
87
+ // When the inactive timer ends, collect a SM and reset the session
88
+ onEnd: () => {
89
+ this.collectSM('inactive', this);
90
+ this.collectSM('duration', this);
91
+ this.reset();
92
+ },
93
+ // When the inactive timer refreshes, it will update the storage values with an update timestamp
94
+ onRefresh: this.refresh.bind(this),
95
+ // When the inactive timer pauses, update the storage values with an update timestamp
96
+ onPause: () => this.write(new Configurable(this.read(), model)),
97
+ ee: this.ee,
98
+ refreshEvents: ['click', 'keydown', 'scroll']
99
+ }, this.inactiveAt - Date.now());
100
+ } else {
101
+ this.inactiveAt = Infinity;
102
+ }
103
+
104
+ // The fact that the session is "new" or pre-existing is used in some places in the agent. Session Replay and Trace
105
+ // can use this info to inform whether to trust a new sampling decision vs continue a previous tracking effort.
106
+ this.isNew = !Object.keys(initialRead).length;
107
+ // if its a "new" session, we write to storage API with the default values. These values may change over the lifespan of the agent run.
108
+ // we can use configurable here to help us know and manage what values are being used. -- see "model" above
109
+ if (this.isNew) this.write(new Configurable(this, model), true);else this.sync(initialRead);
110
+ this.initialized = true;
111
+ }
112
+
113
+ // This is the actual key appended to the storage API
114
+ get lookupKey() {
115
+ return "".concat(PREFIX, "_").concat(this.key);
116
+ }
117
+ sync(data) {
118
+ Object.assign(this, data);
119
+ }
120
+
121
+ /**
122
+ * Fetch the stored values from the storage API tied to this entity
123
+ * @returns {Object}
124
+ */
125
+ read() {
126
+ try {
127
+ const val = this.storage.get(this.lookupKey);
128
+ if (!val) return {};
129
+ // TODO - decompression would need to happen here if we decide to do it
130
+ const obj = typeof val === 'string' ? JSON.parse(val) : val;
131
+ if (this.isInvalid(obj)) return {};
132
+ // if the session expires, collect a SM count before resetting
133
+ if (this.isExpired(obj.expiresAt)) {
134
+ this.collectSM('expired', this);
135
+ this.collectSM('duration', obj, true);
136
+ return this.reset();
137
+ }
138
+ // if "inactive" timer is expired at "read" time -- esp. initial read -- reset
139
+ // collect a SM count before resetting
140
+ if (this.isExpired(obj.inactiveAt)) {
141
+ this.collectSM('inactive', this);
142
+ this.collectSM('duration', obj, true);
143
+ return this.reset();
144
+ }
145
+ return obj;
146
+ } catch (e) {
147
+ warn('Failed to read from storage API', e);
148
+ // storage is inaccessible
149
+ return {};
150
+ }
151
+ }
152
+
153
+ /**
154
+ * Store data to the storage API tied to this entity
155
+ * To preseve existing attributes, the output of ...session.read()
156
+ * should be appended to the data argument
157
+ * @param {Object} data
158
+ * @returns {Object}
159
+ */
160
+ write(data) {
161
+ try {
162
+ if (!data || typeof data !== 'object') return;
163
+ // everytime we update, we can update a timestamp for sanity
164
+ data.updatedAt = Date.now();
165
+ this.sync(data);
166
+ // TODO - compression would need happen here if we decide to do it
167
+ this.storage.set(this.lookupKey, stringify(data));
168
+ return data;
169
+ } catch (e) {
170
+ // storage is inaccessible
171
+ warn('Failed to write to the storage API', e);
172
+ return null;
173
+ }
174
+ }
175
+ reset() {
176
+ // this method should set off a chain of actions across the features by emitting 'new-session'
177
+ // * send off pending payloads
178
+ // * stop recording (stn and sr)...
179
+ // * delete the session and start over
180
+ try {
181
+ if (this.initialized) this.ee.emit('session-reset');
182
+ this.storage.remove(this.lookupKey);
183
+ this.inactiveTimer?.abort?.();
184
+ this.expiresTimer?.clear?.();
185
+ delete this.custom;
186
+ delete this.value;
187
+ this.setup({
188
+ agentIdentifier: this.agentIdentifier,
189
+ key: this.key,
190
+ storageAPI: this.storage,
191
+ expiresMs: this.expiresMs,
192
+ inactiveMs: this.inactiveMs
193
+ });
194
+ return this.read();
195
+ } catch (e) {
196
+ return {};
197
+ }
198
+ }
199
+
200
+ /**
201
+ * Refresh the inactivity timer data
202
+ */
203
+ refresh() {
204
+ // read here & invalidate
205
+ const existingData = this.read();
206
+ this.inactiveAt = this.getFutureTimestamp(this.inactiveMs);
207
+ this.write({
208
+ ...existingData,
209
+ inactiveAt: this.inactiveAt
210
+ });
211
+ }
212
+
213
+ /**
214
+ * @param {number} timestamp
215
+ * @returns {boolean}
216
+ */
217
+ isExpired(timestamp) {
218
+ return Date.now() > timestamp;
219
+ }
220
+
221
+ /**
222
+ * @param {Object} data
223
+ * @returns {boolean}
224
+ */
225
+ isInvalid(data) {
226
+ const requiredKeys = ['value', 'expiresAt', 'inactiveAt'];
227
+ return !requiredKeys.every(x => Object.keys(data).includes(x));
228
+ }
229
+ collectSM(type, data, useUpdatedAt) {
230
+ let value, tag;
231
+ if (type === 'duration') {
232
+ const startingTimestamp = data.expiresAt - data.expiresMs;
233
+ const endingTimestamp = useUpdatedAt ? data.updatedAt : Date.now();
234
+ value = endingTimestamp - startingTimestamp;
235
+ tag = 'Session/Duration/Ms';
236
+ }
237
+ if (type === 'expired') tag = 'Session/Expired/Seen';
238
+ if (type === 'inactive') tag = 'Session/Inactive/Seen';
239
+ if (tag) handle(SUPPORTABILITY_METRIC_CHANNEL, [tag, value], undefined, FEATURE_NAMES.metrics, this.ee);
240
+ }
241
+
242
+ /**
243
+ * @param {number} futureMs - The number of ms to use to generate a future timestamp
244
+ * @returns {number}
245
+ */
246
+ getFutureTimestamp(futureMs) {
247
+ return Date.now() + futureMs;
248
+ }
249
+ syncCustomAttribute(key, value) {
250
+ if (!isBrowserScope) return;
251
+ if (value === null) {
252
+ const curr = this.read();
253
+ if (curr.custom) {
254
+ delete curr.custom[key];
255
+ this.write({
256
+ ...curr
257
+ });
258
+ }
259
+ } else {
260
+ const curr = this.read();
261
+ this.custom = {
262
+ ...(curr?.custom || {}),
263
+ [key]: value
264
+ };
265
+ this.write({
266
+ ...curr,
267
+ custom: this.custom
268
+ });
269
+ }
270
+ }
271
+ }