@newrelic/browser-agent 1.257.0 → 1.258.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (127) hide show
  1. package/CHANGELOG.md +27 -0
  2. package/dist/cjs/common/config/state/configurable.js +8 -5
  3. package/dist/cjs/common/config/state/init.js +0 -2
  4. package/dist/cjs/common/config/state/runtime.js +10 -8
  5. package/dist/cjs/common/constants/env.cdn.js +1 -1
  6. package/dist/cjs/common/constants/env.npm.js +1 -1
  7. package/dist/cjs/common/constants/runtime.js +8 -2
  8. package/dist/cjs/common/deny-list/deny-list.js +6 -8
  9. package/dist/cjs/common/session/constants.js +1 -0
  10. package/dist/cjs/common/session/session-entity.js +3 -0
  11. package/dist/cjs/common/timing/time-keeper.js +45 -9
  12. package/dist/cjs/common/vitals/time-to-first-byte.js +10 -2
  13. package/dist/cjs/common/vitals/vital-metric.js +1 -1
  14. package/dist/cjs/features/ajax/aggregate/chunk.js +50 -0
  15. package/dist/cjs/features/ajax/aggregate/index.js +131 -191
  16. package/dist/cjs/features/ajax/instrument/index.js +11 -11
  17. package/dist/cjs/features/jserrors/aggregate/index.js +7 -3
  18. package/dist/cjs/features/jserrors/instrument/index.js +4 -87
  19. package/dist/cjs/features/jserrors/shared/cast-error.js +66 -0
  20. package/dist/cjs/features/jserrors/{instrument → shared}/uncaught-error.js +4 -2
  21. package/dist/cjs/features/page_view_event/aggregate/index.js +3 -3
  22. package/dist/cjs/features/session_replay/aggregate/index.js +1 -0
  23. package/dist/cjs/features/session_replay/shared/utils.js +3 -3
  24. package/dist/cjs/features/session_trace/aggregate/index.js +9 -7
  25. package/dist/cjs/features/spa/aggregate/index.js +11 -1
  26. package/dist/cjs/features/spa/instrument/index.js +9 -2
  27. package/dist/cjs/features/utils/agent-session.js +1 -5
  28. package/dist/cjs/features/utils/instrument-base.js +1 -1
  29. package/dist/cjs/loaders/api/api.js +6 -14
  30. package/dist/cjs/loaders/api/apiAsync.js +5 -4
  31. package/dist/esm/common/config/state/configurable.js +8 -5
  32. package/dist/esm/common/config/state/init.js +0 -2
  33. package/dist/esm/common/config/state/runtime.js +11 -9
  34. package/dist/esm/common/constants/env.cdn.js +1 -1
  35. package/dist/esm/common/constants/env.npm.js +1 -1
  36. package/dist/esm/common/constants/runtime.js +7 -1
  37. package/dist/esm/common/deny-list/deny-list.js +5 -8
  38. package/dist/esm/common/session/constants.js +1 -0
  39. package/dist/esm/common/session/session-entity.js +3 -0
  40. package/dist/esm/common/timing/time-keeper.js +46 -9
  41. package/dist/esm/common/vitals/time-to-first-byte.js +11 -3
  42. package/dist/esm/common/vitals/vital-metric.js +1 -1
  43. package/dist/esm/features/ajax/aggregate/chunk.js +43 -0
  44. package/dist/esm/features/ajax/aggregate/index.js +130 -191
  45. package/dist/esm/features/ajax/instrument/index.js +12 -12
  46. package/dist/esm/features/jserrors/aggregate/index.js +7 -3
  47. package/dist/esm/features/jserrors/instrument/index.js +4 -87
  48. package/dist/esm/features/jserrors/shared/cast-error.js +59 -0
  49. package/dist/esm/features/jserrors/{instrument → shared}/uncaught-error.js +5 -2
  50. package/dist/esm/features/page_view_event/aggregate/index.js +4 -4
  51. package/dist/esm/features/session_replay/aggregate/index.js +1 -0
  52. package/dist/esm/features/session_replay/shared/utils.js +4 -4
  53. package/dist/esm/features/session_trace/aggregate/index.js +9 -7
  54. package/dist/esm/features/spa/aggregate/index.js +11 -1
  55. package/dist/esm/features/spa/instrument/index.js +9 -2
  56. package/dist/esm/features/utils/agent-session.js +1 -5
  57. package/dist/esm/features/utils/instrument-base.js +1 -1
  58. package/dist/esm/loaders/api/api.js +6 -14
  59. package/dist/esm/loaders/api/apiAsync.js +5 -4
  60. package/dist/types/common/config/state/configurable.d.ts.map +1 -1
  61. package/dist/types/common/config/state/init.d.ts.map +1 -1
  62. package/dist/types/common/config/state/runtime.d.ts.map +1 -1
  63. package/dist/types/common/constants/runtime.d.ts +6 -1
  64. package/dist/types/common/constants/runtime.d.ts.map +1 -1
  65. package/dist/types/common/deny-list/deny-list.d.ts +1 -0
  66. package/dist/types/common/deny-list/deny-list.d.ts.map +1 -1
  67. package/dist/types/common/session/constants.d.ts +1 -0
  68. package/dist/types/common/session/session-entity.d.ts.map +1 -1
  69. package/dist/types/common/timing/time-keeper.d.ts +1 -1
  70. package/dist/types/common/timing/time-keeper.d.ts.map +1 -1
  71. package/dist/types/features/ajax/aggregate/chunk.d.ts +8 -0
  72. package/dist/types/features/ajax/aggregate/chunk.d.ts.map +1 -0
  73. package/dist/types/features/ajax/aggregate/index.d.ts +8 -6
  74. package/dist/types/features/ajax/aggregate/index.d.ts.map +1 -1
  75. package/dist/types/features/ajax/instrument/index.d.ts +2 -2
  76. package/dist/types/features/ajax/instrument/index.d.ts.map +1 -1
  77. package/dist/types/features/jserrors/aggregate/index.d.ts.map +1 -1
  78. package/dist/types/features/jserrors/instrument/index.d.ts.map +1 -1
  79. package/dist/types/features/jserrors/shared/cast-error.d.ts +21 -0
  80. package/dist/types/features/jserrors/shared/cast-error.d.ts.map +1 -0
  81. package/dist/types/features/jserrors/{instrument → shared}/uncaught-error.d.ts +3 -2
  82. package/dist/types/features/jserrors/shared/uncaught-error.d.ts.map +1 -0
  83. package/dist/types/features/session_replay/aggregate/index.d.ts.map +1 -1
  84. package/dist/types/features/session_replay/shared/utils.d.ts +3 -3
  85. package/dist/types/features/session_trace/aggregate/index.d.ts +9 -8
  86. package/dist/types/features/session_trace/aggregate/index.d.ts.map +1 -1
  87. package/dist/types/features/spa/aggregate/index.d.ts.map +1 -1
  88. package/dist/types/features/spa/instrument/index.d.ts.map +1 -1
  89. package/dist/types/features/utils/agent-session.d.ts.map +1 -1
  90. package/dist/types/loaders/api/api.d.ts +1 -1
  91. package/dist/types/loaders/api/api.d.ts.map +1 -1
  92. package/dist/types/loaders/api/apiAsync.d.ts.map +1 -1
  93. package/package.json +1 -1
  94. package/src/common/config/state/configurable.js +9 -8
  95. package/src/common/config/state/init.js +0 -1
  96. package/src/common/config/state/runtime.js +12 -9
  97. package/src/common/constants/__mocks__/runtime.js +2 -0
  98. package/src/common/constants/runtime.js +6 -1
  99. package/src/common/deny-list/deny-list.js +6 -7
  100. package/src/common/session/constants.js +1 -0
  101. package/src/common/session/session-entity.js +2 -0
  102. package/src/common/timing/time-keeper.js +44 -10
  103. package/src/common/vitals/time-to-first-byte.js +10 -3
  104. package/src/common/vitals/vital-metric.js +1 -1
  105. package/src/features/ajax/aggregate/chunk.js +51 -0
  106. package/src/features/ajax/aggregate/index.js +128 -200
  107. package/src/features/ajax/instrument/index.js +12 -13
  108. package/src/features/jserrors/aggregate/index.js +9 -3
  109. package/src/features/jserrors/instrument/index.js +4 -99
  110. package/src/features/jserrors/shared/cast-error.js +69 -0
  111. package/src/features/jserrors/{instrument → shared}/uncaught-error.js +5 -2
  112. package/src/features/page_view_event/aggregate/index.js +4 -4
  113. package/src/features/session_replay/aggregate/index.js +2 -0
  114. package/src/features/session_replay/shared/utils.js +4 -4
  115. package/src/features/session_trace/aggregate/index.js +9 -8
  116. package/src/features/spa/aggregate/index.js +10 -1
  117. package/src/features/spa/instrument/index.js +3 -3
  118. package/src/features/utils/agent-session.js +1 -7
  119. package/src/features/utils/instrument-base.js +1 -1
  120. package/src/loaders/api/api.js +6 -15
  121. package/src/loaders/api/apiAsync.js +5 -4
  122. package/dist/cjs/common/storage/first-party-cookies.js +0 -36
  123. package/dist/esm/common/storage/first-party-cookies.js +0 -29
  124. package/dist/types/common/storage/first-party-cookies.d.ts +0 -8
  125. package/dist/types/common/storage/first-party-cookies.d.ts.map +0 -1
  126. package/dist/types/features/jserrors/instrument/uncaught-error.d.ts.map +0 -1
  127. package/src/common/storage/first-party-cookies.js +0 -32
@@ -0,0 +1,66 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.castError = castError;
7
+ exports.castErrorEvent = castErrorEvent;
8
+ exports.castPromiseRejectionEvent = castPromiseRejectionEvent;
9
+ var _uncaughtError = require("./uncaught-error");
10
+ /**
11
+ * Any value can be used with the `throw` keyword. This function ensures that the value is
12
+ * either a proper Error instance or attempts to convert it to an UncaughtError instance.
13
+ * @param {any} error The value thrown
14
+ * @returns {Error|UncaughtError} The converted error instance
15
+ */
16
+ function castError(error) {
17
+ /** Sometimes a browser can emit an error object with no stack */
18
+ if (canTrustError(error)) {
19
+ return error;
20
+ }
21
+
22
+ /**
23
+ * The thrown value may contain a message property. If it does, try to treat the thrown
24
+ * value as an Error-like object.
25
+ */
26
+ return new _uncaughtError.UncaughtError(error?.message !== undefined ? error.message : error, error?.filename || error?.sourceURL, error?.lineno || error?.line, error?.colno || error?.col, error?.__newrelic);
27
+ }
28
+
29
+ /**
30
+ * Attempts to convert a PromiseRejectionEvent object to an Error object
31
+ * @param {PromiseRejectionEvent} unhandledRejectionEvent The unhandled promise rejection event
32
+ * @returns {Error} An Error object with the message as the casted reason
33
+ */
34
+ function castPromiseRejectionEvent(promiseRejectionEvent) {
35
+ let prefix = 'Unhandled Promise Rejection';
36
+ if (canTrustError(promiseRejectionEvent?.reason)) {
37
+ try {
38
+ promiseRejectionEvent.reason.message = prefix + ': ' + promiseRejectionEvent.reason.message;
39
+ return castError(promiseRejectionEvent.reason);
40
+ } catch (e) {
41
+ return castError(promiseRejectionEvent.reason);
42
+ }
43
+ }
44
+ if (typeof promiseRejectionEvent.reason === 'undefined') return castError(prefix);
45
+ const error = castError(promiseRejectionEvent.reason);
46
+ error.message = prefix + ': ' + error?.message;
47
+ return error;
48
+ }
49
+
50
+ /**
51
+ * Attempts to convert an ErrorEvent object to an Error object
52
+ * @param {ErrorEvent} errorEvent The error event
53
+ * @returns {Error|UncaughtError} The error event converted to an Error object
54
+ */
55
+ function castErrorEvent(errorEvent) {
56
+ if (errorEvent.error instanceof SyntaxError && !/:\d+$/.test(errorEvent.error.stack?.trim())) {
57
+ const error = new _uncaughtError.UncaughtError(errorEvent.message, errorEvent.filename, errorEvent.lineno, errorEvent.colno, errorEvent.error.__newrelic);
58
+ error.name = SyntaxError.name;
59
+ return error;
60
+ }
61
+ if (canTrustError(errorEvent.error)) return errorEvent.error;
62
+ return castError(errorEvent);
63
+ }
64
+ function canTrustError(error) {
65
+ return error instanceof Error && !!error.stack;
66
+ }
@@ -4,6 +4,7 @@ Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
6
  exports.UncaughtError = void 0;
7
+ var _stringify = require("../../../common/util/stringify");
7
8
  /**
8
9
  * Represents an uncaught non Error type error. This class does
9
10
  * not extend the Error class to prevent an invalid stack trace
@@ -11,12 +12,13 @@ exports.UncaughtError = void 0;
11
12
  * do not use the Error class (strings, etc) to an object.
12
13
  */
13
14
  class UncaughtError {
14
- constructor(message, filename, lineno, colno) {
15
+ constructor(message, filename, lineno, colno, newrelic) {
15
16
  this.name = 'UncaughtError';
16
- this.message = message;
17
+ this.message = typeof message === 'string' ? message : (0, _stringify.stringify)(message);
17
18
  this.sourceURL = filename;
18
19
  this.line = lineno;
19
20
  this.column = colno;
21
+ this.__newrelic = newrelic;
20
22
  }
21
23
  }
22
24
  exports.UncaughtError = UncaughtError;
@@ -103,14 +103,14 @@ class Aggregate extends _aggregateBase.AggregateBase {
103
103
  // Navigation Timing level 2 API that replaced PerformanceTiming & PerformanceNavigation
104
104
  const navTimingEntry = _runtime.globalScope?.performance?.getEntriesByType('navigation')?.[0];
105
105
  const perf = {
106
- timing: (0, _navTiming.addPT)(agentRuntime.offset, navTimingEntry, {}),
106
+ timing: (0, _navTiming.addPT)(_runtime.originTime, navTimingEntry, {}),
107
107
  navigation: (0, _navTiming.addPN)(navTimingEntry, {})
108
108
  };
109
109
  queryParameters.perf = (0, _stringify.stringify)(perf);
110
110
  } else if (typeof PerformanceTiming !== 'undefined') {
111
111
  // Safari pre-15 did not support level 2 timing
112
112
  const perf = {
113
- timing: (0, _navTiming.addPT)(agentRuntime.offset, _runtime.globalScope.performance.timing, {}, true),
113
+ timing: (0, _navTiming.addPT)(_runtime.originTime, _runtime.globalScope.performance.timing, {}, true),
114
114
  navigation: (0, _navTiming.addPN)(_runtime.globalScope.performance.navigation, {})
115
115
  };
116
116
  queryParameters.perf = (0, _stringify.stringify)(perf);
@@ -142,7 +142,7 @@ class Aggregate extends _aggregateBase.AggregateBase {
142
142
  return;
143
143
  }
144
144
  try {
145
- const timeKeeper = new _timeKeeper.TimeKeeper();
145
+ const timeKeeper = new _timeKeeper.TimeKeeper(this.agentIdentifier);
146
146
  timeKeeper.processRumRequest(xhr, rumStartTime, rumEndTime);
147
147
  if (!timeKeeper.ready) throw new Error('TimeKeeper not ready');
148
148
  agentRuntime.timeKeeper = timeKeeper;
@@ -373,6 +373,7 @@ class Aggregate extends _aggregateBase.AggregateBase {
373
373
  ...(agentMetadata.entityGuid && {
374
374
  entityGuid: agentMetadata.entityGuid
375
375
  }),
376
+ harvestId: [agentRuntime.session?.state.value, agentRuntime.ptid, agentRuntime.harvestCount].filter(x => x).join('_'),
376
377
  'replay.firstTimestamp': firstTimestamp,
377
378
  'replay.lastTimestamp': lastTimestamp,
378
379
  'replay.nodes': events.length,
@@ -32,8 +32,8 @@ function buildNRMetaNode(timestamp, timeKeeper) {
32
32
  originalTimestamp: timestamp,
33
33
  correctedTimestamp,
34
34
  timestampDiff: timestamp - correctedTimestamp,
35
- timeKeeperOriginTime: timeKeeper.originTime,
36
- timeKeeperCorrectedOriginTime: timeKeeper.correctedOriginTime,
37
- timeKeeperDiff: Math.floor(timeKeeper.originTime - timeKeeper.correctedOriginTime)
35
+ originTime: _runtime.originTime,
36
+ correctedOriginTime: timeKeeper.correctedOriginTime,
37
+ originTimeDiff: Math.floor(_runtime.originTime - timeKeeper.correctedOriginTime)
38
38
  };
39
39
  }
@@ -14,6 +14,7 @@ var _replayMode = require("../../session_replay/shared/replay-mode");
14
14
  var _aggregateBase = require("../../utils/aggregate-base");
15
15
  var _constants2 = require("../../../common/session/constants");
16
16
  var _now = require("../../../common/timing/now");
17
+ var _runtime = require("../../../common/constants/runtime");
17
18
  /*
18
19
  * Copyright 2020 New Relic Corporation. All rights reserved.
19
20
  * SPDX-License-Identifier: Apache-2.0
@@ -53,14 +54,12 @@ class Aggregate extends _aggregateBase.AggregateBase {
53
54
  super(agentIdentifier, aggregator, _constants.FEATURE_NAME);
54
55
  _this = this;
55
56
  this.agentRuntime = (0, _config.getRuntime)(agentIdentifier);
56
-
57
- // Very unlikely, but in case the existing XMLHttpRequest.prototype object on the page couldn't be wrapped.
58
- if (!this.agentRuntime.xhrWrappable) return;
59
57
  this.resourceObserver = argsObj?.resourceObserver; // undefined if observer couldn't be created
60
58
  this.ptid = '';
61
59
  this.trace = {};
62
60
  this.nodeCount = 0;
63
61
  this.sentTrace = null;
62
+ this.prevStoredEvents = new Set();
64
63
  this.harvestTimeSeconds = (0, _config.getConfigurationValue)(agentIdentifier, 'session_trace.harvestTimeSeconds') || 10;
65
64
  this.maxNodesPerHarvest = (0, _config.getConfigurationValue)(agentIdentifier, 'session_trace.maxNodesPerHarvest') || 1000;
66
65
  /**
@@ -128,7 +127,7 @@ class Aggregate extends _aggregateBase.AggregateBase {
128
127
  return controlTraceOp(on);
129
128
  }, this.featureName, this.ee);
130
129
  } else {
131
- (0, _registerHandler.registerHandler)('errorAgg', () => {
130
+ (0, _registerHandler.registerHandler)('trace-jserror', () => {
132
131
  seenAnError = true;
133
132
  switchToFull();
134
133
  }, this.featureName, this.ee);
@@ -223,7 +222,7 @@ class Aggregate extends _aggregateBase.AggregateBase {
223
222
  }
224
223
  return operationalGate.settle(() => _this.storeSTN(...args));
225
224
  }, this.featureName, this.ee);
226
- (0, _registerHandler.registerHandler)('errorAgg', function () {
225
+ (0, _registerHandler.registerHandler)('trace-jserror', function () {
227
226
  for (var _len6 = arguments.length, args = new Array(_len6), _key6 = 0; _key6 < _len6; _key6++) {
228
227
  args[_key6] = arguments[_key6];
229
228
  }
@@ -272,6 +271,7 @@ class Aggregate extends _aggregateBase.AggregateBase {
272
271
  }
273
272
  }
274
273
  #prepareHarvest(options) {
274
+ this.prevStoredEvents.clear(); // release references to past events for GC
275
275
  if (this.isStandalone) {
276
276
  if (this.ptid && (0, _now.now)() >= MAX_TRACE_DURATION) {
277
277
  // Perform a final harvest once we hit or exceed the max session trace time
@@ -338,6 +338,8 @@ class Aggregate extends _aggregateBase.AggregateBase {
338
338
  // Tracks the events and their listener's duration on objects wrapped by wrap-events.
339
339
  storeEvent(currentEvent, target, start, end) {
340
340
  if (this.shouldIgnoreEvent(currentEvent, target)) return;
341
+ if (this.prevStoredEvents.has(currentEvent)) return; // prevent multiple listeners of an event from creating duplicate trace nodes per occurrence. Cleared every harvest. near-zero chance for re-duplication after clearing per harvest since the timestamps of the event are considered for uniqueness.
342
+ this.prevStoredEvents.add(currentEvent);
341
343
  const evt = {
342
344
  n: this.evtName(currentEvent.type),
343
345
  s: start,
@@ -538,13 +540,13 @@ class Aggregate extends _aggregateBase.AggregateBase {
538
540
  }
539
541
  return {
540
542
  qs: {
541
- st: this.agentRuntime.offset,
543
+ st: _runtime.originTime,
542
544
  /** hr === "hasReplay" in NR1, standalone is always checked and processed before harvesting
543
545
  * so a race condition between ST and SR states should not be a concern if implemented here */
544
546
  hr: Number(!this.isStandalone),
545
547
  /** fts === "firstTimestamp" in NR1, indicates what the earliest NODE timestamp was
546
548
  * so that blob parsing doesn't need to happen to support UI/API functions */
547
- fts: this.agentRuntime.offset + earliestTimeStamp,
549
+ fts: _runtime.originTime + earliestTimeStamp,
548
550
  /** n === "nodeCount" in NR1, a count of nodes in the ST payload, so that blob parsing doesn't need to happen to support UI/API functions */
549
551
  n: stns.length,
550
552
  // node count
@@ -651,7 +651,7 @@ class Aggregate extends _aggregateBase.AggregateBase {
651
651
  state.interactionsSent = [];
652
652
  }
653
653
  }
654
- baseEE.on('errorAgg', function (type, name, params, metrics) {
654
+ baseEE.on('spa-jserror', function (type, name, params, metrics) {
655
655
  if (!state.currentNode) return;
656
656
  params._interactionId = state.currentNode.interaction.id;
657
657
  // do not capture parentNodeId when in root node
@@ -659,6 +659,16 @@ class Aggregate extends _aggregateBase.AggregateBase {
659
659
  params._interactionNodeId = state.currentNode.id;
660
660
  }
661
661
  });
662
+ (0, _registerHandler.registerHandler)('function-err', function (args, obj, error) {
663
+ if (!state.currentNode) return;
664
+ error.__newrelic ??= {};
665
+ error.__newrelic[agentIdentifier] = {
666
+ interactionId: state.currentNode.interaction.id
667
+ };
668
+ if (state.currentNode.type && state.currentNode.type !== 'interaction') {
669
+ error.__newrelic[agentIdentifier].interactionNodeId = state.currentNode.id;
670
+ }
671
+ }, this.featureName, baseEE);
662
672
  baseEE.on('interaction', saveInteraction);
663
673
  function getActionText(node) {
664
674
  var nodeType = node.tagName.toLowerCase();
@@ -7,10 +7,10 @@ exports.Instrument = void 0;
7
7
  var _wrap = require("../../../common/wrap");
8
8
  var _eventListenerOpts = require("../../../common/event-listener/event-listener-opts");
9
9
  var _instrumentBase = require("../../utils/instrument-base");
10
- var _config = require("../../../common/config/config");
11
10
  var CONSTANTS = _interopRequireWildcard(require("../constants"));
12
11
  var _runtime = require("../../../common/constants/runtime");
13
12
  var _now = require("../../../common/timing/now");
13
+ var _handle = require("../../../common/event-emitter/handle");
14
14
  function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); }
15
15
  function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && Object.prototype.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; }
16
16
  /*
@@ -33,11 +33,12 @@ const {
33
33
  class Instrument extends _instrumentBase.InstrumentBase {
34
34
  static featureName = FEATURE_NAME;
35
35
  constructor(agentIdentifier, aggregator) {
36
+ var _this;
36
37
  let auto = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : true;
37
38
  super(agentIdentifier, aggregator, FEATURE_NAME, auto);
39
+ _this = this;
38
40
  if (!_runtime.isBrowserScope) return; // SPA not supported outside web env
39
41
 
40
- if (!(0, _config.getRuntime)(agentIdentifier).xhrWrappable) return;
41
42
  try {
42
43
  this.removeOnAbort = new AbortController();
43
44
  } catch (e) {}
@@ -58,6 +59,12 @@ class Instrument extends _instrumentBase.InstrumentBase {
58
59
  this.ee.on(FN_END, endTimestamp);
59
60
  promiseEE.on(CB_END, endTimestamp);
60
61
  jsonpEE.on(CB_END, endTimestamp);
62
+ this.ee.on('fn-err', function () {
63
+ for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
64
+ args[_key] = arguments[_key];
65
+ }
66
+ if (!args[2]?.__newrelic?.[agentIdentifier]) (0, _handle.handle)('function-err', [...args], undefined, _this.featureName, _this.ee);
67
+ });
61
68
  this.ee.buffer([FN_START, FN_END, 'xhr-resolved'], this.featureName);
62
69
  eventsEE.buffer([FN_START], this.featureName);
63
70
  timerEE.buffer(['setTimeout' + END, 'clearTimeout' + START, FN_START], this.featureName);
@@ -10,20 +10,16 @@ var _contextualEe = require("../../common/event-emitter/contextual-ee");
10
10
  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
- var _firstPartyCookies = require("../../common/storage/first-party-cookies");
14
13
  var _constants = require("../../common/session/constants");
15
14
  let ranOnce = 0;
16
15
  function setupAgentSession(agentIdentifier) {
17
16
  const agentRuntime = (0, _config.getRuntime)(agentIdentifier);
18
17
  if (ranOnce++) return agentRuntime.session;
19
18
  const sessionInit = (0, _config.getConfiguration)(agentIdentifier).session;
20
- /* Domain is a string that can be specified by customer. The only way to keep the session object across subdomains is using first party cookies.
21
- This determines which storage wrapper the session manager will use to keep state. */
22
- const storageTypeInst = sessionInit?.domain ? new _firstPartyCookies.FirstPartyCookies(sessionInit.domain) : new _localStorage.LocalStorage();
23
19
  agentRuntime.session = new _sessionEntity.SessionEntity({
24
20
  agentIdentifier,
25
21
  key: _constants.DEFAULT_KEY,
26
- storage: storageTypeInst,
22
+ storage: new _localStorage.LocalStorage(),
27
23
  expiresMs: sessionInit?.expiresMs,
28
24
  inactiveMs: sessionInit?.inactiveMs
29
25
  });
@@ -57,7 +57,7 @@ class InstrumentBase extends _featureBase.FeatureBase {
57
57
  if ((0, _config.getConfigurationValue)(this.agentIdentifier, "".concat(this.featureName, ".autoStart")) === false) this.auto = false;
58
58
  /** if the feature requires opt-in (!auto-start), it will get registered once the api has been called */
59
59
  if (this.auto) (0, _drain.registerDrain)(agentIdentifier, featureName);else {
60
- this.ee.on("".concat(this.featureName, "-opt-in"), (0, _invoke.single)(() => {
60
+ this.ee.on('manual-start-all', (0, _invoke.single)(() => {
61
61
  // register the feature to drain only once the API has been called, it will drain when importAggregator finishes for all the features
62
62
  // called by the api in that cycle
63
63
  (0, _drain.registerDrain)(this.agentIdentifier, this.featureName);
@@ -133,19 +133,10 @@ function setAPI(agentIdentifier, forceDrain) {
133
133
  }
134
134
  return appendJsAttribute('application.version', value, 'setApplicationVersion', false);
135
135
  };
136
- apiInterface.start = features => {
136
+ apiInterface.start = () => {
137
137
  try {
138
- const smTag = !features ? 'undefined' : 'defined';
139
- (0, _handle.handle)(_constants.SUPPORTABILITY_METRIC_CHANNEL, ["API/start/".concat(smTag, "/called")], undefined, _features.FEATURE_NAMES.metrics, instanceEE);
140
- const featNames = Object.values(_features.FEATURE_NAMES);
141
- if (features === undefined) features = featNames;else {
142
- features = Array.isArray(features) && features.length ? features : [features];
143
- if (features.some(f => !featNames.includes(f))) return (0, _console.warn)("Invalid feature name supplied. Acceptable feature names are: ".concat(featNames));
144
- if (!features.includes(_features.FEATURE_NAMES.pageViewEvent)) features.push(_features.FEATURE_NAMES.pageViewEvent);
145
- }
146
- features.forEach(feature => {
147
- instanceEE.emit("".concat(feature, "-opt-in"));
148
- });
138
+ (0, _handle.handle)(_constants.SUPPORTABILITY_METRIC_CHANNEL, ['API/start/called'], undefined, _features.FEATURE_NAMES.metrics, instanceEE);
139
+ instanceEE.emit('manual-start-all');
149
140
  } catch (err) {
150
141
  (0, _console.warn)('An unexpected issue occurred', err);
151
142
  }
@@ -176,9 +167,10 @@ function setAPI(agentIdentifier, forceDrain) {
176
167
  try {
177
168
  return cb.apply(this, arguments);
178
169
  } catch (err) {
179
- tracerEE.emit('fn-err', [arguments, this, err], contextStore);
170
+ const error = typeof err === 'string' ? new Error(err) : err;
171
+ tracerEE.emit('fn-err', [arguments, this, error], contextStore);
180
172
  // the error came from outside the agent, so don't swallow
181
- throw err;
173
+ throw error;
182
174
  } finally {
183
175
  tracerEE.emit('fn-end', [(0, _now.now)()], contextStore);
184
176
  }
@@ -11,6 +11,7 @@ var _handle = require("../../common/event-emitter/handle");
11
11
  var _registerHandler = require("../../common/event-emitter/register-handler");
12
12
  var _invoke = require("../../common/util/invoke");
13
13
  var _constants = require("../../features/metrics/constants");
14
+ var _runtime = require("../../common/constants/runtime");
14
15
  function setAPI(agentIdentifier) {
15
16
  var instanceEE = _contextualEe.ee.get(agentIdentifier);
16
17
  var api = {
@@ -30,13 +31,13 @@ function setAPI(agentIdentifier) {
30
31
  // first parameter. These functions can be called asynchronously.
31
32
 
32
33
  function finished(t, providedTime) {
33
- var time = providedTime ? providedTime - (0, _config.getRuntime)(agentIdentifier).offset : t;
34
+ var time = providedTime ? providedTime - _runtime.originTime : t;
34
35
  (0, _handle.handle)(_constants.CUSTOM_METRIC_CHANNEL, ['finished', {
35
36
  time
36
37
  }], undefined, _features.FEATURE_NAMES.metrics, instanceEE);
37
38
  addToTrace(t, {
38
39
  name: 'finished',
39
- start: time + (0, _config.getRuntime)(agentIdentifier).offset,
40
+ start: time + _runtime.originTime,
40
41
  origin: 'nr'
41
42
  });
42
43
  (0, _handle.handle)('api-addPageAction', [time, 'finished'], undefined, _features.FEATURE_NAMES.pageAction, instanceEE);
@@ -45,8 +46,8 @@ function setAPI(agentIdentifier) {
45
46
  if (!(evt && typeof evt === 'object' && evt.name && evt.start)) return;
46
47
  var report = {
47
48
  n: evt.name,
48
- s: evt.start - (0, _config.getRuntime)(agentIdentifier).offset,
49
- e: (evt.end || evt.start) - (0, _config.getRuntime)(agentIdentifier).offset,
49
+ s: evt.start - _runtime.originTime,
50
+ e: (evt.end || evt.start) - _runtime.originTime,
50
51
  o: evt.origin || '',
51
52
  t: 'api'
52
53
  };
@@ -7,12 +7,15 @@ export function getModeledObject(obj, model) {
7
7
  const output = Object.create(Object.getPrototypeOf(model), Object.getOwnPropertyDescriptors(model));
8
8
  const target = Object.keys(output).length === 0 ? obj : output;
9
9
  for (let key in target) {
10
- if (obj[key] !== undefined) {
11
- try {
12
- if (Array.isArray(obj[key]) && Array.isArray(model[key])) output[key] = Array.from(new Set([...obj[key], ...model[key]]));else if (typeof obj[key] === 'object' && typeof model[key] === 'object') output[key] = getModeledObject(obj[key], model[key]);else output[key] = obj[key];
13
- } catch (e) {
14
- warn('An error occurred while setting a property of a Configurable', e);
10
+ if (obj[key] === undefined) continue;
11
+ try {
12
+ if (obj[key] === null) {
13
+ output[key] = null;
14
+ continue;
15
15
  }
16
+ if (Array.isArray(obj[key]) && Array.isArray(model[key])) output[key] = Array.from(new Set([...obj[key], ...model[key]]));else if (typeof obj[key] === 'object' && typeof model[key] === 'object') output[key] = getModeledObject(obj[key], model[key]);else output[key] = obj[key];
17
+ } catch (e) {
18
+ warn('An error occurred while setting a property of a Configurable', e);
16
19
  }
17
20
  }
18
21
  return output;
@@ -54,8 +54,6 @@ const model = () => {
54
54
  allowed_origins: undefined
55
55
  },
56
56
  session: {
57
- domain: undefined,
58
- // used by first party cookies to set the top-level domain
59
57
  expiresMs: DEFAULT_EXPIRES_MS,
60
58
  inactiveMs: DEFAULT_INACTIVE_MS
61
59
  },
@@ -1,18 +1,19 @@
1
1
  import { getModeledObject } from './configurable';
2
2
  import { getNREUMInitializedAgent } from '../../window/nreum';
3
- import { globalScope } from '../../constants/runtime';
3
+ import { globalScope, originTime } from '../../constants/runtime';
4
4
  import { BUILD_ENV, DIST_METHOD, VERSION } from "../../constants/env.npm";
5
- const model = {
5
+ const readonly = {
6
6
  buildEnv: BUILD_ENV,
7
+ distMethod: DIST_METHOD,
8
+ version: VERSION,
9
+ originTime
10
+ };
11
+ const model = {
7
12
  customTransaction: undefined,
8
13
  disabled: false,
9
- distMethod: DIST_METHOD,
10
14
  isolatedBacklog: false,
11
15
  loaderType: undefined,
12
16
  maxBytes: 30000,
13
- // The "timeOrigin" property is the new standard timestamp property shared across main frame and workers, but is not supported in some early Safari browsers (safari<15) + IE
14
- // ingest expects an integer value, and timeOrigin can return a float.
15
- offset: Math.floor(globalScope?.performance?.timeOrigin || globalScope?.performance?.timing?.navigationStart || Date.now()),
16
17
  onerror: undefined,
17
18
  origin: '' + globalScope.location,
18
19
  ptid: undefined,
@@ -20,8 +21,6 @@ const model = {
20
21
  /** Agent-specific metadata found in the RUM call response. ex. entityGuid */
21
22
  appMetadata: {},
22
23
  session: undefined,
23
- xhrWrappable: typeof globalScope.XMLHttpRequest?.prototype?.addEventListener === 'function',
24
- version: VERSION,
25
24
  denyList: undefined,
26
25
  harvestCount: 0,
27
26
  timeKeeper: undefined
@@ -34,7 +33,10 @@ export function getRuntime(id) {
34
33
  }
35
34
  export function setRuntime(id, obj) {
36
35
  if (!id) throw new Error('All runtime objects require an agent identifier!');
37
- _cache[id] = getModeledObject(obj, model);
36
+ _cache[id] = {
37
+ ...getModeledObject(obj, model),
38
+ ...readonly
39
+ };
38
40
  const agentInst = getNREUMInitializedAgent(id);
39
41
  if (agentInst) agentInst.runtime = _cache[id];
40
42
  }
@@ -6,7 +6,7 @@
6
6
  /**
7
7
  * Exposes the version of the agent
8
8
  */
9
- export const VERSION = "1.257.0";
9
+ export const VERSION = "1.258.1";
10
10
 
11
11
  /**
12
12
  * Exposes the build type of the agent
@@ -6,7 +6,7 @@
6
6
  /**
7
7
  * Exposes the version of the agent
8
8
  */
9
- export const VERSION = "1.257.0";
9
+ export const VERSION = "1.258.1";
10
10
 
11
11
  /**
12
12
  * Exposes the build type of the agent
@@ -38,4 +38,10 @@ export const ffVersion = (() => {
38
38
  export const isIE = Boolean(isBrowserScope && window.document.documentMode); // deprecated property that only works in IE
39
39
 
40
40
  export const supportsSendBeacon = !!globalScope.navigator?.sendBeacon;
41
- export const offset = Math.floor(Date.now() - performance.now());
41
+
42
+ /**
43
+ * Represents the absolute timestamp in milliseconds that the page was loaded
44
+ * according to the browser's local clock.
45
+ * @type {number}
46
+ */
47
+ export const originTime = Math.floor(Date.now() - performance.now());
@@ -10,14 +10,8 @@ var denyList = [];
10
10
  * @returns {boolean} `true` if request does not match any entries of {@link denyList|deny list}; else `false`
11
11
  */
12
12
  export function shouldCollectEvent(params) {
13
- if (denyList.length === 0) {
14
- return true;
15
- }
16
-
17
- // XHR requests with an undefined hostname (e.g., data URLs) should not be collected.
18
- if (params.hostname === undefined) {
19
- return false;
20
- }
13
+ if (hasUndefinedHostname(params)) return false;
14
+ if (denyList.length === 0) return true;
21
15
  for (var i = 0; i < denyList.length; i++) {
22
16
  var parsed = denyList[i];
23
17
  if (parsed.hostname === '*') {
@@ -29,6 +23,9 @@ export function shouldCollectEvent(params) {
29
23
  }
30
24
  return true;
31
25
  }
26
+ export function hasUndefinedHostname(params) {
27
+ return params.hostname === undefined; // requests with an undefined hostname (e.g., data URLs) should not be collected.
28
+ }
32
29
 
33
30
  /**
34
31
  * Initializes the {@link denyList|XHR deny list} by extracting hostname and pathname from an array of filter strings.
@@ -3,6 +3,7 @@ export const DEFAULT_KEY = 'SESSION';
3
3
  export const DEFAULT_EXPIRES_MS = 14400000;
4
4
  export const DEFAULT_INACTIVE_MS = 1800000;
5
5
  export const SESSION_EVENTS = {
6
+ STARTED: 'session-started',
6
7
  PAUSE: 'session-pause',
7
8
  RESET: 'session-reset',
8
9
  RESUME: 'session-resume',
@@ -24,6 +24,8 @@ const model = {
24
24
  sessionReplaySentFirstChunk: false,
25
25
  sessionTraceMode: MODE.OFF,
26
26
  traceHarvestStarted: false,
27
+ serverTimeDiff: null,
28
+ // set by TimeKeeper; "undefined" value will not be stringified and stored but "null" will
27
29
  custom: {}
28
30
  };
29
31
  export class SessionEntity {
@@ -138,6 +140,7 @@ export class SessionEntity {
138
140
  // we can use a modeled object here to help us know and manage what values are being used. -- see "model" above
139
141
  if (this.isNew) this.write(getModeledObject(this.state, model), true);else this.sync(initialRead);
140
142
  this.initialized = true;
143
+ this.ee.emit(SESSION_EVENTS.STARTED, [this.isNew]);
141
144
  }
142
145
 
143
146
  // This is the actual key appended to the storage API
@@ -1,3 +1,8 @@
1
+ import { originTime } from '../constants/runtime';
2
+ import { ee as baseEE } from '../event-emitter/contextual-ee';
3
+ import { getRuntime } from '../config/config';
4
+ import { SESSION_EVENT_TYPES, SESSION_EVENTS } from '../session/constants';
5
+
1
6
  /**
2
7
  * Class used to adjust the timestamp of harvested data to New Relic server time. This
3
8
  * is done by tracking the performance timings of the RUM call and applying a calculation
@@ -5,10 +10,10 @@
5
10
  */
6
11
  export class TimeKeeper {
7
12
  /**
8
- * Represents the browser origin time.
9
- * @type {number}
13
+ * Pointer to the current agent session if it exists.
14
+ * @type {import('../session/session-entity').SessionEntity}
10
15
  */
11
- #originTime;
16
+ #session;
12
17
 
13
18
  /**
14
19
  * Represents the browser origin time corrected to NR server time.
@@ -29,15 +34,24 @@ export class TimeKeeper {
29
34
  * @type {number}
30
35
  */
31
36
  #ready = false;
32
- constructor() {
33
- this.#originTime = Date.now() - performance.now();
37
+ constructor(agentIdentifier) {
38
+ this.#session = getRuntime(agentIdentifier)?.session;
39
+ if (this.#session) {
40
+ const ee = baseEE.get(agentIdentifier);
41
+ ee.on(SESSION_EVENTS.UPDATE, this.#processSessionUpdate.bind(this));
42
+ ee.on(SESSION_EVENTS.STARTED, () => {
43
+ if (this.#ready) {
44
+ this.#session.write({
45
+ serverTimeDiff: this.#localTimeDiff
46
+ });
47
+ }
48
+ });
49
+ this.#processSessionUpdate(null, this.#session.read());
50
+ }
34
51
  }
35
52
  get ready() {
36
53
  return this.#ready;
37
54
  }
38
- get originTime() {
39
- return this.#originTime;
40
- }
41
55
  get correctedOriginTime() {
42
56
  return this.#correctedOriginTime;
43
57
  }
@@ -49,6 +63,8 @@ export class TimeKeeper {
49
63
  * @param endTime {number} The end time of the RUM request
50
64
  */
51
65
  processRumRequest(rumRequest, startTime, endTime) {
66
+ if (this.#ready) return; // Server time calculated from session entity
67
+
52
68
  const responseDateHeader = rumRequest.getResponseHeader('Date');
53
69
  if (!responseDateHeader) {
54
70
  throw new Error('Missing date header on rum response.');
@@ -58,10 +74,13 @@ export class TimeKeeper {
58
74
 
59
75
  // Corrected page origin time
60
76
  this.#correctedOriginTime = Math.floor(Date.parse(responseDateHeader) - serverOffset);
61
- this.#localTimeDiff = this.#originTime - this.#correctedOriginTime;
77
+ this.#localTimeDiff = originTime - this.#correctedOriginTime;
62
78
  if (Number.isNaN(this.#correctedOriginTime)) {
63
79
  throw new Error('Date header invalid format.');
64
80
  }
81
+ if (this.#session) this.#session.write({
82
+ serverTimeDiff: this.#localTimeDiff
83
+ });
65
84
  this.#ready = true;
66
85
  }
67
86
 
@@ -83,4 +102,22 @@ export class TimeKeeper {
83
102
  correctAbsoluteTimestamp(timestamp) {
84
103
  return Math.floor(timestamp - this.#localTimeDiff);
85
104
  }
105
+
106
+ /**
107
+ * Processes a session entity update payload to extract the server time calculated.
108
+ * @param {import('../session/constants').SESSION_EVENT_TYPES | null} type
109
+ * @param {Object} data
110
+ */
111
+ #processSessionUpdate(type, data) {
112
+ if (typeof data?.serverTimeDiff !== 'number') return;
113
+ if (!type && !this.#ready ||
114
+ // This captures the initial read from the session entity when the timekeeper first initializes
115
+ type === SESSION_EVENT_TYPES.CROSS_TAB // This captures any cross-tab write of the session entity
116
+ ) {
117
+ // This captures the initial read from the session entity when the timekeeper first initializes
118
+ this.#localTimeDiff = data.serverTimeDiff;
119
+ this.#correctedOriginTime = originTime - this.#localTimeDiff;
120
+ this.#ready = true;
121
+ }
122
+ }
86
123
  }