rollbar 2.26.3 → 3.0.0-alpha.1

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 (186) hide show
  1. package/.cursor/rules/guidelines.mdc +154 -0
  2. package/.github/workflows/ci.yml +32 -12
  3. package/.lgtm.yml +7 -7
  4. package/.prettierignore +18 -0
  5. package/.vscode/settings.json +39 -0
  6. package/CHANGELOG.md +121 -35
  7. package/CLAUDE.md +201 -0
  8. package/Gruntfile.js +101 -48
  9. package/Makefile +3 -3
  10. package/README.md +2 -4
  11. package/SECURITY.md +5 -0
  12. package/babel.config.json +9 -0
  13. package/bower.json +1 -3
  14. package/codex.md +148 -0
  15. package/defaults.js +17 -5
  16. package/dist/plugins/jquery.min.js +1 -1
  17. package/dist/rollbar.js +18748 -5375
  18. package/dist/rollbar.js.map +1 -1
  19. package/dist/rollbar.min.js +2 -1
  20. package/dist/rollbar.min.js.LICENSE.txt +1 -0
  21. package/dist/rollbar.min.js.map +1 -1
  22. package/dist/rollbar.named-amd.js +19368 -6000
  23. package/dist/rollbar.named-amd.js.map +1 -1
  24. package/dist/rollbar.named-amd.min.js +3 -1
  25. package/dist/rollbar.named-amd.min.js.LICENSE.txt +1 -0
  26. package/dist/rollbar.named-amd.min.js.map +1 -1
  27. package/dist/rollbar.noconflict.umd.js +18749 -5380
  28. package/dist/rollbar.noconflict.umd.js.map +1 -1
  29. package/dist/rollbar.noconflict.umd.min.js +3 -1
  30. package/dist/rollbar.noconflict.umd.min.js.LICENSE.txt +1 -0
  31. package/dist/rollbar.noconflict.umd.min.js.map +1 -1
  32. package/dist/rollbar.snippet.js +1 -1
  33. package/dist/rollbar.umd.js +19367 -6000
  34. package/dist/rollbar.umd.js.map +1 -1
  35. package/dist/rollbar.umd.min.js +3 -1
  36. package/dist/rollbar.umd.min.js.LICENSE.txt +1 -0
  37. package/dist/rollbar.umd.min.js.map +1 -1
  38. package/docs/extension-exceptions.md +35 -30
  39. package/docs/migration_v0_to_v1.md +41 -38
  40. package/eslint.config.mjs +33 -0
  41. package/get_versions.js +33 -0
  42. package/index.d.ts +270 -231
  43. package/karma.conf.js +18 -27
  44. package/package.json +21 -21
  45. package/prettier.config.js +7 -0
  46. package/src/api.js +78 -14
  47. package/src/apiUtility.js +14 -11
  48. package/src/browser/core.js +138 -72
  49. package/src/browser/defaults/scrubFields.js +3 -3
  50. package/src/browser/detection.js +7 -8
  51. package/src/browser/domUtility.js +18 -8
  52. package/src/browser/globalSetup.js +12 -6
  53. package/src/browser/logger.js +1 -1
  54. package/src/browser/plugins/jquery.js +35 -35
  55. package/src/browser/predicates.js +1 -1
  56. package/src/browser/replay/defaults.js +71 -0
  57. package/src/browser/replay/recorder.js +193 -0
  58. package/src/browser/replay/replayMap.js +195 -0
  59. package/src/browser/rollbar.js +12 -8
  60. package/src/browser/rollbarWrapper.js +8 -5
  61. package/src/browser/shim.js +43 -19
  62. package/src/browser/snippet_callback.js +6 -4
  63. package/src/browser/telemetry.js +573 -361
  64. package/src/browser/transforms.js +46 -27
  65. package/src/browser/transport/fetch.js +26 -14
  66. package/src/browser/transport/xhr.js +41 -14
  67. package/src/browser/transport.js +93 -33
  68. package/src/browser/url.js +16 -8
  69. package/src/browser/wrapGlobals.js +27 -8
  70. package/src/defaults.js +3 -3
  71. package/src/errorParser.js +14 -11
  72. package/src/merge.js +32 -23
  73. package/src/notifier.js +16 -13
  74. package/src/predicates.js +43 -23
  75. package/src/queue.js +133 -40
  76. package/src/rateLimiter.js +59 -18
  77. package/src/react-native/logger.js +1 -1
  78. package/src/react-native/rollbar.js +59 -55
  79. package/src/react-native/transforms.js +13 -9
  80. package/src/react-native/transport.js +44 -34
  81. package/src/rollbar.js +72 -21
  82. package/src/scrub.js +0 -1
  83. package/src/server/locals.js +69 -39
  84. package/src/server/logger.js +4 -4
  85. package/src/server/parser.js +72 -47
  86. package/src/server/rollbar.js +135 -56
  87. package/src/server/sourceMap/stackTrace.js +33 -18
  88. package/src/server/telemetry/urlHelpers.js +9 -11
  89. package/src/server/telemetry.js +68 -45
  90. package/src/server/transforms.js +37 -21
  91. package/src/server/transport.js +62 -32
  92. package/src/telemetry.js +162 -33
  93. package/src/tracing/context.js +24 -0
  94. package/src/tracing/contextManager.js +37 -0
  95. package/src/tracing/defaults.js +7 -0
  96. package/src/tracing/exporter.js +188 -0
  97. package/src/tracing/hrtime.js +98 -0
  98. package/src/tracing/id.js +24 -0
  99. package/src/tracing/session.js +55 -0
  100. package/src/tracing/span.js +92 -0
  101. package/src/tracing/spanProcessor.js +15 -0
  102. package/src/tracing/tracer.js +46 -0
  103. package/src/tracing/tracing.js +89 -0
  104. package/src/transforms.js +33 -21
  105. package/src/truncation.js +8 -5
  106. package/src/utility/headers.js +43 -43
  107. package/src/utility/replace.js +9 -0
  108. package/src/utility/traverse.js +1 -1
  109. package/src/utility.js +123 -52
  110. package/test/api.test.js +88 -41
  111. package/test/apiUtility.test.js +48 -50
  112. package/test/browser.core.test.js +142 -141
  113. package/test/browser.domUtility.test.js +53 -36
  114. package/test/browser.predicates.test.js +14 -14
  115. package/test/browser.replay.recorder.test.js +416 -0
  116. package/test/browser.rollbar.test.js +655 -515
  117. package/test/browser.telemetry.test.js +46 -39
  118. package/test/browser.transforms.test.js +164 -139
  119. package/test/browser.transport.test.js +59 -50
  120. package/test/browser.url.test.js +13 -12
  121. package/test/fixtures/locals.fixtures.js +245 -126
  122. package/test/fixtures/replay/index.js +20 -0
  123. package/test/fixtures/replay/payloads.fixtures.js +229 -0
  124. package/test/fixtures/replay/rrwebEvents.fixtures.js +251 -0
  125. package/test/fixtures/replay/rrwebSyntheticEvents.fixtures.js +328 -0
  126. package/test/notifier.test.js +91 -79
  127. package/test/predicates.test.js +261 -215
  128. package/test/queue.test.js +231 -215
  129. package/test/rateLimiter.test.js +51 -43
  130. package/test/react-native.rollbar.test.js +150 -116
  131. package/test/react-native.transforms.test.js +23 -25
  132. package/test/react-native.transport.test.js +26 -14
  133. package/test/replay/index.js +2 -0
  134. package/test/replay/integration/api.spans.test.js +136 -0
  135. package/test/replay/integration/e2e.test.js +228 -0
  136. package/test/replay/integration/index.js +9 -0
  137. package/test/replay/integration/queue.replayMap.test.js +332 -0
  138. package/test/replay/integration/replayMap.test.js +163 -0
  139. package/test/replay/integration/sessionRecording.test.js +390 -0
  140. package/test/replay/unit/api.postSpans.test.js +150 -0
  141. package/test/replay/unit/index.js +7 -0
  142. package/test/replay/unit/queue.replayMap.test.js +225 -0
  143. package/test/replay/unit/replayMap.test.js +348 -0
  144. package/test/replay/util/index.js +5 -0
  145. package/test/replay/util/mockRecordFn.js +80 -0
  146. package/test/server.lambda.mocha.test.mjs +172 -0
  147. package/test/server.locals.constructor.mocha.test.mjs +80 -0
  148. package/test/server.locals.error-handling.mocha.test.mjs +387 -0
  149. package/test/server.locals.merge.mocha.test.mjs +267 -0
  150. package/test/server.locals.test-utils.mjs +114 -0
  151. package/test/server.parser.mocha.test.mjs +87 -0
  152. package/test/server.predicates.mocha.test.mjs +63 -0
  153. package/test/server.rollbar.constructor.mocha.test.mjs +199 -0
  154. package/test/server.rollbar.handlers.mocha.test.mjs +253 -0
  155. package/test/server.rollbar.logging.mocha.test.mjs +326 -0
  156. package/test/server.rollbar.misc.mocha.test.mjs +44 -0
  157. package/test/server.rollbar.test-utils.mjs +57 -0
  158. package/test/server.telemetry.mocha.test.mjs +377 -0
  159. package/test/server.transforms.data.mocha.test.mjs +163 -0
  160. package/test/server.transforms.error.mocha.test.mjs +199 -0
  161. package/test/server.transforms.request.mocha.test.mjs +208 -0
  162. package/test/server.transforms.scrub.mocha.test.mjs +140 -0
  163. package/test/server.transforms.sourcemaps.mocha.test.mjs +122 -0
  164. package/test/server.transforms.test-utils.mjs +62 -0
  165. package/test/server.transport.mocha.test.mjs +269 -0
  166. package/test/telemetry.test.js +178 -38
  167. package/test/tracing/contextManager.test.js +28 -0
  168. package/test/tracing/exporter.toPayload.test.js +400 -0
  169. package/test/tracing/id.test.js +24 -0
  170. package/test/tracing/span.test.js +183 -0
  171. package/test/tracing/spanProcessor.test.js +73 -0
  172. package/test/tracing/tracing.test.js +105 -0
  173. package/test/transforms.test.js +70 -68
  174. package/test/truncation.test.js +57 -55
  175. package/test/utility.test.js +310 -228
  176. package/webpack.config.js +36 -70
  177. package/.eslintignore +0 -7
  178. package/.gitmodules +0 -3
  179. package/test/server.lambda.test.js +0 -177
  180. package/test/server.locals.test.js +0 -841
  181. package/test/server.parser.test.js +0 -72
  182. package/test/server.predicates.test.js +0 -89
  183. package/test/server.rollbar.test.js +0 -676
  184. package/test/server.telemetry.test.js +0 -318
  185. package/test/server.transforms.test.js +0 -1099
  186. package/test/server.transport.test.js +0 -201
package/src/defaults.js CHANGED
@@ -1,9 +1,9 @@
1
1
  module.exports = {
2
- version: '2.26.3',
2
+ version: '3.0.0-alpha.1',
3
3
  endpoint: 'api.rollbar.com/api/1/item/',
4
4
  logLevel: 'debug',
5
5
  reportLevel: 'debug',
6
6
  uncaughtErrorLevel: 'error',
7
7
  maxItems: 0,
8
- itemsPerMin: 60
9
- }
8
+ itemsPerMin: 60,
9
+ };
@@ -1,18 +1,18 @@
1
1
  var ErrorStackParser = require('error-stack-parser');
2
2
 
3
3
  var UNKNOWN_FUNCTION = '?';
4
- var ERR_CLASS_REGEXP = new RegExp('^(([a-zA-Z0-9-_$ ]*): *)?(Uncaught )?([a-zA-Z0-9-_$ ]*): ');
4
+ var ERR_CLASS_REGEXP = new RegExp(
5
+ '^(([a-zA-Z0-9-_$ ]*): *)?(Uncaught )?([a-zA-Z0-9-_$ ]*): ',
6
+ );
5
7
 
6
8
  function guessFunctionName() {
7
9
  return UNKNOWN_FUNCTION;
8
10
  }
9
11
 
10
-
11
12
  function gatherContext() {
12
13
  return null;
13
14
  }
14
15
 
15
-
16
16
  function Frame(stackFrame) {
17
17
  var data = {};
18
18
 
@@ -29,7 +29,6 @@ function Frame(stackFrame) {
29
29
  return data;
30
30
  }
31
31
 
32
-
33
32
  function Stack(exception, skip) {
34
33
  function getStack() {
35
34
  var parserStack = [];
@@ -38,7 +37,7 @@ function Stack(exception, skip) {
38
37
 
39
38
  try {
40
39
  parserStack = ErrorStackParser.parse(exception);
41
- } catch(e) {
40
+ } catch (e) {
42
41
  parserStack = [];
43
42
  }
44
43
 
@@ -56,11 +55,10 @@ function Stack(exception, skip) {
56
55
  message: exception.message,
57
56
  name: _mostSpecificErrorName(exception),
58
57
  rawStack: exception.stack,
59
- rawException: exception
58
+ rawException: exception,
60
59
  };
61
60
  }
62
61
 
63
-
64
62
  function parse(e, skip) {
65
63
  var err = e;
66
64
 
@@ -81,7 +79,6 @@ function parse(e, skip) {
81
79
  }
82
80
  }
83
81
 
84
-
85
82
  function guessErrorClass(errMsg) {
86
83
  if (!errMsg || !errMsg.match) {
87
84
  return ['Unknown error. There was no error message to display.', ''];
@@ -91,7 +88,10 @@ function guessErrorClass(errMsg) {
91
88
 
92
89
  if (errClassMatch) {
93
90
  errClass = errClassMatch[errClassMatch.length - 1];
94
- errMsg = errMsg.replace((errClassMatch[errClassMatch.length - 2] || '') + errClass + ':', '');
91
+ errMsg = errMsg.replace(
92
+ (errClassMatch[errClassMatch.length - 2] || '') + errClass + ':',
93
+ '',
94
+ );
95
95
  errMsg = errMsg.replace(/(^[\s]+|[\s]+$)/g, '');
96
96
  }
97
97
  return [errClass, errMsg];
@@ -102,7 +102,10 @@ function guessErrorClass(errMsg) {
102
102
  // * Prefers name over constructor.name when both are more specific than 'Error'
103
103
  function _mostSpecificErrorName(error) {
104
104
  var name = error.name && error.name.length && error.name;
105
- var constructorName = error.constructor.name && error.constructor.name.length && error.constructor.name;
105
+ var constructorName =
106
+ error.constructor.name &&
107
+ error.constructor.name.length &&
108
+ error.constructor.name;
106
109
 
107
110
  if (!name || !constructorName) {
108
111
  return name || constructorName;
@@ -120,5 +123,5 @@ module.exports = {
120
123
  gatherContext: gatherContext,
121
124
  parse: parse,
122
125
  Stack: Stack,
123
- Frame: Frame
126
+ Frame: Frame,
124
127
  };
package/src/merge.js CHANGED
@@ -4,32 +4,41 @@ var hasOwn = Object.prototype.hasOwnProperty;
4
4
  var toStr = Object.prototype.toString;
5
5
 
6
6
  var isPlainObject = function isPlainObject(obj) {
7
- if (!obj || toStr.call(obj) !== '[object Object]') {
8
- return false;
9
- }
10
-
11
- var hasOwnConstructor = hasOwn.call(obj, 'constructor');
12
- var hasIsPrototypeOf = obj.constructor && obj.constructor.prototype && hasOwn.call(obj.constructor.prototype, 'isPrototypeOf');
13
- // Not own constructor property must be Object
14
- if (obj.constructor && !hasOwnConstructor && !hasIsPrototypeOf) {
15
- return false;
16
- }
17
-
18
- // Own properties are enumerated firstly, so to speed up,
19
- // if last one is own, then all properties are own.
20
- var key;
21
- for (key in obj) {/**/}
22
-
23
- return typeof key === 'undefined' || hasOwn.call(obj, key);
7
+ if (!obj || toStr.call(obj) !== '[object Object]') {
8
+ return false;
9
+ }
10
+
11
+ var hasOwnConstructor = hasOwn.call(obj, 'constructor');
12
+ var hasIsPrototypeOf =
13
+ obj.constructor &&
14
+ obj.constructor.prototype &&
15
+ hasOwn.call(obj.constructor.prototype, 'isPrototypeOf');
16
+ // Not own constructor property must be Object
17
+ if (obj.constructor && !hasOwnConstructor && !hasIsPrototypeOf) {
18
+ return false;
19
+ }
20
+
21
+ // Own properties are enumerated firstly, so to speed up,
22
+ // if last one is own, then all properties are own.
23
+ var key;
24
+ for (key in obj) {
25
+ /**/
26
+ }
27
+
28
+ return typeof key === 'undefined' || hasOwn.call(obj, key);
24
29
  };
25
30
 
26
31
  function merge() {
27
- var i, src, copy, clone, name,
28
- result = {},
29
- current = null,
30
- length = arguments.length;
31
-
32
- for (i=0; i < length; i++) {
32
+ var i,
33
+ src,
34
+ copy,
35
+ clone,
36
+ name,
37
+ result = {},
38
+ current = null,
39
+ length = arguments.length;
40
+
41
+ for (i = 0; i < length; i++) {
33
42
  current = arguments[i];
34
43
  if (current == null) {
35
44
  continue;
package/src/notifier.js CHANGED
@@ -22,7 +22,7 @@ function Notifier(queue, options) {
22
22
  * @param options - an object which gets merged with the current options set on this notifier
23
23
  * @returns this
24
24
  */
25
- Notifier.prototype.configure = function(options) {
25
+ Notifier.prototype.configure = function (options) {
26
26
  this.queue && this.queue.configure(options);
27
27
  var oldOptions = this.options;
28
28
  this.options = _.merge(oldOptions, options);
@@ -40,7 +40,7 @@ Notifier.prototype.configure = function(options) {
40
40
  * with an error to terminate the processing. The item should be the updated item after this
41
41
  * transform is finished modifying it.
42
42
  */
43
- Notifier.prototype.addTransform = function(transform) {
43
+ Notifier.prototype.addTransform = function (transform) {
44
44
  if (_.isFunction(transform)) {
45
45
  this.transforms.push(transform);
46
46
  }
@@ -60,9 +60,9 @@ Notifier.prototype.addTransform = function(transform) {
60
60
  * transform stage if an error occurs inside a transform, or in response to the communication with
61
61
  * the backend. The second argument will be the response from the backend in case of success.
62
62
  */
63
- Notifier.prototype.log = function(item, callback) {
63
+ Notifier.prototype.log = function (item, callback) {
64
64
  if (!callback || !_.isFunction(callback)) {
65
- callback = function() {};
65
+ callback = function () {};
66
66
  }
67
67
 
68
68
  if (!this.options.enabled) {
@@ -71,13 +71,16 @@ Notifier.prototype.log = function(item, callback) {
71
71
 
72
72
  this.queue.addPendingItem(item);
73
73
  var originalError = item.err;
74
- this._applyTransforms(item, function(err, i) {
75
- if (err) {
76
- this.queue.removePendingItem(item);
77
- return callback(err, null);
78
- }
79
- this.queue.addItem(i, callback, originalError, item);
80
- }.bind(this));
74
+ this._applyTransforms(
75
+ item,
76
+ function (err, i) {
77
+ if (err) {
78
+ this.queue.removePendingItem(item);
79
+ return callback(err, null);
80
+ }
81
+ this.queue.addItem(i, callback, originalError, item);
82
+ }.bind(this),
83
+ );
81
84
  };
82
85
 
83
86
  /* Internal */
@@ -91,13 +94,13 @@ Notifier.prototype.log = function(item, callback) {
91
94
  * error and a null item in the case of a transform failure, or a null error and non-null item after
92
95
  * all transforms have been applied.
93
96
  */
94
- Notifier.prototype._applyTransforms = function(item, callback) {
97
+ Notifier.prototype._applyTransforms = function (item, callback) {
95
98
  var transformIndex = -1;
96
99
  var transformsLength = this.transforms.length;
97
100
  var transforms = this.transforms;
98
101
  var options = this.options;
99
102
 
100
- var cb = function(err, i) {
103
+ var cb = function (err, i) {
101
104
  if (err) {
102
105
  callback(err, null);
103
106
  return;
package/src/predicates.js CHANGED
@@ -13,7 +13,7 @@ function checkLevel(item, settings) {
13
13
  }
14
14
 
15
15
  function userCheckIgnore(logger) {
16
- return function(item, settings) {
16
+ return function (item, settings) {
17
17
  var isUncaught = !!item._isUncaught;
18
18
  delete item._isUncaught;
19
19
  var args = item._originalArgs;
@@ -27,7 +27,10 @@ function userCheckIgnore(logger) {
27
27
  logger.error('Error while calling onSendCallback, removing', e);
28
28
  }
29
29
  try {
30
- if (_.isFunction(settings.checkIgnore) && settings.checkIgnore(isUncaught, args, item)) {
30
+ if (
31
+ _.isFunction(settings.checkIgnore) &&
32
+ settings.checkIgnore(isUncaught, args, item)
33
+ ) {
31
34
  return false;
32
35
  }
33
36
  } catch (e) {
@@ -35,27 +38,31 @@ function userCheckIgnore(logger) {
35
38
  logger.error('Error while calling custom checkIgnore(), removing', e);
36
39
  }
37
40
  return true;
38
- }
41
+ };
39
42
  }
40
43
 
41
44
  function urlIsNotBlockListed(logger) {
42
- return function(item, settings) {
45
+ return function (item, settings) {
43
46
  return !urlIsOnAList(item, settings, 'blocklist', logger);
44
- }
47
+ };
45
48
  }
46
49
 
47
50
  function urlIsSafeListed(logger) {
48
- return function(item, settings) {
51
+ return function (item, settings) {
49
52
  return urlIsOnAList(item, settings, 'safelist', logger);
50
- }
53
+ };
51
54
  }
52
55
 
53
56
  function matchFrames(trace, list, block) {
54
- if (!trace) { return !block }
57
+ if (!trace) {
58
+ return !block;
59
+ }
55
60
 
56
61
  var frames = trace.frames;
57
62
 
58
- if (!frames || frames.length === 0) { return !block; }
63
+ if (!frames || frames.length === 0) {
64
+ return !block;
65
+ }
59
66
 
60
67
  var frame, filename, url, urlRegex;
61
68
  var listLength = list.length;
@@ -64,7 +71,9 @@ function matchFrames(trace, list, block) {
64
71
  frame = frames[i];
65
72
  filename = frame.filename;
66
73
 
67
- if (!_.isType(filename, 'string')) { return !block; }
74
+ if (!_.isType(filename, 'string')) {
75
+ return !block;
76
+ }
68
77
 
69
78
  for (var j = 0; j < listLength; j++) {
70
79
  url = list[j];
@@ -101,27 +110,35 @@ function urlIsOnAList(item, settings, safeOrBlock, logger) {
101
110
 
102
111
  var tracesLength = traces.length;
103
112
  for (var i = 0; i < tracesLength; i++) {
104
- if(matchFrames(traces[i], list, block)) {
113
+ if (matchFrames(traces[i], list, block)) {
105
114
  return true;
106
115
  }
107
116
  }
108
- } catch (e)
109
- /* istanbul ignore next */
110
- {
117
+ } catch (
118
+ e
119
+ /* istanbul ignore next */
120
+ ) {
111
121
  if (block) {
112
122
  settings.hostBlockList = null;
113
123
  } else {
114
124
  settings.hostSafeList = null;
115
125
  }
116
126
  var listName = block ? 'hostBlockList' : 'hostSafeList';
117
- logger.error('Error while reading your configuration\'s ' + listName + ' option. Removing custom ' + listName + '.', e);
127
+ logger.error(
128
+ "Error while reading your configuration's " +
129
+ listName +
130
+ ' option. Removing custom ' +
131
+ listName +
132
+ '.',
133
+ e,
134
+ );
118
135
  return !block;
119
136
  }
120
137
  return false;
121
138
  }
122
139
 
123
140
  function messageIsIgnored(logger) {
124
- return function(item, settings) {
141
+ return function (item, settings) {
125
142
  var i, j, ignoredMessages, len, messageIsIgnored, rIgnoredMessage, messages;
126
143
 
127
144
  try {
@@ -134,7 +151,7 @@ function messageIsIgnored(logger) {
134
151
 
135
152
  messages = messagesFromItem(item);
136
153
 
137
- if (messages.length === 0){
154
+ if (messages.length === 0) {
138
155
  return true;
139
156
  }
140
157
 
@@ -150,15 +167,18 @@ function messageIsIgnored(logger) {
150
167
  }
151
168
  }
152
169
  }
153
- } catch(e)
154
- /* istanbul ignore next */
155
- {
170
+ } catch (
171
+ e
172
+ /* istanbul ignore next */
173
+ ) {
156
174
  settings.ignoredMessages = null;
157
- logger.error('Error while reading your configuration\'s ignoredMessages option. Removing custom ignoredMessages.');
175
+ logger.error(
176
+ "Error while reading your configuration's ignoredMessages option. Removing custom ignoredMessages.",
177
+ );
158
178
  }
159
179
 
160
180
  return true;
161
- }
181
+ };
162
182
  }
163
183
 
164
184
  function messagesFromItem(item) {
@@ -189,5 +209,5 @@ module.exports = {
189
209
  userCheckIgnore: userCheckIgnore,
190
210
  urlIsNotBlockListed: urlIsNotBlockListed,
191
211
  urlIsSafeListed: urlIsSafeListed,
192
- messageIsIgnored: messageIsIgnored
212
+ messageIsIgnored: messageIsIgnored,
193
213
  };
package/src/queue.js CHANGED
@@ -12,12 +12,14 @@ var _ = require('./utility');
12
12
  * api.postItem(payload, function(err, response))
13
13
  * @param logger - An object used to log verbose messages if desired
14
14
  * @param options - see Queue.prototype.configure
15
+ * @param replayMap - Optional ReplayMap for coordinating session replay with error occurrences
15
16
  */
16
- function Queue(rateLimiter, api, logger, options) {
17
+ function Queue(rateLimiter, api, logger, options, replayMap) {
17
18
  this.rateLimiter = rateLimiter;
18
19
  this.api = api;
19
20
  this.logger = logger;
20
21
  this.options = options;
22
+ this.replayMap = replayMap;
21
23
  this.predicates = [];
22
24
  this.pendingItems = [];
23
25
  this.pendingRequests = [];
@@ -32,7 +34,7 @@ function Queue(rateLimiter, api, logger, options) {
32
34
  *
33
35
  * @param options
34
36
  */
35
- Queue.prototype.configure = function(options) {
37
+ Queue.prototype.configure = function (options) {
36
38
  this.api && this.api.configure(options);
37
39
  var oldOptions = this.options;
38
40
  this.options = _.merge(oldOptions, options);
@@ -48,18 +50,18 @@ Queue.prototype.configure = function(options) {
48
50
  * Returning {err: Error} means do not add the item to the queue, and the given error explains why
49
51
  * Returning {err: undefined} is equivalent to returning true but don't do that
50
52
  */
51
- Queue.prototype.addPredicate = function(predicate) {
53
+ Queue.prototype.addPredicate = function (predicate) {
52
54
  if (_.isFunction(predicate)) {
53
55
  this.predicates.push(predicate);
54
56
  }
55
57
  return this;
56
58
  };
57
59
 
58
- Queue.prototype.addPendingItem = function(item) {
60
+ Queue.prototype.addPendingItem = function (item) {
59
61
  this.pendingItems.push(item);
60
62
  };
61
63
 
62
- Queue.prototype.removePendingItem = function(item) {
64
+ Queue.prototype.removePendingItem = function (item) {
63
65
  var idx = this.pendingItems.indexOf(item);
64
66
  if (idx !== -1) {
65
67
  this.pendingItems.splice(idx, 1);
@@ -76,9 +78,16 @@ Queue.prototype.removePendingItem = function(item) {
76
78
  * to be an error condition, but nonetheless did not send the item to the API.
77
79
  * @param originalError - The original error before any transformations that is to be logged if any
78
80
  */
79
- Queue.prototype.addItem = function(item, callback, originalError, originalItem) {
81
+ Queue.prototype.addItem = function (
82
+ item,
83
+ callback,
84
+ originalError,
85
+ originalItem,
86
+ ) {
80
87
  if (!callback || !_.isFunction(callback)) {
81
- callback = function() { return; };
88
+ callback = function () {
89
+ return;
90
+ };
82
91
  }
83
92
  var predicateResult = this._applyPredicates(item);
84
93
  if (predicateResult.stop) {
@@ -92,12 +101,28 @@ Queue.prototype.addItem = function(item, callback, originalError, originalItem)
92
101
  callback(new Error('Transmit disabled'));
93
102
  return;
94
103
  }
104
+
105
+ if (this.replayMap && item.body) {
106
+ const replayId = _.getItemAttribute(item, 'replay_id');
107
+ if (replayId) {
108
+ item.replayId = this.replayMap.add(replayId, item.uuid);
109
+ }
110
+ }
111
+
95
112
  this.pendingRequests.push(item);
96
113
  try {
97
- this._makeApiRequest(item, function(err, resp) {
98
- this._dequeuePendingRequest(item);
99
- callback(err, resp);
100
- }.bind(this));
114
+ this._makeApiRequest(
115
+ item,
116
+ function (err, resp, headers) {
117
+ this._dequeuePendingRequest(item);
118
+
119
+ if (!err && resp && item.replayId) {
120
+ this._handleReplayResponse(item.replayId, resp, headers);
121
+ }
122
+
123
+ callback(err, resp);
124
+ }.bind(this),
125
+ );
101
126
  } catch (e) {
102
127
  this._dequeuePendingRequest(item);
103
128
  callback(e);
@@ -110,7 +135,7 @@ Queue.prototype.addItem = function(item, callback, originalError, originalItem)
110
135
  *
111
136
  * @param callback - function() called when all pending items have been sent
112
137
  */
113
- Queue.prototype.wait = function(callback) {
138
+ Queue.prototype.wait = function (callback) {
114
139
  if (!_.isFunction(callback)) {
115
140
  return;
116
141
  }
@@ -121,9 +146,12 @@ Queue.prototype.wait = function(callback) {
121
146
  if (this.waitIntervalID) {
122
147
  this.waitIntervalID = clearInterval(this.waitIntervalID);
123
148
  }
124
- this.waitIntervalID = setInterval(function() {
125
- this._maybeCallWait();
126
- }.bind(this), 500);
149
+ this.waitIntervalID = setInterval(
150
+ function () {
151
+ this._maybeCallWait();
152
+ }.bind(this),
153
+ 500,
154
+ );
127
155
  };
128
156
 
129
157
  /* _applyPredicates - Sequentially applies the predicates that have been added to the queue to the
@@ -133,15 +161,15 @@ Queue.prototype.wait = function(callback) {
133
161
  * @returns {stop: bool, err: (Error|null)} - stop being true means do not add item to the queue,
134
162
  * the error value should be passed up to a callbak if we are stopping.
135
163
  */
136
- Queue.prototype._applyPredicates = function(item) {
164
+ Queue.prototype._applyPredicates = function (item) {
137
165
  var p = null;
138
166
  for (var i = 0, len = this.predicates.length; i < len; i++) {
139
167
  p = this.predicates[i](item, this.options);
140
168
  if (!p || p.err !== undefined) {
141
- return {stop: true, err: p.err};
169
+ return { stop: true, err: p.err };
142
170
  }
143
171
  }
144
- return {stop: false, err: null};
172
+ return { stop: false, err: null };
145
173
  };
146
174
 
147
175
  /*
@@ -151,16 +179,19 @@ Queue.prototype._applyPredicates = function(item) {
151
179
  * @param item - an item ready to send to the backend
152
180
  * @param callback - function(err, response)
153
181
  */
154
- Queue.prototype._makeApiRequest = function(item, callback) {
182
+ Queue.prototype._makeApiRequest = function (item, callback) {
155
183
  var rateLimitResponse = this.rateLimiter.shouldSend(item);
156
184
  if (rateLimitResponse.shouldSend) {
157
- this.api.postItem(item, function(err, resp) {
158
- if (err) {
159
- this._maybeRetry(err, item, callback);
160
- } else {
161
- callback(err, resp);
162
- }
163
- }.bind(this));
185
+ this.api.postItem(
186
+ item,
187
+ function (err, resp, headers) {
188
+ if (err) {
189
+ this._maybeRetry(err, item, callback);
190
+ } else {
191
+ callback(err, resp, headers);
192
+ }
193
+ }.bind(this),
194
+ );
164
195
  } else if (rateLimitResponse.error) {
165
196
  callback(rateLimitResponse.error);
166
197
  } else {
@@ -169,7 +200,16 @@ Queue.prototype._makeApiRequest = function(item, callback) {
169
200
  };
170
201
 
171
202
  // These are errors basically mean there is no internet connection
172
- var RETRIABLE_ERRORS = ['ECONNRESET', 'ENOTFOUND', 'ESOCKETTIMEDOUT', 'ETIMEDOUT', 'ECONNREFUSED', 'EHOSTUNREACH', 'EPIPE', 'EAI_AGAIN'];
203
+ var RETRIABLE_ERRORS = [
204
+ 'ECONNRESET',
205
+ 'ENOTFOUND',
206
+ 'ESOCKETTIMEDOUT',
207
+ 'ETIMEDOUT',
208
+ 'ECONNREFUSED',
209
+ 'EHOSTUNREACH',
210
+ 'EPIPE',
211
+ 'EAI_AGAIN',
212
+ ];
173
213
 
174
214
  /*
175
215
  * _maybeRetry - Given the error returned by the API, decide if we should retry or just callback
@@ -179,7 +219,7 @@ var RETRIABLE_ERRORS = ['ECONNRESET', 'ENOTFOUND', 'ESOCKETTIMEDOUT', 'ETIMEDOUT
179
219
  * @param item - the item that was trying to be sent when this error occured
180
220
  * @param callback - function(err, response)
181
221
  */
182
- Queue.prototype._maybeRetry = function(err, item, callback) {
222
+ Queue.prototype._maybeRetry = function (err, item, callback) {
183
223
  var shouldRetry = false;
184
224
  if (this.options.retryInterval) {
185
225
  for (var i = 0, len = RETRIABLE_ERRORS.length; i < len; i++) {
@@ -209,16 +249,19 @@ Queue.prototype._maybeRetry = function(err, item, callback) {
209
249
  * @param item - an item that failed to send due to an error we deem retriable
210
250
  * @param callback - function(err, response)
211
251
  */
212
- Queue.prototype._retryApiRequest = function(item, callback) {
213
- this.retryQueue.push({item: item, callback: callback});
252
+ Queue.prototype._retryApiRequest = function (item, callback) {
253
+ this.retryQueue.push({ item: item, callback: callback });
214
254
 
215
255
  if (!this.retryHandle) {
216
- this.retryHandle = setInterval(function() {
217
- while (this.retryQueue.length) {
218
- var retryObject = this.retryQueue.shift();
219
- this._makeApiRequest(retryObject.item, retryObject.callback);
220
- }
221
- }.bind(this), this.options.retryInterval);
256
+ this.retryHandle = setInterval(
257
+ function () {
258
+ while (this.retryQueue.length) {
259
+ var retryObject = this.retryQueue.shift();
260
+ this._makeApiRequest(retryObject.item, retryObject.callback);
261
+ }
262
+ }.bind(this),
263
+ this.options.retryInterval,
264
+ );
222
265
  }
223
266
  };
224
267
 
@@ -230,7 +273,7 @@ Queue.prototype._retryApiRequest = function(item, callback) {
230
273
  *
231
274
  * @param item - the item previously added to the pending request queue
232
275
  */
233
- Queue.prototype._dequeuePendingRequest = function(item) {
276
+ Queue.prototype._dequeuePendingRequest = function (item) {
234
277
  var idx = this.pendingRequests.indexOf(item);
235
278
  if (idx !== -1) {
236
279
  this.pendingRequests.splice(idx, 1);
@@ -238,7 +281,7 @@ Queue.prototype._dequeuePendingRequest = function(item) {
238
281
  }
239
282
  };
240
283
 
241
- Queue.prototype._maybeLog = function(data, originalError) {
284
+ Queue.prototype._maybeLog = function (data, originalError) {
242
285
  if (this.logger && this.options.verbose) {
243
286
  var message = originalError;
244
287
  message = message || _.get(data, 'body.trace.exception.message');
@@ -254,8 +297,12 @@ Queue.prototype._maybeLog = function(data, originalError) {
254
297
  }
255
298
  };
256
299
 
257
- Queue.prototype._maybeCallWait = function() {
258
- if (_.isFunction(this.waitCallback) && this.pendingItems.length === 0 && this.pendingRequests.length === 0) {
300
+ Queue.prototype._maybeCallWait = function () {
301
+ if (
302
+ _.isFunction(this.waitCallback) &&
303
+ this.pendingItems.length === 0 &&
304
+ this.pendingRequests.length === 0
305
+ ) {
259
306
  if (this.waitIntervalID) {
260
307
  this.waitIntervalID = clearInterval(this.waitIntervalID);
261
308
  }
@@ -265,4 +312,50 @@ Queue.prototype._maybeCallWait = function() {
265
312
  return false;
266
313
  };
267
314
 
315
+ /**
316
+ * Handles the API response for an item with a replay ID.
317
+ * Based on the success or failure status of the response,
318
+ * it either sends or discards the associated session replay.
319
+ *
320
+ * @param {string} replayId - The ID of the replay to handle
321
+ * @param {Object} response - The API response
322
+ * @returns {Promise<boolean>} A promise that resolves to true if replay was sent successfully,
323
+ * false if replay was discarded or an error occurred
324
+ * @private
325
+ */
326
+ Queue.prototype._handleReplayResponse = async function (replayId, response, headers) {
327
+ if (!this.replayMap) {
328
+ console.warn('Queue._handleReplayResponse: ReplayMap not available');
329
+ return false;
330
+ }
331
+
332
+ if (!replayId) {
333
+ console.warn('Queue._handleReplayResponse: No replayId provided');
334
+ return false;
335
+ }
336
+
337
+ try {
338
+ if (this._shouldSendReplay(response, headers)) {
339
+ return await this.replayMap.send(replayId);
340
+ } else {
341
+ this.replayMap.discard(replayId);
342
+ return false;
343
+ }
344
+ } catch (error) {
345
+ console.error('Error handling replay response:', error);
346
+ return false;
347
+ }
348
+ };
349
+
350
+ Queue.prototype._shouldSendReplay = function (response, headers) {
351
+ if (response?.err !== 0 ||
352
+ !headers ||
353
+ headers['Rollbar-Replay-Enabled'] !== 'true' ||
354
+ headers['Rollbar-Replay-RateLimit-Remaining'] === '0') {
355
+ return false;
356
+ }
357
+
358
+ return true;
359
+ };
360
+
268
361
  module.exports = Queue;