@newrelic/browser-agent 1.274.0 → 1.275.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 (31) hide show
  1. package/CHANGELOG.md +13 -0
  2. package/dist/cjs/common/constants/env.cdn.js +1 -1
  3. package/dist/cjs/common/constants/env.npm.js +1 -1
  4. package/dist/cjs/common/harvest/harvest.js +21 -0
  5. package/dist/cjs/common/util/submit-data.js +42 -5
  6. package/dist/cjs/common/wrap/wrap-events.js +1 -1
  7. package/dist/cjs/common/wrap/wrap-logger.js +5 -1
  8. package/dist/cjs/common/wrap/wrap-xhr.js +1 -0
  9. package/dist/cjs/features/generic_events/aggregate/index.js +10 -10
  10. package/dist/esm/common/constants/env.cdn.js +1 -1
  11. package/dist/esm/common/constants/env.npm.js +1 -1
  12. package/dist/esm/common/harvest/harvest.js +21 -0
  13. package/dist/esm/common/util/submit-data.js +41 -5
  14. package/dist/esm/common/wrap/wrap-events.js +1 -1
  15. package/dist/esm/common/wrap/wrap-logger.js +5 -2
  16. package/dist/esm/common/wrap/wrap-xhr.js +1 -0
  17. package/dist/esm/features/generic_events/aggregate/index.js +10 -10
  18. package/dist/types/common/harvest/harvest.d.ts.map +1 -1
  19. package/dist/types/common/util/submit-data.d.ts +18 -1
  20. package/dist/types/common/util/submit-data.d.ts.map +1 -1
  21. package/dist/types/common/wrap/wrap-logger.d.ts.map +1 -1
  22. package/dist/types/common/wrap/wrap-xhr.d.ts.map +1 -1
  23. package/dist/types/features/generic_events/aggregate/index.d.ts +0 -1
  24. package/dist/types/features/generic_events/aggregate/index.d.ts.map +1 -1
  25. package/package.json +1 -1
  26. package/src/common/harvest/harvest.js +19 -0
  27. package/src/common/util/submit-data.js +37 -4
  28. package/src/common/wrap/wrap-events.js +1 -1
  29. package/src/common/wrap/wrap-logger.js +7 -2
  30. package/src/common/wrap/wrap-xhr.js +2 -0
  31. package/src/features/generic_events/aggregate/index.js +10 -12
package/CHANGELOG.md CHANGED
@@ -3,6 +3,19 @@
3
3
  All notable changes to this project will be documented in this file.
4
4
  See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
5
5
 
6
+ ## [1.275.0](https://github.com/newrelic/newrelic-browser-agent/compare/v1.274.0...v1.275.0) (2024-12-03)
7
+
8
+
9
+ ### Features
10
+
11
+ * Allow logs api wrapper to update custom attributes ([#1265](https://github.com/newrelic/newrelic-browser-agent/issues/1265)) ([8d10e14](https://github.com/newrelic/newrelic-browser-agent/commit/8d10e14953f9a5b9ba97e865ba5476fc527ba384))
12
+ * Enable the browser agent to run in extension background contexts ([#1206](https://github.com/newrelic/newrelic-browser-agent/issues/1206)) ([37e976b](https://github.com/newrelic/newrelic-browser-agent/commit/37e976bf360c209efd163855e7fbe84d665e444b))
13
+
14
+
15
+ ### Bug Fixes
16
+
17
+ * Harvest generic events when max size is reached ([#1250](https://github.com/newrelic/newrelic-browser-agent/issues/1250)) ([e00a469](https://github.com/newrelic/newrelic-browser-agent/commit/e00a46975bcc93c48798bd9153f3a503998b0915))
18
+
6
19
  ## [1.274.0](https://github.com/newrelic/newrelic-browser-agent/compare/v1.273.1...v1.274.0) (2024-11-19)
7
20
 
8
21
 
@@ -12,7 +12,7 @@ exports.VERSION = exports.RRWEB_VERSION = exports.DIST_METHOD = exports.BUILD_EN
12
12
  /**
13
13
  * Exposes the version of the agent
14
14
  */
15
- const VERSION = exports.VERSION = "1.274.0";
15
+ const VERSION = exports.VERSION = "1.275.0";
16
16
 
17
17
  /**
18
18
  * Exposes the build type of the agent
@@ -12,7 +12,7 @@ exports.VERSION = exports.RRWEB_VERSION = exports.DIST_METHOD = exports.BUILD_EN
12
12
  /**
13
13
  * Exposes the version of the agent
14
14
  */
15
- const VERSION = exports.VERSION = "1.274.0";
15
+ const VERSION = exports.VERSION = "1.275.0";
16
16
 
17
17
  /**
18
18
  * Exposes the build type of the agent
@@ -174,6 +174,27 @@ class Harvest extends _sharedContext.SharedContext {
174
174
  }
175
175
  cbFinished(cbResult);
176
176
  }, (0, _eventListenerOpts.eventListenerOpts)(false));
177
+ } else if (!opts.unload && cbFinished && submitMethod === submitData.xhrFetch) {
178
+ const harvestScope = this;
179
+ result.then(async function (response) {
180
+ const status = response.status;
181
+ const cbResult = {
182
+ sent: true,
183
+ status,
184
+ fullUrl,
185
+ fetchResponse: response
186
+ };
187
+ if (response.status === 429) {
188
+ cbResult.retry = true;
189
+ cbResult.delay = harvestScope.tooManyRequestsDelay;
190
+ } else if (status === 408 || status === 500 || status === 503) {
191
+ cbResult.retry = true;
192
+ }
193
+ if (opts.needResponse) {
194
+ cbResult.responseText = await response.text();
195
+ }
196
+ cbFinished(cbResult);
197
+ });
177
198
  }
178
199
  return result;
179
200
  }
@@ -6,6 +6,7 @@ Object.defineProperty(exports, "__esModule", {
6
6
  exports.beacon = beacon;
7
7
  exports.getSubmitMethod = getSubmitMethod;
8
8
  exports.xhr = xhr;
9
+ exports.xhrFetch = xhrFetch;
9
10
  var _runtime = require("../constants/runtime");
10
11
  /**
11
12
  * @file Contains common methods used to transmit harvested data.
@@ -26,11 +27,47 @@ var _runtime = require("../constants/runtime");
26
27
  function getSubmitMethod({
27
28
  isFinalHarvest = false
28
29
  } = {}) {
29
- return isFinalHarvest && _runtime.isBrowserScope
30
- // Use sendBeacon for final harvest
31
- ? beacon
32
- // If not final harvest, or not browserScope, always use xhr post
33
- : xhr;
30
+ if (isFinalHarvest && _runtime.isBrowserScope) {
31
+ // Use sendBeacon for final harvest
32
+ return beacon;
33
+ }
34
+
35
+ // If not final harvest, or not browserScope, use XHR post if available
36
+ if (typeof XMLHttpRequest !== 'undefined') {
37
+ return xhr;
38
+ }
39
+
40
+ // Fallback for web workers where XMLHttpRequest is not available
41
+ return xhrFetch;
42
+ }
43
+
44
+ /**
45
+ *
46
+ * @param url
47
+ * @param body
48
+ * @param method
49
+ * @param headers
50
+ * @returns {Promise<Response>}
51
+ */
52
+ function xhrFetch({
53
+ url,
54
+ body = null,
55
+ method = 'POST',
56
+ headers = [{
57
+ key: 'content-type',
58
+ value: 'text/plain'
59
+ }]
60
+ }) {
61
+ const objHeaders = {};
62
+ for (const header of headers) {
63
+ objHeaders[header.key] = header.value;
64
+ }
65
+ return fetch(url, {
66
+ headers: objHeaders,
67
+ method,
68
+ body,
69
+ credentials: 'include'
70
+ });
34
71
  }
35
72
 
36
73
  /**
@@ -44,8 +44,8 @@ function wrapEvents(sharedEE) {
44
44
  // Guard against instrumenting environments w/o necessary features
45
45
  if ('getPrototypeOf' in Object) {
46
46
  if (_runtime.isBrowserScope) findEventListenerProtoAndCb(document, wrapNode);
47
+ if (XHR) findEventListenerProtoAndCb(XHR.prototype, wrapNode);
47
48
  findEventListenerProtoAndCb(_runtime.globalScope, wrapNode);
48
- findEventListenerProtoAndCb(XHR.prototype, wrapNode);
49
49
  }
50
50
  ee.on(ADD_EVENT_LISTENER + '-start', function (args, target) {
51
51
  var originalListener = args[1];
@@ -18,6 +18,8 @@ var _wrapFunction = require("./wrap-function");
18
18
  * This module is used by: jserrors, spa.
19
19
  */
20
20
 
21
+ const contextMap = new Map();
22
+
21
23
  /**
22
24
  * Wraps a supplied function and adds emitter events under the `-wrap-logger-` prefix
23
25
  * @param {Object} sharedEE - The shared event emitter on which a new scoped event emitter will be based.
@@ -38,9 +40,11 @@ function wrapLogger(sharedEE, parent, loggerFn, context) {
38
40
  const ctx = new _eventContext.EventContext(_contextualEe.contextId);
39
41
  ctx.level = context.level;
40
42
  ctx.customAttributes = context.customAttributes;
43
+ const contextLookupKey = parent[loggerFn]?.[_wrapFunction.flag] || parent[loggerFn];
44
+ contextMap.set(contextLookupKey, ctx);
41
45
 
42
46
  /** observe calls to <loggerFn> and emit events prefixed with `wrap-logger-` */
43
- wrapFn.inPlace(parent, [loggerFn], 'wrap-logger-', ctx);
47
+ wrapFn.inPlace(parent, [loggerFn], 'wrap-logger-', () => contextMap.get(contextLookupKey));
44
48
  return ee;
45
49
  }
46
50
 
@@ -33,6 +33,7 @@ const XHR_PROPS = ['open', 'send']; // these are the specific funcs being wrappe
33
33
  function wrapXhr(sharedEE) {
34
34
  var baseEE = sharedEE || _contextualEe.ee;
35
35
  const ee = scopedEE(baseEE);
36
+ if (typeof _runtime.globalScope.XMLHttpRequest === 'undefined') return ee;
36
37
 
37
38
  // Notice if our wrapping never ran yet, the falsy NaN will not early return; but if it has,
38
39
  // then we increment the count to track # of feats using this at runtime.
@@ -15,7 +15,6 @@ var _now = require("../../../common/timing/now");
15
15
  var _registerHandler = require("../../../common/event-emitter/register-handler");
16
16
  var _constants2 = require("../../metrics/constants");
17
17
  var _traverse = require("../../../common/util/traverse");
18
- var _agentConstants = require("../../../common/constants/agent-constants");
19
18
  var _features = require("../../../loaders/features/features");
20
19
  var _userActionsAggregator = require("./user-actions/user-actions-aggregator");
21
20
  var _iframe = require("../../../common/dom/iframe");
@@ -188,8 +187,16 @@ class Aggregate extends _aggregateBase.AggregateBase {
188
187
  /** Event-specific attributes take precedence over agent-level custom attributes and fallbacks */
189
188
  ...obj
190
189
  };
191
- this.events.add(eventAttributes);
192
- this.checkEventLimits();
190
+ const addedEvent = this.events.add(eventAttributes);
191
+ if (!addedEvent && !this.events.isEmpty()) {
192
+ /** could not add the event because it pushed the buffer over the limit
193
+ * so we harvest early, and try to add it again now that the buffer is cleared
194
+ * if it fails again, we do nothing
195
+ */
196
+ this.ee.emit(_constants2.SUPPORTABILITY_METRIC_CHANNEL, ['GenericEvents/Harvest/Max/Seen']);
197
+ this.harvestScheduler.runHarvest();
198
+ this.events.add(eventAttributes);
199
+ }
193
200
  }
194
201
  serializer(eventBuffer) {
195
202
  return (0, _traverse.applyFnToProps)({
@@ -202,12 +209,5 @@ class Aggregate extends _aggregateBase.AggregateBase {
202
209
  at: this.agentRef.info.atts
203
210
  };
204
211
  }
205
- checkEventLimits() {
206
- // check if we've reached any harvest limits...
207
- if (this.events.byteSize() > _agentConstants.IDEAL_PAYLOAD_SIZE) {
208
- this.ee.emit(_constants2.SUPPORTABILITY_METRIC_CHANNEL, ['GenericEvents/Harvest/Max/Seen']);
209
- this.harvestScheduler.runHarvest();
210
- }
211
- }
212
212
  }
213
213
  exports.Aggregate = Aggregate;
@@ -6,7 +6,7 @@
6
6
  /**
7
7
  * Exposes the version of the agent
8
8
  */
9
- export const VERSION = "1.274.0";
9
+ export const VERSION = "1.275.0";
10
10
 
11
11
  /**
12
12
  * Exposes the build type of the agent
@@ -6,7 +6,7 @@
6
6
  /**
7
7
  * Exposes the version of the agent
8
8
  */
9
- export const VERSION = "1.274.0";
9
+ export const VERSION = "1.275.0";
10
10
 
11
11
  /**
12
12
  * Exposes the build type of the agent
@@ -166,6 +166,27 @@ export class Harvest extends SharedContext {
166
166
  }
167
167
  cbFinished(cbResult);
168
168
  }, eventListenerOpts(false));
169
+ } else if (!opts.unload && cbFinished && submitMethod === submitData.xhrFetch) {
170
+ const harvestScope = this;
171
+ result.then(async function (response) {
172
+ const status = response.status;
173
+ const cbResult = {
174
+ sent: true,
175
+ status,
176
+ fullUrl,
177
+ fetchResponse: response
178
+ };
179
+ if (response.status === 429) {
180
+ cbResult.retry = true;
181
+ cbResult.delay = harvestScope.tooManyRequestsDelay;
182
+ } else if (status === 408 || status === 500 || status === 503) {
183
+ cbResult.retry = true;
184
+ }
185
+ if (opts.needResponse) {
186
+ cbResult.responseText = await response.text();
187
+ }
188
+ cbFinished(cbResult);
189
+ });
169
190
  }
170
191
  return result;
171
192
  }
@@ -19,11 +19,47 @@ import { isBrowserScope } from '../constants/runtime';
19
19
  export function getSubmitMethod({
20
20
  isFinalHarvest = false
21
21
  } = {}) {
22
- return isFinalHarvest && isBrowserScope
23
- // Use sendBeacon for final harvest
24
- ? beacon
25
- // If not final harvest, or not browserScope, always use xhr post
26
- : xhr;
22
+ if (isFinalHarvest && isBrowserScope) {
23
+ // Use sendBeacon for final harvest
24
+ return beacon;
25
+ }
26
+
27
+ // If not final harvest, or not browserScope, use XHR post if available
28
+ if (typeof XMLHttpRequest !== 'undefined') {
29
+ return xhr;
30
+ }
31
+
32
+ // Fallback for web workers where XMLHttpRequest is not available
33
+ return xhrFetch;
34
+ }
35
+
36
+ /**
37
+ *
38
+ * @param url
39
+ * @param body
40
+ * @param method
41
+ * @param headers
42
+ * @returns {Promise<Response>}
43
+ */
44
+ export function xhrFetch({
45
+ url,
46
+ body = null,
47
+ method = 'POST',
48
+ headers = [{
49
+ key: 'content-type',
50
+ value: 'text/plain'
51
+ }]
52
+ }) {
53
+ const objHeaders = {};
54
+ for (const header of headers) {
55
+ objHeaders[header.key] = header.value;
56
+ }
57
+ return fetch(url, {
58
+ headers: objHeaders,
59
+ method,
60
+ body,
61
+ credentials: 'include'
62
+ });
27
63
  }
28
64
 
29
65
  /**
@@ -36,8 +36,8 @@ export function wrapEvents(sharedEE) {
36
36
  // Guard against instrumenting environments w/o necessary features
37
37
  if ('getPrototypeOf' in Object) {
38
38
  if (isBrowserScope) findEventListenerProtoAndCb(document, wrapNode);
39
+ if (XHR) findEventListenerProtoAndCb(XHR.prototype, wrapNode);
39
40
  findEventListenerProtoAndCb(globalScope, wrapNode);
40
- findEventListenerProtoAndCb(XHR.prototype, wrapNode);
41
41
  }
42
42
  ee.on(ADD_EVENT_LISTENER + '-start', function (args, target) {
43
43
  var originalListener = args[1];
@@ -10,7 +10,8 @@
10
10
  import { ee as baseEE, contextId } from '../event-emitter/contextual-ee';
11
11
  import { EventContext } from '../event-emitter/event-context';
12
12
  import { warn } from '../util/console';
13
- import { createWrapperWithEmitter as wfn } from './wrap-function';
13
+ import { flag, createWrapperWithEmitter as wfn } from './wrap-function';
14
+ const contextMap = new Map();
14
15
 
15
16
  /**
16
17
  * Wraps a supplied function and adds emitter events under the `-wrap-logger-` prefix
@@ -32,9 +33,11 @@ export function wrapLogger(sharedEE, parent, loggerFn, context) {
32
33
  const ctx = new EventContext(contextId);
33
34
  ctx.level = context.level;
34
35
  ctx.customAttributes = context.customAttributes;
36
+ const contextLookupKey = parent[loggerFn]?.[flag] || parent[loggerFn];
37
+ contextMap.set(contextLookupKey, ctx);
35
38
 
36
39
  /** observe calls to <loggerFn> and emit events prefixed with `wrap-logger-` */
37
- wrapFn.inPlace(parent, [loggerFn], 'wrap-logger-', ctx);
40
+ wrapFn.inPlace(parent, [loggerFn], 'wrap-logger-', () => contextMap.get(contextLookupKey));
38
41
  return ee;
39
42
  }
40
43
 
@@ -26,6 +26,7 @@ const XHR_PROPS = ['open', 'send']; // these are the specific funcs being wrappe
26
26
  export function wrapXhr(sharedEE) {
27
27
  var baseEE = sharedEE || contextualEE;
28
28
  const ee = scopedEE(baseEE);
29
+ if (typeof globalScope.XMLHttpRequest === 'undefined') return ee;
29
30
 
30
31
  // Notice if our wrapping never ran yet, the falsy NaN will not early return; but if it has,
31
32
  // then we increment the count to track # of feats using this at runtime.
@@ -13,7 +13,6 @@ import { now } from '../../../common/timing/now';
13
13
  import { registerHandler } from '../../../common/event-emitter/register-handler';
14
14
  import { SUPPORTABILITY_METRIC_CHANNEL } from '../../metrics/constants';
15
15
  import { applyFnToProps } from '../../../common/util/traverse';
16
- import { IDEAL_PAYLOAD_SIZE } from '../../../common/constants/agent-constants';
17
16
  import { FEATURE_TO_ENDPOINT } from '../../../loaders/features/features';
18
17
  import { UserActionsAggregator } from './user-actions/user-actions-aggregator';
19
18
  import { isIFrameWindow } from '../../../common/dom/iframe';
@@ -181,8 +180,16 @@ export class Aggregate extends AggregateBase {
181
180
  /** Event-specific attributes take precedence over agent-level custom attributes and fallbacks */
182
181
  ...obj
183
182
  };
184
- this.events.add(eventAttributes);
185
- this.checkEventLimits();
183
+ const addedEvent = this.events.add(eventAttributes);
184
+ if (!addedEvent && !this.events.isEmpty()) {
185
+ /** could not add the event because it pushed the buffer over the limit
186
+ * so we harvest early, and try to add it again now that the buffer is cleared
187
+ * if it fails again, we do nothing
188
+ */
189
+ this.ee.emit(SUPPORTABILITY_METRIC_CHANNEL, ['GenericEvents/Harvest/Max/Seen']);
190
+ this.harvestScheduler.runHarvest();
191
+ this.events.add(eventAttributes);
192
+ }
186
193
  }
187
194
  serializer(eventBuffer) {
188
195
  return applyFnToProps({
@@ -195,11 +202,4 @@ export class Aggregate extends AggregateBase {
195
202
  at: this.agentRef.info.atts
196
203
  };
197
204
  }
198
- checkEventLimits() {
199
- // check if we've reached any harvest limits...
200
- if (this.events.byteSize() > IDEAL_PAYLOAD_SIZE) {
201
- this.ee.emit(SUPPORTABILITY_METRIC_CHANNEL, ['GenericEvents/Harvest/Max/Seen']);
202
- this.harvestScheduler.runHarvest();
203
- }
204
- }
205
205
  }
@@ -1 +1 @@
1
- {"version":3,"file":"harvest.d.ts","sourceRoot":"","sources":["../../../../src/common/harvest/harvest.js"],"names":[],"mappings":"AAsBA;;;;;;GAMG;AACH;IAII,0BAA2H;IAC3H,gBAA2E;IAE3E,YAAiB;IAGnB;;;;;OAKG;IACH,aAFW,eAAe,WAWzB;IAED;;;OAGG;IACH,YAFW,eAAe,WAMzB;IAED;;;;;;OAMG;IACH,gGAJW,eAAe,GACb,OAAO,CA+EnB;IAGD,gDAqBC;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;8BAvOY,OAAO,YAAY,EAAE,eAAe;wCACpC,OAAO,YAAY,EAAE,yBAAyB;6BAC9C,OAAO,YAAY,EAAE,cAAc;qCACnC,OAAO,YAAY,EAAE,sBAAsB;4CAC3C,OAAO,YAAY,EAAE,6BAA6B;8BAbjC,2BAA2B"}
1
+ {"version":3,"file":"harvest.d.ts","sourceRoot":"","sources":["../../../../src/common/harvest/harvest.js"],"names":[],"mappings":"AAsBA;;;;;;GAMG;AACH;IAII,0BAA2H;IAC3H,gBAA2E;IAE3E,YAAiB;IAGnB;;;;;OAKG;IACH,aAFW,eAAe,WAWzB;IAED;;;OAGG;IACH,YAFW,eAAe,WAMzB;IAED;;;;;;OAMG;IACH,gGAJW,eAAe,GACb,OAAO,CAkGnB;IAGD,gDAqBC;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;8BA1PY,OAAO,YAAY,EAAE,eAAe;wCACpC,OAAO,YAAY,EAAE,yBAAyB;6BAC9C,OAAO,YAAY,EAAE,cAAc;qCACnC,OAAO,YAAY,EAAE,sBAAsB;4CAC3C,OAAO,YAAY,EAAE,6BAA6B;8BAbjC,2BAA2B"}
@@ -9,7 +9,24 @@
9
9
  */
10
10
  export function getSubmitMethod({ isFinalHarvest }?: {
11
11
  isFinalHarvest: boolean;
12
- }): typeof xhr | typeof beacon;
12
+ }): typeof xhr | typeof beacon | typeof xhrFetch;
13
+ /**
14
+ *
15
+ * @param url
16
+ * @param body
17
+ * @param method
18
+ * @param headers
19
+ * @returns {Promise<Response>}
20
+ */
21
+ export function xhrFetch({ url, body, method, headers }: {
22
+ url: any;
23
+ body?: null | undefined;
24
+ method?: string | undefined;
25
+ headers?: {
26
+ key: string;
27
+ value: string;
28
+ }[] | undefined;
29
+ }): Promise<Response>;
13
30
  /**
14
31
  * Send via XHR
15
32
  * @param {Object} args - The args.
@@ -1 +1 @@
1
- {"version":3,"file":"submit-data.d.ts","sourceRoot":"","sources":["../../../../src/common/util/submit-data.js"],"names":[],"mappings":"AAQA;;GAEG;AAEH;;;;;GAKG;AACH,qDAHG;IAAsB,cAAc,EAA5B,OAAO;CAEjB,8BAOA;AAED;;;;;;;;;GASG;AACH,0DAPG;IAAqB,GAAG,EAAhB,MAAM;IACQ,IAAI,GAAlB,MAAM,YAAC;IACQ,IAAI,GAAnB,OAAO,YAAC;IACO,MAAM,GAArB,MAAM,YAAC;IAC+B,OAAO;aAAvC,MAAM;eAAS,MAAM;;CACnC,GAAU,cAAc,CAmB1B;AAED;;;;;;GAMG;AACH,sCAJG;IAAqB,GAAG,EAAhB,MAAM;IACQ,IAAI,GAAlB,MAAM,YAAC;CACf,GAAU,OAAO,CAanB;6BAhEY,0BAAU"}
1
+ {"version":3,"file":"submit-data.d.ts","sourceRoot":"","sources":["../../../../src/common/util/submit-data.js"],"names":[],"mappings":"AAQA;;GAEG;AAEH;;;;;GAKG;AACH,qDAHG;IAAsB,cAAc,EAA5B,OAAO;CAEjB,gDAcA;AAED;;;;;;;GAOG;AACH;;;;;;;;IAFa,OAAO,CAAC,QAAQ,CAAC,CAkB7B;AAED;;;;;;;;;GASG;AACH,0DAPG;IAAqB,GAAG,EAAhB,MAAM;IACQ,IAAI,GAAlB,MAAM,YAAC;IACQ,IAAI,GAAnB,OAAO,YAAC;IACO,MAAM,GAArB,MAAM,YAAC;IAC+B,OAAO;aAAvC,MAAM;eAAS,MAAM;;CACnC,GAAU,cAAc,CAmB1B;AAED;;;;;;GAMG;AACH,sCAJG;IAAqB,GAAG,EAAhB,MAAM;IACQ,IAAI,GAAlB,MAAM,YAAC;CACf,GAAU,OAAO,CAanB;6BAjGY,0BAAU"}
@@ -1 +1 @@
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"}
1
+ {"version":3,"file":"wrap-logger.d.ts","sourceRoot":"","sources":["../../../../src/common/wrap/wrap-logger.js"],"names":[],"mappings":"AAgBA;;;;;;GAMG;AAEH,qCANW,MAAM,UACN,MAAM,YACN,MAAM,iBACJ,MAAM,CAuBlB;AAED;;;;;;GAMG;AACH,mCAJW,MAAM,GAEJ,MAAM,CAIlB"}
@@ -1 +1 @@
1
- {"version":3,"file":"wrap-xhr.d.ts","sourceRoot":"","sources":["../../../../src/common/wrap/wrap-xhr.js"],"names":[],"mappings":"AAmBA;;;;;GAKG;AAEH,kCAJW,MAAM,GACJ,MAAM,CA+KlB;AAED;;;;;;GAMG;AACH,mCAJW,MAAM,GAEJ,MAAM,CAIlB"}
1
+ {"version":3,"file":"wrap-xhr.d.ts","sourceRoot":"","sources":["../../../../src/common/wrap/wrap-xhr.js"],"names":[],"mappings":"AAmBA;;;;;GAKG;AAEH,kCAJW,MAAM,GACJ,MAAM,CAiLlB;AAED;;;;;;GAMG;AACH,mCAJW,MAAM,GAEJ,MAAM,CAIlB"}
@@ -24,7 +24,6 @@ export class Aggregate extends AggregateBase {
24
24
  ua: any;
25
25
  at: any;
26
26
  };
27
- checkEventLimits(): void;
28
27
  }
29
28
  import { AggregateBase } from '../../utils/aggregate-base';
30
29
  import { UserActionsAggregator } from './user-actions/user-actions-aggregator';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/features/generic_events/aggregate/index.js"],"names":[],"mappings":"AAoBA;IACE,2BAAiC;IACjC,2BAiHC;IA9GC,yBAA4B;IAC5B,wBAAyE;IAEzE,gCAAkG;IA4B9F,4CAAuD;IAsEzD,mCAGQ;IASZ;;;;;;;;;;;OAWG;IACH,eAHW,MAAM,YAAC,QAmCjB;IAED,qCAEC;IAED;;;MAEC;IAED,yBAMC;CACF;8BA9L6B,4BAA4B;sCAQpB,wCAAwC;iCAZ7C,2CAA2C"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/features/generic_events/aggregate/index.js"],"names":[],"mappings":"AAmBA;IACE,2BAAiC;IACjC,2BAiHC;IA9GC,yBAA4B;IAC5B,wBAAyE;IAEzE,gCAAkG;IA4B9F,4CAAuD;IAsEzD,mCAGQ;IASZ;;;;;;;;;;;OAWG;IACH,eAHW,MAAM,YAAC,QA0CjB;IAED,qCAEC;IAED;;;MAEC;CACF;8BA5L6B,4BAA4B;sCAOpB,wCAAwC;iCAX7C,2CAA2C"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@newrelic/browser-agent",
3
- "version": "1.274.0",
3
+ "version": "1.275.0",
4
4
  "private": false,
5
5
  "author": "New Relic Browser Agent Team <browser-agent@newrelic.com>",
6
6
  "description": "New Relic Browser Agent",
@@ -144,6 +144,25 @@ export class Harvest extends SharedContext {
144
144
  }
145
145
  cbFinished(cbResult)
146
146
  }, eventListenerOpts(false))
147
+ } else if (!opts.unload && cbFinished && submitMethod === submitData.xhrFetch) {
148
+ const harvestScope = this
149
+ result.then(async function (response) {
150
+ const status = response.status
151
+ const cbResult = { sent: true, status, fullUrl, fetchResponse: response }
152
+
153
+ if (response.status === 429) {
154
+ cbResult.retry = true
155
+ cbResult.delay = harvestScope.tooManyRequestsDelay
156
+ } else if (status === 408 || status === 500 || status === 503) {
157
+ cbResult.retry = true
158
+ }
159
+
160
+ if (opts.needResponse) {
161
+ cbResult.responseText = await response.text()
162
+ }
163
+
164
+ cbFinished(cbResult)
165
+ })
147
166
  }
148
167
 
149
168
  return result
@@ -17,11 +17,44 @@ import { isBrowserScope } from '../constants/runtime'
17
17
  * a final harvest within the agent.
18
18
  */
19
19
  export function getSubmitMethod ({ isFinalHarvest = false } = {}) {
20
- return isFinalHarvest && isBrowserScope
20
+ if (isFinalHarvest && isBrowserScope) {
21
21
  // Use sendBeacon for final harvest
22
- ? beacon
23
- // If not final harvest, or not browserScope, always use xhr post
24
- : xhr
22
+ return beacon
23
+ }
24
+
25
+ // If not final harvest, or not browserScope, use XHR post if available
26
+ if (typeof XMLHttpRequest !== 'undefined') {
27
+ return xhr
28
+ }
29
+
30
+ // Fallback for web workers where XMLHttpRequest is not available
31
+ return xhrFetch
32
+ }
33
+
34
+ /**
35
+ *
36
+ * @param url
37
+ * @param body
38
+ * @param method
39
+ * @param headers
40
+ * @returns {Promise<Response>}
41
+ */
42
+ export function xhrFetch ({
43
+ url,
44
+ body = null,
45
+ method = 'POST',
46
+ headers = [{
47
+ key: 'content-type',
48
+ value: 'text/plain'
49
+ }]
50
+ }) {
51
+ const objHeaders = {}
52
+
53
+ for (const header of headers) {
54
+ objHeaders[header.key] = header.value
55
+ }
56
+
57
+ return fetch(url, { headers: objHeaders, method, body, credentials: 'include' })
25
58
  }
26
59
 
27
60
  /**
@@ -37,8 +37,8 @@ export function wrapEvents (sharedEE) {
37
37
  // Guard against instrumenting environments w/o necessary features
38
38
  if ('getPrototypeOf' in Object) {
39
39
  if (isBrowserScope) findEventListenerProtoAndCb(document, wrapNode)
40
+ if (XHR) findEventListenerProtoAndCb(XHR.prototype, wrapNode)
40
41
  findEventListenerProtoAndCb(globalScope, wrapNode)
41
- findEventListenerProtoAndCb(XHR.prototype, wrapNode)
42
42
  }
43
43
 
44
44
  ee.on(ADD_EVENT_LISTENER + '-start', function (args, target) {
@@ -10,7 +10,9 @@
10
10
  import { ee as baseEE, contextId } from '../event-emitter/contextual-ee'
11
11
  import { EventContext } from '../event-emitter/event-context'
12
12
  import { warn } from '../util/console'
13
- import { createWrapperWithEmitter as wfn } from './wrap-function'
13
+ import { flag, createWrapperWithEmitter as wfn } from './wrap-function'
14
+
15
+ const contextMap = new Map()
14
16
 
15
17
  /**
16
18
  * Wraps a supplied function and adds emitter events under the `-wrap-logger-` prefix
@@ -33,8 +35,11 @@ export function wrapLogger(sharedEE, parent, loggerFn, context) {
33
35
  ctx.level = context.level
34
36
  ctx.customAttributes = context.customAttributes
35
37
 
38
+ const contextLookupKey = parent[loggerFn]?.[flag] || parent[loggerFn]
39
+ contextMap.set(contextLookupKey, ctx)
40
+
36
41
  /** observe calls to <loggerFn> and emit events prefixed with `wrap-logger-` */
37
- wrapFn.inPlace(parent, [loggerFn], 'wrap-logger-', ctx)
42
+ wrapFn.inPlace(parent, [loggerFn], 'wrap-logger-', () => contextMap.get(contextLookupKey))
38
43
 
39
44
  return ee
40
45
  }
@@ -28,6 +28,8 @@ export function wrapXhr (sharedEE) {
28
28
  var baseEE = sharedEE || contextualEE
29
29
  const ee = scopedEE(baseEE)
30
30
 
31
+ if (typeof globalScope.XMLHttpRequest === 'undefined') return ee
32
+
31
33
  // Notice if our wrapping never ran yet, the falsy NaN will not early return; but if it has,
32
34
  // then we increment the count to track # of feats using this at runtime.
33
35
  if (wrapped[ee.debugId]++) return ee
@@ -13,7 +13,6 @@ import { now } from '../../../common/timing/now'
13
13
  import { registerHandler } from '../../../common/event-emitter/register-handler'
14
14
  import { SUPPORTABILITY_METRIC_CHANNEL } from '../../metrics/constants'
15
15
  import { applyFnToProps } from '../../../common/util/traverse'
16
- import { IDEAL_PAYLOAD_SIZE } from '../../../common/constants/agent-constants'
17
16
  import { FEATURE_TO_ENDPOINT } from '../../../loaders/features/features'
18
17
  import { UserActionsAggregator } from './user-actions/user-actions-aggregator'
19
18
  import { isIFrameWindow } from '../../../common/dom/iframe'
@@ -177,9 +176,16 @@ export class Aggregate extends AggregateBase {
177
176
  ...obj
178
177
  }
179
178
 
180
- this.events.add(eventAttributes)
181
-
182
- this.checkEventLimits()
179
+ const addedEvent = this.events.add(eventAttributes)
180
+ if (!addedEvent && !this.events.isEmpty()) {
181
+ /** could not add the event because it pushed the buffer over the limit
182
+ * so we harvest early, and try to add it again now that the buffer is cleared
183
+ * if it fails again, we do nothing
184
+ */
185
+ this.ee.emit(SUPPORTABILITY_METRIC_CHANNEL, ['GenericEvents/Harvest/Max/Seen'])
186
+ this.harvestScheduler.runHarvest()
187
+ this.events.add(eventAttributes)
188
+ }
183
189
  }
184
190
 
185
191
  serializer (eventBuffer) {
@@ -189,12 +195,4 @@ export class Aggregate extends AggregateBase {
189
195
  queryStringsBuilder () {
190
196
  return { ua: this.agentRef.info.userAttributes, at: this.agentRef.info.atts }
191
197
  }
192
-
193
- checkEventLimits () {
194
- // check if we've reached any harvest limits...
195
- if (this.events.byteSize() > IDEAL_PAYLOAD_SIZE) {
196
- this.ee.emit(SUPPORTABILITY_METRIC_CHANNEL, ['GenericEvents/Harvest/Max/Seen'])
197
- this.harvestScheduler.runHarvest()
198
- }
199
- }
200
198
  }