@bymax-one/nest-core 1.6.5 → 1.6.6

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 CHANGED
@@ -11,6 +11,27 @@ heading here.
11
11
 
12
12
  ## [Unreleased]
13
13
 
14
+ ## [1.6.6] - 2026-11-07
15
+
16
+ A patch release. The error envelope no longer returns the parser's own text for a request refused before
17
+ routing; see Fixed.
18
+
19
+ **Apply to a derived backend:** `pnpm up @bymax-one/nest-core`. A client that matched on those
20
+ message strings must match `code` instead. A middleware that throws a plain `BadRequestException`
21
+ before routing now answers `Malformed request` with the same 400; to keep its message, throw it with
22
+ a `code` (`new HttpException({ code: 'TENANT_REQUIRED', message }, 400)`).
23
+
24
+ ### Fixed
25
+
26
+ - **A request refused before routing no longer has the parser's own text returned.** The message of
27
+ a body-parser, router or decoding refusal quotes the request: V8's JSON excerpt (on a login body
28
+ with a missing value it ends in the password's last characters), a charset or content-encoding
29
+ header value, or the path. The status is unchanged and the envelope keeps its shape, but the
30
+ message is now fixed per status. It is chosen from the error's own `type` (the body pipeline's
31
+ refusals carry one) or, for the `SyntaxError` and `URIError` that Nest rewrites into a plain
32
+ `BadRequestException`, from a 400 raised while the request matched no route. Not covered: Fastify,
33
+ whose body errors take another path and were not measured here.
34
+
14
35
  ## [1.6.5] - 2026-10-31
15
36
 
16
37
  A documentation release. The compiled JavaScript is byte-identical to `1.6.4`: what changes in the
@@ -1314,4 +1335,5 @@ have regressed from. They are kept because the reasoning is worth having.
1314
1335
  [1.6.3]: https://github.com/bymaxone/nest-core/compare/v1.6.2...v1.6.3
1315
1336
  [1.6.4]: https://github.com/bymaxone/nest-core/compare/v1.6.3...v1.6.4
1316
1337
  [1.6.5]: https://github.com/bymaxone/nest-core/compare/v1.6.4...v1.6.5
1317
- [Unreleased]: https://github.com/bymaxone/nest-core/compare/v1.6.5...HEAD
1338
+ [1.6.6]: https://github.com/bymaxone/nest-core/compare/v1.6.5...v1.6.6
1339
+ [Unreleased]: https://github.com/bymaxone/nest-core/compare/v1.6.6...HEAD
package/README.md CHANGED
@@ -639,6 +639,12 @@ marks itself `expose: true` with a `4xx` status, which is the `http-errors`
639
639
  convention Node's body pipeline follows. Nothing in the application failed, so
640
640
  it is not routed through the unexpected-error seam either.
641
641
 
642
+ The message of such a refusal is fixed per status — `Malformed request` (400),
643
+ `Request entity too large` (413), `Unsupported media type` (415) — because the
644
+ parser's own text quotes the request: a JSON excerpt, a header value, the path.
645
+ A `BadRequestException` a middleware throws before routing gets the same 400
646
+ message; throw it with a `code` to keep its own. Fastify is not covered.
647
+
642
648
  **An error that carries no such marking is a 500, including when a client
643
649
  caused it.** The clearest case is depth: a body of a few kilobytes nested
644
650
  thousands of levels deep overflows the stack during validation and surfaces as
package/dist/index.cjs CHANGED
@@ -359,6 +359,12 @@ function extractRequestInfo(context) {
359
359
  var INTERNAL_ERROR_STATUS = 500;
360
360
  var INTERNAL_ERROR_MESSAGE = "Internal server error";
361
361
  var VALIDATION_FAILED_MESSAGE = "Validation failed";
362
+ var MALFORMED_REQUEST_MESSAGE = "Malformed request";
363
+ var PRE_ROUTING_MESSAGES = /* @__PURE__ */ new Map([
364
+ [400, MALFORMED_REQUEST_MESSAGE],
365
+ [413, "Request entity too large"],
366
+ [415, "Unsupported media type"]
367
+ ]);
362
368
  var ABSOLUTE_FORM_PREFIX = /^[A-Za-z][A-Za-z0-9+.-]*:\/\//;
363
369
  function pathOf(url) {
364
370
  let target = String(url);
@@ -409,8 +415,9 @@ function resolveExposedClientError(exception) {
409
415
  if (typeof status !== "number" || !Number.isInteger(status) || status < CLIENT_ERROR_MIN2 || status >= CLIENT_ERROR_MAX2) {
410
416
  return void 0;
411
417
  }
418
+ const fixed = typeof candidate.type === "string" ? PRE_ROUTING_MESSAGES.get(status) : void 0;
412
419
  const message = exception instanceof Error ? exception.message : "";
413
- return { status, message: message === "" ? INTERNAL_ERROR_MESSAGE : message };
420
+ return { status, message: fixed ?? (message === "" ? INTERNAL_ERROR_MESSAGE : message) };
414
421
  }
415
422
  function isValidationResponse(response) {
416
423
  return typeof response === "object" && response !== null && "message" in response && Array.isArray(response.message);
@@ -611,6 +618,9 @@ exports.BymaxExceptionFilter = class BymaxExceptionFilter {
611
618
  toValidationDetails(response.message)
612
619
  );
613
620
  }
621
+ if (status === 400 && readRequestInfo(request).route === UNMATCHED_ROUTE) {
622
+ return this.toEnvelope(status, codeForStatus(status), MALFORMED_REQUEST_MESSAGE, context);
623
+ }
614
624
  const message = extractHttpMessage(response, exception);
615
625
  const rawUrl = String(this.adapterHost.httpAdapter.getRequestUrl(request));
616
626
  return this.toEnvelope(
package/dist/index.mjs CHANGED
@@ -357,6 +357,12 @@ function extractRequestInfo(context) {
357
357
  var INTERNAL_ERROR_STATUS = 500;
358
358
  var INTERNAL_ERROR_MESSAGE = "Internal server error";
359
359
  var VALIDATION_FAILED_MESSAGE = "Validation failed";
360
+ var MALFORMED_REQUEST_MESSAGE = "Malformed request";
361
+ var PRE_ROUTING_MESSAGES = /* @__PURE__ */ new Map([
362
+ [400, MALFORMED_REQUEST_MESSAGE],
363
+ [413, "Request entity too large"],
364
+ [415, "Unsupported media type"]
365
+ ]);
360
366
  var ABSOLUTE_FORM_PREFIX = /^[A-Za-z][A-Za-z0-9+.-]*:\/\//;
361
367
  function pathOf(url) {
362
368
  let target = String(url);
@@ -407,8 +413,9 @@ function resolveExposedClientError(exception) {
407
413
  if (typeof status !== "number" || !Number.isInteger(status) || status < CLIENT_ERROR_MIN2 || status >= CLIENT_ERROR_MAX2) {
408
414
  return void 0;
409
415
  }
416
+ const fixed = typeof candidate.type === "string" ? PRE_ROUTING_MESSAGES.get(status) : void 0;
410
417
  const message = exception instanceof Error ? exception.message : "";
411
- return { status, message: message === "" ? INTERNAL_ERROR_MESSAGE : message };
418
+ return { status, message: fixed ?? (message === "" ? INTERNAL_ERROR_MESSAGE : message) };
412
419
  }
413
420
  function isValidationResponse(response) {
414
421
  return typeof response === "object" && response !== null && "message" in response && Array.isArray(response.message);
@@ -609,6 +616,9 @@ var BymaxExceptionFilter = class {
609
616
  toValidationDetails(response.message)
610
617
  );
611
618
  }
619
+ if (status === 400 && readRequestInfo(request).route === UNMATCHED_ROUTE) {
620
+ return this.toEnvelope(status, codeForStatus(status), MALFORMED_REQUEST_MESSAGE, context);
621
+ }
612
622
  const message = extractHttpMessage(response, exception);
613
623
  const rawUrl = String(this.adapterHost.httpAdapter.getRequestUrl(request));
614
624
  return this.toEnvelope(
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bymax-one/nest-core",
3
- "version": "1.6.5",
3
+ "version": "1.6.6",
4
4
  "description": "Zero-dependency NestJS 11 application foundation kit: error-envelope exception filter, request-timing middleware, pagination helpers, health endpoints with indicator discovery, an optional Prometheus metrics endpoint with a contribution contract, OpenAPI documents in development, and OpenTelemetry trace correlation.",
5
5
  "author": "Bymax One <support@bymax.one>",
6
6
  "license": "MIT",