@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
@@ -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";
@@ -67,8 +67,8 @@ export class AgentBase {
67
67
  /**
68
68
  * Starts a set of agent features if not running in "autoStart" mode
69
69
  * {@link https://docs.newrelic.com/docs/browser/new-relic-browser/browser-apis/start/}
70
- * @param {string|string[]|undefined} name The feature name(s) to start. If no name(s) are passed, all features will be started
70
+ * @param {string|string[]} [featureNames] The name(s) of the features to start. If no name(s) are passed, all features will be started
71
71
  */
72
- start(featureNames: any): void;
72
+ start(featureNames?: string | string[] | undefined): void;
73
73
  }
74
74
  //# sourceMappingURL=agent-base.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"agent-base.d.ts","sourceRoot":"","sources":["../../../src/loaders/agent-base.js"],"names":[],"mappings":"AAIA;IACE;;;;;OAKG;IACH,oBAHW,MAAM,yCAKhB;IAED;;;;;OAKG;IACH,sBAHW,MAAM,mCAKhB;IAED;;;;;;OAMG;IACH,yBAJW,MAAM,SACN,MAAM,GAAC,MAAM,GAAC,IAAI,uCAK5B;IAED;;;;;OAKG;IACH,mBAHW,KAAK,GAAC,MAAM,+CAKtB;IAED;;;;OAIG;IACH,iBAFW,MAAM,GAAC,IAAI,QAIrB;IAED;;;;;;;OAOG;IACH,6BAJW,MAAM,GAAC,IAAI,QAMrB;IAED;;;;OAIG;IACH,kCAFmB,KAAK,GAAC,MAAM,KAAK,OAAO,GAAG;QAAE,KAAK,EAAE,MAAM,CAAA;KAAE,QAI9D;IAED;;;;OAIG;IACH,+CAEC;IAED;;;;;OAKG;IACH,iBAHW,MAAM,MACN,MAAM,QAIhB;IAED;;;;OAIG;IACH,+BAEC;CACF"}
1
+ {"version":3,"file":"agent-base.d.ts","sourceRoot":"","sources":["../../../src/loaders/agent-base.js"],"names":[],"mappings":"AAIA;IACE;;;;;OAKG;IACH,oBAHW,MAAM,yCAKhB;IAED;;;;;OAKG;IACH,sBAHW,MAAM,mCAKhB;IAED;;;;;;OAMG;IACH,yBAJW,MAAM,SACN,MAAM,GAAC,MAAM,GAAC,IAAI,uCAK5B;IAED;;;;;OAKG;IACH,mBAHW,KAAK,GAAC,MAAM,+CAKtB;IAED;;;;OAIG;IACH,iBAFW,MAAM,GAAC,IAAI,QAIrB;IAED;;;;;;;OAOG;IACH,6BAJW,MAAM,GAAC,IAAI,QAMrB;IAED;;;;OAIG;IACH,kCAFmB,KAAK,GAAC,MAAM,KAAK,OAAO,GAAG;QAAE,KAAK,EAAE,MAAM,CAAA;KAAE,QAI9D;IAED;;;;OAIG;IACH,+CAEC;IAED;;;;;OAKG;IACH,iBAHW,MAAM,MACN,MAAM,QAIhB;IAED;;;;OAIG;IACH,0DAEC;CACF"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@newrelic/browser-agent",
3
- "version": "1.243.1",
3
+ "version": "1.245.0",
4
4
  "private": false,
5
5
  "author": "New Relic Browser Agent Team <browser-agent@newrelic.com>",
6
6
  "description": "New Relic Browser Agent",
@@ -26,9 +26,6 @@
26
26
  "loaders/micro-agent": [
27
27
  "dist/types/loaders/micro-agent.d.ts"
28
28
  ],
29
- "loaders/worker-agent": [
30
- "dist/types/loaders/worker-agent.d.ts"
31
- ],
32
29
  "features/ajax": [
33
30
  "dist/types/features/ajax/index.d.ts"
34
31
  ],
@@ -81,11 +78,6 @@
81
78
  "require": "./dist/cjs/loaders/micro-agent.js",
82
79
  "default": "./dist/esm/loaders/micro-agent.js"
83
80
  },
84
- "./loaders/worker-agent": {
85
- "types": "./dist/types/loaders/worker-agent.d.ts",
86
- "require": "./dist/cjs/loaders/worker-agent.js",
87
- "default": "./dist/esm/loaders/worker-agent.js"
88
- },
89
81
  "./features/ajax": {
90
82
  "types": "./dist/types/features/ajax/index.d.ts",
91
83
  "require": "./dist/cjs/features/ajax/index.js",
@@ -176,7 +168,7 @@
176
168
  "dependencies": {
177
169
  "core-js": "^3.26.0",
178
170
  "fflate": "^0.7.4",
179
- "rrweb": "2.0.0-alpha.8",
171
+ "rrweb": "2.0.0-alpha.11",
180
172
  "web-vitals": "^3.1.0"
181
173
  },
182
174
  "devDependencies": {
@@ -13,8 +13,11 @@ import 'core-js/stable/array/some'
13
13
  import 'core-js/stable/object/assign'
14
14
  import 'core-js/stable/object/entries'
15
15
  import 'core-js/stable/object/values'
16
+ import 'core-js/stable/object/from-entries'
16
17
  import 'core-js/stable/map'
17
18
  import 'core-js/stable/reflect'
18
19
  import 'core-js/stable/set'
19
20
  import 'core-js/stable/weak-set'
20
21
  import 'core-js/stable/object/get-own-property-descriptors'
22
+ import 'core-js/stable/url'
23
+ import 'core-js/stable/url-search-params'
@@ -63,6 +63,9 @@ const model = () => {
63
63
  harvestTimeSeconds: 60,
64
64
  sampling_rate: 50, // float from 0 - 100
65
65
  error_sampling_rate: 50, // float from 0 - 100
66
+ collect_fonts: false, // serialize fonts for collection without public asset url, this is currently broken in RRWeb -- https://github.com/rrweb-io/rrweb/issues/1304. When fixed, revisit with test cases
67
+ inline_images: false, // serialize images for collection without public asset url
68
+ inline_stylesheet: true, // serialize css for collection without public asset url
66
69
  // recording config settings
67
70
  mask_all_inputs: true,
68
71
  // this has a getter/setter to facilitate validation of the selectors
@@ -1,3 +1,4 @@
1
1
  export const VERSION = '0.0.0'
2
2
  export const BUILD_ENV = 'TEST'
3
3
  export const DIST_METHOD = 'TEST'
4
+ export const RRWEB_VERSION = '0.0.0'
@@ -18,3 +18,8 @@ export const BUILD_ENV = process.env.BUILD_ENV
18
18
  * Exposes the distribution method of the agent
19
19
  */
20
20
  export const DIST_METHOD = 'CDN'
21
+
22
+ /**
23
+ * Exposes the lib version of rrweb
24
+ */
25
+ export const RRWEB_VERSION = process.env.RRWEB_VERSION
@@ -21,3 +21,8 @@ export const BUILD_ENV = 'NPM'
21
21
  * Exposes the distribution method of the agent
22
22
  */
23
23
  export const DIST_METHOD = 'NPM'
24
+
25
+ /**
26
+ * Exposes the lib version of rrweb
27
+ */
28
+ export const RRWEB_VERSION = pkgJSON.dependencies.rrweb
@@ -19,3 +19,8 @@ export const BUILD_ENV = 'NPM'
19
19
  * Valid valuse are CDN, NPM
20
20
  */
21
21
  export const DIST_METHOD = 'NPM'
22
+
23
+ /**
24
+ * Exposes the lib version of rrweb
25
+ */
26
+ export const RRWEB_VERSION = process.env.RRWEB_VERSION
@@ -23,20 +23,22 @@ export function parseUrl (url) {
23
23
  var location = globalScope?.location
24
24
  var ret = {}
25
25
 
26
- if (isBrowserScope) {
26
+ try {
27
+ urlEl = new URL(url, location.href)
28
+ } catch (err) {
29
+ if (isBrowserScope) {
27
30
  // Use an anchor dom element to resolve the url natively.
28
- urlEl = document.createElement('a')
29
- urlEl.href = url
30
- } else {
31
- try {
32
- urlEl = new URL(url, location.href)
33
- } catch (err) {
31
+ urlEl = document.createElement('a')
32
+ urlEl.href = url
33
+ } else {
34
34
  return ret
35
35
  }
36
36
  }
37
37
 
38
38
  ret.port = urlEl.port
39
39
 
40
+ ret.search = urlEl.search
41
+
40
42
  var firstSplit = urlEl.href.split('://')
41
43
 
42
44
  if (!ret.port && firstSplit[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,95 @@
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 ({ body, query } = {}) {
18
+ if (!body && !query) return
19
+ try {
20
+ const gqlBody = parseBatchGQL(parseGQLContents(body))
21
+ if (gqlBody) return gqlBody
22
+ const gqlQuery = parseSingleGQL(parseGQLQueryString(query))
23
+ if (gqlQuery) return gqlQuery
24
+ } catch (err) {
25
+ // parsing failed, return undefined
26
+ }
27
+ }
28
+
29
+ /**
30
+ * @param {string|Object} gql The GraphQL object body sent to a GQL server
31
+ * @returns {GQLMetadata}
32
+ */
33
+ function parseSingleGQL (contents) {
34
+ if (typeof contents !== 'object' || !contents.query || typeof contents.query !== 'string') return
35
+
36
+ /** parses gql query string and returns [fullmatch, type match, name match] */
37
+ const matches = contents.query.trim().match(/^(query|mutation|subscription)\s?(\w*)/)
38
+ const operationType = matches?.[1]
39
+ if (!operationType) return
40
+ const operationName = contents.operationName || matches?.[2] || 'Anonymous'
41
+ return {
42
+ operationName, // the operation name of the indiv query
43
+ operationType, // query, mutation, or subscription,
44
+ operationFramework: 'GraphQL'
45
+ }
46
+ }
47
+
48
+ function parseBatchGQL (contents) {
49
+ if (!contents) return
50
+ if (!Array.isArray(contents)) contents = [contents]
51
+
52
+ const opNames = []
53
+ const opTypes = []
54
+ for (let content of contents) {
55
+ const operation = parseSingleGQL(content)
56
+ if (!operation) continue
57
+
58
+ opNames.push(operation.operationName)
59
+ opTypes.push(operation.operationType)
60
+ }
61
+
62
+ if (!opTypes.length) return
63
+ return {
64
+ operationName: opNames.join(','), // the operation name of the indiv query -- joined by ',' for batched results
65
+ operationType: opTypes.join(','), // query, mutation, or subscription -- joined by ',' for batched results
66
+ operationFramework: 'GraphQL'
67
+ }
68
+ }
69
+
70
+ function parseGQLContents (gqlContents) {
71
+ let contents
72
+
73
+ if (!gqlContents || (typeof gqlContents !== 'string' && typeof gqlContents !== 'object')) return
74
+ else if (typeof gqlContents === 'string') contents = JSON.parse(gqlContents)
75
+ else contents = gqlContents
76
+
77
+ if (!isPureObject(contents) && !Array.isArray(contents)) return
78
+
79
+ let isValid = false
80
+ if (Array.isArray(contents)) isValid = contents.some(x => validateGQLObject(x))
81
+ else isValid = validateGQLObject(contents)
82
+
83
+ if (!isValid) return
84
+ return contents
85
+ }
86
+
87
+ function parseGQLQueryString (gqlQueryString) {
88
+ if (!gqlQueryString || typeof gqlQueryString !== 'string') return
89
+ const params = new URLSearchParams(gqlQueryString)
90
+ return parseGQLContents(Object.fromEntries(params))
91
+ }
92
+
93
+ function validateGQLObject (obj) {
94
+ return !(typeof obj !== 'object' || !obj.query || typeof obj.query !== 'string')
95
+ }
@@ -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
 
17
18
  export class Aggregate extends AggregateBase {
18
19
  static featureName = FEATURE_NAME
@@ -119,6 +120,12 @@ export class Aggregate extends AggregateBase {
119
120
  event.spanTimestamp = xhrContext.dt.timestamp
120
121
  }
121
122
 
123
+ // parsed from the AJAX body, looking for operationName param & parsing query for operationType
124
+ event.gql = params.gql = parseGQL({
125
+ body: this.body,
126
+ query: this?.parsedOrigin?.search
127
+ })
128
+
122
129
  // if the ajax happened inside an interaction, hold it until the interaction finishes
123
130
  if (this.spaNode) {
124
131
  var interactionId = this.spaNode.interaction.id
@@ -221,7 +228,8 @@ export class Aggregate extends AggregateBase {
221
228
  var insert = '2,'
222
229
 
223
230
  // add custom attributes
224
- var attrParts = addCustomAttributes(getInfo(agentIdentifier).jsAttributes || {}, this.addString)
231
+ // gql decorators are added as custom attributes to alleviate need for new BEL schema
232
+ var attrParts = addCustomAttributes({ ...(getInfo(agentIdentifier).jsAttributes || {}), ...(event.gql || {}) }, this.addString)
225
233
  fields.unshift(numeric(attrParts.length))
226
234
 
227
235
  insert += fields.join(',')
@@ -157,6 +157,8 @@ function subscribeToEvents (agentIdentifier, ee, handler, dt) {
157
157
 
158
158
  this.startTime = now()
159
159
 
160
+ this.body = data
161
+
160
162
  this.listener = function (evt) {
161
163
  try {
162
164
  if (evt.type === 'abort' && !(context.loadCaptureCalled)) {
@@ -329,6 +331,7 @@ function subscribeToEvents (agentIdentifier, ee, handler, dt) {
329
331
  var method = ('' + ((target && target instanceof origRequest && target.method) ||
330
332
  opts.method || 'GET')).toUpperCase()
331
333
  this.params.method = method
334
+ this.body = opts.body
332
335
 
333
336
  this.txSize = dataSize(opts.body) || 0
334
337
  }
@@ -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'
30
28
 
31
29
  export const AVG_COMPRESSION = 0.12
32
30
 
@@ -68,8 +66,8 @@ let recorder, gzipper, u8
68
66
  export const MAX_PAYLOAD_SIZE = 1000000
69
67
  /** Unloading caps around 64kb */
70
68
  export const IDEAL_PAYLOAD_SIZE = 64000
71
- /** Interval between forcing new full snapshots -- 30 seconds in error mode, 5 minutes in full mode */
72
- const CHECKOUT_MS = { [MODE.ERROR]: 30000, [MODE.FULL]: 300000, [MODE.OFF]: 0 }
69
+ /** Interval between forcing new full snapshots -- 15 seconds in error mode (x2), 5 minutes in full mode */
70
+ const CHECKOUT_MS = { [MODE.ERROR]: 15000, [MODE.FULL]: 300000, [MODE.OFF]: 0 }
73
71
 
74
72
  export class Aggregate extends AggregateBase {
75
73
  static featureName = FEATURE_NAME
@@ -77,6 +75,8 @@ export class Aggregate extends AggregateBase {
77
75
  super(agentIdentifier, aggregator, FEATURE_NAME)
78
76
  /** Each page mutation or event will be stored (raw) in this array. This array will be cleared on each harvest */
79
77
  this.events = []
78
+ /** Backlog used for a 2-part sliding window to guarantee a 15-30s buffer window */
79
+ this.backloggedEvents = []
80
80
  /** The interval to harvest at. This gets overridden if the size of the payload exceeds certain thresholds */
81
81
  this.harvestTimeSeconds = getConfigurationValue(this.agentIdentifier, 'session_replay.harvestTimeSeconds') || 60
82
82
  /** Set once the recorder has fully initialized after flag checks and sampling */
@@ -247,7 +247,10 @@ export class Aggregate extends AggregateBase {
247
247
  prepareHarvest () {
248
248
  if (this.events.length === 0 || (this.mode !== MODE.FULL && !this.blocked)) return
249
249
  const payload = this.getHarvestContents()
250
-
250
+ if (!payload.body.length) {
251
+ this.clearBuffer()
252
+ return
253
+ }
251
254
  if (this.shouldCompress) {
252
255
  payload.body = gzipper(u8(stringify(payload.body)))
253
256
  this.scheduler.opts.gzip = true
@@ -265,6 +268,17 @@ export class Aggregate extends AggregateBase {
265
268
  const agentRuntime = getRuntime(this.agentIdentifier)
266
269
  const info = getInfo(this.agentIdentifier)
267
270
 
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
+ }
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
270
284
  const payloadEndsWithMeta = this.events[this.events.length - 1]?.type === RRWEB_EVENT_TYPES.Meta
@@ -274,16 +288,8 @@ export class Aggregate extends AggregateBase {
274
288
  this.hasMeta = !!this.events.find(x => x.type === RRWEB_EVENT_TYPES.Meta)
275
289
  }
276
290
 
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
-
285
- const firstEventTimestamp = this.events[0]?.timestamp
286
- const lastEventTimestamp = this.events[this.events.length - 1]?.timestamp
291
+ const firstEventTimestamp = this.events[0]?.timestamp // from rrweb node
292
+ const lastEventTimestamp = this.events[this.events.length - 1]?.timestamp // from rrweb node
287
293
  const firstTimestamp = firstEventTimestamp || this.cycleTimestamp
288
294
  const lastTimestamp = lastEventTimestamp || getRuntime(this.agentIdentifier).offset + globalScope.performance.now()
289
295
  return {
@@ -323,6 +329,8 @@ 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
333
+ else this.backloggedEvents = []
326
334
  this.events = []
327
335
  this.hasSnapshot = false
328
336
  this.hasMeta = false
@@ -337,11 +345,8 @@ export class Aggregate extends AggregateBase {
337
345
  warn('Recording library was never imported')
338
346
  return this.abort(ABORT_REASONS.IMPORT)
339
347
  }
340
- this.clearTimestamps()
341
- // set the fallbacks as early as possible
342
- this.setTimestamps()
343
348
  this.recording = true
344
- const { block_class, ignore_class, mask_text_class, block_selector, mask_input_options, mask_text_selector, mask_all_inputs } = getConfigurationValue(this.agentIdentifier, 'session_replay')
349
+ const { block_class, ignore_class, mask_text_class, block_selector, mask_input_options, mask_text_selector, mask_all_inputs, inline_images, inline_stylesheet, collect_fonts } = getConfigurationValue(this.agentIdentifier, 'session_replay')
345
350
  // set up rrweb configurations for maximum privacy --
346
351
  // https://newrelic.atlassian.net/wiki/spaces/O11Y/pages/2792293280/2023+02+28+Browser+-+Session+Replay#Configuration-options
347
352
  const stop = recorder({
@@ -353,6 +358,9 @@ export class Aggregate extends AggregateBase {
353
358
  maskInputOptions: mask_input_options,
354
359
  maskTextSelector: mask_text_selector,
355
360
  maskAllInputs: mask_all_inputs,
361
+ inlineImages: inline_images,
362
+ inlineStylesheet: inline_stylesheet,
363
+ collectFonts: collect_fonts,
356
364
  checkoutEveryNms: CHECKOUT_MS[this.mode]
357
365
  })
358
366
 
@@ -377,7 +385,8 @@ export class Aggregate extends AggregateBase {
377
385
  // Checkout events are flags by the recording lib that indicate a fullsnapshot was taken every n ms. These are important
378
386
  // to help reconstruct the replay later and must be included. While waiting and buffering for errors to come through,
379
387
  // each time we see a new checkout, we can drop the old data.
380
- if (this.mode === MODE.ERROR && isCheckout) {
388
+ // we need to check for meta because rrweb will flag it as checkout twice, once for meta, then once for snapshot
389
+ if (this.mode === MODE.ERROR && isCheckout && event.type === RRWEB_EVENT_TYPES.Meta) {
381
390
  // we are still waiting for an error to throw, so keep wiping the buffer over time
382
391
  this.clearBuffer()
383
392
  }
@@ -385,7 +394,6 @@ export class Aggregate extends AggregateBase {
385
394
  // meta event
386
395
  if (event.type === RRWEB_EVENT_TYPES.Meta) {
387
396
  this.hasMeta = true
388
- this.lastMeta = event
389
397
  }
390
398
  // snapshot event
391
399
  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
 
24
25
  const {
25
26
  FEATURE_NAME, INTERACTION_EVENTS, MAX_TIMER_BUDGET, FN_START, FN_END, CB_START, INTERACTION_API, REMAINING,
@@ -166,7 +167,7 @@ export class Aggregate extends AggregateBase {
166
167
  register(FN_START, function (args, eventSource) {
167
168
  var ev = args[0]
168
169
  var evName = ev.type
169
- var eventNode = ev.__nrNode
170
+ var eventNode = ev[`__nrNode:${bundleId}`]
170
171
 
171
172
  if (!state.pageLoaded && evName === 'load' && eventSource === window) {
172
173
  state.pageLoaded = true
@@ -216,7 +217,7 @@ export class Aggregate extends AggregateBase {
216
217
  }
217
218
  }
218
219
 
219
- ev.__nrNode = state.currentNode
220
+ ev[`__nrNode:${bundleId}`] = state.currentNode
220
221
  }, this.featureName, eventsEE)
221
222
 
222
223
  /**
@@ -102,7 +102,6 @@ export class Serializer extends SharedContext {
102
102
  nullable(attrs.firstPaint, numeric, true) +
103
103
  nullable(attrs.firstContentfulPaint, numeric, false)
104
104
  )
105
-
106
105
  var attrParts = addCustomAttributes(attrs.custom, addString)
107
106
  children = children.concat(attrParts)
108
107
  attrCount = attrParts.length
@@ -128,6 +127,13 @@ export class Serializer extends SharedContext {
128
127
  nullable(node.dt && node.dt.traceId, addString, true) +
129
128
  nullable(node.dt && node.dt.timestamp, numeric, false)
130
129
  )
130
+
131
+ // add params.gql here
132
+ if (Object.keys(params?.gql || {}).length) {
133
+ var ajaxAttrParts = addCustomAttributes(params.gql, addString)
134
+ children = children.concat(ajaxAttrParts)
135
+ attrCount = ajaxAttrParts.length
136
+ }
131
137
  break
132
138
 
133
139
  case 4:
package/src/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
 
6
5
  export { Ajax } from './features/ajax'
@@ -10,7 +10,7 @@ export class AgentBase {
10
10
  * @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.
11
11
  */
12
12
  addPageAction (name, attributes) {
13
- warn('Call to agent api addPageAction failed. The session trace feature is not currently initialized.')
13
+ warn('Call to agent api addPageAction failed. The page action feature is not currently initialized.')
14
14
  }
15
15
 
16
16
  /**
@@ -90,13 +90,13 @@ export class AgentBase {
90
90
  * @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.
91
91
  */
92
92
  addRelease (name, id) {
93
- warn('Call to agent api addRelease failed. The agent is not currently initialized.')
93
+ warn('Call to agent api addRelease failed. The js errors feature is not currently initialized.')
94
94
  }
95
95
 
96
96
  /**
97
97
  * Starts a set of agent features if not running in "autoStart" mode
98
98
  * {@link https://docs.newrelic.com/docs/browser/new-relic-browser/browser-apis/start/}
99
- * @param {string|string[]|undefined} name The feature name(s) to start. If no name(s) are passed, all features will be started
99
+ * @param {string|string[]} [featureNames] The name(s) of the features to start. If no name(s) are passed, all features will be started
100
100
  */
101
101
  start (featureNames) {
102
102
  warn('Call to agent api addRelease failed. The agent is not currently initialized.')
@@ -107,7 +107,7 @@ export class Agent extends AgentBase {
107
107
  * 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.
108
108
  */
109
109
  addToTrace (customAttributes) {
110
- warn('Call to agent api addToTrace failed. The page action feature is not currently initialized.')
110
+ warn('Call to agent api addToTrace failed. The session trace feature is not currently initialized.')
111
111
  }
112
112
 
113
113
  /**
@@ -89,8 +89,8 @@ export function setAPI (agentIdentifier, forceDrain) {
89
89
  warn(`Failed to execute setCustomAttribute.\nName must be a string type, but a type of <${typeof name}> was provided.`)
90
90
  return
91
91
  }
92
- if (!(['string', 'number'].includes(typeof value) || value === null)) {
93
- warn(`Failed to execute setCustomAttribute.\nNon-null value must be a string or number type, but a type of <${typeof value}> was provided.`)
92
+ if (!(['string', 'number', 'boolean'].includes(typeof value) || value === null)) {
93
+ warn(`Failed to execute setCustomAttribute.\nNon-null value must be a string, number or boolean type, but a type of <${typeof value}> was provided.`)
94
94
  return
95
95
  }
96
96
  return appendJsAttribute(name, value, 'setCustomAttribute', persistAttribute)
@@ -1,16 +0,0 @@
1
- "use strict";
2
-
3
- var _agent = require("../loaders/agent");
4
- var _instrument = require("../features/metrics/instrument");
5
- var _instrument2 = require("../features/jserrors/instrument");
6
- var _instrument3 = require("../features/ajax/instrument");
7
- var _instrument4 = require("../features/page_action/instrument");
8
- /**
9
- * @file Creates a "Worker" agent loader bundle composed of the core Agent and the subset of feature modules applicable
10
- * in a service worker context.
11
- */
12
-
13
- new _agent.Agent({
14
- features: [_instrument.Instrument, _instrument2.Instrument, _instrument3.Instrument, _instrument4.Instrument],
15
- loaderType: 'worker'
16
- });
@@ -1,24 +0,0 @@
1
- "use strict";
2
-
3
- Object.defineProperty(exports, "__esModule", {
4
- value: true
5
- });
6
- exports.WorkerAgent = void 0;
7
- var _agent = require("./agent");
8
- var _instrument = require("../features/metrics/instrument");
9
- var _instrument2 = require("../features/jserrors/instrument");
10
- var _instrument3 = require("../features/ajax/instrument");
11
- var _instrument4 = require("../features/page_action/instrument");
12
- /**
13
- * A streamlined agent class designed for the service worker context, limited to features relevant in that scope.
14
- */
15
- class WorkerAgent extends _agent.Agent {
16
- constructor(args) {
17
- super({
18
- ...args,
19
- features: [_instrument.Instrument, _instrument2.Instrument, _instrument3.Instrument, _instrument4.Instrument],
20
- loaderType: 'worker-agent'
21
- });
22
- }
23
- }
24
- exports.WorkerAgent = WorkerAgent;
@@ -1,14 +0,0 @@
1
- /**
2
- * @file Creates a "Worker" agent loader bundle composed of the core Agent and the subset of feature modules applicable
3
- * in a service worker context.
4
- */
5
-
6
- import { Agent } from '../loaders/agent';
7
- import { Instrument as InstrumentMetrics } from '../features/metrics/instrument';
8
- import { Instrument as InstrumentErrors } from '../features/jserrors/instrument';
9
- import { Instrument as InstrumentXhr } from '../features/ajax/instrument';
10
- import { Instrument as InstrumentPageAction } from '../features/page_action/instrument';
11
- new Agent({
12
- features: [InstrumentMetrics, InstrumentErrors, InstrumentXhr, InstrumentPageAction],
13
- loaderType: 'worker'
14
- });
@@ -1,18 +0,0 @@
1
- import { Agent } from './agent';
2
- import { Instrument as InstrumentMetrics } from '../features/metrics/instrument';
3
- import { Instrument as InstrumentErrors } from '../features/jserrors/instrument';
4
- import { Instrument as InstrumentXhr } from '../features/ajax/instrument';
5
- import { Instrument as InstrumentPageAction } from '../features/page_action/instrument';
6
-
7
- /**
8
- * A streamlined agent class designed for the service worker context, limited to features relevant in that scope.
9
- */
10
- export class WorkerAgent extends Agent {
11
- constructor(args) {
12
- super({
13
- ...args,
14
- features: [InstrumentMetrics, InstrumentErrors, InstrumentXhr, InstrumentPageAction],
15
- loaderType: 'worker-agent'
16
- });
17
- }
18
- }
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=worker.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"worker.d.ts","sourceRoot":"","sources":["../../../src/cdn/worker.js"],"names":[],"mappings":""}
@@ -1,8 +0,0 @@
1
- /**
2
- * A streamlined agent class designed for the service worker context, limited to features relevant in that scope.
3
- */
4
- export class WorkerAgent extends Agent {
5
- constructor(args: any);
6
- }
7
- import { Agent } from './agent';
8
- //# sourceMappingURL=worker-agent.d.ts.map