@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,147 @@
1
+ /**
2
+ * Server — manages HTTP server middleware, error handler, and router.
3
+ *
4
+ * Like AdonisJS Server class:
5
+ * - server.use([() => import('#middleware/...')]) — lazy class middleware
6
+ * - server.errorHandler(() => import('#exceptions/handler')) — custom exception handler
7
+ */
8
+
9
+ import type { ExceptionHandler } from '../http/Exception.js'
10
+ import type { HttpContext } from '../http/HttpContext.js'
11
+ import type { MiddlewareFunction } from '../middleware/Pipeline.js'
12
+ import type { Router } from '../router/Router.js'
13
+
14
+ /**
15
+ * A middleware class with a handle method.
16
+ * This is the pattern used by AdonisJS middleware.
17
+ */
18
+ export interface MiddlewareClass {
19
+ handle(ctx: HttpContext, next: () => Promise<void>): Promise<void> | void
20
+ }
21
+
22
+ /** Lazy import returning a module with a default export. */
23
+ export type LazyImport<T> = () => Promise<{ default: T }>
24
+
25
+ // biome-ignore lint/suspicious/noExplicitAny: contravariance — same as ControllerAction
26
+ export type ErrorHandlerClass = new (...args: any[]) => ExceptionHandler
27
+
28
+ // biome-ignore lint/suspicious/noExplicitAny: contravariance — middleware class constructors
29
+ type MiddlewareClassConstructor = new (...args: any[]) => MiddlewareClass
30
+
31
+ /** What server.use() and router.use() accept: lazy imports or direct functions. */
32
+ export type MiddlewareEntry = LazyImport<MiddlewareClassConstructor> | MiddlewareFunction
33
+
34
+ export class Server {
35
+ #router: Router
36
+ #serverMiddleware: MiddlewareFunction[] = []
37
+ #errorHandlerImporter?: LazyImport<ErrorHandlerClass>
38
+ #resolvedErrorHandler?: ExceptionHandler
39
+ #trustedProxies: string[] = []
40
+
41
+ constructor(router: Router) {
42
+ this.#router = router
43
+ }
44
+
45
+ /**
46
+ * Configure the trusted-proxy CIDR list. The Rust HyperServer uses it to
47
+ * pre-resolve `request.ip` once per request — JS reads the field, no CIDR
48
+ * matching in TS.
49
+ *
50
+ * Empty list (default, 2026-05+) is **strict fail-closed**: `X-Forwarded-For`
51
+ * and `X-Real-IP` are ignored entirely; only the socket peer is used.
52
+ * Apps deployed behind a reverse proxy must list the proxy's CIDR
53
+ * (`'10.0.0.0/8'`, `'192.0.2.0/24'`, etc.) or pass the `'*'` sentinel
54
+ * for permissive mode (accepts spoofing as the explicit trade-off when
55
+ * proxy CIDRs can't be enumerated).
56
+ */
57
+ trustedProxies(cidrs: string[]): this {
58
+ this.#trustedProxies = [...cidrs]
59
+ return this
60
+ }
61
+
62
+ /** Read the current trusted-proxy list (consumed by Ignitor at boot). */
63
+ getTrustedProxies(): readonly string[] {
64
+ return this.#trustedProxies
65
+ }
66
+
67
+ /**
68
+ * Register global server middleware.
69
+ * Accepts lazy imports of middleware classes (AdonisJS pattern):
70
+ * server.use([() => import('#middleware/log_request_middleware')])
71
+ */
72
+ use(middleware: MiddlewareEntry[]): this {
73
+ for (const mw of middleware) {
74
+ this.#serverMiddleware.push(resolveMiddlewareEntry(mw))
75
+ }
76
+ return this
77
+ }
78
+
79
+ /**
80
+ * Register a custom error handler.
81
+ * server.errorHandler(() => import('#exceptions/handler'))
82
+ */
83
+ errorHandler(handler: LazyImport<ErrorHandlerClass>): this {
84
+ this.#errorHandlerImporter = handler
85
+ return this
86
+ }
87
+
88
+ /** Boot — resolve the error handler. Called by Ignitor during ready phase. */
89
+ async boot(): Promise<void> {
90
+ if (this.#errorHandlerImporter) {
91
+ const mod = await this.#errorHandlerImporter()
92
+ const HandlerClass = mod.default
93
+ this.#resolvedErrorHandler = new HandlerClass()
94
+ }
95
+ }
96
+
97
+ getServerMiddleware(): MiddlewareFunction[] {
98
+ return [...this.#serverMiddleware]
99
+ }
100
+
101
+ getErrorHandler(): ExceptionHandler | undefined {
102
+ return this.#resolvedErrorHandler
103
+ }
104
+
105
+ getRouter(): Router {
106
+ return this.#router
107
+ }
108
+ }
109
+
110
+ /**
111
+ * Convert a MiddlewareEntry (lazy import or function) into a MiddlewareFunction.
112
+ * Lazy imports are resolved on first call and cached.
113
+ */
114
+ export function resolveMiddlewareEntry(entry: MiddlewareEntry): MiddlewareFunction {
115
+ // Direct middleware function (2 params: ctx, next)
116
+ if (isMiddlewareFunction(entry)) {
117
+ return entry
118
+ }
119
+
120
+ // Lazy import of a middleware class — resolve + cache on first invocation
121
+ // After isMiddlewareFunction returns false, entry is narrowed to LazyImport<MiddlewareClassConstructor>
122
+ const lazyImport = entry
123
+ let cached: MiddlewareClass | undefined
124
+ return async (ctx: HttpContext, next: () => Promise<void>) => {
125
+ if (!cached) {
126
+ const mod = await lazyImport()
127
+ cached = new mod.default()
128
+ }
129
+ await cached.handle(ctx, next)
130
+ }
131
+ }
132
+
133
+ /** Check if an entry is a direct middleware function (not a lazy import). */
134
+ function isMiddlewareFunction(entry: MiddlewareEntry): entry is MiddlewareFunction {
135
+ // A lazy import factory returns a promise (dynamic import). Middleware functions don't.
136
+ // We check: if calling it with no args returns a thenable, it's a lazy import.
137
+ // Better heuristic: lazy imports are () => import(...) which have .length === 0
138
+ // and middleware functions have .length >= 2 (ctx, next).
139
+ // Edge case: arrow with defaults has length 1. But lazy import() always has length 0.
140
+ if (typeof entry !== 'function') return false
141
+ // Functions with 2+ params are definitely middleware
142
+ if (entry.length >= 2) return true
143
+ // Functions with 0 params are likely lazy imports
144
+ if (entry.length === 0) return false
145
+ // 1 param is ambiguous — treat as middleware (ctx only, no next)
146
+ return true
147
+ }
@@ -0,0 +1,34 @@
1
+ /**
2
+ * Application singleton service.
3
+ *
4
+ * Usage:
5
+ * import app from '@c9up/ream/services/app'
6
+ * const logger = app.container.make('logger')
7
+ * if (app.inProduction) { ... }
8
+ */
9
+
10
+ import type { Application } from '../Application.js'
11
+
12
+ let instance: Application | undefined
13
+
14
+ /** @internal Set the app instance (called by Ignitor). */
15
+ export function setApp(app: Application): void {
16
+ instance = app
17
+ }
18
+
19
+ const app: Application = new Proxy({} as Application, {
20
+ get(_target, prop) {
21
+ if (!instance) {
22
+ throw new Error(
23
+ 'Application accessed before initialization. ' +
24
+ 'Ensure this code runs during or after the boot phase.',
25
+ )
26
+ }
27
+ // Bind methods so private-field writes inside the class resolve
28
+ // against the real instance brand instead of the Proxy.
29
+ const value = Reflect.get(instance, prop, instance)
30
+ return typeof value === 'function' ? value.bind(instance) : value
31
+ },
32
+ })
33
+
34
+ export default app
@@ -0,0 +1,44 @@
1
+ /**
2
+ * Router singleton service — proxy to the Ignitor's router instance.
3
+ *
4
+ * Usage in module route files:
5
+ * import router from '@c9up/ream/services/router'
6
+ * router.get('/tasks', [TasksController, 'index'])
7
+ *
8
+ * The proxy is initialized by Ignitor before preload files are imported.
9
+ */
10
+
11
+ import type { Router } from '../router/Router.js'
12
+
13
+ let instance: Router | undefined
14
+
15
+ /** @internal Set the router instance (called by Ignitor). */
16
+ export function setRouter(router: Router): void {
17
+ instance = router
18
+ }
19
+
20
+ /** @internal Get the router instance directly. */
21
+ export function getRouter(): Router | undefined {
22
+ return instance
23
+ }
24
+
25
+ /**
26
+ * Router proxy — defers all property access to the underlying instance.
27
+ * Throws if accessed before Ignitor initializes it.
28
+ */
29
+ const router: Router = new Proxy({} as Router, {
30
+ get(_target, prop) {
31
+ if (!instance) {
32
+ throw new Error(
33
+ 'Router accessed before initialization. ' +
34
+ 'Ensure your route files are loaded as preloads in reamrc.ts, not at import time.',
35
+ )
36
+ }
37
+ // Bind methods so private-field writes inside the class resolve
38
+ // against the real instance brand instead of the Proxy.
39
+ const value = Reflect.get(instance, prop, instance)
40
+ return typeof value === 'function' ? value.bind(instance) : value
41
+ },
42
+ })
43
+
44
+ export default router
@@ -0,0 +1,40 @@
1
+ /**
2
+ * Server singleton service — proxy to the Ignitor's Server instance.
3
+ *
4
+ * Usage in start/kernel.ts:
5
+ * import server from '@c9up/ream/services/server'
6
+ * server.errorHandler(() => import('#exceptions/handler'))
7
+ * server.use([() => import('#middleware/logging')])
8
+ */
9
+
10
+ import type { Server } from '../server/Server.js'
11
+
12
+ let instance: Server | undefined
13
+
14
+ /** @internal Set the server instance (called by Ignitor). */
15
+ export function setServer(server: Server): void {
16
+ instance = server
17
+ }
18
+
19
+ /** @internal Get the server instance directly. */
20
+ export function getServer(): Server | undefined {
21
+ return instance
22
+ }
23
+
24
+ const server: Server = new Proxy({} as Server, {
25
+ get(_target, prop) {
26
+ if (!instance) {
27
+ throw new Error(
28
+ 'Server accessed before initialization. ' +
29
+ 'Ensure your kernel files are loaded as preloads in reamrc.ts.',
30
+ )
31
+ }
32
+ // Bind methods to the real instance so private-field access inside
33
+ // them resolves against the underlying class brand — passing the
34
+ // Proxy as `this` breaks `#field` writes (Proxy isn't branded).
35
+ const value = Reflect.get(instance, prop, instance)
36
+ return typeof value === 'function' ? value.bind(instance) : value
37
+ },
38
+ })
39
+
40
+ export default server
@@ -0,0 +1,231 @@
1
+ /**
2
+ * Session — server-side session management.
3
+ *
4
+ * Like AdonisJS Session:
5
+ * session.get('key')
6
+ * session.put('key', value)
7
+ * session.flash('success', 'Saved!')
8
+ * session.forget('key')
9
+ *
10
+ * Backed by pluggable drivers (cookie, memory, redis).
11
+ */
12
+
13
+ export interface SessionDriver {
14
+ read(sessionId: string): Promise<Record<string, unknown>>
15
+ write(sessionId: string, data: Record<string, unknown>, ttl: number): Promise<void>
16
+ destroy(sessionId: string): Promise<void>
17
+ touch(sessionId: string, ttl: number): Promise<void>
18
+ }
19
+
20
+ export interface SessionConfig {
21
+ driver: string
22
+ cookieName?: string
23
+ maxAge?: number // seconds, default 7200 (2h)
24
+ clearWithBrowser?: boolean
25
+ /**
26
+ * When true, re-emit Set-Cookie on every response so the cookie's `maxAge`
27
+ * window slides forward on each request. Active users won't be logged out
28
+ * mid-session; the trade-off is a `Vary: Cookie` side-channel on every
29
+ * response (defeats shared HTTP caches). Default `false` to preserve cache
30
+ * behaviour. Express-session and AdonisJS expose the same toggle.
31
+ */
32
+ rolling?: boolean
33
+ }
34
+
35
+ /**
36
+ * Generate a fresh session ID. Format mirrors AdonisJS / express-session —
37
+ * 32 bytes of base64url entropy (~256 bits). Defined here so `regenerate()`
38
+ * can pick a new id without bringing in a driver dependency.
39
+ */
40
+ function generateSessionId(): string {
41
+ const bytes = new Uint8Array(32)
42
+ // `crypto.getRandomValues` is a Node ≥ 19 builtin AND the standard browser
43
+ // API; same call site works on both runtimes without a polyfill.
44
+ crypto.getRandomValues(bytes)
45
+ let str = ''
46
+ for (let i = 0; i < bytes.length; i++) {
47
+ str += String.fromCharCode(bytes[i] ?? 0)
48
+ }
49
+ return btoa(str).replace(/\+/g, '-').replace(/\//g, '_').replace(/=/g, '')
50
+ }
51
+
52
+ export class Session {
53
+ #data: Record<string, unknown> = {}
54
+ #flashData: Record<string, unknown> = {}
55
+ #previousFlash: Record<string, unknown> = {}
56
+ #dirty = false
57
+ #sessionId: string
58
+ /**
59
+ * The session id active when the request arrived, captured at construction.
60
+ * The middleware uses this on the way out to migrate driver storage
61
+ * (`driver.write(newId)` + `driver.destroy(oldId)`) when the application
62
+ * called `regenerate()` mid-request.
63
+ */
64
+ readonly #originalSessionId: string
65
+ #regenerated = false
66
+
67
+ constructor(sessionId: string, data: Record<string, unknown> = {}) {
68
+ this.#sessionId = sessionId
69
+ this.#originalSessionId = sessionId
70
+ this.#data = { ...data }
71
+ const flash = data.__flash
72
+ this.#previousFlash =
73
+ typeof flash === 'object' && flash !== null && !Array.isArray(flash)
74
+ ? // biome-ignore lint/suspicious/noExplicitAny: flash narrowed to non-null non-array object; branded as Record for key access
75
+ (flash as any as Record<string, unknown>)
76
+ : {}
77
+ delete this.#data.__flash
78
+ }
79
+
80
+ /** Current session id. Mutated by `regenerate()`. */
81
+ get sessionId(): string {
82
+ return this.#sessionId
83
+ }
84
+
85
+ /**
86
+ * Rotate the session id while preserving the data. The standard mitigation
87
+ * for session-fixation (CWE-384): call this whenever a privilege boundary
88
+ * is crossed — login, role change, sensitive-action confirmation. Warden's
89
+ * `SessionStrategy.login()` calls it automatically before writing the
90
+ * authenticated user id.
91
+ *
92
+ * The new id takes effect inside this Session instance immediately so
93
+ * subsequent `session.get()` / `session.put()` see the same data under
94
+ * the new id. The SessionMiddleware sees `wasRegenerated() === true` on
95
+ * the way out and migrates the driver storage + emits a new Set-Cookie.
96
+ */
97
+ regenerate(): void {
98
+ this.#sessionId = generateSessionId()
99
+ this.#regenerated = true
100
+ this.#dirty = true
101
+ }
102
+
103
+ /** @internal — read by SessionMiddleware to detect a mid-request rotation. */
104
+ wasRegenerated(): boolean {
105
+ return this.#regenerated
106
+ }
107
+
108
+ /** @internal — the id captured at request arrival; used to destroy the old driver entry post-regenerate. */
109
+ originalSessionId(): string {
110
+ return this.#originalSessionId
111
+ }
112
+
113
+ /** Get a session value. */
114
+ get<T = unknown>(key: string, defaultValue?: T): T {
115
+ if (key in this.#data) {
116
+ // biome-ignore lint/suspicious/noExplicitAny: session data stored as unknown; caller brands the value type via T
117
+ return this.#data[key] as any as T
118
+ }
119
+ // biome-ignore lint/suspicious/noExplicitAny: generic default — caller brands the value type via T
120
+ return defaultValue as any as T
121
+ }
122
+
123
+ /** Set a session value. */
124
+ put(key: string, value: unknown): void {
125
+ this.#data[key] = value
126
+ this.#dirty = true
127
+ }
128
+
129
+ /** Check if a key exists. */
130
+ has(key: string): boolean {
131
+ return key in this.#data
132
+ }
133
+
134
+ /** Get all session data. */
135
+ all(): Record<string, unknown> {
136
+ return { ...this.#data }
137
+ }
138
+
139
+ /** Remove a key. */
140
+ forget(key: string): void {
141
+ delete this.#data[key]
142
+ this.#dirty = true
143
+ }
144
+
145
+ /** Get and remove a key. */
146
+ pull<T = unknown>(key: string, defaultValue?: T): T {
147
+ const value = this.get<T>(key, defaultValue)
148
+ this.forget(key)
149
+ return value
150
+ }
151
+
152
+ /** Clear all session data. */
153
+ clear(): void {
154
+ this.#data = {}
155
+ this.#dirty = true
156
+ }
157
+
158
+ /** Increment a numeric value. */
159
+ increment(key: string, amount = 1): void {
160
+ const current = this.get<number>(key) ?? 0
161
+ this.put(key, current + amount)
162
+ }
163
+
164
+ /** Decrement a numeric value. */
165
+ decrement(key: string, amount = 1): void {
166
+ this.increment(key, -amount)
167
+ }
168
+
169
+ // ─── Flash data ───────────────────────────────────────────
170
+
171
+ /** Set flash data (available only on the next request). */
172
+ flash(key: string, value: unknown): void {
173
+ this.#flashData[key] = value
174
+ this.#dirty = true
175
+ }
176
+
177
+ /** Flash all current input. */
178
+ flashAll(input: Record<string, unknown>): void {
179
+ for (const [k, v] of Object.entries(input)) {
180
+ this.#flashData[k] = v
181
+ }
182
+ this.#dirty = true
183
+ }
184
+
185
+ /** Flash only specific keys. */
186
+ flashOnly(input: Record<string, unknown>, keys: string[]): void {
187
+ for (const k of keys) {
188
+ if (k in input) this.#flashData[k] = input[k]
189
+ }
190
+ this.#dirty = true
191
+ }
192
+
193
+ /** Flash all except specific keys. */
194
+ flashExcept(input: Record<string, unknown>, keys: string[]): void {
195
+ const keySet = new Set(keys)
196
+ for (const [k, v] of Object.entries(input)) {
197
+ if (!keySet.has(k)) this.#flashData[k] = v
198
+ }
199
+ this.#dirty = true
200
+ }
201
+
202
+ /** Get a flashed value from the previous request. */
203
+ flashMessages(): Record<string, unknown> {
204
+ return { ...this.#previousFlash }
205
+ }
206
+
207
+ /** Get a specific flash message from the previous request. */
208
+ old<T = unknown>(key: string, defaultValue?: T): T {
209
+ if (key in this.#previousFlash) {
210
+ // biome-ignore lint/suspicious/noExplicitAny: flash data stored as unknown; caller brands the value type via T
211
+ return this.#previousFlash[key] as any as T
212
+ }
213
+ // biome-ignore lint/suspicious/noExplicitAny: generic default — caller brands the value type via T
214
+ return defaultValue as any as T
215
+ }
216
+
217
+ // ─── Serialization ────────────────────────────────────────
218
+
219
+ /** Serialize for storage. Includes flash data for next request. */
220
+ toJSON(): Record<string, unknown> {
221
+ const result = { ...this.#data }
222
+ if (Object.keys(this.#flashData).length > 0) {
223
+ result.__flash = this.#flashData
224
+ }
225
+ return result
226
+ }
227
+
228
+ isDirty(): boolean {
229
+ return this.#dirty
230
+ }
231
+ }
@@ -0,0 +1,153 @@
1
+ /**
2
+ * Session middleware — initializes session on each request.
3
+ *
4
+ * Reads session ID from cookie, loads data via driver, attaches to ctx,
5
+ * and writes back after the handler runs.
6
+ */
7
+
8
+ import { randomBytes } from 'node:crypto'
9
+ import type { HttpContext } from '../http/HttpContext.js'
10
+ import { CookieDriver } from './drivers/CookieDriver.js'
11
+ import { MemoryDriver } from './drivers/MemoryDriver.js'
12
+ import type { SessionConfig, SessionDriver } from './Session.js'
13
+ import { Session } from './Session.js'
14
+
15
+ interface ResolvedSessionConfig {
16
+ driver: string
17
+ cookieName: string
18
+ maxAge: number
19
+ clearWithBrowser: boolean
20
+ rolling: boolean
21
+ }
22
+
23
+ export default class SessionMiddleware {
24
+ #driver: SessionDriver
25
+ #cookieDriver: CookieDriver | null = null
26
+ #config: ResolvedSessionConfig
27
+
28
+ constructor(config?: SessionConfig & { secret?: string }) {
29
+ this.#config = {
30
+ driver: config?.driver ?? 'memory',
31
+ cookieName: config?.cookieName ?? 'ream_session',
32
+ maxAge: config?.maxAge ?? 7200,
33
+ clearWithBrowser: config?.clearWithBrowser ?? false,
34
+ rolling: config?.rolling ?? false,
35
+ }
36
+
37
+ if (this.#config.driver === 'cookie') {
38
+ if (!config?.secret) {
39
+ throw new Error(
40
+ 'Cookie session driver requires a secret. Set session.secret in your config.',
41
+ )
42
+ }
43
+ this.#cookieDriver = new CookieDriver(config.secret)
44
+ this.#driver = this.#cookieDriver
45
+ } else if (this.#config.driver === 'memory') {
46
+ this.#driver = new MemoryDriver()
47
+ } else {
48
+ throw new Error(`Unknown session driver: ${this.#config.driver}`)
49
+ }
50
+ }
51
+
52
+ async handle(ctx: HttpContext, next: () => Promise<void>) {
53
+ const cookieName = this.#config.cookieName
54
+ const maxAge = this.#config.maxAge
55
+ const rolling = this.#config.rolling
56
+ const isProduction = process.env.NODE_ENV === 'production'
57
+
58
+ // Read session ID from cookie — pre-parsed by the Rust HyperServer.
59
+ let sessionId = ctx.request.cookie(cookieName)
60
+ const hadIncomingCookie = sessionId !== null
61
+
62
+ // Cookie driver: the cookie value IS the session data (encrypted)
63
+ if (this.#cookieDriver) {
64
+ const data = await this.#driver.read(sessionId ?? '')
65
+ sessionId = sessionId ?? generateSessionId()
66
+ const session = new Session(sessionId, data)
67
+ ctx.store.set('session', session)
68
+
69
+ await next()
70
+
71
+ // Default: only emit Set-Cookie if the session was actually modified —
72
+ // sending a cookie on every response defeats HTTP caching, leaks state
73
+ // to CDNs, and creates a privacy side-channel (response varies even for
74
+ // read-only GETs).
75
+ // Rolling mode: re-emit on every response with an existing session so
76
+ // the cookie maxAge window slides forward (active users don't time out
77
+ // mid-session). Caller opts in via SessionConfig.rolling.
78
+ const shouldEmit = session.isDirty() || (rolling && hadIncomingCookie)
79
+ if (shouldEmit) {
80
+ const encoded = this.#cookieDriver.encode(session.toJSON())
81
+ ctx.response.cookie(cookieName, encoded, {
82
+ maxAge: this.#config.clearWithBrowser ? undefined : maxAge,
83
+ path: '/',
84
+ httpOnly: true,
85
+ sameSite: 'lax',
86
+ secure: isProduction,
87
+ })
88
+ }
89
+ return
90
+ }
91
+
92
+ // Server-side drivers (memory, redis)
93
+ if (!sessionId) {
94
+ sessionId = generateSessionId()
95
+ }
96
+
97
+ const data = await this.#driver.read(sessionId)
98
+ const session = new Session(sessionId, data)
99
+ ctx.store.set('session', session)
100
+
101
+ await next()
102
+
103
+ // Session-fixation mitigation: if the handler called `session.regenerate()`
104
+ // (typically via Warden's SessionStrategy.login), the session id has been
105
+ // rotated. Migrate the driver storage atomically — write under the new
106
+ // id first, then destroy the old entry so a crash between calls leaves
107
+ // the user with a valid session under one of the two ids rather than
108
+ // none. The cookie below picks up `session.sessionId` which already
109
+ // returns the new value.
110
+ const regenerated = session.wasRegenerated()
111
+ const isNew = !hadIncomingCookie
112
+ if (regenerated) {
113
+ const originalId = session.originalSessionId()
114
+ const currentId = session.sessionId
115
+ await this.#driver.write(currentId, session.toJSON(), maxAge)
116
+ // Best-effort: a destroy failure leaves a stale entry that TTL will
117
+ // sweep, but should not crash the request. The new session is already
118
+ // live under `currentId`.
119
+ try {
120
+ await this.#driver.destroy(originalId)
121
+ } catch {
122
+ // benign — TTL will reclaim it.
123
+ }
124
+ } else if (session.isDirty()) {
125
+ await this.#driver.write(sessionId, session.toJSON(), maxAge)
126
+ } else if (!isNew) {
127
+ await this.#driver.touch(sessionId, maxAge)
128
+ }
129
+
130
+ // Emit Set-Cookie only when there is server-side state worth
131
+ // pinning to the browser: the session was modified (isDirty → we
132
+ // just wrote it), regenerated (rotated id post-login), or rolling
133
+ // mode is sliding an existing session's window. A brand-new but
134
+ // CLEAN anonymous request gets NO cookie — otherwise every first
135
+ // read-only GET hands out a persistent tracking id that points at
136
+ // no server-side row (touch() is a no-op without a backing entry),
137
+ // defeats HTTP caching, and leaks a Set-Cookie to CDNs. This mirrors
138
+ // the cookie-driver path above (which already omits `isNew`).
139
+ if (session.isDirty() || regenerated || (rolling && hadIncomingCookie)) {
140
+ ctx.response.cookie(cookieName, session.sessionId, {
141
+ maxAge: this.#config.clearWithBrowser ? undefined : maxAge,
142
+ path: '/',
143
+ httpOnly: true,
144
+ sameSite: 'lax',
145
+ secure: isProduction,
146
+ })
147
+ }
148
+ }
149
+ }
150
+
151
+ function generateSessionId(): string {
152
+ return randomBytes(24).toString('base64url')
153
+ }
@@ -0,0 +1,48 @@
1
+ import { CookieSigner } from '../../security/CookieSigner.js'
2
+ import type { SessionDriver } from '../Session.js'
3
+
4
+ /**
5
+ * Cookie session driver — stores session data in an encrypted cookie (AES-256-GCM).
6
+ * Stateless on the server. Limited by cookie size (~4KB).
7
+ * Requires a secret for encryption.
8
+ */
9
+ export class CookieDriver implements SessionDriver {
10
+ private signer: CookieSigner
11
+
12
+ constructor(secret: string) {
13
+ if (!secret) {
14
+ throw new Error(
15
+ 'CookieDriver requires a secret for session encryption. Set session.secret in your config.',
16
+ )
17
+ }
18
+ this.signer = new CookieSigner(secret)
19
+ }
20
+
21
+ async read(sessionId: string): Promise<Record<string, unknown>> {
22
+ if (!sessionId) return {}
23
+ const decrypted = this.signer.decrypt(sessionId)
24
+ if (!decrypted) return {}
25
+ try {
26
+ return JSON.parse(decrypted)
27
+ } catch {
28
+ return {}
29
+ }
30
+ }
31
+
32
+ async write(_sessionId: string, _data: Record<string, unknown>, _ttl: number): Promise<void> {
33
+ // Cookie driver writes via the response cookie — handled by middleware
34
+ }
35
+
36
+ async destroy(_sessionId: string): Promise<void> {
37
+ // Cookie deletion handled by middleware setting maxAge=0
38
+ }
39
+
40
+ async touch(_sessionId: string, _ttl: number): Promise<void> {
41
+ // Cookie renewal handled by middleware
42
+ }
43
+
44
+ /** Encode session data for cookie storage (encrypted). */
45
+ encode(data: Record<string, unknown>): string {
46
+ return this.signer.encrypt(JSON.stringify(data))
47
+ }
48
+ }