@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.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,78 @@ 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
|
-
this.controllers = /* @__PURE__ */ new Set();
|
|
496
|
-
this.config = {
|
|
497
|
-
title: "API",
|
|
498
|
-
version: "1.0.0"
|
|
499
|
-
};
|
|
500
|
-
}
|
|
501
|
-
setConfig(config) {
|
|
502
|
-
this.config = config;
|
|
503
|
-
}
|
|
607
|
+
controllers = /* @__PURE__ */ new Set();
|
|
504
608
|
registerController(controllerClass, prefix) {
|
|
505
609
|
this.controllers.add({
|
|
506
610
|
class: controllerClass,
|
|
507
611
|
prefix
|
|
508
612
|
});
|
|
509
613
|
}
|
|
614
|
+
get metadata() {
|
|
615
|
+
return _settings.doc.metadata;
|
|
616
|
+
}
|
|
510
617
|
generateSpec() {
|
|
511
|
-
const
|
|
618
|
+
const metadata = this.metadata;
|
|
512
619
|
const paths = {};
|
|
513
620
|
const tags = [];
|
|
514
621
|
for (const { class: controllerClass, prefix } of this.controllers) {
|
|
@@ -534,14 +641,19 @@ var OpenAPIGenerator = class {
|
|
|
534
641
|
};
|
|
535
642
|
} else responses["200"] = { description: "Successful response" };
|
|
536
643
|
if (routeSchema?.responses) for (const resp of routeSchema.responses) {
|
|
537
|
-
const
|
|
538
|
-
responses[
|
|
539
|
-
|
|
540
|
-
|
|
644
|
+
const statusCode = String(resp.statusCode);
|
|
645
|
+
const existingResponse = responses[statusCode];
|
|
646
|
+
const existingSchema = existingResponse && "content" in existingResponse ? existingResponse.content?.["application/json"]?.schema : void 0;
|
|
647
|
+
const responseSchema = resp.schema ? this.toJsonSchema(resp.schema, "output") : statusCode === "200" ? existingSchema : void 0;
|
|
648
|
+
responses[statusCode] = {
|
|
649
|
+
...existingResponse,
|
|
650
|
+
description: resp.description ?? responseSchema?.description ?? existingResponse?.description ?? `Response ${resp.statusCode}`,
|
|
651
|
+
...responseSchema ? { content: { "application/json": { schema: responseSchema } } } : {}
|
|
541
652
|
};
|
|
542
653
|
}
|
|
543
654
|
const operation = {
|
|
544
655
|
responses,
|
|
656
|
+
summary: routeSchema?.summary,
|
|
545
657
|
description: routeSchema?.description,
|
|
546
658
|
tags: controllerSchema?.title ? [controllerSchema.title] : void 0
|
|
547
659
|
};
|
|
@@ -589,9 +701,9 @@ var OpenAPIGenerator = class {
|
|
|
589
701
|
return {
|
|
590
702
|
openapi: "3.0.0",
|
|
591
703
|
info: {
|
|
592
|
-
title:
|
|
593
|
-
version:
|
|
594
|
-
description:
|
|
704
|
+
title: metadata.title,
|
|
705
|
+
version: metadata.version,
|
|
706
|
+
description: metadata.description
|
|
595
707
|
},
|
|
596
708
|
tags: tags.length > 0 ? tags : void 0,
|
|
597
709
|
paths
|
|
@@ -600,21 +712,17 @@ var OpenAPIGenerator = class {
|
|
|
600
712
|
toJsonSchema(schema, direction = "output") {
|
|
601
713
|
try {
|
|
602
714
|
const jsonSchema = schema["~standard"].jsonSchema;
|
|
603
|
-
|
|
604
|
-
return jsonSchema.output({ target: "openapi-3.0" });
|
|
715
|
+
return direction === "input" ? jsonSchema.input({ target: "openapi-3.0" }) : jsonSchema.output({ target: "openapi-3.0" });
|
|
605
716
|
} catch (e) {
|
|
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
|
|
717
|
+
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 });
|
|
607
718
|
throw e;
|
|
608
719
|
}
|
|
609
720
|
}
|
|
610
721
|
};
|
|
611
722
|
const openApiGenerator = new OpenAPIGenerator();
|
|
612
|
-
function
|
|
723
|
+
function _registerController(controllerClass, prefix) {
|
|
613
724
|
openApiGenerator.registerController(controllerClass, prefix);
|
|
614
725
|
}
|
|
615
|
-
function setOpenApiConfig(config) {
|
|
616
|
-
openApiGenerator.setConfig(config);
|
|
617
|
-
}
|
|
618
726
|
function generateOpenApiSpec() {
|
|
619
727
|
return openApiGenerator.generateSpec();
|
|
620
728
|
}
|
|
@@ -751,60 +859,6 @@ function _resolve(ctor) {
|
|
|
751
859
|
return _InjectableRegistry.get(ctor);
|
|
752
860
|
}
|
|
753
861
|
//#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
|
|
808
862
|
//#region src/annotation/controller.ts
|
|
809
863
|
const _ControllerRegistry = /* @__PURE__ */ new WeakMap();
|
|
810
864
|
/**
|
|
@@ -816,7 +870,7 @@ const _ControllerRegistry = /* @__PURE__ */ new WeakMap();
|
|
|
816
870
|
function Controller({ prefix = "", deps = [] } = {}) {
|
|
817
871
|
return (target) => {
|
|
818
872
|
const targetClass = target;
|
|
819
|
-
|
|
873
|
+
_registerController(target, prefix);
|
|
820
874
|
const router = Router();
|
|
821
875
|
const routes = _getRoutes(target);
|
|
822
876
|
const usedRoutes = /* @__PURE__ */ new Set();
|
|
@@ -841,15 +895,20 @@ Split these into separate @MiddlewareClass classes, or merge the logic into a si
|
|
|
841
895
|
if (method === "USE" && fn.length >= 4) {
|
|
842
896
|
const middlewareFn = async (err, request, response, next) => {
|
|
843
897
|
try {
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
}
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
898
|
+
await validate({
|
|
899
|
+
target,
|
|
900
|
+
fnName,
|
|
901
|
+
request
|
|
902
|
+
});
|
|
903
|
+
await handleResult({
|
|
904
|
+
result: fn.bind(controllerInstance)(err, request, response, next),
|
|
905
|
+
response,
|
|
906
|
+
target,
|
|
907
|
+
fnName,
|
|
908
|
+
method,
|
|
909
|
+
path: path instanceof RegExp ? path.source : fp,
|
|
910
|
+
isErrorMiddleware: true
|
|
911
|
+
});
|
|
853
912
|
} catch (e) {
|
|
854
913
|
console.error(e);
|
|
855
914
|
next(e);
|
|
@@ -859,35 +918,52 @@ Split these into separate @MiddlewareClass classes, or merge the logic into a si
|
|
|
859
918
|
return;
|
|
860
919
|
}
|
|
861
920
|
router[methodName](fp, async (request, response, next) => {
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
}
|
|
875
|
-
const result = await fn.bind(controllerInstance)(request, response, next);
|
|
876
|
-
if (result instanceof ResponseEntity) {
|
|
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));
|
|
879
|
-
return;
|
|
880
|
-
}
|
|
881
|
-
if (result instanceof RedirectView) {
|
|
882
|
-
response.redirect(result.getUrl());
|
|
883
|
-
return;
|
|
884
|
-
}
|
|
885
|
-
if (method !== "USE" && !response.writableEnded) response.status(404).send(Html404ErrorPage(`Cannot ${methodName.toUpperCase()} ${path instanceof RegExp ? path.source : fp}`));
|
|
921
|
+
await validate({
|
|
922
|
+
target,
|
|
923
|
+
fnName,
|
|
924
|
+
request
|
|
925
|
+
});
|
|
926
|
+
await handleResult({
|
|
927
|
+
result: await fn.bind(controllerInstance)(request, response, next),
|
|
928
|
+
response,
|
|
929
|
+
target,
|
|
930
|
+
fnName,
|
|
931
|
+
method,
|
|
932
|
+
path: path instanceof RegExp ? path.source : fp
|
|
933
|
+
});
|
|
886
934
|
});
|
|
887
935
|
}
|
|
888
936
|
_ControllerRegistry.set(targetClass, router);
|
|
889
937
|
};
|
|
890
938
|
}
|
|
939
|
+
async function handleResult({ result, target, fnName, response, method, path, isErrorMiddleware = false }) {
|
|
940
|
+
const schemas = _getValidatorSchema(target, fnName);
|
|
941
|
+
if (result instanceof ResponseEntity) {
|
|
942
|
+
const body = schemas && schemas.responseBody ? await _parseOrThrow(schemas.responseBody, result.getBody(), "resbody", fnName) : result.getBody();
|
|
943
|
+
response.contentType("application/json").status(result.getStatusCode()).set(result.getHeaders()).send(Sapling.serialize(body));
|
|
944
|
+
return;
|
|
945
|
+
}
|
|
946
|
+
if (result instanceof RedirectView) {
|
|
947
|
+
response.redirect(result.getUrl());
|
|
948
|
+
return;
|
|
949
|
+
}
|
|
950
|
+
if (!isErrorMiddleware && method !== "USE" && !response.writableEnded) response.status(404).send(Html404ErrorPage(`Cannot ${method} ${path}`));
|
|
951
|
+
}
|
|
952
|
+
async function validate({ target, fnName, request }) {
|
|
953
|
+
const schemas = _getValidatorSchema(target, fnName);
|
|
954
|
+
if (schemas) {
|
|
955
|
+
if (schemas.requestBody) request.body = await _parseOrThrow(schemas.requestBody, request.body, "reqbody", fnName);
|
|
956
|
+
if (schemas.requestParam) request.params = await _parseOrThrow(schemas.requestParam, request.params, "reqparams", fnName);
|
|
957
|
+
if (schemas.requestQuery) {
|
|
958
|
+
const parsedQuery = await _parseOrThrow(schemas.requestQuery, request.query, "reqquery", fnName);
|
|
959
|
+
Object.defineProperty(request, "query", {
|
|
960
|
+
value: parsedQuery,
|
|
961
|
+
writable: true,
|
|
962
|
+
configurable: true
|
|
963
|
+
});
|
|
964
|
+
}
|
|
965
|
+
}
|
|
966
|
+
}
|
|
891
967
|
//#endregion
|
|
892
968
|
//#region src/annotation/middleware.ts
|
|
893
969
|
/**
|
|
@@ -922,7 +998,10 @@ DefaultBaseErrorMiddleware = __decorate([MiddlewareClass()], DefaultBaseErrorMid
|
|
|
922
998
|
//#region src/middleware/default/error/parse.ts
|
|
923
999
|
let DefaultParserErrorMiddleware = class DefaultParserErrorMiddleware {
|
|
924
1000
|
handle(err, _request, _response, next) {
|
|
925
|
-
if (err instanceof ParserError)
|
|
1001
|
+
if (err instanceof ParserError) {
|
|
1002
|
+
console.warn(err);
|
|
1003
|
+
return ResponseEntity.status(err.status).body({ message: err.message });
|
|
1004
|
+
}
|
|
926
1005
|
next(err);
|
|
927
1006
|
}
|
|
928
1007
|
};
|
|
@@ -949,17 +1028,48 @@ __decorate([GET(_settings.doc.openApiPath)], DefaultOpenApiMiddleware.prototype,
|
|
|
949
1028
|
DefaultOpenApiMiddleware = __decorate([MiddlewareClass()], DefaultOpenApiMiddleware);
|
|
950
1029
|
//#endregion
|
|
951
1030
|
//#region src/middleware/default/swagger/index.ts
|
|
1031
|
+
/**
|
|
1032
|
+
* Enable the serving of the Swagger endpoint used to serve the OpenAPI spec generated by Sapling.
|
|
1033
|
+
*
|
|
1034
|
+
* Configure any middleware-specific settings with `Sapling.Extras.swaggerAndOpenApi`
|
|
1035
|
+
*
|
|
1036
|
+
* You must register `DefaultSwaggerMiddleware.Serve` & `DefaultSwaggerMiddleware.Setup` after `DefaultOpenApiMiddleware`
|
|
1037
|
+
*
|
|
1038
|
+
* ```ts
|
|
1039
|
+
* const middlewares = [
|
|
1040
|
+
* DefaultOpenApiMiddleware,
|
|
1041
|
+
* DefaultSwaggerMiddleware.Serve,
|
|
1042
|
+
* DefaultSwaggerMiddleware.Setup,
|
|
1043
|
+
* ];
|
|
1044
|
+
* middlewares.map(Sapling.resolve).forEach((r) => app.use(r));
|
|
1045
|
+
* ```
|
|
1046
|
+
*/
|
|
952
1047
|
let Serve = class Serve {
|
|
953
|
-
|
|
954
|
-
this.handlers = swagger.serve;
|
|
955
|
-
}
|
|
1048
|
+
handlers = swagger.serve;
|
|
956
1049
|
handle(request, response, next) {
|
|
957
1050
|
return Sapling.chainHandlers(this.handlers, request, response, next);
|
|
958
1051
|
}
|
|
959
1052
|
};
|
|
960
1053
|
__decorate([Middleware(_settings.doc.swaggerPath)], Serve.prototype, "handle", null);
|
|
961
1054
|
Serve = __decorate([MiddlewareClass()], Serve);
|
|
1055
|
+
/**
|
|
1056
|
+
* Enable the serving of the Swagger endpoint used to serve the OpenAPI spec generated by Sapling.
|
|
1057
|
+
*
|
|
1058
|
+
* Configure any middleware-specific settings with `Sapling.Extras.swaggerAndOpenApi`
|
|
1059
|
+
*
|
|
1060
|
+
* You must register `DefaultSwaggerMiddleware.Serve` & `DefaultSwaggerMiddleware.Setup` after `DefaultOpenApiMiddleware`
|
|
1061
|
+
*
|
|
1062
|
+
* ```ts
|
|
1063
|
+
* const middlewares = [
|
|
1064
|
+
* DefaultOpenApiMiddleware,
|
|
1065
|
+
* DefaultSwaggerMiddleware.Serve,
|
|
1066
|
+
* DefaultSwaggerMiddleware.Setup,
|
|
1067
|
+
* ];
|
|
1068
|
+
* middlewares.map(Sapling.resolve).forEach((r) => app.use(r));
|
|
1069
|
+
* ```
|
|
1070
|
+
*/
|
|
962
1071
|
let Setup = class Setup {
|
|
1072
|
+
handler;
|
|
963
1073
|
constructor() {
|
|
964
1074
|
this.handler = swagger.setup(null, { swaggerOptions: { url: _settings.doc.openApiPath } });
|
|
965
1075
|
}
|
|
@@ -969,9 +1079,25 @@ let Setup = class Setup {
|
|
|
969
1079
|
};
|
|
970
1080
|
__decorate([Middleware(_settings.doc.swaggerPath)], Setup.prototype, "handle", null);
|
|
971
1081
|
Setup = __decorate([MiddlewareClass()], Setup);
|
|
1082
|
+
/**
|
|
1083
|
+
* Enable the serving of the Swagger endpoint used to serve the OpenAPI spec generated by Sapling.
|
|
1084
|
+
*
|
|
1085
|
+
* Configure any middleware-specific settings with `Sapling.Extras.swaggerAndOpenApi`
|
|
1086
|
+
*
|
|
1087
|
+
* You must register `DefaultSwaggerMiddleware.Serve` & `DefaultSwaggerMiddleware.Setup` after `DefaultOpenApiMiddleware`
|
|
1088
|
+
*
|
|
1089
|
+
* ```ts
|
|
1090
|
+
* const middlewares = [
|
|
1091
|
+
* DefaultOpenApiMiddleware,
|
|
1092
|
+
* DefaultSwaggerMiddleware.Serve,
|
|
1093
|
+
* DefaultSwaggerMiddleware.Setup,
|
|
1094
|
+
* ];
|
|
1095
|
+
* middlewares.map(Sapling.resolve).forEach((r) => app.use(r));
|
|
1096
|
+
* ```
|
|
1097
|
+
*/
|
|
972
1098
|
const DefaultSwaggerMiddleware = {
|
|
973
1099
|
Serve,
|
|
974
1100
|
Setup
|
|
975
1101
|
};
|
|
976
1102
|
//#endregion
|
|
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,
|
|
1103
|
+
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, _registerController, _resolve, _saveValidatorSchema, _setControllerSchema, _setRouteSchema, _settings, generateOpenApiSpec, methodResolve, openApiGenerator };
|