@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,275 @@
1
+ import type { TestResponse } from './TestClient.js'
2
+
3
+ /**
4
+ * Fluent HTTP request builder with assertion methods.
5
+ *
6
+ * await client
7
+ * .fluent('GET', '/api/users/42')
8
+ * .withAuth(user)
9
+ * .expectStatus(200)
10
+ * .expectJson({ id: 42 })
11
+ *
12
+ * The builder chains: setters return `this`, assertion methods send the
13
+ * request on first call (memoised) and return a `Promise<this>` so further
14
+ * assertions can be awaited or chained via `await`.
15
+ */
16
+
17
+ export interface AuthSubject {
18
+ /** String or numeric user id — used by `withAuth` / `asUser` to sign the session. */
19
+ id: string | number
20
+ /** Optional custom headers added to the request (e.g. tenant markers). */
21
+ extraHeaders?: Record<string, string>
22
+ }
23
+
24
+ export interface AuthStrategy {
25
+ /** Compute the headers Warden expects for this user (Bearer token / session cookie / ...). */
26
+ headersFor(subject: AuthSubject): Record<string, string> | Promise<Record<string, string>>
27
+ /** Compute cookies for session-based strategies. */
28
+ cookiesFor?(subject: AuthSubject): Record<string, string> | Promise<Record<string, string>>
29
+ }
30
+
31
+ /** Assertion failure — plain Error so tests see a clean stack trace. */
32
+ class ExpectationError extends Error {
33
+ constructor(message: string) {
34
+ super(message)
35
+ this.name = 'ExpectationError'
36
+ }
37
+ }
38
+
39
+ export type HttpMethod = 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE'
40
+
41
+ /**
42
+ * Internal low-level sender — matches what the TestClient exposes.
43
+ * Accepting it as an injected callable keeps RequestBuilder framework-agnostic.
44
+ */
45
+ export type HttpSender = (
46
+ method: HttpMethod,
47
+ path: string,
48
+ init: {
49
+ headers: Record<string, string>
50
+ body: Buffer
51
+ },
52
+ ) => Promise<TestResponse>
53
+
54
+ export class RequestBuilder {
55
+ #sender: HttpSender
56
+ #method: HttpMethod
57
+ #path: string
58
+ #headers: Record<string, string> = {}
59
+ #body: Buffer = Buffer.alloc(0)
60
+ #cookies: Record<string, string> = {}
61
+ #authStrategy: AuthStrategy | null
62
+ #pendingAuth: AuthSubject | null = null
63
+ #sent: Promise<TestResponse> | null = null
64
+
65
+ constructor(
66
+ sender: HttpSender,
67
+ method: HttpMethod,
68
+ path: string,
69
+ authStrategy: AuthStrategy | null = null,
70
+ ) {
71
+ this.#sender = sender
72
+ this.#method = method
73
+ this.#path = path
74
+ this.#authStrategy = authStrategy
75
+ }
76
+
77
+ headers(map: Record<string, string>): this {
78
+ for (const [k, v] of Object.entries(map)) {
79
+ this.#headers[k.toLowerCase()] = v
80
+ }
81
+ return this
82
+ }
83
+
84
+ header(name: string, value: string): this {
85
+ this.#headers[name.toLowerCase()] = value
86
+ return this
87
+ }
88
+
89
+ json(data: unknown): this {
90
+ this.#body = Buffer.from(JSON.stringify(data), 'utf8')
91
+ this.#headers['content-type'] = 'application/json'
92
+ return this
93
+ }
94
+
95
+ body(data: Buffer | string, contentType?: string): this {
96
+ this.#body = typeof data === 'string' ? Buffer.from(data, 'utf8') : data
97
+ if (contentType) this.#headers['content-type'] = contentType
98
+ return this
99
+ }
100
+
101
+ form(data: Record<string, string>): this {
102
+ const pairs = Object.entries(data).map(
103
+ ([k, v]) => `${encodeURIComponent(k)}=${encodeURIComponent(v)}`,
104
+ )
105
+ this.#body = Buffer.from(pairs.join('&'), 'utf8')
106
+ this.#headers['content-type'] = 'application/x-www-form-urlencoded'
107
+ return this
108
+ }
109
+
110
+ cookies(map: Record<string, string>): this {
111
+ Object.assign(this.#cookies, map)
112
+ return this
113
+ }
114
+
115
+ cookie(name: string, value: string): this {
116
+ this.#cookies[name] = value
117
+ return this
118
+ }
119
+
120
+ /**
121
+ * Attach auth for a user. Uses the strategy passed to `client`'s `auth`
122
+ * option — Warden's session/JWT/API-key strategy decides the header/cookie
123
+ * shape. Without a strategy, throws at send time.
124
+ */
125
+ withAuth(subject: AuthSubject): this {
126
+ this.#pendingAuth = subject
127
+ return this
128
+ }
129
+
130
+ /** Shortcut for `withAuth({ id: userId })` — user is identified by id only. */
131
+ asUser(userId: string | number): this {
132
+ this.#pendingAuth = { id: userId }
133
+ return this
134
+ }
135
+
136
+ /** Fire the request (once) and return the raw response. Idempotent. */
137
+ send(): Promise<TestResponse> {
138
+ if (this.#sent === null) this.#sent = this.#execute()
139
+ return this.#sent
140
+ }
141
+
142
+ async expectStatus(code: number): Promise<this> {
143
+ const res = await this.send()
144
+ if (res.status !== code) {
145
+ throw new ExpectationError(
146
+ `Expected status ${code}, got ${res.status}. Body: ${capBody(res.body)}`,
147
+ )
148
+ }
149
+ return this
150
+ }
151
+
152
+ async expectHeader(name: string, value: string | RegExp): Promise<this> {
153
+ const res = await this.send()
154
+ const actual = res.headers[name.toLowerCase()]
155
+ if (actual === undefined) {
156
+ throw new ExpectationError(
157
+ `Expected header ${name}, not present in response headers: ${Object.keys(res.headers).join(', ')}`,
158
+ )
159
+ }
160
+ if (value instanceof RegExp) {
161
+ if (!value.test(actual)) {
162
+ throw new ExpectationError(`Expected header ${name} to match ${value}, got "${actual}"`)
163
+ }
164
+ } else if (actual !== value) {
165
+ throw new ExpectationError(`Expected header ${name} = "${value}", got "${actual}"`)
166
+ }
167
+ return this
168
+ }
169
+
170
+ async expectCookie(name: string, value?: string | RegExp): Promise<this> {
171
+ const res = await this.send()
172
+ const setCookie = res.headers['set-cookie']
173
+ if (!setCookie) {
174
+ throw new ExpectationError(`Expected cookie ${name}, but no Set-Cookie header was returned`)
175
+ }
176
+ const cookies = setCookie.split(/,(?=\s*\w+=)/)
177
+ const match = cookies.find((c) => c.trimStart().startsWith(`${name}=`))
178
+ if (!match) {
179
+ throw new ExpectationError(`Expected cookie ${name}, not found in: ${setCookie}`)
180
+ }
181
+ if (value === undefined) return this
182
+ const rawVal = match.split(';')[0]?.split('=')[1] ?? ''
183
+ if (value instanceof RegExp) {
184
+ if (!value.test(rawVal)) {
185
+ throw new ExpectationError(`Expected cookie ${name} to match ${value}, got "${rawVal}"`)
186
+ }
187
+ } else if (rawVal !== value) {
188
+ throw new ExpectationError(`Expected cookie ${name} = "${value}", got "${rawVal}"`)
189
+ }
190
+ return this
191
+ }
192
+
193
+ async expectJson(expected: unknown): Promise<this> {
194
+ const res = await this.send()
195
+ let actual: unknown
196
+ try {
197
+ actual = res.json()
198
+ } catch (err) {
199
+ throw new ExpectationError(
200
+ `Expected JSON body, got non-JSON. Body: ${capBody(res.body)} (parse error: ${err instanceof Error ? err.message : String(err)})`,
201
+ )
202
+ }
203
+ if (!partialMatch(actual, expected)) {
204
+ throw new ExpectationError(
205
+ `JSON partial match failed.\nExpected (partial): ${JSON.stringify(expected)}\nActual: ${capBody(JSON.stringify(actual))}`,
206
+ )
207
+ }
208
+ return this
209
+ }
210
+
211
+ async #execute(): Promise<TestResponse> {
212
+ // Resolve auth before sending.
213
+ if (this.#pendingAuth !== null) {
214
+ if (!this.#authStrategy) {
215
+ throw new Error(
216
+ 'RequestBuilder: `withAuth()` / `asUser()` called but no AuthStrategy was provided via the client `auth` option.',
217
+ )
218
+ }
219
+ const authHeaders = await this.#authStrategy.headersFor(this.#pendingAuth)
220
+ for (const [k, v] of Object.entries(authHeaders)) {
221
+ this.#headers[k.toLowerCase()] = v
222
+ }
223
+ if (this.#authStrategy.cookiesFor) {
224
+ const authCookies = await this.#authStrategy.cookiesFor(this.#pendingAuth)
225
+ Object.assign(this.#cookies, authCookies)
226
+ }
227
+ if (this.#pendingAuth.extraHeaders) {
228
+ for (const [k, v] of Object.entries(this.#pendingAuth.extraHeaders)) {
229
+ this.#headers[k.toLowerCase()] = v
230
+ }
231
+ }
232
+ }
233
+
234
+ // Serialise cookies into a single `Cookie:` header.
235
+ const cookieEntries = Object.entries(this.#cookies)
236
+ if (cookieEntries.length > 0) {
237
+ this.#headers.cookie = cookieEntries.map(([k, v]) => `${k}=${v}`).join('; ')
238
+ }
239
+
240
+ return this.#sender(this.#method, this.#path, {
241
+ headers: this.#headers,
242
+ body: this.#body,
243
+ })
244
+ }
245
+ }
246
+
247
+ function capBody(s: string, max = 512): string {
248
+ return s.length <= max ? s : `${s.slice(0, max)}...[truncated]`
249
+ }
250
+
251
+ /** Narrowing guard — a non-null, non-array object usable as a string map. */
252
+ function isRecord(x: unknown): x is Record<string, unknown> {
253
+ return typeof x === 'object' && x !== null && !Array.isArray(x)
254
+ }
255
+
256
+ /**
257
+ * Partial deep-match. For objects, every key in `expected` must match in
258
+ * `actual`. For arrays, every element in `expected` must match SOMEWHERE in
259
+ * `actual` (order-independent). Primitives compared by strict equality.
260
+ */
261
+ export function partialMatch(actual: unknown, expected: unknown): boolean {
262
+ if (expected === null || expected === undefined) {
263
+ return actual === expected
264
+ }
265
+ if (typeof expected !== 'object') return actual === expected
266
+ if (Array.isArray(expected)) {
267
+ if (!Array.isArray(actual)) return false
268
+ return expected.every((want) => actual.some((have) => partialMatch(have, want)))
269
+ }
270
+ if (!isRecord(actual) || !isRecord(expected)) return false
271
+ for (const key of Object.keys(expected)) {
272
+ if (!partialMatch(actual[key], expected[key])) return false
273
+ }
274
+ return true
275
+ }
@@ -0,0 +1,344 @@
1
+ /**
2
+ * TestClient — supertest-like HTTP client for integration testing.
3
+ *
4
+ * Boots a Ream application on an ephemeral port and provides a fluent API
5
+ * for sending requests and asserting responses.
6
+ *
7
+ * Usage:
8
+ * const client = new TestClient(createApp)
9
+ * await client.boot()
10
+ * const res = await client.get('/api/health')
11
+ * expect(res.status).toBe(200)
12
+ * await client.close()
13
+ *
14
+ * Or with the helper:
15
+ * const client = await createTestClient(createApp)
16
+ * const res = await client.post('/api/users').json({ name: 'Alice' }).send()
17
+ * await client.close()
18
+ */
19
+
20
+ import type { Dict } from '../types/helpers.js'
21
+ import {
22
+ type AuthStrategy,
23
+ type HttpMethod,
24
+ type HttpSender,
25
+ RequestBuilder,
26
+ } from './RequestBuilder.js'
27
+
28
+ export interface TestResponse {
29
+ status: number
30
+ headers: Dict
31
+ body: string
32
+ json<T = unknown>(): T
33
+ }
34
+
35
+ export interface TestRequestBuilder {
36
+ /** Set a request header. */
37
+ header(name: string, value: string): TestRequestBuilder
38
+ /** Set the request body as JSON. */
39
+ json(data: unknown): TestRequestBuilder
40
+ /** Set a raw string body with optional content type. */
41
+ body(content: string, contentType?: string): TestRequestBuilder
42
+ /** Set a cookie. */
43
+ cookie(name: string, value: string): TestRequestBuilder
44
+ /** Send the request and return the response. */
45
+ send(): Promise<TestResponse>
46
+ }
47
+
48
+ export class TestClient {
49
+ #port = 0
50
+ #headers: Dict = {}
51
+ #bootFn: (port: number) => Promise<{ port: number; close: () => Promise<void> | void }>
52
+ #auth: AuthStrategy | null
53
+
54
+ constructor(
55
+ bootFn: (port: number) => Promise<{ port: number; close: () => Promise<void> | void }>,
56
+ options: { auth?: AuthStrategy } = {},
57
+ ) {
58
+ this.#bootFn = bootFn
59
+ this.#auth = options.auth ?? null
60
+ }
61
+
62
+ #server: { port: number; close: () => Promise<void> | void } | null = null
63
+
64
+ /** Boot the application on a random port. */
65
+ async boot(): Promise<void> {
66
+ this.#server = await this.#bootFn(0)
67
+ this.#port = this.#server.port
68
+ }
69
+
70
+ /** Close the server. */
71
+ async close(): Promise<void> {
72
+ if (this.#server) {
73
+ await this.#server.close()
74
+ this.#server = null
75
+ }
76
+ }
77
+
78
+ /**
79
+ * Ephemeral port the booted server bound to. Useful for long-lived
80
+ * connections (SSE / WebSocket) that the fluent buffered request
81
+ * surface can't model — open them with `fetch` against
82
+ * `http://127.0.0.1:${client.port}/...`. Returns 0 before `boot()`.
83
+ */
84
+ get port(): number {
85
+ return this.#port
86
+ }
87
+
88
+ /** Set a default header for all requests. */
89
+ withHeader(name: string, value: string): this {
90
+ this.#headers[name.toLowerCase()] = value
91
+ return this
92
+ }
93
+
94
+ /** GET request. */
95
+ get(path: string): Promise<TestResponse> {
96
+ return this.request('GET', path).send()
97
+ }
98
+
99
+ /** POST request. */
100
+ post(path: string): TestRequestBuilder {
101
+ return this.request('POST', path)
102
+ }
103
+
104
+ /** PUT request. */
105
+ put(path: string): TestRequestBuilder {
106
+ return this.request('PUT', path)
107
+ }
108
+
109
+ /** PATCH request. */
110
+ patch(path: string): TestRequestBuilder {
111
+ return this.request('PATCH', path)
112
+ }
113
+
114
+ /** DELETE request. */
115
+ delete(path: string): Promise<TestResponse> {
116
+ return this.request('DELETE', path).send()
117
+ }
118
+
119
+ /**
120
+ * Build a request with the rich fluent surface: chained assertions
121
+ * (`expectStatus` / `expectJson` / `expectHeader` / `expectCookie`), auth
122
+ * injection (`withAuth` / `asUser`, needs the `auth` client option), and
123
+ * `form()`. The lower-level `request()` (below) stays for raw control.
124
+ */
125
+ fluent(method: HttpMethod, path: string): RequestBuilder {
126
+ const sender: HttpSender = (m, p, init) =>
127
+ sendRequest(
128
+ this.#port,
129
+ m,
130
+ p,
131
+ { ...this.#headers, ...init.headers },
132
+ init.body.toString('utf8'),
133
+ )
134
+ return new RequestBuilder(sender, method, path, this.#auth)
135
+ }
136
+
137
+ /** Build a request with the low-level fluent API. */
138
+ request(method: string, path: string): TestRequestBuilder {
139
+ const headers: Dict = { ...this.#headers }
140
+ let bodyContent = ''
141
+ const cookies: string[] = []
142
+
143
+ const builder: TestRequestBuilder = {
144
+ header(name: string, value: string) {
145
+ headers[name.toLowerCase()] = value
146
+ return builder
147
+ },
148
+ json(data: unknown) {
149
+ bodyContent = JSON.stringify(data)
150
+ headers['content-type'] = 'application/json'
151
+ return builder
152
+ },
153
+ body(content: string, contentType?: string) {
154
+ bodyContent = content
155
+ if (contentType) headers['content-type'] = contentType
156
+ return builder
157
+ },
158
+ cookie(name: string, value: string) {
159
+ cookies.push(`${name}=${value}`)
160
+ return builder
161
+ },
162
+ send: async () => {
163
+ if (cookies.length > 0) {
164
+ headers.cookie = cookies.join('; ')
165
+ }
166
+ return sendRequest(this.#port, method, path, headers, bodyContent)
167
+ },
168
+ }
169
+
170
+ return builder
171
+ }
172
+ }
173
+
174
+ /** Create and boot a test client in one call. */
175
+ export async function createTestClient(
176
+ bootFn: (port: number) => Promise<{ port: number; close: () => Promise<void> | void }>,
177
+ options?: { auth?: AuthStrategy },
178
+ ): Promise<TestClient> {
179
+ const client = new TestClient(bootFn, options)
180
+ await client.boot()
181
+ return client
182
+ }
183
+
184
+ export {
185
+ type AuthStrategy,
186
+ type AuthSubject,
187
+ type HttpMethod,
188
+ type HttpSender,
189
+ partialMatch,
190
+ RequestBuilder,
191
+ } from './RequestBuilder.js'
192
+
193
+ /** Send an HTTP request using raw TCP (no external dependencies). */
194
+ async function sendRequest(
195
+ port: number,
196
+ method: string,
197
+ path: string,
198
+ headers: Dict,
199
+ body: string,
200
+ ): Promise<TestResponse> {
201
+ const { connect } = await import('node:net')
202
+
203
+ return new Promise((resolve, reject) => {
204
+ const socket = connect({ host: '127.0.0.1', port }, () => {
205
+ // Build HTTP/1.1 request
206
+ const headerLines = Object.entries(headers)
207
+ .map(([k, v]) => `${k}: ${v}`)
208
+ .join('\r\n')
209
+
210
+ const contentLength = Buffer.byteLength(body, 'utf8')
211
+
212
+ let request = `${method} ${path} HTTP/1.1\r\n`
213
+ request += `Host: localhost:${port}\r\n`
214
+ request += `Connection: close\r\n`
215
+ if (contentLength > 0) {
216
+ request += `Content-Length: ${contentLength}\r\n`
217
+ }
218
+ if (headerLines) {
219
+ request += `${headerLines}\r\n`
220
+ }
221
+ request += '\r\n'
222
+ request += body
223
+
224
+ socket.write(request)
225
+ })
226
+
227
+ const chunks: Buffer[] = []
228
+ socket.on('data', (chunk) => chunks.push(chunk))
229
+ socket.on('end', () => {
230
+ // Keep the wire bytes as a Buffer through parseHttpResponse — chunked
231
+ // decoding has to count bytes (per RFC 7230), not UTF-16 code units.
232
+ // Converting to a string here used to truncate non-ASCII and binary
233
+ // chunked responses (é / emoji / images / PDF).
234
+ resolve(parseHttpResponse(Buffer.concat(chunks)))
235
+ })
236
+ socket.on('error', reject)
237
+
238
+ // Socket inactivity timeout. 5s was too tight for real e2e flows —
239
+ // signup hashes a password with argon2 (~50-200ms), then an INSERT
240
+ // crosses sqlite's WAL fsync, then JWT sign + serialize. Under
241
+ // cross-package CPU pressure (parallel helix workers, NAPI
242
+ // tokio runtimes) the cumulative latency exceeded 5s ~30% of runs
243
+ // and surfaced as flaky `bob accepts` / `non-member 403` failures
244
+ // in kitchen-sink's workspace.test.ts. 30s matches the helix
245
+ // `--timeout=60000` per-test budget without masking real hangs.
246
+ socket.setTimeout(30_000, () => {
247
+ socket.destroy()
248
+ reject(new Error('TestClient request timed out'))
249
+ })
250
+ })
251
+ }
252
+
253
+ /**
254
+ * Parse a raw HTTP response. Accepts the wire bytes (Buffer) or a UTF-8
255
+ * string for back-compat with older test callers. Chunked decoding runs on
256
+ * the Buffer to keep byte-counting honest — HTTP counts bytes (RFC 7230),
257
+ * not UTF-16 code units, so doing the work on a `.toString('utf8')` string
258
+ * silently truncated non-ASCII and binary responses.
259
+ */
260
+ export function parseHttpResponse(raw: string | Buffer): TestResponse {
261
+ const buf = typeof raw === 'string' ? Buffer.from(raw, 'utf8') : raw
262
+ const HEADER_TERM = Buffer.from('\r\n\r\n', 'ascii')
263
+ const headerEnd = buf.indexOf(HEADER_TERM)
264
+ if (headerEnd === -1) {
265
+ const body = buf.toString('utf8')
266
+ return { status: 0, headers: {}, body, json: () => JSON.parse(body) }
267
+ }
268
+
269
+ // HTTP headers are ASCII (RFC 7230 §3.2); decoding via 'ascii' is safe and
270
+ // makes any non-ASCII byte show as a question-mark, surfacing protocol bugs
271
+ // instead of silently smuggling them.
272
+ const headerSection = buf.subarray(0, headerEnd).toString('ascii')
273
+ let bodyBuffer = buf.subarray(headerEnd + HEADER_TERM.length)
274
+
275
+ const lines = headerSection.split('\r\n')
276
+ const statusLine = lines[0] ?? ''
277
+ const statusMatch = statusLine.match(/HTTP\/\d+\.\d+ (\d+)/)
278
+ const status = statusMatch ? Number.parseInt(statusMatch[1], 10) : 0
279
+
280
+ const headers: Dict = {}
281
+ for (let i = 1; i < lines.length; i++) {
282
+ const colonIdx = lines[i].indexOf(':')
283
+ if (colonIdx === -1) continue
284
+ const key = lines[i].slice(0, colonIdx).trim().toLowerCase()
285
+ const value = lines[i].slice(colonIdx + 1).trim()
286
+ headers[key] = value
287
+ }
288
+
289
+ // RFC 7230 §4.1: when Transfer-Encoding includes `chunked`, the message body
290
+ // is a sequence of `<size-hex>[;ext]\r\n<bytes>\r\n` chunks terminated by
291
+ // `0\r\n\r\n`. Decode on the Buffer so chunk sizes (bytes) match slice
292
+ // boundaries — character-counting corrupts é / emoji / binary payloads.
293
+ const transferEncoding = headers['transfer-encoding'] ?? ''
294
+ if (
295
+ transferEncoding
296
+ .toLowerCase()
297
+ .split(',')
298
+ .map((s) => s.trim())
299
+ .includes('chunked')
300
+ ) {
301
+ bodyBuffer = decodeChunked(bodyBuffer)
302
+ delete headers['transfer-encoding']
303
+ }
304
+
305
+ const body = bodyBuffer.toString('utf8')
306
+ return {
307
+ status,
308
+ headers,
309
+ body,
310
+ json<T = unknown>(): T {
311
+ return JSON.parse(body)
312
+ },
313
+ }
314
+ }
315
+
316
+ /**
317
+ * Decode an HTTP/1.1 chunked-transfer body. Operates on a Buffer because
318
+ * chunk sizes are byte counts per RFC 7230 — character-counting silently
319
+ * corrupts non-ASCII / binary responses. Tolerant: stops at the first
320
+ * malformed chunk header rather than throwing — surfacing a partial body is
321
+ * more useful for a failing test assertion than swallowing the whole
322
+ * response.
323
+ */
324
+ function decodeChunked(raw: Buffer): Buffer {
325
+ const CRLF = Buffer.from('\r\n', 'ascii')
326
+ const out: Buffer[] = []
327
+ let i = 0
328
+ while (i < raw.length) {
329
+ const lineEnd = raw.indexOf(CRLF, i)
330
+ if (lineEnd === -1) break
331
+ const sizeStr = raw.subarray(i, lineEnd).toString('ascii').split(';')[0]?.trim() ?? ''
332
+ const size = Number.parseInt(sizeStr, 16)
333
+ if (!Number.isFinite(size) || size < 0) break
334
+ i = lineEnd + CRLF.length
335
+ if (size === 0) break
336
+ if (i + size > raw.length) {
337
+ out.push(raw.subarray(i))
338
+ break
339
+ }
340
+ out.push(raw.subarray(i, i + size))
341
+ i += size + CRLF.length // chunk + trailing \r\n
342
+ }
343
+ return Buffer.concat(out)
344
+ }
@@ -0,0 +1,52 @@
1
+ /**
2
+ * Type helpers — utility types for the Ream framework.
3
+ */
4
+
5
+ /**
6
+ * Infer route parameters from a route pattern string.
7
+ *
8
+ * Usage:
9
+ * type Params = InferRouteParams<'/users/:id/posts/:postId'>
10
+ * // => { id: string; postId: string }
11
+ */
12
+ export type InferRouteParams<T extends string> = T extends `${string}:${infer Param}/${infer Rest}`
13
+ ? { [K in Param | keyof InferRouteParams<`/${Rest}`>]: string }
14
+ : T extends `${string}:${infer Param}`
15
+ ? { [K in Param]: string }
16
+ : Record<string, never>
17
+
18
+ /** Accept a single value or an array. */
19
+ export type OneOrMore<T> = T | T[]
20
+
21
+ /** Create a nominal/opaque type from a base type. */
22
+ export type Opaque<Base, Tag extends string> = Base & { readonly __tag: Tag }
23
+
24
+ /** Unwrap an opaque type back to its base. */
25
+ export type UnwrapOpaque<T> = T extends Opaque<infer Base, string> ? Base : T
26
+
27
+ /** Accept sync or async values. */
28
+ export type AsyncOrSync<T> = T | Promise<T>
29
+
30
+ /** Constructor type. */
31
+ export type Constructor<T = unknown, Args extends unknown[] = unknown[]> = new (...args: Args) => T
32
+
33
+ /** Abstract constructor type. */
34
+ export type AbstractConstructor<T = unknown> = abstract new (...args: unknown[]) => T
35
+
36
+ /** Extract only function properties from a type. */
37
+ export type ExtractFunctions<T, Ignore extends keyof T = never> = {
38
+ [K in Exclude<keyof T, Ignore>]: T[K] extends (...args: unknown[]) => unknown ? K : never
39
+ }[Exclude<keyof T, Ignore>]
40
+
41
+ /** Prettify a type intersection for better IDE display. */
42
+ export type Prettify<T> = { [K in keyof T]: T[K] } & {}
43
+
44
+ /**
45
+ * String-keyed bag of values — the recurring HTTP-shaped objects:
46
+ * headers, cookies, query params, route params, env vars, form fields...
47
+ *
48
+ * Defaults to a string→string map (the most common case, and what
49
+ * `Record<string, string>` was being repeated for). Pass a value type
50
+ * explicitly when needed: `Dict<unknown>`, `Dict<number>`, ...
51
+ */
52
+ export type Dict<V = string> = Record<string, V>
package/swcrc.app.json ADDED
@@ -0,0 +1,17 @@
1
+ {
2
+ "$schema": "https://json.schemastore.org/swcrc",
3
+ "jsc": {
4
+ "parser": {
5
+ "syntax": "typescript",
6
+ "decorators": true
7
+ },
8
+ "transform": {
9
+ "legacyDecorator": true,
10
+ "decoratorMetadata": true
11
+ },
12
+ "target": "es2022"
13
+ },
14
+ "module": {
15
+ "type": "es6"
16
+ }
17
+ }