@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,29 @@
1
+ import type { SessionDriver } from '../Session.js'
2
+
3
+ export class MemoryDriver implements SessionDriver {
4
+ private store: Map<string, { data: Record<string, unknown>; expiresAt: number }> = new Map()
5
+
6
+ async read(sessionId: string): Promise<Record<string, unknown>> {
7
+ const entry = this.store.get(sessionId)
8
+ if (!entry || entry.expiresAt < Date.now()) {
9
+ this.store.delete(sessionId)
10
+ return {}
11
+ }
12
+ return { ...entry.data }
13
+ }
14
+
15
+ async write(sessionId: string, data: Record<string, unknown>, ttl: number): Promise<void> {
16
+ this.store.set(sessionId, { data, expiresAt: Date.now() + ttl * 1000 })
17
+ }
18
+
19
+ async destroy(sessionId: string): Promise<void> {
20
+ this.store.delete(sessionId)
21
+ }
22
+
23
+ async touch(sessionId: string, ttl: number): Promise<void> {
24
+ const entry = this.store.get(sessionId)
25
+ if (entry) {
26
+ entry.expiresAt = Date.now() + ttl * 1000
27
+ }
28
+ }
29
+ }
@@ -0,0 +1,171 @@
1
+ /**
2
+ * S3Driver — S3-compatible cloud storage (AWS S3, Cloudflare R2, MinIO).
3
+ *
4
+ * Uses fetch API — no external SDK dependency.
5
+ * Implements the StorageDriver interface.
6
+ *
7
+ * @implements MISS-24
8
+ */
9
+
10
+ import { createHash, createHmac } from 'node:crypto'
11
+ import type { StorageDriver } from './StorageManager.js'
12
+
13
+ /**
14
+ * URI-encode an S3 object key for use in BOTH the request URL and the
15
+ * SigV4 canonical path. Each `/`-delimited segment is percent-encoded
16
+ * (so the slashes that delimit the key hierarchy survive), which means
17
+ * spaces, `#`, `?`, `%` and non-ASCII bytes can't slip the requested
18
+ * path away from the signed path — the classic "key with a space gets
19
+ * a SignatureDoesNotMatch / 403, or `?`/`#` truncates the path" bug.
20
+ *
21
+ * S3 expects the canonical URI encoded ONCE (unlike the generic SigV4
22
+ * double-encode), and `encodeURIComponent` already escapes the RFC 3986
23
+ * sub-delims AWS cares about. We additionally escape `!`, `'`, `(`,
24
+ * `)`, `*` which `encodeURIComponent` leaves untouched but AWS expects
25
+ * percent-encoded.
26
+ */
27
+ function encodeS3Key(key: string): string {
28
+ return key
29
+ .split('/')
30
+ .map((segment) =>
31
+ encodeURIComponent(segment).replace(
32
+ /[!'()*]/g,
33
+ (c) => `%${c.charCodeAt(0).toString(16).toUpperCase()}`,
34
+ ),
35
+ )
36
+ .join('/')
37
+ }
38
+
39
+ export interface S3Config {
40
+ bucket: string
41
+ region?: string
42
+ endpoint?: string
43
+ accessKeyId: string
44
+ secretAccessKey: string
45
+ publicUrl?: string
46
+ }
47
+
48
+ export class S3Driver implements StorageDriver {
49
+ #config: S3Config
50
+ #endpoint: string
51
+
52
+ constructor(config: S3Config) {
53
+ this.#config = config
54
+ this.#endpoint = config.endpoint ?? `https://s3.${config.region ?? 'us-east-1'}.amazonaws.com`
55
+ }
56
+
57
+ async put(filePath: string, content: Buffer | string): Promise<void> {
58
+ const body = typeof content === 'string' ? Buffer.from(content) : content
59
+ const key = encodeS3Key(filePath)
60
+ const url = `${this.#endpoint}/${this.#config.bucket}/${key}`
61
+ const headers = this.#signRequest('PUT', key, body)
62
+
63
+ const res = await fetch(url, { method: 'PUT', headers, body: body as unknown as BodyInit })
64
+ if (!res.ok) {
65
+ throw new Error(`S3 PUT failed (${res.status}): ${await res.text()}`)
66
+ }
67
+ }
68
+
69
+ async get(filePath: string): Promise<Buffer | null> {
70
+ const key = encodeS3Key(filePath)
71
+ const url = `${this.#endpoint}/${this.#config.bucket}/${key}`
72
+ const headers = this.#signRequest('GET', key)
73
+
74
+ const res = await fetch(url, { method: 'GET', headers })
75
+ if (res.status === 404) return null
76
+ if (!res.ok) throw new Error(`S3 GET failed (${res.status})`)
77
+
78
+ const arrayBuffer = await res.arrayBuffer()
79
+ return Buffer.from(arrayBuffer)
80
+ }
81
+
82
+ async delete(filePath: string): Promise<boolean> {
83
+ const key = encodeS3Key(filePath)
84
+ const url = `${this.#endpoint}/${this.#config.bucket}/${key}`
85
+ const headers = this.#signRequest('DELETE', key)
86
+
87
+ const res = await fetch(url, { method: 'DELETE', headers })
88
+ return res.ok || res.status === 204
89
+ }
90
+
91
+ async exists(filePath: string): Promise<boolean> {
92
+ const key = encodeS3Key(filePath)
93
+ const url = `${this.#endpoint}/${this.#config.bucket}/${key}`
94
+ const headers = this.#signRequest('HEAD', key)
95
+
96
+ const res = await fetch(url, { method: 'HEAD', headers })
97
+ return res.ok
98
+ }
99
+
100
+ url(filePath: string): string {
101
+ const key = encodeS3Key(filePath)
102
+ if (this.#config.publicUrl) {
103
+ return `${this.#config.publicUrl}/${key}`
104
+ }
105
+ return `${this.#endpoint}/${this.#config.bucket}/${key}`
106
+ }
107
+
108
+ /**
109
+ * Sign a request with AWS Signature V4 (simplified).
110
+ * For production, consider using @aws-sdk/signature-v4 for full compliance.
111
+ */
112
+ #signRequest(method: string, key: string, body?: Buffer): Record<string, string> {
113
+ const now = new Date()
114
+ const dateStamp = `${now.toISOString().replace(/[-:]/g, '').split('.')[0]}Z`
115
+ const shortDate = dateStamp.slice(0, 8)
116
+ const region = this.#config.region ?? 'us-east-1'
117
+ const service = 's3'
118
+
119
+ const payloadHash = createHash('sha256')
120
+ .update(body ?? '')
121
+ .digest('hex')
122
+
123
+ const headers: Record<string, string> = {
124
+ host: new URL(this.#endpoint).host,
125
+ 'x-amz-date': dateStamp,
126
+ 'x-amz-content-sha256': payloadHash,
127
+ }
128
+
129
+ if (body) {
130
+ headers['content-length'] = String(body.length)
131
+ }
132
+
133
+ // Canonical request
134
+ const signedHeaders = Object.keys(headers).sort().join(';')
135
+ const canonicalHeaders = Object.keys(headers)
136
+ .sort()
137
+ .map((k) => `${k}:${headers[k]}\n`)
138
+ .join('')
139
+ const canonicalRequest = [
140
+ method,
141
+ `/${this.#config.bucket}/${key}`,
142
+ '',
143
+ canonicalHeaders,
144
+ signedHeaders,
145
+ payloadHash,
146
+ ].join('\n')
147
+
148
+ // String to sign
149
+ const scope = `${shortDate}/${region}/${service}/aws4_request`
150
+ const stringToSign = [
151
+ 'AWS4-HMAC-SHA256',
152
+ dateStamp,
153
+ scope,
154
+ createHash('sha256').update(canonicalRequest).digest('hex'),
155
+ ].join('\n')
156
+
157
+ // Signing key
158
+ const kDate = createHmac('sha256', `AWS4${this.#config.secretAccessKey}`)
159
+ .update(shortDate)
160
+ .digest()
161
+ const kRegion = createHmac('sha256', kDate).update(region).digest()
162
+ const kService = createHmac('sha256', kRegion).update(service).digest()
163
+ const kSigning = createHmac('sha256', kService).update('aws4_request').digest()
164
+
165
+ const signature = createHmac('sha256', kSigning).update(stringToSign).digest('hex')
166
+
167
+ headers.authorization = `AWS4-HMAC-SHA256 Credential=${this.#config.accessKeyId}/${scope}, SignedHeaders=${signedHeaders}, Signature=${signature}`
168
+
169
+ return headers
170
+ }
171
+ }
@@ -0,0 +1,182 @@
1
+ /**
2
+ * Static file serving middleware.
3
+ *
4
+ * Serves files from a directory with cache headers and ETag support.
5
+ *
6
+ * Usage:
7
+ * server.use(new StaticMiddleware({ root: 'public' }))
8
+ *
9
+ * @implements MISS-25
10
+ */
11
+
12
+ import { createHash } from 'node:crypto'
13
+ import * as fs from 'node:fs'
14
+ import * as path from 'node:path'
15
+ import type { HttpContext } from '../http/HttpContext.js'
16
+
17
+ export interface StaticConfig {
18
+ /** Root directory to serve files from. */
19
+ root: string
20
+ /** URL prefix (default: /static). */
21
+ prefix?: string
22
+ /** Max-age for Cache-Control header in seconds (default: 86400 = 1 day). */
23
+ maxAge?: number
24
+ /** File extensions to serve (default: common web assets). */
25
+ extensions?: string[]
26
+ }
27
+
28
+ const DEFAULT_EXTENSIONS = [
29
+ '.html',
30
+ '.css',
31
+ '.js',
32
+ '.json',
33
+ '.xml',
34
+ '.txt',
35
+ '.png',
36
+ '.jpg',
37
+ '.jpeg',
38
+ '.gif',
39
+ '.svg',
40
+ '.ico',
41
+ '.webp',
42
+ '.woff',
43
+ '.woff2',
44
+ '.ttf',
45
+ '.eot',
46
+ '.mp4',
47
+ '.webm',
48
+ '.mp3',
49
+ '.ogg',
50
+ '.pdf',
51
+ '.zip',
52
+ ]
53
+
54
+ const MIME_TYPES: Record<string, string> = {
55
+ '.html': 'text/html',
56
+ '.css': 'text/css',
57
+ '.js': 'application/javascript',
58
+ '.json': 'application/json',
59
+ '.xml': 'application/xml',
60
+ '.txt': 'text/plain',
61
+ '.png': 'image/png',
62
+ '.jpg': 'image/jpeg',
63
+ '.jpeg': 'image/jpeg',
64
+ '.gif': 'image/gif',
65
+ '.svg': 'image/svg+xml',
66
+ '.ico': 'image/x-icon',
67
+ '.webp': 'image/webp',
68
+ '.woff': 'font/woff',
69
+ '.woff2': 'font/woff2',
70
+ '.ttf': 'font/ttf',
71
+ '.eot': 'application/vnd.ms-fontobject',
72
+ '.mp4': 'video/mp4',
73
+ '.webm': 'video/webm',
74
+ '.mp3': 'audio/mpeg',
75
+ '.ogg': 'audio/ogg',
76
+ '.pdf': 'application/pdf',
77
+ '.zip': 'application/zip',
78
+ }
79
+
80
+ export class StaticMiddleware {
81
+ #root: string
82
+ #prefix: string
83
+ #maxAge: number
84
+ #extensions: Set<string>
85
+
86
+ constructor(config: StaticConfig) {
87
+ this.#root = path.resolve(config.root)
88
+ this.#prefix = config.prefix ?? '/static'
89
+ this.#maxAge = config.maxAge ?? 86400
90
+ this.#extensions = new Set(config.extensions ?? DEFAULT_EXTENSIONS)
91
+ }
92
+
93
+ async handle(ctx: HttpContext, next: () => Promise<void>): Promise<void> {
94
+ if (ctx.request.method() !== 'GET' && ctx.request.method() !== 'HEAD') {
95
+ return next()
96
+ }
97
+
98
+ const resolved = this.#resolveServableFile(ctx.request.path())
99
+ if (!resolved) return next()
100
+ const { realFilePath, ext, stat } = resolved
101
+
102
+ const etag = createHash('md5').update(`${stat.size}-${stat.mtimeMs}`).digest('hex')
103
+
104
+ // ETag check — 304 Not Modified
105
+ const ifNoneMatch = ctx.request.header('if-none-match')
106
+ if (ifNoneMatch === etag) {
107
+ ctx.response.status(304)
108
+ return
109
+ }
110
+
111
+ const mime = MIME_TYPES[ext] ?? 'application/octet-stream'
112
+ ctx.response.header('Content-Type', mime)
113
+ ctx.response.header('Content-Length', String(stat.size))
114
+ ctx.response.header('Cache-Control', `public, max-age=${this.#maxAge}`)
115
+ ctx.response.header('ETag', etag)
116
+
117
+ if (ctx.request.method() === 'HEAD') {
118
+ ctx.response.status(200)
119
+ return
120
+ }
121
+
122
+ // Send the raw Buffer — `content.toString()` would interpret PNG /
123
+ // PDF / ZIP bytes as UTF-8 and replace any byte the encoding can't
124
+ // round-trip (anything in the surrogate range, plus invalid
125
+ // sequences) with U+FFFD.
126
+ const content = fs.readFileSync(realFilePath)
127
+ ctx.response.status(200).sendBuffer(content)
128
+ }
129
+
130
+ /**
131
+ * Resolve a request path to a real, in-root, existing file, or `null` when it
132
+ * is not servable (wrong prefix/extension, outside the root, missing, or a
133
+ * symlink escaping the root). Returning `null` tells `handle` to call `next()`.
134
+ */
135
+ #resolveServableFile(
136
+ reqPath: string,
137
+ ): { realFilePath: string; ext: string; stat: fs.Stats } | null {
138
+ // Match only when the prefix is followed by `/` or end-of-path —
139
+ // otherwise `/staticx/foo.js` would be intercepted when the configured
140
+ // prefix is `/static`, hijacking unrelated routes.
141
+ if (!(reqPath === this.#prefix || reqPath.startsWith(`${this.#prefix}/`))) {
142
+ return null
143
+ }
144
+
145
+ // Strip prefix and resolve file path
146
+ const relativePath = reqPath.slice(this.#prefix.length) || '/index.html'
147
+ const ext = path.extname(relativePath).toLowerCase()
148
+ if (!this.#extensions.has(ext)) return null
149
+
150
+ const filePath = path.resolve(this.#root, relativePath.replace(/^\//, ''))
151
+
152
+ // Two containment checks. The first uses `path.sep` to reject sibling
153
+ // directories with a shared prefix (`/var/www-static-secret` slipping
154
+ // past a guard for `/var/www-static`). The second resolves symlinks
155
+ // via realpathSync so a symlink planted inside the root cannot point
156
+ // outside it. We do realpath AFTER the lexical check so a non-existent
157
+ // path returns `next()` rather than throwing.
158
+ const rootWithSep = this.#root.endsWith(path.sep) ? this.#root : this.#root + path.sep
159
+ if (filePath !== this.#root && !filePath.startsWith(rootWithSep)) {
160
+ return null
161
+ }
162
+
163
+ if (!fs.existsSync(filePath) || !fs.statSync(filePath).isFile()) {
164
+ return null
165
+ }
166
+
167
+ let realFilePath: string
168
+ let realRoot: string
169
+ try {
170
+ realFilePath = fs.realpathSync(filePath)
171
+ realRoot = fs.realpathSync(this.#root)
172
+ } catch {
173
+ return null
174
+ }
175
+ const realRootWithSep = realRoot.endsWith(path.sep) ? realRoot : realRoot + path.sep
176
+ if (realFilePath !== realRoot && !realFilePath.startsWith(realRootWithSep)) {
177
+ return null
178
+ }
179
+
180
+ return { realFilePath, ext, stat: fs.statSync(realFilePath) }
181
+ }
182
+ }
@@ -0,0 +1,40 @@
1
+ /**
2
+ * `StaticProvider` — serves static files from a directory.
3
+ *
4
+ * Opt-in: does nothing unless `config.static.root` is set (or a middleware is
5
+ * injected for tests). When configured, `boot()` mounts a global
6
+ * `StaticMiddleware` (path-traversal + symlink-escape guarded) that serves
7
+ * matching requests and falls through (`next()`) otherwise.
8
+ *
9
+ * // config/static.ts
10
+ * export default { root: 'public', prefix: '/static' }
11
+ *
12
+ * @implements MISS-25
13
+ */
14
+
15
+ import type { AppContext } from '../Provider.js'
16
+ import { Provider } from '../Provider.js'
17
+ import type { Server } from '../server/Server.js'
18
+ import { type StaticConfig, StaticMiddleware } from './StaticMiddleware.js'
19
+
20
+ export interface StaticProviderOptions {
21
+ /** Override the `StaticMiddleware` instance — used by tests. */
22
+ middleware?: StaticMiddleware
23
+ }
24
+
25
+ export class StaticProvider extends Provider {
26
+ readonly #override?: StaticMiddleware
27
+
28
+ constructor(app: AppContext, options: StaticProviderOptions = {}) {
29
+ super(app)
30
+ this.#override = options.middleware
31
+ }
32
+
33
+ override async boot(): Promise<void> {
34
+ const config = this.app.config.get<StaticConfig>('static')
35
+ const middleware = this.#override ?? (config?.root ? new StaticMiddleware(config) : undefined)
36
+ if (!middleware) return // not configured — opt-out
37
+ const server = this.app.container.make<Server>('server')
38
+ server.use([(ctx, next) => middleware.handle(ctx, next)])
39
+ }
40
+ }
@@ -0,0 +1,142 @@
1
+ /**
2
+ * Storage — file storage abstraction with driver pattern.
3
+ *
4
+ * @implements MISS-24
5
+ */
6
+
7
+ import * as fs from 'node:fs'
8
+ import * as path from 'node:path'
9
+
10
+ /**
11
+ * Resolve `filePath` against `root` and reject any result that escapes
12
+ * the storage root. Without this guard a user-controlled path like
13
+ * `../secrets.env` would let put/get/delete/exists touch arbitrary
14
+ * files outside the configured storage root.
15
+ *
16
+ * The lexical check (`path.resolve` + `startsWith`) catches the
17
+ * `../` / absolute-path class. The realpath re-check catches the
18
+ * symlink-escape class: a symlink planted under the root (or anywhere
19
+ * else on the resolved path's chain) that points outside the root
20
+ * would pass the lexical gate but be caught here.
21
+ *
22
+ * We canonicalize the longest existing ancestor of `full` — `realpath`
23
+ * itself throws ENOENT on a target that doesn't exist yet (the `put`
24
+ * happy-path for new files). Symlinks anywhere ABOVE the leaf are
25
+ * still dereferenced, which is the only escape vector that matters:
26
+ * the driver creates the leaf itself, so no symlink can pre-exist
27
+ * there.
28
+ */
29
+ function assertWithinRoot(root: string, filePath: string): string {
30
+ const full = path.resolve(root, filePath)
31
+ const rootAbs = path.resolve(root)
32
+ if (full !== rootAbs && !full.startsWith(rootAbs + path.sep)) {
33
+ throw new Error(`[ream-storage] path '${filePath}' escapes the storage root (E_PATH_TRAVERSAL)`)
34
+ }
35
+ const canonicalRoot = fs.existsSync(rootAbs) ? fs.realpathSync(rootAbs) : rootAbs
36
+ const ancestor = longestExistingAncestor(full)
37
+ const canonicalAncestor = ancestor === full ? fs.realpathSync(full) : fs.realpathSync(ancestor)
38
+ // Re-attach the not-yet-existing leaf to the canonicalized ancestor.
39
+ const canonicalFull = canonicalAncestor + full.slice(ancestor.length)
40
+ if (canonicalFull !== canonicalRoot && !canonicalFull.startsWith(canonicalRoot + path.sep)) {
41
+ throw new Error(
42
+ `[ream-storage] path '${filePath}' escapes the storage root via a symlink (E_PATH_TRAVERSAL)`,
43
+ )
44
+ }
45
+ return full
46
+ }
47
+
48
+ /** Walk up `full` until we find a path that exists on disk. */
49
+ function longestExistingAncestor(full: string): string {
50
+ let current = full
51
+ while (!fs.existsSync(current)) {
52
+ const parent = path.dirname(current)
53
+ if (parent === current) return current
54
+ current = parent
55
+ }
56
+ return current
57
+ }
58
+
59
+ export interface StorageDriver {
60
+ put(filePath: string, content: Buffer | string): Promise<void>
61
+ get(filePath: string): Promise<Buffer | null>
62
+ delete(filePath: string): Promise<boolean>
63
+ exists(filePath: string): Promise<boolean>
64
+ url(filePath: string): string
65
+ }
66
+
67
+ export class LocalDriver implements StorageDriver {
68
+ #root: string
69
+
70
+ constructor(root: string) {
71
+ this.#root = root
72
+ if (!fs.existsSync(root)) {
73
+ fs.mkdirSync(root, { recursive: true })
74
+ }
75
+ }
76
+
77
+ async put(filePath: string, content: Buffer | string): Promise<void> {
78
+ const full = assertWithinRoot(this.#root, filePath)
79
+ const dir = path.dirname(full)
80
+ if (!fs.existsSync(dir)) {
81
+ fs.mkdirSync(dir, { recursive: true })
82
+ }
83
+ fs.writeFileSync(full, content)
84
+ }
85
+
86
+ async get(filePath: string): Promise<Buffer | null> {
87
+ const full = assertWithinRoot(this.#root, filePath)
88
+ if (!fs.existsSync(full)) return null
89
+ return fs.readFileSync(full)
90
+ }
91
+
92
+ async delete(filePath: string): Promise<boolean> {
93
+ const full = assertWithinRoot(this.#root, filePath)
94
+ if (!fs.existsSync(full)) return false
95
+ fs.unlinkSync(full)
96
+ return true
97
+ }
98
+
99
+ async exists(filePath: string): Promise<boolean> {
100
+ let full: string
101
+ try {
102
+ full = assertWithinRoot(this.#root, filePath)
103
+ } catch {
104
+ // exists() must NOT leak existence of out-of-root paths via the
105
+ // thrown error — quietly report "no" for any traversal attempt.
106
+ return false
107
+ }
108
+ return fs.existsSync(full)
109
+ }
110
+
111
+ url(filePath: string): string {
112
+ return `/storage/${filePath}`
113
+ }
114
+ }
115
+
116
+ export class StorageManager {
117
+ #driver: StorageDriver
118
+
119
+ constructor(driver: StorageDriver) {
120
+ this.#driver = driver
121
+ }
122
+
123
+ put(filePath: string, content: Buffer | string): Promise<void> {
124
+ return this.#driver.put(filePath, content)
125
+ }
126
+
127
+ get(filePath: string): Promise<Buffer | null> {
128
+ return this.#driver.get(filePath)
129
+ }
130
+
131
+ delete(filePath: string): Promise<boolean> {
132
+ return this.#driver.delete(filePath)
133
+ }
134
+
135
+ exists(filePath: string): Promise<boolean> {
136
+ return this.#driver.exists(filePath)
137
+ }
138
+
139
+ url(filePath: string): string {
140
+ return this.#driver.url(filePath)
141
+ }
142
+ }