@newrelic/browser-agent 1.243.1 → 1.245.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 (89) hide show
  1. package/CHANGELOG.md +24 -0
  2. package/dist/cjs/cdn/polyfills.js +4 -1
  3. package/dist/cjs/common/config/state/init.js +6 -0
  4. package/dist/cjs/common/constants/env.cdn.js +9 -3
  5. package/dist/cjs/common/constants/env.js +8 -2
  6. package/dist/cjs/common/constants/env.npm.js +9 -3
  7. package/dist/cjs/common/url/parse-url.js +9 -8
  8. package/dist/cjs/common/util/console.js +2 -2
  9. package/dist/cjs/common/util/type-check.js +14 -0
  10. package/dist/cjs/features/ajax/aggregate/gql.js +94 -0
  11. package/dist/cjs/features/ajax/aggregate/index.js +12 -1
  12. package/dist/cjs/features/ajax/instrument/index.js +2 -0
  13. package/dist/cjs/features/session_replay/aggregate/index.js +33 -23
  14. package/dist/cjs/features/spa/aggregate/index.js +3 -2
  15. package/dist/cjs/features/spa/aggregate/serializer.js +7 -0
  16. package/dist/cjs/index.js +0 -7
  17. package/dist/cjs/loaders/agent-base.js +3 -3
  18. package/dist/cjs/loaders/agent.js +1 -1
  19. package/dist/cjs/loaders/api/api.js +2 -2
  20. package/dist/esm/cdn/polyfills.js +4 -1
  21. package/dist/esm/common/config/state/init.js +6 -0
  22. package/dist/esm/common/constants/env.cdn.js +7 -2
  23. package/dist/esm/common/constants/env.js +6 -1
  24. package/dist/esm/common/constants/env.npm.js +7 -2
  25. package/dist/esm/common/url/parse-url.js +9 -8
  26. package/dist/esm/common/util/console.js +2 -2
  27. package/dist/esm/common/util/type-check.js +8 -0
  28. package/dist/esm/features/ajax/aggregate/gql.js +89 -0
  29. package/dist/esm/features/ajax/aggregate/index.js +12 -1
  30. package/dist/esm/features/ajax/instrument/index.js +2 -0
  31. package/dist/esm/features/session_replay/aggregate/index.js +31 -21
  32. package/dist/esm/features/spa/aggregate/index.js +3 -2
  33. package/dist/esm/features/spa/aggregate/serializer.js +7 -0
  34. package/dist/esm/index.js +0 -1
  35. package/dist/esm/loaders/agent-base.js +3 -3
  36. package/dist/esm/loaders/agent.js +1 -1
  37. package/dist/esm/loaders/api/api.js +2 -2
  38. package/dist/types/common/config/state/init.d.ts.map +1 -1
  39. package/dist/types/common/constants/env.cdn.d.ts +4 -0
  40. package/dist/types/common/constants/env.cdn.d.ts.map +1 -1
  41. package/dist/types/common/constants/env.d.ts +4 -0
  42. package/dist/types/common/constants/env.d.ts.map +1 -1
  43. package/dist/types/common/constants/env.npm.d.ts +4 -0
  44. package/dist/types/common/constants/env.npm.d.ts.map +1 -1
  45. package/dist/types/common/url/parse-url.d.ts.map +1 -1
  46. package/dist/types/common/util/console.d.ts +3 -3
  47. package/dist/types/common/util/console.d.ts.map +1 -1
  48. package/dist/types/common/util/type-check.d.ts +7 -0
  49. package/dist/types/common/util/type-check.d.ts.map +1 -0
  50. package/dist/types/features/ajax/aggregate/gql.d.ts +29 -0
  51. package/dist/types/features/ajax/aggregate/gql.d.ts.map +1 -0
  52. package/dist/types/features/ajax/aggregate/index.d.ts.map +1 -1
  53. package/dist/types/features/session_replay/aggregate/index.d.ts +2 -1
  54. package/dist/types/features/session_replay/aggregate/index.d.ts.map +1 -1
  55. package/dist/types/features/spa/aggregate/index.d.ts.map +1 -1
  56. package/dist/types/features/spa/aggregate/serializer.d.ts.map +1 -1
  57. package/dist/types/index.d.ts +0 -1
  58. package/dist/types/loaders/agent-base.d.ts +2 -2
  59. package/dist/types/loaders/agent-base.d.ts.map +1 -1
  60. package/package.json +2 -10
  61. package/src/cdn/polyfills.js +3 -0
  62. package/src/common/config/state/init.js +3 -0
  63. package/src/common/constants/__mocks__/env.js +1 -0
  64. package/src/common/constants/env.cdn.js +5 -0
  65. package/src/common/constants/env.js +5 -0
  66. package/src/common/constants/env.npm.js +5 -0
  67. package/src/common/url/parse-url.js +9 -7
  68. package/src/common/util/console.js +2 -2
  69. package/src/common/util/type-check.js +8 -0
  70. package/src/features/ajax/aggregate/gql.js +95 -0
  71. package/src/features/ajax/aggregate/index.js +9 -1
  72. package/src/features/ajax/instrument/index.js +3 -0
  73. package/src/features/session_replay/aggregate/index.js +30 -22
  74. package/src/features/spa/aggregate/index.js +3 -2
  75. package/src/features/spa/aggregate/serializer.js +7 -1
  76. package/src/index.js +0 -1
  77. package/src/loaders/agent-base.js +3 -3
  78. package/src/loaders/agent.js +1 -1
  79. package/src/loaders/api/api.js +2 -2
  80. package/dist/cjs/cdn/worker.js +0 -16
  81. package/dist/cjs/loaders/worker-agent.js +0 -24
  82. package/dist/esm/cdn/worker.js +0 -14
  83. package/dist/esm/loaders/worker-agent.js +0 -18
  84. package/dist/types/cdn/worker.d.ts +0 -2
  85. package/dist/types/cdn/worker.d.ts.map +0 -1
  86. package/dist/types/loaders/worker-agent.d.ts +0 -8
  87. package/dist/types/loaders/worker-agent.d.ts.map +0 -1
  88. package/src/cdn/worker.js +0 -21
  89. package/src/loaders/worker-agent.js +0 -24
@@ -6,7 +6,7 @@
6
6
  /**
7
7
  * Exposes the version of the agent
8
8
  */
9
- export const VERSION = "1.243.1";
9
+ export const VERSION = "1.245.0";
10
10
 
11
11
  /**
12
12
  * Exposes the build type of the agent
@@ -17,4 +17,9 @@ export const BUILD_ENV = "CDN";
17
17
  /**
18
18
  * Exposes the distribution method of the agent
19
19
  */
20
- export const DIST_METHOD = 'CDN';
20
+ export const DIST_METHOD = 'CDN';
21
+
22
+ /**
23
+ * Exposes the lib version of rrweb
24
+ */
25
+ export const RRWEB_VERSION = "2.0.0-alpha.11";
@@ -20,4 +20,9 @@ export const BUILD_ENV = 'NPM';
20
20
  /**
21
21
  * Exposes the distribution method of the agent
22
22
  */
23
- export const DIST_METHOD = 'NPM';
23
+ export const DIST_METHOD = 'NPM';
24
+
25
+ /**
26
+ * Exposes the lib version of rrweb
27
+ */
28
+ export const RRWEB_VERSION = pkgJSON.dependencies.rrweb;
@@ -6,7 +6,7 @@
6
6
  /**
7
7
  * Exposes the version of the agent
8
8
  */
9
- export const VERSION = "1.243.1";
9
+ export const VERSION = "1.245.0";
10
10
 
11
11
  /**
12
12
  * Exposes the build type of the agent
@@ -18,4 +18,9 @@ export const BUILD_ENV = 'NPM';
18
18
  * Exposes the distribution method of the agent
19
19
  * Valid valuse are CDN, NPM
20
20
  */
21
- export const DIST_METHOD = 'NPM';
21
+ export const DIST_METHOD = 'NPM';
22
+
23
+ /**
24
+ * Exposes the lib version of rrweb
25
+ */
26
+ export const RRWEB_VERSION = "2.0.0-alpha.11";
@@ -19,18 +19,19 @@ export function parseUrl(url) {
19
19
  let urlEl;
20
20
  var location = globalScope?.location;
21
21
  var ret = {};
22
- if (isBrowserScope) {
23
- // Use an anchor dom element to resolve the url natively.
24
- urlEl = document.createElement('a');
25
- urlEl.href = url;
26
- } else {
27
- try {
28
- urlEl = new URL(url, location.href);
29
- } catch (err) {
22
+ try {
23
+ urlEl = new URL(url, location.href);
24
+ } catch (err) {
25
+ if (isBrowserScope) {
26
+ // Use an anchor dom element to resolve the url natively.
27
+ urlEl = document.createElement('a');
28
+ urlEl.href = url;
29
+ } else {
30
30
  return ret;
31
31
  }
32
32
  }
33
33
  ret.port = urlEl.port;
34
+ ret.search = urlEl.search;
34
35
  var firstSplit = urlEl.href.split('://');
35
36
  if (!ret.port && firstSplit[1]) {
36
37
  ret.port = firstSplit[1].split('/')[0].split('@').pop().split(':')[1];
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * A helper method to warn to the console with New Relic: decoration
3
- * @param {string} message - The primary message to warn
4
- * @param {*} secondary - Secondary data to include, usually an error or object
3
+ * @param {string} message The primary message to warn
4
+ * @param {*} [secondary] Secondary data to include, usually an error or object
5
5
  * @returns
6
6
  */
7
7
  export function warn(message, secondary) {
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Tests a passed object to see if it is a pure object or not. All non-primatives in JS
3
+ * are technically objects and would pass a `typeof` check.
4
+ * @param {*} obj Input object to be tested
5
+ **/
6
+ export function isPureObject(obj) {
7
+ return obj?.constructor === {}.constructor;
8
+ }
@@ -0,0 +1,89 @@
1
+ import { isPureObject } from '../../../common/util/type-check';
2
+
3
+ /**
4
+ * @typedef {object} GQLMetadata
5
+ * @property {string} operationName Name of the operation
6
+ * @property {string} operationType Type of the operation
7
+ * @property {string} operationFramework Framework responsible for the operation
8
+ */
9
+
10
+ /**
11
+ * Parses and returns the graphql metadata from a network request. If the network
12
+ * request is not a graphql call, undefined will be returned.
13
+ * @param {object|string} body Ajax request body
14
+ * @param {string} query Ajax request query param string
15
+ * @returns {GQLMetadata | undefined}
16
+ */
17
+ export function parseGQL() {
18
+ let {
19
+ body,
20
+ query
21
+ } = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
22
+ if (!body && !query) return;
23
+ try {
24
+ const gqlBody = parseBatchGQL(parseGQLContents(body));
25
+ if (gqlBody) return gqlBody;
26
+ const gqlQuery = parseSingleGQL(parseGQLQueryString(query));
27
+ if (gqlQuery) return gqlQuery;
28
+ } catch (err) {
29
+ // parsing failed, return undefined
30
+ }
31
+ }
32
+
33
+ /**
34
+ * @param {string|Object} gql The GraphQL object body sent to a GQL server
35
+ * @returns {GQLMetadata}
36
+ */
37
+ function parseSingleGQL(contents) {
38
+ if (typeof contents !== 'object' || !contents.query || typeof contents.query !== 'string') return;
39
+
40
+ /** parses gql query string and returns [fullmatch, type match, name match] */
41
+ const matches = contents.query.trim().match(/^(query|mutation|subscription)\s?(\w*)/);
42
+ const operationType = matches?.[1];
43
+ if (!operationType) return;
44
+ const operationName = contents.operationName || matches?.[2] || 'Anonymous';
45
+ return {
46
+ operationName,
47
+ // the operation name of the indiv query
48
+ operationType,
49
+ // query, mutation, or subscription,
50
+ operationFramework: 'GraphQL'
51
+ };
52
+ }
53
+ function parseBatchGQL(contents) {
54
+ if (!contents) return;
55
+ if (!Array.isArray(contents)) contents = [contents];
56
+ const opNames = [];
57
+ const opTypes = [];
58
+ for (let content of contents) {
59
+ const operation = parseSingleGQL(content);
60
+ if (!operation) continue;
61
+ opNames.push(operation.operationName);
62
+ opTypes.push(operation.operationType);
63
+ }
64
+ if (!opTypes.length) return;
65
+ return {
66
+ operationName: opNames.join(','),
67
+ // the operation name of the indiv query -- joined by ',' for batched results
68
+ operationType: opTypes.join(','),
69
+ // query, mutation, or subscription -- joined by ',' for batched results
70
+ operationFramework: 'GraphQL'
71
+ };
72
+ }
73
+ function parseGQLContents(gqlContents) {
74
+ let contents;
75
+ if (!gqlContents || typeof gqlContents !== 'string' && typeof gqlContents !== 'object') return;else if (typeof gqlContents === 'string') contents = JSON.parse(gqlContents);else contents = gqlContents;
76
+ if (!isPureObject(contents) && !Array.isArray(contents)) return;
77
+ let isValid = false;
78
+ if (Array.isArray(contents)) isValid = contents.some(x => validateGQLObject(x));else isValid = validateGQLObject(contents);
79
+ if (!isValid) return;
80
+ return contents;
81
+ }
82
+ function parseGQLQueryString(gqlQueryString) {
83
+ if (!gqlQueryString || typeof gqlQueryString !== 'string') return;
84
+ const params = new URLSearchParams(gqlQueryString);
85
+ return parseGQLContents(Object.fromEntries(params));
86
+ }
87
+ function validateGQLObject(obj) {
88
+ return !(typeof obj !== 'object' || !obj.query || typeof obj.query !== 'string');
89
+ }
@@ -13,6 +13,7 @@ import { FEATURE_NAME } from '../constants';
13
13
  import { FEATURE_NAMES } from '../../../loaders/features/features';
14
14
  import { SUPPORTABILITY_METRIC_CHANNEL } from '../../metrics/constants';
15
15
  import { AggregateBase } from '../../utils/aggregate-base';
16
+ import { parseGQL } from './gql';
16
17
  export class Aggregate extends AggregateBase {
17
18
  static featureName = FEATURE_NAME;
18
19
  constructor(agentIdentifier, aggregator) {
@@ -109,6 +110,12 @@ export class Aggregate extends AggregateBase {
109
110
  event.spanTimestamp = xhrContext.dt.timestamp;
110
111
  }
111
112
 
113
+ // parsed from the AJAX body, looking for operationName param & parsing query for operationType
114
+ event.gql = params.gql = parseGQL({
115
+ body: this.body,
116
+ query: this?.parsedOrigin?.search
117
+ });
118
+
112
119
  // if the ajax happened inside an interaction, hold it until the interaction finishes
113
120
  if (this.spaNode) {
114
121
  var interactionId = this.spaNode.interaction.id;
@@ -198,7 +205,11 @@ export class Aggregate extends AggregateBase {
198
205
  var insert = '2,';
199
206
 
200
207
  // add custom attributes
201
- var attrParts = addCustomAttributes(getInfo(agentIdentifier).jsAttributes || {}, this.addString);
208
+ // gql decorators are added as custom attributes to alleviate need for new BEL schema
209
+ var attrParts = addCustomAttributes({
210
+ ...(getInfo(agentIdentifier).jsAttributes || {}),
211
+ ...(event.gql || {})
212
+ }, this.addString);
202
213
  fields.unshift(numeric(attrParts.length));
203
214
  insert += fields.join(',');
204
215
  if (attrParts && attrParts.length > 0) {
@@ -153,6 +153,7 @@ function subscribeToEvents(agentIdentifier, ee, handler, dt) {
153
153
  if (size) metrics.txSize = size;
154
154
  }
155
155
  this.startTime = now();
156
+ this.body = data;
156
157
  this.listener = function (evt) {
157
158
  try {
158
159
  if (evt.type === 'abort' && !context.loadCaptureCalled) {
@@ -300,6 +301,7 @@ function subscribeToEvents(agentIdentifier, ee, handler, dt) {
300
301
  addUrl(this, url);
301
302
  var method = ('' + (target && target instanceof origRequest && target.method || opts.method || 'GET')).toUpperCase();
302
303
  this.params.method = method;
304
+ this.body = opts.body;
303
305
  this.txSize = dataSize(opts.body) || 0;
304
306
  }
305
307
 
@@ -24,9 +24,7 @@ import { globalScope } from '../../../common/constants/runtime';
24
24
  import { SUPPORTABILITY_METRIC_CHANNEL } from '../../metrics/constants';
25
25
  import { handle } from '../../../common/event-emitter/handle';
26
26
  import { FEATURE_NAMES } from '../../../loaders/features/features';
27
-
28
- // would be better to get this dynamically in some way
29
- export const RRWEB_VERSION = '2.0.0-alpha.8';
27
+ import { RRWEB_VERSION } from "../../../common/constants/env.npm";
30
28
  export const AVG_COMPRESSION = 0.12;
31
29
  export const RRWEB_EVENT_TYPES = {
32
30
  DomContentLoaded: 0,
@@ -64,9 +62,9 @@ let recorder, gzipper, u8;
64
62
  export const MAX_PAYLOAD_SIZE = 1000000;
65
63
  /** Unloading caps around 64kb */
66
64
  export const IDEAL_PAYLOAD_SIZE = 64000;
67
- /** Interval between forcing new full snapshots -- 30 seconds in error mode, 5 minutes in full mode */
65
+ /** Interval between forcing new full snapshots -- 15 seconds in error mode (x2), 5 minutes in full mode */
68
66
  const CHECKOUT_MS = {
69
- [MODE.ERROR]: 30000,
67
+ [MODE.ERROR]: 15000,
70
68
  [MODE.FULL]: 300000,
71
69
  [MODE.OFF]: 0
72
70
  };
@@ -76,6 +74,8 @@ export class Aggregate extends AggregateBase {
76
74
  super(agentIdentifier, aggregator, FEATURE_NAME);
77
75
  /** Each page mutation or event will be stored (raw) in this array. This array will be cleared on each harvest */
78
76
  this.events = [];
77
+ /** Backlog used for a 2-part sliding window to guarantee a 15-30s buffer window */
78
+ this.backloggedEvents = [];
79
79
  /** The interval to harvest at. This gets overridden if the size of the payload exceeds certain thresholds */
80
80
  this.harvestTimeSeconds = getConfigurationValue(this.agentIdentifier, 'session_replay.harvestTimeSeconds') || 60;
81
81
  /** Set once the recorder has fully initialized after flag checks and sampling */
@@ -245,6 +245,10 @@ export class Aggregate extends AggregateBase {
245
245
  prepareHarvest() {
246
246
  if (this.events.length === 0 || this.mode !== MODE.FULL && !this.blocked) return;
247
247
  const payload = this.getHarvestContents();
248
+ if (!payload.body.length) {
249
+ this.clearBuffer();
250
+ return;
251
+ }
248
252
  if (this.shouldCompress) {
249
253
  payload.body = gzipper(u8(stringify(payload.body)));
250
254
  this.scheduler.opts.gzip = true;
@@ -264,6 +268,16 @@ export class Aggregate extends AggregateBase {
264
268
  getHarvestContents() {
265
269
  const agentRuntime = getRuntime(this.agentIdentifier);
266
270
  const info = getInfo(this.agentIdentifier);
271
+ if (this.backloggedEvents.length) this.events = [...this.backloggedEvents, ...this.events];
272
+
273
+ // do not let the first node be a full snapshot node, since this NEEDS to be preceded by a meta node
274
+ // we will manually inject it if this happens
275
+ const payloadStartsWithFullSnapshot = this.events[0]?.type === RRWEB_EVENT_TYPES.FullSnapshot;
276
+ if (payloadStartsWithFullSnapshot && !!this.lastMeta) {
277
+ this.hasMeta = true;
278
+ this.events.unshift(this.lastMeta); // --> pushed the meta from a previous payload into newer payload... but it still has old timestamps
279
+ this.lastMeta = undefined;
280
+ }
267
281
 
268
282
  // do not let the last node be a meta node, since this NEEDS to precede a snapshot
269
283
  // we will manually inject it later if we find a payload that is missing a meta node
@@ -273,16 +287,8 @@ export class Aggregate extends AggregateBase {
273
287
  this.events = this.events.slice(0, this.events.length - 1);
274
288
  this.hasMeta = !!this.events.find(x => x.type === RRWEB_EVENT_TYPES.Meta);
275
289
  }
276
-
277
- // do not let the first node be a full snapshot node, since this NEEDS to be preceded by a meta node
278
- // we will manually inject it if this happens
279
- const payloadStartsWithFullSnapshot = this.events[0]?.type === RRWEB_EVENT_TYPES.FullSnapshot;
280
- if (payloadStartsWithFullSnapshot) {
281
- this.hasMeta = true;
282
- this.events.unshift(this.lastMeta);
283
- }
284
- const firstEventTimestamp = this.events[0]?.timestamp;
285
- const lastEventTimestamp = this.events[this.events.length - 1]?.timestamp;
290
+ const firstEventTimestamp = this.events[0]?.timestamp; // from rrweb node
291
+ const lastEventTimestamp = this.events[this.events.length - 1]?.timestamp; // from rrweb node
286
292
  const firstTimestamp = firstEventTimestamp || this.cycleTimestamp;
287
293
  const lastTimestamp = lastEventTimestamp || getRuntime(this.agentIdentifier).offset + globalScope.performance.now();
288
294
  return {
@@ -323,6 +329,7 @@ export class Aggregate extends AggregateBase {
323
329
 
324
330
  /** Clears the buffer (this.events), and resets all payload metadata properties */
325
331
  clearBuffer() {
332
+ if (this.mode === MODE.ERROR) this.backloggedEvents = this.events;else this.backloggedEvents = [];
326
333
  this.events = [];
327
334
  this.hasSnapshot = false;
328
335
  this.hasMeta = false;
@@ -337,9 +344,6 @@ export class Aggregate extends AggregateBase {
337
344
  warn('Recording library was never imported');
338
345
  return this.abort(ABORT_REASONS.IMPORT);
339
346
  }
340
- this.clearTimestamps();
341
- // set the fallbacks as early as possible
342
- this.setTimestamps();
343
347
  this.recording = true;
344
348
  const {
345
349
  block_class,
@@ -348,7 +352,10 @@ export class Aggregate extends AggregateBase {
348
352
  block_selector,
349
353
  mask_input_options,
350
354
  mask_text_selector,
351
- mask_all_inputs
355
+ mask_all_inputs,
356
+ inline_images,
357
+ inline_stylesheet,
358
+ collect_fonts
352
359
  } = getConfigurationValue(this.agentIdentifier, 'session_replay');
353
360
  // set up rrweb configurations for maximum privacy --
354
361
  // https://newrelic.atlassian.net/wiki/spaces/O11Y/pages/2792293280/2023+02+28+Browser+-+Session+Replay#Configuration-options
@@ -361,6 +368,9 @@ export class Aggregate extends AggregateBase {
361
368
  maskInputOptions: mask_input_options,
362
369
  maskTextSelector: mask_text_selector,
363
370
  maskAllInputs: mask_all_inputs,
371
+ inlineImages: inline_images,
372
+ inlineStylesheet: inline_stylesheet,
373
+ collectFonts: collect_fonts,
364
374
  checkoutEveryNms: CHECKOUT_MS[this.mode]
365
375
  });
366
376
  this.stopRecording = () => {
@@ -384,7 +394,8 @@ export class Aggregate extends AggregateBase {
384
394
  // Checkout events are flags by the recording lib that indicate a fullsnapshot was taken every n ms. These are important
385
395
  // to help reconstruct the replay later and must be included. While waiting and buffering for errors to come through,
386
396
  // each time we see a new checkout, we can drop the old data.
387
- if (this.mode === MODE.ERROR && isCheckout) {
397
+ // we need to check for meta because rrweb will flag it as checkout twice, once for meta, then once for snapshot
398
+ if (this.mode === MODE.ERROR && isCheckout && event.type === RRWEB_EVENT_TYPES.Meta) {
388
399
  // we are still waiting for an error to throw, so keep wiping the buffer over time
389
400
  this.clearBuffer();
390
401
  }
@@ -392,7 +403,6 @@ export class Aggregate extends AggregateBase {
392
403
  // meta event
393
404
  if (event.type === RRWEB_EVENT_TYPES.Meta) {
394
405
  this.hasMeta = true;
395
- this.lastMeta = event;
396
406
  }
397
407
  // snapshot event
398
408
  if (event.type === RRWEB_EVENT_TYPES.FullSnapshot) {
@@ -20,6 +20,7 @@ import { FEATURE_NAMES } from '../../../loaders/features/features';
20
20
  import { AggregateBase } from '../../utils/aggregate-base';
21
21
  import { firstContentfulPaint } from '../../../common/vitals/first-contentful-paint';
22
22
  import { firstPaint } from '../../../common/vitals/first-paint';
23
+ import { bundleId } from '../../../common/ids/bundle-id';
23
24
  const {
24
25
  FEATURE_NAME,
25
26
  INTERACTION_EVENTS,
@@ -173,7 +174,7 @@ export class Aggregate extends AggregateBase {
173
174
  register(FN_START, function (args, eventSource) {
174
175
  var ev = args[0];
175
176
  var evName = ev.type;
176
- var eventNode = ev.__nrNode;
177
+ var eventNode = ev["__nrNode:".concat(bundleId)];
177
178
  if (!state.pageLoaded && evName === 'load' && eventSource === window) {
178
179
  state.pageLoaded = true;
179
180
  // set to null so prevNode is set correctly
@@ -217,7 +218,7 @@ export class Aggregate extends AggregateBase {
217
218
  }
218
219
  }
219
220
  }
220
- ev.__nrNode = state.currentNode;
221
+ ev["__nrNode:".concat(bundleId)] = state.currentNode;
221
222
  }, this.featureName, eventsEE);
222
223
 
223
224
  /**
@@ -84,6 +84,13 @@ export class Serializer extends SharedContext {
84
84
  break;
85
85
  case 2:
86
86
  fields.push(addString(params.method), numeric(params.status), addString(params.host), addString(params.pathname), numeric(metrics.txSize), numeric(metrics.rxSize), attrs.isFetch ? 1 : attrs.isJSONP ? 2 : '', addString(node.id), nullable(node.dt && node.dt.spanId, addString, true) + nullable(node.dt && node.dt.traceId, addString, true) + nullable(node.dt && node.dt.timestamp, numeric, false));
87
+
88
+ // add params.gql here
89
+ if (Object.keys(params?.gql || {}).length) {
90
+ var ajaxAttrParts = addCustomAttributes(params.gql, addString);
91
+ children = children.concat(ajaxAttrParts);
92
+ attrCount = ajaxAttrParts.length;
93
+ }
87
94
  break;
88
95
  case 4:
89
96
  var tracedTime = attrs.tracedTime;
package/dist/esm/index.js CHANGED
@@ -1,6 +1,5 @@
1
1
  export { Agent } from './loaders/agent';
2
2
  export { BrowserAgent } from './loaders/browser-agent';
3
- export { WorkerAgent } from './loaders/worker-agent';
4
3
  export { MicroAgent } from './loaders/micro-agent';
5
4
  export { Ajax } from './features/ajax';
6
5
  export { JSErrors } from './features/jserrors';
@@ -9,7 +9,7 @@ export class AgentBase {
9
9
  * @param {object} [attributes] JSON object with one or more key/value pairs. For example: {key:"value"}. The key is reported as its own PageAction attribute with the specified values.
10
10
  */
11
11
  addPageAction(name, attributes) {
12
- warn('Call to agent api addPageAction failed. The session trace feature is not currently initialized.');
12
+ warn('Call to agent api addPageAction failed. The page action feature is not currently initialized.');
13
13
  }
14
14
 
15
15
  /**
@@ -89,13 +89,13 @@ export class AgentBase {
89
89
  * @param {string} id The ID or version of this release; for example, a version number, build number from your CI environment, GitHub SHA, GUID, or a hash of the contents.
90
90
  */
91
91
  addRelease(name, id) {
92
- warn('Call to agent api addRelease failed. The agent is not currently initialized.');
92
+ warn('Call to agent api addRelease failed. The js errors feature is not currently initialized.');
93
93
  }
94
94
 
95
95
  /**
96
96
  * Starts a set of agent features if not running in "autoStart" mode
97
97
  * {@link https://docs.newrelic.com/docs/browser/new-relic-browser/browser-apis/start/}
98
- * @param {string|string[]|undefined} name The feature name(s) to start. If no name(s) are passed, all features will be started
98
+ * @param {string|string[]} [featureNames] The name(s) of the features to start. If no name(s) are passed, all features will be started
99
99
  */
100
100
  start(featureNames) {
101
101
  warn('Call to agent api addRelease failed. The agent is not currently initialized.');
@@ -103,7 +103,7 @@ export class Agent extends AgentBase {
103
103
  * If you are sending the same event object to New Relic as a PageAction, omit the TYPE attribute. (type is a string to describe what type of event you are marking inside of a session trace.) If included, it will override the event type and cause the PageAction event to be sent incorrectly. Instead, use the name attribute for event information.
104
104
  */
105
105
  addToTrace(customAttributes) {
106
- warn('Call to agent api addToTrace failed. The page action feature is not currently initialized.');
106
+ warn('Call to agent api addToTrace failed. The session trace feature is not currently initialized.');
107
107
  }
108
108
 
109
109
  /**
@@ -90,8 +90,8 @@ export function setAPI(agentIdentifier, forceDrain) {
90
90
  warn("Failed to execute setCustomAttribute.\nName must be a string type, but a type of <".concat(typeof name, "> was provided."));
91
91
  return;
92
92
  }
93
- if (!(['string', 'number'].includes(typeof value) || value === null)) {
94
- warn("Failed to execute setCustomAttribute.\nNon-null value must be a string or number type, but a type of <".concat(typeof value, "> was provided."));
93
+ if (!(['string', 'number', 'boolean'].includes(typeof value) || value === null)) {
94
+ warn("Failed to execute setCustomAttribute.\nNon-null value must be a string, number or boolean type, but a type of <".concat(typeof value, "> was provided."));
95
95
  return;
96
96
  }
97
97
  return appendJsAttribute(name, value, 'setCustomAttribute', persistAttribute);
@@ -1 +1 @@
1
- {"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../../../../../src/common/config/state/init.js"],"names":[],"mappings":"AAuGA,+CAIC;AAED,0DAIC;AAED,+DAYC"}
1
+ {"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../../../../../src/common/config/state/init.js"],"names":[],"mappings":"AA0GA,+CAIC;AAED,0DAIC;AAED,+DAYC"}
@@ -15,4 +15,8 @@ export const BUILD_ENV: string | undefined;
15
15
  * Exposes the distribution method of the agent
16
16
  */
17
17
  export const DIST_METHOD: "CDN";
18
+ /**
19
+ * Exposes the lib version of rrweb
20
+ */
21
+ export const RRWEB_VERSION: string | undefined;
18
22
  //# sourceMappingURL=env.cdn.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"env.cdn.d.ts","sourceRoot":"","sources":["../../../../src/common/constants/env.cdn.js"],"names":[],"mappings":"AAAA;;;GAGG;AAEH;;GAEG;AACH,yCAAgD;AAEhD;;;GAGG;AACH,2CAA8C;AAE9C;;GAEG;AACH,gCAAgC"}
1
+ {"version":3,"file":"env.cdn.d.ts","sourceRoot":"","sources":["../../../../src/common/constants/env.cdn.js"],"names":[],"mappings":"AAAA;;;GAGG;AAEH;;GAEG;AACH,yCAAgD;AAEhD;;;GAGG;AACH,2CAA8C;AAE9C;;GAEG;AACH,gCAAgC;AAEhC;;GAEG;AACH,+CAAsD"}
@@ -10,4 +10,8 @@ export const BUILD_ENV: "NPM";
10
10
  * Exposes the distribution method of the agent
11
11
  */
12
12
  export const DIST_METHOD: "NPM";
13
+ /**
14
+ * Exposes the lib version of rrweb
15
+ */
16
+ export const RRWEB_VERSION: any;
13
17
  //# sourceMappingURL=env.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"env.d.ts","sourceRoot":"","sources":["../../../../src/common/constants/env.js"],"names":[],"mappings":"AASA;;GAEG;AACH,0BAAsC;AAEtC;;GAEG;AACH,8BAA8B;AAE9B;;GAEG;AACH,gCAAgC"}
1
+ {"version":3,"file":"env.d.ts","sourceRoot":"","sources":["../../../../src/common/constants/env.js"],"names":[],"mappings":"AASA;;GAEG;AACH,0BAAsC;AAEtC;;GAEG;AACH,8BAA8B;AAE9B;;GAEG;AACH,gCAAgC;AAEhC;;GAEG;AACH,gCAAuD"}
@@ -16,4 +16,8 @@ export const BUILD_ENV: "NPM";
16
16
  * Valid valuse are CDN, NPM
17
17
  */
18
18
  export const DIST_METHOD: "NPM";
19
+ /**
20
+ * Exposes the lib version of rrweb
21
+ */
22
+ export const RRWEB_VERSION: string | undefined;
19
23
  //# sourceMappingURL=env.npm.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"env.npm.d.ts","sourceRoot":"","sources":["../../../../src/common/constants/env.npm.js"],"names":[],"mappings":"AAAA;;;GAGG;AAEH;;GAEG;AACH,yCAAgD;AAEhD;;;GAGG;AACH,8BAA8B;AAE9B;;;GAGG;AACH,gCAAgC"}
1
+ {"version":3,"file":"env.npm.d.ts","sourceRoot":"","sources":["../../../../src/common/constants/env.npm.js"],"names":[],"mappings":"AAAA;;;GAGG;AAEH;;GAEG;AACH,yCAAgD;AAEhD;;;GAGG;AACH,8BAA8B;AAE9B;;;GAGG;AACH,gCAAgC;AAEhC;;GAEG;AACH,+CAAsD"}
@@ -1 +1 @@
1
- {"version":3,"file":"parse-url.d.ts","sourceRoot":"","sources":["../../../../src/common/url/parse-url.js"],"names":[],"mappings":"AASA,wCA4DC"}
1
+ {"version":3,"file":"parse-url.d.ts","sourceRoot":"","sources":["../../../../src/common/url/parse-url.js"],"names":[],"mappings":"AASA,wCA8DC"}
@@ -1,8 +1,8 @@
1
1
  /**
2
2
  * A helper method to warn to the console with New Relic: decoration
3
- * @param {string} message - The primary message to warn
4
- * @param {*} secondary - Secondary data to include, usually an error or object
3
+ * @param {string} message The primary message to warn
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(message: string, 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,wBAQhB"}
1
+ {"version":3,"file":"console.d.ts","sourceRoot":"","sources":["../../../../src/common/util/console.js"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,8BAJW,MAAM,yBAQhB"}
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Tests a passed object to see if it is a pure object or not. All non-primatives in JS
3
+ * are technically objects and would pass a `typeof` check.
4
+ * @param {*} obj Input object to be tested
5
+ **/
6
+ export function isPureObject(obj: any): boolean;
7
+ //# sourceMappingURL=type-check.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"type-check.d.ts","sourceRoot":"","sources":["../../../../src/common/util/type-check.js"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,gDAEC"}
@@ -0,0 +1,29 @@
1
+ /**
2
+ * @typedef {object} GQLMetadata
3
+ * @property {string} operationName Name of the operation
4
+ * @property {string} operationType Type of the operation
5
+ * @property {string} operationFramework Framework responsible for the operation
6
+ */
7
+ /**
8
+ * Parses and returns the graphql metadata from a network request. If the network
9
+ * request is not a graphql call, undefined will be returned.
10
+ * @param {object|string} body Ajax request body
11
+ * @param {string} query Ajax request query param string
12
+ * @returns {GQLMetadata | undefined}
13
+ */
14
+ export function parseGQL({ body, query }?: object | string): GQLMetadata | undefined;
15
+ export type GQLMetadata = {
16
+ /**
17
+ * Name of the operation
18
+ */
19
+ operationName: string;
20
+ /**
21
+ * Type of the operation
22
+ */
23
+ operationType: string;
24
+ /**
25
+ * Framework responsible for the operation
26
+ */
27
+ operationFramework: string;
28
+ };
29
+ //# sourceMappingURL=gql.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gql.d.ts","sourceRoot":"","sources":["../../../../../src/features/ajax/aggregate/gql.js"],"names":[],"mappings":"AAEA;;;;;GAKG;AAEH;;;;;;GAMG;AACH,2CAJW,MAAM,GAAC,MAAM,GAEX,WAAW,GAAG,SAAS,CAYnC;;;;;mBAtBa,MAAM;;;;mBACN,MAAM;;;;wBACN,MAAM"}
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/features/ajax/aggregate/index.js"],"names":[],"mappings":"AAgBA;IACE,2BAAiC;IACjC,mDAgOC;IAzMC,qGAAwB;IACxB;;;;4BAAoC;IACpC;;;mBAA2E;CAwM9E;8BArO6B,4BAA4B"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/features/ajax/aggregate/index.js"],"names":[],"mappings":"AAiBA;IACE,2BAAiC;IACjC,mDAuOC;IAhNC,qGAAwB;IACxB;;;;4BAAoC;IACpC;;;mBAA2E;CA+M9E;8BA7O6B,4BAA4B"}
@@ -1,4 +1,3 @@
1
- export const RRWEB_VERSION: "2.0.0-alpha.8";
2
1
  export const AVG_COMPRESSION: 0.12;
3
2
  export namespace RRWEB_EVENT_TYPES {
4
3
  const DomContentLoaded: number;
@@ -17,6 +16,8 @@ export class Aggregate extends AggregateBase {
17
16
  constructor(agentIdentifier: any, aggregator: any);
18
17
  /** Each page mutation or event will be stored (raw) in this array. This array will be cleared on each harvest */
19
18
  events: any[];
19
+ /** Backlog used for a 2-part sliding window to guarantee a 15-30s buffer window */
20
+ backloggedEvents: any[];
20
21
  /** The interval to harvest at. This gets overridden if the size of the payload exceeds certain thresholds */
21
22
  harvestTimeSeconds: any;
22
23
  /** Set once the recorder has fully initialized after flag checks and sampling */
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/features/session_replay/aggregate/index.js"],"names":[],"mappings":"AA4BA,4CAA4C;AAE5C,mCAAmC;;;;;;;;;AAoCnC,uCAAuC;AACvC,uCAAuC;AACvC,iCAAiC;AACjC,uCAAuC;AAIvC;IACE,2BAAiC;IACjC,mDA2GC;IAzGC,iHAAiH;IACjH,cAAgB;IAChB,8GAA8G;IAC9G,wBAAgH;IAChH,iFAAiF;IACjF,qBAAwB;IACxB,mEAAmE;IACnE,sBAAyB;IACzB,6GAA6G;IAC7G,aAAoB;IAGpB,iEAAiE;IACjE,mBAAsB;IACtB,gDAAgD;IAChD,wBAA0B;IAE1B;;;MAGE;IACF,qBAAwB;IACxB,4IAA4I;IAC5I,iBAAoB;IACpB,+HAA+H;IAC/H,kBAAqB;IAErB;;OAEG;IACH,oBAA+B;IAE/B,qGAAqG;IACrG,+BAA+B;IAE/B,kIAAkI;IAClI,cAAyB;IAOzB,uIAAuI;IACvI,0BAAyE;IA0BvE,wCAKQ;IAgCZ;;;;;;OAMG;IACH,kCALW,OAAO,eACP,OAAO,cACP,OAAO,GACL,IAAI,CAuDhB;IAED;;;;;;;;;oBAeC;IAED;;;;;;;;;MAiDC;IAED,qCAOC;IAED,kFAAkF;IAClF,oBAOC;IAED,qDAAqD;IACrD,uBA4BC;IAED,yHAAyH;IACzH,yCAsCC;IAED,0HAA0H;IAC1H,yBAGC;IAED,sBAGC;IAED,wBAEC;IAED,gCAAgC;IAChC,uCAGC;IAED,yDAAyD;IACzD,yBASC;IAED;;;SAGK;IACL,oCAGC;IAED,yCAGC;CACF;8BAjb6B,4BAA4B;iCALzB,2CAA2C"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/features/session_replay/aggregate/index.js"],"names":[],"mappings":"AA4BA,mCAAmC;;;;;;;;;AAoCnC,uCAAuC;AACvC,uCAAuC;AACvC,iCAAiC;AACjC,uCAAuC;AAIvC;IACE,2BAAiC;IACjC,mDA6GC;IA3GC,iHAAiH;IACjH,cAAgB;IAChB,mFAAmF;IACnF,wBAA0B;IAC1B,8GAA8G;IAC9G,wBAAgH;IAChH,iFAAiF;IACjF,qBAAwB;IACxB,mEAAmE;IACnE,sBAAyB;IACzB,6GAA6G;IAC7G,aAAoB;IAGpB,iEAAiE;IACjE,mBAAsB;IACtB,gDAAgD;IAChD,wBAA0B;IAE1B;;;MAGE;IACF,qBAAwB;IACxB,4IAA4I;IAC5I,iBAAoB;IACpB,+HAA+H;IAC/H,kBAAqB;IAErB;;OAEG;IACH,oBAA+B;IAE/B,qGAAqG;IACrG,+BAA+B;IAE/B,kIAAkI;IAClI,cAAyB;IAOzB,uIAAuI;IACvI,0BAAyE;IA0BvE,wCAKQ;IAgCZ;;;;;;OAMG;IACH,kCALW,OAAO,eACP,OAAO,cACP,OAAO,GACL,IAAI,CAuDhB;IAED;;;;;;;;;oBAkBC;IAED;;;;;;;;;MAoDC;IAED,qCAOC;IAED,kFAAkF;IAClF,oBASC;IAED,qDAAqD;IACrD,uBA4BC;IAED,yHAAyH;IACzH,yCAsCC;IAED,0HAA0H;IAC1H,yBAGC;IAED,sBAGC;IAED,wBAEC;IAED,gCAAgC;IAChC,uCAGC;IAED,yDAAyD;IACzD,yBASC;IAED;;;SAGK;IACL,oCAGC;IAED,yCAGC;CACF;8BAzb6B,4BAA4B;iCALzB,2CAA2C"}
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/features/spa/aggregate/index.js"],"names":[],"mappings":"AA2BA;IACE,2BAAiC;IACjC,mDA8rBC;IA3rBC;;;;;;;;;;;;;;;;MAgBC;IAED,uBAAsC;CA0qBzC;8BAzsB6B,4BAA4B;2BAJ/B,cAAc"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/features/spa/aggregate/index.js"],"names":[],"mappings":"AA4BA;IACE,2BAAiC;IACjC,mDA8rBC;IA3rBC;;;;;;;;;;;;;;;;MAgBC;IAED,uBAAsC;CA0qBzC;8BA1sB6B,4BAA4B;2BAJ/B,cAAc"}
@@ -1 +1 @@
1
- {"version":3,"file":"serializer.d.ts","sourceRoot":"","sources":["../../../../../src/features/spa/aggregate/serializer.js"],"names":[],"mappings":"AAUA;IAII;;;;;;OAMG;IACH,gBAFU,MAAM,GAAC,SAAS,CAEK;IAGjC,0EASC;IAED,oFAMC;IAED,iHAyJC;CACF;8BA9L6B,wCAAwC"}
1
+ {"version":3,"file":"serializer.d.ts","sourceRoot":"","sources":["../../../../../src/features/spa/aggregate/serializer.js"],"names":[],"mappings":"AAUA;IAII;;;;;;OAMG;IACH,gBAFU,MAAM,GAAC,SAAS,CAEK;IAGjC,0EASC;IAED,oFAMC;IAED,iHA+JC;CACF;8BApM6B,wCAAwC"}