@carno.js/core 0.2.11 → 1.0.2

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 (314) hide show
  1. package/LICENSE +673 -673
  2. package/README.md +188 -0
  3. package/dist/Carno.js +272 -333
  4. package/dist/Carno.mjs +268 -0
  5. package/dist/DefaultRoutes.js +51 -0
  6. package/dist/DefaultRoutes.mjs +34 -0
  7. package/dist/bun/index.js +183 -0
  8. package/dist/bun/index.js.map +86 -0
  9. package/dist/cache/CacheDriver.js +13 -0
  10. package/dist/cache/CacheDriver.mjs +0 -0
  11. package/dist/cache/CacheService.js +113 -0
  12. package/dist/cache/CacheService.mjs +93 -0
  13. package/dist/cache/MemoryDriver.js +66 -0
  14. package/dist/cache/MemoryDriver.mjs +46 -0
  15. package/dist/cache/RedisDriver.js +81 -0
  16. package/dist/cache/RedisDriver.mjs +61 -0
  17. package/dist/compiler/JITCompiler.js +111 -0
  18. package/dist/compiler/JITCompiler.mjs +89 -0
  19. package/dist/container/Container.js +99 -0
  20. package/dist/container/Container.mjs +78 -0
  21. package/dist/context/Context.js +97 -0
  22. package/dist/context/Context.mjs +77 -0
  23. package/dist/cors/CorsHandler.js +90 -0
  24. package/dist/cors/CorsHandler.mjs +68 -0
  25. package/dist/decorators/Controller.js +42 -0
  26. package/dist/decorators/Controller.mjs +22 -0
  27. package/dist/decorators/Inject.js +30 -0
  28. package/dist/decorators/Inject.mjs +10 -0
  29. package/dist/decorators/Middleware.js +35 -0
  30. package/dist/decorators/Middleware.mjs +15 -0
  31. package/dist/decorators/Service.js +31 -0
  32. package/dist/decorators/Service.mjs +12 -0
  33. package/dist/decorators/methods.js +60 -0
  34. package/dist/decorators/methods.mjs +34 -0
  35. package/dist/decorators/params.js +63 -0
  36. package/dist/decorators/params.mjs +37 -0
  37. package/dist/events/Lifecycle.js +69 -0
  38. package/dist/events/Lifecycle.mjs +41 -0
  39. package/dist/exceptions/HttpException.js +112 -26
  40. package/dist/exceptions/HttpException.mjs +82 -0
  41. package/dist/index.js +129 -31
  42. package/dist/index.mjs +94 -0
  43. package/dist/metadata.js +34 -0
  44. package/dist/metadata.mjs +9 -0
  45. package/dist/middleware/CarnoMiddleware.js +13 -0
  46. package/dist/middleware/CarnoMiddleware.mjs +0 -0
  47. package/dist/router/RadixRouter.js +121 -0
  48. package/dist/router/RadixRouter.mjs +101 -0
  49. package/dist/testing/TestHarness.js +81 -0
  50. package/dist/testing/TestHarness.mjs +60 -0
  51. package/dist/utils/Metadata.js +53 -0
  52. package/dist/utils/Metadata.mjs +31 -0
  53. package/dist/validation/ValibotAdapter.js +67 -0
  54. package/dist/validation/ValibotAdapter.mjs +48 -0
  55. package/dist/validation/ValidatorAdapter.js +35 -19
  56. package/dist/validation/ValidatorAdapter.mjs +14 -0
  57. package/dist/validation/ZodAdapter.js +80 -0
  58. package/dist/validation/ZodAdapter.mjs +59 -0
  59. package/package.json +21 -63
  60. package/src/Carno.ts +605 -0
  61. package/src/DefaultRoutes.ts +34 -0
  62. package/src/cache/CacheDriver.ts +50 -0
  63. package/src/cache/CacheService.ts +139 -0
  64. package/src/cache/MemoryDriver.ts +104 -0
  65. package/src/cache/RedisDriver.ts +116 -0
  66. package/src/compiler/JITCompiler.ts +167 -0
  67. package/src/container/Container.ts +168 -0
  68. package/src/context/Context.ts +128 -0
  69. package/src/cors/CorsHandler.ts +145 -0
  70. package/src/decorators/Controller.ts +63 -0
  71. package/src/decorators/Inject.ts +16 -0
  72. package/src/decorators/Middleware.ts +22 -0
  73. package/src/decorators/Service.ts +18 -0
  74. package/src/decorators/methods.ts +58 -0
  75. package/src/decorators/params.ts +47 -0
  76. package/src/events/Lifecycle.ts +97 -0
  77. package/src/exceptions/HttpException.ts +99 -0
  78. package/src/index.ts +92 -0
  79. package/src/metadata.ts +46 -0
  80. package/src/middleware/CarnoMiddleware.ts +14 -0
  81. package/src/router/RadixRouter.ts +225 -0
  82. package/src/testing/TestHarness.ts +177 -0
  83. package/src/utils/Metadata.ts +43 -0
  84. package/src/validation/ValibotAdapter.ts +95 -0
  85. package/src/validation/ValidatorAdapter.ts +69 -0
  86. package/src/validation/ZodAdapter.ts +102 -0
  87. package/dist/Carno.d.ts +0 -75
  88. package/dist/cache/bento-cache.driver.d.ts +0 -13
  89. package/dist/cache/bento-cache.driver.js +0 -55
  90. package/dist/cache/cache.service.d.ts +0 -8
  91. package/dist/cache/cache.service.js +0 -6
  92. package/dist/commons/decorators/Injectable.decorator.d.ts +0 -20
  93. package/dist/commons/decorators/Injectable.decorator.js +0 -33
  94. package/dist/commons/decorators/controller.decorator.d.ts +0 -8
  95. package/dist/commons/decorators/controller.decorator.js +0 -22
  96. package/dist/commons/decorators/http.decorators.d.ts +0 -13
  97. package/dist/commons/decorators/http.decorators.js +0 -63
  98. package/dist/commons/decorators/index.d.ts +0 -6
  99. package/dist/commons/decorators/index.js +0 -22
  100. package/dist/commons/decorators/inject.decorator.d.ts +0 -1
  101. package/dist/commons/decorators/inject.decorator.js +0 -5
  102. package/dist/commons/decorators/middleware.decorator.d.ts +0 -2
  103. package/dist/commons/decorators/middleware.decorator.js +0 -30
  104. package/dist/commons/decorators/service.decorator.d.ts +0 -2
  105. package/dist/commons/decorators/service.decorator.js +0 -7
  106. package/dist/commons/decorators/validation.decorator.d.ts +0 -32
  107. package/dist/commons/decorators/validation.decorator.js +0 -40
  108. package/dist/commons/http-code.enum.d.ts +0 -50
  109. package/dist/commons/http-code.enum.js +0 -54
  110. package/dist/commons/index.d.ts +0 -3
  111. package/dist/commons/index.js +0 -19
  112. package/dist/commons/registries/ProviderControl.d.ts +0 -77
  113. package/dist/commons/registries/ProviderControl.js +0 -112
  114. package/dist/commons/registries/ProviderRegistry.d.ts +0 -7
  115. package/dist/commons/registries/ProviderRegistry.js +0 -20
  116. package/dist/constants.d.ts +0 -8
  117. package/dist/constants.js +0 -11
  118. package/dist/container/ContainerConfiguration.d.ts +0 -45
  119. package/dist/container/ContainerConfiguration.js +0 -121
  120. package/dist/container/DependencyResolver.d.ts +0 -20
  121. package/dist/container/DependencyResolver.js +0 -85
  122. package/dist/container/InjectorService.d.ts +0 -58
  123. package/dist/container/InjectorService.js +0 -286
  124. package/dist/container/MethodInvoker.d.ts +0 -21
  125. package/dist/container/MethodInvoker.js +0 -83
  126. package/dist/container/RouteResolver.d.ts +0 -27
  127. package/dist/container/RouteResolver.js +0 -173
  128. package/dist/container/container.d.ts +0 -41
  129. package/dist/container/container.js +0 -71
  130. package/dist/container/createContainer.d.ts +0 -3
  131. package/dist/container/createContainer.js +0 -12
  132. package/dist/container/createInjector.d.ts +0 -2
  133. package/dist/container/createInjector.js +0 -7
  134. package/dist/container/index.d.ts +0 -6
  135. package/dist/container/index.js +0 -22
  136. package/dist/container/middleware.resolver.d.ts +0 -9
  137. package/dist/container/middleware.resolver.js +0 -35
  138. package/dist/default-routes-carno.d.ts +0 -3
  139. package/dist/default-routes-carno.js +0 -29
  140. package/dist/domain/BaseContext.d.ts +0 -15
  141. package/dist/domain/BaseContext.js +0 -2
  142. package/dist/domain/CarnoClosure.d.ts +0 -1
  143. package/dist/domain/CarnoClosure.js +0 -2
  144. package/dist/domain/CarnoMiddleware.d.ts +0 -5
  145. package/dist/domain/CarnoMiddleware.js +0 -2
  146. package/dist/domain/Context.d.ts +0 -58
  147. package/dist/domain/Context.js +0 -188
  148. package/dist/domain/FastContext.d.ts +0 -34
  149. package/dist/domain/FastContext.js +0 -59
  150. package/dist/domain/LocalsContainer.d.ts +0 -4
  151. package/dist/domain/LocalsContainer.js +0 -10
  152. package/dist/domain/Metadata.d.ts +0 -449
  153. package/dist/domain/Metadata.js +0 -511
  154. package/dist/domain/cors-config.d.ts +0 -12
  155. package/dist/domain/cors-config.js +0 -18
  156. package/dist/domain/cors-headers-cache.d.ts +0 -17
  157. package/dist/domain/cors-headers-cache.js +0 -101
  158. package/dist/domain/http-method.d.ts +0 -7
  159. package/dist/domain/http-method.js +0 -11
  160. package/dist/domain/index.d.ts +0 -10
  161. package/dist/domain/index.js +0 -26
  162. package/dist/domain/provider-scope.d.ts +0 -5
  163. package/dist/domain/provider-scope.js +0 -9
  164. package/dist/domain/provider-type.d.ts +0 -6
  165. package/dist/domain/provider-type.js +0 -10
  166. package/dist/domain/provider.d.ts +0 -37
  167. package/dist/domain/provider.js +0 -70
  168. package/dist/events/hooks.decorator.d.ts +0 -3
  169. package/dist/events/hooks.decorator.js +0 -29
  170. package/dist/events/index.d.ts +0 -2
  171. package/dist/events/index.js +0 -18
  172. package/dist/events/on-event.d.ts +0 -13
  173. package/dist/events/on-event.js +0 -11
  174. package/dist/exceptions/HttpException.d.ts +0 -9
  175. package/dist/exceptions/index.d.ts +0 -1
  176. package/dist/exceptions/index.js +0 -17
  177. package/dist/index.d.ts +0 -16
  178. package/dist/route/CompiledRoute.d.ts +0 -23
  179. package/dist/route/CompiledRoute.js +0 -9
  180. package/dist/route/FastPathExecutor.d.ts +0 -12
  181. package/dist/route/FastPathExecutor.js +0 -50
  182. package/dist/route/JITCompiler.d.ts +0 -28
  183. package/dist/route/JITCompiler.js +0 -245
  184. package/dist/route/Matcher.d.ts +0 -11
  185. package/dist/route/Matcher.js +0 -48
  186. package/dist/route/ParamResolverFactory.d.ts +0 -14
  187. package/dist/route/ParamResolverFactory.js +0 -49
  188. package/dist/route/RouteCompiler.d.ts +0 -28
  189. package/dist/route/RouteCompiler.js +0 -157
  190. package/dist/route/RouteExecutor.d.ts +0 -12
  191. package/dist/route/RouteExecutor.js +0 -84
  192. package/dist/route/memoirist.d.ts +0 -31
  193. package/dist/route/memoirist.js +0 -373
  194. package/dist/services/logger.service.d.ts +0 -23
  195. package/dist/services/logger.service.js +0 -47
  196. package/dist/testing/core-testing.d.ts +0 -24
  197. package/dist/testing/core-testing.js +0 -102
  198. package/dist/testing/index.d.ts +0 -1
  199. package/dist/testing/index.js +0 -17
  200. package/dist/utils/ValidationCache.d.ts +0 -5
  201. package/dist/utils/ValidationCache.js +0 -35
  202. package/dist/utils/ancestorOf.d.ts +0 -2
  203. package/dist/utils/ancestorOf.js +0 -10
  204. package/dist/utils/ancestorsOf.d.ts +0 -6
  205. package/dist/utils/ancestorsOf.js +0 -20
  206. package/dist/utils/classOf.d.ts +0 -13
  207. package/dist/utils/classOf.js +0 -21
  208. package/dist/utils/cleanObject.d.ts +0 -6
  209. package/dist/utils/cleanObject.js +0 -22
  210. package/dist/utils/constructorOf.d.ts +0 -11
  211. package/dist/utils/constructorOf.js +0 -18
  212. package/dist/utils/createInstance.d.ts +0 -1
  213. package/dist/utils/createInstance.js +0 -7
  214. package/dist/utils/decoratorTypeOf.d.ts +0 -11
  215. package/dist/utils/decoratorTypeOf.js +0 -32
  216. package/dist/utils/deepClone.d.ts +0 -6
  217. package/dist/utils/deepClone.js +0 -63
  218. package/dist/utils/deepMerge.d.ts +0 -9
  219. package/dist/utils/deepMerge.js +0 -62
  220. package/dist/utils/descriptorOf.d.ts +0 -8
  221. package/dist/utils/descriptorOf.js +0 -16
  222. package/dist/utils/formatValidationErrors.d.ts +0 -5
  223. package/dist/utils/formatValidationErrors.js +0 -80
  224. package/dist/utils/getClassOrSymbol.d.ts +0 -1
  225. package/dist/utils/getClassOrSymbol.js +0 -8
  226. package/dist/utils/getConstructorArgNames.d.ts +0 -1
  227. package/dist/utils/getConstructorArgNames.js +0 -12
  228. package/dist/utils/getMethodArgTypes.d.ts +0 -1
  229. package/dist/utils/getMethodArgTypes.js +0 -9
  230. package/dist/utils/getValue.d.ts +0 -32
  231. package/dist/utils/getValue.js +0 -47
  232. package/dist/utils/hasJsonMethod.d.ts +0 -1
  233. package/dist/utils/hasJsonMethod.js +0 -6
  234. package/dist/utils/index.d.ts +0 -15
  235. package/dist/utils/index.js +0 -31
  236. package/dist/utils/isArray.d.ts +0 -13
  237. package/dist/utils/isArray.js +0 -21
  238. package/dist/utils/isArrowFn.d.ts +0 -1
  239. package/dist/utils/isArrowFn.js +0 -7
  240. package/dist/utils/isBoolean.d.ts +0 -7
  241. package/dist/utils/isBoolean.js +0 -15
  242. package/dist/utils/isBuffer.d.ts +0 -7
  243. package/dist/utils/isBuffer.js +0 -19
  244. package/dist/utils/isClass.d.ts +0 -1
  245. package/dist/utils/isClass.js +0 -26
  246. package/dist/utils/isCollection.d.ts +0 -6
  247. package/dist/utils/isCollection.js +0 -20
  248. package/dist/utils/isDate.d.ts +0 -6
  249. package/dist/utils/isDate.js +0 -11
  250. package/dist/utils/isEmpty.d.ts +0 -6
  251. package/dist/utils/isEmpty.js +0 -12
  252. package/dist/utils/isFunction.d.ts +0 -1
  253. package/dist/utils/isFunction.js +0 -6
  254. package/dist/utils/isInheritedFrom.d.ts +0 -1
  255. package/dist/utils/isInheritedFrom.js +0 -24
  256. package/dist/utils/isMomentObject.d.ts +0 -1
  257. package/dist/utils/isMomentObject.js +0 -6
  258. package/dist/utils/isMongooseObject.d.ts +0 -2
  259. package/dist/utils/isMongooseObject.js +0 -11
  260. package/dist/utils/isNil.d.ts +0 -1
  261. package/dist/utils/isNil.js +0 -6
  262. package/dist/utils/isNumber.d.ts +0 -7
  263. package/dist/utils/isNumber.js +0 -15
  264. package/dist/utils/isObject.d.ts +0 -1
  265. package/dist/utils/isObject.js +0 -6
  266. package/dist/utils/isObservable.d.ts +0 -1
  267. package/dist/utils/isObservable.js +0 -6
  268. package/dist/utils/isPlainObject.d.ts +0 -7
  269. package/dist/utils/isPlainObject.js +0 -16
  270. package/dist/utils/isPrimitive.d.ts +0 -14
  271. package/dist/utils/isPrimitive.js +0 -28
  272. package/dist/utils/isPrimitiveType.d.ts +0 -1
  273. package/dist/utils/isPrimitiveType.js +0 -11
  274. package/dist/utils/isPromise.d.ts +0 -7
  275. package/dist/utils/isPromise.js +0 -14
  276. package/dist/utils/isProtectedKey.d.ts +0 -5
  277. package/dist/utils/isProtectedKey.js +0 -10
  278. package/dist/utils/isRegExp.d.ts +0 -1
  279. package/dist/utils/isRegExp.js +0 -6
  280. package/dist/utils/isRequestScope.d.ts +0 -11
  281. package/dist/utils/isRequestScope.js +0 -23
  282. package/dist/utils/isSerializable.d.ts +0 -1
  283. package/dist/utils/isSerializable.js +0 -11
  284. package/dist/utils/isStream.d.ts +0 -1
  285. package/dist/utils/isStream.js +0 -6
  286. package/dist/utils/isString.d.ts +0 -6
  287. package/dist/utils/isString.js +0 -14
  288. package/dist/utils/isSymbol.d.ts +0 -6
  289. package/dist/utils/isSymbol.js +0 -14
  290. package/dist/utils/methodsOf.d.ts +0 -9
  291. package/dist/utils/methodsOf.js +0 -24
  292. package/dist/utils/nameOf.d.ts +0 -14
  293. package/dist/utils/nameOf.js +0 -31
  294. package/dist/utils/objectKeys.d.ts +0 -1
  295. package/dist/utils/objectKeys.js +0 -7
  296. package/dist/utils/primitiveOf.d.ts +0 -1
  297. package/dist/utils/primitiveOf.js +0 -18
  298. package/dist/utils/prototypeOf.d.ts +0 -6
  299. package/dist/utils/prototypeOf.js +0 -12
  300. package/dist/utils/setValue.d.ts +0 -1
  301. package/dist/utils/setValue.js +0 -32
  302. package/dist/utils/toMap.d.ts +0 -3
  303. package/dist/utils/toMap.js +0 -34
  304. package/dist/utils/toStringConstructor.d.ts +0 -1
  305. package/dist/utils/toStringConstructor.js +0 -10
  306. package/dist/validation/ValidatorAdapter.d.ts +0 -66
  307. package/dist/validation/adapters/ClassValidatorAdapter.d.ts +0 -23
  308. package/dist/validation/adapters/ClassValidatorAdapter.js +0 -47
  309. package/dist/validation/adapters/ZodAdapter.d.ts +0 -14
  310. package/dist/validation/adapters/ZodAdapter.js +0 -56
  311. package/dist/validation/adapters/index.d.ts +0 -4
  312. package/dist/validation/adapters/index.js +0 -7
  313. package/dist/validation/index.d.ts +0 -3
  314. package/dist/validation/index.js +0 -20
package/src/index.ts ADDED
@@ -0,0 +1,92 @@
1
+ /**
2
+ * Carno - Ultra-Fast HTTP Framework
3
+ *
4
+ * Design principles:
5
+ * 1. ZERO abstraction at runtime - everything compiled at startup
6
+ * 2. Direct Bun.serve() with native routes
7
+ * 3. JIT compiled handlers with AOT async detection
8
+ * 4. No intermediate layers in hot path
9
+ * 5. Radix tree router for dynamic routes
10
+ */
11
+
12
+ // Application
13
+ export { Carno } from './Carno';
14
+ export type { MiddlewareHandler, CarnoConfig } from './Carno';
15
+
16
+ // Context
17
+ export { Context } from './context/Context';
18
+
19
+ // Decorators - Controller
20
+ export { Controller } from './decorators/Controller';
21
+ export type { ControllerOptions } from './metadata';
22
+
23
+ // Decorators - HTTP Methods
24
+ export { Get, Post, Put, Delete, Patch, Head, Options } from './decorators/methods';
25
+
26
+ // Decorators - Parameters
27
+ export { Param, Query, Body, Header, Req, Ctx, Locals } from './decorators/params';
28
+
29
+ // Decorators - Middleware
30
+ export { Use, Use as Middleware } from './decorators/Middleware';
31
+
32
+ // Middleware Interface
33
+ export type { CarnoMiddleware, CarnoClosure } from './middleware/CarnoMiddleware';
34
+
35
+ // Decorators - DI
36
+ export { Service } from './decorators/Service';
37
+ export { Inject } from './decorators/Inject';
38
+
39
+ // Container
40
+ export { Container, Scope } from './container/Container';
41
+ export type { Token, ProviderConfig } from './container/Container';
42
+
43
+ // Router
44
+ export { RadixRouter } from './router/RadixRouter';
45
+ export type { RouteMatch } from './router/RadixRouter';
46
+
47
+ // CORS
48
+ export { CorsHandler } from './cors/CorsHandler';
49
+ export type { CorsConfig, CorsOrigin } from './cors/CorsHandler';
50
+
51
+ // Validation
52
+ export type { ValidatorAdapter, ValidationResult, ValidationError, ValidationConfig } from './validation/ValidatorAdapter';
53
+ export { Schema, getSchema, VALIDATION_SCHEMA } from './validation/ValidatorAdapter';
54
+ export { ZodAdapter, ValidationException } from './validation/ZodAdapter';
55
+ export { ValibotAdapter } from './validation/ValibotAdapter';
56
+
57
+ // Exceptions
58
+ export {
59
+ HttpException,
60
+ BadRequestException,
61
+ UnauthorizedException,
62
+ ForbiddenException,
63
+ NotFoundException,
64
+ MethodNotAllowedException,
65
+ ConflictException,
66
+ UnprocessableEntityException,
67
+ TooManyRequestsException,
68
+ InternalServerErrorException,
69
+ ServiceUnavailableException
70
+ } from './exceptions/HttpException';
71
+
72
+ // Lifecycle Events
73
+ export {
74
+ EventType,
75
+ OnApplicationInit,
76
+ OnApplicationBoot,
77
+ OnApplicationShutdown
78
+ } from './events/Lifecycle';
79
+
80
+ // Cache
81
+ export { CacheService } from './cache/CacheService';
82
+ export { MemoryDriver } from './cache/MemoryDriver';
83
+ export { RedisDriver } from './cache/RedisDriver';
84
+ export type { RedisConfig } from './cache/RedisDriver';
85
+ export type { CacheDriver, CacheConfig } from './cache/CacheDriver';
86
+
87
+ // Testing
88
+ export { createTestHarness, withTestApp } from './testing/TestHarness';
89
+ export type { TestHarness, TestOptions } from './testing/TestHarness';
90
+
91
+ // Utils
92
+ export { Metadata, isObject, isString } from './utils/Metadata';
@@ -0,0 +1,46 @@
1
+ import { Scope } from './container/Container';
2
+
3
+ /**
4
+ * Controller options for the @Controller decorator.
5
+ */
6
+ export interface ControllerOptions {
7
+ path?: string;
8
+ scope?: Scope;
9
+ children?: any[];
10
+ }
11
+
12
+ /**
13
+ * Controller metadata stored on controller classes.
14
+ */
15
+ export interface ControllerMeta {
16
+ path: string;
17
+ scope?: Scope;
18
+ children?: any[];
19
+ }
20
+
21
+ /**
22
+ * Route metadata stored on controllers.
23
+ */
24
+ export interface RouteInfo {
25
+ method: 'get' | 'post' | 'put' | 'delete' | 'patch' | 'head' | 'options';
26
+ path: string;
27
+ handlerName: string;
28
+ }
29
+
30
+ /**
31
+ * Middleware metadata.
32
+ */
33
+ export interface MiddlewareInfo {
34
+ handler: Function;
35
+ target?: string;
36
+ }
37
+
38
+ /**
39
+ * Metadata keys.
40
+ */
41
+ export const CONTROLLER_META = Symbol('turbo:controller');
42
+ export const ROUTES_META = Symbol('turbo:routes');
43
+ export const PARAMS_META = Symbol('turbo:params');
44
+ export const MIDDLEWARE_META = Symbol('turbo:middleware');
45
+ export const SERVICE_META = Symbol('turbo:service');
46
+ export const INJECT_META = Symbol('turbo:inject');
@@ -0,0 +1,14 @@
1
+ import type { Context } from '../context/Context';
2
+
3
+ /**
4
+ * Closure function to call the next middleware in the chain.
5
+ */
6
+ export type CarnoClosure = () => void | Promise<void>;
7
+
8
+ /**
9
+ * Interface for onion-style middleware.
10
+ * Middleware must call next() to continue the chain.
11
+ */
12
+ export interface CarnoMiddleware {
13
+ handle(ctx: Context, next: CarnoClosure): void | Promise<void>;
14
+ }
@@ -0,0 +1,225 @@
1
+ /**
2
+ * Ultra-fast Radix Router for Turbo.
3
+ *
4
+ * Optimizations:
5
+ * - Char code comparisons (no string allocations)
6
+ * - Frozen empty params object
7
+ * - O(1) method lookup via char code index
8
+ * - Minimal allocations in hot path
9
+ */
10
+
11
+ const EMPTY_PARAMS: Readonly<Record<string, string>> = Object.freeze({});
12
+
13
+ interface Node<T> {
14
+ part: string;
15
+ store: T | null;
16
+ children: Map<number, Node<T>> | null;
17
+ paramChild: ParamNode<T> | null;
18
+ wildcardStore: T | null;
19
+ }
20
+
21
+ interface ParamNode<T> {
22
+ name: string;
23
+ store: T | null;
24
+ child: Node<T> | null;
25
+ }
26
+
27
+ export interface RouteMatch<T> {
28
+ store: T;
29
+ params: Record<string, string>;
30
+ }
31
+
32
+ function createNode<T>(part: string): Node<T> {
33
+ return {
34
+ part,
35
+ store: null,
36
+ children: null,
37
+ paramChild: null,
38
+ wildcardStore: null
39
+ };
40
+ }
41
+
42
+ export class RadixRouter<T> {
43
+ private roots: Record<string, Node<T>> = {};
44
+
45
+ add(method: string, path: string, store: T): void {
46
+ if (path === '') path = '/';
47
+ if (path[0] !== '/') path = '/' + path;
48
+
49
+ const isWildcard = path.endsWith('*');
50
+
51
+ if (isWildcard) path = path.slice(0, -1);
52
+
53
+ let node = this.roots[method];
54
+
55
+ if (!node) {
56
+ node = this.roots[method] = createNode('/');
57
+ }
58
+
59
+ let i = 0;
60
+ const len = path.length;
61
+
62
+ while (i < len) {
63
+ const char = path.charCodeAt(i);
64
+
65
+ if (char === 58) {
66
+ const paramStart = i + 1;
67
+ let paramEnd = paramStart;
68
+
69
+ while (paramEnd < len && path.charCodeAt(paramEnd) !== 47) {
70
+ paramEnd++;
71
+ }
72
+
73
+ const paramName = path.slice(paramStart, paramEnd);
74
+
75
+ if (!node.paramChild) {
76
+ node.paramChild = { name: paramName, store: null, child: null };
77
+ }
78
+
79
+ if (paramEnd >= len) {
80
+ node.paramChild.store = store;
81
+ return;
82
+ }
83
+
84
+ if (!node.paramChild.child) {
85
+ node.paramChild.child = createNode(path.slice(paramEnd));
86
+ }
87
+
88
+ node = node.paramChild.child;
89
+ i = paramEnd;
90
+ continue;
91
+ }
92
+
93
+ let segmentEnd = i;
94
+
95
+ while (segmentEnd < len && path.charCodeAt(segmentEnd) !== 47 && path.charCodeAt(segmentEnd) !== 58) {
96
+ segmentEnd++;
97
+ }
98
+
99
+ if (segmentEnd < len && path.charCodeAt(segmentEnd) === 47) {
100
+ segmentEnd++;
101
+ }
102
+
103
+ const segment = path.slice(i, segmentEnd);
104
+
105
+ if (segment === node.part) {
106
+ i = segmentEnd;
107
+ continue;
108
+ }
109
+
110
+ if (!node.children) {
111
+ node.children = new Map();
112
+ }
113
+
114
+ const firstChar = segment.charCodeAt(0);
115
+ let child = node.children.get(firstChar);
116
+
117
+ if (!child) {
118
+ child = createNode(segment);
119
+ node.children.set(firstChar, child);
120
+ }
121
+
122
+ node = child;
123
+ i = segmentEnd;
124
+ }
125
+
126
+ if (isWildcard) {
127
+ node.wildcardStore = store;
128
+ } else {
129
+ node.store = store;
130
+ }
131
+ }
132
+
133
+ find(method: string, path: string): RouteMatch<T> | null {
134
+ const root = this.roots[method];
135
+
136
+ if (!root) return null;
137
+
138
+ return this.matchPath(root, path, 0, path.length);
139
+ }
140
+
141
+ private matchPath(
142
+ node: Node<T>,
143
+ path: string,
144
+ start: number,
145
+ len: number
146
+ ): RouteMatch<T> | null {
147
+ const partLen = node.part.length;
148
+ const end = start + partLen;
149
+
150
+ if (partLen > 1) {
151
+ if (end > len) return null;
152
+
153
+ for (let i = 1, j = start + 1; i < partLen; i++, j++) {
154
+ if (node.part.charCodeAt(i) !== path.charCodeAt(j)) {
155
+ return null;
156
+ }
157
+ }
158
+ }
159
+
160
+ if (end === len) {
161
+ if (node.store !== null) {
162
+ return { store: node.store, params: EMPTY_PARAMS };
163
+ }
164
+
165
+ if (node.wildcardStore !== null) {
166
+ return { store: node.wildcardStore, params: { '*': '' } };
167
+ }
168
+
169
+ return null;
170
+ }
171
+
172
+ if (node.children) {
173
+ const child = node.children.get(path.charCodeAt(end));
174
+
175
+ if (child) {
176
+ const result = this.matchPath(child, path, end, len);
177
+
178
+ if (result) return result;
179
+ }
180
+ }
181
+
182
+ if (node.paramChild) {
183
+ const param = node.paramChild;
184
+ let paramEnd = end;
185
+
186
+ while (paramEnd < len && path.charCodeAt(paramEnd) !== 47) {
187
+ paramEnd++;
188
+ }
189
+
190
+ if (paramEnd === end) return null;
191
+
192
+ const paramValue = path.slice(end, paramEnd);
193
+
194
+ if (paramEnd >= len) {
195
+ if (param.store !== null) {
196
+ return {
197
+ store: param.store,
198
+ params: { [param.name]: paramValue }
199
+ };
200
+ }
201
+ } else if (param.child) {
202
+ const result = this.matchPath(param.child, path, paramEnd, len);
203
+
204
+ if (result) {
205
+ if (result.params === EMPTY_PARAMS) {
206
+ result.params = { [param.name]: paramValue };
207
+ } else {
208
+ result.params[param.name] = paramValue;
209
+ }
210
+
211
+ return result;
212
+ }
213
+ }
214
+ }
215
+
216
+ if (node.wildcardStore !== null) {
217
+ return {
218
+ store: node.wildcardStore,
219
+ params: { '*': path.slice(end) }
220
+ };
221
+ }
222
+
223
+ return null;
224
+ }
225
+ }
@@ -0,0 +1,177 @@
1
+ import type { Server } from 'bun';
2
+ import { Carno, type CarnoConfig } from '../Carno';
3
+ import { Container, type Token } from '../container/Container';
4
+
5
+ /**
6
+ * Test configuration options.
7
+ */
8
+ export interface TestOptions {
9
+ config?: CarnoConfig;
10
+ listen?: boolean | number;
11
+ port?: number;
12
+ controllers?: (new (...args: any[]) => any)[];
13
+ services?: (Token | any)[];
14
+ }
15
+
16
+ /**
17
+ * Test harness - provides utilities for testing Carno applications.
18
+ */
19
+ export interface TestHarness {
20
+ /** The Carno app instance */
21
+ app: Carno;
22
+
23
+ /** The internal DI container */
24
+ container: Container;
25
+
26
+ /** The HTTP server (if listening) */
27
+ server?: Server<any>;
28
+
29
+ /** The port the server is running on */
30
+ port?: number;
31
+
32
+ /** Resolve a service from the container */
33
+ resolve<T>(token: Token<T>): T;
34
+
35
+ /** Make an HTTP request to the app */
36
+ request(path: string, init?: RequestInit): Promise<Response>;
37
+
38
+ /** Make a GET request */
39
+ get(path: string, init?: Omit<RequestInit, 'method'>): Promise<Response>;
40
+
41
+ /** Make a POST request */
42
+ post(path: string, body?: any, init?: Omit<RequestInit, 'method' | 'body'>): Promise<Response>;
43
+
44
+ /** Make a PUT request */
45
+ put(path: string, body?: any, init?: Omit<RequestInit, 'method' | 'body'>): Promise<Response>;
46
+
47
+ /** Make a DELETE request */
48
+ delete(path: string, init?: Omit<RequestInit, 'method'>): Promise<Response>;
49
+
50
+ /** Close the test harness and cleanup */
51
+ close(): Promise<void>;
52
+ }
53
+
54
+ /**
55
+ * Create a test harness for Turbo applications.
56
+ *
57
+ * @example
58
+ * ```typescript
59
+ * const harness = await createTestHarness({
60
+ * controllers: [UserController],
61
+ * services: [UserService],
62
+ * listen: true
63
+ * });
64
+ *
65
+ * const response = await harness.get('/users');
66
+ * expect(response.status).toBe(200);
67
+ *
68
+ * await harness.close();
69
+ * ```
70
+ */
71
+ export async function createTestHarness(options: TestOptions = {}): Promise<TestHarness> {
72
+ const config: CarnoConfig = {
73
+ ...options.config,
74
+ disableStartupLog: true
75
+ };
76
+
77
+ const app = new Carno(config);
78
+
79
+ // Register controllers
80
+ if (options.controllers) {
81
+ app.controllers(options.controllers);
82
+ }
83
+
84
+ // Register services
85
+ if (options.services) {
86
+ app.services(options.services);
87
+ }
88
+
89
+ const port = resolvePort(options);
90
+ let server: Server<any> | undefined;
91
+
92
+ if (shouldListen(options.listen)) {
93
+ app.listen(port);
94
+ server = (app as any).server;
95
+ }
96
+
97
+ const actualPort = server?.port ?? port;
98
+ const container = (app as any).container as Container;
99
+
100
+ // Pre-bind methods for performance
101
+ const baseUrl = `http://127.0.0.1:${actualPort}`;
102
+
103
+ const request = async (path: string, init?: RequestInit): Promise<Response> => {
104
+ if (!server) {
105
+ throw new Error('Server not running. Set listen: true in options.');
106
+ }
107
+ const url = path.startsWith('http') ? path : `${baseUrl}${path.startsWith('/') ? path : '/' + path}`;
108
+ return fetch(url, init);
109
+ };
110
+
111
+ return {
112
+ app,
113
+ container,
114
+ server,
115
+ port: actualPort,
116
+
117
+ resolve: <T>(token: Token<T>): T => container.get(token),
118
+
119
+ request,
120
+
121
+ get: (path, init) => request(path, { ...init, method: 'GET' }),
122
+
123
+ post: (path, body, init) => request(path, {
124
+ ...init,
125
+ method: 'POST',
126
+ body: body ? JSON.stringify(body) : undefined,
127
+ headers: { 'Content-Type': 'application/json', ...init?.headers }
128
+ }),
129
+
130
+ put: (path, body, init) => request(path, {
131
+ ...init,
132
+ method: 'PUT',
133
+ body: body ? JSON.stringify(body) : undefined,
134
+ headers: { 'Content-Type': 'application/json', ...init?.headers }
135
+ }),
136
+
137
+ delete: (path, init) => request(path, { ...init, method: 'DELETE' }),
138
+
139
+ close: async () => {
140
+ app.stop();
141
+ }
142
+ };
143
+ }
144
+
145
+ /**
146
+ * Run a test routine with automatic harness cleanup.
147
+ *
148
+ * @example
149
+ * ```typescript
150
+ * await withTestApp(async (harness) => {
151
+ * const response = await harness.get('/health');
152
+ * expect(response.status).toBe(200);
153
+ * }, { controllers: [HealthController], listen: true });
154
+ * ```
155
+ */
156
+ export async function withTestApp(
157
+ routine: (harness: TestHarness) => Promise<void>,
158
+ options: TestOptions = {}
159
+ ): Promise<void> {
160
+ const harness = await createTestHarness(options);
161
+
162
+ try {
163
+ await routine(harness);
164
+ } finally {
165
+ await harness.close();
166
+ }
167
+ }
168
+
169
+ function shouldListen(value: TestOptions['listen']): boolean {
170
+ return typeof value === 'number' || Boolean(value);
171
+ }
172
+
173
+ function resolvePort(options: TestOptions): number {
174
+ if (typeof options.listen === 'number') return options.listen;
175
+ if (typeof options.port === 'number') return options.port;
176
+ return 0; // Random port
177
+ }
@@ -0,0 +1,43 @@
1
+ /**
2
+ * Utility class for handling metadata operations.
3
+ * Wraps Reflect.getMetadata and Reflect.defineMetadata.
4
+ */
5
+ export class Metadata {
6
+ static get<T = any>(key: string | symbol, target: any): T | undefined {
7
+ return Reflect.getMetadata(key, target);
8
+ }
9
+
10
+ static set(key: string | symbol, value: any, target: any): void {
11
+ Reflect.defineMetadata(key, value, target);
12
+ }
13
+
14
+ static has(key: string | symbol, target: any): boolean {
15
+ return Reflect.hasMetadata(key, target);
16
+ }
17
+
18
+ static delete(key: string | symbol, target: any): boolean {
19
+ return Reflect.deleteMetadata(key, target);
20
+ }
21
+
22
+ static keys(target: any): (string | symbol)[] {
23
+ return Reflect.getMetadataKeys(target);
24
+ }
25
+
26
+ static getType(target: any, propertyKey: string | symbol): any {
27
+ return Reflect.getMetadata('design:type', target, propertyKey);
28
+ }
29
+ }
30
+
31
+ /**
32
+ * Type guard for checking if value is an object.
33
+ */
34
+ export function isObject(value: unknown): value is Record<string, any> {
35
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
36
+ }
37
+
38
+ /**
39
+ * Type guard for checking if value is a string.
40
+ */
41
+ export function isString(value: unknown): value is string {
42
+ return typeof value === 'string';
43
+ }
@@ -0,0 +1,95 @@
1
+ import type { ValidatorAdapter, ValidationResult, ValidationError } from './ValidatorAdapter';
2
+ import { VALIDATION_SCHEMA, getSchema } from './ValidatorAdapter';
3
+ import { ValidationException } from './ZodAdapter';
4
+
5
+ /**
6
+ * Valibot Adapter for Turbo validation.
7
+ *
8
+ * Usage:
9
+ * ```typescript
10
+ * import * as v from 'valibot';
11
+ *
12
+ * @Schema(v.object({
13
+ * name: v.pipe(v.string(), v.minLength(1)),
14
+ * email: v.pipe(v.string(), v.email())
15
+ * }))
16
+ * class CreateUserDto {
17
+ * name: string;
18
+ * email: string;
19
+ * }
20
+ * ```
21
+ */
22
+ export class ValibotAdapter implements ValidatorAdapter {
23
+ readonly name = 'ValibotAdapter';
24
+
25
+ private schemaCache = new Map<any, any>();
26
+ private valibot: any = null;
27
+
28
+ constructor() {
29
+ // Lazy load valibot
30
+ try {
31
+ this.valibot = require('valibot');
32
+ } catch {
33
+ // Will be loaded on first use
34
+ }
35
+ }
36
+
37
+ private ensureValibot(): any {
38
+ if (!this.valibot) {
39
+ this.valibot = require('valibot');
40
+ }
41
+ return this.valibot;
42
+ }
43
+
44
+ hasValidation(target: any): boolean {
45
+ return getSchema(target) !== undefined;
46
+ }
47
+
48
+ validate<T>(target: any, value: unknown): ValidationResult<T> {
49
+ const schema = this.getOrCacheSchema(target);
50
+
51
+ if (!schema) {
52
+ return { success: true, data: value as T };
53
+ }
54
+
55
+ const v = this.ensureValibot();
56
+ const result = v.safeParse(schema, value);
57
+
58
+ if (result.success) {
59
+ return { success: true, data: result.output };
60
+ }
61
+
62
+ return {
63
+ success: false,
64
+ errors: this.formatErrors(result.issues)
65
+ };
66
+ }
67
+
68
+ validateOrThrow<T>(target: any, value: unknown): T {
69
+ const result = this.validate<T>(target, value);
70
+
71
+ if (result.success) {
72
+ return result.data!;
73
+ }
74
+
75
+ throw new ValidationException(result.errors!);
76
+ }
77
+
78
+ private getOrCacheSchema(target: any): any {
79
+ let schema = this.schemaCache.get(target);
80
+
81
+ if (schema === undefined) {
82
+ schema = getSchema(target) ?? null;
83
+ this.schemaCache.set(target, schema);
84
+ }
85
+
86
+ return schema;
87
+ }
88
+
89
+ private formatErrors(issues: any[]): ValidationError[] {
90
+ return issues.map((issue: any) => ({
91
+ path: issue.path?.map((p: any) => p.key).join('.') || '',
92
+ message: issue.message
93
+ }));
94
+ }
95
+ }