@tahminator/sapling 2.0.5-beta.a565b2cc → 2.0.5-beta.ac279593
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 +191 -165
- package/dist/index.d.cts +43 -85
- package/dist/index.d.mts +43 -85
- package/dist/index.mjs +180 -163
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -258,6 +258,7 @@ var ParserError = class ParserError extends ResponseStatusError {
|
|
|
258
258
|
case "reqbody": return "request body";
|
|
259
259
|
case "reqparams": return "request params";
|
|
260
260
|
case "reqquery": return "request query";
|
|
261
|
+
case "resbody": return "response body";
|
|
261
262
|
}
|
|
262
263
|
})()}: ${formatted}`;
|
|
263
264
|
}
|
|
@@ -381,134 +382,20 @@ var Sapling = class Sapling {
|
|
|
381
382
|
static setSwaggerPath(path) {
|
|
382
383
|
_settings.doc.swaggerPath = path;
|
|
383
384
|
}
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
* const CREATE_BOOK_REQUEST_BODY_SCHEMA = z.object({
|
|
397
|
-
* name: z.string(),
|
|
398
|
-
* description: z.string().optional(),
|
|
399
|
-
* });
|
|
400
|
-
*
|
|
401
|
-
* ⠀@Controller({ prefix: "/api/book" })
|
|
402
|
-
* class BookController {
|
|
403
|
-
* ⠀@RequestBody(CREATE_BOOK_REQUEST_BODY_SCHEMA)
|
|
404
|
-
* ⠀@POST()
|
|
405
|
-
* public createBook(request: e.Request) {
|
|
406
|
-
* const { name, description } = request.body as unknown as z.infer<
|
|
407
|
-
* typeof CREATE_BOOK_REQUEST_BODY_SCHEMA
|
|
408
|
-
* >;
|
|
409
|
-
* }
|
|
410
|
-
* }
|
|
411
|
-
* ```
|
|
412
|
-
*/
|
|
413
|
-
function RequestBody(schema) {
|
|
414
|
-
return (target, propertyKey) => {
|
|
415
|
-
const ctor = target.constructor;
|
|
416
|
-
const fnName = String(propertyKey);
|
|
417
|
-
_setOnce(_getOrCreateRequestSchemaDefinition(ctor, fnName), "body", schema, fnName);
|
|
418
|
-
};
|
|
419
|
-
}
|
|
420
|
-
/**
|
|
421
|
-
* Apply to a route method to have `request.param` be parsed by `schema`.
|
|
422
|
-
*
|
|
423
|
-
* This annotation will parse `request.param` & then override `request.param`.
|
|
424
|
-
* You can then just simply cast `request.param` for your use
|
|
425
|
-
*
|
|
426
|
-
* @example
|
|
427
|
-
* ```ts
|
|
428
|
-
* const GET_BOOK_REQUEST_PARAM_SCHEMA = z.object({
|
|
429
|
-
* bookId: z.string(),
|
|
430
|
-
* });
|
|
431
|
-
*
|
|
432
|
-
* ⠀@Controller({ prefix: "/api/book" })
|
|
433
|
-
* class BookController {
|
|
434
|
-
* ⠀@RequestParam(GET_BOOK_REQUEST_PARAM_SCHEMA)
|
|
435
|
-
* ⠀@GET("/:bookId")
|
|
436
|
-
* public getBook(request: e.Request) {
|
|
437
|
-
* const { bookId } = request.param as unknown as z.infer<
|
|
438
|
-
* typeof GET_BOOK_REQUEST_PARAM_SCHEMA
|
|
439
|
-
* >;
|
|
440
|
-
* }
|
|
441
|
-
* }
|
|
442
|
-
* ```
|
|
443
|
-
*/
|
|
444
|
-
function RequestParam(schema) {
|
|
445
|
-
return (target, propertyKey) => {
|
|
446
|
-
const ctor = target.constructor;
|
|
447
|
-
const fnName = String(propertyKey);
|
|
448
|
-
_setOnce(_getOrCreateRequestSchemaDefinition(ctor, fnName), "param", schema, fnName);
|
|
449
|
-
};
|
|
450
|
-
}
|
|
451
|
-
/**
|
|
452
|
-
* Apply to a route method to have `request.query` be parsed by `schema`.
|
|
453
|
-
*
|
|
454
|
-
* This annotation will parse `request.query` & then override `request.query`.
|
|
455
|
-
* You can then just simply cast `request.query` for your use
|
|
456
|
-
*
|
|
457
|
-
* @example
|
|
458
|
-
* ```ts
|
|
459
|
-
* const LIST_BOOKS_REQUEST_QUERY_SCHEMA = z.object({
|
|
460
|
-
* sort: z.enum(["name", "createdAt"]).optional(),
|
|
461
|
-
* q: z.string().optional(),
|
|
462
|
-
* });
|
|
463
|
-
*
|
|
464
|
-
* ⠀@Controller({ prefix: "/api/book" })
|
|
465
|
-
* class BookController {
|
|
466
|
-
* ⠀@RequestQuery(LIST_BOOKS_REQUEST_QUERY_SCHEMA)
|
|
467
|
-
* ⠀@GET()
|
|
468
|
-
* public listBooks(request: e.Request) {
|
|
469
|
-
* const { sort, q } = request.query as unknown as z.infer<
|
|
470
|
-
* typeof LIST_BOOKS_REQUEST_QUERY_SCHEMA
|
|
471
|
-
* >;
|
|
472
|
-
* }
|
|
473
|
-
* }
|
|
474
|
-
* ```
|
|
475
|
-
*/
|
|
476
|
-
function RequestQuery(schema) {
|
|
477
|
-
return (target, propertyKey) => {
|
|
478
|
-
const ctor = target.constructor;
|
|
479
|
-
const fnName = String(propertyKey);
|
|
480
|
-
_setOnce(_getOrCreateRequestSchemaDefinition(ctor, fnName), "query", schema, fnName);
|
|
481
|
-
};
|
|
482
|
-
}
|
|
483
|
-
function _getOrCreateRequestSchemaDefinition(ctor, fnName) {
|
|
484
|
-
const byFn = (() => {
|
|
485
|
-
const fn = _requestSchemaStore.get(ctor);
|
|
486
|
-
if (fn) return fn;
|
|
487
|
-
const newFn = /* @__PURE__ */ new Map();
|
|
488
|
-
_requestSchemaStore.set(ctor, newFn);
|
|
489
|
-
return newFn;
|
|
490
|
-
})();
|
|
491
|
-
const existing = byFn.get(fnName);
|
|
492
|
-
if (existing) return existing;
|
|
493
|
-
const created = {};
|
|
494
|
-
byFn.set(fnName, created);
|
|
495
|
-
return created;
|
|
496
|
-
}
|
|
497
|
-
function _setOnce(def, key, schema, fnName) {
|
|
498
|
-
if (def[key]) throw new Error(`Duplicate request schema for "${String(key)}" on method "${fnName}"`);
|
|
499
|
-
def[key] = schema;
|
|
500
|
-
}
|
|
501
|
-
function _getRequestSchemas(ctor, fnName) {
|
|
502
|
-
return _requestSchemaStore.get(ctor)?.get(fnName);
|
|
503
|
-
}
|
|
504
|
-
async function _parseOrThrow(schema, input, kind) {
|
|
505
|
-
const result = await schema["~standard"].validate(input);
|
|
506
|
-
if (result.issues) {
|
|
507
|
-
console.debug(`Failed to parse a schema`);
|
|
508
|
-
throw new ParserError(kind, result.issues, schema["~standard"].vendor);
|
|
385
|
+
static chainHandlers(handlers, request, response, next, index = 0) {
|
|
386
|
+
if (index >= handlers.length) {
|
|
387
|
+
next();
|
|
388
|
+
return;
|
|
389
|
+
}
|
|
390
|
+
handlers[index]?.(request, response, (err) => {
|
|
391
|
+
if (err) {
|
|
392
|
+
next(err);
|
|
393
|
+
return;
|
|
394
|
+
}
|
|
395
|
+
Sapling.chainHandlers(handlers, request, response, next, index + 1);
|
|
396
|
+
});
|
|
509
397
|
}
|
|
510
|
-
|
|
511
|
-
}
|
|
398
|
+
};
|
|
512
399
|
//#endregion
|
|
513
400
|
//#region src/annotation/route.ts
|
|
514
401
|
const _routeStore = /* @__PURE__ */ new WeakMap();
|
|
@@ -591,6 +478,42 @@ function _getRoutes(ctor) {
|
|
|
591
478
|
return _routeStore.get(ctor) ?? [];
|
|
592
479
|
}
|
|
593
480
|
//#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
|
+
//#region src/annotation/schema.ts
|
|
491
|
+
const _routeSchemaStore = /* @__PURE__ */ new WeakMap();
|
|
492
|
+
const _controllerSchemaStore = /* @__PURE__ */ new WeakMap();
|
|
493
|
+
function ControllerSchema(options) {
|
|
494
|
+
return (target) => {
|
|
495
|
+
_setControllerSchema(target, options);
|
|
496
|
+
};
|
|
497
|
+
}
|
|
498
|
+
function RouteSchema(options) {
|
|
499
|
+
return (target, propertyKey) => {
|
|
500
|
+
const ctor = target.constructor;
|
|
501
|
+
_setRouteSchema(ctor, String(propertyKey), options);
|
|
502
|
+
};
|
|
503
|
+
}
|
|
504
|
+
function _setRouteSchema(ctor, fnName, options) {
|
|
505
|
+
_getOrCreateMap(_routeSchemaStore, ctor).set(fnName, options);
|
|
506
|
+
}
|
|
507
|
+
function _setControllerSchema(ctor, options) {
|
|
508
|
+
_controllerSchemaStore.set(ctor, options);
|
|
509
|
+
}
|
|
510
|
+
function _getRouteSchema(ctor, fnName) {
|
|
511
|
+
return _routeSchemaStore.get(ctor)?.get(fnName);
|
|
512
|
+
}
|
|
513
|
+
function _getControllerSchema(ctor) {
|
|
514
|
+
return _controllerSchemaStore.get(ctor);
|
|
515
|
+
}
|
|
516
|
+
//#endregion
|
|
594
517
|
//#region src/helper/openapi.ts
|
|
595
518
|
var OpenAPIGenerator = class {
|
|
596
519
|
constructor() {
|
|
@@ -612,42 +535,78 @@ var OpenAPIGenerator = class {
|
|
|
612
535
|
generateSpec() {
|
|
613
536
|
const config = this.config;
|
|
614
537
|
const paths = {};
|
|
538
|
+
const tags = [];
|
|
615
539
|
for (const { class: controllerClass, prefix } of this.controllers) {
|
|
616
540
|
const routes = _getRoutes(controllerClass);
|
|
541
|
+
const controllerSchema = _getControllerSchema(controllerClass);
|
|
542
|
+
if (controllerSchema?.title) tags.push({
|
|
543
|
+
name: controllerSchema.title,
|
|
544
|
+
description: controllerSchema.description
|
|
545
|
+
});
|
|
617
546
|
for (const route of routes) {
|
|
618
547
|
if (route.method === "USE") continue;
|
|
619
|
-
const schemas =
|
|
620
|
-
const
|
|
621
|
-
|
|
548
|
+
const schemas = _getValidatorSchema(controllerClass, route.fnName);
|
|
549
|
+
const routeSchema = _getRouteSchema(controllerClass, route.fnName);
|
|
550
|
+
if (route.path instanceof RegExp) throw new Error(`You have a route with a regex path of ${route.path.source}. This is not compatible with OpenAPI.`);
|
|
551
|
+
const openApiPath = (prefix + route.path).replace(/:([A-Za-z0-9_]+)/g, "{$1}");
|
|
622
552
|
if (!paths[openApiPath]) paths[openApiPath] = {};
|
|
623
|
-
const
|
|
553
|
+
const responses = {};
|
|
554
|
+
if (schemas?.responseBody) {
|
|
555
|
+
const responseSchema = this.toJsonSchema(schemas.responseBody, "output");
|
|
556
|
+
responses["200"] = {
|
|
557
|
+
description: responseSchema.description ?? "Successful response",
|
|
558
|
+
content: { "application/json": { schema: responseSchema } }
|
|
559
|
+
};
|
|
560
|
+
} else responses["200"] = { description: "Successful response" };
|
|
561
|
+
if (routeSchema?.responses) for (const resp of routeSchema.responses) {
|
|
562
|
+
const responseSchema = this.toJsonSchema(resp.schema, "output");
|
|
563
|
+
responses[String(resp.statusCode)] = {
|
|
564
|
+
description: resp.description ?? responseSchema.description ?? `Response ${resp.statusCode}`,
|
|
565
|
+
content: { "application/json": { schema: responseSchema } }
|
|
566
|
+
};
|
|
567
|
+
}
|
|
568
|
+
const operation = {
|
|
569
|
+
responses,
|
|
570
|
+
description: routeSchema?.description,
|
|
571
|
+
tags: controllerSchema?.title ? [controllerSchema.title] : void 0
|
|
572
|
+
};
|
|
624
573
|
const parameters = [];
|
|
625
|
-
if (schemas?.
|
|
626
|
-
const paramSchema = this.toJsonSchema(schemas.
|
|
627
|
-
if (paramSchema.type === "object" && paramSchema.properties) for (const [name, schema] of Object.entries(paramSchema.properties))
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
574
|
+
if (schemas?.requestParam) {
|
|
575
|
+
const paramSchema = this.toJsonSchema(schemas.requestParam, "input");
|
|
576
|
+
if (paramSchema.type === "object" && paramSchema.properties) for (const [name, schema] of Object.entries(paramSchema.properties)) {
|
|
577
|
+
const parameterSchema = schema;
|
|
578
|
+
parameters.push({
|
|
579
|
+
name,
|
|
580
|
+
in: "path",
|
|
581
|
+
required: true,
|
|
582
|
+
description: parameterSchema.description,
|
|
583
|
+
schema: parameterSchema
|
|
584
|
+
});
|
|
585
|
+
}
|
|
633
586
|
}
|
|
634
|
-
if (schemas?.
|
|
635
|
-
const querySchema = this.toJsonSchema(schemas.
|
|
587
|
+
if (schemas?.requestQuery) {
|
|
588
|
+
const querySchema = this.toJsonSchema(schemas.requestQuery, "input");
|
|
636
589
|
if (querySchema.type === "object" && querySchema.properties) for (const [name, schema] of Object.entries(querySchema.properties)) {
|
|
637
590
|
const isRequired = Array.isArray(querySchema.required) && querySchema.required.includes(name);
|
|
591
|
+
const parameterSchema = schema;
|
|
638
592
|
parameters.push({
|
|
639
593
|
name,
|
|
640
594
|
in: "query",
|
|
641
595
|
required: isRequired,
|
|
642
|
-
|
|
596
|
+
description: parameterSchema.description,
|
|
597
|
+
schema: parameterSchema
|
|
643
598
|
});
|
|
644
599
|
}
|
|
645
600
|
}
|
|
646
601
|
if (parameters.length > 0) operation.parameters = parameters;
|
|
647
|
-
if (schemas?.
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
602
|
+
if (schemas?.requestBody) {
|
|
603
|
+
const requestSchema = this.toJsonSchema(schemas.requestBody, "input");
|
|
604
|
+
operation.requestBody = {
|
|
605
|
+
required: true,
|
|
606
|
+
description: requestSchema.description,
|
|
607
|
+
content: { "application/json": { schema: requestSchema } }
|
|
608
|
+
};
|
|
609
|
+
}
|
|
651
610
|
const method = route.method.toLowerCase();
|
|
652
611
|
paths[openApiPath][method] = operation;
|
|
653
612
|
}
|
|
@@ -659,12 +618,15 @@ var OpenAPIGenerator = class {
|
|
|
659
618
|
version: config.version,
|
|
660
619
|
description: config.description
|
|
661
620
|
},
|
|
621
|
+
tags: tags.length > 0 ? tags : void 0,
|
|
662
622
|
paths
|
|
663
623
|
};
|
|
664
624
|
}
|
|
665
|
-
toJsonSchema(schema) {
|
|
625
|
+
toJsonSchema(schema, direction = "output") {
|
|
666
626
|
try {
|
|
667
|
-
|
|
627
|
+
const jsonSchema = schema["~standard"].jsonSchema;
|
|
628
|
+
if (direction === "input" && jsonSchema.input) return jsonSchema.input({ target: "openapi-3.0" });
|
|
629
|
+
return jsonSchema.output({ target: "openapi-3.0" });
|
|
668
630
|
} catch (e) {
|
|
669
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`);
|
|
670
632
|
throw e;
|
|
@@ -675,10 +637,10 @@ const openApiGenerator = new OpenAPIGenerator();
|
|
|
675
637
|
function _registerControllerClass(controllerClass, prefix) {
|
|
676
638
|
openApiGenerator.registerController(controllerClass, prefix);
|
|
677
639
|
}
|
|
678
|
-
function
|
|
640
|
+
function setOpenApiConfig(config) {
|
|
679
641
|
openApiGenerator.setConfig(config);
|
|
680
642
|
}
|
|
681
|
-
function
|
|
643
|
+
function generateOpenApiSpec() {
|
|
682
644
|
return openApiGenerator.generateSpec();
|
|
683
645
|
}
|
|
684
646
|
//#endregion
|
|
@@ -814,6 +776,60 @@ function _resolve(ctor) {
|
|
|
814
776
|
return _InjectableRegistry.get(ctor);
|
|
815
777
|
}
|
|
816
778
|
//#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
|
|
817
833
|
//#region src/annotation/controller.ts
|
|
818
834
|
const _ControllerRegistry = /* @__PURE__ */ new WeakMap();
|
|
819
835
|
/**
|
|
@@ -868,12 +884,12 @@ Split these into separate @MiddlewareClass classes, or merge the logic into a si
|
|
|
868
884
|
return;
|
|
869
885
|
}
|
|
870
886
|
router[methodName](fp, async (request, response, next) => {
|
|
871
|
-
const schemas =
|
|
887
|
+
const schemas = _getValidatorSchema(target, fnName);
|
|
872
888
|
if (schemas) {
|
|
873
|
-
if (schemas.
|
|
874
|
-
if (schemas.
|
|
875
|
-
if (schemas.
|
|
876
|
-
const parsedQuery = await _parseOrThrow(schemas.
|
|
889
|
+
if (schemas.requestBody) request.body = await _parseOrThrow(schemas.requestBody, request.body, "reqbody");
|
|
890
|
+
if (schemas.requestParam) request.params = await _parseOrThrow(schemas.requestParam, request.params, "reqparams");
|
|
891
|
+
if (schemas.requestQuery) {
|
|
892
|
+
const parsedQuery = await _parseOrThrow(schemas.requestQuery, request.query, "reqquery");
|
|
877
893
|
Object.defineProperty(request, "query", {
|
|
878
894
|
value: parsedQuery,
|
|
879
895
|
writable: true,
|
|
@@ -883,7 +899,8 @@ Split these into separate @MiddlewareClass classes, or merge the logic into a si
|
|
|
883
899
|
}
|
|
884
900
|
const result = await fn.bind(controllerInstance)(request, response, next);
|
|
885
901
|
if (result instanceof ResponseEntity) {
|
|
886
|
-
|
|
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));
|
|
887
904
|
return;
|
|
888
905
|
}
|
|
889
906
|
if (result instanceof RedirectView) {
|
|
@@ -950,7 +967,7 @@ DefaultResponseStatusErrorMiddleware = __decorate([MiddlewareClass()], DefaultRe
|
|
|
950
967
|
//#region src/middleware/default/openapi/index.ts
|
|
951
968
|
let DefaultOpenApiMiddleware = class DefaultOpenApiMiddleware {
|
|
952
969
|
handle(_request, _response, _next) {
|
|
953
|
-
return ResponseEntity.ok().body(
|
|
970
|
+
return ResponseEntity.ok().body(generateOpenApiSpec());
|
|
954
971
|
}
|
|
955
972
|
};
|
|
956
973
|
__decorate([GET(_settings.doc.openApiPath)], DefaultOpenApiMiddleware.prototype, "handle", null);
|
|
@@ -961,21 +978,21 @@ let Serve = class Serve {
|
|
|
961
978
|
constructor() {
|
|
962
979
|
this.handlers = swagger_ui_express.default.serve;
|
|
963
980
|
}
|
|
964
|
-
handle(
|
|
965
|
-
return this.handlers;
|
|
981
|
+
handle(request, response, next) {
|
|
982
|
+
return Sapling.chainHandlers(this.handlers, request, response, next);
|
|
966
983
|
}
|
|
967
984
|
};
|
|
968
|
-
__decorate([Middleware()], Serve.prototype, "handle", null);
|
|
985
|
+
__decorate([Middleware(_settings.doc.swaggerPath)], Serve.prototype, "handle", null);
|
|
969
986
|
Serve = __decorate([MiddlewareClass()], Serve);
|
|
970
987
|
let Setup = class Setup {
|
|
971
988
|
constructor() {
|
|
972
|
-
this.handler = swagger_ui_express.default.setup(
|
|
989
|
+
this.handler = swagger_ui_express.default.setup(null, { swaggerOptions: { url: _settings.doc.openApiPath } });
|
|
973
990
|
}
|
|
974
991
|
handle(request, response, next) {
|
|
975
992
|
return this.handler(request, response, next);
|
|
976
993
|
}
|
|
977
994
|
};
|
|
978
|
-
__decorate([Middleware()], Setup.prototype, "handle", null);
|
|
995
|
+
__decorate([Middleware(_settings.doc.swaggerPath)], Setup.prototype, "handle", null);
|
|
979
996
|
Setup = __decorate([MiddlewareClass()], Setup);
|
|
980
997
|
const DefaultSwaggerMiddleware = {
|
|
981
998
|
Serve,
|
|
@@ -983,6 +1000,7 @@ const DefaultSwaggerMiddleware = {
|
|
|
983
1000
|
};
|
|
984
1001
|
//#endregion
|
|
985
1002
|
exports.Controller = Controller;
|
|
1003
|
+
exports.ControllerSchema = ControllerSchema;
|
|
986
1004
|
exports.DELETE = DELETE;
|
|
987
1005
|
Object.defineProperty(exports, "DefaultBaseErrorMiddleware", {
|
|
988
1006
|
enumerable: true,
|
|
@@ -1025,21 +1043,29 @@ exports.RedirectView = RedirectView;
|
|
|
1025
1043
|
exports.RequestBody = RequestBody;
|
|
1026
1044
|
exports.RequestParam = RequestParam;
|
|
1027
1045
|
exports.RequestQuery = RequestQuery;
|
|
1046
|
+
exports.ResponseBody = ResponseBody;
|
|
1028
1047
|
exports.ResponseEntity = ResponseEntity;
|
|
1029
1048
|
exports.ResponseEntityBuilder = ResponseEntityBuilder;
|
|
1030
1049
|
exports.ResponseStatusError = ResponseStatusError;
|
|
1050
|
+
exports.RouteSchema = RouteSchema;
|
|
1031
1051
|
exports.Sapling = Sapling;
|
|
1032
1052
|
exports._ControllerRegistry = _ControllerRegistry;
|
|
1033
1053
|
exports._InjectableDeps = _InjectableDeps;
|
|
1034
1054
|
exports._InjectableRegistry = _InjectableRegistry;
|
|
1035
1055
|
exports._Route = _Route;
|
|
1036
|
-
exports.
|
|
1037
|
-
exports.
|
|
1056
|
+
exports._getControllerSchema = _getControllerSchema;
|
|
1057
|
+
exports._getOrCreateSchemaDefinition = _getOrCreateSchemaDefinition;
|
|
1058
|
+
exports._getRouteSchema = _getRouteSchema;
|
|
1038
1059
|
exports._getRoutes = _getRoutes;
|
|
1060
|
+
exports._getValidatorSchema = _getValidatorSchema;
|
|
1039
1061
|
exports._parseOrThrow = _parseOrThrow;
|
|
1040
1062
|
exports._registerControllerClass = _registerControllerClass;
|
|
1041
1063
|
exports._resolve = _resolve;
|
|
1042
|
-
exports.
|
|
1064
|
+
exports._setControllerSchema = _setControllerSchema;
|
|
1065
|
+
exports._setOnce = _setOnce;
|
|
1066
|
+
exports._setRouteSchema = _setRouteSchema;
|
|
1043
1067
|
exports._settings = _settings;
|
|
1068
|
+
exports.generateOpenApiSpec = generateOpenApiSpec;
|
|
1044
1069
|
exports.methodResolve = methodResolve;
|
|
1045
1070
|
exports.openApiGenerator = openApiGenerator;
|
|
1071
|
+
exports.setOpenApiConfig = setOpenApiConfig;
|
package/dist/index.d.cts
CHANGED
|
@@ -437,7 +437,7 @@ declare class ResponseStatusError extends Error {
|
|
|
437
437
|
}
|
|
438
438
|
//#endregion
|
|
439
439
|
//#region src/helper/error/parse.d.ts
|
|
440
|
-
type ParserErrorLocation = "reqbody" | "reqparams" | "reqquery";
|
|
440
|
+
type ParserErrorLocation = "reqbody" | "reqparams" | "reqquery" | "resbody";
|
|
441
441
|
/**
|
|
442
442
|
* This error should be thrown when some data cannot be parsed by a given schema.
|
|
443
443
|
*/
|
|
@@ -531,6 +531,7 @@ declare class Sapling {
|
|
|
531
531
|
static setDeserializeFn(this: void, fn: (value: string) => any): void;
|
|
532
532
|
static setOpenApiPath(this: void, path: string): void;
|
|
533
533
|
static setSwaggerPath(this: void, path: string): void;
|
|
534
|
+
static chainHandlers(this: void, handlers: RequestHandler[], request: Request, response: Response$1, next: NextFunction, index?: number): void;
|
|
534
535
|
}
|
|
535
536
|
//#endregion
|
|
536
537
|
//#region node_modules/.pnpm/openapi-types@12.1.3/node_modules/openapi-types/dist/index.d.ts
|
|
@@ -869,94 +870,51 @@ declare class OpenAPIGenerator {
|
|
|
869
870
|
}
|
|
870
871
|
declare const openApiGenerator: OpenAPIGenerator;
|
|
871
872
|
declare function _registerControllerClass(controllerClass: Function, prefix: string): void;
|
|
872
|
-
declare function
|
|
873
|
-
declare function
|
|
873
|
+
declare function setOpenApiConfig(config: OpenAPIConfig): void;
|
|
874
|
+
declare function generateOpenApiSpec(): OpenAPIV3.Document;
|
|
874
875
|
//#endregion
|
|
875
|
-
//#region src/annotation/
|
|
876
|
-
type
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
876
|
+
//#region src/annotation/validator.d.ts
|
|
877
|
+
type ValidatorSchema = {
|
|
878
|
+
requestBody?: StandardSchemaV1 & StandardJSONSchemaV1;
|
|
879
|
+
requestParam?: StandardSchemaV1 & StandardJSONSchemaV1;
|
|
880
|
+
requestQuery?: StandardSchemaV1 & StandardJSONSchemaV1;
|
|
881
|
+
responseBody?: StandardSchemaV1 & StandardJSONSchemaV1;
|
|
880
882
|
};
|
|
881
|
-
|
|
882
|
-
* Apply to a route method to have `request.body` be parsed by `schema`.
|
|
883
|
-
*
|
|
884
|
-
* This annotation will parse `request.body` & then override `request.body`.
|
|
885
|
-
* You can then just simply cast `request.body` for your use
|
|
886
|
-
*
|
|
887
|
-
* @example
|
|
888
|
-
* ```ts
|
|
889
|
-
* const CREATE_BOOK_REQUEST_BODY_SCHEMA = z.object({
|
|
890
|
-
* name: z.string(),
|
|
891
|
-
* description: z.string().optional(),
|
|
892
|
-
* });
|
|
893
|
-
*
|
|
894
|
-
* ⠀@Controller({ prefix: "/api/book" })
|
|
895
|
-
* class BookController {
|
|
896
|
-
* ⠀@RequestBody(CREATE_BOOK_REQUEST_BODY_SCHEMA)
|
|
897
|
-
* ⠀@POST()
|
|
898
|
-
* public createBook(request: e.Request) {
|
|
899
|
-
* const { name, description } = request.body as unknown as z.infer<
|
|
900
|
-
* typeof CREATE_BOOK_REQUEST_BODY_SCHEMA
|
|
901
|
-
* >;
|
|
902
|
-
* }
|
|
903
|
-
* }
|
|
904
|
-
* ```
|
|
905
|
-
*/
|
|
883
|
+
declare function ResponseBody(schema: StandardSchemaV1 & StandardJSONSchemaV1): MethodDecorator;
|
|
906
884
|
declare function RequestBody(schema: StandardSchemaV1 & StandardJSONSchemaV1): MethodDecorator;
|
|
907
|
-
/**
|
|
908
|
-
* Apply to a route method to have `request.param` be parsed by `schema`.
|
|
909
|
-
*
|
|
910
|
-
* This annotation will parse `request.param` & then override `request.param`.
|
|
911
|
-
* You can then just simply cast `request.param` for your use
|
|
912
|
-
*
|
|
913
|
-
* @example
|
|
914
|
-
* ```ts
|
|
915
|
-
* const GET_BOOK_REQUEST_PARAM_SCHEMA = z.object({
|
|
916
|
-
* bookId: z.string(),
|
|
917
|
-
* });
|
|
918
|
-
*
|
|
919
|
-
* ⠀@Controller({ prefix: "/api/book" })
|
|
920
|
-
* class BookController {
|
|
921
|
-
* ⠀@RequestParam(GET_BOOK_REQUEST_PARAM_SCHEMA)
|
|
922
|
-
* ⠀@GET("/:bookId")
|
|
923
|
-
* public getBook(request: e.Request) {
|
|
924
|
-
* const { bookId } = request.param as unknown as z.infer<
|
|
925
|
-
* typeof GET_BOOK_REQUEST_PARAM_SCHEMA
|
|
926
|
-
* >;
|
|
927
|
-
* }
|
|
928
|
-
* }
|
|
929
|
-
* ```
|
|
930
|
-
*/
|
|
931
885
|
declare function RequestParam(schema: StandardSchemaV1 & StandardJSONSchemaV1): MethodDecorator;
|
|
932
|
-
/**
|
|
933
|
-
* Apply to a route method to have `request.query` be parsed by `schema`.
|
|
934
|
-
*
|
|
935
|
-
* This annotation will parse `request.query` & then override `request.query`.
|
|
936
|
-
* You can then just simply cast `request.query` for your use
|
|
937
|
-
*
|
|
938
|
-
* @example
|
|
939
|
-
* ```ts
|
|
940
|
-
* const LIST_BOOKS_REQUEST_QUERY_SCHEMA = z.object({
|
|
941
|
-
* sort: z.enum(["name", "createdAt"]).optional(),
|
|
942
|
-
* q: z.string().optional(),
|
|
943
|
-
* });
|
|
944
|
-
*
|
|
945
|
-
* ⠀@Controller({ prefix: "/api/book" })
|
|
946
|
-
* class BookController {
|
|
947
|
-
* ⠀@RequestQuery(LIST_BOOKS_REQUEST_QUERY_SCHEMA)
|
|
948
|
-
* ⠀@GET()
|
|
949
|
-
* public listBooks(request: e.Request) {
|
|
950
|
-
* const { sort, q } = request.query as unknown as z.infer<
|
|
951
|
-
* typeof LIST_BOOKS_REQUEST_QUERY_SCHEMA
|
|
952
|
-
* >;
|
|
953
|
-
* }
|
|
954
|
-
* }
|
|
955
|
-
* ```
|
|
956
|
-
*/
|
|
957
886
|
declare function RequestQuery(schema: StandardSchemaV1 & StandardJSONSchemaV1): MethodDecorator;
|
|
958
|
-
declare function
|
|
887
|
+
declare function _getOrCreateSchemaDefinition(ctor: Function, fnName: string): ValidatorSchema;
|
|
959
888
|
declare function _parseOrThrow<TSchema extends StandardSchemaV1>(schema: TSchema, input: unknown, kind: ParserErrorLocation): Promise<StandardSchemaV1.InferOutput<TSchema>>;
|
|
889
|
+
declare function _getValidatorSchema(ctor: Function, fnName: string): ValidatorSchema | undefined;
|
|
890
|
+
declare function _setOnce(def: ValidatorSchema, key: keyof ValidatorSchema, schema: StandardSchemaV1 & StandardJSONSchemaV1, fnName: string): void;
|
|
891
|
+
//#endregion
|
|
892
|
+
//#region src/annotation/schema.d.ts
|
|
893
|
+
type ResponseSchema = {
|
|
894
|
+
statusCode: HttpStatus;
|
|
895
|
+
description?: string;
|
|
896
|
+
schema: StandardSchemaV1 & StandardJSONSchemaV1;
|
|
897
|
+
};
|
|
898
|
+
type RouteSchemaDefinition = {
|
|
899
|
+
description?: string;
|
|
900
|
+
responses?: ResponseSchema[];
|
|
901
|
+
};
|
|
902
|
+
type ControllerSchemaDefinition = {
|
|
903
|
+
title?: string;
|
|
904
|
+
description?: string;
|
|
905
|
+
};
|
|
906
|
+
declare function ControllerSchema(options: {
|
|
907
|
+
title?: string;
|
|
908
|
+
description?: string;
|
|
909
|
+
}): ClassDecorator;
|
|
910
|
+
declare function RouteSchema(options: {
|
|
911
|
+
description?: string;
|
|
912
|
+
responses?: ResponseSchema[];
|
|
913
|
+
}): MethodDecorator;
|
|
914
|
+
declare function _setRouteSchema(ctor: Function, fnName: string, options: RouteSchemaDefinition): void;
|
|
915
|
+
declare function _setControllerSchema(ctor: Function, options: ControllerSchemaDefinition): void;
|
|
916
|
+
declare function _getRouteSchema(ctor: Function, fnName: string): RouteSchemaDefinition | undefined;
|
|
917
|
+
declare function _getControllerSchema(ctor: Function): ControllerSchemaDefinition | undefined;
|
|
960
918
|
//#endregion
|
|
961
919
|
//#region src/middleware/default/error/base.d.ts
|
|
962
920
|
/**
|
|
@@ -992,7 +950,7 @@ declare class DefaultOpenApiMiddleware {
|
|
|
992
950
|
//#region src/middleware/default/swagger/index.d.ts
|
|
993
951
|
declare class Serve {
|
|
994
952
|
private readonly handlers;
|
|
995
|
-
handle(
|
|
953
|
+
handle(request: Request, response: Response$1, next: NextFunction): void;
|
|
996
954
|
}
|
|
997
955
|
declare class Setup {
|
|
998
956
|
private readonly handler;
|
|
@@ -1004,4 +962,4 @@ declare const DefaultSwaggerMiddleware: {
|
|
|
1004
962
|
Setup: typeof Setup;
|
|
1005
963
|
};
|
|
1006
964
|
//#endregion
|
|
1007
|
-
export { Class, Controller, DELETE, DefaultBaseErrorMiddleware, DefaultOpenApiMiddleware, DefaultParserErrorMiddleware, DefaultResponseStatusErrorMiddleware, DefaultSwaggerMiddleware, ExpressMiddlewareFn, ExpressRouterMethodKey, ExpressRouterMethods, GET, HEAD, Html404ErrorPage, HttpHeaders, HttpStatus, Injectable, Middleware, MiddlewareClass, OPTIONS, PATCH, POST, PUT, ParserError, ParserErrorLocation, RedirectView, RequestBody, RequestParam, RequestQuery, ResponseEntity, ResponseEntityBuilder, ResponseStatusError, RouteDefinition, Sapling, _ControllerRegistry, _InjectableDeps, _InjectableRegistry, _Route,
|
|
965
|
+
export { Class, Controller, ControllerSchema, ControllerSchemaDefinition, DELETE, DefaultBaseErrorMiddleware, DefaultOpenApiMiddleware, DefaultParserErrorMiddleware, DefaultResponseStatusErrorMiddleware, DefaultSwaggerMiddleware, ExpressMiddlewareFn, ExpressRouterMethodKey, ExpressRouterMethods, GET, HEAD, Html404ErrorPage, HttpHeaders, HttpStatus, Injectable, Middleware, MiddlewareClass, OPTIONS, PATCH, POST, PUT, ParserError, ParserErrorLocation, RedirectView, RequestBody, RequestParam, RequestQuery, ResponseBody, ResponseEntity, ResponseEntityBuilder, ResponseSchema, ResponseStatusError, RouteDefinition, RouteSchema, RouteSchemaDefinition, Sapling, ValidatorSchema, _ControllerRegistry, _InjectableDeps, _InjectableRegistry, _Route, _getControllerSchema, _getOrCreateSchemaDefinition, _getRouteSchema, _getRoutes, _getValidatorSchema, _parseOrThrow, _registerControllerClass, _resolve, _setControllerSchema, _setOnce, _setRouteSchema, _settings, generateOpenApiSpec, methodResolve, openApiGenerator, setOpenApiConfig };
|
package/dist/index.d.mts
CHANGED
|
@@ -437,7 +437,7 @@ declare class ResponseStatusError extends Error {
|
|
|
437
437
|
}
|
|
438
438
|
//#endregion
|
|
439
439
|
//#region src/helper/error/parse.d.ts
|
|
440
|
-
type ParserErrorLocation = "reqbody" | "reqparams" | "reqquery";
|
|
440
|
+
type ParserErrorLocation = "reqbody" | "reqparams" | "reqquery" | "resbody";
|
|
441
441
|
/**
|
|
442
442
|
* This error should be thrown when some data cannot be parsed by a given schema.
|
|
443
443
|
*/
|
|
@@ -531,6 +531,7 @@ declare class Sapling {
|
|
|
531
531
|
static setDeserializeFn(this: void, fn: (value: string) => any): void;
|
|
532
532
|
static setOpenApiPath(this: void, path: string): void;
|
|
533
533
|
static setSwaggerPath(this: void, path: string): void;
|
|
534
|
+
static chainHandlers(this: void, handlers: RequestHandler[], request: Request, response: Response$1, next: NextFunction, index?: number): void;
|
|
534
535
|
}
|
|
535
536
|
//#endregion
|
|
536
537
|
//#region node_modules/.pnpm/openapi-types@12.1.3/node_modules/openapi-types/dist/index.d.ts
|
|
@@ -869,94 +870,51 @@ declare class OpenAPIGenerator {
|
|
|
869
870
|
}
|
|
870
871
|
declare const openApiGenerator: OpenAPIGenerator;
|
|
871
872
|
declare function _registerControllerClass(controllerClass: Function, prefix: string): void;
|
|
872
|
-
declare function
|
|
873
|
-
declare function
|
|
873
|
+
declare function setOpenApiConfig(config: OpenAPIConfig): void;
|
|
874
|
+
declare function generateOpenApiSpec(): OpenAPIV3.Document;
|
|
874
875
|
//#endregion
|
|
875
|
-
//#region src/annotation/
|
|
876
|
-
type
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
876
|
+
//#region src/annotation/validator.d.ts
|
|
877
|
+
type ValidatorSchema = {
|
|
878
|
+
requestBody?: StandardSchemaV1 & StandardJSONSchemaV1;
|
|
879
|
+
requestParam?: StandardSchemaV1 & StandardJSONSchemaV1;
|
|
880
|
+
requestQuery?: StandardSchemaV1 & StandardJSONSchemaV1;
|
|
881
|
+
responseBody?: StandardSchemaV1 & StandardJSONSchemaV1;
|
|
880
882
|
};
|
|
881
|
-
|
|
882
|
-
* Apply to a route method to have `request.body` be parsed by `schema`.
|
|
883
|
-
*
|
|
884
|
-
* This annotation will parse `request.body` & then override `request.body`.
|
|
885
|
-
* You can then just simply cast `request.body` for your use
|
|
886
|
-
*
|
|
887
|
-
* @example
|
|
888
|
-
* ```ts
|
|
889
|
-
* const CREATE_BOOK_REQUEST_BODY_SCHEMA = z.object({
|
|
890
|
-
* name: z.string(),
|
|
891
|
-
* description: z.string().optional(),
|
|
892
|
-
* });
|
|
893
|
-
*
|
|
894
|
-
* ⠀@Controller({ prefix: "/api/book" })
|
|
895
|
-
* class BookController {
|
|
896
|
-
* ⠀@RequestBody(CREATE_BOOK_REQUEST_BODY_SCHEMA)
|
|
897
|
-
* ⠀@POST()
|
|
898
|
-
* public createBook(request: e.Request) {
|
|
899
|
-
* const { name, description } = request.body as unknown as z.infer<
|
|
900
|
-
* typeof CREATE_BOOK_REQUEST_BODY_SCHEMA
|
|
901
|
-
* >;
|
|
902
|
-
* }
|
|
903
|
-
* }
|
|
904
|
-
* ```
|
|
905
|
-
*/
|
|
883
|
+
declare function ResponseBody(schema: StandardSchemaV1 & StandardJSONSchemaV1): MethodDecorator;
|
|
906
884
|
declare function RequestBody(schema: StandardSchemaV1 & StandardJSONSchemaV1): MethodDecorator;
|
|
907
|
-
/**
|
|
908
|
-
* Apply to a route method to have `request.param` be parsed by `schema`.
|
|
909
|
-
*
|
|
910
|
-
* This annotation will parse `request.param` & then override `request.param`.
|
|
911
|
-
* You can then just simply cast `request.param` for your use
|
|
912
|
-
*
|
|
913
|
-
* @example
|
|
914
|
-
* ```ts
|
|
915
|
-
* const GET_BOOK_REQUEST_PARAM_SCHEMA = z.object({
|
|
916
|
-
* bookId: z.string(),
|
|
917
|
-
* });
|
|
918
|
-
*
|
|
919
|
-
* ⠀@Controller({ prefix: "/api/book" })
|
|
920
|
-
* class BookController {
|
|
921
|
-
* ⠀@RequestParam(GET_BOOK_REQUEST_PARAM_SCHEMA)
|
|
922
|
-
* ⠀@GET("/:bookId")
|
|
923
|
-
* public getBook(request: e.Request) {
|
|
924
|
-
* const { bookId } = request.param as unknown as z.infer<
|
|
925
|
-
* typeof GET_BOOK_REQUEST_PARAM_SCHEMA
|
|
926
|
-
* >;
|
|
927
|
-
* }
|
|
928
|
-
* }
|
|
929
|
-
* ```
|
|
930
|
-
*/
|
|
931
885
|
declare function RequestParam(schema: StandardSchemaV1 & StandardJSONSchemaV1): MethodDecorator;
|
|
932
|
-
/**
|
|
933
|
-
* Apply to a route method to have `request.query` be parsed by `schema`.
|
|
934
|
-
*
|
|
935
|
-
* This annotation will parse `request.query` & then override `request.query`.
|
|
936
|
-
* You can then just simply cast `request.query` for your use
|
|
937
|
-
*
|
|
938
|
-
* @example
|
|
939
|
-
* ```ts
|
|
940
|
-
* const LIST_BOOKS_REQUEST_QUERY_SCHEMA = z.object({
|
|
941
|
-
* sort: z.enum(["name", "createdAt"]).optional(),
|
|
942
|
-
* q: z.string().optional(),
|
|
943
|
-
* });
|
|
944
|
-
*
|
|
945
|
-
* ⠀@Controller({ prefix: "/api/book" })
|
|
946
|
-
* class BookController {
|
|
947
|
-
* ⠀@RequestQuery(LIST_BOOKS_REQUEST_QUERY_SCHEMA)
|
|
948
|
-
* ⠀@GET()
|
|
949
|
-
* public listBooks(request: e.Request) {
|
|
950
|
-
* const { sort, q } = request.query as unknown as z.infer<
|
|
951
|
-
* typeof LIST_BOOKS_REQUEST_QUERY_SCHEMA
|
|
952
|
-
* >;
|
|
953
|
-
* }
|
|
954
|
-
* }
|
|
955
|
-
* ```
|
|
956
|
-
*/
|
|
957
886
|
declare function RequestQuery(schema: StandardSchemaV1 & StandardJSONSchemaV1): MethodDecorator;
|
|
958
|
-
declare function
|
|
887
|
+
declare function _getOrCreateSchemaDefinition(ctor: Function, fnName: string): ValidatorSchema;
|
|
959
888
|
declare function _parseOrThrow<TSchema extends StandardSchemaV1>(schema: TSchema, input: unknown, kind: ParserErrorLocation): Promise<StandardSchemaV1.InferOutput<TSchema>>;
|
|
889
|
+
declare function _getValidatorSchema(ctor: Function, fnName: string): ValidatorSchema | undefined;
|
|
890
|
+
declare function _setOnce(def: ValidatorSchema, key: keyof ValidatorSchema, schema: StandardSchemaV1 & StandardJSONSchemaV1, fnName: string): void;
|
|
891
|
+
//#endregion
|
|
892
|
+
//#region src/annotation/schema.d.ts
|
|
893
|
+
type ResponseSchema = {
|
|
894
|
+
statusCode: HttpStatus;
|
|
895
|
+
description?: string;
|
|
896
|
+
schema: StandardSchemaV1 & StandardJSONSchemaV1;
|
|
897
|
+
};
|
|
898
|
+
type RouteSchemaDefinition = {
|
|
899
|
+
description?: string;
|
|
900
|
+
responses?: ResponseSchema[];
|
|
901
|
+
};
|
|
902
|
+
type ControllerSchemaDefinition = {
|
|
903
|
+
title?: string;
|
|
904
|
+
description?: string;
|
|
905
|
+
};
|
|
906
|
+
declare function ControllerSchema(options: {
|
|
907
|
+
title?: string;
|
|
908
|
+
description?: string;
|
|
909
|
+
}): ClassDecorator;
|
|
910
|
+
declare function RouteSchema(options: {
|
|
911
|
+
description?: string;
|
|
912
|
+
responses?: ResponseSchema[];
|
|
913
|
+
}): MethodDecorator;
|
|
914
|
+
declare function _setRouteSchema(ctor: Function, fnName: string, options: RouteSchemaDefinition): void;
|
|
915
|
+
declare function _setControllerSchema(ctor: Function, options: ControllerSchemaDefinition): void;
|
|
916
|
+
declare function _getRouteSchema(ctor: Function, fnName: string): RouteSchemaDefinition | undefined;
|
|
917
|
+
declare function _getControllerSchema(ctor: Function): ControllerSchemaDefinition | undefined;
|
|
960
918
|
//#endregion
|
|
961
919
|
//#region src/middleware/default/error/base.d.ts
|
|
962
920
|
/**
|
|
@@ -992,7 +950,7 @@ declare class DefaultOpenApiMiddleware {
|
|
|
992
950
|
//#region src/middleware/default/swagger/index.d.ts
|
|
993
951
|
declare class Serve {
|
|
994
952
|
private readonly handlers;
|
|
995
|
-
handle(
|
|
953
|
+
handle(request: Request, response: Response$1, next: NextFunction): void;
|
|
996
954
|
}
|
|
997
955
|
declare class Setup {
|
|
998
956
|
private readonly handler;
|
|
@@ -1004,4 +962,4 @@ declare const DefaultSwaggerMiddleware: {
|
|
|
1004
962
|
Setup: typeof Setup;
|
|
1005
963
|
};
|
|
1006
964
|
//#endregion
|
|
1007
|
-
export { Class, Controller, DELETE, DefaultBaseErrorMiddleware, DefaultOpenApiMiddleware, DefaultParserErrorMiddleware, DefaultResponseStatusErrorMiddleware, DefaultSwaggerMiddleware, ExpressMiddlewareFn, ExpressRouterMethodKey, ExpressRouterMethods, GET, HEAD, Html404ErrorPage, HttpHeaders, HttpStatus, Injectable, Middleware, MiddlewareClass, OPTIONS, PATCH, POST, PUT, ParserError, ParserErrorLocation, RedirectView, RequestBody, RequestParam, RequestQuery, ResponseEntity, ResponseEntityBuilder, ResponseStatusError, RouteDefinition, Sapling, _ControllerRegistry, _InjectableDeps, _InjectableRegistry, _Route,
|
|
965
|
+
export { Class, Controller, ControllerSchema, ControllerSchemaDefinition, DELETE, DefaultBaseErrorMiddleware, DefaultOpenApiMiddleware, DefaultParserErrorMiddleware, DefaultResponseStatusErrorMiddleware, DefaultSwaggerMiddleware, ExpressMiddlewareFn, ExpressRouterMethodKey, ExpressRouterMethods, GET, HEAD, Html404ErrorPage, HttpHeaders, HttpStatus, Injectable, Middleware, MiddlewareClass, OPTIONS, PATCH, POST, PUT, ParserError, ParserErrorLocation, RedirectView, RequestBody, RequestParam, RequestQuery, ResponseBody, ResponseEntity, ResponseEntityBuilder, ResponseSchema, ResponseStatusError, RouteDefinition, RouteSchema, RouteSchemaDefinition, Sapling, ValidatorSchema, _ControllerRegistry, _InjectableDeps, _InjectableRegistry, _Route, _getControllerSchema, _getOrCreateSchemaDefinition, _getRouteSchema, _getRoutes, _getValidatorSchema, _parseOrThrow, _registerControllerClass, _resolve, _setControllerSchema, _setOnce, _setRouteSchema, _settings, generateOpenApiSpec, methodResolve, openApiGenerator, setOpenApiConfig };
|
package/dist/index.mjs
CHANGED
|
@@ -233,6 +233,7 @@ var ParserError = class ParserError extends ResponseStatusError {
|
|
|
233
233
|
case "reqbody": return "request body";
|
|
234
234
|
case "reqparams": return "request params";
|
|
235
235
|
case "reqquery": return "request query";
|
|
236
|
+
case "resbody": return "response body";
|
|
236
237
|
}
|
|
237
238
|
})()}: ${formatted}`;
|
|
238
239
|
}
|
|
@@ -356,134 +357,20 @@ var Sapling = class Sapling {
|
|
|
356
357
|
static setSwaggerPath(path) {
|
|
357
358
|
_settings.doc.swaggerPath = path;
|
|
358
359
|
}
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
* const CREATE_BOOK_REQUEST_BODY_SCHEMA = z.object({
|
|
372
|
-
* name: z.string(),
|
|
373
|
-
* description: z.string().optional(),
|
|
374
|
-
* });
|
|
375
|
-
*
|
|
376
|
-
* ⠀@Controller({ prefix: "/api/book" })
|
|
377
|
-
* class BookController {
|
|
378
|
-
* ⠀@RequestBody(CREATE_BOOK_REQUEST_BODY_SCHEMA)
|
|
379
|
-
* ⠀@POST()
|
|
380
|
-
* public createBook(request: e.Request) {
|
|
381
|
-
* const { name, description } = request.body as unknown as z.infer<
|
|
382
|
-
* typeof CREATE_BOOK_REQUEST_BODY_SCHEMA
|
|
383
|
-
* >;
|
|
384
|
-
* }
|
|
385
|
-
* }
|
|
386
|
-
* ```
|
|
387
|
-
*/
|
|
388
|
-
function RequestBody(schema) {
|
|
389
|
-
return (target, propertyKey) => {
|
|
390
|
-
const ctor = target.constructor;
|
|
391
|
-
const fnName = String(propertyKey);
|
|
392
|
-
_setOnce(_getOrCreateRequestSchemaDefinition(ctor, fnName), "body", schema, fnName);
|
|
393
|
-
};
|
|
394
|
-
}
|
|
395
|
-
/**
|
|
396
|
-
* Apply to a route method to have `request.param` be parsed by `schema`.
|
|
397
|
-
*
|
|
398
|
-
* This annotation will parse `request.param` & then override `request.param`.
|
|
399
|
-
* You can then just simply cast `request.param` for your use
|
|
400
|
-
*
|
|
401
|
-
* @example
|
|
402
|
-
* ```ts
|
|
403
|
-
* const GET_BOOK_REQUEST_PARAM_SCHEMA = z.object({
|
|
404
|
-
* bookId: z.string(),
|
|
405
|
-
* });
|
|
406
|
-
*
|
|
407
|
-
* ⠀@Controller({ prefix: "/api/book" })
|
|
408
|
-
* class BookController {
|
|
409
|
-
* ⠀@RequestParam(GET_BOOK_REQUEST_PARAM_SCHEMA)
|
|
410
|
-
* ⠀@GET("/:bookId")
|
|
411
|
-
* public getBook(request: e.Request) {
|
|
412
|
-
* const { bookId } = request.param as unknown as z.infer<
|
|
413
|
-
* typeof GET_BOOK_REQUEST_PARAM_SCHEMA
|
|
414
|
-
* >;
|
|
415
|
-
* }
|
|
416
|
-
* }
|
|
417
|
-
* ```
|
|
418
|
-
*/
|
|
419
|
-
function RequestParam(schema) {
|
|
420
|
-
return (target, propertyKey) => {
|
|
421
|
-
const ctor = target.constructor;
|
|
422
|
-
const fnName = String(propertyKey);
|
|
423
|
-
_setOnce(_getOrCreateRequestSchemaDefinition(ctor, fnName), "param", schema, fnName);
|
|
424
|
-
};
|
|
425
|
-
}
|
|
426
|
-
/**
|
|
427
|
-
* Apply to a route method to have `request.query` be parsed by `schema`.
|
|
428
|
-
*
|
|
429
|
-
* This annotation will parse `request.query` & then override `request.query`.
|
|
430
|
-
* You can then just simply cast `request.query` for your use
|
|
431
|
-
*
|
|
432
|
-
* @example
|
|
433
|
-
* ```ts
|
|
434
|
-
* const LIST_BOOKS_REQUEST_QUERY_SCHEMA = z.object({
|
|
435
|
-
* sort: z.enum(["name", "createdAt"]).optional(),
|
|
436
|
-
* q: z.string().optional(),
|
|
437
|
-
* });
|
|
438
|
-
*
|
|
439
|
-
* ⠀@Controller({ prefix: "/api/book" })
|
|
440
|
-
* class BookController {
|
|
441
|
-
* ⠀@RequestQuery(LIST_BOOKS_REQUEST_QUERY_SCHEMA)
|
|
442
|
-
* ⠀@GET()
|
|
443
|
-
* public listBooks(request: e.Request) {
|
|
444
|
-
* const { sort, q } = request.query as unknown as z.infer<
|
|
445
|
-
* typeof LIST_BOOKS_REQUEST_QUERY_SCHEMA
|
|
446
|
-
* >;
|
|
447
|
-
* }
|
|
448
|
-
* }
|
|
449
|
-
* ```
|
|
450
|
-
*/
|
|
451
|
-
function RequestQuery(schema) {
|
|
452
|
-
return (target, propertyKey) => {
|
|
453
|
-
const ctor = target.constructor;
|
|
454
|
-
const fnName = String(propertyKey);
|
|
455
|
-
_setOnce(_getOrCreateRequestSchemaDefinition(ctor, fnName), "query", schema, fnName);
|
|
456
|
-
};
|
|
457
|
-
}
|
|
458
|
-
function _getOrCreateRequestSchemaDefinition(ctor, fnName) {
|
|
459
|
-
const byFn = (() => {
|
|
460
|
-
const fn = _requestSchemaStore.get(ctor);
|
|
461
|
-
if (fn) return fn;
|
|
462
|
-
const newFn = /* @__PURE__ */ new Map();
|
|
463
|
-
_requestSchemaStore.set(ctor, newFn);
|
|
464
|
-
return newFn;
|
|
465
|
-
})();
|
|
466
|
-
const existing = byFn.get(fnName);
|
|
467
|
-
if (existing) return existing;
|
|
468
|
-
const created = {};
|
|
469
|
-
byFn.set(fnName, created);
|
|
470
|
-
return created;
|
|
471
|
-
}
|
|
472
|
-
function _setOnce(def, key, schema, fnName) {
|
|
473
|
-
if (def[key]) throw new Error(`Duplicate request schema for "${String(key)}" on method "${fnName}"`);
|
|
474
|
-
def[key] = schema;
|
|
475
|
-
}
|
|
476
|
-
function _getRequestSchemas(ctor, fnName) {
|
|
477
|
-
return _requestSchemaStore.get(ctor)?.get(fnName);
|
|
478
|
-
}
|
|
479
|
-
async function _parseOrThrow(schema, input, kind) {
|
|
480
|
-
const result = await schema["~standard"].validate(input);
|
|
481
|
-
if (result.issues) {
|
|
482
|
-
console.debug(`Failed to parse a schema`);
|
|
483
|
-
throw new ParserError(kind, result.issues, schema["~standard"].vendor);
|
|
360
|
+
static chainHandlers(handlers, request, response, next, index = 0) {
|
|
361
|
+
if (index >= handlers.length) {
|
|
362
|
+
next();
|
|
363
|
+
return;
|
|
364
|
+
}
|
|
365
|
+
handlers[index]?.(request, response, (err) => {
|
|
366
|
+
if (err) {
|
|
367
|
+
next(err);
|
|
368
|
+
return;
|
|
369
|
+
}
|
|
370
|
+
Sapling.chainHandlers(handlers, request, response, next, index + 1);
|
|
371
|
+
});
|
|
484
372
|
}
|
|
485
|
-
|
|
486
|
-
}
|
|
373
|
+
};
|
|
487
374
|
//#endregion
|
|
488
375
|
//#region src/annotation/route.ts
|
|
489
376
|
const _routeStore = /* @__PURE__ */ new WeakMap();
|
|
@@ -566,6 +453,42 @@ function _getRoutes(ctor) {
|
|
|
566
453
|
return _routeStore.get(ctor) ?? [];
|
|
567
454
|
}
|
|
568
455
|
//#endregion
|
|
456
|
+
//#region src/utils.ts
|
|
457
|
+
function _getOrCreateMap(store, ctor) {
|
|
458
|
+
const existing = store.get(ctor);
|
|
459
|
+
if (existing) return existing;
|
|
460
|
+
const created = /* @__PURE__ */ new Map();
|
|
461
|
+
store.set(ctor, created);
|
|
462
|
+
return created;
|
|
463
|
+
}
|
|
464
|
+
//#endregion
|
|
465
|
+
//#region src/annotation/schema.ts
|
|
466
|
+
const _routeSchemaStore = /* @__PURE__ */ new WeakMap();
|
|
467
|
+
const _controllerSchemaStore = /* @__PURE__ */ new WeakMap();
|
|
468
|
+
function ControllerSchema(options) {
|
|
469
|
+
return (target) => {
|
|
470
|
+
_setControllerSchema(target, options);
|
|
471
|
+
};
|
|
472
|
+
}
|
|
473
|
+
function RouteSchema(options) {
|
|
474
|
+
return (target, propertyKey) => {
|
|
475
|
+
const ctor = target.constructor;
|
|
476
|
+
_setRouteSchema(ctor, String(propertyKey), options);
|
|
477
|
+
};
|
|
478
|
+
}
|
|
479
|
+
function _setRouteSchema(ctor, fnName, options) {
|
|
480
|
+
_getOrCreateMap(_routeSchemaStore, ctor).set(fnName, options);
|
|
481
|
+
}
|
|
482
|
+
function _setControllerSchema(ctor, options) {
|
|
483
|
+
_controllerSchemaStore.set(ctor, options);
|
|
484
|
+
}
|
|
485
|
+
function _getRouteSchema(ctor, fnName) {
|
|
486
|
+
return _routeSchemaStore.get(ctor)?.get(fnName);
|
|
487
|
+
}
|
|
488
|
+
function _getControllerSchema(ctor) {
|
|
489
|
+
return _controllerSchemaStore.get(ctor);
|
|
490
|
+
}
|
|
491
|
+
//#endregion
|
|
569
492
|
//#region src/helper/openapi.ts
|
|
570
493
|
var OpenAPIGenerator = class {
|
|
571
494
|
constructor() {
|
|
@@ -587,42 +510,78 @@ var OpenAPIGenerator = class {
|
|
|
587
510
|
generateSpec() {
|
|
588
511
|
const config = this.config;
|
|
589
512
|
const paths = {};
|
|
513
|
+
const tags = [];
|
|
590
514
|
for (const { class: controllerClass, prefix } of this.controllers) {
|
|
591
515
|
const routes = _getRoutes(controllerClass);
|
|
516
|
+
const controllerSchema = _getControllerSchema(controllerClass);
|
|
517
|
+
if (controllerSchema?.title) tags.push({
|
|
518
|
+
name: controllerSchema.title,
|
|
519
|
+
description: controllerSchema.description
|
|
520
|
+
});
|
|
592
521
|
for (const route of routes) {
|
|
593
522
|
if (route.method === "USE") continue;
|
|
594
|
-
const schemas =
|
|
595
|
-
const
|
|
596
|
-
|
|
523
|
+
const schemas = _getValidatorSchema(controllerClass, route.fnName);
|
|
524
|
+
const routeSchema = _getRouteSchema(controllerClass, route.fnName);
|
|
525
|
+
if (route.path instanceof RegExp) throw new Error(`You have a route with a regex path of ${route.path.source}. This is not compatible with OpenAPI.`);
|
|
526
|
+
const openApiPath = (prefix + route.path).replace(/:([A-Za-z0-9_]+)/g, "{$1}");
|
|
597
527
|
if (!paths[openApiPath]) paths[openApiPath] = {};
|
|
598
|
-
const
|
|
528
|
+
const responses = {};
|
|
529
|
+
if (schemas?.responseBody) {
|
|
530
|
+
const responseSchema = this.toJsonSchema(schemas.responseBody, "output");
|
|
531
|
+
responses["200"] = {
|
|
532
|
+
description: responseSchema.description ?? "Successful response",
|
|
533
|
+
content: { "application/json": { schema: responseSchema } }
|
|
534
|
+
};
|
|
535
|
+
} else responses["200"] = { description: "Successful response" };
|
|
536
|
+
if (routeSchema?.responses) for (const resp of routeSchema.responses) {
|
|
537
|
+
const responseSchema = this.toJsonSchema(resp.schema, "output");
|
|
538
|
+
responses[String(resp.statusCode)] = {
|
|
539
|
+
description: resp.description ?? responseSchema.description ?? `Response ${resp.statusCode}`,
|
|
540
|
+
content: { "application/json": { schema: responseSchema } }
|
|
541
|
+
};
|
|
542
|
+
}
|
|
543
|
+
const operation = {
|
|
544
|
+
responses,
|
|
545
|
+
description: routeSchema?.description,
|
|
546
|
+
tags: controllerSchema?.title ? [controllerSchema.title] : void 0
|
|
547
|
+
};
|
|
599
548
|
const parameters = [];
|
|
600
|
-
if (schemas?.
|
|
601
|
-
const paramSchema = this.toJsonSchema(schemas.
|
|
602
|
-
if (paramSchema.type === "object" && paramSchema.properties) for (const [name, schema] of Object.entries(paramSchema.properties))
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
549
|
+
if (schemas?.requestParam) {
|
|
550
|
+
const paramSchema = this.toJsonSchema(schemas.requestParam, "input");
|
|
551
|
+
if (paramSchema.type === "object" && paramSchema.properties) for (const [name, schema] of Object.entries(paramSchema.properties)) {
|
|
552
|
+
const parameterSchema = schema;
|
|
553
|
+
parameters.push({
|
|
554
|
+
name,
|
|
555
|
+
in: "path",
|
|
556
|
+
required: true,
|
|
557
|
+
description: parameterSchema.description,
|
|
558
|
+
schema: parameterSchema
|
|
559
|
+
});
|
|
560
|
+
}
|
|
608
561
|
}
|
|
609
|
-
if (schemas?.
|
|
610
|
-
const querySchema = this.toJsonSchema(schemas.
|
|
562
|
+
if (schemas?.requestQuery) {
|
|
563
|
+
const querySchema = this.toJsonSchema(schemas.requestQuery, "input");
|
|
611
564
|
if (querySchema.type === "object" && querySchema.properties) for (const [name, schema] of Object.entries(querySchema.properties)) {
|
|
612
565
|
const isRequired = Array.isArray(querySchema.required) && querySchema.required.includes(name);
|
|
566
|
+
const parameterSchema = schema;
|
|
613
567
|
parameters.push({
|
|
614
568
|
name,
|
|
615
569
|
in: "query",
|
|
616
570
|
required: isRequired,
|
|
617
|
-
|
|
571
|
+
description: parameterSchema.description,
|
|
572
|
+
schema: parameterSchema
|
|
618
573
|
});
|
|
619
574
|
}
|
|
620
575
|
}
|
|
621
576
|
if (parameters.length > 0) operation.parameters = parameters;
|
|
622
|
-
if (schemas?.
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
577
|
+
if (schemas?.requestBody) {
|
|
578
|
+
const requestSchema = this.toJsonSchema(schemas.requestBody, "input");
|
|
579
|
+
operation.requestBody = {
|
|
580
|
+
required: true,
|
|
581
|
+
description: requestSchema.description,
|
|
582
|
+
content: { "application/json": { schema: requestSchema } }
|
|
583
|
+
};
|
|
584
|
+
}
|
|
626
585
|
const method = route.method.toLowerCase();
|
|
627
586
|
paths[openApiPath][method] = operation;
|
|
628
587
|
}
|
|
@@ -634,12 +593,15 @@ var OpenAPIGenerator = class {
|
|
|
634
593
|
version: config.version,
|
|
635
594
|
description: config.description
|
|
636
595
|
},
|
|
596
|
+
tags: tags.length > 0 ? tags : void 0,
|
|
637
597
|
paths
|
|
638
598
|
};
|
|
639
599
|
}
|
|
640
|
-
toJsonSchema(schema) {
|
|
600
|
+
toJsonSchema(schema, direction = "output") {
|
|
641
601
|
try {
|
|
642
|
-
|
|
602
|
+
const jsonSchema = schema["~standard"].jsonSchema;
|
|
603
|
+
if (direction === "input" && jsonSchema.input) return jsonSchema.input({ target: "openapi-3.0" });
|
|
604
|
+
return jsonSchema.output({ target: "openapi-3.0" });
|
|
643
605
|
} catch (e) {
|
|
644
606
|
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`);
|
|
645
607
|
throw e;
|
|
@@ -650,10 +612,10 @@ const openApiGenerator = new OpenAPIGenerator();
|
|
|
650
612
|
function _registerControllerClass(controllerClass, prefix) {
|
|
651
613
|
openApiGenerator.registerController(controllerClass, prefix);
|
|
652
614
|
}
|
|
653
|
-
function
|
|
615
|
+
function setOpenApiConfig(config) {
|
|
654
616
|
openApiGenerator.setConfig(config);
|
|
655
617
|
}
|
|
656
|
-
function
|
|
618
|
+
function generateOpenApiSpec() {
|
|
657
619
|
return openApiGenerator.generateSpec();
|
|
658
620
|
}
|
|
659
621
|
//#endregion
|
|
@@ -789,6 +751,60 @@ function _resolve(ctor) {
|
|
|
789
751
|
return _InjectableRegistry.get(ctor);
|
|
790
752
|
}
|
|
791
753
|
//#endregion
|
|
754
|
+
//#region src/annotation/validator.ts
|
|
755
|
+
const _validatorSchemaStore = /* @__PURE__ */ new WeakMap();
|
|
756
|
+
function ResponseBody(schema) {
|
|
757
|
+
return (target, propertyKey) => {
|
|
758
|
+
const ctor = target.constructor;
|
|
759
|
+
const fnName = String(propertyKey);
|
|
760
|
+
_setOnce(_getOrCreateSchemaDefinition(ctor, fnName), "responseBody", schema, fnName);
|
|
761
|
+
};
|
|
762
|
+
}
|
|
763
|
+
function RequestBody(schema) {
|
|
764
|
+
return (target, propertyKey) => {
|
|
765
|
+
const ctor = target.constructor;
|
|
766
|
+
const fnName = String(propertyKey);
|
|
767
|
+
_setOnce(_getOrCreateSchemaDefinition(ctor, fnName), "requestBody", schema, fnName);
|
|
768
|
+
};
|
|
769
|
+
}
|
|
770
|
+
function RequestParam(schema) {
|
|
771
|
+
return (target, propertyKey) => {
|
|
772
|
+
const ctor = target.constructor;
|
|
773
|
+
const fnName = String(propertyKey);
|
|
774
|
+
_setOnce(_getOrCreateSchemaDefinition(ctor, fnName), "requestParam", schema, fnName);
|
|
775
|
+
};
|
|
776
|
+
}
|
|
777
|
+
function RequestQuery(schema) {
|
|
778
|
+
return (target, propertyKey) => {
|
|
779
|
+
const ctor = target.constructor;
|
|
780
|
+
const fnName = String(propertyKey);
|
|
781
|
+
_setOnce(_getOrCreateSchemaDefinition(ctor, fnName), "requestQuery", schema, fnName);
|
|
782
|
+
};
|
|
783
|
+
}
|
|
784
|
+
function _getOrCreateSchemaDefinition(ctor, fnName) {
|
|
785
|
+
const byFn = _getOrCreateMap(_validatorSchemaStore, ctor);
|
|
786
|
+
const existing = byFn.get(fnName);
|
|
787
|
+
if (existing) return existing;
|
|
788
|
+
const created = {};
|
|
789
|
+
byFn.set(fnName, created);
|
|
790
|
+
return created;
|
|
791
|
+
}
|
|
792
|
+
async function _parseOrThrow(schema, input, kind) {
|
|
793
|
+
const result = await schema["~standard"].validate(input);
|
|
794
|
+
if (result.issues) {
|
|
795
|
+
console.debug(`Failed to parse a schema`);
|
|
796
|
+
throw new ParserError(kind, result.issues, schema["~standard"].vendor);
|
|
797
|
+
}
|
|
798
|
+
return result.value;
|
|
799
|
+
}
|
|
800
|
+
function _getValidatorSchema(ctor, fnName) {
|
|
801
|
+
return _validatorSchemaStore.get(ctor)?.get(fnName);
|
|
802
|
+
}
|
|
803
|
+
function _setOnce(def, key, schema, fnName) {
|
|
804
|
+
if (def[key]) throw new Error(`Duplicate schema for "${String(key)}" on method "${fnName}"`);
|
|
805
|
+
def[key] = schema;
|
|
806
|
+
}
|
|
807
|
+
//#endregion
|
|
792
808
|
//#region src/annotation/controller.ts
|
|
793
809
|
const _ControllerRegistry = /* @__PURE__ */ new WeakMap();
|
|
794
810
|
/**
|
|
@@ -843,12 +859,12 @@ Split these into separate @MiddlewareClass classes, or merge the logic into a si
|
|
|
843
859
|
return;
|
|
844
860
|
}
|
|
845
861
|
router[methodName](fp, async (request, response, next) => {
|
|
846
|
-
const schemas =
|
|
862
|
+
const schemas = _getValidatorSchema(target, fnName);
|
|
847
863
|
if (schemas) {
|
|
848
|
-
if (schemas.
|
|
849
|
-
if (schemas.
|
|
850
|
-
if (schemas.
|
|
851
|
-
const parsedQuery = await _parseOrThrow(schemas.
|
|
864
|
+
if (schemas.requestBody) request.body = await _parseOrThrow(schemas.requestBody, request.body, "reqbody");
|
|
865
|
+
if (schemas.requestParam) request.params = await _parseOrThrow(schemas.requestParam, request.params, "reqparams");
|
|
866
|
+
if (schemas.requestQuery) {
|
|
867
|
+
const parsedQuery = await _parseOrThrow(schemas.requestQuery, request.query, "reqquery");
|
|
852
868
|
Object.defineProperty(request, "query", {
|
|
853
869
|
value: parsedQuery,
|
|
854
870
|
writable: true,
|
|
@@ -858,7 +874,8 @@ Split these into separate @MiddlewareClass classes, or merge the logic into a si
|
|
|
858
874
|
}
|
|
859
875
|
const result = await fn.bind(controllerInstance)(request, response, next);
|
|
860
876
|
if (result instanceof ResponseEntity) {
|
|
861
|
-
|
|
877
|
+
const body = schemas && schemas.responseBody ? await _parseOrThrow(schemas.responseBody, result.getBody(), "resbody") : result.getBody();
|
|
878
|
+
response.contentType("application/json").status(result.getStatusCode()).set(result.getHeaders()).send(Sapling.serialize(body));
|
|
862
879
|
return;
|
|
863
880
|
}
|
|
864
881
|
if (result instanceof RedirectView) {
|
|
@@ -925,7 +942,7 @@ DefaultResponseStatusErrorMiddleware = __decorate([MiddlewareClass()], DefaultRe
|
|
|
925
942
|
//#region src/middleware/default/openapi/index.ts
|
|
926
943
|
let DefaultOpenApiMiddleware = class DefaultOpenApiMiddleware {
|
|
927
944
|
handle(_request, _response, _next) {
|
|
928
|
-
return ResponseEntity.ok().body(
|
|
945
|
+
return ResponseEntity.ok().body(generateOpenApiSpec());
|
|
929
946
|
}
|
|
930
947
|
};
|
|
931
948
|
__decorate([GET(_settings.doc.openApiPath)], DefaultOpenApiMiddleware.prototype, "handle", null);
|
|
@@ -936,25 +953,25 @@ let Serve = class Serve {
|
|
|
936
953
|
constructor() {
|
|
937
954
|
this.handlers = swagger.serve;
|
|
938
955
|
}
|
|
939
|
-
handle(
|
|
940
|
-
return this.handlers;
|
|
956
|
+
handle(request, response, next) {
|
|
957
|
+
return Sapling.chainHandlers(this.handlers, request, response, next);
|
|
941
958
|
}
|
|
942
959
|
};
|
|
943
|
-
__decorate([Middleware()], Serve.prototype, "handle", null);
|
|
960
|
+
__decorate([Middleware(_settings.doc.swaggerPath)], Serve.prototype, "handle", null);
|
|
944
961
|
Serve = __decorate([MiddlewareClass()], Serve);
|
|
945
962
|
let Setup = class Setup {
|
|
946
963
|
constructor() {
|
|
947
|
-
this.handler = swagger.setup(
|
|
964
|
+
this.handler = swagger.setup(null, { swaggerOptions: { url: _settings.doc.openApiPath } });
|
|
948
965
|
}
|
|
949
966
|
handle(request, response, next) {
|
|
950
967
|
return this.handler(request, response, next);
|
|
951
968
|
}
|
|
952
969
|
};
|
|
953
|
-
__decorate([Middleware()], Setup.prototype, "handle", null);
|
|
970
|
+
__decorate([Middleware(_settings.doc.swaggerPath)], Setup.prototype, "handle", null);
|
|
954
971
|
Setup = __decorate([MiddlewareClass()], Setup);
|
|
955
972
|
const DefaultSwaggerMiddleware = {
|
|
956
973
|
Serve,
|
|
957
974
|
Setup
|
|
958
975
|
};
|
|
959
976
|
//#endregion
|
|
960
|
-
export { Controller, DELETE, DefaultBaseErrorMiddleware, DefaultOpenApiMiddleware, DefaultParserErrorMiddleware, DefaultResponseStatusErrorMiddleware, DefaultSwaggerMiddleware, GET, HEAD, Html404ErrorPage, HttpStatus, Injectable, Middleware, MiddlewareClass, OPTIONS, PATCH, POST, PUT, ParserError, RedirectView, RequestBody, RequestParam, RequestQuery, ResponseEntity, ResponseEntityBuilder, ResponseStatusError, Sapling, _ControllerRegistry, _InjectableDeps, _InjectableRegistry, _Route,
|
|
977
|
+
export { Controller, ControllerSchema, DELETE, DefaultBaseErrorMiddleware, DefaultOpenApiMiddleware, DefaultParserErrorMiddleware, DefaultResponseStatusErrorMiddleware, DefaultSwaggerMiddleware, GET, HEAD, Html404ErrorPage, HttpStatus, Injectable, Middleware, MiddlewareClass, OPTIONS, PATCH, POST, PUT, ParserError, RedirectView, RequestBody, RequestParam, RequestQuery, ResponseBody, ResponseEntity, ResponseEntityBuilder, ResponseStatusError, RouteSchema, Sapling, _ControllerRegistry, _InjectableDeps, _InjectableRegistry, _Route, _getControllerSchema, _getOrCreateSchemaDefinition, _getRouteSchema, _getRoutes, _getValidatorSchema, _parseOrThrow, _registerControllerClass, _resolve, _setControllerSchema, _setOnce, _setRouteSchema, _settings, generateOpenApiSpec, methodResolve, openApiGenerator, setOpenApiConfig };
|