@tahminator/sapling 2.0.5-beta.ac279593 → 2.0.5-beta.e0403942
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 +278 -153
- package/dist/index.d.cts +220 -99
- package/dist/index.d.mts +220 -99
- package/dist/index.mjs +277 -151
- 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,78 @@ 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
|
-
this.controllers = /* @__PURE__ */ new Set();
|
|
521
|
-
this.config = {
|
|
522
|
-
title: "API",
|
|
523
|
-
version: "1.0.0"
|
|
524
|
-
};
|
|
525
|
-
}
|
|
526
|
-
setConfig(config) {
|
|
527
|
-
this.config = config;
|
|
528
|
-
}
|
|
632
|
+
controllers = /* @__PURE__ */ new Set();
|
|
529
633
|
registerController(controllerClass, prefix) {
|
|
530
634
|
this.controllers.add({
|
|
531
635
|
class: controllerClass,
|
|
532
636
|
prefix
|
|
533
637
|
});
|
|
534
638
|
}
|
|
639
|
+
get metadata() {
|
|
640
|
+
return _settings.doc.metadata;
|
|
641
|
+
}
|
|
535
642
|
generateSpec() {
|
|
536
|
-
const
|
|
643
|
+
const metadata = this.metadata;
|
|
537
644
|
const paths = {};
|
|
538
645
|
const tags = [];
|
|
539
646
|
for (const { class: controllerClass, prefix } of this.controllers) {
|
|
@@ -559,14 +666,19 @@ var OpenAPIGenerator = class {
|
|
|
559
666
|
};
|
|
560
667
|
} else responses["200"] = { description: "Successful response" };
|
|
561
668
|
if (routeSchema?.responses) for (const resp of routeSchema.responses) {
|
|
562
|
-
const
|
|
563
|
-
responses[
|
|
564
|
-
|
|
565
|
-
|
|
669
|
+
const statusCode = String(resp.statusCode);
|
|
670
|
+
const existingResponse = responses[statusCode];
|
|
671
|
+
const existingSchema = existingResponse && "content" in existingResponse ? existingResponse.content?.["application/json"]?.schema : void 0;
|
|
672
|
+
const responseSchema = resp.schema ? this.toJsonSchema(resp.schema, "output") : statusCode === "200" ? existingSchema : void 0;
|
|
673
|
+
responses[statusCode] = {
|
|
674
|
+
...existingResponse,
|
|
675
|
+
description: resp.description ?? responseSchema?.description ?? existingResponse?.description ?? `Response ${resp.statusCode}`,
|
|
676
|
+
...responseSchema ? { content: { "application/json": { schema: responseSchema } } } : {}
|
|
566
677
|
};
|
|
567
678
|
}
|
|
568
679
|
const operation = {
|
|
569
680
|
responses,
|
|
681
|
+
summary: routeSchema?.summary,
|
|
570
682
|
description: routeSchema?.description,
|
|
571
683
|
tags: controllerSchema?.title ? [controllerSchema.title] : void 0
|
|
572
684
|
};
|
|
@@ -614,9 +726,9 @@ var OpenAPIGenerator = class {
|
|
|
614
726
|
return {
|
|
615
727
|
openapi: "3.0.0",
|
|
616
728
|
info: {
|
|
617
|
-
title:
|
|
618
|
-
version:
|
|
619
|
-
description:
|
|
729
|
+
title: metadata.title,
|
|
730
|
+
version: metadata.version,
|
|
731
|
+
description: metadata.description
|
|
620
732
|
},
|
|
621
733
|
tags: tags.length > 0 ? tags : void 0,
|
|
622
734
|
paths
|
|
@@ -625,21 +737,17 @@ var OpenAPIGenerator = class {
|
|
|
625
737
|
toJsonSchema(schema, direction = "output") {
|
|
626
738
|
try {
|
|
627
739
|
const jsonSchema = schema["~standard"].jsonSchema;
|
|
628
|
-
|
|
629
|
-
return jsonSchema.output({ target: "openapi-3.0" });
|
|
740
|
+
return direction === "input" ? jsonSchema.input({ target: "openapi-3.0" }) : jsonSchema.output({ target: "openapi-3.0" });
|
|
630
741
|
} catch (e) {
|
|
631
|
-
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
|
|
742
|
+
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 });
|
|
632
743
|
throw e;
|
|
633
744
|
}
|
|
634
745
|
}
|
|
635
746
|
};
|
|
636
747
|
const openApiGenerator = new OpenAPIGenerator();
|
|
637
|
-
function
|
|
748
|
+
function _registerController(controllerClass, prefix) {
|
|
638
749
|
openApiGenerator.registerController(controllerClass, prefix);
|
|
639
750
|
}
|
|
640
|
-
function setOpenApiConfig(config) {
|
|
641
|
-
openApiGenerator.setConfig(config);
|
|
642
|
-
}
|
|
643
751
|
function generateOpenApiSpec() {
|
|
644
752
|
return openApiGenerator.generateSpec();
|
|
645
753
|
}
|
|
@@ -776,60 +884,6 @@ function _resolve(ctor) {
|
|
|
776
884
|
return _InjectableRegistry.get(ctor);
|
|
777
885
|
}
|
|
778
886
|
//#endregion
|
|
779
|
-
//#region src/annotation/validator.ts
|
|
780
|
-
const _validatorSchemaStore = /* @__PURE__ */ new WeakMap();
|
|
781
|
-
function ResponseBody(schema) {
|
|
782
|
-
return (target, propertyKey) => {
|
|
783
|
-
const ctor = target.constructor;
|
|
784
|
-
const fnName = String(propertyKey);
|
|
785
|
-
_setOnce(_getOrCreateSchemaDefinition(ctor, fnName), "responseBody", schema, fnName);
|
|
786
|
-
};
|
|
787
|
-
}
|
|
788
|
-
function RequestBody(schema) {
|
|
789
|
-
return (target, propertyKey) => {
|
|
790
|
-
const ctor = target.constructor;
|
|
791
|
-
const fnName = String(propertyKey);
|
|
792
|
-
_setOnce(_getOrCreateSchemaDefinition(ctor, fnName), "requestBody", schema, fnName);
|
|
793
|
-
};
|
|
794
|
-
}
|
|
795
|
-
function RequestParam(schema) {
|
|
796
|
-
return (target, propertyKey) => {
|
|
797
|
-
const ctor = target.constructor;
|
|
798
|
-
const fnName = String(propertyKey);
|
|
799
|
-
_setOnce(_getOrCreateSchemaDefinition(ctor, fnName), "requestParam", schema, fnName);
|
|
800
|
-
};
|
|
801
|
-
}
|
|
802
|
-
function RequestQuery(schema) {
|
|
803
|
-
return (target, propertyKey) => {
|
|
804
|
-
const ctor = target.constructor;
|
|
805
|
-
const fnName = String(propertyKey);
|
|
806
|
-
_setOnce(_getOrCreateSchemaDefinition(ctor, fnName), "requestQuery", schema, fnName);
|
|
807
|
-
};
|
|
808
|
-
}
|
|
809
|
-
function _getOrCreateSchemaDefinition(ctor, fnName) {
|
|
810
|
-
const byFn = _getOrCreateMap(_validatorSchemaStore, ctor);
|
|
811
|
-
const existing = byFn.get(fnName);
|
|
812
|
-
if (existing) return existing;
|
|
813
|
-
const created = {};
|
|
814
|
-
byFn.set(fnName, created);
|
|
815
|
-
return created;
|
|
816
|
-
}
|
|
817
|
-
async function _parseOrThrow(schema, input, kind) {
|
|
818
|
-
const result = await schema["~standard"].validate(input);
|
|
819
|
-
if (result.issues) {
|
|
820
|
-
console.debug(`Failed to parse a schema`);
|
|
821
|
-
throw new ParserError(kind, result.issues, schema["~standard"].vendor);
|
|
822
|
-
}
|
|
823
|
-
return result.value;
|
|
824
|
-
}
|
|
825
|
-
function _getValidatorSchema(ctor, fnName) {
|
|
826
|
-
return _validatorSchemaStore.get(ctor)?.get(fnName);
|
|
827
|
-
}
|
|
828
|
-
function _setOnce(def, key, schema, fnName) {
|
|
829
|
-
if (def[key]) throw new Error(`Duplicate schema for "${String(key)}" on method "${fnName}"`);
|
|
830
|
-
def[key] = schema;
|
|
831
|
-
}
|
|
832
|
-
//#endregion
|
|
833
887
|
//#region src/annotation/controller.ts
|
|
834
888
|
const _ControllerRegistry = /* @__PURE__ */ new WeakMap();
|
|
835
889
|
/**
|
|
@@ -841,7 +895,7 @@ const _ControllerRegistry = /* @__PURE__ */ new WeakMap();
|
|
|
841
895
|
function Controller({ prefix = "", deps = [] } = {}) {
|
|
842
896
|
return (target) => {
|
|
843
897
|
const targetClass = target;
|
|
844
|
-
|
|
898
|
+
_registerController(target, prefix);
|
|
845
899
|
const router = (0, express.Router)();
|
|
846
900
|
const routes = _getRoutes(target);
|
|
847
901
|
const usedRoutes = /* @__PURE__ */ new Set();
|
|
@@ -866,15 +920,20 @@ Split these into separate @MiddlewareClass classes, or merge the logic into a si
|
|
|
866
920
|
if (method === "USE" && fn.length >= 4) {
|
|
867
921
|
const middlewareFn = async (err, request, response, next) => {
|
|
868
922
|
try {
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
}
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
923
|
+
await validate({
|
|
924
|
+
target,
|
|
925
|
+
fnName,
|
|
926
|
+
request
|
|
927
|
+
});
|
|
928
|
+
await handleResult({
|
|
929
|
+
result: fn.bind(controllerInstance)(err, request, response, next),
|
|
930
|
+
response,
|
|
931
|
+
target,
|
|
932
|
+
fnName,
|
|
933
|
+
method,
|
|
934
|
+
path: path instanceof RegExp ? path.source : fp,
|
|
935
|
+
isErrorMiddleware: true
|
|
936
|
+
});
|
|
878
937
|
} catch (e) {
|
|
879
938
|
console.error(e);
|
|
880
939
|
next(e);
|
|
@@ -884,35 +943,52 @@ Split these into separate @MiddlewareClass classes, or merge the logic into a si
|
|
|
884
943
|
return;
|
|
885
944
|
}
|
|
886
945
|
router[methodName](fp, async (request, response, next) => {
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
}
|
|
900
|
-
const result = await fn.bind(controllerInstance)(request, response, next);
|
|
901
|
-
if (result instanceof ResponseEntity) {
|
|
902
|
-
const body = schemas && schemas.responseBody ? await _parseOrThrow(schemas.responseBody, result.getBody(), "resbody") : result.getBody();
|
|
903
|
-
response.contentType("application/json").status(result.getStatusCode()).set(result.getHeaders()).send(Sapling.serialize(body));
|
|
904
|
-
return;
|
|
905
|
-
}
|
|
906
|
-
if (result instanceof RedirectView) {
|
|
907
|
-
response.redirect(result.getUrl());
|
|
908
|
-
return;
|
|
909
|
-
}
|
|
910
|
-
if (method !== "USE" && !response.writableEnded) response.status(404).send(Html404ErrorPage(`Cannot ${methodName.toUpperCase()} ${path instanceof RegExp ? path.source : fp}`));
|
|
946
|
+
await validate({
|
|
947
|
+
target,
|
|
948
|
+
fnName,
|
|
949
|
+
request
|
|
950
|
+
});
|
|
951
|
+
await handleResult({
|
|
952
|
+
result: await fn.bind(controllerInstance)(request, response, next),
|
|
953
|
+
response,
|
|
954
|
+
target,
|
|
955
|
+
fnName,
|
|
956
|
+
method,
|
|
957
|
+
path: path instanceof RegExp ? path.source : fp
|
|
958
|
+
});
|
|
911
959
|
});
|
|
912
960
|
}
|
|
913
961
|
_ControllerRegistry.set(targetClass, router);
|
|
914
962
|
};
|
|
915
963
|
}
|
|
964
|
+
async function handleResult({ result, target, fnName, response, method, path, isErrorMiddleware = false }) {
|
|
965
|
+
const schemas = _getValidatorSchema(target, fnName);
|
|
966
|
+
if (result instanceof ResponseEntity) {
|
|
967
|
+
const body = schemas && schemas.responseBody ? await _parseOrThrow(schemas.responseBody, result.getBody(), "resbody", fnName) : result.getBody();
|
|
968
|
+
response.contentType("application/json").status(result.getStatusCode()).set(result.getHeaders()).send(Sapling.serialize(body));
|
|
969
|
+
return;
|
|
970
|
+
}
|
|
971
|
+
if (result instanceof RedirectView) {
|
|
972
|
+
response.redirect(result.getUrl());
|
|
973
|
+
return;
|
|
974
|
+
}
|
|
975
|
+
if (!isErrorMiddleware && method !== "USE" && !response.writableEnded) response.status(404).send(Html404ErrorPage(`Cannot ${method} ${path}`));
|
|
976
|
+
}
|
|
977
|
+
async function validate({ target, fnName, request }) {
|
|
978
|
+
const schemas = _getValidatorSchema(target, fnName);
|
|
979
|
+
if (schemas) {
|
|
980
|
+
if (schemas.requestBody) request.body = await _parseOrThrow(schemas.requestBody, request.body, "reqbody", fnName);
|
|
981
|
+
if (schemas.requestParam) request.params = await _parseOrThrow(schemas.requestParam, request.params, "reqparams", fnName);
|
|
982
|
+
if (schemas.requestQuery) {
|
|
983
|
+
const parsedQuery = await _parseOrThrow(schemas.requestQuery, request.query, "reqquery", fnName);
|
|
984
|
+
Object.defineProperty(request, "query", {
|
|
985
|
+
value: parsedQuery,
|
|
986
|
+
writable: true,
|
|
987
|
+
configurable: true
|
|
988
|
+
});
|
|
989
|
+
}
|
|
990
|
+
}
|
|
991
|
+
}
|
|
916
992
|
//#endregion
|
|
917
993
|
//#region src/annotation/middleware.ts
|
|
918
994
|
/**
|
|
@@ -947,7 +1023,10 @@ DefaultBaseErrorMiddleware = __decorate([MiddlewareClass()], DefaultBaseErrorMid
|
|
|
947
1023
|
//#region src/middleware/default/error/parse.ts
|
|
948
1024
|
let DefaultParserErrorMiddleware = class DefaultParserErrorMiddleware {
|
|
949
1025
|
handle(err, _request, _response, next) {
|
|
950
|
-
if (err instanceof ParserError)
|
|
1026
|
+
if (err instanceof ParserError) {
|
|
1027
|
+
console.warn(err);
|
|
1028
|
+
return ResponseEntity.status(err.status).body({ message: err.message });
|
|
1029
|
+
}
|
|
951
1030
|
next(err);
|
|
952
1031
|
}
|
|
953
1032
|
};
|
|
@@ -974,17 +1053,48 @@ __decorate([GET(_settings.doc.openApiPath)], DefaultOpenApiMiddleware.prototype,
|
|
|
974
1053
|
DefaultOpenApiMiddleware = __decorate([MiddlewareClass()], DefaultOpenApiMiddleware);
|
|
975
1054
|
//#endregion
|
|
976
1055
|
//#region src/middleware/default/swagger/index.ts
|
|
1056
|
+
/**
|
|
1057
|
+
* Enable the serving of the Swagger endpoint used to serve the OpenAPI spec generated by Sapling.
|
|
1058
|
+
*
|
|
1059
|
+
* Configure any middleware-specific settings with `Sapling.Extras.swaggerAndOpenApi`
|
|
1060
|
+
*
|
|
1061
|
+
* You must register `DefaultSwaggerMiddleware.Serve` & `DefaultSwaggerMiddleware.Setup` after `DefaultOpenApiMiddleware`
|
|
1062
|
+
*
|
|
1063
|
+
* ```ts
|
|
1064
|
+
* const middlewares = [
|
|
1065
|
+
* DefaultOpenApiMiddleware,
|
|
1066
|
+
* DefaultSwaggerMiddleware.Serve,
|
|
1067
|
+
* DefaultSwaggerMiddleware.Setup,
|
|
1068
|
+
* ];
|
|
1069
|
+
* middlewares.map(Sapling.resolve).forEach((r) => app.use(r));
|
|
1070
|
+
* ```
|
|
1071
|
+
*/
|
|
977
1072
|
let Serve = class Serve {
|
|
978
|
-
|
|
979
|
-
this.handlers = swagger_ui_express.default.serve;
|
|
980
|
-
}
|
|
1073
|
+
handlers = swagger_ui_express.default.serve;
|
|
981
1074
|
handle(request, response, next) {
|
|
982
1075
|
return Sapling.chainHandlers(this.handlers, request, response, next);
|
|
983
1076
|
}
|
|
984
1077
|
};
|
|
985
1078
|
__decorate([Middleware(_settings.doc.swaggerPath)], Serve.prototype, "handle", null);
|
|
986
1079
|
Serve = __decorate([MiddlewareClass()], Serve);
|
|
1080
|
+
/**
|
|
1081
|
+
* Enable the serving of the Swagger endpoint used to serve the OpenAPI spec generated by Sapling.
|
|
1082
|
+
*
|
|
1083
|
+
* Configure any middleware-specific settings with `Sapling.Extras.swaggerAndOpenApi`
|
|
1084
|
+
*
|
|
1085
|
+
* You must register `DefaultSwaggerMiddleware.Serve` & `DefaultSwaggerMiddleware.Setup` after `DefaultOpenApiMiddleware`
|
|
1086
|
+
*
|
|
1087
|
+
* ```ts
|
|
1088
|
+
* const middlewares = [
|
|
1089
|
+
* DefaultOpenApiMiddleware,
|
|
1090
|
+
* DefaultSwaggerMiddleware.Serve,
|
|
1091
|
+
* DefaultSwaggerMiddleware.Setup,
|
|
1092
|
+
* ];
|
|
1093
|
+
* middlewares.map(Sapling.resolve).forEach((r) => app.use(r));
|
|
1094
|
+
* ```
|
|
1095
|
+
*/
|
|
987
1096
|
let Setup = class Setup {
|
|
1097
|
+
handler;
|
|
988
1098
|
constructor() {
|
|
989
1099
|
this.handler = swagger_ui_express.default.setup(null, { swaggerOptions: { url: _settings.doc.openApiPath } });
|
|
990
1100
|
}
|
|
@@ -994,6 +1104,22 @@ let Setup = class Setup {
|
|
|
994
1104
|
};
|
|
995
1105
|
__decorate([Middleware(_settings.doc.swaggerPath)], Setup.prototype, "handle", null);
|
|
996
1106
|
Setup = __decorate([MiddlewareClass()], Setup);
|
|
1107
|
+
/**
|
|
1108
|
+
* Enable the serving of the Swagger endpoint used to serve the OpenAPI spec generated by Sapling.
|
|
1109
|
+
*
|
|
1110
|
+
* Configure any middleware-specific settings with `Sapling.Extras.swaggerAndOpenApi`
|
|
1111
|
+
*
|
|
1112
|
+
* You must register `DefaultSwaggerMiddleware.Serve` & `DefaultSwaggerMiddleware.Setup` after `DefaultOpenApiMiddleware`
|
|
1113
|
+
*
|
|
1114
|
+
* ```ts
|
|
1115
|
+
* const middlewares = [
|
|
1116
|
+
* DefaultOpenApiMiddleware,
|
|
1117
|
+
* DefaultSwaggerMiddleware.Serve,
|
|
1118
|
+
* DefaultSwaggerMiddleware.Setup,
|
|
1119
|
+
* ];
|
|
1120
|
+
* middlewares.map(Sapling.resolve).forEach((r) => app.use(r));
|
|
1121
|
+
* ```
|
|
1122
|
+
*/
|
|
997
1123
|
const DefaultSwaggerMiddleware = {
|
|
998
1124
|
Serve,
|
|
999
1125
|
Setup
|
|
@@ -1059,13 +1185,12 @@ exports._getRouteSchema = _getRouteSchema;
|
|
|
1059
1185
|
exports._getRoutes = _getRoutes;
|
|
1060
1186
|
exports._getValidatorSchema = _getValidatorSchema;
|
|
1061
1187
|
exports._parseOrThrow = _parseOrThrow;
|
|
1062
|
-
exports.
|
|
1188
|
+
exports._registerController = _registerController;
|
|
1063
1189
|
exports._resolve = _resolve;
|
|
1190
|
+
exports._saveValidatorSchema = _saveValidatorSchema;
|
|
1064
1191
|
exports._setControllerSchema = _setControllerSchema;
|
|
1065
|
-
exports._setOnce = _setOnce;
|
|
1066
1192
|
exports._setRouteSchema = _setRouteSchema;
|
|
1067
1193
|
exports._settings = _settings;
|
|
1068
1194
|
exports.generateOpenApiSpec = generateOpenApiSpec;
|
|
1069
1195
|
exports.methodResolve = methodResolve;
|
|
1070
1196
|
exports.openApiGenerator = openApiGenerator;
|
|
1071
|
-
exports.setOpenApiConfig = setOpenApiConfig;
|