@newrelic/browser-agent 1.251.1 → 1.252.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (67) hide show
  1. package/CHANGELOG.md +25 -0
  2. package/README.md +5 -5
  3. package/dist/cjs/common/config/state/init.js +2 -2
  4. package/dist/cjs/common/constants/env.cdn.js +1 -1
  5. package/dist/cjs/common/constants/env.npm.js +1 -1
  6. package/dist/cjs/common/drain/drain.js +3 -1
  7. package/dist/cjs/common/event-emitter/contextual-ee.js +7 -1
  8. package/dist/cjs/common/harvest/harvest.js +3 -2
  9. package/dist/cjs/common/session/session-entity.js +3 -1
  10. package/dist/cjs/features/ajax/instrument/index.js +2 -0
  11. package/dist/cjs/features/jserrors/instrument/index.js +5 -0
  12. package/dist/cjs/features/page_view_event/aggregate/index.js +1 -1
  13. package/dist/cjs/features/session_replay/aggregate/index.js +27 -2
  14. package/dist/cjs/features/session_replay/shared/recorder.js +10 -4
  15. package/dist/cjs/features/session_replay/shared/stylesheet-evaluator.js +13 -12
  16. package/dist/cjs/features/utils/instrument-base.js +1 -1
  17. package/dist/cjs/loaders/api/api-methods.js +8 -0
  18. package/dist/cjs/loaders/api/api.js +7 -8
  19. package/dist/cjs/loaders/configure/configure.js +1 -1
  20. package/dist/esm/common/config/state/init.js +2 -2
  21. package/dist/esm/common/constants/env.cdn.js +1 -1
  22. package/dist/esm/common/constants/env.npm.js +1 -1
  23. package/dist/esm/common/drain/drain.js +3 -1
  24. package/dist/esm/common/event-emitter/contextual-ee.js +7 -1
  25. package/dist/esm/common/harvest/harvest.js +3 -2
  26. package/dist/esm/common/session/session-entity.js +3 -1
  27. package/dist/esm/features/ajax/instrument/index.js +2 -0
  28. package/dist/esm/features/jserrors/instrument/index.js +5 -0
  29. package/dist/esm/features/page_view_event/aggregate/index.js +1 -1
  30. package/dist/esm/features/session_replay/aggregate/index.js +27 -2
  31. package/dist/esm/features/session_replay/shared/recorder.js +10 -4
  32. package/dist/esm/features/session_replay/shared/stylesheet-evaluator.js +13 -12
  33. package/dist/esm/features/utils/instrument-base.js +1 -1
  34. package/dist/esm/loaders/api/api-methods.js +2 -0
  35. package/dist/esm/loaders/api/api.js +7 -7
  36. package/dist/esm/loaders/configure/configure.js +1 -1
  37. package/dist/types/common/drain/drain.d.ts +2 -1
  38. package/dist/types/common/drain/drain.d.ts.map +1 -1
  39. package/dist/types/common/event-emitter/contextual-ee.d.ts.map +1 -1
  40. package/dist/types/common/harvest/harvest.d.ts.map +1 -1
  41. package/dist/types/common/session/session-entity.d.ts +0 -1
  42. package/dist/types/common/session/session-entity.d.ts.map +1 -1
  43. package/dist/types/features/ajax/instrument/index.d.ts.map +1 -1
  44. package/dist/types/features/jserrors/instrument/index.d.ts.map +1 -1
  45. package/dist/types/features/session_replay/aggregate/index.d.ts.map +1 -1
  46. package/dist/types/features/session_replay/shared/recorder.d.ts.map +1 -1
  47. package/dist/types/features/session_replay/shared/stylesheet-evaluator.d.ts +1 -1
  48. package/dist/types/features/session_replay/shared/stylesheet-evaluator.d.ts.map +1 -1
  49. package/dist/types/loaders/api/api-methods.d.ts +3 -0
  50. package/dist/types/loaders/api/api-methods.d.ts.map +1 -0
  51. package/dist/types/loaders/api/api.d.ts +0 -1
  52. package/dist/types/loaders/api/api.d.ts.map +1 -1
  53. package/package.json +4 -1
  54. package/src/common/config/state/init.js +2 -2
  55. package/src/common/drain/drain.js +3 -2
  56. package/src/common/event-emitter/contextual-ee.js +7 -1
  57. package/src/common/harvest/harvest.js +3 -2
  58. package/src/common/session/session-entity.js +3 -1
  59. package/src/features/ajax/instrument/index.js +2 -0
  60. package/src/features/jserrors/instrument/index.js +6 -0
  61. package/src/features/page_view_event/aggregate/index.js +1 -1
  62. package/src/features/session_replay/aggregate/index.js +21 -3
  63. package/src/features/session_replay/shared/recorder.js +10 -4
  64. package/src/features/session_replay/shared/stylesheet-evaluator.js +13 -12
  65. package/src/features/utils/instrument-base.js +1 -1
  66. package/src/loaders/api/api-methods.js +9 -0
  67. package/src/loaders/api/api.js +7 -17
@@ -52,6 +52,7 @@ export class Aggregate extends AggregateBase {
52
52
  this.entitled = false;
53
53
  this.recorder = args?.recorder;
54
54
  if (this.recorder) this.recorder.parent = this;
55
+ handle(SUPPORTABILITY_METRIC_CHANNEL, ['Config/SessionReplay/Enabled'], undefined, FEATURE_NAMES.metrics, this.ee);
55
56
  const shouldSetup = getConfigurationValue(agentIdentifier, 'privacy.cookies_enabled') === true && getConfigurationValue(agentIdentifier, 'session_trace.enabled') === true;
56
57
  if (shouldSetup) {
57
58
  // The SessionEntity class can emit a message indicating the session was cleared and reset (expiry, inactivity). This feature must abort and never resume if that occurs.
@@ -116,13 +117,37 @@ export class Aggregate extends AggregateBase {
116
117
  this.switchToFull();
117
118
  }
118
119
  }, this.featureName, this.ee);
120
+ const {
121
+ error_sampling_rate,
122
+ sampling_rate,
123
+ autoStart,
124
+ block_selector,
125
+ mask_text_selector,
126
+ mask_all_inputs,
127
+ inline_stylesheet,
128
+ inline_images,
129
+ collect_fonts
130
+ } = getConfigurationValue(this.agentIdentifier, 'session_replay');
119
131
  this.waitForFlags(['sr']).then(_ref => {
120
132
  let [flagOn] = _ref;
121
133
  this.entitled = flagOn;
122
- if (!this.entitled && this.recorder?.recording) this.recorder.abort(ABORT_REASONS.ENTITLEMENTS);
123
- this.initializeRecording(Math.random() * 100 < getConfigurationValue(this.agentIdentifier, 'session_replay.error_sampling_rate'), Math.random() * 100 < getConfigurationValue(this.agentIdentifier, 'session_replay.sampling_rate'));
134
+ if (!this.entitled && this.recorder?.recording) {
135
+ this.recorder.abort(ABORT_REASONS.ENTITLEMENTS);
136
+ handle(SUPPORTABILITY_METRIC_CHANNEL, ['SessionReplay/EnabledNotEntitled/Detected'], undefined, FEATURE_NAMES.metrics, this.ee);
137
+ }
138
+ this.initializeRecording(Math.random() * 100 < error_sampling_rate, Math.random() * 100 < sampling_rate);
124
139
  }).then(() => sharedChannel.onReplayReady(this.mode)); // notify watchers that replay started with the mode
125
140
 
141
+ /** Detect if the default configs have been altered and report a SM. This is useful to evaluate what the reasonable defaults are across a customer base over time */
142
+ if (!autoStart) handle(SUPPORTABILITY_METRIC_CHANNEL, ['Config/SessionReplay/AutoStart/Modified'], undefined, FEATURE_NAMES.metrics, this.ee);
143
+ if (collect_fonts === true) handle(SUPPORTABILITY_METRIC_CHANNEL, ['Config/SessionReplay/CollectFonts/Modified'], undefined, FEATURE_NAMES.metrics, this.ee);
144
+ if (inline_stylesheet !== true) handle(SUPPORTABILITY_METRIC_CHANNEL, ['Config/SessionReplay/InlineStylesheet/Modified'], undefined, FEATURE_NAMES.metrics, this.ee);
145
+ if (inline_images === true) handle(SUPPORTABILITY_METRIC_CHANNEL, ['Config/SessionReplay/InlineImages/Modifed'], undefined, FEATURE_NAMES.metrics, this.ee);
146
+ if (mask_all_inputs !== true) handle(SUPPORTABILITY_METRIC_CHANNEL, ['Config/SessionReplay/MaskAllInputs/Modified'], undefined, FEATURE_NAMES.metrics, this.ee);
147
+ if (block_selector !== '[data-nr-block]') handle(SUPPORTABILITY_METRIC_CHANNEL, ['Config/SessionReplay/BlockSelector/Modified'], undefined, FEATURE_NAMES.metrics, this.ee);
148
+ if (mask_text_selector !== '*') handle(SUPPORTABILITY_METRIC_CHANNEL, ['Config/SessionReplay/MaskTextSelector/Modified'], undefined, FEATURE_NAMES.metrics, this.ee);
149
+ handle(SUPPORTABILITY_METRIC_CHANNEL, ['Config/SessionReplay/SamplingRate/Value', sampling_rate], undefined, FEATURE_NAMES.metrics, this.ee);
150
+ handle(SUPPORTABILITY_METRIC_CHANNEL, ['Config/SessionReplay/ErrorSamplingRate/Value', error_sampling_rate], undefined, FEATURE_NAMES.metrics, this.ee);
126
151
  this.drain();
127
152
  }
128
153
  }
@@ -109,14 +109,15 @@ export class Recorder {
109
109
  const incompletes = stylesheetEvaluator.evaluate();
110
110
  /** Only stop ignoring data if already ignoring and a new valid snapshap is taking place (0 incompletes and we get a meta node for the snap) */
111
111
  if (!incompletes && this.#fixing && event.type === RRWEB_EVENT_TYPES.Meta) this.#fixing = false;
112
- if (incompletes) {
113
- handle(SUPPORTABILITY_METRIC_CHANNEL, ['SessionReplay/Payload/Missing-Inline-Css', incompletes], undefined, FEATURE_NAMES.metrics, this.parent.ee);
112
+ if (incompletes > 0) {
114
113
  /** wait for the evaluator to download/replace the incompletes' src code and then take a new snap */
115
114
  stylesheetEvaluator.fix().then(failedToFix => {
116
- if (failedToFix) {
115
+ if (failedToFix > 0) {
117
116
  this.currentBufferTarget.inlinedAllStylesheets = false;
118
117
  this.shouldFix = false;
119
118
  }
119
+ handle(SUPPORTABILITY_METRIC_CHANNEL, ['SessionReplay/Payload/Missing-Inline-Css/Failed', failedToFix], undefined, FEATURE_NAMES.metrics, this.parent.ee);
120
+ handle(SUPPORTABILITY_METRIC_CHANNEL, ['SessionReplay/Payload/Missing-Inline-Css/Fixed', incompletes - failedToFix], undefined, FEATURE_NAMES.metrics, this.parent.ee);
120
121
  this.takeFullSnapshot();
121
122
  });
122
123
  /** Only start ignoring data if got a faulty snapshot */
@@ -170,7 +171,12 @@ export class Recorder {
170
171
 
171
172
  /** force the recording lib to take a full DOM snapshot. This needs to occur in certain cases, like visibility changes */
172
173
  takeFullSnapshot() {
173
- recorder.takeFullSnapshot();
174
+ try {
175
+ if (!this.recording) return;
176
+ recorder.takeFullSnapshot();
177
+ } catch (err) {
178
+ // in the off chance we think we are recording, but rrweb does not, rrweb's lib will throw an error. This catch is just a precaution
179
+ }
174
180
  }
175
181
  clearTimestamps() {
176
182
  this.currentBufferTarget.cycleTimestamp = undefined;
@@ -8,7 +8,7 @@ class StylesheetEvaluator {
8
8
  * Used at harvest time to denote that all subsequent payloads are subject to this and customers should be advised to handle crossorigin decoration
9
9
  * */
10
10
  invalidStylesheetsDetected = false;
11
- failedToFix = false;
11
+ failedToFix = 0;
12
12
 
13
13
  /**
14
14
  * this works by checking (only ever once) each cssRules obj in the style sheets array. The try/catch will catch an error if the cssRules obj blocks access, triggering the module to try to "fix" the asset`. Returns the count of incomplete assets discovered.
@@ -18,15 +18,15 @@ class StylesheetEvaluator {
18
18
  let incompletes = 0;
19
19
  if (isBrowserScope) {
20
20
  for (let i = 0; i < Object.keys(document.styleSheets).length; i++) {
21
- const ss = document.styleSheets[i];
22
- if (!this.#evaluated.has(ss)) {
23
- this.#evaluated.add(ss);
21
+ if (!this.#evaluated.has(document.styleSheets[i])) {
22
+ this.#evaluated.add(document.styleSheets[i]);
24
23
  try {
25
24
  // eslint-disable-next-line
26
- const temp = ss.cssRules;
25
+ const temp = document.styleSheets[i].cssRules;
27
26
  } catch (err) {
27
+ if (!document.styleSheets[i].href) return;
28
28
  incompletes++;
29
- this.#fetchProms.push(this.#fetchAndOverride(document.styleSheets[i], ss.href));
29
+ this.#fetchProms.push(this.#fetchAndOverride(document.styleSheets[i]));
30
30
  }
31
31
  }
32
32
  }
@@ -43,7 +43,7 @@ class StylesheetEvaluator {
43
43
  await Promise.all(this.#fetchProms);
44
44
  this.#fetchProms = [];
45
45
  const failedToFix = this.failedToFix;
46
- this.failedToFix = false;
46
+ this.failedToFix = 0;
47
47
  return failedToFix;
48
48
  }
49
49
 
@@ -53,11 +53,12 @@ class StylesheetEvaluator {
53
53
  * @param {*} href - The asset href to fetch
54
54
  * @returns {Promise}
55
55
  */
56
- async #fetchAndOverride(target, href) {
56
+ async #fetchAndOverride(target) {
57
+ if (!target?.href) return;
57
58
  try {
58
- const stylesheetContents = await originals.FETCH.bind(window)(href);
59
+ const stylesheetContents = await originals.FETCH.bind(window)(target.href);
59
60
  if (!stylesheetContents.ok) {
60
- this.failedToFix = true;
61
+ this.failedToFix++;
61
62
  return;
62
63
  }
63
64
  const stylesheetText = await stylesheetContents.text();
@@ -82,11 +83,11 @@ class StylesheetEvaluator {
82
83
  return stylesheetText;
83
84
  }
84
85
  });
85
- this.failedToFix = true;
86
+ this.failedToFix++;
86
87
  }
87
88
  } catch (err) {
88
89
  // failed to fetch
89
- this.failedToFix = true;
90
+ this.failedToFix++;
90
91
  }
91
92
  }
92
93
  }
@@ -114,7 +114,7 @@ export class InstrumentBase extends FeatureBase {
114
114
  warn("Downloading and initializing ".concat(this.featureName, " failed..."), e);
115
115
  this.abortHandler?.(); // undo any important alterations made to the page
116
116
  // not supported yet but nice to do: "abort" this agent's EE for this feature specifically
117
- drain(this.agentIdentifier, this.featureName);
117
+ drain(this.agentIdentifier, this.featureName, true);
118
118
  loadedSuccessfully(false);
119
119
  }
120
120
  };
@@ -0,0 +1,2 @@
1
+ export const apiMethods = ['setErrorHandler', 'finished', 'addToTrace', 'addRelease', 'addPageAction', 'setCurrentRouteName', 'setPageViewName', 'setCustomAttribute', 'interaction', 'noticeError', 'setUserId', 'setApplicationVersion', 'start', 'recordReplay', 'pauseReplay'];
2
+ export const asyncApiMethods = ['setErrorHandler', 'finished', 'addToTrace', 'addRelease'];
@@ -13,12 +13,10 @@ import { isBrowserScope } from '../../common/constants/runtime';
13
13
  import { warn } from '../../common/util/console';
14
14
  import { SUPPORTABILITY_METRIC_CHANNEL } from '../../features/metrics/constants';
15
15
  import { gosCDN } from '../../common/window/nreum';
16
- export const CUSTOM_ATTR_GROUP = 'CUSTOM/'; // the subgroup items should be stored under in storage API
17
-
16
+ import { apiMethods, asyncApiMethods } from './api-methods';
18
17
  export function setTopLevelCallers() {
19
18
  const nr = gosCDN();
20
- const funcs = ['setErrorHandler', 'finished', 'addToTrace', 'addRelease', 'addPageAction', 'setCurrentRouteName', 'setPageViewName', 'setCustomAttribute', 'interaction', 'noticeError', 'setUserId', 'setApplicationVersion', 'start', 'recordReplay', 'pauseReplay'];
21
- funcs.forEach(f => {
19
+ apiMethods.forEach(f => {
22
20
  nr[f] = function () {
23
21
  for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
24
22
  args[_key] = arguments[_key];
@@ -44,12 +42,11 @@ export function setAPI(agentIdentifier, forceDrain) {
44
42
  const apiInterface = {};
45
43
  var instanceEE = ee.get(agentIdentifier);
46
44
  var tracerEE = instanceEE.get('tracer');
47
- var asyncApiFns = ['setErrorHandler', 'finished', 'addToTrace', 'addRelease'];
48
45
  var prefix = 'api-';
49
46
  var spaPrefix = prefix + 'ixn-';
50
47
 
51
48
  // Setup stub functions that queue calls for later processing.
52
- asyncApiFns.forEach(fnName => {
49
+ asyncApiMethods.forEach(fnName => {
53
50
  apiInterface[fnName] = apiCall(prefix, fnName, true, 'api');
54
51
  });
55
52
  apiInterface.addPageAction = apiCall(prefix, 'addPageAction', true, FEATURE_NAMES.pageAction);
@@ -200,7 +197,10 @@ export function setAPI(agentIdentifier, forceDrain) {
200
197
  } = _ref;
201
198
  setAPI(agentIdentifier);
202
199
  drain(agentIdentifier, 'api');
203
- }).catch(() => warn('Downloading runtime APIs failed...'));
200
+ }).catch(() => {
201
+ warn('Downloading runtime APIs failed...');
202
+ drain(agentIdentifier, 'api', true);
203
+ });
204
204
  }
205
205
  return apiInterface;
206
206
  }
@@ -3,7 +3,7 @@ import { addToNREUM, gosCDN } from '../../common/window/nreum';
3
3
  import { getConfiguration, setConfiguration, setInfo, setLoaderConfig, setRuntime } from '../../common/config/config';
4
4
  import { activatedFeatures } from '../../common/util/feature-flags';
5
5
  import { isWorkerScope } from '../../common/constants/runtime';
6
- import { redefinePublicPath } from './public-path';
6
+ import { redefinePublicPath } from "./public-path.npm";
7
7
  let alreadySetOnce = false; // the configure() function can run multiple times in agent lifecycle
8
8
 
9
9
  /**
@@ -12,6 +12,7 @@ export function registerDrain(agentIdentifier: string, group: string): void;
12
12
  * its own named group explicitly, when ready.
13
13
  * @param {string} agentIdentifier - A unique 16 character ID corresponding to an instantiated agent.
14
14
  * @param {string} featureName - A named group into which the feature's buffered events are bucketed.
15
+ * @param {boolean} force - Whether to force the drain to occur immediately, bypassing the registry and staging logic.
15
16
  */
16
- export function drain(agentIdentifier?: string, featureName?: string): void;
17
+ export function drain(agentIdentifier?: string, featureName?: string, force?: boolean): void;
17
18
  //# sourceMappingURL=drain.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"drain.d.ts","sourceRoot":"","sources":["../../../../src/common/drain/drain.js"],"names":[],"mappings":"AAYA;;;;;;;GAOG;AACH,+CAHW,MAAM,SACN,MAAM,QAQhB;AAYD;;;;;GAKG;AACH,wCAHW,MAAM,gBACN,MAAM,QAuDhB"}
1
+ {"version":3,"file":"drain.d.ts","sourceRoot":"","sources":["../../../../src/common/drain/drain.js"],"names":[],"mappings":"AAYA;;;;;;;GAOG;AACH,+CAHW,MAAM,SACN,MAAM,QAQhB;AAYD;;;;;;GAMG;AACH,wCAJW,MAAM,gBACN,MAAM,UACN,OAAO,QAuDjB"}
@@ -1 +1 @@
1
- {"version":3,"file":"contextual-ee.d.ts","sourceRoot":"","sources":["../../../../src/common/event-emitter/contextual-ee.js"],"names":[],"mappings":";;;;;;;;;;;;;;;AAYA,+BAA0C;AA0DxC,+FAsBC;AAqBD,6CAEC;AAND,2CAEC;AAMD,mEAaC;AAcH,+BAGC;AAfC,iDAGC"}
1
+ {"version":3,"file":"contextual-ee.d.ts","sourceRoot":"","sources":["../../../../src/common/event-emitter/contextual-ee.js"],"names":[],"mappings":";;;;;;;;;;;;;;;AAYA,+BAA0C;AA0DxC,+FAsBC;AAqBD,6CAEC;AAND,2CAEC;AAMD,mEAaC;AAcH,+BASC;AArBC,iDAGC"}
@@ -1 +1 @@
1
- {"version":3,"file":"harvest.d.ts","sourceRoot":"","sources":["../../../../src/common/harvest/harvest.js"],"names":[],"mappings":"AAsBA;;;;;;GAMG;AACH;IAII,0BAA2H;IAC3H,uBAAoD;IAEpD,YAAiB;IAGnB;;;;;OAKG;IACH,aAFW,eAAe,WAWzB;IAED;;;OAGG;IACH,YAFW,eAAe,WAMzB;IAED;;;OAGG;IACH,wBAFW,eAAe,WAMzB;IAED;;;;;;OAMG;IACH,gGAJW,eAAe,GACb,OAAO,CAkFnB;IAGD,iCAoBC;IAED;;;;;;;OAOG;IACH,wBALW,yBAAyB,WACzB,6BAA6B,GAE3B,cAAc,CA2B1B;IAED;;;;;;;OAOG;IACH,uBAHW,cAAc,GACZ,cAAc,CAuB1B;IAED;;;;;OAKG;IACH,aAHW,yBAAyB,YACzB,sBAAsB,QAQhC;CACF;8BAnPY,OAAO,YAAY,EAAE,eAAe;wCACpC,OAAO,YAAY,EAAE,yBAAyB;6BAC9C,OAAO,YAAY,EAAE,cAAc;qCACnC,OAAO,YAAY,EAAE,sBAAsB;4CAC3C,OAAO,YAAY,EAAE,6BAA6B;8BAZjC,2BAA2B;2BAF9B,mBAAmB"}
1
+ {"version":3,"file":"harvest.d.ts","sourceRoot":"","sources":["../../../../src/common/harvest/harvest.js"],"names":[],"mappings":"AAsBA;;;;;;GAMG;AACH;IAII,0BAA2H;IAC3H,uBAAoD;IAEpD,YAAiB;IAGnB;;;;;OAKG;IACH,aAFW,eAAe,WAWzB;IAED;;;OAGG;IACH,YAFW,eAAe,WAMzB;IAED;;;OAGG;IACH,wBAFW,eAAe,WAMzB;IAED;;;;;;OAMG;IACH,gGAJW,eAAe,GACb,OAAO,CAmFnB;IAGD,iCAoBC;IAED;;;;;;;OAOG;IACH,wBALW,yBAAyB,WACzB,6BAA6B,GAE3B,cAAc,CA2B1B;IAED;;;;;;;OAOG;IACH,uBAHW,cAAc,GACZ,cAAc,CAuB1B;IAED;;;;;OAKG;IACH,aAHW,yBAAyB,YACzB,sBAAsB,QAQhC;CACF;8BApPY,OAAO,YAAY,EAAE,eAAe;wCACpC,OAAO,YAAY,EAAE,yBAAyB;6BAC9C,OAAO,YAAY,EAAE,cAAc;qCACnC,OAAO,YAAY,EAAE,sBAAsB;4CAC3C,OAAO,YAAY,EAAE,6BAA6B;8BAZjC,2BAA2B;2BAF9B,mBAAmB"}
@@ -20,7 +20,6 @@ export class SessionEntity {
20
20
  inactiveMs: number | undefined;
21
21
  expiresTimer: Timer | undefined;
22
22
  inactiveTimer: InteractionTimer | undefined;
23
- isNew: boolean | undefined;
24
23
  initialized: boolean | undefined;
25
24
  get lookupKey(): string;
26
25
  sync(data: any): void;
@@ -1 +1 @@
1
- {"version":3,"file":"session-entity.d.ts","sourceRoot":"","sources":["../../../../src/common/session/session-entity.js"],"names":[],"mappings":"AA6BA;IACE;;;;;OAKG;IACH,uBA0BC;IApBC,qBAAsC;IACtC,aAAsB;IACtB,UAAe;IAGf,SAAc;IAEd,QAAiC;IAenC;;;;aAsEC;IA/DC,8BAA0B;IAC1B,+BAA4B;IAc1B,gCAOqC;IAUrC,4CAiBsC;IAOxC,2BAA6C;IAM7C,iCAAuB;IAIzB,wBAEC;IAED,sBAEC;IAED;;;OAGG;IACH,QAFa,MAAM,CA6BlB;IAED;;;;;;OAMG;IACH,YAHW,MAAM,GACJ,MAAM,CAkBlB;IAED,gBAuBC;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;sBAlSqB,gBAAgB;iCAGL,4BAA4B"}
1
+ {"version":3,"file":"session-entity.d.ts","sourceRoot":"","sources":["../../../../src/common/session/session-entity.js"],"names":[],"mappings":"AA6BA;IACE;;;;;OAKG;IACH,uBA0BC;IApBC,qBAAsC;IACtC,aAAsB;IACtB,UAAe;IAGf,SAAc;IAEd,QAAiC;IAenC;;;;aAwEC;IAjEC,8BAA0B;IAC1B,+BAA4B;IAc1B,gCAOqC;IAUrC,4CAiBsC;IAexC,iCAAuB;IAIzB,wBAEC;IAED,sBAEC;IAED;;;OAGG;IACH,QAFa,MAAM,CA6BlB;IAED;;;;;;OAMG;IACH,YAHW,MAAM,GACJ,MAAM,CAkBlB;IAED,gBAuBC;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;sBApSqB,gBAAgB;iCAGL,4BAA4B"}
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/features/ajax/instrument/index.js"],"names":[],"mappings":"AAyBA;IACE,2BAAiC;IACjC,mEAqCC;IA/BC,mBAAiC;IAEjC,4EAAkF;CA8BrF;+BAlD8B,6BAA6B;mBAFzC,uBAAuB"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/features/ajax/instrument/index.js"],"names":[],"mappings":"AA0BA;IACE,2BAAiC;IACjC,mEAqCC;IA/BC,mBAAiC;IAEjC,4EAAkF;CA8BrF;+BAnD8B,6BAA6B;mBAFzC,uBAAuB"}
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/features/jserrors/instrument/index.js"],"names":[],"mappings":"AAeA;IACE,2BAAiC;IAIjC,mEA4CC;IAvCG,2CAA0C;IAqC5C,yBAA+B;;CA8ElC;+BArI8B,6BAA6B"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/features/jserrors/instrument/index.js"],"names":[],"mappings":"AAeA;IACE,2BAAiC;IAIjC,mEA4CC;IAvCG,2CAA0C;IAqC5C,yBAA+B;;CAoFlC;+BA3I8B,6BAA6B"}
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/features/session_replay/aggregate/index.js"],"names":[],"mappings":"AA8BA;IACE,2BAAiC;IAEjC,8DAsGC;IApGC,8GAA8G;IAC9G,wBAAgH;IAChH,iFAAiF;IACjF,qBAAwB;IAGxB,2CAA2C;IAC3C,sDAAwB;IACxB,6CAA6C;IAC7C,gDAAmB;IAGnB,UAAuD;IAEvD,0BAA0B;IAC1B,kBAAqB;IAErB,cAA8B;IAkC5B,wCAKQ;IAyBN,sBAAwB;IAqB9B,qBAWC;IAED;;;;;;;OAOG;IACH,iCALW,OAAO,cACP,OAAO,iBACP,OAAO,GACL,IAAI,CAsDhB;IAED,2BASC;IAED;;;;;;;;;;;oBAiCC;IAED;;;;;;;;;MAmEC;IAED,qCAOC;IAED;;;;OAIG;IACH,mCAKC;IAED,yDAAyD;IACzD,yBAUC;IAED,yCAGC;CACF;8BA7V6B,4BAA4B;iCAHzB,2CAA2C"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/features/session_replay/aggregate/index.js"],"names":[],"mappings":"AA8BA;IACE,2BAAiC;IAEjC,8DAwHC;IAtHC,8GAA8G;IAC9G,wBAAgH;IAChH,iFAAiF;IACjF,qBAAwB;IAGxB,2CAA2C;IAC3C,sDAAwB;IACxB,6CAA6C;IAC7C,gDAAmB;IAGnB,UAAuD;IAEvD,0BAA0B;IAC1B,kBAAqB;IAErB,cAA8B;IAoC5B,wCAKQ;IAyBN,sBAAwB;IAqC9B,qBAWC;IAED;;;;;;;OAOG;IACH,iCALW,OAAO,cACP,OAAO,iBACP,OAAO,GACL,IAAI,CAsDhB;IAED,2BASC;IAED;;;;;;;;;;;oBAiCC;IAED;;;;;;;;;MAmEC;IAED,qCAOC;IAED;;;;OAIG;IACH,mCAKC;IAED,yDAAyD;IACzD,yBAUC;IAED,yCAGC;CACF;8BA/W6B,4BAA4B;iCAHzB,2CAA2C"}
@@ -1 +1 @@
1
- {"version":3,"file":"recorder.d.ts","sourceRoot":"","sources":["../../../../../src/features/session_replay/shared/recorder.js"],"names":[],"mappings":"AAWA;IAUE,yBAeC;IAdC,iEAAiE;IACjE,mBAAsB;IACtB,6DAA6D;IAC7D,oCAAuC;IACvC,kIAAkI;IAClI,kBAAqB;IACrB,sDAAsD;IACtD,YAAoB;IACpB,oEAAoE;IACpE,6BAAqH;IACrH,0FAA0F;IAC1F,eAA6C;IAC7C,uIAAuI;IACvI,0BAAyE;IAG3E;;;;;;;;;MAYC;IAED,mFAAmF;IACnF,oBAKC;IAED,qDAAqD;IACrD,uBAwBC;IAED;;;;;OAKG;IACH,yCAyBC;IAED,0HAA0H;IAC1H,yCA2CC;IAED,0HAA0H;IAC1H,yBAEC;IAED,wBAEC;IAED,gCAAgC;IAChC,uCAGC;IAED;;;SAGK;IACL,oCAGC;;CACF;+BAzL8B,mBAAmB"}
1
+ {"version":3,"file":"recorder.d.ts","sourceRoot":"","sources":["../../../../../src/features/session_replay/shared/recorder.js"],"names":[],"mappings":"AAWA;IAUE,yBAeC;IAdC,iEAAiE;IACjE,mBAAsB;IACtB,6DAA6D;IAC7D,oCAAuC;IACvC,kIAAkI;IAClI,kBAAqB;IACrB,sDAAsD;IACtD,YAAoB;IACpB,oEAAoE;IACpE,6BAAqH;IACrH,0FAA0F;IAC1F,eAA6C;IAC7C,uIAAuI;IACvI,0BAAyE;IAG3E;;;;;;;;;MAYC;IAED,mFAAmF;IACnF,oBAKC;IAED,qDAAqD;IACrD,uBAwBC;IAED;;;;;OAKG;IACH,yCA0BC;IAED,0HAA0H;IAC1H,yCA2CC;IAED,0HAA0H;IAC1H,yBAOC;IAED,wBAEC;IAED,gCAAgC;IAChC,uCAGC;IAED;;;SAGK;IACL,oCAGC;;CACF;+BA/L8B,mBAAmB"}
@@ -5,7 +5,7 @@ declare class StylesheetEvaluator {
5
5
  * Used at harvest time to denote that all subsequent payloads are subject to this and customers should be advised to handle crossorigin decoration
6
6
  * */
7
7
  invalidStylesheetsDetected: boolean;
8
- failedToFix: boolean;
8
+ failedToFix: number;
9
9
  /**
10
10
  * this works by checking (only ever once) each cssRules obj in the style sheets array. The try/catch will catch an error if the cssRules obj blocks access, triggering the module to try to "fix" the asset`. Returns the count of incomplete assets discovered.
11
11
  * @returns {Number}
@@ -1 +1 @@
1
- {"version":3,"file":"stylesheet-evaluator.d.ts","sourceRoot":"","sources":["../../../../../src/features/session_replay/shared/stylesheet-evaluator.js"],"names":[],"mappings":"AAwFA,sDAA4D;AArF5D;IAGE;;;QAGI;IACJ,oCAAkC;IAClC,qBAAmB;IAEnB;;;OAGG;IACH,mBAmBC;IAED;;;OAGG;IACH,oBAMC;;CAsCF"}
1
+ {"version":3,"file":"stylesheet-evaluator.d.ts","sourceRoot":"","sources":["../../../../../src/features/session_replay/shared/stylesheet-evaluator.js"],"names":[],"mappings":"AAyFA,sDAA4D;AAtF5D;IAGE;;;QAGI;IACJ,oCAAkC;IAClC,oBAAe;IAEf;;;OAGG;IACH,mBAmBC;IAED;;;OAGG;IACH,oBAMC;;CAuCF"}
@@ -0,0 +1,3 @@
1
+ export const apiMethods: string[];
2
+ export const asyncApiMethods: string[];
3
+ //# sourceMappingURL=api-methods.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"api-methods.d.ts","sourceRoot":"","sources":["../../../../src/loaders/api/api-methods.js"],"names":[],"mappings":"AAAA,kCAIC;AAED,uCAEC"}
@@ -22,5 +22,4 @@ export function setAPI(agentIdentifier: any, forceDrain: any): {
22
22
  interaction(): any;
23
23
  noticeError(err: any, customAttributes: any): void;
24
24
  };
25
- export const CUSTOM_ATTR_GROUP: "CUSTOM/";
26
25
  //# sourceMappingURL=api.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"api.d.ts","sourceRoot":"","sources":["../../../../src/loaders/api/api.js"],"names":[],"mappings":"AAkBA,2CAoBC;AAED;;;;;IAyDE;;;;OAIG;qBAFQ,MAAM;IAWjB;;;;OAIG;iCAFQ,MAAM,GAAC,IAAI;;;;;;EAqGvB;AArMD,0CAA0C"}
1
+ {"version":3,"file":"api.d.ts","sourceRoot":"","sources":["../../../../src/loaders/api/api.js"],"names":[],"mappings":"AAiBA,2CAeC;AAED;;;;;IAkDE;;;;OAIG;qBAFQ,MAAM;IAWjB;;;;OAIG;iCAFQ,MAAM,GAAC,IAAI;;;;;;EAwGvB"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@newrelic/browser-agent",
3
- "version": "1.251.1",
3
+ "version": "1.252.1",
4
4
  "private": false,
5
5
  "author": "New Relic Browser Agent Team <browser-agent@newrelic.com>",
6
6
  "description": "New Relic Browser Agent",
@@ -17,6 +17,9 @@
17
17
  "src/*": [
18
18
  "dist/types/*"
19
19
  ],
20
+ "dist/esm/*": [
21
+ "dist/types/*"
22
+ ],
20
23
  "loaders/agent": [
21
24
  "dist/types/loaders/agent.d.ts"
22
25
  ],
@@ -64,8 +64,8 @@ const model = () => {
64
64
  autoStart: true,
65
65
  enabled: false,
66
66
  harvestTimeSeconds: 60,
67
- sampling_rate: 50, // float from 0 - 100
68
- error_sampling_rate: 50, // float from 0 - 100
67
+ sampling_rate: 10, // float from 0 - 100
68
+ error_sampling_rate: 100, // float from 0 - 100
69
69
  collect_fonts: false, // serialize fonts for collection without public asset url, this is currently broken in RRWeb -- https://github.com/rrweb-io/rrweb/issues/1304. When fixed, revisit with test cases
70
70
  inline_images: false, // serialize images for collection without public asset url -- right now this is only useful for testing as it easily generates payloads too large to be harvested
71
71
  inline_stylesheet: true, // serialize css for collection without public asset url
@@ -41,13 +41,14 @@ function curateRegistry (agentIdentifier) {
41
41
  * its own named group explicitly, when ready.
42
42
  * @param {string} agentIdentifier - A unique 16 character ID corresponding to an instantiated agent.
43
43
  * @param {string} featureName - A named group into which the feature's buffered events are bucketed.
44
+ * @param {boolean} force - Whether to force the drain to occur immediately, bypassing the registry and staging logic.
44
45
  */
45
- export function drain (agentIdentifier = '', featureName = 'feature') {
46
+ export function drain (agentIdentifier = '', featureName = 'feature', force = false) {
46
47
  curateRegistry(agentIdentifier)
47
48
  // If the feature for the specified agent is not in the registry, that means the instrument file was bypassed.
48
49
  // This could happen in tests, or loaders that directly import the aggregator. In these cases it is safe to
49
50
  // drain the feature group immediately rather than waiting to drain all at once.
50
- if (!agentIdentifier || !registry[agentIdentifier].get(featureName)) return drainGroup(featureName)
51
+ if (!agentIdentifier || !registry[agentIdentifier].get(featureName) || force) return drainGroup(featureName)
51
52
 
52
53
  // When `drain` is called, this feature is ready to drain (staged).
53
54
  registry[agentIdentifier].get(featureName).staged = true
@@ -144,5 +144,11 @@ function ee (old, debugId) {
144
144
 
145
145
  function abort () {
146
146
  globalInstance.aborted = true
147
- globalInstance.backlog = {}
147
+ // The global backlog can be referenced directly by other emitters,
148
+ // so we need to delete its contents as opposed to replacing it.
149
+ // Otherwise, these references to the old backlog would still exist
150
+ // and the keys will not be garbage collected.
151
+ Object.keys(globalInstance.backlog).forEach(key => {
152
+ delete globalInstance.backlog[key]
153
+ })
148
154
  }
@@ -142,9 +142,10 @@ export class Harvest extends SharedContext {
142
142
 
143
143
  if (!opts.unload && cbFinished && submitMethod === submitData.xhr) {
144
144
  const harvestScope = this
145
- result.addEventListener('load', function () {
145
+ result.addEventListener('loadend', function () {
146
146
  // `this` refers to the XHR object in this scope, do not change this to a fat arrow
147
- const cbResult = { sent: true, status: this.status }
147
+ // status 0 refers to a local error, such as CORS or network failure, or a blocked request by the browser (e.g. adblocker)
148
+ const cbResult = { sent: this.status !== 0, status: this.status }
148
149
  if (this.status === 429) {
149
150
  cbResult.retry = true
150
151
  cbResult.delay = harvestScope.tooManyRequestsDelay
@@ -125,7 +125,9 @@ export class SessionEntity {
125
125
 
126
126
  // The fact that the session is "new" or pre-existing is used in some places in the agent. Session Replay and Trace
127
127
  // can use this info to inform whether to trust a new sampling decision vs continue a previous tracking effort.
128
- this.isNew = !Object.keys(initialRead).length
128
+ /* [NR-230914] 02/2024 - the logical OR assignment is used so that isNew remains 'true' if it was already set as such. This fixes the expires and inactive timestamps timing out in localStorage
129
+ while no page for a given domain is in-use and the session resetting upon user returning to the page as part of a fresh session. */
130
+ this.isNew ||= !Object.keys(initialRead).length
129
131
  // if its a "new" session, we write to storage API with the default values. These values may change over the lifespan of the agent run.
130
132
  // we can use a modeled object here to help us know and manage what values are being used. -- see "model" above
131
133
  if (this.isNew) this.write(getModeledObject(this.state, model), true)
@@ -16,6 +16,7 @@ import { responseSizeFromXhr } from './response-size'
16
16
  import { InstrumentBase } from '../../utils/instrument-base'
17
17
  import { FEATURE_NAME } from '../constants'
18
18
  import { FEATURE_NAMES } from '../../../loaders/features/features'
19
+ import { SUPPORTABILITY_METRIC } from '../../metrics/constants'
19
20
 
20
21
  var handlers = ['load', 'error', 'abort', 'timeout']
21
22
  var handlersLen = handlers.length
@@ -395,6 +396,7 @@ function subscribeToEvents (agentIdentifier, ee, handler, dt) {
395
396
  if (ctx.sameOrigin) {
396
397
  var header = xhr.getResponseHeader('X-NewRelic-App-Data')
397
398
  if (header) {
399
+ handle(SUPPORTABILITY_METRIC, ['Ajax/CrossApplicationTracing/Header/Seen'], undefined, FEATURE_NAMES.metrics, ee)
398
400
  ctx.params.cat = header.split(', ').pop()
399
401
  }
400
402
  }
@@ -128,6 +128,12 @@ export class Instrument extends InstrumentBase {
128
128
  * @returns {Error|UncaughtError} The error event converted to an Error object
129
129
  */
130
130
  #castErrorEvent (errorEvent) {
131
+ if (errorEvent.error instanceof SyntaxError && !/:\d+$/.test(errorEvent.error.stack?.trim())) {
132
+ const error = new UncaughtError(errorEvent.message, errorEvent.filename, errorEvent.lineno, errorEvent.colno)
133
+ error.name = SyntaxError.name
134
+ return error
135
+ }
136
+
131
137
  if (errorEvent.error instanceof Error) {
132
138
  return errorEvent.error
133
139
  }
@@ -101,7 +101,7 @@ export class Aggregate extends AggregateBase {
101
101
  payload: { qs: queryParameters, body },
102
102
  opts: { needResponse: true, sendEmptyBody: true },
103
103
  cbFinished: ({ status, responseText }) => {
104
- if (status >= 400) {
104
+ if (status >= 400 || status === 0) {
105
105
  // Adding retry logic for the rum call will be a separate change
106
106
  this.ee.abort()
107
107
  return
@@ -53,6 +53,8 @@ export class Aggregate extends AggregateBase {
53
53
  this.recorder = args?.recorder
54
54
  if (this.recorder) this.recorder.parent = this
55
55
 
56
+ handle(SUPPORTABILITY_METRIC_CHANNEL, ['Config/SessionReplay/Enabled'], undefined, FEATURE_NAMES.metrics, this.ee)
57
+
56
58
  const shouldSetup = (
57
59
  getConfigurationValue(agentIdentifier, 'privacy.cookies_enabled') === true &&
58
60
  getConfigurationValue(agentIdentifier, 'session_trace.enabled') === true
@@ -122,15 +124,31 @@ export class Aggregate extends AggregateBase {
122
124
  }
123
125
  }, this.featureName, this.ee)
124
126
 
127
+ const { error_sampling_rate, sampling_rate, autoStart, block_selector, mask_text_selector, mask_all_inputs, inline_stylesheet, inline_images, collect_fonts } = getConfigurationValue(this.agentIdentifier, 'session_replay')
128
+
125
129
  this.waitForFlags(['sr']).then(([flagOn]) => {
126
130
  this.entitled = flagOn
127
- if (!this.entitled && this.recorder?.recording) this.recorder.abort(ABORT_REASONS.ENTITLEMENTS)
131
+ if (!this.entitled && this.recorder?.recording) {
132
+ this.recorder.abort(ABORT_REASONS.ENTITLEMENTS)
133
+ handle(SUPPORTABILITY_METRIC_CHANNEL, ['SessionReplay/EnabledNotEntitled/Detected'], undefined, FEATURE_NAMES.metrics, this.ee)
134
+ }
128
135
  this.initializeRecording(
129
- (Math.random() * 100) < getConfigurationValue(this.agentIdentifier, 'session_replay.error_sampling_rate'),
130
- (Math.random() * 100) < getConfigurationValue(this.agentIdentifier, 'session_replay.sampling_rate')
136
+ (Math.random() * 100) < error_sampling_rate,
137
+ (Math.random() * 100) < sampling_rate
131
138
  )
132
139
  }).then(() => sharedChannel.onReplayReady(this.mode)) // notify watchers that replay started with the mode
133
140
 
141
+ /** Detect if the default configs have been altered and report a SM. This is useful to evaluate what the reasonable defaults are across a customer base over time */
142
+ if (!autoStart) handle(SUPPORTABILITY_METRIC_CHANNEL, ['Config/SessionReplay/AutoStart/Modified'], undefined, FEATURE_NAMES.metrics, this.ee)
143
+ if (collect_fonts === true) handle(SUPPORTABILITY_METRIC_CHANNEL, ['Config/SessionReplay/CollectFonts/Modified'], undefined, FEATURE_NAMES.metrics, this.ee)
144
+ if (inline_stylesheet !== true) handle(SUPPORTABILITY_METRIC_CHANNEL, ['Config/SessionReplay/InlineStylesheet/Modified'], undefined, FEATURE_NAMES.metrics, this.ee)
145
+ if (inline_images === true) handle(SUPPORTABILITY_METRIC_CHANNEL, ['Config/SessionReplay/InlineImages/Modifed'], undefined, FEATURE_NAMES.metrics, this.ee)
146
+ if (mask_all_inputs !== true) handle(SUPPORTABILITY_METRIC_CHANNEL, ['Config/SessionReplay/MaskAllInputs/Modified'], undefined, FEATURE_NAMES.metrics, this.ee)
147
+ if (block_selector !== '[data-nr-block]') handle(SUPPORTABILITY_METRIC_CHANNEL, ['Config/SessionReplay/BlockSelector/Modified'], undefined, FEATURE_NAMES.metrics, this.ee)
148
+ if (mask_text_selector !== '*') handle(SUPPORTABILITY_METRIC_CHANNEL, ['Config/SessionReplay/MaskTextSelector/Modified'], undefined, FEATURE_NAMES.metrics, this.ee)
149
+
150
+ handle(SUPPORTABILITY_METRIC_CHANNEL, ['Config/SessionReplay/SamplingRate/Value', sampling_rate], undefined, FEATURE_NAMES.metrics, this.ee)
151
+ handle(SUPPORTABILITY_METRIC_CHANNEL, ['Config/SessionReplay/ErrorSamplingRate/Value', error_sampling_rate], undefined, FEATURE_NAMES.metrics, this.ee)
134
152
  this.drain()
135
153
  }
136
154
  }
@@ -101,14 +101,15 @@ export class Recorder {
101
101
  const incompletes = stylesheetEvaluator.evaluate()
102
102
  /** Only stop ignoring data if already ignoring and a new valid snapshap is taking place (0 incompletes and we get a meta node for the snap) */
103
103
  if (!incompletes && this.#fixing && event.type === RRWEB_EVENT_TYPES.Meta) this.#fixing = false
104
- if (incompletes) {
105
- handle(SUPPORTABILITY_METRIC_CHANNEL, ['SessionReplay/Payload/Missing-Inline-Css', incompletes], undefined, FEATURE_NAMES.metrics, this.parent.ee)
104
+ if (incompletes > 0) {
106
105
  /** wait for the evaluator to download/replace the incompletes' src code and then take a new snap */
107
106
  stylesheetEvaluator.fix().then((failedToFix) => {
108
- if (failedToFix) {
107
+ if (failedToFix > 0) {
109
108
  this.currentBufferTarget.inlinedAllStylesheets = false
110
109
  this.shouldFix = false
111
110
  }
111
+ handle(SUPPORTABILITY_METRIC_CHANNEL, ['SessionReplay/Payload/Missing-Inline-Css/Failed', failedToFix], undefined, FEATURE_NAMES.metrics, this.parent.ee)
112
+ handle(SUPPORTABILITY_METRIC_CHANNEL, ['SessionReplay/Payload/Missing-Inline-Css/Fixed', incompletes - failedToFix], undefined, FEATURE_NAMES.metrics, this.parent.ee)
112
113
  this.takeFullSnapshot()
113
114
  })
114
115
  /** Only start ignoring data if got a faulty snapshot */
@@ -166,7 +167,12 @@ export class Recorder {
166
167
 
167
168
  /** force the recording lib to take a full DOM snapshot. This needs to occur in certain cases, like visibility changes */
168
169
  takeFullSnapshot () {
169
- recorder.takeFullSnapshot()
170
+ try {
171
+ if (!this.recording) return
172
+ recorder.takeFullSnapshot()
173
+ } catch (err) {
174
+ // in the off chance we think we are recording, but rrweb does not, rrweb's lib will throw an error. This catch is just a precaution
175
+ }
170
176
  }
171
177
 
172
178
  clearTimestamps () {
@@ -9,7 +9,7 @@ class StylesheetEvaluator {
9
9
  * Used at harvest time to denote that all subsequent payloads are subject to this and customers should be advised to handle crossorigin decoration
10
10
  * */
11
11
  invalidStylesheetsDetected = false
12
- failedToFix = false
12
+ failedToFix = 0
13
13
 
14
14
  /**
15
15
  * this works by checking (only ever once) each cssRules obj in the style sheets array. The try/catch will catch an error if the cssRules obj blocks access, triggering the module to try to "fix" the asset`. Returns the count of incomplete assets discovered.
@@ -19,15 +19,15 @@ class StylesheetEvaluator {
19
19
  let incompletes = 0
20
20
  if (isBrowserScope) {
21
21
  for (let i = 0; i < Object.keys(document.styleSheets).length; i++) {
22
- const ss = document.styleSheets[i]
23
- if (!this.#evaluated.has(ss)) {
24
- this.#evaluated.add(ss)
22
+ if (!this.#evaluated.has(document.styleSheets[i])) {
23
+ this.#evaluated.add(document.styleSheets[i])
25
24
  try {
26
25
  // eslint-disable-next-line
27
- const temp = ss.cssRules
26
+ const temp = document.styleSheets[i].cssRules
28
27
  } catch (err) {
28
+ if (!document.styleSheets[i].href) return
29
29
  incompletes++
30
- this.#fetchProms.push(this.#fetchAndOverride(document.styleSheets[i], ss.href))
30
+ this.#fetchProms.push(this.#fetchAndOverride(document.styleSheets[i]))
31
31
  }
32
32
  }
33
33
  }
@@ -44,7 +44,7 @@ class StylesheetEvaluator {
44
44
  await Promise.all(this.#fetchProms)
45
45
  this.#fetchProms = []
46
46
  const failedToFix = this.failedToFix
47
- this.failedToFix = false
47
+ this.failedToFix = 0
48
48
  return failedToFix
49
49
  }
50
50
 
@@ -54,11 +54,12 @@ class StylesheetEvaluator {
54
54
  * @param {*} href - The asset href to fetch
55
55
  * @returns {Promise}
56
56
  */
57
- async #fetchAndOverride (target, href) {
57
+ async #fetchAndOverride (target) {
58
+ if (!target?.href) return
58
59
  try {
59
- const stylesheetContents = await originals.FETCH.bind(window)(href)
60
+ const stylesheetContents = await originals.FETCH.bind(window)(target.href)
60
61
  if (!stylesheetContents.ok) {
61
- this.failedToFix = true
62
+ this.failedToFix++
62
63
  return
63
64
  }
64
65
  const stylesheetText = await stylesheetContents.text()
@@ -77,11 +78,11 @@ class StylesheetEvaluator {
77
78
  Object.defineProperty(target, 'cssText', {
78
79
  get () { return stylesheetText }
79
80
  })
80
- this.failedToFix = true
81
+ this.failedToFix++
81
82
  }
82
83
  } catch (err) {
83
84
  // failed to fetch
84
- this.failedToFix = true
85
+ this.failedToFix++
85
86
  }
86
87
  }
87
88
  }
@@ -108,7 +108,7 @@ export class InstrumentBase extends FeatureBase {
108
108
  warn(`Downloading and initializing ${this.featureName} failed...`, e)
109
109
  this.abortHandler?.() // undo any important alterations made to the page
110
110
  // not supported yet but nice to do: "abort" this agent's EE for this feature specifically
111
- drain(this.agentIdentifier, this.featureName)
111
+ drain(this.agentIdentifier, this.featureName, true)
112
112
  loadedSuccessfully(false)
113
113
  }
114
114
  }