@newrelic/browser-agent 1.272.0 → 1.273.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 (115) hide show
  1. package/CHANGELOG.md +8 -0
  2. package/dist/cjs/common/aggregate/aggregator.js +23 -30
  3. package/dist/cjs/common/aggregate/event-aggregator.js +84 -0
  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/harvest/harvest-scheduler.js +1 -1
  7. package/dist/cjs/common/harvest/harvest.js +1 -5
  8. package/dist/cjs/common/harvest/types.js +0 -1
  9. package/dist/cjs/features/ajax/aggregate/index.js +52 -62
  10. package/dist/cjs/features/generic_events/aggregate/index.js +18 -36
  11. package/dist/cjs/features/jserrors/aggregate/index.js +23 -69
  12. package/dist/cjs/features/logging/aggregate/index.js +52 -59
  13. package/dist/cjs/features/metrics/aggregate/index.js +8 -5
  14. package/dist/cjs/features/page_view_timing/aggregate/index.js +8 -25
  15. package/dist/cjs/features/session_replay/aggregate/index.js +11 -10
  16. package/dist/cjs/features/session_replay/shared/recorder-events.js +2 -2
  17. package/dist/cjs/features/session_trace/aggregate/index.js +77 -88
  18. package/dist/cjs/features/session_trace/aggregate/trace/storage.js +22 -13
  19. package/dist/cjs/features/soft_navigations/aggregate/index.js +10 -20
  20. package/dist/cjs/features/soft_navigations/instrument/index.js +5 -9
  21. package/dist/cjs/features/spa/aggregate/index.js +10 -26
  22. package/dist/cjs/features/utils/aggregate-base.js +37 -0
  23. package/dist/cjs/features/utils/event-buffer.js +36 -87
  24. package/dist/cjs/features/utils/instrument-base.js +3 -3
  25. package/dist/cjs/loaders/features/features.js +13 -1
  26. package/dist/esm/common/aggregate/aggregator.js +23 -30
  27. package/dist/esm/common/aggregate/event-aggregator.js +78 -0
  28. package/dist/esm/common/constants/env.cdn.js +1 -1
  29. package/dist/esm/common/constants/env.npm.js +1 -1
  30. package/dist/esm/common/harvest/harvest-scheduler.js +1 -1
  31. package/dist/esm/common/harvest/harvest.js +1 -5
  32. package/dist/esm/common/harvest/types.js +0 -1
  33. package/dist/esm/features/ajax/aggregate/index.js +53 -62
  34. package/dist/esm/features/generic_events/aggregate/index.js +18 -36
  35. package/dist/esm/features/jserrors/aggregate/index.js +24 -70
  36. package/dist/esm/features/logging/aggregate/index.js +52 -59
  37. package/dist/esm/features/metrics/aggregate/index.js +8 -5
  38. package/dist/esm/features/page_view_timing/aggregate/index.js +9 -26
  39. package/dist/esm/features/session_replay/aggregate/index.js +12 -11
  40. package/dist/esm/features/session_replay/shared/recorder-events.js +2 -2
  41. package/dist/esm/features/session_trace/aggregate/index.js +77 -88
  42. package/dist/esm/features/session_trace/aggregate/trace/storage.js +22 -13
  43. package/dist/esm/features/soft_navigations/aggregate/index.js +11 -21
  44. package/dist/esm/features/soft_navigations/instrument/index.js +5 -9
  45. package/dist/esm/features/spa/aggregate/index.js +11 -27
  46. package/dist/esm/features/utils/aggregate-base.js +37 -0
  47. package/dist/esm/features/utils/event-buffer.js +36 -88
  48. package/dist/esm/features/utils/instrument-base.js +3 -3
  49. package/dist/esm/loaders/features/features.js +12 -0
  50. package/dist/types/common/aggregate/aggregator.d.ts +4 -6
  51. package/dist/types/common/aggregate/aggregator.d.ts.map +1 -1
  52. package/dist/types/common/aggregate/event-aggregator.d.ts +26 -0
  53. package/dist/types/common/aggregate/event-aggregator.d.ts.map +1 -0
  54. package/dist/types/common/harvest/harvest.d.ts.map +1 -1
  55. package/dist/types/common/harvest/types.d.ts +1 -4
  56. package/dist/types/common/harvest/types.d.ts.map +1 -1
  57. package/dist/types/features/ajax/aggregate/index.d.ts +2 -10
  58. package/dist/types/features/ajax/aggregate/index.d.ts.map +1 -1
  59. package/dist/types/features/generic_events/aggregate/index.d.ts +5 -11
  60. package/dist/types/features/generic_events/aggregate/index.d.ts.map +1 -1
  61. package/dist/types/features/jserrors/aggregate/index.d.ts +4 -7
  62. package/dist/types/features/jserrors/aggregate/index.d.ts.map +1 -1
  63. package/dist/types/features/logging/aggregate/index.d.ts +10 -28
  64. package/dist/types/features/logging/aggregate/index.d.ts.map +1 -1
  65. package/dist/types/features/metrics/aggregate/index.d.ts.map +1 -1
  66. package/dist/types/features/page_view_timing/aggregate/index.d.ts +1 -9
  67. package/dist/types/features/page_view_timing/aggregate/index.d.ts.map +1 -1
  68. package/dist/types/features/session_replay/aggregate/index.d.ts +3 -4
  69. package/dist/types/features/session_replay/aggregate/index.d.ts.map +1 -1
  70. package/dist/types/features/session_replay/shared/recorder-events.d.ts +1 -1
  71. package/dist/types/features/session_replay/shared/recorder-events.d.ts.map +1 -1
  72. package/dist/types/features/session_replay/shared/recorder.d.ts +1 -1
  73. package/dist/types/features/session_trace/aggregate/index.d.ts +17 -19
  74. package/dist/types/features/session_trace/aggregate/index.d.ts.map +1 -1
  75. package/dist/types/features/session_trace/aggregate/trace/storage.d.ts +10 -6
  76. package/dist/types/features/session_trace/aggregate/trace/storage.d.ts.map +1 -1
  77. package/dist/types/features/soft_navigations/aggregate/index.d.ts +3 -9
  78. package/dist/types/features/soft_navigations/aggregate/index.d.ts.map +1 -1
  79. package/dist/types/features/soft_navigations/instrument/index.d.ts.map +1 -1
  80. package/dist/types/features/spa/aggregate/index.d.ts +2 -3
  81. package/dist/types/features/spa/aggregate/index.d.ts.map +1 -1
  82. package/dist/types/features/utils/aggregate-base.d.ts +14 -0
  83. package/dist/types/features/utils/aggregate-base.d.ts.map +1 -1
  84. package/dist/types/features/utils/event-buffer.d.ts +19 -56
  85. package/dist/types/features/utils/event-buffer.d.ts.map +1 -1
  86. package/dist/types/loaders/features/features.d.ts +3 -0
  87. package/dist/types/loaders/features/features.d.ts.map +1 -1
  88. package/package.json +1 -1
  89. package/src/common/aggregate/aggregator.js +22 -32
  90. package/src/common/aggregate/event-aggregator.js +76 -0
  91. package/src/common/harvest/harvest-scheduler.js +1 -1
  92. package/src/common/harvest/harvest.js +1 -5
  93. package/src/common/harvest/types.js +0 -1
  94. package/src/features/ajax/aggregate/index.js +60 -67
  95. package/src/features/generic_events/aggregate/index.js +14 -39
  96. package/src/features/jserrors/aggregate/index.js +21 -77
  97. package/src/features/logging/aggregate/index.js +46 -60
  98. package/src/features/metrics/aggregate/index.js +6 -4
  99. package/src/features/page_view_timing/aggregate/index.js +9 -30
  100. package/src/features/session_replay/aggregate/index.js +10 -14
  101. package/src/features/session_replay/shared/recorder-events.js +2 -2
  102. package/src/features/session_trace/aggregate/index.js +64 -73
  103. package/src/features/session_trace/aggregate/trace/storage.js +25 -14
  104. package/src/features/soft_navigations/aggregate/index.js +11 -22
  105. package/src/features/soft_navigations/instrument/index.js +6 -9
  106. package/src/features/spa/aggregate/index.js +12 -27
  107. package/src/features/utils/aggregate-base.js +39 -0
  108. package/src/features/utils/event-buffer.js +36 -83
  109. package/src/features/utils/instrument-base.js +3 -3
  110. package/src/loaders/features/features.js +13 -0
  111. package/dist/cjs/features/ajax/aggregate/chunk.js +0 -51
  112. package/dist/esm/features/ajax/aggregate/chunk.js +0 -44
  113. package/dist/types/features/ajax/aggregate/chunk.d.ts +0 -8
  114. package/dist/types/features/ajax/aggregate/chunk.d.ts.map +0 -1
  115. package/src/features/ajax/aggregate/chunk.js +0 -52
@@ -1 +1 @@
1
- {"version":3,"file":"storage.d.ts","sourceRoot":"","sources":["../../../../../../src/features/session_trace/aggregate/trace/storage.js"],"names":[],"mappings":"AAyBA,+HAA+H;AAC/H;IAQE,yBAEC;IATD,kBAAa;IACb,UAAU;IACV,0BAA4B;IAC5B,wBAAmB;IACnB,mBAAgB;IAChB,2BAA4B;IAG1B,YAAoB;IAGtB,gGAAgG;IAChG,yBAiBC;IAED;;;;OAIG;IACH,2BAHW,MAAM,GACJ,MAAM,CAsBlB;IAED,oEAAoE;IACpE;;;;;;;;MAsBC;IAED,mEA6BC;IAED,oDAOC;IAED,mEAuBC;IAGD,uEAcC;IAED,oDAKC;IAED,wBAwBC;IAED,uCAuBC;IAGD,gDAEC;IAID,qCAaC;IAGD,qEAGC;IAGD,mEAGC;IAED,iDAKC;;CACF"}
1
+ {"version":3,"file":"storage.d.ts","sourceRoot":"","sources":["../../../../../../src/features/session_trace/aggregate/trace/storage.js"],"names":[],"mappings":"AAyBA,+HAA+H;AAC/H;IAQE,yBAEC;IATD,kBAAa;IACb,UAAU;IACV,0BAA4B;IAC5B,wBAAmB;IACnB,2BAA4B;IAI1B,YAAoB;IAGtB,gGAAgG;IAChG,yBAcC;IAED;;;;OAIG;IACH,2BAHW,MAAM,GACJ,MAAM,CAsBlB;IAED,oEAAoE;IACpE;;;;MAgBC;IAED,mEA6BC;IAED,oDAOC;IAED,mEAuBC;IAGD,uEAcC;IAED,oDAKC;IAED,wBAwBC;IAED,uCAuBC;IAGD,gDAEC;IAID,qCAaC;IAGD,qEAGC;IAGD,mEAGC;IAGD,mBAEC;IAED,aAEC;IAED;;;;MAAmB;IAEnB,cAMC;IAED,mBAEC;IAED,kBAEC;;CACF"}
@@ -3,17 +3,12 @@ export class Aggregate extends AggregateBase {
3
3
  constructor(agentRef: any, { domObserver }: {
4
4
  domObserver: any;
5
5
  });
6
- interactionsToHarvest: EventBuffer;
6
+ interactionsToHarvest: any;
7
7
  domObserver: any;
8
8
  initialPageLoadInteraction: InitialPageLoadInteraction;
9
9
  latestRouteSetByApi: any;
10
10
  interactionInProgress: Interaction | null;
11
- onHarvestStarted(options: any): {
12
- body: {
13
- e: string;
14
- };
15
- } | undefined;
16
- onHarvestFinished(result: any): void;
11
+ serializer(eventBuffer: any): string;
17
12
  startUIInteraction(eventName: any, startedAt: any, sourceElem: any): void;
18
13
  setClosureHandlers(): void;
19
14
  /**
@@ -24,11 +19,10 @@ export class Aggregate extends AggregateBase {
24
19
  * @param {DOMHighResTimeStamp} timestamp
25
20
  * @returns An {@link Interaction} or undefined, if no active interaction was found.
26
21
  */
27
- getInteractionFor(timestamp: DOMHighResTimeStamp): Object | undefined;
22
+ getInteractionFor(timestamp: DOMHighResTimeStamp): any;
28
23
  #private;
29
24
  }
30
25
  import { AggregateBase } from '../../utils/aggregate-base';
31
- import { EventBuffer } from '../../utils/event-buffer';
32
26
  import { InitialPageLoadInteraction } from './initial-page-load-interaction';
33
27
  import { Interaction } from './interaction';
34
28
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/features/soft_navigations/aggregate/index.js"],"names":[],"mappings":"AAcA;IACE,2BAAiC;IACjC;;OAkDC;IA9CC,mCAA8C;IAC9C,iBAA8B;IAE9B,uDAA0F;IAW1F,yBAA+B;IAC/B,0CAAiC;IAiCnC;;;;kBAgBC;IAED,qCAGC;IAED,0EAeC;IAED,2BAiBC;IAED;;;;;;;OAOG;IACH,6BAHW,mBAAmB,sBAoB7B;;CA2FF;8BA5O6B,4BAA4B;4BAC9B,0BAA0B;2CAGX,iCAAiC;4BAChD,eAAe"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/features/soft_navigations/aggregate/index.js"],"names":[],"mappings":"AAaA;IACE,2BAAiC;IACjC;;OAkDC;IA9CC,2BAAwC;IACxC,iBAA8B;IAE9B,uDAA0F;IAW1F,yBAA+B;IAC/B,0CAAiC;IAiCnC,qCAUC;IAED,0EAeC;IAED,2BAiBC;IAED;;;;;;;OAOG;IACH,6BAHW,mBAAmB,OAqB7B;;CA2FF;8BAjO6B,4BAA4B;2CAGf,iCAAiC;4BAChD,eAAe"}
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/features/soft_navigations/instrument/index.js"],"names":[],"mappings":"AAkBA;IACE,2BAAiC;IACjC,2CA+CC;IAnCG,2CAA0C;CAoC/C;AAED,wCAAiC;+BA/DF,6BAA6B"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/features/soft_navigations/instrument/index.js"],"names":[],"mappings":"AAiBA;IACE,2BAAiC;IACjC,2CA6CC;IA5BG,2CAA0C;CA6B/C;AAED,wCAAiC;+BA7DF,6BAA6B"}
@@ -15,12 +15,11 @@ export class Aggregate extends AggregateBase {
15
15
  childTime: number;
16
16
  depth: number;
17
17
  harvestTimeSeconds: any;
18
- interactionsToHarvest: EventBuffer;
19
18
  disableSpaFix: boolean;
20
19
  };
21
- serializer: Serializer;
20
+ spaSerializerClass: Serializer;
21
+ serializer(eventBuffer: any): string;
22
22
  }
23
23
  import { AggregateBase } from '../../utils/aggregate-base';
24
- import { EventBuffer } from '../../utils/event-buffer';
25
24
  import { Serializer } from './serializer';
26
25
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/features/spa/aggregate/index.js"],"names":[],"mappings":"AA+BA;IACE,2BAAiC;IACjC,2BA4sBC;IAzsBC;;;;;;;;;;;;;;;;MAiBC;IAGD,uBAAsC;CAsrBzC;8BA7tB6B,4BAA4B;4BAQ9B,0BAA0B;2BAZ3B,cAAc"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/features/spa/aggregate/index.js"],"names":[],"mappings":"AA8BA;IACE,2BAAiC;IACjC,2BA0rBC;IAvrBe;;;;;;;;;;;;;;;MAgBb;IACD,+BAA8C;IAwqBhD,qCAEC;CACF;8BA9sB6B,4BAA4B;2BAJ/B,cAAc"}
@@ -1,5 +1,6 @@
1
1
  export class AggregateBase extends FeatureBase {
2
2
  agentRef: any;
3
+ events: any;
3
4
  obfuscator: any;
4
5
  /**
5
6
  * New handler for waiting for multiple flags. Useful when expecting multiple flags simultaneously (ex. stn vs sr)
@@ -9,6 +10,19 @@ export class AggregateBase extends FeatureBase {
9
10
  waitForFlags(flagNames?: string[]): Promise<any>;
10
11
  drain(): void;
11
12
  drained: boolean | undefined;
13
+ /**
14
+ * Return harvest payload. A "serializer" function can be defined on a derived class to format the payload.
15
+ * @param {Boolean} shouldRetryOnFail - harvester flag to backup payload for retry later if harvest request fails; this should be moved to harvester logic
16
+ * @returns final payload, or undefined if there are no pending events
17
+ */
18
+ makeHarvestPayload(shouldRetryOnFail?: boolean, opts?: {}): {
19
+ body: any;
20
+ } | undefined;
21
+ /**
22
+ * Cleanup task after a harvest.
23
+ * @param {Boolean} harvestFailed - harvester flag to restore events in main buffer for retry later if request failed
24
+ */
25
+ postHarvestCleanup(harvestFailed?: boolean, opts?: {}): void;
12
26
  /**
13
27
  * Checks for additional `jsAttributes` items to support backward compatibility with implementations of the agent where
14
28
  * loader configurations may appear after the loader code is executed.
@@ -1 +1 @@
1
- {"version":3,"file":"aggregate-base.d.ts","sourceRoot":"","sources":["../../../../src/features/utils/aggregate-base.js"],"names":[],"mappings":"AAQA;IAGI,cAAwB;IAExB,gBAA6C;IAG/C;;;;OAIG;IACH,yBAHW,MAAM,EAAE,gBAwBlB;IAED,cAGC;IADC,6BAAmB;IAGrB;;;OAGG;IACH,6CA0BC;CACF;4BAhF2B,gBAAgB"}
1
+ {"version":3,"file":"aggregate-base.d.ts","sourceRoot":"","sources":["../../../../src/features/utils/aggregate-base.js"],"names":[],"mappings":"AAUA;IAGI,cAAwB;IAEwD,YAAuC;IAIvH,gBAA6C;IAG/C;;;;OAIG;IACH,yBAHW,MAAM,EAAE,gBAwBlB;IAED,cAGC;IADC,6BAAmB;IAGrB;;;;OAIG;IACH;;kBAiBC;IAED;;;OAGG;IACH,6DAGC;IAED;;;OAGG;IACH,6CA0BC;CACF;4BAvH2B,gBAAgB"}
@@ -1,73 +1,36 @@
1
- /**
2
- * A container that keeps an event buffer and size with helper methods
3
- * @typedef {Object} EventBuffer
4
- * @property {number} size
5
- * @property {*[]} buffer
6
- */
7
- /**
8
- * A container that holds, evaluates, and merges event objects for harvesting
9
- */
10
1
  export class EventBuffer {
11
2
  /**
12
- *
13
- * @param {number=} maxPayloadSize
3
+ * @param {number} maxPayloadSize
14
4
  */
15
- constructor(maxPayloadSize?: number | undefined);
5
+ constructor(maxPayloadSize?: number);
16
6
  maxPayloadSize: number;
7
+ isEmpty(): boolean;
8
+ get(): any[];
9
+ byteSize(): number;
10
+ wouldExceedMaxSize(incomingSize: any): boolean;
17
11
  /**
18
- * buffer is read only, use the helper methods to add or clear buffer data
12
+ * Add feature-processed event to our buffer. If this event would cause our total raw size to exceed the set max payload size, it is dropped.
13
+ * @param {any} event - any primitive type or object
14
+ * @returns {Boolean} true if successfully added; false otherwise
19
15
  */
20
- get buffer(): Object[];
16
+ add(event: any): boolean;
21
17
  /**
22
- * bytes is read only, use the helper methods to add or clear buffer data
18
+ * Wipes the main buffer
23
19
  */
24
- get bytes(): number;
20
+ clear(): void;
25
21
  /**
26
- * held is another event buffer
22
+ * Backup the buffered data and clear the main buffer
23
+ * @returns {Array} the events being backed up
27
24
  */
28
- get held(): EventBuffer;
25
+ save(): any[];
29
26
  /**
30
- * Returns a boolean indicating whether the current size and buffer contain valid data
31
- * @returns {boolean}
27
+ * Wipes the backup buffer
32
28
  */
33
- get hasData(): boolean;
29
+ clearSave(): void;
34
30
  /**
35
- * Adds an event object to the buffer while tallying size. Only adds the event if it is valid
36
- * and would not make the event buffer exceed the maxPayloadSize.
37
- * @param {Object} event the event object to add to the buffer
38
- * @returns {EventBuffer} returns the event buffer for chaining
31
+ * Prepend the backup buffer back into the main buffer
39
32
  */
40
- add(event: Object): EventBuffer;
41
- /**
42
- * clear the buffer data
43
- * @returns {EventBuffer}
44
- */
45
- clear(): EventBuffer;
46
- /**
47
- * Hold the buffer data in a new (child) EventBuffer (.held) to unblock the main buffer.
48
- * This action clears the main buffer
49
- * @returns {EventBuffer}
50
- */
51
- hold(): EventBuffer;
52
- /**
53
- * Prepend the held EventBuffer (.held) back into the main buffer
54
- * This action clears the held buffer
55
- * @returns {EventBuffer}
56
- */
57
- unhold(): EventBuffer;
58
- /**
59
- * Merges an EventBuffer into this EventBuffer
60
- * @param {EventBuffer} events an EventBuffer intended to merge with this EventBuffer
61
- * @param {boolean} prepend if true, the supplied events will be prepended before the events of this class
62
- * @returns {EventBuffer} returns the event buffer for chaining
63
- */
64
- merge(eventBuffer: any, prepend?: boolean): EventBuffer;
65
- /**
66
- * Returns a boolean indicating the resulting size of a merge would be valid. Compares against the maxPayloadSize provided at initialization time.
67
- * @param {number} size
68
- * @returns {boolean}
69
- */
70
- canMerge(size: number): boolean;
33
+ reloadSave(): void;
71
34
  #private;
72
35
  }
73
36
  //# sourceMappingURL=event-buffer.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"event-buffer.d.ts","sourceRoot":"","sources":["../../../../src/features/utils/event-buffer.js"],"names":[],"mappings":"AAGA;;;;;GAKG;AAEH;;GAEG;AACH;IAQE;;;OAGG;IACH,6BAFW,MAAM,YAAC,EAIjB;IADC,uBAAoC;IAGtC;;OAEG;IACH,uBAEC;IAED;;OAEG;IACH,oBAEC;IAED;;OAEG;IACH,wBAGC;IAED;;;OAGG;IACH,uBAEC;IAED;;;;;OAKG;IACH,WAHW,MAAM,GACJ,WAAW,CAQvB;IAED;;;OAGG;IACH,SAFa,WAAW,CAMvB;IAED;;;;OAIG;IACH,QAFa,WAAW,CAMvB;IAED;;;;OAIG;IACH,UAFa,WAAW,CAMvB;IAED;;;;;OAKG;IACH,kCAHW,OAAO,GACL,WAAW,CAOvB;IAED;;;;OAIG;IACH,eAHW,MAAM,GACJ,OAAO,CAInB;;CACF"}
1
+ {"version":3,"file":"event-buffer.d.ts","sourceRoot":"","sources":["../../../../src/features/utils/event-buffer.js"],"names":[],"mappings":"AAGA;IAME;;OAEG;IACH,6BAFW,MAAM,EAIhB;IADC,uBAAoC;IAGtC,mBAEC;IAED,aAEC;IAED,mBAEC;IAED,+CAEC;IAED;;;;OAIG;IACH,WAHW,GAAG,WASb;IAED;;OAEG;IACH,cAGC;IAED;;;OAGG;IACH,cAGC;IAED;;OAEG;IACH,kBAGC;IAED;;OAEG;IACH,mBAKC;;CACF"}
@@ -19,4 +19,7 @@ export namespace FEATURE_NAMES {
19
19
  export const featurePriority: {
20
20
  [x: string]: number;
21
21
  };
22
+ export const FEATURE_TO_ENDPOINT: {
23
+ [x: string]: string;
24
+ };
22
25
  //# sourceMappingURL=features.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"features.d.ts","sourceRoot":"","sources":["../../../../src/loaders/features/features.js"],"names":[],"mappings":";;;;;;;;;;;;;;AAkBA;;;GAGG;AACH;;EAYC"}
1
+ {"version":3,"file":"features.d.ts","sourceRoot":"","sources":["../../../../src/loaders/features/features.js"],"names":[],"mappings":";;;;;;;;;;;;;;AAkBA;;;GAGG;AACH;;EAYC;AAED;;EAWC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@newrelic/browser-agent",
3
- "version": "1.272.0",
3
+ "version": "1.273.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,12 +2,8 @@
2
2
  * Copyright 2020 New Relic Corporation. All rights reserved.
3
3
  * SPDX-License-Identifier: Apache-2.0
4
4
  */
5
-
6
- import { SharedContext } from '../context/shared-context'
7
-
8
- export class Aggregator extends SharedContext {
9
- constructor (parent) {
10
- super(parent)
5
+ export class Aggregator {
6
+ constructor () {
11
7
  this.aggregatedData = {}
12
8
  }
13
9
 
@@ -16,13 +12,14 @@ export class Aggregator extends SharedContext {
16
12
  // metrics are the numeric values to be aggregated
17
13
 
18
14
  store (type, name, params, newMetrics, customParams) {
19
- var bucket = this.getBucket(type, name, params, customParams)
15
+ var bucket = this.#getBucket(type, name, params, customParams)
20
16
  bucket.metrics = aggregateMetrics(newMetrics, bucket.metrics)
21
17
  return bucket
22
18
  }
23
19
 
24
- merge (type, name, metrics, params, customParams) {
25
- var bucket = this.getBucket(type, name, params, customParams)
20
+ merge (type, name, metrics, params, customParams, overwriteParams = false) {
21
+ var bucket = this.#getBucket(type, name, params, customParams)
22
+ if (overwriteParams) bucket.params = params // replace current params with incoming params obj
26
23
 
27
24
  if (!bucket.metrics) {
28
25
  bucket.metrics = metrics
@@ -50,32 +47,13 @@ export class Aggregator extends SharedContext {
50
47
  }
51
48
 
52
49
  storeMetric (type, name, params, value) {
53
- var bucket = this.getBucket(type, name, params)
50
+ var bucket = this.#getBucket(type, name, params)
54
51
  bucket.stats = updateMetric(value, bucket.stats)
55
52
  return bucket
56
53
  }
57
54
 
58
- getBucket (type, name, params, customParams) {
59
- if (!this.aggregatedData[type]) this.aggregatedData[type] = {}
60
- var bucket = this.aggregatedData[type][name]
61
- if (!bucket) {
62
- bucket = this.aggregatedData[type][name] = { params: params || {} }
63
- if (customParams) {
64
- bucket.custom = customParams
65
- }
66
- }
67
- return bucket
68
- }
69
-
70
- get (type, name) {
71
- // if name is passed, get a single bucket
72
- if (name) return this.aggregatedData[type] && this.aggregatedData[type][name]
73
- // else, get all buckets of that type
74
- return this.aggregatedData[type]
75
- }
76
-
77
- // Like get, but for many types and it deletes the retrieved content from the aggregatedData
78
- take (types) {
55
+ // Get all listed types buckets and it deletes the retrieved content from the aggregatedData
56
+ take (types, deleteWhenRetrieved = true) {
79
57
  var results = {}
80
58
  var type = ''
81
59
  var hasData = false
@@ -84,10 +62,22 @@ export class Aggregator extends SharedContext {
84
62
  results[type] = Object.values(this.aggregatedData[type] || {})
85
63
 
86
64
  if (results[type].length) hasData = true
87
- delete this.aggregatedData[type]
65
+ if (deleteWhenRetrieved) delete this.aggregatedData[type]
88
66
  }
89
67
  return hasData ? results : null
90
68
  }
69
+
70
+ #getBucket (type, name, params, customParams) {
71
+ if (!this.aggregatedData[type]) this.aggregatedData[type] = {}
72
+ var bucket = this.aggregatedData[type][name]
73
+ if (!bucket) {
74
+ bucket = this.aggregatedData[type][name] = { params: params || {} }
75
+ if (customParams) {
76
+ bucket.custom = customParams
77
+ }
78
+ }
79
+ return bucket
80
+ }
91
81
  }
92
82
 
93
83
  function aggregateMetrics (newMetrics, oldMetrics) {
@@ -0,0 +1,76 @@
1
+ import { Aggregator } from './aggregator'
2
+
3
+ /**
4
+ * An extension of the Aggregator class that provides an interface similar to that of EventBuffer class.
5
+ * This typecasting allow features that uses Aggregator as their event handler to share the same AggregateBase.events utilization by those features.
6
+ */
7
+ export class EventAggregator {
8
+ #aggregator = new Aggregator()
9
+ #savedNamesToBuckets = {}
10
+
11
+ isEmpty ({ aggregatorTypes }) {
12
+ if (!aggregatorTypes) return Object.keys(this.#aggregator.aggregatedData).length === 0
13
+ return aggregatorTypes.every(type => !this.#aggregator.aggregatedData[type]) // no bucket exist for any of the types we're looking for
14
+ }
15
+
16
+ add (type, name, params, newMetrics, customParams) {
17
+ // Do we need to track byte size here like EventBuffer?
18
+ this.#aggregator.store(type, name, params, newMetrics, customParams)
19
+ return true
20
+ }
21
+
22
+ addMetric (type, name, params, value) {
23
+ this.#aggregator.storeMetric(type, name, params, value)
24
+ return true
25
+ }
26
+
27
+ save ({ aggregatorTypes }) {
28
+ const key = aggregatorTypes.toString() // the stringified types serve as the key to each save call, e.g. ['err', 'ierr', 'xhr'] => 'err,ierr,xhr'
29
+ const backupAggregatedDataSubset = {}
30
+ aggregatorTypes.forEach(type => (backupAggregatedDataSubset[type] = this.#aggregator.aggregatedData[type])) // make a subset of the aggregatedData for each of the types we want to save
31
+ this.#savedNamesToBuckets[key] = backupAggregatedDataSubset
32
+ /*
33
+ { 'err,ierr,xhr': {
34
+ 'err': {
35
+ <aggregateHash>: { metrics: { count: 1, time, ... }, params: {}, custom: {} },
36
+ <otherHashName>: { metrics: { count: 1, ... }, ... }
37
+ },
38
+ 'ierr': { ... },
39
+ 'xhr': { ... }
40
+ }
41
+ }
42
+ */
43
+ }
44
+
45
+ get (opts) {
46
+ const aggregatorTypes = Array.isArray(opts) ? opts : opts.aggregatorTypes
47
+ return this.#aggregator.take(aggregatorTypes, false)
48
+ }
49
+
50
+ clear ({ aggregatorTypes } = {}) {
51
+ if (!aggregatorTypes) {
52
+ this.#aggregator.aggregatedData = {}
53
+ return
54
+ }
55
+ aggregatorTypes.forEach(type => delete this.#aggregator.aggregatedData[type])
56
+ }
57
+
58
+ reloadSave ({ aggregatorTypes }) {
59
+ const key = aggregatorTypes.toString()
60
+ const backupAggregatedDataSubset = this.#savedNamesToBuckets[key]
61
+ // Grabs the previously stored subset and merge it back into aggregatedData.
62
+ aggregatorTypes.forEach(type => {
63
+ Object.keys(backupAggregatedDataSubset[type] || {}).forEach(name => {
64
+ const bucket = backupAggregatedDataSubset[type][name]
65
+ // The older aka saved params take effect over the newer one. This is especially important when merging back for a failed harvest retry if, for example,
66
+ // the first-ever occurrence of an error is in the retry: it contains the params.stack_trace whereas the newer or current bucket.params would not.
67
+ this.#aggregator.merge(type, name, bucket.metrics, bucket.params, bucket.custom, true)
68
+ })
69
+ })
70
+ }
71
+
72
+ clearSave ({ aggregatorTypes }) {
73
+ const key = aggregatorTypes.toString()
74
+ delete this.#savedNamesToBuckets[key]
75
+ }
76
+ }
@@ -98,7 +98,7 @@ export class HarvestScheduler extends SharedContext {
98
98
  let payload
99
99
 
100
100
  if (this.opts.getPayload) {
101
- // Ajax & PVT & SR features provide a callback function to get data for harvesting
101
+ // Ajax, PVT, Softnav, Logging, SR & ST features provide a single callback function to get data for harvesting
102
102
  submitMethod = submitData.getSubmitMethod({ isFinalHarvest: opts?.unload })
103
103
  if (!submitMethod) return false
104
104
 
@@ -106,11 +106,7 @@ export class Harvest extends SharedContext {
106
106
  const gzip = !!qs?.attributes?.includes('gzip')
107
107
 
108
108
  if (!gzip) {
109
- if (endpoint === 'events') {
110
- body = body.e
111
- } else {
112
- body = stringify(body)
113
- }
109
+ if (endpoint !== 'events') body = stringify(body) // all features going to /events/ endpoint should already be serialized & stringified
114
110
  /** Warn --once per endpoint-- if the agent tries to send large payloads */
115
111
  if (body.length > 750000 && (warnings[endpoint] = (warnings?.[endpoint] || 0) + 1) === 1) warn(28, endpoint)
116
112
  }
@@ -12,7 +12,6 @@
12
12
  * @typedef {object} HarvestPayload
13
13
  * @property {object} qs Map of values that should be sent as part of the request query string.
14
14
  * @property {object} body Map of values that should be sent as the body of the request.
15
- * @property {string} body.e Special case of body used for browser interactions.
16
15
  */
17
16
 
18
17
  /**
@@ -8,13 +8,11 @@ import { handle } from '../../../common/event-emitter/handle'
8
8
  import { HarvestScheduler } from '../../../common/harvest/harvest-scheduler'
9
9
  import { setDenyList, shouldCollectEvent } from '../../../common/deny-list/deny-list'
10
10
  import { FEATURE_NAME } from '../constants'
11
- import { FEATURE_NAMES } from '../../../loaders/features/features'
11
+ import { FEATURE_NAMES, FEATURE_TO_ENDPOINT } from '../../../loaders/features/features'
12
12
  import { SUPPORTABILITY_METRIC_CHANNEL } from '../../metrics/constants'
13
13
  import { AggregateBase } from '../../utils/aggregate-base'
14
14
  import { parseGQL } from './gql'
15
- import { getNREUMInitializedAgent } from '../../../common/window/nreum'
16
- import Chunk from './chunk'
17
- import { EventBuffer } from '../../utils/event-buffer'
15
+ import { nullable, numeric, getAddStringContext, addCustomAttributes } from '../../../common/serialize/bel-serializer'
18
16
 
19
17
  export class Aggregate extends AggregateBase {
20
18
  static featureName = FEATURE_NAME
@@ -25,31 +23,30 @@ export class Aggregate extends AggregateBase {
25
23
  const harvestTimeSeconds = agentRef.init.ajax.harvestTimeSeconds || 10
26
24
  setDenyList(agentRef.runtime.denyList)
27
25
 
28
- this.ajaxEvents = new EventBuffer()
29
- this.spaAjaxEvents = {}
26
+ this.underSpaEvents = {}
30
27
  const classThis = this
31
28
 
32
29
  // --- v Used by old spa feature
33
30
  this.ee.on('interactionDone', (interaction, wasSaved) => {
34
- if (!this.spaAjaxEvents[interaction.id]?.hasData) return
31
+ if (!this.underSpaEvents[interaction.id]) return
35
32
 
36
33
  if (!wasSaved) { // if the ixn was saved, then its ajax reqs are part of the payload whereas if it was discarded, it should still be harvested in the ajax feature itself
37
- this.ajaxEvents.merge(this.spaAjaxEvents[interaction.id])
34
+ this.underSpaEvents[interaction.id].forEach((item) => this.events.add(item))
38
35
  }
39
- delete this.spaAjaxEvents[interaction.id]
36
+ delete this.underSpaEvents[interaction.id]
40
37
  })
41
38
  // --- ^
42
39
  // --- v Used by new soft nav
43
- registerHandler('returnAjax', event => this.ajaxEvents.add(event), this.featureName, this.ee)
40
+ registerHandler('returnAjax', event => this.events.add(event), this.featureName, this.ee)
44
41
  // --- ^
45
42
  registerHandler('xhr', function () { // the EE-drain system not only switches "this" but also passes a new EventContext with info. Should consider platform refactor to another system which passes a mutable context around separately and predictably to avoid problems like this.
46
43
  classThis.storeXhr(...arguments, this) // this switches the context back to the class instance while passing the NR context as an argument -- see "ctx" in storeXhr
47
44
  }, this.featureName, this.ee)
48
45
 
49
46
  this.waitForFlags(([])).then(() => {
50
- const scheduler = new HarvestScheduler('events', {
51
- onFinished: this.onEventsHarvestFinished.bind(this),
52
- getPayload: this.prepareHarvest.bind(this)
47
+ const scheduler = new HarvestScheduler(FEATURE_TO_ENDPOINT[this.featureName], {
48
+ onFinished: (result) => this.postHarvestCleanup(result.sent && result.retry),
49
+ getPayload: (options) => this.makeHarvestPayload(options.retry)
53
50
  }, this)
54
51
  scheduler.startTimer(harvestTimeSeconds)
55
52
  this.drain()
@@ -69,10 +66,11 @@ export class Aggregate extends AggregateBase {
69
66
 
70
67
  const shouldCollect = shouldCollectEvent(params)
71
68
  const shouldOmitAjaxMetrics = this.agentRef.init.feature_flags?.includes('ajax_metrics_deny_list')
69
+ const jserrorsInUse = Boolean(this.agentRef.features?.[FEATURE_NAMES.jserrors])
72
70
 
73
- // store for timeslice metric (harvested by jserrors feature)
74
- if (shouldCollect || !shouldOmitAjaxMetrics) {
75
- this.agentRef.sharedAggregator.store('xhr', hash, params, metrics)
71
+ // Report ajax timeslice metric (to be harvested by jserrors feature, but only if it's running).
72
+ if (jserrorsInUse && (shouldCollect || !shouldOmitAjaxMetrics)) {
73
+ this.agentRef.sharedAggregator.add('xhr', hash, params, metrics)
76
74
  }
77
75
 
78
76
  if (!shouldCollect) {
@@ -119,66 +117,61 @@ export class Aggregate extends AggregateBase {
119
117
  })
120
118
  if (event.gql) handle(SUPPORTABILITY_METRIC_CHANNEL, ['Ajax/Events/GraphQL/Bytes-Added', stringify(event.gql).length], undefined, FEATURE_NAMES.metrics, this.ee)
121
119
 
122
- const softNavInUse = Boolean(getNREUMInitializedAgent(this.agentIdentifier)?.features?.[FEATURE_NAMES.softNav])
120
+ const softNavInUse = Boolean(this.agentRef.features?.[FEATURE_NAMES.softNav])
123
121
  if (softNavInUse) { // For newer soft nav (when running), pass the event to it for evaluation -- either part of an interaction or is given back
124
122
  handle('ajax', [event], undefined, FEATURE_NAMES.softNav, this.ee)
125
123
  } else if (ctx.spaNode) { // For old spa (when running), if the ajax happened inside an interaction, hold it until the interaction finishes
126
124
  const interactionId = ctx.spaNode.interaction.id
127
- this.spaAjaxEvents[interactionId] ??= new EventBuffer()
128
- this.spaAjaxEvents[interactionId].add(event)
125
+ this.underSpaEvents[interactionId] ??= []
126
+ this.underSpaEvents[interactionId].push(event)
129
127
  } else {
130
- this.ajaxEvents.add(event)
128
+ this.events.add(event)
131
129
  }
132
130
  }
133
131
 
134
- prepareHarvest (options) {
135
- options = options || {}
136
- if (this.ajaxEvents.buffer.length === 0) return null
137
-
138
- const payload = this.#getPayload(this.ajaxEvents.buffer)
139
- const payloadObjs = []
140
-
141
- for (let i = 0; i < payload.length; i++) payloadObjs.push({ body: { e: payload[i] } })
142
-
143
- if (options.retry) this.ajaxEvents.hold()
144
- else this.ajaxEvents.clear()
145
-
146
- return payloadObjs
147
- }
148
-
149
- onEventsHarvestFinished (result) {
150
- if (result.retry && this.ajaxEvents.held.hasData) this.ajaxEvents.unhold()
151
- else this.ajaxEvents.held.clear()
152
- }
153
-
154
- #getPayload (events, numberOfChunks) {
155
- numberOfChunks = numberOfChunks || 1
156
- const payload = []
157
- const chunkSize = events.length / numberOfChunks
158
- const eventChunks = splitChunks.call(this, events, chunkSize)
159
- let tooBig = false
160
- for (let i = 0; i < eventChunks.length; i++) {
161
- const currentChunk = eventChunks[i]
162
- if (currentChunk.tooBig) {
163
- if (currentChunk.events.length > 1) {
164
- tooBig = true
165
- break // if the payload is too big BUT is made of more than 1 event, we can split it down again
166
- }
167
- // Otherwise, if it consists of one sole event, we do not send it (discarded) since we cannot break it apart any further.
168
- } else {
169
- payload.push(currentChunk.payload)
170
- }
171
- }
172
- // Check if the current payload string is too big, if so then run getPayload again with more buckets.
173
- return tooBig ? this.#getPayload(events, ++numberOfChunks) : payload
174
-
175
- function splitChunks (arr, chunkSize) {
176
- chunkSize = chunkSize || arr.length
177
- const chunks = []
178
- for (let i = 0, len = arr.length; i < len; i += chunkSize) {
179
- chunks.push(new Chunk(arr.slice(i, i + chunkSize), this))
132
+ serializer (eventBuffer) {
133
+ const addString = getAddStringContext(this.agentIdentifier)
134
+ let payload = 'bel.7;'
135
+
136
+ for (let i = 0; i < eventBuffer.length; i++) {
137
+ const event = eventBuffer[i]
138
+ const fields = [
139
+ numeric(event.startTime),
140
+ numeric(event.endTime - event.startTime),
141
+ numeric(0), // callbackEnd
142
+ numeric(0), // no callbackDuration for non-SPA events
143
+ addString(event.method),
144
+ numeric(event.status),
145
+ addString(event.domain),
146
+ addString(event.path),
147
+ numeric(event.requestSize),
148
+ numeric(event.responseSize),
149
+ event.type === 'fetch' ? 1 : '',
150
+ addString(0), // nodeId
151
+ nullable(event.spanId, addString, true) + // guid
152
+ nullable(event.traceId, addString, true) + // traceId
153
+ nullable(event.spanTimestamp, numeric, false) // timestamp
154
+ ]
155
+
156
+ let insert = '2,'
157
+
158
+ // Since configuration objects (like info) are created new each time they are set, we have to grab the current pointer to the attr object here.
159
+ const jsAttributes = this.agentRef.info.jsAttributes
160
+
161
+ // add custom attributes
162
+ // gql decorators are added as custom attributes to alleviate need for new BEL schema
163
+ const attrParts = addCustomAttributes({ ...(jsAttributes || {}), ...(event.gql || {}) }, addString)
164
+ fields.unshift(numeric(attrParts.length))
165
+
166
+ insert += fields.join(',')
167
+ if (attrParts && attrParts.length > 0) {
168
+ insert += ';' + attrParts.join(';')
180
169
  }
181
- return chunks
170
+ if ((i + 1) < eventBuffer.length) insert += ';'
171
+
172
+ payload += insert
182
173
  }
174
+
175
+ return payload
183
176
  }
184
177
  }