@buenojs/bueno 0.8.3 → 0.8.5

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 (218) hide show
  1. package/README.md +136 -16
  2. package/dist/cli/{index.js → bin.js} +3036 -1421
  3. package/dist/container/index.js +250 -0
  4. package/dist/context/index.js +219 -0
  5. package/dist/database/index.js +493 -0
  6. package/dist/frontend/index.js +7697 -0
  7. package/dist/health/index.js +364 -0
  8. package/dist/i18n/index.js +345 -0
  9. package/dist/index.js +11043 -6482
  10. package/dist/jobs/index.js +819 -0
  11. package/dist/lock/index.js +367 -0
  12. package/dist/logger/index.js +281 -0
  13. package/dist/metrics/index.js +289 -0
  14. package/dist/middleware/index.js +77 -0
  15. package/dist/migrations/index.js +571 -0
  16. package/dist/modules/index.js +3346 -0
  17. package/dist/notification/index.js +484 -0
  18. package/dist/observability/index.js +331 -0
  19. package/dist/openapi/index.js +776 -0
  20. package/dist/orm/index.js +1356 -0
  21. package/dist/router/index.js +886 -0
  22. package/dist/rpc/index.js +691 -0
  23. package/dist/schema/index.js +400 -0
  24. package/dist/telemetry/index.js +595 -0
  25. package/dist/template/index.js +640 -0
  26. package/dist/templates/index.js +640 -0
  27. package/dist/testing/index.js +1111 -0
  28. package/dist/types/index.js +60 -0
  29. package/package.json +121 -27
  30. package/src/cache/index.ts +2 -1
  31. package/src/cli/bin.ts +2 -2
  32. package/src/cli/commands/build.ts +183 -165
  33. package/src/cli/commands/dev.ts +96 -89
  34. package/src/cli/commands/generate.ts +142 -111
  35. package/src/cli/commands/help.ts +20 -16
  36. package/src/cli/commands/index.ts +3 -6
  37. package/src/cli/commands/migration.ts +124 -105
  38. package/src/cli/commands/new.ts +392 -438
  39. package/src/cli/commands/start.ts +81 -79
  40. package/src/cli/core/args.ts +68 -50
  41. package/src/cli/core/console.ts +89 -95
  42. package/src/cli/core/index.ts +4 -4
  43. package/src/cli/core/prompt.ts +65 -62
  44. package/src/cli/core/spinner.ts +23 -20
  45. package/src/cli/index.ts +46 -38
  46. package/src/cli/templates/database/index.ts +61 -0
  47. package/src/cli/templates/database/mysql.ts +14 -0
  48. package/src/cli/templates/database/none.ts +16 -0
  49. package/src/cli/templates/database/postgresql.ts +14 -0
  50. package/src/cli/templates/database/sqlite.ts +14 -0
  51. package/src/cli/templates/deploy.ts +29 -26
  52. package/src/cli/templates/docker.ts +41 -30
  53. package/src/cli/templates/frontend/index.ts +63 -0
  54. package/src/cli/templates/frontend/none.ts +17 -0
  55. package/src/cli/templates/frontend/react.ts +140 -0
  56. package/src/cli/templates/frontend/solid.ts +134 -0
  57. package/src/cli/templates/frontend/svelte.ts +131 -0
  58. package/src/cli/templates/frontend/vue.ts +130 -0
  59. package/src/cli/templates/generators/index.ts +339 -0
  60. package/src/cli/templates/generators/types.ts +56 -0
  61. package/src/cli/templates/index.ts +35 -2
  62. package/src/cli/templates/project/api.ts +81 -0
  63. package/src/cli/templates/project/default.ts +140 -0
  64. package/src/cli/templates/project/fullstack.ts +111 -0
  65. package/src/cli/templates/project/index.ts +95 -0
  66. package/src/cli/templates/project/minimal.ts +45 -0
  67. package/src/cli/templates/project/types.ts +94 -0
  68. package/src/cli/templates/project/website.ts +263 -0
  69. package/src/cli/utils/fs.ts +55 -41
  70. package/src/cli/utils/index.ts +3 -2
  71. package/src/cli/utils/strings.ts +47 -33
  72. package/src/cli/utils/version.ts +47 -0
  73. package/src/config/env-validation.ts +100 -0
  74. package/src/config/env.ts +169 -41
  75. package/src/config/index.ts +28 -20
  76. package/src/config/loader.ts +25 -16
  77. package/src/config/merge.ts +21 -10
  78. package/src/config/types.ts +545 -25
  79. package/src/config/validation.ts +215 -7
  80. package/src/container/forward-ref.ts +22 -22
  81. package/src/container/index.ts +34 -12
  82. package/src/context/index.ts +11 -1
  83. package/src/database/index.ts +7 -190
  84. package/src/database/orm/builder.ts +457 -0
  85. package/src/database/orm/casts/index.ts +130 -0
  86. package/src/database/orm/casts/types.ts +25 -0
  87. package/src/database/orm/compiler.ts +304 -0
  88. package/src/database/orm/hooks/index.ts +114 -0
  89. package/src/database/orm/index.ts +61 -0
  90. package/src/database/orm/model-registry.ts +59 -0
  91. package/src/database/orm/model.ts +821 -0
  92. package/src/database/orm/relationships/base.ts +146 -0
  93. package/src/database/orm/relationships/belongs-to-many.ts +179 -0
  94. package/src/database/orm/relationships/belongs-to.ts +56 -0
  95. package/src/database/orm/relationships/has-many.ts +45 -0
  96. package/src/database/orm/relationships/has-one.ts +41 -0
  97. package/src/database/orm/relationships/index.ts +11 -0
  98. package/src/database/orm/scopes/index.ts +55 -0
  99. package/src/events/__tests__/event-system.test.ts +235 -0
  100. package/src/events/config.ts +238 -0
  101. package/src/events/example-usage.ts +185 -0
  102. package/src/events/index.ts +278 -0
  103. package/src/events/manager.ts +385 -0
  104. package/src/events/registry.ts +182 -0
  105. package/src/events/types.ts +124 -0
  106. package/src/frontend/api-routes.ts +65 -23
  107. package/src/frontend/bundler.ts +76 -34
  108. package/src/frontend/console-client.ts +2 -2
  109. package/src/frontend/console-stream.ts +94 -38
  110. package/src/frontend/dev-server.ts +94 -46
  111. package/src/frontend/file-router.ts +61 -19
  112. package/src/frontend/frameworks/index.ts +37 -10
  113. package/src/frontend/frameworks/react.ts +10 -8
  114. package/src/frontend/frameworks/solid.ts +11 -9
  115. package/src/frontend/frameworks/svelte.ts +15 -9
  116. package/src/frontend/frameworks/vue.ts +13 -11
  117. package/src/frontend/hmr-client.ts +12 -10
  118. package/src/frontend/hmr.ts +146 -103
  119. package/src/frontend/index.ts +14 -5
  120. package/src/frontend/islands.ts +41 -22
  121. package/src/frontend/isr.ts +59 -37
  122. package/src/frontend/layout.ts +36 -21
  123. package/src/frontend/ssr/react.ts +74 -27
  124. package/src/frontend/ssr/solid.ts +54 -20
  125. package/src/frontend/ssr/svelte.ts +48 -14
  126. package/src/frontend/ssr/vue.ts +50 -18
  127. package/src/frontend/ssr.ts +83 -39
  128. package/src/frontend/types.ts +91 -56
  129. package/src/health/index.ts +21 -9
  130. package/src/i18n/engine.ts +305 -0
  131. package/src/i18n/index.ts +38 -0
  132. package/src/i18n/loader.ts +218 -0
  133. package/src/i18n/middleware.ts +164 -0
  134. package/src/i18n/negotiator.ts +162 -0
  135. package/src/i18n/types.ts +158 -0
  136. package/src/index.ts +179 -27
  137. package/src/jobs/drivers/memory.ts +315 -0
  138. package/src/jobs/drivers/redis.ts +459 -0
  139. package/src/jobs/index.ts +30 -0
  140. package/src/jobs/queue.ts +281 -0
  141. package/src/jobs/types.ts +295 -0
  142. package/src/jobs/worker.ts +380 -0
  143. package/src/logger/index.ts +1 -3
  144. package/src/logger/transports/index.ts +62 -22
  145. package/src/metrics/index.ts +25 -16
  146. package/src/migrations/index.ts +9 -0
  147. package/src/modules/filters.ts +13 -17
  148. package/src/modules/guards.ts +49 -26
  149. package/src/modules/index.ts +409 -298
  150. package/src/modules/interceptors.ts +58 -20
  151. package/src/modules/lazy.ts +11 -19
  152. package/src/modules/lifecycle.ts +15 -7
  153. package/src/modules/metadata.ts +15 -5
  154. package/src/modules/pipes.ts +94 -72
  155. package/src/notification/channels/base.ts +68 -0
  156. package/src/notification/channels/email.ts +105 -0
  157. package/src/notification/channels/push.ts +104 -0
  158. package/src/notification/channels/sms.ts +105 -0
  159. package/src/notification/channels/whatsapp.ts +104 -0
  160. package/src/notification/index.ts +48 -0
  161. package/src/notification/service.ts +354 -0
  162. package/src/notification/types.ts +344 -0
  163. package/src/observability/__tests__/observability.test.ts +483 -0
  164. package/src/observability/breadcrumbs.ts +114 -0
  165. package/src/observability/index.ts +136 -0
  166. package/src/observability/interceptor.ts +85 -0
  167. package/src/observability/service.ts +303 -0
  168. package/src/observability/trace.ts +37 -0
  169. package/src/observability/types.ts +196 -0
  170. package/src/openapi/__tests__/decorators.test.ts +335 -0
  171. package/src/openapi/__tests__/document-builder.test.ts +285 -0
  172. package/src/openapi/__tests__/route-scanner.test.ts +334 -0
  173. package/src/openapi/__tests__/schema-generator.test.ts +275 -0
  174. package/src/openapi/decorators.ts +328 -0
  175. package/src/openapi/document-builder.ts +274 -0
  176. package/src/openapi/index.ts +112 -0
  177. package/src/openapi/metadata.ts +112 -0
  178. package/src/openapi/route-scanner.ts +289 -0
  179. package/src/openapi/schema-generator.ts +256 -0
  180. package/src/openapi/swagger-module.ts +166 -0
  181. package/src/openapi/types.ts +398 -0
  182. package/src/orm/index.ts +10 -0
  183. package/src/rpc/index.ts +3 -1
  184. package/src/schema/index.ts +9 -0
  185. package/src/security/index.ts +15 -6
  186. package/src/ssg/index.ts +9 -8
  187. package/src/telemetry/index.ts +76 -22
  188. package/src/template/index.ts +7 -0
  189. package/src/templates/engine.ts +224 -0
  190. package/src/templates/index.ts +9 -0
  191. package/src/templates/loader.ts +331 -0
  192. package/src/templates/renderers/markdown.ts +212 -0
  193. package/src/templates/renderers/simple.ts +269 -0
  194. package/src/templates/types.ts +154 -0
  195. package/src/testing/index.ts +100 -27
  196. package/src/types/optional-deps.d.ts +347 -187
  197. package/src/validation/index.ts +92 -2
  198. package/src/validation/schemas.ts +536 -0
  199. package/tests/integration/fullstack.test.ts +4 -4
  200. package/tests/unit/database.test.ts +2 -72
  201. package/tests/unit/env-validation.test.ts +166 -0
  202. package/tests/unit/events.test.ts +910 -0
  203. package/tests/unit/i18n.test.ts +455 -0
  204. package/tests/unit/jobs.test.ts +493 -0
  205. package/tests/unit/notification.test.ts +988 -0
  206. package/tests/unit/observability.test.ts +453 -0
  207. package/tests/unit/orm/builder.test.ts +323 -0
  208. package/tests/unit/orm/casts.test.ts +179 -0
  209. package/tests/unit/orm/compiler.test.ts +220 -0
  210. package/tests/unit/orm/eager-loading.test.ts +285 -0
  211. package/tests/unit/orm/hooks.test.ts +191 -0
  212. package/tests/unit/orm/model.test.ts +373 -0
  213. package/tests/unit/orm/relationships.test.ts +303 -0
  214. package/tests/unit/orm/scopes.test.ts +74 -0
  215. package/tests/unit/templates-simple.test.ts +53 -0
  216. package/tests/unit/templates.test.ts +454 -0
  217. package/tests/unit/validation.test.ts +18 -24
  218. package/tsconfig.json +11 -3
@@ -0,0 +1,776 @@
1
+ // @bun
2
+ var __defProp = Object.defineProperty;
3
+ var __export = (target, all) => {
4
+ for (var name in all)
5
+ __defProp(target, name, {
6
+ get: all[name],
7
+ enumerable: true,
8
+ configurable: true,
9
+ set: (newValue) => all[name] = () => newValue
10
+ });
11
+ };
12
+ var __legacyDecorateClassTS = function(decorators, target, key, desc) {
13
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
14
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function")
15
+ r = Reflect.decorate(decorators, target, key, desc);
16
+ else
17
+ for (var i = decorators.length - 1;i >= 0; i--)
18
+ if (d = decorators[i])
19
+ r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
20
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
21
+ };
22
+ var __legacyMetadataTS = (k, v) => {
23
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function")
24
+ return Reflect.metadata(k, v);
25
+ };
26
+ var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
27
+ var __require = import.meta.require;
28
+
29
+ // src/openapi/document-builder.ts
30
+ class DocumentBuilder {
31
+ document = {
32
+ openapi: "3.1.0",
33
+ info: {
34
+ title: "API",
35
+ version: "1.0.0"
36
+ },
37
+ paths: {},
38
+ components: {
39
+ schemas: {},
40
+ securitySchemes: {}
41
+ },
42
+ tags: []
43
+ };
44
+ setTitle(title) {
45
+ if (!this.document.info) {
46
+ this.document.info = { title, version: "1.0.0" };
47
+ } else {
48
+ this.document.info.title = title;
49
+ }
50
+ return this;
51
+ }
52
+ setDescription(description) {
53
+ if (!this.document.info) {
54
+ this.document.info = { title: "API", version: "1.0.0", description };
55
+ } else {
56
+ this.document.info.description = description;
57
+ }
58
+ return this;
59
+ }
60
+ setVersion(version) {
61
+ if (!this.document.info) {
62
+ this.document.info = { title: "API", version };
63
+ } else {
64
+ this.document.info.version = version;
65
+ }
66
+ return this;
67
+ }
68
+ setContact(name, url, email) {
69
+ if (!this.document.info) {
70
+ this.document.info = { title: "API", version: "1.0.0" };
71
+ }
72
+ this.document.info.contact = { name, url, email };
73
+ return this;
74
+ }
75
+ setLicense(name, url) {
76
+ if (!this.document.info) {
77
+ this.document.info = { title: "API", version: "1.0.0" };
78
+ }
79
+ this.document.info.license = { name, url };
80
+ return this;
81
+ }
82
+ addServer(url, description) {
83
+ if (!this.document.servers) {
84
+ this.document.servers = [];
85
+ }
86
+ this.document.servers.push({ url, description });
87
+ return this;
88
+ }
89
+ addBearerAuth(name = "bearer", options) {
90
+ if (!this.document.components) {
91
+ this.document.components = { schemas: {}, securitySchemes: {} };
92
+ }
93
+ if (!this.document.components.securitySchemes) {
94
+ this.document.components.securitySchemes = {};
95
+ }
96
+ const scheme = {
97
+ type: "http",
98
+ scheme: "bearer",
99
+ bearerFormat: options?.bearerFormat ?? "JWT"
100
+ };
101
+ if (options?.description) {
102
+ scheme.description = options.description;
103
+ }
104
+ this.document.components.securitySchemes[name] = scheme;
105
+ return this;
106
+ }
107
+ addBasicAuth(name = "basic", options) {
108
+ if (!this.document.components) {
109
+ this.document.components = { schemas: {}, securitySchemes: {} };
110
+ }
111
+ if (!this.document.components.securitySchemes) {
112
+ this.document.components.securitySchemes = {};
113
+ }
114
+ const scheme = {
115
+ type: "http",
116
+ scheme: "basic"
117
+ };
118
+ if (options?.description) {
119
+ scheme.description = options.description;
120
+ }
121
+ this.document.components.securitySchemes[name] = scheme;
122
+ return this;
123
+ }
124
+ addApiKey(options, name = "api_key") {
125
+ if (!this.document.components) {
126
+ this.document.components = { schemas: {}, securitySchemes: {} };
127
+ }
128
+ if (!this.document.components.securitySchemes) {
129
+ this.document.components.securitySchemes = {};
130
+ }
131
+ const scheme = {
132
+ type: "apiKey",
133
+ name: options.name,
134
+ in: options.in
135
+ };
136
+ if (options.description) {
137
+ scheme.description = options.description;
138
+ }
139
+ this.document.components.securitySchemes[name] = scheme;
140
+ return this;
141
+ }
142
+ addOAuth2(name, authorizationUrl, tokenUrl, refreshUrl) {
143
+ if (!this.document.components) {
144
+ this.document.components = { schemas: {}, securitySchemes: {} };
145
+ }
146
+ if (!this.document.components.securitySchemes) {
147
+ this.document.components.securitySchemes = {};
148
+ }
149
+ this.document.components.securitySchemes[name] = {
150
+ type: "oauth2",
151
+ flows: {
152
+ authorizationCode: {
153
+ authorizationUrl,
154
+ tokenUrl: tokenUrl || "",
155
+ refreshUrl,
156
+ scopes: {}
157
+ }
158
+ }
159
+ };
160
+ return this;
161
+ }
162
+ addOpenIdConnect(name, url) {
163
+ if (!this.document.components) {
164
+ this.document.components = { schemas: {}, securitySchemes: {} };
165
+ }
166
+ if (!this.document.components.securitySchemes) {
167
+ this.document.components.securitySchemes = {};
168
+ }
169
+ this.document.components.securitySchemes[name] = {
170
+ type: "openIdConnect",
171
+ openIdConnectUrl: url
172
+ };
173
+ return this;
174
+ }
175
+ addTag(name, description) {
176
+ if (!this.document.tags) {
177
+ this.document.tags = [];
178
+ }
179
+ const tag = { name };
180
+ if (description) {
181
+ tag.description = description;
182
+ }
183
+ this.document.tags.push(tag);
184
+ return this;
185
+ }
186
+ build() {
187
+ return this.document;
188
+ }
189
+ }
190
+ // src/openapi/metadata.ts
191
+ var classMetadataStore = new WeakMap;
192
+ function setApiMetadata(target, key, value) {
193
+ if (!classMetadataStore.has(target)) {
194
+ classMetadataStore.set(target, new Map);
195
+ }
196
+ classMetadataStore.get(target)?.set(key, value);
197
+ }
198
+ function getApiMetadata(target, key) {
199
+ return classMetadataStore.get(target)?.get(key);
200
+ }
201
+ var methodMetadataStore = new WeakMap;
202
+ function setApiMethodMetadata(target, key, value) {
203
+ if (!methodMetadataStore.has(target)) {
204
+ methodMetadataStore.set(target, new Map);
205
+ }
206
+ methodMetadataStore.get(target)?.set(key, value);
207
+ }
208
+ function getApiMethodMetadata(target, key) {
209
+ return methodMetadataStore.get(target)?.get(key);
210
+ }
211
+ var propertyMetadataStore = new WeakMap;
212
+ function setApiPropertyMetadata(target, propertyKey, value) {
213
+ if (!propertyMetadataStore.has(target)) {
214
+ propertyMetadataStore.set(target, new Map);
215
+ }
216
+ const key = typeof propertyKey === "symbol" ? propertyKey.toString() : propertyKey;
217
+ propertyMetadataStore.get(target)?.set(key, value);
218
+ }
219
+ function getApiPropertyMetadata(target, propertyKey) {
220
+ const key = typeof propertyKey === "symbol" ? propertyKey.toString() : propertyKey;
221
+ return propertyMetadataStore.get(target)?.get(key);
222
+ }
223
+ function getApiPropertyKeys(target) {
224
+ const metaMap = propertyMetadataStore.get(target);
225
+ if (!metaMap)
226
+ return [];
227
+ return Array.from(metaMap.keys());
228
+ }
229
+
230
+ // src/openapi/schema-generator.ts
231
+ class SchemaGenerator {
232
+ schemas = new Map;
233
+ typeNames = new Map;
234
+ typeCounter = 0;
235
+ generateSchema(type) {
236
+ if (typeof type === "string") {
237
+ return this.generatePrimitiveSchema(type);
238
+ }
239
+ if (typeof type === "function") {
240
+ const typeName = this.getTypeName(type);
241
+ if (this.schemas.has(typeName)) {
242
+ return { $ref: `#/components/schemas/${typeName}` };
243
+ }
244
+ if (this.isBuiltInType(type)) {
245
+ return this.generatePrimitiveSchema(type);
246
+ }
247
+ if (type === Array) {
248
+ return { type: "array", items: { type: "object" } };
249
+ }
250
+ return this.generateObjectSchema(type);
251
+ }
252
+ return { type: "object" };
253
+ }
254
+ generateObjectSchema(type) {
255
+ const typeName = this.getTypeName(type);
256
+ const properties = {};
257
+ const required = [];
258
+ const propertyKeys = getApiPropertyKeys(type.prototype);
259
+ for (const key of propertyKeys) {
260
+ const propName = typeof key === "symbol" ? key.toString() : key;
261
+ const propOptions = getApiPropertyMetadata(type.prototype, key);
262
+ if (propOptions) {
263
+ properties[propName] = this.generatePropertySchema(propOptions);
264
+ if (propOptions.required !== false) {
265
+ required.push(propName);
266
+ }
267
+ }
268
+ }
269
+ const schema = {
270
+ type: "object",
271
+ properties: Object.keys(properties).length > 0 ? properties : undefined,
272
+ required: required.length > 0 ? required : undefined
273
+ };
274
+ this.schemas.set(typeName, schema);
275
+ return { $ref: `#/components/schemas/${typeName}` };
276
+ }
277
+ generatePropertySchema(options) {
278
+ const schema = {};
279
+ if (options.type) {
280
+ if (typeof options.type === "string") {
281
+ const typeSchema = this.mapStringType(options.type);
282
+ Object.assign(schema, typeSchema);
283
+ } else if (typeof options.type === "function") {
284
+ const nested = this.generateSchema(options.type);
285
+ Object.assign(schema, nested);
286
+ }
287
+ }
288
+ if (options.minLength !== undefined)
289
+ schema.minLength = options.minLength;
290
+ if (options.maxLength !== undefined)
291
+ schema.maxLength = options.maxLength;
292
+ if (options.pattern !== undefined)
293
+ schema.pattern = options.pattern;
294
+ if (options.minimum !== undefined)
295
+ schema.minimum = options.minimum;
296
+ if (options.maximum !== undefined)
297
+ schema.maximum = options.maximum;
298
+ if (options.minItems !== undefined)
299
+ schema.minItems = options.minItems;
300
+ if (options.maxItems !== undefined)
301
+ schema.maxItems = options.maxItems;
302
+ if (options.items !== undefined)
303
+ schema.items = options.items;
304
+ if (options.enum !== undefined) {
305
+ schema.enum = options.enum;
306
+ }
307
+ if (options.format !== undefined)
308
+ schema.format = options.format;
309
+ if (options.title !== undefined)
310
+ schema.title = options.title;
311
+ if (options.description !== undefined)
312
+ schema.description = options.description;
313
+ if (options.example !== undefined)
314
+ schema.example = options.example;
315
+ if (options.default !== undefined)
316
+ schema.default = options.default;
317
+ if (options.nullable !== undefined)
318
+ schema.nullable = options.nullable;
319
+ if (options.readOnly !== undefined)
320
+ schema.readOnly = options.readOnly;
321
+ if (options.writeOnly !== undefined)
322
+ schema.writeOnly = options.writeOnly;
323
+ return schema;
324
+ }
325
+ generatePrimitiveSchema(type) {
326
+ if (typeof type === "string") {
327
+ return this.mapStringType(type);
328
+ }
329
+ if (type === String)
330
+ return { type: "string" };
331
+ if (type === Number)
332
+ return { type: "number" };
333
+ if (type === Boolean)
334
+ return { type: "boolean" };
335
+ if (type === Date)
336
+ return { type: "string", format: "date-time" };
337
+ if (type === Array)
338
+ return { type: "array", items: {} };
339
+ return { type: "object" };
340
+ }
341
+ mapStringType(type) {
342
+ switch (type.toLowerCase()) {
343
+ case "string":
344
+ return { type: "string" };
345
+ case "number":
346
+ return { type: "number" };
347
+ case "integer":
348
+ return { type: "integer" };
349
+ case "boolean":
350
+ return { type: "boolean" };
351
+ case "date":
352
+ return { type: "string", format: "date" };
353
+ case "datetime":
354
+ case "date-time":
355
+ return { type: "string", format: "date-time" };
356
+ case "email":
357
+ return { type: "string", format: "email" };
358
+ case "uuid":
359
+ return { type: "string", format: "uuid" };
360
+ case "url":
361
+ case "uri":
362
+ return { type: "string", format: "uri" };
363
+ case "object":
364
+ return { type: "object" };
365
+ case "array":
366
+ return { type: "array", items: {} };
367
+ default:
368
+ return { type: "string" };
369
+ }
370
+ }
371
+ isBuiltInType(type) {
372
+ return type === String || type === Number || type === Boolean || type === Date || type === Array || type === Object;
373
+ }
374
+ getTypeName(type) {
375
+ if (this.typeNames.has(type)) {
376
+ return this.typeNames.get(type);
377
+ }
378
+ let name = type.name;
379
+ if (!name || name === "Object" || name === "Function") {
380
+ name = `Schema_${++this.typeCounter}`;
381
+ }
382
+ this.typeNames.set(type, name);
383
+ return name;
384
+ }
385
+ getSchemas() {
386
+ const result = {};
387
+ for (const [name, schema] of this.schemas) {
388
+ result[name] = schema;
389
+ }
390
+ return result;
391
+ }
392
+ clear() {
393
+ this.schemas.clear();
394
+ this.typeNames.clear();
395
+ this.typeCounter = 0;
396
+ }
397
+ }
398
+ // src/modules/metadata.ts
399
+ var metadataStore = new WeakMap;
400
+ function setMetadata(target, key, value) {
401
+ if (!metadataStore.has(target)) {
402
+ metadataStore.set(target, new Map);
403
+ }
404
+ metadataStore.get(target)?.set(key, value);
405
+ }
406
+ function getMetadata(target, key) {
407
+ return metadataStore.get(target)?.get(key);
408
+ }
409
+ var prototypeMetadataStore = new WeakMap;
410
+ function setPrototypeMetadata(target, key, value) {
411
+ if (!prototypeMetadataStore.has(target)) {
412
+ prototypeMetadataStore.set(target, new Map);
413
+ }
414
+ prototypeMetadataStore.get(target)?.set(key, value);
415
+ }
416
+ function getPrototypeMetadata(target, key) {
417
+ return prototypeMetadataStore.get(target)?.get(key);
418
+ }
419
+ function Injectable() {
420
+ return (target) => {
421
+ setMetadata(target, "injectable", true);
422
+ return target;
423
+ };
424
+ }
425
+ function Controller(path = "") {
426
+ return (target) => {
427
+ setMetadata(target, "controller", true);
428
+ setMetadata(target, "path", path);
429
+ return target;
430
+ };
431
+ }
432
+ function Module(metadata) {
433
+ return (target) => {
434
+ setMetadata(target, "module", metadata);
435
+ return target;
436
+ };
437
+ }
438
+
439
+ // src/openapi/route-scanner.ts
440
+ class RouteScanner {
441
+ schemaGenerator;
442
+ constructor(schemaGenerator) {
443
+ this.schemaGenerator = schemaGenerator;
444
+ }
445
+ scanControllers(controllers) {
446
+ const paths = {};
447
+ for (const controller of controllers) {
448
+ if (getApiMetadata(controller, "api:exclude")) {
449
+ continue;
450
+ }
451
+ const basePath = getMetadata(controller, "path") ?? "";
452
+ const routes = getPrototypeMetadata(controller.prototype, "routes") ?? [];
453
+ const classLevelTags = getApiMetadata(controller, "api:tags") ?? [];
454
+ const classLevelSecurity = getApiMetadata(controller, "api:security") ?? [];
455
+ for (const route of routes) {
456
+ const prototype = controller.prototype;
457
+ const handlerKey = route.handler;
458
+ if (getApiMethodMetadata(prototype, `api:exclude:${String(handlerKey)}`)) {
459
+ continue;
460
+ }
461
+ const fullPath = this.convertPathToOpenAPI(basePath + route.path);
462
+ if (!paths[fullPath]) {
463
+ paths[fullPath] = {};
464
+ }
465
+ const operation = this.generateOperation(controller, route, classLevelTags, classLevelSecurity, basePath);
466
+ paths[fullPath][route.method.toLowerCase()] = operation;
467
+ }
468
+ }
469
+ return paths;
470
+ }
471
+ generateOperation(controller, route, classLevelTags, classLevelSecurity, basePath) {
472
+ const prototype = controller.prototype;
473
+ const handlerKey = String(route.handler);
474
+ const getMethodMeta = (key) => getApiMethodMetadata(prototype, `${key}:${handlerKey}`);
475
+ const operationMeta = getMethodMeta("api:operation");
476
+ const responseMeta = getMethodMeta("api:responses") ?? [];
477
+ const paramMeta = getMethodMeta("api:params") ?? [];
478
+ const queryMeta = getMethodMeta("api:query") ?? [];
479
+ const headerMeta = getMethodMeta("api:headers") ?? [];
480
+ const bodyMeta = getMethodMeta("api:body");
481
+ const methodLevelTags = getMethodMeta("api:tags") ?? [];
482
+ const methodLevelSecurity = getMethodMeta("api:security") ?? [];
483
+ const parameters = [
484
+ ...paramMeta.map((p) => ({
485
+ name: p.name,
486
+ in: "path",
487
+ description: p.description,
488
+ required: p.required ?? true,
489
+ example: p.example,
490
+ schema: p.schema
491
+ })),
492
+ ...queryMeta.map((q) => ({
493
+ name: q.name,
494
+ in: "query",
495
+ description: q.description,
496
+ required: q.required ?? false,
497
+ example: q.example,
498
+ schema: q.schema
499
+ })),
500
+ ...headerMeta.map((h) => ({
501
+ name: h.name,
502
+ in: "header",
503
+ description: h.description,
504
+ required: h.required,
505
+ schema: h.schema
506
+ }))
507
+ ];
508
+ const responses = {};
509
+ for (const resp of responseMeta) {
510
+ const statusCode = String(resp.status);
511
+ const response = { description: resp.description };
512
+ if (resp.type || resp.schema) {
513
+ let schema = resp.schema ?? {};
514
+ if (resp.type) {
515
+ if (Array.isArray(resp.type)) {
516
+ const itemSchema = this.schemaGenerator.generateSchema(resp.type[0]);
517
+ schema = { type: "array", items: itemSchema };
518
+ } else {
519
+ schema = this.schemaGenerator.generateSchema(resp.type);
520
+ }
521
+ }
522
+ const mediaType = { schema };
523
+ response.content = { "application/json": mediaType };
524
+ }
525
+ responses[statusCode] = response;
526
+ }
527
+ if (Object.keys(responses).length === 0) {
528
+ responses["200"] = { description: "Success" };
529
+ }
530
+ const requestBody = bodyMeta ? {
531
+ description: bodyMeta.description,
532
+ required: bodyMeta.required ?? true,
533
+ content: {
534
+ "application/json": {
535
+ schema: bodyMeta.schema ?? (bodyMeta.type ? this.schemaGenerator.generateSchema(bodyMeta.type) : { type: "object" })
536
+ }
537
+ }
538
+ } : undefined;
539
+ const tags = [...new Set([...classLevelTags, ...methodLevelTags])];
540
+ const security = [...classLevelSecurity, ...methodLevelSecurity];
541
+ const operation = {
542
+ operationId: operationMeta?.operationId ?? `${route.method.toLowerCase()}_${basePath.replace(/\//g, "_")}_${handlerKey}`,
543
+ summary: operationMeta?.summary,
544
+ description: operationMeta?.description,
545
+ tags: tags.length > 0 ? tags : undefined,
546
+ parameters: parameters.length > 0 ? parameters : undefined,
547
+ requestBody,
548
+ responses,
549
+ deprecated: operationMeta?.deprecated,
550
+ security: security.length > 0 ? security : undefined
551
+ };
552
+ return operation;
553
+ }
554
+ convertPathToOpenAPI(pattern) {
555
+ return pattern.replace(/:([a-zA-Z_][a-zA-Z0-9_]*)\??(?:<[^>]+>)?/g, "{$1}");
556
+ }
557
+ getSchemaGenerator() {
558
+ return this.schemaGenerator;
559
+ }
560
+ }
561
+ // src/openapi/swagger-module.ts
562
+ class SwaggerModule {
563
+ static createDocument(app, config, controllers) {
564
+ const schemaGenerator = new SchemaGenerator;
565
+ const scanner = new RouteScanner(schemaGenerator);
566
+ const paths = scanner.scanControllers(controllers);
567
+ const schemas = schemaGenerator.getSchemas();
568
+ return {
569
+ ...config,
570
+ paths: {
571
+ ...config.paths,
572
+ ...paths
573
+ },
574
+ components: {
575
+ ...config.components,
576
+ schemas: {
577
+ ...config.components?.schemas ?? {},
578
+ ...schemas
579
+ }
580
+ }
581
+ };
582
+ }
583
+ static setup(path, app, document, options) {
584
+ app.router.get(`${path}-json`, (ctx) => {
585
+ return ctx.json(document);
586
+ });
587
+ app.router.get(path, (ctx) => {
588
+ const html = this.generateSwaggerUI(path, document, options);
589
+ return ctx.html(html);
590
+ });
591
+ }
592
+ static generateSwaggerUI(jsonPath, document, options) {
593
+ const title = options?.title ?? "API Documentation";
594
+ const customCss = options?.customCss ?? "";
595
+ const customSiteTitle = options?.customSiteTitle ?? title;
596
+ const favicon = options?.customfavIcon ?? "";
597
+ return `<!DOCTYPE html>
598
+ <html lang="en">
599
+ <head>
600
+ <meta charset="UTF-8">
601
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
602
+ <title>${customSiteTitle}</title>
603
+ <link rel="stylesheet" href="https://unpkg.com/swagger-ui-dist@5/swagger-ui.css">
604
+ <style>
605
+ html {
606
+ box-sizing: border-box;
607
+ overflow: -moz-scrollbars-vertical;
608
+ overflow-y: scroll;
609
+ }
610
+ *, *:before, *:after {
611
+ box-sizing: inherit;
612
+ }
613
+ body {
614
+ margin: 0;
615
+ padding: 0;
616
+ font-family: sans-serif;
617
+ }
618
+ ${customCss}
619
+ </style>
620
+ ${favicon ? `<link rel="icon" href="${favicon}">` : ""}
621
+ </head>
622
+ <body>
623
+ <div id="swagger-ui"></div>
624
+ <script src="https://unpkg.com/swagger-ui-dist@5/swagger-ui-bundle.js"></script>
625
+ <script src="https://unpkg.com/swagger-ui-dist@5/swagger-ui-standalone-preset.js"></script>
626
+ <script>
627
+ window.onload = function() {
628
+ SwaggerUIBundle({
629
+ url: '${jsonPath}-json',
630
+ dom_id: '#swagger-ui',
631
+ deepLinking: true,
632
+ presets: [
633
+ SwaggerUIBundle.presets.apis,
634
+ SwaggerUIStandalonePreset
635
+ ],
636
+ plugins: [
637
+ SwaggerUIBundle.plugins.DownloadUrl
638
+ ],
639
+ layout: 'StandaloneLayout',
640
+ defaultModelsExpandDepth: 1,
641
+ defaultModelExpandDepth: 1,
642
+ });
643
+ };
644
+ </script>
645
+ </body>
646
+ </html>`;
647
+ }
648
+ }
649
+ // src/openapi/decorators.ts
650
+ function ApiTags(...tags) {
651
+ return (target) => {
652
+ const existingTags = getApiMetadata(target, "api:tags") ?? [];
653
+ const combined = [...new Set([...existingTags, ...tags])];
654
+ setApiMetadata(target, "api:tags", combined);
655
+ return target;
656
+ };
657
+ }
658
+ function ApiBearerAuth(name = "bearer", options) {
659
+ return function(target, propertyKey) {
660
+ const security = [{ [name]: [] }];
661
+ const targetObj = propertyKey ? target : target;
662
+ const store = propertyKey ? getApiMethodMetadata : getApiMetadata;
663
+ const setSt = propertyKey ? setApiMethodMetadata : setApiMetadata;
664
+ const existingSecurity = store(targetObj, "api:security") ?? [];
665
+ setSt(targetObj, "api:security", [...existingSecurity, ...security]);
666
+ return propertyKey ? descriptor : target;
667
+ };
668
+ }
669
+ function ApiBasicAuth(name = "basic") {
670
+ return function(target, propertyKey) {
671
+ const security = [{ [name]: [] }];
672
+ const targetObj = propertyKey ? target : target;
673
+ const store = propertyKey ? getApiMethodMetadata : getApiMetadata;
674
+ const setSt = propertyKey ? setApiMethodMetadata : setApiMetadata;
675
+ const existingSecurity = store(targetObj, "api:security") ?? [];
676
+ setSt(targetObj, "api:security", [...existingSecurity, ...security]);
677
+ return propertyKey ? descriptor : target;
678
+ };
679
+ }
680
+ function ApiApiKey(options, name = "api_key") {
681
+ return function(target, propertyKey) {
682
+ const security = [{ [name]: [] }];
683
+ const targetObj = propertyKey ? target : target;
684
+ const store = propertyKey ? getApiMethodMetadata : getApiMetadata;
685
+ const setSt = propertyKey ? setApiMethodMetadata : setApiMetadata;
686
+ const existingSecurity = store(targetObj, "api:security") ?? [];
687
+ setSt(targetObj, `api:security:scheme:${name}`, options);
688
+ setSt(targetObj, "api:security", [...existingSecurity, ...security]);
689
+ return propertyKey ? descriptor : target;
690
+ };
691
+ }
692
+ function ApiExcludeController() {
693
+ return (target) => {
694
+ setApiMetadata(target, "api:exclude", true);
695
+ return target;
696
+ };
697
+ }
698
+ function ApiOperation(options) {
699
+ return (target, propertyKey) => {
700
+ setApiMethodMetadata(target, `api:operation:${String(propertyKey)}`, options);
701
+ };
702
+ }
703
+ function ApiResponse(options) {
704
+ return (target, propertyKey) => {
705
+ const key = `api:responses:${String(propertyKey)}`;
706
+ const existing = getApiMethodMetadata(target, key) ?? [];
707
+ existing.push(options);
708
+ setApiMethodMetadata(target, key, existing);
709
+ };
710
+ }
711
+ function ApiParam(options) {
712
+ return (target, propertyKey) => {
713
+ const key = `api:params:${String(propertyKey)}`;
714
+ const existing = getApiMethodMetadata(target, key) ?? [];
715
+ existing.push(options);
716
+ setApiMethodMetadata(target, key, existing);
717
+ };
718
+ }
719
+ function ApiQuery(options) {
720
+ return (target, propertyKey) => {
721
+ const key = `api:query:${String(propertyKey)}`;
722
+ const existing = getApiMethodMetadata(target, key) ?? [];
723
+ existing.push(options);
724
+ setApiMethodMetadata(target, key, existing);
725
+ };
726
+ }
727
+ function ApiHeader(options) {
728
+ return (target, propertyKey) => {
729
+ const key = `api:headers:${String(propertyKey)}`;
730
+ const existing = getApiMethodMetadata(target, key) ?? [];
731
+ existing.push(options);
732
+ setApiMethodMetadata(target, key, existing);
733
+ };
734
+ }
735
+ function ApiBody(options) {
736
+ return (target, propertyKey) => {
737
+ setApiMethodMetadata(target, `api:body:${String(propertyKey)}`, options);
738
+ };
739
+ }
740
+ function ApiExcludeEndpoint() {
741
+ return (target, propertyKey) => {
742
+ setApiMethodMetadata(target, `api:exclude:${String(propertyKey)}`, true);
743
+ };
744
+ }
745
+ function ApiProperty(options) {
746
+ return (target, propertyKey) => {
747
+ const opts = { ...options, required: options?.required !== false };
748
+ setApiPropertyMetadata(target, propertyKey, opts);
749
+ };
750
+ }
751
+ function ApiPropertyOptional(options) {
752
+ return (target, propertyKey) => {
753
+ const opts = { ...options, required: false };
754
+ setApiPropertyMetadata(target, propertyKey, opts);
755
+ };
756
+ }
757
+ export {
758
+ SwaggerModule,
759
+ SchemaGenerator,
760
+ RouteScanner,
761
+ DocumentBuilder,
762
+ ApiTags,
763
+ ApiResponse,
764
+ ApiQuery,
765
+ ApiPropertyOptional,
766
+ ApiProperty,
767
+ ApiParam,
768
+ ApiOperation,
769
+ ApiHeader,
770
+ ApiExcludeEndpoint,
771
+ ApiExcludeController,
772
+ ApiBody,
773
+ ApiBearerAuth,
774
+ ApiBasicAuth,
775
+ ApiApiKey
776
+ };