javascript-solid-server 0.0.152 → 0.0.153
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.
- package/package.json +1 -1
- package/src/server.js +2 -0
- package/src/utils/error-handler.js +24 -0
- package/test/error-handler.test.js +83 -0
package/package.json
CHANGED
package/src/server.js
CHANGED
|
@@ -21,6 +21,7 @@ import { dbPlugin } from './db/index.js';
|
|
|
21
21
|
import { webrtcPlugin } from './webrtc/index.js';
|
|
22
22
|
import { tunnelPlugin } from './tunnel/index.js';
|
|
23
23
|
import { terminalPlugin } from './terminal/index.js';
|
|
24
|
+
import { registerErrorHandler } from './utils/error-handler.js';
|
|
24
25
|
|
|
25
26
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
26
27
|
|
|
@@ -154,6 +155,7 @@ export function createServer(options = {}) {
|
|
|
154
155
|
}
|
|
155
156
|
|
|
156
157
|
const fastify = Fastify(fastifyOptions);
|
|
158
|
+
registerErrorHandler(fastify);
|
|
157
159
|
|
|
158
160
|
// Add raw body parser for all content types
|
|
159
161
|
fastify.addContentTypeParser('*', { parseAs: 'buffer' }, (req, body, done) => {
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Top-level Fastify error handler that logs the full stack for any 5xx.
|
|
3
|
+
*
|
|
4
|
+
* Without this, 500 responses carry only Fastify's default 91-byte body and
|
|
5
|
+
* leave no trace in logs — production debugging has to infer the exception
|
|
6
|
+
* from the response body alone (see #309 investigation for why that's bad).
|
|
7
|
+
*
|
|
8
|
+
* 4xx errors carry their own statusCode and don't need stack logging — they
|
|
9
|
+
* are expected client errors with self-explanatory messages.
|
|
10
|
+
*/
|
|
11
|
+
export function registerErrorHandler(fastify) {
|
|
12
|
+
fastify.setErrorHandler(function (err, request, reply) {
|
|
13
|
+
const statusCode = err.statusCode ?? 500;
|
|
14
|
+
if (statusCode >= 500) {
|
|
15
|
+
request.log.error({
|
|
16
|
+
err,
|
|
17
|
+
method: request.method,
|
|
18
|
+
url: request.url,
|
|
19
|
+
hostname: request.hostname
|
|
20
|
+
}, 'Unhandled 5xx error');
|
|
21
|
+
}
|
|
22
|
+
reply.send(err);
|
|
23
|
+
});
|
|
24
|
+
}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for the top-level error handler (#312).
|
|
3
|
+
*
|
|
4
|
+
* Verifies that:
|
|
5
|
+
* 1. Unhandled exceptions from route handlers produce a 500 whose body
|
|
6
|
+
* matches Fastify's default shape — the existing 91-byte response
|
|
7
|
+
* format must not regress (that's the baseline consumers rely on).
|
|
8
|
+
* 2. The error's stack is actually logged (with method/url/hostname
|
|
9
|
+
* context), so production debugging has a real trace.
|
|
10
|
+
* 3. 4xx errors are NOT stack-logged (they're expected client errors).
|
|
11
|
+
*
|
|
12
|
+
* Uses a minimal Fastify instance so the test exercises only the handler,
|
|
13
|
+
* not the full server's auth/routing stack.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
import { describe, it, before, after } from 'node:test';
|
|
17
|
+
import assert from 'node:assert';
|
|
18
|
+
import Fastify from 'fastify';
|
|
19
|
+
import { registerErrorHandler } from '../src/utils/error-handler.js';
|
|
20
|
+
|
|
21
|
+
class LogCapture {
|
|
22
|
+
constructor() { this.lines = []; }
|
|
23
|
+
write(chunk) {
|
|
24
|
+
try { this.lines.push(JSON.parse(chunk)); } catch { /* non-JSON */ }
|
|
25
|
+
return true;
|
|
26
|
+
}
|
|
27
|
+
errorLines() {
|
|
28
|
+
return this.lines.filter((l) => l.level >= 50);
|
|
29
|
+
}
|
|
30
|
+
clear() { this.lines.length = 0; }
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
describe('registerErrorHandler (#312)', () => {
|
|
34
|
+
let app;
|
|
35
|
+
const capture = new LogCapture();
|
|
36
|
+
|
|
37
|
+
before(async () => {
|
|
38
|
+
app = Fastify({
|
|
39
|
+
logger: { level: 'error', stream: capture },
|
|
40
|
+
disableRequestLogging: true
|
|
41
|
+
});
|
|
42
|
+
registerErrorHandler(app);
|
|
43
|
+
app.get('/throw500', async () => { throw new Error('synthetic 5xx for test'); });
|
|
44
|
+
app.get('/throw400', async () => {
|
|
45
|
+
const err = new Error('bad client input');
|
|
46
|
+
err.statusCode = 400;
|
|
47
|
+
throw err;
|
|
48
|
+
});
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
after(async () => { await app.close(); });
|
|
52
|
+
|
|
53
|
+
it('500 response body matches Fastify default shape (no regression)', async () => {
|
|
54
|
+
capture.clear();
|
|
55
|
+
const res = await app.inject({ method: 'GET', url: '/throw500' });
|
|
56
|
+
assert.strictEqual(res.statusCode, 500);
|
|
57
|
+
assert.deepStrictEqual(res.json(), {
|
|
58
|
+
statusCode: 500,
|
|
59
|
+
error: 'Internal Server Error',
|
|
60
|
+
message: 'synthetic 5xx for test'
|
|
61
|
+
});
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
it('500 logs include the stack and request context', async () => {
|
|
65
|
+
capture.clear();
|
|
66
|
+
await app.inject({ method: 'GET', url: '/throw500', headers: { host: 'example.test' } });
|
|
67
|
+
const line = capture.errorLines().find((l) => l.msg === 'Unhandled 5xx error');
|
|
68
|
+
assert.ok(line, `expected 'Unhandled 5xx error' log line, got: ${JSON.stringify(capture.errorLines())}`);
|
|
69
|
+
assert.strictEqual(line.method, 'GET');
|
|
70
|
+
assert.strictEqual(line.url, '/throw500');
|
|
71
|
+
assert.strictEqual(line.hostname, 'example.test');
|
|
72
|
+
assert.ok(line.err && line.err.stack, 'expected err.stack in log');
|
|
73
|
+
assert.ok(line.err.stack.includes('synthetic 5xx'), 'stack should identify the error');
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
it('4xx errors do not trigger the 5xx stack log', async () => {
|
|
77
|
+
capture.clear();
|
|
78
|
+
const res = await app.inject({ method: 'GET', url: '/throw400' });
|
|
79
|
+
assert.strictEqual(res.statusCode, 400);
|
|
80
|
+
const unhandled = capture.errorLines().filter((l) => l.msg === 'Unhandled 5xx error');
|
|
81
|
+
assert.strictEqual(unhandled.length, 0, '4xx must not produce a 5xx stack log');
|
|
82
|
+
});
|
|
83
|
+
});
|