@tahminator/sapling 2.0.5 → 2.1.0
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 +591 -262
- package/dist/index.d.cts +589 -88
- package/dist/index.d.mts +589 -88
- package/dist/index.mjs +563 -262
- package/package.json +8 -2
package/dist/index.cjs
CHANGED
|
@@ -23,6 +23,8 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
23
23
|
//#endregion
|
|
24
24
|
let express = require("express");
|
|
25
25
|
express = __toESM(express);
|
|
26
|
+
let swagger_ui_express = require("swagger-ui-express");
|
|
27
|
+
swagger_ui_express = __toESM(swagger_ui_express);
|
|
26
28
|
//#region src/html/404.ts
|
|
27
29
|
/**
|
|
28
30
|
* Default Express.js 404 error page, as a string.
|
|
@@ -46,6 +48,7 @@ const Html404ErrorPage = (error) => `<!DOCTYPE html>
|
|
|
46
48
|
* You can either return `new RedirectView(url)` or `RedirectView.redirect(url)` inside of a controller method.
|
|
47
49
|
*/
|
|
48
50
|
var RedirectView = class RedirectView {
|
|
51
|
+
_url;
|
|
49
52
|
constructor(url) {
|
|
50
53
|
this._url = url;
|
|
51
54
|
}
|
|
@@ -141,8 +144,10 @@ let HttpStatus = /* @__PURE__ */ function(HttpStatus) {
|
|
|
141
144
|
* @typeParam T - the type of the response body
|
|
142
145
|
*/
|
|
143
146
|
var ResponseEntity = class {
|
|
147
|
+
_statusCode;
|
|
148
|
+
_headers = {};
|
|
149
|
+
_body;
|
|
144
150
|
constructor(body, headers = {}, statusCode = 200) {
|
|
145
|
-
this._headers = {};
|
|
146
151
|
this._body = body;
|
|
147
152
|
this._headers = headers;
|
|
148
153
|
this._statusCode = statusCode;
|
|
@@ -195,8 +200,9 @@ var ResponseEntity = class {
|
|
|
195
200
|
* ensuring type safety when constructing the response.
|
|
196
201
|
*/
|
|
197
202
|
var ResponseEntityBuilder = class {
|
|
203
|
+
_statusCode;
|
|
204
|
+
_headers = {};
|
|
198
205
|
constructor(statusCode) {
|
|
199
|
-
this._headers = {};
|
|
200
206
|
this._statusCode = statusCode;
|
|
201
207
|
}
|
|
202
208
|
/**
|
|
@@ -228,6 +234,7 @@ var ResponseEntityBuilder = class {
|
|
|
228
234
|
* @see {@link Sapling.loadResponseStatusErrorMiddleware}
|
|
229
235
|
*/
|
|
230
236
|
var ResponseStatusError = class ResponseStatusError extends Error {
|
|
237
|
+
status;
|
|
231
238
|
constructor(status, message) {
|
|
232
239
|
super(message ?? "Something went wrong.");
|
|
233
240
|
this.status = status;
|
|
@@ -239,32 +246,42 @@ var ResponseStatusError = class ResponseStatusError extends Error {
|
|
|
239
246
|
//#endregion
|
|
240
247
|
//#region src/helper/error/parse.ts
|
|
241
248
|
/**
|
|
242
|
-
* This error should be thrown when some data cannot be parsed by a given schema.
|
|
249
|
+
* This error should be thrown when some data cannot be parsed by a given Standard Schema compatible schema.
|
|
243
250
|
*/
|
|
244
251
|
var ParserError = class ParserError extends ResponseStatusError {
|
|
245
|
-
constructor(location, issues, vendor) {
|
|
246
|
-
super(400, ParserError.formatMessage(location, issues, vendor));
|
|
252
|
+
constructor(location, issues, vendor, functionName) {
|
|
253
|
+
super(400, ParserError.formatMessage(location, issues, vendor, functionName));
|
|
247
254
|
Object.setPrototypeOf(this, new.target.prototype);
|
|
248
255
|
}
|
|
249
|
-
static formatMessage(location, issues, vendor) {
|
|
256
|
+
static formatMessage(location, issues, vendor, functionName) {
|
|
250
257
|
const formatted = issues.map((i) => {
|
|
251
258
|
const path = Array.isArray(i.path) ? i.path.map((seg) => typeof seg === "object" && seg ? String(seg.key) : String(seg)).join(".") : "";
|
|
252
259
|
return path ? `${path}: ${i.message}` : i.message;
|
|
253
260
|
}).join("; ");
|
|
254
|
-
return
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
+
return `Failed to parse ${this.getPrettyLocationString(location)} with ${vendor} on ${functionName}: ${formatted}`;
|
|
262
|
+
}
|
|
263
|
+
static getPrettyLocationString(location) {
|
|
264
|
+
switch (location) {
|
|
265
|
+
case "reqbody": return "request body";
|
|
266
|
+
case "reqparams": return "request params";
|
|
267
|
+
case "reqquery": return "request query";
|
|
268
|
+
case "resbody": return "response body";
|
|
269
|
+
}
|
|
261
270
|
}
|
|
262
271
|
};
|
|
263
272
|
//#endregion
|
|
264
273
|
//#region src/helper/sapling.ts
|
|
265
|
-
const
|
|
274
|
+
const _settings = {
|
|
266
275
|
serialize: JSON.stringify,
|
|
267
|
-
deserialize: JSON.parse
|
|
276
|
+
deserialize: JSON.parse,
|
|
277
|
+
doc: {
|
|
278
|
+
openApiPath: "/openapi.json",
|
|
279
|
+
swaggerPath: "/swagger.html",
|
|
280
|
+
metadata: {
|
|
281
|
+
title: "API",
|
|
282
|
+
version: "1.0.0"
|
|
283
|
+
}
|
|
284
|
+
}
|
|
268
285
|
};
|
|
269
286
|
/**
|
|
270
287
|
* Collection of utility functions which are essential for Sapling to function.
|
|
@@ -343,13 +360,13 @@ var Sapling = class Sapling {
|
|
|
343
360
|
* @defaultValue `JSON.stringify`
|
|
344
361
|
*/
|
|
345
362
|
static serialize(value) {
|
|
346
|
-
return
|
|
363
|
+
return _settings.serialize(value);
|
|
347
364
|
}
|
|
348
365
|
/**
|
|
349
366
|
* Replace the function used for `serialize`.
|
|
350
367
|
*/
|
|
351
368
|
static setSerializeFn(fn) {
|
|
352
|
-
|
|
369
|
+
_settings.serialize = fn;
|
|
353
370
|
}
|
|
354
371
|
/**
|
|
355
372
|
* De-serialize a JSON string back to a JavaScript object.
|
|
@@ -361,15 +378,389 @@ var Sapling = class Sapling {
|
|
|
361
378
|
* @defaultValue `JSON.parse`
|
|
362
379
|
*/
|
|
363
380
|
static deserialize(value) {
|
|
364
|
-
return
|
|
381
|
+
return _settings.deserialize(value);
|
|
365
382
|
}
|
|
366
383
|
/**
|
|
367
384
|
* Replace the function used for `deserialize`
|
|
368
385
|
*/
|
|
369
386
|
static setDeserializeFn(fn) {
|
|
370
|
-
|
|
387
|
+
_settings.deserialize = fn;
|
|
388
|
+
}
|
|
389
|
+
/**
|
|
390
|
+
* Modify extra settings
|
|
391
|
+
*/
|
|
392
|
+
static Extras = {
|
|
393
|
+
/**
|
|
394
|
+
* Modify default settings applied to OpenAPI & Swagger
|
|
395
|
+
*/
|
|
396
|
+
swaggerAndOpenApi: {
|
|
397
|
+
/**
|
|
398
|
+
* Set base OpenAPI metadata values.
|
|
399
|
+
*
|
|
400
|
+
* @default { title: "API", version: "1.0.0" }
|
|
401
|
+
*/
|
|
402
|
+
setMetadata(metadata) {
|
|
403
|
+
_settings.doc.metadata = metadata;
|
|
404
|
+
},
|
|
405
|
+
/**
|
|
406
|
+
* change default endpoint that will serve OpenAPI spec.
|
|
407
|
+
* Swagger will also load this endpoint on load.
|
|
408
|
+
*
|
|
409
|
+
* @default `/openapi.json`
|
|
410
|
+
*/
|
|
411
|
+
setOpenApiPath(path) {
|
|
412
|
+
_settings.doc.openApiPath = path;
|
|
413
|
+
},
|
|
414
|
+
/**
|
|
415
|
+
* change Swagger endpoint.
|
|
416
|
+
*
|
|
417
|
+
* @default `/swagger.html`
|
|
418
|
+
*/
|
|
419
|
+
setSwaggerPath(path) {
|
|
420
|
+
_settings.doc.swaggerPath = path;
|
|
421
|
+
}
|
|
422
|
+
} };
|
|
423
|
+
/**
|
|
424
|
+
* This method can be used in a `@MiddlewareClass` to register any libraries
|
|
425
|
+
* that expect you to register multiple registers at once. An example is `swagger-ui-express`
|
|
426
|
+
*
|
|
427
|
+
* @example
|
|
428
|
+
* ```ts
|
|
429
|
+
* ⠀@MiddlewareClass()
|
|
430
|
+
* class Serve {
|
|
431
|
+
* // `swagger.serve` returns multiple Express handlers for all the assets and routes
|
|
432
|
+
* // that will be served
|
|
433
|
+
* private readonly handlers: RequestHandler[] = swagger.serve;
|
|
434
|
+
*
|
|
435
|
+
* ⠀@Middleware(_settings.doc.swaggerPath)
|
|
436
|
+
* handle(request: Request, response: Response, next: NextFunction) {
|
|
437
|
+
* return Sapling.chainHandlers(this.handlers, request, response, next);
|
|
438
|
+
* }
|
|
439
|
+
* }
|
|
440
|
+
* ```
|
|
441
|
+
*/
|
|
442
|
+
static chainHandlers(handlers, request, response, next, index = 0) {
|
|
443
|
+
if (index >= handlers.length) {
|
|
444
|
+
next();
|
|
445
|
+
return;
|
|
446
|
+
}
|
|
447
|
+
handlers[index]?.(request, response, (err) => {
|
|
448
|
+
if (err) {
|
|
449
|
+
next(err);
|
|
450
|
+
return;
|
|
451
|
+
}
|
|
452
|
+
Sapling.chainHandlers(handlers, request, response, next, index + 1);
|
|
453
|
+
});
|
|
454
|
+
}
|
|
455
|
+
};
|
|
456
|
+
//#endregion
|
|
457
|
+
//#region src/annotation/route.ts
|
|
458
|
+
const _routeStore = /* @__PURE__ */ new WeakMap();
|
|
459
|
+
/**
|
|
460
|
+
* Custom annotation that will store all routes inside of a map,
|
|
461
|
+
* which can then be used to initialize all the routes to the router.
|
|
462
|
+
*/
|
|
463
|
+
function _Route({ method, path = "" }) {
|
|
464
|
+
return (target, propertyKey) => {
|
|
465
|
+
const ctor = target.constructor;
|
|
466
|
+
const list = _routeStore.get(ctor) ?? [];
|
|
467
|
+
list.push({
|
|
468
|
+
method,
|
|
469
|
+
path: path ?? "",
|
|
470
|
+
fnName: String(propertyKey)
|
|
471
|
+
});
|
|
472
|
+
_routeStore.set(ctor, list);
|
|
473
|
+
};
|
|
474
|
+
}
|
|
475
|
+
/**
|
|
476
|
+
* Register GET route on the given path (default "") for the given controller.
|
|
477
|
+
*/
|
|
478
|
+
const GET = (path = "") => _Route({
|
|
479
|
+
method: "GET",
|
|
480
|
+
path
|
|
481
|
+
});
|
|
482
|
+
/**
|
|
483
|
+
* Register POST route on the given path (default "") for the given controller.
|
|
484
|
+
*/
|
|
485
|
+
const POST = (path = "") => _Route({
|
|
486
|
+
method: "POST",
|
|
487
|
+
path
|
|
488
|
+
});
|
|
489
|
+
/**
|
|
490
|
+
* Register PUT route on the given path (default "") for the given controller.
|
|
491
|
+
*/
|
|
492
|
+
const PUT = (path = "") => _Route({
|
|
493
|
+
method: "PUT",
|
|
494
|
+
path
|
|
495
|
+
});
|
|
496
|
+
/**
|
|
497
|
+
* Register DELETE route on the given path (default "") for the given controller.
|
|
498
|
+
*/
|
|
499
|
+
const DELETE = (path = "") => _Route({
|
|
500
|
+
method: "DELETE",
|
|
501
|
+
path
|
|
502
|
+
});
|
|
503
|
+
/**
|
|
504
|
+
* Register OPTIONS route on the given path (default "") for the given controller.
|
|
505
|
+
*/
|
|
506
|
+
const OPTIONS = (path = "") => _Route({
|
|
507
|
+
method: "OPTIONS",
|
|
508
|
+
path
|
|
509
|
+
});
|
|
510
|
+
/**
|
|
511
|
+
* Register PATCH route on the given path (default "") for the given controller.
|
|
512
|
+
*/
|
|
513
|
+
const PATCH = (path = "") => _Route({
|
|
514
|
+
method: "PATCH",
|
|
515
|
+
path
|
|
516
|
+
});
|
|
517
|
+
/**
|
|
518
|
+
* Register HEAD route on the given path (default "") for the given controller.
|
|
519
|
+
*/
|
|
520
|
+
const HEAD = (path = "") => _Route({
|
|
521
|
+
method: "HEAD",
|
|
522
|
+
path
|
|
523
|
+
});
|
|
524
|
+
/**
|
|
525
|
+
* Register a middleware route on the given path (default "") for the given controller.
|
|
526
|
+
*/
|
|
527
|
+
const Middleware = (path = "") => _Route({
|
|
528
|
+
method: "USE",
|
|
529
|
+
path
|
|
530
|
+
});
|
|
531
|
+
/**
|
|
532
|
+
* Given a class constructor, fetch all the routes attached.
|
|
533
|
+
*/
|
|
534
|
+
function _getRoutes(ctor) {
|
|
535
|
+
return _routeStore.get(ctor) ?? [];
|
|
536
|
+
}
|
|
537
|
+
//#endregion
|
|
538
|
+
//#region src/annotation/schema.ts
|
|
539
|
+
function ControllerSchema(options) {
|
|
540
|
+
return (target) => {
|
|
541
|
+
_setControllerSchema(target, options);
|
|
542
|
+
};
|
|
543
|
+
}
|
|
544
|
+
function RouteSchema(options) {
|
|
545
|
+
return (target, propertyKey) => {
|
|
546
|
+
const ctor = target.constructor;
|
|
547
|
+
_setRouteSchema(ctor, String(propertyKey), options);
|
|
548
|
+
};
|
|
549
|
+
}
|
|
550
|
+
const _routeSchemaStore = /* @__PURE__ */ new WeakMap();
|
|
551
|
+
const _controllerSchemaStore = /* @__PURE__ */ new WeakMap();
|
|
552
|
+
function getOrCreateRouteSchemaStore(store, ctor) {
|
|
553
|
+
const existing = store.get(ctor);
|
|
554
|
+
if (existing) return existing;
|
|
555
|
+
const created = /* @__PURE__ */ new Map();
|
|
556
|
+
store.set(ctor, created);
|
|
557
|
+
return created;
|
|
558
|
+
}
|
|
559
|
+
function _setRouteSchema(ctor, fnName, options) {
|
|
560
|
+
getOrCreateRouteSchemaStore(_routeSchemaStore, ctor).set(fnName, options);
|
|
561
|
+
}
|
|
562
|
+
function _setControllerSchema(ctor, options) {
|
|
563
|
+
_controllerSchemaStore.set(ctor, options);
|
|
564
|
+
}
|
|
565
|
+
function _getRouteSchema(ctor, fnName) {
|
|
566
|
+
return _routeSchemaStore.get(ctor)?.get(fnName);
|
|
567
|
+
}
|
|
568
|
+
function _getControllerSchema(ctor) {
|
|
569
|
+
return _controllerSchemaStore.get(ctor);
|
|
570
|
+
}
|
|
571
|
+
//#endregion
|
|
572
|
+
//#region src/annotation/validator.ts
|
|
573
|
+
const _validatorSchemaStore = /* @__PURE__ */ new WeakMap();
|
|
574
|
+
function ResponseBody(schema) {
|
|
575
|
+
return (target, propertyKey) => {
|
|
576
|
+
const ctor = target.constructor;
|
|
577
|
+
const fnName = String(propertyKey);
|
|
578
|
+
_saveValidatorSchema(_getOrCreateSchemaDefinition(ctor, fnName), "responseBody", schema, fnName);
|
|
579
|
+
};
|
|
580
|
+
}
|
|
581
|
+
function RequestBody(schema) {
|
|
582
|
+
return (target, propertyKey) => {
|
|
583
|
+
const ctor = target.constructor;
|
|
584
|
+
const fnName = String(propertyKey);
|
|
585
|
+
_saveValidatorSchema(_getOrCreateSchemaDefinition(ctor, fnName), "requestBody", schema, fnName);
|
|
586
|
+
};
|
|
587
|
+
}
|
|
588
|
+
function RequestParam(schema) {
|
|
589
|
+
return (target, propertyKey) => {
|
|
590
|
+
const ctor = target.constructor;
|
|
591
|
+
const fnName = String(propertyKey);
|
|
592
|
+
_saveValidatorSchema(_getOrCreateSchemaDefinition(ctor, fnName), "requestParam", schema, fnName);
|
|
593
|
+
};
|
|
594
|
+
}
|
|
595
|
+
function RequestQuery(schema) {
|
|
596
|
+
return (target, propertyKey) => {
|
|
597
|
+
const ctor = target.constructor;
|
|
598
|
+
const fnName = String(propertyKey);
|
|
599
|
+
_saveValidatorSchema(_getOrCreateSchemaDefinition(ctor, fnName), "requestQuery", schema, fnName);
|
|
600
|
+
};
|
|
601
|
+
}
|
|
602
|
+
function getOrCreateValidatorSchemaStore(store, ctor) {
|
|
603
|
+
const existing = store.get(ctor);
|
|
604
|
+
if (existing) return existing;
|
|
605
|
+
const created = /* @__PURE__ */ new Map();
|
|
606
|
+
store.set(ctor, created);
|
|
607
|
+
return created;
|
|
608
|
+
}
|
|
609
|
+
function _getOrCreateSchemaDefinition(ctor, fnName) {
|
|
610
|
+
const byFn = getOrCreateValidatorSchemaStore(_validatorSchemaStore, ctor);
|
|
611
|
+
const existing = byFn.get(fnName);
|
|
612
|
+
if (existing) return existing;
|
|
613
|
+
const created = {};
|
|
614
|
+
byFn.set(fnName, created);
|
|
615
|
+
return created;
|
|
616
|
+
}
|
|
617
|
+
async function _parseOrThrow(schema, input, location, fnName) {
|
|
618
|
+
const result = await schema["~standard"].validate(input);
|
|
619
|
+
if (result.issues) throw new ParserError(location, result.issues, schema["~standard"].vendor, fnName);
|
|
620
|
+
return result.value;
|
|
621
|
+
}
|
|
622
|
+
function _saveValidatorSchema(def, key, schema, fnName) {
|
|
623
|
+
if (def[key]) throw new Error(`Duplicate schema for "${String(key)}" on method "${fnName}"`);
|
|
624
|
+
def[key] = schema;
|
|
625
|
+
}
|
|
626
|
+
function _getValidatorSchema(ctor, fnName) {
|
|
627
|
+
return _validatorSchemaStore.get(ctor)?.get(fnName);
|
|
628
|
+
}
|
|
629
|
+
//#endregion
|
|
630
|
+
//#region src/helper/openapi.ts
|
|
631
|
+
var OpenAPIGenerator = class {
|
|
632
|
+
OPENAPI_VERSION = "3.0.0";
|
|
633
|
+
controllers = /* @__PURE__ */ new Set();
|
|
634
|
+
registerController(controllerClass, prefix) {
|
|
635
|
+
this.controllers.add({
|
|
636
|
+
class: controllerClass,
|
|
637
|
+
prefix
|
|
638
|
+
});
|
|
639
|
+
}
|
|
640
|
+
/**
|
|
641
|
+
* visible for testing
|
|
642
|
+
*/
|
|
643
|
+
_clearControllers() {
|
|
644
|
+
this.controllers.clear();
|
|
645
|
+
}
|
|
646
|
+
get metadata() {
|
|
647
|
+
return _settings.doc.metadata;
|
|
648
|
+
}
|
|
649
|
+
generateSpec() {
|
|
650
|
+
const metadata = this.metadata;
|
|
651
|
+
const paths = {};
|
|
652
|
+
const tags = [];
|
|
653
|
+
for (const { class: controllerClass, prefix } of this.controllers) {
|
|
654
|
+
const routes = _getRoutes(controllerClass);
|
|
655
|
+
const controllerSchema = _getControllerSchema(controllerClass);
|
|
656
|
+
if (controllerSchema?.title) tags.push({
|
|
657
|
+
name: controllerSchema.title,
|
|
658
|
+
description: controllerSchema.description
|
|
659
|
+
});
|
|
660
|
+
for (const route of routes) {
|
|
661
|
+
if (route.method === "USE") continue;
|
|
662
|
+
const schemas = _getValidatorSchema(controllerClass, route.fnName);
|
|
663
|
+
const routeSchema = _getRouteSchema(controllerClass, route.fnName);
|
|
664
|
+
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.`);
|
|
665
|
+
const openApiPath = (prefix + route.path).replace(/:([A-Za-z0-9_]+)/g, "{$1}");
|
|
666
|
+
if (!paths[openApiPath]) paths[openApiPath] = {};
|
|
667
|
+
const responses = {};
|
|
668
|
+
if (schemas?.responseBody) {
|
|
669
|
+
const responseSchema = this.toJsonSchema(schemas.responseBody, "output");
|
|
670
|
+
responses["200"] = {
|
|
671
|
+
description: responseSchema.description ?? "Successful response",
|
|
672
|
+
content: { "application/json": { schema: responseSchema } }
|
|
673
|
+
};
|
|
674
|
+
} else responses["200"] = { description: "Successful response" };
|
|
675
|
+
if (routeSchema?.responses) for (const resp of routeSchema.responses) {
|
|
676
|
+
const statusCode = String(resp.statusCode);
|
|
677
|
+
const existingResponse = responses[statusCode];
|
|
678
|
+
const existingSchema = existingResponse && "content" in existingResponse ? existingResponse.content?.["application/json"]?.schema : void 0;
|
|
679
|
+
const responseSchema = resp.schema ? this.toJsonSchema(resp.schema, "output") : statusCode === "200" ? existingSchema : void 0;
|
|
680
|
+
responses[statusCode] = {
|
|
681
|
+
...existingResponse,
|
|
682
|
+
description: resp.description ?? responseSchema?.description ?? existingResponse?.description ?? `Response ${resp.statusCode}`,
|
|
683
|
+
...responseSchema ? { content: { "application/json": { schema: responseSchema } } } : {}
|
|
684
|
+
};
|
|
685
|
+
}
|
|
686
|
+
const operation = {
|
|
687
|
+
responses,
|
|
688
|
+
summary: routeSchema?.summary,
|
|
689
|
+
description: routeSchema?.description,
|
|
690
|
+
tags: controllerSchema?.title ? [controllerSchema.title] : void 0
|
|
691
|
+
};
|
|
692
|
+
const parameters = [];
|
|
693
|
+
if (schemas?.requestParam) {
|
|
694
|
+
const paramSchema = this.toJsonSchema(schemas.requestParam, "input");
|
|
695
|
+
if (paramSchema.type === "object" && paramSchema.properties) for (const [name, schema] of Object.entries(paramSchema.properties)) {
|
|
696
|
+
const parameterSchema = schema;
|
|
697
|
+
parameters.push({
|
|
698
|
+
name,
|
|
699
|
+
in: "path",
|
|
700
|
+
required: true,
|
|
701
|
+
description: parameterSchema.description,
|
|
702
|
+
schema: parameterSchema
|
|
703
|
+
});
|
|
704
|
+
}
|
|
705
|
+
}
|
|
706
|
+
if (schemas?.requestQuery) {
|
|
707
|
+
const querySchema = this.toJsonSchema(schemas.requestQuery, "input");
|
|
708
|
+
if (querySchema.type === "object" && querySchema.properties) for (const [name, schema] of Object.entries(querySchema.properties)) {
|
|
709
|
+
const isRequired = Array.isArray(querySchema.required) && querySchema.required.includes(name);
|
|
710
|
+
const parameterSchema = schema;
|
|
711
|
+
parameters.push({
|
|
712
|
+
name,
|
|
713
|
+
in: "query",
|
|
714
|
+
required: isRequired,
|
|
715
|
+
description: parameterSchema.description,
|
|
716
|
+
schema: parameterSchema
|
|
717
|
+
});
|
|
718
|
+
}
|
|
719
|
+
}
|
|
720
|
+
if (parameters.length > 0) operation.parameters = parameters;
|
|
721
|
+
if (schemas?.requestBody) {
|
|
722
|
+
const requestSchema = this.toJsonSchema(schemas.requestBody, "input");
|
|
723
|
+
operation.requestBody = {
|
|
724
|
+
required: true,
|
|
725
|
+
description: requestSchema.description,
|
|
726
|
+
content: { "application/json": { schema: requestSchema } }
|
|
727
|
+
};
|
|
728
|
+
}
|
|
729
|
+
const method = route.method.toLowerCase();
|
|
730
|
+
paths[openApiPath][method] = operation;
|
|
731
|
+
}
|
|
732
|
+
}
|
|
733
|
+
return {
|
|
734
|
+
openapi: this.OPENAPI_VERSION,
|
|
735
|
+
info: {
|
|
736
|
+
title: metadata.title,
|
|
737
|
+
version: metadata.version,
|
|
738
|
+
description: metadata.description
|
|
739
|
+
},
|
|
740
|
+
tags: tags.length > 0 ? tags : void 0,
|
|
741
|
+
paths
|
|
742
|
+
};
|
|
743
|
+
}
|
|
744
|
+
toJsonSchema(schema, direction = "output") {
|
|
745
|
+
try {
|
|
746
|
+
const jsonSchema = schema["~standard"].jsonSchema;
|
|
747
|
+
return direction === "input" ? jsonSchema.input({ target: "openapi-3.0" }) : jsonSchema.output({ target: "openapi-3.0" });
|
|
748
|
+
} catch (e) {
|
|
749
|
+
if (e instanceof Error && e.message.includes("Transforms cannot be represented in JSON Schema")) throw new Error(`${e.message}.\nIt appears that you are using z.transform() - it is highly recommended that you use z.codec instead - https://zod.dev/codecs`, { cause: e });
|
|
750
|
+
throw e;
|
|
751
|
+
}
|
|
371
752
|
}
|
|
372
753
|
};
|
|
754
|
+
const openApiGenerator = new OpenAPIGenerator();
|
|
755
|
+
function _registerController(controllerClass, prefix) {
|
|
756
|
+
openApiGenerator.registerController(controllerClass, prefix);
|
|
757
|
+
}
|
|
758
|
+
function generateOpenApiSpec() {
|
|
759
|
+
return openApiGenerator.generateSpec();
|
|
760
|
+
}
|
|
761
|
+
function _clearOpenApiRegistry() {
|
|
762
|
+
openApiGenerator._clearControllers();
|
|
763
|
+
}
|
|
373
764
|
//#endregion
|
|
374
765
|
//#region src/types.ts
|
|
375
766
|
const methodResolve = {
|
|
@@ -503,214 +894,6 @@ function _resolve(ctor) {
|
|
|
503
894
|
return _InjectableRegistry.get(ctor);
|
|
504
895
|
}
|
|
505
896
|
//#endregion
|
|
506
|
-
//#region src/annotation/request.ts
|
|
507
|
-
const _requestSchemaStore = /* @__PURE__ */ new WeakMap();
|
|
508
|
-
/**
|
|
509
|
-
* Apply to a route method to have `request.body` be parsed by `schema`.
|
|
510
|
-
*
|
|
511
|
-
* This annotation will parse `request.body` & then override `request.body`.
|
|
512
|
-
* You can then just simply cast `request.body` for your use
|
|
513
|
-
*
|
|
514
|
-
* @example
|
|
515
|
-
* ```ts
|
|
516
|
-
* const CREATE_BOOK_REQUEST_BODY_SCHEMA = z.object({
|
|
517
|
-
* name: z.string(),
|
|
518
|
-
* description: z.string().optional(),
|
|
519
|
-
* });
|
|
520
|
-
*
|
|
521
|
-
* ⠀@Controller({ prefix: "/api/book" })
|
|
522
|
-
* class BookController {
|
|
523
|
-
* ⠀@RequestBody(CREATE_BOOK_REQUEST_BODY_SCHEMA)
|
|
524
|
-
* ⠀@POST()
|
|
525
|
-
* public createBook(request: e.Request) {
|
|
526
|
-
* const { name, description } = request.body as unknown as z.infer<
|
|
527
|
-
* typeof CREATE_BOOK_REQUEST_BODY_SCHEMA
|
|
528
|
-
* >;
|
|
529
|
-
* }
|
|
530
|
-
* }
|
|
531
|
-
* ```
|
|
532
|
-
*/
|
|
533
|
-
function RequestBody(schema) {
|
|
534
|
-
return (target, propertyKey) => {
|
|
535
|
-
const ctor = target.constructor;
|
|
536
|
-
const fnName = String(propertyKey);
|
|
537
|
-
_setOnce(_getOrCreateRequestSchemaDefinition(ctor, fnName), "body", schema, fnName);
|
|
538
|
-
};
|
|
539
|
-
}
|
|
540
|
-
/**
|
|
541
|
-
* Apply to a route method to have `request.param` be parsed by `schema`.
|
|
542
|
-
*
|
|
543
|
-
* This annotation will parse `request.param` & then override `request.param`.
|
|
544
|
-
* You can then just simply cast `request.param` for your use
|
|
545
|
-
*
|
|
546
|
-
* @example
|
|
547
|
-
* ```ts
|
|
548
|
-
* const GET_BOOK_REQUEST_PARAM_SCHEMA = z.object({
|
|
549
|
-
* bookId: z.string(),
|
|
550
|
-
* });
|
|
551
|
-
*
|
|
552
|
-
* ⠀@Controller({ prefix: "/api/book" })
|
|
553
|
-
* class BookController {
|
|
554
|
-
* ⠀@RequestParam(GET_BOOK_REQUEST_PARAM_SCHEMA)
|
|
555
|
-
* ⠀@GET("/:bookId")
|
|
556
|
-
* public getBook(request: e.Request) {
|
|
557
|
-
* const { bookId } = request.param as unknown as z.infer<
|
|
558
|
-
* typeof GET_BOOK_REQUEST_PARAM_SCHEMA
|
|
559
|
-
* >;
|
|
560
|
-
* }
|
|
561
|
-
* }
|
|
562
|
-
* ```
|
|
563
|
-
*/
|
|
564
|
-
function RequestParam(schema) {
|
|
565
|
-
return (target, propertyKey) => {
|
|
566
|
-
const ctor = target.constructor;
|
|
567
|
-
const fnName = String(propertyKey);
|
|
568
|
-
_setOnce(_getOrCreateRequestSchemaDefinition(ctor, fnName), "param", schema, fnName);
|
|
569
|
-
};
|
|
570
|
-
}
|
|
571
|
-
/**
|
|
572
|
-
* Apply to a route method to have `request.query` be parsed by `schema`.
|
|
573
|
-
*
|
|
574
|
-
* This annotation will parse `request.query` & then override `request.query`.
|
|
575
|
-
* You can then just simply cast `request.query` for your use
|
|
576
|
-
*
|
|
577
|
-
* @example
|
|
578
|
-
* ```ts
|
|
579
|
-
* const LIST_BOOKS_REQUEST_QUERY_SCHEMA = z.object({
|
|
580
|
-
* sort: z.enum(["name", "createdAt"]).optional(),
|
|
581
|
-
* q: z.string().optional(),
|
|
582
|
-
* });
|
|
583
|
-
*
|
|
584
|
-
* ⠀@Controller({ prefix: "/api/book" })
|
|
585
|
-
* class BookController {
|
|
586
|
-
* ⠀@RequestQuery(LIST_BOOKS_REQUEST_QUERY_SCHEMA)
|
|
587
|
-
* ⠀@GET()
|
|
588
|
-
* public listBooks(request: e.Request) {
|
|
589
|
-
* const { sort, q } = request.query as unknown as z.infer<
|
|
590
|
-
* typeof LIST_BOOKS_REQUEST_QUERY_SCHEMA
|
|
591
|
-
* >;
|
|
592
|
-
* }
|
|
593
|
-
* }
|
|
594
|
-
* ```
|
|
595
|
-
*/
|
|
596
|
-
function RequestQuery(schema) {
|
|
597
|
-
return (target, propertyKey) => {
|
|
598
|
-
const ctor = target.constructor;
|
|
599
|
-
const fnName = String(propertyKey);
|
|
600
|
-
_setOnce(_getOrCreateRequestSchemaDefinition(ctor, fnName), "query", schema, fnName);
|
|
601
|
-
};
|
|
602
|
-
}
|
|
603
|
-
function _getOrCreateRequestSchemaDefinition(ctor, fnName) {
|
|
604
|
-
const byFn = (() => {
|
|
605
|
-
const fn = _requestSchemaStore.get(ctor);
|
|
606
|
-
if (fn) return fn;
|
|
607
|
-
const newFn = /* @__PURE__ */ new Map();
|
|
608
|
-
_requestSchemaStore.set(ctor, newFn);
|
|
609
|
-
return newFn;
|
|
610
|
-
})();
|
|
611
|
-
const existing = byFn.get(fnName);
|
|
612
|
-
if (existing) return existing;
|
|
613
|
-
const created = {};
|
|
614
|
-
byFn.set(fnName, created);
|
|
615
|
-
return created;
|
|
616
|
-
}
|
|
617
|
-
function _setOnce(def, key, schema, fnName) {
|
|
618
|
-
if (def[key]) throw new Error(`Duplicate request schema for "${String(key)}" on method "${fnName}"`);
|
|
619
|
-
def[key] = schema;
|
|
620
|
-
}
|
|
621
|
-
function _getRequestSchemas(ctor, fnName) {
|
|
622
|
-
return _requestSchemaStore.get(ctor)?.get(fnName);
|
|
623
|
-
}
|
|
624
|
-
async function _parseOrThrow(schema, input, kind) {
|
|
625
|
-
const result = await schema["~standard"].validate(input);
|
|
626
|
-
if (result.issues) {
|
|
627
|
-
console.debug(`Failed to parse a schema`);
|
|
628
|
-
throw new ParserError(kind, result.issues, schema["~standard"].vendor);
|
|
629
|
-
}
|
|
630
|
-
return result.value;
|
|
631
|
-
}
|
|
632
|
-
//#endregion
|
|
633
|
-
//#region src/annotation/route.ts
|
|
634
|
-
const _routeStore = /* @__PURE__ */ new WeakMap();
|
|
635
|
-
/**
|
|
636
|
-
* Custom annotation that will store all routes inside of a map,
|
|
637
|
-
* which can then be used to initialize all the routes to the router.
|
|
638
|
-
*/
|
|
639
|
-
function _Route({ method, path = "" }) {
|
|
640
|
-
return (target, propertyKey) => {
|
|
641
|
-
const ctor = target.constructor;
|
|
642
|
-
const list = _routeStore.get(ctor) ?? [];
|
|
643
|
-
list.push({
|
|
644
|
-
method,
|
|
645
|
-
path: path ?? "",
|
|
646
|
-
fnName: String(propertyKey)
|
|
647
|
-
});
|
|
648
|
-
_routeStore.set(ctor, list);
|
|
649
|
-
};
|
|
650
|
-
}
|
|
651
|
-
/**
|
|
652
|
-
* Register GET route on the given path (default "") for the given controller.
|
|
653
|
-
*/
|
|
654
|
-
const GET = (path = "") => _Route({
|
|
655
|
-
method: "GET",
|
|
656
|
-
path
|
|
657
|
-
});
|
|
658
|
-
/**
|
|
659
|
-
* Register POST route on the given path (default "") for the given controller.
|
|
660
|
-
*/
|
|
661
|
-
const POST = (path = "") => _Route({
|
|
662
|
-
method: "POST",
|
|
663
|
-
path
|
|
664
|
-
});
|
|
665
|
-
/**
|
|
666
|
-
* Register PUT route on the given path (default "") for the given controller.
|
|
667
|
-
*/
|
|
668
|
-
const PUT = (path = "") => _Route({
|
|
669
|
-
method: "PUT",
|
|
670
|
-
path
|
|
671
|
-
});
|
|
672
|
-
/**
|
|
673
|
-
* Register DELETE route on the given path (default "") for the given controller.
|
|
674
|
-
*/
|
|
675
|
-
const DELETE = (path = "") => _Route({
|
|
676
|
-
method: "DELETE",
|
|
677
|
-
path
|
|
678
|
-
});
|
|
679
|
-
/**
|
|
680
|
-
* Register OPTIONS route on the given path (default "") for the given controller.
|
|
681
|
-
*/
|
|
682
|
-
const OPTIONS = (path = "") => _Route({
|
|
683
|
-
method: "OPTIONS",
|
|
684
|
-
path
|
|
685
|
-
});
|
|
686
|
-
/**
|
|
687
|
-
* Register PATCH route on the given path (default "") for the given controller.
|
|
688
|
-
*/
|
|
689
|
-
const PATCH = (path = "") => _Route({
|
|
690
|
-
method: "PATCH",
|
|
691
|
-
path
|
|
692
|
-
});
|
|
693
|
-
/**
|
|
694
|
-
* Register HEAD route on the given path (default "") for the given controller.
|
|
695
|
-
*/
|
|
696
|
-
const HEAD = (path = "") => _Route({
|
|
697
|
-
method: "HEAD",
|
|
698
|
-
path
|
|
699
|
-
});
|
|
700
|
-
/**
|
|
701
|
-
* Register a middleware route on the given path (default "") for the given controller.
|
|
702
|
-
*/
|
|
703
|
-
const Middleware = (path = "") => _Route({
|
|
704
|
-
method: "USE",
|
|
705
|
-
path
|
|
706
|
-
});
|
|
707
|
-
/**
|
|
708
|
-
* Given a class constructor, fetch all the routes attached.
|
|
709
|
-
*/
|
|
710
|
-
function _getRoutes(ctor) {
|
|
711
|
-
return _routeStore.get(ctor) ?? [];
|
|
712
|
-
}
|
|
713
|
-
//#endregion
|
|
714
897
|
//#region src/annotation/controller.ts
|
|
715
898
|
const _ControllerRegistry = /* @__PURE__ */ new WeakMap();
|
|
716
899
|
/**
|
|
@@ -722,6 +905,7 @@ const _ControllerRegistry = /* @__PURE__ */ new WeakMap();
|
|
|
722
905
|
function Controller({ prefix = "", deps = [] } = {}) {
|
|
723
906
|
return (target) => {
|
|
724
907
|
const targetClass = target;
|
|
908
|
+
_registerController(target, prefix);
|
|
725
909
|
const router = (0, express.Router)();
|
|
726
910
|
const routes = _getRoutes(target);
|
|
727
911
|
const usedRoutes = /* @__PURE__ */ new Set();
|
|
@@ -746,15 +930,20 @@ Split these into separate @MiddlewareClass classes, or merge the logic into a si
|
|
|
746
930
|
if (method === "USE" && fn.length >= 4) {
|
|
747
931
|
const middlewareFn = async (err, request, response, next) => {
|
|
748
932
|
try {
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
}
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
933
|
+
await validate({
|
|
934
|
+
target,
|
|
935
|
+
fnName,
|
|
936
|
+
request
|
|
937
|
+
});
|
|
938
|
+
await handleResult({
|
|
939
|
+
result: fn.bind(controllerInstance)(err, request, response, next),
|
|
940
|
+
response,
|
|
941
|
+
target,
|
|
942
|
+
fnName,
|
|
943
|
+
method,
|
|
944
|
+
path: path instanceof RegExp ? path.source : fp,
|
|
945
|
+
isErrorMiddleware: true
|
|
946
|
+
});
|
|
758
947
|
} catch (e) {
|
|
759
948
|
console.error(e);
|
|
760
949
|
next(e);
|
|
@@ -764,34 +953,52 @@ Split these into separate @MiddlewareClass classes, or merge the logic into a si
|
|
|
764
953
|
return;
|
|
765
954
|
}
|
|
766
955
|
router[methodName](fp, async (request, response, next) => {
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
}
|
|
780
|
-
const result = await fn.bind(controllerInstance)(request, response, next);
|
|
781
|
-
if (result instanceof ResponseEntity) {
|
|
782
|
-
response.contentType("application/json").status(result.getStatusCode()).set(result.getHeaders()).send(Sapling.serialize(result.getBody()));
|
|
783
|
-
return;
|
|
784
|
-
}
|
|
785
|
-
if (result instanceof RedirectView) {
|
|
786
|
-
response.redirect(result.getUrl());
|
|
787
|
-
return;
|
|
788
|
-
}
|
|
789
|
-
if (method !== "USE" && !response.writableEnded) response.status(404).send(Html404ErrorPage(`Cannot ${methodName.toUpperCase()} ${path instanceof RegExp ? path.source : fp}`));
|
|
956
|
+
await validate({
|
|
957
|
+
target,
|
|
958
|
+
fnName,
|
|
959
|
+
request
|
|
960
|
+
});
|
|
961
|
+
await handleResult({
|
|
962
|
+
result: await fn.bind(controllerInstance)(request, response, next),
|
|
963
|
+
response,
|
|
964
|
+
target,
|
|
965
|
+
fnName,
|
|
966
|
+
method,
|
|
967
|
+
path: path instanceof RegExp ? path.source : fp
|
|
968
|
+
});
|
|
790
969
|
});
|
|
791
970
|
}
|
|
792
971
|
_ControllerRegistry.set(targetClass, router);
|
|
793
972
|
};
|
|
794
973
|
}
|
|
974
|
+
async function handleResult({ result, target, fnName, response, method, path, isErrorMiddleware = false }) {
|
|
975
|
+
const schemas = _getValidatorSchema(target, fnName);
|
|
976
|
+
if (result instanceof ResponseEntity) {
|
|
977
|
+
const body = schemas && schemas.responseBody ? await _parseOrThrow(schemas.responseBody, result.getBody(), "resbody", fnName) : result.getBody();
|
|
978
|
+
response.contentType("application/json").status(result.getStatusCode()).set(result.getHeaders()).send(Sapling.serialize(body));
|
|
979
|
+
return;
|
|
980
|
+
}
|
|
981
|
+
if (result instanceof RedirectView) {
|
|
982
|
+
response.redirect(result.getUrl());
|
|
983
|
+
return;
|
|
984
|
+
}
|
|
985
|
+
if (!isErrorMiddleware && method !== "USE" && !response.writableEnded) response.status(404).send(Html404ErrorPage(`Cannot ${method} ${path}`));
|
|
986
|
+
}
|
|
987
|
+
async function validate({ target, fnName, request }) {
|
|
988
|
+
const schemas = _getValidatorSchema(target, fnName);
|
|
989
|
+
if (schemas) {
|
|
990
|
+
if (schemas.requestBody) request.body = await _parseOrThrow(schemas.requestBody, request.body, "reqbody", fnName);
|
|
991
|
+
if (schemas.requestParam) request.params = await _parseOrThrow(schemas.requestParam, request.params, "reqparams", fnName);
|
|
992
|
+
if (schemas.requestQuery) {
|
|
993
|
+
const parsedQuery = await _parseOrThrow(schemas.requestQuery, request.query, "reqquery", fnName);
|
|
994
|
+
Object.defineProperty(request, "query", {
|
|
995
|
+
value: parsedQuery,
|
|
996
|
+
writable: true,
|
|
997
|
+
configurable: true
|
|
998
|
+
});
|
|
999
|
+
}
|
|
1000
|
+
}
|
|
1001
|
+
}
|
|
795
1002
|
//#endregion
|
|
796
1003
|
//#region src/annotation/middleware.ts
|
|
797
1004
|
/**
|
|
@@ -813,7 +1020,7 @@ function __decorate(decorators, target, key, desc) {
|
|
|
813
1020
|
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
814
1021
|
}
|
|
815
1022
|
//#endregion
|
|
816
|
-
//#region src/middleware/default/base.ts
|
|
1023
|
+
//#region src/middleware/default/error/base.ts
|
|
817
1024
|
let DefaultBaseErrorMiddleware = class DefaultBaseErrorMiddleware {
|
|
818
1025
|
handle(err, _request, _response, _next) {
|
|
819
1026
|
console.error("[Error]", err);
|
|
@@ -823,7 +1030,20 @@ let DefaultBaseErrorMiddleware = class DefaultBaseErrorMiddleware {
|
|
|
823
1030
|
__decorate([Middleware()], DefaultBaseErrorMiddleware.prototype, "handle", null);
|
|
824
1031
|
DefaultBaseErrorMiddleware = __decorate([MiddlewareClass()], DefaultBaseErrorMiddleware);
|
|
825
1032
|
//#endregion
|
|
826
|
-
//#region src/middleware/default/
|
|
1033
|
+
//#region src/middleware/default/error/parse.ts
|
|
1034
|
+
let DefaultParserErrorMiddleware = class DefaultParserErrorMiddleware {
|
|
1035
|
+
handle(err, _request, _response, next) {
|
|
1036
|
+
if (err instanceof ParserError) {
|
|
1037
|
+
console.warn(err);
|
|
1038
|
+
return ResponseEntity.status(err.status).body({ message: err.message });
|
|
1039
|
+
}
|
|
1040
|
+
next(err);
|
|
1041
|
+
}
|
|
1042
|
+
};
|
|
1043
|
+
__decorate([Middleware()], DefaultParserErrorMiddleware.prototype, "handle", null);
|
|
1044
|
+
DefaultParserErrorMiddleware = __decorate([MiddlewareClass()], DefaultParserErrorMiddleware);
|
|
1045
|
+
//#endregion
|
|
1046
|
+
//#region src/middleware/default/error/responsestatus.ts
|
|
827
1047
|
let DefaultResponseStatusErrorMiddleware = class DefaultResponseStatusErrorMiddleware {
|
|
828
1048
|
handle(err, _request, _response, next) {
|
|
829
1049
|
if (err instanceof ResponseStatusError) return ResponseEntity.status(err.status).body({ message: err.message });
|
|
@@ -833,7 +1053,90 @@ let DefaultResponseStatusErrorMiddleware = class DefaultResponseStatusErrorMiddl
|
|
|
833
1053
|
__decorate([Middleware()], DefaultResponseStatusErrorMiddleware.prototype, "handle", null);
|
|
834
1054
|
DefaultResponseStatusErrorMiddleware = __decorate([MiddlewareClass()], DefaultResponseStatusErrorMiddleware);
|
|
835
1055
|
//#endregion
|
|
1056
|
+
//#region src/middleware/default/openapi/index.ts
|
|
1057
|
+
let DefaultOpenApiMiddleware = class DefaultOpenApiMiddleware {
|
|
1058
|
+
handle(_request, _response, _next) {
|
|
1059
|
+
return ResponseEntity.ok().body(generateOpenApiSpec());
|
|
1060
|
+
}
|
|
1061
|
+
};
|
|
1062
|
+
__decorate([GET(_settings.doc.openApiPath)], DefaultOpenApiMiddleware.prototype, "handle", null);
|
|
1063
|
+
DefaultOpenApiMiddleware = __decorate([MiddlewareClass()], DefaultOpenApiMiddleware);
|
|
1064
|
+
//#endregion
|
|
1065
|
+
//#region src/middleware/default/swagger/index.ts
|
|
1066
|
+
/**
|
|
1067
|
+
* Enable the serving of the Swagger endpoint used to serve the OpenAPI spec generated by Sapling.
|
|
1068
|
+
*
|
|
1069
|
+
* Configure any middleware-specific settings with `Sapling.Extras.swaggerAndOpenApi`
|
|
1070
|
+
*
|
|
1071
|
+
* You must register `DefaultSwaggerMiddleware.Serve` & `DefaultSwaggerMiddleware.Setup` after `DefaultOpenApiMiddleware`
|
|
1072
|
+
*
|
|
1073
|
+
* ```ts
|
|
1074
|
+
* const middlewares = [
|
|
1075
|
+
* DefaultOpenApiMiddleware,
|
|
1076
|
+
* DefaultSwaggerMiddleware.Serve,
|
|
1077
|
+
* DefaultSwaggerMiddleware.Setup,
|
|
1078
|
+
* ];
|
|
1079
|
+
* middlewares.map(Sapling.resolve).forEach((r) => app.use(r));
|
|
1080
|
+
* ```
|
|
1081
|
+
*/
|
|
1082
|
+
let Serve = class Serve {
|
|
1083
|
+
handlers = swagger_ui_express.default.serve;
|
|
1084
|
+
handle(request, response, next) {
|
|
1085
|
+
return Sapling.chainHandlers(this.handlers, request, response, next);
|
|
1086
|
+
}
|
|
1087
|
+
};
|
|
1088
|
+
__decorate([Middleware(_settings.doc.swaggerPath)], Serve.prototype, "handle", null);
|
|
1089
|
+
Serve = __decorate([MiddlewareClass()], Serve);
|
|
1090
|
+
/**
|
|
1091
|
+
* Enable the serving of the Swagger endpoint used to serve the OpenAPI spec generated by Sapling.
|
|
1092
|
+
*
|
|
1093
|
+
* Configure any middleware-specific settings with `Sapling.Extras.swaggerAndOpenApi`
|
|
1094
|
+
*
|
|
1095
|
+
* You must register `DefaultSwaggerMiddleware.Serve` & `DefaultSwaggerMiddleware.Setup` after `DefaultOpenApiMiddleware`
|
|
1096
|
+
*
|
|
1097
|
+
* ```ts
|
|
1098
|
+
* const middlewares = [
|
|
1099
|
+
* DefaultOpenApiMiddleware,
|
|
1100
|
+
* DefaultSwaggerMiddleware.Serve,
|
|
1101
|
+
* DefaultSwaggerMiddleware.Setup,
|
|
1102
|
+
* ];
|
|
1103
|
+
* middlewares.map(Sapling.resolve).forEach((r) => app.use(r));
|
|
1104
|
+
* ```
|
|
1105
|
+
*/
|
|
1106
|
+
let Setup = class Setup {
|
|
1107
|
+
handler;
|
|
1108
|
+
constructor() {
|
|
1109
|
+
this.handler = swagger_ui_express.default.setup(null, { swaggerOptions: { url: _settings.doc.openApiPath } });
|
|
1110
|
+
}
|
|
1111
|
+
handle(request, response, next) {
|
|
1112
|
+
return this.handler(request, response, next);
|
|
1113
|
+
}
|
|
1114
|
+
};
|
|
1115
|
+
__decorate([Middleware(_settings.doc.swaggerPath)], Setup.prototype, "handle", null);
|
|
1116
|
+
Setup = __decorate([MiddlewareClass()], Setup);
|
|
1117
|
+
/**
|
|
1118
|
+
* Enable the serving of the Swagger endpoint used to serve the OpenAPI spec generated by Sapling.
|
|
1119
|
+
*
|
|
1120
|
+
* Configure any middleware-specific settings with `Sapling.Extras.swaggerAndOpenApi`
|
|
1121
|
+
*
|
|
1122
|
+
* You must register `DefaultSwaggerMiddleware.Serve` & `DefaultSwaggerMiddleware.Setup` after `DefaultOpenApiMiddleware`
|
|
1123
|
+
*
|
|
1124
|
+
* ```ts
|
|
1125
|
+
* const middlewares = [
|
|
1126
|
+
* DefaultOpenApiMiddleware,
|
|
1127
|
+
* DefaultSwaggerMiddleware.Serve,
|
|
1128
|
+
* DefaultSwaggerMiddleware.Setup,
|
|
1129
|
+
* ];
|
|
1130
|
+
* middlewares.map(Sapling.resolve).forEach((r) => app.use(r));
|
|
1131
|
+
* ```
|
|
1132
|
+
*/
|
|
1133
|
+
const DefaultSwaggerMiddleware = {
|
|
1134
|
+
Serve,
|
|
1135
|
+
Setup
|
|
1136
|
+
};
|
|
1137
|
+
//#endregion
|
|
836
1138
|
exports.Controller = Controller;
|
|
1139
|
+
exports.ControllerSchema = ControllerSchema;
|
|
837
1140
|
exports.DELETE = DELETE;
|
|
838
1141
|
Object.defineProperty(exports, "DefaultBaseErrorMiddleware", {
|
|
839
1142
|
enumerable: true,
|
|
@@ -841,12 +1144,25 @@ Object.defineProperty(exports, "DefaultBaseErrorMiddleware", {
|
|
|
841
1144
|
return DefaultBaseErrorMiddleware;
|
|
842
1145
|
}
|
|
843
1146
|
});
|
|
1147
|
+
Object.defineProperty(exports, "DefaultOpenApiMiddleware", {
|
|
1148
|
+
enumerable: true,
|
|
1149
|
+
get: function() {
|
|
1150
|
+
return DefaultOpenApiMiddleware;
|
|
1151
|
+
}
|
|
1152
|
+
});
|
|
1153
|
+
Object.defineProperty(exports, "DefaultParserErrorMiddleware", {
|
|
1154
|
+
enumerable: true,
|
|
1155
|
+
get: function() {
|
|
1156
|
+
return DefaultParserErrorMiddleware;
|
|
1157
|
+
}
|
|
1158
|
+
});
|
|
844
1159
|
Object.defineProperty(exports, "DefaultResponseStatusErrorMiddleware", {
|
|
845
1160
|
enumerable: true,
|
|
846
1161
|
get: function() {
|
|
847
1162
|
return DefaultResponseStatusErrorMiddleware;
|
|
848
1163
|
}
|
|
849
1164
|
});
|
|
1165
|
+
exports.DefaultSwaggerMiddleware = DefaultSwaggerMiddleware;
|
|
850
1166
|
exports.GET = GET;
|
|
851
1167
|
exports.HEAD = HEAD;
|
|
852
1168
|
exports.Html404ErrorPage = Html404ErrorPage;
|
|
@@ -863,16 +1179,29 @@ exports.RedirectView = RedirectView;
|
|
|
863
1179
|
exports.RequestBody = RequestBody;
|
|
864
1180
|
exports.RequestParam = RequestParam;
|
|
865
1181
|
exports.RequestQuery = RequestQuery;
|
|
1182
|
+
exports.ResponseBody = ResponseBody;
|
|
866
1183
|
exports.ResponseEntity = ResponseEntity;
|
|
867
1184
|
exports.ResponseEntityBuilder = ResponseEntityBuilder;
|
|
868
1185
|
exports.ResponseStatusError = ResponseStatusError;
|
|
1186
|
+
exports.RouteSchema = RouteSchema;
|
|
869
1187
|
exports.Sapling = Sapling;
|
|
870
1188
|
exports._ControllerRegistry = _ControllerRegistry;
|
|
871
1189
|
exports._InjectableDeps = _InjectableDeps;
|
|
872
1190
|
exports._InjectableRegistry = _InjectableRegistry;
|
|
873
1191
|
exports._Route = _Route;
|
|
874
|
-
exports.
|
|
1192
|
+
exports._clearOpenApiRegistry = _clearOpenApiRegistry;
|
|
1193
|
+
exports._getControllerSchema = _getControllerSchema;
|
|
1194
|
+
exports._getOrCreateSchemaDefinition = _getOrCreateSchemaDefinition;
|
|
1195
|
+
exports._getRouteSchema = _getRouteSchema;
|
|
875
1196
|
exports._getRoutes = _getRoutes;
|
|
1197
|
+
exports._getValidatorSchema = _getValidatorSchema;
|
|
876
1198
|
exports._parseOrThrow = _parseOrThrow;
|
|
1199
|
+
exports._registerController = _registerController;
|
|
877
1200
|
exports._resolve = _resolve;
|
|
1201
|
+
exports._saveValidatorSchema = _saveValidatorSchema;
|
|
1202
|
+
exports._setControllerSchema = _setControllerSchema;
|
|
1203
|
+
exports._setRouteSchema = _setRouteSchema;
|
|
1204
|
+
exports._settings = _settings;
|
|
1205
|
+
exports.generateOpenApiSpec = generateOpenApiSpec;
|
|
878
1206
|
exports.methodResolve = methodResolve;
|
|
1207
|
+
exports.openApiGenerator = openApiGenerator;
|