rollbar 2.26.4 → 3.0.0-alpha.2

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 (140) hide show
  1. package/.claude/settings.local.json +3 -0
  2. package/.cursor/rules/guidelines.mdc +154 -0
  3. package/.github/workflows/ci.yml +4 -6
  4. package/CLAUDE.local.md +297 -0
  5. package/CLAUDE.md +201 -0
  6. package/CLAUDE.testrunner.md +470 -0
  7. package/Gruntfile.js +59 -16
  8. package/Makefile +3 -3
  9. package/SECURITY.md +5 -0
  10. package/babel.config.json +9 -0
  11. package/codex.md +148 -0
  12. package/dist/plugins/jquery.min.js +1 -1
  13. package/dist/rollbar.js +19332 -6596
  14. package/dist/rollbar.js.map +1 -1
  15. package/dist/rollbar.min.js +2 -1
  16. package/dist/rollbar.min.js.LICENSE.txt +1 -0
  17. package/dist/rollbar.min.js.map +1 -1
  18. package/dist/rollbar.named-amd.js +19332 -6596
  19. package/dist/rollbar.named-amd.js.map +1 -1
  20. package/dist/rollbar.named-amd.min.js +2 -1
  21. package/dist/rollbar.named-amd.min.js.LICENSE.txt +1 -0
  22. package/dist/rollbar.named-amd.min.js.map +1 -1
  23. package/dist/rollbar.noconflict.umd.js +19319 -6581
  24. package/dist/rollbar.noconflict.umd.js.map +1 -1
  25. package/dist/rollbar.noconflict.umd.min.js +2 -1
  26. package/dist/rollbar.noconflict.umd.min.js.LICENSE.txt +1 -0
  27. package/dist/rollbar.noconflict.umd.min.js.map +1 -1
  28. package/dist/rollbar.snippet.js +1 -1
  29. package/dist/rollbar.umd.js +19333 -6597
  30. package/dist/rollbar.umd.js.map +1 -1
  31. package/dist/rollbar.umd.min.js +2 -1
  32. package/dist/rollbar.umd.min.js.LICENSE.txt +1 -0
  33. package/dist/rollbar.umd.min.js.map +1 -1
  34. package/eslint.config.mjs +33 -0
  35. package/karma.conf.js +5 -14
  36. package/package.json +19 -20
  37. package/src/api.js +57 -4
  38. package/src/apiUtility.js +2 -3
  39. package/src/browser/core.js +37 -9
  40. package/src/browser/replay/defaults.js +70 -0
  41. package/src/browser/replay/recorder.js +194 -0
  42. package/src/browser/replay/replayMap.js +195 -0
  43. package/src/browser/rollbar.js +11 -7
  44. package/src/browser/telemetry.js +3 -3
  45. package/src/browser/transport/fetch.js +17 -4
  46. package/src/browser/transport/xhr.js +17 -1
  47. package/src/browser/transport.js +11 -8
  48. package/src/defaults.js +1 -1
  49. package/src/queue.js +65 -4
  50. package/src/react-native/rollbar.js +1 -1
  51. package/src/rollbar.js +52 -10
  52. package/src/server/rollbar.js +3 -2
  53. package/src/telemetry.js +76 -11
  54. package/src/tracing/context.js +24 -0
  55. package/src/tracing/contextManager.js +37 -0
  56. package/src/tracing/defaults.js +7 -0
  57. package/src/tracing/exporter.js +188 -0
  58. package/src/tracing/hrtime.js +98 -0
  59. package/src/tracing/id.js +24 -0
  60. package/src/tracing/session.js +55 -0
  61. package/src/tracing/span.js +92 -0
  62. package/src/tracing/spanProcessor.js +15 -0
  63. package/src/tracing/tracer.js +46 -0
  64. package/src/tracing/tracing.js +89 -0
  65. package/src/utility.js +34 -0
  66. package/test/api.test.js +57 -12
  67. package/test/apiUtility.test.js +5 -6
  68. package/test/browser.core.test.js +1 -10
  69. package/test/browser.domUtility.test.js +1 -1
  70. package/test/browser.predicates.test.js +1 -1
  71. package/test/browser.replay.recorder.test.js +430 -0
  72. package/test/browser.rollbar.test.js +58 -12
  73. package/test/browser.telemetry.test.js +1 -1
  74. package/test/browser.transforms.test.js +20 -13
  75. package/test/browser.transport.test.js +5 -4
  76. package/test/browser.url.test.js +1 -1
  77. package/test/fixtures/replay/index.js +20 -0
  78. package/test/fixtures/replay/payloads.fixtures.js +229 -0
  79. package/test/fixtures/replay/rrwebEvents.fixtures.js +251 -0
  80. package/test/fixtures/replay/rrwebSyntheticEvents.fixtures.js +328 -0
  81. package/test/notifier.test.js +1 -1
  82. package/test/predicates.test.js +1 -1
  83. package/test/queue.test.js +1 -1
  84. package/test/rateLimiter.test.js +1 -1
  85. package/test/react-native.rollbar.test.js +1 -1
  86. package/test/react-native.transforms.test.js +2 -2
  87. package/test/react-native.transport.test.js +3 -3
  88. package/test/replay/index.js +2 -0
  89. package/test/replay/integration/api.spans.test.js +136 -0
  90. package/test/replay/integration/e2e.test.js +228 -0
  91. package/test/replay/integration/index.js +9 -0
  92. package/test/replay/integration/queue.replayMap.test.js +332 -0
  93. package/test/replay/integration/replayMap.test.js +163 -0
  94. package/test/replay/integration/sessionRecording.test.js +390 -0
  95. package/test/replay/unit/api.postSpans.test.js +150 -0
  96. package/test/replay/unit/index.js +7 -0
  97. package/test/replay/unit/queue.replayMap.test.js +225 -0
  98. package/test/replay/unit/replayMap.test.js +348 -0
  99. package/test/replay/util/index.js +5 -0
  100. package/test/replay/util/mockRecordFn.js +80 -0
  101. package/test/server.lambda.mocha.test.mjs +172 -0
  102. package/test/server.locals.constructor.mocha.test.mjs +80 -0
  103. package/test/server.locals.error-handling.mocha.test.mjs +387 -0
  104. package/test/server.locals.merge.mocha.test.mjs +267 -0
  105. package/test/server.locals.test-utils.mjs +114 -0
  106. package/test/server.parser.mocha.test.mjs +87 -0
  107. package/test/server.predicates.mocha.test.mjs +63 -0
  108. package/test/server.rollbar.constructor.mocha.test.mjs +199 -0
  109. package/test/server.rollbar.handlers.mocha.test.mjs +253 -0
  110. package/test/server.rollbar.logging.mocha.test.mjs +326 -0
  111. package/test/server.rollbar.misc.mocha.test.mjs +44 -0
  112. package/test/server.rollbar.test-utils.mjs +57 -0
  113. package/test/server.telemetry.mocha.test.mjs +377 -0
  114. package/test/server.transforms.data.mocha.test.mjs +163 -0
  115. package/test/server.transforms.error.mocha.test.mjs +199 -0
  116. package/test/server.transforms.request.mocha.test.mjs +208 -0
  117. package/test/server.transforms.scrub.mocha.test.mjs +140 -0
  118. package/test/server.transforms.sourcemaps.mocha.test.mjs +122 -0
  119. package/test/server.transforms.test-utils.mjs +62 -0
  120. package/test/server.transport.mocha.test.mjs +269 -0
  121. package/test/telemetry.test.js +132 -1
  122. package/test/tracing/contextManager.test.js +28 -0
  123. package/test/tracing/exporter.toPayload.test.js +400 -0
  124. package/test/tracing/id.test.js +24 -0
  125. package/test/tracing/span.test.js +183 -0
  126. package/test/tracing/spanProcessor.test.js +73 -0
  127. package/test/tracing/tracing.test.js +105 -0
  128. package/test/transforms.test.js +2 -2
  129. package/test/truncation.test.js +2 -2
  130. package/test/utility.test.js +44 -6
  131. package/webpack.config.js +6 -44
  132. package/.eslintignore +0 -7
  133. package/test/server.lambda.test.js +0 -194
  134. package/test/server.locals.test.js +0 -1068
  135. package/test/server.parser.test.js +0 -78
  136. package/test/server.predicates.test.js +0 -91
  137. package/test/server.rollbar.test.js +0 -728
  138. package/test/server.telemetry.test.js +0 -443
  139. package/test/server.transforms.test.js +0 -1193
  140. package/test/server.transport.test.js +0 -269
@@ -0,0 +1,199 @@
1
+ /* globals describe */
2
+ /* globals it */
3
+ /* globals beforeEach */
4
+
5
+ import { expect } from 'chai';
6
+ import * as t from '../src/server/transforms.js';
7
+ import {
8
+ CustomError,
9
+ isMinNodeVersion,
10
+ } from './server.transforms.test-utils.mjs';
11
+
12
+ describe('transforms.handleItemWithError', function () {
13
+ let options;
14
+
15
+ beforeEach(function () {
16
+ options = {
17
+ some: 'stuff',
18
+ captureIp: true,
19
+ };
20
+ });
21
+
22
+ it('should not change the item with no error', function (done) {
23
+ const item = {
24
+ data: { body: { yo: 'hey' } },
25
+ message: 'hey',
26
+ };
27
+
28
+ t.handleItemWithError(item, options, (err, item) => {
29
+ expect(err).to.not.exist;
30
+ expect(item.data.body.yo).to.equal('hey');
31
+ done();
32
+ });
33
+ });
34
+
35
+ it('should add some data to the trace_chain with a simple error', function (done) {
36
+ const item = {
37
+ data: { body: {} },
38
+ err: new Error('wookie'),
39
+ };
40
+
41
+ t.handleItemWithError(item, options, (err, item) => {
42
+ expect(err).to.not.exist;
43
+ expect(item.stackInfo).to.exist;
44
+ done();
45
+ });
46
+ });
47
+
48
+ it('should add some data to the trace_chain with a normal error', function (done) {
49
+ let testError;
50
+
51
+ const test = function () {
52
+ const x = thisVariableIsNotDefined;
53
+ };
54
+
55
+ try {
56
+ test();
57
+ } catch (e) {
58
+ testError = e;
59
+ }
60
+
61
+ const item = {
62
+ data: { body: {} },
63
+ err: testError,
64
+ };
65
+
66
+ t.handleItemWithError(item, options, (err, item) => {
67
+ expect(err).to.not.exist;
68
+ expect(item.stackInfo).to.exist;
69
+ done();
70
+ });
71
+ });
72
+
73
+ it('should have the right data in the trace_chain with a nested error', function (done) {
74
+ let testError;
75
+
76
+ const test = function () {
77
+ const x = thisVariableIsNotDefined;
78
+ };
79
+
80
+ try {
81
+ test();
82
+ } catch (e) {
83
+ testError = new CustomError('nested-message', e);
84
+ }
85
+
86
+ const item = {
87
+ data: { body: {} },
88
+ err: testError,
89
+ };
90
+
91
+ t.handleItemWithError(item, options, (err, item) => {
92
+ expect(err).to.not.exist;
93
+
94
+ const trace_chain = item.stackInfo;
95
+ expect(trace_chain).to.have.lengthOf(2);
96
+ expect(trace_chain[0].exception.class).to.equal('CustomError');
97
+ expect(trace_chain[0].exception.message).to.equal('nested-message');
98
+ expect(trace_chain[1].exception.class).to.equal('ReferenceError');
99
+
100
+ done();
101
+ });
102
+ });
103
+
104
+ it('should have the right data in the trace_chain with a null nested error', function (done) {
105
+ const err = new CustomError('With null nested error');
106
+ err.nested = null;
107
+
108
+ const item = {
109
+ data: { body: {} },
110
+ err: err,
111
+ };
112
+
113
+ t.handleItemWithError(item, options, (err, item) => {
114
+ expect(err).to.not.exist;
115
+
116
+ const trace_chain = item.stackInfo;
117
+ expect(trace_chain).to.have.lengthOf(1);
118
+ expect(trace_chain[0].exception.class).to.equal('CustomError');
119
+
120
+ done();
121
+ });
122
+ });
123
+
124
+ it('should add the error context with error context', function (done) {
125
+ let testError;
126
+
127
+ const test = function () {
128
+ const x = thisVariableIsNotDefined;
129
+ };
130
+
131
+ try {
132
+ test();
133
+ } catch (e) {
134
+ testError = new CustomError('nested-message', e);
135
+ e.rollbarContext = { err1: 'nested context' };
136
+ testError.rollbarContext = { err2: 'error context' };
137
+ }
138
+
139
+ options.addErrorContext = true;
140
+
141
+ const item = {
142
+ data: { body: {} },
143
+ err: testError,
144
+ };
145
+
146
+ t.handleItemWithError(item, options, (err, item) => {
147
+ expect(err).to.not.exist;
148
+
149
+ const trace_chain = item.stackInfo;
150
+ expect(trace_chain).to.have.lengthOf(2);
151
+ expect(item.data.custom.err1).to.equal('nested context');
152
+ expect(item.data.custom.err2).to.equal('error context');
153
+
154
+ done();
155
+ });
156
+ });
157
+
158
+ it('should have the right data in the trace_chain with an error cause', function (done) {
159
+ // Error cause was introduced in Node 16.9.
160
+ if (!isMinNodeVersion(16, 9)) {
161
+ this.skip();
162
+ }
163
+
164
+ let testError;
165
+
166
+ const test = function () {
167
+ const x = thisVariableIsNotDefined;
168
+ };
169
+
170
+ try {
171
+ test();
172
+ } catch (e) {
173
+ testError = new Error('cause message', { cause: e });
174
+ e.rollbarContext = { err1: 'cause context' };
175
+ testError.rollbarContext = { err2: 'error context' };
176
+ }
177
+
178
+ options.addErrorContext = true;
179
+
180
+ const item = {
181
+ data: { body: {} },
182
+ err: testError,
183
+ };
184
+
185
+ t.handleItemWithError(item, options, (err, item) => {
186
+ expect(err).to.not.exist;
187
+
188
+ const trace_chain = item.stackInfo;
189
+ expect(trace_chain).to.have.lengthOf(2);
190
+ expect(trace_chain[0].exception.class).to.equal('Error');
191
+ expect(trace_chain[0].exception.message).to.equal('cause message');
192
+ expect(trace_chain[1].exception.class).to.equal('ReferenceError');
193
+ expect(item.data.custom.err1).to.equal('cause context');
194
+ expect(item.data.custom.err2).to.equal('error context');
195
+
196
+ done();
197
+ });
198
+ });
199
+ });
@@ -0,0 +1,208 @@
1
+ /* globals describe */
2
+ /* globals it */
3
+
4
+ import { expect } from 'chai';
5
+ import * as t from '../src/server/transforms.js';
6
+ import { createTestItem } from './server.transforms.test-utils.mjs';
7
+
8
+ describe('transforms.addRequestData', function () {
9
+ describe('without custom addRequestData method', function () {
10
+ describe('without scrub fields', function () {
11
+ const options = {
12
+ nothing: 'here',
13
+ captureEmail: true,
14
+ captureUsername: true,
15
+ captureIp: true,
16
+ };
17
+
18
+ it('should not change the item without a request', function (done) {
19
+ const item = {
20
+ data: { body: { message: 'hey' } },
21
+ };
22
+
23
+ t.addRequestData(item, options, (err, item) => {
24
+ expect(err).to.not.exist;
25
+ expect(item.request).to.be.undefined;
26
+ expect(item.data.request).to.be.undefined;
27
+ done();
28
+ });
29
+ });
30
+
31
+ it('should not change the request with an empty request object', function (done) {
32
+ const item = {
33
+ request: {},
34
+ data: { body: { message: 'hey' } },
35
+ };
36
+
37
+ t.addRequestData(item, options, (err, item) => {
38
+ expect(err).to.not.exist;
39
+ expect(item.request.headers).to.be.undefined;
40
+ done();
41
+ });
42
+ });
43
+
44
+ it('should have a person, request and context with a request object', function (done) {
45
+ const item = createTestItem({
46
+ request: {
47
+ route: { path: '/api/:bork' },
48
+ },
49
+ });
50
+
51
+ t.addRequestData(item, options, (err, item) => {
52
+ expect(err).to.not.exist;
53
+
54
+ const data = item.data;
55
+ expect(data.person.id).to.equal(42);
56
+ expect(data.person.email).to.equal('fake@example.com');
57
+ expect(data.request.url).to.equal(
58
+ 'https://example.com/some/endpoint',
59
+ );
60
+ expect(data.request.user_ip).to.equal('192.192.192.1');
61
+ expect(data.request.GET).to.exist;
62
+ expect(data.context).to.equal('/api/:bork');
63
+
64
+ done();
65
+ });
66
+ });
67
+
68
+ it('should set some fields based on request data with a request for a nested router with a baseURL', function (done) {
69
+ const item = createTestItem({
70
+ request: {
71
+ baseUrl: '/nested',
72
+ route: { path: '/api/:bork' },
73
+ },
74
+ });
75
+
76
+ t.addRequestData(item, options, (err, item) => {
77
+ expect(err).to.not.exist;
78
+ expect(item.data.request.url).to.equal(
79
+ 'https://example.com/nested/some/endpoint',
80
+ );
81
+ expect(item.data.context).to.equal('/nested/api/:bork');
82
+ done();
83
+ });
84
+ });
85
+
86
+ it('should set a person and some fields based on request data with a request like from hapi', function (done) {
87
+ const item = createTestItem({
88
+ request: {
89
+ url: {
90
+ protocol: null,
91
+ slashes: null,
92
+ auth: null,
93
+ host: null,
94
+ port: null,
95
+ hostname: null,
96
+ hash: null,
97
+ search: '',
98
+ query: {},
99
+ pathname: '/some/endpoint',
100
+ path: '/some/endpoint',
101
+ href: '/some/endpoint',
102
+ },
103
+ method: 'POST',
104
+ payload: {
105
+ token: 'abc123',
106
+ something: 'else',
107
+ },
108
+ route: { path: '/api/:bork' },
109
+ },
110
+ });
111
+
112
+ t.addRequestData(item, options, (err, item) => {
113
+ expect(err).to.not.exist;
114
+
115
+ const data = item.data;
116
+ expect(data.person.id).to.equal(42);
117
+ expect(data.person.email).to.equal('fake@example.com');
118
+ expect(data.request.url).to.equal(
119
+ 'https://example.com/some/endpoint',
120
+ );
121
+ expect(data.request.user_ip).to.equal('192.192.192.1');
122
+ expect(data.request.GET).to.not.exist;
123
+ expect(data.request.POST).to.exist;
124
+ expect(item.data.context).to.equal('/api/:bork');
125
+
126
+ done();
127
+ });
128
+ });
129
+
130
+ it('should set a person and some fields based on request data with a request with an array body', function (done) {
131
+ const item = createTestItem({
132
+ request: {
133
+ method: 'POST',
134
+ body: [
135
+ {
136
+ token: 'abc123',
137
+ something: 'else',
138
+ },
139
+ 'otherStuff',
140
+ ],
141
+ },
142
+ });
143
+
144
+ t.addRequestData(item, options, (err, item) => {
145
+ expect(err).to.not.exist;
146
+
147
+ const data = item.data;
148
+ expect(data.person.id).to.equal(42);
149
+ expect(data.person.email).to.equal('fake@example.com');
150
+ expect(data.request.url).to.equal(
151
+ 'https://example.com/some/endpoint',
152
+ );
153
+ expect(data.request.user_ip).to.equal('192.192.192.1');
154
+ expect(data.request.POST).to.exist;
155
+ expect(data.request.POST['0'].something).to.equal('else');
156
+ expect(data.request.POST['1']).to.equal('otherStuff');
157
+
158
+ done();
159
+ });
160
+ });
161
+ });
162
+
163
+ describe('with scrub fields', function () {
164
+ const options = {
165
+ scrubHeaders: ['x-auth-token'],
166
+ scrubFields: ['passwd', 'access_token', 'request.cookie'],
167
+ };
168
+
169
+ it('should create request data without scrubbing even with scrub options', function (done) {
170
+ const item = createTestItem();
171
+
172
+ t.addRequestData(item, options, (err, item) => {
173
+ expect(err).to.not.exist;
174
+ // Verify that addRequestData doesn't scrub -
175
+ // headers should still contain sensitive data
176
+ expect(item.data.request.headers['x-auth-token']).to.equal('12345');
177
+ expect(item.data.request.GET.token).to.equal('abc123');
178
+ done();
179
+ });
180
+ });
181
+ });
182
+ });
183
+
184
+ describe('with custom addRequestData', function () {
185
+ it('should do what the function does with a request with scrub fields', function (done) {
186
+ const customFn = function (item, request) {
187
+ expect(item.stuff).to.be.undefined;
188
+ expect(item.other).to.equal('thing');
189
+ item.myRequest = { body: request.body.token };
190
+ };
191
+
192
+ const options = {
193
+ captureIp: true,
194
+ addRequestData: customFn,
195
+ scrubFields: ['passwd', 'access_token', 'token', 'request.cookie'],
196
+ };
197
+
198
+ const item = createTestItem();
199
+
200
+ t.addRequestData(item, options, (err, item) => {
201
+ expect(err).to.not.exist;
202
+ expect(item.data.request).to.not.exist;
203
+ expect(item.data.myRequest.body).to.equal('abc123');
204
+ done();
205
+ });
206
+ });
207
+ });
208
+ });
@@ -0,0 +1,140 @@
1
+ /* globals describe */
2
+ /* globals it */
3
+
4
+ import { expect } from 'chai';
5
+ import Rollbar from '../src/server/rollbar.js';
6
+ import * as t from '../src/server/transforms.js';
7
+ import { createTestItem } from './server.transforms.test-utils.mjs';
8
+
9
+ describe('transforms.scrubPayload', function () {
10
+ describe('without scrub fields', function () {
11
+ it('should scrub key/value based on defaults but not okay keys', function (done) {
12
+ const item = {
13
+ data: {
14
+ body: {
15
+ message: 'hey',
16
+ password: '123',
17
+ secret: { stuff: 'here' },
18
+ },
19
+ },
20
+ };
21
+
22
+ t.scrubPayload(item, Rollbar.defaultOptions, (err, item) => {
23
+ expect(err).to.not.exist;
24
+ expect(item.data.body.message).to.equal('hey');
25
+ expect(item.data.body.password).to.match(/\*+/);
26
+ expect(item.data.body.secret).to.match(/\*+/);
27
+ done();
28
+ });
29
+ });
30
+ });
31
+
32
+ describe('with scrub fields', function () {
33
+ const options = {
34
+ captureIp: true,
35
+ scrubHeaders: ['x-auth-token'],
36
+ scrubFields: ['passwd', 'access_token', 'request.cookie', 'sauce'],
37
+ scrubRequestBody: true,
38
+ };
39
+
40
+ it('should scrub based on the options with a request', function (done) {
41
+ const item = createTestItem({
42
+ data: {
43
+ other: 'thing',
44
+ sauce: 'secrets',
45
+ someParams: 'foo=okay&passwd=iamhere',
46
+ },
47
+ });
48
+
49
+ t.addRequestData(item, options, (err, item) => {
50
+ expect(err).to.not.exist;
51
+
52
+ t.scrubPayload(item, options, (err, scrubbedItem) => {
53
+ expect(err).to.not.exist;
54
+
55
+ const data = scrubbedItem.data;
56
+ expect(data.request.GET.token).to.equal('abc123');
57
+ expect(data.request.headers['x-auth-token']).to.match(/\*+/);
58
+ expect(data.request.headers['host']).to.equal('example.com');
59
+ expect(data.sauce).to.match(/\*+/);
60
+ expect(data.other).to.equal('thing');
61
+ expect(data.someParams).to.match(/foo=okay&passwd=\*+/);
62
+
63
+ done();
64
+ });
65
+ });
66
+ });
67
+
68
+ it('should scrub based on the options with a json request body', function (done) {
69
+ const item = createTestItem({
70
+ request: {
71
+ headers: {
72
+ host: 'example.com',
73
+ 'content-type': 'application/json',
74
+ 'x-auth-token': '12345',
75
+ },
76
+ body: JSON.stringify({
77
+ token: 'abc123',
78
+ something: 'else',
79
+ passwd: '123456',
80
+ }),
81
+ },
82
+ data: {
83
+ other: 'thing',
84
+ sauce: 'secrets',
85
+ someParams: 'foo=okay&passwd=iamhere',
86
+ },
87
+ });
88
+
89
+ t.addRequestData(item, options, (err, item) => {
90
+ expect(err).to.not.exist;
91
+
92
+ t.scrubPayload(item, options, (err, scrubbedItem) => {
93
+ expect(err).to.not.exist;
94
+
95
+ const data = scrubbedItem.data;
96
+ expect(data.request.headers['x-auth-token']).to.match(/\*+/);
97
+ expect(data.request.headers['host']).to.equal('example.com');
98
+ expect(data.sauce).to.match(/\*+/);
99
+ expect(data.other).to.equal('thing');
100
+ expect(data.someParams).to.match(/foo=okay&passwd=\*+/);
101
+
102
+ const requestBody = JSON.parse(scrubbedItem.data.request.body);
103
+ expect(requestBody.passwd).to.match(/\*+/);
104
+
105
+ done();
106
+ });
107
+ });
108
+ });
109
+
110
+ it('should delete the body and add a diagnostic error with a bad json request body', function (done) {
111
+ const item = {
112
+ request: {
113
+ headers: {
114
+ 'content-type': 'application/json',
115
+ },
116
+ protocol: 'https',
117
+ url: '/some/endpoint',
118
+ ip: '192.192.192.1',
119
+ method: 'GET',
120
+ body: 'not valid json',
121
+ },
122
+ };
123
+
124
+ t.addRequestData(item, options, (err, item) => {
125
+ expect(err).to.not.exist;
126
+
127
+ t.scrubPayload(item, options, (err, scrubbedItem) => {
128
+ expect(err).to.not.exist;
129
+
130
+ const data = scrubbedItem.data;
131
+ const requestBody = JSON.parse(data.request.body);
132
+ expect(requestBody).to.be.null;
133
+ expect(data.request.error).to.match(/request.body parse failed/);
134
+
135
+ done();
136
+ });
137
+ });
138
+ });
139
+ });
140
+ });
@@ -0,0 +1,122 @@
1
+ /* globals describe */
2
+ /* globals it */
3
+
4
+ import { expect } from 'chai';
5
+ import sinon from 'sinon';
6
+ import Rollbar from '../src/server/rollbar.js';
7
+ import { throwInScriptFile } from './server.transforms.test-utils.mjs';
8
+
9
+ describe('transforms.nodeSourceMaps', function () {
10
+ let rollbar;
11
+ let addItemStub;
12
+ let mochaHandlers;
13
+
14
+ before(function () {
15
+ // Increase max listeners to avoid warnings during tests
16
+ // Multiple Rollbar instances are created and each adds handlers
17
+ process.setMaxListeners(20);
18
+ });
19
+
20
+ after(function () {
21
+ process.setMaxListeners(10);
22
+ });
23
+
24
+ beforeEach(function () {
25
+ // Remove Mocha's uncaught exception handlers to prevent interference
26
+ mochaHandlers = process.listeners('uncaughtException');
27
+ mochaHandlers.forEach((handler) => {
28
+ process.removeListener('uncaughtException', handler);
29
+ });
30
+
31
+ rollbar = new Rollbar({
32
+ accessToken: 'abc123',
33
+ captureUncaught: true,
34
+ nodeSourceMaps: true,
35
+ });
36
+
37
+ addItemStub = sinon.stub(rollbar.client.notifier.queue, 'addItem');
38
+ });
39
+
40
+ afterEach(function () {
41
+ addItemStub.restore();
42
+
43
+ // Restore Mocha's handlers
44
+ mochaHandlers.forEach((handler) => {
45
+ process.on('uncaughtException', handler);
46
+ });
47
+ });
48
+
49
+ it('should map the stack with context when original source is present', async function () {
50
+ await throwInScriptFile('../examples/node-typescript/dist/index.js');
51
+
52
+ expect(addItemStub.called).to.be.true;
53
+
54
+ const frame = addItemStub
55
+ .getCall(0)
56
+ .args[0].body.trace_chain[0].frames.pop();
57
+
58
+ expect(frame.filename).to.include('src/index.ts');
59
+ expect(frame.lineno).to.equal(10);
60
+ expect(frame.colno).to.equal(22);
61
+ expect(frame.code).to.equal(
62
+ " var error = <Error> new CustomError('foo');",
63
+ );
64
+ expect(frame.context.pre[0]).to.equal(' }');
65
+ expect(frame.context.pre[1]).to.equal(' }');
66
+ expect(frame.context.pre[2]).to.equal(
67
+ ' // TypeScript code snippet will include `<Error>`',
68
+ );
69
+ expect(frame.context.post[0]).to.equal(' throw error;');
70
+ expect(frame.context.post[1]).to.equal('}');
71
+
72
+ const sourceMappingURLs =
73
+ addItemStub.getCall(0).args[0].notifier.diagnostic.node_source_maps
74
+ .source_mapping_urls;
75
+ const urls = Object.keys(sourceMappingURLs);
76
+
77
+ expect(urls[0]).to.include('index.js');
78
+ expect(sourceMappingURLs[urls[0]]).to.include('index.js.map');
79
+ expect(urls[1]).to.include('server.transforms.test-utils.mjs');
80
+ expect(sourceMappingURLs[urls[1]]).to.include('not found');
81
+
82
+ // Node until v12 will have 'timers.js' here.
83
+ // Node 12 - 14 will have 'internal/timers.js' here.
84
+ // Starting in v16, this is 'node:internal/timers'.
85
+ // This assert works for all and is specific enough for this test case.
86
+ expect(urls[2]).to.include('timers');
87
+ expect(sourceMappingURLs[urls[2]]).to.include('not found');
88
+ });
89
+
90
+ it('should map the stack with context using sourcesContent', async function () {
91
+ await throwInScriptFile('../examples/node-dist/index.js');
92
+
93
+ expect(addItemStub.called).to.be.true;
94
+
95
+ const frame = addItemStub
96
+ .getCall(0)
97
+ .args[0].body.trace_chain[0].frames.pop();
98
+
99
+ expect(frame.filename).to.include('src/index.ts');
100
+ expect(frame.lineno).to.equal(10);
101
+ expect(frame.colno).to.equal(22);
102
+ expect(frame.code).to.equal(
103
+ " var error = <Error> new CustomError('foo');",
104
+ );
105
+ expect(frame.context.pre[0]).to.equal(' }');
106
+ expect(frame.context.pre[1]).to.equal(' }');
107
+ expect(frame.context.pre[2]).to.equal(
108
+ ' // TypeScript code snippet will include `<Error>`',
109
+ );
110
+ expect(frame.context.post[0]).to.equal(' throw error;');
111
+ expect(frame.context.post[1]).to.equal('}');
112
+
113
+ const sourceMappingURLs =
114
+ addItemStub.getCall(0).args[0].notifier.diagnostic.node_source_maps
115
+ .source_mapping_urls;
116
+ const urls = Object.keys(sourceMappingURLs);
117
+
118
+ expect(urls).to.have.lengthOf(1);
119
+ expect(urls[0]).to.include('index.js');
120
+ expect(sourceMappingURLs[urls[0]]).to.include('index.js.map');
121
+ });
122
+ });