azurajs 3.0.2 → 3.0.4

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 (120) hide show
  1. package/README.md +32 -0
  2. package/dist/IpResolver-BVgnGnpf.d.mts +5 -0
  3. package/dist/IpResolver-BVgnGnpf.d.ts +5 -0
  4. package/dist/SwaggerPlugin-C0UZTjaZ.d.ts +6 -0
  5. package/dist/SwaggerPlugin-wr9S4SRG.d.mts +6 -0
  6. package/dist/cookies/index.d.mts +7 -0
  7. package/dist/cookies/index.d.ts +7 -0
  8. package/dist/cookies/index.js +38 -0
  9. package/dist/cookies/index.js.map +1 -0
  10. package/dist/cookies/index.mjs +35 -0
  11. package/dist/cookies/index.mjs.map +1 -0
  12. package/dist/core/index.d.mts +18 -27
  13. package/dist/core/index.d.ts +18 -27
  14. package/dist/core/index.js +214 -14
  15. package/dist/core/index.js.map +1 -1
  16. package/dist/core/index.mjs +214 -14
  17. package/dist/core/index.mjs.map +1 -1
  18. package/dist/cors/index.d.mts +7 -0
  19. package/dist/cors/index.d.ts +7 -0
  20. package/dist/cors/index.js +52 -0
  21. package/dist/cors/index.js.map +1 -0
  22. package/dist/cors/index.mjs +50 -0
  23. package/dist/cors/index.mjs.map +1 -0
  24. package/dist/decorators/index.d.mts +2 -0
  25. package/dist/decorators/index.d.ts +2 -0
  26. package/dist/decorators/index.js +25 -0
  27. package/dist/decorators/index.js.map +1 -1
  28. package/dist/decorators/index.mjs +24 -1
  29. package/dist/decorators/index.mjs.map +1 -1
  30. package/dist/decorators-B6l3CbxC.d.ts +13 -0
  31. package/dist/decorators-D5nY109r.d.mts +13 -0
  32. package/dist/http-error/index.d.mts +18 -0
  33. package/dist/http-error/index.d.ts +18 -0
  34. package/dist/http-error/index.js +81 -0
  35. package/dist/http-error/index.js.map +1 -0
  36. package/dist/http-error/index.mjs +79 -0
  37. package/dist/http-error/index.mjs.map +1 -0
  38. package/dist/index-j6QGMhZU.d.mts +30 -0
  39. package/dist/index-tpPZS_UK.d.ts +30 -0
  40. package/dist/index.d.mts +16 -5
  41. package/dist/index.d.ts +16 -5
  42. package/dist/index.js +1178 -14
  43. package/dist/index.js.map +1 -1
  44. package/dist/index.mjs +1176 -15
  45. package/dist/index.mjs.map +1 -1
  46. package/dist/infra/index.d.mts +6 -0
  47. package/dist/infra/index.d.ts +6 -0
  48. package/dist/infra/index.js +162 -0
  49. package/dist/infra/index.js.map +1 -0
  50. package/dist/infra/index.mjs +159 -0
  51. package/dist/infra/index.mjs.map +1 -0
  52. package/dist/{Logger-iEQNVVSc.d.mts → logger/index.d.mts} +2 -1
  53. package/dist/{Logger-iEQNVVSc.d.ts → logger/index.d.ts} +2 -1
  54. package/dist/logger/index.js +123 -0
  55. package/dist/logger/index.js.map +1 -0
  56. package/dist/logger/index.mjs +120 -0
  57. package/dist/logger/index.mjs.map +1 -0
  58. package/dist/plugins/index.d.mts +8 -6
  59. package/dist/plugins/index.d.ts +8 -6
  60. package/dist/plugins/index.js +1101 -0
  61. package/dist/plugins/index.js.map +1 -1
  62. package/dist/plugins/index.mjs +1101 -1
  63. package/dist/plugins/index.mjs.map +1 -1
  64. package/dist/rate-limit/index.d.mts +7 -0
  65. package/dist/rate-limit/index.d.ts +7 -0
  66. package/dist/rate-limit/index.js +81 -0
  67. package/dist/rate-limit/index.js.map +1 -0
  68. package/dist/rate-limit/index.mjs +79 -0
  69. package/dist/rate-limit/index.mjs.map +1 -0
  70. package/dist/router/index.d.mts +4 -0
  71. package/dist/router/index.d.ts +4 -0
  72. package/dist/router/index.js +218 -0
  73. package/dist/router/index.js.map +1 -0
  74. package/dist/router/index.mjs +216 -0
  75. package/dist/router/index.mjs.map +1 -0
  76. package/dist/routes.type-DZO5VBW2.d.mts +58 -0
  77. package/dist/routes.type-DzHNkCag.d.ts +58 -0
  78. package/dist/swagger/index.d.mts +30 -0
  79. package/dist/swagger/index.d.ts +30 -0
  80. package/dist/swagger/index.js +1136 -0
  81. package/dist/swagger/index.js.map +1 -0
  82. package/dist/swagger/index.mjs +1126 -0
  83. package/dist/swagger/index.mjs.map +1 -0
  84. package/dist/swagger/swagger-ui-modern.html +894 -0
  85. package/dist/swagger.type-Bfn5nGR8.d.mts +42 -0
  86. package/dist/swagger.type-CfDbFCZC.d.ts +42 -0
  87. package/dist/types/index.d.mts +5 -58
  88. package/dist/types/index.d.ts +5 -58
  89. package/dist/utils/index.d.mts +7 -72
  90. package/dist/utils/index.d.ts +7 -72
  91. package/dist/validators/index.d.mts +48 -0
  92. package/dist/validators/index.d.ts +48 -0
  93. package/dist/validators/index.js +144 -0
  94. package/dist/validators/index.js.map +1 -0
  95. package/dist/validators/index.mjs +141 -0
  96. package/dist/validators/index.mjs.map +1 -0
  97. package/package.json +86 -2
  98. package/src/cookies/index.ts +1 -0
  99. package/src/core/index.ts +1 -0
  100. package/src/core/router.ts +26 -15
  101. package/src/core/server.ts +64 -14
  102. package/src/cors/index.ts +2 -0
  103. package/src/decorators/index.ts +2 -0
  104. package/src/http-error/index.ts +1 -0
  105. package/src/infra/index.ts +3 -0
  106. package/src/logger/index.ts +1 -0
  107. package/src/plugins/SwaggerPlugin.ts +45 -0
  108. package/src/plugins/index.ts +1 -0
  109. package/src/rate-limit/index.ts +2 -0
  110. package/src/router/index.ts +1 -0
  111. package/src/swagger/constants.ts +8 -0
  112. package/src/swagger/decorators.ts +35 -0
  113. package/src/swagger/index.ts +10 -0
  114. package/src/swagger/openapi-builder.ts +199 -0
  115. package/src/swagger/swagger-ui-html.ts +24 -0
  116. package/src/swagger/swagger-ui-modern.html +894 -0
  117. package/src/swagger/swagger-ui-template.ts +5 -0
  118. package/src/types/index.ts +7 -0
  119. package/src/types/swagger.type.ts +36 -0
  120. package/src/validators/index.ts +4 -0
@@ -181,31 +181,37 @@ var Router = class {
181
181
  return path;
182
182
  }
183
183
  getAllRoutes() {
184
+ return this.getRouteDocuments().map(({ method, path }) => ({ method, path }));
185
+ }
186
+ /**
187
+ * Todas as rotas com metadados (ex.: OpenAPI) associados ao handler.
188
+ */
189
+ getRouteDocuments() {
184
190
  const routes = [];
185
- for (const [key] of this.staticRoutes) {
191
+ for (const [key, stored] of this.staticRoutes) {
186
192
  const [method, path] = key.split(":", 2);
187
- routes.push({ method, path });
193
+ routes.push({ method, path, meta: stored.meta });
188
194
  }
189
- this.collectRoutes(this.root, "", routes);
195
+ this.collectRoutesWithMeta(this.root, "", routes);
190
196
  return routes;
191
197
  }
192
- collectRoutes(node, prefix, routes) {
193
- for (const [method] of node.handlers) {
194
- routes.push({ method, path: prefix || "/" });
198
+ collectRoutesWithMeta(node, prefix, routes) {
199
+ for (const [method, stored] of node.handlers) {
200
+ routes.push({ method, path: prefix || "/", meta: stored.meta });
195
201
  }
196
202
  for (const [seg, child] of node.children) {
197
- this.collectRoutes(child, `${prefix}/${seg}`, routes);
203
+ this.collectRoutesWithMeta(child, `${prefix}/${seg}`, routes);
198
204
  }
199
205
  if (node.paramChild) {
200
- this.collectRoutes(
206
+ this.collectRoutesWithMeta(
201
207
  node.paramChild,
202
208
  `${prefix}/:${node.paramChild.paramName}`,
203
209
  routes
204
210
  );
205
211
  }
206
212
  if (node.wildcardHandler) {
207
- for (const [method] of node.wildcardHandler) {
208
- routes.push({ method, path: `${prefix}/*` });
213
+ for (const [method, stored] of node.wildcardHandler) {
214
+ routes.push({ method, path: `${prefix}/*`, meta: stored.meta });
209
215
  }
210
216
  }
211
217
  }
@@ -613,7 +619,174 @@ function clearCookieHeader(name, options = {}) {
613
619
  });
614
620
  }
615
621
 
622
+ // src/swagger/constants.ts
623
+ var API_TAGS_KEY = "azura:api-tags";
624
+ var METHOD_TAGS_KEY = "azura:swagger-method-tags";
625
+ var API_OPERATION_KEY = "azura:swagger-operation";
626
+
627
+ // src/swagger/openapi-builder.ts
628
+ function resolveInfo(options) {
629
+ return options.info ?? {
630
+ title: "AzuraJS API",
631
+ version: "1.0.0",
632
+ description: "Generated OpenAPI specification"
633
+ };
634
+ }
635
+ function toOpenApiPath(path) {
636
+ if (path === "/") return "/";
637
+ const mapped = path.split("/").map((seg) => {
638
+ if (seg === "*") return "{wildcard}";
639
+ return seg.startsWith(":") ? `{${seg.slice(1)}}` : seg;
640
+ }).join("/").replace(/\/{2,}/g, "/");
641
+ if (mapped.endsWith("/*")) {
642
+ return mapped.slice(0, -2) + "/{wildcard}";
643
+ }
644
+ return mapped;
645
+ }
646
+ function pathParametersFromTemplate(path) {
647
+ const re = /\{([^}]+)\}/g;
648
+ const out = [];
649
+ let m;
650
+ const seen = /* @__PURE__ */ new Set();
651
+ while ((m = re.exec(path)) !== null) {
652
+ const name = m[1];
653
+ if (seen.has(name)) continue;
654
+ seen.add(name);
655
+ out.push({
656
+ name,
657
+ in: "path",
658
+ required: true,
659
+ schema: { type: "string" }
660
+ });
661
+ }
662
+ return out;
663
+ }
664
+ function defaultResponses(meta) {
665
+ if (meta?.responses && Object.keys(meta.responses).length > 0) {
666
+ const out = {};
667
+ for (const [code, r] of Object.entries(meta.responses)) {
668
+ out[String(code)] = {
669
+ description: r.description,
670
+ ...r.schema ? { content: { "application/json": { schema: r.schema } } } : {}
671
+ };
672
+ }
673
+ return out;
674
+ }
675
+ return {
676
+ "200": {
677
+ description: "Successful response",
678
+ content: {
679
+ "application/json": {
680
+ schema: { type: "object", additionalProperties: true }
681
+ }
682
+ }
683
+ }
684
+ };
685
+ }
686
+ function mergeParameters(meta, pathParams) {
687
+ const fromMeta = meta?.parameters ?? [];
688
+ const names = new Set(pathParams.map((p) => p.name));
689
+ const rest = fromMeta.filter((p) => !(p.in === "path" && names.has(p.name)));
690
+ const mapped = fromMeta.filter((p) => p.in === "path").map((p) => ({
691
+ name: p.name,
692
+ in: "path",
693
+ required: p.required ?? true,
694
+ description: p.description,
695
+ schema: p.schema ?? { type: p.type ?? "string" }
696
+ }));
697
+ const pathMerged = [...pathParams];
698
+ for (const m of mapped) {
699
+ if (!pathMerged.find((x) => x.name === m.name)) pathMerged.push(m);
700
+ }
701
+ return [...pathMerged, ...rest.map((p) => mapParameter(p))];
702
+ }
703
+ function mapParameter(p) {
704
+ const base = {
705
+ name: p.name,
706
+ in: p.in,
707
+ required: p.required ?? (p.in === "path" ? true : false),
708
+ description: p.description
709
+ };
710
+ if (p.in === "body") {
711
+ return {
712
+ ...base,
713
+ content: {
714
+ "application/json": {
715
+ schema: p.schema ?? { type: "object" }
716
+ }
717
+ }
718
+ };
719
+ }
720
+ return {
721
+ ...base,
722
+ schema: p.schema ?? { type: p.type ?? "string" }
723
+ };
724
+ }
725
+ function operationIdFor(method, oPath) {
726
+ const slug = oPath.replace(/[/{}\-*]/g, "_").replace(/_+/g, "_").replace(/^_|_$/g, "");
727
+ return `${method.toLowerCase()}_${slug || "root"}`;
728
+ }
729
+ function operationForMethod(method, oPath, meta) {
730
+ const op = {
731
+ operationId: operationIdFor(method, oPath),
732
+ summary: meta?.summary ?? `${method} ${oPath}`,
733
+ ...meta?.description ? { description: meta.description } : {},
734
+ ...meta?.deprecated ? { deprecated: true } : {},
735
+ ...meta?.tags?.length ? { tags: meta.tags } : {},
736
+ ...meta?.security?.length ? { security: meta.security } : {}
737
+ };
738
+ if (["POST", "PUT", "PATCH"].includes(method) && !meta?.parameters?.some((x) => x.in === "body")) {
739
+ op.requestBody = {
740
+ content: {
741
+ "application/json": {
742
+ schema: { type: "object", additionalProperties: true }
743
+ }
744
+ },
745
+ required: false
746
+ };
747
+ }
748
+ op.responses = defaultResponses(meta);
749
+ return op;
750
+ }
751
+ function buildOpenApiDocument(routes, options) {
752
+ const info = resolveInfo(options);
753
+ const prefix = options.pathPrefix ?? "";
754
+ const paths = {};
755
+ const seen = /* @__PURE__ */ new Set();
756
+ for (const r of routes) {
757
+ const full = prefix + r.path;
758
+ const oPath = toOpenApiPath(full);
759
+ const key = `${r.method}:${oPath}`;
760
+ if (seen.has(key)) continue;
761
+ seen.add(key);
762
+ const pathParams = pathParametersFromTemplate(oPath);
763
+ const op = operationForMethod(r.method, oPath, r.meta);
764
+ const parameters = mergeParameters(r.meta, pathParams);
765
+ if (parameters.length) op.parameters = parameters;
766
+ const methodLc = r.method.toLowerCase();
767
+ if (!paths[oPath]) paths[oPath] = {};
768
+ paths[oPath][methodLc] = op;
769
+ }
770
+ return {
771
+ openapi: "3.0.3",
772
+ info: {
773
+ title: info.title,
774
+ version: info.version,
775
+ ...info.description ? { description: info.description } : {},
776
+ ...info.contact ? { contact: info.contact } : {},
777
+ ...info.license ? { license: info.license } : {}
778
+ },
779
+ ...options.servers?.length ? { servers: options.servers } : {},
780
+ paths
781
+ };
782
+ }
783
+
616
784
  // src/core/server.ts
785
+ function isRouteDocumentMeta(o) {
786
+ if (!o || typeof o !== "object" || Array.isArray(o)) return false;
787
+ const x = o;
788
+ return "summary" in x || "description" in x || "tags" in x || "deprecated" in x || "parameters" in x || "responses" in x || "security" in x || "produces" in x || "consumes" in x;
789
+ }
617
790
  var CONTROLLER_META_KEY = "azura:controller";
618
791
  var ROUTES_META_KEY = "azura:routes";
619
792
  var PARAMS_META_KEY = "azura:params";
@@ -702,9 +875,19 @@ var AzuraServer = class {
702
875
  return this;
703
876
  }
704
877
  route(method, path, handlers) {
705
- const routeHandler = handlers[handlers.length - 1];
706
- const middlewares = handlers.slice(0, -1);
707
- this.router.add(method, path, routeHandler, middlewares);
878
+ let meta;
879
+ let list = handlers;
880
+ const last = list[list.length - 1];
881
+ if (list.length >= 2 && isRouteDocumentMeta(last)) {
882
+ meta = last;
883
+ list = list.slice(0, -1);
884
+ }
885
+ if (list.length === 0) {
886
+ throw new Error(`Route ${method} ${path} requires a handler function`);
887
+ }
888
+ const routeHandler = list[list.length - 1];
889
+ const middlewares = list.slice(0, -1);
890
+ this.router.add(method, path, routeHandler, middlewares, meta);
708
891
  return this;
709
892
  }
710
893
  register(...controllers) {
@@ -717,6 +900,7 @@ var AzuraServer = class {
717
900
  const instance = new Controller();
718
901
  const prefix = Reflect.getMetadata?.(CONTROLLER_META_KEY, Controller) ?? "";
719
902
  const controllerMiddlewares = Reflect.getMetadata?.("azura:middlewares", Controller) ?? [];
903
+ const controllerTags = Reflect.getMetadata?.(API_TAGS_KEY, Controller) ?? [];
720
904
  const prototype = Controller.prototype;
721
905
  const propertyNames = Object.getOwnPropertyNames(prototype).filter(
722
906
  (p) => p !== "constructor"
@@ -732,6 +916,14 @@ var AzuraServer = class {
732
916
  const paramsMeta = Reflect.getMetadata?.(PARAMS_META_KEY, prototype, propertyKey) ?? [];
733
917
  const routeMiddlewares = Reflect.getMetadata?.("azura:middlewares", prototype, propertyKey) ?? [];
734
918
  const allMiddlewares = [...controllerMiddlewares, ...routeMiddlewares];
919
+ const methodTags = Reflect.getMetadata?.(METHOD_TAGS_KEY, prototype, propertyKey) ?? [];
920
+ const opExtra = Reflect.getMetadata?.(API_OPERATION_KEY, prototype, propertyKey) ?? {};
921
+ const tagList = [...controllerTags, ...methodTags, ...routeMeta.meta?.tags ?? []];
922
+ const mergedMeta = (() => {
923
+ const m = { ...routeMeta.meta, ...opExtra };
924
+ if (tagList.length) m.tags = tagList;
925
+ return Object.keys(m).length ? m : void 0;
926
+ })();
735
927
  const handler = async (req, res) => {
736
928
  const args = this.resolveParams(paramsMeta, req, res);
737
929
  const result = await instance[propertyKey](...args);
@@ -743,7 +935,7 @@ var AzuraServer = class {
743
935
  }
744
936
  }
745
937
  };
746
- this.router.add(routeMeta.method, fullPath, handler, allMiddlewares, routeMeta.meta);
938
+ this.router.add(routeMeta.method, fullPath, handler, allMiddlewares, mergedMeta);
747
939
  }
748
940
  }
749
941
  resolveParams(paramsMeta, req, res) {
@@ -1097,6 +1289,14 @@ var AzuraServer = class {
1097
1289
  getRoutes() {
1098
1290
  return this.router.getAllRoutes();
1099
1291
  }
1292
+ /** Rotas com metadados OpenAPI (para Swagger ou export manual). */
1293
+ getRouteDocuments() {
1294
+ return this.router.getRouteDocuments();
1295
+ }
1296
+ /** Gera documento OpenAPI 3.0 a partir das rotas registadas. */
1297
+ getOpenApiDocument(options = {}) {
1298
+ return buildOpenApiDocument(this.getRouteDocuments(), options);
1299
+ }
1100
1300
  };
1101
1301
 
1102
1302
  export { AzuraServer, Router };