@newrelic/browser-agent 1.262.0 → 1.263.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 (92) hide show
  1. package/CHANGELOG.md +13 -0
  2. package/dist/cjs/common/config/state/configurable.js +4 -4
  3. package/dist/cjs/common/config/state/init.js +5 -3
  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/context/shared-context.js +2 -2
  7. package/dist/cjs/common/drain/drain.js +1 -1
  8. package/dist/cjs/common/harvest/harvest.js +1 -1
  9. package/dist/cjs/common/session/session-entity.js +2 -2
  10. package/dist/cjs/common/timing/time-keeper.js +13 -2
  11. package/dist/cjs/common/util/console.js +3 -4
  12. package/dist/cjs/common/util/obfuscate.js +3 -3
  13. package/dist/cjs/common/wrap/wrap-logger.js +1 -2
  14. package/dist/cjs/common/wrap/wrap-xhr.js +1 -1
  15. package/dist/cjs/features/logging/aggregate/index.js +5 -5
  16. package/dist/cjs/features/logging/constants.js +2 -5
  17. package/dist/cjs/features/metrics/aggregate/index.js +16 -0
  18. package/dist/cjs/features/page_view_event/aggregate/index.js +20 -4
  19. package/dist/cjs/features/session_replay/aggregate/index.js +1 -1
  20. package/dist/cjs/features/session_replay/shared/recorder.js +1 -1
  21. package/dist/cjs/features/spa/aggregate/index.js +1 -1
  22. package/dist/cjs/features/utils/aggregate-base.js +4 -3
  23. package/dist/cjs/features/utils/instrument-base.js +2 -2
  24. package/dist/cjs/loaders/agent-base.js +1 -1
  25. package/dist/cjs/loaders/agent.js +3 -4
  26. package/dist/cjs/loaders/api/api.js +9 -7
  27. package/dist/cjs/loaders/micro-agent.js +5 -5
  28. package/dist/esm/common/config/state/configurable.js +4 -4
  29. package/dist/esm/common/config/state/init.js +5 -3
  30. package/dist/esm/common/constants/env.cdn.js +1 -1
  31. package/dist/esm/common/constants/env.npm.js +1 -1
  32. package/dist/esm/common/context/shared-context.js +2 -2
  33. package/dist/esm/common/drain/drain.js +1 -1
  34. package/dist/esm/common/harvest/harvest.js +1 -1
  35. package/dist/esm/common/session/session-entity.js +2 -2
  36. package/dist/esm/common/timing/time-keeper.js +12 -2
  37. package/dist/esm/common/util/console.js +3 -4
  38. package/dist/esm/common/util/obfuscate.js +3 -3
  39. package/dist/esm/common/wrap/wrap-logger.js +1 -2
  40. package/dist/esm/common/wrap/wrap-xhr.js +1 -1
  41. package/dist/esm/features/logging/aggregate/index.js +6 -6
  42. package/dist/esm/features/logging/constants.js +1 -4
  43. package/dist/esm/features/metrics/aggregate/index.js +16 -0
  44. package/dist/esm/features/page_view_event/aggregate/index.js +21 -5
  45. package/dist/esm/features/session_replay/aggregate/index.js +1 -1
  46. package/dist/esm/features/session_replay/shared/recorder.js +1 -1
  47. package/dist/esm/features/spa/aggregate/index.js +1 -1
  48. package/dist/esm/features/utils/aggregate-base.js +4 -3
  49. package/dist/esm/features/utils/instrument-base.js +2 -2
  50. package/dist/esm/loaders/agent-base.js +1 -1
  51. package/dist/esm/loaders/agent.js +3 -4
  52. package/dist/esm/loaders/api/api.js +9 -7
  53. package/dist/esm/loaders/micro-agent.js +5 -5
  54. package/dist/types/common/config/state/init.d.ts.map +1 -1
  55. package/dist/types/common/timing/time-keeper.d.ts +2 -1
  56. package/dist/types/common/timing/time-keeper.d.ts.map +1 -1
  57. package/dist/types/common/util/console.d.ts +1 -1
  58. package/dist/types/common/util/console.d.ts.map +1 -1
  59. package/dist/types/common/wrap/wrap-logger.d.ts.map +1 -1
  60. package/dist/types/features/logging/constants.d.ts +0 -3
  61. package/dist/types/features/logging/constants.d.ts.map +1 -1
  62. package/dist/types/features/metrics/aggregate/index.d.ts.map +1 -1
  63. package/dist/types/features/page_view_event/aggregate/index.d.ts.map +1 -1
  64. package/dist/types/features/session_replay/shared/recorder.d.ts.map +1 -1
  65. package/dist/types/features/utils/aggregate-base.d.ts.map +1 -1
  66. package/dist/types/loaders/agent.d.ts.map +1 -1
  67. package/dist/types/loaders/api/api.d.ts.map +1 -1
  68. package/package.json +1 -1
  69. package/src/common/config/state/configurable.js +4 -4
  70. package/src/common/config/state/init.js +4 -3
  71. package/src/common/context/shared-context.js +2 -2
  72. package/src/common/drain/drain.js +1 -1
  73. package/src/common/harvest/harvest.js +1 -1
  74. package/src/common/session/session-entity.js +2 -2
  75. package/src/common/timing/time-keeper.js +14 -2
  76. package/src/common/util/console.js +3 -4
  77. package/src/common/util/obfuscate.js +3 -3
  78. package/src/common/wrap/wrap-logger.js +1 -2
  79. package/src/common/wrap/wrap-xhr.js +1 -1
  80. package/src/features/logging/aggregate/index.js +6 -6
  81. package/src/features/logging/constants.js +0 -4
  82. package/src/features/metrics/aggregate/index.js +12 -0
  83. package/src/features/page_view_event/aggregate/index.js +22 -5
  84. package/src/features/session_replay/aggregate/index.js +1 -1
  85. package/src/features/session_replay/shared/recorder.js +1 -1
  86. package/src/features/spa/aggregate/index.js +1 -1
  87. package/src/features/utils/aggregate-base.js +4 -3
  88. package/src/features/utils/instrument-base.js +2 -2
  89. package/src/loaders/agent-base.js +1 -1
  90. package/src/loaders/agent.js +3 -4
  91. package/src/loaders/api/api.js +9 -7
  92. package/src/loaders/micro-agent.js +5 -5
@@ -58,11 +58,11 @@ export class MicroAgent extends AgentBase {
58
58
  const featNames = nonAutoFeatures;
59
59
  if (features === undefined) features = featNames;else {
60
60
  features = Array.isArray(features) && features.length ? features : [features];
61
- if (features.some(f => !featNames.includes(f))) return warn("Invalid feature name supplied. Acceptable feature names are: ".concat(featNames));
61
+ if (features.some(f => !featNames.includes(f))) return warn(37, featNames);
62
62
  if (!features.includes(FEATURE_NAMES.pageViewEvent)) features.push(FEATURE_NAMES.pageViewEvent);
63
63
  }
64
64
  } catch (err) {
65
- warn('An unexpected issue occurred', err);
65
+ warn(23, err);
66
66
  }
67
67
  try {
68
68
  const enabledFeatures = getEnabledFeatures(this.agentIdentifier);
@@ -70,7 +70,7 @@ export class MicroAgent extends AgentBase {
70
70
  // a biproduct of doing this is that the "session manager" is automatically handled through importing this feature
71
71
  this.features.page_view_event = new PVE(this.agentIdentifier, this.sharedAggregator);
72
72
  } catch (err) {
73
- warn('Something prevented the agent from instrumenting.', err);
73
+ warn(24, err);
74
74
  }
75
75
  onWindowLoad(() => {
76
76
  // these features do not import an "instrument" file, meaning they are only hooked up to the API.
@@ -86,13 +86,13 @@ export class MicroAgent extends AgentBase {
86
86
  Aggregate
87
87
  } = _ref2;
88
88
  this.features[f] = new Aggregate(this.agentIdentifier, this.sharedAggregator);
89
- }).catch(err => warn('Something prevented the agent from being downloaded.', err));
89
+ }).catch(err => warn(25, err));
90
90
  }
91
91
  });
92
92
  });
93
93
  return true;
94
94
  } catch (err) {
95
- warn('Failed to initialize instrument classes.', err);
95
+ warn(26, err);
96
96
  return false;
97
97
  }
98
98
  }
@@ -1 +1 @@
1
- {"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../../../../../src/common/config/state/init.js"],"names":[],"mappings":"AAgHA,+CAIC;AAED,0DAKC;AAED,+DAYC"}
1
+ {"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../../../../../src/common/config/state/init.js"],"names":[],"mappings":"AAiHA,+CAIC;AAED,0DAKC;AAED,+DAYC"}
@@ -5,8 +5,9 @@
5
5
  */
6
6
  export class TimeKeeper {
7
7
  constructor(agentIdentifier: any);
8
- get ready(): number;
8
+ get ready(): boolean;
9
9
  get correctedOriginTime(): number;
10
+ get localTimeDiff(): number;
10
11
  /**
11
12
  * Process a rum request to calculate NR server time.
12
13
  * @param rumRequest {XMLHttpRequest} The xhr for the rum request
@@ -1 +1 @@
1
- {"version":3,"file":"time-keeper.d.ts","sourceRoot":"","sources":["../../../../src/common/timing/time-keeper.js"],"names":[],"mappings":"AAGA;;;;GAIG;AACH;IA2BE,kCAGC;IAED,oBAEC;IAED,kCAEC;IAED;;;;;OAKG;IACH,8BAJsB,cAAc,aACf,MAAM,WACR,MAAM,QAuBxB;IAED;;;;;OAKG;IACH,uCAHwB,MAAM,GACjB,MAAM,CAIlB;IAED;;;;OAIG;IACH,oCAHqB,MAAM,GACf,MAAM,CAIjB;IAED,+FAA+F;IAC/F,0BAOC;;CACF"}
1
+ {"version":3,"file":"time-keeper.d.ts","sourceRoot":"","sources":["../../../../src/common/timing/time-keeper.js"],"names":[],"mappings":"AAKA;;;;GAIG;AACH;IA2BE,kCAGC;IAED,qBAEC;IAED,kCAEC;IAED,4BAEC;IAED;;;;;OAKG;IACH,8BAJsB,cAAc,aACf,MAAM,WACR,MAAM,QA2BxB;IAED;;;;;OAKG;IACH,uCAHwB,MAAM,GACjB,MAAM,CAIlB;IAED;;;;OAIG;IACH,oCAHqB,MAAM,GACf,MAAM,CAIjB;IAED,+FAA+F;IAC/F,0BASC;;CACF"}
@@ -4,5 +4,5 @@
4
4
  * @param {*} [secondary] Secondary data to include, usually an error or object
5
5
  * @returns
6
6
  */
7
- export function warn(message: string, secondary?: any): void;
7
+ export function warn(code: any, secondary?: any): void;
8
8
  //# sourceMappingURL=console.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"console.d.ts","sourceRoot":"","sources":["../../../../src/common/util/console.js"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,8BAJW,MAAM,cACN,GAAC,QAOX"}
1
+ {"version":3,"file":"console.d.ts","sourceRoot":"","sources":["../../../../src/common/util/console.js"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,4CAHW,GAAC,QAMX"}
@@ -1 +1 @@
1
- {"version":3,"file":"wrap-logger.d.ts","sourceRoot":"","sources":["../../../../src/common/wrap/wrap-logger.js"],"names":[],"mappings":"AAeA;;;;;;GAMG;AAEH,qCANW,MAAM,UACN,MAAM,YACN,MAAM,iBACJ,MAAM,CAoBlB;AAED;;;;;;GAMG;AACH,mCAJW,MAAM,GAEJ,MAAM,CAIlB"}
1
+ {"version":3,"file":"wrap-logger.d.ts","sourceRoot":"","sources":["../../../../src/common/wrap/wrap-logger.js"],"names":[],"mappings":"AAcA;;;;;;GAMG;AAEH,qCANW,MAAM,UACN,MAAM,YACN,MAAM,iBACJ,MAAM,CAoBlB;AAED;;;;;;GAMG;AACH,mCAJW,MAAM,GAEJ,MAAM,CAIlB"}
@@ -8,7 +8,4 @@ export namespace LOG_LEVELS {
8
8
  export const LOGGING_EVENT_EMITTER_CHANNEL: "log";
9
9
  export const FEATURE_NAME: string;
10
10
  export const MAX_PAYLOAD_SIZE: 1000000;
11
- export const LOGGING_FAILURE_MESSAGE: "failed to wrap logger: ";
12
- export const LOGGING_LEVEL_FAILURE_MESSAGE: "invalid log level: ";
13
- export const LOGGING_IGNORED: "ignored log: ";
14
11
  //# sourceMappingURL=constants.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../../../../src/features/logging/constants.js"],"names":[],"mappings":";;;;;;;AAUA,kDAAkD;AAElD,kCAAiD;AAEjD,uCAAuC;AAEvC,gEAAgE;AAChE,kEAAkE;AAClE,8CAA8C"}
1
+ {"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../../../../src/features/logging/constants.js"],"names":[],"mappings":";;;;;;;AAUA,kDAAkD;AAElD,kCAAiD;AAEjD,uCAAuC"}
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/features/metrics/aggregate/index.js"],"names":[],"mappings":"AAaA;IACE,2BAAiC;IACjC,mDAsBC;IAED,wDAKC;IAED,iDAKC;IAED,qBA6CC;IAED,0BAOC;IAED,eAkCC;IA/BG,mCAAyB;CAgC9B;8BAtI6B,4BAA4B"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/features/metrics/aggregate/index.js"],"names":[],"mappings":"AAaA;IACE,2BAAiC;IACjC,mDAsBC;IAED,wDAKC;IAED,iDAKC;IAED,qBAyDC;IAED,0BAOC;IAED,eAkCC;IA/BG,mCAAyB;CAgC9B;8BAlJ6B,4BAA4B"}
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/features/page_view_event/aggregate/index.js"],"names":[],"mappings":"AAoBA;IACE,2BAA2C;IAC3C,mDAqBC;IAlBC,wBAAwB;IACxB,8BAA8B;IAC9B,8BAA8B;IAC9B,uBAAsD;IAiBxD,gBAsGC;CACF;8BA3I6B,4BAA4B;2BAS/B,oCAAoC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/features/page_view_event/aggregate/index.js"],"names":[],"mappings":"AAoBA;IACE,2BAA2C;IAC3C,mDA0BC;IAvBC,wBAAwB;IACxB,8BAA8B;IAC9B,8BAA8B;IAC9B,uBAAsD;IAsBxD,gBAkHC;CACF;8BA5J6B,4BAA4B;2BAS/B,oCAAoC"}
@@ -1 +1 @@
1
- {"version":3,"file":"recorder.d.ts","sourceRoot":"","sources":["../../../../../src/features/session_replay/shared/recorder.js"],"names":[],"mappings":"AAYA;IAUE,yBAkBC;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,uBAsCC;IAED;;;;;OAKG;IACH,aAHW,GAAC,cACD,GAAC,QA4BX;IAED,0HAA0H;IAC1H,yCAqDC;IA3CG,8BAAoB;IA6CxB,0HAA0H;IAC1H,yBAOC;IAED,wBAEC;IAED,gCAAgC;IAChC,uCAGC;IAED;;;SAGK;IACL,oCAGC;;CACF;+BA3N8B,mBAAmB"}
1
+ {"version":3,"file":"recorder.d.ts","sourceRoot":"","sources":["../../../../../src/features/session_replay/shared/recorder.js"],"names":[],"mappings":"AAYA;IAUE,yBAkBC;IAdC,iEAAiE;IACjE,mBAAsB;IACtB,6DAA6D;IAC7D,oCAAuC;IACvC,kIAAkI;IAClI,kBAAqB;IACrB,sDAAsD;IACtD,YAAoB;IACpB,oEAAoE;IACpE,6BAAqH;IACrH,0FAA0F;IAC1F,eAAqI;IACrI,uIAAuI;IACvI,0BAAyE;IAG3E;;;;;;;;;MAYC;IAED,mFAAmF;IACnF,oBAKC;IAED,qDAAqD;IACrD,uBAsCC;IAED;;;;;OAKG;IACH,aAHW,GAAC,cACD,GAAC,QA4BX;IAED,0HAA0H;IAC1H,yCAqDC;IA3CG,8BAAoB;IA6CxB,0HAA0H;IAC1H,yBAOC;IAED,wBAEC;IAED,gCAAgC;IAChC,uCAGC;IAED;;;SAGK;IACL,oCAGC;;CACF;+BA3N8B,mBAAmB"}
@@ -1 +1 @@
1
- {"version":3,"file":"aggregate-base.d.ts","sourceRoot":"","sources":["../../../../src/features/utils/aggregate-base.js"],"names":[],"mappings":"AAOA;IACE,4BAGC;IAED;;;;OAIG;IACH,yBAHW,MAAM,EAAE,gBAsBlB;IAED,cAGC;IADC,6BAAmB;IAGrB;;;OAGG;IACH,2BAqBC;CACF;4BAtE2B,gBAAgB"}
1
+ {"version":3,"file":"aggregate-base.d.ts","sourceRoot":"","sources":["../../../../src/features/utils/aggregate-base.js"],"names":[],"mappings":"AAOA;IACE,4BAGC;IAED;;;;OAIG;IACH,yBAHW,MAAM,EAAE,gBAsBlB;IAED,cAGC;IADC,6BAAmB;IAGrB;;;OAGG;IACH,2BAsBC;CACF;4BAvE2B,gBAAgB"}
@@ -1 +1 @@
1
- {"version":3,"file":"agent.d.ts","sourceRoot":"","sources":["../../../src/loaders/agent.js"],"names":[],"mappings":"AAkBA;;;GAGG;AACH;IACE;;;OAGG;IACH,qBAHW,MAAM,wCA2BhB;IAdC,yCAAiF;IACjF,yBAAkB;IAGlB,sCAAsD;IAMtD,uCAA6G;IAM/G;;;;;MAOC;IAED,yBAiCC;CACF;0BA5FyB,cAAc;2BAQb,gCAAgC"}
1
+ {"version":3,"file":"agent.d.ts","sourceRoot":"","sources":["../../../src/loaders/agent.js"],"names":[],"mappings":"AAiBA;;;GAGG;AACH;IACE;;;OAGG;IACH,qBAHW,MAAM,wCA2BhB;IAdC,yCAAiF;IACjF,yBAAkB;IAGlB,sCAAsD;IAMtD,uCAA6G;IAM/G;;;;;MAOC;IAED,yBAiCC;CACF;0BA3FyB,cAAc;2BAQb,gCAAgC"}
@@ -1 +1 @@
1
- {"version":3,"file":"api.d.ts","sourceRoot":"","sources":["../../../../src/loaders/api/api.js"],"names":[],"mappings":"AAsBA,2CAiBC;AAID;;;;;;;;;;;;IA+DE;;;;OAIG;qBAFQ,MAAM;IAWjB;;;;OAIG;iCAFQ,MAAM,GAAC,IAAI;;;;;EAiGvB"}
1
+ {"version":3,"file":"api.d.ts","sourceRoot":"","sources":["../../../../src/loaders/api/api.js"],"names":[],"mappings":"AAsBA,2CAiBC;AAID;;;;;;;;;;;;IAiEE;;;;OAIG;qBAFQ,MAAM;IAWjB;;;;OAIG;iCAFQ,MAAM,GAAC,IAAI;;;;;EAiGvB"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@newrelic/browser-agent",
3
- "version": "1.262.0",
3
+ "version": "1.263.0",
4
4
  "private": false,
5
5
  "author": "New Relic Browser Agent Team <browser-agent@newrelic.com>",
6
6
  "description": "New Relic Browser Agent",
@@ -2,8 +2,8 @@ import { warn } from '../../util/console'
2
2
 
3
3
  export function getModeledObject (obj, model) {
4
4
  try {
5
- if (!obj || typeof obj !== 'object') return warn('Setting a Configurable requires an object as input')
6
- if (!model || typeof model !== 'object') return warn('Setting a Configurable requires a model to set its initial properties')
5
+ if (!obj || typeof obj !== 'object') return warn(3)
6
+ if (!model || typeof model !== 'object') return warn(4)
7
7
  // allow getters and setters to pass from model to target
8
8
  const output = Object.create(
9
9
  Object.getPrototypeOf(model),
@@ -19,11 +19,11 @@ export function getModeledObject (obj, model) {
19
19
  else if (typeof obj[key] === 'object' && typeof model[key] === 'object') output[key] = getModeledObject(obj[key], model[key])
20
20
  else output[key] = obj[key]
21
21
  } catch (e) {
22
- warn('An error occurred while setting a property of a Configurable', e)
22
+ warn(1, e)
23
23
  }
24
24
  }
25
25
  return output
26
26
  } catch (err) {
27
- warn('An error occured while setting a Configurable', err)
27
+ warn(2, err)
28
28
  }
29
29
  }
@@ -69,6 +69,7 @@ const model = () => {
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
72
+ fix_stylesheets: true, // fetch missing stylesheet resources for inlining, only works if 'inline_stylesheet' is also true
72
73
  // recording config settings
73
74
  mask_all_inputs: true,
74
75
  // this has a getter/setter to facilitate validation of the selectors
@@ -76,7 +77,7 @@ const model = () => {
76
77
  set mask_text_selector (val) {
77
78
  if (isValidSelector(val)) hiddenState.mask_selector = `${val},${nrMask}`
78
79
  else if (val === '' || val === null) hiddenState.mask_selector = nrMask
79
- else warn('An invalid session_replay.mask_selector was provided. \'*\' will be used.', val)
80
+ else warn(5, val)
80
81
  },
81
82
  // these properties only have getters because they are enforcable constants and should error if someone tries to override them
82
83
  get block_class () { return 'nr-block' },
@@ -89,7 +90,7 @@ const model = () => {
89
90
  },
90
91
  set block_selector (val) {
91
92
  if (isValidSelector(val)) hiddenState.block_selector += `,${val}`
92
- else if (val !== '') warn('An invalid session_replay.block_selector was provided and will not be used', val)
93
+ else if (val !== '') warn(6, val)
93
94
  },
94
95
  // password: must always be present and true no matter what customer sets
95
96
  get mask_input_options () {
@@ -97,7 +98,7 @@ const model = () => {
97
98
  },
98
99
  set mask_input_options (val) {
99
100
  if (val && typeof val === 'object') hiddenState.mask_input_options = { ...val, password: true }
100
- else warn('An invalid session_replay.mask_input_option was provided and will not be used', val)
101
+ else warn(7, val)
101
102
  }
102
103
  },
103
104
  session_trace: { enabled: true, harvestTimeSeconds: 10, autoStart: true },
@@ -8,14 +8,14 @@ const model = {
8
8
  export class SharedContext {
9
9
  constructor (context) {
10
10
  try {
11
- if (typeof context !== 'object') return warn('shared context requires an object as input')
11
+ if (typeof context !== 'object') return warn(8)
12
12
  this.sharedContext = {}
13
13
  Object.assign(this.sharedContext, model)
14
14
  Object.entries(context).forEach(([key, value]) => {
15
15
  if (Object.keys(model).includes(key)) this.sharedContext[key] = value
16
16
  })
17
17
  } catch (err) {
18
- warn('An error occurred while setting SharedContext', err)
18
+ warn(9, err)
19
19
  }
20
20
  }
21
21
  }
@@ -90,7 +90,7 @@ function checkCanDrainAll (agentIdentifier) {
90
90
  function drainGroup (agentIdentifier, group, activateGroup = true) {
91
91
  const baseEE = agentIdentifier ? ee.get(agentIdentifier) : ee
92
92
  const handlers = defaultRegister.handlers // other storage in registerHandler
93
- if (!baseEE.backlog || !handlers) return
93
+ if (baseEE.aborted || !baseEE.backlog || !handlers) return
94
94
 
95
95
  // Only activated features being drained should run queued listeners on buffered events. Deactivated features only need to release memory.
96
96
  if (activateGroup) {
@@ -122,7 +122,7 @@ export class Harvest extends SharedContext {
122
122
  body = stringify(body)
123
123
  }
124
124
  /** Warn --once per endpoint-- if the agent tries to send large payloads */
125
- if (body.length > 750000 && (warnings[endpoint] = (warnings?.[endpoint] || 0) + 1) === 1) warn(`The Browser Agent is attempting to send a very large payload to /${endpoint}. This is usually tied to large amounts of custom attributes. Please check your configurations.`)
125
+ if (body.length > 750000 && (warnings[endpoint] = (warnings?.[endpoint] || 0) + 1) === 1) warn(28, endpoint)
126
126
  }
127
127
 
128
128
  if (!body || body.length === 0 || body === '{}' || body === '[]') {
@@ -182,7 +182,7 @@ export class SessionEntity {
182
182
 
183
183
  return obj
184
184
  } catch (e) {
185
- warn('Failed to read from storage API', e)
185
+ warn(10, e)
186
186
  // storage is inaccessible
187
187
  return {}
188
188
  }
@@ -208,7 +208,7 @@ export class SessionEntity {
208
208
  return data
209
209
  } catch (e) {
210
210
  // storage is inaccessible
211
- warn('Failed to write to the storage API', e)
211
+ warn(11, e)
212
212
  return null
213
213
  }
214
214
  }
@@ -1,6 +1,8 @@
1
1
  import { originTime } from '../constants/runtime'
2
2
  import { getRuntime } from '../config/config'
3
3
 
4
+ const rfc2616Regex = /^(Mon|Tue|Wed|Thu|Fri|Sat|Sun), ([0-3][0-9]) (Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec) ([0-9]{4}) ([01][0-9]|2[0-3])(:[0-5][0-9]){2} GMT$/
5
+
4
6
  /**
5
7
  * Class used to adjust the timestamp of harvested data to New Relic server time. This
6
8
  * is done by tracking the performance timings of the RUM call and applying a calculation
@@ -29,7 +31,7 @@ export class TimeKeeper {
29
31
  /**
30
32
  * Represents whether the timekeeper is in a state that it can accurately convert
31
33
  * timestamps.
32
- * @type {number}
34
+ * @type {boolean}
33
35
  */
34
36
  #ready = false
35
37
 
@@ -46,6 +48,10 @@ export class TimeKeeper {
46
48
  return this.#correctedOriginTime
47
49
  }
48
50
 
51
+ get localTimeDiff () {
52
+ return this.#localTimeDiff
53
+ }
54
+
49
55
  /**
50
56
  * Process a rum request to calculate NR server time.
51
57
  * @param rumRequest {XMLHttpRequest} The xhr for the rum request
@@ -53,12 +59,16 @@ export class TimeKeeper {
53
59
  * @param endTime {number} The end time of the RUM request
54
60
  */
55
61
  processRumRequest (rumRequest, startTime, endTime) {
56
- this.processStoredDiff()
62
+ this.processStoredDiff() // Check session entity for stored time diff
57
63
  if (this.#ready) return // Server time calculated from session entity
64
+
58
65
  const responseDateHeader = rumRequest.getResponseHeader('Date')
59
66
  if (!responseDateHeader) {
60
67
  throw new Error('Missing date header on rum response.')
61
68
  }
69
+ if (!rfc2616Regex.test(responseDateHeader)) {
70
+ throw new Error('Date header invalid format.')
71
+ }
62
72
 
63
73
  const medianRumOffset = (endTime - startTime) / 2
64
74
  const serverOffset = startTime + medianRumOffset
@@ -96,6 +106,8 @@ export class TimeKeeper {
96
106
 
97
107
  /** Process the session entity and use the info to set the main time calculations if present */
98
108
  processStoredDiff () {
109
+ if (this.#ready) return // Time diff has already been calculated
110
+
99
111
  const storedServerTimeDiff = this.#session?.read()?.serverTimeDiff
100
112
  if (typeof storedServerTimeDiff === 'number' && !isNaN(storedServerTimeDiff)) {
101
113
  this.#localTimeDiff = storedServerTimeDiff
@@ -4,8 +4,7 @@
4
4
  * @param {*} [secondary] Secondary data to include, usually an error or object
5
5
  * @returns
6
6
  */
7
- export function warn (message, secondary) {
8
- if (typeof console.warn !== 'function') return
9
- console.warn(`New Relic: ${message}`)
10
- if (secondary) console.warn(secondary)
7
+ export function warn (code, secondary) {
8
+ if (typeof console.debug !== 'function') return
9
+ console.debug(`New Relic Warning: https://github.com/newrelic/newrelic-browser-agent/blob/main/docs/warning-codes.md#${code}`, secondary)
11
10
  }
@@ -49,16 +49,16 @@ export function validateRules (rules) {
49
49
  var invalidRegexDetected = false
50
50
  for (var i = 0; i < rules.length; i++) {
51
51
  if (!('regex' in rules[i])) {
52
- warn('An obfuscation replacement rule was detected missing a "regex" value.')
52
+ warn(12)
53
53
  invalidRegexDetected = true
54
54
  } else if (typeof rules[i].regex !== 'string' && !(rules[i].regex instanceof RegExp)) {
55
- warn('An obfuscation replacement rule contains a "regex" value with an invalid type (must be a string or RegExp)')
55
+ warn(13)
56
56
  invalidRegexDetected = true
57
57
  }
58
58
 
59
59
  var replacement = rules[i].replacement
60
60
  if (replacement && typeof replacement !== 'string') {
61
- warn('An obfuscation replacement rule contains a "replacement" value with an invalid type (must be a string)')
61
+ warn(14)
62
62
  invalidReplacementDetected = true
63
63
  }
64
64
  }
@@ -7,7 +7,6 @@
7
7
  * This module is used by: jserrors, spa.
8
8
  */
9
9
 
10
- import { LOGGING_FAILURE_MESSAGE } from '../../features/logging/constants'
11
10
  import { ee as baseEE, contextId } from '../event-emitter/contextual-ee'
12
11
  import { EventContext } from '../event-emitter/event-context'
13
12
  import { warn } from '../util/console'
@@ -22,7 +21,7 @@ import { createWrapperWithEmitter as wfn } from './wrap-function'
22
21
  */
23
22
  // eslint-disable-next-line
24
23
  export function wrapLogger(sharedEE, parent, loggerFn, context) {
25
- if (!(typeof parent === 'object' && !!parent && typeof loggerFn === 'string' && !!loggerFn && typeof parent[loggerFn] === 'function')) return warn(LOGGING_FAILURE_MESSAGE + 'invalid argument(s)')
24
+ if (!(typeof parent === 'object' && !!parent && typeof loggerFn === 'string' && !!loggerFn && typeof parent[loggerFn] === 'function')) return warn(29)
26
25
  const ee = scopedEE(sharedEE)
27
26
  const wrapFn = wfn(ee)
28
27
 
@@ -56,7 +56,7 @@ export function wrapXhr (sharedEE) {
56
56
  ee.emit('new-xhr', [xhr], context)
57
57
  xhr.addEventListener(READY_STATE_CHANGE, wrapXHR(context), eventListenerOpts(false))
58
58
  } catch (e) {
59
- warn('An error occurred while intercepting XHR', e)
59
+ warn(15, e)
60
60
  try {
61
61
  ee.emit('internal-error', [e])
62
62
  } catch (err) {
@@ -6,7 +6,7 @@ import { warn } from '../../../common/util/console'
6
6
  import { stringify } from '../../../common/util/stringify'
7
7
  import { SUPPORTABILITY_METRIC_CHANNEL } from '../../metrics/constants'
8
8
  import { AggregateBase } from '../../utils/aggregate-base'
9
- import { FEATURE_NAME, LOGGING_EVENT_EMITTER_CHANNEL, LOGGING_IGNORED, LOGGING_LEVEL_FAILURE_MESSAGE, LOG_LEVELS, MAX_PAYLOAD_SIZE } from '../constants'
9
+ import { FEATURE_NAME, LOGGING_EVENT_EMITTER_CHANNEL, LOG_LEVELS, MAX_PAYLOAD_SIZE } from '../constants'
10
10
  import { Log } from '../shared/log'
11
11
  import { isValidLogLevel } from '../shared/utils'
12
12
 
@@ -49,7 +49,7 @@ export class Aggregate extends AggregateBase {
49
49
 
50
50
  if (!attributes || typeof attributes !== 'object') attributes = {}
51
51
  if (typeof level === 'string') level = level.toUpperCase()
52
- if (!isValidLogLevel(level)) return warn(LOGGING_LEVEL_FAILURE_MESSAGE + level, Object.values(LOG_LEVELS))
52
+ if (!isValidLogLevel(level)) return warn(30, level)
53
53
 
54
54
  try {
55
55
  if (typeof message !== 'string') {
@@ -63,13 +63,13 @@ export class Aggregate extends AggregateBase {
63
63
  else message = String(message)
64
64
  }
65
65
  } catch (err) {
66
- warn('could not cast log message to string', message)
66
+ warn(16, message)
67
67
  return
68
68
  }
69
- if (typeof message !== 'string' || !message) return warn(LOGGING_IGNORED + 'invalid message')
69
+ if (typeof message !== 'string' || !message) return warn(32)
70
70
  if (message.length > MAX_PAYLOAD_SIZE) {
71
71
  handle(SUPPORTABILITY_METRIC_CHANNEL, ['Logging/Harvest/Failed/Seen', message.length])
72
- return warn(LOGGING_IGNORED + '> ' + MAX_PAYLOAD_SIZE + ' bytes: ', message.slice(0, 25) + '...')
72
+ return warn(31, message.slice(0, 25) + '...')
73
73
  }
74
74
 
75
75
  const log = new Log(
@@ -81,7 +81,7 @@ export class Aggregate extends AggregateBase {
81
81
  const logBytes = log.message.length + stringify(log.attributes).length + log.level.length + 10 // timestamp == 10 chars
82
82
  if (logBytes > MAX_PAYLOAD_SIZE) {
83
83
  handle(SUPPORTABILITY_METRIC_CHANNEL, ['Logging/Harvest/Failed/Seen', logBytes])
84
- return warn(LOGGING_IGNORED + '> ' + MAX_PAYLOAD_SIZE + ' bytes: ', log.message.slice(0, 25) + '...')
84
+ return warn(31, log.message.slice(0, 25) + '...')
85
85
  }
86
86
 
87
87
  if (this.estimatedBytes + logBytes >= MAX_PAYLOAD_SIZE) {
@@ -13,7 +13,3 @@ export const LOGGING_EVENT_EMITTER_CHANNEL = 'log'
13
13
  export const FEATURE_NAME = FEATURE_NAMES.logging
14
14
 
15
15
  export const MAX_PAYLOAD_SIZE = 1000000
16
-
17
- export const LOGGING_FAILURE_MESSAGE = 'failed to wrap logger: '
18
- export const LOGGING_LEVEL_FAILURE_MESSAGE = 'invalid log level: '
19
- export const LOGGING_IGNORED = 'ignored log: '
@@ -96,6 +96,18 @@ export class Aggregate extends AggregateBase {
96
96
  // Check if proxy for either chunks or beacon is being used
97
97
  if (proxy.assets) this.storeSupportabilityMetrics('Config/AssetsUrl/Changed')
98
98
  if (proxy.beacon) this.storeSupportabilityMetrics('Config/BeaconUrl/Changed')
99
+
100
+ if (isBrowserScope && window.MutationObserver) {
101
+ this.storeSupportabilityMetrics('Generic/VideoElement/Added', window.document.querySelectorAll('video').length)
102
+ const mo = new MutationObserver(records => {
103
+ records.forEach(record => {
104
+ record.addedNodes.forEach(addedNode => {
105
+ if (addedNode instanceof HTMLVideoElement) { this.storeSupportabilityMetrics('Generic/VideoElement/Added', 1) }
106
+ })
107
+ })
108
+ })
109
+ mo.observe(window.document.body, { childList: true, subtree: true })
110
+ }
99
111
  }
100
112
 
101
113
  eachSessionChecks () {
@@ -1,7 +1,7 @@
1
1
  import { globalScope, isBrowserScope, originTime } from '../../../common/constants/runtime'
2
2
  import { addPT, addPN } from '../../../common/timing/nav-timing'
3
3
  import { stringify } from '../../../common/util/stringify'
4
- import { getInfo, getRuntime } from '../../../common/config/config'
4
+ import { getInfo, getRuntime, isConfigured } from '../../../common/config/config'
5
5
  import { Harvest } from '../../../common/harvest/harvest'
6
6
  import * as CONSTANTS from '../constants'
7
7
  import { getActivatedFeaturesFlags } from './initialized-features'
@@ -28,6 +28,11 @@ export class Aggregate extends AggregateBase {
28
28
  this.firstByteToDomContent = 0 // our "dom processing" duration
29
29
  this.timeKeeper = new TimeKeeper(this.agentIdentifier)
30
30
 
31
+ if (!isConfigured(agentIdentifier)) {
32
+ this.ee.abort()
33
+ return warn(43)
34
+ }
35
+
31
36
  if (isBrowserScope) {
32
37
  timeToFirstByte.subscribe(({ value, attrs }) => {
33
38
  const navEntry = attrs.navigationEntry
@@ -48,7 +53,6 @@ export class Aggregate extends AggregateBase {
48
53
  const agentRuntime = getRuntime(this.agentIdentifier)
49
54
  const harvester = new Harvest(this)
50
55
 
51
- if (!info.beacon) return
52
56
  if (info.queueTime) this.aggregator.store('measures', 'qt', { value: info.queueTime })
53
57
  if (info.applicationTime) this.aggregator.store('measures', 'ap', { value: info.applicationTime })
54
58
 
@@ -126,11 +130,24 @@ export class Aggregate extends AggregateBase {
126
130
  if (!this.timeKeeper.ready) throw new Error('TimeKeeper not ready')
127
131
 
128
132
  agentRuntime.timeKeeper = this.timeKeeper
133
+
134
+ // Check if the time diff is such that we need to capture a supportability metric
135
+ if (this.timeKeeper.localTimeDiff >= 12 * 60 * 60 * 1000) {
136
+ handle(SUPPORTABILITY_METRIC_CHANNEL, ['PVE/NRTime/Calculation/DiffExceed12Hrs'], undefined, FEATURE_NAMES.metrics, this.ee)
137
+ } else if (this.timeKeeper.localTimeDiff >= 6 * 60 * 60 * 1000) {
138
+ handle(SUPPORTABILITY_METRIC_CHANNEL, ['PVE/NRTime/Calculation/DiffExceed6Hrs'], undefined, FEATURE_NAMES.metrics, this.ee)
139
+ } else if (this.timeKeeper.localTimeDiff >= 60 * 60 * 1000) {
140
+ handle(SUPPORTABILITY_METRIC_CHANNEL, ['PVE/NRTime/Calculation/DiffExceed1Hrs'], undefined, FEATURE_NAMES.metrics, this.ee)
141
+ }
129
142
  } catch (error) {
130
- handle(SUPPORTABILITY_METRIC_CHANNEL, ['PVE/NRTime/Calculation/Failed'], undefined, FEATURE_NAMES.metrics, this.ee)
143
+ if (error?.message?.indexOf('invalid format') > 0) {
144
+ handle(SUPPORTABILITY_METRIC_CHANNEL, ['PVE/NRTime/Calculation/InvalidFormat'], undefined, FEATURE_NAMES.metrics, this.ee)
145
+ } else {
146
+ handle(SUPPORTABILITY_METRIC_CHANNEL, ['PVE/NRTime/Calculation/Failed'], undefined, FEATURE_NAMES.metrics, this.ee)
147
+ }
131
148
  drain(this.agentIdentifier, FEATURE_NAMES.metrics, true)
132
149
  this.ee.abort()
133
- warn('Could not calculate New Relic server time. Agent shutting down.', error)
150
+ warn(17, error)
134
151
  return
135
152
  }
136
153
 
@@ -141,7 +158,7 @@ export class Aggregate extends AggregateBase {
141
158
  this.drain()
142
159
  } catch (err) {
143
160
  this.ee.abort()
144
- warn('RUM call failed. Agent shutting down.', err)
161
+ warn(18, err)
145
162
  }
146
163
  }
147
164
  })
@@ -394,7 +394,7 @@ export class Aggregate extends AggregateBase {
394
394
 
395
395
  /** Abort the feature, once aborted it will not resume */
396
396
  abort (reason = {}, data) {
397
- warn(`SR aborted -- ${reason.message}`)
397
+ warn(33, reason.message)
398
398
  handle(SUPPORTABILITY_METRIC_CHANNEL, [`SessionReplay/Abort/${reason.sm}`, data], undefined, FEATURE_NAMES.metrics, this.ee)
399
399
  this.blocked = true
400
400
  this.mode = MODE.OFF
@@ -35,7 +35,7 @@ export class Recorder {
35
35
  /** Config to inform to inline stylesheet contents (true default) */
36
36
  this.shouldInlineStylesheets = getConfigurationValue(this.parent.agentIdentifier, 'session_replay.inline_stylesheet')
37
37
  /** A flag that can be set to false by failing conversions to stop the fetching process */
38
- this.shouldFix = this.shouldInlineStylesheets
38
+ this.shouldFix = this.shouldInlineStylesheets && getConfigurationValue(this.parent.agentIdentifier, 'session_replay.fix_stylesheets')
39
39
  /** The method to stop recording. This defaults to a noop, but is overwritten once the recording library is imported and initialized */
40
40
  this.stopRecording = () => { /* no-op until set by rrweb initializer */ }
41
41
  }
@@ -757,7 +757,7 @@ export class Aggregate extends AggregateBase {
757
757
  handle(SUPPORTABILITY_METRIC_CHANNEL, [`Spa/Interaction/${smCategory}/Duration/Ms`, Math.max((interaction.root?.end || 0) - (interaction.root?.start || 0), 0)], undefined, FEATURE_NAMES.metrics, baseEE)
758
758
 
759
759
  scheduler?.scheduleHarvest(0)
760
- if (!scheduler) warn('SPA scheduler is not initialized. Saved interaction is not sent!')
760
+ if (!scheduler) warn(19)
761
761
  }
762
762
 
763
763
  function isEnabled () {
@@ -49,7 +49,8 @@ export class AggregateBase extends FeatureBase {
49
49
  checkConfiguration () {
50
50
  // NOTE: This check has to happen at aggregator load time
51
51
  if (!isConfigured(this.agentIdentifier)) {
52
- let jsAttributes = { ...gosCDN().info?.jsAttributes }
52
+ const cdn = gosCDN()
53
+ let jsAttributes = { ...cdn.info?.jsAttributes }
53
54
  try {
54
55
  jsAttributes = {
55
56
  ...jsAttributes,
@@ -59,9 +60,9 @@ export class AggregateBase extends FeatureBase {
59
60
  // do nothing
60
61
  }
61
62
  configure({ agentIdentifier: this.agentIdentifier }, {
62
- ...gosCDN(),
63
+ ...cdn,
63
64
  info: {
64
- ...gosCDN().info,
65
+ ...cdn.info,
65
66
  jsAttributes
66
67
  },
67
68
  runtime: getRuntime(this.agentIdentifier)
@@ -85,7 +85,7 @@ export class InstrumentBase extends FeatureBase {
85
85
  session = setupAgentSession(this.agentIdentifier)
86
86
  }
87
87
  } catch (e) {
88
- warn('A problem occurred when starting up session manager. This page will not start or extend any session.', e)
88
+ warn(20, e)
89
89
  this.ee.emit('internal-error', [e])
90
90
  if (this.featureName === FEATURE_NAMES.sessionReplay) this.abortHandler?.() // SR should stop recording if session DNE
91
91
  }
@@ -105,7 +105,7 @@ export class InstrumentBase extends FeatureBase {
105
105
  this.featAggregate = new Aggregate(this.agentIdentifier, this.aggregator, argsObjFromInstrument)
106
106
  loadedSuccessfully(true)
107
107
  } catch (e) {
108
- warn(`Downloading and initializing ${this.featureName} failed...`, e)
108
+ warn(34, 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
111
  drain(this.agentIdentifier, this.featureName, true)
@@ -25,7 +25,7 @@ export class AgentBase {
25
25
  * @param {...any} args
26
26
  */
27
27
  #callMethod (methodName, ...args) {
28
- if (typeof this.api?.[methodName] !== 'function') warn(`Call to agent api ${methodName} failed. The API is not currently initialized.`)
28
+ if (typeof this.api?.[methodName] !== 'function') warn(35, methodName)
29
29
  else return this.api[methodName](...args)
30
30
  }
31
31
 
@@ -13,7 +13,6 @@ import { Instrument as PageViewEvent } from '../features/page_view_event/instrum
13
13
  import { Aggregator } from '../common/aggregate/aggregator'
14
14
  import { gosNREUM, setNREUMInitializedAgent } from '../common/window/nreum'
15
15
  import { warn } from '../common/util/console'
16
- import { stringify } from '../common/util/stringify'
17
16
  import { globalScope } from '../common/constants/runtime'
18
17
 
19
18
  /**
@@ -31,7 +30,7 @@ export class Agent extends AgentBase {
31
30
  if (!globalScope) {
32
31
  // We could not determine the runtime environment. Short-circuite the agent here
33
32
  // to avoid possible exceptions later that may cause issues with customer's application.
34
- warn('Failed to initialize the agent. Could not determine the runtime environment.')
33
+ warn(21)
35
34
  return
36
35
  }
37
36
 
@@ -74,12 +73,12 @@ export class Agent extends AgentBase {
74
73
 
75
74
  const dependencies = getFeatureDependencyNames(InstrumentCtor.featureName)
76
75
  const hasAllDeps = dependencies.every(featName => featName in this.features) // any other feature(s) this depends on should've been initialized on prior iterations by priority order
77
- if (!hasAllDeps) warn(`${InstrumentCtor.featureName} is enabled but one or more dependent features has not been initialized (${stringify(dependencies)}). This may cause unintended consequences or missing data...`)
76
+ if (!hasAllDeps) warn(36, InstrumentCtor.featureName)
78
77
 
79
78
  this.features[InstrumentCtor.featureName] = new InstrumentCtor(this.agentIdentifier, this.sharedAggregator)
80
79
  })
81
80
  } catch (err) {
82
- warn('Failed to initialize all enabled instrument classes (agent aborted) -', err)
81
+ warn(22, err)
83
82
  for (const featName in this.features) { // this.features hold only features that have been instantiated
84
83
  this.features[featName].abortHandler?.()
85
84
  }