@tahminator/sapling 2.1.0-beta.0e8a97e8 → 2.1.0-beta.419f8e67

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -401,19 +401,37 @@ function __decorate(decorators, target, key, desc) {
401
401
  //#region src/middleware/health/registrar.ts
402
402
  let HealthRegistrar = class HealthRegistrar {
403
403
  _checks;
404
- _sealed;
404
+ _ready;
405
405
  constructor() {
406
406
  this._checks = [];
407
- this._sealed = false;
407
+ this._ready = false;
408
408
  }
409
+ /**
410
+ * Add a health check.
411
+ *
412
+ * Health checks will be used to determine whether service can serve traffic or not (a.k.a `liveness`).
413
+ */
409
414
  add(healthCheck) {
410
415
  this._checks.push(healthCheck);
411
416
  }
412
- seal() {
413
- this._sealed = true;
417
+ /**
418
+ * @internal used by Sapling library, used to determine once all
419
+ * checks have been registered and server is, at the very least, alive.
420
+ */
421
+ _markReady() {
422
+ this._ready = true;
423
+ }
424
+ /**
425
+ * @internal
426
+ */
427
+ async ready() {
428
+ return this._ready;
414
429
  }
415
- async check() {
416
- if (!this._sealed) return false;
430
+ /**
431
+ * @internal
432
+ */
433
+ async live() {
434
+ if (!this._ready) return false;
417
435
  return (await Promise.all(this._checks.map((c) => c()))).every((c) => c === true);
418
436
  }
419
437
  };
@@ -423,7 +441,10 @@ HealthRegistrar = __decorate([Injectable()], HealthRegistrar);
423
441
  const _settings = {
424
442
  serialize: JSON.stringify,
425
443
  deserialize: JSON.parse,
426
- health: { path: "/up" },
444
+ health: {
445
+ ready: { path: "/ready" },
446
+ live: { path: "/live" }
447
+ },
427
448
  doc: {
428
449
  openApiPath: "/openapi.json",
429
450
  swaggerPath: "/swagger.html",
@@ -491,15 +512,30 @@ var Sapling = class Sapling {
491
512
  * import { Sapling } from "@tahminator/sapling";
492
513
  * import express from "express";
493
514
  *
494
- * const app = express();
495
- *
496
- * app.registerApp(app);
515
+ * // returns the exact same `express.App` type back to you!
516
+ * const app = Sapling.registerApp(express());
497
517
  * ```
498
518
  */
499
519
  static registerApp(app) {
500
520
  app.use(express.default.text({ type: "application/json" }));
501
521
  app.use(Sapling.json());
502
- _InjectableRegistry.get(HealthRegistrar)?.seal();
522
+ return new Proxy(app, { get(target, prop, receiver) {
523
+ if (prop === "listen") {
524
+ const originalListen = target[prop];
525
+ return function(...args) {
526
+ const server = originalListen.apply(target, args);
527
+ server.once("listening", () => {
528
+ Sapling.onPostStartup();
529
+ console.log("Sapling successfully initialized post-startup hooks on server start");
530
+ });
531
+ return server;
532
+ };
533
+ }
534
+ return Reflect.get(target, prop, receiver);
535
+ } });
536
+ }
537
+ static onPostStartup() {
538
+ _InjectableRegistry.get(HealthRegistrar)?._markReady();
503
539
  }
504
540
  /**
505
541
  * Serialize a value into a JSON string.
@@ -540,37 +576,59 @@ var Sapling = class Sapling {
540
576
  /**
541
577
  * Modify extra settings
542
578
  */
543
- static Extras = {
544
- /**
545
- * Modify default settings applied to OpenAPI & Swagger
546
- */
547
- swaggerAndOpenApi: {
579
+ static Extras = {
548
580
  /**
549
- * Set base OpenAPI metadata values.
550
- *
551
- * @default { title: "API", version: "1.0.0" }
581
+ * Modify default settings applied to OpenAPI & Swagger
552
582
  */
553
- setMetadata(metadata) {
554
- _settings.doc.metadata = metadata;
555
- },
556
- /**
557
- * change default endpoint that will serve OpenAPI spec.
558
- * Swagger will also load this endpoint on load.
559
- *
560
- * @default `/openapi.json`
561
- */
562
- setOpenApiPath(path) {
563
- _settings.doc.openApiPath = path;
583
+ swaggerAndOpenApi: {
584
+ /**
585
+ * Set base OpenAPI metadata values.
586
+ *
587
+ * @default { title: "API", version: "1.0.0" }
588
+ */
589
+ setMetadata(metadata) {
590
+ _settings.doc.metadata = metadata;
591
+ },
592
+ /**
593
+ * change default endpoint that will serve OpenAPI spec.
594
+ * Swagger will also load this endpoint on load.
595
+ *
596
+ * @default `/openapi.json`
597
+ */
598
+ setOpenApiPath(path) {
599
+ _settings.doc.openApiPath = path;
600
+ },
601
+ /**
602
+ * change Swagger endpoint.
603
+ *
604
+ * @default `/swagger.html`
605
+ */
606
+ setSwaggerPath(path) {
607
+ _settings.doc.swaggerPath = path;
608
+ }
564
609
  },
565
610
  /**
566
- * change Swagger endpoint.
567
- *
568
- * @default `/swagger.html`
611
+ * Modify default settings applied to health / readiness / liveness.
569
612
  */
570
- setSwaggerPath(path) {
571
- _settings.doc.swaggerPath = path;
613
+ health: {
614
+ /**
615
+ * change default endpoint that ready endpoint will be served on.
616
+ *
617
+ * @default `/ready`
618
+ */
619
+ setReadyPath(path) {
620
+ _settings.health.ready.path = path;
621
+ },
622
+ /**
623
+ * change default endpoint that live endpoint will be served on.
624
+ *
625
+ * @default `/live`
626
+ */
627
+ setLivePath(path) {
628
+ _settings.health.live.path = path;
629
+ }
572
630
  }
573
- } };
631
+ };
574
632
  /**
575
633
  * This method can be used in a `@MiddlewareClass` to register any libraries
576
634
  * that expect you to register multiple registers at once. An example is `swagger-ui-express`
@@ -1163,12 +1221,17 @@ let DefaultHealthMiddleware = class DefaultHealthMiddleware {
1163
1221
  constructor(healthRegistrar) {
1164
1222
  this.healthRegistrar = healthRegistrar;
1165
1223
  }
1166
- async serve(_request, _response, _next) {
1167
- const up = await this.healthRegistrar.check();
1224
+ async readiness(_request, _response, _next) {
1225
+ const up = await this.healthRegistrar.ready();
1226
+ return ResponseEntity.ok().body({ up });
1227
+ }
1228
+ async liveness(_request, _response, _next) {
1229
+ const up = await this.healthRegistrar.live();
1168
1230
  return ResponseEntity.ok().body({ up });
1169
1231
  }
1170
1232
  };
1171
- __decorate([GET(_settings.health.path)], DefaultHealthMiddleware.prototype, "serve", null);
1233
+ __decorate([GET(_settings.health.ready.path)], DefaultHealthMiddleware.prototype, "readiness", null);
1234
+ __decorate([GET(_settings.health.live.path)], DefaultHealthMiddleware.prototype, "liveness", null);
1172
1235
  DefaultHealthMiddleware = __decorate([MiddlewareClass({ deps: [HealthRegistrar] })], DefaultHealthMiddleware);
1173
1236
  //#endregion
1174
1237
  exports.Controller = Controller;
package/dist/index.d.cts CHANGED
@@ -795,7 +795,12 @@ type Settings = {
795
795
  serialize: (value: any) => string;
796
796
  deserialize: (value: string) => any;
797
797
  health: {
798
- path: string;
798
+ ready: {
799
+ path: string;
800
+ };
801
+ live: {
802
+ path: string;
803
+ };
799
804
  };
800
805
  doc: {
801
806
  openApiPath: string;
@@ -843,12 +848,12 @@ declare class Sapling {
843
848
  * import { Sapling } from "@tahminator/sapling";
844
849
  * import express from "express";
845
850
  *
846
- * const app = express();
847
- *
848
- * app.registerApp(app);
851
+ * // returns the exact same `express.App` type back to you!
852
+ * const app = Sapling.registerApp(express());
849
853
  * ```
850
854
  */
851
- static registerApp(app: e.Express): void;
855
+ static registerApp(app: e.Express): e.Express;
856
+ static onPostStartup(): void;
852
857
  /**
853
858
  * Serialize a value into a JSON string.
854
859
  *
@@ -905,6 +910,23 @@ declare class Sapling {
905
910
  */
906
911
  setSwaggerPath(this: void, path: string): void;
907
912
  };
913
+ /**
914
+ * Modify default settings applied to health / readiness / liveness.
915
+ */
916
+ health: {
917
+ /**
918
+ * change default endpoint that ready endpoint will be served on.
919
+ *
920
+ * @default `/ready`
921
+ */
922
+ setReadyPath(this: void, path: string): void;
923
+ /**
924
+ * change default endpoint that live endpoint will be served on.
925
+ *
926
+ * @default `/live`
927
+ */
928
+ setLivePath(this: void, path: string): void;
929
+ };
908
930
  };
909
931
  /**
910
932
  * This method can be used in a `@MiddlewareClass` to register any libraries
@@ -1096,18 +1118,43 @@ declare const DefaultSwaggerMiddleware: {
1096
1118
  type HealthCheck = () => boolean | Promise<boolean>;
1097
1119
  declare class HealthRegistrar {
1098
1120
  private _checks;
1099
- private _sealed;
1121
+ private _ready;
1100
1122
  constructor();
1123
+ /**
1124
+ * Add a health check.
1125
+ *
1126
+ * Health checks will be used to determine whether service can serve traffic or not (a.k.a `liveness`).
1127
+ */
1101
1128
  add(healthCheck: HealthCheck): void;
1102
- seal(): void;
1103
- check(): Promise<boolean>;
1129
+ /**
1130
+ * @internal used by Sapling library, used to determine once all
1131
+ * checks have been registered and server is, at the very least, alive.
1132
+ */
1133
+ _markReady(): void;
1134
+ /**
1135
+ * @internal
1136
+ */
1137
+ ready(): Promise<boolean>;
1138
+ /**
1139
+ * @internal
1140
+ */
1141
+ live(): Promise<boolean>;
1104
1142
  }
1105
1143
  //#endregion
1106
1144
  //#region src/middleware/default/health/index.d.ts
1145
+ /**
1146
+ * Enable the serving of `ready` and `live` endpoints.
1147
+ *
1148
+ * Configure any middleware-specific settings with `Sapling.Extras.health`
1149
+ * ```
1150
+ */
1107
1151
  declare class DefaultHealthMiddleware {
1108
1152
  private readonly healthRegistrar;
1109
1153
  constructor(healthRegistrar: HealthRegistrar);
1110
- serve(_request: Request, _response: Response, _next: NextFunction): Promise<ResponseEntity<{
1154
+ readiness(_request: Request, _response: Response, _next: NextFunction): Promise<ResponseEntity<{
1155
+ up: boolean;
1156
+ }>>;
1157
+ liveness(_request: Request, _response: Response, _next: NextFunction): Promise<ResponseEntity<{
1111
1158
  up: boolean;
1112
1159
  }>>;
1113
1160
  }
package/dist/index.d.mts CHANGED
@@ -795,7 +795,12 @@ type Settings = {
795
795
  serialize: (value: any) => string;
796
796
  deserialize: (value: string) => any;
797
797
  health: {
798
- path: string;
798
+ ready: {
799
+ path: string;
800
+ };
801
+ live: {
802
+ path: string;
803
+ };
799
804
  };
800
805
  doc: {
801
806
  openApiPath: string;
@@ -843,12 +848,12 @@ declare class Sapling {
843
848
  * import { Sapling } from "@tahminator/sapling";
844
849
  * import express from "express";
845
850
  *
846
- * const app = express();
847
- *
848
- * app.registerApp(app);
851
+ * // returns the exact same `express.App` type back to you!
852
+ * const app = Sapling.registerApp(express());
849
853
  * ```
850
854
  */
851
- static registerApp(app: e.Express): void;
855
+ static registerApp(app: e.Express): e.Express;
856
+ static onPostStartup(): void;
852
857
  /**
853
858
  * Serialize a value into a JSON string.
854
859
  *
@@ -905,6 +910,23 @@ declare class Sapling {
905
910
  */
906
911
  setSwaggerPath(this: void, path: string): void;
907
912
  };
913
+ /**
914
+ * Modify default settings applied to health / readiness / liveness.
915
+ */
916
+ health: {
917
+ /**
918
+ * change default endpoint that ready endpoint will be served on.
919
+ *
920
+ * @default `/ready`
921
+ */
922
+ setReadyPath(this: void, path: string): void;
923
+ /**
924
+ * change default endpoint that live endpoint will be served on.
925
+ *
926
+ * @default `/live`
927
+ */
928
+ setLivePath(this: void, path: string): void;
929
+ };
908
930
  };
909
931
  /**
910
932
  * This method can be used in a `@MiddlewareClass` to register any libraries
@@ -1096,18 +1118,43 @@ declare const DefaultSwaggerMiddleware: {
1096
1118
  type HealthCheck = () => boolean | Promise<boolean>;
1097
1119
  declare class HealthRegistrar {
1098
1120
  private _checks;
1099
- private _sealed;
1121
+ private _ready;
1100
1122
  constructor();
1123
+ /**
1124
+ * Add a health check.
1125
+ *
1126
+ * Health checks will be used to determine whether service can serve traffic or not (a.k.a `liveness`).
1127
+ */
1101
1128
  add(healthCheck: HealthCheck): void;
1102
- seal(): void;
1103
- check(): Promise<boolean>;
1129
+ /**
1130
+ * @internal used by Sapling library, used to determine once all
1131
+ * checks have been registered and server is, at the very least, alive.
1132
+ */
1133
+ _markReady(): void;
1134
+ /**
1135
+ * @internal
1136
+ */
1137
+ ready(): Promise<boolean>;
1138
+ /**
1139
+ * @internal
1140
+ */
1141
+ live(): Promise<boolean>;
1104
1142
  }
1105
1143
  //#endregion
1106
1144
  //#region src/middleware/default/health/index.d.ts
1145
+ /**
1146
+ * Enable the serving of `ready` and `live` endpoints.
1147
+ *
1148
+ * Configure any middleware-specific settings with `Sapling.Extras.health`
1149
+ * ```
1150
+ */
1107
1151
  declare class DefaultHealthMiddleware {
1108
1152
  private readonly healthRegistrar;
1109
1153
  constructor(healthRegistrar: HealthRegistrar);
1110
- serve(_request: Request, _response: Response, _next: NextFunction): Promise<ResponseEntity<{
1154
+ readiness(_request: Request, _response: Response, _next: NextFunction): Promise<ResponseEntity<{
1155
+ up: boolean;
1156
+ }>>;
1157
+ liveness(_request: Request, _response: Response, _next: NextFunction): Promise<ResponseEntity<{
1111
1158
  up: boolean;
1112
1159
  }>>;
1113
1160
  }
package/dist/index.mjs CHANGED
@@ -376,19 +376,37 @@ function __decorate(decorators, target, key, desc) {
376
376
  //#region src/middleware/health/registrar.ts
377
377
  let HealthRegistrar = class HealthRegistrar {
378
378
  _checks;
379
- _sealed;
379
+ _ready;
380
380
  constructor() {
381
381
  this._checks = [];
382
- this._sealed = false;
382
+ this._ready = false;
383
383
  }
384
+ /**
385
+ * Add a health check.
386
+ *
387
+ * Health checks will be used to determine whether service can serve traffic or not (a.k.a `liveness`).
388
+ */
384
389
  add(healthCheck) {
385
390
  this._checks.push(healthCheck);
386
391
  }
387
- seal() {
388
- this._sealed = true;
392
+ /**
393
+ * @internal used by Sapling library, used to determine once all
394
+ * checks have been registered and server is, at the very least, alive.
395
+ */
396
+ _markReady() {
397
+ this._ready = true;
398
+ }
399
+ /**
400
+ * @internal
401
+ */
402
+ async ready() {
403
+ return this._ready;
389
404
  }
390
- async check() {
391
- if (!this._sealed) return false;
405
+ /**
406
+ * @internal
407
+ */
408
+ async live() {
409
+ if (!this._ready) return false;
392
410
  return (await Promise.all(this._checks.map((c) => c()))).every((c) => c === true);
393
411
  }
394
412
  };
@@ -398,7 +416,10 @@ HealthRegistrar = __decorate([Injectable()], HealthRegistrar);
398
416
  const _settings = {
399
417
  serialize: JSON.stringify,
400
418
  deserialize: JSON.parse,
401
- health: { path: "/up" },
419
+ health: {
420
+ ready: { path: "/ready" },
421
+ live: { path: "/live" }
422
+ },
402
423
  doc: {
403
424
  openApiPath: "/openapi.json",
404
425
  swaggerPath: "/swagger.html",
@@ -466,15 +487,30 @@ var Sapling = class Sapling {
466
487
  * import { Sapling } from "@tahminator/sapling";
467
488
  * import express from "express";
468
489
  *
469
- * const app = express();
470
- *
471
- * app.registerApp(app);
490
+ * // returns the exact same `express.App` type back to you!
491
+ * const app = Sapling.registerApp(express());
472
492
  * ```
473
493
  */
474
494
  static registerApp(app) {
475
495
  app.use(e.text({ type: "application/json" }));
476
496
  app.use(Sapling.json());
477
- _InjectableRegistry.get(HealthRegistrar)?.seal();
497
+ return new Proxy(app, { get(target, prop, receiver) {
498
+ if (prop === "listen") {
499
+ const originalListen = target[prop];
500
+ return function(...args) {
501
+ const server = originalListen.apply(target, args);
502
+ server.once("listening", () => {
503
+ Sapling.onPostStartup();
504
+ console.log("Sapling successfully initialized post-startup hooks on server start");
505
+ });
506
+ return server;
507
+ };
508
+ }
509
+ return Reflect.get(target, prop, receiver);
510
+ } });
511
+ }
512
+ static onPostStartup() {
513
+ _InjectableRegistry.get(HealthRegistrar)?._markReady();
478
514
  }
479
515
  /**
480
516
  * Serialize a value into a JSON string.
@@ -515,37 +551,59 @@ var Sapling = class Sapling {
515
551
  /**
516
552
  * Modify extra settings
517
553
  */
518
- static Extras = {
519
- /**
520
- * Modify default settings applied to OpenAPI & Swagger
521
- */
522
- swaggerAndOpenApi: {
554
+ static Extras = {
523
555
  /**
524
- * Set base OpenAPI metadata values.
525
- *
526
- * @default { title: "API", version: "1.0.0" }
556
+ * Modify default settings applied to OpenAPI & Swagger
527
557
  */
528
- setMetadata(metadata) {
529
- _settings.doc.metadata = metadata;
530
- },
531
- /**
532
- * change default endpoint that will serve OpenAPI spec.
533
- * Swagger will also load this endpoint on load.
534
- *
535
- * @default `/openapi.json`
536
- */
537
- setOpenApiPath(path) {
538
- _settings.doc.openApiPath = path;
558
+ swaggerAndOpenApi: {
559
+ /**
560
+ * Set base OpenAPI metadata values.
561
+ *
562
+ * @default { title: "API", version: "1.0.0" }
563
+ */
564
+ setMetadata(metadata) {
565
+ _settings.doc.metadata = metadata;
566
+ },
567
+ /**
568
+ * change default endpoint that will serve OpenAPI spec.
569
+ * Swagger will also load this endpoint on load.
570
+ *
571
+ * @default `/openapi.json`
572
+ */
573
+ setOpenApiPath(path) {
574
+ _settings.doc.openApiPath = path;
575
+ },
576
+ /**
577
+ * change Swagger endpoint.
578
+ *
579
+ * @default `/swagger.html`
580
+ */
581
+ setSwaggerPath(path) {
582
+ _settings.doc.swaggerPath = path;
583
+ }
539
584
  },
540
585
  /**
541
- * change Swagger endpoint.
542
- *
543
- * @default `/swagger.html`
586
+ * Modify default settings applied to health / readiness / liveness.
544
587
  */
545
- setSwaggerPath(path) {
546
- _settings.doc.swaggerPath = path;
588
+ health: {
589
+ /**
590
+ * change default endpoint that ready endpoint will be served on.
591
+ *
592
+ * @default `/ready`
593
+ */
594
+ setReadyPath(path) {
595
+ _settings.health.ready.path = path;
596
+ },
597
+ /**
598
+ * change default endpoint that live endpoint will be served on.
599
+ *
600
+ * @default `/live`
601
+ */
602
+ setLivePath(path) {
603
+ _settings.health.live.path = path;
604
+ }
547
605
  }
548
- } };
606
+ };
549
607
  /**
550
608
  * This method can be used in a `@MiddlewareClass` to register any libraries
551
609
  * that expect you to register multiple registers at once. An example is `swagger-ui-express`
@@ -1138,12 +1196,17 @@ let DefaultHealthMiddleware = class DefaultHealthMiddleware {
1138
1196
  constructor(healthRegistrar) {
1139
1197
  this.healthRegistrar = healthRegistrar;
1140
1198
  }
1141
- async serve(_request, _response, _next) {
1142
- const up = await this.healthRegistrar.check();
1199
+ async readiness(_request, _response, _next) {
1200
+ const up = await this.healthRegistrar.ready();
1201
+ return ResponseEntity.ok().body({ up });
1202
+ }
1203
+ async liveness(_request, _response, _next) {
1204
+ const up = await this.healthRegistrar.live();
1143
1205
  return ResponseEntity.ok().body({ up });
1144
1206
  }
1145
1207
  };
1146
- __decorate([GET(_settings.health.path)], DefaultHealthMiddleware.prototype, "serve", null);
1208
+ __decorate([GET(_settings.health.ready.path)], DefaultHealthMiddleware.prototype, "readiness", null);
1209
+ __decorate([GET(_settings.health.live.path)], DefaultHealthMiddleware.prototype, "liveness", null);
1147
1210
  DefaultHealthMiddleware = __decorate([MiddlewareClass({ deps: [HealthRegistrar] })], DefaultHealthMiddleware);
1148
1211
  //#endregion
1149
1212
  export { Controller, ControllerSchema, DELETE, DefaultBaseErrorMiddleware, DefaultHealthMiddleware, DefaultOpenApiMiddleware, DefaultParserErrorMiddleware, DefaultResponseStatusErrorMiddleware, DefaultSwaggerMiddleware, GET, HEAD, HealthRegistrar, Html404ErrorPage, HttpStatus, Injectable, Middleware, MiddlewareClass, OPTIONS, PATCH, POST, PUT, ParserError, RedirectView, RequestBody, RequestParam, RequestQuery, ResponseBody, ResponseEntity, ResponseEntityBuilder, ResponseStatusError, RouteSchema, Sapling, _ControllerRegistry, _InjectableDeps, _InjectableRegistry, _Route, _clearOpenApiRegistry, _getControllerSchema, _getOrCreateSchemaDefinition, _getRouteSchema, _getRoutes, _getValidatorSchema, _parseOrThrow, _registerController, _resolve, _saveValidatorSchema, _setControllerSchema, _setRouteSchema, _settings, generateOpenApiSpec, methodResolve, openApiGenerator };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tahminator/sapling",
3
- "version": "2.1.0-beta.0e8a97e8",
3
+ "version": "2.1.0-beta.419f8e67",
4
4
  "author": "Tahmid Ahmed",
5
5
  "description": "A library to help you write cleaner Express.js code",
6
6
  "repository": {