@tahminator/sapling 2.1.0-beta.6ea2cd3e → 2.1.0-beta.845ac846
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/README.md +59 -3
- package/dist/index.cjs +30 -17
- package/dist/index.d.cts +29 -19
- package/dist/index.d.mts +29 -19
- package/dist/index.mjs +30 -17
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -26,6 +26,10 @@ A lightweight Express.js dependency injection & route abstraction library.
|
|
|
26
26
|
+ [Automatic Spec Generation](#automatic-spec-generation)
|
|
27
27
|
+ [Adding Documentation with Decorators](#adding-documentation-with-decorators)
|
|
28
28
|
+ [Customizing Paths](#customizing-paths)
|
|
29
|
+
* [Health Checks](#health-checks)
|
|
30
|
+
+ [Liveness and Readiness Probes](#liveness-and-readiness-probes)
|
|
31
|
+
+ [Registering Custom Readiness Checks](#registering-custom-readiness-checks)
|
|
32
|
+
+ [Customizing Health Paths](#customizing-health-paths)
|
|
29
33
|
* [Custom Serialization](#custom-serialization)
|
|
30
34
|
- [Advanced Setup](#advanced-setup)
|
|
31
35
|
* [Automatically import controllers](#automatically-import-controllers)
|
|
@@ -97,9 +101,10 @@ class UserController {
|
|
|
97
101
|
}
|
|
98
102
|
}
|
|
99
103
|
|
|
100
|
-
//
|
|
101
|
-
|
|
102
|
-
|
|
104
|
+
// registerApp returns the app back to you - use the returned app instance
|
|
105
|
+
// so Sapling can inject some post-startup hooks into the app.
|
|
106
|
+
// BUT, you still have full access of app to do whatever you want!
|
|
107
|
+
const app = Sapling.registerApp(express());
|
|
103
108
|
|
|
104
109
|
// @MiddlewareClass should be registered first before @Controller and should be registered in order
|
|
105
110
|
// @Injectable classes will automatically be formed into singletons by Sapling behind the scenes!
|
|
@@ -581,6 +586,57 @@ Sapling.Extras.swaggerAndOpenApi.setOpenApiPath("/api-spec.json");
|
|
|
581
586
|
Sapling.Extras.swaggerAndOpenApi.setSwaggerPath("/api-docs");
|
|
582
587
|
```
|
|
583
588
|
|
|
589
|
+
### Health Checks
|
|
590
|
+
|
|
591
|
+
#### Liveness and Readiness Probes
|
|
592
|
+
|
|
593
|
+
Register `DefaultHealthMiddleware` to expose two health endpoints:
|
|
594
|
+
|
|
595
|
+
- `GET /live` — **liveness probe**: returns `{ up: true }` once the server has started listening. Use this to tell your orchestrator the process is alive.
|
|
596
|
+
- `GET /ready` — **readiness probe**: returns `{ up: true }` once the server is live *and* all registered readiness checks pass. Use this to gate traffic until your app is fully initialized.
|
|
597
|
+
|
|
598
|
+
```typescript
|
|
599
|
+
import { Sapling, DefaultHealthMiddleware } from "@tahminator/sapling";
|
|
600
|
+
|
|
601
|
+
const app = Sapling.registerApp(express());
|
|
602
|
+
|
|
603
|
+
app.use(Sapling.resolve(DefaultHealthMiddleware));
|
|
604
|
+
|
|
605
|
+
app.listen(3000); // _markLive() is called automatically once the server is listening
|
|
606
|
+
```
|
|
607
|
+
|
|
608
|
+
> [!IMPORTANT]
|
|
609
|
+
> `Sapling.registerApp` returns `express.App` - please use that as your `app` object inside of your server. There is a small post-startup hook applied via a proxy, in which the liveness probe is enabled from such.
|
|
610
|
+
|
|
611
|
+
#### Registering Custom Readiness Checks
|
|
612
|
+
|
|
613
|
+
Inject `HealthRegistrar` into any `@Injectable` or controller to add readiness checks. A check is any function returning `boolean | Promise<boolean>`. If any check returns `false` (or throws), `/ready` reports `{ up: false }`.
|
|
614
|
+
|
|
615
|
+
```typescript
|
|
616
|
+
import { Injectable, HealthRegistrar } from "@tahminator/sapling";
|
|
617
|
+
|
|
618
|
+
@Injectable([HealthRegistrar])
|
|
619
|
+
class DatabaseService {
|
|
620
|
+
constructor(
|
|
621
|
+
private readonly db: Database,
|
|
622
|
+
private readonly healthRegistrar: HealthRegistrar,
|
|
623
|
+
) {
|
|
624
|
+
healthRegistrar.add(async () => {
|
|
625
|
+
return this.db.isConnected();
|
|
626
|
+
});
|
|
627
|
+
}
|
|
628
|
+
}
|
|
629
|
+
```
|
|
630
|
+
|
|
631
|
+
#### Customizing Health Paths
|
|
632
|
+
|
|
633
|
+
The default paths are `/live` and `/ready`. Override them before registering `DefaultHealthMiddleware`:
|
|
634
|
+
|
|
635
|
+
```typescript
|
|
636
|
+
Sapling.Extras.health.setLivePath("/healthz/live");
|
|
637
|
+
Sapling.Extras.health.setReadyPath("/healthz/ready");
|
|
638
|
+
```
|
|
639
|
+
|
|
584
640
|
### Custom Serialization
|
|
585
641
|
|
|
586
642
|
By default, Sapling uses `JSON.stringify` and `JSON.parse` for serialization. You can override these with custom serializers like [superjson](https://github.com/flightcontrolhq/superjson#readme) to automatically handle Dates, BigInts, and more:
|
package/dist/index.cjs
CHANGED
|
@@ -401,15 +401,15 @@ function __decorate(decorators, target, key, desc) {
|
|
|
401
401
|
//#region src/middleware/health/registrar.ts
|
|
402
402
|
let HealthRegistrar = class HealthRegistrar {
|
|
403
403
|
_checks;
|
|
404
|
-
|
|
404
|
+
_live;
|
|
405
405
|
constructor() {
|
|
406
406
|
this._checks = [];
|
|
407
|
-
this.
|
|
407
|
+
this._live = false;
|
|
408
408
|
}
|
|
409
409
|
/**
|
|
410
410
|
* Add a health check.
|
|
411
411
|
*
|
|
412
|
-
* Health checks will be used to determine whether service can
|
|
412
|
+
* Health checks will be used to determine whether service can serve traffic or not (a.k.a `readiness`).
|
|
413
413
|
*/
|
|
414
414
|
add(healthCheck) {
|
|
415
415
|
this._checks.push(healthCheck);
|
|
@@ -418,12 +418,21 @@ let HealthRegistrar = class HealthRegistrar {
|
|
|
418
418
|
* @internal used by Sapling library, used to determine once all
|
|
419
419
|
* checks have been registered and server is, at the very least, alive.
|
|
420
420
|
*/
|
|
421
|
-
|
|
422
|
-
this.
|
|
421
|
+
_markLive() {
|
|
422
|
+
this._live = true;
|
|
423
423
|
}
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
424
|
+
/**
|
|
425
|
+
* @internal
|
|
426
|
+
*/
|
|
427
|
+
async _liveness() {
|
|
428
|
+
return this._live;
|
|
429
|
+
}
|
|
430
|
+
/**
|
|
431
|
+
* @internal
|
|
432
|
+
*/
|
|
433
|
+
async _readiness() {
|
|
434
|
+
if (!this._live) return false;
|
|
435
|
+
return (await Promise.allSettled(this._checks.map((c) => c()))).every((r) => r.status === "fulfilled" && r.value);
|
|
427
436
|
}
|
|
428
437
|
};
|
|
429
438
|
HealthRegistrar = __decorate([Injectable()], HealthRegistrar);
|
|
@@ -433,8 +442,8 @@ const _settings = {
|
|
|
433
442
|
serialize: JSON.stringify,
|
|
434
443
|
deserialize: JSON.parse,
|
|
435
444
|
health: {
|
|
436
|
-
ready: { path: "/
|
|
437
|
-
live: { path: "/
|
|
445
|
+
ready: { path: "/readyz" },
|
|
446
|
+
live: { path: "/livez" }
|
|
438
447
|
},
|
|
439
448
|
doc: {
|
|
440
449
|
openApiPath: "/openapi.json",
|
|
@@ -516,7 +525,7 @@ var Sapling = class Sapling {
|
|
|
516
525
|
return function(...args) {
|
|
517
526
|
const server = originalListen.apply(target, args);
|
|
518
527
|
server.once("listening", () => {
|
|
519
|
-
Sapling.
|
|
528
|
+
Sapling._onPostStartup();
|
|
520
529
|
console.log("Sapling successfully initialized post-startup hooks on server start");
|
|
521
530
|
});
|
|
522
531
|
return server;
|
|
@@ -525,8 +534,12 @@ var Sapling = class Sapling {
|
|
|
525
534
|
return Reflect.get(target, prop, receiver);
|
|
526
535
|
} });
|
|
527
536
|
}
|
|
528
|
-
|
|
529
|
-
|
|
537
|
+
/**
|
|
538
|
+
* @internal
|
|
539
|
+
* visible for testing
|
|
540
|
+
*/
|
|
541
|
+
static _onPostStartup() {
|
|
542
|
+
_InjectableRegistry.get(HealthRegistrar)?._markLive();
|
|
530
543
|
}
|
|
531
544
|
/**
|
|
532
545
|
* Serialize a value into a JSON string.
|
|
@@ -605,7 +618,7 @@ var Sapling = class Sapling {
|
|
|
605
618
|
/**
|
|
606
619
|
* change default endpoint that ready endpoint will be served on.
|
|
607
620
|
*
|
|
608
|
-
* @default `/
|
|
621
|
+
* @default `/readyz`
|
|
609
622
|
*/
|
|
610
623
|
setReadyPath(path) {
|
|
611
624
|
_settings.health.ready.path = path;
|
|
@@ -613,7 +626,7 @@ var Sapling = class Sapling {
|
|
|
613
626
|
/**
|
|
614
627
|
* change default endpoint that live endpoint will be served on.
|
|
615
628
|
*
|
|
616
|
-
* @default `/
|
|
629
|
+
* @default `/livez`
|
|
617
630
|
*/
|
|
618
631
|
setLivePath(path) {
|
|
619
632
|
_settings.health.live.path = path;
|
|
@@ -1213,11 +1226,11 @@ let DefaultHealthMiddleware = class DefaultHealthMiddleware {
|
|
|
1213
1226
|
this.healthRegistrar = healthRegistrar;
|
|
1214
1227
|
}
|
|
1215
1228
|
async readiness(_request, _response, _next) {
|
|
1216
|
-
const up = await this.healthRegistrar.
|
|
1229
|
+
const up = await this.healthRegistrar._readiness();
|
|
1217
1230
|
return ResponseEntity.ok().body({ up });
|
|
1218
1231
|
}
|
|
1219
1232
|
async liveness(_request, _response, _next) {
|
|
1220
|
-
const up = await this.healthRegistrar.
|
|
1233
|
+
const up = await this.healthRegistrar._liveness();
|
|
1221
1234
|
return ResponseEntity.ok().body({ up });
|
|
1222
1235
|
}
|
|
1223
1236
|
};
|
package/dist/index.d.cts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import e, { ErrorRequestHandler, NextFunction, Request
|
|
1
|
+
import e, { ErrorRequestHandler, NextFunction, Request, RequestHandler, Response as Response$1, Router } from "express";
|
|
2
2
|
|
|
3
3
|
//#region src/html/404.d.ts
|
|
4
4
|
/**
|
|
@@ -26,7 +26,7 @@ type RouteDefinition = {
|
|
|
26
26
|
};
|
|
27
27
|
type Class<T> = new (...args: any[]) => T;
|
|
28
28
|
type HttpHeaders = Record<string, string>;
|
|
29
|
-
type ExpressMiddlewareFn = ($1: Request
|
|
29
|
+
type ExpressMiddlewareFn = ($1: Request, $2: Response$1, $3: NextFunction) => void;
|
|
30
30
|
//#endregion
|
|
31
31
|
//#region src/annotation/controller.d.ts
|
|
32
32
|
declare const _ControllerRegistry: WeakMap<Function, Router | ErrorRequestHandler>;
|
|
@@ -853,7 +853,11 @@ declare class Sapling {
|
|
|
853
853
|
* ```
|
|
854
854
|
*/
|
|
855
855
|
static registerApp(app: e.Express): e.Express;
|
|
856
|
-
|
|
856
|
+
/**
|
|
857
|
+
* @internal
|
|
858
|
+
* visible for testing
|
|
859
|
+
*/
|
|
860
|
+
static _onPostStartup(): void;
|
|
857
861
|
/**
|
|
858
862
|
* Serialize a value into a JSON string.
|
|
859
863
|
*
|
|
@@ -917,13 +921,13 @@ declare class Sapling {
|
|
|
917
921
|
/**
|
|
918
922
|
* change default endpoint that ready endpoint will be served on.
|
|
919
923
|
*
|
|
920
|
-
* @default `/
|
|
924
|
+
* @default `/readyz`
|
|
921
925
|
*/
|
|
922
926
|
setReadyPath(this: void, path: string): void;
|
|
923
927
|
/**
|
|
924
928
|
* change default endpoint that live endpoint will be served on.
|
|
925
929
|
*
|
|
926
|
-
* @default `/
|
|
930
|
+
* @default `/livez`
|
|
927
931
|
*/
|
|
928
932
|
setLivePath(this: void, path: string): void;
|
|
929
933
|
};
|
|
@@ -947,7 +951,7 @@ declare class Sapling {
|
|
|
947
951
|
* }
|
|
948
952
|
* ```
|
|
949
953
|
*/
|
|
950
|
-
static chainHandlers(this: void, handlers: RequestHandler[], request: Request
|
|
954
|
+
static chainHandlers(this: void, handlers: RequestHandler[], request: Request, response: Response$1, next: NextFunction, index?: number): void;
|
|
951
955
|
}
|
|
952
956
|
//#endregion
|
|
953
957
|
//#region src/annotation/validator.d.ts
|
|
@@ -1003,7 +1007,7 @@ declare function _getControllerSchema(ctor: Function): ControllerSchemaDefinitio
|
|
|
1003
1007
|
* If the default is not suitable, you may also easily write your own.
|
|
1004
1008
|
*/
|
|
1005
1009
|
declare class DefaultBaseErrorMiddleware {
|
|
1006
|
-
handle(err: unknown, _request: Request
|
|
1010
|
+
handle(err: unknown, _request: Request, _response: Response$1, _next: NextFunction): ResponseEntity<{
|
|
1007
1011
|
message: string;
|
|
1008
1012
|
}>;
|
|
1009
1013
|
}
|
|
@@ -1014,7 +1018,7 @@ declare class DefaultBaseErrorMiddleware {
|
|
|
1014
1018
|
* If the default is not suitable, you may also easily write your own.
|
|
1015
1019
|
*/
|
|
1016
1020
|
declare class DefaultParserErrorMiddleware {
|
|
1017
|
-
handle(err: unknown, _request: Request
|
|
1021
|
+
handle(err: unknown, _request: Request, _response: Response$1, next: NextFunction): ResponseEntity<{
|
|
1018
1022
|
message: string;
|
|
1019
1023
|
}> | undefined;
|
|
1020
1024
|
}
|
|
@@ -1025,7 +1029,7 @@ declare class DefaultParserErrorMiddleware {
|
|
|
1025
1029
|
* If the default is not suitable, you may also easily write your own.
|
|
1026
1030
|
*/
|
|
1027
1031
|
declare class DefaultResponseStatusErrorMiddleware {
|
|
1028
|
-
handle(err: unknown, _request: Request
|
|
1032
|
+
handle(err: unknown, _request: Request, _response: Response$1, next: NextFunction): ResponseEntity<{
|
|
1029
1033
|
message: string;
|
|
1030
1034
|
}> | undefined;
|
|
1031
1035
|
}
|
|
@@ -1048,7 +1052,7 @@ declare class DefaultResponseStatusErrorMiddleware {
|
|
|
1048
1052
|
* ```
|
|
1049
1053
|
*/
|
|
1050
1054
|
declare class DefaultOpenApiMiddleware {
|
|
1051
|
-
handle(_request: Request
|
|
1055
|
+
handle(_request: Request, _response: Response$1, _next: NextFunction): ResponseEntity<OpenAPIV3.Document<{}>>;
|
|
1052
1056
|
}
|
|
1053
1057
|
//#endregion
|
|
1054
1058
|
//#region src/middleware/default/swagger/index.d.ts
|
|
@@ -1070,7 +1074,7 @@ declare class DefaultOpenApiMiddleware {
|
|
|
1070
1074
|
*/
|
|
1071
1075
|
declare class Serve {
|
|
1072
1076
|
private readonly handlers;
|
|
1073
|
-
handle(request: Request
|
|
1077
|
+
handle(request: Request, response: Response$1, next: NextFunction): void;
|
|
1074
1078
|
}
|
|
1075
1079
|
/**
|
|
1076
1080
|
* Enable the serving of the Swagger endpoint used to serve the OpenAPI spec generated by Sapling.
|
|
@@ -1091,7 +1095,7 @@ declare class Serve {
|
|
|
1091
1095
|
declare class Setup {
|
|
1092
1096
|
private readonly handler;
|
|
1093
1097
|
constructor();
|
|
1094
|
-
handle(request: Request
|
|
1098
|
+
handle(request: Request, response: Response$1, next: NextFunction): unknown;
|
|
1095
1099
|
}
|
|
1096
1100
|
/**
|
|
1097
1101
|
* Enable the serving of the Swagger endpoint used to serve the OpenAPI spec generated by Sapling.
|
|
@@ -1118,20 +1122,27 @@ declare const DefaultSwaggerMiddleware: {
|
|
|
1118
1122
|
type HealthCheck = () => boolean | Promise<boolean>;
|
|
1119
1123
|
declare class HealthRegistrar {
|
|
1120
1124
|
private _checks;
|
|
1121
|
-
private
|
|
1125
|
+
private _live;
|
|
1122
1126
|
constructor();
|
|
1123
1127
|
/**
|
|
1124
1128
|
* Add a health check.
|
|
1125
1129
|
*
|
|
1126
|
-
* Health checks will be used to determine whether service can
|
|
1130
|
+
* Health checks will be used to determine whether service can serve traffic or not (a.k.a `readiness`).
|
|
1127
1131
|
*/
|
|
1128
1132
|
add(healthCheck: HealthCheck): void;
|
|
1129
1133
|
/**
|
|
1130
1134
|
* @internal used by Sapling library, used to determine once all
|
|
1131
1135
|
* checks have been registered and server is, at the very least, alive.
|
|
1132
1136
|
*/
|
|
1133
|
-
|
|
1134
|
-
|
|
1137
|
+
_markLive(): void;
|
|
1138
|
+
/**
|
|
1139
|
+
* @internal
|
|
1140
|
+
*/
|
|
1141
|
+
_liveness(): Promise<boolean>;
|
|
1142
|
+
/**
|
|
1143
|
+
* @internal
|
|
1144
|
+
*/
|
|
1145
|
+
_readiness(): Promise<boolean>;
|
|
1135
1146
|
}
|
|
1136
1147
|
//#endregion
|
|
1137
1148
|
//#region src/middleware/default/health/index.d.ts
|
|
@@ -1139,15 +1150,14 @@ declare class HealthRegistrar {
|
|
|
1139
1150
|
* Enable the serving of `ready` and `live` endpoints.
|
|
1140
1151
|
*
|
|
1141
1152
|
* Configure any middleware-specific settings with `Sapling.Extras.health`
|
|
1142
|
-
* ```
|
|
1143
1153
|
*/
|
|
1144
1154
|
declare class DefaultHealthMiddleware {
|
|
1145
1155
|
private readonly healthRegistrar;
|
|
1146
1156
|
constructor(healthRegistrar: HealthRegistrar);
|
|
1147
|
-
readiness(_request: Request, _response: Response, _next: NextFunction): Promise<ResponseEntity<{
|
|
1157
|
+
readiness(_request: Request, _response: Response$1, _next: NextFunction): Promise<ResponseEntity<{
|
|
1148
1158
|
up: boolean;
|
|
1149
1159
|
}>>;
|
|
1150
|
-
liveness(_request: Request, _response: Response, _next: NextFunction): Promise<ResponseEntity<{
|
|
1160
|
+
liveness(_request: Request, _response: Response$1, _next: NextFunction): Promise<ResponseEntity<{
|
|
1151
1161
|
up: boolean;
|
|
1152
1162
|
}>>;
|
|
1153
1163
|
}
|
package/dist/index.d.mts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import e, { ErrorRequestHandler, NextFunction, Request
|
|
1
|
+
import e, { ErrorRequestHandler, NextFunction, Request, RequestHandler, Response as Response$1, Router } from "express";
|
|
2
2
|
|
|
3
3
|
//#region src/html/404.d.ts
|
|
4
4
|
/**
|
|
@@ -26,7 +26,7 @@ type RouteDefinition = {
|
|
|
26
26
|
};
|
|
27
27
|
type Class<T> = new (...args: any[]) => T;
|
|
28
28
|
type HttpHeaders = Record<string, string>;
|
|
29
|
-
type ExpressMiddlewareFn = ($1: Request
|
|
29
|
+
type ExpressMiddlewareFn = ($1: Request, $2: Response$1, $3: NextFunction) => void;
|
|
30
30
|
//#endregion
|
|
31
31
|
//#region src/annotation/controller.d.ts
|
|
32
32
|
declare const _ControllerRegistry: WeakMap<Function, Router | ErrorRequestHandler>;
|
|
@@ -853,7 +853,11 @@ declare class Sapling {
|
|
|
853
853
|
* ```
|
|
854
854
|
*/
|
|
855
855
|
static registerApp(app: e.Express): e.Express;
|
|
856
|
-
|
|
856
|
+
/**
|
|
857
|
+
* @internal
|
|
858
|
+
* visible for testing
|
|
859
|
+
*/
|
|
860
|
+
static _onPostStartup(): void;
|
|
857
861
|
/**
|
|
858
862
|
* Serialize a value into a JSON string.
|
|
859
863
|
*
|
|
@@ -917,13 +921,13 @@ declare class Sapling {
|
|
|
917
921
|
/**
|
|
918
922
|
* change default endpoint that ready endpoint will be served on.
|
|
919
923
|
*
|
|
920
|
-
* @default `/
|
|
924
|
+
* @default `/readyz`
|
|
921
925
|
*/
|
|
922
926
|
setReadyPath(this: void, path: string): void;
|
|
923
927
|
/**
|
|
924
928
|
* change default endpoint that live endpoint will be served on.
|
|
925
929
|
*
|
|
926
|
-
* @default `/
|
|
930
|
+
* @default `/livez`
|
|
927
931
|
*/
|
|
928
932
|
setLivePath(this: void, path: string): void;
|
|
929
933
|
};
|
|
@@ -947,7 +951,7 @@ declare class Sapling {
|
|
|
947
951
|
* }
|
|
948
952
|
* ```
|
|
949
953
|
*/
|
|
950
|
-
static chainHandlers(this: void, handlers: RequestHandler[], request: Request
|
|
954
|
+
static chainHandlers(this: void, handlers: RequestHandler[], request: Request, response: Response$1, next: NextFunction, index?: number): void;
|
|
951
955
|
}
|
|
952
956
|
//#endregion
|
|
953
957
|
//#region src/annotation/validator.d.ts
|
|
@@ -1003,7 +1007,7 @@ declare function _getControllerSchema(ctor: Function): ControllerSchemaDefinitio
|
|
|
1003
1007
|
* If the default is not suitable, you may also easily write your own.
|
|
1004
1008
|
*/
|
|
1005
1009
|
declare class DefaultBaseErrorMiddleware {
|
|
1006
|
-
handle(err: unknown, _request: Request
|
|
1010
|
+
handle(err: unknown, _request: Request, _response: Response$1, _next: NextFunction): ResponseEntity<{
|
|
1007
1011
|
message: string;
|
|
1008
1012
|
}>;
|
|
1009
1013
|
}
|
|
@@ -1014,7 +1018,7 @@ declare class DefaultBaseErrorMiddleware {
|
|
|
1014
1018
|
* If the default is not suitable, you may also easily write your own.
|
|
1015
1019
|
*/
|
|
1016
1020
|
declare class DefaultParserErrorMiddleware {
|
|
1017
|
-
handle(err: unknown, _request: Request
|
|
1021
|
+
handle(err: unknown, _request: Request, _response: Response$1, next: NextFunction): ResponseEntity<{
|
|
1018
1022
|
message: string;
|
|
1019
1023
|
}> | undefined;
|
|
1020
1024
|
}
|
|
@@ -1025,7 +1029,7 @@ declare class DefaultParserErrorMiddleware {
|
|
|
1025
1029
|
* If the default is not suitable, you may also easily write your own.
|
|
1026
1030
|
*/
|
|
1027
1031
|
declare class DefaultResponseStatusErrorMiddleware {
|
|
1028
|
-
handle(err: unknown, _request: Request
|
|
1032
|
+
handle(err: unknown, _request: Request, _response: Response$1, next: NextFunction): ResponseEntity<{
|
|
1029
1033
|
message: string;
|
|
1030
1034
|
}> | undefined;
|
|
1031
1035
|
}
|
|
@@ -1048,7 +1052,7 @@ declare class DefaultResponseStatusErrorMiddleware {
|
|
|
1048
1052
|
* ```
|
|
1049
1053
|
*/
|
|
1050
1054
|
declare class DefaultOpenApiMiddleware {
|
|
1051
|
-
handle(_request: Request
|
|
1055
|
+
handle(_request: Request, _response: Response$1, _next: NextFunction): ResponseEntity<OpenAPIV3.Document<{}>>;
|
|
1052
1056
|
}
|
|
1053
1057
|
//#endregion
|
|
1054
1058
|
//#region src/middleware/default/swagger/index.d.ts
|
|
@@ -1070,7 +1074,7 @@ declare class DefaultOpenApiMiddleware {
|
|
|
1070
1074
|
*/
|
|
1071
1075
|
declare class Serve {
|
|
1072
1076
|
private readonly handlers;
|
|
1073
|
-
handle(request: Request
|
|
1077
|
+
handle(request: Request, response: Response$1, next: NextFunction): void;
|
|
1074
1078
|
}
|
|
1075
1079
|
/**
|
|
1076
1080
|
* Enable the serving of the Swagger endpoint used to serve the OpenAPI spec generated by Sapling.
|
|
@@ -1091,7 +1095,7 @@ declare class Serve {
|
|
|
1091
1095
|
declare class Setup {
|
|
1092
1096
|
private readonly handler;
|
|
1093
1097
|
constructor();
|
|
1094
|
-
handle(request: Request
|
|
1098
|
+
handle(request: Request, response: Response$1, next: NextFunction): unknown;
|
|
1095
1099
|
}
|
|
1096
1100
|
/**
|
|
1097
1101
|
* Enable the serving of the Swagger endpoint used to serve the OpenAPI spec generated by Sapling.
|
|
@@ -1118,20 +1122,27 @@ declare const DefaultSwaggerMiddleware: {
|
|
|
1118
1122
|
type HealthCheck = () => boolean | Promise<boolean>;
|
|
1119
1123
|
declare class HealthRegistrar {
|
|
1120
1124
|
private _checks;
|
|
1121
|
-
private
|
|
1125
|
+
private _live;
|
|
1122
1126
|
constructor();
|
|
1123
1127
|
/**
|
|
1124
1128
|
* Add a health check.
|
|
1125
1129
|
*
|
|
1126
|
-
* Health checks will be used to determine whether service can
|
|
1130
|
+
* Health checks will be used to determine whether service can serve traffic or not (a.k.a `readiness`).
|
|
1127
1131
|
*/
|
|
1128
1132
|
add(healthCheck: HealthCheck): void;
|
|
1129
1133
|
/**
|
|
1130
1134
|
* @internal used by Sapling library, used to determine once all
|
|
1131
1135
|
* checks have been registered and server is, at the very least, alive.
|
|
1132
1136
|
*/
|
|
1133
|
-
|
|
1134
|
-
|
|
1137
|
+
_markLive(): void;
|
|
1138
|
+
/**
|
|
1139
|
+
* @internal
|
|
1140
|
+
*/
|
|
1141
|
+
_liveness(): Promise<boolean>;
|
|
1142
|
+
/**
|
|
1143
|
+
* @internal
|
|
1144
|
+
*/
|
|
1145
|
+
_readiness(): Promise<boolean>;
|
|
1135
1146
|
}
|
|
1136
1147
|
//#endregion
|
|
1137
1148
|
//#region src/middleware/default/health/index.d.ts
|
|
@@ -1139,15 +1150,14 @@ declare class HealthRegistrar {
|
|
|
1139
1150
|
* Enable the serving of `ready` and `live` endpoints.
|
|
1140
1151
|
*
|
|
1141
1152
|
* Configure any middleware-specific settings with `Sapling.Extras.health`
|
|
1142
|
-
* ```
|
|
1143
1153
|
*/
|
|
1144
1154
|
declare class DefaultHealthMiddleware {
|
|
1145
1155
|
private readonly healthRegistrar;
|
|
1146
1156
|
constructor(healthRegistrar: HealthRegistrar);
|
|
1147
|
-
readiness(_request: Request, _response: Response, _next: NextFunction): Promise<ResponseEntity<{
|
|
1157
|
+
readiness(_request: Request, _response: Response$1, _next: NextFunction): Promise<ResponseEntity<{
|
|
1148
1158
|
up: boolean;
|
|
1149
1159
|
}>>;
|
|
1150
|
-
liveness(_request: Request, _response: Response, _next: NextFunction): Promise<ResponseEntity<{
|
|
1160
|
+
liveness(_request: Request, _response: Response$1, _next: NextFunction): Promise<ResponseEntity<{
|
|
1151
1161
|
up: boolean;
|
|
1152
1162
|
}>>;
|
|
1153
1163
|
}
|
package/dist/index.mjs
CHANGED
|
@@ -376,15 +376,15 @@ function __decorate(decorators, target, key, desc) {
|
|
|
376
376
|
//#region src/middleware/health/registrar.ts
|
|
377
377
|
let HealthRegistrar = class HealthRegistrar {
|
|
378
378
|
_checks;
|
|
379
|
-
|
|
379
|
+
_live;
|
|
380
380
|
constructor() {
|
|
381
381
|
this._checks = [];
|
|
382
|
-
this.
|
|
382
|
+
this._live = false;
|
|
383
383
|
}
|
|
384
384
|
/**
|
|
385
385
|
* Add a health check.
|
|
386
386
|
*
|
|
387
|
-
* Health checks will be used to determine whether service can
|
|
387
|
+
* Health checks will be used to determine whether service can serve traffic or not (a.k.a `readiness`).
|
|
388
388
|
*/
|
|
389
389
|
add(healthCheck) {
|
|
390
390
|
this._checks.push(healthCheck);
|
|
@@ -393,12 +393,21 @@ let HealthRegistrar = class HealthRegistrar {
|
|
|
393
393
|
* @internal used by Sapling library, used to determine once all
|
|
394
394
|
* checks have been registered and server is, at the very least, alive.
|
|
395
395
|
*/
|
|
396
|
-
|
|
397
|
-
this.
|
|
396
|
+
_markLive() {
|
|
397
|
+
this._live = true;
|
|
398
398
|
}
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
399
|
+
/**
|
|
400
|
+
* @internal
|
|
401
|
+
*/
|
|
402
|
+
async _liveness() {
|
|
403
|
+
return this._live;
|
|
404
|
+
}
|
|
405
|
+
/**
|
|
406
|
+
* @internal
|
|
407
|
+
*/
|
|
408
|
+
async _readiness() {
|
|
409
|
+
if (!this._live) return false;
|
|
410
|
+
return (await Promise.allSettled(this._checks.map((c) => c()))).every((r) => r.status === "fulfilled" && r.value);
|
|
402
411
|
}
|
|
403
412
|
};
|
|
404
413
|
HealthRegistrar = __decorate([Injectable()], HealthRegistrar);
|
|
@@ -408,8 +417,8 @@ const _settings = {
|
|
|
408
417
|
serialize: JSON.stringify,
|
|
409
418
|
deserialize: JSON.parse,
|
|
410
419
|
health: {
|
|
411
|
-
ready: { path: "/
|
|
412
|
-
live: { path: "/
|
|
420
|
+
ready: { path: "/readyz" },
|
|
421
|
+
live: { path: "/livez" }
|
|
413
422
|
},
|
|
414
423
|
doc: {
|
|
415
424
|
openApiPath: "/openapi.json",
|
|
@@ -491,7 +500,7 @@ var Sapling = class Sapling {
|
|
|
491
500
|
return function(...args) {
|
|
492
501
|
const server = originalListen.apply(target, args);
|
|
493
502
|
server.once("listening", () => {
|
|
494
|
-
Sapling.
|
|
503
|
+
Sapling._onPostStartup();
|
|
495
504
|
console.log("Sapling successfully initialized post-startup hooks on server start");
|
|
496
505
|
});
|
|
497
506
|
return server;
|
|
@@ -500,8 +509,12 @@ var Sapling = class Sapling {
|
|
|
500
509
|
return Reflect.get(target, prop, receiver);
|
|
501
510
|
} });
|
|
502
511
|
}
|
|
503
|
-
|
|
504
|
-
|
|
512
|
+
/**
|
|
513
|
+
* @internal
|
|
514
|
+
* visible for testing
|
|
515
|
+
*/
|
|
516
|
+
static _onPostStartup() {
|
|
517
|
+
_InjectableRegistry.get(HealthRegistrar)?._markLive();
|
|
505
518
|
}
|
|
506
519
|
/**
|
|
507
520
|
* Serialize a value into a JSON string.
|
|
@@ -580,7 +593,7 @@ var Sapling = class Sapling {
|
|
|
580
593
|
/**
|
|
581
594
|
* change default endpoint that ready endpoint will be served on.
|
|
582
595
|
*
|
|
583
|
-
* @default `/
|
|
596
|
+
* @default `/readyz`
|
|
584
597
|
*/
|
|
585
598
|
setReadyPath(path) {
|
|
586
599
|
_settings.health.ready.path = path;
|
|
@@ -588,7 +601,7 @@ var Sapling = class Sapling {
|
|
|
588
601
|
/**
|
|
589
602
|
* change default endpoint that live endpoint will be served on.
|
|
590
603
|
*
|
|
591
|
-
* @default `/
|
|
604
|
+
* @default `/livez`
|
|
592
605
|
*/
|
|
593
606
|
setLivePath(path) {
|
|
594
607
|
_settings.health.live.path = path;
|
|
@@ -1188,11 +1201,11 @@ let DefaultHealthMiddleware = class DefaultHealthMiddleware {
|
|
|
1188
1201
|
this.healthRegistrar = healthRegistrar;
|
|
1189
1202
|
}
|
|
1190
1203
|
async readiness(_request, _response, _next) {
|
|
1191
|
-
const up = await this.healthRegistrar.
|
|
1204
|
+
const up = await this.healthRegistrar._readiness();
|
|
1192
1205
|
return ResponseEntity.ok().body({ up });
|
|
1193
1206
|
}
|
|
1194
1207
|
async liveness(_request, _response, _next) {
|
|
1195
|
-
const up = await this.healthRegistrar.
|
|
1208
|
+
const up = await this.healthRegistrar._liveness();
|
|
1196
1209
|
return ResponseEntity.ok().body({ up });
|
|
1197
1210
|
}
|
|
1198
1211
|
};
|