@tahminator/sapling 1.5.28-beta.260afa93 → 1.5.28-beta.599ffb98
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 +30 -29
- package/dist/index.d.cts +26 -3
- package/dist/index.d.mts +26 -3
- package/dist/index.mjs +30 -29
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -310,6 +310,34 @@ var Sapling = class Sapling {
|
|
|
310
310
|
app.use(Sapling.json());
|
|
311
311
|
}
|
|
312
312
|
/**
|
|
313
|
+
* Register a middleware that will handle {@link ResponseStatusError}.
|
|
314
|
+
*
|
|
315
|
+
* This middleware will chain to the next middleware if it does not catch {@link ResponseStatusError}.
|
|
316
|
+
* You may still define middleware to handle all other errors in a separate `app.use` call.
|
|
317
|
+
*
|
|
318
|
+
* @example
|
|
319
|
+
* ```ts
|
|
320
|
+
* import express from "express";
|
|
321
|
+
* import { Sapling } from "@tahminator/sapling";
|
|
322
|
+
*
|
|
323
|
+
* const app = express();
|
|
324
|
+
*
|
|
325
|
+
* Sapling.loadResponseStatusErrorMiddleware(app, (err, req, res, next) => {
|
|
326
|
+
* // `err` is guaranteed to be of type ResponseStatusError
|
|
327
|
+
* res.status(err.status).json({
|
|
328
|
+
* success: false,
|
|
329
|
+
* message: err.message,
|
|
330
|
+
* });
|
|
331
|
+
* });
|
|
332
|
+
* ```
|
|
333
|
+
*/
|
|
334
|
+
static loadResponseStatusErrorMiddleware(app, fn) {
|
|
335
|
+
app.use(((err, req, res, next) => {
|
|
336
|
+
if (err instanceof ResponseStatusError) fn(err, req, res, next);
|
|
337
|
+
else next(err);
|
|
338
|
+
}));
|
|
339
|
+
}
|
|
340
|
+
/**
|
|
313
341
|
* Serialize a value into a JSON string.
|
|
314
342
|
*
|
|
315
343
|
* This function is used in {@link ResponseEntity} to serialize the `body`.
|
|
@@ -650,14 +678,6 @@ function Controller({ prefix = "", deps = [] } = {}) {
|
|
|
650
678
|
const usedRoutes = /* @__PURE__ */ new Set();
|
|
651
679
|
_InjectableDeps.set(targetClass, deps);
|
|
652
680
|
const controllerInstance = _resolve(targetClass);
|
|
653
|
-
if (routes.reduce((prev, r) => {
|
|
654
|
-
if (r.method !== "USE") return prev;
|
|
655
|
-
const fn = controllerInstance[r.fnName];
|
|
656
|
-
return typeof fn === "function" && fn.length >= 4 ? prev + 1 : prev;
|
|
657
|
-
}, 0) > 1) throw new Error(`Invalid @MiddlewareClass class "${targetClass.name}":
|
|
658
|
-
Multiple 4-arg @Middleware() error handlers were found.
|
|
659
|
-
Express will not enter routers in error mode, so an error-middleware class must expose exactly one error handler.
|
|
660
|
-
Split these into separate @MiddlewareClass classes, or merge the logic into a single method.`);
|
|
661
681
|
for (const { method, path, fnName } of routes) {
|
|
662
682
|
const fn = controllerInstance[fnName];
|
|
663
683
|
if (typeof fn !== "function") continue;
|
|
@@ -666,26 +686,6 @@ Split these into separate @MiddlewareClass classes, or merge the logic into a si
|
|
|
666
686
|
if (method !== "USE" && usedRoutes.has(routeKey)) throw new Error(`Duplicate route [${method}] "${path instanceof RegExp ? path.source : fp}" detected in controller "${target.name}"`);
|
|
667
687
|
if (method !== "USE") usedRoutes.add(routeKey);
|
|
668
688
|
const methodName = methodResolve[method];
|
|
669
|
-
if (method === "USE" && fn.length >= 4) {
|
|
670
|
-
const middlewareFn = async (err, request, response, next) => {
|
|
671
|
-
try {
|
|
672
|
-
const result = fn.bind(controllerInstance)(err, request, response, next);
|
|
673
|
-
if (result instanceof ResponseEntity) {
|
|
674
|
-
response.contentType("application/json").status(result.getStatusCode()).set(result.getHeaders()).send(Sapling.serialize(result.getBody()));
|
|
675
|
-
return;
|
|
676
|
-
}
|
|
677
|
-
if (result instanceof RedirectView) {
|
|
678
|
-
response.redirect(result.getUrl());
|
|
679
|
-
return;
|
|
680
|
-
}
|
|
681
|
-
} catch (e) {
|
|
682
|
-
console.error(e);
|
|
683
|
-
next(e);
|
|
684
|
-
}
|
|
685
|
-
};
|
|
686
|
-
_ControllerRegistry.set(targetClass, middlewareFn);
|
|
687
|
-
return;
|
|
688
|
-
}
|
|
689
689
|
router[methodName](fp, async (request, response, next) => {
|
|
690
690
|
const schemas = _getRequestSchemas(target, fnName);
|
|
691
691
|
if (schemas) {
|
|
@@ -694,6 +694,7 @@ Split these into separate @MiddlewareClass classes, or merge the logic into a si
|
|
|
694
694
|
if (schemas.query) request.query = await _parseOrThrow(schemas.query, request.query, "reqquery");
|
|
695
695
|
}
|
|
696
696
|
const result = await fn.bind(controllerInstance)(request, response, next);
|
|
697
|
+
if (method === "USE") return;
|
|
697
698
|
if (result instanceof ResponseEntity) {
|
|
698
699
|
response.contentType("application/json").status(result.getStatusCode()).set(result.getHeaders()).send(Sapling.serialize(result.getBody()));
|
|
699
700
|
return;
|
|
@@ -702,7 +703,7 @@ Split these into separate @MiddlewareClass classes, or merge the logic into a si
|
|
|
702
703
|
response.redirect(result.getUrl());
|
|
703
704
|
return;
|
|
704
705
|
}
|
|
705
|
-
if (
|
|
706
|
+
if (!response.writableEnded) response.status(404).send(Html404ErrorPage(`Cannot ${methodName.toUpperCase()} ${path instanceof RegExp ? path.source : fp}`));
|
|
706
707
|
});
|
|
707
708
|
}
|
|
708
709
|
_ControllerRegistry.set(targetClass, router);
|
package/dist/index.d.cts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import e, {
|
|
1
|
+
import e, { NextFunction, Request, Response, Router } from "express";
|
|
2
2
|
|
|
3
3
|
//#region src/html/404.d.ts
|
|
4
4
|
/**
|
|
@@ -29,7 +29,7 @@ type HttpHeaders = Record<string, string>;
|
|
|
29
29
|
type ExpressMiddlewareFn = ($1: Request, $2: Response, $3: NextFunction) => void;
|
|
30
30
|
//#endregion
|
|
31
31
|
//#region src/annotation/controller.d.ts
|
|
32
|
-
declare const _ControllerRegistry: WeakMap<Function, Router
|
|
32
|
+
declare const _ControllerRegistry: WeakMap<Function, Router>;
|
|
33
33
|
type ControllerProps = {
|
|
34
34
|
/**
|
|
35
35
|
* Optional URL prefix applied to all routes in the controller. Defaults to "".
|
|
@@ -416,7 +416,7 @@ declare class Sapling {
|
|
|
416
416
|
* app.use(router);
|
|
417
417
|
* ```
|
|
418
418
|
*/
|
|
419
|
-
static resolve<TClass>(this: void, clazz: Class<TClass>): Router
|
|
419
|
+
static resolve<TClass>(this: void, clazz: Class<TClass>): Router;
|
|
420
420
|
/**
|
|
421
421
|
* Register this function as a middleware in order to utilize Sapling's `deserialize` function.
|
|
422
422
|
*
|
|
@@ -443,6 +443,29 @@ declare class Sapling {
|
|
|
443
443
|
* ```
|
|
444
444
|
*/
|
|
445
445
|
static registerApp(app: e.Express): void;
|
|
446
|
+
/**
|
|
447
|
+
* Register a middleware that will handle {@link ResponseStatusError}.
|
|
448
|
+
*
|
|
449
|
+
* This middleware will chain to the next middleware if it does not catch {@link ResponseStatusError}.
|
|
450
|
+
* You may still define middleware to handle all other errors in a separate `app.use` call.
|
|
451
|
+
*
|
|
452
|
+
* @example
|
|
453
|
+
* ```ts
|
|
454
|
+
* import express from "express";
|
|
455
|
+
* import { Sapling } from "@tahminator/sapling";
|
|
456
|
+
*
|
|
457
|
+
* const app = express();
|
|
458
|
+
*
|
|
459
|
+
* Sapling.loadResponseStatusErrorMiddleware(app, (err, req, res, next) => {
|
|
460
|
+
* // `err` is guaranteed to be of type ResponseStatusError
|
|
461
|
+
* res.status(err.status).json({
|
|
462
|
+
* success: false,
|
|
463
|
+
* message: err.message,
|
|
464
|
+
* });
|
|
465
|
+
* });
|
|
466
|
+
* ```
|
|
467
|
+
*/
|
|
468
|
+
static loadResponseStatusErrorMiddleware(this: void, app: e.Express, fn: (err: ResponseStatusError, request: e.Request, response: e.Response, next: e.NextFunction) => void): void;
|
|
446
469
|
/**
|
|
447
470
|
* Serialize a value into a JSON string.
|
|
448
471
|
*
|
package/dist/index.d.mts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import e, {
|
|
1
|
+
import e, { NextFunction, Request, Response, Router } from "express";
|
|
2
2
|
|
|
3
3
|
//#region src/html/404.d.ts
|
|
4
4
|
/**
|
|
@@ -29,7 +29,7 @@ type HttpHeaders = Record<string, string>;
|
|
|
29
29
|
type ExpressMiddlewareFn = ($1: Request, $2: Response, $3: NextFunction) => void;
|
|
30
30
|
//#endregion
|
|
31
31
|
//#region src/annotation/controller.d.ts
|
|
32
|
-
declare const _ControllerRegistry: WeakMap<Function, Router
|
|
32
|
+
declare const _ControllerRegistry: WeakMap<Function, Router>;
|
|
33
33
|
type ControllerProps = {
|
|
34
34
|
/**
|
|
35
35
|
* Optional URL prefix applied to all routes in the controller. Defaults to "".
|
|
@@ -416,7 +416,7 @@ declare class Sapling {
|
|
|
416
416
|
* app.use(router);
|
|
417
417
|
* ```
|
|
418
418
|
*/
|
|
419
|
-
static resolve<TClass>(this: void, clazz: Class<TClass>): Router
|
|
419
|
+
static resolve<TClass>(this: void, clazz: Class<TClass>): Router;
|
|
420
420
|
/**
|
|
421
421
|
* Register this function as a middleware in order to utilize Sapling's `deserialize` function.
|
|
422
422
|
*
|
|
@@ -443,6 +443,29 @@ declare class Sapling {
|
|
|
443
443
|
* ```
|
|
444
444
|
*/
|
|
445
445
|
static registerApp(app: e.Express): void;
|
|
446
|
+
/**
|
|
447
|
+
* Register a middleware that will handle {@link ResponseStatusError}.
|
|
448
|
+
*
|
|
449
|
+
* This middleware will chain to the next middleware if it does not catch {@link ResponseStatusError}.
|
|
450
|
+
* You may still define middleware to handle all other errors in a separate `app.use` call.
|
|
451
|
+
*
|
|
452
|
+
* @example
|
|
453
|
+
* ```ts
|
|
454
|
+
* import express from "express";
|
|
455
|
+
* import { Sapling } from "@tahminator/sapling";
|
|
456
|
+
*
|
|
457
|
+
* const app = express();
|
|
458
|
+
*
|
|
459
|
+
* Sapling.loadResponseStatusErrorMiddleware(app, (err, req, res, next) => {
|
|
460
|
+
* // `err` is guaranteed to be of type ResponseStatusError
|
|
461
|
+
* res.status(err.status).json({
|
|
462
|
+
* success: false,
|
|
463
|
+
* message: err.message,
|
|
464
|
+
* });
|
|
465
|
+
* });
|
|
466
|
+
* ```
|
|
467
|
+
*/
|
|
468
|
+
static loadResponseStatusErrorMiddleware(this: void, app: e.Express, fn: (err: ResponseStatusError, request: e.Request, response: e.Response, next: e.NextFunction) => void): void;
|
|
446
469
|
/**
|
|
447
470
|
* Serialize a value into a JSON string.
|
|
448
471
|
*
|
package/dist/index.mjs
CHANGED
|
@@ -286,6 +286,34 @@ var Sapling = class Sapling {
|
|
|
286
286
|
app.use(Sapling.json());
|
|
287
287
|
}
|
|
288
288
|
/**
|
|
289
|
+
* Register a middleware that will handle {@link ResponseStatusError}.
|
|
290
|
+
*
|
|
291
|
+
* This middleware will chain to the next middleware if it does not catch {@link ResponseStatusError}.
|
|
292
|
+
* You may still define middleware to handle all other errors in a separate `app.use` call.
|
|
293
|
+
*
|
|
294
|
+
* @example
|
|
295
|
+
* ```ts
|
|
296
|
+
* import express from "express";
|
|
297
|
+
* import { Sapling } from "@tahminator/sapling";
|
|
298
|
+
*
|
|
299
|
+
* const app = express();
|
|
300
|
+
*
|
|
301
|
+
* Sapling.loadResponseStatusErrorMiddleware(app, (err, req, res, next) => {
|
|
302
|
+
* // `err` is guaranteed to be of type ResponseStatusError
|
|
303
|
+
* res.status(err.status).json({
|
|
304
|
+
* success: false,
|
|
305
|
+
* message: err.message,
|
|
306
|
+
* });
|
|
307
|
+
* });
|
|
308
|
+
* ```
|
|
309
|
+
*/
|
|
310
|
+
static loadResponseStatusErrorMiddleware(app, fn) {
|
|
311
|
+
app.use(((err, req, res, next) => {
|
|
312
|
+
if (err instanceof ResponseStatusError) fn(err, req, res, next);
|
|
313
|
+
else next(err);
|
|
314
|
+
}));
|
|
315
|
+
}
|
|
316
|
+
/**
|
|
289
317
|
* Serialize a value into a JSON string.
|
|
290
318
|
*
|
|
291
319
|
* This function is used in {@link ResponseEntity} to serialize the `body`.
|
|
@@ -626,14 +654,6 @@ function Controller({ prefix = "", deps = [] } = {}) {
|
|
|
626
654
|
const usedRoutes = /* @__PURE__ */ new Set();
|
|
627
655
|
_InjectableDeps.set(targetClass, deps);
|
|
628
656
|
const controllerInstance = _resolve(targetClass);
|
|
629
|
-
if (routes.reduce((prev, r) => {
|
|
630
|
-
if (r.method !== "USE") return prev;
|
|
631
|
-
const fn = controllerInstance[r.fnName];
|
|
632
|
-
return typeof fn === "function" && fn.length >= 4 ? prev + 1 : prev;
|
|
633
|
-
}, 0) > 1) throw new Error(`Invalid @MiddlewareClass class "${targetClass.name}":
|
|
634
|
-
Multiple 4-arg @Middleware() error handlers were found.
|
|
635
|
-
Express will not enter routers in error mode, so an error-middleware class must expose exactly one error handler.
|
|
636
|
-
Split these into separate @MiddlewareClass classes, or merge the logic into a single method.`);
|
|
637
657
|
for (const { method, path, fnName } of routes) {
|
|
638
658
|
const fn = controllerInstance[fnName];
|
|
639
659
|
if (typeof fn !== "function") continue;
|
|
@@ -642,26 +662,6 @@ Split these into separate @MiddlewareClass classes, or merge the logic into a si
|
|
|
642
662
|
if (method !== "USE" && usedRoutes.has(routeKey)) throw new Error(`Duplicate route [${method}] "${path instanceof RegExp ? path.source : fp}" detected in controller "${target.name}"`);
|
|
643
663
|
if (method !== "USE") usedRoutes.add(routeKey);
|
|
644
664
|
const methodName = methodResolve[method];
|
|
645
|
-
if (method === "USE" && fn.length >= 4) {
|
|
646
|
-
const middlewareFn = async (err, request, response, next) => {
|
|
647
|
-
try {
|
|
648
|
-
const result = fn.bind(controllerInstance)(err, request, response, next);
|
|
649
|
-
if (result instanceof ResponseEntity) {
|
|
650
|
-
response.contentType("application/json").status(result.getStatusCode()).set(result.getHeaders()).send(Sapling.serialize(result.getBody()));
|
|
651
|
-
return;
|
|
652
|
-
}
|
|
653
|
-
if (result instanceof RedirectView) {
|
|
654
|
-
response.redirect(result.getUrl());
|
|
655
|
-
return;
|
|
656
|
-
}
|
|
657
|
-
} catch (e) {
|
|
658
|
-
console.error(e);
|
|
659
|
-
next(e);
|
|
660
|
-
}
|
|
661
|
-
};
|
|
662
|
-
_ControllerRegistry.set(targetClass, middlewareFn);
|
|
663
|
-
return;
|
|
664
|
-
}
|
|
665
665
|
router[methodName](fp, async (request, response, next) => {
|
|
666
666
|
const schemas = _getRequestSchemas(target, fnName);
|
|
667
667
|
if (schemas) {
|
|
@@ -670,6 +670,7 @@ Split these into separate @MiddlewareClass classes, or merge the logic into a si
|
|
|
670
670
|
if (schemas.query) request.query = await _parseOrThrow(schemas.query, request.query, "reqquery");
|
|
671
671
|
}
|
|
672
672
|
const result = await fn.bind(controllerInstance)(request, response, next);
|
|
673
|
+
if (method === "USE") return;
|
|
673
674
|
if (result instanceof ResponseEntity) {
|
|
674
675
|
response.contentType("application/json").status(result.getStatusCode()).set(result.getHeaders()).send(Sapling.serialize(result.getBody()));
|
|
675
676
|
return;
|
|
@@ -678,7 +679,7 @@ Split these into separate @MiddlewareClass classes, or merge the logic into a si
|
|
|
678
679
|
response.redirect(result.getUrl());
|
|
679
680
|
return;
|
|
680
681
|
}
|
|
681
|
-
if (
|
|
682
|
+
if (!response.writableEnded) response.status(404).send(Html404ErrorPage(`Cannot ${methodName.toUpperCase()} ${path instanceof RegExp ? path.source : fp}`));
|
|
682
683
|
});
|
|
683
684
|
}
|
|
684
685
|
_ControllerRegistry.set(targetClass, router);
|