@bymax-one/nest-core 1.6.6 → 1.6.8

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/dist/index.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import { Catch, Inject, Optional, Injectable, ConfigurableModuleBuilder, Module, HttpException, Logger, Get, Res, Controller, Req, HttpStatus, UnauthorizedException, NotFoundException } from '@nestjs/common';
1
+ import { Injectable, Inject, Catch, Optional, ConfigurableModuleBuilder, Module, BadRequestException, HttpException, Logger, Get, Res, Controller, Req, HttpStatus, UnauthorizedException, NotFoundException } from '@nestjs/common';
2
2
  import { HttpAdapterHost, DiscoveryService, Reflector, BaseExceptionFilter, APP_FILTER, DiscoveryModule } from '@nestjs/core';
3
3
  import { createHash, timingSafeEqual } from 'crypto';
4
4
  import { AsyncResource } from 'async_hooks';
@@ -16,6 +16,121 @@ var __decorateClass = (decorators, target, key, kind) => {
16
16
  };
17
17
  var __decorateParam = (index, decorator) => (target, key) => decorator(target, key, index);
18
18
 
19
+ // src/core.tokens.ts
20
+ var BYMAX_CORE_OPTIONS = /* @__PURE__ */ Symbol.for("@bymax-one/nest-core:core-options");
21
+ var BYMAX_CORRELATION_PROVIDER = /* @__PURE__ */ Symbol.for(
22
+ "@bymax-one/nest-core:correlation-provider"
23
+ );
24
+ var BYMAX_TIMING_SINK = /* @__PURE__ */ Symbol.for("@bymax-one/nest-core:timing-sink");
25
+ var BYMAX_HEALTH_INDICATORS = /* @__PURE__ */ Symbol.for(
26
+ "@bymax-one/nest-core:health-indicators"
27
+ );
28
+ var BYMAX_HEALTH_TRANSITION_SINK = /* @__PURE__ */ Symbol.for(
29
+ "@bymax-one/nest-core:health-transition-sink"
30
+ );
31
+ var BYMAX_UNEXPECTED_ERROR_SINK = /* @__PURE__ */ Symbol.for(
32
+ "@bymax-one/nest-core:unexpected-error-sink"
33
+ );
34
+ var BYMAX_METRICS_REGISTRY = /* @__PURE__ */ Symbol.for(
35
+ "@bymax-one/nest-core:metrics-registry"
36
+ );
37
+ var BYMAX_TRACE_CONTEXT = /* @__PURE__ */ Symbol.for("@bymax-one/nest-core:trace-context");
38
+
39
+ // src/envelope/error-codes.ts
40
+ var BYMAX_BAD_REQUEST = "BYMAX_BAD_REQUEST";
41
+ var BYMAX_VALIDATION_FAILED = "BYMAX_VALIDATION_FAILED";
42
+ var BYMAX_UNAUTHORIZED = "BYMAX_UNAUTHORIZED";
43
+ var BYMAX_FORBIDDEN = "BYMAX_FORBIDDEN";
44
+ var BYMAX_NOT_FOUND = "BYMAX_NOT_FOUND";
45
+ var BYMAX_CONFLICT = "BYMAX_CONFLICT";
46
+ var BYMAX_PAYLOAD_TOO_LARGE = "BYMAX_PAYLOAD_TOO_LARGE";
47
+ var BYMAX_UNSUPPORTED_MEDIA_TYPE = "BYMAX_UNSUPPORTED_MEDIA_TYPE";
48
+ var BYMAX_UNPROCESSABLE_ENTITY = "BYMAX_UNPROCESSABLE_ENTITY";
49
+ var BYMAX_TOO_MANY_REQUESTS = "BYMAX_TOO_MANY_REQUESTS";
50
+ var BYMAX_CLIENT_ERROR = "BYMAX_CLIENT_ERROR";
51
+ var BYMAX_INTERNAL_ERROR = "BYMAX_INTERNAL_ERROR";
52
+ var BYMAX_NOT_IMPLEMENTED = "BYMAX_NOT_IMPLEMENTED";
53
+ var BYMAX_BAD_GATEWAY = "BYMAX_BAD_GATEWAY";
54
+ var BYMAX_SERVICE_UNAVAILABLE = "BYMAX_SERVICE_UNAVAILABLE";
55
+ var BYMAX_GATEWAY_TIMEOUT = "BYMAX_GATEWAY_TIMEOUT";
56
+ var STATUS_CODES = /* @__PURE__ */ new Map([
57
+ [400, BYMAX_BAD_REQUEST],
58
+ [401, BYMAX_UNAUTHORIZED],
59
+ [403, BYMAX_FORBIDDEN],
60
+ [404, BYMAX_NOT_FOUND],
61
+ [409, BYMAX_CONFLICT],
62
+ [413, BYMAX_PAYLOAD_TOO_LARGE],
63
+ [415, BYMAX_UNSUPPORTED_MEDIA_TYPE],
64
+ [422, BYMAX_UNPROCESSABLE_ENTITY],
65
+ [429, BYMAX_TOO_MANY_REQUESTS],
66
+ [500, BYMAX_INTERNAL_ERROR],
67
+ [501, BYMAX_NOT_IMPLEMENTED],
68
+ [502, BYMAX_BAD_GATEWAY],
69
+ [503, BYMAX_SERVICE_UNAVAILABLE],
70
+ [504, BYMAX_GATEWAY_TIMEOUT]
71
+ ]);
72
+ var CLIENT_ERROR_MIN = 400;
73
+ var CLIENT_ERROR_MAX = 500;
74
+ function codeForStatus(status) {
75
+ const catalogued = STATUS_CODES.get(status);
76
+ if (catalogued !== void 0) {
77
+ return catalogued;
78
+ }
79
+ if (status >= CLIENT_ERROR_MIN && status < CLIENT_ERROR_MAX) {
80
+ return BYMAX_CLIENT_ERROR;
81
+ }
82
+ return BYMAX_INTERNAL_ERROR;
83
+ }
84
+
85
+ // src/body-depth/body-depth.middleware.ts
86
+ var BODY_TOO_DEEP_MESSAGE = "Request body is nested too deeply";
87
+ function isContainer(node) {
88
+ if (typeof node !== "object" || node === null) {
89
+ return false;
90
+ }
91
+ const proto = Object.getPrototypeOf(node);
92
+ return Array.isArray(node) || proto === Object.prototype || proto === null;
93
+ }
94
+ function exceedsDepth(value, maxDepth) {
95
+ let level = [value];
96
+ for (let depth = 0; level.length > 0; depth += 1) {
97
+ const next = [];
98
+ for (const node of level) {
99
+ if (isContainer(node)) {
100
+ if (depth >= maxDepth) {
101
+ return true;
102
+ }
103
+ for (const child of Object.values(node)) {
104
+ next.push(child);
105
+ }
106
+ }
107
+ }
108
+ level = next;
109
+ }
110
+ return false;
111
+ }
112
+ var BymaxBodyDepthMiddleware = class {
113
+ constructor(options) {
114
+ this.options = options;
115
+ }
116
+ /**
117
+ * @param req - The request, whose `body` the parser has already filled.
118
+ * @param _res - Unused.
119
+ * @param next - Continues the chain.
120
+ * @throws BadRequestException When the body is nested too deeply.
121
+ */
122
+ use(req, _res, next) {
123
+ if (exceedsDepth(req.body, this.options.bodyDepth.maxDepth)) {
124
+ throw new BadRequestException({ code: BYMAX_BAD_REQUEST, message: BODY_TOO_DEEP_MESSAGE });
125
+ }
126
+ next();
127
+ }
128
+ };
129
+ BymaxBodyDepthMiddleware = __decorateClass([
130
+ Injectable(),
131
+ __decorateParam(0, Inject(BYMAX_CORE_OPTIONS))
132
+ ], BymaxBodyDepthMiddleware);
133
+
19
134
  // src/route-defaults.ts
20
135
  var DEFAULT_HEALTH_PATH = "health";
21
136
  var DEFAULT_METRICS_PATH = "metrics";
@@ -38,6 +153,8 @@ function runtimeEnvironmentName(declared) {
38
153
 
39
154
  // src/core.options.ts
40
155
  var DEFAULT_INDICATOR_TIMEOUT_MS = 5e3;
156
+ var MAX_TIMER_DELAY_MS = 2 ** 31 - 1;
157
+ var DEFAULT_READINESS_CACHE_MS = 1e3;
41
158
  var DEFAULT_OPENAPI_PATH = "docs";
42
159
  var DEFAULT_OPENAPI_JSON_PATH = "docs-json";
43
160
  var DEFAULT_OPENAPI_TITLE = "API";
@@ -57,17 +174,48 @@ function resolveEnvelope(raw) {
57
174
  exposeInternals: raw?.exposeInternals ?? false
58
175
  };
59
176
  }
177
+ function positiveSafeInteger(name, value, max) {
178
+ if (!Number.isSafeInteger(value) || value < 1) {
179
+ throw new Error(`${name} must be a positive safe integer, received ${String(value)}`);
180
+ }
181
+ if (max !== void 0 && value > max) {
182
+ throw new Error(`${name} must be a positive integer of at most ${max}, received ${value}`);
183
+ }
184
+ return value;
185
+ }
60
186
  function resolveTiming(raw) {
61
187
  const enabled = raw?.enabled ?? true;
62
188
  const threshold = raw?.slowRequestThresholdMs;
63
- return threshold === void 0 ? { enabled } : { enabled, slowRequestThresholdMs: threshold };
189
+ return threshold === void 0 ? { enabled } : {
190
+ enabled,
191
+ slowRequestThresholdMs: positiveSafeInteger("timing.slowRequestThresholdMs", threshold)
192
+ };
193
+ }
194
+ var DEFAULT_MAX_BODY_DEPTH = 32;
195
+ function resolveBodyDepth(raw) {
196
+ const maxDepth = positiveSafeInteger(
197
+ "bodyDepth.maxDepth",
198
+ raw?.maxDepth ?? DEFAULT_MAX_BODY_DEPTH
199
+ );
200
+ return { enabled: raw?.enabled ?? false, maxDepth };
64
201
  }
65
202
  function resolveHealth(raw) {
203
+ const readinessCacheMs = raw?.readinessCacheMs ?? DEFAULT_READINESS_CACHE_MS;
204
+ if (!Number.isSafeInteger(readinessCacheMs) || readinessCacheMs < 0) {
205
+ throw new Error(
206
+ `health.readinessCacheMs must be a non-negative safe integer, received ${String(readinessCacheMs)}`
207
+ );
208
+ }
66
209
  return {
210
+ readinessCacheMs,
67
211
  enabled: raw?.enabled ?? true,
68
212
  path: raw?.path ?? DEFAULT_HEALTH_PATH,
69
213
  exposeIndicatorErrors: raw?.exposeIndicatorErrors ?? false,
70
- indicatorTimeoutMs: raw?.indicatorTimeoutMs ?? DEFAULT_INDICATOR_TIMEOUT_MS,
214
+ indicatorTimeoutMs: positiveSafeInteger(
215
+ "health.indicatorTimeoutMs",
216
+ raw?.indicatorTimeoutMs ?? DEFAULT_INDICATOR_TIMEOUT_MS,
217
+ MAX_TIMER_DELAY_MS
218
+ ),
71
219
  autoDiscover: raw?.autoDiscover ?? false
72
220
  };
73
221
  }
@@ -134,6 +282,7 @@ function normalizeCoreOptions(raw) {
134
282
  return deepFreeze({
135
283
  envelope: resolveEnvelope(raw?.envelope),
136
284
  timing: resolveTiming(raw?.timing),
285
+ bodyDepth: resolveBodyDepth(raw?.bodyDepth),
137
286
  health: resolveHealth(raw?.health),
138
287
  metrics: resolveMetrics(raw?.metrics),
139
288
  openapi: resolveOpenApi(raw?.openapi, raw?.environment),
@@ -146,26 +295,6 @@ function normalizeCoreOptions(raw) {
146
295
  }
147
296
  normalizeCoreOptions();
148
297
 
149
- // src/core.tokens.ts
150
- var BYMAX_CORE_OPTIONS = /* @__PURE__ */ Symbol.for("@bymax-one/nest-core:core-options");
151
- var BYMAX_CORRELATION_PROVIDER = /* @__PURE__ */ Symbol.for(
152
- "@bymax-one/nest-core:correlation-provider"
153
- );
154
- var BYMAX_TIMING_SINK = /* @__PURE__ */ Symbol.for("@bymax-one/nest-core:timing-sink");
155
- var BYMAX_HEALTH_INDICATORS = /* @__PURE__ */ Symbol.for(
156
- "@bymax-one/nest-core:health-indicators"
157
- );
158
- var BYMAX_HEALTH_TRANSITION_SINK = /* @__PURE__ */ Symbol.for(
159
- "@bymax-one/nest-core:health-transition-sink"
160
- );
161
- var BYMAX_UNEXPECTED_ERROR_SINK = /* @__PURE__ */ Symbol.for(
162
- "@bymax-one/nest-core:unexpected-error-sink"
163
- );
164
- var BYMAX_METRICS_REGISTRY = /* @__PURE__ */ Symbol.for(
165
- "@bymax-one/nest-core:metrics-registry"
166
- );
167
- var BYMAX_TRACE_CONTEXT = /* @__PURE__ */ Symbol.for("@bymax-one/nest-core:trace-context");
168
-
169
298
  // src/optional-peer.ts
170
299
  function isMissingModuleError(cause) {
171
300
  const code = cause.code;
@@ -273,52 +402,6 @@ function containRejection(returned, onFailure) {
273
402
  }
274
403
  }
275
404
 
276
- // src/envelope/error-codes.ts
277
- var BYMAX_BAD_REQUEST = "BYMAX_BAD_REQUEST";
278
- var BYMAX_VALIDATION_FAILED = "BYMAX_VALIDATION_FAILED";
279
- var BYMAX_UNAUTHORIZED = "BYMAX_UNAUTHORIZED";
280
- var BYMAX_FORBIDDEN = "BYMAX_FORBIDDEN";
281
- var BYMAX_NOT_FOUND = "BYMAX_NOT_FOUND";
282
- var BYMAX_CONFLICT = "BYMAX_CONFLICT";
283
- var BYMAX_PAYLOAD_TOO_LARGE = "BYMAX_PAYLOAD_TOO_LARGE";
284
- var BYMAX_UNSUPPORTED_MEDIA_TYPE = "BYMAX_UNSUPPORTED_MEDIA_TYPE";
285
- var BYMAX_UNPROCESSABLE_ENTITY = "BYMAX_UNPROCESSABLE_ENTITY";
286
- var BYMAX_TOO_MANY_REQUESTS = "BYMAX_TOO_MANY_REQUESTS";
287
- var BYMAX_CLIENT_ERROR = "BYMAX_CLIENT_ERROR";
288
- var BYMAX_INTERNAL_ERROR = "BYMAX_INTERNAL_ERROR";
289
- var BYMAX_NOT_IMPLEMENTED = "BYMAX_NOT_IMPLEMENTED";
290
- var BYMAX_BAD_GATEWAY = "BYMAX_BAD_GATEWAY";
291
- var BYMAX_SERVICE_UNAVAILABLE = "BYMAX_SERVICE_UNAVAILABLE";
292
- var BYMAX_GATEWAY_TIMEOUT = "BYMAX_GATEWAY_TIMEOUT";
293
- var STATUS_CODES = /* @__PURE__ */ new Map([
294
- [400, BYMAX_BAD_REQUEST],
295
- [401, BYMAX_UNAUTHORIZED],
296
- [403, BYMAX_FORBIDDEN],
297
- [404, BYMAX_NOT_FOUND],
298
- [409, BYMAX_CONFLICT],
299
- [413, BYMAX_PAYLOAD_TOO_LARGE],
300
- [415, BYMAX_UNSUPPORTED_MEDIA_TYPE],
301
- [422, BYMAX_UNPROCESSABLE_ENTITY],
302
- [429, BYMAX_TOO_MANY_REQUESTS],
303
- [500, BYMAX_INTERNAL_ERROR],
304
- [501, BYMAX_NOT_IMPLEMENTED],
305
- [502, BYMAX_BAD_GATEWAY],
306
- [503, BYMAX_SERVICE_UNAVAILABLE],
307
- [504, BYMAX_GATEWAY_TIMEOUT]
308
- ]);
309
- var CLIENT_ERROR_MIN = 400;
310
- var CLIENT_ERROR_MAX = 500;
311
- function codeForStatus(status) {
312
- const catalogued = STATUS_CODES.get(status);
313
- if (catalogued !== void 0) {
314
- return catalogued;
315
- }
316
- if (status >= CLIENT_ERROR_MIN && status < CLIENT_ERROR_MAX) {
317
- return BYMAX_CLIENT_ERROR;
318
- }
319
- return BYMAX_INTERNAL_ERROR;
320
- }
321
-
322
405
  // src/envelope/error-envelope.ts
323
406
  function buildErrorEnvelope(input) {
324
407
  const base = {
@@ -791,10 +874,12 @@ function summarizeRejection(reason) {
791
874
  }
792
875
  return `${message.slice(0, MAX_ERROR_MESSAGE_LENGTH - TRUNCATION_ELLIPSIS.length)}${TRUNCATION_ELLIPSIS}`;
793
876
  }
794
- async function runIndicator(indicator, timeoutMs, exposeErrors) {
877
+ function startTimeout(indicator, timeoutMs) {
878
+ const controller = new AbortController();
795
879
  let timer;
796
880
  const timedOut = new Promise((resolve) => {
797
881
  timer = setTimeout(() => {
882
+ controller.abort();
798
883
  resolve({
799
884
  entry: { name: indicator.name, status: "down", details: { timedOutAfterMs: timeoutMs } },
800
885
  isUp: false,
@@ -802,8 +887,12 @@ async function runIndicator(indicator, timeoutMs, exposeErrors) {
802
887
  });
803
888
  }, timeoutMs);
804
889
  });
805
- const checked = Promise.resolve().then(() => indicator.check()).then((result) => {
806
- const entry = { ...result, name: indicator.name };
890
+ return { signal: controller.signal, timedOut, clear: () => clearTimeout(timer) };
891
+ }
892
+ async function runIndicator(indicator, timeoutMs, exposeErrors) {
893
+ const { signal, timedOut, clear } = startTimeout(indicator, timeoutMs);
894
+ const checked = Promise.resolve().then(() => indicator.check(signal)).then((result) => {
895
+ const entry = exposeErrors ? { ...result, name: indicator.name } : { name: indicator.name, status: result.status };
807
896
  return result.status === "up" ? { entry, isUp: true } : { entry, isUp: false, cause: { kind: "reported-down" } };
808
897
  }).catch((reason) => {
809
898
  const message = summarizeRejection(reason);
@@ -816,7 +905,7 @@ async function runIndicator(indicator, timeoutMs, exposeErrors) {
816
905
  try {
817
906
  return await Promise.race([checked, timedOut]);
818
907
  } finally {
819
- clearTimeout(timer);
908
+ clear();
820
909
  }
821
910
  }
822
911
  function describeCause(cause) {
@@ -876,6 +965,8 @@ var HealthService = class {
876
965
  * Comparing states alone would then let the stale observation win.
877
966
  */
878
967
  this.probeSequence = 0;
968
+ /** Set once shutdown begins, after which readiness answers not ready without running anything. */
969
+ this.draining = false;
879
970
  }
880
971
  /**
881
972
  * Report one check's current state, emitting only when it differs from the
@@ -1048,6 +1139,14 @@ var HealthService = class {
1048
1139
  );
1049
1140
  return this.effectiveIndicators;
1050
1141
  }
1142
+ /**
1143
+ * Stop reporting ready the moment shutdown begins, so a load balancer stops routing here while
1144
+ * the process drains. Nothing cached or in progress is consulted again: every later probe is answered
1145
+ * before either is read.
1146
+ */
1147
+ beforeApplicationShutdown() {
1148
+ this.draining = true;
1149
+ }
1051
1150
  /**
1052
1151
  * Liveness check: the process is up and able to respond. Runs no
1053
1152
  * indicators, so it never depends on the health of anything else.
@@ -1062,9 +1161,33 @@ var HealthService = class {
1062
1161
  * aggregate the results. `status` is `'ok'` only when every indicator
1063
1162
  * reports `up`; an empty indicator list is vacuously `'ok'`.
1064
1163
  *
1164
+ * Probes share the work: one that arrives while a run is in progress awaits it, and for
1165
+ * `health.readinessCacheMs` after it ends the answer is returned without running anything.
1166
+ * The endpoint is usually unauthenticated, so probe volume must not set dependency volume.
1167
+ * Once shutdown has begun the answer is always not ready. The returned object is shared between
1168
+ * callers and must not be mutated.
1169
+ *
1170
+ * @returns The aggregated health response.
1171
+ */
1172
+ checkReadiness() {
1173
+ if (this.draining) {
1174
+ return Promise.resolve({ status: "error", checks: [{ name: "shutdown", status: "down" }] });
1175
+ }
1176
+ const cached = this.cached;
1177
+ if (cached !== void 0 && performance.now() - cached.at < this.options.health.readinessCacheMs) {
1178
+ return Promise.resolve(cached.response);
1179
+ }
1180
+ this.inflight ??= this.runReadiness().finally(() => {
1181
+ this.inflight = void 0;
1182
+ });
1183
+ return this.inflight;
1184
+ }
1185
+ /**
1186
+ * Run every indicator once and record the answer.
1187
+ *
1065
1188
  * @returns The aggregated health response.
1066
1189
  */
1067
- async checkReadiness() {
1190
+ async runReadiness() {
1068
1191
  const timeoutMs = this.options.health.indicatorTimeoutMs;
1069
1192
  const exposeErrors = this.options.health.exposeIndicatorErrors;
1070
1193
  const seq = ++this.probeSequence;
@@ -1076,7 +1199,9 @@ var HealthService = class {
1076
1199
  }
1077
1200
  const checks = outcomes.map((outcome) => outcome.entry);
1078
1201
  const status = checks.every((check) => check.status === "up") ? "ok" : "error";
1079
- return { status, checks };
1202
+ const response = { status, checks };
1203
+ this.cached = { at: performance.now(), response };
1204
+ return response;
1080
1205
  }
1081
1206
  };
1082
1207
  HealthService = __decorateClass([
@@ -1695,12 +1820,14 @@ var BymaxCoreModule = class extends BymaxCoreModuleBase {
1695
1820
  * @param consumer - Nest's middleware consumer.
1696
1821
  */
1697
1822
  configure(consumer) {
1698
- if (!this.resolved.timing.enabled) {
1699
- return;
1700
- }
1701
1823
  const adapter = this.adapterHost?.httpAdapter;
1702
- const onFastify = bridgeFastifyRouteMetadata(adapter);
1703
- consumer.apply(BymaxTimingMiddleware).forRoutes(onFastify ? "{*splat}" : "/");
1824
+ if (this.resolved.timing.enabled) {
1825
+ const onFastify = bridgeFastifyRouteMetadata(adapter);
1826
+ consumer.apply(BymaxTimingMiddleware).forRoutes(onFastify ? "{*splat}" : "/");
1827
+ }
1828
+ if (this.resolved.bodyDepth.enabled && adapter?.getType?.() !== "fastify") {
1829
+ consumer.apply(BymaxBodyDepthMiddleware).forRoutes("/");
1830
+ }
1704
1831
  }
1705
1832
  /**
1706
1833
  * Register the module synchronously. Options are known now, so disabled
@@ -1892,4 +2019,4 @@ TimingInterceptor = __decorateClass([
1892
2019
  __decorateParam(3, Inject(BYMAX_TRACE_CONTEXT))
1893
2020
  ], TimingInterceptor);
1894
2021
 
1895
- export { BYMAX_BAD_GATEWAY, BYMAX_BAD_REQUEST, BYMAX_CLIENT_ERROR, BYMAX_CONFLICT, BYMAX_CORE_OPTIONS, BYMAX_CORRELATION_PROVIDER, BYMAX_FORBIDDEN, BYMAX_GATEWAY_TIMEOUT, BYMAX_HEALTH_INDICATORS, BYMAX_HEALTH_TRANSITION_SINK, BYMAX_INTERNAL_ERROR, BYMAX_METRICS_REGISTRY, BYMAX_NOT_FOUND, BYMAX_NOT_IMPLEMENTED, BYMAX_PAYLOAD_TOO_LARGE, BYMAX_SERVICE_UNAVAILABLE, BYMAX_TIMING_SINK, BYMAX_TOO_MANY_REQUESTS, BYMAX_TRACE_CONTEXT, BYMAX_UNAUTHORIZED, BYMAX_UNEXPECTED_ERROR_SINK, BYMAX_UNPROCESSABLE_ENTITY, BYMAX_UNSUPPORTED_MEDIA_TYPE, BYMAX_VALIDATION_FAILED, BymaxCoreModule, BymaxExceptionFilter, BymaxTimingMiddleware, TimingInterceptor, UNMATCHED_ROUTE, buildErrorEnvelope, codeForStatus };
2022
+ export { BYMAX_BAD_GATEWAY, BYMAX_BAD_REQUEST, BYMAX_CLIENT_ERROR, BYMAX_CONFLICT, BYMAX_CORE_OPTIONS, BYMAX_CORRELATION_PROVIDER, BYMAX_FORBIDDEN, BYMAX_GATEWAY_TIMEOUT, BYMAX_HEALTH_INDICATORS, BYMAX_HEALTH_TRANSITION_SINK, BYMAX_INTERNAL_ERROR, BYMAX_METRICS_REGISTRY, BYMAX_NOT_FOUND, BYMAX_NOT_IMPLEMENTED, BYMAX_PAYLOAD_TOO_LARGE, BYMAX_SERVICE_UNAVAILABLE, BYMAX_TIMING_SINK, BYMAX_TOO_MANY_REQUESTS, BYMAX_TRACE_CONTEXT, BYMAX_UNAUTHORIZED, BYMAX_UNEXPECTED_ERROR_SINK, BYMAX_UNPROCESSABLE_ENTITY, BYMAX_UNSUPPORTED_MEDIA_TYPE, BYMAX_VALIDATION_FAILED, BymaxBodyDepthMiddleware, BymaxCoreModule, BymaxExceptionFilter, BymaxTimingMiddleware, TimingInterceptor, UNMATCHED_ROUTE, buildErrorEnvelope, codeForStatus };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bymax-one/nest-core",
3
- "version": "1.6.6",
3
+ "version": "1.6.8",
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",
@@ -63,6 +63,16 @@
63
63
  "default": "./dist/metrics/index.cjs"
64
64
  }
65
65
  },
66
+ "./http": {
67
+ "import": {
68
+ "types": "./dist/http/index.d.ts",
69
+ "default": "./dist/http/index.mjs"
70
+ },
71
+ "require": {
72
+ "types": "./dist/http/index.d.cts",
73
+ "default": "./dist/http/index.cjs"
74
+ }
75
+ },
66
76
  "./openapi": {
67
77
  "import": {
68
78
  "types": "./dist/openapi/index.d.ts",
@@ -77,6 +87,7 @@
77
87
  },
78
88
  "scripts": {
79
89
  "build": "pnpm clean && tsup",
90
+ "check:file-length": "node scripts/check-file-length.mjs",
80
91
  "check:exports": "attw --pack . --profile strict",
81
92
  "check:mutants": "node scripts/check-mutation-directives.mjs",
82
93
  "check:published": "node scripts/check-published-surface.mjs",
@@ -94,8 +105,8 @@
94
105
  "size": "node scripts/check-size.mjs",
95
106
  "test": "jest",
96
107
  "test:cov": "jest --coverage",
97
- "test:cov:all": "jest --config jest.coverage.config.ts --coverage",
98
- "test:e2e": "jest --config jest.e2e.config.ts",
108
+ "test:cov:all": "node --experimental-vm-modules --disable-warning=ExperimentalWarning node_modules/jest/bin/jest.js --config jest.coverage.config.ts --coverage",
109
+ "test:e2e": "node --experimental-vm-modules --disable-warning=ExperimentalWarning node_modules/jest/bin/jest.js --config jest.e2e.config.ts",
99
110
  "test:watch": "jest --watch",
100
111
  "typecheck": "tsc --noEmit"
101
112
  },
@@ -113,6 +124,7 @@
113
124
  "@nestjs/core": "^11.1.18",
114
125
  "@nestjs/swagger": "^11.0.0",
115
126
  "@opentelemetry/api": "^1.9.0",
127
+ "helmet": "^8.0.0",
116
128
  "prom-client": "^15.0.0",
117
129
  "reflect-metadata": "^0.2.0",
118
130
  "rxjs": "^7.0.0"
@@ -124,6 +136,9 @@
124
136
  "@opentelemetry/api": {
125
137
  "optional": true
126
138
  },
139
+ "helmet": {
140
+ "optional": true
141
+ },
127
142
  "prom-client": {
128
143
  "optional": true
129
144
  }
@@ -150,6 +165,8 @@
150
165
  "@types/supertest": "^7.2.0",
151
166
  "@typescript-eslint/eslint-plugin": "^8.59.3",
152
167
  "@typescript-eslint/parser": "^8.59.3",
168
+ "class-transformer": "^0.5.1",
169
+ "class-validator": "^0.15.1",
153
170
  "eslint": "^9.39.4",
154
171
  "eslint-config-prettier": "^10.1.8",
155
172
  "eslint-import-resolver-typescript": "^4.4.4",
@@ -158,6 +175,7 @@
158
175
  "eslint-plugin-security": "^4.0.0",
159
176
  "fastify": "^5.12.5",
160
177
  "globals": "^17.6.0",
178
+ "helmet": "^8.3.0",
161
179
  "husky": "^9.1.7",
162
180
  "jest": "^30.4.2",
163
181
  "lint-staged": "^17.2.0",
@@ -182,6 +200,7 @@
182
200
  "metrics",
183
201
  "openapi",
184
202
  "swagger",
203
+ "helmet",
185
204
  "opentelemetry",
186
205
  "tracing",
187
206
  "typescript"
@@ -209,6 +228,9 @@
209
228
  ],
210
229
  "metrics": [
211
230
  "./dist/metrics/index.d.cts"
231
+ ],
232
+ "http": [
233
+ "./dist/http/index.d.cts"
212
234
  ]
213
235
  }
214
236
  }