@tahminator/sapling 2.0.3 → 2.0.5-beta.23c37926
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 +94 -4
- package/dist/index.cjs +383 -142
- package/dist/index.d.cts +503 -13
- package/dist/index.d.mts +503 -13
- package/dist/index.mjs +365 -143
- 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.
|
|
@@ -238,9 +239,13 @@ var ParserError = class ParserError extends ResponseStatusError {
|
|
|
238
239
|
};
|
|
239
240
|
//#endregion
|
|
240
241
|
//#region src/helper/sapling.ts
|
|
241
|
-
const
|
|
242
|
+
const _settings = {
|
|
242
243
|
serialize: JSON.stringify,
|
|
243
|
-
deserialize: JSON.parse
|
|
244
|
+
deserialize: JSON.parse,
|
|
245
|
+
doc: {
|
|
246
|
+
openApiPath: "/openapi.json",
|
|
247
|
+
swaggerPath: "/swagger.html"
|
|
248
|
+
}
|
|
244
249
|
};
|
|
245
250
|
/**
|
|
246
251
|
* Collection of utility functions which are essential for Sapling to function.
|
|
@@ -319,13 +324,13 @@ var Sapling = class Sapling {
|
|
|
319
324
|
* @defaultValue `JSON.stringify`
|
|
320
325
|
*/
|
|
321
326
|
static serialize(value) {
|
|
322
|
-
return
|
|
327
|
+
return _settings.serialize(value);
|
|
323
328
|
}
|
|
324
329
|
/**
|
|
325
330
|
* Replace the function used for `serialize`.
|
|
326
331
|
*/
|
|
327
332
|
static setSerializeFn(fn) {
|
|
328
|
-
|
|
333
|
+
_settings.serialize = fn;
|
|
329
334
|
}
|
|
330
335
|
/**
|
|
331
336
|
* De-serialize a JSON string back to a JavaScript object.
|
|
@@ -337,16 +342,321 @@ var Sapling = class Sapling {
|
|
|
337
342
|
* @defaultValue `JSON.parse`
|
|
338
343
|
*/
|
|
339
344
|
static deserialize(value) {
|
|
340
|
-
return
|
|
345
|
+
return _settings.deserialize(value);
|
|
341
346
|
}
|
|
342
347
|
/**
|
|
343
348
|
* Replace the function used for `deserialize`
|
|
344
349
|
*/
|
|
345
350
|
static setDeserializeFn(fn) {
|
|
346
|
-
|
|
351
|
+
_settings.deserialize = fn;
|
|
352
|
+
}
|
|
353
|
+
static setOpenApiPath(path) {
|
|
354
|
+
_settings.doc.openApiPath = path;
|
|
355
|
+
}
|
|
356
|
+
static setSwaggerPath(path) {
|
|
357
|
+
_settings.doc.swaggerPath = path;
|
|
347
358
|
}
|
|
348
359
|
};
|
|
349
360
|
//#endregion
|
|
361
|
+
//#region src/annotation/request.ts
|
|
362
|
+
const _requestSchemaStore = /* @__PURE__ */ new WeakMap();
|
|
363
|
+
/**
|
|
364
|
+
* Apply to a route method to have `request.body` be parsed by `schema`.
|
|
365
|
+
*
|
|
366
|
+
* This annotation will parse `request.body` & then override `request.body`.
|
|
367
|
+
* You can then just simply cast `request.body` for your use
|
|
368
|
+
*
|
|
369
|
+
* @example
|
|
370
|
+
* ```ts
|
|
371
|
+
* const CREATE_BOOK_REQUEST_BODY_SCHEMA = z.object({
|
|
372
|
+
* name: z.string(),
|
|
373
|
+
* description: z.string().optional(),
|
|
374
|
+
* });
|
|
375
|
+
*
|
|
376
|
+
* ⠀@Controller({ prefix: "/api/book" })
|
|
377
|
+
* class BookController {
|
|
378
|
+
* ⠀@RequestBody(CREATE_BOOK_REQUEST_BODY_SCHEMA)
|
|
379
|
+
* ⠀@POST()
|
|
380
|
+
* public createBook(request: e.Request) {
|
|
381
|
+
* const { name, description } = request.body as unknown as z.infer<
|
|
382
|
+
* typeof CREATE_BOOK_REQUEST_BODY_SCHEMA
|
|
383
|
+
* >;
|
|
384
|
+
* }
|
|
385
|
+
* }
|
|
386
|
+
* ```
|
|
387
|
+
*/
|
|
388
|
+
function RequestBody(schema) {
|
|
389
|
+
return (target, propertyKey) => {
|
|
390
|
+
const ctor = target.constructor;
|
|
391
|
+
const fnName = String(propertyKey);
|
|
392
|
+
_setOnce(_getOrCreateRequestSchemaDefinition(ctor, fnName), "body", schema, fnName);
|
|
393
|
+
};
|
|
394
|
+
}
|
|
395
|
+
/**
|
|
396
|
+
* Apply to a route method to have `request.param` be parsed by `schema`.
|
|
397
|
+
*
|
|
398
|
+
* This annotation will parse `request.param` & then override `request.param`.
|
|
399
|
+
* You can then just simply cast `request.param` for your use
|
|
400
|
+
*
|
|
401
|
+
* @example
|
|
402
|
+
* ```ts
|
|
403
|
+
* const GET_BOOK_REQUEST_PARAM_SCHEMA = z.object({
|
|
404
|
+
* bookId: z.string(),
|
|
405
|
+
* });
|
|
406
|
+
*
|
|
407
|
+
* ⠀@Controller({ prefix: "/api/book" })
|
|
408
|
+
* class BookController {
|
|
409
|
+
* ⠀@RequestParam(GET_BOOK_REQUEST_PARAM_SCHEMA)
|
|
410
|
+
* ⠀@GET("/:bookId")
|
|
411
|
+
* public getBook(request: e.Request) {
|
|
412
|
+
* const { bookId } = request.param as unknown as z.infer<
|
|
413
|
+
* typeof GET_BOOK_REQUEST_PARAM_SCHEMA
|
|
414
|
+
* >;
|
|
415
|
+
* }
|
|
416
|
+
* }
|
|
417
|
+
* ```
|
|
418
|
+
*/
|
|
419
|
+
function RequestParam(schema) {
|
|
420
|
+
return (target, propertyKey) => {
|
|
421
|
+
const ctor = target.constructor;
|
|
422
|
+
const fnName = String(propertyKey);
|
|
423
|
+
_setOnce(_getOrCreateRequestSchemaDefinition(ctor, fnName), "param", schema, fnName);
|
|
424
|
+
};
|
|
425
|
+
}
|
|
426
|
+
/**
|
|
427
|
+
* Apply to a route method to have `request.query` be parsed by `schema`.
|
|
428
|
+
*
|
|
429
|
+
* This annotation will parse `request.query` & then override `request.query`.
|
|
430
|
+
* You can then just simply cast `request.query` for your use
|
|
431
|
+
*
|
|
432
|
+
* @example
|
|
433
|
+
* ```ts
|
|
434
|
+
* const LIST_BOOKS_REQUEST_QUERY_SCHEMA = z.object({
|
|
435
|
+
* sort: z.enum(["name", "createdAt"]).optional(),
|
|
436
|
+
* q: z.string().optional(),
|
|
437
|
+
* });
|
|
438
|
+
*
|
|
439
|
+
* ⠀@Controller({ prefix: "/api/book" })
|
|
440
|
+
* class BookController {
|
|
441
|
+
* ⠀@RequestQuery(LIST_BOOKS_REQUEST_QUERY_SCHEMA)
|
|
442
|
+
* ⠀@GET()
|
|
443
|
+
* public listBooks(request: e.Request) {
|
|
444
|
+
* const { sort, q } = request.query as unknown as z.infer<
|
|
445
|
+
* typeof LIST_BOOKS_REQUEST_QUERY_SCHEMA
|
|
446
|
+
* >;
|
|
447
|
+
* }
|
|
448
|
+
* }
|
|
449
|
+
* ```
|
|
450
|
+
*/
|
|
451
|
+
function RequestQuery(schema) {
|
|
452
|
+
return (target, propertyKey) => {
|
|
453
|
+
const ctor = target.constructor;
|
|
454
|
+
const fnName = String(propertyKey);
|
|
455
|
+
_setOnce(_getOrCreateRequestSchemaDefinition(ctor, fnName), "query", schema, fnName);
|
|
456
|
+
};
|
|
457
|
+
}
|
|
458
|
+
function _getOrCreateRequestSchemaDefinition(ctor, fnName) {
|
|
459
|
+
const byFn = (() => {
|
|
460
|
+
const fn = _requestSchemaStore.get(ctor);
|
|
461
|
+
if (fn) return fn;
|
|
462
|
+
const newFn = /* @__PURE__ */ new Map();
|
|
463
|
+
_requestSchemaStore.set(ctor, newFn);
|
|
464
|
+
return newFn;
|
|
465
|
+
})();
|
|
466
|
+
const existing = byFn.get(fnName);
|
|
467
|
+
if (existing) return existing;
|
|
468
|
+
const created = {};
|
|
469
|
+
byFn.set(fnName, created);
|
|
470
|
+
return created;
|
|
471
|
+
}
|
|
472
|
+
function _setOnce(def, key, schema, fnName) {
|
|
473
|
+
if (def[key]) throw new Error(`Duplicate request schema for "${String(key)}" on method "${fnName}"`);
|
|
474
|
+
def[key] = schema;
|
|
475
|
+
}
|
|
476
|
+
function _getRequestSchemas(ctor, fnName) {
|
|
477
|
+
return _requestSchemaStore.get(ctor)?.get(fnName);
|
|
478
|
+
}
|
|
479
|
+
async function _parseOrThrow(schema, input, kind) {
|
|
480
|
+
const result = await schema["~standard"].validate(input);
|
|
481
|
+
if (result.issues) {
|
|
482
|
+
console.debug(`Failed to parse a schema`);
|
|
483
|
+
throw new ParserError(kind, result.issues, schema["~standard"].vendor);
|
|
484
|
+
}
|
|
485
|
+
return result.value;
|
|
486
|
+
}
|
|
487
|
+
//#endregion
|
|
488
|
+
//#region src/annotation/route.ts
|
|
489
|
+
const _routeStore = /* @__PURE__ */ new WeakMap();
|
|
490
|
+
/**
|
|
491
|
+
* Custom annotation that will store all routes inside of a map,
|
|
492
|
+
* which can then be used to initialize all the routes to the router.
|
|
493
|
+
*/
|
|
494
|
+
function _Route({ method, path = "" }) {
|
|
495
|
+
return (target, propertyKey) => {
|
|
496
|
+
const ctor = target.constructor;
|
|
497
|
+
const list = _routeStore.get(ctor) ?? [];
|
|
498
|
+
list.push({
|
|
499
|
+
method,
|
|
500
|
+
path: path ?? "",
|
|
501
|
+
fnName: String(propertyKey)
|
|
502
|
+
});
|
|
503
|
+
_routeStore.set(ctor, list);
|
|
504
|
+
};
|
|
505
|
+
}
|
|
506
|
+
/**
|
|
507
|
+
* Register GET route on the given path (default "") for the given controller.
|
|
508
|
+
*/
|
|
509
|
+
const GET = (path = "") => _Route({
|
|
510
|
+
method: "GET",
|
|
511
|
+
path
|
|
512
|
+
});
|
|
513
|
+
/**
|
|
514
|
+
* Register POST route on the given path (default "") for the given controller.
|
|
515
|
+
*/
|
|
516
|
+
const POST = (path = "") => _Route({
|
|
517
|
+
method: "POST",
|
|
518
|
+
path
|
|
519
|
+
});
|
|
520
|
+
/**
|
|
521
|
+
* Register PUT route on the given path (default "") for the given controller.
|
|
522
|
+
*/
|
|
523
|
+
const PUT = (path = "") => _Route({
|
|
524
|
+
method: "PUT",
|
|
525
|
+
path
|
|
526
|
+
});
|
|
527
|
+
/**
|
|
528
|
+
* Register DELETE route on the given path (default "") for the given controller.
|
|
529
|
+
*/
|
|
530
|
+
const DELETE = (path = "") => _Route({
|
|
531
|
+
method: "DELETE",
|
|
532
|
+
path
|
|
533
|
+
});
|
|
534
|
+
/**
|
|
535
|
+
* Register OPTIONS route on the given path (default "") for the given controller.
|
|
536
|
+
*/
|
|
537
|
+
const OPTIONS = (path = "") => _Route({
|
|
538
|
+
method: "OPTIONS",
|
|
539
|
+
path
|
|
540
|
+
});
|
|
541
|
+
/**
|
|
542
|
+
* Register PATCH route on the given path (default "") for the given controller.
|
|
543
|
+
*/
|
|
544
|
+
const PATCH = (path = "") => _Route({
|
|
545
|
+
method: "PATCH",
|
|
546
|
+
path
|
|
547
|
+
});
|
|
548
|
+
/**
|
|
549
|
+
* Register HEAD route on the given path (default "") for the given controller.
|
|
550
|
+
*/
|
|
551
|
+
const HEAD = (path = "") => _Route({
|
|
552
|
+
method: "HEAD",
|
|
553
|
+
path
|
|
554
|
+
});
|
|
555
|
+
/**
|
|
556
|
+
* Register a middleware route on the given path (default "") for the given controller.
|
|
557
|
+
*/
|
|
558
|
+
const Middleware = (path = "") => _Route({
|
|
559
|
+
method: "USE",
|
|
560
|
+
path
|
|
561
|
+
});
|
|
562
|
+
/**
|
|
563
|
+
* Given a class constructor, fetch all the routes attached.
|
|
564
|
+
*/
|
|
565
|
+
function _getRoutes(ctor) {
|
|
566
|
+
return _routeStore.get(ctor) ?? [];
|
|
567
|
+
}
|
|
568
|
+
//#endregion
|
|
569
|
+
//#region src/helper/openapi.ts
|
|
570
|
+
var OpenAPIGenerator = class {
|
|
571
|
+
constructor() {
|
|
572
|
+
this.controllers = /* @__PURE__ */ new Set();
|
|
573
|
+
this.config = {
|
|
574
|
+
title: "API",
|
|
575
|
+
version: "1.0.0"
|
|
576
|
+
};
|
|
577
|
+
}
|
|
578
|
+
setConfig(config) {
|
|
579
|
+
this.config = config;
|
|
580
|
+
}
|
|
581
|
+
registerController(controllerClass, prefix) {
|
|
582
|
+
this.controllers.add({
|
|
583
|
+
class: controllerClass,
|
|
584
|
+
prefix
|
|
585
|
+
});
|
|
586
|
+
}
|
|
587
|
+
generateSpec() {
|
|
588
|
+
const config = this.config;
|
|
589
|
+
const paths = {};
|
|
590
|
+
for (const { class: controllerClass, prefix } of this.controllers) {
|
|
591
|
+
const routes = _getRoutes(controllerClass);
|
|
592
|
+
for (const route of routes) {
|
|
593
|
+
if (route.method === "USE") continue;
|
|
594
|
+
const schemas = _getRequestSchemas(controllerClass, route.fnName);
|
|
595
|
+
const fullPath = route.path instanceof RegExp ? route.path.source : prefix + route.path;
|
|
596
|
+
const openApiPath = typeof fullPath === "string" ? fullPath.replace(/:(\w+)/g, "{$1}") : fullPath;
|
|
597
|
+
if (!paths[openApiPath]) paths[openApiPath] = {};
|
|
598
|
+
const operation = { responses: { "200": { description: "Successful response" } } };
|
|
599
|
+
const parameters = [];
|
|
600
|
+
if (schemas?.param) {
|
|
601
|
+
const paramSchema = this.toJsonSchema(schemas.param);
|
|
602
|
+
if (paramSchema.type === "object" && paramSchema.properties) for (const [name, schema] of Object.entries(paramSchema.properties)) parameters.push({
|
|
603
|
+
name,
|
|
604
|
+
in: "path",
|
|
605
|
+
required: true,
|
|
606
|
+
schema
|
|
607
|
+
});
|
|
608
|
+
}
|
|
609
|
+
if (schemas?.query) {
|
|
610
|
+
const querySchema = this.toJsonSchema(schemas.query);
|
|
611
|
+
if (querySchema.type === "object" && querySchema.properties) for (const [name, schema] of Object.entries(querySchema.properties)) {
|
|
612
|
+
const isRequired = Array.isArray(querySchema.required) && querySchema.required.includes(name);
|
|
613
|
+
parameters.push({
|
|
614
|
+
name,
|
|
615
|
+
in: "query",
|
|
616
|
+
required: isRequired,
|
|
617
|
+
schema
|
|
618
|
+
});
|
|
619
|
+
}
|
|
620
|
+
}
|
|
621
|
+
if (parameters.length > 0) operation.parameters = parameters;
|
|
622
|
+
if (schemas?.body) operation.requestBody = {
|
|
623
|
+
required: true,
|
|
624
|
+
content: { "application/json": { schema: this.toJsonSchema(schemas.body) } }
|
|
625
|
+
};
|
|
626
|
+
const method = route.method.toLowerCase();
|
|
627
|
+
paths[openApiPath][method] = operation;
|
|
628
|
+
}
|
|
629
|
+
}
|
|
630
|
+
return {
|
|
631
|
+
openapi: "3.0.0",
|
|
632
|
+
info: {
|
|
633
|
+
title: config.title,
|
|
634
|
+
version: config.version,
|
|
635
|
+
description: config.description
|
|
636
|
+
},
|
|
637
|
+
paths
|
|
638
|
+
};
|
|
639
|
+
}
|
|
640
|
+
toJsonSchema(schema) {
|
|
641
|
+
try {
|
|
642
|
+
return schema["~standard"].jsonSchema.output({ target: "openapi-3.0" });
|
|
643
|
+
} catch (e) {
|
|
644
|
+
if (e instanceof Error && e.message.includes("Transforms cannot be represented in JSON Schema")) throw new Error(`${e.message}.\nIt appears that you are using z.transform() - it is highly recommended that you use z.codec instead - https://zod.dev/codecs`);
|
|
645
|
+
throw e;
|
|
646
|
+
}
|
|
647
|
+
}
|
|
648
|
+
};
|
|
649
|
+
const openApiGenerator = new OpenAPIGenerator();
|
|
650
|
+
function _registerControllerClass(controllerClass, prefix) {
|
|
651
|
+
openApiGenerator.registerController(controllerClass, prefix);
|
|
652
|
+
}
|
|
653
|
+
function _setOpenApiConfig(config) {
|
|
654
|
+
openApiGenerator.setConfig(config);
|
|
655
|
+
}
|
|
656
|
+
function _generateOpenApiSpec() {
|
|
657
|
+
return openApiGenerator.generateSpec();
|
|
658
|
+
}
|
|
659
|
+
//#endregion
|
|
350
660
|
//#region src/types.ts
|
|
351
661
|
const methodResolve = {
|
|
352
662
|
GET: "get",
|
|
@@ -479,140 +789,6 @@ function _resolve(ctor) {
|
|
|
479
789
|
return _InjectableRegistry.get(ctor);
|
|
480
790
|
}
|
|
481
791
|
//#endregion
|
|
482
|
-
//#region src/annotation/request.ts
|
|
483
|
-
const _requestSchemaStore = /* @__PURE__ */ new WeakMap();
|
|
484
|
-
function _getOrCreateRequestSchemaDefinition(ctor, fnName) {
|
|
485
|
-
const byFn = (() => {
|
|
486
|
-
const fn = _requestSchemaStore.get(ctor);
|
|
487
|
-
if (fn) return fn;
|
|
488
|
-
const newFn = /* @__PURE__ */ new Map();
|
|
489
|
-
_requestSchemaStore.set(ctor, newFn);
|
|
490
|
-
return newFn;
|
|
491
|
-
})();
|
|
492
|
-
const existing = byFn.get(fnName);
|
|
493
|
-
if (existing) return existing;
|
|
494
|
-
const created = {};
|
|
495
|
-
byFn.set(fnName, created);
|
|
496
|
-
return created;
|
|
497
|
-
}
|
|
498
|
-
function _setOnce(def, key, schema, fnName) {
|
|
499
|
-
if (def[key]) throw new Error(`Duplicate request schema for "${String(key)}" on method "${fnName}"`);
|
|
500
|
-
def[key] = schema;
|
|
501
|
-
}
|
|
502
|
-
function RequestBody(schema) {
|
|
503
|
-
return (target, propertyKey) => {
|
|
504
|
-
const ctor = target.constructor;
|
|
505
|
-
const fnName = String(propertyKey);
|
|
506
|
-
_setOnce(_getOrCreateRequestSchemaDefinition(ctor, fnName), "body", schema, fnName);
|
|
507
|
-
};
|
|
508
|
-
}
|
|
509
|
-
function RequestParam(schema) {
|
|
510
|
-
return (target, propertyKey) => {
|
|
511
|
-
const ctor = target.constructor;
|
|
512
|
-
const fnName = String(propertyKey);
|
|
513
|
-
_setOnce(_getOrCreateRequestSchemaDefinition(ctor, fnName), "param", schema, fnName);
|
|
514
|
-
};
|
|
515
|
-
}
|
|
516
|
-
function RequestQuery(schema) {
|
|
517
|
-
return (target, propertyKey) => {
|
|
518
|
-
const ctor = target.constructor;
|
|
519
|
-
const fnName = String(propertyKey);
|
|
520
|
-
_setOnce(_getOrCreateRequestSchemaDefinition(ctor, fnName), "query", schema, fnName);
|
|
521
|
-
};
|
|
522
|
-
}
|
|
523
|
-
function _getRequestSchemas(ctor, fnName) {
|
|
524
|
-
return _requestSchemaStore.get(ctor)?.get(fnName);
|
|
525
|
-
}
|
|
526
|
-
async function _parseOrThrow(schema, input, kind) {
|
|
527
|
-
const result = await schema["~standard"].validate(input);
|
|
528
|
-
if (result.issues) {
|
|
529
|
-
console.debug(`Failed to parse a schema`);
|
|
530
|
-
throw new ParserError(kind, result.issues, schema["~standard"].vendor);
|
|
531
|
-
}
|
|
532
|
-
return result.value;
|
|
533
|
-
}
|
|
534
|
-
//#endregion
|
|
535
|
-
//#region src/annotation/route.ts
|
|
536
|
-
const _routeStore = /* @__PURE__ */ new WeakMap();
|
|
537
|
-
/**
|
|
538
|
-
* Custom annotation that will store all routes inside of a map,
|
|
539
|
-
* which can then be used to initialize all the routes to the router.
|
|
540
|
-
*/
|
|
541
|
-
function _Route({ method, path = "" }) {
|
|
542
|
-
return (target, propertyKey) => {
|
|
543
|
-
const ctor = target.constructor;
|
|
544
|
-
const list = _routeStore.get(ctor) ?? [];
|
|
545
|
-
list.push({
|
|
546
|
-
method,
|
|
547
|
-
path: path ?? "",
|
|
548
|
-
fnName: String(propertyKey)
|
|
549
|
-
});
|
|
550
|
-
_routeStore.set(ctor, list);
|
|
551
|
-
};
|
|
552
|
-
}
|
|
553
|
-
/**
|
|
554
|
-
* Register GET route on the given path (default "") for the given controller.
|
|
555
|
-
*/
|
|
556
|
-
const GET = (path = "") => _Route({
|
|
557
|
-
method: "GET",
|
|
558
|
-
path
|
|
559
|
-
});
|
|
560
|
-
/**
|
|
561
|
-
* Register POST route on the given path (default "") for the given controller.
|
|
562
|
-
*/
|
|
563
|
-
const POST = (path = "") => _Route({
|
|
564
|
-
method: "POST",
|
|
565
|
-
path
|
|
566
|
-
});
|
|
567
|
-
/**
|
|
568
|
-
* Register PUT route on the given path (default "") for the given controller.
|
|
569
|
-
*/
|
|
570
|
-
const PUT = (path = "") => _Route({
|
|
571
|
-
method: "PUT",
|
|
572
|
-
path
|
|
573
|
-
});
|
|
574
|
-
/**
|
|
575
|
-
* Register DELETE route on the given path (default "") for the given controller.
|
|
576
|
-
*/
|
|
577
|
-
const DELETE = (path = "") => _Route({
|
|
578
|
-
method: "DELETE",
|
|
579
|
-
path
|
|
580
|
-
});
|
|
581
|
-
/**
|
|
582
|
-
* Register OPTIONS route on the given path (default "") for the given controller.
|
|
583
|
-
*/
|
|
584
|
-
const OPTIONS = (path = "") => _Route({
|
|
585
|
-
method: "OPTIONS",
|
|
586
|
-
path
|
|
587
|
-
});
|
|
588
|
-
/**
|
|
589
|
-
* Register PATCH route on the given path (default "") for the given controller.
|
|
590
|
-
*/
|
|
591
|
-
const PATCH = (path = "") => _Route({
|
|
592
|
-
method: "PATCH",
|
|
593
|
-
path
|
|
594
|
-
});
|
|
595
|
-
/**
|
|
596
|
-
* Register HEAD route on the given path (default "") for the given controller.
|
|
597
|
-
*/
|
|
598
|
-
const HEAD = (path = "") => _Route({
|
|
599
|
-
method: "HEAD",
|
|
600
|
-
path
|
|
601
|
-
});
|
|
602
|
-
/**
|
|
603
|
-
* Register a middleware route on the given path (default "") for the given controller.
|
|
604
|
-
*/
|
|
605
|
-
const Middleware = (path = "") => _Route({
|
|
606
|
-
method: "USE",
|
|
607
|
-
path
|
|
608
|
-
});
|
|
609
|
-
/**
|
|
610
|
-
* Given a class constructor, fetch all the routes attached.
|
|
611
|
-
*/
|
|
612
|
-
function _getRoutes(ctor) {
|
|
613
|
-
return _routeStore.get(ctor) ?? [];
|
|
614
|
-
}
|
|
615
|
-
//#endregion
|
|
616
792
|
//#region src/annotation/controller.ts
|
|
617
793
|
const _ControllerRegistry = /* @__PURE__ */ new WeakMap();
|
|
618
794
|
/**
|
|
@@ -624,6 +800,7 @@ const _ControllerRegistry = /* @__PURE__ */ new WeakMap();
|
|
|
624
800
|
function Controller({ prefix = "", deps = [] } = {}) {
|
|
625
801
|
return (target) => {
|
|
626
802
|
const targetClass = target;
|
|
803
|
+
_registerControllerClass(target, prefix);
|
|
627
804
|
const router = Router();
|
|
628
805
|
const routes = _getRoutes(target);
|
|
629
806
|
const usedRoutes = /* @__PURE__ */ new Set();
|
|
@@ -715,7 +892,7 @@ function __decorate(decorators, target, key, desc) {
|
|
|
715
892
|
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
716
893
|
}
|
|
717
894
|
//#endregion
|
|
718
|
-
//#region src/middleware/default/base.ts
|
|
895
|
+
//#region src/middleware/default/error/base.ts
|
|
719
896
|
let DefaultBaseErrorMiddleware = class DefaultBaseErrorMiddleware {
|
|
720
897
|
handle(err, _request, _response, _next) {
|
|
721
898
|
console.error("[Error]", err);
|
|
@@ -725,7 +902,17 @@ let DefaultBaseErrorMiddleware = class DefaultBaseErrorMiddleware {
|
|
|
725
902
|
__decorate([Middleware()], DefaultBaseErrorMiddleware.prototype, "handle", null);
|
|
726
903
|
DefaultBaseErrorMiddleware = __decorate([MiddlewareClass()], DefaultBaseErrorMiddleware);
|
|
727
904
|
//#endregion
|
|
728
|
-
//#region src/middleware/default/
|
|
905
|
+
//#region src/middleware/default/error/parse.ts
|
|
906
|
+
let DefaultParserErrorMiddleware = class DefaultParserErrorMiddleware {
|
|
907
|
+
handle(err, _request, _response, next) {
|
|
908
|
+
if (err instanceof ParserError) return ResponseEntity.status(err.status).body({ message: err.message });
|
|
909
|
+
next(err);
|
|
910
|
+
}
|
|
911
|
+
};
|
|
912
|
+
__decorate([Middleware()], DefaultParserErrorMiddleware.prototype, "handle", null);
|
|
913
|
+
DefaultParserErrorMiddleware = __decorate([MiddlewareClass()], DefaultParserErrorMiddleware);
|
|
914
|
+
//#endregion
|
|
915
|
+
//#region src/middleware/default/error/responsestatus.ts
|
|
729
916
|
let DefaultResponseStatusErrorMiddleware = class DefaultResponseStatusErrorMiddleware {
|
|
730
917
|
handle(err, _request, _response, next) {
|
|
731
918
|
if (err instanceof ResponseStatusError) return ResponseEntity.status(err.status).body({ message: err.message });
|
|
@@ -735,4 +922,39 @@ let DefaultResponseStatusErrorMiddleware = class DefaultResponseStatusErrorMiddl
|
|
|
735
922
|
__decorate([Middleware()], DefaultResponseStatusErrorMiddleware.prototype, "handle", null);
|
|
736
923
|
DefaultResponseStatusErrorMiddleware = __decorate([MiddlewareClass()], DefaultResponseStatusErrorMiddleware);
|
|
737
924
|
//#endregion
|
|
738
|
-
|
|
925
|
+
//#region src/middleware/default/openapi/index.ts
|
|
926
|
+
let DefaultOpenApiMiddleware = class DefaultOpenApiMiddleware {
|
|
927
|
+
handle(_request, _response, _next) {
|
|
928
|
+
return ResponseEntity.ok().body(_generateOpenApiSpec());
|
|
929
|
+
}
|
|
930
|
+
};
|
|
931
|
+
__decorate([GET(_settings.doc.openApiPath)], DefaultOpenApiMiddleware.prototype, "handle", null);
|
|
932
|
+
DefaultOpenApiMiddleware = __decorate([MiddlewareClass()], DefaultOpenApiMiddleware);
|
|
933
|
+
//#endregion
|
|
934
|
+
//#region src/middleware/default/swagger/index.ts
|
|
935
|
+
let Serve = class Serve {
|
|
936
|
+
constructor() {
|
|
937
|
+
this.handlers = swagger.serve;
|
|
938
|
+
}
|
|
939
|
+
handle(_request, _response, _next) {
|
|
940
|
+
return this.handlers;
|
|
941
|
+
}
|
|
942
|
+
};
|
|
943
|
+
__decorate([Middleware(_settings.doc.swaggerPath)], Serve.prototype, "handle", null);
|
|
944
|
+
Serve = __decorate([MiddlewareClass()], Serve);
|
|
945
|
+
let Setup = class Setup {
|
|
946
|
+
constructor() {
|
|
947
|
+
this.handler = swagger.setup(void 0, { swaggerOptions: { url: _settings.doc.openApiPath } });
|
|
948
|
+
}
|
|
949
|
+
handle(request, response, next) {
|
|
950
|
+
return this.handler(request, response, next);
|
|
951
|
+
}
|
|
952
|
+
};
|
|
953
|
+
__decorate([Middleware(_settings.doc.swaggerPath)], Setup.prototype, "handle", null);
|
|
954
|
+
Setup = __decorate([MiddlewareClass()], Setup);
|
|
955
|
+
const DefaultSwaggerMiddleware = {
|
|
956
|
+
Serve,
|
|
957
|
+
Setup
|
|
958
|
+
};
|
|
959
|
+
//#endregion
|
|
960
|
+
export { Controller, DELETE, DefaultBaseErrorMiddleware, DefaultOpenApiMiddleware, DefaultParserErrorMiddleware, DefaultResponseStatusErrorMiddleware, DefaultSwaggerMiddleware, GET, HEAD, Html404ErrorPage, HttpStatus, Injectable, Middleware, MiddlewareClass, OPTIONS, PATCH, POST, PUT, ParserError, RedirectView, RequestBody, RequestParam, RequestQuery, ResponseEntity, ResponseEntityBuilder, ResponseStatusError, Sapling, _ControllerRegistry, _InjectableDeps, _InjectableRegistry, _Route, _generateOpenApiSpec, _getRequestSchemas, _getRoutes, _parseOrThrow, _registerControllerClass, _resolve, _setOpenApiConfig, _settings, methodResolve, openApiGenerator };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tahminator/sapling",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.5-beta.23c37926",
|
|
4
4
|
"author": "Tahmid Ahmed",
|
|
5
5
|
"description": "A library to help you write cleaner Express.js code",
|
|
6
6
|
"repository": {
|
|
@@ -44,12 +44,14 @@
|
|
|
44
44
|
"@standard-schema/spec": "^1.1.0",
|
|
45
45
|
"@types/express": "^5",
|
|
46
46
|
"@types/supertest": "^7.2.0",
|
|
47
|
+
"@types/swagger-ui-express": "^4.1.8",
|
|
47
48
|
"@vitest/coverage-istanbul": "^4.1.2",
|
|
48
49
|
"eslint": "^10.1.0",
|
|
49
50
|
"eslint-plugin-perfectionist": "^5.7.0",
|
|
50
51
|
"globals": "^17.4.0",
|
|
51
52
|
"jiti": "^2.6.1",
|
|
52
53
|
"jsdom": "^29.0.1",
|
|
54
|
+
"openapi-types": "12.1.3",
|
|
53
55
|
"prettier": "^3.8.1",
|
|
54
56
|
"superjson": "^2.2.6",
|
|
55
57
|
"supertest": "^7.2.2",
|
|
@@ -60,6 +62,10 @@
|
|
|
60
62
|
"zod": "^4.4.3"
|
|
61
63
|
},
|
|
62
64
|
"inlinedDependencies": {
|
|
63
|
-
"@standard-schema/spec": "1.1.0"
|
|
65
|
+
"@standard-schema/spec": "1.1.0",
|
|
66
|
+
"openapi-types": "12.1.3"
|
|
67
|
+
},
|
|
68
|
+
"dependencies": {
|
|
69
|
+
"swagger-ui-express": "^5.0.1"
|
|
64
70
|
}
|
|
65
71
|
}
|