@c9up/ream 0.1.3

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 (451) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +70 -0
  3. package/dist/Application.d.ts +71 -0
  4. package/dist/Application.d.ts.map +1 -0
  5. package/dist/Application.js +151 -0
  6. package/dist/Application.js.map +1 -0
  7. package/dist/Codemods.d.ts +16 -0
  8. package/dist/Codemods.d.ts.map +1 -0
  9. package/dist/Codemods.js +277 -0
  10. package/dist/Codemods.js.map +1 -0
  11. package/dist/ConfigLoader.d.ts +29 -0
  12. package/dist/ConfigLoader.d.ts.map +1 -0
  13. package/dist/ConfigLoader.js +43 -0
  14. package/dist/ConfigLoader.js.map +1 -0
  15. package/dist/ErrorBoundary.d.ts +51 -0
  16. package/dist/ErrorBoundary.d.ts.map +1 -0
  17. package/dist/ErrorBoundary.js +122 -0
  18. package/dist/ErrorBoundary.js.map +1 -0
  19. package/dist/GracefulShutdown.d.ts +28 -0
  20. package/dist/GracefulShutdown.d.ts.map +1 -0
  21. package/dist/GracefulShutdown.js +55 -0
  22. package/dist/GracefulShutdown.js.map +1 -0
  23. package/dist/HealthCheck.d.ts +42 -0
  24. package/dist/HealthCheck.d.ts.map +1 -0
  25. package/dist/HealthCheck.js +65 -0
  26. package/dist/HealthCheck.js.map +1 -0
  27. package/dist/HotReload.d.ts +28 -0
  28. package/dist/HotReload.d.ts.map +1 -0
  29. package/dist/HotReload.js +60 -0
  30. package/dist/HotReload.js.map +1 -0
  31. package/dist/HttpKernel.d.ts +62 -0
  32. package/dist/HttpKernel.d.ts.map +1 -0
  33. package/dist/HttpKernel.js +210 -0
  34. package/dist/HttpKernel.js.map +1 -0
  35. package/dist/Ignitor.d.ts +195 -0
  36. package/dist/Ignitor.d.ts.map +1 -0
  37. package/dist/Ignitor.js +505 -0
  38. package/dist/Ignitor.js.map +1 -0
  39. package/dist/Provider.d.ts +54 -0
  40. package/dist/Provider.d.ts.map +1 -0
  41. package/dist/Provider.js +37 -0
  42. package/dist/Provider.js.map +1 -0
  43. package/dist/bodyparser/BodyParserMiddleware.d.ts +38 -0
  44. package/dist/bodyparser/BodyParserMiddleware.d.ts.map +1 -0
  45. package/dist/bodyparser/BodyParserMiddleware.js +143 -0
  46. package/dist/bodyparser/BodyParserMiddleware.js.map +1 -0
  47. package/dist/bodyparser/MultipartFile.d.ts +77 -0
  48. package/dist/bodyparser/MultipartFile.d.ts.map +1 -0
  49. package/dist/bodyparser/MultipartFile.js +138 -0
  50. package/dist/bodyparser/MultipartFile.js.map +1 -0
  51. package/dist/bodyparser/parseSize.d.ts +6 -0
  52. package/dist/bodyparser/parseSize.d.ts.map +1 -0
  53. package/dist/bodyparser/parseSize.js +21 -0
  54. package/dist/bodyparser/parseSize.js.map +1 -0
  55. package/dist/bootstrap/hyper-server.d.ts +27 -0
  56. package/dist/bootstrap/hyper-server.d.ts.map +1 -0
  57. package/dist/bootstrap/hyper-server.js +63 -0
  58. package/dist/bootstrap/hyper-server.js.map +1 -0
  59. package/dist/console/CommandRunner.d.ts +18 -0
  60. package/dist/console/CommandRunner.d.ts.map +1 -0
  61. package/dist/console/CommandRunner.js +65 -0
  62. package/dist/console/CommandRunner.js.map +1 -0
  63. package/dist/container/Container.d.ts +60 -0
  64. package/dist/container/Container.d.ts.map +1 -0
  65. package/dist/container/Container.js +329 -0
  66. package/dist/container/Container.js.map +1 -0
  67. package/dist/container/types.d.ts +25 -0
  68. package/dist/container/types.d.ts.map +1 -0
  69. package/dist/container/types.js +6 -0
  70. package/dist/container/types.js.map +1 -0
  71. package/dist/decorators/Lazy.d.ts +19 -0
  72. package/dist/decorators/Lazy.d.ts.map +1 -0
  73. package/dist/decorators/Lazy.js +53 -0
  74. package/dist/decorators/Lazy.js.map +1 -0
  75. package/dist/decorators/Service.d.ts +48 -0
  76. package/dist/decorators/Service.d.ts.map +1 -0
  77. package/dist/decorators/Service.js +70 -0
  78. package/dist/decorators/Service.js.map +1 -0
  79. package/dist/errors/FuzzyMatcher.d.ts +41 -0
  80. package/dist/errors/FuzzyMatcher.d.ts.map +1 -0
  81. package/dist/errors/FuzzyMatcher.js +81 -0
  82. package/dist/errors/FuzzyMatcher.js.map +1 -0
  83. package/dist/errors/ModuleErrors.d.ts +41 -0
  84. package/dist/errors/ModuleErrors.d.ts.map +1 -0
  85. package/dist/errors/ModuleErrors.js +65 -0
  86. package/dist/errors/ModuleErrors.js.map +1 -0
  87. package/dist/errors/PipelineStageError.d.ts +31 -0
  88. package/dist/errors/PipelineStageError.d.ts.map +1 -0
  89. package/dist/errors/PipelineStageError.js +77 -0
  90. package/dist/errors/PipelineStageError.js.map +1 -0
  91. package/dist/errors/ReamError.d.ts +50 -0
  92. package/dist/errors/ReamError.d.ts.map +1 -0
  93. package/dist/errors/ReamError.js +108 -0
  94. package/dist/errors/ReamError.js.map +1 -0
  95. package/dist/events/Emitter.d.ts +124 -0
  96. package/dist/events/Emitter.d.ts.map +1 -0
  97. package/dist/events/Emitter.js +271 -0
  98. package/dist/events/Emitter.js.map +1 -0
  99. package/dist/events/EventsProvider.d.ts +28 -0
  100. package/dist/events/EventsProvider.d.ts.map +1 -0
  101. package/dist/events/EventsProvider.js +36 -0
  102. package/dist/events/EventsProvider.js.map +1 -0
  103. package/dist/events/config.d.ts +8 -0
  104. package/dist/events/config.d.ts.map +1 -0
  105. package/dist/events/config.js +4 -0
  106. package/dist/events/config.js.map +1 -0
  107. package/dist/events/configure.d.ts +10 -0
  108. package/dist/events/configure.d.ts.map +1 -0
  109. package/dist/events/configure.js +11 -0
  110. package/dist/events/configure.js.map +1 -0
  111. package/dist/events/helix/helpers.d.ts +62 -0
  112. package/dist/events/helix/helpers.d.ts.map +1 -0
  113. package/dist/events/helix/helpers.js +187 -0
  114. package/dist/events/helix/helpers.js.map +1 -0
  115. package/dist/events/index.d.ts +17 -0
  116. package/dist/events/index.d.ts.map +1 -0
  117. package/dist/events/index.js +15 -0
  118. package/dist/events/index.js.map +1 -0
  119. package/dist/events/native.d.ts +21 -0
  120. package/dist/events/native.d.ts.map +1 -0
  121. package/dist/events/native.js +17 -0
  122. package/dist/events/native.js.map +1 -0
  123. package/dist/events/services/main.d.ts +18 -0
  124. package/dist/events/services/main.d.ts.map +1 -0
  125. package/dist/events/services/main.js +31 -0
  126. package/dist/events/services/main.js.map +1 -0
  127. package/dist/events/testing/FakeBus.d.ts +66 -0
  128. package/dist/events/testing/FakeBus.d.ts.map +1 -0
  129. package/dist/events/testing/FakeBus.js +356 -0
  130. package/dist/events/testing/FakeBus.js.map +1 -0
  131. package/dist/graphql/GraphQLEngine.d.ts +69 -0
  132. package/dist/graphql/GraphQLEngine.d.ts.map +1 -0
  133. package/dist/graphql/GraphQLEngine.js +309 -0
  134. package/dist/graphql/GraphQLEngine.js.map +1 -0
  135. package/dist/graphql/GraphQLProvider.d.ts +32 -0
  136. package/dist/graphql/GraphQLProvider.d.ts.map +1 -0
  137. package/dist/graphql/GraphQLProvider.js +49 -0
  138. package/dist/graphql/GraphQLProvider.js.map +1 -0
  139. package/dist/helpers/base64.d.ts +40 -0
  140. package/dist/helpers/base64.d.ts.map +1 -0
  141. package/dist/helpers/base64.js +70 -0
  142. package/dist/helpers/base64.js.map +1 -0
  143. package/dist/helpers/index.d.ts +7 -0
  144. package/dist/helpers/index.d.ts.map +1 -0
  145. package/dist/helpers/index.js +5 -0
  146. package/dist/helpers/index.js.map +1 -0
  147. package/dist/helpers/is.d.ts +101 -0
  148. package/dist/helpers/is.d.ts.map +1 -0
  149. package/dist/helpers/is.js +151 -0
  150. package/dist/helpers/is.js.map +1 -0
  151. package/dist/helpers/napi-loader.d.ts +46 -0
  152. package/dist/helpers/napi-loader.d.ts.map +1 -0
  153. package/dist/helpers/napi-loader.js +69 -0
  154. package/dist/helpers/napi-loader.js.map +1 -0
  155. package/dist/helpers/safeEqual.d.ts +9 -0
  156. package/dist/helpers/safeEqual.d.ts.map +1 -0
  157. package/dist/helpers/safeEqual.js +16 -0
  158. package/dist/helpers/safeEqual.js.map +1 -0
  159. package/dist/helpers/string.d.ts +161 -0
  160. package/dist/helpers/string.d.ts.map +1 -0
  161. package/dist/helpers/string.js +429 -0
  162. package/dist/helpers/string.js.map +1 -0
  163. package/dist/helpers/types.d.ts +83 -0
  164. package/dist/helpers/types.d.ts.map +1 -0
  165. package/dist/helpers/types.js +6 -0
  166. package/dist/helpers/types.js.map +1 -0
  167. package/dist/http/Exception.d.ts +103 -0
  168. package/dist/http/Exception.d.ts.map +1 -0
  169. package/dist/http/Exception.js +228 -0
  170. package/dist/http/Exception.js.map +1 -0
  171. package/dist/http/HttpContext.d.ts +69 -0
  172. package/dist/http/HttpContext.d.ts.map +1 -0
  173. package/dist/http/HttpContext.js +55 -0
  174. package/dist/http/HttpContext.js.map +1 -0
  175. package/dist/http/RedirectBuilder.d.ts +31 -0
  176. package/dist/http/RedirectBuilder.d.ts.map +1 -0
  177. package/dist/http/RedirectBuilder.js +75 -0
  178. package/dist/http/RedirectBuilder.js.map +1 -0
  179. package/dist/http/Request.d.ts +160 -0
  180. package/dist/http/Request.d.ts.map +1 -0
  181. package/dist/http/Request.js +484 -0
  182. package/dist/http/Request.js.map +1 -0
  183. package/dist/http/Response.d.ts +114 -0
  184. package/dist/http/Response.d.ts.map +1 -0
  185. package/dist/http/Response.js +303 -0
  186. package/dist/http/Response.js.map +1 -0
  187. package/dist/http/SseStream.d.ts +89 -0
  188. package/dist/http/SseStream.d.ts.map +1 -0
  189. package/dist/http/SseStream.js +228 -0
  190. package/dist/http/SseStream.js.map +1 -0
  191. package/dist/http/urlDecode.d.ts +8 -0
  192. package/dist/http/urlDecode.d.ts.map +1 -0
  193. package/dist/http/urlDecode.js +18 -0
  194. package/dist/http/urlDecode.js.map +1 -0
  195. package/dist/index.d.ts +92 -0
  196. package/dist/index.d.ts.map +1 -0
  197. package/dist/index.js +67 -0
  198. package/dist/index.js.map +1 -0
  199. package/dist/middleware/Pipeline.d.ts +36 -0
  200. package/dist/middleware/Pipeline.d.ts.map +1 -0
  201. package/dist/middleware/Pipeline.js +104 -0
  202. package/dist/middleware/Pipeline.js.map +1 -0
  203. package/dist/openapi/OpenApiGenerator.d.ts +58 -0
  204. package/dist/openapi/OpenApiGenerator.d.ts.map +1 -0
  205. package/dist/openapi/OpenApiGenerator.js +192 -0
  206. package/dist/openapi/OpenApiGenerator.js.map +1 -0
  207. package/dist/openapi/OpenApiMiddleware.d.ts +24 -0
  208. package/dist/openapi/OpenApiMiddleware.d.ts.map +1 -0
  209. package/dist/openapi/OpenApiMiddleware.js +78 -0
  210. package/dist/openapi/OpenApiMiddleware.js.map +1 -0
  211. package/dist/openapi/OpenApiProvider.d.ts +36 -0
  212. package/dist/openapi/OpenApiProvider.d.ts.map +1 -0
  213. package/dist/openapi/OpenApiProvider.js +42 -0
  214. package/dist/openapi/OpenApiProvider.js.map +1 -0
  215. package/dist/resources/ApiResource.d.ts +45 -0
  216. package/dist/resources/ApiResource.d.ts.map +1 -0
  217. package/dist/resources/ApiResource.js +58 -0
  218. package/dist/resources/ApiResource.js.map +1 -0
  219. package/dist/router/Router.d.ts +205 -0
  220. package/dist/router/Router.d.ts.map +1 -0
  221. package/dist/router/Router.js +552 -0
  222. package/dist/router/Router.js.map +1 -0
  223. package/dist/rpc/RpcProvider.d.ts +34 -0
  224. package/dist/rpc/RpcProvider.d.ts.map +1 -0
  225. package/dist/rpc/RpcProvider.js +37 -0
  226. package/dist/rpc/RpcProvider.js.map +1 -0
  227. package/dist/rpc/RpcRouter.d.ts +61 -0
  228. package/dist/rpc/RpcRouter.d.ts.map +1 -0
  229. package/dist/rpc/RpcRouter.js +187 -0
  230. package/dist/rpc/RpcRouter.js.map +1 -0
  231. package/dist/scheduler/Schedule.d.ts +53 -0
  232. package/dist/scheduler/Schedule.d.ts.map +1 -0
  233. package/dist/scheduler/Schedule.js +76 -0
  234. package/dist/scheduler/Schedule.js.map +1 -0
  235. package/dist/scheduler/ScheduleProvider.d.ts +56 -0
  236. package/dist/scheduler/ScheduleProvider.d.ts.map +1 -0
  237. package/dist/scheduler/ScheduleProvider.js +145 -0
  238. package/dist/scheduler/ScheduleProvider.js.map +1 -0
  239. package/dist/scheduler/Scheduler.d.ts +209 -0
  240. package/dist/scheduler/Scheduler.d.ts.map +1 -0
  241. package/dist/scheduler/Scheduler.js +432 -0
  242. package/dist/scheduler/Scheduler.js.map +1 -0
  243. package/dist/scheduler/index.d.ts +14 -0
  244. package/dist/scheduler/index.d.ts.map +1 -0
  245. package/dist/scheduler/index.js +6 -0
  246. package/dist/scheduler/index.js.map +1 -0
  247. package/dist/scheduler/locks/LockBackend.d.ts +37 -0
  248. package/dist/scheduler/locks/LockBackend.d.ts.map +1 -0
  249. package/dist/scheduler/locks/LockBackend.js +13 -0
  250. package/dist/scheduler/locks/LockBackend.js.map +1 -0
  251. package/dist/scheduler/locks/MemoryLockBackend.d.ts +39 -0
  252. package/dist/scheduler/locks/MemoryLockBackend.d.ts.map +1 -0
  253. package/dist/scheduler/locks/MemoryLockBackend.js +71 -0
  254. package/dist/scheduler/locks/MemoryLockBackend.js.map +1 -0
  255. package/dist/scheduler/observability/ErrorReporter.d.ts +15 -0
  256. package/dist/scheduler/observability/ErrorReporter.d.ts.map +1 -0
  257. package/dist/scheduler/observability/ErrorReporter.js +13 -0
  258. package/dist/scheduler/observability/ErrorReporter.js.map +1 -0
  259. package/dist/scheduler/observability/ScheduleEvent.d.ts +57 -0
  260. package/dist/scheduler/observability/ScheduleEvent.d.ts.map +1 -0
  261. package/dist/scheduler/observability/ScheduleEvent.js +12 -0
  262. package/dist/scheduler/observability/ScheduleEvent.js.map +1 -0
  263. package/dist/scheduler/observability/ScheduleEventSink.d.ts +20 -0
  264. package/dist/scheduler/observability/ScheduleEventSink.d.ts.map +1 -0
  265. package/dist/scheduler/observability/ScheduleEventSink.js +17 -0
  266. package/dist/scheduler/observability/ScheduleEventSink.js.map +1 -0
  267. package/dist/scheduler/observability/StatsTracker.d.ts +50 -0
  268. package/dist/scheduler/observability/StatsTracker.d.ts.map +1 -0
  269. package/dist/scheduler/observability/StatsTracker.js +96 -0
  270. package/dist/scheduler/observability/StatsTracker.js.map +1 -0
  271. package/dist/security/CookieSigner.d.ts +13 -0
  272. package/dist/security/CookieSigner.d.ts.map +1 -0
  273. package/dist/security/CookieSigner.js +51 -0
  274. package/dist/security/CookieSigner.js.map +1 -0
  275. package/dist/security/SignedUrl.d.ts +17 -0
  276. package/dist/security/SignedUrl.d.ts.map +1 -0
  277. package/dist/security/SignedUrl.js +63 -0
  278. package/dist/security/SignedUrl.js.map +1 -0
  279. package/dist/security/crypto.d.ts +25 -0
  280. package/dist/security/crypto.d.ts.map +1 -0
  281. package/dist/security/crypto.js +82 -0
  282. package/dist/security/crypto.js.map +1 -0
  283. package/dist/server/HyperServer.d.ts +64 -0
  284. package/dist/server/HyperServer.d.ts.map +1 -0
  285. package/dist/server/HyperServer.js +54 -0
  286. package/dist/server/HyperServer.js.map +1 -0
  287. package/dist/server/Server.d.ts +68 -0
  288. package/dist/server/Server.d.ts.map +1 -0
  289. package/dist/server/Server.js +113 -0
  290. package/dist/server/Server.js.map +1 -0
  291. package/dist/services/app.d.ts +14 -0
  292. package/dist/services/app.d.ts.map +1 -0
  293. package/dist/services/app.js +27 -0
  294. package/dist/services/app.js.map +1 -0
  295. package/dist/services/router.d.ts +21 -0
  296. package/dist/services/router.d.ts.map +1 -0
  297. package/dist/services/router.js +36 -0
  298. package/dist/services/router.js.map +1 -0
  299. package/dist/services/server.d.ts +16 -0
  300. package/dist/services/server.d.ts.map +1 -0
  301. package/dist/services/server.js +32 -0
  302. package/dist/services/server.js.map +1 -0
  303. package/dist/session/Session.d.ts +88 -0
  304. package/dist/session/Session.d.ts.map +1 -0
  305. package/dist/session/Session.js +186 -0
  306. package/dist/session/Session.js.map +1 -0
  307. package/dist/session/SessionMiddleware.d.ts +16 -0
  308. package/dist/session/SessionMiddleware.d.ts.map +1 -0
  309. package/dist/session/SessionMiddleware.js +132 -0
  310. package/dist/session/SessionMiddleware.js.map +1 -0
  311. package/dist/session/drivers/CookieDriver.d.ts +17 -0
  312. package/dist/session/drivers/CookieDriver.d.ts.map +1 -0
  313. package/dist/session/drivers/CookieDriver.js +42 -0
  314. package/dist/session/drivers/CookieDriver.js.map +1 -0
  315. package/dist/session/drivers/MemoryDriver.d.ts +9 -0
  316. package/dist/session/drivers/MemoryDriver.d.ts.map +1 -0
  317. package/dist/session/drivers/MemoryDriver.js +24 -0
  318. package/dist/session/drivers/MemoryDriver.js.map +1 -0
  319. package/dist/storage/S3Driver.d.ts +27 -0
  320. package/dist/storage/S3Driver.d.ts.map +1 -0
  321. package/dist/storage/S3Driver.js +135 -0
  322. package/dist/storage/S3Driver.js.map +1 -0
  323. package/dist/storage/StaticMiddleware.d.ts +27 -0
  324. package/dist/storage/StaticMiddleware.d.ts.map +1 -0
  325. package/dist/storage/StaticMiddleware.js +153 -0
  326. package/dist/storage/StaticMiddleware.js.map +1 -0
  327. package/dist/storage/StaticProvider.d.ts +26 -0
  328. package/dist/storage/StaticProvider.d.ts.map +1 -0
  329. package/dist/storage/StaticProvider.js +31 -0
  330. package/dist/storage/StaticProvider.js.map +1 -0
  331. package/dist/storage/StorageManager.d.ts +31 -0
  332. package/dist/storage/StorageManager.d.ts.map +1 -0
  333. package/dist/storage/StorageManager.js +120 -0
  334. package/dist/storage/StorageManager.js.map +1 -0
  335. package/dist/testing/RequestBuilder.d.ts +67 -0
  336. package/dist/testing/RequestBuilder.d.ts.map +1 -0
  337. package/dist/testing/RequestBuilder.js +202 -0
  338. package/dist/testing/RequestBuilder.js.map +1 -0
  339. package/dist/testing/TestClient.d.ts +96 -0
  340. package/dist/testing/TestClient.d.ts.map +1 -0
  341. package/dist/testing/TestClient.js +263 -0
  342. package/dist/testing/TestClient.js.map +1 -0
  343. package/dist/types/helpers.d.ts +47 -0
  344. package/dist/types/helpers.d.ts.map +1 -0
  345. package/dist/types/helpers.js +5 -0
  346. package/dist/types/helpers.js.map +1 -0
  347. package/events.darwin-arm64.node +0 -0
  348. package/events.darwin-x64.node +0 -0
  349. package/events.linux-arm64-gnu.node +0 -0
  350. package/events.linux-x64-gnu.node +0 -0
  351. package/events.win32-x64-msvc.node +0 -0
  352. package/index.darwin-arm64.node +0 -0
  353. package/index.darwin-x64.node +0 -0
  354. package/index.linux-arm64-gnu.node +0 -0
  355. package/index.linux-x64-gnu.node +0 -0
  356. package/index.win32-x64-msvc.node +0 -0
  357. package/package.json +106 -0
  358. package/scheduler.darwin-arm64.node +0 -0
  359. package/scheduler.darwin-x64.node +0 -0
  360. package/scheduler.linux-arm64-gnu.node +0 -0
  361. package/scheduler.linux-x64-gnu.node +0 -0
  362. package/scheduler.win32-x64-msvc.node +0 -0
  363. package/scripts/copy-napi.mjs +113 -0
  364. package/src/Application.ts +177 -0
  365. package/src/Codemods.ts +343 -0
  366. package/src/ConfigLoader.ts +49 -0
  367. package/src/ErrorBoundary.ts +157 -0
  368. package/src/GracefulShutdown.ts +80 -0
  369. package/src/HealthCheck.ts +96 -0
  370. package/src/HotReload.ts +78 -0
  371. package/src/HttpKernel.ts +305 -0
  372. package/src/Ignitor.ts +656 -0
  373. package/src/Provider.ts +74 -0
  374. package/src/bodyparser/BodyParserMiddleware.ts +183 -0
  375. package/src/bodyparser/MultipartFile.ts +181 -0
  376. package/src/bodyparser/parseSize.ts +19 -0
  377. package/src/bootstrap/hyper-server.ts +78 -0
  378. package/src/console/CommandRunner.ts +82 -0
  379. package/src/container/Container.ts +383 -0
  380. package/src/container/types.ts +30 -0
  381. package/src/decorators/Lazy.ts +57 -0
  382. package/src/decorators/Service.ts +85 -0
  383. package/src/errors/FuzzyMatcher.ts +93 -0
  384. package/src/errors/ModuleErrors.ts +73 -0
  385. package/src/errors/PipelineStageError.ts +97 -0
  386. package/src/errors/ReamError.ts +138 -0
  387. package/src/events/Emitter.ts +333 -0
  388. package/src/events/EventsProvider.ts +58 -0
  389. package/src/events/config.ts +10 -0
  390. package/src/events/configure.ts +19 -0
  391. package/src/events/helix/helpers.ts +260 -0
  392. package/src/events/index.ts +22 -0
  393. package/src/events/native.ts +37 -0
  394. package/src/events/services/main.ts +39 -0
  395. package/src/events/testing/FakeBus.ts +420 -0
  396. package/src/graphql/GraphQLEngine.ts +421 -0
  397. package/src/graphql/GraphQLProvider.ts +59 -0
  398. package/src/helpers/base64.ts +72 -0
  399. package/src/helpers/index.ts +6 -0
  400. package/src/helpers/is.ts +162 -0
  401. package/src/helpers/napi-loader.ts +106 -0
  402. package/src/helpers/safeEqual.ts +15 -0
  403. package/src/helpers/string.ts +469 -0
  404. package/src/helpers/types.ts +100 -0
  405. package/src/http/Exception.ts +272 -0
  406. package/src/http/HttpContext.ts +108 -0
  407. package/src/http/RedirectBuilder.ts +89 -0
  408. package/src/http/Request.ts +540 -0
  409. package/src/http/Response.ts +351 -0
  410. package/src/http/SseStream.ts +250 -0
  411. package/src/http/urlDecode.ts +18 -0
  412. package/src/index.ts +263 -0
  413. package/src/middleware/Pipeline.ts +136 -0
  414. package/src/openapi/OpenApiGenerator.ts +254 -0
  415. package/src/openapi/OpenApiMiddleware.ts +101 -0
  416. package/src/openapi/OpenApiProvider.ts +67 -0
  417. package/src/resources/ApiResource.ts +79 -0
  418. package/src/router/Router.ts +733 -0
  419. package/src/rpc/RpcProvider.ts +54 -0
  420. package/src/rpc/RpcRouter.ts +243 -0
  421. package/src/scheduler/Schedule.ts +96 -0
  422. package/src/scheduler/ScheduleProvider.ts +177 -0
  423. package/src/scheduler/Scheduler.ts +601 -0
  424. package/src/scheduler/index.ts +24 -0
  425. package/src/scheduler/locks/LockBackend.ts +38 -0
  426. package/src/scheduler/locks/MemoryLockBackend.ts +85 -0
  427. package/src/scheduler/observability/ErrorReporter.ts +13 -0
  428. package/src/scheduler/observability/ScheduleEvent.ts +61 -0
  429. package/src/scheduler/observability/ScheduleEventSink.ts +21 -0
  430. package/src/scheduler/observability/StatsTracker.ts +134 -0
  431. package/src/security/CookieSigner.ts +54 -0
  432. package/src/security/SignedUrl.ts +71 -0
  433. package/src/security/crypto.ts +98 -0
  434. package/src/server/HyperServer.ts +121 -0
  435. package/src/server/Server.ts +147 -0
  436. package/src/services/app.ts +34 -0
  437. package/src/services/router.ts +44 -0
  438. package/src/services/server.ts +40 -0
  439. package/src/session/Session.ts +231 -0
  440. package/src/session/SessionMiddleware.ts +153 -0
  441. package/src/session/drivers/CookieDriver.ts +48 -0
  442. package/src/session/drivers/MemoryDriver.ts +29 -0
  443. package/src/storage/S3Driver.ts +171 -0
  444. package/src/storage/StaticMiddleware.ts +182 -0
  445. package/src/storage/StaticProvider.ts +40 -0
  446. package/src/storage/StorageManager.ts +142 -0
  447. package/src/testing/RequestBuilder.ts +275 -0
  448. package/src/testing/TestClient.ts +344 -0
  449. package/src/types/helpers.ts +52 -0
  450. package/swcrc.app.json +17 -0
  451. package/tsconfig.app.json +21 -0
@@ -0,0 +1,733 @@
1
+ /**
2
+ * Fluent Router — AdonisJS v6 compatible routing with controllers, resources, and groups.
3
+ *
4
+ * @implements FR24, FR25, FR26, FR27, FR28
5
+ */
6
+
7
+ import type { HttpContext } from '../http/HttpContext.js'
8
+ import type { MiddlewareFunction } from '../middleware/Pipeline.js'
9
+ import type { MiddlewareEntry } from '../server/Server.js'
10
+ import { resolveMiddlewareEntry } from '../server/Server.js'
11
+
12
+ // ─── Types ──────────────────────────────────────────────────
13
+
14
+ export type RouteHandlerFunction = (ctx: HttpContext) => Promise<void> | void
15
+
16
+ /**
17
+ * Controller tuple: [ControllerClass, 'methodName'].
18
+ * Constructor params are resolved by the IoC container, not by TypeScript —
19
+ * same pattern as AdonisJS (@poppinss/utils Constructor type).
20
+ */
21
+ // biome-ignore lint/suspicious/noExplicitAny: required — TypeScript contravariance makes it impossible to type "any constructor" without `any`
22
+ export type ControllerAction = [target: new (...args: any[]) => any, method: string]
23
+
24
+ /** A route handler is either a closure or a controller tuple. */
25
+ export type RouteHandler = RouteHandlerFunction | ControllerAction
26
+
27
+ /** Param matcher — regex or predefined matcher. */
28
+ export type ParamMatcher = RegExp | { pattern: RegExp }
29
+
30
+ export interface RouteDefinition {
31
+ method: string
32
+ path: string
33
+ handler: RouteHandlerFunction | null
34
+ // biome-ignore lint/suspicious/noExplicitAny: see ControllerAction
35
+ controller?: { target: new (...args: any[]) => any; method: string }
36
+ middleware: string[]
37
+ inlineMiddleware: MiddlewareFunction[]
38
+ guards: string[]
39
+ roles: string[]
40
+ permissions: string[]
41
+ validators: string[]
42
+ name?: string
43
+ version?: string
44
+ domain?: string
45
+ matchers: Record<string, ParamMatcher>
46
+ deprecates?: { version: string; sunset?: string }
47
+ }
48
+
49
+ export interface MatchResult {
50
+ route: RouteDefinition
51
+ params: Record<string, string>
52
+ }
53
+
54
+ // ─── Matchers ───────────────────────────────────────────────
55
+
56
+ export const matchers = {
57
+ /** Match numeric params only. */
58
+ number(): ParamMatcher {
59
+ return { pattern: /^\d+$/ }
60
+ },
61
+ /** Match UUID v4 params only. */
62
+ uuid(): ParamMatcher {
63
+ return { pattern: /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i }
64
+ },
65
+ /** Match slug params only (lowercase alphanumeric + hyphens). */
66
+ slug(): ParamMatcher {
67
+ return { pattern: /^[a-z0-9]+(?:-[a-z0-9]+)*$/ }
68
+ },
69
+ }
70
+
71
+ // ─── RouteBuilder ───────────────────────────────────────────
72
+
73
+ /** Fluent route builder — mutates the already-registered route definition. */
74
+ export class RouteBuilder {
75
+ #route: RouteDefinition
76
+
77
+ constructor(route: RouteDefinition) {
78
+ this.#route = route
79
+ }
80
+
81
+ /** Name this route (for URL generation and redirects). */
82
+ as(name: string): this {
83
+ this.#route.name = name
84
+ return this
85
+ }
86
+
87
+ /** Add named middleware. */
88
+ middleware(...names: string[]): this {
89
+ this.#route.middleware.push(...names)
90
+ return this
91
+ }
92
+
93
+ /** Add inline middleware functions. */
94
+ use(...mw: MiddlewareFunction[]): this {
95
+ this.#route.inlineMiddleware.push(...mw)
96
+ return this
97
+ }
98
+
99
+ /** Add authentication guards. */
100
+ guard(...guards: string[]): this {
101
+ this.#route.guards.push(...guards)
102
+ return this
103
+ }
104
+
105
+ /** Require specific roles. */
106
+ role(...roles: string[]): this {
107
+ this.#route.roles.push(...roles)
108
+ return this
109
+ }
110
+
111
+ /** Require specific permissions. */
112
+ permission(...permissions: string[]): this {
113
+ this.#route.permissions.push(...permissions)
114
+ return this
115
+ }
116
+
117
+ /** Add a param constraint. */
118
+ where(param: string, matcher: ParamMatcher | RegExp): this {
119
+ this.#route.matchers[param] = matcher instanceof RegExp ? { pattern: matcher } : matcher
120
+ return this
121
+ }
122
+
123
+ /** Attach a validator. */
124
+ validate(validator: string): this {
125
+ this.#route.validators.push(validator)
126
+ return this
127
+ }
128
+
129
+ /** Set API version. */
130
+ version(v: string): this {
131
+ this.#route.version = v
132
+ return this
133
+ }
134
+
135
+ /** Restrict to a specific domain. */
136
+ domain(d: string): this {
137
+ this.#route.domain = d
138
+ return this
139
+ }
140
+
141
+ /** Mark as deprecated. */
142
+ deprecates(version: string, options?: { sunset?: string }): this {
143
+ this.#route.deprecates = { version, sunset: options?.sunset }
144
+ return this
145
+ }
146
+
147
+ /** @internal Get the underlying definition. */
148
+ getDefinition(): RouteDefinition {
149
+ return this.#route
150
+ }
151
+ }
152
+
153
+ // ─── GroupBuilder ───────────────────────────────────────────
154
+
155
+ /** Fluent group builder — returned by router.group(callback). */
156
+ export class GroupBuilder {
157
+ #routes: RouteDefinition[]
158
+
159
+ constructor(routes: RouteDefinition[]) {
160
+ this.#routes = routes
161
+ }
162
+
163
+ /** Set URL prefix for all routes in the group. */
164
+ prefix(p: string): this {
165
+ for (const route of this.#routes) {
166
+ route.path = p + route.path
167
+ }
168
+ return this
169
+ }
170
+
171
+ /** Add middleware to all routes in the group. */
172
+ middleware(...names: string[]): this {
173
+ for (const route of this.#routes) {
174
+ route.middleware = [...names, ...route.middleware]
175
+ }
176
+ return this
177
+ }
178
+
179
+ /** Add inline middleware to all routes in the group. */
180
+ use(...mw: MiddlewareFunction[]): this {
181
+ for (const route of this.#routes) {
182
+ route.inlineMiddleware = [...mw, ...route.inlineMiddleware]
183
+ }
184
+ return this
185
+ }
186
+
187
+ /** Add guards to all routes in the group. */
188
+ guard(...guards: string[]): this {
189
+ for (const route of this.#routes) {
190
+ route.guards = [...guards, ...route.guards]
191
+ }
192
+ return this
193
+ }
194
+
195
+ /** Prefix route names for all routes in the group. */
196
+ as(namePrefix: string): this {
197
+ for (const route of this.#routes) {
198
+ if (route.name) {
199
+ route.name = `${namePrefix}.${route.name}`
200
+ }
201
+ }
202
+ return this
203
+ }
204
+
205
+ /** Set domain for all routes in the group. */
206
+ domain(d: string): this {
207
+ for (const route of this.#routes) {
208
+ route.domain = d
209
+ }
210
+ return this
211
+ }
212
+
213
+ /** Add param matcher to all routes in the group. */
214
+ where(param: string, matcher: ParamMatcher | RegExp): this {
215
+ const m = matcher instanceof RegExp ? { pattern: matcher } : matcher
216
+ for (const route of this.#routes) {
217
+ if (!(param in route.matchers)) {
218
+ route.matchers[param] = m
219
+ }
220
+ }
221
+ return this
222
+ }
223
+ }
224
+
225
+ // ─── View engine helper ───────────────────────────────────────
226
+
227
+ interface ViewEngine {
228
+ render(name: string, data?: Record<string, unknown>): Promise<string>
229
+ }
230
+
231
+ function isViewEngine(value: unknown): value is ViewEngine {
232
+ return (
233
+ value !== null &&
234
+ typeof value === 'object' &&
235
+ 'render' in value &&
236
+ typeof value.render === 'function'
237
+ )
238
+ }
239
+
240
+ // ─── OnRouteBuilder ─────────────────────────────────────────
241
+
242
+ /** Builder for on(path).render(view) and on(path).redirect(target). */
243
+ export class OnRouteBuilder {
244
+ #router: Router
245
+ #path: string
246
+
247
+ constructor(router: Router, path: string) {
248
+ this.#router = router
249
+ this.#path = path
250
+ }
251
+
252
+ /** Render a view (requires Photon/view provider). */
253
+ render(view: string, data?: Record<string, unknown>): RouteBuilder {
254
+ return this.#router.get(this.#path, async (ctx) => {
255
+ const raw = ctx.store.get('view')
256
+ if (!isViewEngine(raw)) {
257
+ throw new Error('View engine not configured. Register a view provider (Photon) first.')
258
+ }
259
+ const viewEngine = raw
260
+ const html = await viewEngine.render(view, data)
261
+ ctx.response.type('text/html; charset=utf-8').send(html)
262
+ })
263
+ }
264
+
265
+ /** Redirect to a path. */
266
+ redirect(target: string, status = 302): RouteBuilder {
267
+ return this.#router.get(this.#path, async (ctx) => {
268
+ ctx.response.redirect().status(status).toPath(target)
269
+ })
270
+ }
271
+
272
+ /** Redirect to a named route. */
273
+ redirectToRoute(name: string, params?: Record<string, string>, status = 302): RouteBuilder {
274
+ return this.#router.get(this.#path, async (ctx) => {
275
+ ctx.response.redirect().status(status).toRoute(name, params)
276
+ })
277
+ }
278
+ }
279
+
280
+ // ─── Router ─────────────────────────────────────────────────
281
+
282
+ /** Main Router. */
283
+ export class Router {
284
+ #routes: RouteDefinition[] = []
285
+ #globalMatchers: Record<string, ParamMatcher> = {}
286
+ #routerMiddleware: MiddlewareFunction[] = []
287
+ #namedMiddleware: Map<string, MiddlewareFunction> = new Map()
288
+
289
+ /** Index: static routes by "METHOD:path" for O(1) exact match. */
290
+ #staticIndex: Map<string, RouteDefinition> = new Map()
291
+ /** Index: parametric routes grouped by method. */
292
+ #paramIndex: Map<string, RouteDefinition[]> = new Map()
293
+ /** Index: named routes for O(1) URL generation. */
294
+ #nameIndex: Map<string, RouteDefinition> = new Map()
295
+ /** Whether the index needs rebuilding. */
296
+ #indexDirty = true
297
+
298
+ /** Predefined param matchers. */
299
+ readonly matchers = matchers
300
+
301
+ // ─── Router-level middleware (like AdonisJS) ──────────────
302
+
303
+ /**
304
+ * Register router-level middleware (runs on requests with a matched route).
305
+ * router.use([() => import('#middleware/auth_middleware')])
306
+ */
307
+ use(middleware: MiddlewareEntry[]): this {
308
+ for (const mw of middleware) {
309
+ this.#routerMiddleware.push(resolveMiddlewareEntry(mw))
310
+ }
311
+ return this
312
+ }
313
+
314
+ /**
315
+ * Register named middleware collection.
316
+ * export const middleware = router.named({
317
+ * auth: () => import('#middleware/auth_middleware'),
318
+ * })
319
+ */
320
+ named(collection: Record<string, MiddlewareEntry>): Record<string, MiddlewareFunction> {
321
+ const resolved: Record<string, MiddlewareFunction> = {}
322
+ for (const [name, mw] of Object.entries(collection)) {
323
+ const fn = resolveMiddlewareEntry(mw)
324
+ this.#namedMiddleware.set(name, fn)
325
+ resolved[name] = fn
326
+ }
327
+ return resolved
328
+ }
329
+
330
+ /** Get the router-level middleware stack. */
331
+ getRouterMiddleware(): MiddlewareFunction[] {
332
+ return [...this.#routerMiddleware]
333
+ }
334
+
335
+ /** Get a named middleware by name. */
336
+ getNamedMiddleware(name: string): MiddlewareFunction | undefined {
337
+ return this.#namedMiddleware.get(name)
338
+ }
339
+
340
+ // ─── Route registration ───────────────────────────────────
341
+
342
+ /** Register a route with any HTTP method. */
343
+ route(method: string, path: string, handler: RouteHandler): RouteBuilder {
344
+ const def = this.#createDefinition(method, path, handler)
345
+ this.#routes.push(def)
346
+ this.#indexDirty = true
347
+ return new RouteBuilder(def)
348
+ }
349
+
350
+ get(path: string, handler: RouteHandler): RouteBuilder {
351
+ return this.route('GET', path, handler)
352
+ }
353
+
354
+ post(path: string, handler: RouteHandler): RouteBuilder {
355
+ return this.route('POST', path, handler)
356
+ }
357
+
358
+ put(path: string, handler: RouteHandler): RouteBuilder {
359
+ return this.route('PUT', path, handler)
360
+ }
361
+
362
+ patch(path: string, handler: RouteHandler): RouteBuilder {
363
+ return this.route('PATCH', path, handler)
364
+ }
365
+
366
+ delete(path: string, handler: RouteHandler): RouteBuilder {
367
+ return this.route('DELETE', path, handler)
368
+ }
369
+
370
+ head(path: string, handler: RouteHandler): RouteBuilder {
371
+ return this.route('HEAD', path, handler)
372
+ }
373
+
374
+ options(path: string, handler: RouteHandler): RouteBuilder {
375
+ return this.route('OPTIONS', path, handler)
376
+ }
377
+
378
+ /** Register a route for all HTTP methods. */
379
+ any(path: string, handler: RouteHandler): RouteBuilder {
380
+ return this.route('*', path, handler)
381
+ }
382
+
383
+ // ─── Resource routes ──────────────────────────────────────
384
+
385
+ /**
386
+ * Register resourceful routes for a controller.
387
+ * Generates: index, store, show, update, destroy
388
+ *
389
+ * Usage:
390
+ * router.resource('posts', PostsController)
391
+ * // GET /posts → PostsController.index
392
+ * // POST /posts → PostsController.store
393
+ * // GET /posts/:id → PostsController.show
394
+ * // PUT /posts/:id → PostsController.update
395
+ * // DELETE /posts/:id → PostsController.destroy
396
+ */
397
+ // biome-ignore lint/suspicious/noExplicitAny: see ControllerAction
398
+ resource(path: string, controller: new (...args: any[]) => any): GroupBuilder {
399
+ const baseName = path.replace(/\//g, '.')
400
+ const routes: RouteDefinition[] = []
401
+
402
+ const actions: Array<{ method: string; suffix: string; action: string; nameSuffix: string }> = [
403
+ { method: 'GET', suffix: '', action: 'index', nameSuffix: 'index' },
404
+ { method: 'POST', suffix: '', action: 'store', nameSuffix: 'store' },
405
+ { method: 'GET', suffix: '/:id', action: 'show', nameSuffix: 'show' },
406
+ { method: 'PUT', suffix: '/:id', action: 'update', nameSuffix: 'update' },
407
+ { method: 'PATCH', suffix: '/:id', action: 'update', nameSuffix: 'update' },
408
+ { method: 'DELETE', suffix: '/:id', action: 'destroy', nameSuffix: 'destroy' },
409
+ ]
410
+
411
+ for (const { method, suffix, action, nameSuffix } of actions) {
412
+ const def = this.#createDefinition(method, `/${path}${suffix}`, [controller, action])
413
+ def.name = `${baseName}.${nameSuffix}`
414
+ routes.push(def)
415
+ this.#routes.push(def)
416
+ }
417
+ this.#indexDirty = true
418
+
419
+ return new GroupBuilder(routes)
420
+ }
421
+
422
+ // ─── Groups ───────────────────────────────────────────────
423
+
424
+ /**
425
+ * Create a route group with shared configuration.
426
+ *
427
+ * AdonisJS-style (chainable):
428
+ * router.group(() => {
429
+ * router.get('/users', [UsersController, 'index'])
430
+ * }).prefix('/api').middleware('auth')
431
+ *
432
+ * Legacy-style (config object):
433
+ * router.group({ prefix: '/api', guards: ['jwt'] }, (r) => {
434
+ * r.get('/users', handler)
435
+ * })
436
+ */
437
+ group(callback: () => void): GroupBuilder
438
+ group(
439
+ config: {
440
+ prefix?: string
441
+ middleware?: string[]
442
+ guards?: string[]
443
+ roles?: string[]
444
+ permissions?: string[]
445
+ },
446
+ callback: (router: Router) => void,
447
+ ): void
448
+ group(
449
+ callbackOrConfig:
450
+ | (() => void)
451
+ | {
452
+ prefix?: string
453
+ middleware?: string[]
454
+ guards?: string[]
455
+ roles?: string[]
456
+ permissions?: string[]
457
+ },
458
+ legacyCallback?: (router: Router) => void,
459
+ ): GroupBuilder | undefined {
460
+ if (typeof callbackOrConfig === 'function') {
461
+ // AdonisJS-style: group(() => { ... }).prefix().middleware()
462
+ const snapshot = this.#routes.length
463
+ callbackOrConfig()
464
+ const newRoutes = this.#routes.slice(snapshot)
465
+ return new GroupBuilder(newRoutes)
466
+ }
467
+
468
+ // Legacy-style: group({ prefix, guards }, (r) => { ... })
469
+ const config = callbackOrConfig
470
+ const childRouter = new Router()
471
+ legacyCallback?.(childRouter)
472
+
473
+ for (const route of childRouter.#routes) {
474
+ this.#routes.push({
475
+ ...route,
476
+ path: (config.prefix ?? '') + route.path,
477
+ middleware: [...(config.middleware ?? []), ...route.middleware],
478
+ guards: [...(config.guards ?? []), ...route.guards],
479
+ roles: [...(config.roles ?? []), ...route.roles],
480
+ permissions: [...(config.permissions ?? []), ...route.permissions],
481
+ })
482
+ }
483
+ this.#indexDirty = true
484
+ }
485
+
486
+ // ─── On (view/redirect shortcuts) ─────────────────────────
487
+
488
+ /** Create a view/redirect route shortcut. */
489
+ on(path: string): OnRouteBuilder {
490
+ return new OnRouteBuilder(this, path)
491
+ }
492
+
493
+ // ─── Global matchers ──────────────────────────────────────
494
+
495
+ /** Set a global param matcher (applied to all routes). */
496
+ where(param: string, matcher: ParamMatcher | RegExp): this {
497
+ this.#globalMatchers[param] = matcher instanceof RegExp ? { pattern: matcher } : matcher
498
+ return this
499
+ }
500
+
501
+ // ─── Route matching ───────────────────────────────────────
502
+
503
+ /** Build route indexes for fast matching. Called lazily on first match. */
504
+ #buildIndex(): void {
505
+ this.#staticIndex.clear()
506
+ this.#paramIndex.clear()
507
+ this.#nameIndex.clear()
508
+
509
+ for (const route of this.#routes) {
510
+ // Named route index
511
+ if (route.name) {
512
+ this.#nameIndex.set(route.name, route)
513
+ }
514
+
515
+ const isParam = route.path.includes(':') || route.path.includes('*')
516
+
517
+ if (!isParam && !route.domain) {
518
+ // Static route — O(1) exact match
519
+ const key = route.method === '*' ? `*:${route.path}` : `${route.method}:${route.path}`
520
+ this.#staticIndex.set(key, route)
521
+ } else {
522
+ // Parametric route — indexed by method
523
+ const methods =
524
+ route.method === '*'
525
+ ? ['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'HEAD', 'OPTIONS']
526
+ : [route.method]
527
+ for (const m of methods) {
528
+ if (!this.#paramIndex.has(m)) this.#paramIndex.set(m, [])
529
+ this.#paramIndex.get(m)?.push(route)
530
+ }
531
+ }
532
+ }
533
+
534
+ this.#indexDirty = false
535
+ }
536
+
537
+ /** Find a matching route, extracting :param values and validating matchers. */
538
+ match(method: string, path: string, host?: string): MatchResult | undefined {
539
+ if (this.#indexDirty) this.#buildIndex()
540
+
541
+ const candidates = [path]
542
+ // Trailing-slash normalisation: `/users/` should match `/users`. Done as
543
+ // a fallback (not a primary rewrite) so a route declared as `/users/`
544
+ // still wins its exact match before we try the trimmed form.
545
+ if (path.length > 1 && path.endsWith('/')) {
546
+ candidates.push(path.slice(0, -1))
547
+ }
548
+
549
+ for (const candidatePath of candidates) {
550
+ // 1. Try static exact match (O(1))
551
+ const staticRoute =
552
+ this.#staticIndex.get(`${method}:${candidatePath}`) ??
553
+ this.#staticIndex.get(`*:${candidatePath}`)
554
+ if (staticRoute) {
555
+ if (!staticRoute.domain || !host || matchDomain(staticRoute.domain, host)) {
556
+ return { route: staticRoute, params: {} }
557
+ }
558
+ }
559
+
560
+ // 2. Try parametric routes (only same-method candidates)
561
+ const paramCandidates = this.#paramIndex.get(method) ?? []
562
+ for (const route of paramCandidates) {
563
+ if (route.domain && host && !matchDomain(route.domain, host)) continue
564
+
565
+ const params = matchPath(route.path, candidatePath)
566
+ if (params === null) continue
567
+
568
+ if (!this.#validateMatchers(params, route.matchers)) continue
569
+ if (!this.#validateMatchers(params, this.#globalMatchers)) continue
570
+
571
+ return { route, params }
572
+ }
573
+ }
574
+
575
+ return undefined
576
+ }
577
+
578
+ // ─── URL generation ───────────────────────────────────────
579
+
580
+ /** Generate a URL from a named route. */
581
+ makeUrl(name: string, params?: Record<string, string>): string {
582
+ if (this.#indexDirty) this.#buildIndex()
583
+ const route = this.#nameIndex.get(name)
584
+ if (!route) {
585
+ throw new Error(
586
+ `Route '${name}' not found. Available: ${this.#routes
587
+ .filter((r) => r.name)
588
+ .map((r) => r.name)
589
+ .join(', ')}`,
590
+ )
591
+ }
592
+ let url = route.path
593
+ if (params) {
594
+ for (const [key, value] of Object.entries(params)) {
595
+ url = url
596
+ .replace(`:${key}?`, encodeURIComponent(value))
597
+ .replace(`:${key}`, encodeURIComponent(value))
598
+ }
599
+ }
600
+ // Strip remaining optional placeholders (`:name?` segments not provided).
601
+ url = url.replace(/\/:[A-Za-z_][\w]*\?/g, '')
602
+ // Any remaining `:foo` is a required param the caller forgot. Surface it
603
+ // as a configuration error rather than emitting a broken URL.
604
+ const missing = url.match(/:[A-Za-z_][\w]*/g)
605
+ if (missing && missing.length > 0) {
606
+ throw new Error(
607
+ `Cannot generate URL for route '${name}': missing params ${missing.join(', ')}`,
608
+ )
609
+ }
610
+ return url
611
+ }
612
+
613
+ // ─── Accessors ────────────────────────────────────────────
614
+
615
+ /** Get all registered routes (for OpenAPI generation, introspection). */
616
+ getRoutes(): RouteDefinition[] {
617
+ return [...this.#routes]
618
+ }
619
+
620
+ /** Clear all registered routes (used by hot-reload). */
621
+ clear(): void {
622
+ this.#routes = []
623
+ this.#indexDirty = true
624
+ }
625
+
626
+ get routeCount(): number {
627
+ return this.#routes.length
628
+ }
629
+
630
+ // ─── Internals ────────────────────────────────────────────
631
+
632
+ #createDefinition(method: string, path: string, handler: RouteHandler): RouteDefinition {
633
+ const def: RouteDefinition = {
634
+ method: method.toUpperCase(),
635
+ path,
636
+ handler: null,
637
+ middleware: [],
638
+ inlineMiddleware: [],
639
+ guards: [],
640
+ roles: [],
641
+ permissions: [],
642
+ validators: [],
643
+ matchers: {},
644
+ }
645
+
646
+ if (Array.isArray(handler)) {
647
+ def.controller = { target: handler[0], method: handler[1] }
648
+ } else {
649
+ def.handler = handler
650
+ }
651
+
652
+ return def
653
+ }
654
+
655
+ #validateMatchers(
656
+ params: Record<string, string>,
657
+ matcherMap: Record<string, ParamMatcher>,
658
+ ): boolean {
659
+ for (const [param, value] of Object.entries(params)) {
660
+ const matcher = matcherMap[param]
661
+ if (!matcher) continue
662
+ const regex = matcher instanceof RegExp ? matcher : matcher.pattern
663
+ if (!regex.test(value)) return false
664
+ }
665
+ return true
666
+ }
667
+ }
668
+
669
+ // ─── Path matching ──────────────────────────────────────────
670
+
671
+ /** Match path pattern against actual path, extracting params. Returns null on no match. */
672
+ function matchPath(pattern: string, actual: string): Record<string, string> | null {
673
+ const patternParts = pattern.split('/')
674
+ const actualParts = actual.split('/')
675
+
676
+ // Catch-all wildcard: `/assets/*` greedily swallows the remainder of the
677
+ // path into a `*` param. The wildcard must be the final segment — any
678
+ // pattern after it would never match.
679
+ const wildcardIdx = patternParts.indexOf('*')
680
+ if (wildcardIdx !== -1) {
681
+ if (wildcardIdx !== patternParts.length - 1) return null
682
+ if (actualParts.length < wildcardIdx + 1) return null
683
+ const params: Record<string, string> = {}
684
+ for (let i = 0; i < wildcardIdx; i++) {
685
+ const part = patternParts[i]
686
+ if (part.startsWith(':')) {
687
+ params[part.substring(1)] = actualParts[i]
688
+ } else if (part !== actualParts[i]) {
689
+ return null
690
+ }
691
+ }
692
+ params['*'] = actualParts.slice(wildcardIdx).join('/')
693
+ return params
694
+ }
695
+
696
+ // Count required (non-optional) pattern parts
697
+ const requiredCount = patternParts.filter((p) => !p.endsWith('?')).length
698
+ const maxCount = patternParts.length
699
+
700
+ if (actualParts.length < requiredCount || actualParts.length > maxCount) return null
701
+
702
+ const params: Record<string, string> = {}
703
+
704
+ for (let i = 0; i < patternParts.length; i++) {
705
+ const part = patternParts[i]
706
+ const isOptional = part.endsWith('?')
707
+
708
+ if (part.startsWith(':')) {
709
+ const paramName = isOptional ? part.slice(1, -1) : part.substring(1)
710
+ if (i < actualParts.length) {
711
+ params[paramName] = actualParts[i]
712
+ }
713
+ // Optional param with no actual part — skip (param not set)
714
+ } else if (i < actualParts.length && part !== actualParts[i]) {
715
+ return null
716
+ } else if (i >= actualParts.length && !isOptional) {
717
+ return null
718
+ }
719
+ }
720
+
721
+ return params
722
+ }
723
+
724
+ /** Match domain pattern against actual host. Supports wildcards like *.example.com */
725
+ function matchDomain(pattern: string, host: string): boolean {
726
+ const actualHost = host.split(':')[0] // strip port
727
+ if (pattern === actualHost) return true
728
+ if (pattern.startsWith('*.')) {
729
+ const suffix = pattern.slice(1) // ".example.com"
730
+ return actualHost.endsWith(suffix) && actualHost.length > suffix.length
731
+ }
732
+ return false
733
+ }