@cloudcare/browser-core 2.0.4 → 2.0.5

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 (97) hide show
  1. package/cjs/browser/addEventListener.js +57 -0
  2. package/cjs/browser/pageExitObservable.js +4 -2
  3. package/cjs/browser/runOnReadyState.js +21 -0
  4. package/cjs/browser/xhrObservable.js +7 -5
  5. package/cjs/configuration/configuration.js +5 -3
  6. package/cjs/console/consoleObservable.js +3 -1
  7. package/cjs/dataMap.js +1 -0
  8. package/cjs/helper/byteUtils.js +26 -0
  9. package/cjs/helper/contextHistory.js +4 -2
  10. package/cjs/helper/createEventRateLimiter.js +3 -1
  11. package/cjs/helper/errorTools.js +4 -2
  12. package/cjs/helper/instrumentMethod.js +2 -4
  13. package/cjs/helper/serialisation/contextManager.js +84 -0
  14. package/cjs/helper/serialisation/heavyCustomerDataWarning.js +34 -0
  15. package/cjs/helper/serialisation/jsonStringify.js +57 -0
  16. package/cjs/helper/serialisation/rowData.js +50 -0
  17. package/cjs/helper/timer.js +29 -0
  18. package/cjs/helper/tools.js +10 -331
  19. package/cjs/index.js +117 -0
  20. package/cjs/init.js +6 -6
  21. package/cjs/report/reportObservable.js +4 -2
  22. package/cjs/session/sessionCookieStore.js +3 -1
  23. package/cjs/session/sessionManagement.js +10 -5
  24. package/cjs/session/sessionStore.js +10 -4
  25. package/cjs/transport/batch.js +40 -91
  26. package/cjs/transport/flushController.js +121 -0
  27. package/cjs/transport/httpRequest.js +6 -2
  28. package/cjs/transport/index.js +9 -1
  29. package/cjs/transport/sendWithRetryStrategy.js +8 -4
  30. package/cjs/transport/startBatchWithReplica.js +10 -2
  31. package/cjs/user/index.js +18 -0
  32. package/cjs/user/user.js +42 -0
  33. package/esm/browser/addEventListener.js +46 -0
  34. package/esm/browser/pageExitObservable.js +2 -1
  35. package/esm/browser/runOnReadyState.js +12 -0
  36. package/esm/browser/xhrObservable.js +6 -5
  37. package/esm/configuration/configuration.js +2 -1
  38. package/esm/console/consoleObservable.js +2 -1
  39. package/esm/dataMap.js +1 -0
  40. package/esm/helper/byteUtils.js +16 -0
  41. package/esm/helper/contextHistory.js +1 -0
  42. package/esm/helper/createEventRateLimiter.js +1 -0
  43. package/esm/helper/errorTools.js +2 -1
  44. package/esm/helper/instrumentMethod.js +1 -3
  45. package/esm/helper/serialisation/contextManager.js +70 -0
  46. package/esm/helper/serialisation/heavyCustomerDataWarning.js +20 -0
  47. package/esm/helper/serialisation/jsonStringify.js +46 -0
  48. package/esm/helper/serialisation/rowData.js +33 -0
  49. package/esm/helper/timer.js +14 -0
  50. package/esm/helper/tools.js +1 -292
  51. package/esm/index.js +10 -1
  52. package/esm/init.js +6 -6
  53. package/esm/report/reportObservable.js +3 -2
  54. package/esm/session/sessionCookieStore.js +1 -0
  55. package/esm/session/sessionManagement.js +5 -2
  56. package/esm/session/sessionStore.js +7 -2
  57. package/esm/transport/batch.js +31 -85
  58. package/esm/transport/flushController.js +114 -0
  59. package/esm/transport/httpRequest.js +5 -2
  60. package/esm/transport/index.js +2 -1
  61. package/esm/transport/sendWithRetryStrategy.js +3 -1
  62. package/esm/transport/startBatchWithReplica.js +9 -2
  63. package/esm/user/index.js +1 -0
  64. package/esm/user/user.js +32 -0
  65. package/package.json +22 -22
  66. package/src/browser/addEventListener.js +50 -0
  67. package/src/browser/pageExitObservable.js +4 -5
  68. package/src/browser/runOnReadyState.js +16 -0
  69. package/src/browser/xhrObservable.js +57 -32
  70. package/src/configuration/configuration.js +1 -1
  71. package/src/console/consoleObservable.js +22 -12
  72. package/src/dataMap.js +1 -0
  73. package/src/helper/byteUtils.js +17 -0
  74. package/src/helper/contextHistory.js +1 -1
  75. package/src/helper/createEventRateLimiter.js +7 -9
  76. package/src/helper/errorTools.js +2 -1
  77. package/src/helper/instrumentMethod.js +1 -2
  78. package/src/helper/serialisation/contextManager.js +79 -0
  79. package/src/helper/serialisation/heavyCustomerDataWarning.js +28 -0
  80. package/src/helper/serialisation/jsonStringify.js +47 -0
  81. package/src/helper/serialisation/rowData.js +41 -0
  82. package/src/helper/timer.js +21 -0
  83. package/src/helper/tools.js +2 -301
  84. package/src/index.js +11 -0
  85. package/src/init.js +6 -6
  86. package/src/report/reportObservable.js +40 -25
  87. package/src/session/sessionCookieStore.js +24 -14
  88. package/src/session/sessionManagement.js +34 -17
  89. package/src/session/sessionStore.js +7 -2
  90. package/src/transport/batch.js +39 -91
  91. package/src/transport/flushController.js +115 -0
  92. package/src/transport/httpRequest.js +13 -6
  93. package/src/transport/index.js +1 -0
  94. package/src/transport/sendWithRetryStrategy.js +3 -7
  95. package/src/transport/startBatchWithReplica.js +11 -7
  96. package/src/user/index.js +1 -0
  97. package/src/user/user.js +31 -0
@@ -1,63 +1,68 @@
1
- import { relativeNow, clocksOrigin, ONE_MINUTE, each, addEventListeners, addEventListener } from '../helper/tools'
1
+ import { relativeNow, clocksOrigin, ONE_MINUTE, each } from '../helper/tools'
2
+ import {
3
+ addEventListeners,
4
+ addEventListener
5
+ } from '../browser/addEventListener'
2
6
  import { ContextHistory } from '../helper/contextHistory'
3
7
  import { startSessionStore } from './sessionStore'
4
8
  import { SESSION_TIME_OUT_DELAY } from './sessionConstants'
5
9
  import { DOM_EVENT } from '../helper/enums'
6
-
10
+ import { clearInterval, setInterval } from '../helper/timer'
7
11
  export var VISIBILITY_CHECK_DELAY = ONE_MINUTE
8
12
  var SESSION_CONTEXT_TIMEOUT_DELAY = SESSION_TIME_OUT_DELAY
9
13
  var stopCallbacks = []
10
14
 
11
- export var startSessionManager = function(
15
+ export var startSessionManager = function (
12
16
  options,
13
17
  productKey,
14
18
  computeSessionState
15
19
  ) {
16
20
  var sessionStore = startSessionStore(options, productKey, computeSessionState)
17
- stopCallbacks.push(function() {
21
+ stopCallbacks.push(function () {
18
22
  return sessionStore.stop()
19
23
  })
20
24
 
21
25
  var sessionContextHistory = new ContextHistory(SESSION_CONTEXT_TIMEOUT_DELAY)
22
- stopCallbacks.push(function() {
26
+ stopCallbacks.push(function () {
23
27
  return sessionContextHistory.stop()
24
28
  })
25
29
 
26
- sessionStore.renewObservable.subscribe(function(){
30
+ sessionStore.renewObservable.subscribe(function () {
27
31
  sessionContextHistory.add(buildSessionContext(), relativeNow())
28
32
  })
29
- sessionStore.expireObservable.subscribe(function() {
33
+ sessionStore.expireObservable.subscribe(function () {
30
34
  sessionContextHistory.closeActive(relativeNow())
31
35
  })
32
36
 
33
37
  sessionStore.expandOrRenewSession()
34
38
  sessionContextHistory.add(buildSessionContext(), clocksOrigin().relative)
35
39
 
36
- trackActivity(function() {
40
+ trackActivity(function () {
37
41
  return sessionStore.expandOrRenewSession()
38
42
  })
39
- trackVisibility(function() {
43
+ trackVisibility(function () {
40
44
  return sessionStore.expandSession()
41
45
  })
42
46
 
43
47
  function buildSessionContext() {
44
48
  return {
45
49
  id: sessionStore.getSession().id,
46
- trackingType: sessionStore.getSession()[productKey],
50
+ trackingType: sessionStore.getSession()[productKey]
47
51
  }
48
52
  }
49
53
 
50
54
  return {
51
- findActiveSession: function(startTime) {
55
+ findActiveSession: function (startTime) {
52
56
  return sessionContextHistory.find(startTime)
53
57
  },
54
58
  renewObservable: sessionStore.renewObservable,
55
59
  expireObservable: sessionStore.expireObservable,
60
+ expire: sessionStore.expire
56
61
  }
57
62
  }
58
63
 
59
64
  export function stopSessionManager() {
60
- each(stopCallbacks, function(e) {
65
+ each(stopCallbacks, function (e) {
61
66
  return e()
62
67
  })
63
68
  stopCallbacks = []
@@ -66,7 +71,12 @@ export function stopSessionManager() {
66
71
  function trackActivity(expandOrRenewSession) {
67
72
  var _addEventListeners = addEventListeners(
68
73
  window,
69
- [DOM_EVENT.CLICK,DOM_EVENT.TOUCH_START, DOM_EVENT.KEY_DOWN, DOM_EVENT.SCROLL],
74
+ [
75
+ DOM_EVENT.CLICK,
76
+ DOM_EVENT.TOUCH_START,
77
+ DOM_EVENT.KEY_DOWN,
78
+ DOM_EVENT.SCROLL
79
+ ],
70
80
  expandOrRenewSession,
71
81
  { capture: true, passive: true }
72
82
  )
@@ -74,16 +84,23 @@ function trackActivity(expandOrRenewSession) {
74
84
  }
75
85
 
76
86
  function trackVisibility(expandSession) {
77
- var expandSessionWhenVisible = function() {
87
+ var expandSessionWhenVisible = function () {
78
88
  if (document.visibilityState === 'visible') {
79
89
  expandSession()
80
90
  }
81
91
  }
82
- var _addEventListener= addEventListener(document, DOM_EVENT.VISIBILITY_CHANGE, expandSessionWhenVisible)
92
+ var _addEventListener = addEventListener(
93
+ document,
94
+ DOM_EVENT.VISIBILITY_CHANGE,
95
+ expandSessionWhenVisible
96
+ )
83
97
  stopCallbacks.push(_addEventListener.stop)
84
98
 
85
- var visibilityCheckInterval = setInterval(expandSessionWhenVisible, VISIBILITY_CHECK_DELAY)
86
- stopCallbacks.push(function() {
99
+ var visibilityCheckInterval = setInterval(
100
+ expandSessionWhenVisible,
101
+ VISIBILITY_CHECK_DELAY
102
+ )
103
+ stopCallbacks.push(function () {
87
104
  clearInterval(visibilityCheckInterval)
88
105
  })
89
106
  }
@@ -3,6 +3,7 @@ import { Observable } from '../helper/observable'
3
3
  import { dateNow, UUID, throttle } from '../helper/tools'
4
4
  import { SESSION_TIME_OUT_DELAY } from './sessionConstants'
5
5
  import { retrieveSession, withCookieLockAccess } from './sessionCookieStore'
6
+ import { clearInterval, setInterval } from '../helper/timer'
6
7
 
7
8
  /**
8
9
  * Different session concepts:
@@ -67,7 +68,7 @@ export function startSessionStore(options, productKey, computeSessionState) {
67
68
  }
68
69
  if (hasSessionInCache()) {
69
70
  if (isSessionInCacheOutdated(cookieSession)) {
70
- expireSession()
71
+ expireSessionInCache()
71
72
  } else {
72
73
  sessionCache = cookieSession
73
74
  }
@@ -98,7 +99,7 @@ export function startSessionStore(options, productKey, computeSessionState) {
98
99
  )
99
100
  }
100
101
 
101
- function expireSession() {
102
+ function expireSessionInCache() {
102
103
  sessionCache = {}
103
104
  expireObservable.notify()
104
105
  }
@@ -135,6 +136,10 @@ export function startSessionStore(options, productKey, computeSessionState) {
135
136
  },
136
137
  renewObservable: renewObservable,
137
138
  expireObservable: expireObservable,
139
+ expire: function () {
140
+ deleteSessionCookie(options)
141
+ synchronizeSession({})
142
+ },
138
143
  stop: function () {
139
144
  clearInterval(watchSessionTimeoutId)
140
145
  }
@@ -1,10 +1,7 @@
1
1
  import { display } from '../helper/display'
2
2
  import {
3
- addEventListener,
4
- noop,
5
3
  values,
6
4
  findByPath,
7
- escapeRowData,
8
5
  each,
9
6
  isNumber,
10
7
  isArray,
@@ -13,12 +10,17 @@ import {
13
10
  toServerDuration,
14
11
  isBoolean,
15
12
  isEmptyObject,
16
- isObject,
17
- escapeJsonValue,
18
- escapeRowField
13
+ isObject
19
14
  } from '../helper/tools'
15
+ import {
16
+ escapeJsonValue,
17
+ escapeRowField,
18
+ escapeRowData
19
+ } from '../helper/serialisation/rowData'
20
20
  import { commonTags, dataMap, commonFields } from '../dataMap'
21
- import { DOM_EVENT, RumEventType } from '../helper/enums'
21
+ import { RumEventType } from '../helper/enums'
22
+ import { computeBytesCount } from '../helper/byteUtils'
23
+ import { isPageExitReason } from '../browser/pageExitObservable'
22
24
  // https://en.wikipedia.org/wiki/UTF-8
23
25
  // eslint-disable-next-line no-control-regex
24
26
  var HAS_MULTI_BYTES_CHARACTERS = /[^\u0000-\u007F]/
@@ -133,28 +135,16 @@ export var processedMessageByDataMap = function (message) {
133
135
  rowData: hasFileds ? rowData : undefined
134
136
  }
135
137
  }
136
- var batch = function (
137
- request,
138
- batchMessagesLimit,
139
- batchBytesLimit,
140
- messageBytesLimit,
141
- flushTimeout,
142
- pageExitObservable
143
- ) {
138
+ var batch = function (request, flushController, messageBytesLimit) {
144
139
  this.pushOnlyBuffer = []
145
140
  this.upsertBuffer = {}
146
- this.bufferBytesCount = 0
147
- this.bufferMessagesCount = 0
148
141
  this.request = request
149
- this.batchMessagesLimit = batchMessagesLimit
150
- this.batchBytesLimit = batchBytesLimit
142
+ this.flushController = flushController
151
143
  this.messageBytesLimit = messageBytesLimit
152
- this.flushTimeout = flushTimeout
153
144
  var _this = this
154
- pageExitObservable.subscribe(function () {
155
- _this.flush(_this.request.sendOnExit)
145
+ this.flushController.flushObservable.subscribe(function (event) {
146
+ _this.flush(event)
156
147
  })
157
- this.flushPeriodically()
158
148
  }
159
149
  batch.prototype.add = function (message) {
160
150
  this.addOrUpdate(message)
@@ -162,37 +152,24 @@ batch.prototype.add = function (message) {
162
152
  batch.prototype.upsert = function (message, key) {
163
153
  this.addOrUpdate(message, key)
164
154
  }
165
- batch.prototype.flush = function (sendFn) {
166
- if (typeof sendFn !== 'function') {
167
- sendFn = this.request.send
168
- }
169
- if (this.bufferMessagesCount !== 0) {
170
- var messages = this.pushOnlyBuffer.concat(values(this.upsertBuffer))
171
- var bytesCount = this.bufferBytesCount
172
- this.pushOnlyBuffer = []
173
- this.upsertBuffer = {}
174
- this.bufferBytesCount = 0
175
- this.bufferMessagesCount = 0
176
- if (messages.length > 0) {
177
- sendFn({ data: messages.join('\n'), bytesCount: bytesCount })
155
+ batch.prototype.flush = function (event) {
156
+ var messages = this.pushOnlyBuffer.concat(values(this.upsertBuffer))
157
+ this.pushOnlyBuffer = []
158
+ this.upsertBuffer = {}
159
+ if (messages.length > 0) {
160
+ var payload = {
161
+ data: messages.join('\n'),
162
+ bytesCount: event.bytesCount,
163
+ flushReason: event.reason
164
+ }
165
+ if (isPageExitReason(event.reason)) {
166
+ this.request.sendOnExit(payload)
167
+ } else {
168
+ this.request.send(payload)
178
169
  }
179
170
  }
180
171
  }
181
- batch.prototype.flushOnExit = function () {
182
- this.flush(this.request.sendOnExit)
183
- }
184
- batch.prototype.computeBytesCount = function (candidate) {
185
- // Accurate bytes count computations can degrade performances when there is a lot of events to process
186
- if (!HAS_MULTI_BYTES_CHARACTERS.test(candidate)) {
187
- return candidate.length
188
- }
189
-
190
- if (window.TextEncoder !== undefined) {
191
- return new TextEncoder().encode(candidate).length
192
- }
193
172
 
194
- return new Blob([candidate]).size
195
- }
196
173
  batch.prototype.addOrUpdate = function (message, key) {
197
174
  var _process = this.process(message)
198
175
  var processedMessage = _process.processedMessage
@@ -208,18 +185,11 @@ batch.prototype.addOrUpdate = function (message, key) {
208
185
  if (this.hasMessageFor(key)) {
209
186
  this.remove(key)
210
187
  }
211
- if (this.willReachedBytesLimitWith(messageBytesCount)) {
212
- this.flush()
213
- }
214
-
215
188
  this.push(processedMessage, messageBytesCount, key)
216
- if (this.isFull()) {
217
- this.flush()
218
- }
219
189
  }
220
190
  batch.prototype.process = function (message) {
221
191
  var processedMessage = processedMessageByDataMap(message).rowStr
222
- var messageBytesCount = this.computeBytesCount(processedMessage)
192
+ var messageBytesCount = computeBytesCount(processedMessage)
223
193
  return {
224
194
  processedMessage: processedMessage,
225
195
  messageBytesCount: messageBytesCount
@@ -227,52 +197,30 @@ batch.prototype.process = function (message) {
227
197
  }
228
198
 
229
199
  batch.prototype.push = function (processedMessage, messageBytesCount, key) {
230
- if (this.bufferMessagesCount > 0) {
231
- // \n separator at serialization
232
- this.bufferBytesCount += 1
233
- }
200
+ var separatorBytesCount = this.flushController.getMessagesCount() > 0 ? 1 : 0
201
+ this.flushController.notifyBeforeAddMessage(
202
+ messageBytesCount + separatorBytesCount
203
+ )
234
204
  if (key !== undefined) {
235
205
  this.upsertBuffer[key] = processedMessage
236
206
  } else {
237
207
  this.pushOnlyBuffer.push(processedMessage)
238
208
  }
239
- this.bufferBytesCount += messageBytesCount
240
- this.bufferMessagesCount += 1
209
+ this.flushController.notifyAfterAddMessage()
241
210
  }
242
211
 
243
212
  batch.prototype.remove = function (key) {
244
213
  var removedMessage = this.upsertBuffer[key]
245
214
  delete this.upsertBuffer[key]
246
- var messageBytesCount = this.computeBytesCount(removedMessage)
247
- this.bufferBytesCount -= messageBytesCount
248
- this.bufferMessagesCount -= 1
249
- if (this.bufferMessagesCount > 0) {
250
- this.bufferBytesCount -= 1
251
- }
215
+ var messageBytesCount = computeBytesCount(removedMessage)
216
+ // If there are other messages, a '\n' will be added at serialization
217
+ var separatorBytesCount = this.flushController.getMessagesCount() > 1 ? 1 : 0
218
+ this.flushController.notifyAfterRemoveMessage(
219
+ messageBytesCount + separatorBytesCount
220
+ )
252
221
  }
253
222
 
254
223
  batch.prototype.hasMessageFor = function (key) {
255
224
  return key !== undefined && this.upsertBuffer[key] !== undefined
256
225
  }
257
-
258
- batch.prototype.willReachedBytesLimitWith = function (messageBytesCount) {
259
- // byte of the separator at the end of the message
260
- return this.bufferBytesCount + messageBytesCount + 1 >= this.batchBytesLimit
261
- }
262
-
263
- batch.prototype.isFull = function () {
264
- return (
265
- this.bufferMessagesCount === this.batchMessagesLimit ||
266
- this.bufferBytesCount >= this.batchBytesLimit
267
- )
268
- }
269
-
270
- batch.prototype.flushPeriodically = function () {
271
- var _this = this
272
- setTimeout(function () {
273
- _this.flush()
274
- _this.flushPeriodically()
275
- }, this.flushTimeout)
276
- }
277
-
278
226
  export var Batch = batch
@@ -0,0 +1,115 @@
1
+ import { Observable } from '../helper/observable'
2
+ import { clearTimeout, setTimeout } from '../helper/timer'
3
+
4
+ // type FlushReason = PageExitReason | 'duration_limit' | 'bytes_limit' | 'messages_limit' | 'session_expire'
5
+
6
+ /**
7
+ * Returns a "flush controller", responsible of notifying when flushing a pool of pending data needs
8
+ * to happen. The implementation is designed to support both synchronous and asynchronous usages,
9
+ * but relies on invariants described in each method documentation to keep a coherent state.
10
+ */
11
+ export function createFlushController({
12
+ messagesLimit,
13
+ bytesLimit,
14
+ durationLimit,
15
+ pageExitObservable,
16
+ sessionExpireObservable
17
+ }) {
18
+ var flushObservable = new Observable()
19
+
20
+ pageExitObservable.subscribe(function (event) {
21
+ return flush(event.reason)
22
+ })
23
+ sessionExpireObservable.subscribe(function () {
24
+ return flush('session_expire')
25
+ })
26
+
27
+ var currentBytesCount = 0
28
+ var currentMessagesCount = 0
29
+
30
+ function flush(flushReason) {
31
+ if (currentMessagesCount === 0) {
32
+ return
33
+ }
34
+
35
+ var messagesCount = currentMessagesCount
36
+ var bytesCount = currentBytesCount
37
+
38
+ currentMessagesCount = 0
39
+ currentBytesCount = 0
40
+ cancelDurationLimitTimeout()
41
+
42
+ flushObservable.notify({
43
+ reason: flushReason,
44
+ messagesCount: messagesCount,
45
+ bytesCount: bytesCount
46
+ })
47
+ }
48
+
49
+ var durationLimitTimeoutId
50
+ function scheduleDurationLimitTimeout() {
51
+ if (durationLimitTimeoutId === undefined) {
52
+ durationLimitTimeoutId = setTimeout(function () {
53
+ flush('duration_limit')
54
+ }, durationLimit)
55
+ }
56
+ }
57
+
58
+ function cancelDurationLimitTimeout() {
59
+ clearTimeout(durationLimitTimeoutId)
60
+ durationLimitTimeoutId = undefined
61
+ }
62
+
63
+ return {
64
+ flushObservable: flushObservable,
65
+ getMessagesCount: function () {
66
+ return currentMessagesCount
67
+ },
68
+
69
+ /**
70
+ * Notifies that a message will be added to a pool of pending messages waiting to be flushed.
71
+ *
72
+ * This function needs to be called synchronously, right before adding the message, so no flush
73
+ * event can happen after `notifyBeforeAddMessage` and before adding the message.
74
+ */
75
+ notifyBeforeAddMessage: function (messageBytesCount) {
76
+ if (currentBytesCount + messageBytesCount >= bytesLimit) {
77
+ flush('bytes_limit')
78
+ }
79
+ // Consider the message to be added now rather than in `notifyAfterAddMessage`, because if no
80
+ // message was added yet and `notifyAfterAddMessage` is called asynchronously, we still want
81
+ // to notify when a flush is needed (for example on page exit).
82
+ currentMessagesCount += 1
83
+ currentBytesCount += messageBytesCount
84
+ scheduleDurationLimitTimeout()
85
+ },
86
+
87
+ /**
88
+ * Notifies that a message *was* added to a pool of pending messages waiting to be flushed.
89
+ *
90
+ * This function can be called asynchronously after the message was added, but in this case it
91
+ * should not be called if a flush event occurred in between.
92
+ */
93
+ notifyAfterAddMessage: function () {
94
+ if (currentMessagesCount >= messagesLimit) {
95
+ flush('messages_limit')
96
+ } else if (currentBytesCount >= bytesLimit) {
97
+ flush('bytes_limit')
98
+ }
99
+ },
100
+
101
+ /**
102
+ * Notifies that a message was removed from a pool of pending messages waiting to be flushed.
103
+ *
104
+ * This function needs to be called synchronously, right after removing the message, so no flush
105
+ * event can happen after removing the message and before `notifyAfterRemoveMessage`.
106
+ */
107
+ notifyAfterRemoveMessage: function (messageBytesCount) {
108
+ currentBytesCount -= messageBytesCount
109
+ currentMessagesCount -= 1
110
+ if (currentMessagesCount === 0) {
111
+ cancelDurationLimitTimeout()
112
+ }
113
+ }
114
+ }
115
+ }
@@ -1,5 +1,5 @@
1
1
  import { newRetryState, sendWithRetryStrategy } from './sendWithRetryStrategy'
2
-
2
+ import { addEventListener } from '../browser/addEventListener'
3
3
  /**
4
4
  * Use POST request without content type to:
5
5
  * - avoid CORS preflight requests
@@ -100,10 +100,17 @@ function isKeepAliveSupported() {
100
100
  function sendXHR(url, data, onResponse) {
101
101
  const request = new XMLHttpRequest()
102
102
  request.open('POST', url, true)
103
- request.send(data)
104
- request.addEventListener('loadend', function () {
105
- if (typeof onResponse === 'function') {
106
- onResponse({ status: request.status })
103
+ addEventListener(
104
+ request,
105
+ 'loadend',
106
+ function () {
107
+ if (typeof onResponse === 'function') {
108
+ onResponse({ status: request.status })
109
+ }
110
+ },
111
+ {
112
+ once: true
107
113
  }
108
- })
114
+ )
115
+ request.send(data)
109
116
  }
@@ -1,3 +1,4 @@
1
1
  export { createHttpRequest } from './httpRequest'
2
2
  export { Batch } from './batch'
3
3
  export { startBatchWithReplica } from './startBatchWithReplica'
4
+ export { createFlushController } from './flushController'
@@ -1,11 +1,7 @@
1
- import {
2
- clocksNow,
3
- ONE_KIBI_BYTE,
4
- ONE_MEBI_BYTE,
5
- ONE_SECOND
6
- } from '../helper/tools'
1
+ import { clocksNow, ONE_SECOND } from '../helper/tools'
2
+ import { ONE_MEBI_BYTE, ONE_KIBI_BYTE } from '../helper/byteUtils'
7
3
  import { ErrorSource } from '../helper/errorTools'
8
-
4
+ import { setTimeout } from '../helper/timer'
9
5
  export var MAX_ONGOING_BYTES_COUNT = 80 * ONE_KIBI_BYTE
10
6
  export var MAX_ONGOING_REQUESTS = 32
11
7
  export var MAX_QUEUE_BYTES_COUNT = 3 * ONE_MEBI_BYTE
@@ -1,11 +1,12 @@
1
1
  import { Batch } from './batch'
2
2
  import { createHttpRequest } from './httpRequest'
3
-
3
+ import { createFlushController } from './flushController'
4
4
  export function startBatchWithReplica(
5
5
  configuration,
6
6
  endpointUrl,
7
7
  reportError,
8
- pageExitObservable
8
+ pageExitObservable,
9
+ sessionExpireObservable
9
10
  ) {
10
11
  var primaryBatch = createBatch(endpointUrl)
11
12
 
@@ -16,11 +17,14 @@ export function startBatchWithReplica(
16
17
  configuration.batchBytesLimit,
17
18
  reportError
18
19
  ),
19
- configuration.batchMessagesLimit,
20
- configuration.batchBytesLimit,
21
- configuration.messageBytesLimit,
22
- configuration.flushTimeout,
23
- pageExitObservable
20
+ createFlushController({
21
+ messagesLimit: configuration.batchMessagesLimit,
22
+ bytesLimit: configuration.batchBytesLimit,
23
+ durationLimit: configuration.flushTimeout,
24
+ pageExitObservable: pageExitObservable,
25
+ sessionExpireObservable: sessionExpireObservable
26
+ }),
27
+ configuration.messageBytesLimit
24
28
  )
25
29
  }
26
30
 
@@ -0,0 +1 @@
1
+ export * from './user'
@@ -0,0 +1,31 @@
1
+ import { display } from '../helper/display'
2
+ import { assign, getType, each } from '../helper/tools'
3
+
4
+ /**
5
+ * Clone input data and ensure known user properties (id, name, email)
6
+ * are strings, as defined here:
7
+ * https://docs.datadoghq.com/logs/log_configuration/attributes_naming_convention/#user-related-attributes
8
+ */
9
+ export function sanitizeUser(newUser) {
10
+ // We shallow clone only to prevent mutation of user data.
11
+ var user = assign({}, newUser)
12
+ var keys = ['id', 'name', 'email']
13
+ each(keys, function (key) {
14
+ if (key in user) {
15
+ user[key] = String(user[key])
16
+ }
17
+ })
18
+
19
+ return user
20
+ }
21
+
22
+ /**
23
+ * Simple check to ensure user is valid
24
+ */
25
+ export function checkUser(newUser) {
26
+ var isValid = getType(newUser) === 'object'
27
+ if (!isValid) {
28
+ display.error('Unsupported user:', newUser)
29
+ }
30
+ return isValid
31
+ }