@cloudcare/browser-core 1.2.11 → 2.0.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 (61) hide show
  1. package/cjs/browser/fetchObservable.js +3 -4
  2. package/cjs/browser/htmlDomUtils.js +45 -0
  3. package/cjs/browser/pageExitObservable.js +72 -0
  4. package/cjs/configuration/configuration.js +8 -0
  5. package/cjs/configuration/transportConfiguration.js +7 -0
  6. package/cjs/dataMap.js +12 -4
  7. package/cjs/helper/deviceInfo.js +44 -2
  8. package/cjs/helper/enums.js +3 -0
  9. package/cjs/helper/getZoneJsOriginalValue.js +34 -0
  10. package/cjs/helper/instrumentMethod.js +7 -1
  11. package/cjs/helper/lifeCycle.js +2 -6
  12. package/cjs/helper/readBytesFromStream.js +74 -0
  13. package/cjs/helper/tools.js +123 -46
  14. package/cjs/index.js +52 -0
  15. package/cjs/session/sessionStore.js +1 -1
  16. package/cjs/transport/batch.js +7 -44
  17. package/cjs/transport/httpRequest.js +4 -2
  18. package/cjs/transport/sendWithRetryStrategy.js +8 -8
  19. package/cjs/transport/startBatchWithReplica.js +2 -2
  20. package/esm/browser/fetchObservable.js +3 -4
  21. package/esm/browser/htmlDomUtils.js +26 -0
  22. package/esm/browser/pageExitObservable.js +57 -0
  23. package/esm/configuration/configuration.js +5 -0
  24. package/esm/configuration/transportConfiguration.js +7 -0
  25. package/esm/dataMap.js +9 -2
  26. package/esm/helper/contextHistory.js +1 -1
  27. package/esm/helper/deviceInfo.js +44 -2
  28. package/esm/helper/enums.js +3 -0
  29. package/esm/helper/getZoneJsOriginalValue.js +27 -0
  30. package/esm/helper/instrumentMethod.js +6 -1
  31. package/esm/helper/lifeCycle.js +2 -6
  32. package/esm/helper/readBytesFromStream.js +67 -0
  33. package/esm/helper/tools.js +99 -44
  34. package/esm/index.js +4 -0
  35. package/esm/session/sessionStore.js +1 -1
  36. package/esm/transport/batch.js +8 -45
  37. package/esm/transport/httpRequest.js +4 -2
  38. package/esm/transport/sendWithRetryStrategy.js +8 -8
  39. package/esm/transport/startBatchWithReplica.js +2 -2
  40. package/package.json +2 -2
  41. package/src/browser/fetchObservable.js +21 -17
  42. package/src/browser/htmlDomUtils.js +35 -0
  43. package/src/browser/pageExitObservable.js +70 -0
  44. package/src/configuration/configuration.js +6 -0
  45. package/src/configuration/transportConfiguration.js +30 -6
  46. package/src/dataMap.js +9 -2
  47. package/src/helper/contextHistory.js +1 -1
  48. package/src/helper/deviceInfo.js +39 -3
  49. package/src/helper/enums.js +40 -37
  50. package/src/helper/getZoneJsOriginalValue.js +27 -0
  51. package/src/helper/instrumentMethod.js +40 -50
  52. package/src/helper/lifeCycle.js +2 -7
  53. package/src/helper/readBytesFromStream.js +69 -0
  54. package/src/helper/tools.js +112 -43
  55. package/src/index.js +4 -1
  56. package/src/session/sessionStore.js +27 -24
  57. package/src/synthetics/syntheticsWorkerValues.js +11 -7
  58. package/src/transport/batch.js +70 -81
  59. package/src/transport/httpRequest.js +25 -25
  60. package/src/transport/sendWithRetryStrategy.js +14 -9
  61. package/src/transport/startBatchWithReplica.js +11 -5
@@ -1,8 +1,8 @@
1
1
  import { display } from '../helper/display'
2
- import {
3
- addEventListener,
4
- noop,
5
- values,
2
+ import {
3
+ addEventListener,
4
+ noop,
5
+ values,
6
6
  findByPath,
7
7
  escapeRowData,
8
8
  each,
@@ -15,20 +15,21 @@ import {
15
15
  isEmptyObject,
16
16
  isObject,
17
17
  escapeJsonValue,
18
- escapeRowField
18
+ escapeRowField
19
19
  } from '../helper/tools'
20
- import { commonTags, dataMap } from '../dataMap'
20
+ import { commonTags, dataMap, commonFields } from '../dataMap'
21
21
  import { DOM_EVENT, RumEventType } from '../helper/enums'
22
22
  // https://en.wikipedia.org/wiki/UTF-8
23
23
  // eslint-disable-next-line no-control-regex
24
24
  var HAS_MULTI_BYTES_CHARACTERS = /[^\u0000-\u007F]/
25
25
  var CUSTOM_KEYS = 'custom_keys'
26
26
  export var processedMessageByDataMap = function (message) {
27
- if (!message || !message.type) return {
28
- rowStr: '',
29
- rowData: undefined
30
- }
31
-
27
+ if (!message || !message.type)
28
+ return {
29
+ rowStr: '',
30
+ rowData: undefined
31
+ }
32
+
32
33
  var rowData = { tags: {}, fields: {} }
33
34
  var hasFileds = false
34
35
  var rowStr = ''
@@ -47,9 +48,10 @@ export var processedMessageByDataMap = function (message) {
47
48
  tagsStr.push(escapeRowData(_key) + '=' + escapeRowData(_value))
48
49
  }
49
50
  })
50
-
51
+
51
52
  var fieldsStr = []
52
- each(value.fields, function (_value, _key) {
53
+ var fields = extend({}, commonFields, value.fields)
54
+ each(fields, function (_value, _key) {
53
55
  if (isArray(_value) && _value.length === 2) {
54
56
  var type = _value[0],
55
57
  value_path = _value[1]
@@ -63,18 +65,26 @@ export var processedMessageByDataMap = function (message) {
63
65
  // _valueData.replace(/[\\]*"/g, '"').replace(/"/g, '\\"') +
64
66
  // '"'
65
67
  // : escapeRowData(_valueData)
66
- fieldsStr.push(escapeRowData(_key) + '=' + escapeRowField(_valueData))
68
+ fieldsStr.push(
69
+ escapeRowData(_key) + '=' + escapeRowField(_valueData)
70
+ )
67
71
  }
68
72
  } else if (isString(_value)) {
69
73
  var _valueData = findByPath(message, _value)
70
74
  filterFileds.push(_key)
71
75
  if (_valueData || isNumber(_valueData)) {
72
76
  rowData.fields[_key] = _valueData // 这里不需要转译
73
- fieldsStr.push(escapeRowData(_key) + '=' + escapeRowField(_valueData))
77
+ fieldsStr.push(
78
+ escapeRowData(_key) + '=' + escapeRowField(_valueData)
79
+ )
74
80
  }
75
81
  }
76
82
  })
77
- if (message.tags && isObject(message.tags) && !isEmptyObject(message.tags)) {
83
+ if (
84
+ message.tags &&
85
+ isObject(message.tags) &&
86
+ !isEmptyObject(message.tags)
87
+ ) {
78
88
  // 自定义tag, 存储成field
79
89
  var _tagKeys = []
80
90
  each(message.tags, function (_value, _key) {
@@ -89,7 +99,9 @@ export var processedMessageByDataMap = function (message) {
89
99
  })
90
100
  if (_tagKeys.length) {
91
101
  rowData.fields[CUSTOM_KEYS] = escapeRowField(_tagKeys)
92
- fieldsStr.push(escapeRowData(CUSTOM_KEYS) + '=' + escapeRowField(_tagKeys))
102
+ fieldsStr.push(
103
+ escapeRowData(CUSTOM_KEYS) + '=' + escapeRowField(_tagKeys)
104
+ )
93
105
  }
94
106
  }
95
107
  if (message.type === RumEventType.LOGGER) {
@@ -121,7 +133,14 @@ export var processedMessageByDataMap = function (message) {
121
133
  rowData: hasFileds ? rowData : undefined
122
134
  }
123
135
  }
124
- var batch = function (request, batchMessagesLimit, batchBytesLimit, messageBytesLimit, flushTimeout,beforeUnloadCallback) {
136
+ var batch = function (
137
+ request,
138
+ batchMessagesLimit,
139
+ batchBytesLimit,
140
+ messageBytesLimit,
141
+ flushTimeout,
142
+ pageExitObservable
143
+ ) {
125
144
  this.pushOnlyBuffer = []
126
145
  this.upsertBuffer = {}
127
146
  this.bufferBytesCount = 0
@@ -131,24 +150,22 @@ var batch = function (request, batchMessagesLimit, batchBytesLimit, messageBytes
131
150
  this.batchBytesLimit = batchBytesLimit
132
151
  this.messageBytesLimit = messageBytesLimit
133
152
  this.flushTimeout = flushTimeout
134
- if (typeof beforeUnloadCallback === 'function') {
135
- this.beforeUnloadCallback = beforeUnloadCallback
136
- } else {
137
- this.beforeUnloadCallback = noop
138
- }
139
- this.setupFlushOnExit()
153
+ var _this = this
154
+ pageExitObservable.subscribe(function () {
155
+ _this.flush(_this.request.sendOnExit)
156
+ })
140
157
  this.flushPeriodically()
141
158
  }
142
- batch.prototype.add = function(message) {
159
+ batch.prototype.add = function (message) {
143
160
  this.addOrUpdate(message)
144
161
  }
145
- batch.prototype.upsert = function(message, key) {
162
+ batch.prototype.upsert = function (message, key) {
146
163
  this.addOrUpdate(message, key)
147
164
  }
148
- batch.prototype.flush = function(sendFn) {
165
+ batch.prototype.flush = function (sendFn) {
149
166
  if (typeof sendFn !== 'function') {
150
167
  sendFn = this.request.send
151
- }
168
+ }
152
169
  if (this.bufferMessagesCount !== 0) {
153
170
  var messages = this.pushOnlyBuffer.concat(values(this.upsertBuffer))
154
171
  var bytesCount = this.bufferBytesCount
@@ -161,10 +178,10 @@ batch.prototype.flush = function(sendFn) {
161
178
  }
162
179
  }
163
180
  }
164
- batch.prototype.flushOnExit = function() {
181
+ batch.prototype.flushOnExit = function () {
165
182
  this.flush(this.request.sendOnExit)
166
183
  }
167
- batch.prototype.computeBytesCount = function(candidate) {
184
+ batch.prototype.computeBytesCount = function (candidate) {
168
185
  // Accurate bytes count computations can degrade performances when there is a lot of events to process
169
186
  if (!HAS_MULTI_BYTES_CHARACTERS.test(candidate)) {
170
187
  return candidate.length
@@ -176,13 +193,15 @@ batch.prototype.computeBytesCount = function(candidate) {
176
193
 
177
194
  return new Blob([candidate]).size
178
195
  }
179
- batch.prototype.addOrUpdate = function(message, key) {
196
+ batch.prototype.addOrUpdate = function (message, key) {
180
197
  var _process = this.process(message)
181
198
  var processedMessage = _process.processedMessage
182
199
  var messageBytesCount = _process.messageBytesCount
183
200
  if (messageBytesCount >= this.messageBytesLimit) {
184
201
  display.warn(
185
- 'Discarded a message whose size was bigger than the maximum allowed size ' + this.messageBytesLimit + 'KB.'
202
+ 'Discarded a message whose size was bigger than the maximum allowed size ' +
203
+ this.messageBytesLimit +
204
+ 'KB.'
186
205
  )
187
206
  return
188
207
  }
@@ -198,13 +217,16 @@ batch.prototype.addOrUpdate = function(message, key) {
198
217
  this.flush()
199
218
  }
200
219
  }
201
- batch.prototype.process = function(message) {
220
+ batch.prototype.process = function (message) {
202
221
  var processedMessage = processedMessageByDataMap(message).rowStr
203
222
  var messageBytesCount = this.computeBytesCount(processedMessage)
204
- return { processedMessage: processedMessage, messageBytesCount:messageBytesCount }
223
+ return {
224
+ processedMessage: processedMessage,
225
+ messageBytesCount: messageBytesCount
226
+ }
205
227
  }
206
228
 
207
- batch.prototype.push = function(processedMessage, messageBytesCount, key) {
229
+ batch.prototype.push = function (processedMessage, messageBytesCount, key) {
208
230
  if (this.bufferMessagesCount > 0) {
209
231
  // \n separator at serialization
210
232
  this.bufferBytesCount += 1
@@ -218,7 +240,7 @@ batch.prototype.push = function(processedMessage, messageBytesCount, key) {
218
240
  this.bufferMessagesCount += 1
219
241
  }
220
242
 
221
- batch.prototype.remove = function(key) {
243
+ batch.prototype.remove = function (key) {
222
244
  var removedMessage = this.upsertBuffer[key]
223
245
  delete this.upsertBuffer[key]
224
246
  var messageBytesCount = this.computeBytesCount(removedMessage)
@@ -229,61 +251,28 @@ batch.prototype.remove = function(key) {
229
251
  }
230
252
  }
231
253
 
232
- batch.prototype.hasMessageFor = function(key) {
254
+ batch.prototype.hasMessageFor = function (key) {
233
255
  return key !== undefined && this.upsertBuffer[key] !== undefined
234
256
  }
235
257
 
236
- batch.prototype.willReachedBytesLimitWith = function(messageBytesCount) {
258
+ batch.prototype.willReachedBytesLimitWith = function (messageBytesCount) {
237
259
  // byte of the separator at the end of the message
238
260
  return this.bufferBytesCount + messageBytesCount + 1 >= this.batchBytesLimit
239
261
  }
240
262
 
241
- batch.prototype.isFull = function() {
242
- return this.bufferMessagesCount === this.batchMessagesLimit || this.bufferBytesCount >= this.batchBytesLimit
263
+ batch.prototype.isFull = function () {
264
+ return (
265
+ this.bufferMessagesCount === this.batchMessagesLimit ||
266
+ this.bufferBytesCount >= this.batchBytesLimit
267
+ )
243
268
  }
244
269
 
245
- batch.prototype.flushPeriodically = function() {
270
+ batch.prototype.flushPeriodically = function () {
246
271
  var _this = this
247
- setTimeout(
248
- function(){
249
- _this.flush()
250
- _this.flushPeriodically()
251
- },
252
- this.flushTimeout
253
- )
272
+ setTimeout(function () {
273
+ _this.flush()
274
+ _this.flushPeriodically()
275
+ }, this.flushTimeout)
254
276
  }
255
277
 
256
- batch.prototype.setupFlushOnExit = function() {
257
- /**
258
- * With sendBeacon, requests are guaranteed to be successfully sent during document unload
259
- */
260
- // @ts-ignore this function is not always defined
261
- var _this = this
262
- if (navigator.sendBeacon) {
263
- /**
264
- * beforeunload is called before visibilitychange
265
- * register first to be sure to be called before flush on beforeunload
266
- * caveat: unload can still be canceled by another listener
267
- */
268
- addEventListener(window, DOM_EVENT.BEFORE_UNLOAD, this.beforeUnloadCallback)
269
-
270
- /**
271
- * Only event that guarantee to fire on mobile devices when the page transitions to background state
272
- * (e.g. when user switches to a different application, goes to homescreen, etc), or is being unloaded.
273
- */
274
- addEventListener(document, DOM_EVENT.VISIBILITY_CHANGE, function() {
275
- if (document.visibilityState === 'hidden') {
276
- _this.flushOnExit()
277
- }
278
- })
279
- /**
280
- * Safari does not support yet to send a request during:
281
- * - a visibility change during doc unload (cf: https://bugs.webkit.org/show_bug.cgi?id=194897)
282
- * - a page hide transition (cf: https://bugs.webkit.org/show_bug.cgi?id=188329)
283
- */
284
- addEventListener(window, DOM_EVENT.BEFORE_UNLOAD, function() {
285
- return _this.flushOnExit()
286
- })
287
- }
288
- }
289
278
  export var Batch = batch
@@ -12,27 +12,28 @@ function addBatchPrecision(url) {
12
12
  if (!url) return url
13
13
  return url + (url.indexOf('?') === -1 ? '?' : '&') + 'precision=ms'
14
14
  }
15
- export function createHttpRequest(
16
- endpointUrl,
17
- bytesLimit,
18
- reportError
19
- ) {
15
+ export function createHttpRequest(endpointUrl, bytesLimit, reportError) {
20
16
  var retryState = newRetryState()
21
- var sendStrategyForRetry = function(payload, onResponse) {
17
+ var sendStrategyForRetry = function (payload, onResponse) {
22
18
  return fetchKeepAliveStrategy(endpointUrl, bytesLimit, payload, onResponse)
23
19
  }
24
-
20
+
25
21
  return {
26
- send: function(payload) {
27
- sendWithRetryStrategy(payload, retryState, sendStrategyForRetry, reportError)
22
+ send: function (payload) {
23
+ sendWithRetryStrategy(
24
+ payload,
25
+ retryState,
26
+ sendStrategyForRetry,
27
+ reportError
28
+ )
28
29
  },
29
30
  /**
30
31
  * Since fetch keepalive behaves like regular fetch on Firefox,
31
32
  * keep using sendBeaconStrategy on exit
32
33
  */
33
- sendOnExit: function(payload) {
34
+ sendOnExit: function (payload) {
34
35
  sendBeaconStrategy(endpointUrl, bytesLimit, payload)
35
- },
36
+ }
36
37
  }
37
38
  }
38
39
 
@@ -55,8 +56,6 @@ function sendBeaconStrategy(endpointUrl, bytesLimit, payload) {
55
56
  sendXHR(url, data)
56
57
  }
57
58
 
58
-
59
-
60
59
  export function fetchKeepAliveStrategy(
61
60
  endpointUrl,
62
61
  bytesLimit,
@@ -68,14 +67,18 @@ export function fetchKeepAliveStrategy(
68
67
  var url = addBatchPrecision(endpointUrl)
69
68
  var canUseKeepAlive = isKeepAliveSupported() && bytesCount < bytesLimit
70
69
  if (canUseKeepAlive) {
71
- fetch(url, { method: 'POST', body: data, keepalive: true })
72
- .then(
73
- function(response) {
70
+ fetch(url, {
71
+ method: 'POST',
72
+ body: data,
73
+ keepalive: true,
74
+ mode: 'cors'
75
+ }).then(
76
+ function (response) {
74
77
  if (typeof onResponse === 'function') {
75
- onResponse({ status: response.status })
78
+ onResponse({ status: response.status, type: response.type })
76
79
  }
77
80
  },
78
- function() {
81
+ function () {
79
82
  // failed to queue the request
80
83
  sendXHR(url, data, onResponse)
81
84
  }
@@ -98,12 +101,9 @@ function sendXHR(url, data, onResponse) {
98
101
  const request = new XMLHttpRequest()
99
102
  request.open('POST', url, true)
100
103
  request.send(data)
101
- request.addEventListener(
102
- 'loadend',
103
- function(){
104
- if (typeof onResponse === 'function') {
105
- onResponse({ status: request.status })
106
- }
104
+ request.addEventListener('loadend', function () {
105
+ if (typeof onResponse === 'function') {
106
+ onResponse({ status: request.status })
107
107
  }
108
- )
108
+ })
109
109
  }
@@ -13,14 +13,14 @@ export var MAX_BACKOFF_TIME = 256 * ONE_SECOND
13
13
  export var INITIAL_BACKOFF_TIME = ONE_SECOND
14
14
 
15
15
  var TransportStatus = {
16
- UP: 'UP',
17
- FAILURE_DETECTED: 'FAILURE_DETECTED',
18
- DOWN: 'DOWN'
16
+ UP: 0,
17
+ FAILURE_DETECTED: 1,
18
+ DOWN: 2
19
19
  }
20
20
 
21
21
  var RetryReason = {
22
- AFTER_SUCCESS: 'AFTER_SUCCESS',
23
- AFTER_RESUME: 'AFTER_RESUME'
22
+ AFTER_SUCCESS: 0,
23
+ AFTER_RESUME: 1
24
24
  }
25
25
 
26
26
  export function sendWithRetryStrategy(
@@ -92,7 +92,7 @@ function send(payload, state, sendStrategy, responseData) {
92
92
  state.bandwidthMonitor.add(payload)
93
93
  sendStrategy(payload, function (response) {
94
94
  state.bandwidthMonitor.remove(payload)
95
- if (wasRequestSuccessful(response)) {
95
+ if (!shouldRetryRequest(response)) {
96
96
  state.transportStatus = TransportStatus.UP
97
97
  onSuccess()
98
98
  } else {
@@ -135,10 +135,15 @@ function retryQueuedPayloads(reason, state, sendStrategy, reportError) {
135
135
  }
136
136
  }
137
137
 
138
- function wasRequestSuccessful(response) {
139
- return response.status !== 0 && response.status < 500
138
+ function shouldRetryRequest(response) {
139
+ return (
140
+ response.type !== 'opaque' &&
141
+ ((response.status === 0 && !navigator.onLine) ||
142
+ response.status === 408 ||
143
+ response.status === 429 ||
144
+ response.status >= 500)
145
+ )
140
146
  }
141
-
142
147
  export function newRetryState() {
143
148
  return {
144
149
  transportStatus: TransportStatus.UP,
@@ -4,23 +4,29 @@ import { createHttpRequest } from './httpRequest'
4
4
  export function startBatchWithReplica(
5
5
  configuration,
6
6
  endpointUrl,
7
- reportError
7
+ reportError,
8
+ pageExitObservable
8
9
  ) {
9
10
  var primaryBatch = createBatch(endpointUrl)
10
11
 
11
12
  function createBatch(endpointUrl) {
12
13
  return new Batch(
13
- createHttpRequest(endpointUrl, configuration.batchBytesLimit, reportError),
14
+ createHttpRequest(
15
+ endpointUrl,
16
+ configuration.batchBytesLimit,
17
+ reportError
18
+ ),
14
19
  configuration.batchMessagesLimit,
15
20
  configuration.batchBytesLimit,
16
21
  configuration.messageBytesLimit,
17
- configuration.flushTimeout
22
+ configuration.flushTimeout,
23
+ pageExitObservable
18
24
  )
19
25
  }
20
26
 
21
27
  return {
22
- add: function(message) {
28
+ add: function (message) {
23
29
  primaryBatch.add(message)
24
- },
30
+ }
25
31
  }
26
32
  }