@newrelic/browser-agent 1.290.1 → 1.291.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 (62) hide show
  1. package/CHANGELOG.md +13 -0
  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/harvest/harvester.js +0 -7
  5. package/dist/cjs/common/session/session-entity.js +5 -5
  6. package/dist/cjs/common/util/console.js +12 -0
  7. package/dist/cjs/features/generic_events/aggregate/index.js +16 -0
  8. package/dist/cjs/features/generic_events/instrument/index.js +2 -0
  9. package/dist/cjs/features/session_replay/aggregate/index.js +3 -0
  10. package/dist/cjs/features/session_replay/shared/recorder.js +0 -4
  11. package/dist/cjs/features/session_trace/aggregate/index.js +6 -2
  12. package/dist/cjs/features/session_trace/aggregate/trace/storage.js +0 -7
  13. package/dist/cjs/loaders/api/constants.js +3 -2
  14. package/dist/cjs/loaders/api/measure.js +60 -0
  15. package/dist/cjs/loaders/api-base.js +11 -0
  16. package/dist/esm/common/constants/env.cdn.js +1 -1
  17. package/dist/esm/common/constants/env.npm.js +1 -1
  18. package/dist/esm/common/harvest/harvester.js +0 -7
  19. package/dist/esm/common/session/session-entity.js +5 -5
  20. package/dist/esm/common/util/console.js +13 -0
  21. package/dist/esm/features/generic_events/aggregate/index.js +16 -0
  22. package/dist/esm/features/generic_events/instrument/index.js +2 -0
  23. package/dist/esm/features/session_replay/aggregate/index.js +3 -0
  24. package/dist/esm/features/session_replay/shared/recorder.js +0 -4
  25. package/dist/esm/features/session_trace/aggregate/index.js +6 -2
  26. package/dist/esm/features/session_trace/aggregate/trace/storage.js +0 -7
  27. package/dist/esm/loaders/api/constants.js +2 -1
  28. package/dist/esm/loaders/api/measure.js +53 -0
  29. package/dist/esm/loaders/api-base.js +12 -1
  30. package/dist/tsconfig.tsbuildinfo +1 -1
  31. package/dist/types/common/harvest/harvester.d.ts.map +1 -1
  32. package/dist/types/common/session/session-entity.d.ts +0 -1
  33. package/dist/types/common/session/session-entity.d.ts.map +1 -1
  34. package/dist/types/common/util/console.d.ts +0 -4
  35. package/dist/types/common/util/console.d.ts.map +1 -1
  36. package/dist/types/features/generic_events/aggregate/index.d.ts.map +1 -1
  37. package/dist/types/features/generic_events/instrument/index.d.ts.map +1 -1
  38. package/dist/types/features/session_replay/aggregate/index.d.ts.map +1 -1
  39. package/dist/types/features/session_replay/shared/recorder.d.ts.map +1 -1
  40. package/dist/types/features/session_trace/aggregate/index.d.ts +1 -1
  41. package/dist/types/features/session_trace/aggregate/index.d.ts.map +1 -1
  42. package/dist/types/features/session_trace/aggregate/trace/storage.d.ts +0 -1
  43. package/dist/types/features/session_trace/aggregate/trace/storage.d.ts.map +1 -1
  44. package/dist/types/loaders/api/constants.d.ts +1 -0
  45. package/dist/types/loaders/api/constants.d.ts.map +1 -1
  46. package/dist/types/loaders/api/measure.d.ts +2 -0
  47. package/dist/types/loaders/api/measure.d.ts.map +1 -0
  48. package/dist/types/loaders/api-base.d.ts +13 -0
  49. package/dist/types/loaders/api-base.d.ts.map +1 -1
  50. package/package.json +1 -1
  51. package/src/common/harvest/harvester.js +0 -5
  52. package/src/common/session/session-entity.js +5 -6
  53. package/src/common/util/console.js +13 -0
  54. package/src/features/generic_events/aggregate/index.js +15 -0
  55. package/src/features/generic_events/instrument/index.js +2 -0
  56. package/src/features/session_replay/aggregate/index.js +4 -0
  57. package/src/features/session_replay/shared/recorder.js +0 -4
  58. package/src/features/session_trace/aggregate/index.js +6 -2
  59. package/src/features/session_trace/aggregate/trace/storage.js +0 -8
  60. package/src/loaders/api/constants.js +1 -0
  61. package/src/loaders/api/measure.js +53 -0
  62. package/src/loaders/api-base.js +12 -1
package/CHANGELOG.md CHANGED
@@ -3,6 +3,19 @@
3
3
  All notable changes to this project will be documented in this file.
4
4
  See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
5
5
 
6
+ ## [1.291.0](https://github.com/newrelic/newrelic-browser-agent/compare/v1.290.1...v1.291.0) (2025-05-30)
7
+
8
+
9
+ ### Features
10
+
11
+ * Create the measure API ([#1476](https://github.com/newrelic/newrelic-browser-agent/issues/1476)) ([f944b76](https://github.com/newrelic/newrelic-browser-agent/commit/f944b76c2137e6d75e47e692ded4ba5f04bb4b6d))
12
+
13
+
14
+ ### Bug Fixes
15
+
16
+ * Fix race between end of session and features aborting ([#1487](https://github.com/newrelic/newrelic-browser-agent/issues/1487)) ([531f8d4](https://github.com/newrelic/newrelic-browser-agent/commit/531f8d4228f6c7ead8e2342c8a8dd25c651e9ec6))
17
+ * Harvest first session trace payload immediately ([#1483](https://github.com/newrelic/newrelic-browser-agent/issues/1483)) ([50f4ace](https://github.com/newrelic/newrelic-browser-agent/commit/50f4acea3e27c6b5e65d02ffb46af02b351e4500))
18
+
6
19
  ## [1.290.1](https://github.com/newrelic/newrelic-browser-agent/compare/v1.290.0...v1.290.1) (2025-05-21)
7
20
 
8
21
 
@@ -17,7 +17,7 @@ exports.VERSION = exports.RRWEB_VERSION = exports.DIST_METHOD = exports.BUILD_EN
17
17
  /**
18
18
  * Exposes the version of the agent
19
19
  */
20
- const VERSION = exports.VERSION = "1.290.1";
20
+ const VERSION = exports.VERSION = "1.291.0";
21
21
 
22
22
  /**
23
23
  * Exposes the build type of the agent
@@ -17,7 +17,7 @@ exports.VERSION = exports.RRWEB_VERSION = exports.DIST_METHOD = exports.BUILD_EN
17
17
  /**
18
18
  * Exposes the version of the agent
19
19
  */
20
- const VERSION = exports.VERSION = "1.290.1";
20
+ const VERSION = exports.VERSION = "1.291.0";
21
21
 
22
22
  /**
23
23
  * Exposes the build type of the agent
@@ -10,7 +10,6 @@ var _env = require("../constants/env.npm");
10
10
  var _runtime = require("../constants/runtime");
11
11
  var _handle = require("../event-emitter/handle");
12
12
  var _eventListenerOpts = require("../event-listener/event-listener-opts");
13
- var _constants2 = require("../session/constants");
14
13
  var _now = require("../timing/now");
15
14
  var _eol = require("../unload/eol");
16
15
  var _cleanUrl = require("../url/clean-url");
@@ -43,12 +42,6 @@ class Harvester {
43
42
  }));
44
43
  /* This callback should run in bubble phase, so that that CWV api, like "onLCP", is called before the final harvest so that emitted timings are part of last outgoing. */
45
44
  }, false);
46
-
47
- /* Flush all buffered data if session resets and give up retries. This should be synchronous to ensure that the correct `session` value is sent.
48
- 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 the wrong session ID. */
49
- agentRef.ee.on(_constants2.SESSION_EVENTS.RESET, () => this.initializedAggregates.forEach(aggregateInst => this.triggerHarvestFor(aggregateInst, {
50
- forceNoRetry: true
51
- })));
52
45
  }
53
46
  startTimer(harvestInterval = this.agentRef.init.harvest.interval) {
54
47
  if (this.#started) return;
@@ -240,7 +240,10 @@ class SessionEntity {
240
240
  // * stop recording (stn and sr)...
241
241
  // * delete the session and start over
242
242
  try {
243
- if (this.initialized) this.ee.emit(_constants.SESSION_EVENTS.RESET);
243
+ if (this.initialized) {
244
+ this.ee.emit(_constants.SESSION_EVENTS.RESET);
245
+ this.state.numOfResets++;
246
+ }
244
247
  this.storage.remove(this.lookupKey);
245
248
  this.inactiveTimer?.abort?.();
246
249
  this.expiresTimer?.clear?.();
@@ -251,7 +254,7 @@ class SessionEntity {
251
254
  storage: this.storage,
252
255
  expiresMs: this.expiresMs,
253
256
  inactiveMs: this.inactiveMs,
254
- numOfResets: ++this.state.numOfResets
257
+ numOfResets: this.state.numOfResets
255
258
  });
256
259
  return this.read();
257
260
  } catch (e) {
@@ -270,9 +273,6 @@ class SessionEntity {
270
273
  inactiveAt: this.getFutureTimestamp(this.inactiveMs)
271
274
  });
272
275
  }
273
- isAfterSessionExpiry(timestamp) {
274
- return this.state.numOfResets > 0 || typeof timestamp === 'number' && typeof this.state.expiresAt === 'number' && timestamp >= this.state.expiresAt;
275
- }
276
276
 
277
277
  /**
278
278
  * @param {number} timestamp
@@ -4,6 +4,7 @@ Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
6
  exports.warn = warn;
7
+ var _globalEvent = require("../dispatch/global-event");
7
8
  /**
8
9
  * Copyright 2020-2025 New Relic, Inc. All rights reserved.
9
10
  * SPDX-License-Identifier: Apache-2.0
@@ -20,4 +21,15 @@ exports.warn = warn;
20
21
  function warn(code, secondary) {
21
22
  if (typeof console.debug !== 'function') return;
22
23
  console.debug("New Relic Warning: https://github.com/newrelic/newrelic-browser-agent/blob/main/docs/warning-codes.md#".concat(code), secondary);
24
+ (0, _globalEvent.dispatchGlobalEvent)({
25
+ agentIdentifier: null,
26
+ drained: null,
27
+ type: 'data',
28
+ name: 'warn',
29
+ feature: 'warn',
30
+ data: {
31
+ code,
32
+ secondary
33
+ }
34
+ });
23
35
  }
@@ -224,6 +224,22 @@ class Aggregate extends _aggregateBase.AggregateBase {
224
224
  }
225
225
  }, this.featureName, this.ee);
226
226
  }
227
+ (0, _registerHandler.registerHandler)('api-measure', (args, n) => {
228
+ const {
229
+ start,
230
+ duration,
231
+ customAttributes
232
+ } = args;
233
+ const event = {
234
+ ...customAttributes,
235
+ eventType: 'BrowserPerformance',
236
+ timestamp: Math.floor(agentRef.runtime.timeKeeper.correctRelativeTimestamp(start)),
237
+ entryName: n,
238
+ entryDuration: duration,
239
+ entryType: 'measure'
240
+ };
241
+ this.addEvent(event);
242
+ }, this.featureName, this.ee);
227
243
  agentRef.runtime.harvester.triggerHarvestFor(this);
228
244
  this.drain();
229
245
  });
@@ -12,6 +12,7 @@ var _addPageAction = require("../../../loaders/api/addPageAction");
12
12
  var _finished = require("../../../loaders/api/finished");
13
13
  var _recordCustomEvent = require("../../../loaders/api/recordCustomEvent");
14
14
  var _register = require("../../../loaders/api/register");
15
+ var _measure = require("../../../loaders/api/measure");
15
16
  var _instrumentBase = require("../../utils/instrument-base");
16
17
  var _constants = require("../constants");
17
18
  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); }
@@ -31,6 +32,7 @@ class Instrument extends _instrumentBase.InstrumentBase {
31
32
  (0, _recordCustomEvent.setupRecordCustomEventAPI)(agentRef);
32
33
  (0, _finished.setupFinishedAPI)(agentRef);
33
34
  (0, _register.setupRegisterAPI)(agentRef);
35
+ (0, _measure.setupMeasureAPI)(agentRef);
34
36
  if (_runtime.isBrowserScope) {
35
37
  if (agentRef.init.user_actions.enabled) {
36
38
  _constants.OBSERVED_EVENTS.forEach(eventType => (0, _eventListenerOpts.windowAddEventListener)(eventType, evt => (0, _handle.handle)('ua', [evt], undefined, this.featureName, this.ee), true));
@@ -281,6 +281,9 @@ class Aggregate extends _aggregateBase.AggregateBase {
281
281
  this.recorder.clearBuffer();
282
282
  if (recorderEvents.type === 'preloaded') this.agentRef.runtime.harvester.triggerHarvestFor(this);
283
283
  payloadOutput.payload = payload;
284
+ if (!this.agentRef.runtime.session.state.traceHarvestStarted) {
285
+ (0, _console.warn)(59, JSON.stringify(this.agentRef.runtime.session.state));
286
+ }
284
287
  return [payloadOutput];
285
288
  }
286
289
  getCorrectedTimestamp(node) {
@@ -168,10 +168,6 @@ class Recorder {
168
168
  /** Store a payload in the buffer (this.#events). This should be the callback to the recording lib noticing a mutation */
169
169
  store(event, isCheckout) {
170
170
  if (!event) return;
171
- if (this.parent.agentRef.runtime.session?.isAfterSessionExpiry(event.timestamp)) {
172
- (0, _handle.handle)(_constants3.SUPPORTABILITY_METRIC_CHANNEL, ['Session/Expired/SessionReplay/Seen'], undefined, _features.FEATURE_NAMES.metrics, this.ee);
173
- return;
174
- }
175
171
  if (!(this.parent instanceof _aggregateBase.AggregateBase) && this.#preloaded.length) this.currentBufferTarget = this.#preloaded[this.#preloaded.length - 1];else this.currentBufferTarget = this.#events;
176
172
  if (this.parent.blocked) return;
177
173
  if (this.parent.timeKeeper?.ready && !event.__newrelic) {
@@ -13,6 +13,7 @@ var _runtime = require("../../../common/constants/runtime");
13
13
  var _constants2 = require("../../../common/session/constants");
14
14
  var _traverse = require("../../../common/util/traverse");
15
15
  var _cleanUrl = require("../../../common/url/clean-url");
16
+ var _console = require("../../../common/util/console");
16
17
  /**
17
18
  * Copyright 2020-2025 New Relic, Inc. All rights reserved.
18
19
  * SPDX-License-Identifier: Apache-2.0
@@ -98,9 +99,11 @@ class Aggregate extends _aggregateBase.AggregateBase {
98
99
  sessionTraceMode: this.mode
99
100
  });
100
101
  this.drain();
102
+ /** try to harvest immediately. This will not send if the trace is not running in FULL mode due to the pre-harvest checks. */
103
+ this.agentRef.runtime.harvester.triggerHarvestFor(this);
101
104
  }
102
105
  preHarvestChecks() {
103
- if (this.mode !== _constants2.MODE.FULL) return; // only allow harvest if running in full mode
106
+ if (this.blocked || this.mode !== _constants2.MODE.FULL) return; // only allow harvest if running in full mode
104
107
  if (!this.timeKeeper?.ready) return; // this should likely never happen, but just to be safe, we should never harvest if we cant correct time
105
108
  if (!this.agentRef.runtime.session) return; // session entity is required for trace to run and continue running
106
109
  if (this.sessionId !== this.agentRef.runtime.session.state.value || this.ptid !== this.agentRef.runtime.ptid) {
@@ -194,7 +197,8 @@ class Aggregate extends _aggregateBase.AggregateBase {
194
197
  }
195
198
 
196
199
  /** Stop running for the remainder of the page lifecycle */
197
- abort() {
200
+ abort(code) {
201
+ (0, _console.warn)(60, code);
198
202
  this.blocked = true;
199
203
  this.mode = _constants2.MODE.OFF;
200
204
  this.agentRef.runtime.session.write({
@@ -53,9 +53,6 @@ class TraceStorage {
53
53
  constructor(parent) {
54
54
  this.parent = parent;
55
55
  }
56
- isAfterSessionExpiry(entryTimestamp) {
57
- return this.parent.agentRef.runtime?.session?.isAfterSessionExpiry((this.parent.timeKeeper?.ready && this.parent.timeKeeper.convertRelativeTimestamp(entryTimestamp)) ?? undefined);
58
- }
59
56
 
60
57
  /** Central function called by all the other store__ & addToTrace API to append a trace node. */
61
58
  storeSTN(stn) {
@@ -66,10 +63,6 @@ class TraceStorage {
66
63
  const openedSpace = this.trimSTNs(ERROR_MODE_SECONDS_WINDOW); // but maybe we could make some space by discarding irrelevant nodes if we're in sessioned Error mode
67
64
  if (openedSpace === 0) return;
68
65
  }
69
- if (this.isAfterSessionExpiry(stn.s)) {
70
- this.parent.reportSupportabilityMetric('Session/Expired/SessionTrace/Seen');
71
- return;
72
- }
73
66
  if (this.trace[stn.n]) this.trace[stn.n].push(stn);else this.trace[stn.n] = [stn];
74
67
  if (stn.s < this.earliestTimeStamp) this.earliestTimeStamp = stn.s;
75
68
  if (stn.s > this.latestTimeStamp) this.latestTimeStamp = stn.s;
@@ -3,7 +3,7 @@
3
3
  Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
- exports.spaPrefix = exports.prefix = exports.WRAP_LOGGER = exports.START = exports.SET_USER_ID = exports.SET_PAGE_VIEW_NAME = exports.SET_ERROR_HANDLER = exports.SET_CUSTOM_ATTRIBUTE = exports.SET_CURRENT_ROUTE_NAME = exports.SET_APPLICATION_VERSION = exports.REGISTER = exports.RECORD_REPLAY = exports.RECORD_CUSTOM_EVENT = exports.PAUSE_REPLAY = exports.NOTICE_ERROR = exports.LOG = exports.INTERACTION = exports.FINISHED = exports.ADD_TO_TRACE = exports.ADD_RELEASE = exports.ADD_PAGE_ACTION = void 0;
6
+ exports.spaPrefix = exports.prefix = exports.WRAP_LOGGER = exports.START = exports.SET_USER_ID = exports.SET_PAGE_VIEW_NAME = exports.SET_ERROR_HANDLER = exports.SET_CUSTOM_ATTRIBUTE = exports.SET_CURRENT_ROUTE_NAME = exports.SET_APPLICATION_VERSION = exports.REGISTER = exports.RECORD_REPLAY = exports.RECORD_CUSTOM_EVENT = exports.PAUSE_REPLAY = exports.NOTICE_ERROR = exports.MEASURE = exports.LOG = exports.INTERACTION = exports.FINISHED = exports.ADD_TO_TRACE = exports.ADD_RELEASE = exports.ADD_PAGE_ACTION = void 0;
7
7
  /**
8
8
  * Copyright 2020-2025 New Relic, Inc. All rights reserved.
9
9
  * SPDX-License-Identifier: Apache-2.0
@@ -28,4 +28,5 @@ const SET_ERROR_HANDLER = exports.SET_ERROR_HANDLER = 'setErrorHandler';
28
28
  const SET_PAGE_VIEW_NAME = exports.SET_PAGE_VIEW_NAME = 'setPageViewName';
29
29
  const SET_USER_ID = exports.SET_USER_ID = 'setUserId';
30
30
  const START = exports.START = 'start';
31
- const WRAP_LOGGER = exports.WRAP_LOGGER = 'wrapLogger';
31
+ const WRAP_LOGGER = exports.WRAP_LOGGER = 'wrapLogger';
32
+ const MEASURE = exports.MEASURE = 'measure';
@@ -0,0 +1,60 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.setupMeasureAPI = setupMeasureAPI;
7
+ var _handle = require("../../common/event-emitter/handle");
8
+ var _now = require("../../common/timing/now");
9
+ var _console = require("../../common/util/console");
10
+ var _features = require("../features/features");
11
+ var _constants = require("./constants");
12
+ var _sharedHandlers = require("./sharedHandlers");
13
+ /**
14
+ * Copyright 2020-2025 New Relic, Inc. All rights reserved.
15
+ * SPDX-License-Identifier: Apache-2.0
16
+ */
17
+
18
+ function setupMeasureAPI(agent) {
19
+ (0, _sharedHandlers.setupAPI)(_constants.MEASURE, function (name, options) {
20
+ const n = (0, _now.now)();
21
+ const {
22
+ start,
23
+ end,
24
+ customAttributes
25
+ } = options || {};
26
+ const returnObj = {
27
+ customAttributes: customAttributes || {}
28
+ };
29
+ if (typeof returnObj.customAttributes !== 'object' || typeof name !== 'string' || name.length === 0) {
30
+ (0, _console.warn)(57);
31
+ return;
32
+ }
33
+
34
+ /**
35
+ * getValueFromTiming - Helper function to extract a numeric value from a supplied option.
36
+ * @param {Number|PerformanceMark} [timing] The timing value
37
+ * @param {Number} [d] The default value to return if timing is invalid
38
+ * @returns {Number} The timing value or the default value
39
+ */
40
+ const getValueFromTiming = (timing, d) => {
41
+ if (timing == null) return d;
42
+ if (typeof timing === 'number') return timing;
43
+ if (timing instanceof PerformanceMark) return timing.startTime;
44
+ return Number.NaN;
45
+ };
46
+ returnObj.start = getValueFromTiming(start, 0);
47
+ returnObj.end = getValueFromTiming(end, n);
48
+ if (Number.isNaN(returnObj.start) || Number.isNaN(returnObj.end)) {
49
+ (0, _console.warn)(57);
50
+ return;
51
+ }
52
+ returnObj.duration = returnObj.end - returnObj.start;
53
+ if (returnObj.duration < 0) {
54
+ (0, _console.warn)(58);
55
+ return;
56
+ }
57
+ (0, _handle.handle)(_constants.prefix + _constants.MEASURE, [returnObj, name], undefined, _features.FEATURE_NAMES.genericEvents, agent.ee);
58
+ return returnObj;
59
+ }, agent);
60
+ }
@@ -220,5 +220,16 @@ class ApiBase {
220
220
  wrapLogger(parent, functionName, options) {
221
221
  return this.#callMethod(_constants.WRAP_LOGGER, parent, functionName, options);
222
222
  }
223
+
224
+ /**
225
+ * Measures a task that is recorded as a BrowserPerformance event.
226
+ * {@link https://docs.newrelic.com/docs/browser/new-relic-browser/browser-apis/measure/}
227
+ * @param {string} name The name of the task
228
+ * @param {object?} options An object used to control the way the measure API operates
229
+ * @returns {{start: number, end: number, duration: number, customAttributes: object}} Measurement details
230
+ */
231
+ measure(name, options) {
232
+ return this.#callMethod(_constants.MEASURE, name, options);
233
+ }
223
234
  }
224
235
  exports.ApiBase = ApiBase;
@@ -11,7 +11,7 @@
11
11
  /**
12
12
  * Exposes the version of the agent
13
13
  */
14
- export const VERSION = "1.290.1";
14
+ export const VERSION = "1.291.0";
15
15
 
16
16
  /**
17
17
  * Exposes the build type of the agent
@@ -11,7 +11,7 @@
11
11
  /**
12
12
  * Exposes the version of the agent
13
13
  */
14
- export const VERSION = "1.290.1";
14
+ export const VERSION = "1.291.0";
15
15
 
16
16
  /**
17
17
  * Exposes the build type of the agent
@@ -8,7 +8,6 @@ import { VERSION } from "../constants/env.npm";
8
8
  import { globalScope, isWorkerScope } from '../constants/runtime';
9
9
  import { handle } from '../event-emitter/handle';
10
10
  import { eventListenerOpts } from '../event-listener/event-listener-opts';
11
- import { SESSION_EVENTS } from '../session/constants';
12
11
  import { now } from '../timing/now';
13
12
  import { subscribeToEOL } from '../unload/eol';
14
13
  import { cleanURL } from '../url/clean-url';
@@ -36,12 +35,6 @@ export class Harvester {
36
35
  }));
37
36
  /* This callback should run in bubble phase, so that that CWV api, like "onLCP", is called before the final harvest so that emitted timings are part of last outgoing. */
38
37
  }, false);
39
-
40
- /* Flush all buffered data if session resets and give up retries. This should be synchronous to ensure that the correct `session` value is sent.
41
- 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 the wrong session ID. */
42
- agentRef.ee.on(SESSION_EVENTS.RESET, () => this.initializedAggregates.forEach(aggregateInst => this.triggerHarvestFor(aggregateInst, {
43
- forceNoRetry: true
44
- })));
45
38
  }
46
39
  startTimer(harvestInterval = this.agentRef.init.harvest.interval) {
47
40
  if (this.#started) return;
@@ -234,7 +234,10 @@ export class SessionEntity {
234
234
  // * stop recording (stn and sr)...
235
235
  // * delete the session and start over
236
236
  try {
237
- if (this.initialized) this.ee.emit(SESSION_EVENTS.RESET);
237
+ if (this.initialized) {
238
+ this.ee.emit(SESSION_EVENTS.RESET);
239
+ this.state.numOfResets++;
240
+ }
238
241
  this.storage.remove(this.lookupKey);
239
242
  this.inactiveTimer?.abort?.();
240
243
  this.expiresTimer?.clear?.();
@@ -245,7 +248,7 @@ export class SessionEntity {
245
248
  storage: this.storage,
246
249
  expiresMs: this.expiresMs,
247
250
  inactiveMs: this.inactiveMs,
248
- numOfResets: ++this.state.numOfResets
251
+ numOfResets: this.state.numOfResets
249
252
  });
250
253
  return this.read();
251
254
  } catch (e) {
@@ -264,9 +267,6 @@ export class SessionEntity {
264
267
  inactiveAt: this.getFutureTimestamp(this.inactiveMs)
265
268
  });
266
269
  }
267
- isAfterSessionExpiry(timestamp) {
268
- return this.state.numOfResets > 0 || typeof timestamp === 'number' && typeof this.state.expiresAt === 'number' && timestamp >= this.state.expiresAt;
269
- }
270
270
 
271
271
  /**
272
272
  * @param {number} timestamp
@@ -3,6 +3,8 @@
3
3
  * SPDX-License-Identifier: Apache-2.0
4
4
  */
5
5
 
6
+ import { dispatchGlobalEvent } from '../dispatch/global-event';
7
+
6
8
  /* eslint no-console: ["error", { allow: ["debug"] }] */
7
9
 
8
10
  /**
@@ -14,4 +16,15 @@
14
16
  export function warn(code, secondary) {
15
17
  if (typeof console.debug !== 'function') return;
16
18
  console.debug("New Relic Warning: https://github.com/newrelic/newrelic-browser-agent/blob/main/docs/warning-codes.md#".concat(code), secondary);
19
+ dispatchGlobalEvent({
20
+ agentIdentifier: null,
21
+ drained: null,
22
+ type: 'data',
23
+ name: 'warn',
24
+ feature: 'warn',
25
+ data: {
26
+ code,
27
+ secondary
28
+ }
29
+ });
17
30
  }
@@ -217,6 +217,22 @@ export class Aggregate extends AggregateBase {
217
217
  }
218
218
  }, this.featureName, this.ee);
219
219
  }
220
+ registerHandler('api-measure', (args, n) => {
221
+ const {
222
+ start,
223
+ duration,
224
+ customAttributes
225
+ } = args;
226
+ const event = {
227
+ ...customAttributes,
228
+ eventType: 'BrowserPerformance',
229
+ timestamp: Math.floor(agentRef.runtime.timeKeeper.correctRelativeTimestamp(start)),
230
+ entryName: n,
231
+ entryDuration: duration,
232
+ entryType: 'measure'
233
+ };
234
+ this.addEvent(event);
235
+ }, this.featureName, this.ee);
220
236
  agentRef.runtime.harvester.triggerHarvestFor(this);
221
237
  this.drain();
222
238
  });
@@ -11,6 +11,7 @@ import { setupAddPageActionAPI } from '../../../loaders/api/addPageAction';
11
11
  import { setupFinishedAPI } from '../../../loaders/api/finished';
12
12
  import { setupRecordCustomEventAPI } from '../../../loaders/api/recordCustomEvent';
13
13
  import { setupRegisterAPI } from '../../../loaders/api/register';
14
+ import { setupMeasureAPI } from '../../../loaders/api/measure';
14
15
  import { InstrumentBase } from '../../utils/instrument-base';
15
16
  import { FEATURE_NAME, OBSERVED_EVENTS, OBSERVED_WINDOW_EVENTS } from '../constants';
16
17
  export class Instrument extends InstrumentBase {
@@ -25,6 +26,7 @@ export class Instrument extends InstrumentBase {
25
26
  setupRecordCustomEventAPI(agentRef);
26
27
  setupFinishedAPI(agentRef);
27
28
  setupRegisterAPI(agentRef);
29
+ setupMeasureAPI(agentRef);
28
30
  if (isBrowserScope) {
29
31
  if (agentRef.init.user_actions.enabled) {
30
32
  OBSERVED_EVENTS.forEach(eventType => windowAddEventListener(eventType, evt => handle('ua', [evt], undefined, this.featureName, this.ee), true));
@@ -276,6 +276,9 @@ export class Aggregate extends AggregateBase {
276
276
  this.recorder.clearBuffer();
277
277
  if (recorderEvents.type === 'preloaded') this.agentRef.runtime.harvester.triggerHarvestFor(this);
278
278
  payloadOutput.payload = payload;
279
+ if (!this.agentRef.runtime.session.state.traceHarvestStarted) {
280
+ warn(59, JSON.stringify(this.agentRef.runtime.session.state));
281
+ }
279
282
  return [payloadOutput];
280
283
  }
281
284
  getCorrectedTimestamp(node) {
@@ -161,10 +161,6 @@ export class Recorder {
161
161
  /** Store a payload in the buffer (this.#events). This should be the callback to the recording lib noticing a mutation */
162
162
  store(event, isCheckout) {
163
163
  if (!event) return;
164
- if (this.parent.agentRef.runtime.session?.isAfterSessionExpiry(event.timestamp)) {
165
- handle(SUPPORTABILITY_METRIC_CHANNEL, ['Session/Expired/SessionReplay/Seen'], undefined, FEATURE_NAMES.metrics, this.ee);
166
- return;
167
- }
168
164
  if (!(this.parent instanceof AggregateBase) && this.#preloaded.length) this.currentBufferTarget = this.#preloaded[this.#preloaded.length - 1];else this.currentBufferTarget = this.#events;
169
165
  if (this.parent.blocked) return;
170
166
  if (this.parent.timeKeeper?.ready && !event.__newrelic) {
@@ -11,6 +11,7 @@ import { globalScope } from '../../../common/constants/runtime';
11
11
  import { MODE, SESSION_EVENTS } from '../../../common/session/constants';
12
12
  import { applyFnToProps } from '../../../common/util/traverse';
13
13
  import { cleanURL } from '../../../common/url/clean-url';
14
+ import { warn } from '../../../common/util/console';
14
15
  const ERROR_MODE_SECONDS_WINDOW = 30 * 1000; // sliding window of nodes to track when simply monitoring (but not harvesting) in error mode
15
16
  /** Reserved room for query param attrs */
16
17
  const QUERY_PARAM_PADDING = 5000;
@@ -91,9 +92,11 @@ export class Aggregate extends AggregateBase {
91
92
  sessionTraceMode: this.mode
92
93
  });
93
94
  this.drain();
95
+ /** try to harvest immediately. This will not send if the trace is not running in FULL mode due to the pre-harvest checks. */
96
+ this.agentRef.runtime.harvester.triggerHarvestFor(this);
94
97
  }
95
98
  preHarvestChecks() {
96
- if (this.mode !== MODE.FULL) return; // only allow harvest if running in full mode
99
+ if (this.blocked || this.mode !== MODE.FULL) return; // only allow harvest if running in full mode
97
100
  if (!this.timeKeeper?.ready) return; // this should likely never happen, but just to be safe, we should never harvest if we cant correct time
98
101
  if (!this.agentRef.runtime.session) return; // session entity is required for trace to run and continue running
99
102
  if (this.sessionId !== this.agentRef.runtime.session.state.value || this.ptid !== this.agentRef.runtime.ptid) {
@@ -187,7 +190,8 @@ export class Aggregate extends AggregateBase {
187
190
  }
188
191
 
189
192
  /** Stop running for the remainder of the page lifecycle */
190
- abort() {
193
+ abort(code) {
194
+ warn(60, code);
191
195
  this.blocked = true;
192
196
  this.mode = MODE.OFF;
193
197
  this.agentRef.runtime.session.write({
@@ -46,9 +46,6 @@ export class TraceStorage {
46
46
  constructor(parent) {
47
47
  this.parent = parent;
48
48
  }
49
- isAfterSessionExpiry(entryTimestamp) {
50
- return this.parent.agentRef.runtime?.session?.isAfterSessionExpiry((this.parent.timeKeeper?.ready && this.parent.timeKeeper.convertRelativeTimestamp(entryTimestamp)) ?? undefined);
51
- }
52
49
 
53
50
  /** Central function called by all the other store__ & addToTrace API to append a trace node. */
54
51
  storeSTN(stn) {
@@ -59,10 +56,6 @@ export class TraceStorage {
59
56
  const openedSpace = this.trimSTNs(ERROR_MODE_SECONDS_WINDOW); // but maybe we could make some space by discarding irrelevant nodes if we're in sessioned Error mode
60
57
  if (openedSpace === 0) return;
61
58
  }
62
- if (this.isAfterSessionExpiry(stn.s)) {
63
- this.parent.reportSupportabilityMetric('Session/Expired/SessionTrace/Seen');
64
- return;
65
- }
66
59
  if (this.trace[stn.n]) this.trace[stn.n].push(stn);else this.trace[stn.n] = [stn];
67
60
  if (stn.s < this.earliestTimeStamp) this.earliestTimeStamp = stn.s;
68
61
  if (stn.s > this.latestTimeStamp) this.latestTimeStamp = stn.s;
@@ -22,4 +22,5 @@ export const SET_ERROR_HANDLER = 'setErrorHandler';
22
22
  export const SET_PAGE_VIEW_NAME = 'setPageViewName';
23
23
  export const SET_USER_ID = 'setUserId';
24
24
  export const START = 'start';
25
- export const WRAP_LOGGER = 'wrapLogger';
25
+ export const WRAP_LOGGER = 'wrapLogger';
26
+ export const MEASURE = 'measure';
@@ -0,0 +1,53 @@
1
+ /**
2
+ * Copyright 2020-2025 New Relic, Inc. All rights reserved.
3
+ * SPDX-License-Identifier: Apache-2.0
4
+ */
5
+ import { handle } from '../../common/event-emitter/handle';
6
+ import { now } from '../../common/timing/now';
7
+ import { warn } from '../../common/util/console';
8
+ import { FEATURE_NAMES } from '../features/features';
9
+ import { prefix, MEASURE } from './constants';
10
+ import { setupAPI } from './sharedHandlers';
11
+ export function setupMeasureAPI(agent) {
12
+ setupAPI(MEASURE, function (name, options) {
13
+ const n = now();
14
+ const {
15
+ start,
16
+ end,
17
+ customAttributes
18
+ } = options || {};
19
+ const returnObj = {
20
+ customAttributes: customAttributes || {}
21
+ };
22
+ if (typeof returnObj.customAttributes !== 'object' || typeof name !== 'string' || name.length === 0) {
23
+ warn(57);
24
+ return;
25
+ }
26
+
27
+ /**
28
+ * getValueFromTiming - Helper function to extract a numeric value from a supplied option.
29
+ * @param {Number|PerformanceMark} [timing] The timing value
30
+ * @param {Number} [d] The default value to return if timing is invalid
31
+ * @returns {Number} The timing value or the default value
32
+ */
33
+ const getValueFromTiming = (timing, d) => {
34
+ if (timing == null) return d;
35
+ if (typeof timing === 'number') return timing;
36
+ if (timing instanceof PerformanceMark) return timing.startTime;
37
+ return Number.NaN;
38
+ };
39
+ returnObj.start = getValueFromTiming(start, 0);
40
+ returnObj.end = getValueFromTiming(end, n);
41
+ if (Number.isNaN(returnObj.start) || Number.isNaN(returnObj.end)) {
42
+ warn(57);
43
+ return;
44
+ }
45
+ returnObj.duration = returnObj.end - returnObj.start;
46
+ if (returnObj.duration < 0) {
47
+ warn(58);
48
+ return;
49
+ }
50
+ handle(prefix + MEASURE, [returnObj, name], undefined, FEATURE_NAMES.genericEvents, agent.ee);
51
+ return returnObj;
52
+ }, agent);
53
+ }
@@ -3,7 +3,7 @@
3
3
  * SPDX-License-Identifier: Apache-2.0
4
4
  */
5
5
  import { warn } from '../common/util/console';
6
- import { ADD_PAGE_ACTION, ADD_RELEASE, ADD_TO_TRACE, FINISHED, INTERACTION, LOG, NOTICE_ERROR, PAUSE_REPLAY, RECORD_CUSTOM_EVENT, RECORD_REPLAY, REGISTER, SET_APPLICATION_VERSION, SET_CURRENT_ROUTE_NAME, SET_CUSTOM_ATTRIBUTE, SET_ERROR_HANDLER, SET_PAGE_VIEW_NAME, SET_USER_ID, START, WRAP_LOGGER } from './api/constants';
6
+ import { ADD_PAGE_ACTION, ADD_RELEASE, ADD_TO_TRACE, FINISHED, INTERACTION, LOG, NOTICE_ERROR, PAUSE_REPLAY, RECORD_CUSTOM_EVENT, RECORD_REPLAY, REGISTER, SET_APPLICATION_VERSION, SET_CURRENT_ROUTE_NAME, SET_CUSTOM_ATTRIBUTE, SET_ERROR_HANDLER, SET_PAGE_VIEW_NAME, SET_USER_ID, START, WRAP_LOGGER, MEASURE } from './api/constants';
7
7
 
8
8
  /**
9
9
  * @typedef {import('./api/interaction-types').InteractionInstance} InteractionInstance
@@ -214,4 +214,15 @@ export class ApiBase {
214
214
  wrapLogger(parent, functionName, options) {
215
215
  return this.#callMethod(WRAP_LOGGER, parent, functionName, options);
216
216
  }
217
+
218
+ /**
219
+ * Measures a task that is recorded as a BrowserPerformance event.
220
+ * {@link https://docs.newrelic.com/docs/browser/new-relic-browser/browser-apis/measure/}
221
+ * @param {string} name The name of the task
222
+ * @param {object?} options An object used to control the way the measure API operates
223
+ * @returns {{start: number, end: number, duration: number, customAttributes: object}} Measurement details
224
+ */
225
+ measure(name, options) {
226
+ return this.#callMethod(MEASURE, name, options);
227
+ }
217
228
  }
@@ -1 +1 @@
1
- {"root":["../src/index.js","../src/cdn/experimental.js","../src/cdn/lite.js","../src/cdn/pro.js","../src/cdn/spa.js","../src/common/aggregate/aggregator.js","../src/common/aggregate/event-aggregator.js","../src/common/config/configurable.js","../src/common/config/info.js","../src/common/config/init-types.js","../src/common/config/init.js","../src/common/config/loader-config.js","../src/common/config/runtime.js","../src/common/constants/agent-constants.js","../src/common/constants/env.cdn.js","../src/common/constants/env.js","../src/common/constants/env.npm.js","../src/common/constants/runtime.js","../src/common/constants/shared-channel.js","../src/common/deny-list/deny-list.js","../src/common/dispatch/global-event.js","../src/common/dom/iframe.js","../src/common/dom/query-selector.js","../src/common/dom/selector-path.js","../src/common/drain/drain.js","../src/common/event-emitter/contextual-ee.js","../src/common/event-emitter/event-context.js","../src/common/event-emitter/handle.js","../src/common/event-emitter/register-handler.js","../src/common/event-listener/event-listener-opts.js","../src/common/harvest/harvester.js","../src/common/harvest/types.js","../src/common/ids/bundle-id.js","../src/common/ids/id.js","../src/common/ids/unique-id.js","../src/common/serialize/bel-serializer.js","../src/common/session/constants.js","../src/common/session/session-entity.js","../src/common/storage/local-storage.js","../src/common/timer/interaction-timer.js","../src/common/timer/timer.js","../src/common/timing/nav-timing.js","../src/common/timing/now.js","../src/common/timing/time-keeper.js","../src/common/unload/eol.js","../src/common/url/canonicalize-url.js","../src/common/url/clean-url.js","../src/common/url/encode.js","../src/common/url/location.js","../src/common/url/parse-url.js","../src/common/url/protocol.js","../src/common/util/console.js","../src/common/util/data-size.js","../src/common/util/event-origin.js","../src/common/util/feature-flags.js","../src/common/util/get-or-set.js","../src/common/util/invoke.js","../src/common/util/obfuscate.js","../src/common/util/stringify.js","../src/common/util/submit-data.js","../src/common/util/target.js","../src/common/util/text.js","../src/common/util/traverse.js","../src/common/util/type-check.js","../src/common/vitals/constants.js","../src/common/vitals/cumulative-layout-shift.js","../src/common/vitals/first-contentful-paint.js","../src/common/vitals/first-paint.js","../src/common/vitals/interaction-to-next-paint.js","../src/common/vitals/largest-contentful-paint.js","../src/common/vitals/time-to-first-byte.js","../src/common/vitals/vital-metric.js","../src/common/window/load.js","../src/common/window/nreum.js","../src/common/window/page-visibility.js","../src/common/wrap/wrap-events.js","../src/common/wrap/wrap-fetch.js","../src/common/wrap/wrap-function.js","../src/common/wrap/wrap-history.js","../src/common/wrap/wrap-jsonp.js","../src/common/wrap/wrap-logger.js","../src/common/wrap/wrap-mutation.js","../src/common/wrap/wrap-promise.js","../src/common/wrap/wrap-timer.js","../src/common/wrap/wrap-websocket.js","../src/common/wrap/wrap-xhr.js","../src/features/ajax/constants.js","../src/features/ajax/index.js","../src/features/ajax/aggregate/gql.js","../src/features/ajax/aggregate/index.js","../src/features/ajax/instrument/distributed-tracing.js","../src/features/ajax/instrument/index.js","../src/features/ajax/instrument/response-size.js","../src/features/generic_events/constants.js","../src/features/generic_events/index.js","../src/features/generic_events/aggregate/index.js","../src/features/generic_events/aggregate/user-actions/aggregated-user-action.js","../src/features/generic_events/aggregate/user-actions/user-actions-aggregator.js","../src/features/generic_events/instrument/index.js","../src/features/jserrors/constants.js","../src/features/jserrors/index.js","../src/features/jserrors/aggregate/canonical-function-name.js","../src/features/jserrors/aggregate/compute-stack-trace.js","../src/features/jserrors/aggregate/format-stack-trace.js","../src/features/jserrors/aggregate/index.js","../src/features/jserrors/aggregate/internal-errors.js","../src/features/jserrors/aggregate/string-hash-code.js","../src/features/jserrors/instrument/index.js","../src/features/jserrors/shared/cast-error.js","../src/features/jserrors/shared/uncaught-error.js","../src/features/logging/constants.js","../src/features/logging/index.js","../src/features/logging/aggregate/index.js","../src/features/logging/instrument/index.js","../src/features/logging/shared/log.js","../src/features/logging/shared/utils.js","../src/features/metrics/constants.js","../src/features/metrics/index.js","../src/features/metrics/aggregate/framework-detection.js","../src/features/metrics/aggregate/index.js","../src/features/metrics/aggregate/websocket-detection.js","../src/features/metrics/instrument/index.js","../src/features/page_action/constants.js","../src/features/page_action/index.js","../src/features/page_action/instrument/index.js","../src/features/page_view_event/constants.js","../src/features/page_view_event/index.js","../src/features/page_view_event/aggregate/index.js","../src/features/page_view_event/aggregate/initialized-features.js","../src/features/page_view_event/instrument/index.js","../src/features/page_view_timing/constants.js","../src/features/page_view_timing/index.js","../src/features/page_view_timing/aggregate/index.js","../src/features/page_view_timing/instrument/index.js","../src/features/session_replay/constants.js","../src/features/session_replay/index.js","../src/features/session_replay/aggregate/index.js","../src/features/session_replay/instrument/index.js","../src/features/session_replay/shared/recorder-events.js","../src/features/session_replay/shared/recorder.js","../src/features/session_replay/shared/stylesheet-evaluator.js","../src/features/session_replay/shared/utils.js","../src/features/session_trace/constants.js","../src/features/session_trace/index.js","../src/features/session_trace/aggregate/index.js","../src/features/session_trace/aggregate/trace/node.js","../src/features/session_trace/aggregate/trace/storage.js","../src/features/session_trace/instrument/index.js","../src/features/soft_navigations/constants.js","../src/features/soft_navigations/index.js","../src/features/soft_navigations/aggregate/ajax-node.js","../src/features/soft_navigations/aggregate/bel-node.js","../src/features/soft_navigations/aggregate/index.js","../src/features/soft_navigations/aggregate/initial-page-load-interaction.js","../src/features/soft_navigations/aggregate/interaction.js","../src/features/soft_navigations/instrument/index.js","../src/features/spa/constants.js","../src/features/spa/index.js","../src/features/spa/aggregate/index.js","../src/features/spa/aggregate/interaction-node.js","../src/features/spa/aggregate/interaction.js","../src/features/spa/aggregate/serializer.js","../src/features/spa/instrument/index.js","../src/features/utils/agent-session.js","../src/features/utils/aggregate-base.js","../src/features/utils/entity-manager.js","../src/features/utils/event-buffer.js","../src/features/utils/event-store-manager.js","../src/features/utils/feature-base.js","../src/features/utils/feature-gates.js","../src/features/utils/instrument-base.js","../src/features/utils/nr1-debugger.js","../src/interfaces/registered-entity.js","../src/loaders/agent-base.js","../src/loaders/agent.js","../src/loaders/api-base.js","../src/loaders/browser-agent.js","../src/loaders/micro-agent-base.js","../src/loaders/micro-agent.js","../src/loaders/api/addPageAction.js","../src/loaders/api/addRelease.js","../src/loaders/api/addToTrace.js","../src/loaders/api/constants.js","../src/loaders/api/finished.js","../src/loaders/api/interaction-types.js","../src/loaders/api/interaction.js","../src/loaders/api/log.js","../src/loaders/api/noticeError.js","../src/loaders/api/pauseReplay.js","../src/loaders/api/recordCustomEvent.js","../src/loaders/api/recordReplay.js","../src/loaders/api/register-api-types.js","../src/loaders/api/register-api.js","../src/loaders/api/register.js","../src/loaders/api/setApplicationVersion.js","../src/loaders/api/setCustomAttribute.js","../src/loaders/api/setErrorHandler.js","../src/loaders/api/setPageViewName.js","../src/loaders/api/setUserId.js","../src/loaders/api/sharedHandlers.js","../src/loaders/api/start.js","../src/loaders/api/topLevelCallers.js","../src/loaders/api/wrapLogger.js","../src/loaders/configure/configure.js","../src/loaders/configure/nonce.js","../src/loaders/configure/public-path.js","../src/loaders/features/enabled-features.js","../src/loaders/features/featureDependencies.js","../src/loaders/features/features.js"],"version":"5.7.3"}
1
+ {"root":["../src/index.js","../src/cdn/experimental.js","../src/cdn/lite.js","../src/cdn/pro.js","../src/cdn/spa.js","../src/common/aggregate/aggregator.js","../src/common/aggregate/event-aggregator.js","../src/common/config/configurable.js","../src/common/config/info.js","../src/common/config/init-types.js","../src/common/config/init.js","../src/common/config/loader-config.js","../src/common/config/runtime.js","../src/common/constants/agent-constants.js","../src/common/constants/env.cdn.js","../src/common/constants/env.js","../src/common/constants/env.npm.js","../src/common/constants/runtime.js","../src/common/constants/shared-channel.js","../src/common/deny-list/deny-list.js","../src/common/dispatch/global-event.js","../src/common/dom/iframe.js","../src/common/dom/query-selector.js","../src/common/dom/selector-path.js","../src/common/drain/drain.js","../src/common/event-emitter/contextual-ee.js","../src/common/event-emitter/event-context.js","../src/common/event-emitter/handle.js","../src/common/event-emitter/register-handler.js","../src/common/event-listener/event-listener-opts.js","../src/common/harvest/harvester.js","../src/common/harvest/types.js","../src/common/ids/bundle-id.js","../src/common/ids/id.js","../src/common/ids/unique-id.js","../src/common/serialize/bel-serializer.js","../src/common/session/constants.js","../src/common/session/session-entity.js","../src/common/storage/local-storage.js","../src/common/timer/interaction-timer.js","../src/common/timer/timer.js","../src/common/timing/nav-timing.js","../src/common/timing/now.js","../src/common/timing/time-keeper.js","../src/common/unload/eol.js","../src/common/url/canonicalize-url.js","../src/common/url/clean-url.js","../src/common/url/encode.js","../src/common/url/location.js","../src/common/url/parse-url.js","../src/common/url/protocol.js","../src/common/util/console.js","../src/common/util/data-size.js","../src/common/util/event-origin.js","../src/common/util/feature-flags.js","../src/common/util/get-or-set.js","../src/common/util/invoke.js","../src/common/util/obfuscate.js","../src/common/util/stringify.js","../src/common/util/submit-data.js","../src/common/util/target.js","../src/common/util/text.js","../src/common/util/traverse.js","../src/common/util/type-check.js","../src/common/vitals/constants.js","../src/common/vitals/cumulative-layout-shift.js","../src/common/vitals/first-contentful-paint.js","../src/common/vitals/first-paint.js","../src/common/vitals/interaction-to-next-paint.js","../src/common/vitals/largest-contentful-paint.js","../src/common/vitals/time-to-first-byte.js","../src/common/vitals/vital-metric.js","../src/common/window/load.js","../src/common/window/nreum.js","../src/common/window/page-visibility.js","../src/common/wrap/wrap-events.js","../src/common/wrap/wrap-fetch.js","../src/common/wrap/wrap-function.js","../src/common/wrap/wrap-history.js","../src/common/wrap/wrap-jsonp.js","../src/common/wrap/wrap-logger.js","../src/common/wrap/wrap-mutation.js","../src/common/wrap/wrap-promise.js","../src/common/wrap/wrap-timer.js","../src/common/wrap/wrap-websocket.js","../src/common/wrap/wrap-xhr.js","../src/features/ajax/constants.js","../src/features/ajax/index.js","../src/features/ajax/aggregate/gql.js","../src/features/ajax/aggregate/index.js","../src/features/ajax/instrument/distributed-tracing.js","../src/features/ajax/instrument/index.js","../src/features/ajax/instrument/response-size.js","../src/features/generic_events/constants.js","../src/features/generic_events/index.js","../src/features/generic_events/aggregate/index.js","../src/features/generic_events/aggregate/user-actions/aggregated-user-action.js","../src/features/generic_events/aggregate/user-actions/user-actions-aggregator.js","../src/features/generic_events/instrument/index.js","../src/features/jserrors/constants.js","../src/features/jserrors/index.js","../src/features/jserrors/aggregate/canonical-function-name.js","../src/features/jserrors/aggregate/compute-stack-trace.js","../src/features/jserrors/aggregate/format-stack-trace.js","../src/features/jserrors/aggregate/index.js","../src/features/jserrors/aggregate/internal-errors.js","../src/features/jserrors/aggregate/string-hash-code.js","../src/features/jserrors/instrument/index.js","../src/features/jserrors/shared/cast-error.js","../src/features/jserrors/shared/uncaught-error.js","../src/features/logging/constants.js","../src/features/logging/index.js","../src/features/logging/aggregate/index.js","../src/features/logging/instrument/index.js","../src/features/logging/shared/log.js","../src/features/logging/shared/utils.js","../src/features/metrics/constants.js","../src/features/metrics/index.js","../src/features/metrics/aggregate/framework-detection.js","../src/features/metrics/aggregate/index.js","../src/features/metrics/aggregate/websocket-detection.js","../src/features/metrics/instrument/index.js","../src/features/page_action/constants.js","../src/features/page_action/index.js","../src/features/page_action/instrument/index.js","../src/features/page_view_event/constants.js","../src/features/page_view_event/index.js","../src/features/page_view_event/aggregate/index.js","../src/features/page_view_event/aggregate/initialized-features.js","../src/features/page_view_event/instrument/index.js","../src/features/page_view_timing/constants.js","../src/features/page_view_timing/index.js","../src/features/page_view_timing/aggregate/index.js","../src/features/page_view_timing/instrument/index.js","../src/features/session_replay/constants.js","../src/features/session_replay/index.js","../src/features/session_replay/aggregate/index.js","../src/features/session_replay/instrument/index.js","../src/features/session_replay/shared/recorder-events.js","../src/features/session_replay/shared/recorder.js","../src/features/session_replay/shared/stylesheet-evaluator.js","../src/features/session_replay/shared/utils.js","../src/features/session_trace/constants.js","../src/features/session_trace/index.js","../src/features/session_trace/aggregate/index.js","../src/features/session_trace/aggregate/trace/node.js","../src/features/session_trace/aggregate/trace/storage.js","../src/features/session_trace/instrument/index.js","../src/features/soft_navigations/constants.js","../src/features/soft_navigations/index.js","../src/features/soft_navigations/aggregate/ajax-node.js","../src/features/soft_navigations/aggregate/bel-node.js","../src/features/soft_navigations/aggregate/index.js","../src/features/soft_navigations/aggregate/initial-page-load-interaction.js","../src/features/soft_navigations/aggregate/interaction.js","../src/features/soft_navigations/instrument/index.js","../src/features/spa/constants.js","../src/features/spa/index.js","../src/features/spa/aggregate/index.js","../src/features/spa/aggregate/interaction-node.js","../src/features/spa/aggregate/interaction.js","../src/features/spa/aggregate/serializer.js","../src/features/spa/instrument/index.js","../src/features/utils/agent-session.js","../src/features/utils/aggregate-base.js","../src/features/utils/entity-manager.js","../src/features/utils/event-buffer.js","../src/features/utils/event-store-manager.js","../src/features/utils/feature-base.js","../src/features/utils/feature-gates.js","../src/features/utils/instrument-base.js","../src/features/utils/nr1-debugger.js","../src/interfaces/registered-entity.js","../src/loaders/agent-base.js","../src/loaders/agent.js","../src/loaders/api-base.js","../src/loaders/browser-agent.js","../src/loaders/micro-agent-base.js","../src/loaders/micro-agent.js","../src/loaders/api/addPageAction.js","../src/loaders/api/addRelease.js","../src/loaders/api/addToTrace.js","../src/loaders/api/constants.js","../src/loaders/api/finished.js","../src/loaders/api/interaction-types.js","../src/loaders/api/interaction.js","../src/loaders/api/log.js","../src/loaders/api/measure.js","../src/loaders/api/noticeError.js","../src/loaders/api/pauseReplay.js","../src/loaders/api/recordCustomEvent.js","../src/loaders/api/recordReplay.js","../src/loaders/api/register-api-types.js","../src/loaders/api/register-api.js","../src/loaders/api/register.js","../src/loaders/api/setApplicationVersion.js","../src/loaders/api/setCustomAttribute.js","../src/loaders/api/setErrorHandler.js","../src/loaders/api/setPageViewName.js","../src/loaders/api/setUserId.js","../src/loaders/api/sharedHandlers.js","../src/loaders/api/start.js","../src/loaders/api/topLevelCallers.js","../src/loaders/api/wrapLogger.js","../src/loaders/configure/configure.js","../src/loaders/configure/nonce.js","../src/loaders/configure/public-path.js","../src/loaders/features/enabled-features.js","../src/loaders/features/featureDependencies.js","../src/loaders/features/features.js"],"version":"5.7.3"}
@@ -1 +1 @@
1
- {"version":3,"file":"harvester.d.ts","sourceRoot":"","sources":["../../../../src/common/harvest/harvester.js"],"names":[],"mappings":"AAwBA;IAIE,2BAcC;IAhBD,6BAA0B;IAGxB,cAAwB;IAe1B,wCASC;IAED;;;;;OAKG;IACH,iCAJW,MAAM,cACN,MAAM,GACJ,OAAO,CA+CnB;;CACF;8BAGY,OAAO,YAAY,EAAE,eAAe"}
1
+ {"version":3,"file":"harvester.d.ts","sourceRoot":"","sources":["../../../../src/common/harvest/harvester.js"],"names":[],"mappings":"AAuBA;IAIE,2BAUC;IAZD,6BAA0B;IAGxB,cAAwB;IAW1B,wCASC;IAED;;;;;OAKG;IACH,iCAJW,MAAM,cACN,MAAM,GACJ,OAAO,CA+CnB;;CACF;8BAGY,OAAO,YAAY,EAAE,eAAe"}
@@ -42,7 +42,6 @@ export class SessionEntity {
42
42
  * Refresh the inactivity timer data
43
43
  */
44
44
  refresh(): void;
45
- isAfterSessionExpiry(timestamp: any): boolean;
46
45
  /**
47
46
  * @param {number} timestamp
48
47
  * @returns {boolean}
@@ -1 +1 @@
1
- {"version":3,"file":"session-entity.d.ts","sourceRoot":"","sources":["../../../../src/common/session/session-entity.js"],"names":[],"mappings":"AAqCA;IACE;;;;;OAKG;IACH,uBA+BC;IAzBC,qBAAsC;IACtC,aAAsB;IACtB,UAAe;IAGf,SAAc;IAEd,QAAiC;IAoBnC;;;;;aA6EC;IApEC,8BAA0B;IAC1B,+BAA4B;IAe1B,gCAOqC;IAUrC,4CAkBsC;IAexC,iCAAuB;IAKzB,wBAEC;IAED,sBAEC;IAED;;;OAGG;IACH,QAFa,MAAM,CA6BlB;IAED;;;;;;OAMG;IACH,YAHW,MAAM,GACJ,MAAM,CAkBlB;IAED,gBAwBC;IAED;;OAEG;IACH,gBAIC;IAED,8CAEC;IAED;;;OAGG;IACH,qBAHW,MAAM,GACJ,OAAO,CAInB;IAED;;;OAGG;IACH,gBAHW,MAAM,GACJ,OAAO,CAKnB;IAED,yDAUC;IAED,6DAIC;IAED;;;OAGG;IACH,6BAHW,MAAM,GACJ,MAAM,CAIlB;IAED,gDAaC;IAHG,YAAuD;CAI5D;sBAvTqB,gBAAgB;iCAGL,4BAA4B"}
1
+ {"version":3,"file":"session-entity.d.ts","sourceRoot":"","sources":["../../../../src/common/session/session-entity.js"],"names":[],"mappings":"AAqCA;IACE;;;;;OAKG;IACH,uBA+BC;IAzBC,qBAAsC;IACtC,aAAsB;IACtB,UAAe;IAGf,SAAc;IAEd,QAAiC;IAoBnC;;;;;aA6EC;IApEC,8BAA0B;IAC1B,+BAA4B;IAe1B,gCAOqC;IAUrC,4CAkBsC;IAexC,iCAAuB;IAKzB,wBAEC;IAED,sBAEC;IAED;;;OAGG;IACH,QAFa,MAAM,CA6BlB;IAED;;;;;;OAMG;IACH,YAHW,MAAM,GACJ,MAAM,CAkBlB;IAED,gBA2BC;IAED;;OAEG;IACH,gBAIC;IAED;;;OAGG;IACH,qBAHW,MAAM,GACJ,OAAO,CAInB;IAED;;;OAGG;IACH,gBAHW,MAAM,GACJ,OAAO,CAKnB;IAED,yDAUC;IAED,6DAIC;IAED;;;OAGG;IACH,6BAHW,MAAM,GACJ,MAAM,CAIlB;IAED,gDAaC;IAHG,YAAuD;CAI5D;sBAtTqB,gBAAgB;iCAGL,4BAA4B"}
@@ -1,7 +1,3 @@
1
- /**
2
- * Copyright 2020-2025 New Relic, Inc. All rights reserved.
3
- * SPDX-License-Identifier: Apache-2.0
4
- */
5
1
  /**
6
2
  * A helper method to warn to the console with New Relic: decoration
7
3
  * @param {string} message The primary message to warn
@@ -1 +1 @@
1
- {"version":3,"file":"console.d.ts","sourceRoot":"","sources":["../../../../src/common/util/console.js"],"names":[],"mappings":"AAAA;;;GAGG;AAIH;;;;;GAKG;AACH,4CAHW,GAAC,QAMX"}
1
+ {"version":3,"file":"console.d.ts","sourceRoot":"","sources":["../../../../src/common/util/console.js"],"names":[],"mappings":"AASA;;;;;GAKG;AACH,4CAHW,GAAC,QAiBX"}
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/features/generic_events/aggregate/index.js"],"names":[],"mappings":"AAkBA;IACE,2BAAiC;IACjC,2BA6MC;IA3MC,yBAA4B;IAC5B,gCAAkG;IAwC9F,4CAAuD;IAqK7D;;;;;;;;;;;;OAYG;IACH,eAJW,MAAM,YAAC,qBACP,MAAM,YAAC,QA0CjB;IAED,qCAEC;IAED;;;MAEC;IAED,gCAEC;IAED,mCASC;CACF;8BAxS6B,4BAA4B;sCAMpB,wCAAwC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/features/generic_events/aggregate/index.js"],"names":[],"mappings":"AAkBA;IACE,2BAAiC;IACjC,2BA4NC;IA1NC,yBAA4B;IAC5B,gCAAkG;IAwC9F,4CAAuD;IAoL7D;;;;;;;;;;;;OAYG;IACH,eAJW,MAAM,YAAC,qBACP,MAAM,YAAC,QA0CjB;IAED,qCAEC;IAED;;;MAEC;IAED,gCAEC;IAED,mCASC;CACF;8BAvT6B,4BAA4B;sCAMpB,wCAAwC"}
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/features/generic_events/instrument/index.js"],"names":[],"mappings":"AAgBA;IACE,2BAAiC;IACjC,2BA0CC;CACF;AAED,8CAAuC;+BAlDR,6BAA6B"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/features/generic_events/instrument/index.js"],"names":[],"mappings":"AAiBA;IACE,2BAAiC;IACjC,2BA2CC;CACF;AAED,8CAAuC;+BAnDR,6BAA6B"}
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/features/session_replay/aggregate/index.js"],"names":[],"mappings":"AAyBA;IACE,2BAAiC;IAIjC,sCAsFC;IAzFD,aAAe;IAKb,iFAAiF;IACjF,qBAAwB;IAGxB,2CAA2C;IAC3C,sDAAwB;IACxB,6CAA6C;IAC7C,gDAAmB;IAEnB,0BAA0B;IAC1B,kBAAqB;IACrB,6CAA6C;IAC7C,gBAA2B;IAE3B,cAA8B;IAC9B,kBAA+C;IAG/C,kCAAqG;IAoEvG,0BAEC;IAED,0BAMC;IAED,qBAUC;IAED;;;;;OAKG;IACH,4BAJW,OAAO,iBACP,OAAO,GACL,IAAI,CAuDhB;IAED,2BASC;IAED;;;oBAmDC;IAED,sCAIC;IAED;;;;;;;;;;MAuEC;IAED,sCAKC;IAED;;;;OAIG;IACH,mCAKC;IAED,yDAAyD;IACzD,+CASC;IAED,yCAIC;CACF;8BApX6B,4BAA4B"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/features/session_replay/aggregate/index.js"],"names":[],"mappings":"AAyBA;IACE,2BAAiC;IAIjC,sCAsFC;IAzFD,aAAe;IAKb,iFAAiF;IACjF,qBAAwB;IAGxB,2CAA2C;IAC3C,sDAAwB;IACxB,6CAA6C;IAC7C,gDAAmB;IAEnB,0BAA0B;IAC1B,kBAAqB;IACrB,6CAA6C;IAC7C,gBAA2B;IAE3B,cAA8B;IAC9B,kBAA+C;IAG/C,kCAAqG;IAoEvG,0BAEC;IAED,0BAMC;IAED,qBAUC;IAED;;;;;OAKG;IACH,4BAJW,OAAO,iBACP,OAAO,GACL,IAAI,CAuDhB;IAED,2BASC;IAED;;;oBAuDC;IAED,sCAIC;IAED;;;;;;;;;;MAuEC;IAED,sCAKC;IAED;;;;OAIG;IACH,mCAKC;IAED,yDAAyD;IACzD,+CASC;IAED,yCAIC;CACF;8BAxX6B,4BAA4B"}
@@ -1 +1 @@
1
- {"version":3,"file":"recorder.d.ts","sourceRoot":"","sources":["../../../../../src/features/session_replay/shared/recorder.js"],"names":[],"mappings":"AAmBA;IAYE,yBAiBC;IAhBC,sDAAsD;IACtD,YAAoB;IACpB,0FAA0F;IAC1F,eAAyE;IAKzE,6DAA6D;IAC7D,oCAAuC;IACvC,+IAA+I;IAC/I,yBAA4B;IAC5B,kIAAkI;IAClI,kBAAqB;IACrB,uIAAuI;IACvI,0BAA+E;IAGjF;;;;;;;;;MAmBC;IAED,mFAAmF;IACnF,oBAKC;IAED,qDAAqD;IACrD,uBAiCC;IAED;;;;;OAKG;IACH,aAHW,GAAC,cACD,GAAC,QAiCX;IAED,0HAA0H;IAC1H,yCAmDC;IAED,0HAA0H;IAC1H,yBAOC;IAED,wBAEC;IAED,gCAAgC;IAChC,uCAGC;IAED;;;SAGK;IACL,oCAGC;;CACF;+BArO8B,mBAAmB"}
1
+ {"version":3,"file":"recorder.d.ts","sourceRoot":"","sources":["../../../../../src/features/session_replay/shared/recorder.js"],"names":[],"mappings":"AAmBA;IAYE,yBAiBC;IAhBC,sDAAsD;IACtD,YAAoB;IACpB,0FAA0F;IAC1F,eAAyE;IAKzE,6DAA6D;IAC7D,oCAAuC;IACvC,+IAA+I;IAC/I,yBAA4B;IAC5B,kIAAkI;IAClI,kBAAqB;IACrB,uIAAuI;IACvI,0BAA+E;IAGjF;;;;;;;;;MAmBC;IAED,mFAAmF;IACnF,oBAKC;IAED,qDAAqD;IACrD,uBAiCC;IAED;;;;;OAKG;IACH,aAHW,GAAC,cACD,GAAC,QAiCX;IAED,0HAA0H;IAC1H,yCA+CC;IAED,0HAA0H;IAC1H,yBAOC;IAED,wBAEC;IAED,gCAAgC;IAChC,uCAGC;IAED;;;SAGK;IACL,oCAGC;;CACF;+BAjO8B,mBAAmB"}
@@ -35,7 +35,7 @@ export class Aggregate extends AggregateBase {
35
35
  /** Switch from "off" or "error" to full mode (if entitled) */
36
36
  switchToFull(): void;
37
37
  /** Stop running for the remainder of the page lifecycle */
38
- abort(): void;
38
+ abort(code: any): void;
39
39
  }
40
40
  import { AggregateBase } from '../../utils/aggregate-base';
41
41
  import { TraceStorage } from './trace/storage';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/features/session_trace/aggregate/index.js"],"names":[],"mappings":"AAiBA;IACE,2BAAiC;IAEjC,2BAgBC;IAZC,0GAA0G;IAC1G,cAAyB;IACzB,mIAAmI;IACnI,uBAA0B;IAC1B,0CAA0C;IAC1C,oBAAuB;IACvB,wIAAwI;IACxI,qBAAoC;IAOtC,gLAAgL;IAChL,mEA4DC;IAtDG,iCAAuB;IACvB,yJAAyJ;IACzJ,UAAsC;IACtC,eAA2D;IAyBD,UAAgE;IA4B9H,qCAUC;IAED;;2BAIC;IAED;;;;;;;;;;;MA6CC;IAED,8DAA8D;IAC9D,qBAUC;IAED,2DAA2D;IAC3D,cAKC;CACF;8BApL6B,4BAA4B;6BAC7B,iBAAiB"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/features/session_trace/aggregate/index.js"],"names":[],"mappings":"AAkBA;IACE,2BAAiC;IAEjC,2BAgBC;IAZC,0GAA0G;IAC1G,cAAyB;IACzB,mIAAmI;IACnI,uBAA0B;IAC1B,0CAA0C;IAC1C,oBAAuB;IACvB,wIAAwI;IACxI,qBAAoC;IAOtC,gLAAgL;IAChL,mEA8DC;IAxDG,iCAAuB;IACvB,yJAAyJ;IACzJ,UAAsC;IACtC,eAA2D;IAyBD,UAAgE;IA8B9H,qCAUC;IAED;;2BAIC;IAED;;;;;;;;;;;MA6CC;IAED,8DAA8D;IAC9D,qBAUC;IAED,2DAA2D;IAC3D,uBAMC;CACF;8BAxL6B,4BAA4B;6BAC7B,iBAAiB"}
@@ -7,7 +7,6 @@ export class TraceStorage {
7
7
  latestTimeStamp: number;
8
8
  prevStoredEvents: Set<any>;
9
9
  parent: any;
10
- isAfterSessionExpiry(entryTimestamp: any): any;
11
10
  /** Central function called by all the other store__ & addToTrace API to append a trace node. */
12
11
  storeSTN(stn: any): void;
13
12
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"storage.d.ts","sourceRoot":"","sources":["../../../../../../src/features/session_trace/aggregate/trace/storage.js"],"names":[],"mappings":"AA8BA,+HAA+H;AAC/H;IAQE,yBAEC;IATD,kBAAa;IACb,UAAU;IACV,0BAA4B;IAC5B,wBAAmB;IACnB,2BAA4B;IAI1B,YAAoB;IAGtB,+CAEC;IAED,gGAAgG;IAChG,yBAkBC;IAED;;;;OAIG;IACH,2BAHW,MAAM,GACJ,MAAM,CAsBlB;IAED,oEAAoE;IACpE;;;;MAgBC;IAED,mEA6BC;IAED,oDAEC;IAED,mEAuBC;IAGD,uEAcC;IAED,oDAKC;IAED,wBAwBC;IAGD,gDAEC;IAID,qCAaC;IAGD,qEAGC;IAGD,mEAGC;IAID,mBAEC;IAED,aAEC;IAED;;;;;;;QAEC;IAED,cAMC;IAED,mBAEC;IAED,kBAEC;;CACF"}
1
+ {"version":3,"file":"storage.d.ts","sourceRoot":"","sources":["../../../../../../src/features/session_trace/aggregate/trace/storage.js"],"names":[],"mappings":"AA8BA,+HAA+H;AAC/H;IAQE,yBAEC;IATD,kBAAa;IACb,UAAU;IACV,0BAA4B;IAC5B,wBAAmB;IACnB,2BAA4B;IAI1B,YAAoB;IAGtB,gGAAgG;IAChG,yBAcC;IAED;;;;OAIG;IACH,2BAHW,MAAM,GACJ,MAAM,CAsBlB;IAED,oEAAoE;IACpE;;;;MAgBC;IAED,mEA6BC;IAED,oDAEC;IAED,mEAuBC;IAGD,uEAcC;IAED,oDAKC;IAED,wBAwBC;IAGD,gDAEC;IAID,qCAaC;IAGD,qEAGC;IAGD,mEAGC;IAID,mBAEC;IAED,aAEC;IAED;;;;;;;QAEC;IAED,cAMC;IAED,mBAEC;IAED,kBAEC;;CACF"}
@@ -23,4 +23,5 @@ export const SET_PAGE_VIEW_NAME: "setPageViewName";
23
23
  export const SET_USER_ID: "setUserId";
24
24
  export const START: "start";
25
25
  export const WRAP_LOGGER: "wrapLogger";
26
+ export const MEASURE: "measure";
26
27
  //# sourceMappingURL=constants.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../../../../src/loaders/api/constants.js"],"names":[],"mappings":"AAAA;;;GAGG;AACH,qBAAsB,MAAM,CAAA;AAC5B,+BAAwC;AAExC,8BAA+B,eAAe,CAAA;AAC9C,2BAA4B,YAAY,CAAA;AACxC,0BAA2B,YAAY,CAAA;AACvC,uBAAwB,UAAU,CAAA;AAClC,0BAA2B,aAAa,CAAA;AACxC,kBAAmB,KAAK,CAAA;AACxB,2BAA4B,aAAa,CAAA;AACzC,2BAA4B,aAAa,CAAA;AACzC,kCAAmC,mBAAmB,CAAA;AACtD,4BAA6B,cAAc,CAAA;AAC3C,uBAAwB,UAAU,CAAA;AAClC,sCAAuC,uBAAuB,CAAA;AAC9D,qCAAsC,qBAAqB,CAAA;AAC3D,mCAAoC,oBAAoB,CAAA;AACxD,gCAAiC,iBAAiB,CAAA;AAClD,iCAAkC,iBAAiB,CAAA;AACnD,0BAA2B,WAAW,CAAA;AACtC,oBAAqB,OAAO,CAAA;AAC5B,0BAA2B,YAAY,CAAA"}
1
+ {"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../../../../src/loaders/api/constants.js"],"names":[],"mappings":"AAAA;;;GAGG;AACH,qBAAsB,MAAM,CAAA;AAC5B,+BAAwC;AAExC,8BAA+B,eAAe,CAAA;AAC9C,2BAA4B,YAAY,CAAA;AACxC,0BAA2B,YAAY,CAAA;AACvC,uBAAwB,UAAU,CAAA;AAClC,0BAA2B,aAAa,CAAA;AACxC,kBAAmB,KAAK,CAAA;AACxB,2BAA4B,aAAa,CAAA;AACzC,2BAA4B,aAAa,CAAA;AACzC,kCAAmC,mBAAmB,CAAA;AACtD,4BAA6B,cAAc,CAAA;AAC3C,uBAAwB,UAAU,CAAA;AAClC,sCAAuC,uBAAuB,CAAA;AAC9D,qCAAsC,qBAAqB,CAAA;AAC3D,mCAAoC,oBAAoB,CAAA;AACxD,gCAAiC,iBAAiB,CAAA;AAClD,iCAAkC,iBAAiB,CAAA;AACnD,0BAA2B,WAAW,CAAA;AACtC,oBAAqB,OAAO,CAAA;AAC5B,0BAA2B,YAAY,CAAA;AACvC,sBAAuB,SAAS,CAAA"}
@@ -0,0 +1,2 @@
1
+ export function setupMeasureAPI(agent: any): void;
2
+ //# sourceMappingURL=measure.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"measure.d.ts","sourceRoot":"","sources":["../../../../src/loaders/api/measure.js"],"names":[],"mappings":"AAWA,kDAyCC"}
@@ -163,6 +163,19 @@ export class ApiBase {
163
163
  customAttributes?: object;
164
164
  level?: "ERROR" | "TRACE" | "DEBUG" | "INFO" | "WARN";
165
165
  }): any;
166
+ /**
167
+ * Measures a task that is recorded as a BrowserPerformance event.
168
+ * {@link https://docs.newrelic.com/docs/browser/new-relic-browser/browser-apis/measure/}
169
+ * @param {string} name The name of the task
170
+ * @param {object?} options An object used to control the way the measure API operates
171
+ * @returns {{start: number, end: number, duration: number, customAttributes: object}} Measurement details
172
+ */
173
+ measure(name: string, options: object | null): {
174
+ start: number;
175
+ end: number;
176
+ duration: number;
177
+ customAttributes: object;
178
+ };
166
179
  #private;
167
180
  }
168
181
  export type InteractionInstance = import("./api/interaction-types").InteractionInstance;
@@ -1 +1 @@
1
- {"version":3,"file":"api-base.d.ts","sourceRoot":"","sources":["../../../src/loaders/api-base.js"],"names":[],"mappings":"AAOA;;GAEG;AACH;IAQE;;;;;OAKG;IACH,oBAHW,MAAM,eACN,MAAM,OAIhB;IAED;;;;;;;;;;;OAWG;IACH,iBALG;QAAuB,UAAU,EAAzB,MAAM;QACS,aAAa,EAA5B,MAAM;QACU,UAAU,GAA1B,MAAM,YAAC;KACf,GAAU,MAAM,CAIlB;IAED;;;;;OAKG;IACH,6BAHW,MAAM,eACN,MAAM,OAIhB;IAED;;;;;OAKG;IACH,sBAHW,MAAM,SACN,MAAM,OAIhB;IAED;;;;;;OAMG;IACH,yBAJW,MAAM,SACN,MAAM,GAAC,MAAM,GAAC,OAAO,GAAC,IAAI,YAC1B,OAAO,OAIjB;IAED;;;;;OAKG;IACH,mBAHW,KAAK,GAAC,MAAM,qBACZ,MAAM,OAIhB;IAED;;;;OAIG;IACH,iBAFW,MAAM,GAAC,IAAI,OAIrB;IAED;;;;;;;OAOG;IACH,6BAJW,MAAM,GAAC,IAAI,OAMrB;IAED;;;;OAIG;IACH,0BAFW,CAAC,KAAK,EAAE,KAAK,GAAC,MAAM,KAAK,OAAO,GAAG;QAAE,KAAK,EAAE,MAAM,CAAA;KAAE,OAI9D;IAED;;;;;OAKG;IACH,iBAHW,MAAM,MACN,MAAM,OAIhB;IAED;;;;;MAKE;IACF,aAHW,MAAM,YACN;QAAC,gBAAgB,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,OAAO,GAAC,OAAO,GAAC,OAAO,GAAC,MAAM,GAAC,MAAM,CAAA;KAAC,OAIpF;IAED;;;OAGG;IACH,aAEC;IAED;;;;OAIG;IACH,qBAFW,MAAM,OAIhB;IAED;;;;OAIG;IACH,oBAEC;IAED;;;;;OAKG;IACH,mBAEC;IAED;;;;;;;;;;OAUG;IACH,6BARW;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,GAAG,CAAC,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAA;KAAC,OAUrF;IAED;;;;;OAKG;IACH,0BAHW,MAAM,OAKhB;IAED;;;;;;MAME;IACF,eAJa,mBAAmB,CAM/B;IAED;;;;;;MAME;IACF,mBAJW,MAAM,gBACN,MAAM,YACN;QAAC,gBAAgB,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,OAAO,GAAC,OAAO,GAAC,OAAO,GAAC,MAAM,GAAC,MAAM,CAAA;KAAC,OAIpF;;CACF;kCAjNY,OAAO,yBAAyB,EAAE,mBAAmB"}
1
+ {"version":3,"file":"api-base.d.ts","sourceRoot":"","sources":["../../../src/loaders/api-base.js"],"names":[],"mappings":"AAOA;;GAEG;AACH;IAQE;;;;;OAKG;IACH,oBAHW,MAAM,eACN,MAAM,OAIhB;IAED;;;;;;;;;;;OAWG;IACH,iBALG;QAAuB,UAAU,EAAzB,MAAM;QACS,aAAa,EAA5B,MAAM;QACU,UAAU,GAA1B,MAAM,YAAC;KACf,GAAU,MAAM,CAIlB;IAED;;;;;OAKG;IACH,6BAHW,MAAM,eACN,MAAM,OAIhB;IAED;;;;;OAKG;IACH,sBAHW,MAAM,SACN,MAAM,OAIhB;IAED;;;;;;OAMG;IACH,yBAJW,MAAM,SACN,MAAM,GAAC,MAAM,GAAC,OAAO,GAAC,IAAI,YAC1B,OAAO,OAIjB;IAED;;;;;OAKG;IACH,mBAHW,KAAK,GAAC,MAAM,qBACZ,MAAM,OAIhB;IAED;;;;OAIG;IACH,iBAFW,MAAM,GAAC,IAAI,OAIrB;IAED;;;;;;;OAOG;IACH,6BAJW,MAAM,GAAC,IAAI,OAMrB;IAED;;;;OAIG;IACH,0BAFW,CAAC,KAAK,EAAE,KAAK,GAAC,MAAM,KAAK,OAAO,GAAG;QAAE,KAAK,EAAE,MAAM,CAAA;KAAE,OAI9D;IAED;;;;;OAKG;IACH,iBAHW,MAAM,MACN,MAAM,OAIhB;IAED;;;;;MAKE;IACF,aAHW,MAAM,YACN;QAAC,gBAAgB,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,OAAO,GAAC,OAAO,GAAC,OAAO,GAAC,MAAM,GAAC,MAAM,CAAA;KAAC,OAIpF;IAED;;;OAGG;IACH,aAEC;IAED;;;;OAIG;IACH,qBAFW,MAAM,OAIhB;IAED;;;;OAIG;IACH,oBAEC;IAED;;;;;OAKG;IACH,mBAEC;IAED;;;;;;;;;;OAUG;IACH,6BARW;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,GAAG,CAAC,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAA;KAAC,OAUrF;IAED;;;;;OAKG;IACH,0BAHW,MAAM,OAKhB;IAED;;;;;;MAME;IACF,eAJa,mBAAmB,CAM/B;IAED;;;;;;MAME;IACF,mBAJW,MAAM,gBACN,MAAM,YACN;QAAC,gBAAgB,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,OAAO,GAAC,OAAO,GAAC,OAAO,GAAC,MAAM,GAAC,MAAM,CAAA;KAAC,OAIpF;IAED;;;;;;OAMG;IACH,cAJW,MAAM,WACN,MAAM,OAAC,GACL;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAC;QAAC,gBAAgB,EAAE,MAAM,CAAA;KAAC,CAIpF;;CACF;kCA5NY,OAAO,yBAAyB,EAAE,mBAAmB"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@newrelic/browser-agent",
3
- "version": "1.290.1",
3
+ "version": "1.291.0",
4
4
  "private": false,
5
5
  "author": "New Relic Browser Agent Team <browser-agent@newrelic.com>",
6
6
  "description": "New Relic Browser Agent",
@@ -8,7 +8,6 @@ import { VERSION } from '../constants/env'
8
8
  import { globalScope, isWorkerScope } from '../constants/runtime'
9
9
  import { handle } from '../event-emitter/handle'
10
10
  import { eventListenerOpts } from '../event-listener/event-listener-opts'
11
- import { SESSION_EVENTS } from '../session/constants'
12
11
  import { now } from '../timing/now'
13
12
  import { subscribeToEOL } from '../unload/eol'
14
13
  import { cleanURL } from '../url/clean-url'
@@ -36,10 +35,6 @@ export class Harvester {
36
35
  this.initializedAggregates.forEach(aggregateInst => this.triggerHarvestFor(aggregateInst, { isFinalHarvest: true }))
37
36
  /* This callback should run in bubble phase, so that that CWV api, like "onLCP", is called before the final harvest so that emitted timings are part of last outgoing. */
38
37
  }, false)
39
-
40
- /* Flush all buffered data if session resets and give up retries. This should be synchronous to ensure that the correct `session` value is sent.
41
- 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 the wrong session ID. */
42
- agentRef.ee.on(SESSION_EVENTS.RESET, () => this.initializedAggregates.forEach(aggregateInst => this.triggerHarvestFor(aggregateInst, { forceNoRetry: true })))
43
38
  }
44
39
 
45
40
  startTimer (harvestInterval = this.agentRef.init.harvest.interval) {
@@ -227,7 +227,10 @@ export class SessionEntity {
227
227
  // * stop recording (stn and sr)...
228
228
  // * delete the session and start over
229
229
  try {
230
- if (this.initialized) this.ee.emit(SESSION_EVENTS.RESET)
230
+ if (this.initialized) {
231
+ this.ee.emit(SESSION_EVENTS.RESET)
232
+ this.state.numOfResets++
233
+ }
231
234
  this.storage.remove(this.lookupKey)
232
235
  this.inactiveTimer?.abort?.()
233
236
  this.expiresTimer?.clear?.()
@@ -239,7 +242,7 @@ export class SessionEntity {
239
242
  storage: this.storage,
240
243
  expiresMs: this.expiresMs,
241
244
  inactiveMs: this.inactiveMs,
242
- numOfResets: ++this.state.numOfResets
245
+ numOfResets: this.state.numOfResets
243
246
  })
244
247
  return this.read()
245
248
  } catch (e) {
@@ -256,10 +259,6 @@ export class SessionEntity {
256
259
  this.write({ ...existingData, inactiveAt: this.getFutureTimestamp(this.inactiveMs) })
257
260
  }
258
261
 
259
- isAfterSessionExpiry (timestamp) {
260
- return this.state.numOfResets > 0 || (typeof timestamp === 'number' && typeof this.state.expiresAt === 'number' && timestamp >= this.state.expiresAt)
261
- }
262
-
263
262
  /**
264
263
  * @param {number} timestamp
265
264
  * @returns {boolean}
@@ -3,6 +3,8 @@
3
3
  * SPDX-License-Identifier: Apache-2.0
4
4
  */
5
5
 
6
+ import { dispatchGlobalEvent } from '../dispatch/global-event'
7
+
6
8
  /* eslint no-console: ["error", { allow: ["debug"] }] */
7
9
 
8
10
  /**
@@ -14,4 +16,15 @@
14
16
  export function warn (code, secondary) {
15
17
  if (typeof console.debug !== 'function') return
16
18
  console.debug(`New Relic Warning: https://github.com/newrelic/newrelic-browser-agent/blob/main/docs/warning-codes.md#${code}`, secondary)
19
+ dispatchGlobalEvent({
20
+ agentIdentifier: null,
21
+ drained: null,
22
+ type: 'data',
23
+ name: 'warn',
24
+ feature: 'warn',
25
+ data: {
26
+ code,
27
+ secondary
28
+ }
29
+ })
17
30
  }
@@ -220,6 +220,21 @@ export class Aggregate extends AggregateBase {
220
220
  }, this.featureName, this.ee)
221
221
  }
222
222
 
223
+ registerHandler('api-measure', (args, n) => {
224
+ const { start, duration, customAttributes } = args
225
+
226
+ const event = {
227
+ ...customAttributes,
228
+ eventType: 'BrowserPerformance',
229
+ timestamp: Math.floor(agentRef.runtime.timeKeeper.correctRelativeTimestamp(start)),
230
+ entryName: n,
231
+ entryDuration: duration,
232
+ entryType: 'measure'
233
+ }
234
+
235
+ this.addEvent(event)
236
+ }, this.featureName, this.ee)
237
+
223
238
  agentRef.runtime.harvester.triggerHarvestFor(this)
224
239
  this.drain()
225
240
  })
@@ -11,6 +11,7 @@ import { setupAddPageActionAPI } from '../../../loaders/api/addPageAction'
11
11
  import { setupFinishedAPI } from '../../../loaders/api/finished'
12
12
  import { setupRecordCustomEventAPI } from '../../../loaders/api/recordCustomEvent'
13
13
  import { setupRegisterAPI } from '../../../loaders/api/register'
14
+ import { setupMeasureAPI } from '../../../loaders/api/measure'
14
15
  import { InstrumentBase } from '../../utils/instrument-base'
15
16
  import { FEATURE_NAME, OBSERVED_EVENTS, OBSERVED_WINDOW_EVENTS } from '../constants'
16
17
 
@@ -32,6 +33,7 @@ export class Instrument extends InstrumentBase {
32
33
  setupRecordCustomEventAPI(agentRef)
33
34
  setupFinishedAPI(agentRef)
34
35
  setupRegisterAPI(agentRef)
36
+ setupMeasureAPI(agentRef)
35
37
 
36
38
  if (isBrowserScope) {
37
39
  if (agentRef.init.user_actions.enabled) {
@@ -262,6 +262,10 @@ export class Aggregate extends AggregateBase {
262
262
  if (recorderEvents.type === 'preloaded') this.agentRef.runtime.harvester.triggerHarvestFor(this)
263
263
  payloadOutput.payload = payload
264
264
 
265
+ if (!this.agentRef.runtime.session.state.traceHarvestStarted) {
266
+ warn(59, JSON.stringify(this.agentRef.runtime.session.state))
267
+ }
268
+
265
269
  return [payloadOutput]
266
270
  }
267
271
 
@@ -155,10 +155,6 @@ export class Recorder {
155
155
  /** Store a payload in the buffer (this.#events). This should be the callback to the recording lib noticing a mutation */
156
156
  store (event, isCheckout) {
157
157
  if (!event) return
158
- if (this.parent.agentRef.runtime.session?.isAfterSessionExpiry(event.timestamp)) {
159
- handle(SUPPORTABILITY_METRIC_CHANNEL, ['Session/Expired/SessionReplay/Seen'], undefined, FEATURE_NAMES.metrics, this.ee)
160
- return
161
- }
162
158
 
163
159
  if (!(this.parent instanceof AggregateBase) && this.#preloaded.length) this.currentBufferTarget = this.#preloaded[this.#preloaded.length - 1]
164
160
  else this.currentBufferTarget = this.#events
@@ -11,6 +11,7 @@ import { globalScope } from '../../../common/constants/runtime'
11
11
  import { MODE, SESSION_EVENTS } from '../../../common/session/constants'
12
12
  import { applyFnToProps } from '../../../common/util/traverse'
13
13
  import { cleanURL } from '../../../common/url/clean-url'
14
+ import { warn } from '../../../common/util/console'
14
15
 
15
16
  const ERROR_MODE_SECONDS_WINDOW = 30 * 1000 // sliding window of nodes to track when simply monitoring (but not harvesting) in error mode
16
17
  /** Reserved room for query param attrs */
@@ -97,10 +98,12 @@ export class Aggregate extends AggregateBase {
97
98
  }
98
99
  this.agentRef.runtime.session.write({ sessionTraceMode: this.mode })
99
100
  this.drain()
101
+ /** try to harvest immediately. This will not send if the trace is not running in FULL mode due to the pre-harvest checks. */
102
+ this.agentRef.runtime.harvester.triggerHarvestFor(this)
100
103
  }
101
104
 
102
105
  preHarvestChecks () {
103
- if (this.mode !== MODE.FULL) return // only allow harvest if running in full mode
106
+ if (this.blocked || this.mode !== MODE.FULL) return // only allow harvest if running in full mode
104
107
  if (!this.timeKeeper?.ready) return // this should likely never happen, but just to be safe, we should never harvest if we cant correct time
105
108
  if (!this.agentRef.runtime.session) return // session entity is required for trace to run and continue running
106
109
  if (this.sessionId !== this.agentRef.runtime.session.state.value || this.ptid !== this.agentRef.runtime.ptid) {
@@ -178,7 +181,8 @@ export class Aggregate extends AggregateBase {
178
181
  }
179
182
 
180
183
  /** Stop running for the remainder of the page lifecycle */
181
- abort () {
184
+ abort (code) {
185
+ warn(60, code)
182
186
  this.blocked = true
183
187
  this.mode = MODE.OFF
184
188
  this.agentRef.runtime.session.write({ sessionTraceMode: this.mode })
@@ -41,10 +41,6 @@ export class TraceStorage {
41
41
  this.parent = parent
42
42
  }
43
43
 
44
- isAfterSessionExpiry (entryTimestamp) {
45
- return this.parent.agentRef.runtime?.session?.isAfterSessionExpiry((this.parent.timeKeeper?.ready && this.parent.timeKeeper.convertRelativeTimestamp(entryTimestamp)) ?? undefined)
46
- }
47
-
48
44
  /** Central function called by all the other store__ & addToTrace API to append a trace node. */
49
45
  storeSTN (stn) {
50
46
  if (this.parent.blocked) return
@@ -53,10 +49,6 @@ export class TraceStorage {
53
49
  const openedSpace = this.trimSTNs(ERROR_MODE_SECONDS_WINDOW) // but maybe we could make some space by discarding irrelevant nodes if we're in sessioned Error mode
54
50
  if (openedSpace === 0) return
55
51
  }
56
- if (this.isAfterSessionExpiry(stn.s)) {
57
- this.parent.reportSupportabilityMetric('Session/Expired/SessionTrace/Seen')
58
- return
59
- }
60
52
 
61
53
  if (this.trace[stn.n]) this.trace[stn.n].push(stn)
62
54
  else this.trace[stn.n] = [stn]
@@ -24,3 +24,4 @@ export const SET_PAGE_VIEW_NAME = 'setPageViewName'
24
24
  export const SET_USER_ID = 'setUserId'
25
25
  export const START = 'start'
26
26
  export const WRAP_LOGGER = 'wrapLogger'
27
+ export const MEASURE = 'measure'
@@ -0,0 +1,53 @@
1
+ /**
2
+ * Copyright 2020-2025 New Relic, Inc. All rights reserved.
3
+ * SPDX-License-Identifier: Apache-2.0
4
+ */
5
+ import { handle } from '../../common/event-emitter/handle'
6
+ import { now } from '../../common/timing/now'
7
+ import { warn } from '../../common/util/console'
8
+ import { FEATURE_NAMES } from '../features/features'
9
+ import { prefix, MEASURE } from './constants'
10
+ import { setupAPI } from './sharedHandlers'
11
+
12
+ export function setupMeasureAPI (agent) {
13
+ setupAPI(MEASURE, function (name, options) {
14
+ const n = now()
15
+ const { start, end, customAttributes } = options || {}
16
+ const returnObj = { customAttributes: customAttributes || {} }
17
+
18
+ if (typeof returnObj.customAttributes !== 'object' || typeof name !== 'string' || name.length === 0) {
19
+ warn(57)
20
+ return
21
+ }
22
+
23
+ /**
24
+ * getValueFromTiming - Helper function to extract a numeric value from a supplied option.
25
+ * @param {Number|PerformanceMark} [timing] The timing value
26
+ * @param {Number} [d] The default value to return if timing is invalid
27
+ * @returns {Number} The timing value or the default value
28
+ */
29
+ const getValueFromTiming = (timing, d) => {
30
+ if (timing == null) return d
31
+ if (typeof timing === 'number') return timing
32
+ if (timing instanceof PerformanceMark) return timing.startTime
33
+ return Number.NaN
34
+ }
35
+
36
+ returnObj.start = getValueFromTiming(start, 0)
37
+ returnObj.end = getValueFromTiming(end, n)
38
+ if (Number.isNaN(returnObj.start) || Number.isNaN(returnObj.end)) {
39
+ warn(57)
40
+ return
41
+ }
42
+
43
+ returnObj.duration = returnObj.end - returnObj.start
44
+ if (returnObj.duration < 0) {
45
+ warn(58)
46
+ return
47
+ }
48
+
49
+ handle(prefix + MEASURE, [returnObj, name], undefined, FEATURE_NAMES.genericEvents, agent.ee)
50
+
51
+ return returnObj
52
+ }, agent)
53
+ }
@@ -3,7 +3,7 @@
3
3
  * SPDX-License-Identifier: Apache-2.0
4
4
  */
5
5
  import { warn } from '../common/util/console'
6
- import { ADD_PAGE_ACTION, ADD_RELEASE, ADD_TO_TRACE, FINISHED, INTERACTION, LOG, NOTICE_ERROR, PAUSE_REPLAY, RECORD_CUSTOM_EVENT, RECORD_REPLAY, REGISTER, SET_APPLICATION_VERSION, SET_CURRENT_ROUTE_NAME, SET_CUSTOM_ATTRIBUTE, SET_ERROR_HANDLER, SET_PAGE_VIEW_NAME, SET_USER_ID, START, WRAP_LOGGER } from './api/constants'
6
+ import { ADD_PAGE_ACTION, ADD_RELEASE, ADD_TO_TRACE, FINISHED, INTERACTION, LOG, NOTICE_ERROR, PAUSE_REPLAY, RECORD_CUSTOM_EVENT, RECORD_REPLAY, REGISTER, SET_APPLICATION_VERSION, SET_CURRENT_ROUTE_NAME, SET_CUSTOM_ATTRIBUTE, SET_ERROR_HANDLER, SET_PAGE_VIEW_NAME, SET_USER_ID, START, WRAP_LOGGER, MEASURE } from './api/constants'
7
7
 
8
8
  /**
9
9
  * @typedef {import('./api/interaction-types').InteractionInstance} InteractionInstance
@@ -215,4 +215,15 @@ export class ApiBase {
215
215
  wrapLogger (parent, functionName, options) {
216
216
  return this.#callMethod(WRAP_LOGGER, parent, functionName, options)
217
217
  }
218
+
219
+ /**
220
+ * Measures a task that is recorded as a BrowserPerformance event.
221
+ * {@link https://docs.newrelic.com/docs/browser/new-relic-browser/browser-apis/measure/}
222
+ * @param {string} name The name of the task
223
+ * @param {object?} options An object used to control the way the measure API operates
224
+ * @returns {{start: number, end: number, duration: number, customAttributes: object}} Measurement details
225
+ */
226
+ measure (name, options) {
227
+ return this.#callMethod(MEASURE, name, options)
228
+ }
218
229
  }