@newrelic/browser-agent 1.234.0 → 1.235.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 (196) hide show
  1. package/README.md +1 -1
  2. package/dist/cjs/common/constants/env.cdn.js +1 -1
  3. package/dist/cjs/common/constants/env.npm.js +1 -1
  4. package/dist/cjs/common/constants/shared-channel.js +19 -0
  5. package/dist/cjs/common/harvest/harvest-scheduler.js +21 -5
  6. package/dist/cjs/common/session/{session-entity.test.js → session-entity.component-test.js} +79 -42
  7. package/dist/cjs/common/session/session-entity.js +19 -11
  8. package/dist/cjs/common/timer/interaction-timer.js +1 -1
  9. package/dist/cjs/common/url/canonicalize-url.test.js +26 -30
  10. package/dist/cjs/common/util/data-size.test.js +37 -20
  11. package/dist/cjs/common/util/feature-flags.js +23 -12
  12. package/dist/cjs/common/util/feature-flags.test.js +84 -0
  13. package/dist/cjs/common/util/global-scope.js +1 -32
  14. package/dist/cjs/common/util/global-scope.test.js +72 -0
  15. package/dist/cjs/common/util/obfuscate.component-test.js +129 -0
  16. package/dist/cjs/common/util/obfuscate.js +2 -2
  17. package/dist/cjs/common/util/submit-data.js +3 -3
  18. package/dist/cjs/common/util/submit-data.test.js +145 -121
  19. package/dist/cjs/common/wrap/wrap-raf.js +1 -1
  20. package/dist/cjs/common/wrap/wrap-timer.js +1 -1
  21. package/dist/cjs/features/jserrors/aggregate/index.js +4 -0
  22. package/dist/cjs/features/jserrors/instrument/index.js +2 -15
  23. package/dist/cjs/features/session_replay/aggregate/index.component-test.js +457 -0
  24. package/dist/cjs/features/session_replay/aggregate/index.js +99 -82
  25. package/dist/cjs/features/session_replay/replay-mode.js +28 -0
  26. package/dist/cjs/features/session_trace/aggregate/index.js +222 -99
  27. package/dist/cjs/features/session_trace/constants.js +1 -3
  28. package/dist/cjs/features/session_trace/instrument/index.js +0 -16
  29. package/dist/cjs/features/spa/constants.js +0 -1
  30. package/dist/cjs/features/utils/agent-session.js +20 -36
  31. package/dist/cjs/features/utils/agent-session.test.js +211 -0
  32. package/dist/cjs/features/utils/aggregate-base.js +7 -12
  33. package/dist/cjs/features/utils/aggregate-base.test.js +110 -0
  34. package/dist/cjs/features/utils/feature-base.test.js +42 -0
  35. package/dist/cjs/features/utils/handler-cache.js +28 -23
  36. package/dist/cjs/features/utils/handler-cache.test.js +53 -0
  37. package/dist/cjs/features/utils/instrument-base.js +58 -39
  38. package/dist/cjs/features/utils/instrument-base.test.js +179 -0
  39. package/dist/cjs/features/utils/lazy-feature-loader.test.js +30 -0
  40. package/dist/cjs/loaders/agent.js +0 -1
  41. package/dist/cjs/loaders/api/api.js +1 -1
  42. package/dist/cjs/loaders/features/featureDependencies.js +2 -0
  43. package/dist/esm/common/constants/env.cdn.js +1 -1
  44. package/dist/esm/common/constants/env.npm.js +1 -1
  45. package/dist/esm/common/constants/shared-channel.js +12 -0
  46. package/dist/esm/common/harvest/harvest-scheduler.js +21 -5
  47. package/dist/esm/common/session/{session-entity.test.js → session-entity.component-test.js} +77 -40
  48. package/dist/esm/common/session/session-entity.js +17 -11
  49. package/dist/esm/common/timer/interaction-timer.js +1 -1
  50. package/dist/esm/common/url/canonicalize-url.test.js +25 -29
  51. package/dist/esm/common/util/data-size.test.js +35 -20
  52. package/dist/esm/common/util/feature-flags.js +23 -12
  53. package/dist/esm/common/util/feature-flags.test.js +80 -0
  54. package/dist/esm/common/util/global-scope.js +1 -29
  55. package/dist/esm/common/util/global-scope.test.js +70 -0
  56. package/dist/esm/common/util/obfuscate.component-test.js +125 -0
  57. package/dist/esm/common/util/obfuscate.js +2 -2
  58. package/dist/esm/common/util/submit-data.js +3 -3
  59. package/dist/esm/common/util/submit-data.test.js +143 -121
  60. package/dist/esm/common/wrap/wrap-raf.js +1 -1
  61. package/dist/esm/common/wrap/wrap-timer.js +1 -1
  62. package/dist/esm/features/jserrors/aggregate/index.js +4 -0
  63. package/dist/esm/features/jserrors/instrument/index.js +2 -15
  64. package/dist/esm/features/session_replay/aggregate/index.component-test.js +453 -0
  65. package/dist/esm/features/session_replay/aggregate/index.js +92 -78
  66. package/dist/esm/features/session_replay/replay-mode.js +23 -0
  67. package/dist/esm/features/session_trace/aggregate/index.js +223 -100
  68. package/dist/esm/features/session_trace/constants.js +0 -1
  69. package/dist/esm/features/session_trace/instrument/index.js +1 -17
  70. package/dist/esm/features/spa/constants.js +0 -1
  71. package/dist/esm/features/utils/agent-session.js +21 -37
  72. package/dist/esm/features/utils/agent-session.test.js +207 -0
  73. package/dist/esm/features/utils/aggregate-base.js +7 -12
  74. package/dist/esm/features/utils/aggregate-base.test.js +108 -0
  75. package/dist/esm/features/utils/feature-base.test.js +40 -0
  76. package/dist/esm/features/utils/handler-cache.js +28 -23
  77. package/dist/esm/features/utils/handler-cache.test.js +51 -0
  78. package/dist/esm/features/utils/instrument-base.js +58 -39
  79. package/dist/esm/features/utils/instrument-base.test.js +175 -0
  80. package/dist/esm/features/utils/lazy-feature-loader.test.js +29 -0
  81. package/dist/esm/loaders/agent.js +0 -1
  82. package/dist/esm/loaders/api/api.js +2 -2
  83. package/dist/esm/loaders/features/featureDependencies.js +2 -0
  84. package/dist/types/common/constants/shared-channel.d.ts +5 -0
  85. package/dist/types/common/constants/shared-channel.d.ts.map +1 -0
  86. package/dist/types/common/harvest/harvest-scheduler.component-test.d.ts +2 -0
  87. package/dist/types/common/harvest/harvest-scheduler.component-test.d.ts.map +1 -0
  88. package/dist/types/common/harvest/harvest-scheduler.d.ts +4 -0
  89. package/dist/types/common/harvest/harvest-scheduler.d.ts.map +1 -1
  90. package/dist/types/common/harvest/harvest.component-test.d.ts +2 -0
  91. package/dist/types/common/harvest/harvest.component-test.d.ts.map +1 -0
  92. package/dist/types/common/session/session-entity.component-test.d.ts +2 -0
  93. package/dist/types/common/session/session-entity.component-test.d.ts.map +1 -0
  94. package/dist/types/common/session/session-entity.d.ts +9 -5
  95. package/dist/types/common/session/session-entity.d.ts.map +1 -1
  96. package/dist/types/common/timer/interaction-timer.component-test.d.ts +2 -0
  97. package/dist/types/common/timer/interaction-timer.component-test.d.ts.map +1 -0
  98. package/dist/types/common/url/encode.component-test.d.ts +2 -0
  99. package/dist/types/common/url/encode.component-test.d.ts.map +1 -0
  100. package/dist/types/common/url/protocol.component-test.d.ts +2 -0
  101. package/dist/types/common/url/protocol.component-test.d.ts.map +1 -0
  102. package/dist/types/common/util/feature-flags.d.ts +1 -0
  103. package/dist/types/common/util/feature-flags.d.ts.map +1 -1
  104. package/dist/types/common/util/global-scope.d.ts +0 -9
  105. package/dist/types/common/util/global-scope.d.ts.map +1 -1
  106. package/dist/types/common/util/obfuscate.component-test.d.ts +2 -0
  107. package/dist/types/common/util/obfuscate.component-test.d.ts.map +1 -0
  108. package/dist/types/features/jserrors/aggregate/index.d.ts +1 -0
  109. package/dist/types/features/jserrors/aggregate/index.d.ts.map +1 -1
  110. package/dist/types/features/session_replay/aggregate/index.component-test.d.ts +2 -0
  111. package/dist/types/features/session_replay/aggregate/index.component-test.d.ts.map +1 -0
  112. package/dist/types/features/session_replay/aggregate/index.d.ts +14 -5
  113. package/dist/types/features/session_replay/aggregate/index.d.ts.map +1 -1
  114. package/dist/types/features/session_replay/instrument/index.d.ts.map +1 -1
  115. package/dist/types/features/session_replay/replay-mode.d.ts +9 -0
  116. package/dist/types/features/session_replay/replay-mode.d.ts.map +1 -0
  117. package/dist/types/features/session_trace/aggregate/index.d.ts +21 -3
  118. package/dist/types/features/session_trace/aggregate/index.d.ts.map +1 -1
  119. package/dist/types/features/session_trace/constants.d.ts +0 -1
  120. package/dist/types/features/session_trace/constants.d.ts.map +1 -1
  121. package/dist/types/features/session_trace/instrument/index.d.ts +0 -2
  122. package/dist/types/features/session_trace/instrument/index.d.ts.map +1 -1
  123. package/dist/types/features/spa/constants.d.ts.map +1 -1
  124. package/dist/types/features/utils/agent-session.d.ts.map +1 -1
  125. package/dist/types/features/utils/aggregate-base.d.ts +6 -1
  126. package/dist/types/features/utils/aggregate-base.d.ts.map +1 -1
  127. package/dist/types/features/utils/handler-cache.d.ts +12 -11
  128. package/dist/types/features/utils/handler-cache.d.ts.map +1 -1
  129. package/dist/types/features/utils/instrument-base.d.ts +17 -1
  130. package/dist/types/features/utils/instrument-base.d.ts.map +1 -1
  131. package/dist/types/loaders/agent.d.ts.map +1 -1
  132. package/dist/types/loaders/features/featureDependencies.d.ts.map +1 -1
  133. package/package.json +9 -7
  134. package/src/common/constants/shared-channel.js +13 -0
  135. package/src/common/harvest/harvest-scheduler.js +17 -6
  136. package/src/common/session/{session-entity.test.js → session-entity.component-test.js} +70 -47
  137. package/src/common/session/session-entity.js +15 -12
  138. package/src/common/timer/interaction-timer.js +1 -1
  139. package/src/common/url/canonicalize-url.test.js +32 -21
  140. package/src/common/util/data-size.test.js +27 -20
  141. package/src/common/util/feature-flags.js +24 -12
  142. package/src/common/util/feature-flags.test.js +98 -0
  143. package/src/common/util/global-scope.js +0 -26
  144. package/src/common/util/global-scope.test.js +87 -0
  145. package/src/common/util/obfuscate.component-test.js +173 -0
  146. package/src/common/util/obfuscate.js +2 -2
  147. package/src/common/util/submit-data.js +3 -3
  148. package/src/common/util/submit-data.test.js +123 -115
  149. package/src/common/wrap/wrap-raf.js +1 -1
  150. package/src/common/wrap/wrap-timer.js +1 -1
  151. package/src/features/jserrors/aggregate/index.js +5 -0
  152. package/src/features/jserrors/instrument/index.js +2 -15
  153. package/src/features/session_replay/aggregate/index.component-test.js +368 -0
  154. package/src/features/session_replay/aggregate/index.js +96 -71
  155. package/src/features/session_replay/instrument/index.js +0 -1
  156. package/src/features/session_replay/replay-mode.js +23 -0
  157. package/src/features/session_trace/aggregate/index.js +198 -79
  158. package/src/features/session_trace/constants.js +0 -1
  159. package/src/features/session_trace/instrument/index.js +2 -19
  160. package/src/features/spa/constants.js +0 -1
  161. package/src/features/utils/agent-session.js +22 -34
  162. package/src/features/utils/agent-session.test.js +194 -0
  163. package/src/features/utils/aggregate-base.js +12 -9
  164. package/src/features/utils/aggregate-base.test.js +122 -0
  165. package/src/features/utils/feature-base.test.js +45 -0
  166. package/src/features/utils/handler-cache.js +29 -23
  167. package/src/features/utils/handler-cache.test.js +72 -0
  168. package/src/features/utils/instrument-base.js +45 -29
  169. package/src/features/utils/instrument-base.test.js +190 -0
  170. package/src/features/utils/lazy-feature-loader.test.js +37 -0
  171. package/src/loaders/agent.js +0 -1
  172. package/src/loaders/api/api.js +2 -2
  173. package/src/loaders/features/featureDependencies.js +2 -0
  174. package/dist/cjs/common/storage/local-memory.js +0 -35
  175. package/dist/cjs/common/storage/local-memory.test.js +0 -20
  176. package/dist/esm/common/storage/local-memory.js +0 -28
  177. package/dist/esm/common/storage/local-memory.test.js +0 -18
  178. package/dist/types/common/storage/local-memory.d.ts +0 -8
  179. package/dist/types/common/storage/local-memory.d.ts.map +0 -1
  180. package/src/common/storage/local-memory.js +0 -30
  181. package/src/common/storage/local-memory.test.js +0 -19
  182. /package/dist/cjs/common/harvest/{harvest-scheduler.test.js → harvest-scheduler.component-test.js} +0 -0
  183. /package/dist/cjs/common/harvest/{harvest.test.js → harvest.component-test.js} +0 -0
  184. /package/dist/cjs/common/timer/{interaction-timer.test.js → interaction-timer.component-test.js} +0 -0
  185. /package/dist/cjs/common/url/{encode.test.js → encode.component-test.js} +0 -0
  186. /package/dist/cjs/common/url/{protocol.test.js → protocol.component-test.js} +0 -0
  187. /package/dist/esm/common/harvest/{harvest-scheduler.test.js → harvest-scheduler.component-test.js} +0 -0
  188. /package/dist/esm/common/harvest/{harvest.test.js → harvest.component-test.js} +0 -0
  189. /package/dist/esm/common/timer/{interaction-timer.test.js → interaction-timer.component-test.js} +0 -0
  190. /package/dist/esm/common/url/{encode.test.js → encode.component-test.js} +0 -0
  191. /package/dist/esm/common/url/{protocol.test.js → protocol.component-test.js} +0 -0
  192. /package/src/common/harvest/{harvest-scheduler.test.js → harvest-scheduler.component-test.js} +0 -0
  193. /package/src/common/harvest/{harvest.test.js → harvest.component-test.js} +0 -0
  194. /package/src/common/timer/{interaction-timer.test.js → interaction-timer.component-test.js} +0 -0
  195. /package/src/common/url/{encode.test.js → encode.component-test.js} +0 -0
  196. /package/src/common/url/{protocol.test.js → protocol.component-test.js} +0 -0
@@ -1 +1 @@
1
- {"version":3,"file":"aggregate-base.d.ts","sourceRoot":"","sources":["../../../../src/features/utils/aggregate-base.js"],"names":[],"mappings":"AAMA;IACE,4BAGC;IAED,gDAUC;IAED;;;OAGG;IACH,2BAqBC;CACF;4BAjD2B,gBAAgB"}
1
+ {"version":3,"file":"aggregate-base.d.ts","sourceRoot":"","sources":["../../../../src/features/utils/aggregate-base.js"],"names":[],"mappings":"AAMA;IACE,4BAGC;IAED;;;;OAIG;IACH,yBAHW,MAAM,EAAE,kBAWlB;IAED;;;OAGG;IACH,2BAqBC;CACF;4BApD2B,gBAAgB"}
@@ -3,20 +3,21 @@
3
3
  */
4
4
  export class HandlerCache {
5
5
  /**
6
- * Wrap callback functions with this method to defer their execution until a decision has been reached
7
- * @param {Function} handler
8
- * @returns {void}
9
- */
6
+ * Wrap callback functions with this method to defer their execution until a decision has been reached
7
+ * @param {Function} handler
8
+ * @returns {void}
9
+ */
10
10
  settle(handler: Function): void;
11
11
  /**
12
- * Make a decision about what to do with the cache of callbacks.
13
- * --- if true: tell the handlerCache that its ok to immediately execute the callbacks that are triggered by the ee from this moment on
14
- * and execute all the storage callbacks saved up in the handlerCache ---
15
- * --- if false: tell the handlerCache not to execute any of the storage callbacks
16
- * and wipe out all the storage callbacks saved up in the handlerCache
17
- * @param {boolean} decision
18
- */
12
+ * Make a decision about what to do with the cache of callbacks.
13
+ * --- if true: tell the handlerCache that its ok to immediately execute the callbacks that are triggered by the ee from this moment on
14
+ * and execute all the storage callbacks saved up in the handlerCache ---
15
+ * --- if false: tell the handlerCache not to execute any of the storage callbacks
16
+ * and wipe out all the storage callbacks saved up in the handlerCache
17
+ * @param {boolean} decision
18
+ */
19
19
  decide(decision: boolean): void;
20
+ permanentlyDecide(decision: any): void;
20
21
  #private;
21
22
  }
22
23
  //# sourceMappingURL=handler-cache.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"handler-cache.d.ts","sourceRoot":"","sources":["../../../../src/features/utils/handler-cache.js"],"names":[],"mappings":"AAAA;;GAEG;AACH;IA6BE;;;;SAIK;IACL,2BAFe,IAAI,CAMlB;IAED;;;;;;;SAOK;IACL,iBAFa,OAAO,QAOnB;;CACF"}
1
+ {"version":3,"file":"handler-cache.d.ts","sourceRoot":"","sources":["../../../../src/features/utils/handler-cache.js"],"names":[],"mappings":"AAAA;;GAEG;AACH;IA6BE;;;;OAIG;IACH,2BAFa,IAAI,CAMhB;IAED;;;;;;;OAOG;IACH,iBAFW,OAAO,QAOjB;IAED,uCAIC;;CACF"}
@@ -13,10 +13,19 @@ export class InstrumentBase extends FeatureBase {
13
13
  * immediately. Primarily useful for fine-grained control in tests.
14
14
  */
15
15
  constructor(agentIdentifier: string, aggregator: Aggregator, featureName: string, auto?: boolean | undefined);
16
- hasAggregator: boolean;
17
16
  auto: boolean;
18
17
  /** @type {Function | undefined} This should be set by any derived Instrument class if it has things to do when feature fails or is killed. */
19
18
  abortHandler: Function | undefined;
19
+ /**
20
+ * @type {Class} Holds the reference to the feature's aggregate module counterpart, if and after it has been initialized. This may not be assigned until after page loads!
21
+ * The only purpose of this for now is to expose it to the NREUM interface, as the feature's instrument instance is already exposed.
22
+ */
23
+ featAggregate: Class;
24
+ /**
25
+ * @type {Promise} Assigned immediately after @see importAggregator runs. Serves as a signal for when the inner async fn finishes execution. Useful for features to await
26
+ * one another if there are inter-features dependencies.
27
+ */
28
+ onAggregateImported: Promise<any>;
20
29
  /**
21
30
  * Lazy-load the latter part of the feature: its aggregator. This method is called by the first part of the feature
22
31
  * (the instrumentation) when instrumentation is complete.
@@ -24,6 +33,13 @@ export class InstrumentBase extends FeatureBase {
24
33
  * @returns void
25
34
  */
26
35
  importAggregator(argsObjFromInstrument?: Object | undefined): void;
36
+ /**
37
+ * Make a determination if an aggregate class should even be imported
38
+ * @param {string} featureName
39
+ * @param {SessionEntity} session
40
+ * @returns
41
+ */
42
+ shouldImportAgg(featureName: string, session: SessionEntity): boolean;
27
43
  }
28
44
  import { FeatureBase } from './feature-base';
29
45
  //# sourceMappingURL=instrument-base.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"instrument-base.d.ts","sourceRoot":"","sources":["../../../../src/features/utils/instrument-base.js"],"names":[],"mappings":"AAcA;;;GAGG;AACH;IACE;;;;;;;;OAQG;IACH,6BAPW,MAAM,uCAEN,MAAM,8BAchB;IAPC,uBAA0B;IAC1B,cAAgB;IAEhB,8IAA8I;IAC9I,cADW,WAAW,SAAS,CACd;IAKnB;;;;;OAKG;IACH,mEAyCC;CACF;4BAhF2B,gBAAgB"}
1
+ {"version":3,"file":"instrument-base.d.ts","sourceRoot":"","sources":["../../../../src/features/utils/instrument-base.js"],"names":[],"mappings":"AAcA;;;GAGG;AACH;IACE;;;;;;;;OAQG;IACH,6BAPW,MAAM,uCAEN,MAAM,8BAuBhB;IAhBC,cAAgB;IAEhB,8IAA8I;IAC9I,cADW,WAAW,SAAS,CACd;IACjB;;;MAGE;IACF,qBAAkB;IAClB;;;MAGE;IACF,kCAAwB;IAK1B;;;;;OAKG;IACH,mEA4CC;IAED;;;;;KAKC;IACD,6BAJS,MAAM,mCAed;CACF;4BA/G2B,gBAAgB"}
@@ -1 +1 @@
1
- {"version":3,"file":"agent.d.ts","sourceRoot":"","sources":["../../../src/loaders/agent.js"],"names":[],"mappings":"AAeA;;;GAGG;AACH;IACE,oDAeC;IAdC,wBAAsC;IACtC,6BAAiF;IACjF,aAAkB;IAElB,0BAAsD;IAYxD;;;;;MAOC;IAED,2BAgCC;CACF;2BAvE0B,gCAAgC"}
1
+ {"version":3,"file":"agent.d.ts","sourceRoot":"","sources":["../../../src/loaders/agent.js"],"names":[],"mappings":"AAeA;;;GAGG;AACH;IACE,oDAcC;IAbC,wBAAsC;IACtC,6BAAiF;IACjF,aAAkB;IAElB,0BAAsD;IAWxD;;;;;MAOC;IAED,2BAgCC;CACF;2BAtE0B,gCAAgC"}
@@ -1 +1 @@
1
- {"version":3,"file":"featureDependencies.d.ts","sourceRoot":"","sources":["../../../../src/loaders/features/featureDependencies.js"],"names":[],"mappings":"AAEA,kEAWC"}
1
+ {"version":3,"file":"featureDependencies.d.ts","sourceRoot":"","sources":["../../../../src/loaders/features/featureDependencies.js"],"names":[],"mappings":"AAEA,kEAaC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@newrelic/browser-agent",
3
- "version": "1.234.0",
3
+ "version": "1.235.0",
4
4
  "private": false,
5
5
  "author": "New Relic Browser Agent Team <browser-agent@newrelic.com>",
6
6
  "description": "Tests for the New Relic JavaScript agent",
@@ -89,9 +89,8 @@
89
89
  "scripts": {
90
90
  "wdio": "node --max-old-space-size=8192 tools/wdio/bin/cli.js",
91
91
  "start": "npm-run-all --parallel cdn:watch test-server",
92
- "test": "jest",
93
- "test:watch": "jest --watch",
94
- "test:coverage": "jest --coverage",
92
+ "test:unit": "jest",
93
+ "test:component": "jest --config jest.component-config.js",
95
94
  "build:all": "npm run cdn:build:local && npm run build:npm && npm run tools:test-builds",
96
95
  "build:npm": "npm run npm:build:esm && npm run npm:build:cjs && npm run npm:build:types && npm run npm:pack",
97
96
  "build:browser-tests": "npm --prefix ./tools/test-builds/browser-tests run build",
@@ -196,10 +195,13 @@
196
195
  "gzip-size": "^7.0.0",
197
196
  "html-webpack-plugin": "^5.5.0",
198
197
  "husky": "^8.0.0",
199
- "jest": "^28.1.1",
200
- "jest-environment-jsdom": "28.1.1",
198
+ "istanbul-lib-coverage": "^3.2.0",
199
+ "istanbul-lib-instrument": "^5.2.1",
200
+ "istanbul-lib-report": "^3.0.0",
201
+ "istanbul-reports": "^3.1.5",
202
+ "jest": "^29.5.0",
203
+ "jest-environment-jsdom": "29.5.0",
201
204
  "jest-extended": "^3.2.4",
202
- "jquery": "1.11.3",
203
205
  "jung": "^2.1.0",
204
206
  "just-debounce": "^1.0.0",
205
207
  "mime-types": "^2.1.11",
@@ -0,0 +1,13 @@
1
+ /**
2
+ * @file Keeps an object alive that is passed to all feature aggregate modules.
3
+ * The purpose is to have a way for communication and signals to relay across features at runtime.
4
+ * This object can hold any arbitrary values and should be treated as on-the-fly dynamic.
5
+ */
6
+
7
+ let onReplayReady
8
+ const sessionReplayInitialized = new Promise(resolve => onReplayReady = resolve)
9
+
10
+ export const sharedChannel = Object.freeze({
11
+ onReplayReady,
12
+ sessionReplayInitialized
13
+ })
@@ -8,6 +8,7 @@ import { SharedContext } from '../context/shared-context'
8
8
  import { Harvest, getSubmitMethod } from './harvest'
9
9
  import { subscribeToEOL } from '../unload/eol'
10
10
  import { getConfigurationValue } from '../config/config'
11
+ import { SESSION_EVENTS } from '../session/session-entity'
11
12
 
12
13
  /**
13
14
  * Periodically invokes harvest calls and handles retries
@@ -37,10 +38,16 @@ export class HarvestScheduler extends SharedContext {
37
38
  // unload if EOL mechanism fires
38
39
  subscribeToEOL(this.unload.bind(this), getConfigurationValue(this.sharedContext.agentIdentifier, 'allow_bfcache')) // TO DO: remove feature flag after rls stable
39
40
 
40
- // unload if session resets
41
- this.sharedContext?.ee.on('session-reset', this.unload.bind(this))
41
+ /* Flush all buffered data if session resets and give up retries. This should be synchronous to ensure that the correct `session` value is sent.
42
+ Since session-reset generates a new session ID and the ID is grabbed at send-time, any delays or retries would cause the payload to be sent under
43
+ the wrong session ID. */
44
+ this.sharedContext?.ee.on(SESSION_EVENTS.RESET, () => this.runHarvest({ forceNoRetry: true }))
42
45
  }
43
46
 
47
+ /**
48
+ * This function is only meant for the last outgoing harvest cycle of a page. It trickles down to using sendBeacon, which should not be used
49
+ * to send payloads while the page is still active, due to limitations on how much data can be buffered in the API at any one time.
50
+ */
44
51
  unload () {
45
52
  if (this.aborted) return
46
53
  // If opts.onUnload is defined, these are special actions to execute before attempting to send the final payload.
@@ -118,7 +125,7 @@ export class HarvestScheduler extends SharedContext {
118
125
  payload,
119
126
  opts,
120
127
  submitMethod,
121
- cbFinished: onHarvestFinished,
128
+ cbFinished: cbRanAfterSend,
122
129
  customUrl: this.opts.customUrl,
123
130
  raw: this.opts.raw
124
131
  })
@@ -129,9 +136,13 @@ export class HarvestScheduler extends SharedContext {
129
136
  }
130
137
  return
131
138
 
132
- function onHarvestFinished (result) {
133
- if (result.blocked) scheduler.onHarvestBlocked(opts, result)
134
- else scheduler.onHarvestFinished(opts, result)
139
+ /**
140
+ * This is executed immediately after harvest sends the data via XHR, or if there's nothing to send. Note that this excludes on unloading / sendBeacon.
141
+ * @param {Object} result
142
+ */
143
+ function cbRanAfterSend (result) {
144
+ if (opts?.forceNoRetry) result.retry = false // discard unsent data rather than re-queuing for next harvest attempt
145
+ scheduler.onHarvestFinished(opts, result)
135
146
  }
136
147
  }
137
148
 
@@ -1,12 +1,39 @@
1
- import { LocalMemory } from '../storage/local-memory'
2
- import { LocalStorage } from '../storage/local-storage'
3
-
4
1
  import { PREFIX } from './constants'
5
2
  import { SessionEntity } from './session-entity'
6
3
 
7
4
  const agentIdentifier = 'test_agent_identifier'
8
5
  const key = 'test_key'
9
6
  const value = 'test_value'
7
+ class LocalMemory {
8
+ constructor (initialState = {}) {
9
+ this.state = initialState
10
+ }
11
+
12
+ get (key) {
13
+ try {
14
+ return this.state[key]
15
+ } catch (err) {
16
+ return ''
17
+ }
18
+ }
19
+
20
+ set (key, value) {
21
+ try {
22
+ if (value === undefined || value === null) return this.remove(key)
23
+ this.state[key] = value
24
+ } catch (err) {
25
+ return
26
+ }
27
+ }
28
+
29
+ remove (key) {
30
+ try {
31
+ delete this.state[key]
32
+ } catch (err) {
33
+ return
34
+ }
35
+ }
36
+ }
10
37
 
11
38
  jest.mock('../timer/timer')
12
39
  jest.mock('../timer/interaction-timer')
@@ -23,9 +50,11 @@ jest.mock('../util/global-scope', () => ({
23
50
  }
24
51
  }))
25
52
 
53
+ let storage
26
54
  beforeEach(() => {
27
55
  jest.restoreAllMocks()
28
56
  mockBrowserScope.mockReturnValue(true)
57
+ storage = new LocalMemory()
29
58
  })
30
59
 
31
60
  describe('constructor', () => {
@@ -36,7 +65,7 @@ describe('constructor', () => {
36
65
  })
37
66
 
38
67
  test('top-level properties are set and exposed', () => {
39
- const session = new SessionEntity({ agentIdentifier, key })
68
+ const session = new SessionEntity({ agentIdentifier, key, storage })
40
69
  expect(session).toMatchObject({
41
70
  agentIdentifier: expect.any(String),
42
71
  key: expect.any(String),
@@ -51,119 +80,113 @@ describe('constructor', () => {
51
80
  expiresAt: expect.any(Number),
52
81
  inactiveAt: expect.any(Number),
53
82
  sessionReplay: expect.any(Number),
54
- sessionTraceActive: expect.any(Boolean)
83
+ sessionTraceMode: expect.any(Number)
55
84
  })
56
85
  })
57
86
  })
58
87
 
59
88
  test('can use sane defaults', () => {
60
- const session = new SessionEntity({ agentIdentifier, key })
89
+ const session = new SessionEntity({ agentIdentifier, key, storage })
61
90
  expect(session.state).toEqual(expect.objectContaining({
62
91
  value: expect.any(String),
63
92
  expiresAt: expect.any(Number),
64
93
  inactiveAt: expect.any(Number),
65
94
  updatedAt: expect.any(Number),
66
95
  sessionReplay: expect.any(Number),
67
- sessionTraceActive: expect.any(Boolean)
96
+ sessionTraceMode: expect.any(Number)
68
97
  }))
69
98
  })
70
99
 
71
- test('Workers are forced to use local memory', () => {
72
- mockBrowserScope.mockReturnValueOnce(false)
73
- const session = new SessionEntity({ agentIdentifier, key, storageAPI: new LocalStorage() })
74
- expect(session.storage instanceof LocalMemory).toEqual(true)
75
- })
76
-
77
100
  test('expiresAt is the correct future timestamp - new session', () => {
78
101
  const now = Date.now()
79
102
  jest.setSystemTime(now)
80
- const session = new SessionEntity({ agentIdentifier, key, expiresMs: 100 })
103
+ const session = new SessionEntity({ agentIdentifier, key, storage, expiresMs: 100 })
81
104
  expect(session.state.expiresAt).toEqual(now + 100)
82
105
  })
83
106
 
84
107
  test('expiresAt is the correct future timestamp - existing session', () => {
85
108
  const now = Date.now()
86
109
  jest.setSystemTime(now)
87
- const existingData = new LocalMemory({ [`${PREFIX}_${key}`]: { value, expiresAt: now + 5000, inactiveAt: Infinity, updatedAt: now, sessionReplay: 0, sessionTraceActive: false, custom: {} } })
88
- const session = new SessionEntity({ agentIdentifier, key, expiresMs: 100, storageAPI: existingData })
110
+ const existingData = new LocalMemory({ [`${PREFIX}_${key}`]: { value, expiresAt: now + 5000, inactiveAt: Infinity, updatedAt: now, sessionReplay: 0, sessionTraceMode: 0, custom: {} } })
111
+ const session = new SessionEntity({ agentIdentifier, key, expiresMs: 100, storage: existingData })
89
112
  expect(session.state.expiresAt).toEqual(now + 5000)
90
113
  })
91
114
 
92
115
  test('expiresAt never expires if 0', () => {
93
- const session = new SessionEntity({ agentIdentifier, key, expiresMs: 0 })
116
+ const session = new SessionEntity({ agentIdentifier, key, storage, expiresMs: 0 })
94
117
  expect(session.state.expiresAt).toEqual(Infinity)
95
118
  })
96
119
 
97
120
  test('inactiveAt is the correct future timestamp - new session', () => {
98
121
  const now = Date.now()
99
122
  jest.setSystemTime(now)
100
- const session = new SessionEntity({ agentIdentifier, key, inactiveMs: 100 })
123
+ const session = new SessionEntity({ agentIdentifier, key, storage, inactiveMs: 100 })
101
124
  expect(session.state.inactiveAt).toEqual(now + 100)
102
125
  })
103
126
 
104
127
  test('inactiveAt is the correct future timestamp - existing session', () => {
105
128
  const now = Date.now()
106
129
  jest.setSystemTime(now)
107
- const existingData = new LocalMemory({ [`${PREFIX}_${key}`]: { value, inactiveAt: now + 5000, expiresAt: Infinity, updatedAt: now, sessionReplay: 0, sessionTraceActive: false, custom: {} } })
108
- const session = new SessionEntity({ agentIdentifier, key, inactiveMs: 100, storageAPI: existingData })
130
+ const existingData = new LocalMemory({ [`${PREFIX}_${key}`]: { value, inactiveAt: now + 5000, expiresAt: Infinity, updatedAt: now, sessionReplay: 0, sessionTraceMode: 0, custom: {} } })
131
+ const session = new SessionEntity({ agentIdentifier, key, inactiveMs: 100, storage: existingData })
109
132
  expect(session.state.inactiveAt).toEqual(now + 5000)
110
133
  })
111
134
 
112
135
  test('inactiveAt never expires if 0', () => {
113
- const session = new SessionEntity({ agentIdentifier, key, inactiveMs: 0 })
136
+ const session = new SessionEntity({ agentIdentifier, key, storage, inactiveMs: 0 })
114
137
  expect(session.state.inactiveAt).toEqual(Infinity)
115
138
  })
116
139
 
117
140
  test('should handle isNew', () => {
118
- const newSession = new SessionEntity({ agentIdentifier, key, expiresMs: 10 })
141
+ const newSession = new SessionEntity({ agentIdentifier, key, storage, expiresMs: 10 })
119
142
  expect(newSession.isNew).toBeTruthy()
120
143
 
121
- const storageAPI = new LocalMemory({ [`${PREFIX}_${key}`]: { value, expiresAt: Infinity, inactiveAt: Infinity, updatedAt: Date.now(), sessionReplay: 0, sessionTraceActive: false, custom: {} } })
122
- const existingSession = new SessionEntity({ agentIdentifier, key, expiresMs: 10, storageAPI })
144
+ const newStorage = new LocalMemory({ [`${PREFIX}_${key}`]: { value, expiresAt: Infinity, inactiveAt: Infinity, updatedAt: Date.now(), sessionReplay: 0, sessionTraceMode: 0, custom: {} } })
145
+ const existingSession = new SessionEntity({ agentIdentifier, key, expiresMs: 10, storage: newStorage })
123
146
  expect(existingSession.isNew).toBeFalsy()
124
147
  })
125
148
 
126
149
  test('invalid stored values sets new defaults', () => {
127
150
  // missing required fields
128
- const storageAPI = new LocalMemory({ [`${PREFIX}_${key}`]: { invalid_fields: true } })
129
- const session = new SessionEntity({ agentIdentifier, key, storageAPI })
151
+ const storage = new LocalMemory({ [`${PREFIX}_${key}`]: { invalid_fields: true } })
152
+ const session = new SessionEntity({ agentIdentifier, key, storage })
130
153
  expect(session.state).toEqual(expect.objectContaining({
131
154
  value: expect.any(String),
132
155
  expiresAt: expect.any(Number),
133
156
  inactiveAt: expect.any(Number),
134
157
  updatedAt: expect.any(Number),
135
158
  sessionReplay: expect.any(Number),
136
- sessionTraceActive: expect.any(Boolean)
159
+ sessionTraceMode: expect.any(Number)
137
160
  }))
138
161
  })
139
162
 
140
163
  test('expired expiresAt value in storage sets new defaults', () => {
141
164
  const now = Date.now()
142
165
  jest.setSystemTime(now)
143
- const storageAPI = new LocalMemory({ [`${PREFIX}_${key}`]: { value, expiresAt: now - 100, inactiveAt: Infinity } })
144
- const session = new SessionEntity({ agentIdentifier, key, storageAPI })
166
+ const storage = new LocalMemory({ [`${PREFIX}_${key}`]: { value, expiresAt: now - 100, inactiveAt: Infinity } })
167
+ const session = new SessionEntity({ agentIdentifier, key, storage })
145
168
  expect(session.state).toEqual(expect.objectContaining({
146
169
  value: expect.any(String),
147
170
  expiresAt: expect.any(Number),
148
171
  inactiveAt: expect.any(Number),
149
172
  updatedAt: expect.any(Number),
150
173
  sessionReplay: expect.any(Number),
151
- sessionTraceActive: expect.any(Boolean)
174
+ sessionTraceMode: expect.any(Number)
152
175
  }))
153
176
  })
154
177
 
155
178
  test('expired inactiveAt value in storage sets new defaults', () => {
156
179
  const now = Date.now()
157
180
  jest.setSystemTime(now)
158
- const storageAPI = new LocalMemory({ [`${PREFIX}_${key}`]: { value, inactiveAt: now - 100, expiresAt: Infinity } })
159
- const session = new SessionEntity({ agentIdentifier, key, storageAPI })
181
+ const storage = new LocalMemory({ [`${PREFIX}_${key}`]: { value, inactiveAt: now - 100, expiresAt: Infinity } })
182
+ const session = new SessionEntity({ agentIdentifier, key, storage })
160
183
  expect(session.state).toEqual(expect.objectContaining({
161
184
  value: expect.any(String),
162
185
  expiresAt: expect.any(Number),
163
186
  inactiveAt: expect.any(Number),
164
187
  updatedAt: expect.any(Number),
165
188
  sessionReplay: expect.any(Number),
166
- sessionTraceActive: expect.any(Boolean)
189
+ sessionTraceMode: expect.any(Number)
167
190
  }))
168
191
  })
169
192
  })
@@ -172,7 +195,7 @@ describe('reset()', () => {
172
195
  test('should create new default values when resetting', () => {
173
196
  const now = Date.now()
174
197
  jest.setSystemTime(now)
175
- const session = new SessionEntity({ agentIdentifier, key, expiresMs: 10 })
198
+ const session = new SessionEntity({ agentIdentifier, key, storage, expiresMs: 10 })
176
199
  const sessionVal = session.value
177
200
  expect(session.state.value).toBeTruthy()
178
201
  session.reset()
@@ -183,7 +206,7 @@ describe('reset()', () => {
183
206
  test('custom data should be wiped on reset', () => {
184
207
  const now = Date.now()
185
208
  jest.setSystemTime(now)
186
- const session = new SessionEntity({ agentIdentifier, key, expiresMs: 10 })
209
+ const session = new SessionEntity({ agentIdentifier, key, storage, expiresMs: 10 })
187
210
  session.syncCustomAttribute('test', 123)
188
211
  expect(session.state.custom.test).toEqual(123)
189
212
  expect(session.read().custom.test).toEqual(123)
@@ -197,7 +220,7 @@ describe('reset()', () => {
197
220
 
198
221
  describe('read()', () => {
199
222
  test('"new" sessions get data from read()', () => {
200
- const newSession = new SessionEntity({ agentIdentifier, key, expiresMs: 10 })
223
+ const newSession = new SessionEntity({ agentIdentifier, key, storage, expiresMs: 10 })
201
224
  expect(newSession.isNew).toBeTruthy()
202
225
 
203
226
  expect(newSession.read()).toEqual(expect.objectContaining({
@@ -205,13 +228,13 @@ describe('read()', () => {
205
228
  expiresAt: expect.any(Number),
206
229
  inactiveAt: expect.any(Number),
207
230
  sessionReplay: expect.any(Number),
208
- sessionTraceActive: expect.any(Boolean)
231
+ sessionTraceMode: expect.any(Number)
209
232
  }))
210
233
  })
211
234
 
212
235
  test('"pre-existing" sessions get data from read()', () => {
213
- const storageAPI = new LocalMemory({ [`${PREFIX}_${key}`]: { value, expiresAt: Infinity, inactiveAt: Infinity, updatedAt: Date.now(), sessionReplay: 0, sessionTraceActive: false, custom: {} } })
214
- const session = new SessionEntity({ agentIdentifier, key, storageAPI })
236
+ const storage = new LocalMemory({ [`${PREFIX}_${key}`]: { value, expiresAt: Infinity, inactiveAt: Infinity, updatedAt: Date.now(), sessionReplay: 0, sessionTraceMode: 0, custom: {} } })
237
+ const session = new SessionEntity({ agentIdentifier, key, storage })
215
238
  expect(session.isNew).toBeFalsy()
216
239
  expect(session.read()).toEqual(expect.objectContaining({
217
240
  value,
@@ -223,7 +246,7 @@ describe('read()', () => {
223
246
 
224
247
  describe('write()', () => {
225
248
  test('write() sets data to top-level wrapper', () => {
226
- const session = new SessionEntity({ agentIdentifier, key })
249
+ const session = new SessionEntity({ agentIdentifier, key, storage })
227
250
  expect(session.state.value).not.toEqual(value)
228
251
  expect(session.state.expiresAt).not.toEqual(Infinity)
229
252
  expect(session.state.inactiveAt).not.toEqual(Infinity)
@@ -236,7 +259,7 @@ describe('write()', () => {
236
259
  test('write() sets data that read() can access', () => {
237
260
  const now = Date.now()
238
261
  jest.setSystemTime(now)
239
- const session = new SessionEntity({ agentIdentifier, key })
262
+ const session = new SessionEntity({ agentIdentifier, key, storage })
240
263
  session.write({ ...session.state, value, expiresAt: now + 100, inactiveAt: now + 100 })
241
264
  const read = session.read()
242
265
  expect(read.value).toEqual(value)
@@ -245,7 +268,7 @@ describe('write()', () => {
245
268
  })
246
269
 
247
270
  test('write() does not run with invalid data', () => {
248
- const session = new SessionEntity({ agentIdentifier, key })
271
+ const session = new SessionEntity({ agentIdentifier, key, storage })
249
272
  let out = session.write()
250
273
  expect(out).toEqual(undefined)
251
274
  out = session.write('string')
@@ -263,7 +286,7 @@ describe('refresh()', () => {
263
286
  test('refresh sets inactiveAt to future time', () => {
264
287
  const now = Date.now()
265
288
  jest.setSystemTime(now)
266
- const session = new SessionEntity({ agentIdentifier, key, inactiveMs: 100 })
289
+ const session = new SessionEntity({ agentIdentifier, key, storage, inactiveMs: 100 })
267
290
  expect(session.state.inactiveAt).toEqual(now + 100)
268
291
  jest.setSystemTime(now + 1000)
269
292
  session.refresh()
@@ -273,7 +296,7 @@ describe('refresh()', () => {
273
296
  test('refresh resets the entity if expiresTimer is invalid', () => {
274
297
  const now = Date.now()
275
298
  jest.setSystemTime(now)
276
- const session = new SessionEntity({ agentIdentifier, key, value })
299
+ const session = new SessionEntity({ agentIdentifier, key, storage, value })
277
300
  expect(session.state.value).toEqual(value)
278
301
  session.write({ ...session.state, expiresAt: now - 1 })
279
302
  session.refresh()
@@ -283,7 +306,7 @@ describe('refresh()', () => {
283
306
  test('refresh resets the entity if inactiveTimer is invalid', () => {
284
307
  const now = Date.now()
285
308
  jest.setSystemTime(now)
286
- const session = new SessionEntity({ agentIdentifier, key, value })
309
+ const session = new SessionEntity({ agentIdentifier, key, storage, value })
287
310
  expect(session.state.value).toEqual(value)
288
311
  session.write({ ...session.state, inactiveAt: now - 1 })
289
312
  session.refresh()
@@ -293,7 +316,7 @@ describe('refresh()', () => {
293
316
 
294
317
  describe('syncCustomAttribute()', () => {
295
318
  test('Custom data can be managed by session entity', () => {
296
- const session = new SessionEntity({ agentIdentifier, key })
319
+ const session = new SessionEntity({ agentIdentifier, key, storage })
297
320
 
298
321
  // if custom has never been set, and a "delete" action is triggered, do nothing
299
322
  session.syncCustomAttribute('test', null)
@@ -315,7 +338,7 @@ describe('syncCustomAttribute()', () => {
315
338
 
316
339
  test('Only runs in browser scope', () => {
317
340
  mockBrowserScope.mockReturnValue(false)
318
- const session = new SessionEntity({ agentIdentifier, key })
341
+ const session = new SessionEntity({ agentIdentifier, key, storage })
319
342
  session.syncCustomAttribute('test', 1)
320
343
  expect(session.read().custom?.test).toEqual(undefined)
321
344
  })
@@ -5,7 +5,6 @@ import { ee } from '../event-emitter/contextual-ee'
5
5
  import { Timer } from '../timer/timer'
6
6
  import { isBrowserScope } from '../util/global-scope'
7
7
  import { DEFAULT_EXPIRES_MS, DEFAULT_INACTIVE_MS, PREFIX } from './constants'
8
- import { LocalMemory } from '../storage/local-memory'
9
8
  import { InteractionTimer } from '../timer/interaction-timer'
10
9
  import { wrapEvents } from '../wrap'
11
10
  import { getModeledObject } from '../config/state/configurable'
@@ -13,6 +12,11 @@ import { handle } from '../event-emitter/handle'
13
12
  import { SUPPORTABILITY_METRIC_CHANNEL } from '../../features/metrics/constants'
14
13
  import { FEATURE_NAMES } from '../../loaders/features/features'
15
14
 
15
+ export const MODE = {
16
+ OFF: 0,
17
+ FULL: 1,
18
+ ERROR: 2
19
+ }
16
20
  // this is what can be stored in local storage (not enforced but probably should be)
17
21
  // these values should sync between local storage and the parent class props
18
22
  const model = {
@@ -20,11 +24,10 @@ const model = {
20
24
  inactiveAt: 0,
21
25
  expiresAt: 0,
22
26
  updatedAt: Date.now(),
23
- sessionReplay: 0,
24
- sessionTraceActive: false,
27
+ sessionReplay: MODE.OFF,
28
+ sessionTraceMode: MODE.OFF,
25
29
  custom: {}
26
30
  }
27
-
28
31
  export const SESSION_EVENTS = {
29
32
  PAUSE: 'session-pause',
30
33
  RESET: 'session-reset',
@@ -42,16 +45,16 @@ export class SessionEntity {
42
45
  this.setup(opts)
43
46
  }
44
47
 
45
- setup ({ agentIdentifier, key, value = generateRandomHexString(16), expiresMs = DEFAULT_EXPIRES_MS, inactiveMs = DEFAULT_INACTIVE_MS, storageAPI = new LocalMemory() }) {
46
- if (!agentIdentifier || !key) throw new Error('Missing Required Fields')
47
- if (!isBrowserScope) this.storage = new LocalMemory()
48
- else this.storage = storageAPI
49
-
48
+ setup ({ agentIdentifier, key, storage, value = generateRandomHexString(16), expiresMs = DEFAULT_EXPIRES_MS, inactiveMs = DEFAULT_INACTIVE_MS }) {
49
+ if (!agentIdentifier || !key || !storage) {
50
+ throw new Error(`Missing required field(s):${!agentIdentifier ? ' agentID' : ''}${!key ? ' key' : ''}${!storage ? ' storage' : ''}`)
51
+ }
52
+ this.agentIdentifier = agentIdentifier
53
+ this.storage = storage
50
54
  this.state = {}
51
55
 
52
56
  this.sync(model)
53
57
 
54
- this.agentIdentifier = agentIdentifier
55
58
  // key is intended to act as the k=v pair
56
59
  this.key = key
57
60
  // value is intended to act as the primary value of the k=v pair
@@ -183,7 +186,7 @@ export class SessionEntity {
183
186
  this.sync(data) // update the parent class "state" properties with the local storage values
184
187
  //
185
188
  // TODO - compression would need happen here if we decide to do it
186
- this.storage.set(this.lookupKey, stringify(data))
189
+ this.storage.set(this.lookupKey, stringify(this.state))
187
190
  return data
188
191
  } catch (e) {
189
192
  // storage is inaccessible
@@ -207,7 +210,7 @@ export class SessionEntity {
207
210
  this.setup({
208
211
  agentIdentifier: this.agentIdentifier,
209
212
  key: this.key,
210
- storageAPI: this.storage,
213
+ storage: this.storage,
211
214
  expiresMs: this.expiresMs,
212
215
  inactiveMs: this.inactiveMs
213
216
  })
@@ -44,8 +44,8 @@ export class InteractionTimer extends Timer {
44
44
  if (state === 'hidden') this.pause()
45
45
  // vis change --> visible is treated like a new interaction with the page
46
46
  else {
47
- this.onResume()
48
47
  this.refresh()
48
+ this.onResume() // emit resume event after state updated
49
49
  }
50
50
  }, false, false, this.abortController?.signal)
51
51
  }