@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,383 @@
1
+ /**
2
+ * IoC Container — AdonisJS Fold-compatible dependency injection.
3
+ *
4
+ * - make(Class) auto-constructs any class with @inject(), no explicit binding needed
5
+ * - call(instance, 'method') for method-level injection
6
+ * - singleton() / bind() for explicit factory bindings
7
+ * - swap() / restore() for testing
8
+ *
9
+ * @implements FR11, FR12, FR14, FR16
10
+ */
11
+
12
+ import 'reflect-metadata'
13
+ import { createLazyProxy, getLazyParams } from '../decorators/Lazy.js'
14
+ import { getInjectTokens, getServiceMetadata, getServiceRegistry } from '../decorators/Service.js'
15
+ import { didYouMean } from '../errors/FuzzyMatcher.js'
16
+ import { ReamError } from '../errors/ReamError.js'
17
+ import type { Binding, ServiceFactory, ServiceToken } from './types.js'
18
+
19
+ /**
20
+ * Symbol descriptions that would collide with object-prototype keys if used
21
+ * as raw map keys. We reject them on registration so tokens cannot be used
22
+ * as a prototype-pollution vector.
23
+ */
24
+ const RESERVED_TOKEN_NAMES = new Set(['__proto__', 'constructor', 'prototype'])
25
+
26
+ export class Container {
27
+ #bindings: Map<string, Binding> = new Map()
28
+ #singletons: Map<string, unknown> = new Map()
29
+ #overrides: Map<string, ServiceFactory> = new Map()
30
+ /**
31
+ * Snapshot of singletons taken when `swap`/`override` shadows a previously
32
+ * resolved instance. `restore()` re-seats them without re-invoking the
33
+ * original factory, so callers that captured the live reference keep
34
+ * pointing at the same object after the test ends.
35
+ */
36
+ #singletonBackup: Map<string, unknown> = new Map()
37
+ #resolutionStack: string[] = []
38
+ #resolutionSet: Set<string> = new Set()
39
+
40
+ // ─── Explicit bindings ────────────────────────────────────
41
+
42
+ /** Register a singleton binding (factory called once, cached). */
43
+ singleton<_T>(token: ServiceToken, factory: ServiceFactory): void {
44
+ const key = this.#tokenToKey(token)
45
+ this.#bindings.set(key, { token, factory, scope: 'singleton', dependencies: [] })
46
+ }
47
+
48
+ /** Register a transient binding (new instance per resolve). */
49
+ bind<_T>(token: ServiceToken, factory: ServiceFactory): void {
50
+ const key = this.#tokenToKey(token)
51
+ this.#bindings.set(key, { token, factory, scope: 'transient', dependencies: [] })
52
+ }
53
+
54
+ /** Bind an existing value directly. */
55
+ bindValue<T>(token: ServiceToken, value: T): void {
56
+ const key = this.#tokenToKey(token)
57
+ this.#singletons.set(key, value)
58
+ this.#bindings.set(key, { token, factory: () => value, scope: 'singleton', dependencies: [] })
59
+ }
60
+
61
+ // ─── Testing ──────────────────────────────────────────────
62
+
63
+ /**
64
+ * Replace a binding's factory for testing (AdonisJS-compatible swap).
65
+ * Snapshots the existing singleton (if any) so `restore()` re-seats the
66
+ * original instance instead of re-invoking the underlying factory.
67
+ */
68
+ swap(token: ServiceToken, factory: ServiceFactory): void {
69
+ const key = this.#tokenToKey(token)
70
+ if (!this.#overrides.has(key) && this.#singletons.has(key)) {
71
+ this.#singletonBackup.set(key, this.#singletons.get(key))
72
+ }
73
+ this.#overrides.set(key, factory)
74
+ this.#singletons.delete(key)
75
+ }
76
+
77
+ /**
78
+ * Replace a binding with a literal value for testing. The value is wrapped
79
+ * as a factory internally, so each `resolve()` call returns it unchanged.
80
+ * Pair with `restore()` to undo without losing the original singleton's
81
+ * identity.
82
+ */
83
+ override<T>(token: ServiceToken, value: T): void {
84
+ this.swap(token, () => value)
85
+ }
86
+
87
+ /** Restore a specific swap, or all swaps if no token given. */
88
+ restore(token?: ServiceToken): void {
89
+ if (token) {
90
+ this.#restoreOne(this.#tokenToKey(token))
91
+ return
92
+ }
93
+ for (const key of [...this.#overrides.keys()]) {
94
+ this.#restoreOne(key)
95
+ }
96
+ }
97
+
98
+ #restoreOne(key: string): void {
99
+ this.#overrides.delete(key)
100
+ this.#singletons.delete(key)
101
+ if (this.#singletonBackup.has(key)) {
102
+ this.#singletons.set(key, this.#singletonBackup.get(key))
103
+ this.#singletonBackup.delete(key)
104
+ }
105
+ }
106
+
107
+ // ─── Resolution ───────────────────────────────────────────
108
+
109
+ /**
110
+ * Resolve/construct a class or binding.
111
+ * Like AdonisJS `container.make()`:
112
+ * 1. Check swaps (test overrides)
113
+ * 2. Check cached singletons
114
+ * 3. Check explicit bindings (singleton/bind)
115
+ * 4. Auto-construct if class has @inject() or @Service()
116
+ * 5. Auto-construct any class (plain `new Class()`) as fallback
117
+ */
118
+ make<T>(token: ServiceToken): T {
119
+ return this.resolve<T>(token)
120
+ }
121
+
122
+ /** Alias for make() — backward compatible. */
123
+ resolve<T>(token: ServiceToken): T {
124
+ const key = this.#tokenToKey(token)
125
+
126
+ if (this.#resolutionSet.has(key)) {
127
+ const cycle = [...this.#resolutionStack, key].join(' → ')
128
+ throw new ReamError('CIRCULAR_DEPENDENCY', `Circular dependency detected: ${cycle}`, {
129
+ hint: 'Use @Lazy() on one of the constructor parameters to break the cycle.',
130
+ context: { chain: cycle },
131
+ })
132
+ }
133
+ this.#resolutionStack.push(key)
134
+ this.#resolutionSet.add(key)
135
+
136
+ try {
137
+ return this.#resolveInner<T>(key, token)
138
+ } finally {
139
+ this.#resolutionStack.pop()
140
+ this.#resolutionSet.delete(key)
141
+ }
142
+ }
143
+
144
+ /**
145
+ * Call a method on an instance with dependency injection.
146
+ * Like AdonisJS `container.call()`.
147
+ * Resolves method parameters from reflect-metadata and @inject().
148
+ */
149
+ async call<T, K extends string & keyof T>(
150
+ instance: T,
151
+ method: K,
152
+ runtimeValues?: unknown[],
153
+ ): Promise<unknown> {
154
+ type Ctor = new (...args: unknown[]) => unknown
155
+ // `Object.getPrototypeOf` is typed `any` in lib.dom, so the assignment
156
+ // narrows it back to a typed constructor without a cast expression.
157
+ const target: Ctor = Object.getPrototypeOf(instance).constructor
158
+ const paramTypes: unknown[] =
159
+ Reflect.getMetadata('design:paramtypes', target.prototype, method) ?? []
160
+
161
+ const args = paramTypes.map((type, index) => {
162
+ // Runtime values take precedence
163
+ if (runtimeValues && index < runtimeValues.length) {
164
+ return runtimeValues[index]
165
+ }
166
+ // Auto-resolve class dependencies — type is a constructor function here
167
+ if (isClassConstructor(type)) {
168
+ return this.resolve(type)
169
+ }
170
+ return undefined
171
+ })
172
+
173
+ const member: unknown = instance[method]
174
+ if (!isCallable(member)) {
175
+ throw new Error(`Container.call: '${method}' is not a callable method`)
176
+ }
177
+ return member.apply(instance, args)
178
+ }
179
+
180
+ // ─── Introspection ────────────────────────────────────────
181
+
182
+ /** Check if a token is registered or resolvable. */
183
+ has(token: ServiceToken): boolean {
184
+ const key = this.#tokenToKey(token)
185
+ return (
186
+ this.#bindings.has(key) ||
187
+ this.#overrides.has(key) ||
188
+ this.#singletons.has(key) ||
189
+ (typeof token === 'function' && getServiceMetadata(token) !== undefined)
190
+ )
191
+ }
192
+
193
+ get size(): number {
194
+ return this.#bindings.size
195
+ }
196
+
197
+ /** Auto-register all @Service() decorated classes from the global registry. */
198
+ autoRegister(): void {
199
+ for (const [target, metadata] of getServiceRegistry()) {
200
+ const key = metadata.as ?? target.name
201
+ if (!this.#bindings.has(key)) {
202
+ const targetClass = target
203
+ this.#bindings.set(key, {
204
+ token: metadata.as ?? target,
205
+ factory: () => this.#autoConstruct(targetClass),
206
+ scope: metadata.scope,
207
+ dependencies: [],
208
+ })
209
+ }
210
+ }
211
+ }
212
+
213
+ // ─── Internal resolution ──────────────────────────────────
214
+
215
+ #resolveInner<T>(key: string, token: ServiceToken): T {
216
+ // 1. Check swaps (test overrides)
217
+ if (this.#overrides.has(key)) {
218
+ // biome-ignore lint/suspicious/noExplicitAny: IoC container factory returns unknown; caller brands via T
219
+ return this.#overrides.get(key)?.() as any as T
220
+ }
221
+
222
+ // 2. Check cached singletons
223
+ if (this.#singletons.has(key)) {
224
+ // biome-ignore lint/suspicious/noExplicitAny: IoC singleton stored as unknown; caller brands via T
225
+ return this.#singletons.get(key) as any as T
226
+ }
227
+
228
+ // 3. Check explicit bindings
229
+ const binding = this.#bindings.get(key)
230
+ if (binding) {
231
+ const instance = binding.factory ? binding.factory() : undefined
232
+ if (binding.scope === 'singleton') {
233
+ this.#singletons.set(key, instance)
234
+ }
235
+ // biome-ignore lint/suspicious/noExplicitAny: IoC binding factory returns unknown; caller brands via T
236
+ return instance as any as T
237
+ }
238
+
239
+ // 4. Auto-construct if it's a class
240
+ if (typeof token === 'function') {
241
+ // biome-ignore lint/suspicious/noExplicitAny: IoC auto-construct returns unknown; caller brands via T
242
+ return this.#autoConstruct(token) as any as T
243
+ }
244
+
245
+ // 5. Not found
246
+ const allKeys = [...this.#bindings.keys(), ...this.#overrides.keys()]
247
+ const suggestion = didYouMean(key, allKeys)
248
+ throw new ReamError(
249
+ 'CONTAINER_NOT_FOUND',
250
+ `No binding found for '${key}'.${suggestion ? ` ${suggestion}` : ''}`,
251
+ {
252
+ hint: 'Register it with container.singleton() or decorate with @inject().',
253
+ },
254
+ )
255
+ }
256
+
257
+ /**
258
+ * Auto-construct a class by reading its dependency hints.
259
+ *
260
+ * Resolution order (like AdonisJS Fold):
261
+ * 1. static containerInjections._constructor.dependencies — explicit deps array
262
+ * 2. Reflect.getMetadata('design:paramtypes') — decorator metadata (requires SWC/tsc)
263
+ * 3. No params → plain `new Class()`
264
+ */
265
+ #autoConstruct(target: new (...args: unknown[]) => unknown): unknown {
266
+ const metadata = getServiceMetadata(target)
267
+ const scope = metadata?.scope ?? 'transient'
268
+ const key = metadata?.as ?? target.name
269
+
270
+ if (scope === 'singleton' && this.#singletons.has(key)) {
271
+ return this.#singletons.get(key)
272
+ }
273
+
274
+ // 1. Check static containerInjections (AdonisJS Fold-compatible, works
275
+ // without emitDecoratorMetadata). The `in` operator narrows the optional
276
+ // field to `unknown`, then `readContainerInjectionDeps` validates the
277
+ // shape at runtime — no type-level cast required.
278
+ const explicitDeps = readContainerInjectionDeps(target)
279
+
280
+ // 2. Fallback to reflect-metadata
281
+ const paramTypes: unknown[] =
282
+ explicitDeps ?? Reflect.getMetadata('design:paramtypes', target) ?? []
283
+
284
+ if (paramTypes.length === 0) {
285
+ const instance = new target()
286
+ if (scope === 'singleton') this.#singletons.set(key, instance)
287
+ return instance
288
+ }
289
+
290
+ const injectTokens = getInjectTokens(target)
291
+ const lazyIndices = getLazyParams(target)
292
+
293
+ const deps = paramTypes.map((type, index) => {
294
+ const namedToken = injectTokens.get(index)
295
+ // After the typeof check, type is a constructor function and valid as ServiceToken
296
+ const depToken: ServiceToken | undefined =
297
+ namedToken ??
298
+ // biome-ignore lint/suspicious/noExplicitAny: reflect-metadata returns unknown; constructor functions are valid ServiceTokens
299
+ (typeof type === 'function' && type !== Object ? (type as any as ServiceToken) : undefined)
300
+
301
+ if (!depToken) return undefined
302
+
303
+ if (lazyIndices.includes(index)) {
304
+ // biome-ignore lint/suspicious/noExplicitAny: lazy proxy target must be object; IoC resolution returns unknown
305
+ return createLazyProxy(() => this.resolve(depToken) as any as object)
306
+ }
307
+
308
+ return this.resolve(depToken)
309
+ })
310
+
311
+ const instance = new target(...deps)
312
+ if (scope === 'singleton') this.#singletons.set(key, instance)
313
+ return instance
314
+ }
315
+
316
+ #tokenToKey(token: ServiceToken): string {
317
+ if (typeof token === 'string') {
318
+ this.#assertNotReserved(token, 'string')
319
+ return token
320
+ }
321
+ if (typeof token === 'symbol') {
322
+ // Reject unique `Symbol(x)` — two `Symbol("foo")` calls create distinct
323
+ // symbols, so an override registered with one would be invisible to a
324
+ // resolver holding the other. Force callers onto `Symbol.for(x)` which
325
+ // is the global registry and round-trips across modules.
326
+ if (Symbol.keyFor(token) === undefined) {
327
+ throw new ReamError(
328
+ 'CONTAINER_INVALID_TOKEN',
329
+ `Unique Symbol() tokens are not supported — use Symbol.for("${token.description ?? 'name'}") so different modules see the same symbol.`,
330
+ )
331
+ }
332
+ const desc = token.description
333
+ if (desc === undefined) {
334
+ throw new ReamError('CONTAINER_INVALID_TOKEN', 'Symbol tokens must have a description.')
335
+ }
336
+ this.#assertNotReserved(desc, 'symbol description')
337
+ return `Symbol(${desc})`
338
+ }
339
+ return token.name
340
+ }
341
+
342
+ #assertNotReserved(name: string, kind: 'string' | 'symbol description'): void {
343
+ if (RESERVED_TOKEN_NAMES.has(name)) {
344
+ throw new ReamError('CONTAINER_RESERVED_TOKEN', `Reserved container token name '${name}'.`, {
345
+ hint: `'${name}' would collide with an Object.prototype key. Pick a different ${kind}.`,
346
+ })
347
+ }
348
+ }
349
+ }
350
+
351
+ // `design:paramtypes` returns `unknown[]` per our annotation; we narrow each
352
+ // entry before treating it as a class token. `Object` slips through because
353
+ // emitDecoratorMetadata uses it for `any`/`unknown` parameters.
354
+ function isClassConstructor(value: unknown): value is new (...args: unknown[]) => unknown {
355
+ return typeof value === 'function' && value !== Object
356
+ }
357
+
358
+ function isCallable(value: unknown): value is (...args: unknown[]) => unknown {
359
+ return typeof value === 'function'
360
+ }
361
+
362
+ /**
363
+ * Read `static containerInjections._constructor.dependencies` from a class,
364
+ * AdonisJS Fold-style, when the static is present. The static is undeclared
365
+ * on the constructor signature, so we walk the shape with `in`-narrowing
366
+ * (TS 4.9+) plus a per-element `isServiceToken` guard — no casts.
367
+ */
368
+ function readContainerInjectionDeps(target: object): ServiceToken[] | undefined {
369
+ if (!('containerInjections' in target)) return undefined
370
+ const injections = target.containerInjections
371
+ if (typeof injections !== 'object' || injections === null) return undefined
372
+ if (!('_constructor' in injections)) return undefined
373
+ const ctor = injections._constructor
374
+ if (typeof ctor !== 'object' || ctor === null) return undefined
375
+ if (!('dependencies' in ctor)) return undefined
376
+ const deps = ctor.dependencies
377
+ if (!Array.isArray(deps)) return undefined
378
+ return deps.every(isServiceToken) ? deps : undefined
379
+ }
380
+
381
+ function isServiceToken(value: unknown): value is ServiceToken {
382
+ return typeof value === 'string' || typeof value === 'symbol' || typeof value === 'function'
383
+ }
@@ -0,0 +1,30 @@
1
+ /**
2
+ * Container types and interfaces.
3
+ * @implements FR11, FR14, FR16
4
+ */
5
+
6
+ /** Service scope determines instance lifecycle. */
7
+ export type ServiceScope = 'singleton' | 'transient' | 'request'
8
+
9
+ /** Service metadata stored by @Service() decorator. */
10
+ export interface ServiceMetadata {
11
+ scope: ServiceScope
12
+ /** If set, binds as this named interface instead of the class itself. */
13
+ as?: string
14
+ }
15
+
16
+ /** Token used to resolve a service — class constructor, string name, or registered symbol. */
17
+ // biome-ignore lint/suspicious/noExplicitAny: IoC container tokens must accept any constructor signature
18
+ export type ServiceToken = (new (...args: any[]) => unknown) | string | symbol
19
+
20
+ /** Factory function for creating service instances. */
21
+ export type ServiceFactory = () => unknown
22
+
23
+ /** Binding entry in the container. */
24
+ export interface Binding {
25
+ token: ServiceToken
26
+ factory?: ServiceFactory
27
+ instance?: unknown
28
+ scope: ServiceScope
29
+ dependencies: ServiceToken[]
30
+ }
@@ -0,0 +1,57 @@
1
+ /**
2
+ * @Lazy() decorator — deferred injection to break circular dependencies.
3
+ *
4
+ * @implements FR15
5
+ */
6
+
7
+ import 'reflect-metadata'
8
+
9
+ const LAZY_KEY = Symbol.for('ream:lazy')
10
+
11
+ /**
12
+ * @Lazy() — marks a constructor parameter for deferred resolution.
13
+ * The dependency is resolved on first property access, not at construction.
14
+ */
15
+ export function Lazy(): ParameterDecorator {
16
+ return (target, propertyKey, parameterIndex) => {
17
+ const existing: number[] = Reflect.getOwnMetadata(LAZY_KEY, target, propertyKey ?? '') ?? []
18
+ existing.push(parameterIndex)
19
+ Reflect.defineMetadata(LAZY_KEY, existing, target, propertyKey ?? '')
20
+ }
21
+ }
22
+
23
+ /** Get lazy parameter indices for a constructor. */
24
+ export function getLazyParams(target: object): number[] {
25
+ return Reflect.getOwnMetadata(LAZY_KEY, target, '') ?? []
26
+ }
27
+
28
+ /**
29
+ * Create a lazy proxy that defers resolution until first access.
30
+ * Traps get, set, has, ownKeys, getOwnPropertyDescriptor for full compatibility.
31
+ */
32
+ export function createLazyProxy<T extends object>(resolver: () => T): T {
33
+ let instance: T | undefined
34
+ const ensure = (): T => {
35
+ if (!instance) instance = resolver()
36
+ return instance
37
+ }
38
+
39
+ // biome-ignore lint/suspicious/noExplicitAny: Proxy target must be an object literal; T is branded at call site
40
+ return new Proxy({} as any as T, {
41
+ get(_target, prop, receiver) {
42
+ return Reflect.get(ensure(), prop, receiver)
43
+ },
44
+ set(_target, prop, value, receiver) {
45
+ return Reflect.set(ensure(), prop, value, receiver)
46
+ },
47
+ has(_target, prop) {
48
+ return Reflect.has(ensure(), prop)
49
+ },
50
+ ownKeys() {
51
+ return Reflect.ownKeys(ensure())
52
+ },
53
+ getOwnPropertyDescriptor(_target, prop) {
54
+ return Object.getOwnPropertyDescriptor(ensure(), prop)
55
+ },
56
+ })
57
+ }
@@ -0,0 +1,85 @@
1
+ /**
2
+ * @Service() and @Inject() decorators for IoC container integration.
3
+ *
4
+ * @implements FR11, FR12, FR14
5
+ */
6
+
7
+ import 'reflect-metadata'
8
+ import type { ServiceMetadata, ServiceScope } from '../container/types.js'
9
+
10
+ const SERVICE_METADATA_KEY = Symbol('ream:service')
11
+ const INJECT_METADATA_KEY = Symbol('ream:inject')
12
+
13
+ /** Constructor type — accepts unknown args, returns unknown instance. */
14
+ type AnyConstructor = new (...args: unknown[]) => unknown
15
+
16
+ /** Registry of all decorated services. */
17
+ const serviceRegistry: Map<AnyConstructor, ServiceMetadata> = new Map()
18
+
19
+ export function getServiceRegistry(): ReadonlyMap<AnyConstructor, ServiceMetadata> {
20
+ return serviceRegistry
21
+ }
22
+
23
+ /** Clear the service registry (for test isolation and hot-reload). */
24
+ export function clearServiceRegistry(): void {
25
+ serviceRegistry.clear()
26
+ }
27
+
28
+ export function getServiceMetadata(target: AnyConstructor): ServiceMetadata | undefined {
29
+ return serviceRegistry.get(target)
30
+ }
31
+
32
+ /**
33
+ * @Service() decorator.
34
+ * Registers the class in the IoC container for auto-resolution.
35
+ */
36
+ export function Service(options: { scope?: ServiceScope; as?: string } = {}): ClassDecorator {
37
+ return (target) => {
38
+ const metadata: ServiceMetadata = {
39
+ scope: options.scope ?? 'singleton',
40
+ as: options.as,
41
+ }
42
+ // biome-ignore lint/suspicious/noExplicitAny: ClassDecorator target is Function; registry requires AnyConstructor which is a subtype
43
+ serviceRegistry.set(target as any as AnyConstructor, metadata)
44
+ Reflect.defineMetadata(SERVICE_METADATA_KEY, metadata, target)
45
+ }
46
+ }
47
+
48
+ /**
49
+ * @Inject() decorator — marks a constructor parameter for named injection.
50
+ *
51
+ * @implements FR16
52
+ */
53
+ export function Inject(token: string): ParameterDecorator {
54
+ return (target, _propertyKey, parameterIndex) => {
55
+ const existingTokens: Map<number, string> =
56
+ Reflect.getOwnMetadata(INJECT_METADATA_KEY, target) ?? new Map()
57
+ existingTokens.set(parameterIndex, token)
58
+ Reflect.defineMetadata(INJECT_METADATA_KEY, existingTokens, target)
59
+ }
60
+ }
61
+
62
+ /**
63
+ * Get named injection tokens for a class constructor.
64
+ */
65
+ export function getInjectTokens(target: AnyConstructor): Map<number, string> {
66
+ return Reflect.getOwnMetadata(INJECT_METADATA_KEY, target) ?? new Map()
67
+ }
68
+
69
+ /**
70
+ * @inject() class decorator — marks a controller (or service) for transient IoC resolution.
71
+ *
72
+ * Equivalent to @Service({ scope: 'transient' }). Use on controllers so the container
73
+ * auto-resolves constructor dependencies per-request.
74
+ *
75
+ * Usage:
76
+ * @inject()
77
+ * export default class UsersController {
78
+ * constructor(private userService: UserService) {}
79
+ * }
80
+ */
81
+ export function inject(): ClassDecorator {
82
+ return Service({ scope: 'transient' })
83
+ }
84
+
85
+ export { INJECT_METADATA_KEY, SERVICE_METADATA_KEY }
@@ -0,0 +1,93 @@
1
+ /**
2
+ * Fuzzy Matcher — suggests corrections for typos using Levenshtein distance.
3
+ *
4
+ * @implements FR72
5
+ */
6
+
7
+ /**
8
+ * Calculate Levenshtein distance between two strings.
9
+ */
10
+ export function levenshtein(a: string, b: string): number {
11
+ const m = a.length
12
+ const n = b.length
13
+
14
+ if (m === 0) return n
15
+ if (n === 0) return m
16
+
17
+ const dp: number[][] = Array.from({ length: m + 1 }, () => Array<number>(n + 1).fill(0))
18
+
19
+ for (let i = 0; i <= m; i++) dp[i][0] = i
20
+ for (let j = 0; j <= n; j++) dp[0][j] = j
21
+
22
+ for (let i = 1; i <= m; i++) {
23
+ for (let j = 1; j <= n; j++) {
24
+ const cost = a[i - 1] === b[j - 1] ? 0 : 1
25
+ dp[i][j] = Math.min(
26
+ dp[i - 1][j] + 1, // deletion
27
+ dp[i][j - 1] + 1, // insertion
28
+ dp[i - 1][j - 1] + cost, // substitution
29
+ )
30
+ }
31
+ }
32
+
33
+ return dp[m][n]
34
+ }
35
+
36
+ /**
37
+ * Find the closest matches from a list of candidates.
38
+ *
39
+ * @param input - The typo'd string
40
+ * @param candidates - Available options
41
+ * @param maxDistance - Maximum Levenshtein distance to consider (default: 3)
42
+ * @param maxResults - Maximum suggestions to return (default: 3)
43
+ * @returns Sorted array of { candidate, distance }
44
+ */
45
+ /**
46
+ * Find the closest matches from a list of candidates.
47
+ * Exact matches (distance 0) are excluded — this is for "did you mean" suggestions.
48
+ *
49
+ * @param input - The typo'd string (max 100 chars — longer inputs return empty)
50
+ * @param candidates - Available options
51
+ * @param maxDistance - Maximum Levenshtein distance to consider (default: 3)
52
+ * @param maxResults - Maximum suggestions to return (default: 3)
53
+ * @returns Sorted array of { candidate, distance }
54
+ */
55
+ export function findClosestMatches(
56
+ input: string,
57
+ candidates: string[],
58
+ maxDistance = 3,
59
+ maxResults = 3,
60
+ ): Array<{ candidate: string; distance: number }> {
61
+ // Guard against very long inputs to avoid O(m*n) allocation pressure
62
+ if (input.length > 100 || candidates.length === 0) return []
63
+
64
+ const results = candidates
65
+ .map((candidate) => ({
66
+ candidate,
67
+ distance: levenshtein(input.toLowerCase(), candidate.toLowerCase()),
68
+ }))
69
+ .filter((r) => r.distance <= maxDistance && r.distance > 0)
70
+ .sort((a, b) => a.distance - b.distance)
71
+ .slice(0, maxResults)
72
+
73
+ return results
74
+ }
75
+
76
+ /**
77
+ * Format a "did you mean?" suggestion string.
78
+ *
79
+ * @param input - What the user typed
80
+ * @param candidates - Available options to match against
81
+ * @returns Formatted suggestion string, or empty string if no matches
82
+ */
83
+ export function didYouMean(input: string, candidates: string[]): string {
84
+ const matches = findClosestMatches(input, candidates)
85
+ if (matches.length === 0) return ''
86
+
87
+ if (matches.length === 1) {
88
+ return `Did you mean '${matches[0].candidate}'?`
89
+ }
90
+
91
+ const suggestions = matches.map((m) => `'${m.candidate}'`).join(', ')
92
+ return `Did you mean one of: ${suggestions}?`
93
+ }