@bymax-one/nest-core 1.0.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.
- package/CHANGELOG.md +77 -0
- package/LICENSE +21 -0
- package/README.md +716 -0
- package/dist/health/index.cjs +2 -0
- package/dist/health/index.d.cts +64 -0
- package/dist/health/index.d.ts +64 -0
- package/dist/health/index.mjs +1 -0
- package/dist/index.cjs +984 -0
- package/dist/index.d.cts +550 -0
- package/dist/index.d.ts +550 -0
- package/dist/index.mjs +960 -0
- package/dist/pagination/index.cjs +112 -0
- package/dist/pagination/index.d.cts +146 -0
- package/dist/pagination/index.d.ts +146 -0
- package/dist/pagination/index.mjs +105 -0
- package/package.json +169 -0
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,550 @@
|
|
|
1
|
+
import * as _nestjs_common from '@nestjs/common';
|
|
2
|
+
import { DynamicModule, ExceptionFilter, ArgumentsHost, NestInterceptor, ExecutionContext, CallHandler } from '@nestjs/common';
|
|
3
|
+
import { HttpAdapterHost } from '@nestjs/core';
|
|
4
|
+
import { Observable } from 'rxjs';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* @fileoverview Public configuration surface for `BymaxCoreModule` plus the
|
|
8
|
+
* resolution pipeline that merges consumer options over the documented defaults
|
|
9
|
+
* and deep-freezes the result. Every feature reads its effective configuration
|
|
10
|
+
* from the resolved snapshot, never from the raw consumer input.
|
|
11
|
+
* @layer Config
|
|
12
|
+
*/
|
|
13
|
+
/** Error-envelope exception-filter configuration. */
|
|
14
|
+
interface EnvelopeOptions {
|
|
15
|
+
/** Register the global exception filter. Default: `true`. */
|
|
16
|
+
enabled?: boolean;
|
|
17
|
+
/**
|
|
18
|
+
* Include the original message and stack of unknown errors in the envelope
|
|
19
|
+
* details. Never enable in production. Default: `false`.
|
|
20
|
+
*/
|
|
21
|
+
exposeInternals?: boolean;
|
|
22
|
+
}
|
|
23
|
+
/** Request-timing interceptor configuration. */
|
|
24
|
+
interface TimingOptions {
|
|
25
|
+
/** Register the timing interceptor. Default: `true`. */
|
|
26
|
+
enabled?: boolean;
|
|
27
|
+
/** Samples above this threshold are flagged as slow. No default (unset). */
|
|
28
|
+
slowRequestThresholdMs?: number;
|
|
29
|
+
}
|
|
30
|
+
/** Liveness and readiness endpoint configuration. */
|
|
31
|
+
interface HealthOptions {
|
|
32
|
+
/** Register the health controller. Default: `true`. */
|
|
33
|
+
enabled?: boolean;
|
|
34
|
+
/** Route prefix for the health endpoints. Default: `'health'`. */
|
|
35
|
+
path?: string;
|
|
36
|
+
/** Per-indicator timeout before a check is reported as down. Default: `5000`. */
|
|
37
|
+
indicatorTimeoutMs?: number;
|
|
38
|
+
}
|
|
39
|
+
/** Prometheus metrics endpoint configuration. */
|
|
40
|
+
interface MetricsOptions {
|
|
41
|
+
/** Register the metrics controller. Default: `false`. */
|
|
42
|
+
enabled?: boolean;
|
|
43
|
+
/** Route for the metrics endpoint. Default: `'metrics'`. */
|
|
44
|
+
path?: string;
|
|
45
|
+
/** Static labels added to every metric. Default: `{}`. */
|
|
46
|
+
defaultLabels?: Record<string, string>;
|
|
47
|
+
/** Collect `prom-client` default process metrics. Default: `true`. */
|
|
48
|
+
collectDefaultMetrics?: boolean;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Consumer-facing options for `BymaxCoreModule.forRoot` / `forRootAsync`. Every
|
|
52
|
+
* block is optional; omitted values fall back to the documented defaults.
|
|
53
|
+
*/
|
|
54
|
+
interface BymaxCoreModuleOptions {
|
|
55
|
+
/** Error-envelope exception filter. Default: enabled. */
|
|
56
|
+
envelope?: EnvelopeOptions;
|
|
57
|
+
/** Request timing interceptor. Default: enabled. */
|
|
58
|
+
timing?: TimingOptions;
|
|
59
|
+
/** Liveness and readiness endpoints. Default: enabled. */
|
|
60
|
+
health?: HealthOptions;
|
|
61
|
+
/** Prometheus metrics endpoint. Default: disabled. */
|
|
62
|
+
metrics?: MetricsOptions;
|
|
63
|
+
}
|
|
64
|
+
/** Fully-resolved envelope options. */
|
|
65
|
+
interface ResolvedEnvelopeOptions {
|
|
66
|
+
enabled: boolean;
|
|
67
|
+
exposeInternals: boolean;
|
|
68
|
+
}
|
|
69
|
+
/** Fully-resolved timing options. `slowRequestThresholdMs` stays absent when unset. */
|
|
70
|
+
interface ResolvedTimingOptions {
|
|
71
|
+
enabled: boolean;
|
|
72
|
+
slowRequestThresholdMs?: number;
|
|
73
|
+
}
|
|
74
|
+
/** Fully-resolved health options. */
|
|
75
|
+
interface ResolvedHealthOptions {
|
|
76
|
+
enabled: boolean;
|
|
77
|
+
path: string;
|
|
78
|
+
indicatorTimeoutMs: number;
|
|
79
|
+
}
|
|
80
|
+
/** Fully-resolved metrics options. */
|
|
81
|
+
interface ResolvedMetricsOptions {
|
|
82
|
+
enabled: boolean;
|
|
83
|
+
path: string;
|
|
84
|
+
collectDefaultMetrics: boolean;
|
|
85
|
+
defaultLabels: Record<string, string>;
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* The effective, defaults-applied configuration exposed under
|
|
89
|
+
* `BYMAX_CORE_OPTIONS`. Fields with a documented default are always present;
|
|
90
|
+
* the only optional field is `timing.slowRequestThresholdMs`, which has no
|
|
91
|
+
* default and is absent unless the consumer sets it.
|
|
92
|
+
*/
|
|
93
|
+
interface ResolvedCoreOptions {
|
|
94
|
+
envelope: ResolvedEnvelopeOptions;
|
|
95
|
+
timing: ResolvedTimingOptions;
|
|
96
|
+
health: ResolvedHealthOptions;
|
|
97
|
+
metrics: ResolvedMetricsOptions;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/** Non-option extras accepted by `forRoot` / `forRootAsync`. */
|
|
101
|
+
interface BymaxCoreModuleExtras {
|
|
102
|
+
/** Register the module globally. Default: `true`. */
|
|
103
|
+
isGlobal?: boolean;
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Builder-generated base. `BUILDER_OPTIONS_TOKEN` carries the RAW consumer
|
|
107
|
+
* options and stays internal; the public, defaults-applied snapshot is exposed
|
|
108
|
+
* separately under {@link BYMAX_CORE_OPTIONS}. `isGlobal` (default `true`) maps
|
|
109
|
+
* to `DynamicModule.global`, replacing a manual `@Global()` decorator.
|
|
110
|
+
*/
|
|
111
|
+
declare const BymaxCoreModuleBase: _nestjs_common.ConfigurableModuleCls<BymaxCoreModuleOptions, "forRoot", "create", BymaxCoreModuleExtras>;
|
|
112
|
+
declare const OPTIONS_TYPE: BymaxCoreModuleOptions & Partial<BymaxCoreModuleExtras>;
|
|
113
|
+
declare const ASYNC_OPTIONS_TYPE: _nestjs_common.ConfigurableModuleAsyncOptions<BymaxCoreModuleOptions, "create"> & Partial<BymaxCoreModuleExtras>;
|
|
114
|
+
/**
|
|
115
|
+
* `BymaxCoreModule`, the application foundation module for NestJS 11.
|
|
116
|
+
*/
|
|
117
|
+
declare class BymaxCoreModule extends BymaxCoreModuleBase {
|
|
118
|
+
/**
|
|
119
|
+
* Register the module synchronously. Options are known now, so disabled
|
|
120
|
+
* features are omitted from the providers and controllers arrays and the
|
|
121
|
+
* resolved snapshot is provided under {@link BYMAX_CORE_OPTIONS}.
|
|
122
|
+
*
|
|
123
|
+
* @param options - Core options plus the optional `isGlobal` extra. Omit for
|
|
124
|
+
* all documented defaults.
|
|
125
|
+
* @returns The configured `DynamicModule`.
|
|
126
|
+
* @example
|
|
127
|
+
* BymaxCoreModule.forRoot({ metrics: { enabled: true } })
|
|
128
|
+
*/
|
|
129
|
+
static forRoot(options?: typeof OPTIONS_TYPE): DynamicModule;
|
|
130
|
+
/**
|
|
131
|
+
* Register the module asynchronously. The resolved options are produced by
|
|
132
|
+
* the consumer's factory and normalized under {@link BYMAX_CORE_OPTIONS}.
|
|
133
|
+
* Because those options are unknown when the module is defined, the pipeline
|
|
134
|
+
* slots register unconditionally and gate at runtime with transparent
|
|
135
|
+
* pass-throughs. The health controller cannot register conditionally either,
|
|
136
|
+
* since its route metadata is fixed before the async options resolve: it is
|
|
137
|
+
* always registered at the default health path, and its handlers guard
|
|
138
|
+
* every request against the resolved options being disabled or requesting a
|
|
139
|
+
* different path, throwing a descriptive configuration error in either case.
|
|
140
|
+
* The metrics controller follows the same mechanism at the default metrics
|
|
141
|
+
* path; the `BYMAX_METRICS_REGISTRY` factory gates on the resolved options and
|
|
142
|
+
* resolves to a guarded placeholder when metrics are disabled, so the optional
|
|
143
|
+
* peer `prom-client` is never loaded unless metrics are actually enabled.
|
|
144
|
+
*
|
|
145
|
+
* @param options - Async options (factory + inject + imports, or class).
|
|
146
|
+
* @returns The configured `DynamicModule`.
|
|
147
|
+
* @example
|
|
148
|
+
* BymaxCoreModule.forRootAsync({ inject: [Config], useFactory: (c) => ({ ... }) })
|
|
149
|
+
*/
|
|
150
|
+
static forRootAsync(options: typeof ASYNC_OPTIONS_TYPE): DynamicModule;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* @fileoverview Dependency-injection tokens for `@bymax-one/nest-core`.
|
|
155
|
+
* Every token is a `Symbol`, so the container never collides with a consumer's
|
|
156
|
+
* string tokens and the public contracts stay explicit at every injection site.
|
|
157
|
+
* @layer Constants
|
|
158
|
+
*/
|
|
159
|
+
/**
|
|
160
|
+
* Provide the resolved, deep-frozen {@link ResolvedCoreOptions} snapshot.
|
|
161
|
+
* Consumers inject this to read the effective configuration after defaults.
|
|
162
|
+
*/
|
|
163
|
+
declare const BYMAX_CORE_OPTIONS: unique symbol;
|
|
164
|
+
/**
|
|
165
|
+
* Provide the `ICorrelationIdProvider` used to stamp the current correlation id
|
|
166
|
+
* onto error envelopes. Defaults to a no-op that returns `undefined`.
|
|
167
|
+
*/
|
|
168
|
+
declare const BYMAX_CORRELATION_PROVIDER: unique symbol;
|
|
169
|
+
/**
|
|
170
|
+
* Provide the `ITimingSink` that receives one sample per completed request.
|
|
171
|
+
* Defaults to a no-op sink.
|
|
172
|
+
*/
|
|
173
|
+
declare const BYMAX_TIMING_SINK: unique symbol;
|
|
174
|
+
/**
|
|
175
|
+
* Provide the array of health indicators aggregated by the health endpoints.
|
|
176
|
+
* Defaults to an empty array.
|
|
177
|
+
*/
|
|
178
|
+
declare const BYMAX_HEALTH_INDICATORS: unique symbol;
|
|
179
|
+
/**
|
|
180
|
+
* Provide the `prom-client` `Registry` backing the metrics endpoint. Bound
|
|
181
|
+
* lazily and only when the metrics feature is enabled.
|
|
182
|
+
*/
|
|
183
|
+
declare const BYMAX_METRICS_REGISTRY: unique symbol;
|
|
184
|
+
|
|
185
|
+
/**
|
|
186
|
+
* @fileoverview Correlation-id contract consumed by the error envelope. The
|
|
187
|
+
* filter resolves this provider to stamp the current request's correlation id
|
|
188
|
+
* onto every error response. The default binding is a no-op; any implementation
|
|
189
|
+
* (for example the AsyncLocalStorage-based log context of `@bymax-one/nest-logger`)
|
|
190
|
+
* plugs in through the `BYMAX_CORRELATION_PROVIDER` token with no hard coupling.
|
|
191
|
+
* @layer Contract
|
|
192
|
+
*/
|
|
193
|
+
/**
|
|
194
|
+
* Resolve the correlation id for the current execution context.
|
|
195
|
+
*/
|
|
196
|
+
interface ICorrelationIdProvider {
|
|
197
|
+
/**
|
|
198
|
+
* Return the correlation id for the current execution context.
|
|
199
|
+
*
|
|
200
|
+
* @returns The correlation id, or `undefined` when none is bound.
|
|
201
|
+
*/
|
|
202
|
+
getCorrelationId(): string | undefined;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
/**
|
|
206
|
+
* Neutral view of the current request handed to {@link BymaxExceptionFilter}
|
|
207
|
+
* mappers and to the {@link BymaxExceptionFilter.onUnexpectedError} seam. It
|
|
208
|
+
* exposes only the framework-agnostic surface (path, method, correlation id).
|
|
209
|
+
*/
|
|
210
|
+
interface FilterErrorContext {
|
|
211
|
+
/** HTTP method, read through the adapter (Express and Fastify neutral). */
|
|
212
|
+
readonly method: string;
|
|
213
|
+
/** Request URL path, read through the adapter (Express and Fastify neutral). */
|
|
214
|
+
readonly path: string;
|
|
215
|
+
/** Correlation id for the current request; absent when no provider resolves one. */
|
|
216
|
+
readonly correlationId?: string;
|
|
217
|
+
}
|
|
218
|
+
/**
|
|
219
|
+
* Global exception filter emitting the stable error envelope. Registered as the
|
|
220
|
+
* outermost `APP_FILTER` when the envelope feature is enabled, so it formats
|
|
221
|
+
* every error that escapes a handler, including those thrown by other filters.
|
|
222
|
+
*/
|
|
223
|
+
declare class BymaxExceptionFilter implements ExceptionFilter {
|
|
224
|
+
private readonly options;
|
|
225
|
+
private readonly adapterHost;
|
|
226
|
+
/** Clock used to stamp the envelope timestamp; overridable in tests via fake timers. */
|
|
227
|
+
private readonly now;
|
|
228
|
+
/** The resolved correlation provider, or the no-op fallback when none is bound. */
|
|
229
|
+
private readonly correlation;
|
|
230
|
+
/**
|
|
231
|
+
* @param options - Resolved core options; drives the `exposeInternals` switch.
|
|
232
|
+
* @param correlation - Provider resolving the current request's correlation id.
|
|
233
|
+
* Injected with `@Optional()`: `BymaxCoreModule` binds no local default for
|
|
234
|
+
* this token, so a consumer's own `BYMAX_CORRELATION_PROVIDER` binding
|
|
235
|
+
* (from their own, globally-visible module) is not shadowed by one; when
|
|
236
|
+
* nothing is bound, this falls back to a no-op that omits `correlationId`.
|
|
237
|
+
* @param adapterHost - Host of the live HTTP adapter, resolved lazily per catch.
|
|
238
|
+
*/
|
|
239
|
+
constructor(options: ResolvedCoreOptions, correlation: ICorrelationIdProvider | undefined, adapterHost: HttpAdapterHost);
|
|
240
|
+
/**
|
|
241
|
+
* Format the exception into the stable envelope and reply with it.
|
|
242
|
+
*
|
|
243
|
+
* Non-HTTP execution contexts (GraphQL, RPC) are out of scope and are
|
|
244
|
+
* rethrown untouched so their own error handling applies.
|
|
245
|
+
*
|
|
246
|
+
* @param exception - The error that escaped the handler.
|
|
247
|
+
* @param host - The arguments host for the current execution context.
|
|
248
|
+
*/
|
|
249
|
+
catch(exception: unknown, host: ArgumentsHost): void;
|
|
250
|
+
/**
|
|
251
|
+
* Select the mapping rule for the exception and build its envelope. An
|
|
252
|
+
* unknown error is handed to the observability seam before it collapses, so
|
|
253
|
+
* an integration can record the original error with the request context.
|
|
254
|
+
*
|
|
255
|
+
* @param exception - The error that escaped the handler.
|
|
256
|
+
* @param context - The neutral request context.
|
|
257
|
+
* @returns The formatted envelope.
|
|
258
|
+
*/
|
|
259
|
+
private buildEnvelope;
|
|
260
|
+
/**
|
|
261
|
+
* Map an `HttpException` to the envelope. Explicit domain codes pass through;
|
|
262
|
+
* the validation shape becomes `BYMAX_VALIDATION_FAILED` with structured
|
|
263
|
+
* details; everything else derives its code from the status.
|
|
264
|
+
*
|
|
265
|
+
* @param exception - The HTTP exception to format.
|
|
266
|
+
* @param context - The neutral request context.
|
|
267
|
+
* @returns The formatted envelope.
|
|
268
|
+
*/
|
|
269
|
+
private mapHttpException;
|
|
270
|
+
/**
|
|
271
|
+
* Collapse an unknown error to the fixed, production-safe 500. The original
|
|
272
|
+
* error is never serialized unless `exposeInternals` is on, in which case its
|
|
273
|
+
* message and stack are attached to `details` (development only).
|
|
274
|
+
*
|
|
275
|
+
* @param exception - The original thrown value.
|
|
276
|
+
* @param context - The neutral request context.
|
|
277
|
+
* @returns The generic internal-error envelope.
|
|
278
|
+
*/
|
|
279
|
+
private mapUnknown;
|
|
280
|
+
/**
|
|
281
|
+
* Assemble the envelope through the pure builder, threading the shared clock
|
|
282
|
+
* and omitting absent optional details.
|
|
283
|
+
*
|
|
284
|
+
* @param statusCode - HTTP status for the envelope.
|
|
285
|
+
* @param code - Stable machine-readable code.
|
|
286
|
+
* @param message - Human-readable, end-user-safe message.
|
|
287
|
+
* @param context - The neutral request context.
|
|
288
|
+
* @param details - Optional structured context; omitted when absent.
|
|
289
|
+
* @returns The formatted envelope.
|
|
290
|
+
*/
|
|
291
|
+
private toEnvelope;
|
|
292
|
+
/**
|
|
293
|
+
* Observability seam invoked for every unexpected (non-`HttpException`) error
|
|
294
|
+
* before it collapses to the generic 500. The base implementation is a no-op:
|
|
295
|
+
* this library owns no logger. An integration (for example
|
|
296
|
+
* `@bymax-one/nest-logger`) subclasses the filter and overrides this to record
|
|
297
|
+
* the original error with the current request context and correlation id.
|
|
298
|
+
* Overrides must never throw and must never write to the response.
|
|
299
|
+
*
|
|
300
|
+
* @param _error - The original thrown value.
|
|
301
|
+
* @param _context - The neutral request context.
|
|
302
|
+
*/
|
|
303
|
+
protected onUnexpectedError(_error: unknown, _context: FilterErrorContext): void;
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
/**
|
|
307
|
+
* @fileoverview The stable error-envelope contract and its pure builder. Every
|
|
308
|
+
* error leaving a `@bymax-one/nest-core` application is serialized into this
|
|
309
|
+
* shape, which is versioned with the package: adding an optional field is a
|
|
310
|
+
* minor release, changing or removing a field is a major release. The builder
|
|
311
|
+
* omits absent optional fields entirely so they never surface as `undefined`
|
|
312
|
+
* keys in the serialized JSON.
|
|
313
|
+
* @layer DTO
|
|
314
|
+
*/
|
|
315
|
+
/**
|
|
316
|
+
* Structured error context attached to an envelope. Validation failures use the
|
|
317
|
+
* array form (one entry per violation); the development-only internals dump uses
|
|
318
|
+
* the object form. The contract keeps it deliberately open: array or object.
|
|
319
|
+
*/
|
|
320
|
+
type ErrorDetails = readonly unknown[] | Readonly<Record<string, unknown>>;
|
|
321
|
+
/**
|
|
322
|
+
* The exact shape of every error response. Field presence is part of the
|
|
323
|
+
* contract:
|
|
324
|
+
*
|
|
325
|
+
* - `statusCode`, `code`, `message`, `timestamp`, and `path` are always present.
|
|
326
|
+
* - `details` is present only when structured context exists (validation issues
|
|
327
|
+
* or, in development, the collapsed internal error).
|
|
328
|
+
* - `correlationId` is present only when a correlation provider resolves an id.
|
|
329
|
+
*/
|
|
330
|
+
interface ErrorEnvelope {
|
|
331
|
+
/** HTTP status code of the response. Always present. */
|
|
332
|
+
readonly statusCode: number;
|
|
333
|
+
/** Stable machine-readable code from the `BYMAX_*` catalog or a passed-through domain code. Always present. */
|
|
334
|
+
readonly code: string;
|
|
335
|
+
/** Human-readable message, safe to show end users. Always present. */
|
|
336
|
+
readonly message: string;
|
|
337
|
+
/** Structured context, such as validation issues. Present only when it exists. */
|
|
338
|
+
readonly details?: ErrorDetails;
|
|
339
|
+
/** Correlation id for the current request. Present only when a provider resolves one. */
|
|
340
|
+
readonly correlationId?: string;
|
|
341
|
+
/** ISO 8601 instant the error was formatted. Always present. */
|
|
342
|
+
readonly timestamp: string;
|
|
343
|
+
/** Request URL path. Always present. */
|
|
344
|
+
readonly path: string;
|
|
345
|
+
}
|
|
346
|
+
/**
|
|
347
|
+
* Primitive inputs the builder assembles into an {@link ErrorEnvelope}. The
|
|
348
|
+
* clock is injected as `now` so callers (and tests) control the timestamp
|
|
349
|
+
* without patching global time.
|
|
350
|
+
*/
|
|
351
|
+
interface BuildErrorEnvelopeInput {
|
|
352
|
+
/** HTTP status code of the response. */
|
|
353
|
+
readonly statusCode: number;
|
|
354
|
+
/** Stable machine-readable code. */
|
|
355
|
+
readonly code: string;
|
|
356
|
+
/** Human-readable, end-user-safe message. */
|
|
357
|
+
readonly message: string;
|
|
358
|
+
/** Structured context. Omit when there is none; never pass `undefined`. */
|
|
359
|
+
readonly details?: ErrorDetails;
|
|
360
|
+
/** Correlation id. Omit when none is bound; never pass `undefined`. */
|
|
361
|
+
readonly correlationId?: string;
|
|
362
|
+
/** Request URL path. */
|
|
363
|
+
readonly path: string;
|
|
364
|
+
/** Injectable clock; called once to stamp the ISO 8601 timestamp. */
|
|
365
|
+
readonly now: () => Date;
|
|
366
|
+
}
|
|
367
|
+
/**
|
|
368
|
+
* Assemble an {@link ErrorEnvelope} from primitive inputs.
|
|
369
|
+
*
|
|
370
|
+
* Pure: the only side effect is calling `now()` once for the timestamp. Absent
|
|
371
|
+
* optional fields (`details`, `correlationId`) are omitted from the returned
|
|
372
|
+
* object rather than set to `undefined`, so `JSON.stringify` never emits them.
|
|
373
|
+
*
|
|
374
|
+
* @param input - The envelope fields plus the injectable clock.
|
|
375
|
+
* @returns A fully-formed envelope with no `undefined`-valued keys.
|
|
376
|
+
*/
|
|
377
|
+
declare function buildErrorEnvelope(input: BuildErrorEnvelopeInput): ErrorEnvelope;
|
|
378
|
+
|
|
379
|
+
/**
|
|
380
|
+
* @fileoverview Monotonic clock seam for request-timing duration math. Wall-clock
|
|
381
|
+
* date sources (`Date.now()`, `new Date()`) are banned for measuring elapsed
|
|
382
|
+
* time anywhere in this feature: the system clock can jump backward or forward
|
|
383
|
+
* (NTP adjustments, leap seconds, manual changes), which would corrupt a
|
|
384
|
+
* duration measurement. Every elapsed-time computation in `src/timing/` reads
|
|
385
|
+
* from this seam instead, so the interceptor stays testable with a stub clock
|
|
386
|
+
* that advances by controlled amounts.
|
|
387
|
+
* @layer Utility
|
|
388
|
+
*/
|
|
389
|
+
/** A source of monotonically increasing timestamps, in milliseconds. */
|
|
390
|
+
interface MonotonicClock {
|
|
391
|
+
/**
|
|
392
|
+
* Read the current monotonic timestamp.
|
|
393
|
+
*
|
|
394
|
+
* @returns Milliseconds from a monotonic, ever-increasing source. Only
|
|
395
|
+
* differences between two calls are meaningful; the absolute value carries
|
|
396
|
+
* no calendar significance.
|
|
397
|
+
*/
|
|
398
|
+
now(): number;
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
/**
|
|
402
|
+
* @fileoverview Request-timing contracts. The timing interceptor emits one
|
|
403
|
+
* {@link RequestTimingSample} per completed request to the bound
|
|
404
|
+
* {@link ITimingSink}. The default sink is a no-op; consumers plug in a logger
|
|
405
|
+
* bridge or the metrics bridge through the `BYMAX_TIMING_SINK` token.
|
|
406
|
+
* @layer Contract
|
|
407
|
+
*/
|
|
408
|
+
/**
|
|
409
|
+
* A single request-timing measurement. The route template (not the raw URL) is
|
|
410
|
+
* used to keep cardinality bounded for downstream metric sinks.
|
|
411
|
+
*/
|
|
412
|
+
interface RequestTimingSample {
|
|
413
|
+
/** HTTP method, for example `"GET"`. */
|
|
414
|
+
method: string;
|
|
415
|
+
/** Route template, for example `"/invoices/:id"` (not the raw URL). */
|
|
416
|
+
route: string;
|
|
417
|
+
/** Final HTTP status, including error statuses. */
|
|
418
|
+
statusCode: number;
|
|
419
|
+
/** Wall-clock duration from a monotonic clock, in milliseconds. */
|
|
420
|
+
durationMs: number;
|
|
421
|
+
/** Whether the sample exceeded the configured slow-request threshold. */
|
|
422
|
+
slow: boolean;
|
|
423
|
+
}
|
|
424
|
+
/**
|
|
425
|
+
* Receive request-timing samples. Implementations must never throw: a sink
|
|
426
|
+
* failure is caught and silenced by the interceptor so timing never breaks a
|
|
427
|
+
* request.
|
|
428
|
+
*/
|
|
429
|
+
interface ITimingSink {
|
|
430
|
+
/**
|
|
431
|
+
* Record one sample for a completed request.
|
|
432
|
+
*
|
|
433
|
+
* @param sample - The timing sample to record.
|
|
434
|
+
*/
|
|
435
|
+
record(sample: RequestTimingSample): void;
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
/**
|
|
439
|
+
* Request-timing interceptor. Registered as the `APP_INTERCEPTOR` when the
|
|
440
|
+
* timing feature is enabled, on both the sync and async registration paths.
|
|
441
|
+
* Non-HTTP execution contexts (GraphQL, RPC) pass through untouched: this
|
|
442
|
+
* feature is HTTP-first, matching the exception filter's documented scope.
|
|
443
|
+
*/
|
|
444
|
+
declare class TimingInterceptor implements NestInterceptor {
|
|
445
|
+
private readonly options;
|
|
446
|
+
private readonly clock;
|
|
447
|
+
/** The bound timing sink, or the no-op fallback when none resolves. */
|
|
448
|
+
private readonly sink;
|
|
449
|
+
/**
|
|
450
|
+
* @param options - Resolved core options; supplies `slowRequestThresholdMs`.
|
|
451
|
+
* @param sink - The bound timing sink; its `record` failures are swallowed.
|
|
452
|
+
* Injected with `@Optional()`: `BymaxCoreModule` binds no local default for
|
|
453
|
+
* this token on the sync path when the metrics bridge is not registered, so
|
|
454
|
+
* a consumer's own `BYMAX_TIMING_SINK` binding is not shadowed by one; when
|
|
455
|
+
* nothing resolves, this falls back to a no-op sink.
|
|
456
|
+
* @param clock - Monotonic clock seam; defaults to `performance.now()`, and
|
|
457
|
+
* is bound explicitly through {@link BYMAX_TIMING_CLOCK} so tests inject a
|
|
458
|
+
* stub advancing by controlled amounts.
|
|
459
|
+
*/
|
|
460
|
+
constructor(options: ResolvedCoreOptions, sink: ITimingSink | undefined, clock?: MonotonicClock);
|
|
461
|
+
/**
|
|
462
|
+
* Measure the handler chain and record exactly one sample per completed
|
|
463
|
+
* request, on the success path and on the error path alike.
|
|
464
|
+
*
|
|
465
|
+
* @param context - The execution context of the current request.
|
|
466
|
+
* @param next - The next handler in the chain.
|
|
467
|
+
* @returns The downstream response stream, unmodified beyond the measurement.
|
|
468
|
+
*/
|
|
469
|
+
intercept(context: ExecutionContext, next: CallHandler): Observable<unknown>;
|
|
470
|
+
/**
|
|
471
|
+
* Read the final status code from the response object on the success path.
|
|
472
|
+
*
|
|
473
|
+
* @param context - The execution context of the current request.
|
|
474
|
+
* @returns The response's status code, or the default success status when absent.
|
|
475
|
+
*/
|
|
476
|
+
private readSuccessStatus;
|
|
477
|
+
/**
|
|
478
|
+
* Derive the final status code for an error that escaped the handler.
|
|
479
|
+
*
|
|
480
|
+
* @param error - The error propagated by the handler chain.
|
|
481
|
+
* @returns The `HttpException` status, or the generic 500 for anything else.
|
|
482
|
+
*/
|
|
483
|
+
private readErrorStatus;
|
|
484
|
+
/**
|
|
485
|
+
* Build the sample, compute the slow flag, and deliver it to the sink inside
|
|
486
|
+
* a try/catch that silences any failure: a throwing sink must never affect
|
|
487
|
+
* the request it is observing.
|
|
488
|
+
*
|
|
489
|
+
* @param method - HTTP method of the request.
|
|
490
|
+
* @param route - Route template of the request.
|
|
491
|
+
* @param statusCode - Final status code, success or error.
|
|
492
|
+
* @param start - Monotonic start timestamp captured before the handler ran.
|
|
493
|
+
*/
|
|
494
|
+
private recordSample;
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
/**
|
|
498
|
+
* @fileoverview Stable `BYMAX_*` error-code catalog and HTTP-status derivation.
|
|
499
|
+
* These codes are the machine-readable half of the error envelope contract and
|
|
500
|
+
* are shared by the exception filter and the cursor codec, so they live in the
|
|
501
|
+
* foundation rather than in any single feature.
|
|
502
|
+
* @layer Constants
|
|
503
|
+
*/
|
|
504
|
+
/** Generic malformed-request code for HTTP 400. */
|
|
505
|
+
declare const BYMAX_BAD_REQUEST = "BYMAX_BAD_REQUEST";
|
|
506
|
+
/** Code emitted when a request fails structured validation. */
|
|
507
|
+
declare const BYMAX_VALIDATION_FAILED = "BYMAX_VALIDATION_FAILED";
|
|
508
|
+
/** Missing or invalid authentication code for HTTP 401. */
|
|
509
|
+
declare const BYMAX_UNAUTHORIZED = "BYMAX_UNAUTHORIZED";
|
|
510
|
+
/** Authenticated-but-not-allowed code for HTTP 403. */
|
|
511
|
+
declare const BYMAX_FORBIDDEN = "BYMAX_FORBIDDEN";
|
|
512
|
+
/** Unknown-resource code for HTTP 404. */
|
|
513
|
+
declare const BYMAX_NOT_FOUND = "BYMAX_NOT_FOUND";
|
|
514
|
+
/** State-conflict code for HTTP 409. */
|
|
515
|
+
declare const BYMAX_CONFLICT = "BYMAX_CONFLICT";
|
|
516
|
+
/** Oversized-payload code for HTTP 413. */
|
|
517
|
+
declare const BYMAX_PAYLOAD_TOO_LARGE = "BYMAX_PAYLOAD_TOO_LARGE";
|
|
518
|
+
/** Unsupported-content-type code for HTTP 415. */
|
|
519
|
+
declare const BYMAX_UNSUPPORTED_MEDIA_TYPE = "BYMAX_UNSUPPORTED_MEDIA_TYPE";
|
|
520
|
+
/** Semantically-invalid-entity code for HTTP 422. */
|
|
521
|
+
declare const BYMAX_UNPROCESSABLE_ENTITY = "BYMAX_UNPROCESSABLE_ENTITY";
|
|
522
|
+
/** Rate-limit code for HTTP 429. */
|
|
523
|
+
declare const BYMAX_TOO_MANY_REQUESTS = "BYMAX_TOO_MANY_REQUESTS";
|
|
524
|
+
/** Generic client-error fallback for any uncatalogued 4xx status. */
|
|
525
|
+
declare const BYMAX_CLIENT_ERROR = "BYMAX_CLIENT_ERROR";
|
|
526
|
+
/** Internal-failure code for HTTP 500 and any uncatalogued non-4xx status. */
|
|
527
|
+
declare const BYMAX_INTERNAL_ERROR = "BYMAX_INTERNAL_ERROR";
|
|
528
|
+
/** Unimplemented-handler code for HTTP 501. */
|
|
529
|
+
declare const BYMAX_NOT_IMPLEMENTED = "BYMAX_NOT_IMPLEMENTED";
|
|
530
|
+
/** Upstream-failure code for HTTP 502. */
|
|
531
|
+
declare const BYMAX_BAD_GATEWAY = "BYMAX_BAD_GATEWAY";
|
|
532
|
+
/** Temporary-unavailability code for HTTP 503. */
|
|
533
|
+
declare const BYMAX_SERVICE_UNAVAILABLE = "BYMAX_SERVICE_UNAVAILABLE";
|
|
534
|
+
/** Upstream-timeout code for HTTP 504. */
|
|
535
|
+
declare const BYMAX_GATEWAY_TIMEOUT = "BYMAX_GATEWAY_TIMEOUT";
|
|
536
|
+
/**
|
|
537
|
+
* Derive the stable `BYMAX_*` code for an HTTP status when a thrown exception
|
|
538
|
+
* carries no explicit `code`.
|
|
539
|
+
*
|
|
540
|
+
* The validation code is never derived here: whether a 400 is a validation
|
|
541
|
+
* failure is a shape decision made by the exception filter, so status 400
|
|
542
|
+
* resolves to {@link BYMAX_BAD_REQUEST}.
|
|
543
|
+
*
|
|
544
|
+
* @param status - The HTTP status code of the response.
|
|
545
|
+
* @returns The catalogued code, or the client/internal fallback for any status
|
|
546
|
+
* without a dedicated row.
|
|
547
|
+
*/
|
|
548
|
+
declare function codeForStatus(status: number): string;
|
|
549
|
+
|
|
550
|
+
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_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_UNAUTHORIZED, BYMAX_UNPROCESSABLE_ENTITY, BYMAX_UNSUPPORTED_MEDIA_TYPE, BYMAX_VALIDATION_FAILED, type BuildErrorEnvelopeInput, BymaxCoreModule, type BymaxCoreModuleOptions, BymaxExceptionFilter, type EnvelopeOptions, type ErrorDetails, type ErrorEnvelope, type FilterErrorContext, type HealthOptions, type ICorrelationIdProvider, type ITimingSink, type MetricsOptions, type RequestTimingSample, type ResolvedCoreOptions, TimingInterceptor, type TimingOptions, buildErrorEnvelope, codeForStatus };
|