@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.mjs
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import e, { Router } from "express";
|
|
2
|
+
import swagger from "swagger-ui-express";
|
|
2
3
|
//#region src/html/404.ts
|
|
3
4
|
/**
|
|
4
5
|
* Default Express.js 404 error page, as a string.
|
|
@@ -22,6 +23,7 @@ const Html404ErrorPage = (error) => `<!DOCTYPE html>
|
|
|
22
23
|
* You can either return `new RedirectView(url)` or `RedirectView.redirect(url)` inside of a controller method.
|
|
23
24
|
*/
|
|
24
25
|
var RedirectView = class RedirectView {
|
|
26
|
+
_url;
|
|
25
27
|
constructor(url) {
|
|
26
28
|
this._url = url;
|
|
27
29
|
}
|
|
@@ -117,8 +119,10 @@ let HttpStatus = /* @__PURE__ */ function(HttpStatus) {
|
|
|
117
119
|
* @typeParam T - the type of the response body
|
|
118
120
|
*/
|
|
119
121
|
var ResponseEntity = class {
|
|
122
|
+
_statusCode;
|
|
123
|
+
_headers = {};
|
|
124
|
+
_body;
|
|
120
125
|
constructor(body, headers = {}, statusCode = 200) {
|
|
121
|
-
this._headers = {};
|
|
122
126
|
this._body = body;
|
|
123
127
|
this._headers = headers;
|
|
124
128
|
this._statusCode = statusCode;
|
|
@@ -171,8 +175,9 @@ var ResponseEntity = class {
|
|
|
171
175
|
* ensuring type safety when constructing the response.
|
|
172
176
|
*/
|
|
173
177
|
var ResponseEntityBuilder = class {
|
|
178
|
+
_statusCode;
|
|
179
|
+
_headers = {};
|
|
174
180
|
constructor(statusCode) {
|
|
175
|
-
this._headers = {};
|
|
176
181
|
this._statusCode = statusCode;
|
|
177
182
|
}
|
|
178
183
|
/**
|
|
@@ -204,6 +209,7 @@ var ResponseEntityBuilder = class {
|
|
|
204
209
|
* @see {@link Sapling.loadResponseStatusErrorMiddleware}
|
|
205
210
|
*/
|
|
206
211
|
var ResponseStatusError = class ResponseStatusError extends Error {
|
|
212
|
+
status;
|
|
207
213
|
constructor(status, message) {
|
|
208
214
|
super(message ?? "Something went wrong.");
|
|
209
215
|
this.status = status;
|
|
@@ -215,32 +221,42 @@ var ResponseStatusError = class ResponseStatusError extends Error {
|
|
|
215
221
|
//#endregion
|
|
216
222
|
//#region src/helper/error/parse.ts
|
|
217
223
|
/**
|
|
218
|
-
* 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.
|
|
219
225
|
*/
|
|
220
226
|
var ParserError = class ParserError extends ResponseStatusError {
|
|
221
|
-
constructor(location, issues, vendor) {
|
|
222
|
-
super(400, ParserError.formatMessage(location, issues, vendor));
|
|
227
|
+
constructor(location, issues, vendor, functionName) {
|
|
228
|
+
super(400, ParserError.formatMessage(location, issues, vendor, functionName));
|
|
223
229
|
Object.setPrototypeOf(this, new.target.prototype);
|
|
224
230
|
}
|
|
225
|
-
static formatMessage(location, issues, vendor) {
|
|
231
|
+
static formatMessage(location, issues, vendor, functionName) {
|
|
226
232
|
const formatted = issues.map((i) => {
|
|
227
233
|
const path = Array.isArray(i.path) ? i.path.map((seg) => typeof seg === "object" && seg ? String(seg.key) : String(seg)).join(".") : "";
|
|
228
234
|
return path ? `${path}: ${i.message}` : i.message;
|
|
229
235
|
}).join("; ");
|
|
230
|
-
return
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
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
|
+
}
|
|
237
245
|
}
|
|
238
246
|
};
|
|
239
247
|
//#endregion
|
|
240
248
|
//#region src/helper/sapling.ts
|
|
241
|
-
const
|
|
249
|
+
const _settings = {
|
|
242
250
|
serialize: JSON.stringify,
|
|
243
|
-
deserialize: JSON.parse
|
|
251
|
+
deserialize: JSON.parse,
|
|
252
|
+
doc: {
|
|
253
|
+
openApiPath: "/openapi.json",
|
|
254
|
+
swaggerPath: "/swagger.html",
|
|
255
|
+
metadata: {
|
|
256
|
+
title: "API",
|
|
257
|
+
version: "1.0.0"
|
|
258
|
+
}
|
|
259
|
+
}
|
|
244
260
|
};
|
|
245
261
|
/**
|
|
246
262
|
* Collection of utility functions which are essential for Sapling to function.
|
|
@@ -319,13 +335,13 @@ var Sapling = class Sapling {
|
|
|
319
335
|
* @defaultValue `JSON.stringify`
|
|
320
336
|
*/
|
|
321
337
|
static serialize(value) {
|
|
322
|
-
return
|
|
338
|
+
return _settings.serialize(value);
|
|
323
339
|
}
|
|
324
340
|
/**
|
|
325
341
|
* Replace the function used for `serialize`.
|
|
326
342
|
*/
|
|
327
343
|
static setSerializeFn(fn) {
|
|
328
|
-
|
|
344
|
+
_settings.serialize = fn;
|
|
329
345
|
}
|
|
330
346
|
/**
|
|
331
347
|
* De-serialize a JSON string back to a JavaScript object.
|
|
@@ -337,15 +353,389 @@ var Sapling = class Sapling {
|
|
|
337
353
|
* @defaultValue `JSON.parse`
|
|
338
354
|
*/
|
|
339
355
|
static deserialize(value) {
|
|
340
|
-
return
|
|
356
|
+
return _settings.deserialize(value);
|
|
341
357
|
}
|
|
342
358
|
/**
|
|
343
359
|
* Replace the function used for `deserialize`
|
|
344
360
|
*/
|
|
345
361
|
static setDeserializeFn(fn) {
|
|
346
|
-
|
|
362
|
+
_settings.deserialize = fn;
|
|
363
|
+
}
|
|
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
|
+
*/
|
|
417
|
+
static chainHandlers(handlers, request, response, next, index = 0) {
|
|
418
|
+
if (index >= handlers.length) {
|
|
419
|
+
next();
|
|
420
|
+
return;
|
|
421
|
+
}
|
|
422
|
+
handlers[index]?.(request, response, (err) => {
|
|
423
|
+
if (err) {
|
|
424
|
+
next(err);
|
|
425
|
+
return;
|
|
426
|
+
}
|
|
427
|
+
Sapling.chainHandlers(handlers, request, response, next, index + 1);
|
|
428
|
+
});
|
|
429
|
+
}
|
|
430
|
+
};
|
|
431
|
+
//#endregion
|
|
432
|
+
//#region src/annotation/route.ts
|
|
433
|
+
const _routeStore = /* @__PURE__ */ new WeakMap();
|
|
434
|
+
/**
|
|
435
|
+
* Custom annotation that will store all routes inside of a map,
|
|
436
|
+
* which can then be used to initialize all the routes to the router.
|
|
437
|
+
*/
|
|
438
|
+
function _Route({ method, path = "" }) {
|
|
439
|
+
return (target, propertyKey) => {
|
|
440
|
+
const ctor = target.constructor;
|
|
441
|
+
const list = _routeStore.get(ctor) ?? [];
|
|
442
|
+
list.push({
|
|
443
|
+
method,
|
|
444
|
+
path: path ?? "",
|
|
445
|
+
fnName: String(propertyKey)
|
|
446
|
+
});
|
|
447
|
+
_routeStore.set(ctor, list);
|
|
448
|
+
};
|
|
449
|
+
}
|
|
450
|
+
/**
|
|
451
|
+
* Register GET route on the given path (default "") for the given controller.
|
|
452
|
+
*/
|
|
453
|
+
const GET = (path = "") => _Route({
|
|
454
|
+
method: "GET",
|
|
455
|
+
path
|
|
456
|
+
});
|
|
457
|
+
/**
|
|
458
|
+
* Register POST route on the given path (default "") for the given controller.
|
|
459
|
+
*/
|
|
460
|
+
const POST = (path = "") => _Route({
|
|
461
|
+
method: "POST",
|
|
462
|
+
path
|
|
463
|
+
});
|
|
464
|
+
/**
|
|
465
|
+
* Register PUT route on the given path (default "") for the given controller.
|
|
466
|
+
*/
|
|
467
|
+
const PUT = (path = "") => _Route({
|
|
468
|
+
method: "PUT",
|
|
469
|
+
path
|
|
470
|
+
});
|
|
471
|
+
/**
|
|
472
|
+
* Register DELETE route on the given path (default "") for the given controller.
|
|
473
|
+
*/
|
|
474
|
+
const DELETE = (path = "") => _Route({
|
|
475
|
+
method: "DELETE",
|
|
476
|
+
path
|
|
477
|
+
});
|
|
478
|
+
/**
|
|
479
|
+
* Register OPTIONS route on the given path (default "") for the given controller.
|
|
480
|
+
*/
|
|
481
|
+
const OPTIONS = (path = "") => _Route({
|
|
482
|
+
method: "OPTIONS",
|
|
483
|
+
path
|
|
484
|
+
});
|
|
485
|
+
/**
|
|
486
|
+
* Register PATCH route on the given path (default "") for the given controller.
|
|
487
|
+
*/
|
|
488
|
+
const PATCH = (path = "") => _Route({
|
|
489
|
+
method: "PATCH",
|
|
490
|
+
path
|
|
491
|
+
});
|
|
492
|
+
/**
|
|
493
|
+
* Register HEAD route on the given path (default "") for the given controller.
|
|
494
|
+
*/
|
|
495
|
+
const HEAD = (path = "") => _Route({
|
|
496
|
+
method: "HEAD",
|
|
497
|
+
path
|
|
498
|
+
});
|
|
499
|
+
/**
|
|
500
|
+
* Register a middleware route on the given path (default "") for the given controller.
|
|
501
|
+
*/
|
|
502
|
+
const Middleware = (path = "") => _Route({
|
|
503
|
+
method: "USE",
|
|
504
|
+
path
|
|
505
|
+
});
|
|
506
|
+
/**
|
|
507
|
+
* Given a class constructor, fetch all the routes attached.
|
|
508
|
+
*/
|
|
509
|
+
function _getRoutes(ctor) {
|
|
510
|
+
return _routeStore.get(ctor) ?? [];
|
|
511
|
+
}
|
|
512
|
+
//#endregion
|
|
513
|
+
//#region src/annotation/schema.ts
|
|
514
|
+
function ControllerSchema(options) {
|
|
515
|
+
return (target) => {
|
|
516
|
+
_setControllerSchema(target, options);
|
|
517
|
+
};
|
|
518
|
+
}
|
|
519
|
+
function RouteSchema(options) {
|
|
520
|
+
return (target, propertyKey) => {
|
|
521
|
+
const ctor = target.constructor;
|
|
522
|
+
_setRouteSchema(ctor, String(propertyKey), options);
|
|
523
|
+
};
|
|
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
|
+
}
|
|
534
|
+
function _setRouteSchema(ctor, fnName, options) {
|
|
535
|
+
getOrCreateRouteSchemaStore(_routeSchemaStore, ctor).set(fnName, options);
|
|
536
|
+
}
|
|
537
|
+
function _setControllerSchema(ctor, options) {
|
|
538
|
+
_controllerSchemaStore.set(ctor, options);
|
|
539
|
+
}
|
|
540
|
+
function _getRouteSchema(ctor, fnName) {
|
|
541
|
+
return _routeSchemaStore.get(ctor)?.get(fnName);
|
|
542
|
+
}
|
|
543
|
+
function _getControllerSchema(ctor) {
|
|
544
|
+
return _controllerSchemaStore.get(ctor);
|
|
545
|
+
}
|
|
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
|
|
605
|
+
//#region src/helper/openapi.ts
|
|
606
|
+
var OpenAPIGenerator = class {
|
|
607
|
+
OPENAPI_VERSION = "3.0.0";
|
|
608
|
+
controllers = /* @__PURE__ */ new Set();
|
|
609
|
+
registerController(controllerClass, prefix) {
|
|
610
|
+
this.controllers.add({
|
|
611
|
+
class: controllerClass,
|
|
612
|
+
prefix
|
|
613
|
+
});
|
|
614
|
+
}
|
|
615
|
+
/**
|
|
616
|
+
* visible for testing
|
|
617
|
+
*/
|
|
618
|
+
_clearControllers() {
|
|
619
|
+
this.controllers.clear();
|
|
620
|
+
}
|
|
621
|
+
get metadata() {
|
|
622
|
+
return _settings.doc.metadata;
|
|
623
|
+
}
|
|
624
|
+
generateSpec() {
|
|
625
|
+
const metadata = this.metadata;
|
|
626
|
+
const paths = {};
|
|
627
|
+
const tags = [];
|
|
628
|
+
for (const { class: controllerClass, prefix } of this.controllers) {
|
|
629
|
+
const routes = _getRoutes(controllerClass);
|
|
630
|
+
const controllerSchema = _getControllerSchema(controllerClass);
|
|
631
|
+
if (controllerSchema?.title) tags.push({
|
|
632
|
+
name: controllerSchema.title,
|
|
633
|
+
description: controllerSchema.description
|
|
634
|
+
});
|
|
635
|
+
for (const route of routes) {
|
|
636
|
+
if (route.method === "USE") continue;
|
|
637
|
+
const schemas = _getValidatorSchema(controllerClass, route.fnName);
|
|
638
|
+
const routeSchema = _getRouteSchema(controllerClass, route.fnName);
|
|
639
|
+
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.`);
|
|
640
|
+
const openApiPath = (prefix + route.path).replace(/:([A-Za-z0-9_]+)/g, "{$1}");
|
|
641
|
+
if (!paths[openApiPath]) paths[openApiPath] = {};
|
|
642
|
+
const responses = {};
|
|
643
|
+
if (schemas?.responseBody) {
|
|
644
|
+
const responseSchema = this.toJsonSchema(schemas.responseBody, "output");
|
|
645
|
+
responses["200"] = {
|
|
646
|
+
description: responseSchema.description ?? "Successful response",
|
|
647
|
+
content: { "application/json": { schema: responseSchema } }
|
|
648
|
+
};
|
|
649
|
+
} else responses["200"] = { description: "Successful response" };
|
|
650
|
+
if (routeSchema?.responses) for (const resp of routeSchema.responses) {
|
|
651
|
+
const statusCode = String(resp.statusCode);
|
|
652
|
+
const existingResponse = responses[statusCode];
|
|
653
|
+
const existingSchema = existingResponse && "content" in existingResponse ? existingResponse.content?.["application/json"]?.schema : void 0;
|
|
654
|
+
const responseSchema = resp.schema ? this.toJsonSchema(resp.schema, "output") : statusCode === "200" ? existingSchema : void 0;
|
|
655
|
+
responses[statusCode] = {
|
|
656
|
+
...existingResponse,
|
|
657
|
+
description: resp.description ?? responseSchema?.description ?? existingResponse?.description ?? `Response ${resp.statusCode}`,
|
|
658
|
+
...responseSchema ? { content: { "application/json": { schema: responseSchema } } } : {}
|
|
659
|
+
};
|
|
660
|
+
}
|
|
661
|
+
const operation = {
|
|
662
|
+
responses,
|
|
663
|
+
summary: routeSchema?.summary,
|
|
664
|
+
description: routeSchema?.description,
|
|
665
|
+
tags: controllerSchema?.title ? [controllerSchema.title] : void 0
|
|
666
|
+
};
|
|
667
|
+
const parameters = [];
|
|
668
|
+
if (schemas?.requestParam) {
|
|
669
|
+
const paramSchema = this.toJsonSchema(schemas.requestParam, "input");
|
|
670
|
+
if (paramSchema.type === "object" && paramSchema.properties) for (const [name, schema] of Object.entries(paramSchema.properties)) {
|
|
671
|
+
const parameterSchema = schema;
|
|
672
|
+
parameters.push({
|
|
673
|
+
name,
|
|
674
|
+
in: "path",
|
|
675
|
+
required: true,
|
|
676
|
+
description: parameterSchema.description,
|
|
677
|
+
schema: parameterSchema
|
|
678
|
+
});
|
|
679
|
+
}
|
|
680
|
+
}
|
|
681
|
+
if (schemas?.requestQuery) {
|
|
682
|
+
const querySchema = this.toJsonSchema(schemas.requestQuery, "input");
|
|
683
|
+
if (querySchema.type === "object" && querySchema.properties) for (const [name, schema] of Object.entries(querySchema.properties)) {
|
|
684
|
+
const isRequired = Array.isArray(querySchema.required) && querySchema.required.includes(name);
|
|
685
|
+
const parameterSchema = schema;
|
|
686
|
+
parameters.push({
|
|
687
|
+
name,
|
|
688
|
+
in: "query",
|
|
689
|
+
required: isRequired,
|
|
690
|
+
description: parameterSchema.description,
|
|
691
|
+
schema: parameterSchema
|
|
692
|
+
});
|
|
693
|
+
}
|
|
694
|
+
}
|
|
695
|
+
if (parameters.length > 0) operation.parameters = parameters;
|
|
696
|
+
if (schemas?.requestBody) {
|
|
697
|
+
const requestSchema = this.toJsonSchema(schemas.requestBody, "input");
|
|
698
|
+
operation.requestBody = {
|
|
699
|
+
required: true,
|
|
700
|
+
description: requestSchema.description,
|
|
701
|
+
content: { "application/json": { schema: requestSchema } }
|
|
702
|
+
};
|
|
703
|
+
}
|
|
704
|
+
const method = route.method.toLowerCase();
|
|
705
|
+
paths[openApiPath][method] = operation;
|
|
706
|
+
}
|
|
707
|
+
}
|
|
708
|
+
return {
|
|
709
|
+
openapi: this.OPENAPI_VERSION,
|
|
710
|
+
info: {
|
|
711
|
+
title: metadata.title,
|
|
712
|
+
version: metadata.version,
|
|
713
|
+
description: metadata.description
|
|
714
|
+
},
|
|
715
|
+
tags: tags.length > 0 ? tags : void 0,
|
|
716
|
+
paths
|
|
717
|
+
};
|
|
718
|
+
}
|
|
719
|
+
toJsonSchema(schema, direction = "output") {
|
|
720
|
+
try {
|
|
721
|
+
const jsonSchema = schema["~standard"].jsonSchema;
|
|
722
|
+
return direction === "input" ? jsonSchema.input({ target: "openapi-3.0" }) : jsonSchema.output({ target: "openapi-3.0" });
|
|
723
|
+
} catch (e) {
|
|
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 });
|
|
725
|
+
throw e;
|
|
726
|
+
}
|
|
347
727
|
}
|
|
348
728
|
};
|
|
729
|
+
const openApiGenerator = new OpenAPIGenerator();
|
|
730
|
+
function _registerController(controllerClass, prefix) {
|
|
731
|
+
openApiGenerator.registerController(controllerClass, prefix);
|
|
732
|
+
}
|
|
733
|
+
function generateOpenApiSpec() {
|
|
734
|
+
return openApiGenerator.generateSpec();
|
|
735
|
+
}
|
|
736
|
+
function _clearOpenApiRegistry() {
|
|
737
|
+
openApiGenerator._clearControllers();
|
|
738
|
+
}
|
|
349
739
|
//#endregion
|
|
350
740
|
//#region src/types.ts
|
|
351
741
|
const methodResolve = {
|
|
@@ -479,214 +869,6 @@ function _resolve(ctor) {
|
|
|
479
869
|
return _InjectableRegistry.get(ctor);
|
|
480
870
|
}
|
|
481
871
|
//#endregion
|
|
482
|
-
//#region src/annotation/request.ts
|
|
483
|
-
const _requestSchemaStore = /* @__PURE__ */ new WeakMap();
|
|
484
|
-
/**
|
|
485
|
-
* Apply to a route method to have `request.body` be parsed by `schema`.
|
|
486
|
-
*
|
|
487
|
-
* This annotation will parse `request.body` & then override `request.body`.
|
|
488
|
-
* You can then just simply cast `request.body` for your use
|
|
489
|
-
*
|
|
490
|
-
* @example
|
|
491
|
-
* ```ts
|
|
492
|
-
* const CREATE_BOOK_REQUEST_BODY_SCHEMA = z.object({
|
|
493
|
-
* name: z.string(),
|
|
494
|
-
* description: z.string().optional(),
|
|
495
|
-
* });
|
|
496
|
-
*
|
|
497
|
-
* ⠀@Controller({ prefix: "/api/book" })
|
|
498
|
-
* class BookController {
|
|
499
|
-
* ⠀@RequestBody(CREATE_BOOK_REQUEST_BODY_SCHEMA)
|
|
500
|
-
* ⠀@POST()
|
|
501
|
-
* public createBook(request: e.Request) {
|
|
502
|
-
* const { name, description } = request.body as unknown as z.infer<
|
|
503
|
-
* typeof CREATE_BOOK_REQUEST_BODY_SCHEMA
|
|
504
|
-
* >;
|
|
505
|
-
* }
|
|
506
|
-
* }
|
|
507
|
-
* ```
|
|
508
|
-
*/
|
|
509
|
-
function RequestBody(schema) {
|
|
510
|
-
return (target, propertyKey) => {
|
|
511
|
-
const ctor = target.constructor;
|
|
512
|
-
const fnName = String(propertyKey);
|
|
513
|
-
_setOnce(_getOrCreateRequestSchemaDefinition(ctor, fnName), "body", schema, fnName);
|
|
514
|
-
};
|
|
515
|
-
}
|
|
516
|
-
/**
|
|
517
|
-
* Apply to a route method to have `request.param` be parsed by `schema`.
|
|
518
|
-
*
|
|
519
|
-
* This annotation will parse `request.param` & then override `request.param`.
|
|
520
|
-
* You can then just simply cast `request.param` for your use
|
|
521
|
-
*
|
|
522
|
-
* @example
|
|
523
|
-
* ```ts
|
|
524
|
-
* const GET_BOOK_REQUEST_PARAM_SCHEMA = z.object({
|
|
525
|
-
* bookId: z.string(),
|
|
526
|
-
* });
|
|
527
|
-
*
|
|
528
|
-
* ⠀@Controller({ prefix: "/api/book" })
|
|
529
|
-
* class BookController {
|
|
530
|
-
* ⠀@RequestParam(GET_BOOK_REQUEST_PARAM_SCHEMA)
|
|
531
|
-
* ⠀@GET("/:bookId")
|
|
532
|
-
* public getBook(request: e.Request) {
|
|
533
|
-
* const { bookId } = request.param as unknown as z.infer<
|
|
534
|
-
* typeof GET_BOOK_REQUEST_PARAM_SCHEMA
|
|
535
|
-
* >;
|
|
536
|
-
* }
|
|
537
|
-
* }
|
|
538
|
-
* ```
|
|
539
|
-
*/
|
|
540
|
-
function RequestParam(schema) {
|
|
541
|
-
return (target, propertyKey) => {
|
|
542
|
-
const ctor = target.constructor;
|
|
543
|
-
const fnName = String(propertyKey);
|
|
544
|
-
_setOnce(_getOrCreateRequestSchemaDefinition(ctor, fnName), "param", schema, fnName);
|
|
545
|
-
};
|
|
546
|
-
}
|
|
547
|
-
/**
|
|
548
|
-
* Apply to a route method to have `request.query` be parsed by `schema`.
|
|
549
|
-
*
|
|
550
|
-
* This annotation will parse `request.query` & then override `request.query`.
|
|
551
|
-
* You can then just simply cast `request.query` for your use
|
|
552
|
-
*
|
|
553
|
-
* @example
|
|
554
|
-
* ```ts
|
|
555
|
-
* const LIST_BOOKS_REQUEST_QUERY_SCHEMA = z.object({
|
|
556
|
-
* sort: z.enum(["name", "createdAt"]).optional(),
|
|
557
|
-
* q: z.string().optional(),
|
|
558
|
-
* });
|
|
559
|
-
*
|
|
560
|
-
* ⠀@Controller({ prefix: "/api/book" })
|
|
561
|
-
* class BookController {
|
|
562
|
-
* ⠀@RequestQuery(LIST_BOOKS_REQUEST_QUERY_SCHEMA)
|
|
563
|
-
* ⠀@GET()
|
|
564
|
-
* public listBooks(request: e.Request) {
|
|
565
|
-
* const { sort, q } = request.query as unknown as z.infer<
|
|
566
|
-
* typeof LIST_BOOKS_REQUEST_QUERY_SCHEMA
|
|
567
|
-
* >;
|
|
568
|
-
* }
|
|
569
|
-
* }
|
|
570
|
-
* ```
|
|
571
|
-
*/
|
|
572
|
-
function RequestQuery(schema) {
|
|
573
|
-
return (target, propertyKey) => {
|
|
574
|
-
const ctor = target.constructor;
|
|
575
|
-
const fnName = String(propertyKey);
|
|
576
|
-
_setOnce(_getOrCreateRequestSchemaDefinition(ctor, fnName), "query", schema, fnName);
|
|
577
|
-
};
|
|
578
|
-
}
|
|
579
|
-
function _getOrCreateRequestSchemaDefinition(ctor, fnName) {
|
|
580
|
-
const byFn = (() => {
|
|
581
|
-
const fn = _requestSchemaStore.get(ctor);
|
|
582
|
-
if (fn) return fn;
|
|
583
|
-
const newFn = /* @__PURE__ */ new Map();
|
|
584
|
-
_requestSchemaStore.set(ctor, newFn);
|
|
585
|
-
return newFn;
|
|
586
|
-
})();
|
|
587
|
-
const existing = byFn.get(fnName);
|
|
588
|
-
if (existing) return existing;
|
|
589
|
-
const created = {};
|
|
590
|
-
byFn.set(fnName, created);
|
|
591
|
-
return created;
|
|
592
|
-
}
|
|
593
|
-
function _setOnce(def, key, schema, fnName) {
|
|
594
|
-
if (def[key]) throw new Error(`Duplicate request schema for "${String(key)}" on method "${fnName}"`);
|
|
595
|
-
def[key] = schema;
|
|
596
|
-
}
|
|
597
|
-
function _getRequestSchemas(ctor, fnName) {
|
|
598
|
-
return _requestSchemaStore.get(ctor)?.get(fnName);
|
|
599
|
-
}
|
|
600
|
-
async function _parseOrThrow(schema, input, kind) {
|
|
601
|
-
const result = await schema["~standard"].validate(input);
|
|
602
|
-
if (result.issues) {
|
|
603
|
-
console.debug(`Failed to parse a schema`);
|
|
604
|
-
throw new ParserError(kind, result.issues, schema["~standard"].vendor);
|
|
605
|
-
}
|
|
606
|
-
return result.value;
|
|
607
|
-
}
|
|
608
|
-
//#endregion
|
|
609
|
-
//#region src/annotation/route.ts
|
|
610
|
-
const _routeStore = /* @__PURE__ */ new WeakMap();
|
|
611
|
-
/**
|
|
612
|
-
* Custom annotation that will store all routes inside of a map,
|
|
613
|
-
* which can then be used to initialize all the routes to the router.
|
|
614
|
-
*/
|
|
615
|
-
function _Route({ method, path = "" }) {
|
|
616
|
-
return (target, propertyKey) => {
|
|
617
|
-
const ctor = target.constructor;
|
|
618
|
-
const list = _routeStore.get(ctor) ?? [];
|
|
619
|
-
list.push({
|
|
620
|
-
method,
|
|
621
|
-
path: path ?? "",
|
|
622
|
-
fnName: String(propertyKey)
|
|
623
|
-
});
|
|
624
|
-
_routeStore.set(ctor, list);
|
|
625
|
-
};
|
|
626
|
-
}
|
|
627
|
-
/**
|
|
628
|
-
* Register GET route on the given path (default "") for the given controller.
|
|
629
|
-
*/
|
|
630
|
-
const GET = (path = "") => _Route({
|
|
631
|
-
method: "GET",
|
|
632
|
-
path
|
|
633
|
-
});
|
|
634
|
-
/**
|
|
635
|
-
* Register POST route on the given path (default "") for the given controller.
|
|
636
|
-
*/
|
|
637
|
-
const POST = (path = "") => _Route({
|
|
638
|
-
method: "POST",
|
|
639
|
-
path
|
|
640
|
-
});
|
|
641
|
-
/**
|
|
642
|
-
* Register PUT route on the given path (default "") for the given controller.
|
|
643
|
-
*/
|
|
644
|
-
const PUT = (path = "") => _Route({
|
|
645
|
-
method: "PUT",
|
|
646
|
-
path
|
|
647
|
-
});
|
|
648
|
-
/**
|
|
649
|
-
* Register DELETE route on the given path (default "") for the given controller.
|
|
650
|
-
*/
|
|
651
|
-
const DELETE = (path = "") => _Route({
|
|
652
|
-
method: "DELETE",
|
|
653
|
-
path
|
|
654
|
-
});
|
|
655
|
-
/**
|
|
656
|
-
* Register OPTIONS route on the given path (default "") for the given controller.
|
|
657
|
-
*/
|
|
658
|
-
const OPTIONS = (path = "") => _Route({
|
|
659
|
-
method: "OPTIONS",
|
|
660
|
-
path
|
|
661
|
-
});
|
|
662
|
-
/**
|
|
663
|
-
* Register PATCH route on the given path (default "") for the given controller.
|
|
664
|
-
*/
|
|
665
|
-
const PATCH = (path = "") => _Route({
|
|
666
|
-
method: "PATCH",
|
|
667
|
-
path
|
|
668
|
-
});
|
|
669
|
-
/**
|
|
670
|
-
* Register HEAD route on the given path (default "") for the given controller.
|
|
671
|
-
*/
|
|
672
|
-
const HEAD = (path = "") => _Route({
|
|
673
|
-
method: "HEAD",
|
|
674
|
-
path
|
|
675
|
-
});
|
|
676
|
-
/**
|
|
677
|
-
* Register a middleware route on the given path (default "") for the given controller.
|
|
678
|
-
*/
|
|
679
|
-
const Middleware = (path = "") => _Route({
|
|
680
|
-
method: "USE",
|
|
681
|
-
path
|
|
682
|
-
});
|
|
683
|
-
/**
|
|
684
|
-
* Given a class constructor, fetch all the routes attached.
|
|
685
|
-
*/
|
|
686
|
-
function _getRoutes(ctor) {
|
|
687
|
-
return _routeStore.get(ctor) ?? [];
|
|
688
|
-
}
|
|
689
|
-
//#endregion
|
|
690
872
|
//#region src/annotation/controller.ts
|
|
691
873
|
const _ControllerRegistry = /* @__PURE__ */ new WeakMap();
|
|
692
874
|
/**
|
|
@@ -698,6 +880,7 @@ const _ControllerRegistry = /* @__PURE__ */ new WeakMap();
|
|
|
698
880
|
function Controller({ prefix = "", deps = [] } = {}) {
|
|
699
881
|
return (target) => {
|
|
700
882
|
const targetClass = target;
|
|
883
|
+
_registerController(target, prefix);
|
|
701
884
|
const router = Router();
|
|
702
885
|
const routes = _getRoutes(target);
|
|
703
886
|
const usedRoutes = /* @__PURE__ */ new Set();
|
|
@@ -722,15 +905,20 @@ Split these into separate @MiddlewareClass classes, or merge the logic into a si
|
|
|
722
905
|
if (method === "USE" && fn.length >= 4) {
|
|
723
906
|
const middlewareFn = async (err, request, response, next) => {
|
|
724
907
|
try {
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
}
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
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
|
+
});
|
|
734
922
|
} catch (e) {
|
|
735
923
|
console.error(e);
|
|
736
924
|
next(e);
|
|
@@ -740,34 +928,52 @@ Split these into separate @MiddlewareClass classes, or merge the logic into a si
|
|
|
740
928
|
return;
|
|
741
929
|
}
|
|
742
930
|
router[methodName](fp, async (request, response, next) => {
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
}
|
|
756
|
-
const result = await fn.bind(controllerInstance)(request, response, next);
|
|
757
|
-
if (result instanceof ResponseEntity) {
|
|
758
|
-
response.contentType("application/json").status(result.getStatusCode()).set(result.getHeaders()).send(Sapling.serialize(result.getBody()));
|
|
759
|
-
return;
|
|
760
|
-
}
|
|
761
|
-
if (result instanceof RedirectView) {
|
|
762
|
-
response.redirect(result.getUrl());
|
|
763
|
-
return;
|
|
764
|
-
}
|
|
765
|
-
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
|
+
});
|
|
766
944
|
});
|
|
767
945
|
}
|
|
768
946
|
_ControllerRegistry.set(targetClass, router);
|
|
769
947
|
};
|
|
770
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
|
+
}
|
|
771
977
|
//#endregion
|
|
772
978
|
//#region src/annotation/middleware.ts
|
|
773
979
|
/**
|
|
@@ -789,7 +995,7 @@ function __decorate(decorators, target, key, desc) {
|
|
|
789
995
|
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
790
996
|
}
|
|
791
997
|
//#endregion
|
|
792
|
-
//#region src/middleware/default/base.ts
|
|
998
|
+
//#region src/middleware/default/error/base.ts
|
|
793
999
|
let DefaultBaseErrorMiddleware = class DefaultBaseErrorMiddleware {
|
|
794
1000
|
handle(err, _request, _response, _next) {
|
|
795
1001
|
console.error("[Error]", err);
|
|
@@ -799,7 +1005,20 @@ let DefaultBaseErrorMiddleware = class DefaultBaseErrorMiddleware {
|
|
|
799
1005
|
__decorate([Middleware()], DefaultBaseErrorMiddleware.prototype, "handle", null);
|
|
800
1006
|
DefaultBaseErrorMiddleware = __decorate([MiddlewareClass()], DefaultBaseErrorMiddleware);
|
|
801
1007
|
//#endregion
|
|
802
|
-
//#region src/middleware/default/
|
|
1008
|
+
//#region src/middleware/default/error/parse.ts
|
|
1009
|
+
let DefaultParserErrorMiddleware = class DefaultParserErrorMiddleware {
|
|
1010
|
+
handle(err, _request, _response, next) {
|
|
1011
|
+
if (err instanceof ParserError) {
|
|
1012
|
+
console.warn(err);
|
|
1013
|
+
return ResponseEntity.status(err.status).body({ message: err.message });
|
|
1014
|
+
}
|
|
1015
|
+
next(err);
|
|
1016
|
+
}
|
|
1017
|
+
};
|
|
1018
|
+
__decorate([Middleware()], DefaultParserErrorMiddleware.prototype, "handle", null);
|
|
1019
|
+
DefaultParserErrorMiddleware = __decorate([MiddlewareClass()], DefaultParserErrorMiddleware);
|
|
1020
|
+
//#endregion
|
|
1021
|
+
//#region src/middleware/default/error/responsestatus.ts
|
|
803
1022
|
let DefaultResponseStatusErrorMiddleware = class DefaultResponseStatusErrorMiddleware {
|
|
804
1023
|
handle(err, _request, _response, next) {
|
|
805
1024
|
if (err instanceof ResponseStatusError) return ResponseEntity.status(err.status).body({ message: err.message });
|
|
@@ -809,4 +1028,86 @@ let DefaultResponseStatusErrorMiddleware = class DefaultResponseStatusErrorMiddl
|
|
|
809
1028
|
__decorate([Middleware()], DefaultResponseStatusErrorMiddleware.prototype, "handle", null);
|
|
810
1029
|
DefaultResponseStatusErrorMiddleware = __decorate([MiddlewareClass()], DefaultResponseStatusErrorMiddleware);
|
|
811
1030
|
//#endregion
|
|
812
|
-
|
|
1031
|
+
//#region src/middleware/default/openapi/index.ts
|
|
1032
|
+
let DefaultOpenApiMiddleware = class DefaultOpenApiMiddleware {
|
|
1033
|
+
handle(_request, _response, _next) {
|
|
1034
|
+
return ResponseEntity.ok().body(generateOpenApiSpec());
|
|
1035
|
+
}
|
|
1036
|
+
};
|
|
1037
|
+
__decorate([GET(_settings.doc.openApiPath)], DefaultOpenApiMiddleware.prototype, "handle", null);
|
|
1038
|
+
DefaultOpenApiMiddleware = __decorate([MiddlewareClass()], DefaultOpenApiMiddleware);
|
|
1039
|
+
//#endregion
|
|
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
|
+
*/
|
|
1057
|
+
let Serve = class Serve {
|
|
1058
|
+
handlers = swagger.serve;
|
|
1059
|
+
handle(request, response, next) {
|
|
1060
|
+
return Sapling.chainHandlers(this.handlers, request, response, next);
|
|
1061
|
+
}
|
|
1062
|
+
};
|
|
1063
|
+
__decorate([Middleware(_settings.doc.swaggerPath)], Serve.prototype, "handle", null);
|
|
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
|
+
*/
|
|
1081
|
+
let Setup = class Setup {
|
|
1082
|
+
handler;
|
|
1083
|
+
constructor() {
|
|
1084
|
+
this.handler = swagger.setup(null, { swaggerOptions: { url: _settings.doc.openApiPath } });
|
|
1085
|
+
}
|
|
1086
|
+
handle(request, response, next) {
|
|
1087
|
+
return this.handler(request, response, next);
|
|
1088
|
+
}
|
|
1089
|
+
};
|
|
1090
|
+
__decorate([Middleware(_settings.doc.swaggerPath)], Setup.prototype, "handle", null);
|
|
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
|
+
*/
|
|
1108
|
+
const DefaultSwaggerMiddleware = {
|
|
1109
|
+
Serve,
|
|
1110
|
+
Setup
|
|
1111
|
+
};
|
|
1112
|
+
//#endregion
|
|
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 };
|