@bymax-one/nest-core 1.5.2 → 1.6.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 +225 -1
- package/README.md +225 -14
- package/dist/health/index.d.cts +1 -0
- package/dist/health/index.d.ts +1 -0
- package/dist/health.transition-B_IgXJTz.d.cts +119 -0
- package/dist/health.transition-B_IgXJTz.d.ts +119 -0
- package/dist/index.cjs +218 -27
- package/dist/index.d.cts +29 -11
- package/dist/index.d.ts +29 -11
- package/dist/index.mjs +218 -28
- package/dist/openapi/index.cjs +41 -17
- package/dist/openapi/index.mjs +41 -17
- package/dist/pagination/index.cjs +11 -1
- package/dist/pagination/index.d.cts +32 -3
- package/dist/pagination/index.d.ts +32 -3
- package/dist/pagination/index.mjs +11 -1
- package/package.json +1 -1
package/dist/index.d.cts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import * as _nestjs_common from '@nestjs/common';
|
|
2
2
|
import { NestModule, MiddlewareConsumer, DynamicModule, ExceptionFilter, ArgumentsHost, NestInterceptor, ExecutionContext, CallHandler, NestMiddleware } from '@nestjs/common';
|
|
3
3
|
import { HttpAdapterHost } from '@nestjs/core';
|
|
4
|
+
export { H as HealthTransition, a as HealthTransitionCause, I as IHealthTransitionSink } from './health.transition-B_IgXJTz.cjs';
|
|
4
5
|
import { Observable } from 'rxjs';
|
|
5
6
|
|
|
6
7
|
/** Error-envelope exception-filter configuration. */
|
|
@@ -430,9 +431,12 @@ declare class BymaxCoreModule extends BymaxCoreModuleBase implements NestModule
|
|
|
430
431
|
* it is scoped to, while `'{*splat}'` — the form Nest 11's migration guide
|
|
431
432
|
* prescribes for "all routes" — stops matching the prefixed root once an
|
|
432
433
|
* application calls `setGlobalPrefix`. That was reported as nest#14520 and
|
|
433
|
-
* fixed by nest#14522, whose regression test covers Fastify; on
|
|
434
|
-
*
|
|
435
|
-
*
|
|
434
|
+
* fixed by nest#14522, whose regression test covers Fastify; on the Express
|
|
435
|
+
* adapter the prefixed root still reaches no middleware while resolving to
|
|
436
|
+
* `200`. Measured on `@nestjs/core` 11.1.28 and re-measured unchanged on
|
|
437
|
+
* 11.2.1 — a minor release is exactly where this would plausibly have been
|
|
438
|
+
* fixed, so the version this was last confirmed against is part of the
|
|
439
|
+
* claim rather than a footnote to it.
|
|
436
440
|
*
|
|
437
441
|
* On Fastify the same `'/'` is an exact match rather than a mount — one of
|
|
438
442
|
* three requests reached the middleware — so the wildcard is the only form
|
|
@@ -553,6 +557,13 @@ declare const BYMAX_TIMING_SINK: unique symbol;
|
|
|
553
557
|
* Defaults to an empty array.
|
|
554
558
|
*/
|
|
555
559
|
declare const BYMAX_HEALTH_INDICATORS: unique symbol;
|
|
560
|
+
/**
|
|
561
|
+
* Provide the `IHealthTransitionSink` that receives one event per change of
|
|
562
|
+
* readiness state, per check. Nothing is bound by default; with no sink the
|
|
563
|
+
* aggregator writes its own transition lines to Nest's logger, and binding one
|
|
564
|
+
* stands that line down in favour of the sink.
|
|
565
|
+
*/
|
|
566
|
+
declare const BYMAX_HEALTH_TRANSITION_SINK: unique symbol;
|
|
556
567
|
/**
|
|
557
568
|
* Provide the `prom-client` `Registry` backing the metrics endpoint. Bound
|
|
558
569
|
* lazily and only when the metrics feature is enabled.
|
|
@@ -894,9 +905,16 @@ interface RequestTimingSample {
|
|
|
894
905
|
spanId?: string;
|
|
895
906
|
}
|
|
896
907
|
/**
|
|
897
|
-
* Receive request-timing samples. Implementations
|
|
898
|
-
*
|
|
899
|
-
*
|
|
908
|
+
* Receive request-timing samples. Implementations should not fail, and a failure
|
|
909
|
+
* is absorbed by the recorder either way so timing never breaks a request.
|
|
910
|
+
*
|
|
911
|
+
* Both ways an implementation can fail are absorbed: a synchronous throw, and a
|
|
912
|
+
* rejection from an `async record()` — which compiles despite the `void` return
|
|
913
|
+
* type, because TypeScript accepts any return value in a void-returning
|
|
914
|
+
* position, and is the natural shape when the backend behind the sink is async.
|
|
915
|
+
* The failure is swallowed rather than logged, unlike `IHealthTransitionSink`:
|
|
916
|
+
* this runs on every request, so a sink failing systematically would turn one
|
|
917
|
+
* broken backend into a second flood beside it.
|
|
900
918
|
*/
|
|
901
919
|
interface ITimingSink {
|
|
902
920
|
/**
|
|
@@ -969,9 +987,8 @@ declare class TimingInterceptor implements NestInterceptor {
|
|
|
969
987
|
*/
|
|
970
988
|
private readErrorStatus;
|
|
971
989
|
/**
|
|
972
|
-
* Build the sample and deliver it
|
|
973
|
-
*
|
|
974
|
-
* observing.
|
|
990
|
+
* Build the sample and deliver it. `deliverSample` absorbs whatever the sink
|
|
991
|
+
* does with it: a broken sink must never affect the request it observes.
|
|
975
992
|
*
|
|
976
993
|
* @param method - HTTP method of the request.
|
|
977
994
|
* @param route - Route template of the request.
|
|
@@ -1097,7 +1114,8 @@ declare class BymaxTimingMiddleware implements NestMiddleware {
|
|
|
1097
1114
|
*/
|
|
1098
1115
|
use(request: RequestShape, response: ResponseShape, next: () => void): void;
|
|
1099
1116
|
/**
|
|
1100
|
-
* Build the sample and
|
|
1117
|
+
* Build the sample and deliver it. `deliverSample` absorbs whatever the sink
|
|
1118
|
+
* does with it, so a broken sink never reaches the request it observes.
|
|
1101
1119
|
*
|
|
1102
1120
|
* @param request - The framework request object.
|
|
1103
1121
|
* @param response - The framework response object.
|
|
@@ -1161,4 +1179,4 @@ declare const BYMAX_GATEWAY_TIMEOUT = "BYMAX_GATEWAY_TIMEOUT";
|
|
|
1161
1179
|
*/
|
|
1162
1180
|
declare function codeForStatus(status: number): string;
|
|
1163
1181
|
|
|
1164
|
-
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 };
|
|
1182
|
+
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_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 };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import * as _nestjs_common from '@nestjs/common';
|
|
2
2
|
import { NestModule, MiddlewareConsumer, DynamicModule, ExceptionFilter, ArgumentsHost, NestInterceptor, ExecutionContext, CallHandler, NestMiddleware } from '@nestjs/common';
|
|
3
3
|
import { HttpAdapterHost } from '@nestjs/core';
|
|
4
|
+
export { H as HealthTransition, a as HealthTransitionCause, I as IHealthTransitionSink } from './health.transition-B_IgXJTz.js';
|
|
4
5
|
import { Observable } from 'rxjs';
|
|
5
6
|
|
|
6
7
|
/** Error-envelope exception-filter configuration. */
|
|
@@ -430,9 +431,12 @@ declare class BymaxCoreModule extends BymaxCoreModuleBase implements NestModule
|
|
|
430
431
|
* it is scoped to, while `'{*splat}'` — the form Nest 11's migration guide
|
|
431
432
|
* prescribes for "all routes" — stops matching the prefixed root once an
|
|
432
433
|
* application calls `setGlobalPrefix`. That was reported as nest#14520 and
|
|
433
|
-
* fixed by nest#14522, whose regression test covers Fastify; on
|
|
434
|
-
*
|
|
435
|
-
*
|
|
434
|
+
* fixed by nest#14522, whose regression test covers Fastify; on the Express
|
|
435
|
+
* adapter the prefixed root still reaches no middleware while resolving to
|
|
436
|
+
* `200`. Measured on `@nestjs/core` 11.1.28 and re-measured unchanged on
|
|
437
|
+
* 11.2.1 — a minor release is exactly where this would plausibly have been
|
|
438
|
+
* fixed, so the version this was last confirmed against is part of the
|
|
439
|
+
* claim rather than a footnote to it.
|
|
436
440
|
*
|
|
437
441
|
* On Fastify the same `'/'` is an exact match rather than a mount — one of
|
|
438
442
|
* three requests reached the middleware — so the wildcard is the only form
|
|
@@ -553,6 +557,13 @@ declare const BYMAX_TIMING_SINK: unique symbol;
|
|
|
553
557
|
* Defaults to an empty array.
|
|
554
558
|
*/
|
|
555
559
|
declare const BYMAX_HEALTH_INDICATORS: unique symbol;
|
|
560
|
+
/**
|
|
561
|
+
* Provide the `IHealthTransitionSink` that receives one event per change of
|
|
562
|
+
* readiness state, per check. Nothing is bound by default; with no sink the
|
|
563
|
+
* aggregator writes its own transition lines to Nest's logger, and binding one
|
|
564
|
+
* stands that line down in favour of the sink.
|
|
565
|
+
*/
|
|
566
|
+
declare const BYMAX_HEALTH_TRANSITION_SINK: unique symbol;
|
|
556
567
|
/**
|
|
557
568
|
* Provide the `prom-client` `Registry` backing the metrics endpoint. Bound
|
|
558
569
|
* lazily and only when the metrics feature is enabled.
|
|
@@ -894,9 +905,16 @@ interface RequestTimingSample {
|
|
|
894
905
|
spanId?: string;
|
|
895
906
|
}
|
|
896
907
|
/**
|
|
897
|
-
* Receive request-timing samples. Implementations
|
|
898
|
-
*
|
|
899
|
-
*
|
|
908
|
+
* Receive request-timing samples. Implementations should not fail, and a failure
|
|
909
|
+
* is absorbed by the recorder either way so timing never breaks a request.
|
|
910
|
+
*
|
|
911
|
+
* Both ways an implementation can fail are absorbed: a synchronous throw, and a
|
|
912
|
+
* rejection from an `async record()` — which compiles despite the `void` return
|
|
913
|
+
* type, because TypeScript accepts any return value in a void-returning
|
|
914
|
+
* position, and is the natural shape when the backend behind the sink is async.
|
|
915
|
+
* The failure is swallowed rather than logged, unlike `IHealthTransitionSink`:
|
|
916
|
+
* this runs on every request, so a sink failing systematically would turn one
|
|
917
|
+
* broken backend into a second flood beside it.
|
|
900
918
|
*/
|
|
901
919
|
interface ITimingSink {
|
|
902
920
|
/**
|
|
@@ -969,9 +987,8 @@ declare class TimingInterceptor implements NestInterceptor {
|
|
|
969
987
|
*/
|
|
970
988
|
private readErrorStatus;
|
|
971
989
|
/**
|
|
972
|
-
* Build the sample and deliver it
|
|
973
|
-
*
|
|
974
|
-
* observing.
|
|
990
|
+
* Build the sample and deliver it. `deliverSample` absorbs whatever the sink
|
|
991
|
+
* does with it: a broken sink must never affect the request it observes.
|
|
975
992
|
*
|
|
976
993
|
* @param method - HTTP method of the request.
|
|
977
994
|
* @param route - Route template of the request.
|
|
@@ -1097,7 +1114,8 @@ declare class BymaxTimingMiddleware implements NestMiddleware {
|
|
|
1097
1114
|
*/
|
|
1098
1115
|
use(request: RequestShape, response: ResponseShape, next: () => void): void;
|
|
1099
1116
|
/**
|
|
1100
|
-
* Build the sample and
|
|
1117
|
+
* Build the sample and deliver it. `deliverSample` absorbs whatever the sink
|
|
1118
|
+
* does with it, so a broken sink never reaches the request it observes.
|
|
1101
1119
|
*
|
|
1102
1120
|
* @param request - The framework request object.
|
|
1103
1121
|
* @param response - The framework response object.
|
|
@@ -1161,4 +1179,4 @@ declare const BYMAX_GATEWAY_TIMEOUT = "BYMAX_GATEWAY_TIMEOUT";
|
|
|
1161
1179
|
*/
|
|
1162
1180
|
declare function codeForStatus(status: number): string;
|
|
1163
1181
|
|
|
1164
|
-
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 };
|
|
1182
|
+
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_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 };
|
package/dist/index.mjs
CHANGED
|
@@ -155,6 +155,9 @@ var BYMAX_TIMING_SINK = /* @__PURE__ */ Symbol.for("@bymax-one/nest-core:timing-
|
|
|
155
155
|
var BYMAX_HEALTH_INDICATORS = /* @__PURE__ */ Symbol.for(
|
|
156
156
|
"@bymax-one/nest-core:health-indicators"
|
|
157
157
|
);
|
|
158
|
+
var BYMAX_HEALTH_TRANSITION_SINK = /* @__PURE__ */ Symbol.for(
|
|
159
|
+
"@bymax-one/nest-core:health-transition-sink"
|
|
160
|
+
);
|
|
158
161
|
var BYMAX_METRICS_REGISTRY = /* @__PURE__ */ Symbol.for(
|
|
159
162
|
"@bymax-one/nest-core:metrics-registry"
|
|
160
163
|
);
|
|
@@ -634,6 +637,13 @@ function selectAsyncExceptionFilter(options, correlation, adapterHost) {
|
|
|
634
637
|
return options.envelope.enabled ? new BymaxExceptionFilter(options, correlation, adapterHost) : new PassThroughExceptionFilter(adapterHost);
|
|
635
638
|
}
|
|
636
639
|
|
|
640
|
+
// src/contain-rejection.ts
|
|
641
|
+
function containRejection(returned, onFailure) {
|
|
642
|
+
if (typeof returned?.then === "function") {
|
|
643
|
+
Promise.resolve(returned).catch(onFailure);
|
|
644
|
+
}
|
|
645
|
+
}
|
|
646
|
+
|
|
637
647
|
// src/discovery.ts
|
|
638
648
|
function labelFor(className, token) {
|
|
639
649
|
return className === "" ? String(token) : className;
|
|
@@ -685,7 +695,7 @@ var TRUNCATION_ELLIPSIS = "...";
|
|
|
685
695
|
function summarizeRejection(reason) {
|
|
686
696
|
let message;
|
|
687
697
|
try {
|
|
688
|
-
message = reason instanceof Error ? reason.message :
|
|
698
|
+
message = String(reason instanceof Error ? reason.message : reason);
|
|
689
699
|
} catch {
|
|
690
700
|
message = "Unknown error";
|
|
691
701
|
}
|
|
@@ -694,17 +704,27 @@ function summarizeRejection(reason) {
|
|
|
694
704
|
}
|
|
695
705
|
return `${message.slice(0, MAX_ERROR_MESSAGE_LENGTH - TRUNCATION_ELLIPSIS.length)}${TRUNCATION_ELLIPSIS}`;
|
|
696
706
|
}
|
|
697
|
-
async function runIndicator(indicator, timeoutMs, exposeErrors
|
|
707
|
+
async function runIndicator(indicator, timeoutMs, exposeErrors) {
|
|
698
708
|
let timer;
|
|
699
709
|
const timedOut = new Promise((resolve) => {
|
|
700
710
|
timer = setTimeout(() => {
|
|
701
|
-
resolve({
|
|
711
|
+
resolve({
|
|
712
|
+
entry: { name: indicator.name, status: "down", details: { timedOutAfterMs: timeoutMs } },
|
|
713
|
+
isUp: false,
|
|
714
|
+
cause: { kind: "timed-out", timeoutMs }
|
|
715
|
+
});
|
|
702
716
|
}, timeoutMs);
|
|
703
717
|
});
|
|
704
|
-
const checked = Promise.resolve().then(() => indicator.check()).then((result) =>
|
|
718
|
+
const checked = Promise.resolve().then(() => indicator.check()).then((result) => {
|
|
719
|
+
const entry = { ...result, name: indicator.name };
|
|
720
|
+
return result.status === "up" ? { entry, isUp: true } : { entry, isUp: false, cause: { kind: "reported-down" } };
|
|
721
|
+
}).catch((reason) => {
|
|
705
722
|
const message = summarizeRejection(reason);
|
|
706
|
-
|
|
707
|
-
|
|
723
|
+
return {
|
|
724
|
+
entry: exposeErrors ? { name: indicator.name, status: "down", details: { error: message } } : { name: indicator.name, status: "down" },
|
|
725
|
+
isUp: false,
|
|
726
|
+
cause: { kind: "rejected", message }
|
|
727
|
+
};
|
|
708
728
|
});
|
|
709
729
|
try {
|
|
710
730
|
return await Promise.race([checked, timedOut]);
|
|
@@ -712,6 +732,16 @@ async function runIndicator(indicator, timeoutMs, exposeErrors, logger) {
|
|
|
712
732
|
clearTimeout(timer);
|
|
713
733
|
}
|
|
714
734
|
}
|
|
735
|
+
function describeCause(cause) {
|
|
736
|
+
switch (cause.kind) {
|
|
737
|
+
case "rejected":
|
|
738
|
+
return `the indicator rejected: ${cause.message}`;
|
|
739
|
+
case "timed-out":
|
|
740
|
+
return `the indicator did not answer within ${cause.timeoutMs}ms`;
|
|
741
|
+
case "reported-down":
|
|
742
|
+
return "the indicator reported it down";
|
|
743
|
+
}
|
|
744
|
+
}
|
|
715
745
|
var HealthService = class {
|
|
716
746
|
/**
|
|
717
747
|
* @param indicators - Every explicitly registered indicator; empty when none
|
|
@@ -726,18 +756,169 @@ var HealthService = class {
|
|
|
726
756
|
* without it when the feature is off.
|
|
727
757
|
* @param reflector - Nest's metadata reader, used to match the indicator
|
|
728
758
|
* marker. Optional for the same reason.
|
|
759
|
+
* @param transitionSink - Receives one event per change of readiness state.
|
|
760
|
+
* `@Optional()` and bound to nothing by default, for the same reason as the
|
|
761
|
+
* indicator array: a local default here would shadow a consumer's override.
|
|
729
762
|
*/
|
|
730
|
-
constructor(indicators = [], options, discovery, reflector) {
|
|
763
|
+
constructor(indicators = [], options, discovery, reflector, transitionSink) {
|
|
731
764
|
this.indicators = indicators;
|
|
732
765
|
this.options = options;
|
|
733
766
|
this.discovery = discovery;
|
|
734
767
|
this.reflector = reflector;
|
|
768
|
+
this.transitionSink = transitionSink;
|
|
735
769
|
/**
|
|
736
770
|
* Nest's own logger, scoped to this class. The failure reason of a `down`
|
|
737
771
|
* indicator is written here rather than into the HTTP response, so the
|
|
738
772
|
* diagnostic survives without being served to whoever can reach the probe.
|
|
739
773
|
*/
|
|
740
774
|
this.logger = new Logger(HealthService.name);
|
|
775
|
+
/**
|
|
776
|
+
* The last state observed per check name, absent until the first observation,
|
|
777
|
+
* tagged with the probe that observed it. Keyed by name, so one map serves
|
|
778
|
+
* every check and one dependency can neither trigger nor suppress another's
|
|
779
|
+
* line. This is the state that makes the difference between a line per change
|
|
780
|
+
* and a line per probe.
|
|
781
|
+
*/
|
|
782
|
+
this.lastState = /* @__PURE__ */ new Map();
|
|
783
|
+
/**
|
|
784
|
+
* Counts readiness probes, so an outcome can be ordered against what is
|
|
785
|
+
* already recorded. Readiness is not called one at a time: an orchestrator's
|
|
786
|
+
* probe and a load balancer's health check reach this concurrently, and this
|
|
787
|
+
* feature's own subject — a dependency that hangs until `indicatorTimeoutMs`
|
|
788
|
+
* elapses — is exactly what makes an earlier probe finish after a later one.
|
|
789
|
+
* Comparing states alone would then let the stale observation win.
|
|
790
|
+
*/
|
|
791
|
+
this.probeSequence = 0;
|
|
792
|
+
}
|
|
793
|
+
/**
|
|
794
|
+
* Report one check's current state, emitting only when it differs from the
|
|
795
|
+
* last state observed for that name.
|
|
796
|
+
*
|
|
797
|
+
* The asymmetry on a first observation is deliberate. A first observation that
|
|
798
|
+
* is FAILING is a transition: a process that boots against a dependency
|
|
799
|
+
* already down would otherwise look healthy in the log forever. A first
|
|
800
|
+
* observation that is HEALTHY is not: that is the expected state, and
|
|
801
|
+
* announcing it would write one line per dependency on every boot — the noise
|
|
802
|
+
* the probe-path exclusion exists to keep out.
|
|
803
|
+
*
|
|
804
|
+
* A cause is recorded at the transition, so a dependency that stays down while
|
|
805
|
+
* its failure mode changes underneath keeps the first one. That is the cost of
|
|
806
|
+
* one line per outage instead of one per probe.
|
|
807
|
+
*
|
|
808
|
+
* @param outcome - The indicator's resolved outcome for this probe.
|
|
809
|
+
* @param seq - The probe that produced it, from {@link probeSequence}.
|
|
810
|
+
*/
|
|
811
|
+
reportTransition(outcome, seq) {
|
|
812
|
+
const name = outcome.entry.name;
|
|
813
|
+
const previous = this.lastState.get(name);
|
|
814
|
+
if (previous !== void 0 && seq <= previous.seq) {
|
|
815
|
+
return;
|
|
816
|
+
}
|
|
817
|
+
const changed = previous?.isUp !== outcome.isUp;
|
|
818
|
+
this.lastState.set(name, { isUp: outcome.isUp, seq });
|
|
819
|
+
if (!changed) {
|
|
820
|
+
return;
|
|
821
|
+
}
|
|
822
|
+
if (outcome.isUp) {
|
|
823
|
+
if (previous === void 0) {
|
|
824
|
+
return;
|
|
825
|
+
}
|
|
826
|
+
this.describe(`Health check "${name}" recovered`, false);
|
|
827
|
+
this.emit({ name, isUp: true });
|
|
828
|
+
return;
|
|
829
|
+
}
|
|
830
|
+
this.describe(`Health check "${name}" went down: ${describeCause(outcome.cause)}`, true);
|
|
831
|
+
this.emit({ name, isUp: false, cause: outcome.cause });
|
|
832
|
+
}
|
|
833
|
+
/**
|
|
834
|
+
* Write a transition to this package's own logger, unless a sink is bound —
|
|
835
|
+
* the reasoning for standing down is on `IHealthTransitionSink`.
|
|
836
|
+
*
|
|
837
|
+
* Down is a warning, not an error: an unreachable dependency is what readiness
|
|
838
|
+
* exists to route around, while `error` is what pages someone.
|
|
839
|
+
*
|
|
840
|
+
* @param message - The line to write.
|
|
841
|
+
* @param isDown - Whether it describes a check going down, which sets the level.
|
|
842
|
+
*/
|
|
843
|
+
describe(message, isDown) {
|
|
844
|
+
if (this.transitionSink !== void 0) {
|
|
845
|
+
return;
|
|
846
|
+
}
|
|
847
|
+
this.writeLine(message, isDown);
|
|
848
|
+
}
|
|
849
|
+
/**
|
|
850
|
+
* Write one line to this package's logger, absorbing a logger that fails.
|
|
851
|
+
*
|
|
852
|
+
* Nest's logger is replaceable, so what this calls is application code. It can
|
|
853
|
+
* throw — most plausibly for the very reason a transition is being reported at
|
|
854
|
+
* all, when the sink and the logger share a backend that is down. Uncontained
|
|
855
|
+
* that throw leaves the readiness path: synchronously it rejects the probe,
|
|
856
|
+
* and from the asynchronous sink handler it becomes an unhandled rejection.
|
|
857
|
+
* Either way a healthy deployment leaves rotation because its logging broke,
|
|
858
|
+
* which is the outcome this whole seam exists to prevent.
|
|
859
|
+
*
|
|
860
|
+
* The failure is swallowed rather than reported, because a logger is the last
|
|
861
|
+
* place a failure could be reported to. There is nowhere left to go.
|
|
862
|
+
*
|
|
863
|
+
* @param message - The line to write.
|
|
864
|
+
* @param asWarning - Whether to write it at warning level.
|
|
865
|
+
*/
|
|
866
|
+
writeLine(message, asWarning) {
|
|
867
|
+
try {
|
|
868
|
+
if (asWarning) {
|
|
869
|
+
this.logger.warn(message);
|
|
870
|
+
return;
|
|
871
|
+
}
|
|
872
|
+
this.logger.log(message);
|
|
873
|
+
} catch {
|
|
874
|
+
}
|
|
875
|
+
}
|
|
876
|
+
/**
|
|
877
|
+
* Hand a transition to the consumer's sink, if one is bound. A failure is
|
|
878
|
+
* contained: readiness answering `500` because its own logging broke would
|
|
879
|
+
* take a healthy deployment out of rotation over an observability fault.
|
|
880
|
+
*
|
|
881
|
+
* Both ways a sink can fail are caught, for the same reason `runIndicator`
|
|
882
|
+
* defends against an indicator that throws synchronously — a public seam
|
|
883
|
+
* cannot assume the implementation behind it honors its own signature.
|
|
884
|
+
* `record` is declared to return `void`, but TypeScript accepts any return
|
|
885
|
+
* value in a void-returning position, so `async record()` compiles and is the
|
|
886
|
+
* shape a consumer reaches for when the logger it delegates to is async. Its
|
|
887
|
+
* rejection lands a microtask after the `try` block has exited, which is an
|
|
888
|
+
* unhandled rejection rather than the contained failure documented on the
|
|
889
|
+
* contract.
|
|
890
|
+
*
|
|
891
|
+
* Whatever comes back is assimilated with `Promise.resolve`, not tested with
|
|
892
|
+
* `instanceof Promise`: `Promise` is a per-realm binding, so an `async` sink
|
|
893
|
+
* defined in another realm returns a native promise that fails `instanceof`
|
|
894
|
+
* here, and a userland promise library's result is not an instance either.
|
|
895
|
+
* Assimilation is the language's own thenable test, and it is inert for the
|
|
896
|
+
* `undefined` an ordinary synchronous sink returns.
|
|
897
|
+
*
|
|
898
|
+
* @param transition - The event to deliver.
|
|
899
|
+
*/
|
|
900
|
+
emit(transition) {
|
|
901
|
+
if (this.transitionSink === void 0) {
|
|
902
|
+
return;
|
|
903
|
+
}
|
|
904
|
+
try {
|
|
905
|
+
containRejection(this.transitionSink.record(transition), (error) => {
|
|
906
|
+
this.reportSinkFailure(error);
|
|
907
|
+
});
|
|
908
|
+
} catch (error) {
|
|
909
|
+
this.reportSinkFailure(error);
|
|
910
|
+
}
|
|
911
|
+
}
|
|
912
|
+
/**
|
|
913
|
+
* Log a sink failure, without letting it reach the probe.
|
|
914
|
+
*
|
|
915
|
+
* @param error - Whatever the sink threw or rejected with.
|
|
916
|
+
*/
|
|
917
|
+
reportSinkFailure(error) {
|
|
918
|
+
this.writeLine(
|
|
919
|
+
`Health transition sink threw and was ignored: ${summarizeRejection(error)}`,
|
|
920
|
+
true
|
|
921
|
+
);
|
|
741
922
|
}
|
|
742
923
|
/**
|
|
743
924
|
* Resolve the readiness set once the whole container is instantiated.
|
|
@@ -799,11 +980,14 @@ var HealthService = class {
|
|
|
799
980
|
async checkReadiness() {
|
|
800
981
|
const timeoutMs = this.options.health.indicatorTimeoutMs;
|
|
801
982
|
const exposeErrors = this.options.health.exposeIndicatorErrors;
|
|
802
|
-
const
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
)
|
|
983
|
+
const seq = ++this.probeSequence;
|
|
984
|
+
const outcomes = await Promise.all(
|
|
985
|
+
this.resolveIndicators().map((indicator) => runIndicator(indicator, timeoutMs, exposeErrors))
|
|
806
986
|
);
|
|
987
|
+
for (const outcome of outcomes) {
|
|
988
|
+
this.reportTransition(outcome, seq);
|
|
989
|
+
}
|
|
990
|
+
const checks = outcomes.map((outcome) => outcome.entry);
|
|
807
991
|
const status = checks.every((check) => check.status === "up") ? "ok" : "error";
|
|
808
992
|
return { status, checks };
|
|
809
993
|
}
|
|
@@ -816,7 +1000,9 @@ HealthService = __decorateClass([
|
|
|
816
1000
|
__decorateParam(2, Optional()),
|
|
817
1001
|
__decorateParam(2, Inject(DiscoveryService)),
|
|
818
1002
|
__decorateParam(3, Optional()),
|
|
819
|
-
__decorateParam(3, Inject(Reflector))
|
|
1003
|
+
__decorateParam(3, Inject(Reflector)),
|
|
1004
|
+
__decorateParam(4, Optional()),
|
|
1005
|
+
__decorateParam(4, Inject(BYMAX_HEALTH_TRANSITION_SINK))
|
|
820
1006
|
], HealthService);
|
|
821
1007
|
|
|
822
1008
|
// src/health/health.controller.ts
|
|
@@ -1204,6 +1390,13 @@ function buildTimingSample(input) {
|
|
|
1204
1390
|
...trace !== void 0 ? { traceId: trace.traceId, spanId: trace.spanId } : {}
|
|
1205
1391
|
};
|
|
1206
1392
|
}
|
|
1393
|
+
function deliverSample(sink, sample) {
|
|
1394
|
+
try {
|
|
1395
|
+
containRejection(sink.record(sample), () => {
|
|
1396
|
+
});
|
|
1397
|
+
} catch {
|
|
1398
|
+
}
|
|
1399
|
+
}
|
|
1207
1400
|
|
|
1208
1401
|
// src/timing/timing.middleware.ts
|
|
1209
1402
|
var BymaxTimingMiddleware = class {
|
|
@@ -1253,7 +1446,8 @@ var BymaxTimingMiddleware = class {
|
|
|
1253
1446
|
next();
|
|
1254
1447
|
}
|
|
1255
1448
|
/**
|
|
1256
|
-
* Build the sample and
|
|
1449
|
+
* Build the sample and deliver it. `deliverSample` absorbs whatever the sink
|
|
1450
|
+
* does with it, so a broken sink never reaches the request it observes.
|
|
1257
1451
|
*
|
|
1258
1452
|
* @param request - The framework request object.
|
|
1259
1453
|
* @param response - The framework response object.
|
|
@@ -1271,10 +1465,7 @@ var BymaxTimingMiddleware = class {
|
|
|
1271
1465
|
threshold: this.options.timing.slowRequestThresholdMs,
|
|
1272
1466
|
trace
|
|
1273
1467
|
});
|
|
1274
|
-
|
|
1275
|
-
this.sink.record(sample);
|
|
1276
|
-
} catch {
|
|
1277
|
-
}
|
|
1468
|
+
deliverSample(this.sink, sample);
|
|
1278
1469
|
}
|
|
1279
1470
|
};
|
|
1280
1471
|
BymaxTimingMiddleware = __decorateClass([
|
|
@@ -1395,9 +1586,12 @@ var BymaxCoreModule = class extends BymaxCoreModuleBase {
|
|
|
1395
1586
|
* it is scoped to, while `'{*splat}'` — the form Nest 11's migration guide
|
|
1396
1587
|
* prescribes for "all routes" — stops matching the prefixed root once an
|
|
1397
1588
|
* application calls `setGlobalPrefix`. That was reported as nest#14520 and
|
|
1398
|
-
* fixed by nest#14522, whose regression test covers Fastify; on
|
|
1399
|
-
*
|
|
1400
|
-
*
|
|
1589
|
+
* fixed by nest#14522, whose regression test covers Fastify; on the Express
|
|
1590
|
+
* adapter the prefixed root still reaches no middleware while resolving to
|
|
1591
|
+
* `200`. Measured on `@nestjs/core` 11.1.28 and re-measured unchanged on
|
|
1592
|
+
* 11.2.1 — a minor release is exactly where this would plausibly have been
|
|
1593
|
+
* fixed, so the version this was last confirmed against is part of the
|
|
1594
|
+
* claim rather than a footnote to it.
|
|
1401
1595
|
*
|
|
1402
1596
|
* On Fastify the same `'/'` is an exact match rather than a mount — one of
|
|
1403
1597
|
* three requests reached the middleware — so the wildcard is the only form
|
|
@@ -1587,9 +1781,8 @@ var TimingInterceptor = class {
|
|
|
1587
1781
|
return error instanceof HttpException ? error.getStatus() : UNKNOWN_ERROR_STATUS;
|
|
1588
1782
|
}
|
|
1589
1783
|
/**
|
|
1590
|
-
* Build the sample and deliver it
|
|
1591
|
-
*
|
|
1592
|
-
* observing.
|
|
1784
|
+
* Build the sample and deliver it. `deliverSample` absorbs whatever the sink
|
|
1785
|
+
* does with it: a broken sink must never affect the request it observes.
|
|
1593
1786
|
*
|
|
1594
1787
|
* @param method - HTTP method of the request.
|
|
1595
1788
|
* @param route - Route template of the request.
|
|
@@ -1605,10 +1798,7 @@ var TimingInterceptor = class {
|
|
|
1605
1798
|
threshold: this.options.timing.slowRequestThresholdMs,
|
|
1606
1799
|
trace: readTraceContext(this.traceContext)
|
|
1607
1800
|
});
|
|
1608
|
-
|
|
1609
|
-
this.sink.record(sample);
|
|
1610
|
-
} catch {
|
|
1611
|
-
}
|
|
1801
|
+
deliverSample(this.sink, sample);
|
|
1612
1802
|
}
|
|
1613
1803
|
};
|
|
1614
1804
|
TimingInterceptor = __decorateClass([
|
|
@@ -1621,4 +1811,4 @@ TimingInterceptor = __decorateClass([
|
|
|
1621
1811
|
__decorateParam(3, Inject(BYMAX_TRACE_CONTEXT))
|
|
1622
1812
|
], TimingInterceptor);
|
|
1623
1813
|
|
|
1624
|
-
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, BymaxCoreModule, BymaxExceptionFilter, BymaxTimingMiddleware, TimingInterceptor, UNMATCHED_ROUTE, buildErrorEnvelope, codeForStatus };
|
|
1814
|
+
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_UNPROCESSABLE_ENTITY, BYMAX_UNSUPPORTED_MEDIA_TYPE, BYMAX_VALIDATION_FAILED, BymaxCoreModule, BymaxExceptionFilter, BymaxTimingMiddleware, TimingInterceptor, UNMATCHED_ROUTE, buildErrorEnvelope, codeForStatus };
|
package/dist/openapi/index.cjs
CHANGED
|
@@ -326,7 +326,7 @@ var CORE_PARAMETERS = {
|
|
|
326
326
|
}
|
|
327
327
|
};
|
|
328
328
|
|
|
329
|
-
// src/openapi/openapi.
|
|
329
|
+
// src/openapi/openapi.shape.ts
|
|
330
330
|
var OPERATION_METHODS = [
|
|
331
331
|
"get",
|
|
332
332
|
"post",
|
|
@@ -337,21 +337,26 @@ var OPERATION_METHODS = [
|
|
|
337
337
|
"options",
|
|
338
338
|
"trace"
|
|
339
339
|
];
|
|
340
|
-
var METRICS_SCHEME_NAME = "BymaxMetricsAuth";
|
|
341
|
-
var ERROR_ENVELOPE_SCHEMA = "BymaxErrorEnvelope";
|
|
342
|
-
var HEALTH_RESPONSE_SCHEMA = "BymaxHealthResponse";
|
|
343
340
|
function asRecord(value) {
|
|
344
341
|
if (typeof value !== "object" || value === null || Array.isArray(value)) {
|
|
345
342
|
return {};
|
|
346
343
|
}
|
|
347
344
|
return value;
|
|
348
345
|
}
|
|
349
|
-
function mergeAbsent(existing, additions) {
|
|
350
|
-
return { ...additions, ...existing };
|
|
351
|
-
}
|
|
352
346
|
function operationsOf(item) {
|
|
353
347
|
return Object.entries(asRecord(item)).filter(([key]) => OPERATION_METHODS.includes(key));
|
|
354
348
|
}
|
|
349
|
+
function operationKey(method, path) {
|
|
350
|
+
return `${method.toUpperCase()} ${path}`;
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
// src/openapi/openapi.document.ts
|
|
354
|
+
var METRICS_SCHEME_NAME = "BymaxMetricsAuth";
|
|
355
|
+
var ERROR_ENVELOPE_SCHEMA = "BymaxErrorEnvelope";
|
|
356
|
+
var HEALTH_RESPONSE_SCHEMA = "BymaxHealthResponse";
|
|
357
|
+
function mergeAbsent(existing, additions) {
|
|
358
|
+
return { ...additions, ...existing };
|
|
359
|
+
}
|
|
355
360
|
function mergeResponses(existing, additions) {
|
|
356
361
|
const merged = new Map(Object.entries(existing));
|
|
357
362
|
for (const [status, value] of Object.entries(additions)) {
|
|
@@ -377,21 +382,24 @@ function withoutDisabledRoutes(paths, options, routes) {
|
|
|
377
382
|
});
|
|
378
383
|
return Object.fromEntries(kept.filter(([, item]) => item !== void 0));
|
|
379
384
|
}
|
|
380
|
-
function
|
|
385
|
+
function inheritsRequirement(document, openapi) {
|
|
386
|
+
if (document.security === void 0) {
|
|
387
|
+
return openapi.security.length > 0;
|
|
388
|
+
}
|
|
389
|
+
return Array.isArray(document.security) && document.security.length > 0;
|
|
390
|
+
}
|
|
391
|
+
function ownRouteSecurity(path, method, options, routes, inherits) {
|
|
381
392
|
if (method !== "get") {
|
|
382
393
|
return void 0;
|
|
383
394
|
}
|
|
384
395
|
if (options.metrics.authToken !== void 0 && routes.isMetrics(path)) {
|
|
385
396
|
return [{ [METRICS_SCHEME_NAME]: [] }];
|
|
386
397
|
}
|
|
387
|
-
if (
|
|
398
|
+
if (inherits && (routes.isHealth(path) || routes.isMetrics(path))) {
|
|
388
399
|
return [];
|
|
389
400
|
}
|
|
390
401
|
return void 0;
|
|
391
402
|
}
|
|
392
|
-
function operationKey(method, path) {
|
|
393
|
-
return `${method.toUpperCase()} ${path}`;
|
|
394
|
-
}
|
|
395
403
|
function coreResponses(path, options, routes) {
|
|
396
404
|
const responses = {};
|
|
397
405
|
if (options.envelope.enabled) {
|
|
@@ -425,7 +433,7 @@ function mergeFragment(operation, fragment) {
|
|
|
425
433
|
}
|
|
426
434
|
return merged;
|
|
427
435
|
}
|
|
428
|
-
function augmentOperation(operation, path, method, options, routes, contributions) {
|
|
436
|
+
function augmentOperation(operation, path, method, options, routes, contributions, inherits) {
|
|
429
437
|
const declaredByDocument = operation["security"] !== void 0;
|
|
430
438
|
let result = { ...operation };
|
|
431
439
|
for (const fragment of fragmentsFor(result["operationId"], contributions)) {
|
|
@@ -434,7 +442,7 @@ function augmentOperation(operation, path, method, options, routes, contribution
|
|
|
434
442
|
if (!declaredByDocument) {
|
|
435
443
|
const override = options.openapi.operationSecurity[operationKey(method, path)];
|
|
436
444
|
const describedByLibrary = result["security"] !== void 0;
|
|
437
|
-
const security = override ?? (describedByLibrary ? void 0 : ownRouteSecurity(path, method, options, routes));
|
|
445
|
+
const security = override ?? (describedByLibrary ? void 0 : ownRouteSecurity(path, method, options, routes, inherits));
|
|
438
446
|
if (security !== void 0) {
|
|
439
447
|
result["security"] = security;
|
|
440
448
|
}
|
|
@@ -509,12 +517,20 @@ function assertOverridesMatch(paths, openapi) {
|
|
|
509
517
|
`[BymaxCoreModule] openapi.operationSecurity addresses ${unmatched.length} operation(s) that the document does not contain: ${unmatched.join(", ")}. Keys are "<METHOD> <path>" with the path exactly as documented, including any global prefix. The document contains: ${documented.length === 0 ? "(none)" : documented.join(", ")}.`
|
|
510
518
|
);
|
|
511
519
|
}
|
|
512
|
-
function augmentPaths(paths, options, routes, contributions) {
|
|
520
|
+
function augmentPaths(paths, options, routes, contributions, inherits) {
|
|
513
521
|
return Object.fromEntries(
|
|
514
522
|
Object.entries(paths).map(([path, item]) => {
|
|
515
523
|
const augmented = operationsOf(item).map(([method, operation]) => [
|
|
516
524
|
method,
|
|
517
|
-
augmentOperation(
|
|
525
|
+
augmentOperation(
|
|
526
|
+
asRecord(operation),
|
|
527
|
+
path,
|
|
528
|
+
method,
|
|
529
|
+
options,
|
|
530
|
+
routes,
|
|
531
|
+
contributions,
|
|
532
|
+
inherits
|
|
533
|
+
)
|
|
518
534
|
]);
|
|
519
535
|
return [path, { ...asRecord(item), ...Object.fromEntries(augmented) }];
|
|
520
536
|
})
|
|
@@ -552,7 +568,15 @@ function augmentDocument(document, options, pathPrefixes = [""], contributions =
|
|
|
552
568
|
const routes = indexOwnRoutes(options, pathPrefixes);
|
|
553
569
|
const served = withoutDisabledRoutes(asRecord(document.paths), options, routes);
|
|
554
570
|
assertOverridesMatch(served, openapi);
|
|
555
|
-
const paths = document.paths === void 0 ? {} : {
|
|
571
|
+
const paths = document.paths === void 0 ? {} : {
|
|
572
|
+
paths: augmentPaths(
|
|
573
|
+
served,
|
|
574
|
+
options,
|
|
575
|
+
routes,
|
|
576
|
+
contributions,
|
|
577
|
+
inheritsRequirement(document, openapi)
|
|
578
|
+
)
|
|
579
|
+
};
|
|
556
580
|
const security = openapi.security.length > 0 && document.security === void 0 ? { security: openapi.security } : {};
|
|
557
581
|
return { ...document, components: Object.fromEntries(merged), ...paths, ...security };
|
|
558
582
|
}
|