@tahminator/sapling 2.0.5-beta.0b3bd061 → 2.0.5-beta.1843d6fa
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 +161 -12
- package/dist/index.cjs +281 -150
- package/dist/index.d.cts +223 -98
- package/dist/index.d.mts +223 -98
- package/dist/index.mjs +279 -148
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -48,6 +48,7 @@ const Html404ErrorPage = (error) => `<!DOCTYPE html>
|
|
|
48
48
|
* You can either return `new RedirectView(url)` or `RedirectView.redirect(url)` inside of a controller method.
|
|
49
49
|
*/
|
|
50
50
|
var RedirectView = class RedirectView {
|
|
51
|
+
_url;
|
|
51
52
|
constructor(url) {
|
|
52
53
|
this._url = url;
|
|
53
54
|
}
|
|
@@ -143,8 +144,10 @@ let HttpStatus = /* @__PURE__ */ function(HttpStatus) {
|
|
|
143
144
|
* @typeParam T - the type of the response body
|
|
144
145
|
*/
|
|
145
146
|
var ResponseEntity = class {
|
|
147
|
+
_statusCode;
|
|
148
|
+
_headers = {};
|
|
149
|
+
_body;
|
|
146
150
|
constructor(body, headers = {}, statusCode = 200) {
|
|
147
|
-
this._headers = {};
|
|
148
151
|
this._body = body;
|
|
149
152
|
this._headers = headers;
|
|
150
153
|
this._statusCode = statusCode;
|
|
@@ -197,8 +200,9 @@ var ResponseEntity = class {
|
|
|
197
200
|
* ensuring type safety when constructing the response.
|
|
198
201
|
*/
|
|
199
202
|
var ResponseEntityBuilder = class {
|
|
203
|
+
_statusCode;
|
|
204
|
+
_headers = {};
|
|
200
205
|
constructor(statusCode) {
|
|
201
|
-
this._headers = {};
|
|
202
206
|
this._statusCode = statusCode;
|
|
203
207
|
}
|
|
204
208
|
/**
|
|
@@ -230,6 +234,7 @@ var ResponseEntityBuilder = class {
|
|
|
230
234
|
* @see {@link Sapling.loadResponseStatusErrorMiddleware}
|
|
231
235
|
*/
|
|
232
236
|
var ResponseStatusError = class ResponseStatusError extends Error {
|
|
237
|
+
status;
|
|
233
238
|
constructor(status, message) {
|
|
234
239
|
super(message ?? "Something went wrong.");
|
|
235
240
|
this.status = status;
|
|
@@ -241,26 +246,27 @@ var ResponseStatusError = class ResponseStatusError extends Error {
|
|
|
241
246
|
//#endregion
|
|
242
247
|
//#region src/helper/error/parse.ts
|
|
243
248
|
/**
|
|
244
|
-
* This error should be thrown when some data cannot be parsed by a given schema.
|
|
249
|
+
* This error should be thrown when some data cannot be parsed by a given Standard Schema compatible schema.
|
|
245
250
|
*/
|
|
246
251
|
var ParserError = class ParserError extends ResponseStatusError {
|
|
247
|
-
constructor(location, issues, vendor) {
|
|
248
|
-
super(400, ParserError.formatMessage(location, issues, vendor));
|
|
252
|
+
constructor(location, issues, vendor, functionName) {
|
|
253
|
+
super(400, ParserError.formatMessage(location, issues, vendor, functionName));
|
|
249
254
|
Object.setPrototypeOf(this, new.target.prototype);
|
|
250
255
|
}
|
|
251
|
-
static formatMessage(location, issues, vendor) {
|
|
256
|
+
static formatMessage(location, issues, vendor, functionName) {
|
|
252
257
|
const formatted = issues.map((i) => {
|
|
253
258
|
const path = Array.isArray(i.path) ? i.path.map((seg) => typeof seg === "object" && seg ? String(seg.key) : String(seg)).join(".") : "";
|
|
254
259
|
return path ? `${path}: ${i.message}` : i.message;
|
|
255
260
|
}).join("; ");
|
|
256
|
-
return
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
261
|
+
return `Failed to parse ${this.getPrettyLocationString(location)} with ${vendor} on ${functionName}: ${formatted}`;
|
|
262
|
+
}
|
|
263
|
+
static getPrettyLocationString(location) {
|
|
264
|
+
switch (location) {
|
|
265
|
+
case "reqbody": return "request body";
|
|
266
|
+
case "reqparams": return "request params";
|
|
267
|
+
case "reqquery": return "request query";
|
|
268
|
+
case "resbody": return "response body";
|
|
269
|
+
}
|
|
264
270
|
}
|
|
265
271
|
};
|
|
266
272
|
//#endregion
|
|
@@ -270,7 +276,11 @@ const _settings = {
|
|
|
270
276
|
deserialize: JSON.parse,
|
|
271
277
|
doc: {
|
|
272
278
|
openApiPath: "/openapi.json",
|
|
273
|
-
swaggerPath: "/swagger.html"
|
|
279
|
+
swaggerPath: "/swagger.html",
|
|
280
|
+
metadata: {
|
|
281
|
+
title: "API",
|
|
282
|
+
version: "1.0.0"
|
|
283
|
+
}
|
|
274
284
|
}
|
|
275
285
|
};
|
|
276
286
|
/**
|
|
@@ -376,12 +386,59 @@ var Sapling = class Sapling {
|
|
|
376
386
|
static setDeserializeFn(fn) {
|
|
377
387
|
_settings.deserialize = fn;
|
|
378
388
|
}
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
static
|
|
383
|
-
|
|
384
|
-
|
|
389
|
+
/**
|
|
390
|
+
* Modify extra settings
|
|
391
|
+
*/
|
|
392
|
+
static Extras = {
|
|
393
|
+
/**
|
|
394
|
+
* Modify default settings applied to OpenAPI & Swagger
|
|
395
|
+
*/
|
|
396
|
+
swaggerAndOpenApi: {
|
|
397
|
+
/**
|
|
398
|
+
* Set base OpenAPI metadata values.
|
|
399
|
+
*
|
|
400
|
+
* @default { title: "API", version: "1.0.0" }
|
|
401
|
+
*/
|
|
402
|
+
setMetadata(metadata) {
|
|
403
|
+
_settings.doc.metadata = metadata;
|
|
404
|
+
},
|
|
405
|
+
/**
|
|
406
|
+
* change default endpoint that will serve OpenAPI spec.
|
|
407
|
+
* Swagger will also load this endpoint on load.
|
|
408
|
+
*
|
|
409
|
+
* @default `/openapi.json`
|
|
410
|
+
*/
|
|
411
|
+
setOpenApiPath(path) {
|
|
412
|
+
_settings.doc.openApiPath = path;
|
|
413
|
+
},
|
|
414
|
+
/**
|
|
415
|
+
* change Swagger endpoint.
|
|
416
|
+
*
|
|
417
|
+
* @default `/swagger.html`
|
|
418
|
+
*/
|
|
419
|
+
setSwaggerPath(path) {
|
|
420
|
+
_settings.doc.swaggerPath = path;
|
|
421
|
+
}
|
|
422
|
+
} };
|
|
423
|
+
/**
|
|
424
|
+
* This method can be used in a `@MiddlewareClass` to register any libraries
|
|
425
|
+
* that expect you to register multiple registers at once. An example is `swagger-ui-express`
|
|
426
|
+
*
|
|
427
|
+
* @example
|
|
428
|
+
* ```ts
|
|
429
|
+
* ⠀@MiddlewareClass()
|
|
430
|
+
* class Serve {
|
|
431
|
+
* // `swagger.serve` returns multiple Express handlers for all the assets and routes
|
|
432
|
+
* // that will be served
|
|
433
|
+
* private readonly handlers: RequestHandler[] = swagger.serve;
|
|
434
|
+
*
|
|
435
|
+
* ⠀@Middleware(_settings.doc.swaggerPath)
|
|
436
|
+
* handle(request: Request, response: Response, next: NextFunction) {
|
|
437
|
+
* return Sapling.chainHandlers(this.handlers, request, response, next);
|
|
438
|
+
* }
|
|
439
|
+
* }
|
|
440
|
+
* ```
|
|
441
|
+
*/
|
|
385
442
|
static chainHandlers(handlers, request, response, next, index = 0) {
|
|
386
443
|
if (index >= handlers.length) {
|
|
387
444
|
next();
|
|
@@ -478,18 +535,7 @@ function _getRoutes(ctor) {
|
|
|
478
535
|
return _routeStore.get(ctor) ?? [];
|
|
479
536
|
}
|
|
480
537
|
//#endregion
|
|
481
|
-
//#region src/utils.ts
|
|
482
|
-
function _getOrCreateMap(store, ctor) {
|
|
483
|
-
const existing = store.get(ctor);
|
|
484
|
-
if (existing) return existing;
|
|
485
|
-
const created = /* @__PURE__ */ new Map();
|
|
486
|
-
store.set(ctor, created);
|
|
487
|
-
return created;
|
|
488
|
-
}
|
|
489
|
-
//#endregion
|
|
490
538
|
//#region src/annotation/schema.ts
|
|
491
|
-
const _routeSchemaStore = /* @__PURE__ */ new WeakMap();
|
|
492
|
-
const _controllerSchemaStore = /* @__PURE__ */ new WeakMap();
|
|
493
539
|
function ControllerSchema(options) {
|
|
494
540
|
return (target) => {
|
|
495
541
|
_setControllerSchema(target, options);
|
|
@@ -501,8 +547,17 @@ function RouteSchema(options) {
|
|
|
501
547
|
_setRouteSchema(ctor, String(propertyKey), options);
|
|
502
548
|
};
|
|
503
549
|
}
|
|
550
|
+
const _routeSchemaStore = /* @__PURE__ */ new WeakMap();
|
|
551
|
+
const _controllerSchemaStore = /* @__PURE__ */ new WeakMap();
|
|
552
|
+
function getOrCreateRouteSchemaStore(store, ctor) {
|
|
553
|
+
const existing = store.get(ctor);
|
|
554
|
+
if (existing) return existing;
|
|
555
|
+
const created = /* @__PURE__ */ new Map();
|
|
556
|
+
store.set(ctor, created);
|
|
557
|
+
return created;
|
|
558
|
+
}
|
|
504
559
|
function _setRouteSchema(ctor, fnName, options) {
|
|
505
|
-
|
|
560
|
+
getOrCreateRouteSchemaStore(_routeSchemaStore, ctor).set(fnName, options);
|
|
506
561
|
}
|
|
507
562
|
function _setControllerSchema(ctor, options) {
|
|
508
563
|
_controllerSchemaStore.set(ctor, options);
|
|
@@ -514,26 +569,85 @@ function _getControllerSchema(ctor) {
|
|
|
514
569
|
return _controllerSchemaStore.get(ctor);
|
|
515
570
|
}
|
|
516
571
|
//#endregion
|
|
572
|
+
//#region src/annotation/validator.ts
|
|
573
|
+
const _validatorSchemaStore = /* @__PURE__ */ new WeakMap();
|
|
574
|
+
function ResponseBody(schema) {
|
|
575
|
+
return (target, propertyKey) => {
|
|
576
|
+
const ctor = target.constructor;
|
|
577
|
+
const fnName = String(propertyKey);
|
|
578
|
+
_saveValidatorSchema(_getOrCreateSchemaDefinition(ctor, fnName), "responseBody", schema, fnName);
|
|
579
|
+
};
|
|
580
|
+
}
|
|
581
|
+
function RequestBody(schema) {
|
|
582
|
+
return (target, propertyKey) => {
|
|
583
|
+
const ctor = target.constructor;
|
|
584
|
+
const fnName = String(propertyKey);
|
|
585
|
+
_saveValidatorSchema(_getOrCreateSchemaDefinition(ctor, fnName), "requestBody", schema, fnName);
|
|
586
|
+
};
|
|
587
|
+
}
|
|
588
|
+
function RequestParam(schema) {
|
|
589
|
+
return (target, propertyKey) => {
|
|
590
|
+
const ctor = target.constructor;
|
|
591
|
+
const fnName = String(propertyKey);
|
|
592
|
+
_saveValidatorSchema(_getOrCreateSchemaDefinition(ctor, fnName), "requestParam", schema, fnName);
|
|
593
|
+
};
|
|
594
|
+
}
|
|
595
|
+
function RequestQuery(schema) {
|
|
596
|
+
return (target, propertyKey) => {
|
|
597
|
+
const ctor = target.constructor;
|
|
598
|
+
const fnName = String(propertyKey);
|
|
599
|
+
_saveValidatorSchema(_getOrCreateSchemaDefinition(ctor, fnName), "requestQuery", schema, fnName);
|
|
600
|
+
};
|
|
601
|
+
}
|
|
602
|
+
function getOrCreateValidatorSchemaStore(store, ctor) {
|
|
603
|
+
const existing = store.get(ctor);
|
|
604
|
+
if (existing) return existing;
|
|
605
|
+
const created = /* @__PURE__ */ new Map();
|
|
606
|
+
store.set(ctor, created);
|
|
607
|
+
return created;
|
|
608
|
+
}
|
|
609
|
+
function _getOrCreateSchemaDefinition(ctor, fnName) {
|
|
610
|
+
const byFn = getOrCreateValidatorSchemaStore(_validatorSchemaStore, ctor);
|
|
611
|
+
const existing = byFn.get(fnName);
|
|
612
|
+
if (existing) return existing;
|
|
613
|
+
const created = {};
|
|
614
|
+
byFn.set(fnName, created);
|
|
615
|
+
return created;
|
|
616
|
+
}
|
|
617
|
+
async function _parseOrThrow(schema, input, location, fnName) {
|
|
618
|
+
const result = await schema["~standard"].validate(input);
|
|
619
|
+
if (result.issues) throw new ParserError(location, result.issues, schema["~standard"].vendor, fnName);
|
|
620
|
+
return result.value;
|
|
621
|
+
}
|
|
622
|
+
function _saveValidatorSchema(def, key, schema, fnName) {
|
|
623
|
+
if (def[key]) throw new Error(`Duplicate schema for "${String(key)}" on method "${fnName}"`);
|
|
624
|
+
def[key] = schema;
|
|
625
|
+
}
|
|
626
|
+
function _getValidatorSchema(ctor, fnName) {
|
|
627
|
+
return _validatorSchemaStore.get(ctor)?.get(fnName);
|
|
628
|
+
}
|
|
629
|
+
//#endregion
|
|
517
630
|
//#region src/helper/openapi.ts
|
|
518
631
|
var OpenAPIGenerator = class {
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
this.config = {
|
|
522
|
-
title: "API",
|
|
523
|
-
version: "1.0.0"
|
|
524
|
-
};
|
|
525
|
-
}
|
|
526
|
-
setConfig(config) {
|
|
527
|
-
this.config = config;
|
|
528
|
-
}
|
|
632
|
+
OPENAPI_VERSION = "3.0.0";
|
|
633
|
+
controllers = /* @__PURE__ */ new Set();
|
|
529
634
|
registerController(controllerClass, prefix) {
|
|
530
635
|
this.controllers.add({
|
|
531
636
|
class: controllerClass,
|
|
532
637
|
prefix
|
|
533
638
|
});
|
|
534
639
|
}
|
|
640
|
+
/**
|
|
641
|
+
* visible for testing
|
|
642
|
+
*/
|
|
643
|
+
_clearControllers() {
|
|
644
|
+
this.controllers.clear();
|
|
645
|
+
}
|
|
646
|
+
get metadata() {
|
|
647
|
+
return _settings.doc.metadata;
|
|
648
|
+
}
|
|
535
649
|
generateSpec() {
|
|
536
|
-
const
|
|
650
|
+
const metadata = this.metadata;
|
|
537
651
|
const paths = {};
|
|
538
652
|
const tags = [];
|
|
539
653
|
for (const { class: controllerClass, prefix } of this.controllers) {
|
|
@@ -617,11 +731,11 @@ var OpenAPIGenerator = class {
|
|
|
617
731
|
}
|
|
618
732
|
}
|
|
619
733
|
return {
|
|
620
|
-
openapi:
|
|
734
|
+
openapi: this.OPENAPI_VERSION,
|
|
621
735
|
info: {
|
|
622
|
-
title:
|
|
623
|
-
version:
|
|
624
|
-
description:
|
|
736
|
+
title: metadata.title,
|
|
737
|
+
version: metadata.version,
|
|
738
|
+
description: metadata.description
|
|
625
739
|
},
|
|
626
740
|
tags: tags.length > 0 ? tags : void 0,
|
|
627
741
|
paths
|
|
@@ -630,24 +744,23 @@ var OpenAPIGenerator = class {
|
|
|
630
744
|
toJsonSchema(schema, direction = "output") {
|
|
631
745
|
try {
|
|
632
746
|
const jsonSchema = schema["~standard"].jsonSchema;
|
|
633
|
-
|
|
634
|
-
return jsonSchema.output({ target: "openapi-3.0" });
|
|
747
|
+
return direction === "input" ? jsonSchema.input({ target: "openapi-3.0" }) : jsonSchema.output({ target: "openapi-3.0" });
|
|
635
748
|
} catch (e) {
|
|
636
|
-
if (e instanceof Error && e.message.includes("Transforms cannot be represented in JSON Schema")) throw new Error(`${e.message}.\nIt appears that you are using z.transform() - it is highly recommended that you use z.codec instead - https://zod.dev/codecs
|
|
749
|
+
if (e instanceof Error && e.message.includes("Transforms cannot be represented in JSON Schema")) throw new Error(`${e.message}.\nIt appears that you are using z.transform() - it is highly recommended that you use z.codec instead - https://zod.dev/codecs`, { cause: e });
|
|
637
750
|
throw e;
|
|
638
751
|
}
|
|
639
752
|
}
|
|
640
753
|
};
|
|
641
754
|
const openApiGenerator = new OpenAPIGenerator();
|
|
642
|
-
function
|
|
755
|
+
function _registerController(controllerClass, prefix) {
|
|
643
756
|
openApiGenerator.registerController(controllerClass, prefix);
|
|
644
757
|
}
|
|
645
|
-
function setOpenApiConfig(config) {
|
|
646
|
-
openApiGenerator.setConfig(config);
|
|
647
|
-
}
|
|
648
758
|
function generateOpenApiSpec() {
|
|
649
759
|
return openApiGenerator.generateSpec();
|
|
650
760
|
}
|
|
761
|
+
function _clearOpenApiRegistry() {
|
|
762
|
+
openApiGenerator._clearControllers();
|
|
763
|
+
}
|
|
651
764
|
//#endregion
|
|
652
765
|
//#region src/types.ts
|
|
653
766
|
const methodResolve = {
|
|
@@ -781,60 +894,6 @@ function _resolve(ctor) {
|
|
|
781
894
|
return _InjectableRegistry.get(ctor);
|
|
782
895
|
}
|
|
783
896
|
//#endregion
|
|
784
|
-
//#region src/annotation/validator.ts
|
|
785
|
-
const _validatorSchemaStore = /* @__PURE__ */ new WeakMap();
|
|
786
|
-
function ResponseBody(schema) {
|
|
787
|
-
return (target, propertyKey) => {
|
|
788
|
-
const ctor = target.constructor;
|
|
789
|
-
const fnName = String(propertyKey);
|
|
790
|
-
_setOnce(_getOrCreateSchemaDefinition(ctor, fnName), "responseBody", schema, fnName);
|
|
791
|
-
};
|
|
792
|
-
}
|
|
793
|
-
function RequestBody(schema) {
|
|
794
|
-
return (target, propertyKey) => {
|
|
795
|
-
const ctor = target.constructor;
|
|
796
|
-
const fnName = String(propertyKey);
|
|
797
|
-
_setOnce(_getOrCreateSchemaDefinition(ctor, fnName), "requestBody", schema, fnName);
|
|
798
|
-
};
|
|
799
|
-
}
|
|
800
|
-
function RequestParam(schema) {
|
|
801
|
-
return (target, propertyKey) => {
|
|
802
|
-
const ctor = target.constructor;
|
|
803
|
-
const fnName = String(propertyKey);
|
|
804
|
-
_setOnce(_getOrCreateSchemaDefinition(ctor, fnName), "requestParam", schema, fnName);
|
|
805
|
-
};
|
|
806
|
-
}
|
|
807
|
-
function RequestQuery(schema) {
|
|
808
|
-
return (target, propertyKey) => {
|
|
809
|
-
const ctor = target.constructor;
|
|
810
|
-
const fnName = String(propertyKey);
|
|
811
|
-
_setOnce(_getOrCreateSchemaDefinition(ctor, fnName), "requestQuery", schema, fnName);
|
|
812
|
-
};
|
|
813
|
-
}
|
|
814
|
-
function _getOrCreateSchemaDefinition(ctor, fnName) {
|
|
815
|
-
const byFn = _getOrCreateMap(_validatorSchemaStore, ctor);
|
|
816
|
-
const existing = byFn.get(fnName);
|
|
817
|
-
if (existing) return existing;
|
|
818
|
-
const created = {};
|
|
819
|
-
byFn.set(fnName, created);
|
|
820
|
-
return created;
|
|
821
|
-
}
|
|
822
|
-
async function _parseOrThrow(schema, input, kind) {
|
|
823
|
-
const result = await schema["~standard"].validate(input);
|
|
824
|
-
if (result.issues) {
|
|
825
|
-
console.debug(`Failed to parse ${schema["~standard"].vendor} schema\nissues: ${JSON.stringify(result.issues, null, 2)}`);
|
|
826
|
-
throw new ParserError(kind, result.issues, schema["~standard"].vendor);
|
|
827
|
-
}
|
|
828
|
-
return result.value;
|
|
829
|
-
}
|
|
830
|
-
function _getValidatorSchema(ctor, fnName) {
|
|
831
|
-
return _validatorSchemaStore.get(ctor)?.get(fnName);
|
|
832
|
-
}
|
|
833
|
-
function _setOnce(def, key, schema, fnName) {
|
|
834
|
-
if (def[key]) throw new Error(`Duplicate schema for "${String(key)}" on method "${fnName}"`);
|
|
835
|
-
def[key] = schema;
|
|
836
|
-
}
|
|
837
|
-
//#endregion
|
|
838
897
|
//#region src/annotation/controller.ts
|
|
839
898
|
const _ControllerRegistry = /* @__PURE__ */ new WeakMap();
|
|
840
899
|
/**
|
|
@@ -846,7 +905,7 @@ const _ControllerRegistry = /* @__PURE__ */ new WeakMap();
|
|
|
846
905
|
function Controller({ prefix = "", deps = [] } = {}) {
|
|
847
906
|
return (target) => {
|
|
848
907
|
const targetClass = target;
|
|
849
|
-
|
|
908
|
+
_registerController(target, prefix);
|
|
850
909
|
const router = (0, express.Router)();
|
|
851
910
|
const routes = _getRoutes(target);
|
|
852
911
|
const usedRoutes = /* @__PURE__ */ new Set();
|
|
@@ -871,15 +930,20 @@ Split these into separate @MiddlewareClass classes, or merge the logic into a si
|
|
|
871
930
|
if (method === "USE" && fn.length >= 4) {
|
|
872
931
|
const middlewareFn = async (err, request, response, next) => {
|
|
873
932
|
try {
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
}
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
933
|
+
await validate({
|
|
934
|
+
target,
|
|
935
|
+
fnName,
|
|
936
|
+
request
|
|
937
|
+
});
|
|
938
|
+
await handleResult({
|
|
939
|
+
result: fn.bind(controllerInstance)(err, request, response, next),
|
|
940
|
+
response,
|
|
941
|
+
target,
|
|
942
|
+
fnName,
|
|
943
|
+
method,
|
|
944
|
+
path: path instanceof RegExp ? path.source : fp,
|
|
945
|
+
isErrorMiddleware: true
|
|
946
|
+
});
|
|
883
947
|
} catch (e) {
|
|
884
948
|
console.error(e);
|
|
885
949
|
next(e);
|
|
@@ -889,35 +953,52 @@ Split these into separate @MiddlewareClass classes, or merge the logic into a si
|
|
|
889
953
|
return;
|
|
890
954
|
}
|
|
891
955
|
router[methodName](fp, async (request, response, next) => {
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
}
|
|
905
|
-
const result = await fn.bind(controllerInstance)(request, response, next);
|
|
906
|
-
if (result instanceof ResponseEntity) {
|
|
907
|
-
const body = schemas && schemas.responseBody ? await _parseOrThrow(schemas.responseBody, result.getBody(), "resbody") : result.getBody();
|
|
908
|
-
response.contentType("application/json").status(result.getStatusCode()).set(result.getHeaders()).send(Sapling.serialize(body));
|
|
909
|
-
return;
|
|
910
|
-
}
|
|
911
|
-
if (result instanceof RedirectView) {
|
|
912
|
-
response.redirect(result.getUrl());
|
|
913
|
-
return;
|
|
914
|
-
}
|
|
915
|
-
if (method !== "USE" && !response.writableEnded) response.status(404).send(Html404ErrorPage(`Cannot ${methodName.toUpperCase()} ${path instanceof RegExp ? path.source : fp}`));
|
|
956
|
+
await validate({
|
|
957
|
+
target,
|
|
958
|
+
fnName,
|
|
959
|
+
request
|
|
960
|
+
});
|
|
961
|
+
await handleResult({
|
|
962
|
+
result: await fn.bind(controllerInstance)(request, response, next),
|
|
963
|
+
response,
|
|
964
|
+
target,
|
|
965
|
+
fnName,
|
|
966
|
+
method,
|
|
967
|
+
path: path instanceof RegExp ? path.source : fp
|
|
968
|
+
});
|
|
916
969
|
});
|
|
917
970
|
}
|
|
918
971
|
_ControllerRegistry.set(targetClass, router);
|
|
919
972
|
};
|
|
920
973
|
}
|
|
974
|
+
async function handleResult({ result, target, fnName, response, method, path, isErrorMiddleware = false }) {
|
|
975
|
+
const schemas = _getValidatorSchema(target, fnName);
|
|
976
|
+
if (result instanceof ResponseEntity) {
|
|
977
|
+
const body = schemas && schemas.responseBody ? await _parseOrThrow(schemas.responseBody, result.getBody(), "resbody", fnName) : result.getBody();
|
|
978
|
+
response.contentType("application/json").status(result.getStatusCode()).set(result.getHeaders()).send(Sapling.serialize(body));
|
|
979
|
+
return;
|
|
980
|
+
}
|
|
981
|
+
if (result instanceof RedirectView) {
|
|
982
|
+
response.redirect(result.getUrl());
|
|
983
|
+
return;
|
|
984
|
+
}
|
|
985
|
+
if (!isErrorMiddleware && method !== "USE" && !response.writableEnded) response.status(404).send(Html404ErrorPage(`Cannot ${method} ${path}`));
|
|
986
|
+
}
|
|
987
|
+
async function validate({ target, fnName, request }) {
|
|
988
|
+
const schemas = _getValidatorSchema(target, fnName);
|
|
989
|
+
if (schemas) {
|
|
990
|
+
if (schemas.requestBody) request.body = await _parseOrThrow(schemas.requestBody, request.body, "reqbody", fnName);
|
|
991
|
+
if (schemas.requestParam) request.params = await _parseOrThrow(schemas.requestParam, request.params, "reqparams", fnName);
|
|
992
|
+
if (schemas.requestQuery) {
|
|
993
|
+
const parsedQuery = await _parseOrThrow(schemas.requestQuery, request.query, "reqquery", fnName);
|
|
994
|
+
Object.defineProperty(request, "query", {
|
|
995
|
+
value: parsedQuery,
|
|
996
|
+
writable: true,
|
|
997
|
+
configurable: true
|
|
998
|
+
});
|
|
999
|
+
}
|
|
1000
|
+
}
|
|
1001
|
+
}
|
|
921
1002
|
//#endregion
|
|
922
1003
|
//#region src/annotation/middleware.ts
|
|
923
1004
|
/**
|
|
@@ -952,7 +1033,10 @@ DefaultBaseErrorMiddleware = __decorate([MiddlewareClass()], DefaultBaseErrorMid
|
|
|
952
1033
|
//#region src/middleware/default/error/parse.ts
|
|
953
1034
|
let DefaultParserErrorMiddleware = class DefaultParserErrorMiddleware {
|
|
954
1035
|
handle(err, _request, _response, next) {
|
|
955
|
-
if (err instanceof ParserError)
|
|
1036
|
+
if (err instanceof ParserError) {
|
|
1037
|
+
console.warn(err);
|
|
1038
|
+
return ResponseEntity.status(err.status).body({ message: err.message });
|
|
1039
|
+
}
|
|
956
1040
|
next(err);
|
|
957
1041
|
}
|
|
958
1042
|
};
|
|
@@ -979,17 +1063,48 @@ __decorate([GET(_settings.doc.openApiPath)], DefaultOpenApiMiddleware.prototype,
|
|
|
979
1063
|
DefaultOpenApiMiddleware = __decorate([MiddlewareClass()], DefaultOpenApiMiddleware);
|
|
980
1064
|
//#endregion
|
|
981
1065
|
//#region src/middleware/default/swagger/index.ts
|
|
1066
|
+
/**
|
|
1067
|
+
* Enable the serving of the Swagger endpoint used to serve the OpenAPI spec generated by Sapling.
|
|
1068
|
+
*
|
|
1069
|
+
* Configure any middleware-specific settings with `Sapling.Extras.swaggerAndOpenApi`
|
|
1070
|
+
*
|
|
1071
|
+
* You must register `DefaultSwaggerMiddleware.Serve` & `DefaultSwaggerMiddleware.Setup` after `DefaultOpenApiMiddleware`
|
|
1072
|
+
*
|
|
1073
|
+
* ```ts
|
|
1074
|
+
* const middlewares = [
|
|
1075
|
+
* DefaultOpenApiMiddleware,
|
|
1076
|
+
* DefaultSwaggerMiddleware.Serve,
|
|
1077
|
+
* DefaultSwaggerMiddleware.Setup,
|
|
1078
|
+
* ];
|
|
1079
|
+
* middlewares.map(Sapling.resolve).forEach((r) => app.use(r));
|
|
1080
|
+
* ```
|
|
1081
|
+
*/
|
|
982
1082
|
let Serve = class Serve {
|
|
983
|
-
|
|
984
|
-
this.handlers = swagger_ui_express.default.serve;
|
|
985
|
-
}
|
|
1083
|
+
handlers = swagger_ui_express.default.serve;
|
|
986
1084
|
handle(request, response, next) {
|
|
987
1085
|
return Sapling.chainHandlers(this.handlers, request, response, next);
|
|
988
1086
|
}
|
|
989
1087
|
};
|
|
990
1088
|
__decorate([Middleware(_settings.doc.swaggerPath)], Serve.prototype, "handle", null);
|
|
991
1089
|
Serve = __decorate([MiddlewareClass()], Serve);
|
|
1090
|
+
/**
|
|
1091
|
+
* Enable the serving of the Swagger endpoint used to serve the OpenAPI spec generated by Sapling.
|
|
1092
|
+
*
|
|
1093
|
+
* Configure any middleware-specific settings with `Sapling.Extras.swaggerAndOpenApi`
|
|
1094
|
+
*
|
|
1095
|
+
* You must register `DefaultSwaggerMiddleware.Serve` & `DefaultSwaggerMiddleware.Setup` after `DefaultOpenApiMiddleware`
|
|
1096
|
+
*
|
|
1097
|
+
* ```ts
|
|
1098
|
+
* const middlewares = [
|
|
1099
|
+
* DefaultOpenApiMiddleware,
|
|
1100
|
+
* DefaultSwaggerMiddleware.Serve,
|
|
1101
|
+
* DefaultSwaggerMiddleware.Setup,
|
|
1102
|
+
* ];
|
|
1103
|
+
* middlewares.map(Sapling.resolve).forEach((r) => app.use(r));
|
|
1104
|
+
* ```
|
|
1105
|
+
*/
|
|
992
1106
|
let Setup = class Setup {
|
|
1107
|
+
handler;
|
|
993
1108
|
constructor() {
|
|
994
1109
|
this.handler = swagger_ui_express.default.setup(null, { swaggerOptions: { url: _settings.doc.openApiPath } });
|
|
995
1110
|
}
|
|
@@ -999,6 +1114,22 @@ let Setup = class Setup {
|
|
|
999
1114
|
};
|
|
1000
1115
|
__decorate([Middleware(_settings.doc.swaggerPath)], Setup.prototype, "handle", null);
|
|
1001
1116
|
Setup = __decorate([MiddlewareClass()], Setup);
|
|
1117
|
+
/**
|
|
1118
|
+
* Enable the serving of the Swagger endpoint used to serve the OpenAPI spec generated by Sapling.
|
|
1119
|
+
*
|
|
1120
|
+
* Configure any middleware-specific settings with `Sapling.Extras.swaggerAndOpenApi`
|
|
1121
|
+
*
|
|
1122
|
+
* You must register `DefaultSwaggerMiddleware.Serve` & `DefaultSwaggerMiddleware.Setup` after `DefaultOpenApiMiddleware`
|
|
1123
|
+
*
|
|
1124
|
+
* ```ts
|
|
1125
|
+
* const middlewares = [
|
|
1126
|
+
* DefaultOpenApiMiddleware,
|
|
1127
|
+
* DefaultSwaggerMiddleware.Serve,
|
|
1128
|
+
* DefaultSwaggerMiddleware.Setup,
|
|
1129
|
+
* ];
|
|
1130
|
+
* middlewares.map(Sapling.resolve).forEach((r) => app.use(r));
|
|
1131
|
+
* ```
|
|
1132
|
+
*/
|
|
1002
1133
|
const DefaultSwaggerMiddleware = {
|
|
1003
1134
|
Serve,
|
|
1004
1135
|
Setup
|
|
@@ -1058,19 +1189,19 @@ exports._ControllerRegistry = _ControllerRegistry;
|
|
|
1058
1189
|
exports._InjectableDeps = _InjectableDeps;
|
|
1059
1190
|
exports._InjectableRegistry = _InjectableRegistry;
|
|
1060
1191
|
exports._Route = _Route;
|
|
1192
|
+
exports._clearOpenApiRegistry = _clearOpenApiRegistry;
|
|
1061
1193
|
exports._getControllerSchema = _getControllerSchema;
|
|
1062
1194
|
exports._getOrCreateSchemaDefinition = _getOrCreateSchemaDefinition;
|
|
1063
1195
|
exports._getRouteSchema = _getRouteSchema;
|
|
1064
1196
|
exports._getRoutes = _getRoutes;
|
|
1065
1197
|
exports._getValidatorSchema = _getValidatorSchema;
|
|
1066
1198
|
exports._parseOrThrow = _parseOrThrow;
|
|
1067
|
-
exports.
|
|
1199
|
+
exports._registerController = _registerController;
|
|
1068
1200
|
exports._resolve = _resolve;
|
|
1201
|
+
exports._saveValidatorSchema = _saveValidatorSchema;
|
|
1069
1202
|
exports._setControllerSchema = _setControllerSchema;
|
|
1070
|
-
exports._setOnce = _setOnce;
|
|
1071
1203
|
exports._setRouteSchema = _setRouteSchema;
|
|
1072
1204
|
exports._settings = _settings;
|
|
1073
1205
|
exports.generateOpenApiSpec = generateOpenApiSpec;
|
|
1074
1206
|
exports.methodResolve = methodResolve;
|
|
1075
1207
|
exports.openApiGenerator = openApiGenerator;
|
|
1076
|
-
exports.setOpenApiConfig = setOpenApiConfig;
|