@devmoods/sentry 0.5.0 → 0.5.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.
@@ -1,10 +1,10 @@
1
1
  <?xml version="1.0" encoding="UTF-8"?>
2
- <coverage generated="1782839143347" clover="3.2.0">
3
- <project timestamp="1782839143347" name="All files">
2
+ <coverage generated="1783497115890" clover="3.2.0">
3
+ <project timestamp="1783497115890" name="All files">
4
4
  <metrics statements="1" coveredstatements="1" conditionals="0" coveredconditionals="0" methods="0" coveredmethods="0" elements="1" coveredelements="1" complexity="0" loc="1" ncloc="1" packages="1" files="1" classes="1"/>
5
5
  <file name="index.ts" path="/Users/hanse/dev/github.com/devmoods/express-extras/packages/@devmoods/sentry/src/index.ts">
6
6
  <metrics statements="1" coveredstatements="1" conditionals="0" coveredconditionals="0" methods="0" coveredmethods="0"/>
7
- <line num="14" count="10" type="stmt"/>
7
+ <line num="14" count="13" type="stmt"/>
8
8
  </file>
9
9
  </project>
10
10
  </coverage>
@@ -1,2 +1,2 @@
1
- {"/Users/hanse/dev/github.com/devmoods/express-extras/packages/@devmoods/sentry/src/index.ts": {"path":"/Users/hanse/dev/github.com/devmoods/express-extras/packages/@devmoods/sentry/src/index.ts","statementMap":{"0":{"start":{"line":14,"column":13},"end":{"line":19,"column":null}}},"fnMap":{},"branchMap":{},"s":{"0":10},"f":{},"b":{},"meta":{"lastBranch":0,"lastFunction":0,"lastStatement":1,"seen":{"s:14:13:19:Infinity":0},"fnNames":{}}}
1
+ {"/Users/hanse/dev/github.com/devmoods/express-extras/packages/@devmoods/sentry/src/index.ts": {"path":"/Users/hanse/dev/github.com/devmoods/express-extras/packages/@devmoods/sentry/src/index.ts","statementMap":{"0":{"start":{"line":14,"column":13},"end":{"line":19,"column":null}}},"fnMap":{},"branchMap":{},"s":{"0":13},"f":{},"b":{},"meta":{"lastBranch":0,"lastFunction":0,"lastStatement":1,"seen":{"s:14:13:19:Infinity":0},"fnNames":{}}}
2
2
  }
@@ -101,7 +101,7 @@
101
101
  <div class='footer quiet pad2 space-top1 center small'>
102
102
  Code coverage generated by
103
103
  <a href="https://istanbul.js.org/" target="_blank" rel="noopener noreferrer">istanbul</a>
104
- at 2026-06-30T17:05:43.338Z
104
+ at 2026-07-08T07:51:55.887Z
105
105
  </div>
106
106
  <script src="prettify.js"></script>
107
107
  <script>
@@ -241,7 +241,66 @@
241
241
  <a name='L176'></a><a href='#L176'>176</a>
242
242
  <a name='L177'></a><a href='#L177'>177</a>
243
243
  <a name='L178'></a><a href='#L178'>178</a>
244
- <a name='L179'></a><a href='#L179'>179</a></td><td class="line-coverage quiet"><span class="cline-any cline-neutral">&nbsp;</span>
244
+ <a name='L179'></a><a href='#L179'>179</a>
245
+ <a name='L180'></a><a href='#L180'>180</a>
246
+ <a name='L181'></a><a href='#L181'>181</a>
247
+ <a name='L182'></a><a href='#L182'>182</a>
248
+ <a name='L183'></a><a href='#L183'>183</a>
249
+ <a name='L184'></a><a href='#L184'>184</a>
250
+ <a name='L185'></a><a href='#L185'>185</a>
251
+ <a name='L186'></a><a href='#L186'>186</a>
252
+ <a name='L187'></a><a href='#L187'>187</a>
253
+ <a name='L188'></a><a href='#L188'>188</a>
254
+ <a name='L189'></a><a href='#L189'>189</a>
255
+ <a name='L190'></a><a href='#L190'>190</a>
256
+ <a name='L191'></a><a href='#L191'>191</a>
257
+ <a name='L192'></a><a href='#L192'>192</a>
258
+ <a name='L193'></a><a href='#L193'>193</a>
259
+ <a name='L194'></a><a href='#L194'>194</a>
260
+ <a name='L195'></a><a href='#L195'>195</a>
261
+ <a name='L196'></a><a href='#L196'>196</a>
262
+ <a name='L197'></a><a href='#L197'>197</a>
263
+ <a name='L198'></a><a href='#L198'>198</a>
264
+ <a name='L199'></a><a href='#L199'>199</a>
265
+ <a name='L200'></a><a href='#L200'>200</a>
266
+ <a name='L201'></a><a href='#L201'>201</a>
267
+ <a name='L202'></a><a href='#L202'>202</a>
268
+ <a name='L203'></a><a href='#L203'>203</a>
269
+ <a name='L204'></a><a href='#L204'>204</a>
270
+ <a name='L205'></a><a href='#L205'>205</a>
271
+ <a name='L206'></a><a href='#L206'>206</a>
272
+ <a name='L207'></a><a href='#L207'>207</a>
273
+ <a name='L208'></a><a href='#L208'>208</a></td><td class="line-coverage quiet"><span class="cline-any cline-neutral">&nbsp;</span>
274
+ <span class="cline-any cline-neutral">&nbsp;</span>
275
+ <span class="cline-any cline-neutral">&nbsp;</span>
276
+ <span class="cline-any cline-neutral">&nbsp;</span>
277
+ <span class="cline-any cline-neutral">&nbsp;</span>
278
+ <span class="cline-any cline-neutral">&nbsp;</span>
279
+ <span class="cline-any cline-neutral">&nbsp;</span>
280
+ <span class="cline-any cline-neutral">&nbsp;</span>
281
+ <span class="cline-any cline-neutral">&nbsp;</span>
282
+ <span class="cline-any cline-neutral">&nbsp;</span>
283
+ <span class="cline-any cline-neutral">&nbsp;</span>
284
+ <span class="cline-any cline-neutral">&nbsp;</span>
285
+ <span class="cline-any cline-neutral">&nbsp;</span>
286
+ <span class="cline-any cline-yes">13x</span>
287
+ <span class="cline-any cline-neutral">&nbsp;</span>
288
+ <span class="cline-any cline-neutral">&nbsp;</span>
289
+ <span class="cline-any cline-neutral">&nbsp;</span>
290
+ <span class="cline-any cline-neutral">&nbsp;</span>
291
+ <span class="cline-any cline-neutral">&nbsp;</span>
292
+ <span class="cline-any cline-neutral">&nbsp;</span>
293
+ <span class="cline-any cline-neutral">&nbsp;</span>
294
+ <span class="cline-any cline-neutral">&nbsp;</span>
295
+ <span class="cline-any cline-neutral">&nbsp;</span>
296
+ <span class="cline-any cline-neutral">&nbsp;</span>
297
+ <span class="cline-any cline-neutral">&nbsp;</span>
298
+ <span class="cline-any cline-neutral">&nbsp;</span>
299
+ <span class="cline-any cline-neutral">&nbsp;</span>
300
+ <span class="cline-any cline-neutral">&nbsp;</span>
301
+ <span class="cline-any cline-neutral">&nbsp;</span>
302
+ <span class="cline-any cline-neutral">&nbsp;</span>
303
+ <span class="cline-any cline-neutral">&nbsp;</span>
245
304
  <span class="cline-any cline-neutral">&nbsp;</span>
246
305
  <span class="cline-any cline-neutral">&nbsp;</span>
247
306
  <span class="cline-any cline-neutral">&nbsp;</span>
@@ -254,7 +313,6 @@
254
313
  <span class="cline-any cline-neutral">&nbsp;</span>
255
314
  <span class="cline-any cline-neutral">&nbsp;</span>
256
315
  <span class="cline-any cline-neutral">&nbsp;</span>
257
- <span class="cline-any cline-yes">10x</span>
258
316
  <span class="cline-any cline-neutral">&nbsp;</span>
259
317
  <span class="cline-any cline-neutral">&nbsp;</span>
260
318
  <span class="cline-any cline-neutral">&nbsp;</span>
@@ -498,12 +556,41 @@ export async function sentryOptions(
498
556
  };
499
557
  }
500
558
  &nbsp;
559
+ function getHttpStatusCode(error: unknown) {
560
+ if (error == null || typeof error !== 'object') {
561
+ return undefined;
562
+ }
563
+ &nbsp;
564
+ const {
565
+ status,
566
+ statusCode,
567
+ status_code: statusCodeSnake,
568
+ output,
569
+ } = error as {
570
+ status?: number | string;
571
+ statusCode?: number | string;
572
+ status_code?: number | string;
573
+ output?: { statusCode?: number | string };
574
+ };
575
+ const value = status ?? statusCode ?? statusCodeSnake ?? output?.statusCode;
576
+ const parsed = typeof value === 'string' ? Number.parseInt(value, 10) : value;
577
+ &nbsp;
578
+ return typeof parsed === 'number' &amp;&amp; Number.isFinite(parsed)
579
+ ? parsed
580
+ : undefined;
581
+ }
582
+ &nbsp;
501
583
  export function sentryErrorHandler(
502
584
  options: Sentry.NodeOptions = {},
503
585
  ): ErrorRequestHandler {
504
586
  return Sentry.expressErrorHandler({
505
- shouldHandleError() {
506
- return process.env.NODE_ENV === 'production';
587
+ shouldHandleError(error) {
588
+ const status = getHttpStatusCode(error);
589
+ &nbsp;
590
+ return (
591
+ process.env.NODE_ENV === 'production' &amp;&amp;
592
+ (status == null || status &lt; 400 || status &gt;= 500)
593
+ );
507
594
  },
508
595
  ...options,
509
596
  }) as unknown as ErrorRequestHandler;
@@ -604,7 +691,7 @@ export class SentryLoggerAdapter implements LoggerAdapter {
604
691
  <div class='footer quiet pad2 space-top1 center small'>
605
692
  Code coverage generated by
606
693
  <a href="https://istanbul.js.org/" target="_blank" rel="noopener noreferrer">istanbul</a>
607
- at 2026-06-30T17:05:43.338Z
694
+ at 2026-07-08T07:51:55.887Z
608
695
  </div>
609
696
  <script src="prettify.js"></script>
610
697
  <script>
package/dist/index.js CHANGED
@@ -53,10 +53,23 @@ export async function sentryOptions(options = {}) {
53
53
  ...options,
54
54
  };
55
55
  }
56
+ function getHttpStatusCode(error) {
57
+ if (error == null || typeof error !== 'object') {
58
+ return undefined;
59
+ }
60
+ const { status, statusCode, status_code: statusCodeSnake, output, } = error;
61
+ const value = status ?? statusCode ?? statusCodeSnake ?? output?.statusCode;
62
+ const parsed = typeof value === 'string' ? Number.parseInt(value, 10) : value;
63
+ return typeof parsed === 'number' && Number.isFinite(parsed)
64
+ ? parsed
65
+ : undefined;
66
+ }
56
67
  export function sentryErrorHandler(options = {}) {
57
68
  return Sentry.expressErrorHandler({
58
- shouldHandleError() {
59
- return process.env.NODE_ENV === 'production';
69
+ shouldHandleError(error) {
70
+ const status = getHttpStatusCode(error);
71
+ return (process.env.NODE_ENV === 'production' &&
72
+ (status == null || status < 400 || status >= 500));
60
73
  },
61
74
  ...options,
62
75
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@devmoods/sentry",
3
- "version": "0.5.0",
3
+ "version": "0.5.1",
4
4
  "type": "module",
5
5
  "main": "./dist/index.js",
6
6
  "types": "./dist/index.d.ts",
@@ -24,13 +24,13 @@
24
24
  "dependencies": {
25
25
  "@devmoods/config": "0.3.0",
26
26
  "@devmoods/observability": "0.4.0",
27
- "@sentry/node": "^10.63.0",
28
- "@sentry/node-native": "^10.63.0",
29
- "@sentry/profiling-node": "^10.63.0",
27
+ "@sentry/node": "^10.64.0",
28
+ "@sentry/node-native": "^10.64.0",
29
+ "@sentry/profiling-node": "^10.64.0",
30
30
  "express": "^4.21.2"
31
31
  },
32
32
  "peerDependencies": {
33
33
  "express": ">=4.21.2 <5.0.0"
34
34
  },
35
- "gitHead": "52519bed307a1eedf13ab334f11c1ad9c7c8db91"
35
+ "gitHead": "2981e0cfae881922af5b04af07c3d3e641dece37"
36
36
  }
@@ -59,6 +59,51 @@ test('sentryOptions', async () => {
59
59
  reset();
60
60
  });
61
61
 
62
+ describe('sentryErrorHandler', () => {
63
+ beforeEach(() => {
64
+ vi.unstubAllEnvs();
65
+ });
66
+
67
+ test('ignores HTTP 40x errors in production', async () => {
68
+ vi.stubEnv('NODE_ENV', 'production');
69
+
70
+ const { Sentry, sentryErrorHandler } = await loadModuleWithSentryMock();
71
+
72
+ sentryErrorHandler();
73
+
74
+ const options = Sentry.expressErrorHandler.mock.calls[0]![0];
75
+ expect(options.shouldHandleError({ status: 400 })).toEqual(false);
76
+ expect(options.shouldHandleError({ statusCode: 401 })).toEqual(false);
77
+ expect(options.shouldHandleError({ status_code: '403' })).toEqual(false);
78
+ expect(options.shouldHandleError({ output: { statusCode: 429 } })).toEqual(
79
+ false,
80
+ );
81
+ });
82
+
83
+ test('handles server and unknown errors in production', async () => {
84
+ vi.stubEnv('NODE_ENV', 'production');
85
+
86
+ const { Sentry, sentryErrorHandler } = await loadModuleWithSentryMock();
87
+
88
+ sentryErrorHandler();
89
+
90
+ const options = Sentry.expressErrorHandler.mock.calls[0]![0];
91
+ expect(options.shouldHandleError(new Error('boom'))).toEqual(true);
92
+ expect(options.shouldHandleError({ status: 500 })).toEqual(true);
93
+ });
94
+
95
+ test('does not handle errors outside production', async () => {
96
+ vi.stubEnv('NODE_ENV', 'test');
97
+
98
+ const { Sentry, sentryErrorHandler } = await loadModuleWithSentryMock();
99
+
100
+ sentryErrorHandler();
101
+
102
+ const options = Sentry.expressErrorHandler.mock.calls[0]![0];
103
+ expect(options.shouldHandleError({ status: 500 })).toEqual(false);
104
+ });
105
+ });
106
+
62
107
  describe('SentryMetricsAdapter', () => {
63
108
  beforeEach(() => {
64
109
  vi.restoreAllMocks();
package/src/index.ts CHANGED
@@ -77,12 +77,41 @@ export async function sentryOptions(
77
77
  };
78
78
  }
79
79
 
80
+ function getHttpStatusCode(error: unknown) {
81
+ if (error == null || typeof error !== 'object') {
82
+ return undefined;
83
+ }
84
+
85
+ const {
86
+ status,
87
+ statusCode,
88
+ status_code: statusCodeSnake,
89
+ output,
90
+ } = error as {
91
+ status?: number | string;
92
+ statusCode?: number | string;
93
+ status_code?: number | string;
94
+ output?: { statusCode?: number | string };
95
+ };
96
+ const value = status ?? statusCode ?? statusCodeSnake ?? output?.statusCode;
97
+ const parsed = typeof value === 'string' ? Number.parseInt(value, 10) : value;
98
+
99
+ return typeof parsed === 'number' && Number.isFinite(parsed)
100
+ ? parsed
101
+ : undefined;
102
+ }
103
+
80
104
  export function sentryErrorHandler(
81
105
  options: Sentry.NodeOptions = {},
82
106
  ): ErrorRequestHandler {
83
107
  return Sentry.expressErrorHandler({
84
- shouldHandleError() {
85
- return process.env.NODE_ENV === 'production';
108
+ shouldHandleError(error) {
109
+ const status = getHttpStatusCode(error);
110
+
111
+ return (
112
+ process.env.NODE_ENV === 'production' &&
113
+ (status == null || status < 400 || status >= 500)
114
+ );
86
115
  },
87
116
  ...options,
88
117
  }) as unknown as ErrorRequestHandler;