@newrelic/browser-agent 1.246.1 → 1.248.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 (107) hide show
  1. package/CHANGELOG.md +24 -0
  2. package/README.md +10 -2
  3. package/dist/cjs/common/config/state/info.js +2 -1
  4. package/dist/cjs/common/config/state/init.js +2 -1
  5. package/dist/cjs/common/config/state/loader-config.js +2 -1
  6. package/dist/cjs/common/config/state/runtime.js +2 -1
  7. package/dist/cjs/common/constants/env.cdn.js +1 -1
  8. package/dist/cjs/common/constants/env.npm.js +1 -1
  9. package/dist/cjs/common/constants/runtime.js +3 -1
  10. package/dist/cjs/common/dispatch/global-event.js +19 -0
  11. package/dist/cjs/common/session/session-entity.js +14 -9
  12. package/dist/cjs/common/util/feature-flags.js +17 -12
  13. package/dist/cjs/common/window/load.js +1 -0
  14. package/dist/cjs/common/window/nreum.js +18 -17
  15. package/dist/cjs/features/metrics/aggregate/index.js +4 -2
  16. package/dist/cjs/features/session_replay/aggregate/index.js +70 -20
  17. package/dist/cjs/features/session_trace/aggregate/index.js +33 -18
  18. package/dist/cjs/features/spa/aggregate/index.js +7 -1
  19. package/dist/cjs/features/utils/aggregate-base.js +3 -1
  20. package/dist/cjs/loaders/agent-base.js +19 -0
  21. package/dist/cjs/loaders/agent.js +6 -4
  22. package/dist/cjs/loaders/api/api.js +10 -1
  23. package/dist/cjs/loaders/configure/configure.js +14 -13
  24. package/dist/cjs/loaders/configure/nonce.js +13 -0
  25. package/dist/cjs/loaders/configure/nonce.npm.js +2 -0
  26. package/dist/cjs/loaders/micro-agent.js +5 -4
  27. package/dist/esm/common/config/state/info.js +3 -2
  28. package/dist/esm/common/config/state/init.js +3 -2
  29. package/dist/esm/common/config/state/loader-config.js +3 -2
  30. package/dist/esm/common/config/state/runtime.js +3 -2
  31. package/dist/esm/common/constants/env.cdn.js +1 -1
  32. package/dist/esm/common/constants/env.npm.js +1 -1
  33. package/dist/esm/common/constants/runtime.js +1 -0
  34. package/dist/esm/common/dispatch/global-event.js +13 -0
  35. package/dist/esm/common/session/session-entity.js +14 -9
  36. package/dist/esm/common/util/feature-flags.js +17 -12
  37. package/dist/esm/common/window/load.js +1 -1
  38. package/dist/esm/common/window/nreum.js +16 -16
  39. package/dist/esm/features/metrics/aggregate/index.js +4 -2
  40. package/dist/esm/features/session_replay/aggregate/index.js +69 -19
  41. package/dist/esm/features/session_trace/aggregate/index.js +33 -18
  42. package/dist/esm/features/spa/aggregate/index.js +7 -1
  43. package/dist/esm/features/utils/aggregate-base.js +3 -1
  44. package/dist/esm/loaders/agent-base.js +19 -0
  45. package/dist/esm/loaders/agent.js +7 -5
  46. package/dist/esm/loaders/api/api.js +10 -1
  47. package/dist/esm/loaders/configure/configure.js +15 -14
  48. package/dist/esm/loaders/configure/nonce.js +11 -0
  49. package/dist/esm/loaders/configure/nonce.npm.js +1 -0
  50. package/dist/esm/loaders/micro-agent.js +6 -5
  51. package/dist/types/common/config/state/info.d.ts.map +1 -1
  52. package/dist/types/common/config/state/init.d.ts.map +1 -1
  53. package/dist/types/common/config/state/loader-config.d.ts.map +1 -1
  54. package/dist/types/common/config/state/runtime.d.ts.map +1 -1
  55. package/dist/types/common/constants/runtime.d.ts +1 -0
  56. package/dist/types/common/constants/runtime.d.ts.map +1 -1
  57. package/dist/types/common/dispatch/global-event.d.ts +2 -0
  58. package/dist/types/common/dispatch/global-event.d.ts.map +1 -0
  59. package/dist/types/common/session/session-entity.d.ts +1 -0
  60. package/dist/types/common/session/session-entity.d.ts.map +1 -1
  61. package/dist/types/common/util/feature-flags.d.ts.map +1 -1
  62. package/dist/types/common/window/load.d.ts +1 -0
  63. package/dist/types/common/window/load.d.ts.map +1 -1
  64. package/dist/types/common/window/nreum.d.ts +7 -1
  65. package/dist/types/common/window/nreum.d.ts.map +1 -1
  66. package/dist/types/features/metrics/aggregate/index.d.ts.map +1 -1
  67. package/dist/types/features/session_replay/aggregate/index.d.ts +11 -1
  68. package/dist/types/features/session_replay/aggregate/index.d.ts.map +1 -1
  69. package/dist/types/features/session_trace/aggregate/index.d.ts.map +1 -1
  70. package/dist/types/features/spa/aggregate/index.d.ts.map +1 -1
  71. package/dist/types/loaders/agent-base.d.ts +13 -0
  72. package/dist/types/loaders/agent-base.d.ts.map +1 -1
  73. package/dist/types/loaders/agent.d.ts.map +1 -1
  74. package/dist/types/loaders/api/api.d.ts +2 -0
  75. package/dist/types/loaders/api/api.d.ts.map +1 -1
  76. package/dist/types/loaders/configure/configure.d.ts +4 -11
  77. package/dist/types/loaders/configure/configure.d.ts.map +1 -1
  78. package/dist/types/loaders/configure/nonce.d.ts +1 -0
  79. package/dist/types/loaders/configure/nonce.d.ts.map +1 -0
  80. package/dist/types/loaders/configure/nonce.npm.d.ts +1 -0
  81. package/dist/types/loaders/configure/nonce.npm.d.ts.map +1 -0
  82. package/dist/types/loaders/micro-agent.d.ts +2 -2
  83. package/dist/types/loaders/micro-agent.d.ts.map +1 -1
  84. package/package.json +2 -1
  85. package/src/common/config/state/info.js +3 -2
  86. package/src/common/config/state/init.js +3 -2
  87. package/src/common/config/state/loader-config.js +3 -2
  88. package/src/common/config/state/runtime.js +3 -2
  89. package/src/common/constants/runtime.js +2 -0
  90. package/src/common/dispatch/global-event.js +12 -0
  91. package/src/common/session/session-entity.js +13 -9
  92. package/src/common/util/feature-flags.js +15 -12
  93. package/src/common/window/__mocks__/nreum.js +2 -1
  94. package/src/common/window/load.js +1 -1
  95. package/src/common/window/nreum.js +15 -18
  96. package/src/features/metrics/aggregate/index.js +5 -1
  97. package/src/features/session_replay/aggregate/index.js +74 -23
  98. package/src/features/session_trace/aggregate/index.js +33 -14
  99. package/src/features/spa/aggregate/index.js +10 -1
  100. package/src/features/utils/aggregate-base.js +1 -1
  101. package/src/loaders/agent-base.js +19 -0
  102. package/src/loaders/agent.js +5 -5
  103. package/src/loaders/api/api.js +12 -1
  104. package/src/loaders/configure/configure.js +17 -15
  105. package/src/loaders/configure/nonce.js +12 -0
  106. package/src/loaders/configure/nonce.npm.js +1 -0
  107. package/src/loaders/micro-agent.js +5 -4
@@ -22,6 +22,9 @@ var _aggregateBase = require("../../utils/aggregate-base");
22
22
  var _firstContentfulPaint = require("../../../common/vitals/first-contentful-paint");
23
23
  var _firstPaint = require("../../../common/vitals/first-paint");
24
24
  var _bundleId = require("../../../common/ids/bundle-id");
25
+ var _runtime = require("../../../common/constants/runtime");
26
+ var _handle = require("../../../common/event-emitter/handle");
27
+ var _constants2 = require("../../metrics/constants");
25
28
  function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function (nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); }
26
29
  function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; }
27
30
  /*
@@ -186,7 +189,7 @@ class Aggregate extends _aggregateBase.AggregateBase {
186
189
  var ev = args[0];
187
190
  var evName = ev.type;
188
191
  var eventNode = ev["__nrNode:".concat(_bundleId.bundleId)];
189
- if (!state.pageLoaded && evName === 'load' && eventSource === window) {
192
+ if (!state.pageLoaded && (evName === 'load' && eventSource === window || _runtime.loadedAsDeferredBrowserScript)) {
190
193
  state.pageLoaded = true;
191
194
  // set to null so prevNode is set correctly
192
195
  this.prevNode = state.currentNode = null;
@@ -671,6 +674,9 @@ class Aggregate extends _aggregateBase.AggregateBase {
671
674
  }
672
675
  baseEE.emit('interactionSaved', [interaction]);
673
676
  state.interactionsToHarvest.push(interaction);
677
+ let smCategory = 'RouteChange';
678
+ if (interaction.root?.attrs?.trigger === 'initialPageLoad') smCategory = 'InitialPageLoad';else if (interaction.root?.attrs?.trigger === 'api') smCategory = 'Custom';
679
+ (0, _handle.handle)(_constants2.SUPPORTABILITY_METRIC_CHANNEL, ["Spa/Interaction/".concat(smCategory, "/Duration/Ms"), Math.max((interaction.root?.end || 0) - (interaction.root?.start || 0), 0)], undefined, _features.FEATURE_NAMES.metrics, baseEE);
674
680
  scheduler.scheduleHarvest(0);
675
681
  }
676
682
  function isEnabled() {
@@ -49,7 +49,9 @@ class AggregateBase extends _featureBase.FeatureBase {
49
49
  } catch (err) {
50
50
  // do nothing
51
51
  }
52
- (0, _configure.configure)(this.agentIdentifier, {
52
+ (0, _configure.configure)({
53
+ agentIdentifier: this.agentIdentifier
54
+ }, {
53
55
  ...(0, _nreum.gosCDN)(),
54
56
  info: {
55
57
  ...(0, _nreum.gosCDN)().info,
@@ -106,5 +106,24 @@ class AgentBase {
106
106
  start(featureNames) {
107
107
  (0, _console.warn)('Call to agent api addRelease failed. The agent is not currently initialized.');
108
108
  }
109
+
110
+ /**
111
+ * Forces a replay to record. If a replay is already actively recording, this call will be ignored.
112
+ * If a recording has not been started, a new one will be created. If a recording has been started, but is currently not recording, it will resume recording.
113
+ * {@link https://docs.newrelic.com/docs/browser/new-relic-browser/browser-apis/recordReplay/}
114
+ */
115
+ recordReplay() {
116
+ (0, _console.warn)('Call to agent api recordReplay failed. The agent is not currently initialized.');
117
+ }
118
+
119
+ /**
120
+ * Forces an active replay to pause recording. If a replay is already actively recording, this call will cause the recording to pause.
121
+ * If a recording is not currently recording, this call will be ignored. This API will pause both manual and automatic replays that are in progress.
122
+ * The only way to resume recording after manually pausing a replay is to manually record again using the recordReplay() API.
123
+ * {@link https://docs.newrelic.com/docs/browser/new-relic-browser/browser-apis/recordReplay/}
124
+ */
125
+ pauseReplay() {
126
+ (0, _console.warn)('Call to agent api pauseReplay failed. The agent is not currently initialized.');
127
+ }
109
128
  }
110
129
  exports.AgentBase = AgentBase;
@@ -5,6 +5,7 @@ Object.defineProperty(exports, "__esModule", {
5
5
  });
6
6
  exports.Agent = void 0;
7
7
  require("./configure/public-path.npm");
8
+ require("./configure/nonce.npm");
8
9
  var _agentBase = require("./agent-base");
9
10
  var _enabledFeatures = require("./features/enabled-features");
10
11
  var _configure = require("./configure/configure");
@@ -49,12 +50,15 @@ class Agent extends _agentBase.AgentBase {
49
50
  agentIdentifier: this.agentIdentifier
50
51
  });
51
52
  this.features = {};
53
+ (0, _nreum.setNREUMInitializedAgent)(agentIdentifier, this); // append this agent onto the global NREUM.initializedAgents
54
+
52
55
  this.desiredFeatures = new Set(options.features || []); // expected to be a list of static Instrument/InstrumentBase classes, see "spa.js" for example
53
56
  // For Now... ALL agents must make the rum call whether the page_view_event feature was enabled or not.
54
57
  // NR1 creates an index on the rum call, and if not seen for a few days, will remove the browser app!
55
58
  // Future work is being planned to evaluate removing this behavior from the backend, but for now we must ensure this call is made
56
59
  this.desiredFeatures.add(_instrument.Instrument);
57
- Object.assign(this, (0, _configure.configure)(this.agentIdentifier, options, options.loaderType || 'agent'));
60
+ (0, _configure.configure)(this, options, options.loaderType || 'agent'); // add api, exposed, and other config properties
61
+
58
62
  this.run();
59
63
  }
60
64
  get config() {
@@ -66,7 +70,6 @@ class Agent extends _agentBase.AgentBase {
66
70
  };
67
71
  }
68
72
  run() {
69
- const NR_FEATURES_REF_NAME = 'features';
70
73
  // Attempt to initialize all the requested features (sequentially in prio order & synchronously), with any failure aborting the whole process.
71
74
  try {
72
75
  const enabledFeatures = (0, _enabledFeatures.getEnabledFeatures)(this.agentIdentifier);
@@ -81,7 +84,6 @@ class Agent extends _agentBase.AgentBase {
81
84
  this.features[InstrumentCtor.featureName] = new InstrumentCtor(this.agentIdentifier, this.sharedAggregator);
82
85
  }
83
86
  });
84
- (0, _nreum.gosNREUMInitializedAgents)(this.agentIdentifier, this.features, NR_FEATURES_REF_NAME);
85
87
  } catch (err) {
86
88
  (0, _console.warn)('Failed to initialize all enabled instrument classes (agent aborted) -', err);
87
89
  for (const featName in this.features) {
@@ -90,7 +92,7 @@ class Agent extends _agentBase.AgentBase {
90
92
  }
91
93
  const newrelic = (0, _nreum.gosNREUM)();
92
94
  delete newrelic.initializedAgents[this.agentIdentifier]?.api; // prevent further calls to agent-specific APIs (see "configure.js")
93
- delete newrelic.initializedAgents[this.agentIdentifier]?.[NR_FEATURES_REF_NAME]; // GC mem used internally by features
95
+ delete newrelic.initializedAgents[this.agentIdentifier]?.features; // GC mem used internally by features
94
96
  delete this.sharedAggregator;
95
97
  // Keep the initialized agent object with its configs for troubleshooting purposes.
96
98
  newrelic.ee?.abort(); // set flag and clear global backlog
@@ -26,7 +26,7 @@ const CUSTOM_ATTR_GROUP = 'CUSTOM/'; // the subgroup items should be stored unde
26
26
  exports.CUSTOM_ATTR_GROUP = CUSTOM_ATTR_GROUP;
27
27
  function setTopLevelCallers() {
28
28
  const nr = (0, _nreum.gosCDN)();
29
- const funcs = ['setErrorHandler', 'finished', 'addToTrace', 'addRelease', 'addPageAction', 'setCurrentRouteName', 'setPageViewName', 'setCustomAttribute', 'interaction', 'noticeError', 'setUserId', 'setApplicationVersion', 'start'];
29
+ const funcs = ['setErrorHandler', 'finished', 'addToTrace', 'addRelease', 'addPageAction', 'setCurrentRouteName', 'setPageViewName', 'setCustomAttribute', 'interaction', 'noticeError', 'setUserId', 'setApplicationVersion', 'start', 'recordReplay', 'pauseReplay'];
30
30
  funcs.forEach(f => {
31
31
  nr[f] = function () {
32
32
  for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
@@ -147,6 +147,14 @@ function setAPI(agentIdentifier, forceDrain) {
147
147
  (0, _console.warn)('An unexpected issue occurred', err);
148
148
  }
149
149
  };
150
+ apiInterface.recordReplay = function () {
151
+ (0, _handle.handle)(_constants.SUPPORTABILITY_METRIC_CHANNEL, ['API/recordReplay/called'], undefined, _features.FEATURE_NAMES.metrics, instanceEE);
152
+ (0, _handle.handle)('recordReplay', [], undefined, _features.FEATURE_NAMES.sessionReplay, instanceEE);
153
+ };
154
+ apiInterface.pauseReplay = function () {
155
+ (0, _handle.handle)(_constants.SUPPORTABILITY_METRIC_CHANNEL, ['API/pauseReplay/called'], undefined, _features.FEATURE_NAMES.metrics, instanceEE);
156
+ (0, _handle.handle)('pauseReplay', [], undefined, _features.FEATURE_NAMES.sessionReplay, instanceEE);
157
+ };
150
158
  apiInterface.interaction = function () {
151
159
  return new InteractionHandle().get();
152
160
  };
@@ -156,6 +164,7 @@ function setAPI(agentIdentifier, forceDrain) {
156
164
  var contextStore = {};
157
165
  var ixn = this;
158
166
  var hasCb = typeof cb === 'function';
167
+ (0, _handle.handle)(_constants.SUPPORTABILITY_METRIC_CHANNEL, ['API/createTracer/called'], undefined, _features.FEATURE_NAMES.metrics, instanceEE);
159
168
  (0, _handle.handle)(spaPrefix + 'tracer', [(0, _now.now)(), name, contextStore], ixn, _features.FEATURE_NAMES.spa, instanceEE);
160
169
  return function () {
161
170
  tracerEE.emit((hasCb ? '' : 'no-') + 'fn-start', [(0, _now.now)(), ixn, hasCb], contextStore);
@@ -12,7 +12,10 @@ var _runtime = require("../../common/constants/runtime");
12
12
  var _publicPath = require("./public-path");
13
13
  let alreadySetOnce = false; // the configure() function can run multiple times in agent lifecycle
14
14
 
15
- function configure(agentIdentifier) {
15
+ /**
16
+ * Sets or re-sets the agent's configuration values from global settings. This also attach those as properties to the agent instance.
17
+ */
18
+ function configure(agent) {
16
19
  let opts = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
17
20
  let loaderType = arguments.length > 2 ? arguments[2] : undefined;
18
21
  let forceDrain = arguments.length > 3 ? arguments[3] : undefined;
@@ -33,31 +36,29 @@ function configure(agentIdentifier) {
33
36
  // eslint-disable-next-line camelcase
34
37
  loader_config = nr.loader_config;
35
38
  }
36
- (0, _config.setConfiguration)(agentIdentifier, init || {});
39
+ (0, _config.setConfiguration)(agent.agentIdentifier, init || {});
37
40
  // eslint-disable-next-line camelcase
38
- (0, _config.setLoaderConfig)(agentIdentifier, loader_config || {});
41
+ (0, _config.setLoaderConfig)(agent.agentIdentifier, loader_config || {});
39
42
  info.jsAttributes ??= {};
40
43
  if (_runtime.isWorkerScope) {
41
44
  // add a default attr to all worker payloads
42
45
  info.jsAttributes.isWorker = true;
43
46
  }
44
- (0, _config.setInfo)(agentIdentifier, info);
45
- const updatedInit = (0, _config.getConfiguration)(agentIdentifier);
47
+ (0, _config.setInfo)(agent.agentIdentifier, info);
48
+ const updatedInit = (0, _config.getConfiguration)(agent.agentIdentifier);
46
49
  const internalTrafficList = [info.beacon, info.errorBeacon];
47
50
  if (!alreadySetOnce) {
48
- alreadySetOnce = true;
49
51
  if (updatedInit.proxy.assets) {
50
52
  (0, _publicPath.redefinePublicPath)(updatedInit.proxy.assets);
51
53
  internalTrafficList.push(updatedInit.proxy.assets);
52
54
  }
53
55
  if (updatedInit.proxy.beacon) internalTrafficList.push(updatedInit.proxy.beacon);
56
+ (0, _api.setTopLevelCallers)(); // no need to set global APIs on newrelic obj more than once
57
+ (0, _nreum.addToNREUM)('activatedFeatures', _featureFlags.activatedFeatures);
54
58
  }
55
59
  runtime.denyList = [...(updatedInit.ajax.deny_list || []), ...(updatedInit.ajax.block_internal ? internalTrafficList : [])];
56
- (0, _config.setRuntime)(agentIdentifier, runtime);
57
- (0, _api.setTopLevelCallers)();
58
- const api = (0, _api.setAPI)(agentIdentifier, forceDrain);
59
- (0, _nreum.gosNREUMInitializedAgents)(agentIdentifier, api, 'api');
60
- (0, _nreum.gosNREUMInitializedAgents)(agentIdentifier, exposed, 'exposed');
61
- (0, _nreum.addToNREUM)('activatedFeatures', _featureFlags.activatedFeatures);
62
- return api;
60
+ (0, _config.setRuntime)(agent.agentIdentifier, runtime);
61
+ if (agent.api === undefined) agent.api = (0, _api.setAPI)(agent.agentIdentifier, forceDrain);
62
+ if (agent.exposed === undefined) agent.exposed = exposed;
63
+ alreadySetOnce = true;
63
64
  }
@@ -0,0 +1,13 @@
1
+ "use strict";
2
+
3
+ /* global __webpack_require__ */
4
+
5
+ __webpack_require__.nc = (() => {
6
+ try {
7
+ return document?.currentScript?.nonce;
8
+ } catch (ex) {
9
+ // Swallow error and proceed like nonce is not defined
10
+ // This will happen when the agent is loaded in a worker scope
11
+ }
12
+ return '';
13
+ })();
@@ -0,0 +1,2 @@
1
+ // We don't support setting automating the nonce attribute in the npm package
2
+ "use strict";
@@ -38,12 +38,14 @@ class MicroAgent extends _agentBase.AgentBase {
38
38
  agentIdentifier: this.agentIdentifier
39
39
  });
40
40
  this.features = {};
41
- Object.assign(this, (0, _configure.configure)(this.agentIdentifier, {
41
+ (0, _nreum.setNREUMInitializedAgent)(agentIdentifier, this);
42
+ (0, _configure.configure)(this, {
42
43
  ...options,
43
44
  runtime: {
44
45
  isolatedBacklog: true
45
46
  }
46
- }, options.loaderType || 'micro-agent'));
47
+ }, options.loaderType || 'micro-agent');
48
+ Object.assign(this, this.api); // the APIs should be available at the class level for micro-agent
47
49
 
48
50
  /**
49
51
  * Starts a set of agent features if not running in "autoStart" mode
@@ -98,8 +100,7 @@ class MicroAgent extends _agentBase.AgentBase {
98
100
  }
99
101
  });
100
102
  });
101
- (0, _nreum.gosNREUMInitializedAgents)(this.agentIdentifier, this.features, 'features');
102
- return this;
103
+ return true;
103
104
  } catch (err) {
104
105
  (0, _console.warn)('Failed to initialize instrument classes.', err);
105
106
  return false;
@@ -1,4 +1,4 @@
1
- import { defaults as nrDefaults, gosNREUMInitializedAgents } from '../../window/nreum';
1
+ import { defaults as nrDefaults, getNREUMInitializedAgent } from '../../window/nreum';
2
2
  import { getModeledObject } from './configurable';
3
3
  const model = {
4
4
  // preset defaults
@@ -38,5 +38,6 @@ export function getInfo(id) {
38
38
  export function setInfo(id, obj) {
39
39
  if (!id) throw new Error('All info objects require an agent identifier!');
40
40
  _cache[id] = getModeledObject(obj, model);
41
- gosNREUMInitializedAgents(id, _cache[id], 'info');
41
+ const agentInst = getNREUMInitializedAgent(id);
42
+ if (agentInst) agentInst.info = _cache[id];
42
43
  }
@@ -1,7 +1,7 @@
1
1
  import { isValidSelector } from '../../dom/query-selector';
2
2
  import { DEFAULT_EXPIRES_MS, DEFAULT_INACTIVE_MS } from '../../session/constants';
3
3
  import { warn } from '../../util/console';
4
- import { gosNREUMInitializedAgents } from '../../window/nreum';
4
+ import { getNREUMInitializedAgent } from '../../window/nreum';
5
5
  import { getModeledObject } from './configurable';
6
6
  const model = () => {
7
7
  const hiddenState = {
@@ -165,7 +165,8 @@ export function getConfiguration(id) {
165
165
  export function setConfiguration(id, obj) {
166
166
  if (!id) throw new Error(missingAgentIdError);
167
167
  _cache[id] = getModeledObject(obj, model());
168
- gosNREUMInitializedAgents(id, _cache[id], 'config');
168
+ const agentInst = getNREUMInitializedAgent(id);
169
+ if (agentInst) agentInst.init = _cache[id];
169
170
  }
170
171
  export function getConfigurationValue(id, path) {
171
172
  if (!id) throw new Error(missingAgentIdError);
@@ -1,4 +1,4 @@
1
- import { gosNREUMInitializedAgents } from '../../window/nreum';
1
+ import { getNREUMInitializedAgent } from '../../window/nreum';
2
2
  import { getModeledObject } from './configurable';
3
3
  const model = {
4
4
  accountID: undefined,
@@ -17,5 +17,6 @@ export function getLoaderConfig(id) {
17
17
  export function setLoaderConfig(id, obj) {
18
18
  if (!id) throw new Error('All loader-config objects require an agent identifier!');
19
19
  _cache[id] = getModeledObject(obj, model);
20
- gosNREUMInitializedAgents(id, _cache[id], 'loader_config');
20
+ const agentInst = getNREUMInitializedAgent(id);
21
+ if (agentInst) agentInst.loader_config = _cache[id];
21
22
  }
@@ -1,5 +1,5 @@
1
1
  import { getModeledObject } from './configurable';
2
- import { gosNREUMInitializedAgents } from '../../window/nreum';
2
+ import { getNREUMInitializedAgent } from '../../window/nreum';
3
3
  import { globalScope } from '../../constants/runtime';
4
4
  import { BUILD_ENV, DIST_METHOD, VERSION } from "../../constants/env.npm";
5
5
  const model = {
@@ -31,5 +31,6 @@ export function getRuntime(id) {
31
31
  export function setRuntime(id, obj) {
32
32
  if (!id) throw new Error('All runtime objects require an agent identifier!');
33
33
  _cache[id] = getModeledObject(obj, model);
34
- gosNREUMInitializedAgents(id, _cache[id], 'runtime');
34
+ const agentInst = getNREUMInitializedAgent(id);
35
+ if (agentInst) agentInst.runtime = _cache[id];
35
36
  }
@@ -6,7 +6,7 @@
6
6
  /**
7
7
  * Exposes the version of the agent
8
8
  */
9
- export const VERSION = "1.246.1";
9
+ export const VERSION = "1.248.0";
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.246.1";
9
+ export const VERSION = "1.248.0";
10
10
 
11
11
  /**
12
12
  * Exposes the build type of the agent
@@ -15,6 +15,7 @@ export const isBrowserScope = typeof window !== 'undefined' && !!window.document
15
15
  */
16
16
  export const isWorkerScope = typeof WorkerGlobalScope !== 'undefined' && (typeof self !== 'undefined' && self instanceof WorkerGlobalScope && self.navigator instanceof WorkerNavigator || typeof globalThis !== 'undefined' && globalThis instanceof WorkerGlobalScope && globalThis.navigator instanceof WorkerNavigator);
17
17
  export const globalScope = isBrowserScope ? window : typeof WorkerGlobalScope !== 'undefined' && (typeof self !== 'undefined' && self instanceof WorkerGlobalScope && self || typeof globalThis !== 'undefined' && globalThis instanceof WorkerGlobalScope && globalThis);
18
+ export const loadedAsDeferredBrowserScript = globalScope?.document?.readyState === 'complete';
18
19
  export const initiallyHidden = Boolean(globalScope?.document?.visibilityState === 'hidden');
19
20
  export const initialLocation = '' + globalScope?.location;
20
21
  export const isiOS = /iPad|iPhone|iPod/.test(globalScope.navigator?.userAgent);
@@ -0,0 +1,13 @@
1
+ import { globalScope } from '../constants/runtime';
2
+ const GLOBAL_EVENT_NAMESPACE = 'newrelic';
3
+ export function dispatchGlobalEvent() {
4
+ let detail = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
5
+ try {
6
+ globalScope.dispatchEvent(new CustomEvent(GLOBAL_EVENT_NAMESPACE, {
7
+ detail
8
+ }));
9
+ } catch (err) {
10
+ // something happened... dispatchEvent or CustomEvent might not be supported
11
+ // decide what to do about it here
12
+ }
13
+ }
@@ -104,8 +104,8 @@ export class SessionEntity {
104
104
  this.expiresTimer = new Timer({
105
105
  // When the inactive timer ends, collect a SM and reset the session
106
106
  onEnd: () => {
107
- this.collectSM('expired', this);
108
- this.collectSM('duration', this);
107
+ this.collectSM('expired');
108
+ this.collectSM('duration');
109
109
  this.reset();
110
110
  }
111
111
  }, this.state.expiresAt - Date.now());
@@ -121,8 +121,8 @@ export class SessionEntity {
121
121
  this.inactiveTimer = new InteractionTimer({
122
122
  // When the inactive timer ends, collect a SM and reset the session
123
123
  onEnd: () => {
124
- this.collectSM('inactive', this);
125
- this.collectSM('duration', this);
124
+ this.collectSM('inactive');
125
+ this.collectSM('duration');
126
126
  this.reset();
127
127
  },
128
128
  // When the inactive timer refreshes, it will update the storage values with an update timestamp
@@ -172,14 +172,14 @@ export class SessionEntity {
172
172
  if (this.isInvalid(obj)) return {};
173
173
  // if the session expires, collect a SM count before resetting
174
174
  if (this.isExpired(obj.expiresAt)) {
175
- this.collectSM('expired', this);
175
+ this.collectSM('expired');
176
176
  this.collectSM('duration', obj, true);
177
177
  return this.reset();
178
178
  }
179
179
  // if "inactive" timer is expired at "read" time -- esp. initial read -- reset
180
180
  // collect a SM count before resetting
181
181
  if (this.isExpired(obj.inactiveAt)) {
182
- this.collectSM('inactive', this);
182
+ this.collectSM('inactive');
183
183
  this.collectSM('duration', obj, true);
184
184
  return this.reset();
185
185
  }
@@ -270,15 +270,20 @@ export class SessionEntity {
270
270
  collectSM(type, data, useUpdatedAt) {
271
271
  let value, tag;
272
272
  if (type === 'duration') {
273
- const startingTimestamp = data.expiresAt - data.expiresMs;
274
- const endingTimestamp = useUpdatedAt ? data.updatedAt : Date.now();
275
- value = endingTimestamp - startingTimestamp;
273
+ value = this.getDuration(data, useUpdatedAt);
276
274
  tag = 'Session/Duration/Ms';
277
275
  }
278
276
  if (type === 'expired') tag = 'Session/Expired/Seen';
279
277
  if (type === 'inactive') tag = 'Session/Inactive/Seen';
280
278
  if (tag) handle(SUPPORTABILITY_METRIC_CHANNEL, [tag, value], undefined, FEATURE_NAMES.metrics, this.ee);
281
279
  }
280
+ getDuration() {
281
+ let data = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : this.state;
282
+ let useUpdatedAt = arguments.length > 1 ? arguments[1] : undefined;
283
+ const startingTimestamp = data.expiresAt - this.expiresMs;
284
+ const endingTimestamp = !useUpdatedAt ? data.updatedAt : Date.now();
285
+ return endingTimestamp - startingTimestamp;
286
+ }
282
287
 
283
288
  /**
284
289
  * @param {number} futureMs - The number of ms to use to generate a future timestamp
@@ -5,6 +5,7 @@
5
5
  import { ee } from '../event-emitter/contextual-ee';
6
6
  import { handle } from '../event-emitter/handle';
7
7
  import { FEATURE_NAMES } from '../../loaders/features/features';
8
+ import { dispatchGlobalEvent } from '../dispatch/global-event';
8
9
  const bucketMap = {
9
10
  stn: [FEATURE_NAMES.sessionTrace],
10
11
  err: [FEATURE_NAMES.jserrors, FEATURE_NAMES.metrics],
@@ -18,18 +19,17 @@ const sentIds = new Set();
18
19
  export function activateFeatures(flags, agentIdentifier) {
19
20
  const sharedEE = ee.get(agentIdentifier);
20
21
  if (!(flags && typeof flags === 'object')) return;
21
- if (!sentIds.has(agentIdentifier)) {
22
- Object.entries(flags).forEach(_ref => {
23
- let [flag, num] = _ref;
24
- if (bucketMap[flag]) {
25
- bucketMap[flag].forEach(feat => {
26
- if (!num) handle('block-' + flag, [], undefined, feat, sharedEE);else handle('feat-' + flag, [], undefined, feat, sharedEE);
27
- handle('rumresp-' + flag, [Boolean(num)], undefined, feat, sharedEE); // this is a duplicate of feat-/block- but makes awaiting for 1 event easier than 2
28
- });
29
- } else if (num) handle('feat-' + flag, [], undefined, undefined, sharedEE); // not sure what other flags are overlooked, but there's a test for ones not in the map --
30
- activatedFeatures[flag] = Boolean(num);
31
- });
32
- }
22
+ if (sentIds.has(agentIdentifier)) return;
23
+ Object.entries(flags).forEach(_ref => {
24
+ let [flag, num] = _ref;
25
+ if (bucketMap[flag]) {
26
+ bucketMap[flag].forEach(feat => {
27
+ if (!num) handle('block-' + flag, [], undefined, feat, sharedEE);else handle('feat-' + flag, [], undefined, feat, sharedEE);
28
+ handle('rumresp-' + flag, [Boolean(num)], undefined, feat, sharedEE); // this is a duplicate of feat-/block- but makes awaiting for 1 event easier than 2
29
+ });
30
+ } else if (num) handle('feat-' + flag, [], undefined, undefined, sharedEE); // not sure what other flags are overlooked, but there's a test for ones not in the map --
31
+ activatedFeatures[flag] = Boolean(num);
32
+ });
33
33
 
34
34
  // Let the features waiting on their respective flags know that RUM response was received and that any missing flags are interpreted as bad entitlement / "off".
35
35
  // Hence, those features will not be hanging forever if their flags aren't included in the response.
@@ -40,5 +40,10 @@ export function activateFeatures(flags, agentIdentifier) {
40
40
  }
41
41
  });
42
42
  sentIds.add(agentIdentifier);
43
+
44
+ // let any window level subscribers know that the agent is running
45
+ dispatchGlobalEvent({
46
+ loaded: true
47
+ });
43
48
  }
44
49
  export const activatedFeatures = {};
@@ -1,5 +1,5 @@
1
1
  import { windowAddEventListener, documentAddEventListener } from '../event-listener/event-listener-opts';
2
- function checkState() {
2
+ export function checkState() {
3
3
  return typeof document === 'undefined' || document.readyState === 'complete';
4
4
  }
5
5
  export function onWindowLoad(cb, useCapture) {
@@ -55,24 +55,24 @@ export function gosNREUMOriginals() {
55
55
  }
56
56
  return nr;
57
57
  }
58
- export function gosNREUMInitializedAgents(id, obj, target) {
58
+ export function setNREUMInitializedAgent(id, newAgentInstance) {
59
59
  let nr = gosNREUM();
60
- const externallySupplied = nr.initializedAgents || {};
61
- const curr = externallySupplied[id] || {};
62
- if (!Object.keys(curr).length) {
63
- curr.initializedAt = {
64
- ms: now(),
65
- date: new Date()
66
- };
67
- }
68
- nr.initializedAgents = {
69
- ...externallySupplied,
70
- [id]: {
71
- ...curr,
72
- [target]: obj
73
- }
60
+ nr.initializedAgents ??= {};
61
+ newAgentInstance.initializedAt = {
62
+ ms: now(),
63
+ date: new Date()
74
64
  };
75
- return nr;
65
+ nr.initializedAgents[id] = newAgentInstance;
66
+ }
67
+
68
+ /**
69
+ * Get the agent instance under the associated identifier on the global newrelic object.
70
+ * @see setNREUMInitializedAgents
71
+ * @returns Existing agent instance under newrelic.initializedAgent[id], or undefined if it does not exist.
72
+ */
73
+ export function getNREUMInitializedAgent(id) {
74
+ let nr = gosNREUM();
75
+ return nr.initializedAgents?.[id];
76
76
  }
77
77
  export function addToNREUM(fnName, fn) {
78
78
  let nr = gosNREUM();
@@ -63,10 +63,12 @@ export class Aggregate extends AggregateBase {
63
63
  } = getRuntime(this.agentIdentifier);
64
64
  if (loaderType) this.storeSupportabilityMetrics("Generic/LoaderType/".concat(loaderType, "/Detected"));
65
65
  if (distMethod) this.storeSupportabilityMetrics("Generic/DistMethod/".concat(distMethod, "/Detected"));
66
-
67
- // frameworks on page
68
66
  if (isBrowserScope) {
69
67
  this.storeSupportabilityMetrics('Generic/Runtime/Browser/Detected');
68
+ const nonce = document?.currentScript?.nonce;
69
+ if (nonce && nonce !== '') {
70
+ this.storeSupportabilityMetrics('Generic/Runtime/Nonce/Detected');
71
+ }
70
72
 
71
73
  // These SMs are used by the AppExp team
72
74
  onDOMContentLoaded(() => {