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
@@ -0,0 +1,62 @@
1
+ /**
2
+ * Common test utilities for server transforms tests
3
+ */
4
+
5
+ import Rollbar from '../src/server/rollbar.js';
6
+ import merge from '../src/merge.js';
7
+
8
+ export class CustomError extends Rollbar.Error {
9
+ constructor(message, nested) {
10
+ super(message, nested);
11
+ }
12
+ }
13
+
14
+ export const nodeVersion = process.versions.node
15
+ .split('.')
16
+ .map((v) => parseInt(v));
17
+
18
+ export const isMinNodeVersion = (major, minor) =>
19
+ nodeVersion[0] > major ||
20
+ (nodeVersion[0] === major && nodeVersion[1] >= minor);
21
+
22
+ export async function wait(ms) {
23
+ return new Promise((resolve) => {
24
+ setTimeout(resolve, ms);
25
+ });
26
+ }
27
+
28
+ export async function throwInScriptFile(filepath) {
29
+ // Dynamic imports inside setTimeout with async/await can create timing
30
+ // issues where the error escapes before Rollbar's handler can catch it.
31
+ const module = await import(filepath);
32
+ setTimeout(() => module.default(), 10);
33
+ await wait(500);
34
+ }
35
+
36
+ // Base test item factory for request-related tests
37
+ export function createTestItem(overrides = {}) {
38
+ const base = {
39
+ request: {
40
+ headers: {
41
+ host: 'example.com',
42
+ 'x-auth-token': '12345',
43
+ },
44
+ protocol: 'https',
45
+ url: '/some/endpoint',
46
+ ip: '192.192.192.1',
47
+ method: 'GET',
48
+ body: {
49
+ token: 'abc123',
50
+ something: 'else',
51
+ },
52
+ user: {
53
+ id: 42,
54
+ email: 'fake@example.com',
55
+ },
56
+ },
57
+ stuff: 'hey',
58
+ data: { other: 'thing' },
59
+ };
60
+
61
+ return merge(base, overrides);
62
+ }
@@ -0,0 +1,269 @@
1
+ /* globals describe */
2
+ /* globals it */
3
+
4
+ import { expect } from 'chai';
5
+ import Transport from '../src/server/transport.js';
6
+
7
+ class TestTransport {
8
+ constructor(options, error, response, assertions) {
9
+ this.options = options;
10
+ this.error = error;
11
+ this.response = response;
12
+ this.requestOpts = null;
13
+ this.requestCallback = null;
14
+ this.assertions = assertions;
15
+ }
16
+
17
+ request(opts, cb) {
18
+ this.requestOpts = opts;
19
+ this.requestCallback = cb;
20
+ return new TestRequest(this.error, this.response, this);
21
+ }
22
+ }
23
+
24
+ class TestRequest {
25
+ constructor(error, response, transport) {
26
+ this.error = error;
27
+ this.responseData = response;
28
+ this.data = [];
29
+ this.events = {};
30
+ this.transport = transport;
31
+ this.response = null;
32
+ }
33
+
34
+ write(data) {
35
+ this.data.push(data);
36
+ }
37
+
38
+ on(event, cb) {
39
+ this.events[event] = cb;
40
+ }
41
+
42
+ end() {
43
+ if (this.transport.assertions) {
44
+ this.transport.assertions();
45
+ }
46
+ if (this.error) {
47
+ if (this.events['error']) {
48
+ this.events['error'](this.error);
49
+ }
50
+ } else {
51
+ this.response = new TestResponse();
52
+ this.transport.requestCallback(this.response);
53
+ if (this.response.events['data']) {
54
+ this.response.events['data'](this.responseData);
55
+ }
56
+ if (this.response.events['end']) {
57
+ this.response.events['end']();
58
+ }
59
+ }
60
+ }
61
+ }
62
+
63
+ class TestResponse {
64
+ constructor(options = {}) {
65
+ this.encoding = null;
66
+ this.events = {};
67
+ this.headers = options.headers || {};
68
+ this.statusCode = options.statusCode || 200;
69
+ }
70
+
71
+ setEncoding(s) {
72
+ this.encoding = s;
73
+ }
74
+
75
+ on(event, cb) {
76
+ this.events[event] = cb;
77
+ }
78
+ }
79
+
80
+ const transportFactory = (error, response, assertions) => (options) =>
81
+ new TestTransport(options, error, response, assertions);
82
+
83
+ describe('transport', function () {
84
+ const t = new Transport();
85
+
86
+ describe('post', function () {
87
+ describe('base data', function () {
88
+ const baseData = {
89
+ accessToken: 'abc123',
90
+ options: {},
91
+ payload: {
92
+ access_token: 'abc123',
93
+ data: { a: 1 },
94
+ },
95
+ };
96
+
97
+ it('should have an error with no payload', function (done) {
98
+ const factory = transportFactory(
99
+ null,
100
+ '{"err": null, "result":"all good"}',
101
+ );
102
+
103
+ t.post(
104
+ baseData.accessToken,
105
+ baseData.options,
106
+ null,
107
+ (err, resp) => {
108
+ expect(err).to.exist;
109
+ expect(resp).to.not.exist;
110
+ done();
111
+ },
112
+ factory,
113
+ );
114
+ });
115
+
116
+ it('should have the right response data with a payload and no error', function (done) {
117
+ const factory = transportFactory(
118
+ null,
119
+ '{"err": null, "result":{"uuid":"42def", "message":"all good"}}',
120
+ function () {
121
+ expect(this.options.headers['Content-Type']).to.equal(
122
+ 'application/json',
123
+ );
124
+ expect(this.options.headers['Content-Length']).to.be.a('number');
125
+ expect(this.options.headers['Content-Length']).to.be.above(0);
126
+ expect(this.options.headers['X-Rollbar-Access-Token']).to.equal(
127
+ baseData.accessToken,
128
+ );
129
+ },
130
+ );
131
+
132
+ t.post(
133
+ baseData.accessToken,
134
+ baseData.options,
135
+ baseData.payload,
136
+ (err, resp) => {
137
+ expect(err).to.not.exist;
138
+ expect(resp.message).to.equal('all good');
139
+ done();
140
+ },
141
+ factory,
142
+ );
143
+ });
144
+
145
+ it('should error with a payload and an error in the response', function (done) {
146
+ const factory = transportFactory(
147
+ null,
148
+ '{"err": "bork", "message":"things broke"}',
149
+ function () {
150
+ expect(this.options.headers['Content-Type']).to.equal(
151
+ 'application/json',
152
+ );
153
+ expect(this.options.headers['Content-Length']).to.be.a('number');
154
+ expect(this.options.headers['Content-Length']).to.be.above(0);
155
+ expect(this.options.headers['X-Rollbar-Access-Token']).to.equal(
156
+ baseData.accessToken,
157
+ );
158
+ },
159
+ );
160
+
161
+ t.post(
162
+ baseData.accessToken,
163
+ baseData.options,
164
+ baseData.payload,
165
+ (err, resp) => {
166
+ expect(err).to.exist;
167
+ expect(err.message).to.match(/things broke/);
168
+ expect(resp).to.not.exist;
169
+ done();
170
+ },
171
+ factory,
172
+ );
173
+ });
174
+
175
+ it('should error with a payload and an error during sending', function (done) {
176
+ const factory = transportFactory(new Error('bork'), null, function () {
177
+ expect(this.options.headers['Content-Type']).to.equal(
178
+ 'application/json',
179
+ );
180
+ expect(this.options.headers['Content-Length']).to.be.a('number');
181
+ expect(this.options.headers['Content-Length']).to.be.above(0);
182
+ expect(this.options.headers['X-Rollbar-Access-Token']).to.equal(
183
+ baseData.accessToken,
184
+ );
185
+ });
186
+
187
+ t.post(
188
+ baseData.accessToken,
189
+ baseData.options,
190
+ baseData.payload,
191
+ (err, resp) => {
192
+ expect(err).to.exist;
193
+ expect(err.message).to.match(/bork/);
194
+ expect(resp).to.not.exist;
195
+ done();
196
+ },
197
+ factory,
198
+ );
199
+ });
200
+ });
201
+
202
+ describe('with rate limiting', function () {
203
+ let transport;
204
+
205
+ beforeEach(function () {
206
+ transport = new Transport();
207
+ });
208
+
209
+ it('should transmit non-rate limited requests', function (done) {
210
+ const factory = transportFactory(
211
+ null,
212
+ '{"err": null, "result": "all good"}',
213
+ );
214
+ const response = new TestResponse({
215
+ statusCode: 200,
216
+ headers: {
217
+ 'x-rate-limit-remaining': '1',
218
+ 'x-rate-limit-remaining-seconds': '100',
219
+ },
220
+ });
221
+
222
+ expect(transport.rateLimitExpires).to.equal(0);
223
+
224
+ transport.handleResponse(response);
225
+
226
+ transport.post(
227
+ 'token',
228
+ {},
229
+ 'payload',
230
+ (err) => {
231
+ expect(err).to.not.exist;
232
+ expect(Math.floor(Date.now() / 1000)).to.be.at.least(
233
+ transport.rateLimitExpires,
234
+ );
235
+ done();
236
+ },
237
+ factory,
238
+ );
239
+ });
240
+
241
+ it('should drop rate limited requests and set timeout', function (done) {
242
+ const factory = transportFactory(new Error('bork'), null);
243
+ const response = new TestResponse({
244
+ statusCode: 429,
245
+ headers: {
246
+ 'x-rate-limit-remaining': '0',
247
+ 'x-rate-limit-remaining-seconds': '100',
248
+ },
249
+ });
250
+
251
+ transport.handleResponse(response);
252
+
253
+ transport.post(
254
+ 'token',
255
+ {},
256
+ 'payload',
257
+ (err) => {
258
+ expect(err.message).to.match(/Exceeded rate limit/);
259
+ expect(Math.floor(Date.now() / 1000)).to.be.below(
260
+ transport.rateLimitExpires,
261
+ );
262
+ done();
263
+ },
264
+ factory,
265
+ );
266
+ });
267
+ });
268
+ });
269
+ });
@@ -3,10 +3,11 @@
3
3
  /* globals it */
4
4
  /* globals sinon */
5
5
 
6
- var Telemeter = require('../src/telemetry');
6
+ import Telemeter from '../src/telemetry.js';
7
+ import Tracing from '../src/tracing/tracing.js';
7
8
 
8
- describe('Telemetry()', function() {
9
- it('should have all of the expected methods', function(done) {
9
+ describe('Telemetry()', function () {
10
+ it('should have all of the expected methods', function (done) {
10
11
  var options = {};
11
12
  var t = new Telemeter(options);
12
13
  expect(t).to.have.property('copyEvents');
@@ -20,12 +21,12 @@ describe('Telemetry()', function() {
20
21
  });
21
22
  });
22
23
 
23
- describe('capture', function() {
24
- it('should return a valid telemetry event', function(done) {
24
+ describe('capture', function () {
25
+ it('should return a valid telemetry event', function (done) {
25
26
  var options = {};
26
27
  var t = new Telemeter(options);
27
28
  var now = +new Date();
28
- var event = t.capture('network', {url: 'a.com'}, 'debug');
29
+ var event = t.capture('network', { url: 'a.com' }, 'debug');
29
30
  expect(event.timestamp_ms - now).to.be.below(500);
30
31
  expect(event.type).to.equal('network');
31
32
  expect(event.level).to.equal('debug');
@@ -35,10 +36,10 @@ describe('capture', function() {
35
36
  });
36
37
  });
37
38
 
38
- describe('captureEvent', function() {
39
- it('should return a valid telemetry event', function(done) {
39
+ describe('captureEvent', function () {
40
+ it('should return a valid telemetry event', function (done) {
40
41
  var t = new Telemeter();
41
- var event = t.captureEvent('log', {message: 'bar'}, 'info');
42
+ var event = t.captureEvent('log', { message: 'bar' }, 'info');
42
43
  expect(event.type).to.equal('log');
43
44
  expect(event.level).to.equal('info');
44
45
  expect(event.body.message).to.equal('bar');
@@ -47,32 +48,171 @@ describe('captureEvent', function() {
47
48
  });
48
49
  });
49
50
 
50
- describe('filterTelemetry', function() {
51
- it('should filter out events that don\'t match the test', function(done) {
52
- var options = {
53
- filterTelemetry: function(e) {
54
- return e.type === 'network'
55
- && (e.body.subtype === 'xhr' || e.body.subtype === 'fetch')
56
- && e.body.url.indexOf('https://spammer.com') === 0;
51
+ describe('capture events', function () {
52
+ beforeEach(function () {
53
+ const tracing = new Tracing(
54
+ window,
55
+ {
56
+ resource: {
57
+ 'service.name': 'Test',
58
+ },
57
59
  }
60
+ );
61
+ tracing.initSession();
62
+ this.t = new Telemeter({includeItemsInTelemetry: true}, tracing);
63
+ });
64
+
65
+ it('should return a valid log event', function (done) {
66
+ const timestamp = 12345.678;
67
+ const event = this.t.captureLog('foo', 'info', null, timestamp);
68
+ expect(event.type).to.equal('log');
69
+ expect(event.level).to.equal('info');
70
+ expect(event.body.message).to.equal('foo');
71
+ expect(event.timestamp_ms).to.equal(timestamp);
72
+
73
+ expect(this.t.telemetrySpan).to.be.an('object');
74
+ const otelEvent = this.t.telemetrySpan.span.events[0];
75
+ expect(otelEvent.name).to.equal('rollbar-log-event');
76
+ expect(otelEvent.time).to.eql([ 12, 345678000 ]);
77
+ expect(otelEvent.attributes).to.eql({ message: 'foo', level: 'info' });
78
+ done();
79
+ });
80
+
81
+ it('should return a valid error event', function (done) {
82
+ const timestamp = 12345.678;
83
+ const error = new Error('foo');
84
+ const uuid = '12345-67890';
85
+ const event = this.t.captureError(error, 'info', uuid, timestamp);
86
+ expect(event.type).to.eql('error');
87
+ expect(event.level).to.equal('info');
88
+ expect(event.body.message).to.equal('foo');
89
+ expect(event.timestamp_ms).to.equal(timestamp);
90
+
91
+ expect(this.t.telemetrySpan).to.be.an('object');
92
+ const otelEvent = this.t.telemetrySpan.span.events[0];
93
+ expect(otelEvent.name).to.eql('rollbar-occurrence-event');
94
+ expect(otelEvent.time).to.eql([ 12, 345678000 ]);
95
+ expect(otelEvent.attributes).to.eql(
96
+ { type: 'error', 'occurrence.type': 'error', message: 'foo', level: 'info', uuid: uuid, 'occurrence.uuid': uuid }
97
+ );
98
+ done();
99
+ });
100
+
101
+ it('should return a valid message event', function (done) {
102
+ const timestamp = 12345.678;
103
+ const uuid = '12345-67890';
104
+ const item = { message: 'foo', level: 'info', uuid: uuid, timestamp: timestamp };
105
+ const event = this.t._captureRollbarItem(item);
106
+ expect(event.type).to.eql('log');
107
+ expect(event.level).to.equal('info');
108
+ expect(event.body.message).to.equal('foo');
109
+ expect(event.timestamp_ms).to.equal(timestamp);
110
+
111
+ expect(this.t.telemetrySpan).to.be.an('object');
112
+ const otelEvent = this.t.telemetrySpan.span.events[0];
113
+ expect(otelEvent.name).to.eql('rollbar-occurrence-event');
114
+ expect(otelEvent.time).to.eql([ 12, 345678000 ]);
115
+ expect(otelEvent.attributes).to.eql(
116
+ { type: 'message', 'occurrence.type': 'message', message: 'foo', level: 'info', uuid: uuid, 'occurrence.uuid': uuid }
117
+ );
118
+ done();
119
+ });
120
+
121
+ it('should return a valid navigation event', function (done) {
122
+ const timestamp = 12345.678;
123
+ const from = 'foo';
124
+ const to = 'bar';
125
+ const event = this.t.captureNavigation(from, to, null, timestamp);
126
+ expect(event.type).to.equal('navigation');
127
+ expect(event.level).to.equal('info');
128
+ expect(event.body.from).to.equal('foo');
129
+ expect(event.body.to).to.equal('bar');
130
+ expect(event.timestamp_ms).to.equal(timestamp);
131
+
132
+ expect(this.t.telemetrySpan).to.be.an('object');
133
+ const otelEvent = this.t.telemetrySpan.span.events[0];
134
+ expect(otelEvent.name).to.equal('rollbar-navigation-event');
135
+ expect(otelEvent.time).to.eql([ 12, 345678000 ]);
136
+ expect(otelEvent.attributes).to.eql({ 'previous.url.full': 'foo', 'url.full': 'bar' });
137
+ done();
138
+ });
139
+
140
+ it('should return a valid network event', function (done) {
141
+ const subtype = 'xhr';
142
+ const timestamp = 12345.678;
143
+ const metadata = {
144
+ url: 'https://example.com',
145
+ method: 'GET',
146
+ status_code: 400,
147
+ request_headers: { 'Content-Type': 'application/json' },
148
+ response: {
149
+ headers: { 'Content-Type': 'application/json' },
150
+ },
151
+ start_time_ms: timestamp,
152
+ end_time_ms: 12345678,
153
+ };
154
+ const event = this.t.captureNetwork(metadata, subtype, null, null);
155
+ expect(event.type).to.eql('network');
156
+ expect(event.level).to.equal('error');
157
+ expect(event.body.url).to.equal(metadata.url);
158
+ expect(event.body.method).to.equal(metadata.method);
159
+ expect(event.body.status_code).to.equal(metadata.status_code);
160
+ expect(event.timestamp_ms).to.equal(timestamp);
161
+
162
+ expect(this.t.telemetrySpan).to.be.an('object');
163
+ const otelEvent = this.t.telemetrySpan.span.events[0];
164
+ expect(otelEvent.name).to.eql('rollbar-network-event');
165
+ expect(otelEvent.time).to.eql([ 12, 345678000 ]);
166
+ expect(otelEvent.attributes).to.eql(
167
+ {
168
+ type: subtype,
169
+ method: metadata.method,
170
+ statusCode: metadata.status_code,
171
+ url: metadata.url,
172
+ 'request.headers': JSON.stringify(metadata.request_headers),
173
+ 'response.headers': JSON.stringify(metadata.response.headers),
174
+ 'response.timeUnixNano': (metadata.end_time_ms * 1e6).toString(),
175
+ }
176
+ );
177
+ done();
178
+ });
179
+ });
180
+
181
+ describe('filterTelemetry', function () {
182
+ it("should filter out events that don't match the test", function (done) {
183
+ var options = {
184
+ filterTelemetry: function (e) {
185
+ return (
186
+ e.type === 'network' &&
187
+ (e.body.subtype === 'xhr' || e.body.subtype === 'fetch') &&
188
+ e.body.url.indexOf('https://spammer.com') === 0
189
+ );
190
+ },
58
191
  };
59
192
  var t = new Telemeter(options);
60
- var evt = t.capture('network', {url: 'https://spammer.com', subtype: 'xhr'}, 'debug');
193
+ var evt = t.capture(
194
+ 'network',
195
+ { url: 'https://spammer.com', subtype: 'xhr' },
196
+ 'debug',
197
+ );
61
198
  expect(evt).to.be(false);
62
199
 
63
200
  done();
64
201
  });
65
202
 
66
- it('should filter out events in copy even if they are modified after capture', function(done) {
203
+ it('should filter out events in copy even if they are modified after capture', function (done) {
67
204
  var options = {
68
- filterTelemetry: function(e) {
69
- return e.type === 'network'
70
- && e.body.statusCode === 200;
71
- }
205
+ filterTelemetry: function (e) {
206
+ return e.type === 'network' && e.body.statusCode === 200;
207
+ },
72
208
  };
73
209
  var t = new Telemeter(options);
74
- var evt = t.capture('network', {url: 'https://spammer.com'}, 'debug');
75
- var evt2 = t.capture('network', {url: 'https://spammer.com', statusCode: 404}, 'debug');
210
+ var evt = t.capture('network', { url: 'https://spammer.com' }, 'debug');
211
+ var evt2 = t.capture(
212
+ 'network',
213
+ { url: 'https://spammer.com', statusCode: 404 },
214
+ 'debug',
215
+ );
76
216
  expect(evt).not.to.be(false);
77
217
  expect(evt2).not.to.be(false);
78
218
  var events = t.copyEvents();
@@ -88,50 +228,50 @@ describe('filterTelemetry', function() {
88
228
  });
89
229
  });
90
230
 
91
- describe('configure', function() {
92
- it('should truncate events to new max', function(done) {
93
- var options = {maxTelemetryEvents: 5};
231
+ describe('configure', function () {
232
+ it('should truncate events to new max', function (done) {
233
+ var options = { maxTelemetryEvents: 5 };
94
234
  var t = new Telemeter(options);
95
235
 
96
236
  for (var i = 0; i < 7; i++) {
97
- t.capture('network', {url: 'a.com'}, 'debug');
237
+ t.capture('network', { url: 'a.com' }, 'debug');
98
238
  }
99
239
 
100
240
  expect(t.queue.length).to.equal(5);
101
- t.configure({maxTelemetryEvents: 3});
241
+ t.configure({ maxTelemetryEvents: 3 });
102
242
  expect(t.queue.length).to.equal(3);
103
243
  done();
104
244
  });
105
- it('should lengthen events to allow new max', function(done) {
106
- var options = {maxTelemetryEvents: 3};
245
+ it('should lengthen events to allow new max', function (done) {
246
+ var options = { maxTelemetryEvents: 3 };
107
247
  var t = new Telemeter(options);
108
248
 
109
249
  for (var i = 0; i < 7; i++) {
110
- t.capture('network', {url: 'a.com'}, 'debug');
250
+ t.capture('network', { url: 'a.com' }, 'debug');
111
251
  }
112
252
 
113
253
  expect(t.queue.length).to.equal(3);
114
- t.configure({maxTelemetryEvents: 5});
254
+ t.configure({ maxTelemetryEvents: 5 });
115
255
  expect(t.queue.length).to.equal(3);
116
256
  for (var i = 0; i < 7; i++) {
117
- t.capture('network', {url: 'a.com'}, 'debug');
257
+ t.capture('network', { url: 'a.com' }, 'debug');
118
258
  }
119
259
  expect(t.queue.length).to.equal(5);
120
260
  done();
121
261
  });
122
- it('does not drop existing options that are not passed to configure', function(done) {
123
- var options = {maxTelemetryEvents: 3};
262
+ it('does not drop existing options that are not passed to configure', function (done) {
263
+ var options = { maxTelemetryEvents: 3 };
124
264
  var t = new Telemeter(options);
125
265
 
126
266
  for (var i = 0; i < 7; i++) {
127
- t.capture('network', {url: 'a.com'}, 'debug');
267
+ t.capture('network', { url: 'a.com' }, 'debug');
128
268
  }
129
269
 
130
270
  expect(t.queue.length).to.equal(3);
131
271
  t.configure({});
132
272
  expect(t.queue.length).to.equal(3);
133
273
  for (var i = 0; i < 7; i++) {
134
- t.capture('network', {url: 'a.com'}, 'debug');
274
+ t.capture('network', { url: 'a.com' }, 'debug');
135
275
  }
136
276
  expect(t.queue.length).to.equal(3);
137
277
  done();
@@ -0,0 +1,28 @@
1
+ /* globals describe */
2
+ /* globals it */
3
+
4
+ import { expect } from 'chai';
5
+
6
+ import { ContextManager, createContextKey } from '../../src/tracing/contextManager.js';
7
+ import { ROOT_CONTEXT } from '../../src/tracing/context.js';
8
+
9
+ describe('ContextManager()', function () {
10
+ it('should process pending spans', function (done) {
11
+ const SPAN_KEY = createContextKey('Rollbar Context Key SPAN');
12
+ const contextManager = new ContextManager();
13
+
14
+ expect(contextManager.active()).to.equal(ROOT_CONTEXT);
15
+
16
+ let context = contextManager.active().setValue(SPAN_KEY, 'a')
17
+ let prevContext = contextManager.enterContext(context);
18
+
19
+ expect(prevContext).to.equal(ROOT_CONTEXT);
20
+ expect(contextManager.active()).to.equal(context);
21
+
22
+ contextManager.exitContext(prevContext);
23
+
24
+ expect(contextManager.active()).to.equal(ROOT_CONTEXT);
25
+
26
+ done();
27
+ });
28
+ });