azurajs 3.0.3 → 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 (113) 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 +17 -26
  13. package/dist/core/index.d.ts +17 -26
  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 +15 -4
  41. package/dist/index.d.ts +15 -4
  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/plugins/index.d.mts +8 -6
  53. package/dist/plugins/index.d.ts +8 -6
  54. package/dist/plugins/index.js +1101 -0
  55. package/dist/plugins/index.js.map +1 -1
  56. package/dist/plugins/index.mjs +1101 -1
  57. package/dist/plugins/index.mjs.map +1 -1
  58. package/dist/rate-limit/index.d.mts +7 -0
  59. package/dist/rate-limit/index.d.ts +7 -0
  60. package/dist/rate-limit/index.js +81 -0
  61. package/dist/rate-limit/index.js.map +1 -0
  62. package/dist/rate-limit/index.mjs +79 -0
  63. package/dist/rate-limit/index.mjs.map +1 -0
  64. package/dist/router/index.d.mts +4 -0
  65. package/dist/router/index.d.ts +4 -0
  66. package/dist/router/index.js +218 -0
  67. package/dist/router/index.js.map +1 -0
  68. package/dist/router/index.mjs +216 -0
  69. package/dist/router/index.mjs.map +1 -0
  70. package/dist/routes.type-DZO5VBW2.d.mts +58 -0
  71. package/dist/routes.type-DzHNkCag.d.ts +58 -0
  72. package/dist/swagger/index.d.mts +30 -0
  73. package/dist/swagger/index.d.ts +30 -0
  74. package/dist/swagger/index.js +1136 -0
  75. package/dist/swagger/index.js.map +1 -0
  76. package/dist/swagger/index.mjs +1126 -0
  77. package/dist/swagger/index.mjs.map +1 -0
  78. package/dist/swagger/swagger-ui-modern.html +894 -0
  79. package/dist/swagger.type-Bfn5nGR8.d.mts +42 -0
  80. package/dist/swagger.type-CfDbFCZC.d.ts +42 -0
  81. package/dist/types/index.d.mts +5 -58
  82. package/dist/types/index.d.ts +5 -58
  83. package/dist/utils/index.d.mts +6 -71
  84. package/dist/utils/index.d.ts +6 -71
  85. package/dist/validators/index.d.mts +48 -0
  86. package/dist/validators/index.d.ts +48 -0
  87. package/dist/validators/index.js +144 -0
  88. package/dist/validators/index.js.map +1 -0
  89. package/dist/validators/index.mjs +141 -0
  90. package/dist/validators/index.mjs.map +1 -0
  91. package/package.json +77 -2
  92. package/src/cookies/index.ts +1 -0
  93. package/src/core/index.ts +1 -0
  94. package/src/core/router.ts +26 -15
  95. package/src/core/server.ts +64 -14
  96. package/src/cors/index.ts +2 -0
  97. package/src/decorators/index.ts +2 -0
  98. package/src/http-error/index.ts +1 -0
  99. package/src/infra/index.ts +3 -0
  100. package/src/plugins/SwaggerPlugin.ts +45 -0
  101. package/src/plugins/index.ts +1 -0
  102. package/src/rate-limit/index.ts +2 -0
  103. package/src/router/index.ts +1 -0
  104. package/src/swagger/constants.ts +8 -0
  105. package/src/swagger/decorators.ts +35 -0
  106. package/src/swagger/index.ts +10 -0
  107. package/src/swagger/openapi-builder.ts +199 -0
  108. package/src/swagger/swagger-ui-html.ts +24 -0
  109. package/src/swagger/swagger-ui-modern.html +894 -0
  110. package/src/swagger/swagger-ui-template.ts +5 -0
  111. package/src/types/index.ts +7 -0
  112. package/src/types/swagger.type.ts +36 -0
  113. package/src/validators/index.ts +4 -0
package/README.md CHANGED
@@ -100,6 +100,38 @@ app.listen(3000);
100
100
  | `SSEPlugin` | Server-Sent Events with client management |
101
101
  | `ProxyPlugin` | HTTP reverse proxy |
102
102
  | `MultipartPlugin` | Multipart file upload parsing |
103
+ | `SwaggerPlugin` | OpenAPI 3 spec + Swagger UI (dark minimal theme, zero extra deps) |
104
+
105
+ ### OpenAPI & Swagger UI
106
+
107
+ Register `SwaggerPlugin` **before** heavy middleware so `/docs` and `/openapi.json` are served. Use `@ApiTags` / `@ApiOperation` on controllers, or pass a [RouteMeta](src/types/routes.type.ts) object as the last argument to `app.get()` / `app.post()` for imperative routes.
108
+
109
+ ```typescript
110
+ import { AzuraServer, SwaggerPlugin, ApiTags, ApiOperation, Controller, Get } from "azurats";
111
+
112
+ @Controller("/api")
113
+ class UsersController {
114
+ @ApiTags("users")
115
+ @ApiOperation({ summary: "List users" })
116
+ @Get("/")
117
+ list() {
118
+ return { users: [] };
119
+ }
120
+ }
121
+
122
+ const app = new AzuraServer();
123
+ app.plugin(
124
+ SwaggerPlugin({
125
+ getDocuments: () => app.getRouteDocuments(),
126
+ info: { title: "My API", version: "1.0.0" },
127
+ }),
128
+ );
129
+ app.register(UsersController);
130
+ // http://localhost:3000/docs — Swagger UI
131
+ // http://localhost:3000/openapi.json — raw OpenAPI
132
+ ```
133
+
134
+ If you use `HelmetPlugin` with a strict `Content-Security-Policy`, allow inline scripts for the docs path or register Swagger without CSP on `/docs`.
103
135
 
104
136
  ### Plugin Usage
105
137
 
@@ -0,0 +1,5 @@
1
+ import { IncomingMessage } from 'node:http';
2
+
3
+ declare function resolveIp(req: IncomingMessage): string;
4
+
5
+ export { resolveIp as r };
@@ -0,0 +1,5 @@
1
+ import { IncomingMessage } from 'node:http';
2
+
3
+ declare function resolveIp(req: IncomingMessage): string;
4
+
5
+ export { resolveIp as r };
@@ -0,0 +1,6 @@
1
+ import { g as PluginHandler } from './plugin.type-D4HHceYz.js';
2
+ import { S as SwaggerPluginOptions } from './swagger.type-CfDbFCZC.js';
3
+
4
+ declare function SwaggerPlugin(options: SwaggerPluginOptions): PluginHandler;
5
+
6
+ export { SwaggerPlugin as S };
@@ -0,0 +1,6 @@
1
+ import { g as PluginHandler } from './plugin.type-BNooWhKa.mjs';
2
+ import { S as SwaggerPluginOptions } from './swagger.type-Bfn5nGR8.mjs';
3
+
4
+ declare function SwaggerPlugin(options: SwaggerPluginOptions): PluginHandler;
5
+
6
+ export { SwaggerPlugin as S };
@@ -0,0 +1,7 @@
1
+ import { C as CookieOptions } from '../common.type-BhGCNyEm.mjs';
2
+ import 'node:http';
3
+
4
+ declare function serializeCookie(name: string, value: string, options?: CookieOptions): string;
5
+ declare function clearCookieHeader(name: string, options?: CookieOptions): string;
6
+
7
+ export { clearCookieHeader, serializeCookie };
@@ -0,0 +1,7 @@
1
+ import { C as CookieOptions } from '../common.type-BhGCNyEm.js';
2
+ import 'node:http';
3
+
4
+ declare function serializeCookie(name: string, value: string, options?: CookieOptions): string;
5
+ declare function clearCookieHeader(name: string, options?: CookieOptions): string;
6
+
7
+ export { clearCookieHeader, serializeCookie };
@@ -0,0 +1,38 @@
1
+ 'use strict';
2
+
3
+ // src/utils/cookies/CookieManager.ts
4
+ function serializeCookie(name, value, options = {}) {
5
+ let cookie = `${encodeURIComponent(name)}=${encodeURIComponent(value)}`;
6
+ if (options.maxAge != null) {
7
+ cookie += `; Max-Age=${Math.floor(options.maxAge)}`;
8
+ }
9
+ if (options.expires) {
10
+ cookie += `; Expires=${options.expires.toUTCString()}`;
11
+ }
12
+ if (options.domain) {
13
+ cookie += `; Domain=${options.domain}`;
14
+ }
15
+ cookie += `; Path=${options.path ?? "/"}`;
16
+ if (options.secure) {
17
+ cookie += "; Secure";
18
+ }
19
+ if (options.httpOnly !== false) {
20
+ cookie += "; HttpOnly";
21
+ }
22
+ if (options.sameSite) {
23
+ cookie += `; SameSite=${options.sameSite}`;
24
+ }
25
+ return cookie;
26
+ }
27
+ function clearCookieHeader(name, options = {}) {
28
+ return serializeCookie(name, "", {
29
+ ...options,
30
+ maxAge: 0,
31
+ expires: /* @__PURE__ */ new Date(0)
32
+ });
33
+ }
34
+
35
+ exports.clearCookieHeader = clearCookieHeader;
36
+ exports.serializeCookie = serializeCookie;
37
+ //# sourceMappingURL=index.js.map
38
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/utils/cookies/CookieManager.ts"],"names":[],"mappings":";;;AAEO,SAAS,eAAA,CACd,IAAA,EACA,KAAA,EACA,OAAA,GAAyB,EAAC,EAClB;AACR,EAAA,IAAI,MAAA,GAAS,GAAG,kBAAA,CAAmB,IAAI,CAAC,CAAA,CAAA,EAAI,kBAAA,CAAmB,KAAK,CAAC,CAAA,CAAA;AAErE,EAAA,IAAI,OAAA,CAAQ,UAAU,IAAA,EAAM;AAC1B,IAAA,MAAA,IAAU,CAAA,UAAA,EAAa,IAAA,CAAK,KAAA,CAAM,OAAA,CAAQ,MAAM,CAAC,CAAA,CAAA;AAAA,EACnD;AAEA,EAAA,IAAI,QAAQ,OAAA,EAAS;AACnB,IAAA,MAAA,IAAU,CAAA,UAAA,EAAa,OAAA,CAAQ,OAAA,CAAQ,WAAA,EAAa,CAAA,CAAA;AAAA,EACtD;AAEA,EAAA,IAAI,QAAQ,MAAA,EAAQ;AAClB,IAAA,MAAA,IAAU,CAAA,SAAA,EAAY,QAAQ,MAAM,CAAA,CAAA;AAAA,EACtC;AAEA,EAAA,MAAA,IAAU,CAAA,OAAA,EAAU,OAAA,CAAQ,IAAA,IAAQ,GAAG,CAAA,CAAA;AAEvC,EAAA,IAAI,QAAQ,MAAA,EAAQ;AAClB,IAAA,MAAA,IAAU,UAAA;AAAA,EACZ;AAEA,EAAA,IAAI,OAAA,CAAQ,aAAa,KAAA,EAAO;AAC9B,IAAA,MAAA,IAAU,YAAA;AAAA,EACZ;AAEA,EAAA,IAAI,QAAQ,QAAA,EAAU;AACpB,IAAA,MAAA,IAAU,CAAA,WAAA,EAAc,QAAQ,QAAQ,CAAA,CAAA;AAAA,EAC1C;AAEA,EAAA,OAAO,MAAA;AACT;AAEO,SAAS,iBAAA,CACd,IAAA,EACA,OAAA,GAAyB,EAAC,EAClB;AACR,EAAA,OAAO,eAAA,CAAgB,MAAM,EAAA,EAAI;AAAA,IAC/B,GAAG,OAAA;AAAA,IACH,MAAA,EAAQ,CAAA;AAAA,IACR,OAAA,kBAAS,IAAI,IAAA,CAAK,CAAC;AAAA,GACpB,CAAA;AACH","file":"index.js","sourcesContent":["import type { CookieOptions } from \"../../types/common.type.js\";\r\n\r\nexport function serializeCookie(\r\n name: string,\r\n value: string,\r\n options: CookieOptions = {},\r\n): string {\r\n let cookie = `${encodeURIComponent(name)}=${encodeURIComponent(value)}`;\r\n\r\n if (options.maxAge != null) {\r\n cookie += `; Max-Age=${Math.floor(options.maxAge)}`;\r\n }\r\n\r\n if (options.expires) {\r\n cookie += `; Expires=${options.expires.toUTCString()}`;\r\n }\r\n\r\n if (options.domain) {\r\n cookie += `; Domain=${options.domain}`;\r\n }\r\n\r\n cookie += `; Path=${options.path ?? \"/\"}`;\r\n\r\n if (options.secure) {\r\n cookie += \"; Secure\";\r\n }\r\n\r\n if (options.httpOnly !== false) {\r\n cookie += \"; HttpOnly\";\r\n }\r\n\r\n if (options.sameSite) {\r\n cookie += `; SameSite=${options.sameSite}`;\r\n }\r\n\r\n return cookie;\r\n}\r\n\r\nexport function clearCookieHeader(\r\n name: string,\r\n options: CookieOptions = {},\r\n): string {\r\n return serializeCookie(name, \"\", {\r\n ...options,\r\n maxAge: 0,\r\n expires: new Date(0),\r\n });\r\n}\r\n"]}
@@ -0,0 +1,35 @@
1
+ // src/utils/cookies/CookieManager.ts
2
+ function serializeCookie(name, value, options = {}) {
3
+ let cookie = `${encodeURIComponent(name)}=${encodeURIComponent(value)}`;
4
+ if (options.maxAge != null) {
5
+ cookie += `; Max-Age=${Math.floor(options.maxAge)}`;
6
+ }
7
+ if (options.expires) {
8
+ cookie += `; Expires=${options.expires.toUTCString()}`;
9
+ }
10
+ if (options.domain) {
11
+ cookie += `; Domain=${options.domain}`;
12
+ }
13
+ cookie += `; Path=${options.path ?? "/"}`;
14
+ if (options.secure) {
15
+ cookie += "; Secure";
16
+ }
17
+ if (options.httpOnly !== false) {
18
+ cookie += "; HttpOnly";
19
+ }
20
+ if (options.sameSite) {
21
+ cookie += `; SameSite=${options.sameSite}`;
22
+ }
23
+ return cookie;
24
+ }
25
+ function clearCookieHeader(name, options = {}) {
26
+ return serializeCookie(name, "", {
27
+ ...options,
28
+ maxAge: 0,
29
+ expires: /* @__PURE__ */ new Date(0)
30
+ });
31
+ }
32
+
33
+ export { clearCookieHeader, serializeCookie };
34
+ //# sourceMappingURL=index.mjs.map
35
+ //# sourceMappingURL=index.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/utils/cookies/CookieManager.ts"],"names":[],"mappings":";AAEO,SAAS,eAAA,CACd,IAAA,EACA,KAAA,EACA,OAAA,GAAyB,EAAC,EAClB;AACR,EAAA,IAAI,MAAA,GAAS,GAAG,kBAAA,CAAmB,IAAI,CAAC,CAAA,CAAA,EAAI,kBAAA,CAAmB,KAAK,CAAC,CAAA,CAAA;AAErE,EAAA,IAAI,OAAA,CAAQ,UAAU,IAAA,EAAM;AAC1B,IAAA,MAAA,IAAU,CAAA,UAAA,EAAa,IAAA,CAAK,KAAA,CAAM,OAAA,CAAQ,MAAM,CAAC,CAAA,CAAA;AAAA,EACnD;AAEA,EAAA,IAAI,QAAQ,OAAA,EAAS;AACnB,IAAA,MAAA,IAAU,CAAA,UAAA,EAAa,OAAA,CAAQ,OAAA,CAAQ,WAAA,EAAa,CAAA,CAAA;AAAA,EACtD;AAEA,EAAA,IAAI,QAAQ,MAAA,EAAQ;AAClB,IAAA,MAAA,IAAU,CAAA,SAAA,EAAY,QAAQ,MAAM,CAAA,CAAA;AAAA,EACtC;AAEA,EAAA,MAAA,IAAU,CAAA,OAAA,EAAU,OAAA,CAAQ,IAAA,IAAQ,GAAG,CAAA,CAAA;AAEvC,EAAA,IAAI,QAAQ,MAAA,EAAQ;AAClB,IAAA,MAAA,IAAU,UAAA;AAAA,EACZ;AAEA,EAAA,IAAI,OAAA,CAAQ,aAAa,KAAA,EAAO;AAC9B,IAAA,MAAA,IAAU,YAAA;AAAA,EACZ;AAEA,EAAA,IAAI,QAAQ,QAAA,EAAU;AACpB,IAAA,MAAA,IAAU,CAAA,WAAA,EAAc,QAAQ,QAAQ,CAAA,CAAA;AAAA,EAC1C;AAEA,EAAA,OAAO,MAAA;AACT;AAEO,SAAS,iBAAA,CACd,IAAA,EACA,OAAA,GAAyB,EAAC,EAClB;AACR,EAAA,OAAO,eAAA,CAAgB,MAAM,EAAA,EAAI;AAAA,IAC/B,GAAG,OAAA;AAAA,IACH,MAAA,EAAQ,CAAA;AAAA,IACR,OAAA,kBAAS,IAAI,IAAA,CAAK,CAAC;AAAA,GACpB,CAAA;AACH","file":"index.mjs","sourcesContent":["import type { CookieOptions } from \"../../types/common.type.js\";\r\n\r\nexport function serializeCookie(\r\n name: string,\r\n value: string,\r\n options: CookieOptions = {},\r\n): string {\r\n let cookie = `${encodeURIComponent(name)}=${encodeURIComponent(value)}`;\r\n\r\n if (options.maxAge != null) {\r\n cookie += `; Max-Age=${Math.floor(options.maxAge)}`;\r\n }\r\n\r\n if (options.expires) {\r\n cookie += `; Expires=${options.expires.toUTCString()}`;\r\n }\r\n\r\n if (options.domain) {\r\n cookie += `; Domain=${options.domain}`;\r\n }\r\n\r\n cookie += `; Path=${options.path ?? \"/\"}`;\r\n\r\n if (options.secure) {\r\n cookie += \"; Secure\";\r\n }\r\n\r\n if (options.httpOnly !== false) {\r\n cookie += \"; HttpOnly\";\r\n }\r\n\r\n if (options.sameSite) {\r\n cookie += `; SameSite=${options.sameSite}`;\r\n }\r\n\r\n return cookie;\r\n}\r\n\r\nexport function clearCookieHeader(\r\n name: string,\r\n options: CookieOptions = {},\r\n): string {\r\n return serializeCookie(name, \"\", {\r\n ...options,\r\n maxAge: 0,\r\n expires: new Date(0),\r\n });\r\n}\r\n"]}
@@ -1,24 +1,11 @@
1
+ import { R as Router, a as RouteDocument } from '../index-j6QGMhZU.mjs';
1
2
  import { Server } from 'node:http';
2
- import { H as HttpMethod, R as RouteHandler, M as MiddlewareHandler, b as RouteMatch, E as ErrorHandler } from '../common.type-BhGCNyEm.mjs';
3
3
  import { Logger } from '../logger/index.mjs';
4
+ import { M as MiddlewareHandler, E as ErrorHandler, H as HttpMethod } from '../common.type-BhGCNyEm.mjs';
4
5
  import { A as AzuraConfig } from '../config.type-4K-xcMSy.mjs';
5
6
  import { g as PluginHandler } from '../plugin.type-BNooWhKa.mjs';
6
-
7
- declare class Router {
8
- private root;
9
- private cache;
10
- private staticRoutes;
11
- constructor(cacheSize?: number);
12
- add(method: HttpMethod, path: string, handler: RouteHandler, middlewares?: MiddlewareHandler[], meta?: Record<string, any>): void;
13
- find(method: HttpMethod, path: string): RouteMatch | null;
14
- private matchNode;
15
- private normalizePath;
16
- getAllRoutes(): Array<{
17
- method: HttpMethod;
18
- path: string;
19
- }>;
20
- private collectRoutes;
21
- }
7
+ import { d as RouteMeta } from '../routes.type-DZO5VBW2.mjs';
8
+ import { B as BuildOpenApiOptions } from '../swagger.type-Bfn5nGR8.mjs';
22
9
 
23
10
  declare class AzuraServer {
24
11
  private server;
@@ -33,14 +20,14 @@ declare class AzuraServer {
33
20
  use(handler: MiddlewareHandler | PluginHandler): this;
34
21
  plugin(handler: PluginHandler): this;
35
22
  onError(handler: ErrorHandler): this;
36
- get(path: string, ...handlers: (MiddlewareHandler | Function)[]): this;
37
- post(path: string, ...handlers: (MiddlewareHandler | Function)[]): this;
38
- put(path: string, ...handlers: (MiddlewareHandler | Function)[]): this;
39
- delete(path: string, ...handlers: (MiddlewareHandler | Function)[]): this;
40
- patch(path: string, ...handlers: (MiddlewareHandler | Function)[]): this;
41
- head(path: string, ...handlers: (MiddlewareHandler | Function)[]): this;
42
- options(path: string, ...handlers: (MiddlewareHandler | Function)[]): this;
43
- all(path: string, ...handlers: (MiddlewareHandler | Function)[]): this;
23
+ get(path: string, ...handlers: (MiddlewareHandler | Function | RouteMeta)[]): this;
24
+ post(path: string, ...handlers: (MiddlewareHandler | Function | RouteMeta)[]): this;
25
+ put(path: string, ...handlers: (MiddlewareHandler | Function | RouteMeta)[]): this;
26
+ delete(path: string, ...handlers: (MiddlewareHandler | Function | RouteMeta)[]): this;
27
+ patch(path: string, ...handlers: (MiddlewareHandler | Function | RouteMeta)[]): this;
28
+ head(path: string, ...handlers: (MiddlewareHandler | Function | RouteMeta)[]): this;
29
+ options(path: string, ...handlers: (MiddlewareHandler | Function | RouteMeta)[]): this;
30
+ all(path: string, ...handlers: (MiddlewareHandler | Function | RouteMeta)[]): this;
44
31
  private route;
45
32
  register(...controllers: (new (...args: any[]) => any)[]): this;
46
33
  private registerController;
@@ -60,6 +47,10 @@ declare class AzuraServer {
60
47
  method: HttpMethod;
61
48
  path: string;
62
49
  }>;
50
+ /** Rotas com metadados OpenAPI (para Swagger ou export manual). */
51
+ getRouteDocuments(): RouteDocument[];
52
+ /** Gera documento OpenAPI 3.0 a partir das rotas registadas. */
53
+ getOpenApiDocument(options?: BuildOpenApiOptions): Record<string, unknown>;
63
54
  }
64
55
 
65
- export { AzuraServer, Router };
56
+ export { AzuraServer, RouteDocument, Router };
@@ -1,24 +1,11 @@
1
+ import { R as Router, a as RouteDocument } from '../index-tpPZS_UK.js';
1
2
  import { Server } from 'node:http';
2
- import { H as HttpMethod, R as RouteHandler, M as MiddlewareHandler, b as RouteMatch, E as ErrorHandler } from '../common.type-BhGCNyEm.js';
3
3
  import { Logger } from '../logger/index.js';
4
+ import { M as MiddlewareHandler, E as ErrorHandler, H as HttpMethod } from '../common.type-BhGCNyEm.js';
4
5
  import { A as AzuraConfig } from '../config.type-s3ImgfM_.js';
5
6
  import { g as PluginHandler } from '../plugin.type-D4HHceYz.js';
6
-
7
- declare class Router {
8
- private root;
9
- private cache;
10
- private staticRoutes;
11
- constructor(cacheSize?: number);
12
- add(method: HttpMethod, path: string, handler: RouteHandler, middlewares?: MiddlewareHandler[], meta?: Record<string, any>): void;
13
- find(method: HttpMethod, path: string): RouteMatch | null;
14
- private matchNode;
15
- private normalizePath;
16
- getAllRoutes(): Array<{
17
- method: HttpMethod;
18
- path: string;
19
- }>;
20
- private collectRoutes;
21
- }
7
+ import { d as RouteMeta } from '../routes.type-DzHNkCag.js';
8
+ import { B as BuildOpenApiOptions } from '../swagger.type-CfDbFCZC.js';
22
9
 
23
10
  declare class AzuraServer {
24
11
  private server;
@@ -33,14 +20,14 @@ declare class AzuraServer {
33
20
  use(handler: MiddlewareHandler | PluginHandler): this;
34
21
  plugin(handler: PluginHandler): this;
35
22
  onError(handler: ErrorHandler): this;
36
- get(path: string, ...handlers: (MiddlewareHandler | Function)[]): this;
37
- post(path: string, ...handlers: (MiddlewareHandler | Function)[]): this;
38
- put(path: string, ...handlers: (MiddlewareHandler | Function)[]): this;
39
- delete(path: string, ...handlers: (MiddlewareHandler | Function)[]): this;
40
- patch(path: string, ...handlers: (MiddlewareHandler | Function)[]): this;
41
- head(path: string, ...handlers: (MiddlewareHandler | Function)[]): this;
42
- options(path: string, ...handlers: (MiddlewareHandler | Function)[]): this;
43
- all(path: string, ...handlers: (MiddlewareHandler | Function)[]): this;
23
+ get(path: string, ...handlers: (MiddlewareHandler | Function | RouteMeta)[]): this;
24
+ post(path: string, ...handlers: (MiddlewareHandler | Function | RouteMeta)[]): this;
25
+ put(path: string, ...handlers: (MiddlewareHandler | Function | RouteMeta)[]): this;
26
+ delete(path: string, ...handlers: (MiddlewareHandler | Function | RouteMeta)[]): this;
27
+ patch(path: string, ...handlers: (MiddlewareHandler | Function | RouteMeta)[]): this;
28
+ head(path: string, ...handlers: (MiddlewareHandler | Function | RouteMeta)[]): this;
29
+ options(path: string, ...handlers: (MiddlewareHandler | Function | RouteMeta)[]): this;
30
+ all(path: string, ...handlers: (MiddlewareHandler | Function | RouteMeta)[]): this;
44
31
  private route;
45
32
  register(...controllers: (new (...args: any[]) => any)[]): this;
46
33
  private registerController;
@@ -60,6 +47,10 @@ declare class AzuraServer {
60
47
  method: HttpMethod;
61
48
  path: string;
62
49
  }>;
50
+ /** Rotas com metadados OpenAPI (para Swagger ou export manual). */
51
+ getRouteDocuments(): RouteDocument[];
52
+ /** Gera documento OpenAPI 3.0 a partir das rotas registadas. */
53
+ getOpenApiDocument(options?: BuildOpenApiOptions): Record<string, unknown>;
63
54
  }
64
55
 
65
- export { AzuraServer, Router };
56
+ export { AzuraServer, RouteDocument, Router };
@@ -183,31 +183,37 @@ var Router = class {
183
183
  return path;
184
184
  }
185
185
  getAllRoutes() {
186
+ return this.getRouteDocuments().map(({ method, path }) => ({ method, path }));
187
+ }
188
+ /**
189
+ * Todas as rotas com metadados (ex.: OpenAPI) associados ao handler.
190
+ */
191
+ getRouteDocuments() {
186
192
  const routes = [];
187
- for (const [key] of this.staticRoutes) {
193
+ for (const [key, stored] of this.staticRoutes) {
188
194
  const [method, path] = key.split(":", 2);
189
- routes.push({ method, path });
195
+ routes.push({ method, path, meta: stored.meta });
190
196
  }
191
- this.collectRoutes(this.root, "", routes);
197
+ this.collectRoutesWithMeta(this.root, "", routes);
192
198
  return routes;
193
199
  }
194
- collectRoutes(node, prefix, routes) {
195
- for (const [method] of node.handlers) {
196
- routes.push({ method, path: prefix || "/" });
200
+ collectRoutesWithMeta(node, prefix, routes) {
201
+ for (const [method, stored] of node.handlers) {
202
+ routes.push({ method, path: prefix || "/", meta: stored.meta });
197
203
  }
198
204
  for (const [seg, child] of node.children) {
199
- this.collectRoutes(child, `${prefix}/${seg}`, routes);
205
+ this.collectRoutesWithMeta(child, `${prefix}/${seg}`, routes);
200
206
  }
201
207
  if (node.paramChild) {
202
- this.collectRoutes(
208
+ this.collectRoutesWithMeta(
203
209
  node.paramChild,
204
210
  `${prefix}/:${node.paramChild.paramName}`,
205
211
  routes
206
212
  );
207
213
  }
208
214
  if (node.wildcardHandler) {
209
- for (const [method] of node.wildcardHandler) {
210
- routes.push({ method, path: `${prefix}/*` });
215
+ for (const [method, stored] of node.wildcardHandler) {
216
+ routes.push({ method, path: `${prefix}/*`, meta: stored.meta });
211
217
  }
212
218
  }
213
219
  }
@@ -615,7 +621,174 @@ function clearCookieHeader(name, options = {}) {
615
621
  });
616
622
  }
617
623
 
624
+ // src/swagger/constants.ts
625
+ var API_TAGS_KEY = "azura:api-tags";
626
+ var METHOD_TAGS_KEY = "azura:swagger-method-tags";
627
+ var API_OPERATION_KEY = "azura:swagger-operation";
628
+
629
+ // src/swagger/openapi-builder.ts
630
+ function resolveInfo(options) {
631
+ return options.info ?? {
632
+ title: "AzuraJS API",
633
+ version: "1.0.0",
634
+ description: "Generated OpenAPI specification"
635
+ };
636
+ }
637
+ function toOpenApiPath(path) {
638
+ if (path === "/") return "/";
639
+ const mapped = path.split("/").map((seg) => {
640
+ if (seg === "*") return "{wildcard}";
641
+ return seg.startsWith(":") ? `{${seg.slice(1)}}` : seg;
642
+ }).join("/").replace(/\/{2,}/g, "/");
643
+ if (mapped.endsWith("/*")) {
644
+ return mapped.slice(0, -2) + "/{wildcard}";
645
+ }
646
+ return mapped;
647
+ }
648
+ function pathParametersFromTemplate(path) {
649
+ const re = /\{([^}]+)\}/g;
650
+ const out = [];
651
+ let m;
652
+ const seen = /* @__PURE__ */ new Set();
653
+ while ((m = re.exec(path)) !== null) {
654
+ const name = m[1];
655
+ if (seen.has(name)) continue;
656
+ seen.add(name);
657
+ out.push({
658
+ name,
659
+ in: "path",
660
+ required: true,
661
+ schema: { type: "string" }
662
+ });
663
+ }
664
+ return out;
665
+ }
666
+ function defaultResponses(meta) {
667
+ if (meta?.responses && Object.keys(meta.responses).length > 0) {
668
+ const out = {};
669
+ for (const [code, r] of Object.entries(meta.responses)) {
670
+ out[String(code)] = {
671
+ description: r.description,
672
+ ...r.schema ? { content: { "application/json": { schema: r.schema } } } : {}
673
+ };
674
+ }
675
+ return out;
676
+ }
677
+ return {
678
+ "200": {
679
+ description: "Successful response",
680
+ content: {
681
+ "application/json": {
682
+ schema: { type: "object", additionalProperties: true }
683
+ }
684
+ }
685
+ }
686
+ };
687
+ }
688
+ function mergeParameters(meta, pathParams) {
689
+ const fromMeta = meta?.parameters ?? [];
690
+ const names = new Set(pathParams.map((p) => p.name));
691
+ const rest = fromMeta.filter((p) => !(p.in === "path" && names.has(p.name)));
692
+ const mapped = fromMeta.filter((p) => p.in === "path").map((p) => ({
693
+ name: p.name,
694
+ in: "path",
695
+ required: p.required ?? true,
696
+ description: p.description,
697
+ schema: p.schema ?? { type: p.type ?? "string" }
698
+ }));
699
+ const pathMerged = [...pathParams];
700
+ for (const m of mapped) {
701
+ if (!pathMerged.find((x) => x.name === m.name)) pathMerged.push(m);
702
+ }
703
+ return [...pathMerged, ...rest.map((p) => mapParameter(p))];
704
+ }
705
+ function mapParameter(p) {
706
+ const base = {
707
+ name: p.name,
708
+ in: p.in,
709
+ required: p.required ?? (p.in === "path" ? true : false),
710
+ description: p.description
711
+ };
712
+ if (p.in === "body") {
713
+ return {
714
+ ...base,
715
+ content: {
716
+ "application/json": {
717
+ schema: p.schema ?? { type: "object" }
718
+ }
719
+ }
720
+ };
721
+ }
722
+ return {
723
+ ...base,
724
+ schema: p.schema ?? { type: p.type ?? "string" }
725
+ };
726
+ }
727
+ function operationIdFor(method, oPath) {
728
+ const slug = oPath.replace(/[/{}\-*]/g, "_").replace(/_+/g, "_").replace(/^_|_$/g, "");
729
+ return `${method.toLowerCase()}_${slug || "root"}`;
730
+ }
731
+ function operationForMethod(method, oPath, meta) {
732
+ const op = {
733
+ operationId: operationIdFor(method, oPath),
734
+ summary: meta?.summary ?? `${method} ${oPath}`,
735
+ ...meta?.description ? { description: meta.description } : {},
736
+ ...meta?.deprecated ? { deprecated: true } : {},
737
+ ...meta?.tags?.length ? { tags: meta.tags } : {},
738
+ ...meta?.security?.length ? { security: meta.security } : {}
739
+ };
740
+ if (["POST", "PUT", "PATCH"].includes(method) && !meta?.parameters?.some((x) => x.in === "body")) {
741
+ op.requestBody = {
742
+ content: {
743
+ "application/json": {
744
+ schema: { type: "object", additionalProperties: true }
745
+ }
746
+ },
747
+ required: false
748
+ };
749
+ }
750
+ op.responses = defaultResponses(meta);
751
+ return op;
752
+ }
753
+ function buildOpenApiDocument(routes, options) {
754
+ const info = resolveInfo(options);
755
+ const prefix = options.pathPrefix ?? "";
756
+ const paths = {};
757
+ const seen = /* @__PURE__ */ new Set();
758
+ for (const r of routes) {
759
+ const full = prefix + r.path;
760
+ const oPath = toOpenApiPath(full);
761
+ const key = `${r.method}:${oPath}`;
762
+ if (seen.has(key)) continue;
763
+ seen.add(key);
764
+ const pathParams = pathParametersFromTemplate(oPath);
765
+ const op = operationForMethod(r.method, oPath, r.meta);
766
+ const parameters = mergeParameters(r.meta, pathParams);
767
+ if (parameters.length) op.parameters = parameters;
768
+ const methodLc = r.method.toLowerCase();
769
+ if (!paths[oPath]) paths[oPath] = {};
770
+ paths[oPath][methodLc] = op;
771
+ }
772
+ return {
773
+ openapi: "3.0.3",
774
+ info: {
775
+ title: info.title,
776
+ version: info.version,
777
+ ...info.description ? { description: info.description } : {},
778
+ ...info.contact ? { contact: info.contact } : {},
779
+ ...info.license ? { license: info.license } : {}
780
+ },
781
+ ...options.servers?.length ? { servers: options.servers } : {},
782
+ paths
783
+ };
784
+ }
785
+
618
786
  // src/core/server.ts
787
+ function isRouteDocumentMeta(o) {
788
+ if (!o || typeof o !== "object" || Array.isArray(o)) return false;
789
+ const x = o;
790
+ 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;
791
+ }
619
792
  var CONTROLLER_META_KEY = "azura:controller";
620
793
  var ROUTES_META_KEY = "azura:routes";
621
794
  var PARAMS_META_KEY = "azura:params";
@@ -704,9 +877,19 @@ var AzuraServer = class {
704
877
  return this;
705
878
  }
706
879
  route(method, path, handlers) {
707
- const routeHandler = handlers[handlers.length - 1];
708
- const middlewares = handlers.slice(0, -1);
709
- this.router.add(method, path, routeHandler, middlewares);
880
+ let meta;
881
+ let list = handlers;
882
+ const last = list[list.length - 1];
883
+ if (list.length >= 2 && isRouteDocumentMeta(last)) {
884
+ meta = last;
885
+ list = list.slice(0, -1);
886
+ }
887
+ if (list.length === 0) {
888
+ throw new Error(`Route ${method} ${path} requires a handler function`);
889
+ }
890
+ const routeHandler = list[list.length - 1];
891
+ const middlewares = list.slice(0, -1);
892
+ this.router.add(method, path, routeHandler, middlewares, meta);
710
893
  return this;
711
894
  }
712
895
  register(...controllers) {
@@ -719,6 +902,7 @@ var AzuraServer = class {
719
902
  const instance = new Controller();
720
903
  const prefix = Reflect.getMetadata?.(CONTROLLER_META_KEY, Controller) ?? "";
721
904
  const controllerMiddlewares = Reflect.getMetadata?.("azura:middlewares", Controller) ?? [];
905
+ const controllerTags = Reflect.getMetadata?.(API_TAGS_KEY, Controller) ?? [];
722
906
  const prototype = Controller.prototype;
723
907
  const propertyNames = Object.getOwnPropertyNames(prototype).filter(
724
908
  (p) => p !== "constructor"
@@ -734,6 +918,14 @@ var AzuraServer = class {
734
918
  const paramsMeta = Reflect.getMetadata?.(PARAMS_META_KEY, prototype, propertyKey) ?? [];
735
919
  const routeMiddlewares = Reflect.getMetadata?.("azura:middlewares", prototype, propertyKey) ?? [];
736
920
  const allMiddlewares = [...controllerMiddlewares, ...routeMiddlewares];
921
+ const methodTags = Reflect.getMetadata?.(METHOD_TAGS_KEY, prototype, propertyKey) ?? [];
922
+ const opExtra = Reflect.getMetadata?.(API_OPERATION_KEY, prototype, propertyKey) ?? {};
923
+ const tagList = [...controllerTags, ...methodTags, ...routeMeta.meta?.tags ?? []];
924
+ const mergedMeta = (() => {
925
+ const m = { ...routeMeta.meta, ...opExtra };
926
+ if (tagList.length) m.tags = tagList;
927
+ return Object.keys(m).length ? m : void 0;
928
+ })();
737
929
  const handler = async (req, res) => {
738
930
  const args = this.resolveParams(paramsMeta, req, res);
739
931
  const result = await instance[propertyKey](...args);
@@ -745,7 +937,7 @@ var AzuraServer = class {
745
937
  }
746
938
  }
747
939
  };
748
- this.router.add(routeMeta.method, fullPath, handler, allMiddlewares, routeMeta.meta);
940
+ this.router.add(routeMeta.method, fullPath, handler, allMiddlewares, mergedMeta);
749
941
  }
750
942
  }
751
943
  resolveParams(paramsMeta, req, res) {
@@ -1099,6 +1291,14 @@ var AzuraServer = class {
1099
1291
  getRoutes() {
1100
1292
  return this.router.getAllRoutes();
1101
1293
  }
1294
+ /** Rotas com metadados OpenAPI (para Swagger ou export manual). */
1295
+ getRouteDocuments() {
1296
+ return this.router.getRouteDocuments();
1297
+ }
1298
+ /** Gera documento OpenAPI 3.0 a partir das rotas registadas. */
1299
+ getOpenApiDocument(options = {}) {
1300
+ return buildOpenApiDocument(this.getRouteDocuments(), options);
1301
+ }
1102
1302
  };
1103
1303
 
1104
1304
  exports.AzuraServer = AzuraServer;