@newrelic/browser-agent 1.256.1 → 1.258.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 (140) hide show
  1. package/CHANGELOG.md +32 -0
  2. package/dist/cjs/common/config/state/configurable.js +8 -5
  3. package/dist/cjs/common/config/state/init.js +0 -2
  4. package/dist/cjs/common/config/state/runtime.js +10 -8
  5. package/dist/cjs/common/constants/env.cdn.js +1 -1
  6. package/dist/cjs/common/constants/env.npm.js +1 -1
  7. package/dist/cjs/common/constants/runtime.js +8 -2
  8. package/dist/cjs/common/harvest/harvest.js +7 -5
  9. package/dist/cjs/common/session/constants.js +1 -0
  10. package/dist/cjs/common/session/session-entity.js +3 -0
  11. package/dist/cjs/common/timing/time-keeper.js +45 -9
  12. package/dist/cjs/common/vitals/time-to-first-byte.js +1 -1
  13. package/dist/cjs/common/vitals/vital-metric.js +1 -1
  14. package/dist/cjs/features/ajax/aggregate/chunk.js +50 -0
  15. package/dist/cjs/features/ajax/aggregate/index.js +131 -191
  16. package/dist/cjs/features/ajax/instrument/index.js +0 -3
  17. package/dist/cjs/features/jserrors/aggregate/index.js +26 -13
  18. package/dist/cjs/features/page_view_event/aggregate/index.js +3 -3
  19. package/dist/cjs/features/session_replay/aggregate/index.js +12 -5
  20. package/dist/cjs/features/session_replay/constants.js +2 -1
  21. package/dist/cjs/features/session_replay/instrument/index.js +15 -5
  22. package/dist/cjs/features/session_replay/shared/recorder.js +6 -3
  23. package/dist/cjs/features/session_replay/shared/utils.js +9 -8
  24. package/dist/cjs/features/session_trace/aggregate/index.js +3 -5
  25. package/dist/cjs/features/soft_navigations/aggregate/index.js +2 -2
  26. package/dist/cjs/features/spa/instrument/index.js +0 -2
  27. package/dist/cjs/features/utils/agent-session.js +1 -5
  28. package/dist/cjs/features/utils/instrument-base.js +11 -14
  29. package/dist/cjs/features/utils/nr1-debugger.js +27 -0
  30. package/dist/cjs/loaders/agent.js +4 -0
  31. package/dist/cjs/loaders/api/apiAsync.js +5 -4
  32. package/dist/esm/common/config/state/configurable.js +8 -5
  33. package/dist/esm/common/config/state/init.js +0 -2
  34. package/dist/esm/common/config/state/runtime.js +11 -9
  35. package/dist/esm/common/constants/env.cdn.js +1 -1
  36. package/dist/esm/common/constants/env.npm.js +1 -1
  37. package/dist/esm/common/constants/runtime.js +7 -1
  38. package/dist/esm/common/harvest/harvest.js +7 -5
  39. package/dist/esm/common/session/constants.js +1 -0
  40. package/dist/esm/common/session/session-entity.js +3 -0
  41. package/dist/esm/common/timing/time-keeper.js +46 -9
  42. package/dist/esm/common/vitals/time-to-first-byte.js +2 -2
  43. package/dist/esm/common/vitals/vital-metric.js +1 -1
  44. package/dist/esm/features/ajax/aggregate/chunk.js +43 -0
  45. package/dist/esm/features/ajax/aggregate/index.js +130 -191
  46. package/dist/esm/features/ajax/instrument/index.js +1 -4
  47. package/dist/esm/features/jserrors/aggregate/index.js +26 -13
  48. package/dist/esm/features/page_view_event/aggregate/index.js +4 -4
  49. package/dist/esm/features/session_replay/aggregate/index.js +12 -5
  50. package/dist/esm/features/session_replay/constants.js +2 -1
  51. package/dist/esm/features/session_replay/instrument/index.js +16 -6
  52. package/dist/esm/features/session_replay/shared/recorder.js +6 -3
  53. package/dist/esm/features/session_replay/shared/utils.js +9 -7
  54. package/dist/esm/features/session_trace/aggregate/index.js +3 -5
  55. package/dist/esm/features/soft_navigations/aggregate/index.js +2 -2
  56. package/dist/esm/features/spa/instrument/index.js +0 -2
  57. package/dist/esm/features/utils/agent-session.js +1 -5
  58. package/dist/esm/features/utils/instrument-base.js +11 -14
  59. package/dist/esm/features/utils/nr1-debugger.js +21 -0
  60. package/dist/esm/loaders/agent.js +4 -0
  61. package/dist/esm/loaders/api/apiAsync.js +5 -4
  62. package/dist/types/common/config/state/configurable.d.ts.map +1 -1
  63. package/dist/types/common/config/state/init.d.ts.map +1 -1
  64. package/dist/types/common/config/state/runtime.d.ts.map +1 -1
  65. package/dist/types/common/constants/runtime.d.ts +6 -1
  66. package/dist/types/common/constants/runtime.d.ts.map +1 -1
  67. package/dist/types/common/harvest/harvest.d.ts +1 -1
  68. package/dist/types/common/harvest/harvest.d.ts.map +1 -1
  69. package/dist/types/common/session/constants.d.ts +1 -0
  70. package/dist/types/common/session/session-entity.d.ts.map +1 -1
  71. package/dist/types/common/timing/time-keeper.d.ts +1 -1
  72. package/dist/types/common/timing/time-keeper.d.ts.map +1 -1
  73. package/dist/types/features/ajax/aggregate/chunk.d.ts +8 -0
  74. package/dist/types/features/ajax/aggregate/chunk.d.ts.map +1 -0
  75. package/dist/types/features/ajax/aggregate/index.d.ts +8 -6
  76. package/dist/types/features/ajax/aggregate/index.d.ts.map +1 -1
  77. package/dist/types/features/ajax/instrument/index.d.ts +2 -2
  78. package/dist/types/features/ajax/instrument/index.d.ts.map +1 -1
  79. package/dist/types/features/jserrors/aggregate/index.d.ts +0 -1
  80. package/dist/types/features/jserrors/aggregate/index.d.ts.map +1 -1
  81. package/dist/types/features/session_replay/aggregate/index.d.ts +0 -1
  82. package/dist/types/features/session_replay/aggregate/index.d.ts.map +1 -1
  83. package/dist/types/features/session_replay/constants.d.ts +1 -0
  84. package/dist/types/features/session_replay/constants.d.ts.map +1 -1
  85. package/dist/types/features/session_replay/instrument/index.d.ts +1 -0
  86. package/dist/types/features/session_replay/instrument/index.d.ts.map +1 -1
  87. package/dist/types/features/session_replay/shared/recorder.d.ts +1 -0
  88. package/dist/types/features/session_replay/shared/recorder.d.ts.map +1 -1
  89. package/dist/types/features/session_replay/shared/utils.d.ts +5 -5
  90. package/dist/types/features/session_replay/shared/utils.d.ts.map +1 -1
  91. package/dist/types/features/session_trace/aggregate/index.d.ts +8 -8
  92. package/dist/types/features/session_trace/aggregate/index.d.ts.map +1 -1
  93. package/dist/types/features/spa/instrument/index.d.ts.map +1 -1
  94. package/dist/types/features/utils/agent-session.d.ts.map +1 -1
  95. package/dist/types/features/utils/instrument-base.d.ts.map +1 -1
  96. package/dist/types/features/utils/nr1-debugger.d.ts +2 -0
  97. package/dist/types/features/utils/nr1-debugger.d.ts.map +1 -0
  98. package/dist/types/loaders/agent.d.ts +5 -1
  99. package/dist/types/loaders/agent.d.ts.map +1 -1
  100. package/dist/types/loaders/api/apiAsync.d.ts.map +1 -1
  101. package/package.json +1 -1
  102. package/src/common/config/state/configurable.js +9 -8
  103. package/src/common/config/state/init.js +0 -1
  104. package/src/common/config/state/runtime.js +12 -9
  105. package/src/common/constants/__mocks__/runtime.js +2 -0
  106. package/src/common/constants/runtime.js +6 -1
  107. package/src/common/drain/__mocks__/drain.js +2 -0
  108. package/src/common/harvest/harvest.js +8 -6
  109. package/src/common/session/constants.js +1 -0
  110. package/src/common/session/session-entity.js +2 -0
  111. package/src/common/timing/time-keeper.js +44 -10
  112. package/src/common/vitals/time-to-first-byte.js +2 -2
  113. package/src/common/vitals/vital-metric.js +1 -1
  114. package/src/common/window/__mocks__/load.js +3 -0
  115. package/src/features/ajax/aggregate/chunk.js +51 -0
  116. package/src/features/ajax/aggregate/index.js +128 -200
  117. package/src/features/ajax/instrument/index.js +1 -4
  118. package/src/features/jserrors/aggregate/index.js +28 -11
  119. package/src/features/page_view_event/aggregate/index.js +4 -4
  120. package/src/features/session_replay/aggregate/index.js +19 -7
  121. package/src/features/session_replay/constants.js +2 -1
  122. package/src/features/session_replay/instrument/index.js +16 -6
  123. package/src/features/session_replay/shared/__mocks__/utils.js +2 -0
  124. package/src/features/session_replay/shared/recorder.js +7 -4
  125. package/src/features/session_replay/shared/utils.js +9 -7
  126. package/src/features/session_trace/aggregate/index.js +3 -6
  127. package/src/features/soft_navigations/aggregate/index.js +2 -2
  128. package/src/features/spa/instrument/index.js +0 -3
  129. package/src/features/utils/__mocks__/agent-session.js +1 -0
  130. package/src/features/utils/__mocks__/feature-base.js +11 -0
  131. package/src/features/utils/agent-session.js +1 -7
  132. package/src/features/utils/instrument-base.js +11 -14
  133. package/src/features/utils/nr1-debugger.js +22 -0
  134. package/src/loaders/agent.js +4 -0
  135. package/src/loaders/api/apiAsync.js +5 -4
  136. package/dist/cjs/common/storage/first-party-cookies.js +0 -36
  137. package/dist/esm/common/storage/first-party-cookies.js +0 -29
  138. package/dist/types/common/storage/first-party-cookies.d.ts +0 -8
  139. package/dist/types/common/storage/first-party-cookies.d.ts.map +0 -1
  140. package/src/common/storage/first-party-cookies.js +0 -32
@@ -1 +1 @@
1
- {"version":3,"file":"apiAsync.d.ts","sourceRoot":"","sources":["../../../../src/loaders/api/apiAsync.js"],"names":[],"mappings":"AAQA,mDA8CC"}
1
+ {"version":3,"file":"apiAsync.d.ts","sourceRoot":"","sources":["../../../../src/loaders/api/apiAsync.js"],"names":[],"mappings":"AASA,mDA8CC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@newrelic/browser-agent",
3
- "version": "1.256.1",
3
+ "version": "1.258.0",
4
4
  "private": false,
5
5
  "author": "New Relic Browser Agent Team <browser-agent@newrelic.com>",
6
6
  "description": "New Relic Browser Agent",
@@ -11,14 +11,15 @@ export function getModeledObject (obj, model) {
11
11
  )
12
12
  const target = Object.keys(output).length === 0 ? obj : output
13
13
  for (let key in target) {
14
- if (obj[key] !== undefined) {
15
- try {
16
- if (Array.isArray(obj[key]) && Array.isArray(model[key])) output[key] = Array.from(new Set([...obj[key], ...model[key]]))
17
- else if (typeof obj[key] === 'object' && typeof model[key] === 'object') output[key] = getModeledObject(obj[key], model[key])
18
- else output[key] = obj[key]
19
- } catch (e) {
20
- warn('An error occurred while setting a property of a Configurable', e)
21
- }
14
+ if (obj[key] === undefined) continue
15
+ try {
16
+ if (obj[key] === null) { output[key] = null; continue }
17
+
18
+ if (Array.isArray(obj[key]) && Array.isArray(model[key])) output[key] = Array.from(new Set([...obj[key], ...model[key]]))
19
+ else if (typeof obj[key] === 'object' && typeof model[key] === 'object') output[key] = getModeledObject(obj[key], model[key])
20
+ else output[key] = obj[key]
21
+ } catch (e) {
22
+ warn('An error occurred while setting a property of a Configurable', e)
22
23
  }
23
24
  }
24
25
  return output
@@ -46,7 +46,6 @@ const model = () => {
46
46
  allowed_origins: undefined
47
47
  },
48
48
  session: {
49
- domain: undefined, // used by first party cookies to set the top-level domain
50
49
  expiresMs: DEFAULT_EXPIRES_MS,
51
50
  inactiveMs: DEFAULT_INACTIVE_MS
52
51
  },
@@ -1,19 +1,21 @@
1
1
  import { getModeledObject } from './configurable'
2
2
  import { getNREUMInitializedAgent } from '../../window/nreum'
3
- import { globalScope } from '../../constants/runtime'
3
+ import { globalScope, originTime } from '../../constants/runtime'
4
4
  import { BUILD_ENV, DIST_METHOD, VERSION } from '../../constants/env'
5
5
 
6
- const model = {
6
+ const readonly = {
7
7
  buildEnv: BUILD_ENV,
8
+ distMethod: DIST_METHOD,
9
+ version: VERSION,
10
+ originTime
11
+ }
12
+
13
+ const model = {
8
14
  customTransaction: undefined,
9
15
  disabled: false,
10
- distMethod: DIST_METHOD,
11
16
  isolatedBacklog: false,
12
17
  loaderType: undefined,
13
18
  maxBytes: 30000,
14
- // The "timeOrigin" property is the new standard timestamp property shared across main frame and workers, but is not supported in some early Safari browsers (safari<15) + IE
15
- // ingest expects an integer value, and timeOrigin can return a float.
16
- offset: Math.floor(globalScope?.performance?.timeOrigin || globalScope?.performance?.timing?.navigationStart || Date.now()),
17
19
  onerror: undefined,
18
20
  origin: '' + globalScope.location,
19
21
  ptid: undefined,
@@ -21,8 +23,6 @@ const model = {
21
23
  /** Agent-specific metadata found in the RUM call response. ex. entityGuid */
22
24
  appMetadata: {},
23
25
  session: undefined,
24
- xhrWrappable: typeof globalScope.XMLHttpRequest?.prototype?.addEventListener === 'function',
25
- version: VERSION,
26
26
  denyList: undefined,
27
27
  harvestCount: 0,
28
28
  timeKeeper: undefined
@@ -38,7 +38,10 @@ export function getRuntime (id) {
38
38
 
39
39
  export function setRuntime (id, obj) {
40
40
  if (!id) throw new Error('All runtime objects require an agent identifier!')
41
- _cache[id] = getModeledObject(obj, model)
41
+ _cache[id] = {
42
+ ...getModeledObject(obj, model),
43
+ ...readonly
44
+ }
42
45
  const agentInst = getNREUMInitializedAgent(id)
43
46
  if (agentInst) agentInst.runtime = _cache[id]
44
47
  }
@@ -6,3 +6,5 @@ export const isiOS = false
6
6
  export const iOSBelow16 = false
7
7
  export const ffVersion = 0
8
8
  export const supportsSendBeacon = true
9
+
10
+ export const originTime = Date.now()
@@ -74,4 +74,9 @@ export const isIE = Boolean(isBrowserScope && window.document.documentMode) // d
74
74
 
75
75
  export const supportsSendBeacon = !!globalScope.navigator?.sendBeacon
76
76
 
77
- export const offset = Math.floor(Date.now() - performance.now())
77
+ /**
78
+ * Represents the absolute timestamp in milliseconds that the page was loaded
79
+ * according to the browser's local clock.
80
+ * @type {number}
81
+ */
82
+ export const originTime = Math.floor(Date.now() - performance.now())
@@ -0,0 +1,2 @@
1
+ export const drain = jest.fn()
2
+ export const registerDrain = jest.fn()
@@ -103,7 +103,7 @@ export class Harvest extends SharedContext {
103
103
  if (customUrl) url = customUrl
104
104
  if (raw) url = `${protocol}://${perceviedBeacon}/${endpoint}`
105
105
 
106
- const baseParams = !raw && includeBaseParams ? this.baseQueryString(qs) : ''
106
+ const baseParams = !raw && includeBaseParams ? this.baseQueryString(qs, endpoint) : ''
107
107
  let payloadParams = encodeObj(qs, agentRuntime.maxBytes)
108
108
  if (!submitMethod) {
109
109
  submitMethod = submitData.getSubmitMethod({ isFinalHarvest: opts.unload })
@@ -167,14 +167,15 @@ export class Harvest extends SharedContext {
167
167
  }
168
168
 
169
169
  // The stuff that gets sent every time.
170
- baseQueryString (qs) {
170
+ baseQueryString (qs, endpoint) {
171
171
  const runtime = getRuntime(this.sharedContext.agentIdentifier)
172
172
  const info = getInfo(this.sharedContext.agentIdentifier)
173
173
 
174
174
  const location = cleanURL(getLocation())
175
175
  const ref = this.obfuscator.shouldObfuscate() ? this.obfuscator.obfuscateString(location) : location
176
+ const hr = runtime?.session?.state.sessionReplayMode === 1 && endpoint !== 'jserrors'
176
177
 
177
- return ([
178
+ const qps = [
178
179
  'a=' + info.applicationID,
179
180
  encodeParam('sa', (info.sa ? '' + info.sa : '')),
180
181
  encodeParam('v', VERSION),
@@ -184,9 +185,10 @@ export class Harvest extends SharedContext {
184
185
  '&ck=0', // ck param DEPRECATED - still expected by backend
185
186
  '&s=' + (runtime.session?.state.value || '0'), // the 0 id encaps all untrackable and default traffic
186
187
  encodeParam('ref', ref),
187
- encodeParam('ptid', (runtime.ptid ? '' + runtime.ptid : '')),
188
- encodeParam('hr', (runtime?.session?.state.sessionReplayMode === 1 ? '1' : '0'), qs) // hasReplay
189
- ].join(''))
188
+ encodeParam('ptid', (runtime.ptid ? '' + runtime.ptid : ''))
189
+ ]
190
+ if (hr) qps.push(encodeParam('hr', '1', qs))
191
+ return qps.join('')
190
192
  }
191
193
 
192
194
  /**
@@ -4,6 +4,7 @@ export const DEFAULT_EXPIRES_MS = 14400000
4
4
  export const DEFAULT_INACTIVE_MS = 1800000
5
5
 
6
6
  export const SESSION_EVENTS = {
7
+ STARTED: 'session-started',
7
8
  PAUSE: 'session-pause',
8
9
  RESET: 'session-reset',
9
10
  RESUME: 'session-resume',
@@ -24,6 +24,7 @@ const model = {
24
24
  sessionReplaySentFirstChunk: false,
25
25
  sessionTraceMode: MODE.OFF,
26
26
  traceHarvestStarted: false,
27
+ serverTimeDiff: null, // set by TimeKeeper; "undefined" value will not be stringified and stored but "null" will
27
28
  custom: {}
28
29
  }
29
30
 
@@ -135,6 +136,7 @@ export class SessionEntity {
135
136
  else this.sync(initialRead)
136
137
 
137
138
  this.initialized = true
139
+ this.ee.emit(SESSION_EVENTS.STARTED, [this.isNew])
138
140
  }
139
141
 
140
142
  // This is the actual key appended to the storage API
@@ -1,3 +1,8 @@
1
+ import { originTime } from '../constants/runtime'
2
+ import { ee as baseEE } from '../event-emitter/contextual-ee'
3
+ import { getRuntime } from '../config/config'
4
+ import { SESSION_EVENT_TYPES, SESSION_EVENTS } from '../session/constants'
5
+
1
6
  /**
2
7
  * Class used to adjust the timestamp of harvested data to New Relic server time. This
3
8
  * is done by tracking the performance timings of the RUM call and applying a calculation
@@ -5,10 +10,10 @@
5
10
  */
6
11
  export class TimeKeeper {
7
12
  /**
8
- * Represents the browser origin time.
9
- * @type {number}
13
+ * Pointer to the current agent session if it exists.
14
+ * @type {import('../session/session-entity').SessionEntity}
10
15
  */
11
- #originTime
16
+ #session
12
17
 
13
18
  /**
14
19
  * Represents the browser origin time corrected to NR server time.
@@ -30,18 +35,25 @@ export class TimeKeeper {
30
35
  */
31
36
  #ready = false
32
37
 
33
- constructor () {
34
- this.#originTime = Date.now() - performance.now()
38
+ constructor (agentIdentifier) {
39
+ this.#session = getRuntime(agentIdentifier)?.session
40
+
41
+ if (this.#session) {
42
+ const ee = baseEE.get(agentIdentifier)
43
+ ee.on(SESSION_EVENTS.UPDATE, this.#processSessionUpdate.bind(this))
44
+ ee.on(SESSION_EVENTS.STARTED, () => {
45
+ if (this.#ready) {
46
+ this.#session.write({ serverTimeDiff: this.#localTimeDiff })
47
+ }
48
+ })
49
+ this.#processSessionUpdate(null, this.#session.read())
50
+ }
35
51
  }
36
52
 
37
53
  get ready () {
38
54
  return this.#ready
39
55
  }
40
56
 
41
- get originTime () {
42
- return this.#originTime
43
- }
44
-
45
57
  get correctedOriginTime () {
46
58
  return this.#correctedOriginTime
47
59
  }
@@ -53,6 +65,8 @@ export class TimeKeeper {
53
65
  * @param endTime {number} The end time of the RUM request
54
66
  */
55
67
  processRumRequest (rumRequest, startTime, endTime) {
68
+ if (this.#ready) return // Server time calculated from session entity
69
+
56
70
  const responseDateHeader = rumRequest.getResponseHeader('Date')
57
71
  if (!responseDateHeader) {
58
72
  throw new Error('Missing date header on rum response.')
@@ -63,12 +77,13 @@ export class TimeKeeper {
63
77
 
64
78
  // Corrected page origin time
65
79
  this.#correctedOriginTime = Math.floor(Date.parse(responseDateHeader) - serverOffset)
66
- this.#localTimeDiff = this.#originTime - this.#correctedOriginTime
80
+ this.#localTimeDiff = originTime - this.#correctedOriginTime
67
81
 
68
82
  if (Number.isNaN(this.#correctedOriginTime)) {
69
83
  throw new Error('Date header invalid format.')
70
84
  }
71
85
 
86
+ if (this.#session) this.#session.write({ serverTimeDiff: this.#localTimeDiff })
72
87
  this.#ready = true
73
88
  }
74
89
 
@@ -90,4 +105,23 @@ export class TimeKeeper {
90
105
  correctAbsoluteTimestamp (timestamp) {
91
106
  return Math.floor(timestamp - this.#localTimeDiff)
92
107
  }
108
+
109
+ /**
110
+ * Processes a session entity update payload to extract the server time calculated.
111
+ * @param {import('../session/constants').SESSION_EVENT_TYPES | null} type
112
+ * @param {Object} data
113
+ */
114
+ #processSessionUpdate (type, data) {
115
+ if (typeof data?.serverTimeDiff !== 'number') return
116
+
117
+ if (
118
+ (!type && !this.#ready) || // This captures the initial read from the session entity when the timekeeper first initializes
119
+ type === SESSION_EVENT_TYPES.CROSS_TAB // This captures any cross-tab write of the session entity
120
+ ) {
121
+ // This captures the initial read from the session entity when the timekeeper first initializes
122
+ this.#localTimeDiff = data.serverTimeDiff
123
+ this.#correctedOriginTime = originTime - this.#localTimeDiff
124
+ this.#ready = true
125
+ }
126
+ }
93
127
  }
@@ -1,4 +1,4 @@
1
- import { globalScope, isBrowserScope, isiOS, offset } from '../constants/runtime'
1
+ import { globalScope, isBrowserScope, isiOS, originTime } from '../constants/runtime'
2
2
  import { VITAL_NAMES } from './constants'
3
3
  import { VitalMetric } from './vital-metric'
4
4
  import { onTTFB } from 'web-vitals/attribution'
@@ -14,7 +14,7 @@ if (isBrowserScope && typeof PerformanceNavigationTiming !== 'undefined' && !isi
14
14
  if (!timeToFirstByte.isValid) {
15
15
  const entry = {}
16
16
  // convert real timestamps to relative timestamps to match web-vitals behavior
17
- for (let key in globalScope?.performance?.timing || {}) entry[key] = Math.max(globalScope?.performance?.timing[key] - offset, 0)
17
+ for (let key in globalScope?.performance?.timing || {}) entry[key] = Math.max(globalScope?.performance?.timing[key] - originTime, 0)
18
18
 
19
19
  // ttfb is equiv to document's responseStart property in timing API --> https://developer.mozilla.org/en-US/docs/Web/API/PerformanceTiming/responseStart
20
20
  timeToFirstByte.update({ value: entry.responseStart, attrs: { navigationEntry: entry } })
@@ -9,7 +9,7 @@ export class VitalMetric {
9
9
  }
10
10
 
11
11
  update ({ value, attrs = {} }) {
12
- if (value < 0) return
12
+ if (value === undefined || value === null || value < 0) return
13
13
  const state = {
14
14
  value: this.roundingMethod(value),
15
15
  name: this.name,
@@ -0,0 +1,3 @@
1
+ export const checkState = jest.fn()
2
+ export const onWindowLoad = jest.fn()
3
+ export const onDOMContentLoaded = jest.fn()
@@ -0,0 +1,51 @@
1
+ import { nullable, numeric, getAddStringContext, addCustomAttributes } from '../../../common/serialize/bel-serializer'
2
+ import { getInfo } from '../../../common/config/config'
3
+
4
+ export default class Chunk {
5
+ constructor (events, aggregateInstance) {
6
+ this.addString = getAddStringContext(aggregateInstance.agentIdentifier) // pass agentIdentifier here
7
+ this.events = events
8
+ this.payload = 'bel.7;'
9
+
10
+ for (let i = 0; i < events.length; i++) {
11
+ const event = events[i]
12
+ const fields = [
13
+ numeric(event.startTime),
14
+ numeric(event.endTime - event.startTime),
15
+ numeric(0), // callbackEnd
16
+ numeric(0), // no callbackDuration for non-SPA events
17
+ this.addString(event.method),
18
+ numeric(event.status),
19
+ this.addString(event.domain),
20
+ this.addString(event.path),
21
+ numeric(event.requestSize),
22
+ numeric(event.responseSize),
23
+ event.type === 'fetch' ? 1 : '',
24
+ this.addString(0), // nodeId
25
+ nullable(event.spanId, this.addString, true) + // guid
26
+ nullable(event.traceId, this.addString, true) + // traceId
27
+ nullable(event.spanTimestamp, numeric, false) // timestamp
28
+ ]
29
+
30
+ let insert = '2,'
31
+
32
+ // Since configuration objects (like info) are created new each time they are set, we have to grab the current pointer to the attr object here.
33
+ const jsAttributes = getInfo(aggregateInstance.agentIdentifier).jsAttributes
34
+
35
+ // add custom attributes
36
+ // gql decorators are added as custom attributes to alleviate need for new BEL schema
37
+ const attrParts = addCustomAttributes({ ...(jsAttributes || {}), ...(event.gql || {}) }, this.addString)
38
+ fields.unshift(numeric(attrParts.length))
39
+
40
+ insert += fields.join(',')
41
+ if (attrParts && attrParts.length > 0) {
42
+ insert += ';' + attrParts.join(';')
43
+ }
44
+ if ((i + 1) < events.length) insert += ';'
45
+
46
+ this.payload += insert
47
+ }
48
+
49
+ this.tooBig = this.payload.length * 2 > aggregateInstance.MAX_PAYLOAD_SIZE
50
+ }
51
+ }