@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.mjs
CHANGED
|
@@ -23,6 +23,7 @@ const Html404ErrorPage = (error) => `<!DOCTYPE html>
|
|
|
23
23
|
* You can either return `new RedirectView(url)` or `RedirectView.redirect(url)` inside of a controller method.
|
|
24
24
|
*/
|
|
25
25
|
var RedirectView = class RedirectView {
|
|
26
|
+
_url;
|
|
26
27
|
constructor(url) {
|
|
27
28
|
this._url = url;
|
|
28
29
|
}
|
|
@@ -118,8 +119,10 @@ let HttpStatus = /* @__PURE__ */ function(HttpStatus) {
|
|
|
118
119
|
* @typeParam T - the type of the response body
|
|
119
120
|
*/
|
|
120
121
|
var ResponseEntity = class {
|
|
122
|
+
_statusCode;
|
|
123
|
+
_headers = {};
|
|
124
|
+
_body;
|
|
121
125
|
constructor(body, headers = {}, statusCode = 200) {
|
|
122
|
-
this._headers = {};
|
|
123
126
|
this._body = body;
|
|
124
127
|
this._headers = headers;
|
|
125
128
|
this._statusCode = statusCode;
|
|
@@ -172,8 +175,9 @@ var ResponseEntity = class {
|
|
|
172
175
|
* ensuring type safety when constructing the response.
|
|
173
176
|
*/
|
|
174
177
|
var ResponseEntityBuilder = class {
|
|
178
|
+
_statusCode;
|
|
179
|
+
_headers = {};
|
|
175
180
|
constructor(statusCode) {
|
|
176
|
-
this._headers = {};
|
|
177
181
|
this._statusCode = statusCode;
|
|
178
182
|
}
|
|
179
183
|
/**
|
|
@@ -205,6 +209,7 @@ var ResponseEntityBuilder = class {
|
|
|
205
209
|
* @see {@link Sapling.loadResponseStatusErrorMiddleware}
|
|
206
210
|
*/
|
|
207
211
|
var ResponseStatusError = class ResponseStatusError extends Error {
|
|
212
|
+
status;
|
|
208
213
|
constructor(status, message) {
|
|
209
214
|
super(message ?? "Something went wrong.");
|
|
210
215
|
this.status = status;
|
|
@@ -216,26 +221,27 @@ var ResponseStatusError = class ResponseStatusError extends Error {
|
|
|
216
221
|
//#endregion
|
|
217
222
|
//#region src/helper/error/parse.ts
|
|
218
223
|
/**
|
|
219
|
-
* This error should be thrown when some data cannot be parsed by a given schema.
|
|
224
|
+
* This error should be thrown when some data cannot be parsed by a given Standard Schema compatible schema.
|
|
220
225
|
*/
|
|
221
226
|
var ParserError = class ParserError extends ResponseStatusError {
|
|
222
|
-
constructor(location, issues, vendor) {
|
|
223
|
-
super(400, ParserError.formatMessage(location, issues, vendor));
|
|
227
|
+
constructor(location, issues, vendor, functionName) {
|
|
228
|
+
super(400, ParserError.formatMessage(location, issues, vendor, functionName));
|
|
224
229
|
Object.setPrototypeOf(this, new.target.prototype);
|
|
225
230
|
}
|
|
226
|
-
static formatMessage(location, issues, vendor) {
|
|
231
|
+
static formatMessage(location, issues, vendor, functionName) {
|
|
227
232
|
const formatted = issues.map((i) => {
|
|
228
233
|
const path = Array.isArray(i.path) ? i.path.map((seg) => typeof seg === "object" && seg ? String(seg.key) : String(seg)).join(".") : "";
|
|
229
234
|
return path ? `${path}: ${i.message}` : i.message;
|
|
230
235
|
}).join("; ");
|
|
231
|
-
return
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
236
|
+
return `Failed to parse ${this.getPrettyLocationString(location)} with ${vendor} on ${functionName}: ${formatted}`;
|
|
237
|
+
}
|
|
238
|
+
static getPrettyLocationString(location) {
|
|
239
|
+
switch (location) {
|
|
240
|
+
case "reqbody": return "request body";
|
|
241
|
+
case "reqparams": return "request params";
|
|
242
|
+
case "reqquery": return "request query";
|
|
243
|
+
case "resbody": return "response body";
|
|
244
|
+
}
|
|
239
245
|
}
|
|
240
246
|
};
|
|
241
247
|
//#endregion
|
|
@@ -245,7 +251,11 @@ const _settings = {
|
|
|
245
251
|
deserialize: JSON.parse,
|
|
246
252
|
doc: {
|
|
247
253
|
openApiPath: "/openapi.json",
|
|
248
|
-
swaggerPath: "/swagger.html"
|
|
254
|
+
swaggerPath: "/swagger.html",
|
|
255
|
+
metadata: {
|
|
256
|
+
title: "API",
|
|
257
|
+
version: "1.0.0"
|
|
258
|
+
}
|
|
249
259
|
}
|
|
250
260
|
};
|
|
251
261
|
/**
|
|
@@ -351,12 +361,59 @@ var Sapling = class Sapling {
|
|
|
351
361
|
static setDeserializeFn(fn) {
|
|
352
362
|
_settings.deserialize = fn;
|
|
353
363
|
}
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
static
|
|
358
|
-
|
|
359
|
-
|
|
364
|
+
/**
|
|
365
|
+
* Modify extra settings
|
|
366
|
+
*/
|
|
367
|
+
static Extras = {
|
|
368
|
+
/**
|
|
369
|
+
* Modify default settings applied to OpenAPI & Swagger
|
|
370
|
+
*/
|
|
371
|
+
swaggerAndOpenApi: {
|
|
372
|
+
/**
|
|
373
|
+
* Set base OpenAPI metadata values.
|
|
374
|
+
*
|
|
375
|
+
* @default { title: "API", version: "1.0.0" }
|
|
376
|
+
*/
|
|
377
|
+
setMetadata(metadata) {
|
|
378
|
+
_settings.doc.metadata = metadata;
|
|
379
|
+
},
|
|
380
|
+
/**
|
|
381
|
+
* change default endpoint that will serve OpenAPI spec.
|
|
382
|
+
* Swagger will also load this endpoint on load.
|
|
383
|
+
*
|
|
384
|
+
* @default `/openapi.json`
|
|
385
|
+
*/
|
|
386
|
+
setOpenApiPath(path) {
|
|
387
|
+
_settings.doc.openApiPath = path;
|
|
388
|
+
},
|
|
389
|
+
/**
|
|
390
|
+
* change Swagger endpoint.
|
|
391
|
+
*
|
|
392
|
+
* @default `/swagger.html`
|
|
393
|
+
*/
|
|
394
|
+
setSwaggerPath(path) {
|
|
395
|
+
_settings.doc.swaggerPath = path;
|
|
396
|
+
}
|
|
397
|
+
} };
|
|
398
|
+
/**
|
|
399
|
+
* This method can be used in a `@MiddlewareClass` to register any libraries
|
|
400
|
+
* that expect you to register multiple registers at once. An example is `swagger-ui-express`
|
|
401
|
+
*
|
|
402
|
+
* @example
|
|
403
|
+
* ```ts
|
|
404
|
+
* ⠀@MiddlewareClass()
|
|
405
|
+
* class Serve {
|
|
406
|
+
* // `swagger.serve` returns multiple Express handlers for all the assets and routes
|
|
407
|
+
* // that will be served
|
|
408
|
+
* private readonly handlers: RequestHandler[] = swagger.serve;
|
|
409
|
+
*
|
|
410
|
+
* ⠀@Middleware(_settings.doc.swaggerPath)
|
|
411
|
+
* handle(request: Request, response: Response, next: NextFunction) {
|
|
412
|
+
* return Sapling.chainHandlers(this.handlers, request, response, next);
|
|
413
|
+
* }
|
|
414
|
+
* }
|
|
415
|
+
* ```
|
|
416
|
+
*/
|
|
360
417
|
static chainHandlers(handlers, request, response, next, index = 0) {
|
|
361
418
|
if (index >= handlers.length) {
|
|
362
419
|
next();
|
|
@@ -453,18 +510,7 @@ function _getRoutes(ctor) {
|
|
|
453
510
|
return _routeStore.get(ctor) ?? [];
|
|
454
511
|
}
|
|
455
512
|
//#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
513
|
//#region src/annotation/schema.ts
|
|
466
|
-
const _routeSchemaStore = /* @__PURE__ */ new WeakMap();
|
|
467
|
-
const _controllerSchemaStore = /* @__PURE__ */ new WeakMap();
|
|
468
514
|
function ControllerSchema(options) {
|
|
469
515
|
return (target) => {
|
|
470
516
|
_setControllerSchema(target, options);
|
|
@@ -476,8 +522,17 @@ function RouteSchema(options) {
|
|
|
476
522
|
_setRouteSchema(ctor, String(propertyKey), options);
|
|
477
523
|
};
|
|
478
524
|
}
|
|
525
|
+
const _routeSchemaStore = /* @__PURE__ */ new WeakMap();
|
|
526
|
+
const _controllerSchemaStore = /* @__PURE__ */ new WeakMap();
|
|
527
|
+
function getOrCreateRouteSchemaStore(store, ctor) {
|
|
528
|
+
const existing = store.get(ctor);
|
|
529
|
+
if (existing) return existing;
|
|
530
|
+
const created = /* @__PURE__ */ new Map();
|
|
531
|
+
store.set(ctor, created);
|
|
532
|
+
return created;
|
|
533
|
+
}
|
|
479
534
|
function _setRouteSchema(ctor, fnName, options) {
|
|
480
|
-
|
|
535
|
+
getOrCreateRouteSchemaStore(_routeSchemaStore, ctor).set(fnName, options);
|
|
481
536
|
}
|
|
482
537
|
function _setControllerSchema(ctor, options) {
|
|
483
538
|
_controllerSchemaStore.set(ctor, options);
|
|
@@ -489,26 +544,85 @@ function _getControllerSchema(ctor) {
|
|
|
489
544
|
return _controllerSchemaStore.get(ctor);
|
|
490
545
|
}
|
|
491
546
|
//#endregion
|
|
547
|
+
//#region src/annotation/validator.ts
|
|
548
|
+
const _validatorSchemaStore = /* @__PURE__ */ new WeakMap();
|
|
549
|
+
function ResponseBody(schema) {
|
|
550
|
+
return (target, propertyKey) => {
|
|
551
|
+
const ctor = target.constructor;
|
|
552
|
+
const fnName = String(propertyKey);
|
|
553
|
+
_saveValidatorSchema(_getOrCreateSchemaDefinition(ctor, fnName), "responseBody", schema, fnName);
|
|
554
|
+
};
|
|
555
|
+
}
|
|
556
|
+
function RequestBody(schema) {
|
|
557
|
+
return (target, propertyKey) => {
|
|
558
|
+
const ctor = target.constructor;
|
|
559
|
+
const fnName = String(propertyKey);
|
|
560
|
+
_saveValidatorSchema(_getOrCreateSchemaDefinition(ctor, fnName), "requestBody", schema, fnName);
|
|
561
|
+
};
|
|
562
|
+
}
|
|
563
|
+
function RequestParam(schema) {
|
|
564
|
+
return (target, propertyKey) => {
|
|
565
|
+
const ctor = target.constructor;
|
|
566
|
+
const fnName = String(propertyKey);
|
|
567
|
+
_saveValidatorSchema(_getOrCreateSchemaDefinition(ctor, fnName), "requestParam", schema, fnName);
|
|
568
|
+
};
|
|
569
|
+
}
|
|
570
|
+
function RequestQuery(schema) {
|
|
571
|
+
return (target, propertyKey) => {
|
|
572
|
+
const ctor = target.constructor;
|
|
573
|
+
const fnName = String(propertyKey);
|
|
574
|
+
_saveValidatorSchema(_getOrCreateSchemaDefinition(ctor, fnName), "requestQuery", schema, fnName);
|
|
575
|
+
};
|
|
576
|
+
}
|
|
577
|
+
function getOrCreateValidatorSchemaStore(store, ctor) {
|
|
578
|
+
const existing = store.get(ctor);
|
|
579
|
+
if (existing) return existing;
|
|
580
|
+
const created = /* @__PURE__ */ new Map();
|
|
581
|
+
store.set(ctor, created);
|
|
582
|
+
return created;
|
|
583
|
+
}
|
|
584
|
+
function _getOrCreateSchemaDefinition(ctor, fnName) {
|
|
585
|
+
const byFn = getOrCreateValidatorSchemaStore(_validatorSchemaStore, ctor);
|
|
586
|
+
const existing = byFn.get(fnName);
|
|
587
|
+
if (existing) return existing;
|
|
588
|
+
const created = {};
|
|
589
|
+
byFn.set(fnName, created);
|
|
590
|
+
return created;
|
|
591
|
+
}
|
|
592
|
+
async function _parseOrThrow(schema, input, location, fnName) {
|
|
593
|
+
const result = await schema["~standard"].validate(input);
|
|
594
|
+
if (result.issues) throw new ParserError(location, result.issues, schema["~standard"].vendor, fnName);
|
|
595
|
+
return result.value;
|
|
596
|
+
}
|
|
597
|
+
function _saveValidatorSchema(def, key, schema, fnName) {
|
|
598
|
+
if (def[key]) throw new Error(`Duplicate schema for "${String(key)}" on method "${fnName}"`);
|
|
599
|
+
def[key] = schema;
|
|
600
|
+
}
|
|
601
|
+
function _getValidatorSchema(ctor, fnName) {
|
|
602
|
+
return _validatorSchemaStore.get(ctor)?.get(fnName);
|
|
603
|
+
}
|
|
604
|
+
//#endregion
|
|
492
605
|
//#region src/helper/openapi.ts
|
|
493
606
|
var OpenAPIGenerator = class {
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
this.config = {
|
|
497
|
-
title: "API",
|
|
498
|
-
version: "1.0.0"
|
|
499
|
-
};
|
|
500
|
-
}
|
|
501
|
-
setConfig(config) {
|
|
502
|
-
this.config = config;
|
|
503
|
-
}
|
|
607
|
+
OPENAPI_VERSION = "3.0.0";
|
|
608
|
+
controllers = /* @__PURE__ */ new Set();
|
|
504
609
|
registerController(controllerClass, prefix) {
|
|
505
610
|
this.controllers.add({
|
|
506
611
|
class: controllerClass,
|
|
507
612
|
prefix
|
|
508
613
|
});
|
|
509
614
|
}
|
|
615
|
+
/**
|
|
616
|
+
* visible for testing
|
|
617
|
+
*/
|
|
618
|
+
_clearControllers() {
|
|
619
|
+
this.controllers.clear();
|
|
620
|
+
}
|
|
621
|
+
get metadata() {
|
|
622
|
+
return _settings.doc.metadata;
|
|
623
|
+
}
|
|
510
624
|
generateSpec() {
|
|
511
|
-
const
|
|
625
|
+
const metadata = this.metadata;
|
|
512
626
|
const paths = {};
|
|
513
627
|
const tags = [];
|
|
514
628
|
for (const { class: controllerClass, prefix } of this.controllers) {
|
|
@@ -592,11 +706,11 @@ var OpenAPIGenerator = class {
|
|
|
592
706
|
}
|
|
593
707
|
}
|
|
594
708
|
return {
|
|
595
|
-
openapi:
|
|
709
|
+
openapi: this.OPENAPI_VERSION,
|
|
596
710
|
info: {
|
|
597
|
-
title:
|
|
598
|
-
version:
|
|
599
|
-
description:
|
|
711
|
+
title: metadata.title,
|
|
712
|
+
version: metadata.version,
|
|
713
|
+
description: metadata.description
|
|
600
714
|
},
|
|
601
715
|
tags: tags.length > 0 ? tags : void 0,
|
|
602
716
|
paths
|
|
@@ -605,24 +719,23 @@ var OpenAPIGenerator = class {
|
|
|
605
719
|
toJsonSchema(schema, direction = "output") {
|
|
606
720
|
try {
|
|
607
721
|
const jsonSchema = schema["~standard"].jsonSchema;
|
|
608
|
-
|
|
609
|
-
return jsonSchema.output({ target: "openapi-3.0" });
|
|
722
|
+
return direction === "input" ? jsonSchema.input({ target: "openapi-3.0" }) : jsonSchema.output({ target: "openapi-3.0" });
|
|
610
723
|
} catch (e) {
|
|
611
|
-
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
|
|
724
|
+
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 });
|
|
612
725
|
throw e;
|
|
613
726
|
}
|
|
614
727
|
}
|
|
615
728
|
};
|
|
616
729
|
const openApiGenerator = new OpenAPIGenerator();
|
|
617
|
-
function
|
|
730
|
+
function _registerController(controllerClass, prefix) {
|
|
618
731
|
openApiGenerator.registerController(controllerClass, prefix);
|
|
619
732
|
}
|
|
620
|
-
function setOpenApiConfig(config) {
|
|
621
|
-
openApiGenerator.setConfig(config);
|
|
622
|
-
}
|
|
623
733
|
function generateOpenApiSpec() {
|
|
624
734
|
return openApiGenerator.generateSpec();
|
|
625
735
|
}
|
|
736
|
+
function _clearOpenApiRegistry() {
|
|
737
|
+
openApiGenerator._clearControllers();
|
|
738
|
+
}
|
|
626
739
|
//#endregion
|
|
627
740
|
//#region src/types.ts
|
|
628
741
|
const methodResolve = {
|
|
@@ -756,60 +869,6 @@ function _resolve(ctor) {
|
|
|
756
869
|
return _InjectableRegistry.get(ctor);
|
|
757
870
|
}
|
|
758
871
|
//#endregion
|
|
759
|
-
//#region src/annotation/validator.ts
|
|
760
|
-
const _validatorSchemaStore = /* @__PURE__ */ new WeakMap();
|
|
761
|
-
function ResponseBody(schema) {
|
|
762
|
-
return (target, propertyKey) => {
|
|
763
|
-
const ctor = target.constructor;
|
|
764
|
-
const fnName = String(propertyKey);
|
|
765
|
-
_setOnce(_getOrCreateSchemaDefinition(ctor, fnName), "responseBody", schema, fnName);
|
|
766
|
-
};
|
|
767
|
-
}
|
|
768
|
-
function RequestBody(schema) {
|
|
769
|
-
return (target, propertyKey) => {
|
|
770
|
-
const ctor = target.constructor;
|
|
771
|
-
const fnName = String(propertyKey);
|
|
772
|
-
_setOnce(_getOrCreateSchemaDefinition(ctor, fnName), "requestBody", schema, fnName);
|
|
773
|
-
};
|
|
774
|
-
}
|
|
775
|
-
function RequestParam(schema) {
|
|
776
|
-
return (target, propertyKey) => {
|
|
777
|
-
const ctor = target.constructor;
|
|
778
|
-
const fnName = String(propertyKey);
|
|
779
|
-
_setOnce(_getOrCreateSchemaDefinition(ctor, fnName), "requestParam", schema, fnName);
|
|
780
|
-
};
|
|
781
|
-
}
|
|
782
|
-
function RequestQuery(schema) {
|
|
783
|
-
return (target, propertyKey) => {
|
|
784
|
-
const ctor = target.constructor;
|
|
785
|
-
const fnName = String(propertyKey);
|
|
786
|
-
_setOnce(_getOrCreateSchemaDefinition(ctor, fnName), "requestQuery", schema, fnName);
|
|
787
|
-
};
|
|
788
|
-
}
|
|
789
|
-
function _getOrCreateSchemaDefinition(ctor, fnName) {
|
|
790
|
-
const byFn = _getOrCreateMap(_validatorSchemaStore, ctor);
|
|
791
|
-
const existing = byFn.get(fnName);
|
|
792
|
-
if (existing) return existing;
|
|
793
|
-
const created = {};
|
|
794
|
-
byFn.set(fnName, created);
|
|
795
|
-
return created;
|
|
796
|
-
}
|
|
797
|
-
async function _parseOrThrow(schema, input, kind) {
|
|
798
|
-
const result = await schema["~standard"].validate(input);
|
|
799
|
-
if (result.issues) {
|
|
800
|
-
console.debug(`Failed to parse ${schema["~standard"].vendor} schema\nissues: ${JSON.stringify(result.issues, null, 2)}`);
|
|
801
|
-
throw new ParserError(kind, result.issues, schema["~standard"].vendor);
|
|
802
|
-
}
|
|
803
|
-
return result.value;
|
|
804
|
-
}
|
|
805
|
-
function _getValidatorSchema(ctor, fnName) {
|
|
806
|
-
return _validatorSchemaStore.get(ctor)?.get(fnName);
|
|
807
|
-
}
|
|
808
|
-
function _setOnce(def, key, schema, fnName) {
|
|
809
|
-
if (def[key]) throw new Error(`Duplicate schema for "${String(key)}" on method "${fnName}"`);
|
|
810
|
-
def[key] = schema;
|
|
811
|
-
}
|
|
812
|
-
//#endregion
|
|
813
872
|
//#region src/annotation/controller.ts
|
|
814
873
|
const _ControllerRegistry = /* @__PURE__ */ new WeakMap();
|
|
815
874
|
/**
|
|
@@ -821,7 +880,7 @@ const _ControllerRegistry = /* @__PURE__ */ new WeakMap();
|
|
|
821
880
|
function Controller({ prefix = "", deps = [] } = {}) {
|
|
822
881
|
return (target) => {
|
|
823
882
|
const targetClass = target;
|
|
824
|
-
|
|
883
|
+
_registerController(target, prefix);
|
|
825
884
|
const router = Router();
|
|
826
885
|
const routes = _getRoutes(target);
|
|
827
886
|
const usedRoutes = /* @__PURE__ */ new Set();
|
|
@@ -846,15 +905,20 @@ Split these into separate @MiddlewareClass classes, or merge the logic into a si
|
|
|
846
905
|
if (method === "USE" && fn.length >= 4) {
|
|
847
906
|
const middlewareFn = async (err, request, response, next) => {
|
|
848
907
|
try {
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
}
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
908
|
+
await validate({
|
|
909
|
+
target,
|
|
910
|
+
fnName,
|
|
911
|
+
request
|
|
912
|
+
});
|
|
913
|
+
await handleResult({
|
|
914
|
+
result: fn.bind(controllerInstance)(err, request, response, next),
|
|
915
|
+
response,
|
|
916
|
+
target,
|
|
917
|
+
fnName,
|
|
918
|
+
method,
|
|
919
|
+
path: path instanceof RegExp ? path.source : fp,
|
|
920
|
+
isErrorMiddleware: true
|
|
921
|
+
});
|
|
858
922
|
} catch (e) {
|
|
859
923
|
console.error(e);
|
|
860
924
|
next(e);
|
|
@@ -864,35 +928,52 @@ Split these into separate @MiddlewareClass classes, or merge the logic into a si
|
|
|
864
928
|
return;
|
|
865
929
|
}
|
|
866
930
|
router[methodName](fp, async (request, response, next) => {
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
}
|
|
880
|
-
const result = await fn.bind(controllerInstance)(request, response, next);
|
|
881
|
-
if (result instanceof ResponseEntity) {
|
|
882
|
-
const body = schemas && schemas.responseBody ? await _parseOrThrow(schemas.responseBody, result.getBody(), "resbody") : result.getBody();
|
|
883
|
-
response.contentType("application/json").status(result.getStatusCode()).set(result.getHeaders()).send(Sapling.serialize(body));
|
|
884
|
-
return;
|
|
885
|
-
}
|
|
886
|
-
if (result instanceof RedirectView) {
|
|
887
|
-
response.redirect(result.getUrl());
|
|
888
|
-
return;
|
|
889
|
-
}
|
|
890
|
-
if (method !== "USE" && !response.writableEnded) response.status(404).send(Html404ErrorPage(`Cannot ${methodName.toUpperCase()} ${path instanceof RegExp ? path.source : fp}`));
|
|
931
|
+
await validate({
|
|
932
|
+
target,
|
|
933
|
+
fnName,
|
|
934
|
+
request
|
|
935
|
+
});
|
|
936
|
+
await handleResult({
|
|
937
|
+
result: await fn.bind(controllerInstance)(request, response, next),
|
|
938
|
+
response,
|
|
939
|
+
target,
|
|
940
|
+
fnName,
|
|
941
|
+
method,
|
|
942
|
+
path: path instanceof RegExp ? path.source : fp
|
|
943
|
+
});
|
|
891
944
|
});
|
|
892
945
|
}
|
|
893
946
|
_ControllerRegistry.set(targetClass, router);
|
|
894
947
|
};
|
|
895
948
|
}
|
|
949
|
+
async function handleResult({ result, target, fnName, response, method, path, isErrorMiddleware = false }) {
|
|
950
|
+
const schemas = _getValidatorSchema(target, fnName);
|
|
951
|
+
if (result instanceof ResponseEntity) {
|
|
952
|
+
const body = schemas && schemas.responseBody ? await _parseOrThrow(schemas.responseBody, result.getBody(), "resbody", fnName) : result.getBody();
|
|
953
|
+
response.contentType("application/json").status(result.getStatusCode()).set(result.getHeaders()).send(Sapling.serialize(body));
|
|
954
|
+
return;
|
|
955
|
+
}
|
|
956
|
+
if (result instanceof RedirectView) {
|
|
957
|
+
response.redirect(result.getUrl());
|
|
958
|
+
return;
|
|
959
|
+
}
|
|
960
|
+
if (!isErrorMiddleware && method !== "USE" && !response.writableEnded) response.status(404).send(Html404ErrorPage(`Cannot ${method} ${path}`));
|
|
961
|
+
}
|
|
962
|
+
async function validate({ target, fnName, request }) {
|
|
963
|
+
const schemas = _getValidatorSchema(target, fnName);
|
|
964
|
+
if (schemas) {
|
|
965
|
+
if (schemas.requestBody) request.body = await _parseOrThrow(schemas.requestBody, request.body, "reqbody", fnName);
|
|
966
|
+
if (schemas.requestParam) request.params = await _parseOrThrow(schemas.requestParam, request.params, "reqparams", fnName);
|
|
967
|
+
if (schemas.requestQuery) {
|
|
968
|
+
const parsedQuery = await _parseOrThrow(schemas.requestQuery, request.query, "reqquery", fnName);
|
|
969
|
+
Object.defineProperty(request, "query", {
|
|
970
|
+
value: parsedQuery,
|
|
971
|
+
writable: true,
|
|
972
|
+
configurable: true
|
|
973
|
+
});
|
|
974
|
+
}
|
|
975
|
+
}
|
|
976
|
+
}
|
|
896
977
|
//#endregion
|
|
897
978
|
//#region src/annotation/middleware.ts
|
|
898
979
|
/**
|
|
@@ -927,7 +1008,10 @@ DefaultBaseErrorMiddleware = __decorate([MiddlewareClass()], DefaultBaseErrorMid
|
|
|
927
1008
|
//#region src/middleware/default/error/parse.ts
|
|
928
1009
|
let DefaultParserErrorMiddleware = class DefaultParserErrorMiddleware {
|
|
929
1010
|
handle(err, _request, _response, next) {
|
|
930
|
-
if (err instanceof ParserError)
|
|
1011
|
+
if (err instanceof ParserError) {
|
|
1012
|
+
console.warn(err);
|
|
1013
|
+
return ResponseEntity.status(err.status).body({ message: err.message });
|
|
1014
|
+
}
|
|
931
1015
|
next(err);
|
|
932
1016
|
}
|
|
933
1017
|
};
|
|
@@ -954,17 +1038,48 @@ __decorate([GET(_settings.doc.openApiPath)], DefaultOpenApiMiddleware.prototype,
|
|
|
954
1038
|
DefaultOpenApiMiddleware = __decorate([MiddlewareClass()], DefaultOpenApiMiddleware);
|
|
955
1039
|
//#endregion
|
|
956
1040
|
//#region src/middleware/default/swagger/index.ts
|
|
1041
|
+
/**
|
|
1042
|
+
* Enable the serving of the Swagger endpoint used to serve the OpenAPI spec generated by Sapling.
|
|
1043
|
+
*
|
|
1044
|
+
* Configure any middleware-specific settings with `Sapling.Extras.swaggerAndOpenApi`
|
|
1045
|
+
*
|
|
1046
|
+
* You must register `DefaultSwaggerMiddleware.Serve` & `DefaultSwaggerMiddleware.Setup` after `DefaultOpenApiMiddleware`
|
|
1047
|
+
*
|
|
1048
|
+
* ```ts
|
|
1049
|
+
* const middlewares = [
|
|
1050
|
+
* DefaultOpenApiMiddleware,
|
|
1051
|
+
* DefaultSwaggerMiddleware.Serve,
|
|
1052
|
+
* DefaultSwaggerMiddleware.Setup,
|
|
1053
|
+
* ];
|
|
1054
|
+
* middlewares.map(Sapling.resolve).forEach((r) => app.use(r));
|
|
1055
|
+
* ```
|
|
1056
|
+
*/
|
|
957
1057
|
let Serve = class Serve {
|
|
958
|
-
|
|
959
|
-
this.handlers = swagger.serve;
|
|
960
|
-
}
|
|
1058
|
+
handlers = swagger.serve;
|
|
961
1059
|
handle(request, response, next) {
|
|
962
1060
|
return Sapling.chainHandlers(this.handlers, request, response, next);
|
|
963
1061
|
}
|
|
964
1062
|
};
|
|
965
1063
|
__decorate([Middleware(_settings.doc.swaggerPath)], Serve.prototype, "handle", null);
|
|
966
1064
|
Serve = __decorate([MiddlewareClass()], Serve);
|
|
1065
|
+
/**
|
|
1066
|
+
* Enable the serving of the Swagger endpoint used to serve the OpenAPI spec generated by Sapling.
|
|
1067
|
+
*
|
|
1068
|
+
* Configure any middleware-specific settings with `Sapling.Extras.swaggerAndOpenApi`
|
|
1069
|
+
*
|
|
1070
|
+
* You must register `DefaultSwaggerMiddleware.Serve` & `DefaultSwaggerMiddleware.Setup` after `DefaultOpenApiMiddleware`
|
|
1071
|
+
*
|
|
1072
|
+
* ```ts
|
|
1073
|
+
* const middlewares = [
|
|
1074
|
+
* DefaultOpenApiMiddleware,
|
|
1075
|
+
* DefaultSwaggerMiddleware.Serve,
|
|
1076
|
+
* DefaultSwaggerMiddleware.Setup,
|
|
1077
|
+
* ];
|
|
1078
|
+
* middlewares.map(Sapling.resolve).forEach((r) => app.use(r));
|
|
1079
|
+
* ```
|
|
1080
|
+
*/
|
|
967
1081
|
let Setup = class Setup {
|
|
1082
|
+
handler;
|
|
968
1083
|
constructor() {
|
|
969
1084
|
this.handler = swagger.setup(null, { swaggerOptions: { url: _settings.doc.openApiPath } });
|
|
970
1085
|
}
|
|
@@ -974,9 +1089,25 @@ let Setup = class Setup {
|
|
|
974
1089
|
};
|
|
975
1090
|
__decorate([Middleware(_settings.doc.swaggerPath)], Setup.prototype, "handle", null);
|
|
976
1091
|
Setup = __decorate([MiddlewareClass()], Setup);
|
|
1092
|
+
/**
|
|
1093
|
+
* Enable the serving of the Swagger endpoint used to serve the OpenAPI spec generated by Sapling.
|
|
1094
|
+
*
|
|
1095
|
+
* Configure any middleware-specific settings with `Sapling.Extras.swaggerAndOpenApi`
|
|
1096
|
+
*
|
|
1097
|
+
* You must register `DefaultSwaggerMiddleware.Serve` & `DefaultSwaggerMiddleware.Setup` after `DefaultOpenApiMiddleware`
|
|
1098
|
+
*
|
|
1099
|
+
* ```ts
|
|
1100
|
+
* const middlewares = [
|
|
1101
|
+
* DefaultOpenApiMiddleware,
|
|
1102
|
+
* DefaultSwaggerMiddleware.Serve,
|
|
1103
|
+
* DefaultSwaggerMiddleware.Setup,
|
|
1104
|
+
* ];
|
|
1105
|
+
* middlewares.map(Sapling.resolve).forEach((r) => app.use(r));
|
|
1106
|
+
* ```
|
|
1107
|
+
*/
|
|
977
1108
|
const DefaultSwaggerMiddleware = {
|
|
978
1109
|
Serve,
|
|
979
1110
|
Setup
|
|
980
1111
|
};
|
|
981
1112
|
//#endregion
|
|
982
|
-
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,
|
|
1113
|
+
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, _clearOpenApiRegistry, _getControllerSchema, _getOrCreateSchemaDefinition, _getRouteSchema, _getRoutes, _getValidatorSchema, _parseOrThrow, _registerController, _resolve, _saveValidatorSchema, _setControllerSchema, _setRouteSchema, _settings, generateOpenApiSpec, methodResolve, openApiGenerator };
|