@newrelic/browser-agent 1.252.1 → 1.253.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 (203) hide show
  1. package/CHANGELOG.md +10 -0
  2. package/README.md +1 -1
  3. package/dist/cjs/cdn/experimental.js +6 -2
  4. package/dist/cjs/cdn/spa.js +5 -3
  5. package/dist/cjs/common/aggregate/aggregator.js +1 -8
  6. package/dist/cjs/common/config/state/init.js +7 -0
  7. package/dist/cjs/common/constants/env.cdn.js +1 -1
  8. package/dist/cjs/common/constants/env.npm.js +1 -1
  9. package/dist/cjs/common/context/observation-context-manager.js +56 -0
  10. package/dist/cjs/common/event-emitter/contextual-ee.js +12 -9
  11. package/dist/cjs/common/session/constants.js +2 -1
  12. package/dist/cjs/common/timing/nav-timing.js +8 -3
  13. package/dist/cjs/common/timing/now.js +1 -1
  14. package/dist/cjs/common/util/feature-flags.js +1 -1
  15. package/dist/cjs/common/wrap/index.js +0 -7
  16. package/dist/cjs/common/wrap/wrap-events.js +2 -2
  17. package/dist/cjs/common/wrap/wrap-fetch.js +2 -1
  18. package/dist/cjs/common/wrap/wrap-function.js +5 -7
  19. package/dist/cjs/common/wrap/wrap-promise.js +2 -1
  20. package/dist/cjs/features/ajax/aggregate/index.js +34 -16
  21. package/dist/cjs/features/jserrors/aggregate/index.js +77 -66
  22. package/dist/cjs/features/page_view_event/aggregate/index.js +1 -1
  23. package/dist/cjs/features/page_view_event/aggregate/initialized-features.js +1 -0
  24. package/dist/cjs/features/session_replay/aggregate/index.js +96 -94
  25. package/dist/cjs/features/session_replay/constants.js +5 -1
  26. package/dist/cjs/features/session_replay/instrument/index.js +24 -8
  27. package/dist/cjs/features/session_replay/shared/utils.js +26 -0
  28. package/dist/cjs/features/soft_navigations/aggregate/ajax-node.js +50 -0
  29. package/dist/cjs/features/soft_navigations/aggregate/bel-node.js +29 -0
  30. package/dist/cjs/features/soft_navigations/aggregate/index.js +263 -0
  31. package/dist/cjs/features/soft_navigations/aggregate/initial-page-load-interaction.js +62 -0
  32. package/dist/cjs/features/soft_navigations/aggregate/interaction.js +146 -0
  33. package/dist/cjs/features/soft_navigations/constants.js +31 -0
  34. package/dist/cjs/features/soft_navigations/index.js +12 -0
  35. package/dist/cjs/features/soft_navigations/instrument/index.js +79 -0
  36. package/dist/cjs/features/spa/aggregate/index.js +4 -4
  37. package/dist/cjs/features/utils/agent-session.js +2 -1
  38. package/dist/cjs/features/utils/instrument-base.js +6 -9
  39. package/dist/cjs/features/utils/lazy-feature-loader.js +2 -0
  40. package/dist/cjs/loaders/agent-base.js +18 -3
  41. package/dist/cjs/loaders/agent.js +15 -18
  42. package/dist/cjs/loaders/api/api-methods.js +2 -1
  43. package/dist/cjs/loaders/api/api.js +14 -11
  44. package/dist/cjs/loaders/configure/configure.js +4 -1
  45. package/dist/cjs/loaders/features/enabled-features.js +1 -1
  46. package/dist/cjs/loaders/features/features.js +3 -1
  47. package/dist/esm/cdn/experimental.js +5 -2
  48. package/dist/esm/cdn/spa.js +3 -1
  49. package/dist/esm/common/aggregate/aggregator.js +1 -8
  50. package/dist/esm/common/config/state/init.js +7 -0
  51. package/dist/esm/common/constants/env.cdn.js +1 -1
  52. package/dist/esm/common/constants/env.npm.js +1 -1
  53. package/dist/esm/common/context/observation-context-manager.js +49 -0
  54. package/dist/esm/common/event-emitter/contextual-ee.js +12 -9
  55. package/dist/esm/common/session/constants.js +1 -0
  56. package/dist/esm/common/timing/nav-timing.js +8 -3
  57. package/dist/esm/common/timing/now.js +1 -1
  58. package/dist/esm/common/util/feature-flags.js +1 -1
  59. package/dist/esm/common/wrap/index.js +1 -2
  60. package/dist/esm/common/wrap/wrap-events.js +3 -3
  61. package/dist/esm/common/wrap/wrap-fetch.js +3 -2
  62. package/dist/esm/common/wrap/wrap-function.js +4 -5
  63. package/dist/esm/common/wrap/wrap-promise.js +3 -2
  64. package/dist/esm/features/ajax/aggregate/index.js +36 -18
  65. package/dist/esm/features/jserrors/aggregate/index.js +77 -66
  66. package/dist/esm/features/page_view_event/aggregate/index.js +1 -1
  67. package/dist/esm/features/page_view_event/aggregate/initialized-features.js +1 -0
  68. package/dist/esm/features/session_replay/aggregate/index.js +97 -95
  69. package/dist/esm/features/session_replay/constants.js +4 -0
  70. package/dist/esm/features/session_replay/instrument/index.js +25 -9
  71. package/dist/esm/features/session_replay/shared/utils.js +17 -0
  72. package/dist/esm/features/soft_navigations/aggregate/ajax-node.js +43 -0
  73. package/dist/esm/features/soft_navigations/aggregate/bel-node.js +22 -0
  74. package/dist/esm/features/soft_navigations/aggregate/index.js +256 -0
  75. package/dist/esm/features/soft_navigations/aggregate/initial-page-load-interaction.js +55 -0
  76. package/dist/esm/features/soft_navigations/aggregate/interaction.js +140 -0
  77. package/dist/esm/features/soft_navigations/constants.js +25 -0
  78. package/dist/esm/features/soft_navigations/index.js +1 -0
  79. package/dist/esm/features/soft_navigations/instrument/index.js +73 -0
  80. package/dist/esm/features/spa/aggregate/index.js +4 -4
  81. package/dist/esm/features/utils/agent-session.js +2 -1
  82. package/dist/esm/features/utils/instrument-base.js +7 -10
  83. package/dist/esm/features/utils/lazy-feature-loader.js +2 -0
  84. package/dist/esm/loaders/agent-base.js +18 -3
  85. package/dist/esm/loaders/agent.js +15 -18
  86. package/dist/esm/loaders/api/api-methods.js +2 -1
  87. package/dist/esm/loaders/api/api.js +14 -11
  88. package/dist/esm/loaders/configure/configure.js +4 -1
  89. package/dist/esm/loaders/features/enabled-features.js +1 -1
  90. package/dist/esm/loaders/features/features.js +3 -1
  91. package/dist/types/common/aggregate/aggregator.d.ts.map +1 -1
  92. package/dist/types/common/config/state/init.d.ts.map +1 -1
  93. package/dist/types/common/context/event-context.d.ts.map +1 -0
  94. package/dist/types/common/context/observation-context-manager.d.ts +28 -0
  95. package/dist/types/common/context/observation-context-manager.d.ts.map +1 -0
  96. package/dist/types/common/event-emitter/contextual-ee.d.ts +2 -2
  97. package/dist/types/common/event-emitter/contextual-ee.d.ts.map +1 -1
  98. package/dist/types/common/session/constants.d.ts +1 -0
  99. package/dist/types/common/session/constants.d.ts.map +1 -1
  100. package/dist/types/common/timing/nav-timing.d.ts.map +1 -1
  101. package/dist/types/common/wrap/index.d.ts +1 -2
  102. package/dist/types/common/wrap/index.d.ts.map +1 -1
  103. package/dist/types/common/wrap/wrap-fetch.d.ts.map +1 -1
  104. package/dist/types/common/wrap/wrap-function.d.ts +0 -1
  105. package/dist/types/common/wrap/wrap-function.d.ts.map +1 -1
  106. package/dist/types/common/wrap/wrap-promise.d.ts.map +1 -1
  107. package/dist/types/features/ajax/aggregate/index.d.ts.map +1 -1
  108. package/dist/types/features/jserrors/aggregate/index.d.ts +4 -3
  109. package/dist/types/features/jserrors/aggregate/index.d.ts.map +1 -1
  110. package/dist/types/features/page_view_event/aggregate/initialized-features.d.ts.map +1 -1
  111. package/dist/types/features/session_replay/aggregate/index.d.ts +1 -1
  112. package/dist/types/features/session_replay/aggregate/index.d.ts.map +1 -1
  113. package/dist/types/features/session_replay/constants.d.ts +4 -0
  114. package/dist/types/features/session_replay/constants.d.ts.map +1 -1
  115. package/dist/types/features/session_replay/instrument/index.d.ts.map +1 -1
  116. package/dist/types/features/session_replay/shared/utils.d.ts +4 -0
  117. package/dist/types/features/session_replay/shared/utils.d.ts.map +1 -0
  118. package/dist/types/features/soft_navigations/aggregate/ajax-node.d.ts +19 -0
  119. package/dist/types/features/soft_navigations/aggregate/ajax-node.d.ts.map +1 -0
  120. package/dist/types/features/soft_navigations/aggregate/bel-node.d.ts +16 -0
  121. package/dist/types/features/soft_navigations/aggregate/bel-node.d.ts.map +1 -0
  122. package/dist/types/features/soft_navigations/aggregate/index.d.ts +36 -0
  123. package/dist/types/features/soft_navigations/aggregate/index.d.ts.map +1 -0
  124. package/dist/types/features/soft_navigations/aggregate/initial-page-load-interaction.d.ts +12 -0
  125. package/dist/types/features/soft_navigations/aggregate/initial-page-load-interaction.d.ts.map +1 -0
  126. package/dist/types/features/soft_navigations/aggregate/interaction.d.ts +50 -0
  127. package/dist/types/features/soft_navigations/aggregate/interaction.d.ts.map +1 -0
  128. package/dist/types/features/soft_navigations/constants.d.ts +20 -0
  129. package/dist/types/features/soft_navigations/constants.d.ts.map +1 -0
  130. package/dist/types/features/soft_navigations/index.d.ts +2 -0
  131. package/dist/types/features/soft_navigations/index.d.ts.map +1 -0
  132. package/dist/types/features/soft_navigations/instrument/index.d.ts +7 -0
  133. package/dist/types/features/soft_navigations/instrument/index.d.ts.map +1 -0
  134. package/dist/types/features/spa/aggregate/index.d.ts.map +1 -1
  135. package/dist/types/features/utils/agent-session.d.ts.map +1 -1
  136. package/dist/types/features/utils/instrument-base.d.ts +1 -7
  137. package/dist/types/features/utils/instrument-base.d.ts.map +1 -1
  138. package/dist/types/features/utils/lazy-feature-loader.d.ts.map +1 -1
  139. package/dist/types/loaders/agent-base.d.ts +5 -1
  140. package/dist/types/loaders/agent-base.d.ts.map +1 -1
  141. package/dist/types/loaders/agent.d.ts +2 -2
  142. package/dist/types/loaders/agent.d.ts.map +1 -1
  143. package/dist/types/loaders/api/api-methods.d.ts.map +1 -1
  144. package/dist/types/loaders/api/api.d.ts +3 -5
  145. package/dist/types/loaders/api/api.d.ts.map +1 -1
  146. package/dist/types/loaders/configure/configure.d.ts.map +1 -1
  147. package/dist/types/loaders/features/features.d.ts +1 -0
  148. package/dist/types/loaders/features/features.d.ts.map +1 -1
  149. package/dist/types/loaders/micro-agent.d.ts +0 -1
  150. package/dist/types/loaders/micro-agent.d.ts.map +1 -1
  151. package/package.json +1 -1
  152. package/src/cdn/experimental.js +4 -2
  153. package/src/cdn/spa.js +3 -1
  154. package/src/common/aggregate/aggregator.js +2 -11
  155. package/src/common/config/state/init.js +3 -1
  156. package/src/common/context/observation-context-manager.js +55 -0
  157. package/src/common/event-emitter/contextual-ee.js +20 -10
  158. package/src/common/session/constants.js +1 -0
  159. package/src/common/timing/nav-timing.js +7 -3
  160. package/src/common/timing/now.js +1 -1
  161. package/src/common/util/feature-flags.js +1 -1
  162. package/src/common/wrap/index.js +1 -2
  163. package/src/common/wrap/wrap-events.js +3 -3
  164. package/src/common/wrap/wrap-fetch.js +3 -2
  165. package/src/common/wrap/wrap-function.js +4 -6
  166. package/src/common/wrap/wrap-promise.js +3 -2
  167. package/src/features/ajax/aggregate/index.js +36 -18
  168. package/src/features/jserrors/aggregate/index.js +70 -73
  169. package/src/features/page_view_event/aggregate/index.js +1 -1
  170. package/src/features/page_view_event/aggregate/initialized-features.js +1 -0
  171. package/src/features/session_replay/aggregate/index.js +92 -95
  172. package/src/features/session_replay/constants.js +5 -0
  173. package/src/features/session_replay/instrument/index.js +24 -9
  174. package/src/features/session_replay/shared/utils.js +19 -0
  175. package/src/features/soft_navigations/aggregate/ajax-node.js +57 -0
  176. package/src/features/soft_navigations/aggregate/bel-node.js +26 -0
  177. package/src/features/soft_navigations/aggregate/index.js +254 -0
  178. package/src/features/soft_navigations/aggregate/initial-page-load-interaction.js +53 -0
  179. package/src/features/soft_navigations/aggregate/interaction.js +159 -0
  180. package/src/features/soft_navigations/constants.js +29 -0
  181. package/src/features/soft_navigations/index.js +1 -0
  182. package/src/features/soft_navigations/instrument/index.js +67 -0
  183. package/src/features/spa/aggregate/index.js +5 -4
  184. package/src/features/utils/agent-session.js +2 -1
  185. package/src/features/utils/instrument-base.js +7 -10
  186. package/src/features/utils/lazy-feature-loader.js +2 -0
  187. package/src/loaders/agent-base.js +18 -3
  188. package/src/loaders/agent.js +18 -17
  189. package/src/loaders/api/api-methods.js +4 -1
  190. package/src/loaders/api/api.js +14 -12
  191. package/src/loaders/configure/configure.js +4 -1
  192. package/src/loaders/features/enabled-features.js +1 -1
  193. package/src/loaders/features/features.js +3 -1
  194. package/dist/cjs/common/wrap/wrap-raf.js +0 -55
  195. package/dist/esm/common/wrap/wrap-raf.js +0 -48
  196. package/dist/types/common/event-emitter/event-context.d.ts.map +0 -1
  197. package/dist/types/common/wrap/wrap-raf.d.ts +0 -16
  198. package/dist/types/common/wrap/wrap-raf.d.ts.map +0 -1
  199. package/src/common/wrap/wrap-raf.js +0 -52
  200. /package/dist/cjs/common/{event-emitter → context}/event-context.js +0 -0
  201. /package/dist/esm/common/{event-emitter → context}/event-context.js +0 -0
  202. /package/dist/types/common/{event-emitter → context}/event-context.d.ts +0 -0
  203. /package/src/common/{event-emitter → context}/event-context.js +0 -0
@@ -0,0 +1,263 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.Aggregate = void 0;
7
+ var _config = require("../../../common/config/config");
8
+ var _handle = require("../../../common/event-emitter/handle");
9
+ var _registerHandler = require("../../../common/event-emitter/register-handler");
10
+ var _harvestScheduler = require("../../../common/harvest/harvest-scheduler");
11
+ var _invoke = require("../../../common/util/invoke");
12
+ var _timeToFirstByte = require("../../../common/vitals/time-to-first-byte");
13
+ var _features = require("../../../loaders/features/features");
14
+ var _constants = require("../../metrics/constants");
15
+ var _aggregateBase = require("../../utils/aggregate-base");
16
+ var _constants2 = require("../constants");
17
+ var _ajaxNode = require("./ajax-node");
18
+ var _initialPageLoadInteraction = require("./initial-page-load-interaction");
19
+ var _interaction = require("./interaction");
20
+ class Aggregate extends _aggregateBase.AggregateBase {
21
+ static featureName = _constants2.FEATURE_NAME;
22
+ constructor(agentIdentifier, aggregator, _ref) {
23
+ let {
24
+ domObserver
25
+ } = _ref;
26
+ super(agentIdentifier, aggregator, _constants2.FEATURE_NAME);
27
+ const harvestTimeSeconds = (0, _config.getConfigurationValue)(agentIdentifier, 'soft_navigations.harvestTimeSeconds') || 10;
28
+ this.interactionsToHarvest = [];
29
+ this.interactionsAwaitingRetry = [];
30
+ this.domObserver = domObserver;
31
+ this.scheduler = new _harvestScheduler.HarvestScheduler('events', {
32
+ onFinished: this.onHarvestFinished.bind(this),
33
+ retryDelay: harvestTimeSeconds,
34
+ onUnload: () => this.interactionInProgress?.done() // return any held ajax or jserr events so they can be sent with EoL harvest
35
+ }, {
36
+ agentIdentifier,
37
+ ee: this.ee
38
+ });
39
+ this.scheduler.harvest.on('events', this.onHarvestStarted.bind(this));
40
+ this.initialPageLoadInteraction = new _initialPageLoadInteraction.InitialPageLoadInteraction(agentIdentifier);
41
+ _timeToFirstByte.timeToFirstByte.subscribe(_ref2 => {
42
+ let {
43
+ entries
44
+ } = _ref2;
45
+ const loadEventTime = entries[0].loadEventEnd;
46
+ this.initialPageLoadInteraction.forceSave = true;
47
+ this.initialPageLoadInteraction.done(loadEventTime);
48
+ this.interactionsToHarvest.push(this.initialPageLoadInteraction);
49
+ this.initialPageLoadInteraction = null;
50
+ // Report metric on the initial page load time
51
+ (0, _handle.handle)(_constants.SUPPORTABILITY_METRIC_CHANNEL, ['SoftNav/Interaction/InitialPageLoad/Duration/Ms', Math.round(loadEventTime)], undefined, _features.FEATURE_NAMES.metrics, this.ee);
52
+ });
53
+ this.latestRouteSetByApi = null;
54
+ this.interactionInProgress = null; // aside from the "page load" interaction, there can only ever be 1 ongoing at a time
55
+
56
+ this.blocked = false;
57
+ this.waitForFlags(['spa']).then(_ref3 => {
58
+ let [spaOn] = _ref3;
59
+ if (spaOn) this.scheduler.startTimer(harvestTimeSeconds, 0);else this.blocked = true; // if rum response determines that customer lacks entitlements for spa endpoint, this feature shouldn't harvest
60
+ });
61
+
62
+ // By default, a complete UI driven interaction requires event -> URL change -> DOM mod in that exact order.
63
+ (0, _registerHandler.registerHandler)('newUIEvent', event => this.startUIInteraction(event.type, event.timeStamp, event.target), this.featureName, this.ee);
64
+ (0, _registerHandler.registerHandler)('newURL', (timestamp, url) => this.interactionInProgress?.updateHistory(timestamp, url), this.featureName, this.ee);
65
+ (0, _registerHandler.registerHandler)('newDom', timestamp => {
66
+ this.interactionInProgress?.updateDom(timestamp);
67
+ if (this.interactionInProgress?.seenHistoryAndDomChange()) this.interactionInProgress.done();
68
+ }, this.featureName, this.ee);
69
+ this.#registerApiHandlers();
70
+ (0, _registerHandler.registerHandler)('ajax', this.#handleAjaxEvent.bind(this), this.featureName, this.ee);
71
+ (0, _registerHandler.registerHandler)('jserror', this.#handleJserror.bind(this), this.featureName, this.ee);
72
+ this.drain();
73
+ }
74
+ onHarvestStarted(options) {
75
+ if (this.interactionsToHarvest.length === 0 || this.blocked) return;
76
+
77
+ // The payload depacker takes the first ixn of a payload (if there are multiple ixns) and positively offset the subsequent ixns timestamps by that amount.
78
+ // In order to accurately portray the real start & end times of the 2nd & onward ixns, we hence need to negatively offset their start timestamps with that of the 1st ixn.
79
+ let firstIxnStartTime = 0; // the very 1st ixn does not require any offsetting
80
+ const serializedIxnList = [];
81
+ for (const interaction of this.interactionsToHarvest) {
82
+ serializedIxnList.push(interaction.serialize(firstIxnStartTime));
83
+ if (!firstIxnStartTime) firstIxnStartTime = Math.floor(interaction.start);
84
+ }
85
+ const payload = "bel.7;".concat(serializedIxnList.join(';'));
86
+ if (options.retry) this.interactionsAwaitingRetry.push(...this.interactionsToHarvest);
87
+ this.interactionsToHarvest = [];
88
+ return {
89
+ body: {
90
+ e: payload
91
+ }
92
+ };
93
+ }
94
+ onHarvestFinished(result) {
95
+ if (result.sent && result.retry && this.interactionsAwaitingRetry.length > 0) {
96
+ this.interactionsToHarvest = [...this.interactionsAwaitingRetry, ...this.interactionsToHarvest];
97
+ this.interactionsAwaitingRetry = [];
98
+ }
99
+ }
100
+ startUIInteraction(eventName, startedAt, sourceElem) {
101
+ // this is throttled by instrumentation so that it isn't excessively called
102
+ if (this.interactionInProgress?.createdByApi) return; // api-started interactions cannot be disrupted aka cancelled by UI events (and the vice versa applies as well)
103
+ if (this.interactionInProgress?.done() === false) return;
104
+ this.interactionInProgress = new _interaction.Interaction(this.agentIdentifier, eventName, startedAt, this.latestRouteSetByApi);
105
+ if (eventName === 'click') {
106
+ const sourceElemText = getActionText(sourceElem);
107
+ if (sourceElemText) this.interactionInProgress.customAttributes.actionText = sourceElemText;
108
+ }
109
+ this.interactionInProgress.cancellationTimer = setTimeout(() => {
110
+ this.interactionInProgress.done();
111
+ // Report metric on frequency of cancellation due to timeout for UI ixn
112
+ (0, _handle.handle)(_constants.SUPPORTABILITY_METRIC_CHANNEL, ['SoftNav/Interaction/TimeOut'], undefined, _features.FEATURE_NAMES.metrics, this.ee);
113
+ }, 30000); // UI ixn are disregarded after 30 seconds if it's not completed by then
114
+ this.setClosureHandlers();
115
+ }
116
+ setClosureHandlers() {
117
+ this.interactionInProgress.on('finished', () => {
118
+ const ref = this.interactionInProgress;
119
+ this.interactionsToHarvest.push(this.interactionInProgress);
120
+ this.interactionInProgress = null;
121
+ this.domObserver.disconnect(); // can stop observing whenever our interaction logic completes a cycle
122
+
123
+ // Report metric on the ixn duration
124
+ (0, _handle.handle)(_constants.SUPPORTABILITY_METRIC_CHANNEL, ["SoftNav/Interaction/".concat(ref.newURL !== ref.oldURL ? 'RouteChange' : 'Custom', "/Duration/Ms"), Math.round(ref.end - ref.start)], undefined, _features.FEATURE_NAMES.metrics, this.ee);
125
+ });
126
+ this.interactionInProgress.on('cancelled', () => {
127
+ this.interactionInProgress = null;
128
+ this.domObserver.disconnect();
129
+ });
130
+ }
131
+
132
+ /**
133
+ * Find the active interaction (current or past) for a given timestamp. Note that historic lookups mostly only go as far back as the last harvest for this feature.
134
+ * Also, the caller should check the status of the interaction returned if found via {@link Interaction.status}, if that's pertinent.
135
+ * TIP: Cancelled (status) interactions are NOT returned!
136
+ * IMPORTANT: Finished interactions are in queue for next harvest! It's highly recommended that consumer logic be synchronous for safe reference.
137
+ * @param {DOMHighResTimeStamp} timestamp
138
+ * @returns An {@link Interaction} or undefined, if no active interaction was found.
139
+ */
140
+ getInteractionFor(timestamp) {
141
+ /* In the sole case wherein there can be two "interactions" overlapping (initialPageLoad + regular route-change),
142
+ the regular interaction should get precedence in being assigned the "active" interaction in regards to our one-at-a-time model.
143
+ */
144
+ if (this.interactionInProgress?.isActiveDuring(timestamp)) return this.interactionInProgress;
145
+ let saveIxn;
146
+ for (let idx = this.interactionsToHarvest.length - 1; idx >= 0; idx--) {
147
+ // reverse search for the latest completed interaction for efficiency
148
+ const finishedInteraction = this.interactionsToHarvest[idx];
149
+ if (finishedInteraction.isActiveDuring(timestamp)) {
150
+ if (finishedInteraction.trigger !== 'initialPageLoad') return finishedInteraction;
151
+ // It's possible that a complete interaction occurs before page is fully loaded, so we need to consider if a route-change ixn may have overlapped this iPL
152
+ else saveIxn = finishedInteraction;
153
+ }
154
+ }
155
+ if (saveIxn) return saveIxn; // if an iPL was determined to be active and no route-change was found active for the same time, then iPL is deemed the one
156
+ if (this.initialPageLoadInteraction?.isActiveDuring(timestamp)) return this.initialPageLoadInteraction; // lowest precedence and also only if it's still in-progress
157
+ // Time must be when no interaction is happening, so return undefined.
158
+ }
159
+
160
+ /**
161
+ * Handles or redirect ajax event based on the interaction, if any, that it's tied to.
162
+ * @param {Object} event see Ajax feature's storeXhr function for object definition
163
+ */
164
+ #handleAjaxEvent(event) {
165
+ const associatedInteraction = this.getInteractionFor(event.startTime);
166
+ if (!associatedInteraction) {
167
+ // no interaction was happening when this ajax started, so give it back to Ajax feature for processing
168
+ (0, _handle.handle)('returnAjax', [event], undefined, _features.FEATURE_NAMES.ajax, this.ee);
169
+ } else {
170
+ if (associatedInteraction.status === _constants2.INTERACTION_STATUS.FIN) processAjax(this.agentIdentifier, event, associatedInteraction); // tack ajax onto the ixn object awaiting harvest
171
+ else {
172
+ // same thing as above, just at a later time -- if the interaction in progress is cancelled, just send the event back to ajax feat unmodified
173
+ associatedInteraction.on('finished', () => processAjax(this.agentIdentifier, event, associatedInteraction));
174
+ associatedInteraction.on('cancelled', () => (0, _handle.handle)('returnAjax', [event], undefined, _features.FEATURE_NAMES.ajax, this.ee));
175
+ }
176
+ }
177
+ function processAjax(agentId, event, parentInteraction) {
178
+ const newNode = new _ajaxNode.AjaxNode(agentId, event);
179
+ parentInteraction.addChild(newNode);
180
+ }
181
+ }
182
+
183
+ /**
184
+ * Decorate the passed-in params obj with properties relating to any associated interaction at the time of the timestamp.
185
+ * @param {Object} params reference to the local var instance in Jserrors feature's storeError
186
+ * @param {DOMHighResTimeStamp} timestamp time the jserror occurred
187
+ */
188
+ #handleJserror(params, timestamp) {
189
+ const associatedInteraction = this.getInteractionFor(timestamp);
190
+ if (!associatedInteraction) return; // do not need to decorate this jserror params
191
+
192
+ // Whether the interaction is in-progress or already finished, the id will let jserror buffer it under its index, until it gets the next step instruction.
193
+ params.browserInteractionId = associatedInteraction.id;
194
+ if (associatedInteraction.status === _constants2.INTERACTION_STATUS.FIN) {
195
+ // This information cannot be relayed back via handle() that flushes buffered errs because this is being called by a jserror's handle() per se and before the err is buffered.
196
+ params._softNavFinished = true; // instead, signal that this err can be processed right away without needing to be buffered aka wait for an in-progress ixn
197
+ params._softNavAttributes = associatedInteraction.customAttributes;
198
+ } else {
199
+ // These callbacks may be added multiple times for an ixn, but just a single run will deal with all jserrors associated with the interaction.
200
+ // As such, be cautious not to use the params object since that's tied to one specific jserror and won't affect the rest of them.
201
+ associatedInteraction.on('finished', (0, _invoke.single)(() => (0, _handle.handle)('softNavFlush', [associatedInteraction.id, true, associatedInteraction.customAttributes], undefined, _features.FEATURE_NAMES.jserrors, this.ee)));
202
+ associatedInteraction.on('cancelled', (0, _invoke.single)(() => (0, _handle.handle)('softNavFlush', [associatedInteraction.id, false, undefined], undefined, _features.FEATURE_NAMES.jserrors, this.ee))); // don't take custom attrs from cancelled ixns
203
+ }
204
+ }
205
+ #registerApiHandlers() {
206
+ const INTERACTION_API = 'api-ixn-';
207
+ const thisClass = this;
208
+ (0, _registerHandler.registerHandler)(INTERACTION_API + 'get', function (time) {
209
+ let {
210
+ waitForEnd
211
+ } = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
212
+ // In here, 'this' refers to the EventContext specific to per InteractionHandle instance spawned by each .interaction() api call.
213
+ // Each api call aka IH instance would therefore retain a reference to either the in-progress interaction *at the time of the call* OR a new api-started interaction.
214
+ this.associatedInteraction = thisClass.getInteractionFor(time);
215
+ if (!this.associatedInteraction) {
216
+ // This new api-driven interaction will be the target of any subsequent .interaction() call, until it is closed by EITHER .end() OR the regular seenHistoryAndDomChange process.
217
+ this.associatedInteraction = thisClass.interactionInProgress = new _interaction.Interaction(thisClass.agentIdentifier, _constants2.API_TRIGGER_NAME, time, thisClass.latestRouteSetByApi);
218
+ thisClass.setClosureHandlers();
219
+ }
220
+ if (waitForEnd === true) this.associatedInteraction.keepOpenUntilEndApi = true;
221
+ }, thisClass.featureName, thisClass.ee);
222
+ (0, _registerHandler.registerHandler)(INTERACTION_API + 'end', function (timeNow) {
223
+ this.associatedInteraction.done(timeNow);
224
+ }, thisClass.featureName, thisClass.ee);
225
+ (0, _registerHandler.registerHandler)(INTERACTION_API + 'save', function () {
226
+ this.associatedInteraction.forceSave = true;
227
+ }, thisClass.featureName, thisClass.ee);
228
+ (0, _registerHandler.registerHandler)(INTERACTION_API + 'ignore', function () {
229
+ this.associatedInteraction.forceIgnore = true;
230
+ }, thisClass.featureName, thisClass.ee);
231
+ (0, _registerHandler.registerHandler)(INTERACTION_API + 'getContext', function (time, callback) {
232
+ if (typeof callback !== 'function') return;
233
+ setTimeout(() => callback(this.associatedInteraction.customDataByApi), 0);
234
+ }, thisClass.featureName, thisClass.ee);
235
+ (0, _registerHandler.registerHandler)(INTERACTION_API + 'onEnd', function (time, callback) {
236
+ if (typeof callback !== 'function') return;
237
+ this.associatedInteraction.onDone.push(callback);
238
+ }, thisClass.featureName, thisClass.ee);
239
+ (0, _registerHandler.registerHandler)(INTERACTION_API + 'actionText', function (time, newActionText) {
240
+ if (newActionText) this.associatedInteraction.customAttributes.actionText = newActionText;
241
+ }, thisClass.featureName, thisClass.ee);
242
+ (0, _registerHandler.registerHandler)(INTERACTION_API + 'setName', function (time, name, trigger) {
243
+ if (name) this.associatedInteraction.customName = name;
244
+ if (trigger) this.associatedInteraction.trigger = trigger;
245
+ }, thisClass.featureName, thisClass.ee);
246
+ (0, _registerHandler.registerHandler)(INTERACTION_API + 'setAttribute', function (time, key, value) {
247
+ this.associatedInteraction.customAttributes[key] = value;
248
+ }, thisClass.featureName, thisClass.ee);
249
+ (0, _registerHandler.registerHandler)(INTERACTION_API + 'routeName', function (time, newRouteName) {
250
+ // notice that this fn tampers with the ixn IP, not with the linked ixn
251
+ thisClass.latestRouteSetByApi = newRouteName;
252
+ if (thisClass.interactionInProgress) thisClass.interactionInProgress.newRoute = newRouteName;
253
+ }, thisClass.featureName, thisClass.ee);
254
+ }
255
+ }
256
+ exports.Aggregate = Aggregate;
257
+ function getActionText(elem) {
258
+ const tagName = elem.tagName.toLowerCase();
259
+ const elementsOfInterest = ['a', 'button', 'input'];
260
+ if (elementsOfInterest.includes(tagName)) {
261
+ return elem.title || elem.value || elem.innerText;
262
+ }
263
+ }
@@ -0,0 +1,62 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.InitialPageLoadInteraction = void 0;
7
+ var _navTiming = require("../../../common/timing/nav-timing");
8
+ var _interaction = require("./interaction");
9
+ var _belSerializer = require("../../../common/serialize/bel-serializer");
10
+ var _firstPaint = require("../../../common/vitals/first-paint");
11
+ var _firstContentfulPaint = require("../../../common/vitals/first-contentful-paint");
12
+ var _config = require("../../../common/config/config");
13
+ class InitialPageLoadInteraction extends _interaction.Interaction {
14
+ constructor(agentIdentifier) {
15
+ super(agentIdentifier, 'initialPageLoad', 0, null);
16
+ const agentInfo = (0, _config.getInfo)(agentIdentifier);
17
+ this.queueTime = agentInfo.queueTime;
18
+ this.appTime = agentInfo.applicationTime;
19
+ }
20
+ get firstPaint() {
21
+ return _firstPaint.firstPaint.current.value;
22
+ }
23
+ get firstContentfulPaint() {
24
+ return _firstContentfulPaint.firstContentfulPaint.current.value;
25
+ }
26
+
27
+ /**
28
+ * Build the navTiming node. This assumes the navTimingValues array in nav-timing.js has already been filled with values via the PageViewEvent feature having
29
+ * executed the addPT function first and foremost.
30
+ */
31
+ get navTiming() {
32
+ if (!_navTiming.navTimingValues.length) return;
33
+ /*
34
+ 1. we initialize the seperator to ',' (seperates the nodeType id from the first value)
35
+ 2. we initialize the navTiming node to 'b' (the nodeType id)
36
+ 3. if the value is present, we add the seperator followed by the value;
37
+ otherwise:
38
+ - we add null seperator ('!') to the navTimingNode
39
+ - we set the seperator to an empty string since we already wrote it above
40
+ the reason for writing the null seperator instead of setting the seperator
41
+ is to ensure we still write it if the null is the last navTiming value.
42
+ */
43
+ let seperator = ',';
44
+ let navTimingNode = 'b';
45
+ let prev = 0;
46
+
47
+ // Get all navTiming values except offset aka timeOrigin since we just consider that (this.start) 0.
48
+ // These are the latter 20 of the 21 timings appended by addPT:
49
+ _navTiming.navTimingValues.slice(1, 21).forEach(v => {
50
+ if (v !== undefined) {
51
+ navTimingNode += seperator + (0, _belSerializer.numeric)(v - prev);
52
+ seperator = ',';
53
+ prev = v;
54
+ } else {
55
+ navTimingNode += seperator + '!';
56
+ seperator = '';
57
+ }
58
+ });
59
+ return navTimingNode;
60
+ }
61
+ }
62
+ exports.InitialPageLoadInteraction = InitialPageLoadInteraction;
@@ -0,0 +1,146 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.Interaction = void 0;
7
+ var _config = require("../../../common/config/config");
8
+ var _runtime = require("../../../common/constants/runtime");
9
+ var _uniqueId = require("../../../common/ids/unique-id");
10
+ var _belSerializer = require("../../../common/serialize/bel-serializer");
11
+ var _cleanUrl = require("../../../common/url/clean-url");
12
+ var _constants = require("../constants");
13
+ var _belNode = require("./bel-node");
14
+ /**
15
+ * link https://github.com/newrelic/nr-querypack/blob/main/schemas/bel/7.qpschema
16
+ **/
17
+ class Interaction extends _belNode.BelNode {
18
+ id = (0, _uniqueId.generateUuid)(); // unique id that is serialized and used to link interactions with errors
19
+ initialPageURL = _runtime.initialLocation;
20
+ oldURL = '' + _runtime.globalScope?.location;
21
+ newURL = '' + _runtime.globalScope?.location;
22
+ customName;
23
+ customAttributes = {};
24
+ customDataByApi = {};
25
+ queueTime; // only used by initialPageLoad interactions
26
+ appTime; // only used by initialPageLoad interactions
27
+ newRoute;
28
+ /** Internal state of this interaction: in-progress, finished, or cancelled. */
29
+ status = _constants.INTERACTION_STATUS.IP;
30
+ domTimestamp = 0;
31
+ historyTimestamp = 0;
32
+ createdByApi = false;
33
+ keepOpenUntilEndApi = false;
34
+ onDone = [];
35
+ cancellationTimer;
36
+ constructor(agentIdentifier, uiEvent, uiEventTimestamp, currentRouteKnown) {
37
+ super(agentIdentifier);
38
+ this.belType = _constants.NODE_TYPE.INTERACTION;
39
+ this.trigger = uiEvent;
40
+ this.start = uiEventTimestamp;
41
+ this.oldRoute = currentRouteKnown;
42
+ this.eventSubscription = new Map([['finished', []], ['cancelled', []]]);
43
+ this.forceSave = this.forceIgnore = false;
44
+ if (this.trigger === _constants.API_TRIGGER_NAME) this.createdByApi = true;
45
+ }
46
+ updateDom(timestamp) {
47
+ this.domTimestamp = timestamp || performance.now(); // default timestamp should be precise for accurate isActiveDuring calculations
48
+ }
49
+ updateHistory(timestamp, newUrl) {
50
+ this.newURL = newUrl || '' + _runtime.globalScope?.location;
51
+ this.historyTimestamp = timestamp || performance.now();
52
+ }
53
+ seenHistoryAndDomChange() {
54
+ return this.historyTimestamp > 0 && this.domTimestamp > this.historyTimestamp; // URL must change before DOM does
55
+ }
56
+ on(event, cb) {
57
+ if (!this.eventSubscription.has(event)) throw new Error('Cannot subscribe to non pre-defined events.');
58
+ if (typeof cb !== 'function') throw new Error('Must supply function as callback.');
59
+ this.eventSubscription.get(event).push(cb);
60
+ }
61
+ done(customEndTime) {
62
+ // User could've mark this interaction--regardless UI or api started--as "don't close until .end() is called on it". Only .end provides a timestamp; the default flows do not.
63
+ if (this.keepOpenUntilEndApi && customEndTime === undefined) return false;
64
+ this.onDone.forEach(apiProvidedCb => apiProvidedCb(this.customDataByApi)); // this interaction's .save or .ignore can still be set by these user provided callbacks for example
65
+
66
+ if (this.forceIgnore) this.#cancel(); // .ignore() always has precedence over save actions
67
+ else if (this.seenHistoryAndDomChange()) this.#finish(customEndTime); // then this should've already finished while it was the interactionInProgress, with a natural end time
68
+ else if (this.forceSave) this.#finish(customEndTime || performance.now()); // a manually saved ixn (did not fulfill conditions) must have a specified end time, if one wasn't provided
69
+ else this.#cancel();
70
+ return true;
71
+ }
72
+ #finish() {
73
+ let customEndTime = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 0;
74
+ if (this.status !== _constants.INTERACTION_STATUS.IP) return; // disallow this call if the ixn is already done aka not in-progress
75
+ clearTimeout(this.cancellationTimer);
76
+ this.end = Math.max(this.domTimestamp, this.historyTimestamp, customEndTime);
77
+ this.customAttributes = {
78
+ ...(0, _config.getInfo)(this.agentIdentifier).jsAttributes,
79
+ ...this.customAttributes
80
+ }; // attrs specific to this interaction should have precedence over the general custom attrs
81
+ this.status = _constants.INTERACTION_STATUS.FIN;
82
+
83
+ // Run all the callbacks awaiting this interaction to finish.
84
+ const callbacks = this.eventSubscription.get('finished');
85
+ callbacks.forEach(fn => fn());
86
+ }
87
+ #cancel() {
88
+ if (this.status !== _constants.INTERACTION_STATUS.IP) return; // disallow this call if the ixn is already done aka not in-progress
89
+ clearTimeout(this.cancellationTimer);
90
+ this.status = _constants.INTERACTION_STATUS.CAN;
91
+
92
+ // Run all the callbacks listening to this interaction's potential cancellation.
93
+ const callbacks = this.eventSubscription.get('cancelled');
94
+ callbacks.forEach(fn => fn());
95
+ }
96
+
97
+ /**
98
+ * Given a timestamp, determine if it falls within this interaction's span, i.e. if this was the active interaction during that time.
99
+ * For in-progress interactions, this only compares the time with the start of span. Cancelled interactions are not considered active at all.
100
+ * @param {DOMHighResTimeStamp} timestamp
101
+ * @returns True or false boolean.
102
+ */
103
+ isActiveDuring(timestamp) {
104
+ if (this.status === _constants.INTERACTION_STATUS.IP) return this.start <= timestamp;
105
+ return this.status === _constants.INTERACTION_STATUS.FIN && this.start <= timestamp && this.end >= timestamp;
106
+ }
107
+
108
+ // Following are virtual properties overridden by a subclass:
109
+ get firstPaint() {}
110
+ get firstContentfulPaint() {}
111
+ get navTiming() {}
112
+ serialize(firstStartTimeOfPayload) {
113
+ const addString = (0, _belSerializer.getAddStringContext)(this.agentIdentifier);
114
+ const nodeList = [];
115
+ let ixnType;
116
+ if (this.trigger === 'initialPageLoad') ixnType = _constants.INTERACTION_TYPE.INITIAL_PAGE_LOAD;else if (this.newURL !== this.oldURL) ixnType = _constants.INTERACTION_TYPE.ROUTE_CHANGE;else ixnType = _constants.INTERACTION_TYPE.UNSPECIFIED;
117
+
118
+ // IMPORTANT: The order in which addString is called matters and correlates to the order in which string shows up in the harvest payload. Do not re-order the following code.
119
+ const fields = [(0, _belSerializer.numeric)(this.belType), 0,
120
+ // this will be overwritten below with number of attached nodes
121
+ (0, _belSerializer.numeric)(Math.floor(this.start - firstStartTimeOfPayload)),
122
+ // relative to first node
123
+ (0, _belSerializer.numeric)(Math.floor(this.end - this.start)),
124
+ // end -- relative to start
125
+ (0, _belSerializer.numeric)(this.callbackEnd),
126
+ // cbEnd -- relative to start; not used by BrowserInteraction events
127
+ (0, _belSerializer.numeric)(this.callbackDuration),
128
+ // not relative
129
+ addString(this.trigger), addString((0, _cleanUrl.cleanURL)(this.initialPageURL, true)), addString((0, _cleanUrl.cleanURL)(this.oldURL, true)), addString((0, _cleanUrl.cleanURL)(this.newURL, true)), addString(this.customName), ixnType, (0, _belSerializer.nullable)(this.queueTime, _belSerializer.numeric, true) + (0, _belSerializer.nullable)(this.appTime, _belSerializer.numeric, true) + (0, _belSerializer.nullable)(this.oldRoute, addString, true) + (0, _belSerializer.nullable)(this.newRoute, addString, true) + addString(this.id), addString(this.nodeId), (0, _belSerializer.nullable)(this.firstPaint, _belSerializer.numeric, true) + (0, _belSerializer.nullable)(this.firstContentfulPaint, _belSerializer.numeric)];
130
+ const allAttachedNodes = (0, _belSerializer.addCustomAttributes)(this.customAttributes || {}, addString); // start with all custom attributes
131
+ if ((0, _config.getInfo)(this.agentIdentifier).atts) allAttachedNodes.push('a,' + addString((0, _config.getInfo)(this.agentIdentifier).atts)); // add apm provided attributes
132
+ /* Querypack encoder+decoder quirkiness:
133
+ - If first ixn node of payload is being processed, we use this node's start to offset. (firstStartTime should be 0--or undefined.)
134
+ - Else for subsequent ixn nodes, we use the first ixn node's start to offset. */
135
+ this.children.forEach(node => allAttachedNodes.push(node.serialize(firstStartTimeOfPayload || this.start))); // recursively add the serialized string of every child of this (ixn) bel node
136
+
137
+ fields[1] = (0, _belSerializer.numeric)(allAttachedNodes.length);
138
+ nodeList.push(fields);
139
+ if (allAttachedNodes.length) nodeList.push(allAttachedNodes.join(';'));
140
+ if (this.navTiming) nodeList.push(this.navTiming);else nodeList.push('');
141
+ // nodeList = [<fields array>, <serialized string of all attributes and children>, <serialized nav timing info> || '']
142
+
143
+ return nodeList.join(';');
144
+ }
145
+ }
146
+ exports.Interaction = Interaction;
@@ -0,0 +1,31 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.NODE_TYPE = exports.INTERACTION_TYPE = exports.INTERACTION_TRIGGERS = exports.INTERACTION_STATUS = exports.FEATURE_NAME = exports.API_TRIGGER_NAME = void 0;
7
+ var _features = require("../../loaders/features/features");
8
+ const INTERACTION_TRIGGERS = exports.INTERACTION_TRIGGERS = ['click',
9
+ // e.g. user clicks link or the page back/forward buttons
10
+ 'keydown',
11
+ // e.g. user presses left and right arrow key to switch between displayed photo gallery
12
+ 'submit' // e.g. user clicks submit butotn or presses enter while editing a form field
13
+ ];
14
+ const API_TRIGGER_NAME = exports.API_TRIGGER_NAME = 'api';
15
+ const FEATURE_NAME = exports.FEATURE_NAME = _features.FEATURE_NAMES.softNav;
16
+ const INTERACTION_TYPE = exports.INTERACTION_TYPE = {
17
+ INITIAL_PAGE_LOAD: '',
18
+ ROUTE_CHANGE: 1,
19
+ UNSPECIFIED: 2
20
+ };
21
+ const NODE_TYPE = exports.NODE_TYPE = {
22
+ INTERACTION: 1,
23
+ AJAX: 2,
24
+ CUSTOM_END: 3,
25
+ CUSTOM_TRACER: 4
26
+ };
27
+ const INTERACTION_STATUS = exports.INTERACTION_STATUS = {
28
+ IP: 'in progress',
29
+ FIN: 'finished',
30
+ CAN: 'cancelled'
31
+ };
@@ -0,0 +1,12 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ Object.defineProperty(exports, "SoftNav", {
7
+ enumerable: true,
8
+ get: function () {
9
+ return _index.Instrument;
10
+ }
11
+ });
12
+ var _index = require("./instrument/index");
@@ -0,0 +1,79 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.Instrument = void 0;
7
+ var _config = require("../../../common/config/config");
8
+ var _runtime = require("../../../common/constants/runtime");
9
+ var _handle = require("../../../common/event-emitter/handle");
10
+ var _eventListenerOpts = require("../../../common/event-listener/event-listener-opts");
11
+ var _now = require("../../../common/timing/now");
12
+ var _invoke = require("../../../common/util/invoke");
13
+ var _wrap = require("../../../common/wrap");
14
+ var _instrumentBase = require("../../utils/instrument-base");
15
+ var _constants = require("../constants");
16
+ /** The minimal time after a UI event for which no further events will be processed - i.e. a throttling rate to reduce spam.
17
+ * This also give some time for the new interaction to complete without being discarded by a subsequent UI event and wrongly attributed.
18
+ * This value is still subject to change and critique, as it is derived from beyond worst case time to next frame of a page.
19
+ */
20
+ const UI_WAIT_INTERVAL = 1 / 10 * 1000; // assume 10 fps
21
+
22
+ class Instrument extends _instrumentBase.InstrumentBase {
23
+ static featureName = _constants.FEATURE_NAME;
24
+ constructor(agentIdentifier, aggregator) {
25
+ let auto = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : true;
26
+ super(agentIdentifier, aggregator, _constants.FEATURE_NAME, auto);
27
+ if (!_runtime.isBrowserScope || !_config.originals.MO) return; // soft navigations is not supported outside web env or browsers without the mutation observer API
28
+
29
+ const historyEE = (0, _wrap.wrapHistory)(this.ee);
30
+ const eventsEE = (0, _wrap.wrapEvents)(this.ee);
31
+ const trackURLChange = () => (0, _handle.handle)('newURL', [(0, _now.now)(), '' + window.location], undefined, this.featureName, this.ee);
32
+ historyEE.on('pushState-end', trackURLChange);
33
+ historyEE.on('replaceState-end', trackURLChange);
34
+ try {
35
+ this.removeOnAbort = new AbortController();
36
+ } catch (e) {}
37
+ const trackURLChangeEvent = evt => (0, _handle.handle)('newURL', [evt.timeStamp, '' + window.location], undefined, this.featureName, this.ee);
38
+ (0, _eventListenerOpts.windowAddEventListener)('popstate', trackURLChangeEvent, true, this.removeOnAbort?.signal);
39
+ let oncePerFrame = false; // attempt to reduce dom noice since the observer runs very frequently with below options
40
+ const domObserver = new _config.originals.MO((domChanges, observer) => {
41
+ if (oncePerFrame) return;
42
+ oncePerFrame = true;
43
+ requestAnimationFrame(() => {
44
+ // waiting for next frame to time when any visuals are supposedly updated
45
+ (0, _handle.handle)('newDom', [(0, _now.now)()], undefined, this.featureName, this.ee);
46
+ oncePerFrame = false;
47
+ });
48
+ });
49
+ const processUserInteraction = (0, _invoke.debounce)(event => {
50
+ (0, _handle.handle)('newUIEvent', [event], undefined, this.featureName, this.ee);
51
+ domObserver.observe(document.body, {
52
+ attributes: true,
53
+ childList: true,
54
+ subtree: true,
55
+ characterData: true
56
+ });
57
+ }, UI_WAIT_INTERVAL, {
58
+ leading: true
59
+ });
60
+ eventsEE.on('fn-start', _ref => {
61
+ let [evt] = _ref;
62
+ // set up a new user ixn before the callback for the triggering event executes
63
+ if (_constants.INTERACTION_TRIGGERS.includes(evt?.type)) {
64
+ processUserInteraction(evt);
65
+ }
66
+ });
67
+ for (let eventType of _constants.INTERACTION_TRIGGERS) document.addEventListener(eventType, () => {/* no-op, this ensures the UI events are monitored by our callback above */});
68
+ this.abortHandler = abort;
69
+ this.importAggregator({
70
+ domObserver
71
+ });
72
+ function abort() {
73
+ this.removeOnAbort?.abort();
74
+ domObserver.disconnect();
75
+ this.abortHandler = undefined; // weakly allow this abort op to run only once
76
+ }
77
+ }
78
+ }
79
+ exports.Instrument = Instrument;
@@ -656,7 +656,7 @@ class Aggregate extends _aggregateBase.AggregateBase {
656
656
  }
657
657
  function saveInteraction(interaction) {
658
658
  if (interaction.ignored || !interaction.save && !interaction.routeChange) {
659
- baseEE.emit('interactionDiscarded', [interaction]);
659
+ baseEE.emit('interactionDone', [interaction, false]);
660
660
  return;
661
661
  }
662
662
  if (state.prevInteraction === interaction) {
@@ -672,10 +672,10 @@ class Aggregate extends _aggregateBase.AggregateBase {
672
672
  interaction.root.attrs.firstPaint = _firstPaint.firstPaint.current.value;
673
673
  interaction.root.attrs.firstContentfulPaint = _firstContentfulPaint.firstContentfulPaint.current.value;
674
674
  }
675
- baseEE.emit('interactionSaved', [interaction]);
675
+ baseEE.emit('interactionDone', [interaction, true]);
676
676
  state.interactionsToHarvest.push(interaction);
677
- let smCategory = 'RouteChange';
678
- if (interaction.root?.attrs?.trigger === 'initialPageLoad') smCategory = 'InitialPageLoad';else if (interaction.root?.attrs?.trigger === 'api') smCategory = 'Custom';
677
+ let smCategory;
678
+ if (interaction.root?.attrs?.trigger === 'initialPageLoad') smCategory = 'InitialPageLoad';else if (interaction.routeChange) smCategory = 'RouteChange';else smCategory = 'Custom';
679
679
  (0, _handle.handle)(_constants2.SUPPORTABILITY_METRIC_CHANNEL, ["Spa/Interaction/".concat(smCategory, "/Duration/Ms"), Math.max((interaction.root?.end || 0) - (interaction.root?.start || 0), 0)], undefined, _features.FEATURE_NAMES.metrics, baseEE);
680
680
  scheduler.scheduleHarvest(0);
681
681
  }
@@ -11,6 +11,7 @@ var _registerHandler = require("../../common/event-emitter/register-handler");
11
11
  var _sessionEntity = require("../../common/session/session-entity");
12
12
  var _localStorage = require("../../common/storage/local-storage.js");
13
13
  var _firstPartyCookies = require("../../common/storage/first-party-cookies");
14
+ var _constants = require("../../common/session/constants");
14
15
  let ranOnce = 0;
15
16
  function setupAgentSession(agentIdentifier) {
16
17
  const agentRuntime = (0, _config.getRuntime)(agentIdentifier);
@@ -21,7 +22,7 @@ function setupAgentSession(agentIdentifier) {
21
22
  const storageTypeInst = sessionInit?.domain ? new _firstPartyCookies.FirstPartyCookies(sessionInit.domain) : new _localStorage.LocalStorage();
22
23
  agentRuntime.session = new _sessionEntity.SessionEntity({
23
24
  agentIdentifier,
24
- key: 'SESSION',
25
+ key: _constants.DEFAULT_KEY,
25
26
  storage: storageTypeInst,
26
27
  expiresMs: sessionInit?.expiresMs,
27
28
  inactiveMs: sessionInit?.inactiveMs