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
@@ -1,9 +1,7 @@
1
1
  /* globals jQuery */
2
2
  /* globals __JQUERY_PLUGIN_VERSION__ */
3
3
 
4
-
5
- (function(jQuery, window, document) {
6
-
4
+ (function (jQuery, window, document) {
7
5
  var rb = window.Rollbar;
8
6
  if (!rb) {
9
7
  return;
@@ -16,14 +14,14 @@
16
14
  notifier: {
17
15
  plugins: {
18
16
  jquery: {
19
- version: JQUERY_PLUGIN_VERSION
20
- }
21
- }
22
- }
23
- }
17
+ version: JQUERY_PLUGIN_VERSION,
18
+ },
19
+ },
20
+ },
21
+ },
24
22
  });
25
23
 
26
- var logError = function(e) {
24
+ var logError = function (e) {
27
25
  rb.error(e);
28
26
  if (window.console) {
29
27
  var msg = '[reported to Rollbar]';
@@ -35,35 +33,37 @@
35
33
  };
36
34
 
37
35
  // Report any ajax errors to Rollbar
38
- jQuery(document).ajaxError(function(event, jqXHR, ajaxSettings, thrownError) {
39
- var status = jqXHR.status;
40
- var url = ajaxSettings.url;
41
- var type = ajaxSettings.type;
36
+ jQuery(document).ajaxError(
37
+ function (event, jqXHR, ajaxSettings, thrownError) {
38
+ var status = jqXHR.status;
39
+ var url = ajaxSettings.url;
40
+ var type = ajaxSettings.type;
42
41
 
43
- // If status === 0 it means the user left the page before the ajax event finished
44
- // or other uninteresting events.
45
- if (!status) {
46
- return;
47
- }
42
+ // If status === 0 it means the user left the page before the ajax event finished
43
+ // or other uninteresting events.
44
+ if (!status) {
45
+ return;
46
+ }
48
47
 
49
- var extra = {
50
- status: status,
51
- url: url,
52
- type: type,
53
- isAjax: true,
54
- data: ajaxSettings.data,
55
- jqXHR_responseText: jqXHR.responseText,
56
- jqXHR_statusText: jqXHR.statusText
57
- };
58
- var msg = thrownError ? thrownError : 'jQuery ajax error for ' + type;
59
- rb.warning(msg, extra);
60
- });
48
+ var extra = {
49
+ status: status,
50
+ url: url,
51
+ type: type,
52
+ isAjax: true,
53
+ data: ajaxSettings.data,
54
+ jqXHR_responseText: jqXHR.responseText,
55
+ jqXHR_statusText: jqXHR.statusText,
56
+ };
57
+ var msg = thrownError ? thrownError : 'jQuery ajax error for ' + type;
58
+ rb.warning(msg, extra);
59
+ },
60
+ );
61
61
 
62
62
  // Wraps functions passed into jQuery's ready() with try/catch to
63
63
  // report errors to Rollbar
64
64
  var origReady = jQuery.fn.ready;
65
- jQuery.fn.ready = function(fn) {
66
- return origReady.call(this, function($) {
65
+ jQuery.fn.ready = function (fn) {
66
+ return origReady.call(this, function ($) {
67
67
  try {
68
68
  fn($);
69
69
  } catch (e) {
@@ -75,10 +75,10 @@
75
75
  // Modified from the code removed from Tracekit in this commit
76
76
  // https://github.com/occ/TraceKit/commit/0d39401
77
77
  var _oldEventAdd = jQuery.event.add;
78
- jQuery.event.add = function(elem, types, handler, data, selector) {
78
+ jQuery.event.add = function (elem, types, handler, data, selector) {
79
79
  var _handler;
80
- var wrap = function(fn) {
81
- return function() {
80
+ var wrap = function (fn) {
81
+ return function () {
82
82
  try {
83
83
  return fn.apply(this, arguments);
84
84
  } catch (e) {
@@ -8,5 +8,5 @@ function checkIgnore(item, settings) {
8
8
  }
9
9
 
10
10
  module.exports = {
11
- checkIgnore: checkIgnore
11
+ checkIgnore: checkIgnore,
12
12
  };
@@ -0,0 +1,71 @@
1
+ /**
2
+ * Default options for the rrweb recorder
3
+ * See https://github.com/rrweb-io/rrweb/blob/master/guide.md#options for details
4
+ */
5
+ export default {
6
+ enabled: false, // Whether recording is enabled
7
+ autoStart: true, // Start recording automatically when Rollbar initializes
8
+ maxSeconds: 300, // Maximum recording duration in seconds
9
+
10
+ // trigger options
11
+ triggerOptions: {
12
+ // Trigger replay on specific items (occurrences)
13
+ item: {
14
+ levels: ['error', 'critical'], // Trigger on item level
15
+ }
16
+ },
17
+
18
+ debug: {
19
+ logErrors: true, // Whether to log errors emitted by rrweb.
20
+ logEmits: false, // Whether to log emitted events
21
+ },
22
+
23
+ // Recording options
24
+ inlineStylesheet: true, // Whether to inline stylesheets to improve replay accuracy
25
+ inlineImages: false, // Whether to record the image content
26
+ collectFonts: true, // Whether to collect fonts in the website
27
+
28
+ // Privacy options
29
+ // Fine-grained control over which input types to mask
30
+ // By default only password inputs are masked if maskInputs is true
31
+ maskInputOptions: {
32
+ password: true,
33
+ email: false,
34
+ tel: false,
35
+ text: false,
36
+ color: false,
37
+ date: false,
38
+ 'datetime-local': false,
39
+ month: false,
40
+ number: false,
41
+ range: false,
42
+ search: false,
43
+ time: false,
44
+ url: false,
45
+ week: false,
46
+ },
47
+
48
+ // Remove unnecessary parts of the DOM
49
+ // By default all removable elements are removed
50
+ slimDOMOptions: {
51
+ script: true, // Remove script elements
52
+ comment: true, // Remove comments
53
+ headFavicon: true, // Remove favicons in the head
54
+ headWhitespace: true, // Remove whitespace in head
55
+ headMetaDescKeywords: true, // Remove meta description and keywords
56
+ headMetaSocial: true, // Remove social media meta tags
57
+ headMetaRobots: true, // Remove robots meta directives
58
+ headMetaHttpEquiv: true, // Remove http-equiv meta directives
59
+ headMetaAuthorship: true, // Remove authorship meta directives
60
+ headMetaVerification: true, // Remove verification meta directives
61
+ },
62
+
63
+ // Custom callbacks for advanced use cases
64
+ // These are undefined by default and can be set programmatically
65
+ // maskInputFn: undefined, // Custom function to mask input values
66
+ // maskTextFn: undefined, // Custom function to mask text content
67
+ // errorHandler: undefined, // Custom error handler for recording errors
68
+
69
+ // Plugin system
70
+ // plugins: [] // List of plugins to use (must be set programmatically)
71
+ };
@@ -0,0 +1,193 @@
1
+ import { record as rrwebRecordFn } from '@rrweb/record';
2
+ import { EventType } from '@rrweb/types';
3
+
4
+ import hrtime from '../../tracing/hrtime.js';
5
+ import logger from '../logger.js';
6
+
7
+ export default class Recorder {
8
+ #options;
9
+ #rrwebOptions;
10
+ #stopFn = null;
11
+ #recordFn;
12
+ #events = {
13
+ previous: [],
14
+ current: [],
15
+ };
16
+
17
+ /**
18
+ * Creates a new Recorder instance for capturing DOM events
19
+ *
20
+ * @param {Object} options - Configuration options for the recorder
21
+ * @param {Function} [recordFn=rrwebRecordFn] - The recording function to use
22
+ */
23
+ constructor(options, recordFn = rrwebRecordFn) {
24
+ if (!recordFn) {
25
+ throw new TypeError("Expected 'recordFn' to be provided");
26
+ }
27
+
28
+ this.options = options;
29
+ this.#recordFn = recordFn;
30
+ }
31
+
32
+ get isRecording() {
33
+ return this.#stopFn !== null;
34
+ }
35
+
36
+ get options() {
37
+ return this.#options;
38
+ }
39
+
40
+ set options(newOptions) {
41
+ this.configure(newOptions);
42
+ }
43
+
44
+ configure(newOptions) {
45
+ const {
46
+ // Rollbar options
47
+ enabled,
48
+ autoStart,
49
+ maxSeconds,
50
+ triggerOptions,
51
+
52
+ // disallowed rrweb options
53
+ emit,
54
+ checkoutEveryNms,
55
+
56
+ // rrweb options
57
+ ...rrwebOptions
58
+ } = newOptions;
59
+ this.#options = { enabled, autoStart, maxSeconds, triggerOptions };
60
+ this.#rrwebOptions = rrwebOptions;
61
+
62
+ if (this.isRecording && newOptions.enabled === false) {
63
+ this.stop();
64
+ }
65
+ }
66
+
67
+ checkoutEveryNms() {
68
+ // Recording may be up to two checkout intervals, therefore the checkout
69
+ // interval is set to half of the maxSeconds.
70
+ return (this.options.maxSeconds || 10) * 1000 / 2;
71
+ }
72
+
73
+ /**
74
+ * Converts recorded events into a formatted payload ready for transport.
75
+ *
76
+ * This method takes the recorder's stored events, creates a new span with the
77
+ * provided tracing context, attaches all events with their timestamps as span
78
+ * events, and then returns a payload ready for transport to the server.
79
+ *
80
+ * @param {Object} tracing - The tracing system instance to create spans
81
+ * @param {string} replayId - Unique identifier to associate with this replay recording
82
+ * @returns {Object|null} A formatted payload containing spans data in OTLP format, or null if no events exist
83
+ */
84
+ dump(tracing, replayId, occurrenceUuid) {
85
+ const events = this.#events.previous.concat(this.#events.current);
86
+
87
+ if (events.length < 2) {
88
+ logger.error('Replay recording cannot have less than 2 events');
89
+ return null;
90
+ }
91
+
92
+ const recordingSpan = tracing.startSpan('rrweb-replay-recording', {});
93
+
94
+ recordingSpan.setAttribute('rollbar.replay.id', replayId);
95
+
96
+ if (occurrenceUuid) {
97
+ recordingSpan.setAttribute('rollbar.occurrence.uuid', occurrenceUuid);
98
+ }
99
+
100
+ const earliestEvent = events.reduce((earliestEvent, event) =>
101
+ event.timestamp < earliestEvent.timestamp ? event : earliestEvent,
102
+ );
103
+
104
+ recordingSpan.span.startTime = hrtime.fromMillis(earliestEvent.timestamp);
105
+
106
+ for (const event of events) {
107
+ recordingSpan.addEvent(
108
+ 'rrweb-replay-events',
109
+ {
110
+ eventType: event.type,
111
+ json: JSON.stringify(event.data),
112
+ 'rollbar.replay.id': replayId,
113
+ },
114
+ hrtime.fromMillis(event.timestamp),
115
+ );
116
+ }
117
+
118
+ recordingSpan.end();
119
+
120
+ return tracing.exporter.toPayload();
121
+ }
122
+
123
+ start() {
124
+ if (this.isRecording || this.options.enabled === false) {
125
+ return;
126
+ }
127
+
128
+ this.clear();
129
+
130
+ this.#stopFn = this.#recordFn({
131
+ emit: (event, isCheckout) => {
132
+ if (this.options.debug?.logEmits) {
133
+ this._logEvent(event, isCheckout);
134
+ }
135
+
136
+ if (isCheckout && event.type === EventType.Meta) {
137
+ this.#events.previous = this.#events.current;
138
+ this.#events.current = [];
139
+ }
140
+
141
+ this.#events.current.push(event);
142
+ },
143
+ checkoutEveryNms: this.checkoutEveryNms(),
144
+ errorHandler: (error) => {
145
+ if (this.options.debug?.logErrors) {
146
+ logger.error('Error during replay recording', error);
147
+ }
148
+ return true; // swallow the error instead of throwing it to the window
149
+ },
150
+ ...this.#rrwebOptions,
151
+ });
152
+
153
+ return this;
154
+ }
155
+
156
+ stop() {
157
+ if (!this.isRecording) {
158
+ return;
159
+ }
160
+
161
+ this.#stopFn();
162
+ this.#stopFn = null;
163
+
164
+ return this;
165
+ }
166
+
167
+ clear() {
168
+ this.#events = {
169
+ previous: [],
170
+ current: [],
171
+ };
172
+ }
173
+
174
+ _logEvent(event, isCheckout) {
175
+ logger.log(
176
+ `Recorder: ${isCheckout ? 'checkout' : ''} event\n`,
177
+ ((e) => {
178
+ const seen = new WeakSet();
179
+ return JSON.stringify(
180
+ e,
181
+ (_, v) => {
182
+ if (typeof v === 'object' && v !== null) {
183
+ if (seen.has(v)) return '[Circular]';
184
+ seen.add(v);
185
+ }
186
+ return v;
187
+ },
188
+ 2,
189
+ );
190
+ })(event),
191
+ );
192
+ }
193
+ }
@@ -0,0 +1,195 @@
1
+ import id from '../../tracing/id.js';
2
+ import logger from '../logger.js';
3
+
4
+ /**
5
+ * ReplayMap - Manages the mapping between error occurrences and their associated
6
+ * session recordings. This class handles the coordination between when recordings
7
+ * are dumped and when they are eventually sent to the backend.
8
+ */
9
+ export default class ReplayMap {
10
+ #map;
11
+ #recorder;
12
+ #api;
13
+ #tracing;
14
+
15
+ /**
16
+ * Creates a new ReplayMap instance
17
+ *
18
+ * @param {Object} props - Configuration props
19
+ * @param {Object} props.recorder - The recorder instance that dumps replay data into spans
20
+ * @param {Object} props.api - The API instance used to send replay payloads to the backend
21
+ * @param {Object} props.tracing - The tracing instance used to create spans and manage context
22
+ */
23
+ constructor({ recorder, api, tracing }) {
24
+ if (!recorder) {
25
+ throw new TypeError("Expected 'recorder' to be provided");
26
+ }
27
+
28
+ if (!api) {
29
+ throw new TypeError("Expected 'api' to be provided");
30
+ }
31
+
32
+ if (!tracing) {
33
+ throw new TypeError("Expected 'tracing' to be provided");
34
+ }
35
+
36
+ this.#map = new Map();
37
+ this.#recorder = recorder;
38
+ this.#api = api;
39
+ this.#tracing = tracing;
40
+ }
41
+
42
+ /**
43
+ * Processes a replay by converting recorder events into a transport-ready payload.
44
+ *
45
+ * Calls recorder.dump() to capture events as spans, formats them into a proper payload,
46
+ * and stores the result in the map using replayId as the key.
47
+ *
48
+ * @param {string} replayId - The unique ID for this replay
49
+ * @returns {Promise<string>} A promise resolving to the processed replayId
50
+ * @private
51
+ */
52
+ async _processReplay(replayId, occurrenceUuid) {
53
+ try {
54
+ const payload = this.#recorder.dump(
55
+ this.#tracing,
56
+ replayId,
57
+ occurrenceUuid,
58
+ );
59
+
60
+ this.#map.set(replayId, payload);
61
+ } catch (transformError) {
62
+ logger.error('Error transforming spans:', transformError);
63
+
64
+ this.#map.set(replayId, null); // TODO(matux): Error span?
65
+ }
66
+
67
+ return replayId;
68
+ }
69
+
70
+ /**
71
+ * Adds a replay to the map and returns a uniquely generated replay ID.
72
+ *
73
+ * This method immediately returns the replayId and asynchronously processes
74
+ * the replay data in the background. The processing involves converting
75
+ * recorder events into a payload format and storing it in the map.
76
+ *
77
+ * @returns {string} A unique identifier for this replay
78
+ */
79
+ add(replayId, occurrenceUuid) {
80
+ replayId = replayId || id.gen(8);
81
+
82
+ this._processReplay(replayId, occurrenceUuid).catch((error) => {
83
+ logger.error('Failed to process replay:', error);
84
+ });
85
+
86
+ return replayId;
87
+ }
88
+
89
+ /**
90
+ * Sends the replay payload associated with the given replayId to the backend
91
+ * and removes it from the map.
92
+ *
93
+ * Retrieves the payload from the map, checks if it's valid, then sends it
94
+ * to the API endpoint for processing. The payload can be either a spans array
95
+ * or a formatted OTLP payload object.
96
+ *
97
+ * @param {string} replayId - The ID of the replay to send
98
+ * @returns {Promise<boolean>} A promise that resolves to true if the payload was found and sent, false otherwise
99
+ */
100
+ async send(replayId) {
101
+ if (!replayId) {
102
+ logger.error('ReplayMap.send: No replayId provided');
103
+ return false;
104
+ }
105
+
106
+ if (!this.#map.has(replayId)) {
107
+ logger.error(`ReplayMap.send: No replay found for replayId: ${replayId}`);
108
+ return false;
109
+ }
110
+
111
+ const payload = this.#map.get(replayId);
112
+ this.#map.delete(replayId);
113
+
114
+ // Check if payload is empty (could be raw spans array or OTLP payload)
115
+ const isEmpty =
116
+ !payload ||
117
+ (Array.isArray(payload) && payload.length === 0) ||
118
+ (payload.resourceSpans && payload.resourceSpans.length === 0);
119
+
120
+ if (isEmpty) {
121
+ logger.error(
122
+ `ReplayMap.send: No payload found for replayId: ${replayId}`,
123
+ );
124
+ return false;
125
+ }
126
+
127
+ try {
128
+ await this.#api.postSpans(payload);
129
+ return true;
130
+ } catch (error) {
131
+ logger.error('Error sending replay:', error);
132
+ return false;
133
+ }
134
+ }
135
+
136
+ /**
137
+ * Discards the replay associated with the given replay ID by removing
138
+ * it from the map without sending it.
139
+ *
140
+ * @param {string} replayId - The ID of the replay to discard
141
+ * @returns {boolean} True if a replay was found and discarded, false otherwise
142
+ */
143
+ discard(replayId) {
144
+ if (!replayId) {
145
+ logger.error('ReplayMap.discard: No replayId provided');
146
+ return false;
147
+ }
148
+
149
+ if (!this.#map.has(replayId)) {
150
+ logger.error(
151
+ `ReplayMap.discard: No replay found for replayId: ${replayId}`,
152
+ );
153
+ return false;
154
+ }
155
+
156
+ this.#map.delete(replayId);
157
+ return true;
158
+ }
159
+
160
+ /**
161
+ * Gets spans for the given replay ID
162
+ *
163
+ * @param {string} replayId - The ID to retrieve spans for
164
+ * @returns {Array|null} The spans array or null if not found
165
+ */
166
+ getSpans(replayId) {
167
+ return this.#map.get(replayId) ?? null;
168
+ }
169
+
170
+ /**
171
+ * Sets spans for a given replay ID
172
+ *
173
+ * @param {string} replayId - The ID to set spans for
174
+ * @param {Array} spans - The spans to set
175
+ */
176
+ setSpans(replayId, spans) {
177
+ this.#map.set(replayId, spans);
178
+ }
179
+
180
+ /**
181
+ * Returns the size of the map (number of stored replays)
182
+ *
183
+ * @returns {number} The number of replays currently stored
184
+ */
185
+ get size() {
186
+ return this.#map.size;
187
+ }
188
+
189
+ /**
190
+ * Clears all stored replays without sending them
191
+ */
192
+ clear() {
193
+ this.#map.clear();
194
+ }
195
+ }
@@ -1,10 +1,12 @@
1
- var Rollbar = require('./core');
2
- var telemeter = require('../telemetry');
3
- var instrumenter = require('./telemetry');
4
- var polyfillJSON = require('../utility/polyfillJSON');
5
- var wrapGlobals = require('./wrapGlobals');
6
- var scrub = require('../scrub');
7
- var truncation = require('../truncation');
1
+ const Rollbar = require('./core');
2
+ const telemeter = require('../telemetry');
3
+ const instrumenter = require('./telemetry');
4
+ const polyfillJSON = require('../utility/polyfillJSON');
5
+ const wrapGlobals = require('./wrapGlobals');
6
+ const scrub = require('../scrub');
7
+ const truncation = require('../truncation');
8
+ const Tracing = require('../tracing/tracing');
9
+ const Recorder = require('./replay/recorder');
8
10
 
9
11
  Rollbar.setComponents({
10
12
  telemeter: telemeter,
@@ -12,7 +14,9 @@ Rollbar.setComponents({
12
14
  polyfillJSON: polyfillJSON,
13
15
  wrapGlobals: wrapGlobals,
14
16
  scrub: scrub,
15
- truncation: truncation
17
+ truncation: truncation,
18
+ tracing: Tracing.default,
19
+ recorder: Recorder.default,
16
20
  });
17
21
 
18
22
  module.exports = Rollbar;
@@ -5,8 +5,8 @@ function RollbarWrap(impl, options) {
5
5
  }
6
6
 
7
7
  function _setupForwarding(prototype) {
8
- var _forward = function(method) {
9
- return function() {
8
+ var _forward = function (method) {
9
+ return function () {
10
10
  var args = Array.prototype.slice.call(arguments, 0);
11
11
  if (this.impl[method]) {
12
12
  return this.impl[method].apply(this.impl, args);
@@ -14,13 +14,16 @@ function _setupForwarding(prototype) {
14
14
  };
15
15
  };
16
16
 
17
- var _methods = 'log,debug,info,warn,warning,error,critical,global,configure,handleUncaughtException,handleAnonymousErrors,handleUnhandledRejection,_createItem,wrap,loadFull,shimId,captureEvent,captureDomContentLoaded,captureLoad'.split(',');
18
- for (var i=0; i<_methods.length; i++) {
17
+ var _methods =
18
+ 'log,debug,info,warn,warning,error,critical,global,configure,handleUncaughtException,handleAnonymousErrors,handleUnhandledRejection,_createItem,wrap,loadFull,shimId,captureEvent,captureDomContentLoaded,captureLoad'.split(
19
+ ',',
20
+ );
21
+ for (var i = 0; i < _methods.length; i++) {
19
22
  prototype[_methods[i]] = _forward(_methods[i]);
20
23
  }
21
24
  }
22
25
 
23
- RollbarWrap.prototype._swapAndProcessMessages = function(impl, messages) {
26
+ RollbarWrap.prototype._swapAndProcessMessages = function (impl, messages) {
24
27
  this.impl = impl(this.options);
25
28
  var msg, method, args;
26
29
  while ((msg = messages.shift())) {