@bymax-one/nest-core 1.3.1 → 1.4.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 +251 -1
- package/README.md +418 -48
- package/dist/index.cjs +344 -163
- package/dist/index.d.cts +327 -18
- package/dist/index.d.ts +327 -18
- package/dist/index.mjs +345 -165
- package/dist/openapi/index.cjs +401 -15
- package/dist/openapi/index.d.cts +159 -4
- package/dist/openapi/index.d.ts +159 -4
- package/dist/openapi/index.mjs +400 -17
- package/package.json +3 -1
package/dist/index.d.cts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as _nestjs_common from '@nestjs/common';
|
|
2
|
-
import { DynamicModule, ExceptionFilter, ArgumentsHost, NestInterceptor, ExecutionContext, CallHandler } from '@nestjs/common';
|
|
2
|
+
import { NestModule, MiddlewareConsumer, DynamicModule, ExceptionFilter, ArgumentsHost, NestInterceptor, ExecutionContext, CallHandler, NestMiddleware } from '@nestjs/common';
|
|
3
3
|
import { HttpAdapterHost } from '@nestjs/core';
|
|
4
4
|
import { Observable } from 'rxjs';
|
|
5
5
|
|
|
@@ -68,6 +68,59 @@ interface OpenApiServerDescriptor {
|
|
|
68
68
|
* so the consumer's declaration reaches the UI unchanged.
|
|
69
69
|
*/
|
|
70
70
|
type OpenApiSecurityScheme = Readonly<Record<string, unknown>>;
|
|
71
|
+
/**
|
|
72
|
+
* One security requirement: scheme name to the scopes it needs, empty for a
|
|
73
|
+
* scheme that takes none. An operation's requirements are alternatives — any
|
|
74
|
+
* one of them satisfies it — so an empty *array* of requirements means the
|
|
75
|
+
* operation needs no authentication at all, which is how the specification
|
|
76
|
+
* expresses a public route that overrides a document-level default.
|
|
77
|
+
*/
|
|
78
|
+
type OpenApiSecurityRequirement = Readonly<Record<string, readonly string[]>>;
|
|
79
|
+
/** The HTTP methods an OpenAPI path item can carry an operation under. */
|
|
80
|
+
type OpenApiHttpMethod = 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE' | 'HEAD' | 'OPTIONS' | 'TRACE';
|
|
81
|
+
/**
|
|
82
|
+
* Addresses one operation in the generated document, as `"<METHOD> <path>"`.
|
|
83
|
+
*
|
|
84
|
+
* This format is a documented contract, not an implementation detail: a sibling
|
|
85
|
+
* library may ship a plain-data map of its own operations keyed this way, so a
|
|
86
|
+
* consumer spreads it into {@link OpenApiOptions.operationSecurity} instead of
|
|
87
|
+
* restating which of that library's routes are public. Import the type to get
|
|
88
|
+
* that map checked at the library's own compile time.
|
|
89
|
+
*
|
|
90
|
+
* The method is uppercase, one space separates the two parts, and the path is
|
|
91
|
+
* written **exactly as it appears in the generated document**: leading slash,
|
|
92
|
+
* OpenAPI template braces (`/users/{id}`), no trailing slash, and including any
|
|
93
|
+
* global prefix the application sets. That last part is the one that surprises:
|
|
94
|
+
* `@nestjs/swagger` includes `app.setGlobalPrefix('api')` in the documented
|
|
95
|
+
* paths, so the key is `'POST /api/auth/login'` in an application that sets one.
|
|
96
|
+
* A library shipping such a map should therefore expose a *function* taking the
|
|
97
|
+
* prefix rather than a frozen constant — the call site is the only place that
|
|
98
|
+
* knows it.
|
|
99
|
+
*
|
|
100
|
+
* @example 'GET /users/{id}'
|
|
101
|
+
* @example 'POST /api/auth/login'
|
|
102
|
+
*/
|
|
103
|
+
type OpenApiOperationKey = `${OpenApiHttpMethod} /${string}`;
|
|
104
|
+
/**
|
|
105
|
+
* Per-operation security requirements, keyed by {@link OpenApiOperationKey}.
|
|
106
|
+
* An empty array marks the operation public, overriding any document default.
|
|
107
|
+
*/
|
|
108
|
+
type OperationSecurityMap = Readonly<Record<OpenApiOperationKey, readonly OpenApiSecurityRequirement[]>>;
|
|
109
|
+
/**
|
|
110
|
+
* Names the operation a route handler produced, in the generated document.
|
|
111
|
+
*
|
|
112
|
+
* Matches `@nestjs/swagger`'s own factory signature so a consumer's existing one
|
|
113
|
+
* can be handed over unchanged. This package installs a factory of its own to
|
|
114
|
+
* learn which handler produced which operation — a contract a library keys its
|
|
115
|
+
* contributed fragments against — and delegates the id string to this one when
|
|
116
|
+
* it is set, so nothing an application already generates is renamed.
|
|
117
|
+
*
|
|
118
|
+
* @param controllerKey - The controller class name.
|
|
119
|
+
* @param methodKey - The handler method name.
|
|
120
|
+
* @param version - The route's version, when the application versions routes.
|
|
121
|
+
* @returns The operation id to publish.
|
|
122
|
+
*/
|
|
123
|
+
type OpenApiOperationIdFactory = (controllerKey: string, methodKey: string, version?: string) => string;
|
|
71
124
|
/**
|
|
72
125
|
* OpenAPI document configuration.
|
|
73
126
|
*
|
|
@@ -96,10 +149,63 @@ interface OpenApiOptions {
|
|
|
96
149
|
servers?: readonly OpenApiServerDescriptor[];
|
|
97
150
|
/** Security schemes added to the document's components. Default: `{}`. */
|
|
98
151
|
securitySchemes?: Readonly<Record<string, OpenApiSecurityScheme>>;
|
|
152
|
+
/**
|
|
153
|
+
* The requirement every operation carries unless it says otherwise, naming
|
|
154
|
+
* schemes declared in {@link OpenApiOptions.securitySchemes}. Default: `[]`,
|
|
155
|
+
* which documents nothing and leaves every operation as it was generated.
|
|
156
|
+
*
|
|
157
|
+
* Set this when most of the API is authenticated, and mark the exceptions
|
|
158
|
+
* public through {@link OpenApiOptions.operationSecurity}. An operation that
|
|
159
|
+
* already declares its own requirement is never overwritten.
|
|
160
|
+
*
|
|
161
|
+
* @example [{ cookieAuth: [] }]
|
|
162
|
+
*/
|
|
163
|
+
security?: readonly OpenApiSecurityRequirement[];
|
|
164
|
+
/**
|
|
165
|
+
* Per-operation overrides of {@link OpenApiOptions.security}, keyed by
|
|
166
|
+
* {@link OpenApiOperationKey}. An empty array marks that operation public.
|
|
167
|
+
* Default: `{}`.
|
|
168
|
+
*
|
|
169
|
+
* A key matching no operation in the generated document is a configuration
|
|
170
|
+
* error and fails the document build, naming the keys that do exist. Silence
|
|
171
|
+
* would be worse: a route renamed out from under a stale key would quietly
|
|
172
|
+
* inherit the document default and be documented as authenticated when it is
|
|
173
|
+
* not, or the reverse.
|
|
174
|
+
*
|
|
175
|
+
* That check runs only when the document is built. With
|
|
176
|
+
* {@link OpenApiOptions.enabled} false, or in a production runtime where the
|
|
177
|
+
* feature is forced off, a stale key is not reported — refusing to boot a
|
|
178
|
+
* service over a documentation setting it never serves would be the wrong
|
|
179
|
+
* trade. The cost is that the error waits for an environment that has the
|
|
180
|
+
* document switched on.
|
|
181
|
+
*
|
|
182
|
+
* @example
|
|
183
|
+
* {
|
|
184
|
+
* 'POST /auth/login': [],
|
|
185
|
+
* 'POST /auth/refresh': [{ refreshCookie: [] }]
|
|
186
|
+
* }
|
|
187
|
+
*/
|
|
188
|
+
operationSecurity?: OperationSecurityMap;
|
|
189
|
+
/**
|
|
190
|
+
* Name the operations in the generated document. Default: the format
|
|
191
|
+
* `@nestjs/swagger` itself produces, `<ControllerKey>_<methodKey>`.
|
|
192
|
+
*
|
|
193
|
+
* Set it to control the ids a client generator will use. This package always
|
|
194
|
+
* installs a factory of its own so it can learn which handler produced which
|
|
195
|
+
* operation, and delegates to this one when it is set — so supplying it
|
|
196
|
+
* changes the published ids, and leaving it unset changes nothing.
|
|
197
|
+
*/
|
|
198
|
+
operationIdFactory?: OpenApiOperationIdFactory;
|
|
99
199
|
/**
|
|
100
200
|
* Contribute the schemas this package owns — the error envelope, the health
|
|
101
|
-
* response, and the pagination shapes — to the document's components
|
|
102
|
-
*
|
|
201
|
+
* response, and the pagination shapes — to the document's components, and
|
|
202
|
+
* reference them from the operations that return them: the error envelope as
|
|
203
|
+
* every operation's `default` response, and the health response on the health
|
|
204
|
+
* endpoints this package registers. Default: `true`.
|
|
205
|
+
*
|
|
206
|
+
* The two halves are one switch because they are one decision. Referencing a
|
|
207
|
+
* schema this package did not contribute would leave a dangling `$ref`, and a
|
|
208
|
+
* document that resolves nowhere is worse than one that says less.
|
|
103
209
|
*/
|
|
104
210
|
includeCoreSchemas?: boolean;
|
|
105
211
|
}
|
|
@@ -221,6 +327,9 @@ interface ResolvedOpenApiOptions {
|
|
|
221
327
|
version: string;
|
|
222
328
|
servers: readonly OpenApiServerDescriptor[];
|
|
223
329
|
securitySchemes: Readonly<Record<string, OpenApiSecurityScheme>>;
|
|
330
|
+
security: readonly OpenApiSecurityRequirement[];
|
|
331
|
+
operationSecurity: OperationSecurityMap;
|
|
332
|
+
operationIdFactory?: OpenApiOperationIdFactory;
|
|
224
333
|
includeCoreSchemas: boolean;
|
|
225
334
|
}
|
|
226
335
|
/**
|
|
@@ -255,7 +364,69 @@ declare const ASYNC_OPTIONS_TYPE: _nestjs_common.ConfigurableModuleAsyncOptions<
|
|
|
255
364
|
/**
|
|
256
365
|
* `BymaxCoreModule`, the application foundation module for NestJS 11.
|
|
257
366
|
*/
|
|
258
|
-
declare class BymaxCoreModule extends BymaxCoreModuleBase {
|
|
367
|
+
declare class BymaxCoreModule extends BymaxCoreModuleBase implements NestModule {
|
|
368
|
+
private readonly resolved;
|
|
369
|
+
private readonly adapterHost?;
|
|
370
|
+
/**
|
|
371
|
+
* @param options - The resolved snapshot, read to decide whether the timing
|
|
372
|
+
* middleware is applied at all.
|
|
373
|
+
*/
|
|
374
|
+
constructor(resolved: ResolvedCoreOptions, adapterHost?: HttpAdapterHost | undefined);
|
|
375
|
+
/**
|
|
376
|
+
* Apply the request-timing middleware to every route.
|
|
377
|
+
*
|
|
378
|
+
* Middleware rather than the interceptor this replaced, because guards run
|
|
379
|
+
* before interceptors: a request rejected by an authentication, authorization
|
|
380
|
+
* or throttling guard never reached the interceptor, and one matching no
|
|
381
|
+
* route never reached a controller at all. A deployment could therefore be
|
|
382
|
+
* under a credential-stuffing run — a flood of 401s — with a flat error
|
|
383
|
+
* graph, which is why this is a security fix rather than a metrics
|
|
384
|
+
* improvement.
|
|
385
|
+
*
|
|
386
|
+
* Applied on both registration paths, and only when timing is enabled: the
|
|
387
|
+
* middleware is the sole recorder now, since a second one would count every
|
|
388
|
+
* matched request twice.
|
|
389
|
+
*
|
|
390
|
+
* There is no single pattern that covers both adapters, which is the whole
|
|
391
|
+
* reason this reads the adapter first. Measured, requesting the root, a
|
|
392
|
+
* parameterised route, a nested path and an unmatched path, with and without
|
|
393
|
+
* `setGlobalPrefix('api')`:
|
|
394
|
+
*
|
|
395
|
+
* | `forRoutes(...)` | Express | Fastify |
|
|
396
|
+
* | ---------------- | ---------------- | -------------------- |
|
|
397
|
+
* | `'*splat'` | skips the root | — |
|
|
398
|
+
* | `'{*splat}'` | skips `/api` | every path |
|
|
399
|
+
* | `'/'` | every path | matches `/` only |
|
|
400
|
+
*
|
|
401
|
+
* On Express `'/'` is a mount and matches everything beneath whatever prefix
|
|
402
|
+
* it is scoped to, while `'{*splat}'` — the form Nest 11's migration guide
|
|
403
|
+
* prescribes for "all routes" — stops matching the prefixed root once an
|
|
404
|
+
* application calls `setGlobalPrefix`. That was reported as nest#14520 and
|
|
405
|
+
* fixed by nest#14522, whose regression test covers Fastify; on
|
|
406
|
+
* `@nestjs/core` 11.1.28 with the Express adapter the prefixed root still
|
|
407
|
+
* reaches no middleware while resolving to `200`.
|
|
408
|
+
*
|
|
409
|
+
* On Fastify the same `'/'` is an exact match rather than a mount — one of
|
|
410
|
+
* three requests reached the middleware — so the wildcard is the only form
|
|
411
|
+
* that works there.
|
|
412
|
+
*
|
|
413
|
+
* A recorder that quietly omits one route is the same class of defect this
|
|
414
|
+
* middleware exists to fix, so each adapter gets the form that omits none.
|
|
415
|
+
*
|
|
416
|
+
* Fastify also needs {@link bridgeFastifyRouteMetadata}, because middie hands
|
|
417
|
+
* middleware the raw request, which carries no route metadata at all; see
|
|
418
|
+
* that file for why the label would otherwise be `<unmatched>` for every
|
|
419
|
+
* request.
|
|
420
|
+
*
|
|
421
|
+
* One limit stays and is documented in the README: Nest scopes module
|
|
422
|
+
* middleware to the global prefix, so with `setGlobalPrefix('api')` a request
|
|
423
|
+
* to `/nope` — outside the prefix entirely — reaches no middleware and is not
|
|
424
|
+
* recorded, while `/api/nope` is. No `forRoutes` argument changes that, and
|
|
425
|
+
* nothing this module can register reaches outside its own scope.
|
|
426
|
+
*
|
|
427
|
+
* @param consumer - Nest's middleware consumer.
|
|
428
|
+
*/
|
|
429
|
+
configure(consumer: MiddlewareConsumer): void;
|
|
259
430
|
/**
|
|
260
431
|
* Register the module synchronously. Options are known now, so disabled
|
|
261
432
|
* features are omitted from the providers and controllers arrays and the
|
|
@@ -345,7 +516,7 @@ declare const BYMAX_CORE_OPTIONS: unique symbol;
|
|
|
345
516
|
*/
|
|
346
517
|
declare const BYMAX_CORRELATION_PROVIDER: unique symbol;
|
|
347
518
|
/**
|
|
348
|
-
* Provide the `ITimingSink` that receives one sample per
|
|
519
|
+
* Provide the `ITimingSink` that receives one sample per closed request.
|
|
349
520
|
* Defaults to a no-op sink.
|
|
350
521
|
*/
|
|
351
522
|
declare const BYMAX_TIMING_SINK: unique symbol;
|
|
@@ -662,9 +833,11 @@ interface MonotonicClock {
|
|
|
662
833
|
}
|
|
663
834
|
|
|
664
835
|
/**
|
|
665
|
-
* @fileoverview Request-timing contracts.
|
|
666
|
-
* {@link RequestTimingSample} per
|
|
667
|
-
* {@link ITimingSink}
|
|
836
|
+
* @fileoverview Request-timing contracts. `BymaxTimingMiddleware` emits one
|
|
837
|
+
* {@link RequestTimingSample} per **closed** request to the bound
|
|
838
|
+
* {@link ITimingSink} — every request the server finished with, including the
|
|
839
|
+
* ones a guard rejected, the ones that matched no route, and the ones a client
|
|
840
|
+
* abandoned mid-flight. The default sink is a no-op; consumers plug in a logger
|
|
668
841
|
* bridge or the metrics bridge through the `BYMAX_TIMING_SINK` token.
|
|
669
842
|
* @layer Contract
|
|
670
843
|
*/
|
|
@@ -694,12 +867,16 @@ interface RequestTimingSample {
|
|
|
694
867
|
}
|
|
695
868
|
/**
|
|
696
869
|
* Receive request-timing samples. Implementations must never throw: a sink
|
|
697
|
-
* failure is caught and silenced by the
|
|
870
|
+
* failure is caught and silenced by the recorder so timing never breaks a
|
|
698
871
|
* request.
|
|
699
872
|
*/
|
|
700
873
|
interface ITimingSink {
|
|
701
874
|
/**
|
|
702
|
-
* Record one sample for a
|
|
875
|
+
* Record one sample for a closed request, however it ended.
|
|
876
|
+
*
|
|
877
|
+
* Called once per request the server closed — not only the ones a handler
|
|
878
|
+
* answered. A rejection issued by a guard, a request matching no route, and a
|
|
879
|
+
* client that hung up mid-response all arrive here.
|
|
703
880
|
*
|
|
704
881
|
* @param sample - The timing sample to record.
|
|
705
882
|
*/
|
|
@@ -707,10 +884,15 @@ interface ITimingSink {
|
|
|
707
884
|
}
|
|
708
885
|
|
|
709
886
|
/**
|
|
710
|
-
* Request-timing interceptor.
|
|
711
|
-
*
|
|
712
|
-
*
|
|
713
|
-
* feature is HTTP-first,
|
|
887
|
+
* Request-timing interceptor. No longer registered by `BymaxCoreModule`, which
|
|
888
|
+
* records through {@link BymaxTimingMiddleware} instead so that requests ended
|
|
889
|
+
* by a guard or by no route matching are counted too. Non-HTTP execution
|
|
890
|
+
* contexts (GraphQL, RPC) pass through untouched: this feature is HTTP-first,
|
|
891
|
+
* matching the exception filter's documented scope.
|
|
892
|
+
*
|
|
893
|
+
* @deprecated Since 1.4.0, superseded by `BymaxTimingMiddleware`, which the
|
|
894
|
+
* module registers automatically. Registering this interceptor as well
|
|
895
|
+
* records a second sample for every request that reaches a handler.
|
|
714
896
|
*/
|
|
715
897
|
declare class TimingInterceptor implements NestInterceptor {
|
|
716
898
|
private readonly options;
|
|
@@ -759,9 +941,9 @@ declare class TimingInterceptor implements NestInterceptor {
|
|
|
759
941
|
*/
|
|
760
942
|
private readErrorStatus;
|
|
761
943
|
/**
|
|
762
|
-
* Build the sample
|
|
763
|
-
*
|
|
764
|
-
*
|
|
944
|
+
* Build the sample and deliver it to the sink inside a try/catch that
|
|
945
|
+
* silences any failure: a throwing sink must never affect the request it is
|
|
946
|
+
* observing.
|
|
765
947
|
*
|
|
766
948
|
* @param method - HTTP method of the request.
|
|
767
949
|
* @param route - Route template of the request.
|
|
@@ -771,6 +953,133 @@ declare class TimingInterceptor implements NestInterceptor {
|
|
|
771
953
|
private recordSample;
|
|
772
954
|
}
|
|
773
955
|
|
|
956
|
+
/**
|
|
957
|
+
* @fileoverview Neutral request-info accessor for request timing. Reads the
|
|
958
|
+
* HTTP method and the route template off a request without assuming Express or
|
|
959
|
+
* Fastify, so `RequestTimingSample.route` always carries a bounded-cardinality
|
|
960
|
+
* label instead of the raw URL.
|
|
961
|
+
*
|
|
962
|
+
* The bound is a security property, not a tidiness one. Requests that match no
|
|
963
|
+
* route are exactly the ones a scanner produces, each at a different path, so a
|
|
964
|
+
* label taken from the URL would mint one time series per probe — turning the
|
|
965
|
+
* scrape endpoint into the most expensive route in the service and the metric
|
|
966
|
+
* into the outage. Every unmatched request therefore shares one label.
|
|
967
|
+
*
|
|
968
|
+
* Dropping the raw-URL fallback bought a **second** guarantee that is worth
|
|
969
|
+
* stating because it was a side effect rather than the intent, and anyone
|
|
970
|
+
* weighing the fallback again would otherwise only re-weigh the cardinality
|
|
971
|
+
* argument. The recorder is middleware mounted at `'/'`, and Express gives
|
|
972
|
+
* mounted middleware a `req.url` relative to its mount point: under
|
|
973
|
+
* `setGlobalPrefix('api')` a request to `/api` arrives as `/`, so a path read
|
|
974
|
+
* there reports somewhere the caller never asked for. Nothing in this file
|
|
975
|
+
* reads `req.url` any more — the only `.url` left is Fastify's
|
|
976
|
+
* `routeOptions.url`, which is a template, not a path — so that class of bug
|
|
977
|
+
* has nowhere to land. Reintroducing a path-derived label brings both problems
|
|
978
|
+
* back, not just the cardinality one. If a raw path is ever genuinely needed,
|
|
979
|
+
* the correct read is `req.originalUrl ?? req.url`: `originalUrl` is Express's
|
|
980
|
+
* and carries the mount prefix, and the `??` covers an adapter that supplies
|
|
981
|
+
* only `url`, where no mount trimmed it in the first place.
|
|
982
|
+
* @layer Utility
|
|
983
|
+
*/
|
|
984
|
+
|
|
985
|
+
/**
|
|
986
|
+
* The route label carried by every request that matched no route.
|
|
987
|
+
*
|
|
988
|
+
* A single constant rather than the request's own path, and exported rather
|
|
989
|
+
* than inlined: it is the value an alert rule matches on to see a scan, so it
|
|
990
|
+
* belongs to the contract and must not drift from what this package writes.
|
|
991
|
+
* Reading `route="<unmatched>"` in a dashboard says what happened; an empty
|
|
992
|
+
* label — which Prometheus treats as equivalent to no label at all — reads as
|
|
993
|
+
* missing data and gets scrolled past, which for a signal whose purpose is to
|
|
994
|
+
* be noticed is the same as not emitting it.
|
|
995
|
+
*/
|
|
996
|
+
declare const UNMATCHED_ROUTE = "<unmatched>";
|
|
997
|
+
/** Structural shape of an Express request's route metadata. */
|
|
998
|
+
interface ExpressRouteShape {
|
|
999
|
+
route?: {
|
|
1000
|
+
path?: string;
|
|
1001
|
+
};
|
|
1002
|
+
baseUrl?: string;
|
|
1003
|
+
}
|
|
1004
|
+
/** Structural shape of a Fastify request's route metadata. */
|
|
1005
|
+
interface FastifyRouteShape {
|
|
1006
|
+
routeOptions?: {
|
|
1007
|
+
url?: string;
|
|
1008
|
+
};
|
|
1009
|
+
}
|
|
1010
|
+
/** Structural shape shared by both frameworks for the method. */
|
|
1011
|
+
interface RawRequestShape {
|
|
1012
|
+
method?: string;
|
|
1013
|
+
}
|
|
1014
|
+
/** The combined structural shape read off the request object. */
|
|
1015
|
+
type RequestShape = ExpressRouteShape & FastifyRouteShape & RawRequestShape;
|
|
1016
|
+
|
|
1017
|
+
/** The part of a response this middleware reads and listens on. */
|
|
1018
|
+
interface ResponseShape {
|
|
1019
|
+
/** Node's event emitter, used for the single `'close'` subscription. */
|
|
1020
|
+
on(event: 'close', listener: () => void): unknown;
|
|
1021
|
+
/**
|
|
1022
|
+
* Final status code. Optional and explicitly nullable: a connection can close
|
|
1023
|
+
* before anything settled one, and that case has to be expressible rather
|
|
1024
|
+
* than assumed away.
|
|
1025
|
+
*/
|
|
1026
|
+
statusCode?: number | undefined;
|
|
1027
|
+
}
|
|
1028
|
+
/**
|
|
1029
|
+
* Records one timing sample per request, whatever ended it.
|
|
1030
|
+
*
|
|
1031
|
+
* Registered by `BymaxCoreModule` for every route when the timing feature is
|
|
1032
|
+
* enabled. It replaces the interceptor as the recorder rather than joining it:
|
|
1033
|
+
* two recorders would count every matched request twice, which is a worse
|
|
1034
|
+
* defect than the one being fixed because it is silent and plausible.
|
|
1035
|
+
*/
|
|
1036
|
+
declare class BymaxTimingMiddleware implements NestMiddleware {
|
|
1037
|
+
private readonly options;
|
|
1038
|
+
private readonly clock;
|
|
1039
|
+
/** The bound sink, or an in-code no-op when nothing is bound. */
|
|
1040
|
+
private readonly sink;
|
|
1041
|
+
/** The bound trace reader, or an in-code no-op when nothing is bound. */
|
|
1042
|
+
private readonly traceContext;
|
|
1043
|
+
/**
|
|
1044
|
+
* @param options - The resolved options, read for the slow-request threshold.
|
|
1045
|
+
* @param sink - The bound timing sink; its failures are swallowed, because a
|
|
1046
|
+
* throwing sink must never affect the request it is observing.
|
|
1047
|
+
* @param clock - Monotonic clock seam, so a test advances time by controlled
|
|
1048
|
+
* amounts instead of sleeping.
|
|
1049
|
+
* @param traceContext - Reads the active span's identifiers. Optional: the
|
|
1050
|
+
* middleware stays constructible on its own, and a no-op resolves nothing.
|
|
1051
|
+
*/
|
|
1052
|
+
constructor(options: ResolvedCoreOptions, sink: ITimingSink | undefined, clock?: MonotonicClock, traceContext?: ITraceContextProvider);
|
|
1053
|
+
/**
|
|
1054
|
+
* Start the measurement and arrange for the sample to be recorded once the
|
|
1055
|
+
* connection closes, then hand the request straight on.
|
|
1056
|
+
*
|
|
1057
|
+
* The route is read inside the listener rather than here: at middleware time
|
|
1058
|
+
* the router has not matched yet, so `req.route` is still empty. By the time
|
|
1059
|
+
* the connection closes it is populated — including for a request a guard
|
|
1060
|
+
* rejected, since matching happens before guards run.
|
|
1061
|
+
*
|
|
1062
|
+
* The trace lookup is tried in the live context first and in this moment's
|
|
1063
|
+
* captured context second; see this file's header for the measurements that
|
|
1064
|
+
* decided that order.
|
|
1065
|
+
*
|
|
1066
|
+
* @param request - The framework request object.
|
|
1067
|
+
* @param response - The framework response object.
|
|
1068
|
+
* @param next - Continues the chain; called synchronously and unconditionally.
|
|
1069
|
+
*/
|
|
1070
|
+
use(request: RequestShape, response: ResponseShape, next: () => void): void;
|
|
1071
|
+
/**
|
|
1072
|
+
* Build the sample and hand it to the sink, guarding both steps.
|
|
1073
|
+
*
|
|
1074
|
+
* @param request - The framework request object.
|
|
1075
|
+
* @param response - The framework response object.
|
|
1076
|
+
* @param start - Monotonic timestamp captured before the chain ran.
|
|
1077
|
+
* @param trace - The span identifiers already resolved by the caller, which
|
|
1078
|
+
* owns the choice of which context to read them from.
|
|
1079
|
+
*/
|
|
1080
|
+
private record;
|
|
1081
|
+
}
|
|
1082
|
+
|
|
774
1083
|
/**
|
|
775
1084
|
* @fileoverview Stable `BYMAX_*` error-code catalog and HTTP-status derivation.
|
|
776
1085
|
* These codes are the machine-readable half of the error envelope contract and
|
|
@@ -824,4 +1133,4 @@ declare const BYMAX_GATEWAY_TIMEOUT = "BYMAX_GATEWAY_TIMEOUT";
|
|
|
824
1133
|
*/
|
|
825
1134
|
declare function codeForStatus(status: number): string;
|
|
826
1135
|
|
|
827
|
-
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_TRACE_CONTEXT, 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 ITraceContextProvider, type MetricsOptions, type OpenApiOptions, type OpenApiSecurityScheme, type OpenApiServerDescriptor, type RequestTimingSample, type ResolvedCoreOptions, type TelemetryOptions, TimingInterceptor, type TimingOptions, type TraceContext, buildErrorEnvelope, codeForStatus };
|
|
1136
|
+
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_TRACE_CONTEXT, BYMAX_UNAUTHORIZED, BYMAX_UNPROCESSABLE_ENTITY, BYMAX_UNSUPPORTED_MEDIA_TYPE, BYMAX_VALIDATION_FAILED, type BuildErrorEnvelopeInput, BymaxCoreModule, type BymaxCoreModuleOptions, BymaxExceptionFilter, BymaxTimingMiddleware, type EnvelopeOptions, type ErrorDetails, type ErrorEnvelope, type FilterErrorContext, type HealthOptions, type ICorrelationIdProvider, type ITimingSink, type ITraceContextProvider, type MetricsOptions, type OpenApiHttpMethod, type OpenApiOperationIdFactory, type OpenApiOperationKey, type OpenApiOptions, type OpenApiSecurityRequirement, type OpenApiSecurityScheme, type OpenApiServerDescriptor, type OperationSecurityMap, type RequestTimingSample, type ResolvedCoreOptions, type TelemetryOptions, TimingInterceptor, type TimingOptions, type TraceContext, UNMATCHED_ROUTE, buildErrorEnvelope, codeForStatus };
|