arkos 2.0.0-next.29 → 2.0.0-next.30

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 (132) hide show
  1. package/dist/esm/app.js +2 -0
  2. package/dist/esm/app.js.map +1 -1
  3. package/dist/esm/components/arkos-gateway/arkos-gateway.js +22 -5
  4. package/dist/esm/components/arkos-gateway/arkos-gateway.js.map +1 -1
  5. package/dist/esm/components/arkos-gateway/types.js.map +1 -1
  6. package/dist/esm/components/arkos-route-hook/index.js +9 -3
  7. package/dist/esm/components/arkos-route-hook/index.js.map +1 -1
  8. package/dist/esm/components/arkos-route-hook/reader.js.map +1 -1
  9. package/dist/esm/components/arkos-route-hook/types.js.map +1 -1
  10. package/dist/esm/exports/file-upload.js +2 -0
  11. package/dist/esm/exports/file-upload.js.map +1 -0
  12. package/dist/esm/exports/index.js.map +1 -1
  13. package/dist/esm/exports/websockets.js.map +1 -1
  14. package/dist/esm/generated.js.map +1 -1
  15. package/dist/esm/modules/auth/auth.controller.js +84 -141
  16. package/dist/esm/modules/auth/auth.controller.js.map +1 -1
  17. package/dist/esm/modules/auth/auth.router.js +1 -1
  18. package/dist/esm/modules/auth/auth.router.js.map +1 -1
  19. package/dist/esm/modules/auth/auth.service.js +93 -15
  20. package/dist/esm/modules/auth/auth.service.js.map +1 -1
  21. package/dist/esm/modules/auth/auth.types.js +2 -0
  22. package/dist/esm/modules/auth/auth.types.js.map +1 -0
  23. package/dist/esm/modules/auth/utils/helpers/auth.controller.helpers.js +1 -0
  24. package/dist/esm/modules/auth/utils/helpers/auth.controller.helpers.js.map +1 -1
  25. package/dist/esm/modules/auth/utils/token-extraction.js +57 -0
  26. package/dist/esm/modules/auth/utils/token-extraction.js.map +1 -0
  27. package/dist/esm/modules/base/base.controller.js +5 -0
  28. package/dist/esm/modules/base/base.controller.js.map +1 -1
  29. package/dist/esm/modules/base/base.middlewares.js +4 -4
  30. package/dist/esm/modules/base/base.middlewares.js.map +1 -1
  31. package/dist/esm/modules/base/utils/error-prettifier.js +64 -0
  32. package/dist/esm/modules/base/utils/error-prettifier.js.map +1 -1
  33. package/dist/esm/modules/error-handler/utils/error-handler.helpers.js +52 -8
  34. package/dist/esm/modules/error-handler/utils/error-handler.helpers.js.map +1 -1
  35. package/dist/esm/modules/error-handler/utils/get-handled-error.js +5 -0
  36. package/dist/esm/modules/error-handler/utils/get-handled-error.js.map +1 -1
  37. package/dist/esm/modules/file-upload/file-upload.controller.js +25 -7
  38. package/dist/esm/modules/file-upload/file-upload.controller.js.map +1 -1
  39. package/dist/esm/modules/file-upload/file-upload.router.js +21 -5
  40. package/dist/esm/modules/file-upload/file-upload.router.js.map +1 -1
  41. package/dist/esm/modules/file-upload/file-upload.service.js +28 -18
  42. package/dist/esm/modules/file-upload/file-upload.service.js.map +1 -1
  43. package/dist/esm/modules/file-upload/utils/file-upload-json-schema-generator.js +37 -12
  44. package/dist/esm/modules/file-upload/utils/file-upload-json-schema-generator.js.map +1 -1
  45. package/dist/esm/modules/file-upload/utils/helpers/file-upload.helpers.js +73 -35
  46. package/dist/esm/modules/file-upload/utils/helpers/file-upload.helpers.js.map +1 -1
  47. package/dist/esm/modules/file-upload/utils/helpers/serve-upload-dir.js +206 -0
  48. package/dist/esm/modules/file-upload/utils/helpers/serve-upload-dir.js.map +1 -0
  49. package/dist/esm/modules/file-upload/utils/helpers/upload-path.helper.js +103 -0
  50. package/dist/esm/modules/file-upload/utils/helpers/upload-path.helper.js.map +1 -0
  51. package/dist/esm/modules/file-upload/utils/helpers/upload-serving-validation.js +212 -0
  52. package/dist/esm/modules/file-upload/utils/helpers/upload-serving-validation.js.map +1 -0
  53. package/dist/esm/modules/file-upload/utils/helpers/upload-serving.registry.js +30 -0
  54. package/dist/esm/modules/file-upload/utils/helpers/upload-serving.registry.js.map +1 -0
  55. package/dist/esm/modules/swagger/utils/helpers/get-swagger-default-configs.js +1 -1
  56. package/dist/esm/modules/swagger/utils/helpers/get-swagger-default-configs.js.map +1 -1
  57. package/dist/esm/modules/swagger/utils/helpers/openapi-schema-converter.js +7 -8
  58. package/dist/esm/modules/swagger/utils/helpers/openapi-schema-converter.js.map +1 -1
  59. package/dist/esm/types/index.js.map +1 -1
  60. package/dist/esm/types/internal.js +2 -0
  61. package/dist/esm/types/internal.js.map +1 -0
  62. package/dist/esm/types/new-arkos-config.js.map +1 -1
  63. package/dist/esm/types/upload.js.map +1 -1
  64. package/dist/esm/types/validation/validation-manager.js +18 -10
  65. package/dist/esm/types/validation/validation-manager.js.map +1 -1
  66. package/dist/esm/types/validation/validator.js.map +1 -1
  67. package/dist/esm/utils/arkos-router/arkos-router-openapi-manager.js.map +1 -1
  68. package/dist/esm/utils/arkos-router/index.js +2 -11
  69. package/dist/esm/utils/arkos-router/index.js.map +1 -1
  70. package/dist/esm/utils/arkos-router/route-config-validator.js +11 -10
  71. package/dist/esm/utils/arkos-router/route-config-validator.js.map +1 -1
  72. package/dist/esm/utils/arkos-router/types/index.js.map +1 -1
  73. package/dist/esm/utils/arkos-router/types/upload-config.js.map +1 -1
  74. package/dist/esm/utils/arkos-router/utils/helpers/index.js +1 -0
  75. package/dist/esm/utils/arkos-router/utils/helpers/index.js.map +1 -1
  76. package/dist/esm/utils/arkos-router/utils/helpers/upload-manager.js +326 -37
  77. package/dist/esm/utils/arkos-router/utils/helpers/upload-manager.js.map +1 -1
  78. package/dist/esm/utils/cli/dev.js +15 -2
  79. package/dist/esm/utils/cli/dev.js.map +1 -1
  80. package/dist/esm/utils/cli/generate.js +8 -3
  81. package/dist/esm/utils/cli/generate.js.map +1 -1
  82. package/dist/esm/utils/cli/index.js +21 -20
  83. package/dist/esm/utils/cli/index.js.map +1 -1
  84. package/dist/esm/utils/cli/prisma-generate.js +24 -5
  85. package/dist/esm/utils/cli/prisma-generate.js.map +1 -1
  86. package/dist/esm/utils/cli/start.js +2 -0
  87. package/dist/esm/utils/cli/start.js.map +1 -1
  88. package/dist/esm/utils/cli/utils/cli.helpers.js +1 -1
  89. package/dist/esm/utils/cli/utils/dependency-version-check.js +39 -0
  90. package/dist/esm/utils/cli/utils/dependency-version-check.js.map +1 -0
  91. package/dist/esm/utils/cli/utils/template-generator/templates/router-template.js.map +1 -1
  92. package/dist/esm/utils/define-config.js +1 -0
  93. package/dist/esm/utils/define-config.js.map +1 -1
  94. package/dist/esm/utils/initialize-app.js +2 -0
  95. package/dist/esm/utils/initialize-app.js.map +1 -1
  96. package/dist/types/app.d.ts +5 -2
  97. package/dist/types/components/arkos-gateway/arkos-gateway.d.ts +19 -7
  98. package/dist/types/components/arkos-gateway/types.d.ts +19 -2
  99. package/dist/types/components/arkos-route-hook/index.d.ts +4 -3
  100. package/dist/types/components/arkos-route-hook/reader.d.ts +1 -1
  101. package/dist/types/components/arkos-route-hook/types.d.ts +4 -1
  102. package/dist/types/exports/file-upload.d.ts +1 -0
  103. package/dist/types/exports/index.d.ts +1 -2
  104. package/dist/types/exports/websockets.d.ts +1 -1
  105. package/dist/types/modules/auth/auth.controller.d.ts +10 -3
  106. package/dist/types/modules/auth/auth.service.d.ts +90 -0
  107. package/dist/types/modules/auth/auth.types.d.ts +72 -0
  108. package/dist/types/modules/auth/utils/helpers/auth.controller.helpers.d.ts +1 -0
  109. package/dist/types/modules/auth/utils/token-extraction.d.ts +53 -0
  110. package/dist/types/modules/base/base.controller.d.ts +1 -0
  111. package/dist/types/modules/base/utils/error-prettifier.d.ts +8 -0
  112. package/dist/types/modules/error-handler/utils/error-handler.helpers.d.ts +1 -0
  113. package/dist/types/modules/file-upload/file-upload.service.d.ts +2 -6
  114. package/dist/types/modules/file-upload/utils/helpers/file-upload.helpers.d.ts +21 -1
  115. package/dist/types/modules/file-upload/utils/helpers/serve-upload-dir.d.ts +43 -0
  116. package/dist/types/modules/file-upload/utils/helpers/upload-path.helper.d.ts +14 -0
  117. package/dist/types/modules/file-upload/utils/helpers/upload-serving-validation.d.ts +5 -0
  118. package/dist/types/modules/file-upload/utils/helpers/upload-serving.registry.d.ts +31 -0
  119. package/dist/types/modules/swagger/utils/helpers/openapi-schema-converter.d.ts +1 -1
  120. package/dist/types/types/index.d.ts +5 -0
  121. package/dist/types/types/internal.d.ts +25 -0
  122. package/dist/types/types/new-arkos-config.d.ts +24 -2
  123. package/dist/types/types/upload.d.ts +35 -0
  124. package/dist/types/types/validation/validation-manager.d.ts +6 -8
  125. package/dist/types/types/validation/validator.d.ts +2 -2
  126. package/dist/types/utils/arkos-router/types/index.d.ts +3 -2
  127. package/dist/types/utils/arkos-router/types/upload-config.d.ts +102 -11
  128. package/dist/types/utils/arkos-router/utils/helpers/upload-manager.d.ts +20 -0
  129. package/dist/types/utils/cli/utils/dependency-version-check.d.ts +15 -0
  130. package/dist/types/utils/define-config.d.ts +56 -2
  131. package/dist/types/utils/helpers/prisma.helpers.d.ts +1 -1
  132. package/package.json +15 -38
@@ -0,0 +1,103 @@
1
+ import fs from "fs";
2
+ import path from "path";
3
+ export const DEFAULT_UPLOAD_DIRS = [
4
+ "images",
5
+ "videos",
6
+ "documents",
7
+ "files",
8
+ ];
9
+ const SAFE_SEGMENT = /^[A-Za-z0-9][A-Za-z0-9._-]*$/;
10
+ const PARAM_SEGMENT = /^:[A-Za-z_][A-Za-z0-9_]*$/;
11
+ const RESERVED_WINDOWS_NAMES = /^(CON|PRN|AUX|NUL|COM[1-9]|LPT[1-9])$/i;
12
+ export function isSafeUploadSegment(segment, allowParam = false) {
13
+ if (!segment)
14
+ return false;
15
+ if (allowParam && PARAM_SEGMENT.test(segment))
16
+ return true;
17
+ if (!SAFE_SEGMENT.test(segment))
18
+ return false;
19
+ if (segment === "." || segment === "..")
20
+ return false;
21
+ if (/[. ]$/.test(segment))
22
+ return false;
23
+ if (RESERVED_WINDOWS_NAMES.test(segment))
24
+ return false;
25
+ return true;
26
+ }
27
+ export function assertSafeUploadPath(value, options = {}) {
28
+ const label = options.label || "upload path";
29
+ if (typeof value !== "string" || value.length === 0)
30
+ throw new Error(`Invalid ${label}: it must be a non-empty string.`);
31
+ if (path.isAbsolute(value))
32
+ throw new Error(`Invalid ${label} "${value}": absolute paths are not allowed.`);
33
+ if (value.includes("\\") || value.includes("\0"))
34
+ throw new Error(`Invalid ${label} "${value}": backslashes and null bytes are not allowed.`);
35
+ for (const segment of value.split("/")) {
36
+ if (!segment || segment === "." || segment === "..")
37
+ throw new Error(`Invalid ${label} "${value}": empty, "." and ".." segments are not allowed.`);
38
+ if (!isSafeUploadSegment(segment, options.allowParams))
39
+ throw new Error(`Invalid ${label} "${value}": segment "${segment}" is not allowed. Use letters, numbers, ".", "_" and "-".`);
40
+ }
41
+ return value;
42
+ }
43
+ export function normalizeUploadDir(dir) {
44
+ return dir.replace(/^\/+/, "").replace(/\/+$/, "");
45
+ }
46
+ export function normalizeUploadUrlPath(urlPath) {
47
+ let result = urlPath.startsWith("/") ? urlPath : `/${urlPath}`;
48
+ if (result.length > 1)
49
+ result = result.replace(/\/+$/, "");
50
+ return result;
51
+ }
52
+ export function isSafeUploadParam(value) {
53
+ return (typeof value === "string" &&
54
+ value.length > 0 &&
55
+ value.length <= 128 &&
56
+ SAFE_SEGMENT.test(value) &&
57
+ value !== "." &&
58
+ value !== ".." &&
59
+ !/[. ]$/.test(value) &&
60
+ !RESERVED_WINDOWS_NAMES.test(value));
61
+ }
62
+ export function isPathInside(base, target) {
63
+ const relative = path.relative(base, target);
64
+ return (relative === "" ||
65
+ (!relative.startsWith(`..${path.sep}`) &&
66
+ relative !== ".." &&
67
+ !path.isAbsolute(relative)));
68
+ }
69
+ export function resolveUploadPath(root, relative, options = {}) {
70
+ if (typeof relative !== "string")
71
+ return null;
72
+ const cleaned = relative.replace(/^\/+/, "");
73
+ if (cleaned.includes("\0") || cleaned.includes("\\"))
74
+ return null;
75
+ if (cleaned === "")
76
+ return path.resolve(root);
77
+ for (const segment of cleaned.split("/")) {
78
+ if (!segment || segment === "." || segment === "..")
79
+ return null;
80
+ if (options.strictSegments && !isSafeUploadSegment(segment))
81
+ return null;
82
+ }
83
+ const resolvedRoot = path.resolve(root);
84
+ const resolved = path.resolve(resolvedRoot, ...cleaned.split("/"));
85
+ if (!isPathInside(resolvedRoot, resolved))
86
+ return null;
87
+ return resolved;
88
+ }
89
+ export async function isRealPathInside(base, target) {
90
+ try {
91
+ const [realBase, realTarget] = await Promise.all([
92
+ fs.promises.realpath(base),
93
+ fs.promises.realpath(target),
94
+ ]);
95
+ return isPathInside(realBase, realTarget);
96
+ }
97
+ catch (error) {
98
+ if (error?.code === "ENOENT")
99
+ return true;
100
+ return false;
101
+ }
102
+ }
103
+ //# sourceMappingURL=upload-path.helper.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"upload-path.helper.js","sourceRoot":"","sources":["../../../../../../src/modules/file-upload/utils/helpers/upload-path.helper.ts"],"names":[],"mappings":"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","sourcesContent":["import fs from \"fs\";\nimport path from \"path\";\n\nexport const DEFAULT_UPLOAD_DIRS = [\n \"images\",\n \"videos\",\n \"documents\",\n \"files\",\n] as const;\n\nconst SAFE_SEGMENT = /^[A-Za-z0-9][A-Za-z0-9._-]*$/;\nconst PARAM_SEGMENT = /^:[A-Za-z_][A-Za-z0-9_]*$/;\nconst RESERVED_WINDOWS_NAMES = /^(CON|PRN|AUX|NUL|COM[1-9]|LPT[1-9])$/i;\n\nexport function isSafeUploadSegment(\n segment: string,\n allowParam = false\n): boolean {\n if (!segment) return false;\n if (allowParam && PARAM_SEGMENT.test(segment)) return true;\n if (!SAFE_SEGMENT.test(segment)) return false;\n if (segment === \".\" || segment === \"..\") return false;\n if (/[. ]$/.test(segment)) return false;\n if (RESERVED_WINDOWS_NAMES.test(segment)) return false;\n return true;\n}\n\nexport function assertSafeUploadPath(\n value: string,\n options: { allowParams?: boolean; label?: string } = {}\n) {\n const label = options.label || \"upload path\";\n\n if (typeof value !== \"string\" || value.length === 0)\n throw new Error(`Invalid ${label}: it must be a non-empty string.`);\n\n if (path.isAbsolute(value))\n throw new Error(\n `Invalid ${label} \"${value}\": absolute paths are not allowed.`\n );\n\n if (value.includes(\"\\\\\") || value.includes(\"\\0\"))\n throw new Error(\n `Invalid ${label} \"${value}\": backslashes and null bytes are not allowed.`\n );\n\n for (const segment of value.split(\"/\")) {\n if (!segment || segment === \".\" || segment === \"..\")\n throw new Error(\n `Invalid ${label} \"${value}\": empty, \".\" and \"..\" segments are not allowed.`\n );\n\n if (!isSafeUploadSegment(segment, options.allowParams))\n throw new Error(\n `Invalid ${label} \"${value}\": segment \"${segment}\" is not allowed. Use letters, numbers, \".\", \"_\" and \"-\".`\n );\n }\n\n return value;\n}\n\nexport function normalizeUploadDir(dir: string): string {\n return dir.replace(/^\\/+/, \"\").replace(/\\/+$/, \"\");\n}\n\nexport function normalizeUploadUrlPath(urlPath: string): string {\n let result = urlPath.startsWith(\"/\") ? urlPath : `/${urlPath}`;\n if (result.length > 1) result = result.replace(/\\/+$/, \"\");\n return result;\n}\n\nexport function isSafeUploadParam(value: unknown): value is string {\n return (\n typeof value === \"string\" &&\n value.length > 0 &&\n value.length <= 128 &&\n SAFE_SEGMENT.test(value) &&\n value !== \".\" &&\n value !== \"..\" &&\n !/[. ]$/.test(value) &&\n !RESERVED_WINDOWS_NAMES.test(value)\n );\n}\n\nexport function isPathInside(base: string, target: string): boolean {\n const relative = path.relative(base, target);\n return (\n relative === \"\" ||\n (!relative.startsWith(`..${path.sep}`) &&\n relative !== \"..\" &&\n !path.isAbsolute(relative))\n );\n}\n\nexport function resolveUploadPath(\n root: string,\n relative: string,\n options: { strictSegments?: boolean } = {}\n): string | null {\n if (typeof relative !== \"string\") return null;\n\n const cleaned = relative.replace(/^\\/+/, \"\");\n if (cleaned.includes(\"\\0\") || cleaned.includes(\"\\\\\")) return null;\n if (cleaned === \"\") return path.resolve(root);\n\n for (const segment of cleaned.split(\"/\")) {\n if (!segment || segment === \".\" || segment === \"..\") return null;\n if (options.strictSegments && !isSafeUploadSegment(segment)) return null;\n }\n\n const resolvedRoot = path.resolve(root);\n const resolved = path.resolve(resolvedRoot, ...cleaned.split(\"/\"));\n\n if (!isPathInside(resolvedRoot, resolved)) return null;\n\n return resolved;\n}\n\nexport async function isRealPathInside(\n base: string,\n target: string\n): Promise<boolean> {\n try {\n const [realBase, realTarget] = await Promise.all([\n fs.promises.realpath(base),\n fs.promises.realpath(target),\n ]);\n\n return isPathInside(realBase, realTarget);\n } catch (error: any) {\n if (error?.code === \"ENOENT\") return true;\n return false;\n }\n}\n"]}
@@ -0,0 +1,212 @@
1
+ import sheu from "../../../../utils/sheu.js";
2
+ import { getArkosConfig } from "../../../../server.js";
3
+ import { extractPathParams } from "../../../../utils/arkos-router/utils/helpers/index.js";
4
+ import { DEFAULT_UPLOAD_DIRS, assertSafeUploadPath, normalizeUploadDir, normalizeUploadUrlPath, } from "./upload-path.helper.js";
5
+ import { uploadServingRegistry, } from "./upload-serving.registry.js";
6
+ function isExactUrlPath(urlPath) {
7
+ return !!urlPath && typeof urlPath === "object";
8
+ }
9
+ function normalizeBaseRoute(baseRoute) {
10
+ const withLeadingSlash = baseRoute.startsWith("/")
11
+ ? baseRoute
12
+ : `/${baseRoute}`;
13
+ return withLeadingSlash.length > 1
14
+ ? withLeadingSlash.replace(/\/+$/, "")
15
+ : withLeadingSlash;
16
+ }
17
+ function assertUrlPathIsRelativeToBaseRoute(urlPath, baseRoute, route) {
18
+ const normalizedBaseRoute = normalizeBaseRoute(baseRoute);
19
+ if (urlPath === normalizedBaseRoute ||
20
+ urlPath.startsWith(`${normalizedBaseRoute}/`))
21
+ throw new Error(`Invalid urlPath "${urlPath}" on route "${route}": it is relative to baseRoute and must not include "${normalizedBaseRoute}".`);
22
+ }
23
+ function substitutePath(pathTemplate, params) {
24
+ return pathTemplate
25
+ .split("/")
26
+ .map((segment) => segment.startsWith(":") ? params[segment.slice(1)] : segment)
27
+ .join("/");
28
+ }
29
+ function matchDirTemplates(serveDir, writeDir, recursive) {
30
+ const serveSegments = normalizeUploadDir(serveDir).split("/");
31
+ const writeSegments = normalizeUploadDir(writeDir).split("/");
32
+ if (serveSegments.length > writeSegments.length)
33
+ return null;
34
+ if (serveSegments.length < writeSegments.length && !recursive)
35
+ return null;
36
+ const params = {};
37
+ for (let index = 0; index < serveSegments.length; index++) {
38
+ const serveSegment = serveSegments[index];
39
+ const writeSegment = writeSegments[index];
40
+ if (serveSegment.startsWith(":")) {
41
+ if (writeSegment.startsWith(":")) {
42
+ if (serveSegment.slice(1) !== writeSegment.slice(1))
43
+ return null;
44
+ params[serveSegment.slice(1)] = serveSegment;
45
+ }
46
+ else
47
+ params[serveSegment.slice(1)] = writeSegment;
48
+ }
49
+ else if (serveSegment !== writeSegment)
50
+ return null;
51
+ }
52
+ return {
53
+ params,
54
+ matched: serveSegments.length,
55
+ remaining: writeSegments.slice(serveSegments.length),
56
+ };
57
+ }
58
+ function validateWriteUrlPath(registration, baseRoute) {
59
+ const { urlPath, route, dir } = registration;
60
+ if (urlPath === undefined)
61
+ return;
62
+ if (!dir)
63
+ throw new Error(`Route "${route}" declares urlPath but does not declare uploadDir.`);
64
+ if (isExactUrlPath(urlPath)) {
65
+ const exact = urlPath.exact;
66
+ if (typeof exact !== "string" || !exact.startsWith("/"))
67
+ throw new Error(`Invalid exact urlPath on route "${route}": it must be a string starting with "/".`);
68
+ if (exact.length > 1 && exact.endsWith("/"))
69
+ throw new Error(`Invalid exact urlPath "${exact}" on route "${route}": it must not end with "/".`);
70
+ if (exact === "/")
71
+ throw new Error(`Invalid exact urlPath "/" on route "${route}": it must contain at least one segment.`);
72
+ assertSafeUploadPath(exact.replace(/^\//, ""), {
73
+ label: "exact upload urlPath",
74
+ });
75
+ assertUrlPathIsRelativeToBaseRoute(exact, baseRoute, route);
76
+ if (registration.type === "array")
77
+ throw new Error(`Exact urlPath "${exact}" on route "${route}" cannot be used with array uploads because every file would share the same URL.`);
78
+ return;
79
+ }
80
+ if (!urlPath.startsWith("/"))
81
+ throw new Error(`Invalid urlPath "${urlPath}" on route "${route}": it must start with "/".`);
82
+ if (urlPath.endsWith("/"))
83
+ throw new Error(`Invalid urlPath "${urlPath}" on route "${route}": it must not end with "/".`);
84
+ assertSafeUploadPath(urlPath.replace(/^\//, ""), {
85
+ allowParams: true,
86
+ label: "upload urlPath",
87
+ });
88
+ assertUrlPathIsRelativeToBaseRoute(urlPath, baseRoute, route);
89
+ }
90
+ function warnAboutUnusedUrlParams(dir, urlPath, route, suppress) {
91
+ if (suppress)
92
+ return;
93
+ const dirParams = extractPathParams(`/${dir}`);
94
+ const unused = extractPathParams(urlPath).filter((param) => !dirParams.includes(param));
95
+ if (unused.length > 0)
96
+ sheu.warn(`Upload urlPath "${urlPath}" on route "${route}" declares ${unused
97
+ .map((param) => `":${param}"`)
98
+ .join(", ")} but uploadDir "${dir}" does not use them.`, { timestamp: true });
99
+ }
100
+ export function validateUploadServingConfig() {
101
+ const config = getArkosConfig();
102
+ const serving = config.fileUpload?.serving || "all";
103
+ const findFileEnabled = uploadServingRegistry.isFindFileServingEnabled();
104
+ const baseRoute = config.fileUpload?.baseRoute || "/api/uploads";
105
+ const suppressUnusedUrlParams = config.warnings?.fileUpload?.suppressUnusedUrlParams === true;
106
+ const serveRegistrations = uploadServingRegistry.getServeRegistrations();
107
+ const writeRegistrations = uploadServingRegistry.getWriteRegistrations();
108
+ const seenServePaths = new Map();
109
+ const seenServeDirs = new Map();
110
+ const seenExactWritePaths = new Map();
111
+ const warnedRoutes = new Set();
112
+ for (const registration of serveRegistrations) {
113
+ const dir = normalizeUploadDir(registration.dir);
114
+ const publicPath = normalizeUploadUrlPath(registration.urlPath);
115
+ const firstDirSegment = dir.split("/")[0];
116
+ const firstPathSegment = publicPath.replace(/^\//, "").split("/")[0];
117
+ if (registration.dynamic && !registration.hasPrepare)
118
+ throw new Error(`Dynamic upload directory "${dir}" requires a prepare function.`);
119
+ if (registration.pathParams.length > 0 && !suppressUnusedUrlParams) {
120
+ const unused = registration.pathParams.filter((param) => !registration.dirParams.includes(param));
121
+ if (unused.length > 0)
122
+ sheu.warn(`Upload URL path "${publicPath}" declares ${unused
123
+ .map((param) => `":${param}"`)
124
+ .join(", ")} but the upload directory "${dir}" does not use them.`, { timestamp: true });
125
+ }
126
+ if (findFileEnabled &&
127
+ DEFAULT_UPLOAD_DIRS.includes(firstDirSegment))
128
+ throw new Error(`Cannot serve upload directory "${dir}" with arkos.serveUploadDir(): "${firstDirSegment}" is a built-in upload root and is always served while findFile is enabled.`);
129
+ if (findFileEnabled &&
130
+ DEFAULT_UPLOAD_DIRS.includes(firstPathSegment))
131
+ throw new Error(`Cannot register upload URL path "${publicPath}": "/${firstPathSegment}" is a built-in upload root and is always served while findFile is enabled.`);
132
+ if (seenServePaths.has(publicPath))
133
+ throw new Error(`Duplicate upload serving path "${publicPath}" registered with arkos.serveUploadDir().`);
134
+ seenServePaths.set(publicPath, dir);
135
+ if (seenServeDirs.has(dir))
136
+ throw new Error(seenServeDirs.get(dir) === publicPath
137
+ ? `Duplicate arkos.serveUploadDir() registration for "${dir}".`
138
+ : `Upload directory "${dir}" is served at multiple paths: "${seenServeDirs.get(dir)}" and "${publicPath}".`);
139
+ seenServeDirs.set(dir, publicPath);
140
+ }
141
+ let warnedAboutAllMode = false;
142
+ for (const registration of writeRegistrations) {
143
+ const dir = registration.dir
144
+ ? normalizeUploadDir(registration.dir)
145
+ : undefined;
146
+ validateWriteUrlPath(registration, baseRoute);
147
+ if (!dir)
148
+ continue;
149
+ assertSafeUploadPath(dir, { allowParams: true, label: "uploadDir" });
150
+ const dirParams = extractPathParams(`/${dir}`);
151
+ if (dirParams.length > 0 && !registration.hasPrepare)
152
+ throw new Error(`Dynamic upload directory "${dir}" on route "${registration.route}" requires a prepare function.`);
153
+ if (registration.urlPath !== undefined &&
154
+ !isExactUrlPath(registration.urlPath)) {
155
+ const warningKey = `${registration.route}:${dir}:${registration.urlPath}`;
156
+ if (!warnedRoutes.has(warningKey)) {
157
+ warnAboutUnusedUrlParams(dir, registration.urlPath, registration.route, suppressUnusedUrlParams);
158
+ warnedRoutes.add(warningKey);
159
+ }
160
+ }
161
+ if (isExactUrlPath(registration.urlPath)) {
162
+ const exact = registration.urlPath.exact;
163
+ if (seenExactWritePaths.has(exact))
164
+ throw new Error(`Duplicate exact urlPath "${exact}" on route "${registration.route}"; it is already used on route "${seenExactWritePaths.get(exact)}".`);
165
+ seenExactWritePaths.set(exact, registration.route);
166
+ continue;
167
+ }
168
+ const firstDirSegment = dir.split("/")[0];
169
+ if (findFileEnabled &&
170
+ DEFAULT_UPLOAD_DIRS.includes(firstDirSegment))
171
+ continue;
172
+ if (serving === "all") {
173
+ if (!warnedAboutAllMode && serveRegistrations.length > 0) {
174
+ warningAboutAllMode();
175
+ warnedAboutAllMode = true;
176
+ }
177
+ continue;
178
+ }
179
+ if (registration.serve === false)
180
+ continue;
181
+ const writePublicPath = normalizeUploadUrlPath(registration.urlPath ?? `/${dir}`);
182
+ let bestMatch;
183
+ for (const serveRegistration of serveRegistrations) {
184
+ const match = matchDirTemplates(serveRegistration.dir, dir, serveRegistration.recursive);
185
+ if (!match)
186
+ continue;
187
+ if (!bestMatch || match.matched > bestMatch.match.matched) {
188
+ bestMatch = { registration: serveRegistration, match };
189
+ continue;
190
+ }
191
+ if (match.matched === bestMatch.match.matched &&
192
+ normalizeUploadUrlPath(serveRegistration.urlPath) !==
193
+ normalizeUploadUrlPath(bestMatch.registration.urlPath))
194
+ throw new Error(`Upload directory "${dir}" on route "${registration.route}" is ambiguously served by "${bestMatch.registration.urlPath}" and "${serveRegistration.urlPath}".`);
195
+ }
196
+ if (!bestMatch)
197
+ throw new Error(`Upload directory "${dir}" used by route "${registration.route}" is not served. Register it with arkos.serveUploadDir("${dir}"), register one of its parent folders with arkos.serveUploadDir(parent, { recursive: true }), or set "serve: false" in the route's uploads config.`);
198
+ const servedPublicPath = normalizeUploadUrlPath(`${substitutePath(bestMatch.registration.urlPath, bestMatch.match.params)}${bestMatch.match.remaining.length
199
+ ? `/${bestMatch.match.remaining.join("/")}`
200
+ : ""}`);
201
+ if (servedPublicPath !== writePublicPath)
202
+ throw new Error(`Generated URL "${writePublicPath}" does not match served URL "${servedPublicPath}" for upload directory "${dir}".`);
203
+ }
204
+ if (serving === "all" &&
205
+ serveRegistrations.length > 0 &&
206
+ !warnedAboutAllMode)
207
+ warningAboutAllMode();
208
+ }
209
+ function warningAboutAllMode() {
210
+ sheu.warn(`fileUpload.serving is "all"; per-folder permissions registered via arkos.serveUploadDir() are not enforced.`, { timestamp: true });
211
+ }
212
+ //# sourceMappingURL=upload-serving-validation.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"upload-serving-validation.js","sourceRoot":"","sources":["../../../../../../src/modules/file-upload/utils/helpers/upload-serving-validation.ts"],"names":[],"mappings":"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","sourcesContent":["import sheu from \"../../../../utils/sheu\";\nimport { getArkosConfig } from \"../../../../server\";\nimport { extractPathParams } from \"../../../../utils/arkos-router/utils/helpers\";\nimport type { UploadUrlPath } from \"../../../../types/upload\";\nimport {\n DEFAULT_UPLOAD_DIRS,\n assertSafeUploadPath,\n normalizeUploadDir,\n normalizeUploadUrlPath,\n} from \"./upload-path.helper\";\nimport {\n uploadServingRegistry,\n UploadServeRegistration,\n UploadWriteRegistration,\n} from \"./upload-serving.registry\";\n\ntype TemplateMatch = {\n params: Record<string, string>;\n matched: number;\n remaining: string[];\n};\n\nfunction isExactUrlPath(\n urlPath: UploadUrlPath | undefined\n): urlPath is { exact: string } {\n return !!urlPath && typeof urlPath === \"object\";\n}\n\nfunction normalizeBaseRoute(baseRoute: string): string {\n const withLeadingSlash = baseRoute.startsWith(\"/\")\n ? baseRoute\n : `/${baseRoute}`;\n\n return withLeadingSlash.length > 1\n ? withLeadingSlash.replace(/\\/+$/, \"\")\n : withLeadingSlash;\n}\n\nfunction assertUrlPathIsRelativeToBaseRoute(\n urlPath: string,\n baseRoute: string,\n route: string\n) {\n const normalizedBaseRoute = normalizeBaseRoute(baseRoute);\n\n if (\n urlPath === normalizedBaseRoute ||\n urlPath.startsWith(`${normalizedBaseRoute}/`)\n )\n throw new Error(\n `Invalid urlPath \"${urlPath}\" on route \"${route}\": it is relative to baseRoute and must not include \"${normalizedBaseRoute}\".`\n );\n}\n\nfunction substitutePath(\n pathTemplate: string,\n params: Record<string, string>\n): string {\n return pathTemplate\n .split(\"/\")\n .map((segment) =>\n segment.startsWith(\":\") ? params[segment.slice(1)] : segment\n )\n .join(\"/\");\n}\n\nfunction matchDirTemplates(\n serveDir: string,\n writeDir: string,\n recursive: boolean\n): TemplateMatch | null {\n const serveSegments = normalizeUploadDir(serveDir).split(\"/\");\n const writeSegments = normalizeUploadDir(writeDir).split(\"/\");\n\n if (serveSegments.length > writeSegments.length) return null;\n if (serveSegments.length < writeSegments.length && !recursive) return null;\n\n const params: Record<string, string> = {};\n\n for (let index = 0; index < serveSegments.length; index++) {\n const serveSegment = serveSegments[index];\n const writeSegment = writeSegments[index];\n\n if (serveSegment.startsWith(\":\")) {\n if (writeSegment.startsWith(\":\")) {\n if (serveSegment.slice(1) !== writeSegment.slice(1)) return null;\n params[serveSegment.slice(1)] = serveSegment;\n } else params[serveSegment.slice(1)] = writeSegment;\n } else if (serveSegment !== writeSegment) return null;\n }\n\n return {\n params,\n matched: serveSegments.length,\n remaining: writeSegments.slice(serveSegments.length),\n };\n}\n\nfunction validateWriteUrlPath(\n registration: UploadWriteRegistration,\n baseRoute: string\n) {\n const { urlPath, route, dir } = registration;\n\n if (urlPath === undefined) return;\n\n if (!dir)\n throw new Error(\n `Route \"${route}\" declares urlPath but does not declare uploadDir.`\n );\n\n if (isExactUrlPath(urlPath)) {\n const exact = urlPath.exact;\n\n if (typeof exact !== \"string\" || !exact.startsWith(\"/\"))\n throw new Error(\n `Invalid exact urlPath on route \"${route}\": it must be a string starting with \"/\".`\n );\n\n if (exact.length > 1 && exact.endsWith(\"/\"))\n throw new Error(\n `Invalid exact urlPath \"${exact}\" on route \"${route}\": it must not end with \"/\".`\n );\n\n if (exact === \"/\")\n throw new Error(\n `Invalid exact urlPath \"/\" on route \"${route}\": it must contain at least one segment.`\n );\n\n assertSafeUploadPath(exact.replace(/^\\//, \"\"), {\n label: \"exact upload urlPath\",\n });\n\n assertUrlPathIsRelativeToBaseRoute(exact, baseRoute, route);\n\n if (registration.type === \"array\")\n throw new Error(\n `Exact urlPath \"${exact}\" on route \"${route}\" cannot be used with array uploads because every file would share the same URL.`\n );\n\n return;\n }\n\n if (!urlPath.startsWith(\"/\"))\n throw new Error(\n `Invalid urlPath \"${urlPath}\" on route \"${route}\": it must start with \"/\".`\n );\n\n if (urlPath.endsWith(\"/\"))\n throw new Error(\n `Invalid urlPath \"${urlPath}\" on route \"${route}\": it must not end with \"/\".`\n );\n\n assertSafeUploadPath(urlPath.replace(/^\\//, \"\"), {\n allowParams: true,\n label: \"upload urlPath\",\n });\n\n assertUrlPathIsRelativeToBaseRoute(urlPath, baseRoute, route);\n}\n\nfunction warnAboutUnusedUrlParams(\n dir: string,\n urlPath: string,\n route: string,\n suppress: boolean\n) {\n if (suppress) return;\n\n const dirParams = extractPathParams(`/${dir}`);\n const unused = extractPathParams(urlPath).filter(\n (param) => !dirParams.includes(param)\n );\n\n if (unused.length > 0)\n sheu.warn(\n `Upload urlPath \"${urlPath}\" on route \"${route}\" declares ${unused\n .map((param) => `\":${param}\"`)\n .join(\", \")} but uploadDir \"${dir}\" does not use them.`,\n { timestamp: true }\n );\n}\n\n/**\n * Validates the upload serving registrations collected while building the\n * routers. Must run once, after every router has been constructed.\n */\nexport function validateUploadServingConfig() {\n const config = getArkosConfig();\n const serving = config.fileUpload?.serving || \"all\";\n const findFileEnabled = uploadServingRegistry.isFindFileServingEnabled();\n const baseRoute = config.fileUpload?.baseRoute || \"/api/uploads\";\n const suppressUnusedUrlParams =\n config.warnings?.fileUpload?.suppressUnusedUrlParams === true;\n const serveRegistrations = uploadServingRegistry.getServeRegistrations();\n const writeRegistrations = uploadServingRegistry.getWriteRegistrations();\n\n const seenServePaths = new Map<string, string>();\n const seenServeDirs = new Map<string, string>();\n const seenExactWritePaths = new Map<string, string>();\n const warnedRoutes = new Set<string>();\n\n for (const registration of serveRegistrations) {\n const dir = normalizeUploadDir(registration.dir);\n const publicPath = normalizeUploadUrlPath(registration.urlPath);\n const firstDirSegment = dir.split(\"/\")[0];\n const firstPathSegment = publicPath.replace(/^\\//, \"\").split(\"/\")[0];\n\n if (registration.dynamic && !registration.hasPrepare)\n throw new Error(\n `Dynamic upload directory \"${dir}\" requires a prepare function.`\n );\n\n if (registration.pathParams.length > 0 && !suppressUnusedUrlParams) {\n const unused = registration.pathParams.filter(\n (param) => !registration.dirParams.includes(param)\n );\n\n if (unused.length > 0)\n sheu.warn(\n `Upload URL path \"${publicPath}\" declares ${unused\n .map((param) => `\":${param}\"`)\n .join(\", \")} but the upload directory \"${dir}\" does not use them.`,\n { timestamp: true }\n );\n }\n\n if (\n findFileEnabled &&\n (DEFAULT_UPLOAD_DIRS as readonly string[]).includes(firstDirSegment)\n )\n throw new Error(\n `Cannot serve upload directory \"${dir}\" with arkos.serveUploadDir(): \"${firstDirSegment}\" is a built-in upload root and is always served while findFile is enabled.`\n );\n\n if (\n findFileEnabled &&\n (DEFAULT_UPLOAD_DIRS as readonly string[]).includes(firstPathSegment)\n )\n throw new Error(\n `Cannot register upload URL path \"${publicPath}\": \"/${firstPathSegment}\" is a built-in upload root and is always served while findFile is enabled.`\n );\n\n if (seenServePaths.has(publicPath))\n throw new Error(\n `Duplicate upload serving path \"${publicPath}\" registered with arkos.serveUploadDir().`\n );\n\n seenServePaths.set(publicPath, dir);\n\n if (seenServeDirs.has(dir))\n throw new Error(\n seenServeDirs.get(dir) === publicPath\n ? `Duplicate arkos.serveUploadDir() registration for \"${dir}\".`\n : `Upload directory \"${dir}\" is served at multiple paths: \"${seenServeDirs.get(\n dir\n )}\" and \"${publicPath}\".`\n );\n\n seenServeDirs.set(dir, publicPath);\n }\n\n let warnedAboutAllMode = false;\n\n for (const registration of writeRegistrations) {\n const dir = registration.dir\n ? normalizeUploadDir(registration.dir)\n : undefined;\n\n validateWriteUrlPath(registration, baseRoute);\n\n if (!dir) continue;\n\n assertSafeUploadPath(dir, { allowParams: true, label: \"uploadDir\" });\n\n const dirParams = extractPathParams(`/${dir}`);\n\n if (dirParams.length > 0 && !registration.hasPrepare)\n throw new Error(\n `Dynamic upload directory \"${dir}\" on route \"${registration.route}\" requires a prepare function.`\n );\n\n if (\n registration.urlPath !== undefined &&\n !isExactUrlPath(registration.urlPath)\n ) {\n const warningKey = `${registration.route}:${dir}:${registration.urlPath}`;\n\n if (!warnedRoutes.has(warningKey)) {\n warnAboutUnusedUrlParams(\n dir,\n registration.urlPath,\n registration.route,\n suppressUnusedUrlParams\n );\n warnedRoutes.add(warningKey);\n }\n }\n\n if (isExactUrlPath(registration.urlPath)) {\n const exact = registration.urlPath.exact;\n\n if (seenExactWritePaths.has(exact))\n throw new Error(\n `Duplicate exact urlPath \"${exact}\" on route \"${registration.route}\"; it is already used on route \"${seenExactWritePaths.get(\n exact\n )}\".`\n );\n\n seenExactWritePaths.set(exact, registration.route);\n continue;\n }\n\n const firstDirSegment = dir.split(\"/\")[0];\n\n if (\n findFileEnabled &&\n (DEFAULT_UPLOAD_DIRS as readonly string[]).includes(firstDirSegment)\n )\n continue;\n\n if (serving === \"all\") {\n if (!warnedAboutAllMode && serveRegistrations.length > 0) {\n warningAboutAllMode();\n warnedAboutAllMode = true;\n }\n continue;\n }\n\n if (registration.serve === false) continue;\n\n const writePublicPath = normalizeUploadUrlPath(\n registration.urlPath ?? `/${dir}`\n );\n\n let bestMatch:\n | { registration: UploadServeRegistration; match: TemplateMatch }\n | undefined;\n\n for (const serveRegistration of serveRegistrations) {\n const match = matchDirTemplates(\n serveRegistration.dir,\n dir,\n serveRegistration.recursive\n );\n\n if (!match) continue;\n\n if (!bestMatch || match.matched > bestMatch.match.matched) {\n bestMatch = { registration: serveRegistration, match };\n continue;\n }\n\n if (\n match.matched === bestMatch.match.matched &&\n normalizeUploadUrlPath(serveRegistration.urlPath) !==\n normalizeUploadUrlPath(bestMatch.registration.urlPath)\n )\n throw new Error(\n `Upload directory \"${dir}\" on route \"${registration.route}\" is ambiguously served by \"${bestMatch.registration.urlPath}\" and \"${serveRegistration.urlPath}\".`\n );\n }\n\n if (!bestMatch)\n throw new Error(\n `Upload directory \"${dir}\" used by route \"${registration.route}\" is not served. Register it with arkos.serveUploadDir(\"${dir}\"), register one of its parent folders with arkos.serveUploadDir(parent, { recursive: true }), or set \"serve: false\" in the route's uploads config.`\n );\n\n const servedPublicPath = normalizeUploadUrlPath(\n `${substitutePath(bestMatch.registration.urlPath, bestMatch.match.params)}${\n bestMatch.match.remaining.length\n ? `/${bestMatch.match.remaining.join(\"/\")}`\n : \"\"\n }`\n );\n\n if (servedPublicPath !== writePublicPath)\n throw new Error(\n `Generated URL \"${writePublicPath}\" does not match served URL \"${servedPublicPath}\" for upload directory \"${dir}\".`\n );\n }\n\n if (\n serving === \"all\" &&\n serveRegistrations.length > 0 &&\n !warnedAboutAllMode\n )\n warningAboutAllMode();\n}\n\nfunction warningAboutAllMode() {\n sheu.warn(\n `fileUpload.serving is \"all\"; per-folder permissions registered via arkos.serveUploadDir() are not enforced.`,\n { timestamp: true }\n );\n}\n"]}
@@ -0,0 +1,30 @@
1
+ export class UploadServingRegistry {
2
+ writeRegistrations = [];
3
+ serveRegistrations = [];
4
+ findFileServingEnabled = false;
5
+ registerWrite(registration) {
6
+ this.writeRegistrations.push(registration);
7
+ }
8
+ registerServe(registration) {
9
+ this.serveRegistrations.push(registration);
10
+ }
11
+ getWriteRegistrations() {
12
+ return [...this.writeRegistrations];
13
+ }
14
+ getServeRegistrations() {
15
+ return [...this.serveRegistrations];
16
+ }
17
+ setFindFileServingEnabled(enabled) {
18
+ this.findFileServingEnabled = enabled;
19
+ }
20
+ isFindFileServingEnabled() {
21
+ return this.findFileServingEnabled;
22
+ }
23
+ reset() {
24
+ this.writeRegistrations = [];
25
+ this.serveRegistrations = [];
26
+ this.findFileServingEnabled = false;
27
+ }
28
+ }
29
+ export const uploadServingRegistry = new UploadServingRegistry();
30
+ //# sourceMappingURL=upload-serving.registry.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"upload-serving.registry.js","sourceRoot":"","sources":["../../../../../../src/modules/file-upload/utils/helpers/upload-serving.registry.ts"],"names":[],"mappings":"AAqBA,MAAM,OAAO,qBAAqB;IACxB,kBAAkB,GAA8B,EAAE,CAAC;IACnD,kBAAkB,GAA8B,EAAE,CAAC;IACnD,sBAAsB,GAAG,KAAK,CAAC;IAEvC,aAAa,CAAC,YAAqC;QACjD,IAAI,CAAC,kBAAkB,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;IAC7C,CAAC;IAED,aAAa,CAAC,YAAqC;QACjD,IAAI,CAAC,kBAAkB,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;IAC7C,CAAC;IAED,qBAAqB;QACnB,OAAO,CAAC,GAAG,IAAI,CAAC,kBAAkB,CAAC,CAAC;IACtC,CAAC;IAED,qBAAqB;QACnB,OAAO,CAAC,GAAG,IAAI,CAAC,kBAAkB,CAAC,CAAC;IACtC,CAAC;IAED,yBAAyB,CAAC,OAAgB;QACxC,IAAI,CAAC,sBAAsB,GAAG,OAAO,CAAC;IACxC,CAAC;IAED,wBAAwB;QACtB,OAAO,IAAI,CAAC,sBAAsB,CAAC;IACrC,CAAC;IAED,KAAK;QACH,IAAI,CAAC,kBAAkB,GAAG,EAAE,CAAC;QAC7B,IAAI,CAAC,kBAAkB,GAAG,EAAE,CAAC;QAC7B,IAAI,CAAC,sBAAsB,GAAG,KAAK,CAAC;IACtC,CAAC;CACF;AAED,MAAM,CAAC,MAAM,qBAAqB,GAAG,IAAI,qBAAqB,EAAE,CAAC","sourcesContent":["import type { UploadUrlPath } from \"../../../../types/upload\";\n\nexport type UploadWriteRegistration = {\n dir?: string;\n urlPath?: UploadUrlPath;\n serve?: boolean;\n route: string;\n hasPrepare?: boolean;\n type?: \"single\" | \"array\" | \"fields\";\n};\n\nexport type UploadServeRegistration = {\n dir: string;\n urlPath: string;\n dirParams: string[];\n pathParams: string[];\n recursive: boolean;\n dynamic: boolean;\n hasPrepare: boolean;\n};\n\nexport class UploadServingRegistry {\n private writeRegistrations: UploadWriteRegistration[] = [];\n private serveRegistrations: UploadServeRegistration[] = [];\n private findFileServingEnabled = false;\n\n registerWrite(registration: UploadWriteRegistration) {\n this.writeRegistrations.push(registration);\n }\n\n registerServe(registration: UploadServeRegistration) {\n this.serveRegistrations.push(registration);\n }\n\n getWriteRegistrations() {\n return [...this.writeRegistrations];\n }\n\n getServeRegistrations() {\n return [...this.serveRegistrations];\n }\n\n setFindFileServingEnabled(enabled: boolean) {\n this.findFileServingEnabled = enabled;\n }\n\n isFindFileServingEnabled() {\n return this.findFileServingEnabled;\n }\n\n reset() {\n this.writeRegistrations = [];\n this.serveRegistrations = [];\n this.findFileServingEnabled = false;\n }\n}\n\nexport const uploadServingRegistry = new UploadServingRegistry();\n"]}
@@ -53,7 +53,7 @@ export default function getSwaggerDefaultConfig(defaultModelsPaths) {
53
53
  description: "API documentation generated by Arkos.js",
54
54
  },
55
55
  pageTitle: "Arkos.js API Documentation",
56
- customCss: `.scalar-client {
56
+ customCss: `.scalar-app-client {
57
57
  width: 100vw !important;
58
58
  height: 100vh !important;
59
59
  max-width: 100vw !important;
@@ -1 +1 @@
1
- {"version":3,"file":"get-swagger-default-configs.js","sourceRoot":"","sources":["../../../../../../src/modules/swagger/utils/helpers/get-swagger-default-configs.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AACpD,OAAO,EAAE,UAAU,EAAE,MAAM,wCAAwC,CAAC;AAEpE,MAAM,CAAC,OAAO,UAAU,uBAAuB,CAC7C,kBAAuC;IAEvC,MAAM,WAAW,GAAG;QAClB,IAAI,EAAE,OAAO,CAAC,GAAG,CAAC,MAAM,IAAI,OAAO,CAAC,GAAG,CAAC,IAAI,IAAI,MAAM;QACtD,IAAI,EAAE,OAAO,CAAC,GAAG,CAAC,MAAO,IAAI,OAAO,CAAC,GAAG,CAAC,IAAI,IAAI,WAAW;KAC7D,CAAC;IACF,MAAM,MAAM,GAAG,cAAc,EAAE,CAAC;IAEhC,OAAO;QACL,QAAQ,EAAE,OAAO;QACjB,IAAI,EAAE,QAAQ;QACd,MAAM,EAAE,KAAK;QACb,OAAO,EAAE;YACP,UAAU,EAAE;gBACV,OAAO,EAAE,OAAO;gBAChB,IAAI,EAAE;oBACJ,KAAK,EAAE,qBAAqB;oBAC5B,OAAO,EAAE,OAAO;oBAChB,WAAW,EACT,sFAAsF;iBACzF;gBACD,OAAO,EAAE;oBACP;wBACE,GAAG,EAAE,UAAU,CAAC,SAAS,EAAE,WAAW,EAAE,WAAW,CAAC,CAAC,QAAQ,CAAC,WAAW,CAAC,IAAK,CAAC,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,WAAW,CAAC,IAAI,IAAI,WAAW,CAAC,IAAI,EAAE;wBACvI,WAAW,EAAE,SAAS,UAAU,CAAC,OAAO,CAAC,GAAG,CAAC,QAAQ,IAAI,aAAa,CAAC,SAAS;qBACjF;iBACF;gBACD,KAAK,EAAE,kBAAkB;gBACzB,UAAU,EAAE;oBACV,eAAe,EAAE;wBACf,UAAU,EAAE;4BACV,IAAI,EAAE,MAAM;4BACZ,MAAM,EAAE,QAAQ;4BAChB,YAAY,EAAE,KAAK;yBACpB;qBACF;iBACF;gBACD,QAAQ,EAAE,EAAE;aACb;YACD,IAAI,EAAE,CAAC,2BAA2B,EAAE,0BAA0B,CAAC;YAC/D,WAAW,EAAE,IAAI;YACjB,eAAe,EAAE,IAAI;YACrB,oBAAoB,EAAE,KAAK;SAC5B;QACD,+BAA+B,EAAE;YAC/B,KAAK,EAAE,WAAW;YAClB,QAAQ,EAAE,IAAI;YACd,MAAM,EAAE,QAAQ;YAChB,WAAW,EAAE,IAAI;YACjB,kBAAkB,EAAE,KAAK;YACzB,GAAG,EAAE,MAAM,CAAC,YAAY,GAAG,6CAA6C;YACxE,QAAQ,EAAE;gBACR,KAAK,EAAE,4BAA4B;gBACnC,WAAW,EAAE,yCAAyC;aACvD;YACD,SAAS,EAAE,4BAA4B;YACvC,SAAS,EAAE;;;;;;;;;EASf;SACG;KACF,CAAC;AACJ,CAAC","sourcesContent":["import { getArkosConfig } from \"../../../../server\";\nimport { capitalize } from \"../../../../utils/helpers/text.helpers\";\n\nexport default function getSwaggerDefaultConfig(\n defaultModelsPaths: Record<string, any>\n) {\n const portAndHost = {\n port: process.env.__PORT || process.env.PORT || \"8000\",\n host: process.env.__HOST! || process.env.HOST || \"127.0.0.1\",\n };\n const config = getArkosConfig();\n\n return {\n endpoint: \"/docs\",\n mode: \"prisma\",\n strict: false,\n options: {\n definition: {\n openapi: \"3.1.0\",\n info: {\n title: \"Powered By Arkos.js\",\n version: \"1.0.0\",\n description:\n \"This API was automatically generated by Arkos.js, read more about at www.arkosjs.com\",\n },\n servers: [\n {\n url: `http://${[\"0.0.0.0\", \"localhost\", \"127.0.0.1\"].includes(portAndHost.host!) ? \"localhost\" : portAndHost.host}:${portAndHost.port}`,\n description: `Local ${capitalize(process.env.NODE_ENV || \"development\")} Server`,\n },\n ],\n paths: defaultModelsPaths,\n components: {\n securitySchemes: {\n BearerAuth: {\n type: \"http\",\n scheme: \"bearer\",\n bearerFormat: \"JWT\",\n },\n },\n },\n security: [],\n },\n apis: [\"./src/**/*.router.{ts,js}\", \"./src/**/*.route.{ts,js}\"],\n deepLinking: true,\n tryItOutEnabled: true,\n persistAuthorization: false,\n },\n scalarApiReferenceConfiguration: {\n theme: \"deepSpace\",\n darkMode: true,\n layout: \"modern\",\n showSidebar: true,\n hideDownloadButton: false,\n cdn: config.globalPrefix + \"/scalar-api-reference/browser/standalone.js\",\n metaData: {\n title: \"Arkos.js API Documentation\",\n description: \"API documentation generated by Arkos.js\",\n },\n pageTitle: \"Arkos.js API Documentation\",\n customCss: `.scalar-client {\nwidth: 100vw !important;\nheight: 100vh !important;\nmax-width: 100vw !important;\nmax-height: 100vh !important;\ntop: 0 !important;\nleft: 0 !important;\ntransform: none !important;\nborder-radius: 0 !important;\n}`,\n },\n };\n}\n"]}
1
+ {"version":3,"file":"get-swagger-default-configs.js","sourceRoot":"","sources":["../../../../../../src/modules/swagger/utils/helpers/get-swagger-default-configs.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AACpD,OAAO,EAAE,UAAU,EAAE,MAAM,wCAAwC,CAAC;AAEpE,MAAM,CAAC,OAAO,UAAU,uBAAuB,CAC7C,kBAAuC;IAEvC,MAAM,WAAW,GAAG;QAClB,IAAI,EAAE,OAAO,CAAC,GAAG,CAAC,MAAM,IAAI,OAAO,CAAC,GAAG,CAAC,IAAI,IAAI,MAAM;QACtD,IAAI,EAAE,OAAO,CAAC,GAAG,CAAC,MAAO,IAAI,OAAO,CAAC,GAAG,CAAC,IAAI,IAAI,WAAW;KAC7D,CAAC;IACF,MAAM,MAAM,GAAG,cAAc,EAAE,CAAC;IAEhC,OAAO;QACL,QAAQ,EAAE,OAAO;QACjB,IAAI,EAAE,QAAQ;QACd,MAAM,EAAE,KAAK;QACb,OAAO,EAAE;YACP,UAAU,EAAE;gBACV,OAAO,EAAE,OAAO;gBAChB,IAAI,EAAE;oBACJ,KAAK,EAAE,qBAAqB;oBAC5B,OAAO,EAAE,OAAO;oBAChB,WAAW,EACT,sFAAsF;iBACzF;gBACD,OAAO,EAAE;oBACP;wBACE,GAAG,EAAE,UAAU,CAAC,SAAS,EAAE,WAAW,EAAE,WAAW,CAAC,CAAC,QAAQ,CAAC,WAAW,CAAC,IAAK,CAAC,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,WAAW,CAAC,IAAI,IAAI,WAAW,CAAC,IAAI,EAAE;wBACvI,WAAW,EAAE,SAAS,UAAU,CAAC,OAAO,CAAC,GAAG,CAAC,QAAQ,IAAI,aAAa,CAAC,SAAS;qBACjF;iBACF;gBACD,KAAK,EAAE,kBAAkB;gBACzB,UAAU,EAAE;oBACV,eAAe,EAAE;wBACf,UAAU,EAAE;4BACV,IAAI,EAAE,MAAM;4BACZ,MAAM,EAAE,QAAQ;4BAChB,YAAY,EAAE,KAAK;yBACpB;qBACF;iBACF;gBACD,QAAQ,EAAE,EAAE;aACb;YACD,IAAI,EAAE,CAAC,2BAA2B,EAAE,0BAA0B,CAAC;YAC/D,WAAW,EAAE,IAAI;YACjB,eAAe,EAAE,IAAI;YACrB,oBAAoB,EAAE,KAAK;SAC5B;QACD,+BAA+B,EAAE;YAC/B,KAAK,EAAE,WAAW;YAClB,QAAQ,EAAE,IAAI;YACd,MAAM,EAAE,QAAQ;YAChB,WAAW,EAAE,IAAI;YACjB,kBAAkB,EAAE,KAAK;YACzB,GAAG,EAAE,MAAM,CAAC,YAAY,GAAG,6CAA6C;YACxE,QAAQ,EAAE;gBACR,KAAK,EAAE,4BAA4B;gBACnC,WAAW,EAAE,yCAAyC;aACvD;YACD,SAAS,EAAE,4BAA4B;YACvC,SAAS,EAAE;;;;;;;;;EASf;SACG;KACF,CAAC;AACJ,CAAC","sourcesContent":["import { getArkosConfig } from \"../../../../server\";\nimport { capitalize } from \"../../../../utils/helpers/text.helpers\";\n\nexport default function getSwaggerDefaultConfig(\n defaultModelsPaths: Record<string, any>,\n) {\n const portAndHost = {\n port: process.env.__PORT || process.env.PORT || \"8000\",\n host: process.env.__HOST! || process.env.HOST || \"127.0.0.1\",\n };\n const config = getArkosConfig();\n\n return {\n endpoint: \"/docs\",\n mode: \"prisma\",\n strict: false,\n options: {\n definition: {\n openapi: \"3.1.0\",\n info: {\n title: \"Powered By Arkos.js\",\n version: \"1.0.0\",\n description:\n \"This API was automatically generated by Arkos.js, read more about at www.arkosjs.com\",\n },\n servers: [\n {\n url: `http://${[\"0.0.0.0\", \"localhost\", \"127.0.0.1\"].includes(portAndHost.host!) ? \"localhost\" : portAndHost.host}:${portAndHost.port}`,\n description: `Local ${capitalize(process.env.NODE_ENV || \"development\")} Server`,\n },\n ],\n paths: defaultModelsPaths,\n components: {\n securitySchemes: {\n BearerAuth: {\n type: \"http\",\n scheme: \"bearer\",\n bearerFormat: \"JWT\",\n },\n },\n },\n security: [],\n },\n apis: [\"./src/**/*.router.{ts,js}\", \"./src/**/*.route.{ts,js}\"],\n deepLinking: true,\n tryItOutEnabled: true,\n persistAuthorization: false,\n },\n scalarApiReferenceConfiguration: {\n theme: \"deepSpace\",\n darkMode: true,\n layout: \"modern\",\n showSidebar: true,\n hideDownloadButton: false,\n cdn: config.globalPrefix + \"/scalar-api-reference/browser/standalone.js\",\n metaData: {\n title: \"Arkos.js API Documentation\",\n description: \"API documentation generated by Arkos.js\",\n },\n pageTitle: \"Arkos.js API Documentation\",\n customCss: `.scalar-app-client {\nwidth: 100vw !important;\nheight: 100vh !important;\nmax-width: 100vw !important;\nmax-height: 100vh !important;\ntop: 0 !important;\nleft: 0 !important;\ntransform: none !important;\nborder-radius: 0 !important;\n}`,\n },\n };\n}\n\n"]}
@@ -1,18 +1,17 @@
1
1
  import { isClass, isZodSchema } from '../../../../utils/dynamic-loader.js';
2
2
  import classValidatorToJsonSchema from './class-validator-to-json-schema.js';
3
- import { getArkosConfig } from '../../../../server.js';
4
3
  import z from 'zod';
5
4
  class OpenAPIchemaConverter {
6
5
  validatorToJsonSchema;
7
6
  constructor() {
8
7
  this.validatorToJsonSchema = (schema) => {
9
- const validationResolver = getArkosConfig()?.validation?.resolver;
10
- const fn = validationResolver === 'zod'
11
- ? (schema) => {
12
- return z.toJSONSchema(schema, { target: 'openapi-3.0' });
13
- }
14
- : classValidatorToJsonSchema;
15
- return fn(schema);
8
+ return isZodSchema(schema)
9
+ ? z.toJSONSchema(schema, {
10
+ target: 'openapi-3.0',
11
+ io: 'input',
12
+ unrepresentable: 'any',
13
+ })
14
+ : classValidatorToJsonSchema(schema);
16
15
  };
17
16
  }
18
17
  isSchemaLike(value) {
@@ -1 +1 @@
1
- {"version":3,"file":"openapi-schema-converter.js","sourceRoot":"","sources":["../../../../../../src/modules/swagger/utils/helpers/openapi-schema-converter.ts"],"names":[],"mappings":"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","sourcesContent":["import { OpenAPIV3 } from 'openapi-types';\nimport { isClass, isZodSchema } from '../../../../utils/dynamic-loader';\nimport classValidatorToJsonSchema from './class-validator-to-json-schema';\nimport { getArkosConfig } from '../../../../server';\nimport z, { ZodType } from 'zod';\n\n/**\n * Singleton class responsible for converting various schema formats (Zod, Class DTOs, JSON Schema)\n * into OpenAPI-compliant schema objects. Handles shorthand syntax for responses, request bodies,\n * and parameters.\n */\nclass OpenAPIchemaConverter {\n private validatorToJsonSchema: (schema: any) => any;\n\n constructor() {\n this.validatorToJsonSchema = (schema: any) => {\n const validationResolver = getArkosConfig()?.validation?.resolver;\n const fn =\n validationResolver === 'zod'\n ? (schema: ZodType) => {\n return z.toJSONSchema(schema, { target: 'openapi-3.0' });\n }\n : classValidatorToJsonSchema;\n\n return fn(schema);\n };\n }\n\n /**\n * Checks if a value is a schema-like object (Zod, Class, or JSON Schema).\n *\n * @param value - The value to check\n * @returns True if the value is a schema-like object\n */\n private isSchemaLike(value: any): boolean {\n if (!value) return false;\n\n if (isZodSchema(value)) return true;\n\n if (isClass(value)) return true;\n\n if (this.isPlainObject(value) && value.type) return true;\n\n return false;\n }\n\n /**\n * Checks if a value is a plain JavaScript object.\n *\n * @param value - The value to check\n * @returns True if the value is a plain object\n */\n private isPlainObject(value: any): boolean {\n return Object.prototype.toString.call(value) === '[object Object]';\n }\n\n /**\n * Converts a schema-like object (Zod, Class, or JSON Schema) into a JSON Schema object.\n *\n * @param schema - The schema to convert\n * @returns The JSON Schema representation, or undefined if schema is null/undefined\n */\n private convertToJsonSchema(schema: any): any {\n if (this.isPlainObject(schema) && schema?.type) return schema;\n\n if (isZodSchema(schema) || isClass(schema))\n return this.validatorToJsonSchema(schema);\n\n throw new Error(\n `Unsupported schema type. Expected Zod schema, class constructor, or JSON Schema object.`,\n );\n }\n\n /**\n * Gets the default description for an HTTP status code.\n *\n * @param statusCode - The HTTP status code\n * @returns A human-readable default description\n */\n private getDefaultDescription(statusCode: string): string {\n const defaults: Record<string, string> = {\n '200': 'Success',\n '201': 'Created',\n '202': 'Accepted',\n '204': 'No Content',\n '400': 'Bad Request',\n '401': 'Unauthorized',\n '403': 'Forbidden',\n '404': 'Not Found',\n '409': 'Conflict',\n '422': 'Unprocessable Entity',\n '500': 'Internal Server Error',\n '502': 'Bad Gateway',\n '503': 'Service Unavailable',\n };\n\n return defaults[statusCode] || 'Response';\n }\n\n /**\n * Core flattening logic that converts a JSON schema into bracket notation.\n * Handles nested objects, arrays, and circular references.\n *\n * @param schema - The JSON schema to flatten\n * @param prefix - Optional prefix for nested properties\n * @param visitedRefs - Set to track circular references\n * @returns Array of flattened field descriptors\n */\n flattenSchemaCore(\n schema: any,\n prefix = '',\n visitedRefs = new Set<string>(),\n ): Array<{ name: string; schema: any; required: boolean }> {\n const flattened: Array<{ name: string; schema: any; required: boolean }> =\n [];\n\n if (schema.$ref) {\n const refPath = schema.$ref;\n if (visitedRefs.has(refPath)) return flattened;\n\n visitedRefs.add(refPath);\n const resolvedSchema = this.resolveRef(schema, refPath);\n if (resolvedSchema) {\n flattened.push(\n ...this.flattenSchemaCore(resolvedSchema, prefix, visitedRefs),\n );\n }\n visitedRefs.delete(refPath);\n return flattened;\n }\n\n if (schema.type === 'array' && schema.items) {\n const arrayPrefix =\n prefix && schema.items?.format === 'binary' ? prefix : `${prefix}[0]`;\n\n // Check if items is an array FIRST, before checking for object\n if (schema.items.type === 'array') {\n flattened.push(\n ...this.flattenSchemaCore(schema.items, arrayPrefix, visitedRefs),\n );\n } else if (schema.items.properties || schema.items.type === 'object') {\n flattened.push(\n ...this.flattenSchemaCore(schema.items, arrayPrefix, visitedRefs),\n );\n } else if (schema.items.type || schema.items.$ref) {\n flattened.push({\n name: arrayPrefix,\n schema: {\n type: schema.items.type,\n ...(schema.items.enum && { enum: schema.items.enum }),\n ...(schema.items.format && { format: schema.items.format }),\n },\n required: false,\n });\n }\n\n return flattened;\n }\n\n if ((schema.type === 'object' || !schema.type) && schema.properties) {\n for (const [key, value] of Object.entries(schema.properties) as any) {\n const paramName = prefix ? `${prefix}[${key}]` : key;\n\n if (value.$ref) {\n const refPath = value.$ref;\n if (visitedRefs.has(refPath)) continue;\n\n visitedRefs.add(refPath);\n const resolvedSchema = this.resolveRef(schema, refPath);\n\n if (resolvedSchema) {\n flattened.push(\n ...this.flattenSchemaCore(resolvedSchema, paramName, visitedRefs),\n );\n }\n visitedRefs.delete(refPath);\n } else if (value.type === 'array' && value.items) {\n const arrayPrefix =\n paramName && value.items?.format == 'binary'\n ? paramName\n : `${paramName}[0]`;\n\n if (value.items.type === 'array') {\n flattened.push(\n ...this.flattenSchemaCore(value.items, arrayPrefix, visitedRefs),\n );\n } else if (value.items.type === 'object' || value.items.properties) {\n flattened.push(\n ...this.flattenSchemaCore(value.items, arrayPrefix, visitedRefs),\n );\n } else {\n flattened.push({\n name: arrayPrefix,\n schema: {\n type: value.items.type,\n ...(value.items.enum && { enum: value.items.enum }),\n ...(value.items.format && { format: value.items.format }),\n },\n required: false,\n });\n }\n } else if (value.type === 'object' && value.properties) {\n flattened.push(\n ...this.flattenSchemaCore(value, paramName, visitedRefs),\n );\n } else {\n flattened.push({\n name: paramName,\n schema: {\n type: value.type,\n ...(value.enum && { enum: value.enum }),\n ...(value.format && { format: value.format }),\n },\n required: schema.required?.includes(key) || false,\n });\n }\n }\n return flattened;\n }\n\n // Handle primitive types with prefix\n if (schema.type && prefix) {\n flattened.push({\n name: prefix,\n schema: {\n type: schema.type,\n ...(schema.enum && { enum: schema.enum }),\n ...(schema.format && { format: schema.format }),\n },\n required: false,\n });\n }\n\n return flattened;\n }\n\n /**\n * Flattens a JSON schema while preserving the schema structure.\n * Converts nested objects and arrays into bracket notation properties.\n * Used for multipart/form-data schema representation.\n *\n * @param schema - The JSON schema to flatten\n * @returns A flattened schema object with bracket notation properties\n */\n flattenSchema(schema: any): any {\n const flattened = this.flattenSchemaCore(schema);\n\n // Convert the flattened array into a schema object\n const flattenedSchema: any = {\n ...schema,\n properties: {},\n required: [],\n };\n\n for (const item of flattened) {\n flattenedSchema.properties[item.name] = item.schema;\n if (item.required) {\n flattenedSchema.required.push(item.name);\n }\n }\n\n // Remove empty required array if no required fields\n if (flattenedSchema.required.length === 0) {\n delete flattenedSchema.required;\n }\n\n return flattenedSchema;\n }\n\n /**\n * Converts a response definition (shorthand, medium, or full format) into a standard\n * OpenAPI ResponseObject.\n *\n * Supports three formats:\n * 1. Shorthand: ProfileDto → assumes application/json + default description\n * 2. Medium: { content: ProfileDto, description: \"...\" } → custom description\n * 3. Full: { content: { \"application/json\": { schema: ProfileDto } } } → full control\n *\n * @param statusCode - The HTTP status code for this response\n * @param definition - The response definition to convert\n * @returns A standard OpenAPI ResponseObject\n */\n convertResponseDefinition(\n statusCode: string,\n definition: any,\n ): OpenAPIV3.ResponseObject {\n if (!definition.content && this.isSchemaLike(definition)) {\n return {\n description: this.getDefaultDescription(statusCode),\n content: {\n 'application/json': {\n schema: this.convertToJsonSchema(definition),\n },\n },\n };\n }\n\n if (definition.content && this.isSchemaLike(definition.content)) {\n return {\n description:\n definition.description || this.getDefaultDescription(statusCode),\n content: {\n 'application/json': {\n schema: this.convertToJsonSchema(definition.content),\n },\n },\n ...(definition.headers && { headers: definition.headers }),\n ...(definition.links && { links: definition.links }),\n };\n }\n\n if (definition.content) {\n const converted: OpenAPIV3.ResponseObject = {\n description:\n definition.description || this.getDefaultDescription(statusCode),\n content: {},\n };\n\n for (const [mediaType, mediaObj] of Object.entries(\n definition.content,\n ) as any[]) {\n converted.content![mediaType] = {\n ...mediaObj,\n schema: this.convertToJsonSchema(mediaObj.schema),\n };\n }\n\n if (definition.headers) converted.headers = definition.headers;\n if (definition.links) converted.links = definition.links;\n\n return converted;\n }\n\n return definition;\n }\n\n /**\n * Converts a request body definition (shorthand, medium, or full format) into a standard\n * OpenAPI RequestBodyObject.\n *\n * Supports three formats:\n * 1. Shorthand: CreateUserDto → assumes application/json + required: true\n * 2. Medium: { content: CreateUserDto, required?: boolean } → custom config\n * 3. Full: { content: { \"application/json\": { schema: CreateUserDto } } } → full control\n *\n * For multipart/form-data, automatically converts the schema into an array of parameters.\n *\n * @param definition - The request body definition to convert\n * @returns A standard OpenAPI RequestBodyObject, or undefined if no definition provided\n */\n convertRequestBodyDefinition(\n definition: any,\n ): OpenAPIV3.RequestBodyObject | undefined {\n if (!definition) return undefined;\n\n if (!definition.content && this.isSchemaLike(definition)) {\n return {\n required: true,\n content: {\n 'application/json': {\n schema: this.convertToJsonSchema(definition),\n },\n },\n };\n }\n\n if (definition.content && this.isSchemaLike(definition.content)) {\n return {\n required: definition.required === false ? false : true,\n description: definition.description,\n content: {\n 'application/json': {\n schema: this.convertToJsonSchema(definition.content),\n },\n },\n };\n }\n\n if (definition.content) {\n const converted: OpenAPIV3.RequestBodyObject = {\n required: definition.required ?? true,\n content: {},\n };\n\n if (definition.description)\n converted.description = definition.description;\n\n for (const [mediaType, mediaObj] of Object.entries(\n definition.content,\n ) as any[]) {\n const jsonSchema = this.convertToJsonSchema(mediaObj.schema);\n\n // For multipart/form-data, flatten the schema into bracket notation\n if (mediaType === 'multipart/form-data') {\n converted.content[mediaType] = {\n ...mediaObj,\n schema: this.flattenSchema(jsonSchema),\n };\n } else {\n // For other media types (like application/json), keep the full schema\n converted.content[mediaType] = {\n ...mediaObj,\n schema: jsonSchema,\n };\n }\n }\n\n return converted;\n }\n\n return definition;\n }\n\n /**\n * Converts an array of parameter definitions, transforming any Zod/Class schemas\n * into JSON Schema objects.\n *\n * @param parameters - Array of parameter definitions to convert\n * @returns Array of standard OpenAPI ParameterObjects, or undefined if no parameters provided\n */\n convertParameters(\n parameters: any[] | undefined,\n ): (OpenAPIV3.ParameterObject | OpenAPIV3.ReferenceObject)[] | undefined {\n if (!parameters) return undefined;\n\n return parameters.map((param) => {\n if (param.$ref) return param;\n\n if (param.schema)\n return {\n ...param,\n schema: this.convertToJsonSchema(param.schema),\n };\n\n return param;\n });\n }\n\n /**\n * Converts all responses in a responses object, handling shorthand, medium, and full formats.\n *\n * @param responses - Object containing response definitions keyed by status code\n * @returns Object with converted OpenAPI ResponseObjects\n */\n convertResponses(\n responses: Record<string, any> | undefined,\n ): Record<string, OpenAPIV3.ResponseObject> | undefined {\n if (!responses) return undefined;\n\n const converted: Record<string, OpenAPIV3.ResponseObject> = {};\n\n for (const [statusCode, definition] of Object.entries(responses)) {\n converted[statusCode] = this.convertResponseDefinition(\n statusCode,\n definition,\n );\n }\n\n return converted;\n }\n\n /**\n * Converts a complete OpenAPI operation configuration, processing responses, requestBody,\n * and parameters to handle shorthand syntax and schema conversions.\n *\n * @param config - The OpenAPI operation configuration to convert\n * @returns The converted configuration with all schemas transformed to JSON Schema\n */\n convertOpenAPIConfig(config: any): any {\n if (!config || config === false) return config;\n\n const converted = { ...config };\n\n if (config.responses)\n converted.responses = this.convertResponses(config.responses);\n\n if (config.requestBody)\n converted.requestBody = this.convertRequestBodyDefinition(\n config.requestBody,\n );\n\n if (config.parameters)\n converted.parameters = this.convertParameters(config.parameters);\n\n return converted;\n }\n\n /**\n * Converts a JSON schema to OpenAPI parameter objects with flattened paths.\n * This is used for query/path/header parameters.\n *\n * @param paramType - The parameter location (query, path, header, cookie)\n * @param schema - The JSON schema to convert\n * @param prefix - Optional prefix for nested properties\n * @param visitedRefs - Set to track circular references\n * @returns Array of OpenAPI parameter objects\n */\n jsonSchemaToOpenApiParameters(\n paramType: string,\n schema: any,\n prefix = '',\n visitedRefs = new Set<string>(),\n ) {\n const flattened = this.flattenSchemaCore(schema, prefix, visitedRefs);\n\n return flattened.map(({ name, schema, required }) => ({\n in: paramType,\n name,\n required,\n schema,\n }));\n }\n\n resolveRef(rootSchema: any, refPath: string): any {\n if (!refPath.startsWith('#/properties/')) {\n return null;\n }\n\n const path = refPath.substring(2).split('/');\n let current = rootSchema;\n\n for (const part of path) {\n if (current && current[part] !== undefined) {\n current = current[part];\n } else {\n return null;\n }\n }\n\n return current;\n }\n}\n\nconst openApiSchemaConverter = new OpenAPIchemaConverter();\n\nexport default openApiSchemaConverter;\n\n"]}
1
+ {"version":3,"file":"openapi-schema-converter.js","sourceRoot":"","sources":["../../../../../../src/modules/swagger/utils/helpers/openapi-schema-converter.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,OAAO,EAAE,WAAW,EAAE,MAAM,kCAAkC,CAAC;AACxE,OAAO,0BAA0B,MAAM,kCAAkC,CAAC;AAC1E,OAAO,CAAC,MAAM,KAAK,CAAC;AAOpB,MAAM,qBAAqB;IACzB,qBAAqB,CAAuB;IAE5C;QACE,IAAI,CAAC,qBAAqB,GAAG,CAAC,MAAW,EAAE,EAAE;YAC3C,OAAO,WAAW,CAAC,MAAM,CAAC;gBACxB,CAAC,CAAC,CAAC,CAAC,YAAY,CAAC,MAAM,EAAE;oBACrB,MAAM,EAAE,aAAa;oBACrB,EAAE,EAAE,OAAO;oBACX,eAAe,EAAE,KAAK;iBACvB,CAAC;gBACJ,CAAC,CAAC,0BAA0B,CAAC,MAAM,CAAC,CAAC;QACzC,CAAC,CAAC;IACJ,CAAC;IAQO,YAAY,CAAC,KAAU;QAC7B,IAAI,CAAC,KAAK;YAAE,OAAO,KAAK,CAAC;QAEzB,IAAI,WAAW,CAAC,KAAK,CAAC;YAAE,OAAO,IAAI,CAAC;QAEpC,IAAI,OAAO,CAAC,KAAK,CAAC;YAAE,OAAO,IAAI,CAAC;QAEhC,IAAI,IAAI,CAAC,aAAa,CAAC,KAAK,CAAC,IAAI,KAAK,CAAC,IAAI;YAAE,OAAO,IAAI,CAAC;QAEzD,OAAO,KAAK,CAAC;IACf,CAAC;IAQO,aAAa,CAAC,KAAU;QAC9B,OAAO,MAAM,CAAC,SAAS,CAAC,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,iBAAiB,CAAC;IACrE,CAAC;IAQO,mBAAmB,CAAC,MAAW;QACrC,IAAI,IAAI,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,MAAM,EAAE,IAAI;YAAE,OAAO,MAAM,CAAC;QAE9D,IAAI,WAAW,CAAC,MAAM,CAAC,IAAI,OAAO,CAAC,MAAM,CAAC;YACxC,OAAO,IAAI,CAAC,qBAAqB,CAAC,MAAM,CAAC,CAAC;QAE5C,MAAM,IAAI,KAAK,CACb,yFAAyF,CAC1F,CAAC;IACJ,CAAC;IAQO,qBAAqB,CAAC,UAAkB;QAC9C,MAAM,QAAQ,GAA2B;YACvC,KAAK,EAAE,SAAS;YAChB,KAAK,EAAE,SAAS;YAChB,KAAK,EAAE,UAAU;YACjB,KAAK,EAAE,YAAY;YACnB,KAAK,EAAE,aAAa;YACpB,KAAK,EAAE,cAAc;YACrB,KAAK,EAAE,WAAW;YAClB,KAAK,EAAE,WAAW;YAClB,KAAK,EAAE,UAAU;YACjB,KAAK,EAAE,sBAAsB;YAC7B,KAAK,EAAE,uBAAuB;YAC9B,KAAK,EAAE,aAAa;YACpB,KAAK,EAAE,qBAAqB;SAC7B,CAAC;QAEF,OAAO,QAAQ,CAAC,UAAU,CAAC,IAAI,UAAU,CAAC;IAC5C,CAAC;IAWD,iBAAiB,CACf,MAAW,EACX,MAAM,GAAG,EAAE,EACX,cAAc,IAAI,GAAG,EAAU;QAE/B,MAAM,SAAS,GACb,EAAE,CAAC;QAEL,IAAI,MAAM,CAAC,IAAI,EAAE,CAAC;YAChB,MAAM,OAAO,GAAG,MAAM,CAAC,IAAI,CAAC;YAC5B,IAAI,WAAW,CAAC,GAAG,CAAC,OAAO,CAAC;gBAAE,OAAO,SAAS,CAAC;YAE/C,WAAW,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;YACzB,MAAM,cAAc,GAAG,IAAI,CAAC,UAAU,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;YACxD,IAAI,cAAc,EAAE,CAAC;gBACnB,SAAS,CAAC,IAAI,CACZ,GAAG,IAAI,CAAC,iBAAiB,CAAC,cAAc,EAAE,MAAM,EAAE,WAAW,CAAC,CAC/D,CAAC;YACJ,CAAC;YACD,WAAW,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;YAC5B,OAAO,SAAS,CAAC;QACnB,CAAC;QAED,IAAI,MAAM,CAAC,IAAI,KAAK,OAAO,IAAI,MAAM,CAAC,KAAK,EAAE,CAAC;YAC5C,MAAM,WAAW,GACf,MAAM,IAAI,MAAM,CAAC,KAAK,EAAE,MAAM,KAAK,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,GAAG,MAAM,KAAK,CAAC;YAGxE,IAAI,MAAM,CAAC,KAAK,CAAC,IAAI,KAAK,OAAO,EAAE,CAAC;gBAClC,SAAS,CAAC,IAAI,CACZ,GAAG,IAAI,CAAC,iBAAiB,CAAC,MAAM,CAAC,KAAK,EAAE,WAAW,EAAE,WAAW,CAAC,CAClE,CAAC;YACJ,CAAC;iBAAM,IAAI,MAAM,CAAC,KAAK,CAAC,UAAU,IAAI,MAAM,CAAC,KAAK,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;gBACrE,SAAS,CAAC,IAAI,CACZ,GAAG,IAAI,CAAC,iBAAiB,CAAC,MAAM,CAAC,KAAK,EAAE,WAAW,EAAE,WAAW,CAAC,CAClE,CAAC;YACJ,CAAC;iBAAM,IAAI,MAAM,CAAC,KAAK,CAAC,IAAI,IAAI,MAAM,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC;gBAClD,SAAS,CAAC,IAAI,CAAC;oBACb,IAAI,EAAE,WAAW;oBACjB,MAAM,EAAE;wBACN,IAAI,EAAE,MAAM,CAAC,KAAK,CAAC,IAAI;wBACvB,GAAG,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,IAAI,EAAE,IAAI,EAAE,MAAM,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC;wBACrD,GAAG,CAAC,MAAM,CAAC,KAAK,CAAC,MAAM,IAAI,EAAE,MAAM,EAAE,MAAM,CAAC,KAAK,CAAC,MAAM,EAAE,CAAC;qBAC5D;oBACD,QAAQ,EAAE,KAAK;iBAChB,CAAC,CAAC;YACL,CAAC;YAED,OAAO,SAAS,CAAC;QACnB,CAAC;QAED,IAAI,CAAC,MAAM,CAAC,IAAI,KAAK,QAAQ,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,MAAM,CAAC,UAAU,EAAE,CAAC;YACpE,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,UAAU,CAAQ,EAAE,CAAC;gBACpE,MAAM,SAAS,GAAG,MAAM,CAAC,CAAC,CAAC,GAAG,MAAM,IAAI,GAAG,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC;gBAErD,IAAI,KAAK,CAAC,IAAI,EAAE,CAAC;oBACf,MAAM,OAAO,GAAG,KAAK,CAAC,IAAI,CAAC;oBAC3B,IAAI,WAAW,CAAC,GAAG,CAAC,OAAO,CAAC;wBAAE,SAAS;oBAEvC,WAAW,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;oBACzB,MAAM,cAAc,GAAG,IAAI,CAAC,UAAU,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;oBAExD,IAAI,cAAc,EAAE,CAAC;wBACnB,SAAS,CAAC,IAAI,CACZ,GAAG,IAAI,CAAC,iBAAiB,CAAC,cAAc,EAAE,SAAS,EAAE,WAAW,CAAC,CAClE,CAAC;oBACJ,CAAC;oBACD,WAAW,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;gBAC9B,CAAC;qBAAM,IAAI,KAAK,CAAC,IAAI,KAAK,OAAO,IAAI,KAAK,CAAC,KAAK,EAAE,CAAC;oBACjD,MAAM,WAAW,GACf,SAAS,IAAI,KAAK,CAAC,KAAK,EAAE,MAAM,IAAI,QAAQ;wBAC1C,CAAC,CAAC,SAAS;wBACX,CAAC,CAAC,GAAG,SAAS,KAAK,CAAC;oBAExB,IAAI,KAAK,CAAC,KAAK,CAAC,IAAI,KAAK,OAAO,EAAE,CAAC;wBACjC,SAAS,CAAC,IAAI,CACZ,GAAG,IAAI,CAAC,iBAAiB,CAAC,KAAK,CAAC,KAAK,EAAE,WAAW,EAAE,WAAW,CAAC,CACjE,CAAC;oBACJ,CAAC;yBAAM,IAAI,KAAK,CAAC,KAAK,CAAC,IAAI,KAAK,QAAQ,IAAI,KAAK,CAAC,KAAK,CAAC,UAAU,EAAE,CAAC;wBACnE,SAAS,CAAC,IAAI,CACZ,GAAG,IAAI,CAAC,iBAAiB,CAAC,KAAK,CAAC,KAAK,EAAE,WAAW,EAAE,WAAW,CAAC,CACjE,CAAC;oBACJ,CAAC;yBAAM,CAAC;wBACN,SAAS,CAAC,IAAI,CAAC;4BACb,IAAI,EAAE,WAAW;4BACjB,MAAM,EAAE;gCACN,IAAI,EAAE,KAAK,CAAC,KAAK,CAAC,IAAI;gCACtB,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,IAAI,EAAE,IAAI,EAAE,KAAK,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC;gCACnD,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC,MAAM,IAAI,EAAE,MAAM,EAAE,KAAK,CAAC,KAAK,CAAC,MAAM,EAAE,CAAC;6BAC1D;4BACD,QAAQ,EAAE,KAAK;yBAChB,CAAC,CAAC;oBACL,CAAC;gBACH,CAAC;qBAAM,IAAI,KAAK,CAAC,IAAI,KAAK,QAAQ,IAAI,KAAK,CAAC,UAAU,EAAE,CAAC;oBACvD,SAAS,CAAC,IAAI,CACZ,GAAG,IAAI,CAAC,iBAAiB,CAAC,KAAK,EAAE,SAAS,EAAE,WAAW,CAAC,CACzD,CAAC;gBACJ,CAAC;qBAAM,CAAC;oBACN,SAAS,CAAC,IAAI,CAAC;wBACb,IAAI,EAAE,SAAS;wBACf,MAAM,EAAE;4BACN,IAAI,EAAE,KAAK,CAAC,IAAI;4BAChB,GAAG,CAAC,KAAK,CAAC,IAAI,IAAI,EAAE,IAAI,EAAE,KAAK,CAAC,IAAI,EAAE,CAAC;4BACvC,GAAG,CAAC,KAAK,CAAC,MAAM,IAAI,EAAE,MAAM,EAAE,KAAK,CAAC,MAAM,EAAE,CAAC;yBAC9C;wBACD,QAAQ,EAAE,MAAM,CAAC,QAAQ,EAAE,QAAQ,CAAC,GAAG,CAAC,IAAI,KAAK;qBAClD,CAAC,CAAC;gBACL,CAAC;YACH,CAAC;YACD,OAAO,SAAS,CAAC;QACnB,CAAC;QAGD,IAAI,MAAM,CAAC,IAAI,IAAI,MAAM,EAAE,CAAC;YAC1B,SAAS,CAAC,IAAI,CAAC;gBACb,IAAI,EAAE,MAAM;gBACZ,MAAM,EAAE;oBACN,IAAI,EAAE,MAAM,CAAC,IAAI;oBACjB,GAAG,CAAC,MAAM,CAAC,IAAI,IAAI,EAAE,IAAI,EAAE,MAAM,CAAC,IAAI,EAAE,CAAC;oBACzC,GAAG,CAAC,MAAM,CAAC,MAAM,IAAI,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC;iBAChD;gBACD,QAAQ,EAAE,KAAK;aAChB,CAAC,CAAC;QACL,CAAC;QAED,OAAO,SAAS,CAAC;IACnB,CAAC;IAUD,aAAa,CAAC,MAAW;QACvB,MAAM,SAAS,GAAG,IAAI,CAAC,iBAAiB,CAAC,MAAM,CAAC,CAAC;QAGjD,MAAM,eAAe,GAAQ;YAC3B,GAAG,MAAM;YACT,UAAU,EAAE,EAAE;YACd,QAAQ,EAAE,EAAE;SACb,CAAC;QAEF,KAAK,MAAM,IAAI,IAAI,SAAS,EAAE,CAAC;YAC7B,eAAe,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,MAAM,CAAC;YACpD,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC;gBAClB,eAAe,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YAC3C,CAAC;QACH,CAAC;QAGD,IAAI,eAAe,CAAC,QAAQ,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAC1C,OAAO,eAAe,CAAC,QAAQ,CAAC;QAClC,CAAC;QAED,OAAO,eAAe,CAAC;IACzB,CAAC;IAeD,yBAAyB,CACvB,UAAkB,EAClB,UAAe;QAEf,IAAI,CAAC,UAAU,CAAC,OAAO,IAAI,IAAI,CAAC,YAAY,CAAC,UAAU,CAAC,EAAE,CAAC;YACzD,OAAO;gBACL,WAAW,EAAE,IAAI,CAAC,qBAAqB,CAAC,UAAU,CAAC;gBACnD,OAAO,EAAE;oBACP,kBAAkB,EAAE;wBAClB,MAAM,EAAE,IAAI,CAAC,mBAAmB,CAAC,UAAU,CAAC;qBAC7C;iBACF;aACF,CAAC;QACJ,CAAC;QAED,IAAI,UAAU,CAAC,OAAO,IAAI,IAAI,CAAC,YAAY,CAAC,UAAU,CAAC,OAAO,CAAC,EAAE,CAAC;YAChE,OAAO;gBACL,WAAW,EACT,UAAU,CAAC,WAAW,IAAI,IAAI,CAAC,qBAAqB,CAAC,UAAU,CAAC;gBAClE,OAAO,EAAE;oBACP,kBAAkB,EAAE;wBAClB,MAAM,EAAE,IAAI,CAAC,mBAAmB,CAAC,UAAU,CAAC,OAAO,CAAC;qBACrD;iBACF;gBACD,GAAG,CAAC,UAAU,CAAC,OAAO,IAAI,EAAE,OAAO,EAAE,UAAU,CAAC,OAAO,EAAE,CAAC;gBAC1D,GAAG,CAAC,UAAU,CAAC,KAAK,IAAI,EAAE,KAAK,EAAE,UAAU,CAAC,KAAK,EAAE,CAAC;aACrD,CAAC;QACJ,CAAC;QAED,IAAI,UAAU,CAAC,OAAO,EAAE,CAAC;YACvB,MAAM,SAAS,GAA6B;gBAC1C,WAAW,EACT,UAAU,CAAC,WAAW,IAAI,IAAI,CAAC,qBAAqB,CAAC,UAAU,CAAC;gBAClE,OAAO,EAAE,EAAE;aACZ,CAAC;YAEF,KAAK,MAAM,CAAC,SAAS,EAAE,QAAQ,CAAC,IAAI,MAAM,CAAC,OAAO,CAChD,UAAU,CAAC,OAAO,CACV,EAAE,CAAC;gBACX,SAAS,CAAC,OAAQ,CAAC,SAAS,CAAC,GAAG;oBAC9B,GAAG,QAAQ;oBACX,MAAM,EAAE,IAAI,CAAC,mBAAmB,CAAC,QAAQ,CAAC,MAAM,CAAC;iBAClD,CAAC;YACJ,CAAC;YAED,IAAI,UAAU,CAAC,OAAO;gBAAE,SAAS,CAAC,OAAO,GAAG,UAAU,CAAC,OAAO,CAAC;YAC/D,IAAI,UAAU,CAAC,KAAK;gBAAE,SAAS,CAAC,KAAK,GAAG,UAAU,CAAC,KAAK,CAAC;YAEzD,OAAO,SAAS,CAAC;QACnB,CAAC;QAED,OAAO,UAAU,CAAC;IACpB,CAAC;IAgBD,4BAA4B,CAC1B,UAAe;QAEf,IAAI,CAAC,UAAU;YAAE,OAAO,SAAS,CAAC;QAElC,IAAI,CAAC,UAAU,CAAC,OAAO,IAAI,IAAI,CAAC,YAAY,CAAC,UAAU,CAAC,EAAE,CAAC;YACzD,OAAO;gBACL,QAAQ,EAAE,IAAI;gBACd,OAAO,EAAE;oBACP,kBAAkB,EAAE;wBAClB,MAAM,EAAE,IAAI,CAAC,mBAAmB,CAAC,UAAU,CAAC;qBAC7C;iBACF;aACF,CAAC;QACJ,CAAC;QAED,IAAI,UAAU,CAAC,OAAO,IAAI,IAAI,CAAC,YAAY,CAAC,UAAU,CAAC,OAAO,CAAC,EAAE,CAAC;YAChE,OAAO;gBACL,QAAQ,EAAE,UAAU,CAAC,QAAQ,KAAK,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI;gBACtD,WAAW,EAAE,UAAU,CAAC,WAAW;gBACnC,OAAO,EAAE;oBACP,kBAAkB,EAAE;wBAClB,MAAM,EAAE,IAAI,CAAC,mBAAmB,CAAC,UAAU,CAAC,OAAO,CAAC;qBACrD;iBACF;aACF,CAAC;QACJ,CAAC;QAED,IAAI,UAAU,CAAC,OAAO,EAAE,CAAC;YACvB,MAAM,SAAS,GAAgC;gBAC7C,QAAQ,EAAE,UAAU,CAAC,QAAQ,IAAI,IAAI;gBACrC,OAAO,EAAE,EAAE;aACZ,CAAC;YAEF,IAAI,UAAU,CAAC,WAAW;gBACxB,SAAS,CAAC,WAAW,GAAG,UAAU,CAAC,WAAW,CAAC;YAEjD,KAAK,MAAM,CAAC,SAAS,EAAE,QAAQ,CAAC,IAAI,MAAM,CAAC,OAAO,CAChD,UAAU,CAAC,OAAO,CACV,EAAE,CAAC;gBACX,MAAM,UAAU,GAAG,IAAI,CAAC,mBAAmB,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;gBAG7D,IAAI,SAAS,KAAK,qBAAqB,EAAE,CAAC;oBACxC,SAAS,CAAC,OAAO,CAAC,SAAS,CAAC,GAAG;wBAC7B,GAAG,QAAQ;wBACX,MAAM,EAAE,IAAI,CAAC,aAAa,CAAC,UAAU,CAAC;qBACvC,CAAC;gBACJ,CAAC;qBAAM,CAAC;oBAEN,SAAS,CAAC,OAAO,CAAC,SAAS,CAAC,GAAG;wBAC7B,GAAG,QAAQ;wBACX,MAAM,EAAE,UAAU;qBACnB,CAAC;gBACJ,CAAC;YACH,CAAC;YAED,OAAO,SAAS,CAAC;QACnB,CAAC;QAED,OAAO,UAAU,CAAC;IACpB,CAAC;IASD,iBAAiB,CACf,UAA6B;QAE7B,IAAI,CAAC,UAAU;YAAE,OAAO,SAAS,CAAC;QAElC,OAAO,UAAU,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE;YAC9B,IAAI,KAAK,CAAC,IAAI;gBAAE,OAAO,KAAK,CAAC;YAE7B,IAAI,KAAK,CAAC,MAAM;gBACd,OAAO;oBACL,GAAG,KAAK;oBACR,MAAM,EAAE,IAAI,CAAC,mBAAmB,CAAC,KAAK,CAAC,MAAM,CAAC;iBAC/C,CAAC;YAEJ,OAAO,KAAK,CAAC;QACf,CAAC,CAAC,CAAC;IACL,CAAC;IAQD,gBAAgB,CACd,SAA0C;QAE1C,IAAI,CAAC,SAAS;YAAE,OAAO,SAAS,CAAC;QAEjC,MAAM,SAAS,GAA6C,EAAE,CAAC;QAE/D,KAAK,MAAM,CAAC,UAAU,EAAE,UAAU,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,EAAE,CAAC;YACjE,SAAS,CAAC,UAAU,CAAC,GAAG,IAAI,CAAC,yBAAyB,CACpD,UAAU,EACV,UAAU,CACX,CAAC;QACJ,CAAC;QAED,OAAO,SAAS,CAAC;IACnB,CAAC;IASD,oBAAoB,CAAC,MAAW;QAC9B,IAAI,CAAC,MAAM,IAAI,MAAM,KAAK,KAAK;YAAE,OAAO,MAAM,CAAC;QAE/C,MAAM,SAAS,GAAG,EAAE,GAAG,MAAM,EAAE,CAAC;QAEhC,IAAI,MAAM,CAAC,SAAS;YAClB,SAAS,CAAC,SAAS,GAAG,IAAI,CAAC,gBAAgB,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;QAEhE,IAAI,MAAM,CAAC,WAAW;YACpB,SAAS,CAAC,WAAW,GAAG,IAAI,CAAC,4BAA4B,CACvD,MAAM,CAAC,WAAW,CACnB,CAAC;QAEJ,IAAI,MAAM,CAAC,UAAU;YACnB,SAAS,CAAC,UAAU,GAAG,IAAI,CAAC,iBAAiB,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC;QAEnE,OAAO,SAAS,CAAC;IACnB,CAAC;IAYD,6BAA6B,CAC3B,SAAiB,EACjB,MAAW,EACX,MAAM,GAAG,EAAE,EACX,cAAc,IAAI,GAAG,EAAU;QAE/B,MAAM,SAAS,GAAG,IAAI,CAAC,iBAAiB,CAAC,MAAM,EAAE,MAAM,EAAE,WAAW,CAAC,CAAC;QAEtE,OAAO,SAAS,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,EAAE,EAAE,CAAC,CAAC;YACpD,EAAE,EAAE,SAAS;YACb,IAAI;YACJ,QAAQ;YACR,MAAM;SACP,CAAC,CAAC,CAAC;IACN,CAAC;IAED,UAAU,CAAC,UAAe,EAAE,OAAe;QACzC,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,eAAe,CAAC,EAAE,CAAC;YACzC,OAAO,IAAI,CAAC;QACd,CAAC;QAED,MAAM,IAAI,GAAG,OAAO,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QAC7C,IAAI,OAAO,GAAG,UAAU,CAAC;QAEzB,KAAK,MAAM,IAAI,IAAI,IAAI,EAAE,CAAC;YACxB,IAAI,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC,KAAK,SAAS,EAAE,CAAC;gBAC3C,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;YAC1B,CAAC;iBAAM,CAAC;gBACN,OAAO,IAAI,CAAC;YACd,CAAC;QACH,CAAC;QAED,OAAO,OAAO,CAAC;IACjB,CAAC;CACF;AAED,MAAM,sBAAsB,GAAG,IAAI,qBAAqB,EAAE,CAAC;AAE3D,eAAe,sBAAsB,CAAC","sourcesContent":["import { OpenAPIV3 } from 'openapi-types';\nimport { isClass, isZodSchema } from '../../../../utils/dynamic-loader';\nimport classValidatorToJsonSchema from './class-validator-to-json-schema';\nimport z from 'zod';\n\n/**\n * Singleton class responsible for converting various schema formats (Zod, Class DTOs, JSON Schema)\n * into OpenAPI-compliant schema objects. Handles shorthand syntax for responses, request bodies,\n * and parameters.\n */\nclass OpenAPIchemaConverter {\n validatorToJsonSchema: (schema: any) => any;\n\n constructor() {\n this.validatorToJsonSchema = (schema: any) => {\n return isZodSchema(schema)\n ? z.toJSONSchema(schema, {\n target: 'openapi-3.0',\n io: 'input',\n unrepresentable: 'any',\n })\n : classValidatorToJsonSchema(schema);\n };\n }\n\n /**\n * Checks if a value is a schema-like object (Zod, Class, or JSON Schema).\n *\n * @param value - The value to check\n * @returns True if the value is a schema-like object\n */\n private isSchemaLike(value: any): boolean {\n if (!value) return false;\n\n if (isZodSchema(value)) return true;\n\n if (isClass(value)) return true;\n\n if (this.isPlainObject(value) && value.type) return true;\n\n return false;\n }\n\n /**\n * Checks if a value is a plain JavaScript object.\n *\n * @param value - The value to check\n * @returns True if the value is a plain object\n */\n private isPlainObject(value: any): boolean {\n return Object.prototype.toString.call(value) === '[object Object]';\n }\n\n /**\n * Converts a schema-like object (Zod, Class, or JSON Schema) into a JSON Schema object.\n *\n * @param schema - The schema to convert\n * @returns The JSON Schema representation, or undefined if schema is null/undefined\n */\n private convertToJsonSchema(schema: any): any {\n if (this.isPlainObject(schema) && schema?.type) return schema;\n\n if (isZodSchema(schema) || isClass(schema))\n return this.validatorToJsonSchema(schema);\n\n throw new Error(\n `Unsupported schema type. Expected Zod schema, class constructor, or JSON Schema object.`,\n );\n }\n\n /**\n * Gets the default description for an HTTP status code.\n *\n * @param statusCode - The HTTP status code\n * @returns A human-readable default description\n */\n private getDefaultDescription(statusCode: string): string {\n const defaults: Record<string, string> = {\n '200': 'Success',\n '201': 'Created',\n '202': 'Accepted',\n '204': 'No Content',\n '400': 'Bad Request',\n '401': 'Unauthorized',\n '403': 'Forbidden',\n '404': 'Not Found',\n '409': 'Conflict',\n '422': 'Unprocessable Entity',\n '500': 'Internal Server Error',\n '502': 'Bad Gateway',\n '503': 'Service Unavailable',\n };\n\n return defaults[statusCode] || 'Response';\n }\n\n /**\n * Core flattening logic that converts a JSON schema into bracket notation.\n * Handles nested objects, arrays, and circular references.\n *\n * @param schema - The JSON schema to flatten\n * @param prefix - Optional prefix for nested properties\n * @param visitedRefs - Set to track circular references\n * @returns Array of flattened field descriptors\n */\n flattenSchemaCore(\n schema: any,\n prefix = '',\n visitedRefs = new Set<string>(),\n ): Array<{ name: string; schema: any; required: boolean }> {\n const flattened: Array<{ name: string; schema: any; required: boolean }> =\n [];\n\n if (schema.$ref) {\n const refPath = schema.$ref;\n if (visitedRefs.has(refPath)) return flattened;\n\n visitedRefs.add(refPath);\n const resolvedSchema = this.resolveRef(schema, refPath);\n if (resolvedSchema) {\n flattened.push(\n ...this.flattenSchemaCore(resolvedSchema, prefix, visitedRefs),\n );\n }\n visitedRefs.delete(refPath);\n return flattened;\n }\n\n if (schema.type === 'array' && schema.items) {\n const arrayPrefix =\n prefix && schema.items?.format === 'binary' ? prefix : `${prefix}[0]`;\n\n // Check if items is an array FIRST, before checking for object\n if (schema.items.type === 'array') {\n flattened.push(\n ...this.flattenSchemaCore(schema.items, arrayPrefix, visitedRefs),\n );\n } else if (schema.items.properties || schema.items.type === 'object') {\n flattened.push(\n ...this.flattenSchemaCore(schema.items, arrayPrefix, visitedRefs),\n );\n } else if (schema.items.type || schema.items.$ref) {\n flattened.push({\n name: arrayPrefix,\n schema: {\n type: schema.items.type,\n ...(schema.items.enum && { enum: schema.items.enum }),\n ...(schema.items.format && { format: schema.items.format }),\n },\n required: false,\n });\n }\n\n return flattened;\n }\n\n if ((schema.type === 'object' || !schema.type) && schema.properties) {\n for (const [key, value] of Object.entries(schema.properties) as any) {\n const paramName = prefix ? `${prefix}[${key}]` : key;\n\n if (value.$ref) {\n const refPath = value.$ref;\n if (visitedRefs.has(refPath)) continue;\n\n visitedRefs.add(refPath);\n const resolvedSchema = this.resolveRef(schema, refPath);\n\n if (resolvedSchema) {\n flattened.push(\n ...this.flattenSchemaCore(resolvedSchema, paramName, visitedRefs),\n );\n }\n visitedRefs.delete(refPath);\n } else if (value.type === 'array' && value.items) {\n const arrayPrefix =\n paramName && value.items?.format == 'binary'\n ? paramName\n : `${paramName}[0]`;\n\n if (value.items.type === 'array') {\n flattened.push(\n ...this.flattenSchemaCore(value.items, arrayPrefix, visitedRefs),\n );\n } else if (value.items.type === 'object' || value.items.properties) {\n flattened.push(\n ...this.flattenSchemaCore(value.items, arrayPrefix, visitedRefs),\n );\n } else {\n flattened.push({\n name: arrayPrefix,\n schema: {\n type: value.items.type,\n ...(value.items.enum && { enum: value.items.enum }),\n ...(value.items.format && { format: value.items.format }),\n },\n required: false,\n });\n }\n } else if (value.type === 'object' && value.properties) {\n flattened.push(\n ...this.flattenSchemaCore(value, paramName, visitedRefs),\n );\n } else {\n flattened.push({\n name: paramName,\n schema: {\n type: value.type,\n ...(value.enum && { enum: value.enum }),\n ...(value.format && { format: value.format }),\n },\n required: schema.required?.includes(key) || false,\n });\n }\n }\n return flattened;\n }\n\n // Handle primitive types with prefix\n if (schema.type && prefix) {\n flattened.push({\n name: prefix,\n schema: {\n type: schema.type,\n ...(schema.enum && { enum: schema.enum }),\n ...(schema.format && { format: schema.format }),\n },\n required: false,\n });\n }\n\n return flattened;\n }\n\n /**\n * Flattens a JSON schema while preserving the schema structure.\n * Converts nested objects and arrays into bracket notation properties.\n * Used for multipart/form-data schema representation.\n *\n * @param schema - The JSON schema to flatten\n * @returns A flattened schema object with bracket notation properties\n */\n flattenSchema(schema: any): any {\n const flattened = this.flattenSchemaCore(schema);\n\n // Convert the flattened array into a schema object\n const flattenedSchema: any = {\n ...schema,\n properties: {},\n required: [],\n };\n\n for (const item of flattened) {\n flattenedSchema.properties[item.name] = item.schema;\n if (item.required) {\n flattenedSchema.required.push(item.name);\n }\n }\n\n // Remove empty required array if no required fields\n if (flattenedSchema.required.length === 0) {\n delete flattenedSchema.required;\n }\n\n return flattenedSchema;\n }\n\n /**\n * Converts a response definition (shorthand, medium, or full format) into a standard\n * OpenAPI ResponseObject.\n *\n * Supports three formats:\n * 1. Shorthand: ProfileDto → assumes application/json + default description\n * 2. Medium: { content: ProfileDto, description: \"...\" } → custom description\n * 3. Full: { content: { \"application/json\": { schema: ProfileDto } } } → full control\n *\n * @param statusCode - The HTTP status code for this response\n * @param definition - The response definition to convert\n * @returns A standard OpenAPI ResponseObject\n */\n convertResponseDefinition(\n statusCode: string,\n definition: any,\n ): OpenAPIV3.ResponseObject {\n if (!definition.content && this.isSchemaLike(definition)) {\n return {\n description: this.getDefaultDescription(statusCode),\n content: {\n 'application/json': {\n schema: this.convertToJsonSchema(definition),\n },\n },\n };\n }\n\n if (definition.content && this.isSchemaLike(definition.content)) {\n return {\n description:\n definition.description || this.getDefaultDescription(statusCode),\n content: {\n 'application/json': {\n schema: this.convertToJsonSchema(definition.content),\n },\n },\n ...(definition.headers && { headers: definition.headers }),\n ...(definition.links && { links: definition.links }),\n };\n }\n\n if (definition.content) {\n const converted: OpenAPIV3.ResponseObject = {\n description:\n definition.description || this.getDefaultDescription(statusCode),\n content: {},\n };\n\n for (const [mediaType, mediaObj] of Object.entries(\n definition.content,\n ) as any[]) {\n converted.content![mediaType] = {\n ...mediaObj,\n schema: this.convertToJsonSchema(mediaObj.schema),\n };\n }\n\n if (definition.headers) converted.headers = definition.headers;\n if (definition.links) converted.links = definition.links;\n\n return converted;\n }\n\n return definition;\n }\n\n /**\n * Converts a request body definition (shorthand, medium, or full format) into a standard\n * OpenAPI RequestBodyObject.\n *\n * Supports three formats:\n * 1. Shorthand: CreateUserDto → assumes application/json + required: true\n * 2. Medium: { content: CreateUserDto, required?: boolean } → custom config\n * 3. Full: { content: { \"application/json\": { schema: CreateUserDto } } } → full control\n *\n * For multipart/form-data, automatically converts the schema into an array of parameters.\n *\n * @param definition - The request body definition to convert\n * @returns A standard OpenAPI RequestBodyObject, or undefined if no definition provided\n */\n convertRequestBodyDefinition(\n definition: any,\n ): OpenAPIV3.RequestBodyObject | undefined {\n if (!definition) return undefined;\n\n if (!definition.content && this.isSchemaLike(definition)) {\n return {\n required: true,\n content: {\n 'application/json': {\n schema: this.convertToJsonSchema(definition),\n },\n },\n };\n }\n\n if (definition.content && this.isSchemaLike(definition.content)) {\n return {\n required: definition.required === false ? false : true,\n description: definition.description,\n content: {\n 'application/json': {\n schema: this.convertToJsonSchema(definition.content),\n },\n },\n };\n }\n\n if (definition.content) {\n const converted: OpenAPIV3.RequestBodyObject = {\n required: definition.required ?? true,\n content: {},\n };\n\n if (definition.description)\n converted.description = definition.description;\n\n for (const [mediaType, mediaObj] of Object.entries(\n definition.content,\n ) as any[]) {\n const jsonSchema = this.convertToJsonSchema(mediaObj.schema);\n\n // For multipart/form-data, flatten the schema into bracket notation\n if (mediaType === 'multipart/form-data') {\n converted.content[mediaType] = {\n ...mediaObj,\n schema: this.flattenSchema(jsonSchema),\n };\n } else {\n // For other media types (like application/json), keep the full schema\n converted.content[mediaType] = {\n ...mediaObj,\n schema: jsonSchema,\n };\n }\n }\n\n return converted;\n }\n\n return definition;\n }\n\n /**\n * Converts an array of parameter definitions, transforming any Zod/Class schemas\n * into JSON Schema objects.\n *\n * @param parameters - Array of parameter definitions to convert\n * @returns Array of standard OpenAPI ParameterObjects, or undefined if no parameters provided\n */\n convertParameters(\n parameters: any[] | undefined,\n ): (OpenAPIV3.ParameterObject | OpenAPIV3.ReferenceObject)[] | undefined {\n if (!parameters) return undefined;\n\n return parameters.map((param) => {\n if (param.$ref) return param;\n\n if (param.schema)\n return {\n ...param,\n schema: this.convertToJsonSchema(param.schema),\n };\n\n return param;\n });\n }\n\n /**\n * Converts all responses in a responses object, handling shorthand, medium, and full formats.\n *\n * @param responses - Object containing response definitions keyed by status code\n * @returns Object with converted OpenAPI ResponseObjects\n */\n convertResponses(\n responses: Record<string, any> | undefined,\n ): Record<string, OpenAPIV3.ResponseObject> | undefined {\n if (!responses) return undefined;\n\n const converted: Record<string, OpenAPIV3.ResponseObject> = {};\n\n for (const [statusCode, definition] of Object.entries(responses)) {\n converted[statusCode] = this.convertResponseDefinition(\n statusCode,\n definition,\n );\n }\n\n return converted;\n }\n\n /**\n * Converts a complete OpenAPI operation configuration, processing responses, requestBody,\n * and parameters to handle shorthand syntax and schema conversions.\n *\n * @param config - The OpenAPI operation configuration to convert\n * @returns The converted configuration with all schemas transformed to JSON Schema\n */\n convertOpenAPIConfig(config: any): any {\n if (!config || config === false) return config;\n\n const converted = { ...config };\n\n if (config.responses)\n converted.responses = this.convertResponses(config.responses);\n\n if (config.requestBody)\n converted.requestBody = this.convertRequestBodyDefinition(\n config.requestBody,\n );\n\n if (config.parameters)\n converted.parameters = this.convertParameters(config.parameters);\n\n return converted;\n }\n\n /**\n * Converts a JSON schema to OpenAPI parameter objects with flattened paths.\n * This is used for query/path/header parameters.\n *\n * @param paramType - The parameter location (query, path, header, cookie)\n * @param schema - The JSON schema to convert\n * @param prefix - Optional prefix for nested properties\n * @param visitedRefs - Set to track circular references\n * @returns Array of OpenAPI parameter objects\n */\n jsonSchemaToOpenApiParameters(\n paramType: string,\n schema: any,\n prefix = '',\n visitedRefs = new Set<string>(),\n ) {\n const flattened = this.flattenSchemaCore(schema, prefix, visitedRefs);\n\n return flattened.map(({ name, schema, required }) => ({\n in: paramType,\n name,\n required,\n schema,\n }));\n }\n\n resolveRef(rootSchema: any, refPath: string): any {\n if (!refPath.startsWith('#/properties/')) {\n return null;\n }\n\n const path = refPath.substring(2).split('/');\n let current = rootSchema;\n\n for (const part of path) {\n if (current && current[part] !== undefined) {\n current = current[part];\n } else {\n return null;\n }\n }\n\n return current;\n }\n}\n\nconst openApiSchemaConverter = new OpenAPIchemaConverter();\n\nexport default openApiSchemaConverter;\n\n"]}