@tsed/openapi-utils 8.3.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.
Files changed (59) hide show
  1. package/lib/esm/decorators/docs.js +14 -0
  2. package/lib/esm/decorators/docs.js.map +1 -0
  3. package/lib/esm/index.js +15 -0
  4. package/lib/esm/index.js.map +1 -0
  5. package/lib/esm/interfaces/OpenApiSettings.js +2 -0
  6. package/lib/esm/interfaces/OpenApiSettings.js.map +1 -0
  7. package/lib/esm/middlewares/cssMiddleware.js +20 -0
  8. package/lib/esm/middlewares/cssMiddleware.js.map +1 -0
  9. package/lib/esm/middlewares/jsMiddleware.js +15 -0
  10. package/lib/esm/middlewares/jsMiddleware.js.map +1 -0
  11. package/lib/esm/middlewares/openApiMiddleware.js +13 -0
  12. package/lib/esm/middlewares/openApiMiddleware.js.map +1 -0
  13. package/lib/esm/middlewares/redirectMiddleware.js +14 -0
  14. package/lib/esm/middlewares/redirectMiddleware.js.map +1 -0
  15. package/lib/esm/services/OpenAPIBaseModule.js +67 -0
  16. package/lib/esm/services/OpenAPIBaseModule.js.map +1 -0
  17. package/lib/esm/services/OpenAPIService.js +46 -0
  18. package/lib/esm/services/OpenAPIService.js.map +1 -0
  19. package/lib/esm/utils/includeRoute.js +16 -0
  20. package/lib/esm/utils/includeRoute.js.map +1 -0
  21. package/lib/esm/utils/matchPath.js +5 -0
  22. package/lib/esm/utils/matchPath.js.map +1 -0
  23. package/lib/esm/utils/readSpec.js +14 -0
  24. package/lib/esm/utils/readSpec.js.map +1 -0
  25. package/lib/types/decorators/docs.d.ts +10 -0
  26. package/lib/types/index.d.ts +14 -0
  27. package/lib/types/interfaces/OpenApiSettings.d.ts +96 -0
  28. package/lib/types/middlewares/cssMiddleware.d.ts +5 -0
  29. package/lib/types/middlewares/jsMiddleware.d.ts +5 -0
  30. package/lib/types/middlewares/openApiMiddleware.d.ts +5 -0
  31. package/lib/types/middlewares/redirectMiddleware.d.ts +5 -0
  32. package/lib/types/services/OpenAPIBaseModule.d.ts +18 -0
  33. package/lib/types/services/OpenAPIService.d.ts +15 -0
  34. package/lib/types/utils/includeRoute.d.ts +3 -0
  35. package/lib/types/utils/matchPath.d.ts +1 -0
  36. package/lib/types/utils/readSpec.d.ts +1 -0
  37. package/package.json +59 -0
  38. package/readme.md +257 -0
  39. package/src/decorators/docs.ts +14 -0
  40. package/src/index.ts +14 -0
  41. package/src/interfaces/OpenApiSettings.ts +98 -0
  42. package/src/middlewares/cssMiddleware.spec.ts +25 -0
  43. package/src/middlewares/cssMiddleware.ts +22 -0
  44. package/src/middlewares/jsMiddleware.spec.ts +25 -0
  45. package/src/middlewares/jsMiddleware.ts +16 -0
  46. package/src/middlewares/openApiMiddleware.spec.ts +31 -0
  47. package/src/middlewares/openApiMiddleware.ts +16 -0
  48. package/src/middlewares/redirectMiddleware.spec.ts +32 -0
  49. package/src/middlewares/redirectMiddleware.ts +15 -0
  50. package/src/services/OpenAPIBaseModule.ts +91 -0
  51. package/src/services/OpenAPIService.spec.ts +35 -0
  52. package/src/services/OpenAPIService.ts +65 -0
  53. package/src/services/__snapshots__/OpenAPIService.spec.ts.snap +11 -0
  54. package/src/services/__snapshots__/SwaggerService.spec.ts.snap +11 -0
  55. package/src/utils/includeRoute.spec.ts +111 -0
  56. package/src/utils/includeRoute.ts +22 -0
  57. package/src/utils/matchPath.ts +5 -0
  58. package/src/utils/readSpec.ts +15 -0
  59. package/vitest.config.mts +21 -0
@@ -0,0 +1,14 @@
1
+ import { StoreMerge } from "@tsed/core";
2
+ /**
3
+ * Set the type documentation for this class.
4
+ *
5
+ * See [swagger documentation](/tutorials/swagger.html)
6
+ *
7
+ * @decorator
8
+ * @swagger
9
+ * @param docs
10
+ */
11
+ export function Docs(...docs) {
12
+ return StoreMerge("docs", docs);
13
+ }
14
+ //# sourceMappingURL=docs.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"docs.js","sourceRoot":"","sources":["../../../src/decorators/docs.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,UAAU,EAAC,MAAM,YAAY,CAAC;AAEtC;;;;;;;;GAQG;AACH,MAAM,UAAU,IAAI,CAAC,GAAG,IAAc;IACpC,OAAO,UAAU,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;AAClC,CAAC"}
@@ -0,0 +1,15 @@
1
+ /**
2
+ * @file Automatically generated by @tsed/barrels.
3
+ */
4
+ export * from "./decorators/docs.js";
5
+ export * from "./interfaces/OpenApiSettings.js";
6
+ export * from "./middlewares/cssMiddleware.js";
7
+ export * from "./middlewares/jsMiddleware.js";
8
+ export * from "./middlewares/openApiMiddleware.js";
9
+ export * from "./middlewares/redirectMiddleware.js";
10
+ export * from "./services/OpenAPIBaseModule.js";
11
+ export * from "./services/OpenAPIService.js";
12
+ export * from "./utils/includeRoute.js";
13
+ export * from "./utils/matchPath.js";
14
+ export * from "./utils/readSpec.js";
15
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,cAAc,sBAAsB,CAAC;AACrC,cAAc,iCAAiC,CAAC;AAChD,cAAc,gCAAgC,CAAC;AAC/C,cAAc,+BAA+B,CAAC;AAC9C,cAAc,oCAAoC,CAAC;AACnD,cAAc,qCAAqC,CAAC;AACpD,cAAc,iCAAiC,CAAC;AAChD,cAAc,8BAA8B,CAAC;AAC7C,cAAc,yBAAyB,CAAC;AACxC,cAAc,sBAAsB,CAAC;AACrC,cAAc,qBAAqB,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=OpenApiSettings.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"OpenApiSettings.js","sourceRoot":"","sources":["../../../src/interfaces/OpenApiSettings.ts"],"names":[],"mappings":""}
@@ -0,0 +1,20 @@
1
+ import { context } from "@tsed/di";
2
+ import Fs from "fs";
3
+ import { resolve } from "path";
4
+ /**
5
+ * Expose a css file.
6
+ * @param path
7
+ */
8
+ export function cssMiddleware(path) {
9
+ return () => {
10
+ const ctx = context();
11
+ const content = Fs.readFileSync(resolve(path), { encoding: "utf8" });
12
+ ctx.response
13
+ .setHeaders({
14
+ "Content-Type": "text/css"
15
+ })
16
+ .status(200)
17
+ .body(content);
18
+ };
19
+ }
20
+ //# sourceMappingURL=cssMiddleware.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cssMiddleware.js","sourceRoot":"","sources":["../../../src/middlewares/cssMiddleware.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,OAAO,EAAC,MAAM,UAAU,CAAC;AACjC,OAAO,EAAE,MAAM,IAAI,CAAC;AACpB,OAAO,EAAC,OAAO,EAAC,MAAM,MAAM,CAAC;AAE7B;;;GAGG;AACH,MAAM,UAAU,aAAa,CAAC,IAAY;IACxC,OAAO,GAAG,EAAE;QACV,MAAM,GAAG,GAAG,OAAO,EAAE,CAAC;QAEtB,MAAM,OAAO,GAAG,EAAE,CAAC,YAAY,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,EAAC,QAAQ,EAAE,MAAM,EAAC,CAAC,CAAC;QAEnE,GAAG,CAAC,QAAQ;aACT,UAAU,CAAC;YACV,cAAc,EAAE,UAAU;SAC3B,CAAC;aACD,MAAM,CAAC,GAAG,CAAC;aACX,IAAI,CAAC,OAAO,CAAC,CAAC;IACnB,CAAC,CAAC;AACJ,CAAC"}
@@ -0,0 +1,15 @@
1
+ import { context } from "@tsed/di";
2
+ import Fs from "fs";
3
+ import { resolve } from "path";
4
+ /**
5
+ * Expose a js file.
6
+ * @param path
7
+ */
8
+ export function jsMiddleware(path) {
9
+ return () => {
10
+ const ctx = context();
11
+ const content = Fs.readFileSync(resolve(path), { encoding: "utf8" });
12
+ ctx.response.setHeaders({ "Content-Type": "application/javascript" }).status(200).body(content);
13
+ };
14
+ }
15
+ //# sourceMappingURL=jsMiddleware.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"jsMiddleware.js","sourceRoot":"","sources":["../../../src/middlewares/jsMiddleware.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,OAAO,EAAC,MAAM,UAAU,CAAC;AACjC,OAAO,EAAE,MAAM,IAAI,CAAC;AACpB,OAAO,EAAC,OAAO,EAAC,MAAM,MAAM,CAAC;AAE7B;;;GAGG;AACH,MAAM,UAAU,YAAY,CAAC,IAAY;IACvC,OAAO,GAAG,EAAE;QACV,MAAM,GAAG,GAAG,OAAO,EAAE,CAAC;QAEtB,MAAM,OAAO,GAAG,EAAE,CAAC,YAAY,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,EAAC,QAAQ,EAAE,MAAM,EAAC,CAAC,CAAC;QACnE,GAAG,CAAC,QAAQ,CAAC,UAAU,CAAC,EAAC,cAAc,EAAE,wBAAwB,EAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IAChG,CAAC,CAAC;AACJ,CAAC"}
@@ -0,0 +1,13 @@
1
+ import { context, injector } from "@tsed/di";
2
+ import { OpenAPIService } from "../services/OpenAPIService.js";
3
+ /**
4
+ * Return a middleware to expose the OpenAPI spec.
5
+ */
6
+ export function openApiMiddleware(conf) {
7
+ return async () => {
8
+ const ctx = context();
9
+ const spec = await injector().get(OpenAPIService).getOpenAPISpec(conf);
10
+ ctx.response.status(200).body(spec);
11
+ };
12
+ }
13
+ //# sourceMappingURL=openApiMiddleware.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"openApiMiddleware.js","sourceRoot":"","sources":["../../../src/middlewares/openApiMiddleware.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,OAAO,EAAE,QAAQ,EAAC,MAAM,UAAU,CAAC;AAG3C,OAAO,EAAC,cAAc,EAAC,MAAM,+BAA+B,CAAC;AAE7D;;GAEG;AACH,MAAM,UAAU,iBAAiB,CAAC,IAAqB;IACrD,OAAO,KAAK,IAAI,EAAE;QAChB,MAAM,GAAG,GAAG,OAAO,EAAE,CAAC;QACtB,MAAM,IAAI,GAAG,MAAM,QAAQ,EAAE,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC,cAAc,CAAC,IAAI,CAAC,CAAC;QAEvE,GAAG,CAAC,QAAQ,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACtC,CAAC,CAAC;AACJ,CAAC"}
@@ -0,0 +1,14 @@
1
+ import { context } from "@tsed/di";
2
+ /**
3
+ * Redirect to the same path with a trailing slash
4
+ * @param path
5
+ */
6
+ export function redirectMiddleware(path) {
7
+ return () => {
8
+ const ctx = context();
9
+ if (ctx.request.method?.toUpperCase() === "GET" && ctx.request.url === path && !ctx.request.url.match(/\/$/)) {
10
+ ctx.response.redirect(302, `${path}/`);
11
+ }
12
+ };
13
+ }
14
+ //# sourceMappingURL=redirectMiddleware.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"redirectMiddleware.js","sourceRoot":"","sources":["../../../src/middlewares/redirectMiddleware.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,OAAO,EAAC,MAAM,UAAU,CAAC;AAEjC;;;GAGG;AACH,MAAM,UAAU,kBAAkB,CAAC,IAAY;IAC7C,OAAO,GAAG,EAAE;QACV,MAAM,GAAG,GAAG,OAAO,EAAE,CAAC;QAEtB,IAAI,GAAG,CAAC,OAAO,CAAC,MAAM,EAAE,WAAW,EAAE,KAAK,KAAK,IAAI,GAAG,CAAC,OAAO,CAAC,GAAG,KAAK,IAAI,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC,EAAE,CAAC;YAC7G,GAAG,CAAC,QAAQ,CAAC,QAAQ,CAAC,GAAG,EAAE,GAAG,IAAI,GAAG,CAAC,CAAC;QACzC,CAAC;IACH,CAAC,CAAC;AACJ,CAAC"}
@@ -0,0 +1,67 @@
1
+ import { basename } from "node:path";
2
+ import { configuration, constant, inject, logger } from "@tsed/di";
3
+ import { normalizePath } from "@tsed/normalize-path";
4
+ import { application } from "@tsed/platform-http";
5
+ import { PlatformRouter, useContextHandler } from "@tsed/platform-router";
6
+ import { cssMiddleware } from "../middlewares/cssMiddleware.js";
7
+ import { jsMiddleware } from "../middlewares/jsMiddleware.js";
8
+ import { openApiMiddleware } from "../middlewares/openApiMiddleware.js";
9
+ import { redirectMiddleware } from "../middlewares/redirectMiddleware.js";
10
+ import { OpenAPIService } from "../services/OpenAPIService.js";
11
+ export class OpenAPIBaseModule {
12
+ constructor() {
13
+ this.openAPIService = inject(OpenAPIService);
14
+ this.env = constant("env");
15
+ this.disableRoutesSummary = constant("logger.disableRoutesSummary");
16
+ this.loaded = false;
17
+ }
18
+ $onRoutesInit() {
19
+ if (this.loaded) {
20
+ return;
21
+ }
22
+ this.settings.forEach((conf) => {
23
+ const { path = "/" } = conf;
24
+ application().use(path, useContextHandler(redirectMiddleware(path)));
25
+ application().use(path, this.createRouter(conf));
26
+ });
27
+ this.loaded = true;
28
+ }
29
+ $onReady() {
30
+ // istanbul ignore next
31
+ if (configuration().getBestHost && !this.disableRoutesSummary) {
32
+ const host = configuration().getBestHost();
33
+ const url = host.toString();
34
+ const displayLog = (conf) => {
35
+ const { path, fileName, doc, specVersion } = conf;
36
+ logger().info(`[${doc || "default"}] ${specVersion === "2.0" ? "Swagger" : "OpenAPI"} JSON is available on ${url}${normalizePath(path, fileName)}`);
37
+ logger().info(`[${doc || "default"}] ${this.name} UI is available on ${url}${path}/`);
38
+ };
39
+ this.settings.forEach((conf) => {
40
+ displayLog(conf);
41
+ });
42
+ }
43
+ this.generateSpecFiles();
44
+ }
45
+ generateSpecFiles() {
46
+ return Promise.all(this.settings.map(async (conf) => {
47
+ this.openAPIService.writeOpenAPISpec(conf);
48
+ }));
49
+ }
50
+ createRouter(conf) {
51
+ const { disableSpec, fileName, cssPath, jsPath, viewPath } = conf;
52
+ const router = new PlatformRouter();
53
+ if (!disableSpec) {
54
+ router.get(normalizePath("/", fileName), useContextHandler(openApiMiddleware(conf)));
55
+ }
56
+ if (viewPath) {
57
+ if (cssPath) {
58
+ router.get(`/${basename(cssPath)}`, useContextHandler(cssMiddleware(cssPath)));
59
+ }
60
+ if (jsPath) {
61
+ router.get(`/${basename(jsPath)}`, useContextHandler(jsMiddleware(jsPath)));
62
+ }
63
+ }
64
+ return router;
65
+ }
66
+ }
67
+ //# sourceMappingURL=OpenAPIBaseModule.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"OpenAPIBaseModule.js","sourceRoot":"","sources":["../../../src/services/OpenAPIBaseModule.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,QAAQ,EAAC,MAAM,WAAW,CAAC;AAGnC,OAAO,EAAC,aAAa,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAC,MAAM,UAAU,CAAC;AACjE,OAAO,EAAC,aAAa,EAAC,MAAM,sBAAsB,CAAC;AACnD,OAAO,EAAC,WAAW,EAAwB,MAAM,qBAAqB,CAAC;AACvE,OAAO,EAAC,cAAc,EAAE,iBAAiB,EAAC,MAAM,uBAAuB,CAAC;AAGxE,OAAO,EAAC,aAAa,EAAC,MAAM,iCAAiC,CAAC;AAC9D,OAAO,EAAC,YAAY,EAAC,MAAM,gCAAgC,CAAC;AAC5D,OAAO,EAAC,iBAAiB,EAAC,MAAM,qCAAqC,CAAC;AACtE,OAAO,EAAC,kBAAkB,EAAC,MAAM,sCAAsC,CAAC;AACxE,OAAO,EAAC,cAAc,EAAC,MAAM,+BAA+B,CAAC;AAE7D,MAAM,OAAgB,iBAAiB;IAAvC;QAIY,mBAAc,GAAG,MAAM,CAAC,cAAc,CAAC,CAAC;QACxC,QAAG,GAAG,QAAQ,CAAM,KAAK,CAAC,CAAC;QAC3B,yBAAoB,GAAG,QAAQ,CAAU,6BAA6B,CAAC,CAAC;QAC1E,WAAM,GAAG,KAAK,CAAC;IAoEzB,CAAC;IAlEC,aAAa;QACX,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC;YAChB,OAAO;QACT,CAAC;QAED,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,IAAqB,EAAE,EAAE;YAC9C,MAAM,EAAC,IAAI,GAAG,GAAG,EAAC,GAAG,IAAI,CAAC;YAE1B,WAAW,EAAE,CAAC,GAAG,CAAC,IAAI,EAAE,iBAAiB,CAAC,kBAAkB,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;YACrE,WAAW,EAAE,CAAC,GAAG,CAAC,IAAI,EAAE,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC,CAAC;QACnD,CAAC,CAAC,CAAC;QAEH,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC;IACrB,CAAC;IAED,QAAQ;QACN,uBAAuB;QACvB,IAAI,aAAa,EAAE,CAAC,WAAW,IAAI,CAAC,IAAI,CAAC,oBAAoB,EAAE,CAAC;YAC9D,MAAM,IAAI,GAAG,aAAa,EAAE,CAAC,WAAW,EAAE,CAAC;YAC3C,MAAM,GAAG,GAAG,IAAI,CAAC,QAAQ,EAAE,CAAC;YAE5B,MAAM,UAAU,GAAG,CAAC,IAAqB,EAAE,EAAE;gBAC3C,MAAM,EAAC,IAAI,EAAE,QAAQ,EAAE,GAAG,EAAE,WAAW,EAAC,GAAG,IAAI,CAAC;gBAEhD,MAAM,EAAE,CAAC,IAAI,CACX,IAAI,GAAG,IAAI,SAAS,KAAK,WAAW,KAAK,KAAK,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,SAAS,yBAAyB,GAAG,GAAG,aAAa,CAAC,IAAI,EAAE,QAAS,CAAC,EAAE,CACtI,CAAC;gBACF,MAAM,EAAE,CAAC,IAAI,CAAC,IAAI,GAAG,IAAI,SAAS,KAAK,IAAI,CAAC,IAAI,uBAAuB,GAAG,GAAG,IAAI,GAAG,CAAC,CAAC;YACxF,CAAC,CAAC;YAEF,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,IAAI,EAAE,EAAE;gBAC7B,UAAU,CAAC,IAAI,CAAC,CAAC;YACnB,CAAC,CAAC,CAAC;QACL,CAAC;QAED,IAAI,CAAC,iBAAiB,EAAE,CAAC;IAC3B,CAAC;IAED,iBAAiB;QACf,OAAO,OAAO,CAAC,GAAG,CAChB,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,KAAK,EAAE,IAAI,EAAE,EAAE;YAC/B,IAAI,CAAC,cAAc,CAAC,gBAAgB,CAAC,IAAI,CAAC,CAAC;QAC7C,CAAC,CAAC,CACH,CAAC;IACJ,CAAC;IAES,YAAY,CAAC,IAAqB;QAC1C,MAAM,EAAC,WAAW,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAC,GAAG,IAAI,CAAC;QAChE,MAAM,MAAM,GAAG,IAAI,cAAc,EAAE,CAAC;QAEpC,IAAI,CAAC,WAAW,EAAE,CAAC;YACjB,MAAM,CAAC,GAAG,CAAC,aAAa,CAAC,GAAG,EAAE,QAAS,CAAC,EAAE,iBAAiB,CAAC,iBAAiB,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QACxF,CAAC;QAED,IAAI,QAAQ,EAAE,CAAC;YACb,IAAI,OAAO,EAAE,CAAC;gBACZ,MAAM,CAAC,GAAG,CAAC,IAAI,QAAQ,CAAC,OAAO,CAAC,EAAE,EAAE,iBAAiB,CAAC,aAAa,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;YACjF,CAAC;YAED,IAAI,MAAM,EAAE,CAAC;gBACX,MAAM,CAAC,GAAG,CAAC,IAAI,QAAQ,CAAC,MAAM,CAAC,EAAE,EAAE,iBAAiB,CAAC,YAAY,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;YAC9E,CAAC;QACH,CAAC;QAED,OAAO,MAAM,CAAC;IAChB,CAAC;CACF"}
@@ -0,0 +1,46 @@
1
+ import { writeFile } from "node:fs/promises";
2
+ import { Env } from "@tsed/core";
3
+ import { constant, inject, injectable } from "@tsed/di";
4
+ import { Platform } from "@tsed/platform-http";
5
+ import { generateSpec } from "@tsed/schema";
6
+ import { includeRoute } from "../utils/includeRoute.js";
7
+ import { readSpec } from "../utils/readSpec.js";
8
+ export class OpenAPIService {
9
+ #specs;
10
+ constructor() {
11
+ this.platform = inject(Platform);
12
+ this.#specs = new Map();
13
+ }
14
+ async getOpenAPISpec(conf) {
15
+ if (!this.#specs.has(conf.path)) {
16
+ const version = constant("version", "1.0.0");
17
+ const acceptMimes = constant("acceptMimes");
18
+ const specPath = conf.specPath;
19
+ const tokens = this.platform
20
+ .getMountedControllers()
21
+ .filter(({ routes, provider }) => [...routes.values()].some((route) => includeRoute(route, provider, conf)))
22
+ .map(({ route, provider }) => ({ token: provider.token, rootPath: route }));
23
+ const spec = generateSpec({
24
+ tokens,
25
+ ...conf,
26
+ fileSpec: specPath ? await readSpec(specPath) : {},
27
+ version,
28
+ acceptMimes
29
+ });
30
+ this.#specs.set(conf.path, spec);
31
+ }
32
+ return this.#specs.get(conf.path);
33
+ }
34
+ async writeOpenAPISpec(conf) {
35
+ const { outFile } = conf;
36
+ const env = constant("env");
37
+ if (env === Env.PROD || outFile) {
38
+ const spec = await this.getOpenAPISpec(conf);
39
+ if (outFile) {
40
+ return writeFile(outFile, JSON.stringify(spec, null, 2), { encoding: "utf8" });
41
+ }
42
+ }
43
+ }
44
+ }
45
+ injectable(OpenAPIService);
46
+ //# sourceMappingURL=OpenAPIService.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"OpenAPIService.js","sourceRoot":"","sources":["../../../src/services/OpenAPIService.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,SAAS,EAAC,MAAM,kBAAkB,CAAC;AAE3C,OAAO,EAAC,GAAG,EAAY,MAAM,YAAY,CAAC;AAC1C,OAAO,EAAC,QAAQ,EAAE,MAAM,EAAE,UAAU,EAAC,MAAM,UAAU,CAAC;AAEtD,OAAO,EAAC,QAAQ,EAAC,MAAM,qBAAqB,CAAC;AAC7C,OAAO,EAAC,YAAY,EAAC,MAAM,cAAc,CAAC;AAG1C,OAAO,EAAC,YAAY,EAAC,MAAM,0BAA0B,CAAC;AACtD,OAAO,EAAC,QAAQ,EAAC,MAAM,sBAAsB,CAAC;AAE9C,MAAM,OAAO,cAAc;IAGzB,MAAM,CAAiD;IAEvD;QAJU,aAAQ,GAAG,MAAM,CAAC,QAAQ,CAAC,CAAC;QAEtC,WAAM,GAAuC,IAAI,GAAG,EAAE,CAAC;IAExC,CAAC;IAQT,KAAK,CAAC,cAAc,CAAC,IAAqB;QAC/C,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;YAChC,MAAM,OAAO,GAAG,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAC,CAAC;YAC7C,MAAM,WAAW,GAAG,QAAQ,CAAS,aAAa,CAAC,CAAC;YACpD,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,CAAC;YAE/B,MAAM,MAAM,GAAG,IAAI,CAAC,QAAQ;iBACzB,qBAAqB,EAAE;iBACvB,MAAM,CAAC,CAAC,EAAC,MAAM,EAAE,QAAQ,EAAC,EAAE,EAAE,CAAC,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,YAAY,CAAC,KAAK,EAAE,QAAQ,EAAE,IAAI,CAAC,CAAC,CAAC;iBACzG,GAAG,CAAC,CAAC,EAAC,KAAK,EAAE,QAAQ,EAAC,EAAE,EAAE,CAAC,CAAC,EAAC,KAAK,EAAE,QAAQ,CAAC,KAAa,EAAE,QAAQ,EAAE,KAAK,EAAC,CAAC,CAAC,CAAC;YAElF,MAAM,IAAI,GAAG,YAAY,CAAC;gBACxB,MAAM;gBACN,GAAG,IAAI;gBACP,QAAQ,EAAE,QAAQ,CAAC,CAAC,CAAC,MAAM,QAAQ,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,EAAE;gBAClD,OAAO;gBACP,WAAW;aACZ,CAAC,CAAC;YAEH,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;QACnC,CAAC;QAED,OAAO,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACpC,CAAC;IAED,KAAK,CAAC,gBAAgB,CAAC,IAAqB;QAC1C,MAAM,EAAC,OAAO,EAAC,GAAG,IAAI,CAAC;QACvB,MAAM,GAAG,GAAG,QAAQ,CAAM,KAAK,CAAC,CAAC;QAEjC,IAAI,GAAG,KAAK,GAAG,CAAC,IAAI,IAAI,OAAO,EAAE,CAAC;YAChC,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,cAAc,CAAC,IAAI,CAAC,CAAC;YAE7C,IAAI,OAAO,EAAE,CAAC;gBACZ,OAAO,SAAS,CAAC,OAAO,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,EAAC,QAAQ,EAAE,MAAM,EAAC,CAAC,CAAC;YAC/E,CAAC;QACH,CAAC;IACH,CAAC;CACF;AAED,UAAU,CAAC,cAAc,CAAC,CAAC"}
@@ -0,0 +1,16 @@
1
+ import { matchPath } from "./matchPath.js";
2
+ export function includeRoute(route, provider, conf) {
3
+ const hidden = provider.store.get("hidden");
4
+ const docs = provider.store.get("docs") || [];
5
+ const { doc, pathPatterns } = conf;
6
+ const inDoc = doc && docs.indexOf(doc) > -1;
7
+ const pathPatternMatch = pathPatterns && matchPath(route, pathPatterns);
8
+ if (hidden) {
9
+ return false;
10
+ }
11
+ if (!doc && !pathPatterns) {
12
+ return true;
13
+ }
14
+ return !!(inDoc || pathPatternMatch);
15
+ }
16
+ //# sourceMappingURL=includeRoute.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"includeRoute.js","sourceRoot":"","sources":["../../../src/utils/includeRoute.ts"],"names":[],"mappings":"AAGA,OAAO,EAAC,SAAS,EAAC,MAAM,gBAAgB,CAAC;AAEzC,MAAM,UAAU,YAAY,CAAC,KAAa,EAAE,QAAkB,EAAE,IAAqB;IACnF,MAAM,MAAM,GAAG,QAAQ,CAAC,KAAK,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;IAC5C,MAAM,IAAI,GAAG,QAAQ,CAAC,KAAK,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC;IAC9C,MAAM,EAAC,GAAG,EAAE,YAAY,EAAC,GAAG,IAAI,CAAC;IACjC,MAAM,KAAK,GAAG,GAAG,IAAI,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC;IAC5C,MAAM,gBAAgB,GAAG,YAAY,IAAI,SAAS,CAAC,KAAK,EAAE,YAAY,CAAC,CAAC;IAExE,IAAI,MAAM,EAAE,CAAC;QACX,OAAO,KAAK,CAAC;IACf,CAAC;IAED,IAAI,CAAC,GAAG,IAAI,CAAC,YAAY,EAAE,CAAC;QAC1B,OAAO,IAAI,CAAC;IACd,CAAC;IAED,OAAO,CAAC,CAAC,CAAC,KAAK,IAAI,gBAAgB,CAAC,CAAC;AACvC,CAAC"}
@@ -0,0 +1,5 @@
1
+ import micromatch from "micromatch";
2
+ export function matchPath(path, patterns) {
3
+ return patterns && patterns.length ? micromatch.isMatch(path, patterns) : true;
4
+ }
5
+ //# sourceMappingURL=matchPath.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"matchPath.js","sourceRoot":"","sources":["../../../src/utils/matchPath.ts"],"names":[],"mappings":"AAAA,OAAO,UAAU,MAAM,YAAY,CAAC;AAEpC,MAAM,UAAU,SAAS,CAAC,IAAY,EAAE,QAAmB;IACzD,OAAO,QAAQ,IAAI,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,UAAU,CAAC,OAAO,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;AACjF,CAAC"}
@@ -0,0 +1,14 @@
1
+ export async function readSpec(path) {
2
+ const { readFile } = await import("node:fs/promises");
3
+ const { existsSync } = await import("node:fs");
4
+ if (existsSync(path)) {
5
+ try {
6
+ const response = await readFile(path, { encoding: "utf8" });
7
+ return JSON.parse(response);
8
+ }
9
+ catch (e) { }
10
+ }
11
+ /* istanbul ignore next */
12
+ return {};
13
+ }
14
+ //# sourceMappingURL=readSpec.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"readSpec.js","sourceRoot":"","sources":["../../../src/utils/readSpec.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,KAAK,UAAU,QAAQ,CAAC,IAAY;IACzC,MAAM,EAAC,QAAQ,EAAC,GAAG,MAAM,MAAM,CAAC,kBAAkB,CAAC,CAAC;IACpD,MAAM,EAAC,UAAU,EAAC,GAAG,MAAM,MAAM,CAAC,SAAS,CAAC,CAAC;IAE7C,IAAI,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC;QACrB,IAAI,CAAC;YACH,MAAM,QAAQ,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,EAAC,QAAQ,EAAE,MAAM,EAAC,CAAC,CAAC;YAE1D,OAAO,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;QAC9B,CAAC;QAAC,OAAO,CAAC,EAAE,CAAC,CAAA,CAAC;IAChB,CAAC;IAED,0BAA0B;IAC1B,OAAO,EAAE,CAAC;AACZ,CAAC"}
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Set the type documentation for this class.
3
+ *
4
+ * See [swagger documentation](/tutorials/swagger.html)
5
+ *
6
+ * @decorator
7
+ * @swagger
8
+ * @param docs
9
+ */
10
+ export declare function Docs(...docs: string[]): Function;
@@ -0,0 +1,14 @@
1
+ /**
2
+ * @file Automatically generated by @tsed/barrels.
3
+ */
4
+ export * from "./decorators/docs.js";
5
+ export * from "./interfaces/OpenApiSettings.js";
6
+ export * from "./middlewares/cssMiddleware.js";
7
+ export * from "./middlewares/jsMiddleware.js";
8
+ export * from "./middlewares/openApiMiddleware.js";
9
+ export * from "./middlewares/redirectMiddleware.js";
10
+ export * from "./services/OpenAPIBaseModule.js";
11
+ export * from "./services/OpenAPIService.js";
12
+ export * from "./utils/includeRoute.js";
13
+ export * from "./utils/matchPath.js";
14
+ export * from "./utils/readSpec.js";
@@ -0,0 +1,96 @@
1
+ import { OpenSpec2, OpenSpec3, OpenSpecVersions, OS2Versions, OS3Versions } from "@tsed/openspec";
2
+ export interface SwaggerUIOptions {
3
+ configUrl?: string;
4
+ url?: string;
5
+ urls?: {
6
+ url: string;
7
+ name: string;
8
+ primaryName?: string;
9
+ }[];
10
+ layout?: string;
11
+ validatorUrl?: string;
12
+ oauth?: any;
13
+ authorize?: any;
14
+ [key: string]: any;
15
+ }
16
+ export interface OpenApiSettingsBase {
17
+ /**
18
+ * The url subpath to access to the documentation.
19
+ */
20
+ path: string;
21
+ /**
22
+ * Specify the spec version you want to generate.
23
+ */
24
+ specVersion?: OpenSpecVersions;
25
+ /**
26
+ * Swagger file name. By default swagger.json
27
+ */
28
+ fileName?: string;
29
+ /**
30
+ * The documentation key used by `@Docs` decorator to create several swagger documentations.
31
+ */
32
+ doc?: string;
33
+ /**
34
+ * The path to the CSS file.
35
+ */
36
+ cssPath?: string;
37
+ /**
38
+ * The path to the JS file.
39
+ */
40
+ jsPath?: string;
41
+ /**
42
+ * The path to the ejs file to create html page.
43
+ */
44
+ viewPath?: string | false;
45
+ /**
46
+ * Display the search field in the navbar.
47
+ */
48
+ showExplorer?: boolean;
49
+ /**
50
+ * Load the base spec documentation from the specified path.
51
+ */
52
+ specPath?: string;
53
+ /**
54
+ * Write the `swagger.json` spec documentation on the specified path.
55
+ */
56
+ outFile?: string;
57
+ /**
58
+ * Display the documentation in the browser.
59
+ */
60
+ hidden?: boolean;
61
+ /**
62
+ * Sort paths by alphabetical order
63
+ */
64
+ sortPaths?: boolean;
65
+ /**
66
+ * A function to generate the operationId.
67
+ */
68
+ operationIdFormatter?: (name: string, propertyKey: string, path: string) => string;
69
+ /**
70
+ * A pattern to generate the operationId. Format of operationId field (%c: class name, %m: method name).
71
+ */
72
+ operationIdPattern?: string;
73
+ /**
74
+ *
75
+ */
76
+ disableSpec?: boolean;
77
+ /**
78
+ * Include only controllers whose paths match the pattern list provided.
79
+ */
80
+ pathPatterns?: string[];
81
+ }
82
+ export interface Swagger2Settings extends OpenApiSettingsBase {
83
+ specVersion?: OS2Versions;
84
+ /**
85
+ * OpenSpec 2
86
+ */
87
+ spec?: Partial<OpenSpec2>;
88
+ }
89
+ export interface OpenAPI3Settings extends OpenApiSettingsBase {
90
+ specVersion?: OS3Versions;
91
+ /**
92
+ * OpenSpec 3
93
+ */
94
+ spec?: Partial<OpenSpec3>;
95
+ }
96
+ export type OpenApiSettings = Swagger2Settings | OpenAPI3Settings;
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Expose a css file.
3
+ * @param path
4
+ */
5
+ export declare function cssMiddleware(path: string): () => void;
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Expose a js file.
3
+ * @param path
4
+ */
5
+ export declare function jsMiddleware(path: string): () => void;
@@ -0,0 +1,5 @@
1
+ import type { OpenApiSettings } from "../interfaces/OpenApiSettings.js";
2
+ /**
3
+ * Return a middleware to expose the OpenAPI spec.
4
+ */
5
+ export declare function openApiMiddleware(conf: OpenApiSettings): () => Promise<void>;
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Redirect to the same path with a trailing slash
3
+ * @param path
4
+ */
5
+ export declare function redirectMiddleware(path: string): () => void;
@@ -0,0 +1,18 @@
1
+ import { Env } from "@tsed/core";
2
+ import { OnReady, OnRoutesInit } from "@tsed/platform-http";
3
+ import { PlatformRouter } from "@tsed/platform-router";
4
+ import type { OpenApiSettings } from "../interfaces/OpenApiSettings.js";
5
+ import { OpenAPIService } from "../services/OpenAPIService.js";
6
+ export declare abstract class OpenAPIBaseModule implements OnRoutesInit, OnReady {
7
+ abstract name: string;
8
+ abstract rootDir: string;
9
+ abstract settings: OpenApiSettings[];
10
+ protected openAPIService: OpenAPIService;
11
+ protected env: Env | undefined;
12
+ protected disableRoutesSummary: boolean | undefined;
13
+ private loaded;
14
+ $onRoutesInit(): void;
15
+ $onReady(): void;
16
+ generateSpecFiles(): Promise<void[]>;
17
+ protected createRouter(conf: OpenApiSettings): PlatformRouter;
18
+ }
@@ -0,0 +1,15 @@
1
+ import { OpenSpec2, OpenSpec3 } from "@tsed/openspec";
2
+ import { Platform } from "@tsed/platform-http";
3
+ import { OpenAPI3Settings, OpenApiSettings, Swagger2Settings } from "../interfaces/OpenApiSettings.js";
4
+ export declare class OpenAPIService {
5
+ #private;
6
+ protected platform: Platform;
7
+ constructor();
8
+ /**
9
+ * Generate Spec for the given configuration
10
+ */
11
+ getOpenAPISpec(conf: OpenAPI3Settings): Promise<OpenSpec3>;
12
+ getOpenAPISpec(conf: Swagger2Settings): Promise<OpenSpec2>;
13
+ getOpenAPISpec(conf: OpenApiSettings): Promise<OpenSpec2>;
14
+ writeOpenAPISpec(conf: OpenApiSettings): Promise<void>;
15
+ }
@@ -0,0 +1,3 @@
1
+ import { Provider } from "@tsed/di";
2
+ import { OpenApiSettings } from "../interfaces/OpenApiSettings.js";
3
+ export declare function includeRoute(route: string, provider: Provider, conf: OpenApiSettings): boolean;
@@ -0,0 +1 @@
1
+ export declare function matchPath(path: string, patterns?: string[]): boolean;
@@ -0,0 +1 @@
1
+ export declare function readSpec(path: string): Promise<any>;
package/package.json ADDED
@@ -0,0 +1,59 @@
1
+ {
2
+ "name": "@tsed/openapi-utils",
3
+ "description": "OpenAPI utils to build @tsed/swagger package or @tsed/scalar",
4
+ "type": "module",
5
+ "version": "8.3.0",
6
+ "source": "./src/index.ts",
7
+ "main": "./lib/esm/index.js",
8
+ "module": "./lib/esm/index.js",
9
+ "typings": "./lib/types/index.d.ts",
10
+ "exports": {
11
+ ".": {
12
+ "@tsed/source": "./src/index.ts",
13
+ "types": "./lib/types/index.d.ts",
14
+ "import": "./lib/esm/index.js",
15
+ "default": "./lib/esm/index.js"
16
+ }
17
+ },
18
+ "scripts": {
19
+ "clean": "rm -rf lib",
20
+ "build": "yarn clean && yarn barrels && yarn build:ts",
21
+ "barrels": "barrels",
22
+ "test": "vitest run",
23
+ "build:ts": "tsc --build tsconfig.json",
24
+ "test:ci": "vitest run --coverage.thresholds.autoUpdate=true"
25
+ },
26
+ "dependencies": {
27
+ "@tsed/openspec": "8.3.0",
28
+ "micromatch": "4.0.8",
29
+ "tslib": "2.7.0"
30
+ },
31
+ "devDependencies": {
32
+ "@tsed/barrels": "8.3.0",
33
+ "@tsed/core": "8.3.0",
34
+ "@tsed/di": "8.3.0",
35
+ "@tsed/json-mapper": "8.3.0",
36
+ "@tsed/openspec": "8.3.0",
37
+ "@tsed/platform-http": "8.3.0",
38
+ "@tsed/schema": "8.3.0",
39
+ "@tsed/typescript": "8.3.0",
40
+ "eslint": "9.12.0",
41
+ "typescript": "5.4.5",
42
+ "vitest": "2.1.2"
43
+ },
44
+ "peerDependencies": {
45
+ "@tsed/platform-http": "8.3.0"
46
+ },
47
+ "peerDependenciesMeta": {
48
+ "@tsed/platform-http": {
49
+ "optional": false
50
+ }
51
+ },
52
+ "repository": "https://github.com/tsedio/tsed",
53
+ "bugs": {
54
+ "url": "https://github.com/tsedio/tsed/issues"
55
+ },
56
+ "homepage": "https://github.com/tsedio/tsed/tree/production/packages/specs/openapi-utils",
57
+ "author": "Romain Lenzotti",
58
+ "license": "MIT"
59
+ }