@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,886 @@
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/router/linear.ts
30
+ function patternToRegex(pattern) {
31
+ const paramNames = [];
32
+ let isStatic = true;
33
+ let hasWildcard = false;
34
+ const segments = [];
35
+ let i = 0;
36
+ while (i < pattern.length) {
37
+ if (pattern[i] === ":") {
38
+ i++;
39
+ let name = "";
40
+ while (i < pattern.length && /[a-zA-Z0-9_]/.test(pattern[i])) {
41
+ name += pattern[i];
42
+ i++;
43
+ }
44
+ let optional = false;
45
+ if (i < pattern.length && pattern[i] === "?") {
46
+ optional = true;
47
+ i++;
48
+ }
49
+ let customRegex = "";
50
+ if (i < pattern.length && pattern[i] === "<") {
51
+ i++;
52
+ while (i < pattern.length && pattern[i] !== ">") {
53
+ customRegex += pattern[i];
54
+ i++;
55
+ }
56
+ i++;
57
+ }
58
+ paramNames.push(name);
59
+ isStatic = false;
60
+ if (optional) {
61
+ if (segments.length > 0 && segments[segments.length - 1] === "/") {
62
+ segments.pop();
63
+ }
64
+ segments.push("(?:/([^/]*))?");
65
+ } else if (customRegex) {
66
+ segments.push(`(${customRegex})`);
67
+ } else {
68
+ segments.push("([^/]+)");
69
+ }
70
+ } else if (pattern[i] === "*") {
71
+ hasWildcard = true;
72
+ isStatic = false;
73
+ paramNames.push("*");
74
+ segments.push("(.*)");
75
+ i++;
76
+ } else {
77
+ const char = pattern[i];
78
+ if (/[.+^${}()|[\]\\]/.test(char)) {
79
+ segments.push(`\\${char}`);
80
+ } else {
81
+ segments.push(char);
82
+ }
83
+ i++;
84
+ }
85
+ }
86
+ const regexStr = `^${segments.join("")}/?$`;
87
+ return {
88
+ regex: new RegExp(regexStr, "i"),
89
+ paramNames,
90
+ isStatic,
91
+ hasWildcard
92
+ };
93
+ }
94
+ function extractParams(regex, paramNames, pathname) {
95
+ const params = {};
96
+ const match = pathname.match(regex);
97
+ if (match) {
98
+ paramNames.forEach((name, index) => {
99
+ if (match[index + 1] !== undefined) {
100
+ params[name] = match[index + 1];
101
+ }
102
+ });
103
+ }
104
+ return params;
105
+ }
106
+
107
+ class LinearRouter {
108
+ staticRoutes = new Map;
109
+ dynamicRoutes = [];
110
+ groupPrefix = "";
111
+ groupMiddleware = [];
112
+ routeCounter = 0;
113
+ addRoute(method, pattern, handler, options) {
114
+ const fullPattern = this.groupPrefix + pattern;
115
+ const { regex, paramNames, isStatic } = patternToRegex(fullPattern);
116
+ const optsMiddleware = options?.middleware;
117
+ const routeMiddleware = optsMiddleware ? Array.isArray(optsMiddleware) ? optsMiddleware : [optsMiddleware] : [];
118
+ const middleware = [...this.groupMiddleware, ...routeMiddleware];
119
+ if (isStatic) {
120
+ const normalizedPath = fullPattern.toLowerCase();
121
+ const key = `${method}:${normalizedPath}`;
122
+ this.staticRoutes.set(key, { handler, middleware, name: options?.name });
123
+ if (!normalizedPath.endsWith("/")) {
124
+ this.staticRoutes.set(`${method}:${normalizedPath}/`, {
125
+ handler,
126
+ middleware,
127
+ name: options?.name
128
+ });
129
+ } else if (normalizedPath.length > 1) {
130
+ this.staticRoutes.set(`${method}:${normalizedPath.slice(0, -1)}`, {
131
+ handler,
132
+ middleware,
133
+ name: options?.name
134
+ });
135
+ }
136
+ } else {
137
+ this.dynamicRoutes.push({
138
+ method,
139
+ pattern: fullPattern,
140
+ handler,
141
+ middleware,
142
+ name: options?.name,
143
+ regex,
144
+ paramNames,
145
+ priority: this.routeCounter++
146
+ });
147
+ this.sortDynamicRoutes();
148
+ }
149
+ }
150
+ sortDynamicRoutes() {
151
+ this.dynamicRoutes.sort((a, b) => {
152
+ if (a.paramNames.length !== b.paramNames.length) {
153
+ return a.paramNames.length - b.paramNames.length;
154
+ }
155
+ return a.priority - b.priority;
156
+ });
157
+ }
158
+ match(method, pathname) {
159
+ const normalizedPath = pathname.toLowerCase();
160
+ const staticKey = `${method}:${normalizedPath}`;
161
+ const staticRoute = this.staticRoutes.get(staticKey);
162
+ if (staticRoute) {
163
+ return {
164
+ handler: staticRoute.handler,
165
+ params: {},
166
+ middleware: staticRoute.middleware,
167
+ name: staticRoute.name
168
+ };
169
+ }
170
+ const allKey = `ALL:${normalizedPath}`;
171
+ const allRoute = this.staticRoutes.get(allKey);
172
+ if (allRoute) {
173
+ return {
174
+ handler: allRoute.handler,
175
+ params: {},
176
+ middleware: allRoute.middleware,
177
+ name: allRoute.name
178
+ };
179
+ }
180
+ for (const route of this.dynamicRoutes) {
181
+ if (route.method !== "ALL" && route.method !== method) {
182
+ continue;
183
+ }
184
+ if (route.regex.test(pathname)) {
185
+ const params = extractParams(route.regex, route.paramNames, pathname);
186
+ return {
187
+ handler: route.handler,
188
+ params,
189
+ middleware: route.middleware,
190
+ name: route.name
191
+ };
192
+ }
193
+ }
194
+ return;
195
+ }
196
+ group(prefix, options) {
197
+ const childRouter = new LinearRouter;
198
+ childRouter.staticRoutes = this.staticRoutes;
199
+ childRouter.dynamicRoutes = this.dynamicRoutes;
200
+ childRouter.groupPrefix = this.groupPrefix + prefix;
201
+ const optsMiddleware = options?.middleware;
202
+ const middlewareArray = optsMiddleware ? Array.isArray(optsMiddleware) ? optsMiddleware : [optsMiddleware] : [];
203
+ childRouter.groupMiddleware = [...this.groupMiddleware, ...middlewareArray];
204
+ childRouter.routeCounter = this.routeCounter;
205
+ return childRouter;
206
+ }
207
+ getRoutes() {
208
+ const routes = [];
209
+ const seenPatterns = new Set;
210
+ for (const [key, value] of this.staticRoutes) {
211
+ const [method, path] = key.split(":");
212
+ const pattern = path.endsWith("/") && path.length > 1 ? path.slice(0, -1) : path;
213
+ const dedupeKey = `${method}:${pattern}`;
214
+ if (!seenPatterns.has(dedupeKey)) {
215
+ seenPatterns.add(dedupeKey);
216
+ routes.push({ method, pattern: pattern || "/", name: value.name });
217
+ }
218
+ }
219
+ for (const route of this.dynamicRoutes) {
220
+ routes.push({
221
+ method: route.method,
222
+ pattern: route.pattern,
223
+ name: route.name
224
+ });
225
+ }
226
+ return routes;
227
+ }
228
+ getRouterType() {
229
+ return "linear";
230
+ }
231
+ getRouteCount() {
232
+ const staticCount = Math.floor(this.staticRoutes.size / 2);
233
+ return {
234
+ static: staticCount,
235
+ dynamic: this.dynamicRoutes.length,
236
+ total: staticCount + this.dynamicRoutes.length
237
+ };
238
+ }
239
+ get(pattern, handler, options) {
240
+ this.addRoute("GET", pattern, handler, options);
241
+ }
242
+ post(pattern, handler, options) {
243
+ this.addRoute("POST", pattern, handler, options);
244
+ }
245
+ put(pattern, handler, options) {
246
+ this.addRoute("PUT", pattern, handler, options);
247
+ }
248
+ patch(pattern, handler, options) {
249
+ this.addRoute("PATCH", pattern, handler, options);
250
+ }
251
+ delete(pattern, handler, options) {
252
+ this.addRoute("DELETE", pattern, handler, options);
253
+ }
254
+ head(pattern, handler, options) {
255
+ this.addRoute("HEAD", pattern, handler, options);
256
+ }
257
+ options(pattern, handler, options) {
258
+ this.addRoute("OPTIONS", pattern, handler, options);
259
+ }
260
+ all(pattern, handler, options) {
261
+ this.addRoute("ALL", pattern, handler, options);
262
+ }
263
+ }
264
+
265
+ // src/router/regex.ts
266
+ function patternToRegex2(pattern) {
267
+ const paramNames = [];
268
+ let isStatic = true;
269
+ let hasWildcard = false;
270
+ const segments = [];
271
+ let i = 0;
272
+ while (i < pattern.length) {
273
+ if (pattern[i] === ":") {
274
+ i++;
275
+ let name = "";
276
+ while (i < pattern.length && /[a-zA-Z0-9_]/.test(pattern[i])) {
277
+ name += pattern[i];
278
+ i++;
279
+ }
280
+ let optional = false;
281
+ if (i < pattern.length && pattern[i] === "?") {
282
+ optional = true;
283
+ i++;
284
+ }
285
+ let customRegex = "";
286
+ if (i < pattern.length && pattern[i] === "<") {
287
+ i++;
288
+ while (i < pattern.length && pattern[i] !== ">") {
289
+ customRegex += pattern[i];
290
+ i++;
291
+ }
292
+ i++;
293
+ }
294
+ paramNames.push(name);
295
+ isStatic = false;
296
+ if (optional) {
297
+ if (segments.length > 0 && segments[segments.length - 1] === "/") {
298
+ segments.pop();
299
+ }
300
+ segments.push("(?:/([^/]*))?");
301
+ } else if (customRegex) {
302
+ segments.push(`(${customRegex})`);
303
+ } else {
304
+ segments.push("([^/]+)");
305
+ }
306
+ } else if (pattern[i] === "*") {
307
+ hasWildcard = true;
308
+ isStatic = false;
309
+ paramNames.push("*");
310
+ segments.push("(.*)");
311
+ i++;
312
+ } else {
313
+ const char = pattern[i];
314
+ if (/[.+^${}()|[\]\\]/.test(char)) {
315
+ segments.push(`\\${char}`);
316
+ } else {
317
+ segments.push(char);
318
+ }
319
+ i++;
320
+ }
321
+ }
322
+ const regexStr = `^${segments.join("")}/?$`;
323
+ return {
324
+ regex: new RegExp(regexStr, "i"),
325
+ paramNames,
326
+ isStatic,
327
+ hasWildcard
328
+ };
329
+ }
330
+ function extractParams2(regex, paramNames, pathname) {
331
+ const params = {};
332
+ const match = pathname.match(regex);
333
+ if (match) {
334
+ paramNames.forEach((name, index) => {
335
+ if (match[index + 1] !== undefined) {
336
+ params[name] = match[index + 1];
337
+ }
338
+ });
339
+ }
340
+ return params;
341
+ }
342
+
343
+ class RegexRouter {
344
+ routes = [];
345
+ groupPrefix = "";
346
+ groupMiddleware = [];
347
+ routeCounter = 0;
348
+ addRoute(method, pattern, handler, options) {
349
+ const fullPattern = this.groupPrefix + pattern;
350
+ const { regex, paramNames, isStatic } = patternToRegex2(fullPattern);
351
+ const optsMiddleware = options?.middleware;
352
+ const routeMiddleware = optsMiddleware ? Array.isArray(optsMiddleware) ? optsMiddleware : [optsMiddleware] : [];
353
+ this.routes.push({
354
+ method,
355
+ pattern: fullPattern,
356
+ handler,
357
+ middleware: [...this.groupMiddleware, ...routeMiddleware],
358
+ name: options?.name,
359
+ regex,
360
+ paramNames,
361
+ isStatic,
362
+ priority: this.routeCounter++
363
+ });
364
+ this.sortRoutes();
365
+ }
366
+ sortRoutes() {
367
+ this.routes.sort((a, b) => {
368
+ if (a.isStatic !== b.isStatic) {
369
+ return a.isStatic ? -1 : 1;
370
+ }
371
+ if (a.paramNames.length !== b.paramNames.length) {
372
+ return a.paramNames.length - b.paramNames.length;
373
+ }
374
+ return a.priority - b.priority;
375
+ });
376
+ }
377
+ match(method, pathname) {
378
+ for (const route of this.routes) {
379
+ if (route.method !== "ALL" && route.method !== method) {
380
+ continue;
381
+ }
382
+ if (route.regex.test(pathname)) {
383
+ const params = extractParams2(route.regex, route.paramNames, pathname);
384
+ return {
385
+ handler: route.handler,
386
+ params,
387
+ middleware: route.middleware,
388
+ name: route.name
389
+ };
390
+ }
391
+ }
392
+ return;
393
+ }
394
+ group(prefix, options) {
395
+ const childRouter = new RegexRouter;
396
+ childRouter.routes = this.routes;
397
+ childRouter.groupPrefix = this.groupPrefix + prefix;
398
+ const optsMiddleware = options?.middleware;
399
+ const middlewareArray = optsMiddleware ? Array.isArray(optsMiddleware) ? optsMiddleware : [optsMiddleware] : [];
400
+ childRouter.groupMiddleware = [...this.groupMiddleware, ...middlewareArray];
401
+ childRouter.routeCounter = this.routeCounter;
402
+ return childRouter;
403
+ }
404
+ getRoutes() {
405
+ return this.routes.map((r) => ({
406
+ method: r.method,
407
+ pattern: r.pattern,
408
+ name: r.name
409
+ }));
410
+ }
411
+ getRouterType() {
412
+ return "regex";
413
+ }
414
+ getRouteCount() {
415
+ return this.routes.length;
416
+ }
417
+ get(pattern, handler, options) {
418
+ this.addRoute("GET", pattern, handler, options);
419
+ }
420
+ post(pattern, handler, options) {
421
+ this.addRoute("POST", pattern, handler, options);
422
+ }
423
+ put(pattern, handler, options) {
424
+ this.addRoute("PUT", pattern, handler, options);
425
+ }
426
+ patch(pattern, handler, options) {
427
+ this.addRoute("PATCH", pattern, handler, options);
428
+ }
429
+ delete(pattern, handler, options) {
430
+ this.addRoute("DELETE", pattern, handler, options);
431
+ }
432
+ head(pattern, handler, options) {
433
+ this.addRoute("HEAD", pattern, handler, options);
434
+ }
435
+ options(pattern, handler, options) {
436
+ this.addRoute("OPTIONS", pattern, handler, options);
437
+ }
438
+ all(pattern, handler, options) {
439
+ this.addRoute("ALL", pattern, handler, options);
440
+ }
441
+ }
442
+
443
+ // src/router/tree.ts
444
+ class TreeRouter {
445
+ root;
446
+ groupPrefix = "";
447
+ groupMiddleware = [];
448
+ routeCount = 0;
449
+ constructor() {
450
+ this.root = this.createNode("");
451
+ }
452
+ createNode(path, paramName, paramRegex, isWildcard = false) {
453
+ return {
454
+ path,
455
+ children: new Map,
456
+ handlers: new Map,
457
+ paramName,
458
+ paramRegex,
459
+ isWildcard
460
+ };
461
+ }
462
+ addRoute(method, pattern, handler, options) {
463
+ const fullPattern = this.groupPrefix + pattern;
464
+ const optsMiddleware = options?.middleware;
465
+ const routeMiddleware = optsMiddleware ? Array.isArray(optsMiddleware) ? optsMiddleware : [optsMiddleware] : [];
466
+ const middleware = [...this.groupMiddleware, ...routeMiddleware];
467
+ const segments = this.parsePattern(fullPattern);
468
+ const node = this.insertSegments(this.root, segments, 0);
469
+ node.handlers.set(method, {
470
+ handler,
471
+ middleware,
472
+ name: options?.name
473
+ });
474
+ this.routeCount++;
475
+ }
476
+ parsePattern(pattern) {
477
+ const segments = [];
478
+ let normalized = pattern;
479
+ if (!normalized.startsWith("/")) {
480
+ normalized = `/${normalized}`;
481
+ }
482
+ if (normalized.length > 1 && normalized.endsWith("/")) {
483
+ normalized = normalized.slice(0, -1);
484
+ }
485
+ if (normalized === "/") {
486
+ return [{ type: "static", value: "" }];
487
+ }
488
+ const parts = normalized.split("/").filter(Boolean);
489
+ for (const part of parts) {
490
+ if (part === "*") {
491
+ segments.push({ type: "wildcard" });
492
+ } else if (part.startsWith(":")) {
493
+ let name = part.slice(1);
494
+ let regex;
495
+ let optional = false;
496
+ if (name.endsWith("?")) {
497
+ optional = true;
498
+ name = name.slice(0, -1);
499
+ }
500
+ const regexMatch = name.match(/^(\w+)<(.+)>$/);
501
+ if (regexMatch) {
502
+ name = regexMatch[1];
503
+ regex = new RegExp(`^(${regexMatch[2]})$`);
504
+ }
505
+ segments.push({ type: "param", name, regex, optional });
506
+ } else {
507
+ segments.push({ type: "static", value: part.toLowerCase() });
508
+ }
509
+ }
510
+ return segments;
511
+ }
512
+ insertSegments(node, segments, index) {
513
+ if (index >= segments.length) {
514
+ return node;
515
+ }
516
+ const segment = segments[index];
517
+ if (segment.type === "wildcard") {
518
+ if (!node.wildcardChild) {
519
+ node.wildcardChild = this.createNode("*", "*", undefined, true);
520
+ }
521
+ return node.wildcardChild;
522
+ }
523
+ if (segment.type === "param") {
524
+ if (!node.paramChild) {
525
+ node.paramChild = this.createNode(`:${segment.name}`, segment.name, segment.regex);
526
+ }
527
+ return this.insertSegments(node.paramChild, segments, index + 1);
528
+ }
529
+ const value = segment.value;
530
+ let child = node.children.get(value);
531
+ if (!child) {
532
+ child = this.createNode(value);
533
+ node.children.set(value, child);
534
+ }
535
+ return this.insertSegments(child, segments, index + 1);
536
+ }
537
+ match(method, pathname) {
538
+ const params = {};
539
+ let normalized = pathname.toLowerCase();
540
+ if (normalized.length > 1 && normalized.endsWith("/")) {
541
+ normalized = normalized.slice(0, -1);
542
+ }
543
+ const parts = normalized === "/" ? [""] : normalized.split("/").filter(Boolean);
544
+ const result = this.searchTree(this.root, parts, 0, method, params);
545
+ return result;
546
+ }
547
+ searchTree(node, parts, partIndex, method, params) {
548
+ if (partIndex >= parts.length) {
549
+ const handlerEntry = node.handlers.get(method) || node.handlers.get("ALL");
550
+ if (handlerEntry) {
551
+ return {
552
+ handler: handlerEntry.handler,
553
+ params: { ...params },
554
+ middleware: handlerEntry.middleware,
555
+ name: handlerEntry.name
556
+ };
557
+ }
558
+ return;
559
+ }
560
+ const part = parts[partIndex];
561
+ const staticChild = node.children.get(part);
562
+ if (staticChild) {
563
+ const result = this.searchTree(staticChild, parts, partIndex + 1, method, params);
564
+ if (result)
565
+ return result;
566
+ }
567
+ if (node.paramChild) {
568
+ const paramNode = node.paramChild;
569
+ if (paramNode.paramRegex) {
570
+ if (paramNode.paramRegex.test(part)) {
571
+ params[paramNode.paramName] = part;
572
+ const result = this.searchTree(paramNode, parts, partIndex + 1, method, params);
573
+ if (result)
574
+ return result;
575
+ delete params[paramNode.paramName];
576
+ }
577
+ } else {
578
+ params[paramNode.paramName] = part;
579
+ const result = this.searchTree(paramNode, parts, partIndex + 1, method, params);
580
+ if (result)
581
+ return result;
582
+ delete params[paramNode.paramName];
583
+ }
584
+ }
585
+ if (node.wildcardChild) {
586
+ const wildcardNode = node.wildcardChild;
587
+ params["*"] = parts.slice(partIndex).join("/");
588
+ const handlerEntry = wildcardNode.handlers.get(method) || wildcardNode.handlers.get("ALL");
589
+ if (handlerEntry) {
590
+ return {
591
+ handler: handlerEntry.handler,
592
+ params: { ...params },
593
+ middleware: handlerEntry.middleware,
594
+ name: handlerEntry.name
595
+ };
596
+ }
597
+ }
598
+ return;
599
+ }
600
+ group(prefix, options) {
601
+ const childRouter = new TreeRouter;
602
+ childRouter.root = this.root;
603
+ childRouter.groupPrefix = this.groupPrefix + prefix;
604
+ const optsMiddleware = options?.middleware;
605
+ const middlewareArray = optsMiddleware ? Array.isArray(optsMiddleware) ? optsMiddleware : [optsMiddleware] : [];
606
+ childRouter.groupMiddleware = [...this.groupMiddleware, ...middlewareArray];
607
+ childRouter.routeCount = this.routeCount;
608
+ return childRouter;
609
+ }
610
+ getRoutes() {
611
+ const routes = [];
612
+ this.traverseTree(this.root, "", routes);
613
+ return routes;
614
+ }
615
+ traverseTree(node, currentPath, routes) {
616
+ for (const [method, entry] of node.handlers) {
617
+ routes.push({
618
+ method,
619
+ pattern: currentPath || "/",
620
+ name: entry.name
621
+ });
622
+ }
623
+ for (const [path, child] of node.children) {
624
+ const childPath = `${currentPath}/${path}`;
625
+ this.traverseTree(child, childPath, routes);
626
+ }
627
+ if (node.paramChild) {
628
+ const childPath = `${currentPath}/:${node.paramChild.paramName}`;
629
+ this.traverseTree(node.paramChild, childPath, routes);
630
+ }
631
+ if (node.wildcardChild) {
632
+ const childPath = `${currentPath}/*`;
633
+ this.traverseTree(node.wildcardChild, childPath, routes);
634
+ }
635
+ }
636
+ getRouterType() {
637
+ return "tree";
638
+ }
639
+ getRouteCount() {
640
+ return this.routeCount;
641
+ }
642
+ getTreeStats() {
643
+ return {
644
+ nodes: this.countNodes(this.root),
645
+ depth: this.getTreeDepth(this.root),
646
+ routes: this.routeCount
647
+ };
648
+ }
649
+ countNodes(node) {
650
+ let count = 1;
651
+ for (const child of node.children.values()) {
652
+ count += this.countNodes(child);
653
+ }
654
+ if (node.paramChild) {
655
+ count += this.countNodes(node.paramChild);
656
+ }
657
+ if (node.wildcardChild) {
658
+ count += this.countNodes(node.wildcardChild);
659
+ }
660
+ return count;
661
+ }
662
+ getTreeDepth(node) {
663
+ let maxDepth = 0;
664
+ for (const child of node.children.values()) {
665
+ maxDepth = Math.max(maxDepth, this.getTreeDepth(child));
666
+ }
667
+ if (node.paramChild) {
668
+ maxDepth = Math.max(maxDepth, this.getTreeDepth(node.paramChild));
669
+ }
670
+ if (node.wildcardChild) {
671
+ maxDepth = Math.max(maxDepth, this.getTreeDepth(node.wildcardChild));
672
+ }
673
+ return maxDepth + 1;
674
+ }
675
+ get(pattern, handler, options) {
676
+ this.addRoute("GET", pattern, handler, options);
677
+ }
678
+ post(pattern, handler, options) {
679
+ this.addRoute("POST", pattern, handler, options);
680
+ }
681
+ put(pattern, handler, options) {
682
+ this.addRoute("PUT", pattern, handler, options);
683
+ }
684
+ patch(pattern, handler, options) {
685
+ this.addRoute("PATCH", pattern, handler, options);
686
+ }
687
+ delete(pattern, handler, options) {
688
+ this.addRoute("DELETE", pattern, handler, options);
689
+ }
690
+ head(pattern, handler, options) {
691
+ this.addRoute("HEAD", pattern, handler, options);
692
+ }
693
+ options(pattern, handler, options) {
694
+ this.addRoute("OPTIONS", pattern, handler, options);
695
+ }
696
+ all(pattern, handler, options) {
697
+ this.addRoute("ALL", pattern, handler, options);
698
+ }
699
+ }
700
+
701
+ // src/router/index.ts
702
+ var DEFAULT_LINEAR_THRESHOLD = 10;
703
+ var DEFAULT_REGEX_THRESHOLD = 50;
704
+
705
+ class Router {
706
+ router;
707
+ config;
708
+ pendingRoutes = [];
709
+ isBuilt = false;
710
+ groupPrefix = "";
711
+ groupMiddleware = [];
712
+ constructor(config = {}) {
713
+ this.config = {
714
+ type: config.type ?? "auto",
715
+ linearThreshold: config.linearThreshold ?? DEFAULT_LINEAR_THRESHOLD,
716
+ regexThreshold: config.regexThreshold ?? DEFAULT_REGEX_THRESHOLD
717
+ };
718
+ if (this.config.type !== "auto") {
719
+ this.router = this.createRouter(this.config.type);
720
+ } else {
721
+ this.router = new LinearRouter;
722
+ }
723
+ }
724
+ createRouter(type) {
725
+ switch (type) {
726
+ case "linear":
727
+ return new LinearRouter;
728
+ case "regex":
729
+ return new RegexRouter;
730
+ case "tree":
731
+ return new TreeRouter;
732
+ default:
733
+ return new LinearRouter;
734
+ }
735
+ }
736
+ getOptimalRouterType(count) {
737
+ if (count <= this.config.linearThreshold) {
738
+ return "linear";
739
+ }
740
+ if (count <= this.config.regexThreshold) {
741
+ return "regex";
742
+ }
743
+ return "tree";
744
+ }
745
+ migrateRouter(newType) {
746
+ this.router = this.createRouter(newType);
747
+ for (const route of this.pendingRoutes) {
748
+ this.addToRouter(route.method, route.pattern, route.handler, route.options);
749
+ }
750
+ }
751
+ addToRouter(method, pattern, handler, options) {
752
+ switch (method) {
753
+ case "GET":
754
+ this.router.get(pattern, handler, options);
755
+ break;
756
+ case "POST":
757
+ this.router.post(pattern, handler, options);
758
+ break;
759
+ case "PUT":
760
+ this.router.put(pattern, handler, options);
761
+ break;
762
+ case "PATCH":
763
+ this.router.patch(pattern, handler, options);
764
+ break;
765
+ case "DELETE":
766
+ this.router.delete(pattern, handler, options);
767
+ break;
768
+ case "HEAD":
769
+ this.router.head(pattern, handler, options);
770
+ break;
771
+ case "OPTIONS":
772
+ this.router.options(pattern, handler, options);
773
+ break;
774
+ case "ALL":
775
+ this.router.all(pattern, handler, options);
776
+ break;
777
+ }
778
+ }
779
+ addRoute(method, pattern, handler, options) {
780
+ const fullPattern = this.groupPrefix + pattern;
781
+ const optsMiddleware = options?.middleware;
782
+ const routeMiddleware = optsMiddleware ? Array.isArray(optsMiddleware) ? optsMiddleware : [optsMiddleware] : [];
783
+ const allMiddleware = [...this.groupMiddleware, ...routeMiddleware];
784
+ const fullOptions = options?.name || allMiddleware.length > 0 ? {
785
+ name: options?.name,
786
+ middleware: allMiddleware
787
+ } : undefined;
788
+ this.pendingRoutes.push({
789
+ method,
790
+ pattern: fullPattern,
791
+ handler,
792
+ options: fullOptions
793
+ });
794
+ this.addToRouter(method, fullPattern, handler, fullOptions);
795
+ if (this.config.type === "auto" && !this.isBuilt) {
796
+ const routeCount = this.pendingRoutes.length;
797
+ const optimalType = this.getOptimalRouterType(routeCount);
798
+ const currentType = this.router.getRouterType();
799
+ if (currentType !== optimalType) {
800
+ this.migrateRouter(optimalType);
801
+ }
802
+ }
803
+ }
804
+ match(method, pathname) {
805
+ this.isBuilt = true;
806
+ return this.router.match(method, pathname);
807
+ }
808
+ group(prefix, options) {
809
+ const childRouter = new Router(this.config);
810
+ childRouter.router = this.router;
811
+ childRouter.pendingRoutes = this.pendingRoutes;
812
+ childRouter.groupPrefix = this.groupPrefix + prefix;
813
+ childRouter.isBuilt = this.isBuilt;
814
+ const optsMiddleware = options?.middleware;
815
+ const middlewareArray = optsMiddleware ? Array.isArray(optsMiddleware) ? optsMiddleware : [optsMiddleware] : [];
816
+ childRouter.groupMiddleware = [...this.groupMiddleware, ...middlewareArray];
817
+ return childRouter;
818
+ }
819
+ getRoutes() {
820
+ return this.router.getRoutes();
821
+ }
822
+ getRouterType() {
823
+ return this.router.getRouterType();
824
+ }
825
+ getRouteCount() {
826
+ return this.pendingRoutes.length;
827
+ }
828
+ getConfig() {
829
+ return { ...this.config };
830
+ }
831
+ get(pattern, handler, options) {
832
+ this.addRoute("GET", pattern, handler, options);
833
+ }
834
+ post(pattern, handler, options) {
835
+ this.addRoute("POST", pattern, handler, options);
836
+ }
837
+ put(pattern, handler, options) {
838
+ this.addRoute("PUT", pattern, handler, options);
839
+ }
840
+ patch(pattern, handler, options) {
841
+ this.addRoute("PATCH", pattern, handler, options);
842
+ }
843
+ delete(pattern, handler, options) {
844
+ this.addRoute("DELETE", pattern, handler, options);
845
+ }
846
+ head(pattern, handler, options) {
847
+ this.addRoute("HEAD", pattern, handler, options);
848
+ }
849
+ options(pattern, handler, options) {
850
+ this.addRoute("OPTIONS", pattern, handler, options);
851
+ }
852
+ all(pattern, handler, options) {
853
+ this.addRoute("ALL", pattern, handler, options);
854
+ }
855
+ }
856
+ function generateUrl(pattern, params = {}) {
857
+ return pattern.replace(/:([a-zA-Z_][a-zA-Z0-9_]*)\?/g, (_, name) => params[name] ?? "").replace(/:([a-zA-Z_][a-zA-Z0-9_]*)<[^>]+>/g, (_, name) => params[name] ?? "").replace(/:([a-zA-Z_][a-zA-Z0-9_]*)/g, (_, name) => {
858
+ if (params[name] === undefined) {
859
+ throw new Error(`Missing required parameter: ${name}`);
860
+ }
861
+ return params[name];
862
+ }).replace(/\*/g, () => params["*"] ?? "");
863
+ }
864
+ function createRouter(config) {
865
+ return new Router(config);
866
+ }
867
+ function createLinearRouter() {
868
+ return new LinearRouter;
869
+ }
870
+ function createRegexRouter() {
871
+ return new RegexRouter;
872
+ }
873
+ function createTreeRouter() {
874
+ return new TreeRouter;
875
+ }
876
+ export {
877
+ generateUrl,
878
+ createTreeRouter,
879
+ createRouter,
880
+ createRegexRouter,
881
+ createLinearRouter,
882
+ TreeRouter,
883
+ Router,
884
+ RegexRouter,
885
+ LinearRouter
886
+ };