@cloudcare/browser-core 2.0.4 → 2.0.6

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 (100) hide show
  1. package/cjs/browser/addEventListener.js +57 -0
  2. package/cjs/browser/fetchObservable.js +4 -5
  3. package/cjs/browser/pageExitObservable.js +4 -2
  4. package/cjs/browser/runOnReadyState.js +21 -0
  5. package/cjs/browser/xhrObservable.js +7 -5
  6. package/cjs/configuration/configuration.js +5 -3
  7. package/cjs/console/consoleObservable.js +3 -1
  8. package/cjs/dataMap.js +1 -0
  9. package/cjs/helper/byteUtils.js +26 -0
  10. package/cjs/helper/contextHistory.js +4 -2
  11. package/cjs/helper/createEventRateLimiter.js +3 -1
  12. package/cjs/helper/errorTools.js +4 -2
  13. package/cjs/helper/instrumentMethod.js +2 -4
  14. package/cjs/helper/serialisation/contextManager.js +84 -0
  15. package/cjs/helper/serialisation/heavyCustomerDataWarning.js +34 -0
  16. package/cjs/helper/serialisation/jsonStringify.js +57 -0
  17. package/cjs/helper/serialisation/rowData.js +50 -0
  18. package/cjs/helper/timer.js +29 -0
  19. package/cjs/helper/tools.js +10 -331
  20. package/cjs/index.js +117 -0
  21. package/cjs/init.js +6 -6
  22. package/cjs/report/reportObservable.js +4 -2
  23. package/cjs/session/sessionCookieStore.js +3 -1
  24. package/cjs/session/sessionManagement.js +10 -5
  25. package/cjs/session/sessionStore.js +10 -4
  26. package/cjs/transport/batch.js +40 -91
  27. package/cjs/transport/flushController.js +121 -0
  28. package/cjs/transport/httpRequest.js +6 -2
  29. package/cjs/transport/index.js +9 -1
  30. package/cjs/transport/sendWithRetryStrategy.js +8 -4
  31. package/cjs/transport/startBatchWithReplica.js +10 -2
  32. package/cjs/user/index.js +18 -0
  33. package/cjs/user/user.js +42 -0
  34. package/esm/browser/addEventListener.js +46 -0
  35. package/esm/browser/fetchObservable.js +4 -3
  36. package/esm/browser/pageExitObservable.js +2 -1
  37. package/esm/browser/runOnReadyState.js +12 -0
  38. package/esm/browser/xhrObservable.js +6 -5
  39. package/esm/configuration/configuration.js +2 -1
  40. package/esm/console/consoleObservable.js +2 -1
  41. package/esm/dataMap.js +1 -0
  42. package/esm/helper/byteUtils.js +16 -0
  43. package/esm/helper/contextHistory.js +1 -0
  44. package/esm/helper/createEventRateLimiter.js +1 -0
  45. package/esm/helper/errorTools.js +2 -1
  46. package/esm/helper/instrumentMethod.js +1 -3
  47. package/esm/helper/serialisation/contextManager.js +70 -0
  48. package/esm/helper/serialisation/heavyCustomerDataWarning.js +20 -0
  49. package/esm/helper/serialisation/jsonStringify.js +46 -0
  50. package/esm/helper/serialisation/rowData.js +33 -0
  51. package/esm/helper/timer.js +14 -0
  52. package/esm/helper/tools.js +1 -292
  53. package/esm/index.js +10 -1
  54. package/esm/init.js +6 -6
  55. package/esm/report/reportObservable.js +3 -2
  56. package/esm/session/sessionCookieStore.js +1 -0
  57. package/esm/session/sessionManagement.js +5 -2
  58. package/esm/session/sessionStore.js +7 -2
  59. package/esm/transport/batch.js +31 -85
  60. package/esm/transport/flushController.js +114 -0
  61. package/esm/transport/httpRequest.js +5 -2
  62. package/esm/transport/index.js +2 -1
  63. package/esm/transport/sendWithRetryStrategy.js +3 -1
  64. package/esm/transport/startBatchWithReplica.js +9 -2
  65. package/esm/user/index.js +1 -0
  66. package/esm/user/user.js +32 -0
  67. package/package.json +22 -22
  68. package/src/browser/addEventListener.js +50 -0
  69. package/src/browser/fetchObservable.js +5 -5
  70. package/src/browser/pageExitObservable.js +4 -5
  71. package/src/browser/runOnReadyState.js +16 -0
  72. package/src/browser/xhrObservable.js +57 -32
  73. package/src/configuration/configuration.js +1 -1
  74. package/src/console/consoleObservable.js +22 -12
  75. package/src/dataMap.js +1 -0
  76. package/src/helper/byteUtils.js +17 -0
  77. package/src/helper/contextHistory.js +1 -1
  78. package/src/helper/createEventRateLimiter.js +7 -9
  79. package/src/helper/errorTools.js +2 -1
  80. package/src/helper/instrumentMethod.js +1 -2
  81. package/src/helper/serialisation/contextManager.js +79 -0
  82. package/src/helper/serialisation/heavyCustomerDataWarning.js +28 -0
  83. package/src/helper/serialisation/jsonStringify.js +47 -0
  84. package/src/helper/serialisation/rowData.js +41 -0
  85. package/src/helper/timer.js +21 -0
  86. package/src/helper/tools.js +2 -301
  87. package/src/index.js +11 -0
  88. package/src/init.js +6 -6
  89. package/src/report/reportObservable.js +40 -25
  90. package/src/session/sessionCookieStore.js +24 -14
  91. package/src/session/sessionManagement.js +34 -17
  92. package/src/session/sessionStore.js +7 -2
  93. package/src/transport/batch.js +39 -91
  94. package/src/transport/flushController.js +115 -0
  95. package/src/transport/httpRequest.js +13 -6
  96. package/src/transport/index.js +1 -0
  97. package/src/transport/sendWithRetryStrategy.js +3 -7
  98. package/src/transport/startBatchWithReplica.js +11 -7
  99. package/src/user/index.js +1 -0
  100. package/src/user/user.js +31 -0
@@ -0,0 +1,114 @@
1
+ import { Observable } from '../helper/observable';
2
+ import { clearTimeout, setTimeout } from '../helper/timer'; // type FlushReason = PageExitReason | 'duration_limit' | 'bytes_limit' | 'messages_limit' | 'session_expire'
3
+
4
+ /**
5
+ * Returns a "flush controller", responsible of notifying when flushing a pool of pending data needs
6
+ * to happen. The implementation is designed to support both synchronous and asynchronous usages,
7
+ * but relies on invariants described in each method documentation to keep a coherent state.
8
+ */
9
+
10
+ export function createFlushController(_ref) {
11
+ var {
12
+ messagesLimit,
13
+ bytesLimit,
14
+ durationLimit,
15
+ pageExitObservable,
16
+ sessionExpireObservable
17
+ } = _ref;
18
+ var flushObservable = new Observable();
19
+ pageExitObservable.subscribe(function (event) {
20
+ return flush(event.reason);
21
+ });
22
+ sessionExpireObservable.subscribe(function () {
23
+ return flush('session_expire');
24
+ });
25
+ var currentBytesCount = 0;
26
+ var currentMessagesCount = 0;
27
+
28
+ function flush(flushReason) {
29
+ if (currentMessagesCount === 0) {
30
+ return;
31
+ }
32
+
33
+ var messagesCount = currentMessagesCount;
34
+ var bytesCount = currentBytesCount;
35
+ currentMessagesCount = 0;
36
+ currentBytesCount = 0;
37
+ cancelDurationLimitTimeout();
38
+ flushObservable.notify({
39
+ reason: flushReason,
40
+ messagesCount: messagesCount,
41
+ bytesCount: bytesCount
42
+ });
43
+ }
44
+
45
+ var durationLimitTimeoutId;
46
+
47
+ function scheduleDurationLimitTimeout() {
48
+ if (durationLimitTimeoutId === undefined) {
49
+ durationLimitTimeoutId = setTimeout(function () {
50
+ flush('duration_limit');
51
+ }, durationLimit);
52
+ }
53
+ }
54
+
55
+ function cancelDurationLimitTimeout() {
56
+ clearTimeout(durationLimitTimeoutId);
57
+ durationLimitTimeoutId = undefined;
58
+ }
59
+
60
+ return {
61
+ flushObservable: flushObservable,
62
+ getMessagesCount: function getMessagesCount() {
63
+ return currentMessagesCount;
64
+ },
65
+
66
+ /**
67
+ * Notifies that a message will be added to a pool of pending messages waiting to be flushed.
68
+ *
69
+ * This function needs to be called synchronously, right before adding the message, so no flush
70
+ * event can happen after `notifyBeforeAddMessage` and before adding the message.
71
+ */
72
+ notifyBeforeAddMessage: function notifyBeforeAddMessage(messageBytesCount) {
73
+ if (currentBytesCount + messageBytesCount >= bytesLimit) {
74
+ flush('bytes_limit');
75
+ } // Consider the message to be added now rather than in `notifyAfterAddMessage`, because if no
76
+ // message was added yet and `notifyAfterAddMessage` is called asynchronously, we still want
77
+ // to notify when a flush is needed (for example on page exit).
78
+
79
+
80
+ currentMessagesCount += 1;
81
+ currentBytesCount += messageBytesCount;
82
+ scheduleDurationLimitTimeout();
83
+ },
84
+
85
+ /**
86
+ * Notifies that a message *was* added to a pool of pending messages waiting to be flushed.
87
+ *
88
+ * This function can be called asynchronously after the message was added, but in this case it
89
+ * should not be called if a flush event occurred in between.
90
+ */
91
+ notifyAfterAddMessage: function notifyAfterAddMessage() {
92
+ if (currentMessagesCount >= messagesLimit) {
93
+ flush('messages_limit');
94
+ } else if (currentBytesCount >= bytesLimit) {
95
+ flush('bytes_limit');
96
+ }
97
+ },
98
+
99
+ /**
100
+ * Notifies that a message was removed from a pool of pending messages waiting to be flushed.
101
+ *
102
+ * This function needs to be called synchronously, right after removing the message, so no flush
103
+ * event can happen after removing the message and before `notifyAfterRemoveMessage`.
104
+ */
105
+ notifyAfterRemoveMessage: function notifyAfterRemoveMessage(messageBytesCount) {
106
+ currentBytesCount -= messageBytesCount;
107
+ currentMessagesCount -= 1;
108
+
109
+ if (currentMessagesCount === 0) {
110
+ cancelDurationLimitTimeout();
111
+ }
112
+ }
113
+ };
114
+ }
@@ -1,4 +1,5 @@
1
1
  import { newRetryState, sendWithRetryStrategy } from './sendWithRetryStrategy';
2
+ import { addEventListener } from '../browser/addEventListener';
2
3
  /**
3
4
  * Use POST request without content type to:
4
5
  * - avoid CORS preflight requests
@@ -95,12 +96,14 @@ function isKeepAliveSupported() {
95
96
  function sendXHR(url, data, onResponse) {
96
97
  var request = new XMLHttpRequest();
97
98
  request.open('POST', url, true);
98
- request.send(data);
99
- request.addEventListener('loadend', function () {
99
+ addEventListener(request, 'loadend', function () {
100
100
  if (typeof onResponse === 'function') {
101
101
  onResponse({
102
102
  status: request.status
103
103
  });
104
104
  }
105
+ }, {
106
+ once: true
105
107
  });
108
+ request.send(data);
106
109
  }
@@ -1,3 +1,4 @@
1
1
  export { createHttpRequest } from './httpRequest';
2
2
  export { Batch } from './batch';
3
- export { startBatchWithReplica } from './startBatchWithReplica';
3
+ export { startBatchWithReplica } from './startBatchWithReplica';
4
+ export { createFlushController } from './flushController';
@@ -1,5 +1,7 @@
1
- import { clocksNow, ONE_KIBI_BYTE, ONE_MEBI_BYTE, ONE_SECOND } from '../helper/tools';
1
+ import { clocksNow, ONE_SECOND } from '../helper/tools';
2
+ import { ONE_MEBI_BYTE, ONE_KIBI_BYTE } from '../helper/byteUtils';
2
3
  import { ErrorSource } from '../helper/errorTools';
4
+ import { setTimeout } from '../helper/timer';
3
5
  export var MAX_ONGOING_BYTES_COUNT = 80 * ONE_KIBI_BYTE;
4
6
  export var MAX_ONGOING_REQUESTS = 32;
5
7
  export var MAX_QUEUE_BYTES_COUNT = 3 * ONE_MEBI_BYTE;
@@ -1,10 +1,17 @@
1
1
  import { Batch } from './batch';
2
2
  import { createHttpRequest } from './httpRequest';
3
- export function startBatchWithReplica(configuration, endpointUrl, reportError, pageExitObservable) {
3
+ import { createFlushController } from './flushController';
4
+ export function startBatchWithReplica(configuration, endpointUrl, reportError, pageExitObservable, sessionExpireObservable) {
4
5
  var primaryBatch = createBatch(endpointUrl);
5
6
 
6
7
  function createBatch(endpointUrl) {
7
- return new Batch(createHttpRequest(endpointUrl, configuration.batchBytesLimit, reportError), configuration.batchMessagesLimit, configuration.batchBytesLimit, configuration.messageBytesLimit, configuration.flushTimeout, pageExitObservable);
8
+ return new Batch(createHttpRequest(endpointUrl, configuration.batchBytesLimit, reportError), createFlushController({
9
+ messagesLimit: configuration.batchMessagesLimit,
10
+ bytesLimit: configuration.batchBytesLimit,
11
+ durationLimit: configuration.flushTimeout,
12
+ pageExitObservable: pageExitObservable,
13
+ sessionExpireObservable: sessionExpireObservable
14
+ }), configuration.messageBytesLimit);
8
15
  }
9
16
 
10
17
  return {
@@ -0,0 +1 @@
1
+ export * from './user';
@@ -0,0 +1,32 @@
1
+ import { display } from '../helper/display';
2
+ import { assign, getType, each } from '../helper/tools';
3
+ /**
4
+ * Clone input data and ensure known user properties (id, name, email)
5
+ * are strings, as defined here:
6
+ * https://docs.datadoghq.com/logs/log_configuration/attributes_naming_convention/#user-related-attributes
7
+ */
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
+ return user;
19
+ }
20
+ /**
21
+ * Simple check to ensure user is valid
22
+ */
23
+
24
+ export function checkUser(newUser) {
25
+ var isValid = getType(newUser) === 'object';
26
+
27
+ if (!isValid) {
28
+ display.error('Unsupported user:', newUser);
29
+ }
30
+
31
+ return isValid;
32
+ }
package/package.json CHANGED
@@ -1,24 +1,24 @@
1
1
  {
2
- "name": "@cloudcare/browser-core",
3
- "version": "2.0.4",
4
- "main": "cjs/index.js",
5
- "module": "esm/index.js",
6
- "scripts": {
7
- "build": "run-p build:cjs build:esm",
8
- "build:cjs": "rm -rf cjs && babel --config-file ./babel.cjs.json --out-dir cjs ./src",
9
- "build:esm": "rm -rf esm && babel --config-file ./babel.esm.json --out-dir esm ./src "
10
- },
11
- "keywords": [
12
- "dataflux",
13
- "logs",
14
- "sdk"
15
- ],
16
- "repository": {
17
- "url": "https://github.com/DataFlux-cn/datakit-js",
18
- "type": "git"
19
- },
20
- "author": "dataflux",
21
- "license": "MIT",
22
- "description": "DataFlux RUM Web 端数据指标监控",
23
- "gitHead": "c8c2e9dfd59732cdadc5c7869eba9d7ddf6abeb2"
2
+ "name": "@cloudcare/browser-core",
3
+ "version": "2.0.6",
4
+ "main": "cjs/index.js",
5
+ "module": "esm/index.js",
6
+ "scripts": {
7
+ "build": "run-p build:cjs build:esm",
8
+ "build:cjs": "rm -rf cjs && babel --config-file ./babel.cjs.json --out-dir cjs ./src",
9
+ "build:esm": "rm -rf esm && babel --config-file ./babel.esm.json --out-dir esm ./src "
10
+ },
11
+ "keywords": [
12
+ "dataflux",
13
+ "logs",
14
+ "sdk"
15
+ ],
16
+ "repository": {
17
+ "url": "https://github.com/DataFlux-cn/datakit-js",
18
+ "type": "git"
19
+ },
20
+ "author": "dataflux",
21
+ "license": "MIT",
22
+ "description": "DataFlux RUM Web 端数据指标监控",
23
+ "gitHead": "ffbab227685c4ea3bf9b26e6e5e9c00233e64bb2"
24
24
  }
@@ -0,0 +1,50 @@
1
+ import { each } from '../helper/tools'
2
+ import { getZoneJsOriginalValue } from '../helper/getZoneJsOriginalValue'
3
+ export function addEventListener(eventTarget, event, listener, options) {
4
+ return addEventListeners(eventTarget, [event], listener, options)
5
+ }
6
+
7
+ /**
8
+ * Add event listeners to an event emitter object (Window, Element, mock object...). This provides
9
+ * a few conveniences compared to using `element.addEventListener` directly:
10
+ *
11
+ * * supports IE11 by:
12
+ * * using an option object only if needed
13
+ * * emulating the `once` option
14
+ *
15
+ * * wraps the listener with a `monitor` function
16
+ *
17
+ * * returns a `stop` function to remove the listener
18
+ *
19
+ * * with `once: true`, the listener will be called at most once, even if different events are
20
+ * listened
21
+ */
22
+
23
+ export function addEventListeners(eventTarget, events, listener, options) {
24
+ var wrappedListener =
25
+ options && options.once
26
+ ? function (event) {
27
+ stop()
28
+ listener(event)
29
+ }
30
+ : listener
31
+
32
+ options =
33
+ options && options.passive
34
+ ? { capture: options.capture, passive: options.passive }
35
+ : options && options.capture
36
+ var add = getZoneJsOriginalValue(eventTarget, 'addEventListener')
37
+
38
+ each(events, function (event) {
39
+ add.call(eventTarget, event, wrappedListener, options)
40
+ })
41
+ var stop = function () {
42
+ var remove = getZoneJsOriginalValue(eventTarget, 'removeEventListener')
43
+ each(events, function (event) {
44
+ remove.call(eventTarget, event, wrappedListener, options)
45
+ })
46
+ }
47
+ return {
48
+ stop: stop
49
+ }
50
+ }
@@ -47,10 +47,9 @@ function createFetchObservable() {
47
47
 
48
48
  function beforeSend(observable, input, init) {
49
49
  var method =
50
- (init && init.method) ||
51
- (typeof input === 'object' && input.method) ||
52
- 'GET'
53
- var url = normalizeUrl((typeof input === 'object' && input.url) || input)
50
+ (init && init.method) || (input instanceof Request && input.method) || 'GET'
51
+ var url = input instanceof Request ? input.url : normalizeUrl(String(input))
52
+
54
53
  var startClocks = clocksNow()
55
54
 
56
55
  var context = {
@@ -80,7 +79,8 @@ function afterSend(observable, responsePromise, startContext) {
80
79
  context.error = response
81
80
  } else if ('status' in response) {
82
81
  context.response = response
83
- context.responseType = response.type
82
+ context.responseType =
83
+ (response instanceof Response && response.type) || '' // issue The Response type getter can only be used on instances of Response
84
84
  context.status = response.status
85
85
  context.isAborted = false
86
86
  }
@@ -1,11 +1,10 @@
1
1
  import { Observable } from '../helper/observable'
2
+ import { includes, values } from '../helper/tools'
3
+ import { DOM_EVENT } from '../helper/enums'
2
4
  import {
3
5
  addEventListener,
4
- addEventListeners,
5
- includes,
6
- values
7
- } from '../helper/tools'
8
- import { DOM_EVENT } from '../helper/enums'
6
+ addEventListeners
7
+ } from '../browser/addEventListener'
9
8
  export var PageExitReason = {
10
9
  HIDDEN: 'visibility_hidden',
11
10
  UNLOADING: 'before_unload',
@@ -0,0 +1,16 @@
1
+ import { addEventListener } from './addEventListener'
2
+ import { DOM_EVENT } from '../helper/enums'
3
+ export function runOnReadyState(expectedReadyState, callback) {
4
+ if (
5
+ document.readyState === expectedReadyState ||
6
+ document.readyState === 'complete'
7
+ ) {
8
+ callback()
9
+ } else {
10
+ var eventName =
11
+ expectedReadyState === 'complete'
12
+ ? DOM_EVENT.LOAD
13
+ : DOM_EVENT.DOM_CONTENT_LOADED
14
+ addEventListener(window, eventName, callback, { once: true })
15
+ }
16
+ }
@@ -1,8 +1,15 @@
1
1
  import { instrumentMethodAndCallOriginal } from '../helper/instrumentMethod'
2
2
  import { Observable } from '../helper/observable'
3
3
  import { normalizeUrl } from '../helper/urlPolyfill'
4
- import { shallowClone, elapsed, relativeNow, clocksNow, timeStampNow, UUID } from '../helper/tools'
5
-
4
+ import {
5
+ shallowClone,
6
+ elapsed,
7
+ relativeNow,
8
+ clocksNow,
9
+ timeStampNow,
10
+ UUID
11
+ } from '../helper/tools'
12
+ import { addEventListener } from '../browser/addEventListener'
6
13
  var xhrObservable
7
14
  var xhrContexts = {}
8
15
  var DATA_FLUX_REQUEST_ID_KEY = '_DATAFLUX_REQUEST_UUID'
@@ -14,22 +21,34 @@ export function initXhrObservable() {
14
21
  }
15
22
 
16
23
  function createXhrObservable() {
17
- var observable = new Observable(function(){
18
- var openInstrumentMethod = instrumentMethodAndCallOriginal(XMLHttpRequest.prototype, 'open', {
19
- before: openXhr,
20
- })
24
+ var observable = new Observable(function () {
25
+ var openInstrumentMethod = instrumentMethodAndCallOriginal(
26
+ XMLHttpRequest.prototype,
27
+ 'open',
28
+ {
29
+ before: openXhr
30
+ }
31
+ )
21
32
 
22
- var sendInstrumentMethod = instrumentMethodAndCallOriginal(XMLHttpRequest.prototype, 'send', {
23
- before: function() {
24
- sendXhr.call(this, observable)
25
- },
26
- })
33
+ var sendInstrumentMethod = instrumentMethodAndCallOriginal(
34
+ XMLHttpRequest.prototype,
35
+ 'send',
36
+ {
37
+ before: function () {
38
+ sendXhr.call(this, observable)
39
+ }
40
+ }
41
+ )
27
42
 
28
- var abortInstrumentMethod = instrumentMethodAndCallOriginal(XMLHttpRequest.prototype, 'abort', {
29
- before:abortXhr,
30
- })
43
+ var abortInstrumentMethod = instrumentMethodAndCallOriginal(
44
+ XMLHttpRequest.prototype,
45
+ 'abort',
46
+ {
47
+ before: abortXhr
48
+ }
49
+ )
31
50
 
32
- return function() {
51
+ return function () {
33
52
  openInstrumentMethod.stop()
34
53
  sendInstrumentMethod.stop()
35
54
  abortInstrumentMethod.stop()
@@ -44,7 +63,7 @@ function openXhr(method, url) {
44
63
  xhrContexts[requestUUID] = {
45
64
  state: 'open',
46
65
  method: method,
47
- url: normalizeUrl(String(url)),
66
+ url: normalizeUrl(String(url))
48
67
  }
49
68
  }
50
69
 
@@ -60,34 +79,40 @@ function sendXhr(observable) {
60
79
  startContext.isAborted = false
61
80
  startContext.xhr = this
62
81
  var hasBeenReported = false
63
- var onreadyStateChangeInstrumentMethod = instrumentMethodAndCallOriginal(this, 'onreadystatechange', {
64
- before: function() {
65
- if (this.readyState === XMLHttpRequest.DONE) {
66
- // Try to report the XHR as soon as possible, because the XHR may be mutated by the
67
- // application during a future event. For example, Angular is calling .abort() on
68
- // completed requests during a onreadystatechange event, so the status becomes '0'
69
- // before the request is collected.
70
- onEnd.call(this)
71
-
82
+ var stopInstrumentingOnReadyStateChange = instrumentMethodAndCallOriginal(
83
+ this,
84
+ 'onreadystatechange',
85
+ {
86
+ before: function () {
87
+ if (this.readyState === XMLHttpRequest.DONE) {
88
+ // Try to report the XHR as soon as possible, because the XHR may be mutated by the
89
+ // application during a future event. For example, Angular is calling .abort() on
90
+ // completed requests during a onreadystatechange event, so the status becomes '0'
91
+ // before the request is collected.
92
+ onEnd.call(this)
93
+ }
72
94
  }
73
- },
74
- })
95
+ }
96
+ ).stop
75
97
 
76
- var onEnd = function() {
77
- this.removeEventListener('loadend', onEnd)
78
- onreadyStateChangeInstrumentMethod.stop()
98
+ var onEnd = function () {
99
+ unsubscribeLoadEndListener()
100
+ stopInstrumentingOnReadyStateChange()
79
101
  if (hasBeenReported) {
80
102
  return
81
103
  }
82
104
  hasBeenReported = true
83
105
  var completeContext = context
84
106
  completeContext.state = 'complete'
85
- completeContext.duration = elapsed(startContext.startClocks.timeStamp, timeStampNow())
107
+ completeContext.duration = elapsed(
108
+ startContext.startClocks.timeStamp,
109
+ timeStampNow()
110
+ )
86
111
  completeContext.status = this.status
87
112
  observable.notify(shallowClone(completeContext))
88
113
  clearRequestId.call(this)
89
114
  }
90
- this.addEventListener('loadend', onEnd)
115
+ var unsubscribeLoadEndListener = addEventListener(this, 'loadend', onEnd).stop
91
116
  observable.notify(startContext)
92
117
  }
93
118
  function clearRequestId() {
@@ -4,10 +4,10 @@ import { display } from '../helper/display'
4
4
  import {
5
5
  assign,
6
6
  isPercentage,
7
- ONE_KIBI_BYTE,
8
7
  ONE_SECOND,
9
8
  isNullUndefinedDefaultValue
10
9
  } from '../helper/tools'
10
+ import { ONE_KIBI_BYTE } from '../helper/byteUtils'
11
11
  import { computeTransportConfiguration } from './transportConfiguration'
12
12
 
13
13
  export var DefaultPrivacyLevel = {
@@ -1,14 +1,18 @@
1
1
  import { computeStackTrace } from '../tracekit'
2
- import { createHandlingStack, formatErrorMessage, toStackTraceString } from '../helper/errorTools'
2
+ import {
3
+ createHandlingStack,
4
+ formatErrorMessage,
5
+ toStackTraceString
6
+ } from '../helper/errorTools'
3
7
  import { mergeObservables, Observable } from '../helper/observable'
4
- import { find, jsonStringify, map } from '../helper/tools'
8
+ import { find, map } from '../helper/tools'
9
+ import { jsonStringify } from '../helper/serialisation/jsonStringify'
5
10
  import { ConsoleApiName } from '../helper/display'
6
11
 
7
-
8
12
  var consoleObservablesByApi = {}
9
13
 
10
14
  export function initConsoleObservable(apis) {
11
- var consoleObservables = map(apis, function(api) {
15
+ var consoleObservables = map(apis, function (api) {
12
16
  if (!consoleObservablesByApi[api]) {
13
17
  consoleObservablesByApi[api] = createConsoleObservable(api)
14
18
  }
@@ -20,15 +24,15 @@ export function initConsoleObservable(apis) {
20
24
 
21
25
  /* eslint-disable no-console */
22
26
  function createConsoleObservable(api) {
23
- var observable = new Observable(function(){
27
+ var observable = new Observable(function () {
24
28
  var originalConsoleApi = console[api]
25
- console[api] = function() {
29
+ console[api] = function () {
26
30
  var params = [].slice.call(arguments)
27
31
  originalConsoleApi.apply(console, arguments)
28
32
  var handlingStack = createHandlingStack()
29
33
  observable.notify(buildConsoleLog(params, api, handlingStack))
30
34
  }
31
- return function() {
35
+ return function () {
32
36
  console[api] = originalConsoleApi
33
37
  }
34
38
  })
@@ -37,20 +41,26 @@ function createConsoleObservable(api) {
37
41
 
38
42
  function buildConsoleLog(params, api, handlingStack) {
39
43
  // Todo: remove console error prefix in the next major version
40
- var message = map(params, function(param) { return formatConsoleParameters(param) }).join(' ')
44
+ var message = map(params, function (param) {
45
+ return formatConsoleParameters(param)
46
+ }).join(' ')
41
47
  var stack
42
48
 
43
49
  if (api === ConsoleApiName.error) {
44
- var firstErrorParam = find(params, function(param) { return param instanceof Error })
45
- stack = firstErrorParam ? toStackTraceString(computeStackTrace(firstErrorParam)) : undefined
46
- message = 'console error: '+ message
50
+ var firstErrorParam = find(params, function (param) {
51
+ return param instanceof Error
52
+ })
53
+ stack = firstErrorParam
54
+ ? toStackTraceString(computeStackTrace(firstErrorParam))
55
+ : undefined
56
+ message = 'console error: ' + message
47
57
  }
48
58
 
49
59
  return {
50
60
  api: api,
51
61
  message: message,
52
62
  stack: stack,
53
- handlingStack: handlingStack,
63
+ handlingStack: handlingStack
54
64
  }
55
65
  }
56
66
 
package/src/dataMap.js CHANGED
@@ -47,6 +47,7 @@ export var dataMap = {
47
47
  },
48
48
  fields: {
49
49
  session_replay_stats: '_dd.replay_stats',
50
+ session_is_active: 'session.is_active',
50
51
  view_error_count: 'view.error.count',
51
52
  view_resource_count: 'view.resource.count',
52
53
  view_long_task_count: 'view.long_task.count',
@@ -0,0 +1,17 @@
1
+ export var ONE_KIBI_BYTE = 1024
2
+ export var ONE_MEBI_BYTE = 1024 * ONE_KIBI_BYTE
3
+ // eslint-disable-next-line no-control-regex
4
+ var HAS_MULTI_BYTES_CHARACTERS = /[^\u0000-\u007F]/
5
+
6
+ export function computeBytesCount(candidate) {
7
+ // Accurate bytes count computations can degrade performances when there is a lot of events to process
8
+ if (!HAS_MULTI_BYTES_CHARACTERS.test(candidate)) {
9
+ return candidate.length
10
+ }
11
+
12
+ if (window.TextEncoder !== undefined) {
13
+ return new TextEncoder().encode(candidate).length
14
+ }
15
+
16
+ return new Blob([candidate]).size
17
+ }
@@ -1,5 +1,5 @@
1
1
  import { relativeNow, ONE_MINUTE, filter, map } from './tools'
2
-
2
+ import { setInterval, clearInterval } from './timer'
3
3
  var END_OF_TIMES = Infinity
4
4
 
5
5
  export var CLEAR_OLD_CONTEXTS_INTERVAL = ONE_MINUTE
@@ -1,16 +1,13 @@
1
1
  import { ErrorSource } from './errorTools'
2
2
  import { clocksNow, ONE_MINUTE } from './tools'
3
- export function createEventRateLimiter(
4
- eventType,
5
- limit,
6
- onLimitReached
7
- ) {
3
+ import { setTimeout } from './timer'
4
+ export function createEventRateLimiter(eventType, limit, onLimitReached) {
8
5
  var eventCount = 0
9
6
  var allowNextEvent = false
10
7
  return {
11
- isLimitReached: function() {
8
+ isLimitReached: function () {
12
9
  if (eventCount === 0) {
13
- setTimeout(function(){
10
+ setTimeout(function () {
14
11
  eventCount = 0
15
12
  }, ONE_MINUTE)
16
13
  }
@@ -23,9 +20,10 @@ export function createEventRateLimiter(
23
20
  allowNextEvent = true
24
21
  try {
25
22
  onLimitReached({
26
- message: 'Reached max number of ' + eventType +'s by minute: ' + limit,
23
+ message:
24
+ 'Reached max number of ' + eventType + 's by minute: ' + limit,
27
25
  source: ErrorSource.AGENT,
28
- startClocks: clocksNow(),
26
+ startClocks: clocksNow()
29
27
  })
30
28
  } finally {
31
29
  allowNextEvent = false
@@ -1,4 +1,5 @@
1
- import { each, noop, jsonStringify } from './tools'
1
+ import { each, noop } from './tools'
2
+ import { jsonStringify } from '../helper/serialisation/jsonStringify'
2
3
  import { computeStackTrace } from '../tracekit'
3
4
 
4
5
  export var ErrorSource = {