alepha 0.13.6 → 0.13.8

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 (487) hide show
  1. package/README.md +5 -2
  2. package/assets/swagger-ui/swagger-ui-bundle.js +1 -1
  3. package/assets/swagger-ui/swagger-ui-standalone-preset.js +1 -1
  4. package/assets/swagger-ui/swagger-ui.css +1 -1
  5. package/dist/api/audits/index.browser.js +116 -0
  6. package/dist/api/audits/index.browser.js.map +1 -0
  7. package/dist/api/audits/index.d.ts +1194 -0
  8. package/dist/api/audits/index.js +674 -0
  9. package/dist/api/audits/index.js.map +1 -0
  10. package/dist/{api-files → api/files}/index.browser.js +5 -5
  11. package/dist/api/files/index.browser.js.map +1 -0
  12. package/dist/{api-files → api/files}/index.d.ts +16 -9
  13. package/dist/{api-files → api/files}/index.js +10 -10
  14. package/dist/api/files/index.js.map +1 -0
  15. package/dist/{api-jobs → api/jobs}/index.browser.js +5 -5
  16. package/dist/api/jobs/index.browser.js.map +1 -0
  17. package/dist/{api-jobs → api/jobs}/index.d.ts +35 -35
  18. package/dist/{api-jobs → api/jobs}/index.js +9 -9
  19. package/dist/api/jobs/index.js.map +1 -0
  20. package/dist/{api-notifications → api/notifications}/index.browser.js +11 -11
  21. package/dist/api/notifications/index.browser.js.map +1 -0
  22. package/dist/api/notifications/index.d.ts +327 -0
  23. package/dist/{api-notifications → api/notifications}/index.js +11 -11
  24. package/dist/api/notifications/index.js.map +1 -0
  25. package/dist/api/parameters/index.browser.js +60 -0
  26. package/dist/api/parameters/index.browser.js.map +1 -0
  27. package/dist/api/parameters/index.d.ts +761 -0
  28. package/dist/api/parameters/index.js +877 -0
  29. package/dist/api/parameters/index.js.map +1 -0
  30. package/dist/{api-users → api/users}/index.browser.js +6 -6
  31. package/dist/api/users/index.browser.js.map +1 -0
  32. package/dist/{api-users → api/users}/index.d.ts +259 -247
  33. package/dist/{api-users → api/users}/index.js +125 -112
  34. package/dist/api/users/index.js.map +1 -0
  35. package/dist/{api-verifications → api/verifications}/index.browser.js +5 -5
  36. package/dist/api/verifications/index.browser.js.map +1 -0
  37. package/dist/api/verifications/index.d.ts +248 -0
  38. package/dist/{api-verifications → api/verifications}/index.js +13 -12
  39. package/dist/api/verifications/index.js.map +1 -0
  40. package/dist/bin/index.js +1 -0
  41. package/dist/bin/index.js.map +1 -1
  42. package/dist/cache/{index.d.ts → core/index.d.ts} +4 -4
  43. package/dist/cache/{index.js → core/index.js} +5 -5
  44. package/dist/cache/core/index.js.map +1 -0
  45. package/dist/{cache-redis → cache/redis}/index.d.ts +2 -2
  46. package/dist/{cache-redis → cache/redis}/index.js +2 -2
  47. package/dist/cache/redis/index.js.map +1 -0
  48. package/dist/cli/index.d.ts +71 -9
  49. package/dist/cli/index.js +280 -79
  50. package/dist/cli/index.js.map +1 -1
  51. package/dist/command/index.d.ts +63 -2
  52. package/dist/command/index.js +30 -3
  53. package/dist/command/index.js.map +1 -1
  54. package/dist/core/index.browser.js +241 -61
  55. package/dist/core/index.browser.js.map +1 -1
  56. package/dist/core/index.d.ts +170 -90
  57. package/dist/core/index.js +264 -67
  58. package/dist/core/index.js.map +1 -1
  59. package/dist/core/index.native.js +248 -65
  60. package/dist/core/index.native.js.map +1 -1
  61. package/dist/email/index.js +15 -10554
  62. package/dist/email/index.js.map +1 -1
  63. package/dist/lock/{index.d.ts → core/index.d.ts} +5 -5
  64. package/dist/lock/{index.js → core/index.js} +5 -5
  65. package/dist/lock/core/index.js.map +1 -0
  66. package/dist/{lock-redis → lock/redis}/index.d.ts +2 -2
  67. package/dist/{lock-redis → lock/redis}/index.js +2 -2
  68. package/dist/lock/redis/index.js.map +1 -0
  69. package/dist/logger/index.d.ts +4 -4
  70. package/dist/logger/index.js +77 -72
  71. package/dist/logger/index.js.map +1 -1
  72. package/dist/orm/index.d.ts +5 -1
  73. package/dist/orm/index.js +24 -7
  74. package/dist/orm/index.js.map +1 -1
  75. package/dist/queue/core/index.d.ts +548 -0
  76. package/dist/queue/core/index.js +391 -0
  77. package/dist/queue/core/index.js.map +1 -0
  78. package/dist/queue/redis/index.d.ts +28 -0
  79. package/dist/queue/redis/index.js +43 -0
  80. package/dist/queue/redis/index.js.map +1 -0
  81. package/dist/scheduler/index.d.ts +7 -7
  82. package/dist/scheduler/index.js +1 -393
  83. package/dist/scheduler/index.js.map +1 -1
  84. package/dist/security/index.d.ts +1 -1
  85. package/dist/security/index.js +2 -1413
  86. package/dist/security/index.js.map +1 -1
  87. package/dist/{server-auth → server/auth}/index.browser.js +6 -6
  88. package/dist/server/auth/index.browser.js.map +1 -0
  89. package/dist/{server-auth → server/auth}/index.d.ts +175 -164
  90. package/dist/server/auth/index.js +742 -0
  91. package/dist/server/auth/index.js.map +1 -0
  92. package/dist/{server-cache → server/cache}/index.d.ts +2 -2
  93. package/dist/{server-cache → server/cache}/index.js +2 -2
  94. package/dist/server/cache/index.js.map +1 -0
  95. package/dist/{server-compress → server/compress}/index.d.ts +2 -2
  96. package/dist/{server-compress → server/compress}/index.js +2 -2
  97. package/dist/server/compress/index.js.map +1 -0
  98. package/dist/{server-cookies → server/cookies}/index.browser.js +3 -3
  99. package/dist/server/cookies/index.browser.js.map +1 -0
  100. package/dist/{server-cookies → server/cookies}/index.d.ts +4 -4
  101. package/dist/{server-cookies → server/cookies}/index.js +9 -5
  102. package/dist/server/cookies/index.js.map +1 -0
  103. package/dist/server/{index.browser.js → core/index.browser.js} +14 -14
  104. package/dist/server/core/index.browser.js.map +1 -0
  105. package/dist/server/{index.d.ts → core/index.d.ts} +46 -37
  106. package/dist/server/{index.js → core/index.js} +47 -33
  107. package/dist/server/core/index.js.map +1 -0
  108. package/dist/{server-cors → server/cors}/index.d.ts +3 -3
  109. package/dist/{server-cors → server/cors}/index.js +3 -3
  110. package/dist/server/cors/index.js.map +1 -0
  111. package/dist/{server-health → server/health}/index.d.ts +3 -3
  112. package/dist/{server-health → server/health}/index.js +3 -3
  113. package/dist/server/health/index.js.map +1 -0
  114. package/dist/{server-helmet → server/helmet}/index.d.ts +2 -2
  115. package/dist/{server-helmet → server/helmet}/index.js +2 -2
  116. package/dist/server/helmet/index.js.map +1 -0
  117. package/dist/{server-links → server/links}/index.browser.js +5 -5
  118. package/dist/server/links/index.browser.js.map +1 -0
  119. package/dist/{server-links → server/links}/index.d.ts +40 -40
  120. package/dist/{server-links → server/links}/index.js +7 -7
  121. package/dist/server/links/index.js.map +1 -0
  122. package/dist/{server-metrics → server/metrics}/index.d.ts +2 -2
  123. package/dist/server/metrics/index.js +74 -0
  124. package/dist/server/metrics/index.js.map +1 -0
  125. package/dist/{server-multipart → server/multipart}/index.d.ts +2 -2
  126. package/dist/{server-multipart → server/multipart}/index.js +2 -2
  127. package/dist/server/multipart/index.js.map +1 -0
  128. package/dist/{server-proxy → server/proxy}/index.d.ts +3 -3
  129. package/dist/{server-proxy → server/proxy}/index.js +3 -3
  130. package/dist/server/proxy/index.js.map +1 -0
  131. package/dist/{server-rate-limit → server/rate-limit}/index.d.ts +4 -4
  132. package/dist/{server-rate-limit → server/rate-limit}/index.js +4 -4
  133. package/dist/server/rate-limit/index.js.map +1 -0
  134. package/dist/{server-security → server/security}/index.browser.js +1 -1
  135. package/dist/server/security/index.browser.js.map +1 -0
  136. package/dist/{server-security → server/security}/index.d.ts +4 -4
  137. package/dist/{server-security → server/security}/index.js +4 -4
  138. package/dist/server/security/index.js.map +1 -0
  139. package/dist/{server-static → server/static}/index.d.ts +3 -3
  140. package/dist/{server-static → server/static}/index.js +3 -3
  141. package/dist/server/static/index.js.map +1 -0
  142. package/dist/{server-swagger → server/swagger}/index.d.ts +3 -3
  143. package/dist/{server-swagger → server/swagger}/index.js +4 -4
  144. package/dist/server/swagger/index.js.map +1 -0
  145. package/dist/thread/index.js +2 -2
  146. package/dist/thread/index.js.map +1 -1
  147. package/dist/topic/{index.d.ts → core/index.d.ts} +6 -6
  148. package/dist/topic/{index.js → core/index.js} +6 -6
  149. package/dist/topic/core/index.js.map +1 -0
  150. package/dist/{topic-redis → topic/redis}/index.d.ts +2 -2
  151. package/dist/{topic-redis → topic/redis}/index.js +2 -2
  152. package/dist/topic/redis/index.js.map +1 -0
  153. package/dist/vite/index.d.ts +13 -2
  154. package/dist/vite/index.js +114 -50
  155. package/dist/vite/index.js.map +1 -1
  156. package/dist/websocket/index.browser.js +3 -3
  157. package/dist/websocket/index.browser.js.map +1 -1
  158. package/dist/websocket/index.js +4 -4
  159. package/dist/websocket/index.js.map +1 -1
  160. package/package.json +160 -156
  161. package/src/api/audits/controllers/AuditController.ts +186 -0
  162. package/src/api/audits/entities/audits.ts +132 -0
  163. package/src/api/audits/index.browser.ts +18 -0
  164. package/src/api/audits/index.ts +58 -0
  165. package/src/api/audits/primitives/$audit.ts +159 -0
  166. package/src/api/audits/schemas/auditQuerySchema.ts +23 -0
  167. package/src/api/audits/schemas/auditResourceSchema.ts +9 -0
  168. package/src/api/audits/schemas/createAuditSchema.ts +27 -0
  169. package/src/api/audits/services/AuditService.ts +412 -0
  170. package/src/{api-files → api/files}/index.ts +1 -0
  171. package/src/api/parameters/controllers/ConfigController.ts +324 -0
  172. package/src/api/parameters/entities/parameters.ts +113 -0
  173. package/src/api/parameters/index.ts +60 -0
  174. package/src/api/parameters/primitives/$config.ts +351 -0
  175. package/src/api/parameters/schedulers/ConfigActivationScheduler.ts +30 -0
  176. package/src/api/parameters/services/ConfigStore.ts +491 -0
  177. package/src/{api-users → api/users}/atoms/realmAuthSettingsAtom.ts +19 -0
  178. package/src/{api-users → api/users}/controllers/UserRealmController.ts +0 -2
  179. package/src/{api-users → api/users}/index.ts +2 -0
  180. package/src/{api-users → api/users}/primitives/$userRealm.ts +18 -3
  181. package/src/{api-users → api/users}/providers/UserRealmProvider.ts +12 -10
  182. package/src/{api-users → api/users}/services/RegistrationService.ts +2 -1
  183. package/src/{api-users → api/users}/services/SessionService.ts +4 -0
  184. package/src/{api-users → api/users}/services/UserService.ts +3 -0
  185. package/src/{api-verifications → api/verifications}/index.ts +9 -1
  186. package/src/bin/index.ts +1 -0
  187. package/src/cli/apps/AlephaPackageBuilderCli.ts +73 -48
  188. package/src/cli/assets/appRouterTs.ts +1 -1
  189. package/src/cli/assets/biomeJson.ts +2 -2
  190. package/src/cli/assets/dummySpecTs.ts +7 -0
  191. package/src/cli/assets/editorconfig.ts +13 -0
  192. package/src/cli/assets/indexHtml.ts +1 -1
  193. package/src/cli/assets/mainBrowserTs.ts +1 -1
  194. package/src/cli/assets/mainTs.ts +14 -0
  195. package/src/cli/assets/viteConfigTs.ts +1 -1
  196. package/src/cli/commands/BiomeCommands.ts +2 -0
  197. package/src/cli/commands/CoreCommands.ts +38 -15
  198. package/src/cli/commands/VerifyCommands.ts +6 -2
  199. package/src/cli/commands/ViteCommands.ts +28 -18
  200. package/src/cli/services/AlephaCliUtils.ts +243 -37
  201. package/src/command/helpers/Asker.ts +0 -1
  202. package/src/command/primitives/$command.ts +67 -0
  203. package/src/command/providers/CliProvider.ts +39 -8
  204. package/src/core/Alepha.ts +40 -30
  205. package/src/core/helpers/jsonSchemaToTypeBox.ts +307 -0
  206. package/src/core/index.shared.ts +1 -0
  207. package/src/core/index.ts +30 -3
  208. package/src/core/providers/EventManager.ts +1 -1
  209. package/src/core/providers/SchemaValidator.ts +1 -1
  210. package/src/core/providers/StateManager.ts +23 -12
  211. package/src/core/providers/TypeProvider.ts +26 -34
  212. package/src/logger/index.ts +8 -6
  213. package/src/logger/primitives/$logger.ts +1 -1
  214. package/src/logger/providers/{SimpleFormatterProvider.ts → PrettyFormatterProvider.ts} +10 -1
  215. package/src/orm/index.ts +6 -0
  216. package/src/orm/services/PgRelationManager.ts +2 -2
  217. package/src/orm/services/PostgresModelBuilder.ts +11 -7
  218. package/src/orm/services/Repository.ts +16 -7
  219. package/src/orm/services/SqliteModelBuilder.ts +10 -0
  220. package/src/queue/{index.ts → core/index.ts} +2 -3
  221. package/src/queue/{primitives → core/primitives}/$queue.ts +17 -162
  222. package/src/queue/core/providers/MemoryQueueProvider.ts +19 -0
  223. package/src/queue/core/providers/QueueProvider.ts +23 -0
  224. package/src/queue/core/providers/WorkerProvider.ts +244 -0
  225. package/src/queue/redis/providers/RedisQueueProvider.ts +31 -0
  226. package/src/{server-auth → server/auth}/primitives/$auth.ts +7 -0
  227. package/src/{server-auth → server/auth}/providers/ServerAuthProvider.ts +51 -8
  228. package/src/{server-cookies → server/cookies}/index.ts +2 -1
  229. package/src/server/{index.ts → core/index.ts} +7 -0
  230. package/src/server/{primitives → core/primitives}/$action.ts +10 -1
  231. package/src/server/{providers → core/providers}/ServerBodyParserProvider.ts +11 -5
  232. package/src/server/{providers → core/providers}/ServerRouterProvider.ts +13 -7
  233. package/src/{server-rate-limit → server/rate-limit}/index.ts +1 -1
  234. package/src/{server-swagger → server/swagger}/providers/ServerSwaggerProvider.ts +1 -0
  235. package/src/thread/primitives/$thread.ts +2 -2
  236. package/src/vite/index.ts +0 -2
  237. package/src/vite/tasks/buildServer.ts +3 -4
  238. package/src/vite/tasks/copyAssets.ts +32 -8
  239. package/src/vite/tasks/generateCloudflare.ts +35 -19
  240. package/src/vite/tasks/generateDocker.ts +18 -4
  241. package/src/vite/tasks/generateSitemap.ts +5 -7
  242. package/src/vite/tasks/generateVercel.ts +76 -41
  243. package/src/vite/tasks/runAlepha.ts +16 -1
  244. package/src/websocket/providers/NodeWebSocketServerProvider.ts +3 -11
  245. package/src/websocket/services/WebSocketClient.ts +3 -3
  246. package/dist/api-files/index.browser.js.map +0 -1
  247. package/dist/api-files/index.js.map +0 -1
  248. package/dist/api-jobs/index.browser.js.map +0 -1
  249. package/dist/api-jobs/index.js.map +0 -1
  250. package/dist/api-notifications/index.browser.js.map +0 -1
  251. package/dist/api-notifications/index.d.ts +0 -327
  252. package/dist/api-notifications/index.js.map +0 -1
  253. package/dist/api-parameters/index.browser.js +0 -29
  254. package/dist/api-parameters/index.browser.js.map +0 -1
  255. package/dist/api-parameters/index.d.ts +0 -83
  256. package/dist/api-parameters/index.js +0 -63
  257. package/dist/api-parameters/index.js.map +0 -1
  258. package/dist/api-users/index.browser.js.map +0 -1
  259. package/dist/api-users/index.js.map +0 -1
  260. package/dist/api-verifications/index.browser.js.map +0 -1
  261. package/dist/api-verifications/index.d.ts +0 -229
  262. package/dist/api-verifications/index.js.map +0 -1
  263. package/dist/cache/index.js.map +0 -1
  264. package/dist/cache-redis/index.js.map +0 -1
  265. package/dist/cli/dist-BlfFtOk2.js +0 -2770
  266. package/dist/cli/dist-BlfFtOk2.js.map +0 -1
  267. package/dist/lock/index.js.map +0 -1
  268. package/dist/lock-redis/index.js.map +0 -1
  269. package/dist/queue/index.d.ts +0 -1265
  270. package/dist/queue/index.js +0 -1037
  271. package/dist/queue/index.js.map +0 -1
  272. package/dist/queue-redis/index.d.ts +0 -82
  273. package/dist/queue-redis/index.js +0 -872
  274. package/dist/queue-redis/index.js.map +0 -1
  275. package/dist/server/index.browser.js.map +0 -1
  276. package/dist/server/index.js.map +0 -1
  277. package/dist/server-auth/index.browser.js.map +0 -1
  278. package/dist/server-auth/index.js +0 -1943
  279. package/dist/server-auth/index.js.map +0 -1
  280. package/dist/server-cache/index.js.map +0 -1
  281. package/dist/server-compress/index.js.map +0 -1
  282. package/dist/server-cookies/index.browser.js.map +0 -1
  283. package/dist/server-cookies/index.js.map +0 -1
  284. package/dist/server-cors/index.js.map +0 -1
  285. package/dist/server-health/index.js.map +0 -1
  286. package/dist/server-helmet/index.js.map +0 -1
  287. package/dist/server-links/index.browser.js.map +0 -1
  288. package/dist/server-links/index.js.map +0 -1
  289. package/dist/server-metrics/index.js +0 -4532
  290. package/dist/server-metrics/index.js.map +0 -1
  291. package/dist/server-multipart/index.js.map +0 -1
  292. package/dist/server-proxy/index.js.map +0 -1
  293. package/dist/server-rate-limit/index.js.map +0 -1
  294. package/dist/server-security/index.browser.js.map +0 -1
  295. package/dist/server-security/index.js.map +0 -1
  296. package/dist/server-static/index.js.map +0 -1
  297. package/dist/server-swagger/index.js.map +0 -1
  298. package/dist/topic/index.js.map +0 -1
  299. package/dist/topic-redis/index.js.map +0 -1
  300. package/src/api-parameters/controllers/ParameterController.ts +0 -45
  301. package/src/api-parameters/entities/parameters.ts +0 -30
  302. package/src/api-parameters/index.ts +0 -21
  303. package/src/api-parameters/primitives/$config.ts +0 -79
  304. package/src/api-parameters/services/ParameterStore.ts +0 -23
  305. package/src/queue/interfaces/QueueJob.ts +0 -459
  306. package/src/queue/providers/MemoryQueueProvider.ts +0 -850
  307. package/src/queue/providers/QueueProvider.ts +0 -319
  308. package/src/queue/providers/WorkerProvider.ts +0 -344
  309. package/src/queue-redis/providers/RedisQueueProvider.ts +0 -1209
  310. /package/src/{api-files → api/files}/controllers/FileController.ts +0 -0
  311. /package/src/{api-files → api/files}/controllers/StorageStatsController.ts +0 -0
  312. /package/src/{api-files → api/files}/entities/files.ts +0 -0
  313. /package/src/{api-files → api/files}/index.browser.ts +0 -0
  314. /package/src/{api-files → api/files}/jobs/FileJobs.ts +0 -0
  315. /package/src/{api-files → api/files}/schemas/fileQuerySchema.ts +0 -0
  316. /package/src/{api-files → api/files}/schemas/fileResourceSchema.ts +0 -0
  317. /package/src/{api-files → api/files}/schemas/storageStatsSchema.ts +0 -0
  318. /package/src/{api-files → api/files}/services/FileService.ts +0 -0
  319. /package/src/{api-jobs → api/jobs}/controllers/JobController.ts +0 -0
  320. /package/src/{api-jobs → api/jobs}/entities/jobExecutions.ts +0 -0
  321. /package/src/{api-jobs → api/jobs}/index.browser.ts +0 -0
  322. /package/src/{api-jobs → api/jobs}/index.ts +0 -0
  323. /package/src/{api-jobs → api/jobs}/primitives/$job.ts +0 -0
  324. /package/src/{api-jobs → api/jobs}/providers/JobProvider.ts +0 -0
  325. /package/src/{api-jobs → api/jobs}/schemas/jobExecutionQuerySchema.ts +0 -0
  326. /package/src/{api-jobs → api/jobs}/schemas/jobExecutionResourceSchema.ts +0 -0
  327. /package/src/{api-jobs → api/jobs}/schemas/triggerJobSchema.ts +0 -0
  328. /package/src/{api-jobs → api/jobs}/services/JobService.ts +0 -0
  329. /package/src/{api-notifications → api/notifications}/controllers/NotificationController.ts +0 -0
  330. /package/src/{api-notifications → api/notifications}/entities/notifications.ts +0 -0
  331. /package/src/{api-notifications → api/notifications}/index.browser.ts +0 -0
  332. /package/src/{api-notifications → api/notifications}/index.ts +0 -0
  333. /package/src/{api-notifications → api/notifications}/jobs/NotificationJobs.ts +0 -0
  334. /package/src/{api-notifications → api/notifications}/primitives/$notification.ts +0 -0
  335. /package/src/{api-notifications → api/notifications}/queues/NotificationQueues.ts +0 -0
  336. /package/src/{api-notifications → api/notifications}/schemas/notificationContactPreferencesSchema.ts +0 -0
  337. /package/src/{api-notifications → api/notifications}/schemas/notificationContactSchema.ts +0 -0
  338. /package/src/{api-notifications → api/notifications}/schemas/notificationCreateSchema.ts +0 -0
  339. /package/src/{api-notifications → api/notifications}/schemas/notificationQuerySchema.ts +0 -0
  340. /package/src/{api-notifications → api/notifications}/services/NotificationSenderService.ts +0 -0
  341. /package/src/{api-notifications → api/notifications}/services/NotificationService.ts +0 -0
  342. /package/src/{api-parameters → api/parameters}/index.browser.ts +0 -0
  343. /package/src/{api-users → api/users}/controllers/IdentityController.ts +0 -0
  344. /package/src/{api-users → api/users}/controllers/SessionController.ts +0 -0
  345. /package/src/{api-users → api/users}/controllers/UserController.ts +0 -0
  346. /package/src/{api-users → api/users}/entities/identities.ts +0 -0
  347. /package/src/{api-users → api/users}/entities/sessions.ts +0 -0
  348. /package/src/{api-users → api/users}/entities/users.ts +0 -0
  349. /package/src/{api-users → api/users}/index.browser.ts +0 -0
  350. /package/src/{api-users → api/users}/notifications/UserNotifications.ts +0 -0
  351. /package/src/{api-users → api/users}/schemas/completePasswordResetRequestSchema.ts +0 -0
  352. /package/src/{api-users → api/users}/schemas/completeRegistrationRequestSchema.ts +0 -0
  353. /package/src/{api-users → api/users}/schemas/createUserSchema.ts +0 -0
  354. /package/src/{api-users → api/users}/schemas/identityQuerySchema.ts +0 -0
  355. /package/src/{api-users → api/users}/schemas/identityResourceSchema.ts +0 -0
  356. /package/src/{api-users → api/users}/schemas/loginSchema.ts +0 -0
  357. /package/src/{api-users → api/users}/schemas/passwordResetIntentResponseSchema.ts +0 -0
  358. /package/src/{api-users → api/users}/schemas/registerQuerySchema.ts +0 -0
  359. /package/src/{api-users → api/users}/schemas/registerRequestSchema.ts +0 -0
  360. /package/src/{api-users → api/users}/schemas/registerResponseSchema.ts +0 -0
  361. /package/src/{api-users → api/users}/schemas/registerSchema.ts +0 -0
  362. /package/src/{api-users → api/users}/schemas/registrationIntentResponseSchema.ts +0 -0
  363. /package/src/{api-users → api/users}/schemas/resetPasswordSchema.ts +0 -0
  364. /package/src/{api-users → api/users}/schemas/sessionQuerySchema.ts +0 -0
  365. /package/src/{api-users → api/users}/schemas/sessionResourceSchema.ts +0 -0
  366. /package/src/{api-users → api/users}/schemas/updateUserSchema.ts +0 -0
  367. /package/src/{api-users → api/users}/schemas/userQuerySchema.ts +0 -0
  368. /package/src/{api-users → api/users}/schemas/userRealmConfigSchema.ts +0 -0
  369. /package/src/{api-users → api/users}/schemas/userResourceSchema.ts +0 -0
  370. /package/src/{api-users → api/users}/services/CredentialService.ts +0 -0
  371. /package/src/{api-users → api/users}/services/IdentityService.ts +0 -0
  372. /package/src/{api-users → api/users}/services/SessionCrudService.ts +0 -0
  373. /package/src/{api-verifications → api/verifications}/controllers/VerificationController.ts +0 -0
  374. /package/src/{api-verifications → api/verifications}/entities/verifications.ts +0 -0
  375. /package/src/{api-verifications → api/verifications}/index.browser.ts +0 -0
  376. /package/src/{api-verifications → api/verifications}/jobs/VerificationJobs.ts +0 -0
  377. /package/src/{api-verifications → api/verifications}/parameters/VerificationParameters.ts +0 -0
  378. /package/src/{api-verifications → api/verifications}/schemas/requestVerificationCodeResponseSchema.ts +0 -0
  379. /package/src/{api-verifications → api/verifications}/schemas/validateVerificationCodeResponseSchema.ts +0 -0
  380. /package/src/{api-verifications → api/verifications}/schemas/verificationSettingsSchema.ts +0 -0
  381. /package/src/{api-verifications → api/verifications}/schemas/verificationTypeEnumSchema.ts +0 -0
  382. /package/src/{api-verifications → api/verifications}/services/VerificationService.ts +0 -0
  383. /package/src/cache/{errors → core/errors}/CacheError.ts +0 -0
  384. /package/src/cache/{index.ts → core/index.ts} +0 -0
  385. /package/src/cache/{primitives → core/primitives}/$cache.ts +0 -0
  386. /package/src/cache/{providers → core/providers}/CacheProvider.ts +0 -0
  387. /package/src/cache/{providers → core/providers}/MemoryCacheProvider.ts +0 -0
  388. /package/src/{cache-redis → cache/redis}/index.ts +0 -0
  389. /package/src/{cache-redis → cache/redis}/providers/RedisCacheProvider.ts +0 -0
  390. /package/src/lock/{index.ts → core/index.ts} +0 -0
  391. /package/src/lock/{primitives → core/primitives}/$lock.ts +0 -0
  392. /package/src/lock/{providers → core/providers}/LockProvider.ts +0 -0
  393. /package/src/lock/{providers → core/providers}/LockTopicProvider.ts +0 -0
  394. /package/src/lock/{providers → core/providers}/MemoryLockProvider.ts +0 -0
  395. /package/src/{lock-redis → lock/redis}/index.ts +0 -0
  396. /package/src/{lock-redis → lock/redis}/providers/RedisLockProvider.ts +0 -0
  397. /package/src/queue/{primitives → core/primitives}/$consumer.ts +0 -0
  398. /package/src/{queue-redis → queue/redis}/index.ts +0 -0
  399. /package/src/{server-auth → server/auth}/constants/routes.ts +0 -0
  400. /package/src/{server-auth → server/auth}/index.browser.ts +0 -0
  401. /package/src/{server-auth → server/auth}/index.shared.ts +0 -0
  402. /package/src/{server-auth → server/auth}/index.ts +0 -0
  403. /package/src/{server-auth → server/auth}/primitives/$authApple.ts +0 -0
  404. /package/src/{server-auth → server/auth}/primitives/$authCredentials.ts +0 -0
  405. /package/src/{server-auth → server/auth}/primitives/$authGithub.ts +0 -0
  406. /package/src/{server-auth → server/auth}/primitives/$authGoogle.ts +0 -0
  407. /package/src/{server-auth → server/auth}/schemas/authenticationProviderSchema.ts +0 -0
  408. /package/src/{server-auth → server/auth}/schemas/tokenResponseSchema.ts +0 -0
  409. /package/src/{server-auth → server/auth}/schemas/tokensSchema.ts +0 -0
  410. /package/src/{server-auth → server/auth}/schemas/userinfoResponseSchema.ts +0 -0
  411. /package/src/{server-cache → server/cache}/index.ts +0 -0
  412. /package/src/{server-cache → server/cache}/providers/ServerCacheProvider.ts +0 -0
  413. /package/src/{server-compress → server/compress}/index.ts +0 -0
  414. /package/src/{server-compress → server/compress}/providers/ServerCompressProvider.ts +0 -0
  415. /package/src/{server-cookies → server/cookies}/index.browser.ts +0 -0
  416. /package/src/{server-cookies → server/cookies}/primitives/$cookie.browser.ts +0 -0
  417. /package/src/{server-cookies → server/cookies}/primitives/$cookie.ts +0 -0
  418. /package/src/{server-cookies → server/cookies}/providers/ServerCookiesProvider.ts +0 -0
  419. /package/src/{server-cookies → server/cookies}/services/CookieParser.ts +0 -0
  420. /package/src/server/{constants → core/constants}/routeMethods.ts +0 -0
  421. /package/src/server/{errors → core/errors}/BadRequestError.ts +0 -0
  422. /package/src/server/{errors → core/errors}/ConflictError.ts +0 -0
  423. /package/src/server/{errors → core/errors}/ForbiddenError.ts +0 -0
  424. /package/src/server/{errors → core/errors}/HttpError.ts +0 -0
  425. /package/src/server/{errors → core/errors}/NotFoundError.ts +0 -0
  426. /package/src/server/{errors → core/errors}/UnauthorizedError.ts +0 -0
  427. /package/src/server/{errors → core/errors}/ValidationError.ts +0 -0
  428. /package/src/server/{helpers → core/helpers}/ServerReply.ts +0 -0
  429. /package/src/server/{helpers → core/helpers}/isMultipart.ts +0 -0
  430. /package/src/server/{index.browser.ts → core/index.browser.ts} +0 -0
  431. /package/src/server/{index.shared.ts → core/index.shared.ts} +0 -0
  432. /package/src/server/{interfaces → core/interfaces}/ServerRequest.ts +0 -0
  433. /package/src/server/{primitives → core/primitives}/$route.ts +0 -0
  434. /package/src/server/{providers → core/providers}/BunHttpServerProvider.ts +0 -0
  435. /package/src/server/{providers → core/providers}/NodeHttpServerProvider.ts +0 -0
  436. /package/src/server/{providers → core/providers}/ServerLoggerProvider.ts +0 -0
  437. /package/src/server/{providers → core/providers}/ServerNotReadyProvider.ts +0 -0
  438. /package/src/server/{providers → core/providers}/ServerProvider.ts +0 -0
  439. /package/src/server/{providers → core/providers}/ServerTimingProvider.ts +0 -0
  440. /package/src/server/{schemas → core/schemas}/errorSchema.ts +0 -0
  441. /package/src/server/{schemas → core/schemas}/okSchema.ts +0 -0
  442. /package/src/server/{services → core/services}/HttpClient.ts +0 -0
  443. /package/src/server/{services → core/services}/ServerRequestParser.ts +0 -0
  444. /package/src/server/{services → core/services}/UserAgentParser.ts +0 -0
  445. /package/src/{server-cors → server/cors}/index.ts +0 -0
  446. /package/src/{server-cors → server/cors}/primitives/$cors.ts +0 -0
  447. /package/src/{server-cors → server/cors}/providers/ServerCorsProvider.ts +0 -0
  448. /package/src/{server-health → server/health}/index.ts +0 -0
  449. /package/src/{server-health → server/health}/providers/ServerHealthProvider.ts +0 -0
  450. /package/src/{server-health → server/health}/schemas/healthSchema.ts +0 -0
  451. /package/src/{server-helmet → server/helmet}/index.ts +0 -0
  452. /package/src/{server-helmet → server/helmet}/providers/ServerHelmetProvider.ts +0 -0
  453. /package/src/{server-links → server/links}/index.browser.ts +0 -0
  454. /package/src/{server-links → server/links}/index.ts +0 -0
  455. /package/src/{server-links → server/links}/primitives/$client.ts +0 -0
  456. /package/src/{server-links → server/links}/primitives/$remote.ts +0 -0
  457. /package/src/{server-links → server/links}/providers/LinkProvider.ts +0 -0
  458. /package/src/{server-links → server/links}/providers/RemotePrimitiveProvider.ts +0 -0
  459. /package/src/{server-links → server/links}/providers/ServerLinksProvider.ts +0 -0
  460. /package/src/{server-links → server/links}/schemas/apiLinksResponseSchema.ts +0 -0
  461. /package/src/{server-metrics → server/metrics}/index.ts +0 -0
  462. /package/src/{server-metrics → server/metrics}/providers/ServerMetricsProvider.ts +0 -0
  463. /package/src/{server-multipart → server/multipart}/index.ts +0 -0
  464. /package/src/{server-multipart → server/multipart}/providers/ServerMultipartProvider.ts +0 -0
  465. /package/src/{server-proxy → server/proxy}/index.ts +0 -0
  466. /package/src/{server-proxy → server/proxy}/primitives/$proxy.ts +0 -0
  467. /package/src/{server-proxy → server/proxy}/providers/ServerProxyProvider.ts +0 -0
  468. /package/src/{server-rate-limit → server/rate-limit}/primitives/$rateLimit.ts +0 -0
  469. /package/src/{server-rate-limit → server/rate-limit}/providers/ServerRateLimitProvider.ts +0 -0
  470. /package/src/{server-security → server/security}/index.browser.ts +0 -0
  471. /package/src/{server-security → server/security}/index.ts +0 -0
  472. /package/src/{server-security → server/security}/primitives/$basicAuth.ts +0 -0
  473. /package/src/{server-security → server/security}/providers/ServerBasicAuthProvider.ts +0 -0
  474. /package/src/{server-security → server/security}/providers/ServerSecurityProvider.ts +0 -0
  475. /package/src/{server-static → server/static}/index.ts +0 -0
  476. /package/src/{server-static → server/static}/primitives/$serve.ts +0 -0
  477. /package/src/{server-static → server/static}/providers/ServerStaticProvider.ts +0 -0
  478. /package/src/{server-swagger → server/swagger}/index.ts +0 -0
  479. /package/src/{server-swagger → server/swagger}/primitives/$swagger.ts +0 -0
  480. /package/src/topic/{errors → core/errors}/TopicTimeoutError.ts +0 -0
  481. /package/src/topic/{index.ts → core/index.ts} +0 -0
  482. /package/src/topic/{primitives → core/primitives}/$subscriber.ts +0 -0
  483. /package/src/topic/{primitives → core/primitives}/$topic.ts +0 -0
  484. /package/src/topic/{providers → core/providers}/MemoryTopicProvider.ts +0 -0
  485. /package/src/topic/{providers → core/providers}/TopicProvider.ts +0 -0
  486. /package/src/{topic-redis → topic/redis}/index.ts +0 -0
  487. /package/src/{topic-redis → topic/redis}/providers/RedisTopicProvider.ts +0 -0
@@ -13,6 +13,19 @@ export interface CopyAssetsOptions {
13
13
  * Output directory for copied assets.
14
14
  */
15
15
  distDir: string;
16
+
17
+ /**
18
+ * @default process.cwd()
19
+ */
20
+ root?: string;
21
+
22
+ /**
23
+ * Add Runner for logging (@see Alepha CLI)
24
+ */
25
+ run?: (opts: {
26
+ name: string;
27
+ handler: () => Promise<void>;
28
+ }) => Promise<string>;
16
29
  }
17
30
 
18
31
  /**
@@ -25,7 +38,7 @@ export interface CopyAssetsOptions {
25
38
  * Used by modules like AlephaServerSwagger to distribute UI files.
26
39
  */
27
40
  export async function copyAssets(opts: CopyAssetsOptions): Promise<void> {
28
- const root = process.cwd();
41
+ const root = opts.root ?? process.cwd();
29
42
  const alepha = await importAlepha(opts.entry);
30
43
  const assets = alepha.store.get("alepha.build.assets");
31
44
 
@@ -33,13 +46,24 @@ export async function copyAssets(opts: CopyAssetsOptions): Promise<void> {
33
46
  return;
34
47
  }
35
48
 
36
- const require = createRequire(join(root, opts.entry));
37
- const buildAssetsDir = join(root, `${opts.distDir}/assets`);
38
- await mkdir(buildAssetsDir).catch(() => null);
49
+ const fn = async () => {
50
+ const require = createRequire(join(root, opts.entry));
51
+ const buildAssetsDir = join(root, `${opts.distDir}/assets`);
52
+ await mkdir(buildAssetsDir).catch(() => null);
53
+
54
+ for (const pkgName of assets ?? []) {
55
+ const pkgDir = dirname(require.resolve(`${pkgName}/package.json`));
56
+ const assetsPkgDir = resolve(pkgDir, "assets");
57
+ await cp(assetsPkgDir, buildAssetsDir, { recursive: true });
58
+ }
59
+ };
39
60
 
40
- for (const pkgName of assets ?? []) {
41
- const pkgDir = dirname(require.resolve(`${pkgName}/package.json`));
42
- const assetsPkgDir = resolve(pkgDir, "assets");
43
- await cp(assetsPkgDir, buildAssetsDir, { recursive: true });
61
+ if (opts.run) {
62
+ await opts.run({
63
+ name: "copy assets",
64
+ handler: fn,
65
+ });
66
+ } else {
67
+ await fn();
44
68
  }
45
69
  }
@@ -1,5 +1,5 @@
1
- import { writeFileSync } from "node:fs";
2
- import { join } from "node:path";
1
+ import { writeFile } from "node:fs/promises";
2
+ import { basename, join } from "node:path";
3
3
 
4
4
  export interface GenerateCloudflareOptions {
5
5
  /**
@@ -24,43 +24,59 @@ const WARNING_COMMENT =
24
24
  export async function generateCloudflare(
25
25
  opts: GenerateCloudflareOptions = {},
26
26
  ): Promise<void> {
27
- const dist = opts.distDir ?? "dist";
27
+ const distDir = opts.distDir ?? "dist";
28
28
  const root = process.cwd();
29
+ const name = basename(root);
29
30
 
30
- // Get current directory name for worker name
31
- const parts = root.split("/");
32
- const name = parts[parts.length - 1];
31
+ await writeWranglerConfig(root, distDir, name);
32
+ await writeWorkerEntryPoint(root, distDir);
33
+ }
33
34
 
35
+ /**
36
+ * Write the wrangler.jsonc configuration file for Cloudflare Workers
37
+ */
38
+ async function writeWranglerConfig(
39
+ root: string,
40
+ distDir: string,
41
+ name: string,
42
+ ): Promise<void> {
34
43
  const wrangler = {
35
- name: name,
36
- main: "./worker.js",
44
+ name,
45
+ main: "./main.cloudflare.js",
37
46
  compatibility_flags: ["nodejs_compat"],
38
47
  compatibility_date: "2025-11-17",
39
48
  };
40
49
 
41
- writeFileSync(
42
- join(root, dist, "wrangler.jsonc"),
50
+ await writeFile(
51
+ join(root, distDir, "wrangler.jsonc"),
43
52
  JSON.stringify(wrangler, null, 2),
44
53
  );
54
+ }
45
55
 
56
+ /**
57
+ * Write the worker entry point that bootstraps Alepha and handles fetch requests
58
+ */
59
+ async function writeWorkerEntryPoint(
60
+ root: string,
61
+ distDir: string,
62
+ ): Promise<void> {
46
63
  const workerCode = `
47
- process.env.ALEPHA_SERVERLESS = "true";
48
-
49
- await import("./index.js");
64
+ import "./index.js";
50
65
 
51
66
  export default {
52
- fetch: async (request, env, ctx) => {
67
+ fetch: async (request) => {
68
+ const ctx = { req: request, res: undefined };
53
69
 
54
70
  await __alepha.start();
55
- const ev = { req: request, res: undefined };
56
- await __alepha.events.emit("web:request", ev);
57
- return ev.res;
71
+ await __alepha.events.emit("web:request", ctx);
72
+
73
+ return ctx.res;
58
74
  },
59
75
  };
60
76
  `.trim();
61
77
 
62
- writeFileSync(
63
- join(root, dist, "worker.js"),
78
+ await writeFile(
79
+ join(root, distDir, "main.cloudflare.js"),
64
80
  `${WARNING_COMMENT}\n${workerCode}`.trim(),
65
81
  );
66
82
  }
@@ -38,14 +38,28 @@ export async function generateDocker(
38
38
  const image = opts.image ?? "node:24-alpine";
39
39
  const command = opts.command ?? "node";
40
40
 
41
- // Copy drizzle migrations if they exist
41
+ await copyDrizzleMigrations(distDir);
42
+ await writeDockerfile(distDir, image, command);
43
+ }
44
+
45
+ /**
46
+ * Copy drizzle migrations to the dist directory if they exist
47
+ */
48
+ async function copyDrizzleMigrations(distDir: string): Promise<void> {
42
49
  const hasMigrations = await fileExists("drizzle");
43
50
  if (hasMigrations) {
44
- await cp("drizzle", `${distDir}/drizzle`, {
45
- recursive: true,
46
- });
51
+ await cp("drizzle", `${distDir}/drizzle`, { recursive: true });
47
52
  }
53
+ }
48
54
 
55
+ /**
56
+ * Write the Dockerfile with the specified base image and command
57
+ */
58
+ async function writeDockerfile(
59
+ distDir: string,
60
+ image: string,
61
+ command: string,
62
+ ): Promise<void> {
49
63
  const dockerfile = `# This file was automatically generated. DO NOT MODIFY.
50
64
  # Changes to this file will be lost when the code is regenerated.
51
65
  FROM ${image}
@@ -28,13 +28,11 @@ export interface GenerateSitemapOptions {
28
28
  export async function generateSitemap(
29
29
  opts: GenerateSitemapOptions,
30
30
  ): Promise<string> {
31
- const alepha = await importAlepha(opts.entry, {
32
- env: opts.template
33
- ? {
34
- REACT_SERVER_TEMPLATE: opts.template,
35
- }
36
- : {},
37
- });
31
+ const alepha = await importAlepha(opts.entry);
32
+
33
+ if (opts.template) {
34
+ alepha.set("alepha.react.server.template", opts.template);
35
+ }
38
36
 
39
37
  if (!alepha.isConfigured()) {
40
38
  await alepha.events.emit("configure", alepha);
@@ -1,4 +1,4 @@
1
- import { existsSync, mkdirSync, writeFileSync } from "node:fs";
1
+ import { mkdir, stat, writeFile } from "node:fs/promises";
2
2
  import { importVite } from "../helpers/importVite.ts";
3
3
 
4
4
  export interface GenerateVercelOptions {
@@ -52,33 +52,62 @@ export async function generateVercel(
52
52
  const distDir = opts.distDir ?? "dist";
53
53
  const clientDir = opts.clientDir ?? "public";
54
54
  const { loadEnv } = await importVite();
55
-
56
55
  const env = loadEnv("production", process.cwd(), "");
57
56
 
58
- // Ensure the api directory exists
59
- if (!existsSync(`${distDir}/api`)) {
60
- mkdirSync(`${distDir}/api`);
57
+ await writeApiEntryPoint(distDir);
58
+ await writeVercelConfig(distDir, clientDir, opts.config?.config);
59
+
60
+ const projectId = env.VERCEL_PROJECT_ID ?? opts.config?.projectId;
61
+ const projectName = env.VERCEL_PROJECT_NAME ?? opts.config?.projectName;
62
+ const orgId = env.VERCEL_ORG_ID ?? opts.config?.orgId;
63
+
64
+ if (projectId && orgId) {
65
+ await writeProjectConfig(distDir, projectId, projectName, orgId);
61
66
  }
62
67
 
63
- // Add the only one entry point for Vercel
64
- writeFileSync(
68
+ await ensureClientDir(distDir, clientDir);
69
+ }
70
+
71
+ /**
72
+ * Check if a file or directory exists at the given path
73
+ */
74
+ async function exists(path: string): Promise<boolean> {
75
+ return stat(path)
76
+ .then(() => true)
77
+ .catch(() => false);
78
+ }
79
+
80
+ /**
81
+ * Create the serverless function entry point that bootstraps Alepha and handles requests
82
+ */
83
+ async function writeApiEntryPoint(distDir: string): Promise<void> {
84
+ await mkdir(`${distDir}/api`, { recursive: true });
85
+ await writeFile(
65
86
  `${distDir}/api/index.js`,
66
87
  `${WARNING_COMMENT}
67
88
  import "../index.js";
68
89
 
69
- export default async function (req, res) {
90
+ export default async (req, res) => {
70
91
  \tawait __alepha.start();
71
- \t__alepha.events.emit("node:request", { req, res });
92
+ \tawait __alepha.events.emit("node:request", { req, res });
72
93
  }
73
94
  `,
74
95
  );
96
+ }
75
97
 
76
- // Always generate a vercel.json file
77
- writeFileSync(
98
+ /**
99
+ * Generate vercel.json with route rewrites to direct all traffic to the serverless function
100
+ */
101
+ async function writeVercelConfig(
102
+ distDir: string,
103
+ clientDir: string,
104
+ config?: VercelConfig["config"],
105
+ ): Promise<void> {
106
+ await writeFile(
78
107
  `${distDir}/vercel.json`,
79
108
  JSON.stringify(
80
109
  {
81
- ...opts?.config?.config,
110
+ ...config,
82
111
  rewrites: [
83
112
  {
84
113
  source: "/(.*)",
@@ -93,36 +122,42 @@ export default async function (req, res) {
93
122
  " ",
94
123
  ),
95
124
  );
125
+ }
96
126
 
97
- // Generate .vercel/project.json if VERCEL_PROJECT_ID and VERCEL_ORG_ID are set
98
- const projectId = env.VERCEL_PROJECT_ID ?? opts.config?.projectId;
99
- const projectName = env.VERCEL_PROJECT_NAME ?? opts.config?.projectName;
100
- const orgId = env.VERCEL_ORG_ID ?? opts.config?.orgId;
101
-
102
- if (projectId && orgId) {
103
- try {
104
- mkdirSync(`${distDir}/.vercel`, { recursive: true });
105
- } catch (_e) {
106
- // Ignore error if directory already exists
107
- }
108
-
109
- writeFileSync(
110
- `${distDir}/.vercel/project.json`,
111
- JSON.stringify(
112
- {
113
- projectId,
114
- projectName,
115
- orgId,
116
- },
117
- null,
118
- " ",
119
- ),
120
- );
121
- }
127
+ /**
128
+ * Generate .vercel/project.json to link the deployment to a Vercel project
129
+ */
130
+ async function writeProjectConfig(
131
+ distDir: string,
132
+ projectId: string,
133
+ projectName: string | undefined,
134
+ orgId: string,
135
+ ): Promise<void> {
136
+ await mkdir(`${distDir}/.vercel`, { recursive: true });
137
+ await writeFile(
138
+ `${distDir}/.vercel/project.json`,
139
+ JSON.stringify(
140
+ {
141
+ projectId,
142
+ projectName,
143
+ orgId,
144
+ },
145
+ null,
146
+ " ",
147
+ ),
148
+ );
149
+ }
122
150
 
123
- // If /public does not exist, create an empty one to avoid Vercel errors
124
- if (!existsSync(`${distDir}/${clientDir}`)) {
125
- mkdirSync(`${distDir}/${clientDir}`, { recursive: true });
126
- writeFileSync(`${distDir}/${clientDir}/.keep`, "");
151
+ /**
152
+ * Create the client directory with a .keep file if it doesn't exist to avoid Vercel errors
153
+ */
154
+ async function ensureClientDir(
155
+ distDir: string,
156
+ clientDir: string,
157
+ ): Promise<void> {
158
+ const path = `${distDir}/${clientDir}`;
159
+ if (!(await exists(path))) {
160
+ await mkdir(path, { recursive: true });
161
+ await writeFile(`${path}/.keep`, "");
127
162
  }
128
163
  }
@@ -91,6 +91,18 @@ export class AlephaRunner {
91
91
  async start(server: ViteDevServer): Promise<void> {
92
92
  const { loadEnv } = await importVite();
93
93
 
94
+ // unfortunately, vite SSR loader does not fix stack traces automatically
95
+ // for now, we make a global helper and alepha/logger will use it before logging errors
96
+ // that's not ideal but works for now
97
+ (global as any).ssrFixStacktrace = (e: Error) => {
98
+ server.ssrFixStacktrace(e);
99
+ let it: any = e;
100
+ do {
101
+ server.ssrFixStacktrace(it);
102
+ it = it.cause;
103
+ } while (it instanceof Error);
104
+ };
105
+
94
106
  if (this.state.started) {
95
107
  await this.restart(server, true);
96
108
  return;
@@ -130,7 +142,9 @@ export class AlephaRunner {
130
142
 
131
143
  try {
132
144
  const now = Date.now();
133
- await server.ssrLoadModule(fileUrl);
145
+ await server.ssrLoadModule(fileUrl, {
146
+ fixStacktrace: true,
147
+ });
134
148
  this.state.log(`[DEBUG] Alepha app loaded in ${Date.now() - now}ms`);
135
149
  await new Promise((r) => setTimeout(r, 10));
136
150
 
@@ -160,6 +174,7 @@ export class AlephaRunner {
160
174
  if (e.cause instanceof Error) {
161
175
  server.ssrFixStacktrace(e.cause);
162
176
  }
177
+
163
178
  this.state.app?.log?.error("App failed to start:", e);
164
179
  this.state.app?.log?.info("Waiting for changes to restart...");
165
180
  }
@@ -1,13 +1,5 @@
1
1
  import type { IncomingMessage } from "node:http";
2
- import {
3
- $env,
4
- $hook,
5
- $inject,
6
- Alepha,
7
- type Static,
8
- TypeBoxValue,
9
- t,
10
- } from "alepha";
2
+ import { $env, $hook, $inject, Alepha, type Static, t, Value } from "alepha";
11
3
  import { $logger } from "alepha/logger";
12
4
  import { WebSocket, WebSocketServer } from "ws";
13
5
  import { WebSocketValidationError } from "../errors/WebSocketError.ts";
@@ -507,8 +499,8 @@ export class NodeWebSocketConnection implements WebSocketConnection {
507
499
 
508
500
  // Validate message against schema (out = client→server)
509
501
  const outSchema = this.endpoint.channel.options.schema.out;
510
- if (!TypeBoxValue.Check(outSchema, message)) {
511
- const errors = Array.from(TypeBoxValue.Errors(outSchema, message));
502
+ if (!Value.Check(outSchema, message)) {
503
+ const errors = Array.from(Value.Errors(outSchema, message));
512
504
  throw new WebSocketValidationError(
513
505
  `Message validation failed: ${errors.map((e: any) => e.message).join(", ")}`,
514
506
  );
@@ -4,8 +4,8 @@ import {
4
4
  Alepha,
5
5
  AlephaError,
6
6
  type Static,
7
- TypeBoxValue,
8
7
  t,
8
+ Value,
9
9
  } from "alepha";
10
10
  import { $logger } from "alepha/logger";
11
11
  import type { ChannelPrimitive, TWSObject } from "../primitives/$channel.ts";
@@ -332,8 +332,8 @@ export class WebSocketChannelConnection<
332
332
 
333
333
  // Validate outgoing message against schema
334
334
  const outSchema = this.channel.options.schema.out;
335
- if (!TypeBoxValue.Check(outSchema, message)) {
336
- const errors = Array.from(TypeBoxValue.Errors(outSchema, message));
335
+ if (!Value.Check(outSchema, message)) {
336
+ const errors = Array.from(Value.Errors(outSchema, message));
337
337
  this.log.warn("Message validation failed", { errors });
338
338
  throw new Error(
339
339
  `Message validation failed: ${errors.map((e) => e.message).join(", ")}`,
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.browser.js","names":[],"sources":["../../src/api-files/entities/files.ts","../../src/api-files/schemas/fileQuerySchema.ts","../../src/api-files/schemas/fileResourceSchema.ts","../../src/api-files/schemas/storageStatsSchema.ts","../../src/api-files/index.browser.ts"],"sourcesContent":["import { type Static, t } from \"alepha\";\nimport { $entity, pg } from \"alepha/orm\";\n\nexport const files = $entity({\n name: \"files\",\n schema: t.object({\n id: pg.primaryKey(t.uuid()),\n version: pg.version(),\n createdAt: pg.createdAt(),\n updatedAt: pg.updatedAt(),\n blobId: t.text(),\n creator: t.optional(t.uuid()),\n creatorRealm: t.optional(t.string()),\n creatorName: t.optional(t.string()),\n bucket: t.text(),\n expirationDate: t.optional(t.datetime()),\n name: t.text(),\n size: t.number(),\n mimeType: t.string(),\n tags: t.optional(t.array(t.text())),\n checksum: t.optional(t.string()),\n }),\n indexes: [\n \"expirationDate\",\n \"bucket\",\n \"creator\",\n \"createdAt\",\n \"mimeType\",\n {\n columns: [\"bucket\", \"createdAt\"],\n },\n ],\n});\n\nexport type FileEntity = Static<typeof files.schema>;\n","import type { Static } from \"alepha\";\nimport { t } from \"alepha\";\nimport { pageQuerySchema } from \"alepha/orm\";\n\nexport const fileQuerySchema = t.extend(pageQuerySchema, {\n bucket: t.optional(t.string()),\n tags: t.optional(t.array(t.string())),\n name: t.optional(t.string()),\n mimeType: t.optional(t.string()),\n creator: t.optional(t.uuid()),\n createdAfter: t.optional(t.datetime()),\n createdBefore: t.optional(t.datetime()),\n});\n\nexport type FileQuery = Static<typeof fileQuerySchema>;\n","import { type Static, t } from \"alepha\";\nimport { files } from \"../entities/files.ts\";\n\nexport const fileResourceSchema = t.extend(\n files.schema,\n {},\n {\n title: \"FileResource\",\n description: \"A file resource representing a file stored in the system.\",\n },\n);\n\nexport type FileResource = Static<typeof fileResourceSchema>;\n","import type { Static } from \"alepha\";\nimport { t } from \"alepha\";\n\nexport const bucketStatsSchema = t.object({\n bucket: t.string(),\n totalSize: t.number(),\n fileCount: t.number(),\n});\n\nexport const mimeTypeStatsSchema = t.object({\n mimeType: t.string(),\n fileCount: t.number(),\n});\n\nexport const storageStatsSchema = t.object({\n totalSize: t.number(),\n totalFiles: t.number(),\n byBucket: t.array(bucketStatsSchema),\n byMimeType: t.array(mimeTypeStatsSchema),\n});\n\nexport type BucketStats = Static<typeof bucketStatsSchema>;\nexport type MimeTypeStats = Static<typeof mimeTypeStatsSchema>;\nexport type StorageStats = Static<typeof storageStatsSchema>;\n","import { $module } from \"alepha\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport type * from \"./controllers/FileController.ts\";\nexport type * from \"./controllers/StorageStatsController.ts\";\nexport * from \"./entities/files.ts\";\nexport * from \"./schemas/fileQuerySchema.ts\";\nexport * from \"./schemas/fileResourceSchema.ts\";\nexport * from \"./schemas/storageStatsSchema.ts\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport const AlephaApiFiles = $module({\n name: \"alepha.api.files\",\n services: [],\n});\n"],"mappings":";;;;AAGA,MAAa,QAAQ,QAAQ;CAC3B,MAAM;CACN,QAAQ,EAAE,OAAO;EACf,IAAI,GAAG,WAAW,EAAE,MAAM,CAAC;EAC3B,SAAS,GAAG,SAAS;EACrB,WAAW,GAAG,WAAW;EACzB,WAAW,GAAG,WAAW;EACzB,QAAQ,EAAE,MAAM;EAChB,SAAS,EAAE,SAAS,EAAE,MAAM,CAAC;EAC7B,cAAc,EAAE,SAAS,EAAE,QAAQ,CAAC;EACpC,aAAa,EAAE,SAAS,EAAE,QAAQ,CAAC;EACnC,QAAQ,EAAE,MAAM;EAChB,gBAAgB,EAAE,SAAS,EAAE,UAAU,CAAC;EACxC,MAAM,EAAE,MAAM;EACd,MAAM,EAAE,QAAQ;EAChB,UAAU,EAAE,QAAQ;EACpB,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;EACnC,UAAU,EAAE,SAAS,EAAE,QAAQ,CAAC;EACjC,CAAC;CACF,SAAS;EACP;EACA;EACA;EACA;EACA;EACA,EACE,SAAS,CAAC,UAAU,YAAY,EACjC;EACF;CACF,CAAC;;;;AC5BF,MAAa,kBAAkB,EAAE,OAAO,iBAAiB;CACvD,QAAQ,EAAE,SAAS,EAAE,QAAQ,CAAC;CAC9B,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,QAAQ,CAAC,CAAC;CACrC,MAAM,EAAE,SAAS,EAAE,QAAQ,CAAC;CAC5B,UAAU,EAAE,SAAS,EAAE,QAAQ,CAAC;CAChC,SAAS,EAAE,SAAS,EAAE,MAAM,CAAC;CAC7B,cAAc,EAAE,SAAS,EAAE,UAAU,CAAC;CACtC,eAAe,EAAE,SAAS,EAAE,UAAU,CAAC;CACxC,CAAC;;;;ACTF,MAAa,qBAAqB,EAAE,OAClC,MAAM,QACN,EAAE,EACF;CACE,OAAO;CACP,aAAa;CACd,CACF;;;;ACPD,MAAa,oBAAoB,EAAE,OAAO;CACxC,QAAQ,EAAE,QAAQ;CAClB,WAAW,EAAE,QAAQ;CACrB,WAAW,EAAE,QAAQ;CACtB,CAAC;AAEF,MAAa,sBAAsB,EAAE,OAAO;CAC1C,UAAU,EAAE,QAAQ;CACpB,WAAW,EAAE,QAAQ;CACtB,CAAC;AAEF,MAAa,qBAAqB,EAAE,OAAO;CACzC,WAAW,EAAE,QAAQ;CACrB,YAAY,EAAE,QAAQ;CACtB,UAAU,EAAE,MAAM,kBAAkB;CACpC,YAAY,EAAE,MAAM,oBAAoB;CACzC,CAAC;;;;ACNF,MAAa,iBAAiB,QAAQ;CACpC,MAAM;CACN,UAAU,EAAE;CACb,CAAC"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.js","names":["expirationDate: string | undefined","updateData: Partial<FileEntity>","files"],"sources":["../../src/api-files/schemas/fileQuerySchema.ts","../../src/api-files/entities/files.ts","../../src/api-files/schemas/fileResourceSchema.ts","../../src/api-files/services/FileService.ts","../../src/api-files/controllers/FileController.ts","../../src/api-files/schemas/storageStatsSchema.ts","../../src/api-files/controllers/StorageStatsController.ts","../../src/api-files/jobs/FileJobs.ts","../../src/api-files/index.ts"],"sourcesContent":["import type { Static } from \"alepha\";\nimport { t } from \"alepha\";\nimport { pageQuerySchema } from \"alepha/orm\";\n\nexport const fileQuerySchema = t.extend(pageQuerySchema, {\n bucket: t.optional(t.string()),\n tags: t.optional(t.array(t.string())),\n name: t.optional(t.string()),\n mimeType: t.optional(t.string()),\n creator: t.optional(t.uuid()),\n createdAfter: t.optional(t.datetime()),\n createdBefore: t.optional(t.datetime()),\n});\n\nexport type FileQuery = Static<typeof fileQuerySchema>;\n","import { type Static, t } from \"alepha\";\nimport { $entity, pg } from \"alepha/orm\";\n\nexport const files = $entity({\n name: \"files\",\n schema: t.object({\n id: pg.primaryKey(t.uuid()),\n version: pg.version(),\n createdAt: pg.createdAt(),\n updatedAt: pg.updatedAt(),\n blobId: t.text(),\n creator: t.optional(t.uuid()),\n creatorRealm: t.optional(t.string()),\n creatorName: t.optional(t.string()),\n bucket: t.text(),\n expirationDate: t.optional(t.datetime()),\n name: t.text(),\n size: t.number(),\n mimeType: t.string(),\n tags: t.optional(t.array(t.text())),\n checksum: t.optional(t.string()),\n }),\n indexes: [\n \"expirationDate\",\n \"bucket\",\n \"creator\",\n \"createdAt\",\n \"mimeType\",\n {\n columns: [\"bucket\", \"createdAt\"],\n },\n ],\n});\n\nexport type FileEntity = Static<typeof files.schema>;\n","import { type Static, t } from \"alepha\";\nimport { files } from \"../entities/files.ts\";\n\nexport const fileResourceSchema = t.extend(\n files.schema,\n {},\n {\n title: \"FileResource\",\n description: \"A file resource representing a file stored in the system.\",\n },\n);\n\nexport type FileResource = Static<typeof fileResourceSchema>;\n","import { createHash } from \"node:crypto\";\nimport { $hook, $inject, Alepha, type FileLike } from \"alepha\";\nimport {\n $bucket,\n type BucketPrimitive,\n FileNotFoundError,\n} from \"alepha/bucket\";\nimport {\n type DateTime,\n DateTimeProvider,\n type DurationLike,\n} from \"alepha/datetime\";\nimport { $logger } from \"alepha/logger\";\nimport { $repository, type Page } from \"alepha/orm\";\nimport type { UserAccountToken } from \"alepha/security\";\nimport type { Ok } from \"alepha/server\";\nimport { NotFoundError } from \"alepha/server\";\nimport { type FileEntity, files } from \"../entities/files.ts\";\nimport type { FileQuery } from \"../schemas/fileQuerySchema.ts\";\nimport type { FileResource } from \"../schemas/fileResourceSchema.ts\";\nimport type { StorageStats } from \"../schemas/storageStatsSchema.ts\";\n\nexport class FileService {\n protected readonly alepha = $inject(Alepha);\n protected readonly log = $logger();\n protected readonly fileRepository = $repository(files);\n protected readonly dateTimeProvider = $inject(DateTimeProvider);\n protected readonly defaultBucket = $bucket({ name: \"default\" });\n\n protected onUploadFile = $hook({\n on: \"bucket:file:uploaded\",\n handler: async ({ file, bucket, options, id }) => {\n if (options.persist === false) {\n return;\n }\n\n const checksum = await this.calculateChecksum(file);\n\n await this.fileRepository.create({\n blobId: id,\n mimeType: file.type,\n name: file.name,\n size: file.size,\n creator: options.user?.id,\n creatorRealm: options.user?.realm,\n expirationDate: this.getExpirationDate(options.ttl),\n bucket: bucket.name,\n checksum,\n });\n },\n });\n\n protected onDeleteBucketFile = $hook({\n on: \"bucket:file:deleted\",\n handler: async ({ bucket, id }) => {\n await this.fileRepository.deleteMany({\n blobId: { eq: id },\n bucket: { eq: bucket.name },\n });\n },\n });\n\n // -------------------------------------------------------------------------------------------------------------------\n\n /**\n * Calculates SHA-256 checksum of a file.\n *\n * @param file - The file to calculate checksum for\n * @returns Hexadecimal string representation of the SHA-256 hash\n * @protected\n */\n protected async calculateChecksum(file: FileLike): Promise<string> {\n const buffer = await file.arrayBuffer();\n const hash = createHash(\"sha256\");\n hash.update(Buffer.from(buffer));\n return hash.digest(\"hex\");\n }\n\n /**\n * Gets a bucket primitive by name.\n *\n * @param bucketName - The name of the bucket to retrieve (defaults to \"default\")\n * @returns The bucket primitive\n * @throws {NotFoundError} If the bucket is not found\n */\n public bucket(bucketName: string = this.defaultBucket.name): BucketPrimitive {\n const bucket = this.alepha\n .primitives($bucket)\n .find((it) => it.name === bucketName);\n\n if (!bucket) {\n throw new NotFoundError(`Bucket '${bucketName}' not found.`);\n }\n\n return bucket;\n }\n\n // -------------------------------------------------------------------------------------------------------------------\n\n /**\n * Finds files matching the given query criteria with pagination support.\n * Supports filtering by bucket, tags, name, mimeType, creator, and date range.\n *\n * @param q - Query parameters including bucket, tags, name, mimeType, creator, date range, pagination, and sorting\n * @returns Paginated list of file entities\n */\n public async findFiles(q: FileQuery = {}): Promise<Page<FileEntity>> {\n q.sort ??= \"-createdAt\";\n\n const where = this.fileRepository.createQueryWhere();\n\n if (q.bucket) {\n where.bucket = { eq: q.bucket };\n }\n\n if (q.tags) {\n where.tags = { arrayContains: q.tags };\n }\n\n if (q.name) {\n where.name = { ilike: `%${q.name}%` };\n }\n\n if (q.mimeType) {\n where.mimeType = { eq: q.mimeType };\n }\n\n if (q.creator) {\n where.creator = { eq: q.creator };\n }\n\n if (q.createdAfter && q.createdBefore) {\n where.createdAt = {\n gte: q.createdAfter,\n lte: q.createdBefore,\n };\n } else if (q.createdAfter) {\n where.createdAt = { gte: q.createdAfter };\n } else if (q.createdBefore) {\n where.createdAt = { lte: q.createdBefore };\n }\n\n return await this.fileRepository\n .paginate(q, { where }, { count: true })\n .then((page) => {\n return {\n ...page,\n content: page.content.map((it) => this.entityToResource(it)),\n };\n });\n }\n\n /**\n * Finds files that have expired based on their expiration date.\n * Limited to 1000 files per call to prevent memory issues.\n *\n * @returns Array of expired file entities\n */\n public async findExpiredFiles(): Promise<FileEntity[]> {\n return await this.fileRepository.findMany({\n limit: 1000,\n where: {\n expirationDate: { lte: this.dateTimeProvider.nowISOString() },\n },\n });\n }\n\n /**\n * Calculates an expiration date based on a TTL (time to live) duration.\n *\n * @param ttl - Duration like \"1 day\", \"2 hours\", etc.\n * @returns DateTime representation of the expiration date, or undefined if no TTL provided\n * @protected\n */\n protected getExpirationDate(ttl?: DurationLike): string | undefined {\n return ttl\n ? this.dateTimeProvider\n .now()\n .add(this.dateTimeProvider.duration(ttl))\n .toISOString()\n : undefined;\n }\n\n /**\n * Uploads a file to a bucket and creates a database record with metadata.\n * Automatically calculates and stores the file checksum (SHA-256).\n *\n * @param file - The file to upload\n * @param options - Upload options including bucket, expiration, user, and tags\n * @param options.bucket - Target bucket name (defaults to \"default\")\n * @param options.expirationDate - When the file should expire\n * @param options.user - User performing the upload (for audit trail)\n * @param options.tags - Tags to associate with the file\n * @returns The created file entity with all metadata\n * @throws {NotFoundError} If the specified bucket doesn't exist\n */\n public async uploadFile(\n file: FileLike,\n options: {\n expirationDate?: string | DateTime;\n bucket?: string;\n user?: UserAccountToken;\n tags?: string[];\n } = {},\n ): Promise<FileEntity> {\n const bucket = this.bucket(options.bucket);\n\n const checksum = await this.calculateChecksum(file);\n const blobId = await bucket.upload(file, { persist: false });\n\n let expirationDate: string | undefined;\n if (options.expirationDate) {\n expirationDate = this.dateTimeProvider\n .of(options.expirationDate)\n .toISOString();\n } else if (bucket.options.ttl) {\n expirationDate = this.getExpirationDate(bucket.options.ttl);\n }\n\n return await this.fileRepository.create({\n blobId: blobId,\n mimeType: file.type,\n name: file.name,\n size: file.size,\n creator: options.user?.id,\n creatorRealm: options.user?.realm,\n creatorName: options.user?.name,\n expirationDate,\n bucket: bucket.name,\n tags: options.tags,\n checksum,\n });\n }\n\n /**\n * Streams a file from storage by its database ID.\n *\n * @param id - The database ID (UUID) of the file to stream\n * @returns The file object ready for streaming/downloading\n * @throws {NotFoundError} If the file doesn't exist in the database\n * @throws {FileNotFoundError} If the file exists in database but not in storage\n */\n public async streamFile(id: string): Promise<FileLike> {\n const entity = await this.getFileById(id);\n const bucket = this.bucket(entity.bucket);\n\n return await bucket.download(entity.blobId);\n }\n\n /**\n * Updates file metadata (name, tags, expiration date).\n * Does not modify the actual file content in storage.\n *\n * @param id - The database ID (UUID) of the file to update\n * @param data - Partial file data to update\n * @param data.name - New file name\n * @param data.tags - New tags array\n * @param data.expirationDate - New expiration date\n * @returns The updated file entity\n * @throws {NotFoundError} If the file doesn't exist in the database\n */\n public async updateFile(\n id: string,\n data: {\n name?: string;\n tags?: string[];\n expirationDate?: DateTime | string;\n },\n ): Promise<FileEntity> {\n const file = await this.getFileById(id);\n\n const updateData: Partial<FileEntity> = {};\n\n if (data.name !== undefined) {\n updateData.name = data.name;\n }\n\n if (data.tags !== undefined) {\n updateData.tags = data.tags;\n }\n\n if (data.expirationDate !== undefined) {\n updateData.expirationDate = this.dateTimeProvider\n .of(data.expirationDate)\n .toISOString();\n }\n\n return await this.fileRepository.updateById(file.id, updateData);\n }\n\n /**\n * Deletes a file from both storage and database.\n * Handles cases where file is already deleted from storage gracefully.\n * Always ensures database record is removed even if storage deletion fails.\n *\n * @param id - The database ID (UUID) of the file to delete\n * @returns Success response with the deleted file ID\n * @throws {NotFoundError} If the file doesn't exist in the database\n */\n public async deleteFile(id: string): Promise<Ok> {\n const file = await this.getFileById(id);\n const bucket = this.bucket(file.bucket);\n\n // Always delete the database record\n await this.fileRepository.deleteById(file.id);\n\n try {\n await bucket.delete(file.blobId, true);\n } catch (e) {\n if (e instanceof FileNotFoundError) {\n // File is already deleted in the bucket, this is okay\n this.log.debug(\n `File ${file.blobId} not found in bucket ${bucket.name}, cleaning up database record`,\n );\n } else {\n // Other errors (permission, network, etc.) - log but continue to clean up database\n this.log.warn(\n `Failed to delete file ${file.blobId} from bucket ${bucket.name}`,\n e,\n );\n }\n }\n\n return { ok: true, id: String(file.id) };\n }\n\n /**\n * Retrieves a file entity by its ID.\n * If already an entity object, returns it as-is (convenience method).\n *\n * @param id - Either a UUID string or an existing FileEntity object\n * @returns The file entity\n * @throws {NotFoundError} If the file doesn't exist in the database\n */\n public async getFileById(id: string | FileEntity): Promise<FileEntity> {\n if (typeof id === \"object\") {\n return id;\n }\n\n return await this.fileRepository.findById(id);\n }\n\n /**\n * Gets storage statistics including total size, file count, and breakdowns by bucket and MIME type.\n *\n * @returns Storage statistics with aggregated data\n */\n public async getStorageStats(): Promise<StorageStats> {\n const allFiles = await this.fileRepository.findMany({});\n\n const totalSize = allFiles.reduce((sum, file) => sum + file.size, 0);\n const totalFiles = allFiles.length;\n\n // Group by bucket\n const bucketMap = new Map<\n string,\n { totalSize: number; fileCount: number }\n >();\n for (const file of allFiles) {\n const existing = bucketMap.get(file.bucket) || {\n totalSize: 0,\n fileCount: 0,\n };\n existing.totalSize += file.size;\n existing.fileCount += 1;\n bucketMap.set(file.bucket, existing);\n }\n\n // Group by MIME type\n const mimeTypeMap = new Map<string, number>();\n for (const file of allFiles) {\n const existing = mimeTypeMap.get(file.mimeType) || 0;\n mimeTypeMap.set(file.mimeType, existing + 1);\n }\n\n return {\n totalSize,\n totalFiles,\n byBucket: Array.from(bucketMap.entries()).map(([bucket, stats]) => ({\n bucket,\n totalSize: stats.totalSize,\n fileCount: stats.fileCount,\n })),\n byMimeType: Array.from(mimeTypeMap.entries()).map(\n ([mimeType, fileCount]) => ({\n mimeType,\n fileCount,\n }),\n ),\n };\n }\n\n /**\n * Converts a file entity to a file resource (API response format).\n * Currently a pass-through, but allows for future transformation logic.\n *\n * @param entity - The file entity to convert\n * @returns The file resource for API responses\n */\n public entityToResource(entity: FileEntity): FileResource {\n return entity;\n }\n}\n","import { $inject, t } from \"alepha\";\nimport { pg } from \"alepha/orm\";\nimport { $action, okSchema } from \"alepha/server\";\nimport { fileQuerySchema } from \"../schemas/fileQuerySchema.ts\";\nimport { fileResourceSchema } from \"../schemas/fileResourceSchema.ts\";\nimport { FileService } from \"../services/FileService.ts\";\n\n/**\n * REST API controller for file management operations.\n * Provides endpoints for uploading, downloading, listing, and deleting files.\n */\nexport class FileController {\n protected readonly url = \"/files\";\n protected readonly group = \"files\";\n protected readonly fileService = $inject(FileService);\n\n /**\n * GET /files - Lists files with optional filtering and pagination.\n * Supports filtering by bucket and tags.\n */\n public readonly findFiles = $action({\n path: this.url,\n group: this.group,\n description: \"List files with filtering and pagination\",\n schema: {\n query: fileQuerySchema,\n response: pg.page(fileResourceSchema),\n },\n handler: ({ query }) => this.fileService.findFiles(query),\n });\n\n /**\n * DELETE /files/:id - Deletes a file from both storage and database.\n * Removes the file from the bucket and cleans up the database record.\n */\n public readonly deleteFile = $action({\n method: \"DELETE\",\n path: `${this.url}/:id`,\n group: this.group,\n description: \"Delete a file\",\n schema: {\n params: t.object({\n id: t.uuid(),\n }),\n response: okSchema,\n },\n handler: ({ params }) => this.fileService.deleteFile(params.id),\n });\n\n /**\n * POST /files - Uploads a new file to storage.\n * Creates a database record with metadata and calculates checksum.\n * Optionally specify bucket and expiration date.\n */\n public readonly uploadFile = $action({\n path: this.url,\n group: this.group,\n description: \"Upload a new file\",\n schema: {\n body: t.object({\n file: t.file(),\n }),\n query: t.object({\n expirationDate: t.optional(t.datetime()),\n bucket: t.optional(t.string()),\n }),\n response: fileResourceSchema,\n },\n handler: async ({ body, user, query }) =>\n this.fileService.uploadFile(body.file, {\n user,\n ...query,\n }),\n });\n\n /**\n * PATCH /files/:id - Updates file metadata.\n * Allows updating name, tags, and expiration date without modifying file content.\n */\n public readonly updateFile = $action({\n method: \"PATCH\",\n path: `${this.url}/:id`,\n group: this.group,\n description: \"Update file metadata\",\n schema: {\n params: t.object({\n id: t.uuid(),\n }),\n body: t.object({\n name: t.optional(t.string()),\n tags: t.optional(t.array(t.string())),\n expirationDate: t.optional(t.datetime()),\n }),\n response: fileResourceSchema,\n },\n handler: ({ params, body }) => this.fileService.updateFile(params.id, body),\n });\n\n /**\n * GET /files/:id - Streams/downloads a file by its ID.\n * Returns the file content with appropriate Content-Type header.\n * Cached with ETag support for 1 year (immutable).\n */\n public readonly streamFile = $action({\n path: `${this.url}/:id`,\n group: this.group,\n description: \"Download a file\",\n cache: {\n etag: true,\n control: {\n public: true,\n maxAge: [1, \"year\"],\n immutable: true,\n },\n },\n schema: {\n params: t.object({\n id: t.uuid(),\n }),\n response: t.file(),\n },\n handler: async ({ params }) => {\n return await this.fileService.streamFile(params.id);\n },\n });\n}\n","import type { Static } from \"alepha\";\nimport { t } from \"alepha\";\n\nexport const bucketStatsSchema = t.object({\n bucket: t.string(),\n totalSize: t.number(),\n fileCount: t.number(),\n});\n\nexport const mimeTypeStatsSchema = t.object({\n mimeType: t.string(),\n fileCount: t.number(),\n});\n\nexport const storageStatsSchema = t.object({\n totalSize: t.number(),\n totalFiles: t.number(),\n byBucket: t.array(bucketStatsSchema),\n byMimeType: t.array(mimeTypeStatsSchema),\n});\n\nexport type BucketStats = Static<typeof bucketStatsSchema>;\nexport type MimeTypeStats = Static<typeof mimeTypeStatsSchema>;\nexport type StorageStats = Static<typeof storageStatsSchema>;\n","import \"alepha/server/security\";\nimport { $inject } from \"alepha\";\nimport { $action } from \"alepha/server\";\nimport { storageStatsSchema } from \"../schemas/storageStatsSchema.ts\";\nimport { FileService } from \"../services/FileService.ts\";\n\n/**\n * REST API controller for storage analytics and statistics.\n * Provides endpoints for viewing storage usage metrics.\n */\nexport class StorageStatsController {\n protected readonly url = \"/files/stats\";\n protected readonly group = \"files\";\n protected readonly fileService = $inject(FileService);\n\n /**\n * GET /files/stats - Gets storage statistics.\n * Returns aggregated data including total size, file count,\n * and breakdowns by bucket and MIME type.\n */\n public readonly getStats = $action({\n path: this.url,\n group: this.group,\n description: \"Get storage statistics\",\n schema: {\n response: storageStatsSchema,\n },\n handler: () => this.fileService.getStorageStats(),\n });\n}\n","import { $inject } from \"alepha\";\nimport { $scheduler } from \"alepha/scheduler\";\nimport { FileService } from \"../services/FileService.ts\";\n\nexport class FileJobs {\n protected readonly fileService = $inject(FileService);\n\n public readonly purgeFiles = $scheduler({\n description: \"Purge files that are marked for deletion\",\n cron: \"*/15 * * * *\", // Every 15 minutes\n handler: async () => {\n const files = await this.fileService.findExpiredFiles();\n\n await Promise.all(\n files.map((file) => this.fileService.deleteFile(file.id)),\n );\n },\n });\n}\n","import { $module } from \"alepha\";\nimport { AlephaBucket } from \"alepha/bucket\";\nimport type { DurationLike } from \"alepha/datetime\";\nimport type { UserAccountToken } from \"alepha/security\";\nimport { AlephaServerCache } from \"alepha/server/cache\";\nimport { AlephaServerMultipart } from \"alepha/server/multipart\";\nimport { FileController } from \"./controllers/FileController.ts\";\nimport { StorageStatsController } from \"./controllers/StorageStatsController.ts\";\nimport { FileJobs } from \"./jobs/FileJobs.ts\";\nimport { FileService } from \"./services/FileService.ts\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport * from \"./controllers/FileController.ts\";\nexport * from \"./controllers/StorageStatsController.ts\";\nexport * from \"./entities/files.ts\";\nexport * from \"./schemas/storageStatsSchema.ts\";\nexport * from \"./services/FileService.ts\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\ndeclare module \"alepha/bucket\" {\n interface BucketFileOptions {\n /**\n * Time to live for the files in the bucket.\n */\n ttl?: DurationLike;\n\n /**\n * Tags for the bucket.\n */\n tags?: string[];\n\n /**\n * User performing the operation.\n */\n user?: UserAccountToken;\n\n /**\n * Whether to persist the file metadata in the database.\n *\n * @default true\n */\n persist?: boolean;\n }\n}\n\n// ---------------------------------------------------------------------------------------------------------------------\n\n/**\n * Provides file management API endpoints for Alepha applications.\n *\n * This module includes file upload, download, storage management,\n * and file metadata operations.\n *\n * @module alepha.api.files\n */\nexport const AlephaApiFiles = $module({\n name: \"alepha.api.files\",\n services: [FileController, StorageStatsController, FileJobs, FileService],\n register: (alepha) => {\n alepha\n .with(AlephaBucket)\n .with(AlephaServerCache)\n .with(AlephaServerMultipart)\n .with(FileController)\n .with(StorageStatsController)\n .with(FileJobs);\n },\n});\n"],"mappings":"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"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.browser.js","names":[],"sources":["../../src/api-jobs/entities/jobExecutions.ts","../../src/api-jobs/schemas/jobExecutionQuerySchema.ts","../../src/api-jobs/schemas/jobExecutionResourceSchema.ts","../../src/api-jobs/schemas/triggerJobSchema.ts","../../src/api-jobs/index.browser.ts"],"sourcesContent":["import { type Static, t } from \"alepha\";\nimport { logEntrySchema } from \"alepha/logger\";\nimport { $entity, pg } from \"alepha/orm\";\n\nexport const jobExecutions = $entity({\n name: \"job_executions\",\n schema: t.object({\n id: pg.primaryKey(t.uuid()),\n version: pg.version(),\n createdAt: pg.createdAt(),\n updatedAt: pg.updatedAt(),\n finishedAt: t.optional(t.datetime()),\n job: t.string(),\n status: t.enum([\"STARTED\", \"FAILED\", \"COMPLETED\"]),\n error: t.optional(t.string()),\n logs: t.optional(t.array(logEntrySchema)),\n }),\n});\n\nexport type JobExecutionEntity = Static<typeof jobExecutions.schema>;\n","import type { Static } from \"alepha\";\nimport { t } from \"alepha\";\nimport { pageQuerySchema } from \"alepha/orm\";\n\nexport const jobExecutionQuerySchema = t.extend(pageQuerySchema, {\n status: t.optional(t.enum([\"STARTED\", \"FAILED\", \"COMPLETED\"])),\n job: t.optional(\n t.text({\n description: \"Filter by job name\",\n }),\n ),\n});\n\nexport type JobExecutionQuery = Static<typeof jobExecutionQuerySchema>;\n","import { type Static, t } from \"alepha\";\nimport { jobExecutions } from \"../entities/jobExecutions.ts\";\n\nexport const jobExecutionResourceSchema = t.extend(\n jobExecutions.schema,\n {},\n {\n title: \"JobExecutionResource\",\n description:\n \"A job execution resource representing the execution details of a job.\",\n },\n);\n\nexport type JobExecutionResource = Static<typeof jobExecutionResourceSchema>;\n","import type { Static } from \"alepha\";\nimport { t } from \"alepha\";\n\nexport const triggerJobSchema = t.object({\n name: t.string(),\n});\n\nexport type TriggerJob = Static<typeof triggerJobSchema>;\n","import { $module } from \"alepha\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport * from \"./entities/jobExecutions.ts\";\nexport * from \"./schemas/jobExecutionQuerySchema.ts\";\nexport * from \"./schemas/jobExecutionResourceSchema.ts\";\nexport * from \"./schemas/triggerJobSchema.ts\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport const AlephaApiJobs = $module({\n name: \"alepha.api.jobs\",\n services: [],\n});\n"],"mappings":";;;;;AAIA,MAAa,gBAAgB,QAAQ;CACnC,MAAM;CACN,QAAQ,EAAE,OAAO;EACf,IAAI,GAAG,WAAW,EAAE,MAAM,CAAC;EAC3B,SAAS,GAAG,SAAS;EACrB,WAAW,GAAG,WAAW;EACzB,WAAW,GAAG,WAAW;EACzB,YAAY,EAAE,SAAS,EAAE,UAAU,CAAC;EACpC,KAAK,EAAE,QAAQ;EACf,QAAQ,EAAE,KAAK;GAAC;GAAW;GAAU;GAAY,CAAC;EAClD,OAAO,EAAE,SAAS,EAAE,QAAQ,CAAC;EAC7B,MAAM,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;EAC1C,CAAC;CACH,CAAC;;;;ACbF,MAAa,0BAA0B,EAAE,OAAO,iBAAiB;CAC/D,QAAQ,EAAE,SAAS,EAAE,KAAK;EAAC;EAAW;EAAU;EAAY,CAAC,CAAC;CAC9D,KAAK,EAAE,SACL,EAAE,KAAK,EACL,aAAa,sBACd,CAAC,CACH;CACF,CAAC;;;;ACRF,MAAa,6BAA6B,EAAE,OAC1C,cAAc,QACd,EAAE,EACF;CACE,OAAO;CACP,aACE;CACH,CACF;;;;ACRD,MAAa,mBAAmB,EAAE,OAAO,EACvC,MAAM,EAAE,QAAQ,EACjB,CAAC;;;;ACMF,MAAa,gBAAgB,QAAQ;CACnC,MAAM;CACN,UAAU,EAAE;CACb,CAAC"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.js","names":["registration: JobRegistration"],"sources":["../../src/api-jobs/schemas/jobExecutionQuerySchema.ts","../../src/api-jobs/entities/jobExecutions.ts","../../src/api-jobs/schemas/jobExecutionResourceSchema.ts","../../src/api-jobs/schemas/triggerJobSchema.ts","../../src/api-jobs/providers/JobProvider.ts","../../src/api-jobs/primitives/$job.ts","../../src/api-jobs/services/JobService.ts","../../src/api-jobs/controllers/JobController.ts","../../src/api-jobs/index.ts"],"sourcesContent":["import type { Static } from \"alepha\";\nimport { t } from \"alepha\";\nimport { pageQuerySchema } from \"alepha/orm\";\n\nexport const jobExecutionQuerySchema = t.extend(pageQuerySchema, {\n status: t.optional(t.enum([\"STARTED\", \"FAILED\", \"COMPLETED\"])),\n job: t.optional(\n t.text({\n description: \"Filter by job name\",\n }),\n ),\n});\n\nexport type JobExecutionQuery = Static<typeof jobExecutionQuerySchema>;\n","import { type Static, t } from \"alepha\";\nimport { logEntrySchema } from \"alepha/logger\";\nimport { $entity, pg } from \"alepha/orm\";\n\nexport const jobExecutions = $entity({\n name: \"job_executions\",\n schema: t.object({\n id: pg.primaryKey(t.uuid()),\n version: pg.version(),\n createdAt: pg.createdAt(),\n updatedAt: pg.updatedAt(),\n finishedAt: t.optional(t.datetime()),\n job: t.string(),\n status: t.enum([\"STARTED\", \"FAILED\", \"COMPLETED\"]),\n error: t.optional(t.string()),\n logs: t.optional(t.array(logEntrySchema)),\n }),\n});\n\nexport type JobExecutionEntity = Static<typeof jobExecutions.schema>;\n","import { type Static, t } from \"alepha\";\nimport { jobExecutions } from \"../entities/jobExecutions.ts\";\n\nexport const jobExecutionResourceSchema = t.extend(\n jobExecutions.schema,\n {},\n {\n title: \"JobExecutionResource\",\n description:\n \"A job execution resource representing the execution details of a job.\",\n },\n);\n\nexport type JobExecutionResource = Static<typeof jobExecutionResourceSchema>;\n","import type { Static } from \"alepha\";\nimport { t } from \"alepha\";\n\nexport const triggerJobSchema = t.object({\n name: t.string(),\n});\n\nexport type TriggerJob = Static<typeof triggerJobSchema>;\n","import { $env, $inject, Alepha, type Async, type Static, t } from \"alepha\";\nimport { type DateTime, DateTimeProvider } from \"alepha/datetime\";\nimport { $lock, type LockPrimitive } from \"alepha/lock\";\nimport type { LogEntry } from \"alepha/logger\";\nimport { $repository } from \"alepha/orm\";\nimport { CronProvider } from \"alepha/scheduler\";\nimport { jobExecutions } from \"../entities/jobExecutions.ts\";\n\nconst envSchema = t.object({\n JOB_PREFIX: t.optional(\n t.text({\n description: \"Prefix for job lock keys\",\n }),\n ),\n});\n\ndeclare module \"alepha\" {\n interface Env extends Partial<Static<typeof envSchema>> {}\n}\n\n/**\n * Provider for job management and execution.\n * Handles job lifecycle, execution tracking, log capturing, and event emission.\n */\nexport class JobProvider {\n protected readonly alepha = $inject(Alepha);\n protected readonly dateTimeProvider = $inject(DateTimeProvider);\n protected readonly cronProvider = $inject(CronProvider);\n protected readonly executionRepository = $repository(jobExecutions);\n protected readonly env = $env(envSchema);\n protected readonly logs = new Map<string, LogEntry[]>();\n protected readonly jobs = new Map<string, JobRegistration>();\n\n /**\n * Register and set up a job for execution (called during primitive initialization).\n */\n public registerJob(options: Job): JobRegistration {\n const jobName = options.name;\n\n // Set up log capturing for this job (only once)\n if (this.jobs.size === 0) {\n this.alepha.events.on(\"log\", ({ entry }) => {\n const context = entry.context;\n if (!context) {\n return;\n }\n\n const entries = this.logs.get(context);\n if (!entries) {\n return;\n }\n\n entries.push(entry);\n this.logs.set(context, entries);\n });\n }\n\n // Create lock primitive if locking is enabled\n const lockPrimitive =\n options.lock !== false\n ? $lock({\n name: () => {\n const prefix = this.env.JOB_PREFIX\n ? `${this.env.JOB_PREFIX}:`\n : \"\";\n return `${prefix}job:${jobName}`;\n },\n handler: async () => {\n await this.executeJob(jobName, options.handler);\n },\n })\n : null;\n\n const registration: JobRegistration = {\n name: jobName,\n options,\n lockPrimitive,\n };\n\n this.jobs.set(jobName, registration);\n\n // Set up cron scheduling if provided\n if (options.cron) {\n this.cronProvider.createCronJob(jobName, options.cron, () =>\n this.triggerJob(jobName),\n );\n }\n\n return registration;\n }\n\n /**\n * Trigger a job by name.\n */\n public async triggerJob(jobName: string): Promise<void> {\n const registration = this.jobs.get(jobName);\n if (!registration) {\n throw new Error(`Job not registered: ${jobName}`);\n }\n\n // Execute handler with or without lock\n if (registration.options.lock !== false && registration.lockPrimitive) {\n await registration.lockPrimitive.run();\n } else {\n await this.executeJob(jobName, registration.options.handler);\n }\n }\n\n /**\n * Execute a job handler (called by the job primitive).\n */\n public async executeJob(\n jobName: string,\n handler: (args: { now: DateTime }) => Async<void>,\n ): Promise<void> {\n if (!this.alepha.isStarted()) {\n return;\n }\n\n const context = this.alepha.context.createContextId();\n\n await this.alepha.context.run(\n async () => {\n try {\n const now = this.dateTimeProvider.now();\n\n // Initialize log collection for this context\n this.logs.set(context, []);\n\n // Create execution record\n await this.executionRepository.create({\n job: jobName,\n status: \"STARTED\",\n });\n\n await this.alepha.events.emit(\"scheduler:begin\", {\n name: jobName,\n now,\n context,\n });\n\n // Execute the handler\n await handler({ now });\n\n // Update execution as completed\n const logs = this.logs.get(context) || [];\n const exec = await this.executionRepository.findOne({\n where: {\n job: jobName,\n status: \"STARTED\",\n },\n });\n\n exec.status = \"COMPLETED\";\n exec.logs = logs;\n exec.finishedAt = this.dateTimeProvider.nowISOString();\n\n await this.executionRepository.save(exec);\n\n await this.alepha.events.emit(\n \"scheduler:success\",\n {\n name: jobName,\n context,\n },\n {\n catch: true,\n },\n );\n } catch (error) {\n // Update execution as failed\n const logs = this.logs.get(context) || [];\n const exec = await this.executionRepository.findOne({\n where: {\n job: jobName,\n status: \"STARTED\",\n },\n });\n\n exec.status = \"FAILED\";\n exec.error = (error as Error).message;\n exec.logs = logs;\n exec.finishedAt = this.dateTimeProvider.nowISOString();\n\n await this.executionRepository.save(exec);\n\n await this.alepha.events.emit(\n \"scheduler:error\",\n {\n name: jobName,\n error: error as Error,\n context,\n },\n {\n catch: true,\n },\n );\n\n // Don't re-throw, jobs should handle errors gracefully\n }\n\n // Clean up logs\n this.logs.delete(context);\n\n await this.alepha.events.emit(\n \"scheduler:end\",\n {\n name: jobName,\n context,\n },\n {\n catch: true,\n },\n );\n },\n {\n context,\n },\n );\n }\n}\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport interface Job {\n /**\n * Name of the job.\n */\n name: string;\n\n /**\n * Optional description of the job.\n */\n description?: string;\n\n /**\n * Function to run on schedule.\n */\n handler: (args: { now: DateTime }) => Async<void>;\n\n /**\n * Cron expression to run the job.\n */\n cron?: string;\n\n /**\n * If true, the job will be locked and only one instance will run at a time.\n * You probably need to import {@link AlephaLockRedis} for distributed locking.\n *\n * @default true\n */\n lock?: boolean;\n\n /**\n * Optional prefix for job lock keys.\n */\n lockPrefix?: string;\n}\n\nexport interface JobRegistration {\n name: string;\n options: Job;\n lockPrimitive: LockPrimitive<() => Promise<void>> | null;\n}\n","import { $inject, createPrimitive, KIND, Primitive } from \"alepha\";\nimport type { DateTime } from \"alepha/datetime\";\nimport { type Job, JobProvider } from \"../providers/JobProvider.ts\";\n\n/**\n * Job primitive - a drop-in replacement for $scheduler with built-in execution tracking.\n */\nexport const $job = (options: JobPrimitiveOptions): JobPrimitive => {\n return createPrimitive(JobPrimitive, options);\n};\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport type JobPrimitiveOptions = Omit<Job, \"name\"> & {\n /**\n * Name of the job. Defaults to the primitive property name.\n */\n name?: string;\n};\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport class JobPrimitive extends Primitive<JobPrimitiveOptions> {\n protected readonly jobProvider = $inject(JobProvider);\n\n public get name(): string {\n return (\n this.options.name ??\n `${this.config.service.name}.${this.config.propertyKey}`\n );\n }\n\n protected onInit() {\n // Register job with JobProvider\n this.jobProvider.registerJob({\n ...this.options,\n name: this.name,\n });\n }\n\n public async trigger(): Promise<void> {\n await this.jobProvider.triggerJob(this.name);\n }\n}\n\n$job[KIND] = JobPrimitive;\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport interface JobHandlerArguments {\n now: DateTime;\n}\n","import { $inject, Alepha } from \"alepha\";\nimport { $repository } from \"alepha/orm\";\nimport { jobExecutions } from \"../entities/jobExecutions.ts\";\nimport { $job } from \"../primitives/$job.ts\";\nimport type { JobExecutionQuery } from \"../schemas/jobExecutionQuerySchema.ts\";\n\nexport class JobService {\n protected readonly alepha = $inject(Alepha);\n protected readonly executionRepository = $repository(jobExecutions);\n\n public async getJobs(): Promise<string[]> {\n const jobPrimitives = this.alepha.primitives($job);\n return jobPrimitives.map((job) => job.name);\n }\n\n public async getJobExecutions(query: JobExecutionQuery = {}) {\n query.sort ??= \"-createdAt\";\n\n const where = this.executionRepository.createQueryWhere();\n\n if (query.job) {\n where.job = { eq: query.job };\n }\n\n if (query.status) {\n where.status = { eq: query.status };\n }\n\n return await this.executionRepository.paginate(\n query,\n { where },\n { count: true },\n );\n }\n\n public async triggerJob(name: string): Promise<{ ok: boolean }> {\n const jobPrimitives = this.alepha.primitives($job);\n const job = jobPrimitives.find((j) => j.name === name);\n\n if (!job) {\n throw new Error(`Job not found: ${name}`);\n }\n\n await job.trigger();\n return { ok: true };\n }\n}\n","import { $inject, t } from \"alepha\";\nimport { $action, okSchema } from \"alepha/server\";\nimport { jobExecutionQuerySchema } from \"../schemas/jobExecutionQuerySchema.ts\";\nimport { jobExecutionResourceSchema } from \"../schemas/jobExecutionResourceSchema.ts\";\nimport { triggerJobSchema } from \"../schemas/triggerJobSchema.ts\";\nimport { JobService } from \"../services/JobService.ts\";\n\nexport class JobController {\n protected readonly url: string = \"/jobs\";\n protected readonly group: string = \"jobs\";\n protected readonly jobService = $inject(JobService);\n\n public readonly getJobs = $action({\n path: this.url,\n group: this.group,\n schema: {\n response: t.array(t.string()),\n },\n handler: () => this.jobService.getJobs(),\n });\n\n public readonly getJobExecutions = $action({\n path: `${this.url}/executions`,\n group: this.group,\n schema: {\n query: jobExecutionQuerySchema,\n response: t.page(jobExecutionResourceSchema),\n },\n handler: ({ query }) => this.jobService.getJobExecutions(query),\n });\n\n public readonly triggerJob = $action({\n method: \"POST\",\n path: `${this.url}/trigger`,\n group: this.group,\n schema: {\n body: triggerJobSchema,\n response: okSchema,\n },\n handler: ({ body }) => this.jobService.triggerJob(body.name),\n });\n}\n","import { $module } from \"alepha\";\nimport { JobController } from \"./controllers/JobController.ts\";\nimport { JobProvider } from \"./providers/JobProvider.ts\";\nimport { JobService } from \"./services/JobService.ts\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport * from \"./controllers/JobController.ts\";\nexport * from \"./entities/jobExecutions.ts\";\nexport * from \"./primitives/$job.ts\";\nexport * from \"./providers/JobProvider.ts\";\nexport * from \"./schemas/jobExecutionQuerySchema.ts\";\nexport * from \"./schemas/jobExecutionResourceSchema.ts\";\nexport * from \"./schemas/triggerJobSchema.ts\";\nexport * from \"./services/JobService.ts\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\n/**\n * Provides job management API endpoints for Alepha applications.\n *\n * This module includes job queue operations, job status monitoring,\n * and background task management capabilities.\n *\n * @module alepha.api.jobs\n */\nexport const AlephaApiJobs = $module({\n name: \"alepha.api.jobs\",\n services: [JobController, JobProvider, JobService],\n});\n"],"mappings":"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"}