@tahminator/sapling 2.1.0-beta.0e8a97e8 → 2.1.0-beta.6ea2cd3e

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,28 @@ 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 service 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;
414
423
  }
415
424
  async check() {
416
- if (!this._sealed) return false;
425
+ if (!this._ready) return false;
417
426
  return (await Promise.all(this._checks.map((c) => c()))).every((c) => c === true);
418
427
  }
419
428
  };
@@ -423,7 +432,10 @@ HealthRegistrar = __decorate([Injectable()], HealthRegistrar);
423
432
  const _settings = {
424
433
  serialize: JSON.stringify,
425
434
  deserialize: JSON.parse,
426
- health: { path: "/up" },
435
+ health: {
436
+ ready: { path: "/up" },
437
+ live: { path: "/live" }
438
+ },
427
439
  doc: {
428
440
  openApiPath: "/openapi.json",
429
441
  swaggerPath: "/swagger.html",
@@ -491,15 +503,30 @@ var Sapling = class Sapling {
491
503
  * import { Sapling } from "@tahminator/sapling";
492
504
  * import express from "express";
493
505
  *
494
- * const app = express();
495
- *
496
- * app.registerApp(app);
506
+ * // returns the exact same `express.App` type back to you!
507
+ * const app = Sapling.registerApp(express());
497
508
  * ```
498
509
  */
499
510
  static registerApp(app) {
500
511
  app.use(express.default.text({ type: "application/json" }));
501
512
  app.use(Sapling.json());
502
- _InjectableRegistry.get(HealthRegistrar)?.seal();
513
+ return new Proxy(app, { get(target, prop, receiver) {
514
+ if (prop === "listen") {
515
+ const originalListen = target[prop];
516
+ return function(...args) {
517
+ const server = originalListen.apply(target, args);
518
+ server.once("listening", () => {
519
+ Sapling.onPostStartup();
520
+ console.log("Sapling successfully initialized post-startup hooks on server start");
521
+ });
522
+ return server;
523
+ };
524
+ }
525
+ return Reflect.get(target, prop, receiver);
526
+ } });
527
+ }
528
+ static onPostStartup() {
529
+ _InjectableRegistry.get(HealthRegistrar)?._markReady();
503
530
  }
504
531
  /**
505
532
  * Serialize a value into a JSON string.
@@ -540,37 +567,59 @@ var Sapling = class Sapling {
540
567
  /**
541
568
  * Modify extra settings
542
569
  */
543
- static Extras = {
544
- /**
545
- * Modify default settings applied to OpenAPI & Swagger
546
- */
547
- swaggerAndOpenApi: {
570
+ static Extras = {
548
571
  /**
549
- * Set base OpenAPI metadata values.
550
- *
551
- * @default { title: "API", version: "1.0.0" }
572
+ * Modify default settings applied to OpenAPI & Swagger
552
573
  */
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;
574
+ swaggerAndOpenApi: {
575
+ /**
576
+ * Set base OpenAPI metadata values.
577
+ *
578
+ * @default { title: "API", version: "1.0.0" }
579
+ */
580
+ setMetadata(metadata) {
581
+ _settings.doc.metadata = metadata;
582
+ },
583
+ /**
584
+ * change default endpoint that will serve OpenAPI spec.
585
+ * Swagger will also load this endpoint on load.
586
+ *
587
+ * @default `/openapi.json`
588
+ */
589
+ setOpenApiPath(path) {
590
+ _settings.doc.openApiPath = path;
591
+ },
592
+ /**
593
+ * change Swagger endpoint.
594
+ *
595
+ * @default `/swagger.html`
596
+ */
597
+ setSwaggerPath(path) {
598
+ _settings.doc.swaggerPath = path;
599
+ }
564
600
  },
565
601
  /**
566
- * change Swagger endpoint.
567
- *
568
- * @default `/swagger.html`
602
+ * Modify default settings applied to health / readiness / liveness.
569
603
  */
570
- setSwaggerPath(path) {
571
- _settings.doc.swaggerPath = path;
604
+ health: {
605
+ /**
606
+ * change default endpoint that ready endpoint will be served on.
607
+ *
608
+ * @default `/up`
609
+ */
610
+ setReadyPath(path) {
611
+ _settings.health.ready.path = path;
612
+ },
613
+ /**
614
+ * change default endpoint that live endpoint will be served on.
615
+ *
616
+ * @default `/live`
617
+ */
618
+ setLivePath(path) {
619
+ _settings.health.live.path = path;
620
+ }
572
621
  }
573
- } };
622
+ };
574
623
  /**
575
624
  * This method can be used in a `@MiddlewareClass` to register any libraries
576
625
  * that expect you to register multiple registers at once. An example is `swagger-ui-express`
@@ -1163,12 +1212,17 @@ let DefaultHealthMiddleware = class DefaultHealthMiddleware {
1163
1212
  constructor(healthRegistrar) {
1164
1213
  this.healthRegistrar = healthRegistrar;
1165
1214
  }
1166
- async serve(_request, _response, _next) {
1215
+ async readiness(_request, _response, _next) {
1216
+ const up = await this.healthRegistrar.check();
1217
+ return ResponseEntity.ok().body({ up });
1218
+ }
1219
+ async liveness(_request, _response, _next) {
1167
1220
  const up = await this.healthRegistrar.check();
1168
1221
  return ResponseEntity.ok().body({ up });
1169
1222
  }
1170
1223
  };
1171
- __decorate([GET(_settings.health.path)], DefaultHealthMiddleware.prototype, "serve", null);
1224
+ __decorate([GET(_settings.health.ready.path)], DefaultHealthMiddleware.prototype, "readiness", null);
1225
+ __decorate([GET(_settings.health.live.path)], DefaultHealthMiddleware.prototype, "liveness", null);
1172
1226
  DefaultHealthMiddleware = __decorate([MiddlewareClass({ deps: [HealthRegistrar] })], DefaultHealthMiddleware);
1173
1227
  //#endregion
1174
1228
  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 `/up`
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,36 @@ 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 service traffic or not (a.k.a `liveness`).
1127
+ */
1101
1128
  add(healthCheck: HealthCheck): void;
1102
- seal(): void;
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;
1103
1134
  check(): Promise<boolean>;
1104
1135
  }
1105
1136
  //#endregion
1106
1137
  //#region src/middleware/default/health/index.d.ts
1138
+ /**
1139
+ * Enable the serving of `ready` and `live` endpoints.
1140
+ *
1141
+ * Configure any middleware-specific settings with `Sapling.Extras.health`
1142
+ * ```
1143
+ */
1107
1144
  declare class DefaultHealthMiddleware {
1108
1145
  private readonly healthRegistrar;
1109
1146
  constructor(healthRegistrar: HealthRegistrar);
1110
- serve(_request: Request, _response: Response, _next: NextFunction): Promise<ResponseEntity<{
1147
+ readiness(_request: Request, _response: Response, _next: NextFunction): Promise<ResponseEntity<{
1148
+ up: boolean;
1149
+ }>>;
1150
+ liveness(_request: Request, _response: Response, _next: NextFunction): Promise<ResponseEntity<{
1111
1151
  up: boolean;
1112
1152
  }>>;
1113
1153
  }
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 `/up`
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,36 @@ 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 service traffic or not (a.k.a `liveness`).
1127
+ */
1101
1128
  add(healthCheck: HealthCheck): void;
1102
- seal(): void;
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;
1103
1134
  check(): Promise<boolean>;
1104
1135
  }
1105
1136
  //#endregion
1106
1137
  //#region src/middleware/default/health/index.d.ts
1138
+ /**
1139
+ * Enable the serving of `ready` and `live` endpoints.
1140
+ *
1141
+ * Configure any middleware-specific settings with `Sapling.Extras.health`
1142
+ * ```
1143
+ */
1107
1144
  declare class DefaultHealthMiddleware {
1108
1145
  private readonly healthRegistrar;
1109
1146
  constructor(healthRegistrar: HealthRegistrar);
1110
- serve(_request: Request, _response: Response, _next: NextFunction): Promise<ResponseEntity<{
1147
+ readiness(_request: Request, _response: Response, _next: NextFunction): Promise<ResponseEntity<{
1148
+ up: boolean;
1149
+ }>>;
1150
+ liveness(_request: Request, _response: Response, _next: NextFunction): Promise<ResponseEntity<{
1111
1151
  up: boolean;
1112
1152
  }>>;
1113
1153
  }
package/dist/index.mjs CHANGED
@@ -376,19 +376,28 @@ 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 service 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;
389
398
  }
390
399
  async check() {
391
- if (!this._sealed) return false;
400
+ if (!this._ready) return false;
392
401
  return (await Promise.all(this._checks.map((c) => c()))).every((c) => c === true);
393
402
  }
394
403
  };
@@ -398,7 +407,10 @@ HealthRegistrar = __decorate([Injectable()], HealthRegistrar);
398
407
  const _settings = {
399
408
  serialize: JSON.stringify,
400
409
  deserialize: JSON.parse,
401
- health: { path: "/up" },
410
+ health: {
411
+ ready: { path: "/up" },
412
+ live: { path: "/live" }
413
+ },
402
414
  doc: {
403
415
  openApiPath: "/openapi.json",
404
416
  swaggerPath: "/swagger.html",
@@ -466,15 +478,30 @@ var Sapling = class Sapling {
466
478
  * import { Sapling } from "@tahminator/sapling";
467
479
  * import express from "express";
468
480
  *
469
- * const app = express();
470
- *
471
- * app.registerApp(app);
481
+ * // returns the exact same `express.App` type back to you!
482
+ * const app = Sapling.registerApp(express());
472
483
  * ```
473
484
  */
474
485
  static registerApp(app) {
475
486
  app.use(e.text({ type: "application/json" }));
476
487
  app.use(Sapling.json());
477
- _InjectableRegistry.get(HealthRegistrar)?.seal();
488
+ return new Proxy(app, { get(target, prop, receiver) {
489
+ if (prop === "listen") {
490
+ const originalListen = target[prop];
491
+ return function(...args) {
492
+ const server = originalListen.apply(target, args);
493
+ server.once("listening", () => {
494
+ Sapling.onPostStartup();
495
+ console.log("Sapling successfully initialized post-startup hooks on server start");
496
+ });
497
+ return server;
498
+ };
499
+ }
500
+ return Reflect.get(target, prop, receiver);
501
+ } });
502
+ }
503
+ static onPostStartup() {
504
+ _InjectableRegistry.get(HealthRegistrar)?._markReady();
478
505
  }
479
506
  /**
480
507
  * Serialize a value into a JSON string.
@@ -515,37 +542,59 @@ var Sapling = class Sapling {
515
542
  /**
516
543
  * Modify extra settings
517
544
  */
518
- static Extras = {
519
- /**
520
- * Modify default settings applied to OpenAPI & Swagger
521
- */
522
- swaggerAndOpenApi: {
545
+ static Extras = {
523
546
  /**
524
- * Set base OpenAPI metadata values.
525
- *
526
- * @default { title: "API", version: "1.0.0" }
547
+ * Modify default settings applied to OpenAPI & Swagger
527
548
  */
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;
549
+ swaggerAndOpenApi: {
550
+ /**
551
+ * Set base OpenAPI metadata values.
552
+ *
553
+ * @default { title: "API", version: "1.0.0" }
554
+ */
555
+ setMetadata(metadata) {
556
+ _settings.doc.metadata = metadata;
557
+ },
558
+ /**
559
+ * change default endpoint that will serve OpenAPI spec.
560
+ * Swagger will also load this endpoint on load.
561
+ *
562
+ * @default `/openapi.json`
563
+ */
564
+ setOpenApiPath(path) {
565
+ _settings.doc.openApiPath = path;
566
+ },
567
+ /**
568
+ * change Swagger endpoint.
569
+ *
570
+ * @default `/swagger.html`
571
+ */
572
+ setSwaggerPath(path) {
573
+ _settings.doc.swaggerPath = path;
574
+ }
539
575
  },
540
576
  /**
541
- * change Swagger endpoint.
542
- *
543
- * @default `/swagger.html`
577
+ * Modify default settings applied to health / readiness / liveness.
544
578
  */
545
- setSwaggerPath(path) {
546
- _settings.doc.swaggerPath = path;
579
+ health: {
580
+ /**
581
+ * change default endpoint that ready endpoint will be served on.
582
+ *
583
+ * @default `/up`
584
+ */
585
+ setReadyPath(path) {
586
+ _settings.health.ready.path = path;
587
+ },
588
+ /**
589
+ * change default endpoint that live endpoint will be served on.
590
+ *
591
+ * @default `/live`
592
+ */
593
+ setLivePath(path) {
594
+ _settings.health.live.path = path;
595
+ }
547
596
  }
548
- } };
597
+ };
549
598
  /**
550
599
  * This method can be used in a `@MiddlewareClass` to register any libraries
551
600
  * that expect you to register multiple registers at once. An example is `swagger-ui-express`
@@ -1138,12 +1187,17 @@ let DefaultHealthMiddleware = class DefaultHealthMiddleware {
1138
1187
  constructor(healthRegistrar) {
1139
1188
  this.healthRegistrar = healthRegistrar;
1140
1189
  }
1141
- async serve(_request, _response, _next) {
1190
+ async readiness(_request, _response, _next) {
1191
+ const up = await this.healthRegistrar.check();
1192
+ return ResponseEntity.ok().body({ up });
1193
+ }
1194
+ async liveness(_request, _response, _next) {
1142
1195
  const up = await this.healthRegistrar.check();
1143
1196
  return ResponseEntity.ok().body({ up });
1144
1197
  }
1145
1198
  };
1146
- __decorate([GET(_settings.health.path)], DefaultHealthMiddleware.prototype, "serve", null);
1199
+ __decorate([GET(_settings.health.ready.path)], DefaultHealthMiddleware.prototype, "readiness", null);
1200
+ __decorate([GET(_settings.health.live.path)], DefaultHealthMiddleware.prototype, "liveness", null);
1147
1201
  DefaultHealthMiddleware = __decorate([MiddlewareClass({ deps: [HealthRegistrar] })], DefaultHealthMiddleware);
1148
1202
  //#endregion
1149
1203
  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.6ea2cd3e",
4
4
  "author": "Tahmid Ahmed",
5
5
  "description": "A library to help you write cleaner Express.js code",
6
6
  "repository": {