@telorun/http-server 0.20.0 → 0.26.0

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.
@@ -164,8 +164,35 @@ class HttpServer {
164
164
  }, { eventName: "http.server.request" });
165
165
  });
166
166
  }
167
+ /**
168
+ * Accept a multipart body out of the box.
169
+ *
170
+ * Fastify ships parsers for JSON and urlencoded and nothing else, so a route
171
+ * receiving a file upload answered 415 before any handler ran — a failure that
172
+ * names a media type the author DID send and points at no fix. Every server
173
+ * taking an upload had to discover `contentTypeParsers` first.
174
+ *
175
+ * Registered as RAW BYTES rather than a string, because that is what a
176
+ * multipart body is: decoding it as text corrupts every binary part, and the
177
+ * parts are the point. The handler receives the undrained request stream, which
178
+ * `Multipart.Decoder` consumes.
179
+ *
180
+ * Registered UNCONDITIONALLY, as a regex. Fastify keys a duplicate on the exact
181
+ * string (or the regex's `toString()`) and consults its string parsers before
182
+ * its regex ones, so a declared `multipart/form-data` neither collides with this
183
+ * nor is shadowed by it — it simply wins for its own type. Skipping the default
184
+ * whenever any multipart parser was declared would instead disable it for the
185
+ * SIBLING subtypes the author did not customize, so declaring a parser for
186
+ * `form-data` would silently restore the 415 for `related` and `mixed`.
187
+ */
188
+ installDefaultMultipartParser() {
189
+ this.app.addContentTypeParser(/^multipart\//, (_req, payload, done) => {
190
+ done(null, payload);
191
+ });
192
+ }
167
193
  async setupPlugins() {
168
194
  this.installRequestLogging();
195
+ this.installDefaultMultipartParser();
169
196
  for (const { contentType, parser, stream } of this.resource.contentTypeParsers ?? []) {
170
197
  if (stream) {
171
198
  // Raw passthrough: omit `parseAs` so Fastify hands the handler the
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@telorun/http-server",
3
- "version": "0.20.0",
3
+ "version": "0.26.0",
4
4
  "description": "Telo HTTP Server module - HTTP server and API resource kinds for Telo manifests.",
5
5
  "keywords": [
6
6
  "telo",
@@ -49,13 +49,13 @@
49
49
  "ajv": "^8.17.1",
50
50
  "ajv-formats": "^3.0.1",
51
51
  "fastify": "^5.7.2",
52
- "@telorun/http-dispatch": "0.4.2"
52
+ "@telorun/http-dispatch": "0.11.1"
53
53
  },
54
54
  "devDependencies": {
55
55
  "@types/node": "^20.0.0",
56
56
  "typescript": "^5.0.0",
57
57
  "vitest": "^2.1.8",
58
- "@telorun/sdk": "0.73.0"
58
+ "@telorun/sdk": "0.75.0"
59
59
  },
60
60
  "peerDependencies": {
61
61
  "@telorun/sdk": "*"
@@ -261,8 +261,36 @@ class HttpServer implements ResourceInstance {
261
261
  });
262
262
  }
263
263
 
264
+ /**
265
+ * Accept a multipart body out of the box.
266
+ *
267
+ * Fastify ships parsers for JSON and urlencoded and nothing else, so a route
268
+ * receiving a file upload answered 415 before any handler ran — a failure that
269
+ * names a media type the author DID send and points at no fix. Every server
270
+ * taking an upload had to discover `contentTypeParsers` first.
271
+ *
272
+ * Registered as RAW BYTES rather than a string, because that is what a
273
+ * multipart body is: decoding it as text corrupts every binary part, and the
274
+ * parts are the point. The handler receives the undrained request stream, which
275
+ * `Multipart.Decoder` consumes.
276
+ *
277
+ * Registered UNCONDITIONALLY, as a regex. Fastify keys a duplicate on the exact
278
+ * string (or the regex's `toString()`) and consults its string parsers before
279
+ * its regex ones, so a declared `multipart/form-data` neither collides with this
280
+ * nor is shadowed by it — it simply wins for its own type. Skipping the default
281
+ * whenever any multipart parser was declared would instead disable it for the
282
+ * SIBLING subtypes the author did not customize, so declaring a parser for
283
+ * `form-data` would silently restore the 415 for `related` and `mixed`.
284
+ */
285
+ private installDefaultMultipartParser(): void {
286
+ this.app.addContentTypeParser(/^multipart\//, (_req, payload, done) => {
287
+ done(null, payload);
288
+ });
289
+ }
290
+
264
291
  private async setupPlugins() {
265
292
  this.installRequestLogging();
293
+ this.installDefaultMultipartParser();
266
294
  for (const { contentType, parser, stream } of this.resource.contentTypeParsers ?? []) {
267
295
  if (stream) {
268
296
  // Raw passthrough: omit `parseAs` so Fastify hands the handler the