@telorun/http-server 0.15.0 → 0.15.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.
- package/CHANGELOG.md +19 -0
- package/dist/http-api-controller.js +22 -9
- package/dist/http-server-controller.js +25 -14
- package/package.json +2 -2
- package/src/http-api-controller.ts +18 -3
- package/src/http-server-controller.ts +24 -14
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,24 @@
|
|
|
1
1
|
# @telorun/http-server
|
|
2
2
|
|
|
3
|
+
## 0.15.2
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- a33b8cc: Log mid-stream failures server-side. When a `mode: stream` route's stream throws
|
|
8
|
+
after the response headers are flushed, the failure can't reach Fastify's error
|
|
9
|
+
handler or a `catches:` entry, so it was previously silent for the operator (it
|
|
10
|
+
was only emitted as an `Http.Api.streamFailed` event, which nothing logs unless a
|
|
11
|
+
debug consumer is attached). The stream-error hook now also logs on the server's
|
|
12
|
+
request logger (the one `Http.Server.logger` configures) at `error` level with
|
|
13
|
+
the error, route, status, and MIME. Client-facing behaviour is unchanged.
|
|
14
|
+
- @telorun/http-dispatch@0.4.1
|
|
15
|
+
|
|
16
|
+
## 0.15.1
|
|
17
|
+
|
|
18
|
+
### Patch Changes
|
|
19
|
+
|
|
20
|
+
- 06c675b: Fix CORS preflight returning 404. `Http.Server` forwarded every `cors` option to `@fastify/cors`, including the ones the manifest left unset — spreading `preflight: undefined` (and friends) clobbered the plugin's own default `preflight: true`, so its `OPTIONS *` handler `callNotFound()`'d and the preflight came back 404. Browsers reject that ("Response to preflight request … does not have HTTP ok status") and block every cross-origin `POST`. Now only the fields actually set on `cors` are passed through, so unset options keep the plugin's defaults and preflight replies 204.
|
|
21
|
+
|
|
3
22
|
## 0.15.0
|
|
4
23
|
|
|
5
24
|
### Minor Changes
|
|
@@ -165,15 +165,28 @@ export class HttpServerApi {
|
|
|
165
165
|
return dispatchCatches(route.catches, { code: err.code, message: err.message, data: err.data }, requestContext, acceptHeader, this.ctx.moduleContext, this.ctx.validateSchema.bind(this.ctx), sink);
|
|
166
166
|
}
|
|
167
167
|
await span.settle("ok");
|
|
168
|
-
return dispatchReturns(route.returns, result, requestContext, acceptHeader, this.ctx.moduleContext, this.ctx.validateSchema.bind(this.ctx), sink, (err, errCtx) =>
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
error
|
|
174
|
-
|
|
175
|
-
:
|
|
176
|
-
|
|
168
|
+
return dispatchReturns(route.returns, result, requestContext, acceptHeader, this.ctx.moduleContext, this.ctx.validateSchema.bind(this.ctx), sink, (err, errCtx) => {
|
|
169
|
+
// Headers are already flushed, so this failure can't reach Fastify's
|
|
170
|
+
// error handler (which logs pre-stream throws) — log it here, on the
|
|
171
|
+
// same request logger `logger:` configures, so a mid-stream failure
|
|
172
|
+
// isn't silent server-side. The event below stays for debug tooling.
|
|
173
|
+
reply.log.error({
|
|
174
|
+
err,
|
|
175
|
+
path: route.request.path,
|
|
176
|
+
method: route.request.method,
|
|
177
|
+
status: errCtx.status,
|
|
178
|
+
mime: errCtx.mime,
|
|
179
|
+
}, "Http.Api stream failed after response headers were flushed");
|
|
180
|
+
return this.ctx.emitEvent("Http.Api.streamFailed", {
|
|
181
|
+
path: route.request.path,
|
|
182
|
+
method: route.request.method,
|
|
183
|
+
status: errCtx.status,
|
|
184
|
+
mime: errCtx.mime,
|
|
185
|
+
error: err instanceof Error
|
|
186
|
+
? { message: err.message, stack: err.stack, code: err.code }
|
|
187
|
+
: { message: String(err) },
|
|
188
|
+
});
|
|
189
|
+
});
|
|
177
190
|
},
|
|
178
191
|
});
|
|
179
192
|
}
|
|
@@ -73,20 +73,31 @@ class HttpServer {
|
|
|
73
73
|
}
|
|
74
74
|
}
|
|
75
75
|
if (this.resource.cors) {
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
76
|
+
// Only forward the fields the manifest actually set. Spreading `undefined`
|
|
77
|
+
// for an unset option overrides @fastify/cors's own defaults with
|
|
78
|
+
// `undefined` — notably `preflight: undefined` disables the preflight 204
|
|
79
|
+
// reply (its `OPTIONS *` handler then `callNotFound()`s → 404), which a
|
|
80
|
+
// browser reports as "preflight … does not have HTTP ok status".
|
|
81
|
+
const cfg = this.resource.cors;
|
|
82
|
+
const corsOptions = {};
|
|
83
|
+
for (const key of [
|
|
84
|
+
"origin",
|
|
85
|
+
"methods",
|
|
86
|
+
"allowedHeaders",
|
|
87
|
+
"exposedHeaders",
|
|
88
|
+
"credentials",
|
|
89
|
+
"maxAge",
|
|
90
|
+
"cacheControl",
|
|
91
|
+
"preflightContinue",
|
|
92
|
+
"optionsSuccessStatus",
|
|
93
|
+
"preflight",
|
|
94
|
+
"strictPreflight",
|
|
95
|
+
"hideOptionsRoute",
|
|
96
|
+
]) {
|
|
97
|
+
if (cfg[key] !== undefined)
|
|
98
|
+
corsOptions[key] = cfg[key];
|
|
99
|
+
}
|
|
100
|
+
await this.app.register(cors, corsOptions);
|
|
90
101
|
}
|
|
91
102
|
// Register custom error handler for validation errors
|
|
92
103
|
this.app.setErrorHandler((error, request, reply) => {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@telorun/http-server",
|
|
3
|
-
"version": "0.15.
|
|
3
|
+
"version": "0.15.2",
|
|
4
4
|
"description": "Telo HTTP Server module - HTTP server and API resource kinds for Telo manifests.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"telo",
|
|
@@ -55,7 +55,7 @@
|
|
|
55
55
|
"@types/node": "^20.0.0",
|
|
56
56
|
"typescript": "^5.0.0",
|
|
57
57
|
"vitest": "^2.1.8",
|
|
58
|
-
"@telorun/sdk": "0.
|
|
58
|
+
"@telorun/sdk": "0.44.0"
|
|
59
59
|
},
|
|
60
60
|
"peerDependencies": {
|
|
61
61
|
"@telorun/sdk": "*"
|
|
@@ -222,8 +222,22 @@ export class HttpServerApi implements ResourceInstance {
|
|
|
222
222
|
this.ctx.moduleContext,
|
|
223
223
|
this.ctx.validateSchema.bind(this.ctx),
|
|
224
224
|
sink,
|
|
225
|
-
(err, errCtx) =>
|
|
226
|
-
|
|
225
|
+
(err, errCtx) => {
|
|
226
|
+
// Headers are already flushed, so this failure can't reach Fastify's
|
|
227
|
+
// error handler (which logs pre-stream throws) — log it here, on the
|
|
228
|
+
// same request logger `logger:` configures, so a mid-stream failure
|
|
229
|
+
// isn't silent server-side. The event below stays for debug tooling.
|
|
230
|
+
reply.log.error(
|
|
231
|
+
{
|
|
232
|
+
err,
|
|
233
|
+
path: route.request.path,
|
|
234
|
+
method: route.request.method,
|
|
235
|
+
status: errCtx.status,
|
|
236
|
+
mime: errCtx.mime,
|
|
237
|
+
},
|
|
238
|
+
"Http.Api stream failed after response headers were flushed",
|
|
239
|
+
);
|
|
240
|
+
return this.ctx.emitEvent("Http.Api.streamFailed", {
|
|
227
241
|
path: route.request.path,
|
|
228
242
|
method: route.request.method,
|
|
229
243
|
status: errCtx.status,
|
|
@@ -232,7 +246,8 @@ export class HttpServerApi implements ResourceInstance {
|
|
|
232
246
|
err instanceof Error
|
|
233
247
|
? { message: err.message, stack: err.stack, code: (err as { code?: string }).code }
|
|
234
248
|
: { message: String(err) },
|
|
235
|
-
})
|
|
249
|
+
});
|
|
250
|
+
},
|
|
236
251
|
);
|
|
237
252
|
},
|
|
238
253
|
});
|
|
@@ -155,20 +155,30 @@ class HttpServer implements ResourceInstance {
|
|
|
155
155
|
}
|
|
156
156
|
|
|
157
157
|
if (this.resource.cors) {
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
158
|
+
// Only forward the fields the manifest actually set. Spreading `undefined`
|
|
159
|
+
// for an unset option overrides @fastify/cors's own defaults with
|
|
160
|
+
// `undefined` — notably `preflight: undefined` disables the preflight 204
|
|
161
|
+
// reply (its `OPTIONS *` handler then `callNotFound()`s → 404), which a
|
|
162
|
+
// browser reports as "preflight … does not have HTTP ok status".
|
|
163
|
+
const cfg = this.resource.cors;
|
|
164
|
+
const corsOptions: Record<string, unknown> = {};
|
|
165
|
+
for (const key of [
|
|
166
|
+
"origin",
|
|
167
|
+
"methods",
|
|
168
|
+
"allowedHeaders",
|
|
169
|
+
"exposedHeaders",
|
|
170
|
+
"credentials",
|
|
171
|
+
"maxAge",
|
|
172
|
+
"cacheControl",
|
|
173
|
+
"preflightContinue",
|
|
174
|
+
"optionsSuccessStatus",
|
|
175
|
+
"preflight",
|
|
176
|
+
"strictPreflight",
|
|
177
|
+
"hideOptionsRoute",
|
|
178
|
+
] as const) {
|
|
179
|
+
if (cfg[key] !== undefined) corsOptions[key] = cfg[key];
|
|
180
|
+
}
|
|
181
|
+
await this.app.register(cors, corsOptions);
|
|
172
182
|
}
|
|
173
183
|
|
|
174
184
|
// Register custom error handler for validation errors
|