@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,54 @@
1
+ /**
2
+ * `RpcProvider` — wires a JSON-RPC 2.0 endpoint into the Ream pipeline.
3
+ *
4
+ * - `register()` — bind a shared `RpcRouter` (with the IoC container, so
5
+ * `namespace()` controllers are DI-resolved) under the token `'rpc'`. Apps
6
+ * register methods by resolving it:
7
+ *
8
+ * const rpc = app.container.make<RpcRouter>('rpc')
9
+ * rpc.method('task.validate', handler).guard('jwt')
10
+ * rpc.namespace('user', UserController)
11
+ *
12
+ * - `boot()` — mount `POST <config.rpc.path ?? '/rpc'>` on the core router,
13
+ * dispatching every request body (single or batch) to `RpcRouter.handle()`.
14
+ *
15
+ * @implements Story 24.1, 24.2
16
+ */
17
+
18
+ import type { HttpContext } from '../http/HttpContext.js'
19
+ import type { AppContext } from '../Provider.js'
20
+ import { Provider } from '../Provider.js'
21
+ import type { Router } from '../router/Router.js'
22
+ import { RpcRouter } from './RpcRouter.js'
23
+
24
+ export interface RpcProviderOptions {
25
+ /** Override the `RpcRouter` instance — used by tests. */
26
+ router?: RpcRouter
27
+ }
28
+
29
+ export interface RpcConfig {
30
+ /** HTTP path the JSON-RPC endpoint is mounted at. Default `/rpc`. */
31
+ path?: string
32
+ }
33
+
34
+ export class RpcProvider extends Provider {
35
+ readonly rpc: RpcRouter
36
+
37
+ constructor(app: AppContext, options: RpcProviderOptions = {}) {
38
+ super(app)
39
+ this.rpc = options.router ?? new RpcRouter()
40
+ }
41
+
42
+ override register(): void {
43
+ this.rpc.useContainer(this.app.container)
44
+ this.app.container.singleton('rpc', () => this.rpc)
45
+ }
46
+
47
+ override async boot(): Promise<void> {
48
+ const path = this.app.config.get<RpcConfig>('rpc')?.path ?? '/rpc'
49
+ const router = this.app.container.make<Router>('router')
50
+ // RpcRouter.handle() reads the registered methods at request time, so the
51
+ // mount can happen before apps finish registering methods.
52
+ router.post(path, (ctx: HttpContext) => this.rpc.handle(ctx))
53
+ }
54
+ }
@@ -0,0 +1,243 @@
1
+ /**
2
+ * JSON-RPC 2.0 Router — dispatches RPC method calls to handlers.
3
+ *
4
+ * Same middleware/guard/validate pipeline as REST routes.
5
+ *
6
+ * Usage:
7
+ * rpc.method('task.validate', handler).guard('jwt').role('cs_member')
8
+ * rpc.namespace('task', TaskController) // every public method, DI-resolved per call
9
+ * rpc.group({ guard: 'jwt' }, (r) => { r.method('user.find', handler) })
10
+ *
11
+ * @implements MISS-27
12
+ */
13
+
14
+ import type { HttpContext } from '../http/HttpContext.js'
15
+
16
+ export type RpcHandler = (ctx: HttpContext, params: unknown) => Promise<unknown> | unknown
17
+
18
+ export interface RpcMethodDefinition {
19
+ name: string
20
+ handler: RpcHandler
21
+ guards: string[]
22
+ roles: string[]
23
+ permissions: string[]
24
+ middleware: string[]
25
+ validator?: string
26
+ }
27
+
28
+ export class RpcMethodBuilder {
29
+ #def: RpcMethodDefinition
30
+
31
+ constructor(def: RpcMethodDefinition) {
32
+ this.#def = def
33
+ }
34
+
35
+ guard(...guards: string[]): this {
36
+ this.#def.guards.push(...guards)
37
+ return this
38
+ }
39
+
40
+ role(...roles: string[]): this {
41
+ this.#def.roles.push(...roles)
42
+ return this
43
+ }
44
+
45
+ permission(...permissions: string[]): this {
46
+ this.#def.permissions.push(...permissions)
47
+ return this
48
+ }
49
+
50
+ validate(validator: string): this {
51
+ this.#def.validator = validator
52
+ return this
53
+ }
54
+
55
+ middleware(...names: string[]): this {
56
+ this.#def.middleware.push(...names)
57
+ return this
58
+ }
59
+ }
60
+
61
+ interface RpcErrorResponse {
62
+ jsonrpc: '2.0'
63
+ error: { code: number; message: string }
64
+ id: unknown
65
+ }
66
+
67
+ /** Build a JSON-RPC 2.0 error envelope. */
68
+ function rpcError(code: number, message: string, id: unknown): RpcErrorResponse {
69
+ return { jsonrpc: '2.0', error: { code, message }, id }
70
+ }
71
+
72
+ type ParsedRpcRequest =
73
+ | { ok: true; method: string; params: unknown; id: unknown }
74
+ | { ok: false; response: RpcErrorResponse }
75
+
76
+ /** Validate the JSON-RPC envelope and extract method/params/id. */
77
+ function parseRpcRequest(request: unknown): ParsedRpcRequest {
78
+ if (!request || typeof request !== 'object') {
79
+ return { ok: false, response: rpcError(-32600, 'Invalid Request', null) }
80
+ }
81
+ // request is narrowed to a non-null object by the typeof check above.
82
+ const jsonrpc =
83
+ 'jsonrpc' in request && typeof request.jsonrpc === 'string' ? request.jsonrpc : undefined
84
+ const method =
85
+ 'method' in request && typeof request.method === 'string' ? request.method : undefined
86
+ const params = 'params' in request ? request.params : undefined
87
+ const id = 'id' in request ? request.id : undefined
88
+ if (jsonrpc !== '2.0' || !method) {
89
+ return { ok: false, response: rpcError(-32600, 'Invalid Request', id ?? null) }
90
+ }
91
+ return { ok: true, method, params, id }
92
+ }
93
+
94
+ /**
95
+ * Enforce the method's guards/roles/permissions against the request auth state.
96
+ * Returns the JSON-RPC error code+message to reject with, or undefined when the
97
+ * caller is authorized.
98
+ */
99
+ function checkRpcAuthorization(
100
+ ctx: HttpContext,
101
+ def: RpcMethodDefinition,
102
+ ): { code: number; message: string } | undefined {
103
+ const needsAuth = def.guards.length > 0 || def.roles.length > 0 || def.permissions.length > 0
104
+ if (needsAuth && !ctx.auth?.authenticated) {
105
+ return { code: -32003, message: 'Unauthorized' }
106
+ }
107
+ if (def.roles.length > 0) {
108
+ const userRoles = ctx.auth?.roles ?? []
109
+ if (!def.roles.some((r: string) => userRoles.includes(r))) {
110
+ return { code: -32003, message: 'Insufficient role' }
111
+ }
112
+ }
113
+ if (def.permissions.length > 0) {
114
+ const userPerms = ctx.auth?.permissions ?? []
115
+ if (!def.permissions.every((p: string) => userPerms.includes(p))) {
116
+ return { code: -32003, message: 'Insufficient permissions' }
117
+ }
118
+ }
119
+ return undefined
120
+ }
121
+
122
+ export class RpcRouter {
123
+ #methods: Map<string, RpcMethodDefinition> = new Map()
124
+ #groupConfig: { guards: string[]; roles: string[]; middleware: string[] } | null = null
125
+ #container?: { make<T>(target: new (...args: unknown[]) => T): T }
126
+
127
+ /**
128
+ * Set the IoC container used to resolve `namespace()` controllers — mirrors
129
+ * `GraphQLEngine.useContainer()` so RPC and GraphQL resolve class handlers the
130
+ * same way (fresh DI per call). Without a container, controllers fall back to
131
+ * `new Controller()`.
132
+ */
133
+ useContainer(container: { make<T>(target: new (...args: unknown[]) => T): T }): void {
134
+ this.#container = container
135
+ }
136
+
137
+ /** Register an RPC method. */
138
+ method(name: string, handler: RpcHandler): RpcMethodBuilder {
139
+ const def: RpcMethodDefinition = {
140
+ name,
141
+ handler,
142
+ guards: [...(this.#groupConfig?.guards ?? [])],
143
+ roles: [...(this.#groupConfig?.roles ?? [])],
144
+ permissions: [],
145
+ middleware: [...(this.#groupConfig?.middleware ?? [])],
146
+ }
147
+ this.#methods.set(name, def)
148
+ return new RpcMethodBuilder(def)
149
+ }
150
+
151
+ /** Register methods from a controller — auto-registers all public methods. */
152
+ namespace(
153
+ prefix: string,
154
+ controller: new (...args: unknown[]) => Record<string, (...args: unknown[]) => unknown>,
155
+ ): void {
156
+ const proto = controller.prototype
157
+ const methods = Object.getOwnPropertyNames(proto).filter(
158
+ (m) => m !== 'constructor' && typeof proto[m] === 'function',
159
+ )
160
+ for (const methodName of methods) {
161
+ this.method(`${prefix}.${methodName}`, async (ctx, params) => {
162
+ // Resolve through the container on every call (fresh DI per request,
163
+ // like GraphQLEngine), falling back to a bare `new` when unset.
164
+ const instance = this.#container ? this.#container.make(controller) : new controller()
165
+ return instance[methodName](ctx, params)
166
+ })
167
+ }
168
+ }
169
+
170
+ /** Group methods with shared guards/middleware. */
171
+ group(
172
+ config: { guard?: string; guards?: string[]; middleware?: string[] },
173
+ callback: (rpc: RpcRouter) => void,
174
+ ): void {
175
+ const prevConfig = this.#groupConfig
176
+ this.#groupConfig = {
177
+ guards: [...(config.guards ?? []), ...(config.guard ? [config.guard] : [])],
178
+ roles: [],
179
+ middleware: config.middleware ?? [],
180
+ }
181
+ callback(this)
182
+ this.#groupConfig = prevConfig
183
+ }
184
+
185
+ /** Handle an incoming JSON-RPC request. */
186
+ async handle(ctx: HttpContext): Promise<void> {
187
+ const body = ctx.request.body()
188
+
189
+ // Batch support (max 50 to prevent DoS)
190
+ if (Array.isArray(body)) {
191
+ if (body.length > 50) {
192
+ ctx.response.status(400).json({
193
+ jsonrpc: '2.0',
194
+ error: { code: -32600, message: 'Batch too large (max 50)' },
195
+ id: null,
196
+ })
197
+ return
198
+ }
199
+ // Note: batch items share the same ctx (auth state). Each is processed sequentially.
200
+ const results: unknown[] = []
201
+ for (const req of body) {
202
+ results.push(await this.#processOne(ctx, req))
203
+ }
204
+ ctx.response.status(200).json(results)
205
+ return
206
+ }
207
+
208
+ const result = await this.#processOne(ctx, body)
209
+ ctx.response.status(200).json(result)
210
+ }
211
+
212
+ async #processOne(ctx: HttpContext, request: unknown): Promise<unknown> {
213
+ const parsed = parseRpcRequest(request)
214
+ if (!parsed.ok) return parsed.response
215
+ const { method, params, id } = parsed
216
+
217
+ const def = this.#methods.get(method)
218
+ if (!def) return rpcError(-32601, 'Method not found', id)
219
+
220
+ try {
221
+ const denied = checkRpcAuthorization(ctx, def)
222
+ if (denied) return rpcError(denied.code, denied.message, id)
223
+
224
+ const result = await def.handler(ctx, params)
225
+ return { jsonrpc: '2.0', result, id }
226
+ } catch (err) {
227
+ // Don't leak internal error details to caller
228
+ const isDev = process.env.NODE_ENV !== 'production'
229
+ const message = isDev && err instanceof Error ? err.message : 'Internal error'
230
+ return rpcError(-32603, message, id)
231
+ }
232
+ }
233
+
234
+ /** Get all registered method names. */
235
+ getMethods(): string[] {
236
+ return [...this.#methods.keys()]
237
+ }
238
+
239
+ /** Get method definition for introspection. */
240
+ getMethod(name: string): RpcMethodDefinition | undefined {
241
+ return this.#methods.get(name)
242
+ }
243
+ }
@@ -0,0 +1,96 @@
1
+ /**
2
+ * `@Schedule('cron-expr')` method decorator — declares a recurring task.
3
+ *
4
+ * Tasks are discovered at boot by `ScheduleProvider` iterating the IoC
5
+ * service registry, reading this metadata, and registering each entry
6
+ * with the Rust-backed `Scheduler` (Story 28.1). The Rust ticker owns
7
+ * the loop; TypeScript only declares *what* runs and *when*.
8
+ *
9
+ * @implements Story 28.2
10
+ */
11
+
12
+ import 'reflect-metadata'
13
+ import { ReamError } from '../errors/ReamError.js'
14
+ import type { Constructor } from '../helpers/types.js'
15
+
16
+ export const SCHEDULE_METADATA_KEY = Symbol('ream:schedule')
17
+
18
+ export interface ScheduleMetadata {
19
+ cronExpr: string
20
+ methodName: string | symbol
21
+ target: Constructor
22
+ }
23
+
24
+ /**
25
+ * Declares that a method should run on a cron schedule.
26
+ *
27
+ * Expressions are standard 5-field cron (minute hour day-of-month month
28
+ * day-of-week), evaluated in **UTC** by the Rust core — callers that
29
+ * want local-time semantics must pre-translate.
30
+ *
31
+ * Multiple `@Schedule` decorators on different methods of the same class
32
+ * accumulate into an array rather than overwriting each other. Metadata
33
+ * is stored with `Reflect.defineMetadata` under a stable symbol key on
34
+ * the class constructor; inherited metadata is NOT shared (each class
35
+ * owns its own array — subclasses that add schedules do not mutate the
36
+ * base class's array).
37
+ *
38
+ * Getters, setters, and static methods are rejected at decoration time
39
+ * with a clear `ReamError` so that misuse surfaces early rather than at
40
+ * first fire.
41
+ *
42
+ * Usage:
43
+ * ```ts
44
+ * @Service()
45
+ * class Cleanup {
46
+ * @Schedule('0 *\/5 * * *')
47
+ * async runCleanup() { ... }
48
+ * }
49
+ * ```
50
+ */
51
+ export function Schedule(cronExpr: string): MethodDecorator {
52
+ return (target, propertyKey, descriptor) => {
53
+ // Reject static methods. For a static, `target` IS the constructor
54
+ // (callable function); for an instance method, `target` is the
55
+ // prototype. We need the real class either way.
56
+ if (typeof target === 'function') {
57
+ throw new ReamError(
58
+ 'SCHEDULE_INVALID_TARGET',
59
+ `@Schedule cannot be applied to static method '${String(propertyKey)}'`,
60
+ {
61
+ hint: 'Move the scheduled logic to an instance method, or register the task manually via Scheduler.register().',
62
+ },
63
+ )
64
+ }
65
+
66
+ // Reject getters/setters — `descriptor.value` is `undefined` for
67
+ // accessors, and the ticker needs a callable method.
68
+ if (descriptor && typeof descriptor.value !== 'function') {
69
+ throw new ReamError(
70
+ 'SCHEDULE_INVALID_TARGET',
71
+ `@Schedule cannot be applied to non-method '${String(propertyKey)}' (getters and setters are not supported)`,
72
+ {
73
+ hint: 'Apply @Schedule to a regular method, not to an accessor.',
74
+ },
75
+ )
76
+ }
77
+
78
+ const ctor = target.constructor as Constructor
79
+ // getOwnMetadata — do NOT walk the prototype chain. A subclass must
80
+ // not mutate the base class's shared array; each class owns its own
81
+ // schedule list. Matches the convention in decorators/Service.ts.
82
+ const existing: ScheduleMetadata[] = Reflect.getOwnMetadata(SCHEDULE_METADATA_KEY, ctor) ?? []
83
+ existing.push({ cronExpr, methodName: propertyKey, target: ctor })
84
+ Reflect.defineMetadata(SCHEDULE_METADATA_KEY, existing, ctor)
85
+ }
86
+ }
87
+
88
+ /**
89
+ * Return every `@Schedule` entry declared on `target` (empty array if
90
+ * none). Returns a shallow copy so callers cannot mutate the stored
91
+ * metadata by accident.
92
+ */
93
+ export function getScheduleMetadata(target: Constructor): ScheduleMetadata[] {
94
+ const stored: ScheduleMetadata[] = Reflect.getOwnMetadata(SCHEDULE_METADATA_KEY, target) ?? []
95
+ return [...stored]
96
+ }
@@ -0,0 +1,177 @@
1
+ /**
2
+ * `ScheduleProvider` — wires `@Schedule`-decorated methods into the
3
+ * Rust-backed [`Scheduler`].
4
+ *
5
+ * Lifecycle:
6
+ * - `boot()` — walks the IoC service registry, reads each class's
7
+ * `@Schedule` metadata, and registers a callback per entry.
8
+ * Idempotent: calling `boot()` twice is a no-op on the second call.
9
+ * Partial failures are rolled back — if the Nth task's registration
10
+ * throws, tasks 1..N-1 registered in the same boot are unregistered
11
+ * before the error propagates, so the scheduler is never left in a
12
+ * half-registered state.
13
+ * - `start()` — launches the Rust tick loop.
14
+ * - `shutdown()` — cancels the Rust tick loop.
15
+ *
16
+ * The registered callback resolves the service **at invocation time**
17
+ * (not registration time) via `container.make(target)`, so every run
18
+ * receives freshly-DI'd dependencies for transient-scoped services.
19
+ *
20
+ * @implements Story 28.2
21
+ */
22
+
23
+ import { getServiceRegistry } from '../decorators/Service.js'
24
+ import { ReamError } from '../errors/ReamError.js'
25
+ import type { AppContext } from '../Provider.js'
26
+ import { Provider } from '../Provider.js'
27
+ import { getScheduleMetadata } from './Schedule.js'
28
+ import { Scheduler } from './Scheduler.js'
29
+
30
+ export interface ScheduleProviderOptions {
31
+ /**
32
+ * Override the `Scheduler` instance — used by tests to inject a
33
+ * mock. Production code relies on the default (`new Scheduler()`).
34
+ */
35
+ scheduler?: Scheduler
36
+ }
37
+
38
+ export class ScheduleProvider extends Provider {
39
+ readonly scheduler: Scheduler
40
+ #booted = false
41
+
42
+ constructor(app: AppContext, options: ScheduleProviderOptions = {}) {
43
+ super(app)
44
+ this.scheduler = options.scheduler ?? new Scheduler()
45
+ }
46
+
47
+ /**
48
+ * Phase 1 — bind the scheduler into the IoC container under the
49
+ * stable token `'scheduler'`. CLI entrypoints
50
+ * (`ream schedule:list` / `ream schedule:run <name>`) retrieve the
51
+ * scheduler via `container.resolve('scheduler')` once the app has
52
+ * been booted through `Ignitor`. Consumers that import the
53
+ * provider directly still read the `readonly scheduler` field.
54
+ *
55
+ * Idempotent: re-registering the same provider instance (hot
56
+ * reload, repeated `app.start()` in tests) re-binds the same
57
+ * scheduler without error. A DIFFERENT `ScheduleProvider` trying
58
+ * to claim the `'scheduler'` token throws
59
+ * `SCHEDULE_PROVIDER_ALREADY_REGISTERED` so dueling providers do
60
+ * not silently overwrite each other.
61
+ */
62
+ override register(): void {
63
+ const scheduler = this.scheduler
64
+ const container = this.app.container
65
+ // Probe for an existing binding. The `make` / `resolve` API
66
+ // throws when unbound; use it to distinguish "same instance"
67
+ // from "another provider already claimed the token".
68
+ let existing: unknown
69
+ try {
70
+ existing = container.resolve('scheduler')
71
+ } catch {
72
+ existing = undefined
73
+ }
74
+ if (existing !== undefined) {
75
+ if (existing === scheduler) {
76
+ // Same provider re-running its register phase — no-op.
77
+ return
78
+ }
79
+ throw new ReamError(
80
+ 'SCHEDULE_PROVIDER_ALREADY_REGISTERED',
81
+ "Container token 'scheduler' is already bound to a different instance",
82
+ {
83
+ hint: 'Only one ScheduleProvider can own the scheduler binding. Remove the duplicate provider from your reamrc.ts.',
84
+ },
85
+ )
86
+ }
87
+ container.singleton('scheduler', () => scheduler)
88
+ }
89
+
90
+ override async boot(): Promise<void> {
91
+ if (this.#booted) return
92
+ const registry = getServiceRegistry()
93
+ const registered: string[] = []
94
+
95
+ try {
96
+ for (const [target] of registry) {
97
+ const schedules = getScheduleMetadata(target)
98
+ if (schedules.length === 0) continue
99
+
100
+ if (!target.name) {
101
+ throw new ReamError(
102
+ 'SCHEDULE_ANONYMOUS_CLASS',
103
+ 'Anonymous classes cannot declare @Schedule — task names would collide',
104
+ {
105
+ hint: 'Name the class explicitly (e.g. `export class Jobs { ... }`) so task names are unique and debuggable.',
106
+ },
107
+ )
108
+ }
109
+
110
+ for (const { cronExpr, methodName } of schedules) {
111
+ // Reject symbol method keys — `String(Symbol('x'))` yields
112
+ // `Symbol(x)` which collides across distinct symbol instances
113
+ // sharing the same description. String keys are the
114
+ // documented contract.
115
+ if (typeof methodName === 'symbol') {
116
+ throw new ReamError(
117
+ 'SCHEDULE_SYMBOL_METHOD',
118
+ `@Schedule on symbol-keyed method is not supported on class '${target.name}'`,
119
+ {
120
+ hint: 'Use a regular named method so the task can be addressed uniquely.',
121
+ },
122
+ )
123
+ }
124
+
125
+ const taskName = `${target.name}.${methodName}`
126
+ try {
127
+ this.scheduler.register(taskName, cronExpr, async () => {
128
+ // Resolve from the container on every fire so transient
129
+ // services get a fresh instance each run.
130
+ const instance = this.app.container.make<Record<string, unknown>>(target)
131
+ const method = instance[methodName]
132
+ if (typeof method !== 'function') {
133
+ throw new ReamError(
134
+ 'SCHEDULE_METHOD_NOT_FOUND',
135
+ `Scheduled method ${taskName} is not callable on resolved instance`,
136
+ )
137
+ }
138
+ await (method as (...args: unknown[]) => unknown).call(instance)
139
+ })
140
+ registered.push(taskName)
141
+ } catch (cause) {
142
+ const inner = cause as ReamError & { message?: string; hint?: string }
143
+ throw new ReamError(
144
+ 'SCHEDULE_INVALID_CRON',
145
+ `Failed to register scheduled task '${taskName}': ${inner.message ?? String(cause)}`,
146
+ {
147
+ context: { task: taskName, cronExpr },
148
+ hint: inner.hint,
149
+ },
150
+ )
151
+ }
152
+ }
153
+ }
154
+ this.#booted = true
155
+ } catch (err) {
156
+ // Roll back any partial registrations so a retry does not hit
157
+ // DUPLICATE_TASK and the scheduler is never half-initialized.
158
+ for (const name of registered) {
159
+ try {
160
+ this.scheduler.unregister(name)
161
+ } catch {
162
+ // Best-effort rollback; swallow secondary failures so the
163
+ // original error reaches the caller unchanged.
164
+ }
165
+ }
166
+ throw err
167
+ }
168
+ }
169
+
170
+ override async start(): Promise<void> {
171
+ this.scheduler.start()
172
+ }
173
+
174
+ override async shutdown(): Promise<void> {
175
+ this.scheduler.stop()
176
+ }
177
+ }