alepha 0.22.0 → 0.24.0

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 (869) hide show
  1. package/assets/swagger-ui/swagger-ui-bundle.js +1 -1
  2. package/assets/swagger-ui/swagger-ui-standalone-preset.js +1 -1
  3. package/dist/api/audits/index.browser.js +49 -52
  4. package/dist/api/audits/index.browser.js.map +1 -1
  5. package/dist/api/audits/index.d.ts +629 -565
  6. package/dist/api/audits/index.d.ts.map +1 -1
  7. package/dist/api/audits/index.js +206 -98
  8. package/dist/api/audits/index.js.map +1 -1
  9. package/dist/api/files/index.browser.js +44 -44
  10. package/dist/api/files/index.browser.js.map +1 -1
  11. package/dist/api/files/index.d.ts +292 -293
  12. package/dist/api/files/index.d.ts.map +1 -1
  13. package/dist/api/files/index.js +61 -64
  14. package/dist/api/files/index.js.map +1 -1
  15. package/dist/api/jobs/index.browser.js +61 -59
  16. package/dist/api/jobs/index.browser.js.map +1 -1
  17. package/dist/api/jobs/index.d.ts +546 -347
  18. package/dist/api/jobs/index.d.ts.map +1 -1
  19. package/dist/api/jobs/index.js +165 -86
  20. package/dist/api/jobs/index.js.map +1 -1
  21. package/dist/api/keys/index.d.ts +242 -230
  22. package/dist/api/keys/index.d.ts.map +1 -1
  23. package/dist/api/keys/index.js +63 -66
  24. package/dist/api/keys/index.js.map +1 -1
  25. package/dist/api/notifications/index.browser.js +40 -38
  26. package/dist/api/notifications/index.browser.js.map +1 -1
  27. package/dist/api/notifications/index.d.ts +339 -209
  28. package/dist/api/notifications/index.d.ts.map +1 -1
  29. package/dist/api/notifications/index.js +108 -60
  30. package/dist/api/notifications/index.js.map +1 -1
  31. package/dist/api/oauth/index.d.ts +190 -78
  32. package/dist/api/oauth/index.d.ts.map +1 -1
  33. package/dist/api/oauth/index.js +290 -68
  34. package/dist/api/oauth/index.js.map +1 -1
  35. package/dist/api/organizations/index.browser.js +14 -14
  36. package/dist/api/organizations/index.browser.js.map +1 -1
  37. package/dist/api/organizations/index.d.ts +158 -134
  38. package/dist/api/organizations/index.d.ts.map +1 -1
  39. package/dist/api/organizations/index.js +18 -18
  40. package/dist/api/organizations/index.js.map +1 -1
  41. package/dist/api/parameters/index.browser.js +74 -53
  42. package/dist/api/parameters/index.browser.js.map +1 -1
  43. package/dist/api/parameters/index.d.ts +607 -513
  44. package/dist/api/parameters/index.d.ts.map +1 -1
  45. package/dist/api/parameters/index.js +205 -127
  46. package/dist/api/parameters/index.js.map +1 -1
  47. package/dist/api/payments/index.d.ts +652 -449
  48. package/dist/api/payments/index.d.ts.map +1 -1
  49. package/dist/api/payments/index.js +159 -93
  50. package/dist/api/payments/index.js.map +1 -1
  51. package/dist/api/subscriptions/index.d.ts +1032 -762
  52. package/dist/api/subscriptions/index.d.ts.map +1 -1
  53. package/dist/api/subscriptions/index.js +166 -185
  54. package/dist/api/subscriptions/index.js.map +1 -1
  55. package/dist/api/users/index.browser.js +48 -71
  56. package/dist/api/users/index.browser.js.map +1 -1
  57. package/dist/api/users/index.d.ts +1297 -1109
  58. package/dist/api/users/index.d.ts.map +1 -1
  59. package/dist/api/users/index.js +454 -369
  60. package/dist/api/users/index.js.map +1 -1
  61. package/dist/api/verifications/index.browser.js +17 -17
  62. package/dist/api/verifications/index.browser.js.map +1 -1
  63. package/dist/api/verifications/index.d.ts +169 -157
  64. package/dist/api/verifications/index.d.ts.map +1 -1
  65. package/dist/api/verifications/index.js +47 -95
  66. package/dist/api/verifications/index.js.map +1 -1
  67. package/dist/background/index.d.ts +0 -1
  68. package/dist/background/index.d.ts.map +1 -1
  69. package/dist/background/index.js.map +1 -1
  70. package/dist/background/index.workerd.js.map +1 -1
  71. package/dist/batch/index.d.ts +0 -1
  72. package/dist/batch/index.d.ts.map +1 -1
  73. package/dist/batch/index.js.map +1 -1
  74. package/dist/bin/index.js +1 -1
  75. package/dist/bin/index.js.map +1 -1
  76. package/dist/bucket/index.d.ts +27 -32
  77. package/dist/bucket/index.d.ts.map +1 -1
  78. package/dist/bucket/index.js +56 -26
  79. package/dist/bucket/index.js.map +1 -1
  80. package/dist/bucket/index.workerd.js +52 -24
  81. package/dist/bucket/index.workerd.js.map +1 -1
  82. package/dist/cache/core/index.d.ts +4 -6
  83. package/dist/cache/core/index.d.ts.map +1 -1
  84. package/dist/cache/core/index.js +4 -10
  85. package/dist/cache/core/index.js.map +1 -1
  86. package/dist/cache/core/index.workerd.js +4 -10
  87. package/dist/cache/core/index.workerd.js.map +1 -1
  88. package/dist/cache/database/index.d.ts +22 -23
  89. package/dist/cache/database/index.d.ts.map +1 -1
  90. package/dist/cache/database/index.js +11 -20
  91. package/dist/cache/database/index.js.map +1 -1
  92. package/dist/cache/redis/index.d.ts +3 -4
  93. package/dist/cache/redis/index.d.ts.map +1 -1
  94. package/dist/cache/redis/index.js +2 -2
  95. package/dist/cache/redis/index.js.map +1 -1
  96. package/dist/captcha/index.d.ts.map +1 -1
  97. package/dist/captcha/index.js +4 -4
  98. package/dist/captcha/index.js.map +1 -1
  99. package/dist/cli/config/index.d.ts +0 -1
  100. package/dist/cli/config/index.d.ts.map +1 -1
  101. package/dist/cli/core/index.d.ts +244 -475
  102. package/dist/cli/core/index.d.ts.map +1 -1
  103. package/dist/cli/core/index.js +308 -383
  104. package/dist/cli/core/index.js.map +1 -1
  105. package/dist/cli/devtools/index.d.ts +3 -9
  106. package/dist/cli/devtools/index.d.ts.map +1 -1
  107. package/dist/cli/devtools/index.js +3 -3
  108. package/dist/cli/devtools/index.js.map +1 -1
  109. package/dist/cli/i18n/index.d.ts +20 -39
  110. package/dist/cli/i18n/index.d.ts.map +1 -1
  111. package/dist/cli/i18n/index.js +51 -17
  112. package/dist/cli/i18n/index.js.map +1 -1
  113. package/dist/cli/platform/index.d.ts +89 -82
  114. package/dist/cli/platform/index.d.ts.map +1 -1
  115. package/dist/cli/platform/index.js +27 -33
  116. package/dist/cli/platform/index.js.map +1 -1
  117. package/dist/cli/platform-lib/index.d.ts +383 -410
  118. package/dist/cli/platform-lib/index.d.ts.map +1 -1
  119. package/dist/cli/platform-lib/index.js +300 -258
  120. package/dist/cli/platform-lib/index.js.map +1 -1
  121. package/dist/cli/vendor/index.d.ts +17 -41
  122. package/dist/cli/vendor/index.d.ts.map +1 -1
  123. package/dist/cli/vendor/index.js +10 -13
  124. package/dist/cli/vendor/index.js.map +1 -1
  125. package/dist/command/index.d.ts +82 -194
  126. package/dist/command/index.d.ts.map +1 -1
  127. package/dist/command/index.js +160 -563
  128. package/dist/command/index.js.map +1 -1
  129. package/dist/core/index.browser.js +845 -859
  130. package/dist/core/index.browser.js.map +1 -1
  131. package/dist/core/index.d.ts +787 -527
  132. package/dist/core/index.d.ts.map +1 -1
  133. package/dist/core/index.js +845 -859
  134. package/dist/core/index.js.map +1 -1
  135. package/dist/core/index.native.js +845 -859
  136. package/dist/core/index.native.js.map +1 -1
  137. package/dist/core/index.workerd.js +845 -859
  138. package/dist/core/index.workerd.js.map +1 -1
  139. package/dist/crypto/index.browser.js +1 -1
  140. package/dist/crypto/index.browser.js.map +1 -1
  141. package/dist/crypto/index.d.ts +3 -4
  142. package/dist/crypto/index.d.ts.map +1 -1
  143. package/dist/crypto/index.js +6 -3
  144. package/dist/crypto/index.js.map +1 -1
  145. package/dist/datetime/index.d.ts +12 -2
  146. package/dist/datetime/index.d.ts.map +1 -1
  147. package/dist/datetime/index.js +6 -1
  148. package/dist/datetime/index.js.map +1 -1
  149. package/dist/email/brevo/index.d.ts +0 -1
  150. package/dist/email/brevo/index.d.ts.map +1 -1
  151. package/dist/email/brevo/index.js +4 -4
  152. package/dist/email/brevo/index.js.map +1 -1
  153. package/dist/email/cloudflare/index.d.ts +0 -1
  154. package/dist/email/cloudflare/index.d.ts.map +1 -1
  155. package/dist/email/cloudflare/index.js +2 -2
  156. package/dist/email/cloudflare/index.js.map +1 -1
  157. package/dist/email/core/index.d.ts +15 -7
  158. package/dist/email/core/index.d.ts.map +1 -1
  159. package/dist/email/core/index.js +14 -5
  160. package/dist/email/core/index.js.map +1 -1
  161. package/dist/email/core/index.workerd.js +5 -1
  162. package/dist/email/core/index.workerd.js.map +1 -1
  163. package/dist/email/smtp/index.d.ts +8 -9
  164. package/dist/email/smtp/index.d.ts.map +1 -1
  165. package/dist/email/smtp/index.js +702 -479
  166. package/dist/email/smtp/index.js.map +1 -1
  167. package/dist/fake/index.d.ts +1580 -1238
  168. package/dist/fake/index.d.ts.map +1 -1
  169. package/dist/fake/index.js +11728 -32131
  170. package/dist/fake/index.js.map +1 -1
  171. package/dist/lock/core/index.d.ts +13 -9
  172. package/dist/lock/core/index.d.ts.map +1 -1
  173. package/dist/lock/core/index.js +5 -5
  174. package/dist/lock/core/index.js.map +1 -1
  175. package/dist/lock/redis/index.d.ts +0 -1
  176. package/dist/lock/redis/index.d.ts.map +1 -1
  177. package/dist/lock/redis/index.js.map +1 -1
  178. package/dist/logger/index.d.ts +81 -69
  179. package/dist/logger/index.d.ts.map +1 -1
  180. package/dist/logger/index.js +123 -56
  181. package/dist/logger/index.js.map +1 -1
  182. package/dist/mcp/index.d.ts +36 -90
  183. package/dist/mcp/index.d.ts.map +1 -1
  184. package/dist/mcp/index.js +65 -70
  185. package/dist/mcp/index.js.map +1 -1
  186. package/dist/orm/core/index.browser.js +54 -40
  187. package/dist/orm/core/index.browser.js.map +1 -1
  188. package/dist/orm/core/index.bun.js +140 -106
  189. package/dist/orm/core/index.bun.js.map +1 -1
  190. package/dist/orm/core/index.d.ts +145 -150
  191. package/dist/orm/core/index.d.ts.map +1 -1
  192. package/dist/orm/core/index.js +144 -109
  193. package/dist/orm/core/index.js.map +1 -1
  194. package/dist/orm/postgres/index.bun.js +64 -54
  195. package/dist/orm/postgres/index.bun.js.map +1 -1
  196. package/dist/orm/postgres/index.d.ts +36 -76
  197. package/dist/orm/postgres/index.d.ts.map +1 -1
  198. package/dist/orm/postgres/index.js +67 -57
  199. package/dist/orm/postgres/index.js.map +1 -1
  200. package/dist/queue/core/index.d.ts +62 -75
  201. package/dist/queue/core/index.d.ts.map +1 -1
  202. package/dist/queue/core/index.js +48 -71
  203. package/dist/queue/core/index.js.map +1 -1
  204. package/dist/queue/core/index.workerd.js +48 -71
  205. package/dist/queue/core/index.workerd.js.map +1 -1
  206. package/dist/queue/redis/index.d.ts +3 -4
  207. package/dist/queue/redis/index.d.ts.map +1 -1
  208. package/dist/queue/redis/index.js +2 -2
  209. package/dist/queue/redis/index.js.map +1 -1
  210. package/dist/react/auth/index.browser.js.map +1 -1
  211. package/dist/react/auth/index.d.ts +7 -4
  212. package/dist/react/auth/index.d.ts.map +1 -1
  213. package/dist/react/auth/index.js.map +1 -1
  214. package/dist/react/core/index.d.ts +87 -5
  215. package/dist/react/core/index.d.ts.map +1 -1
  216. package/dist/react/core/index.js +170 -23
  217. package/dist/react/core/index.js.map +1 -1
  218. package/dist/react/form/index.d.ts +4 -6
  219. package/dist/react/form/index.d.ts.map +1 -1
  220. package/dist/react/form/index.js +107 -71
  221. package/dist/react/form/index.js.map +1 -1
  222. package/dist/react/head/index.browser.js.map +1 -1
  223. package/dist/react/head/index.d.ts +0 -1
  224. package/dist/react/head/index.d.ts.map +1 -1
  225. package/dist/react/head/index.js.map +1 -1
  226. package/dist/react/i18n/index.d.ts +47 -5
  227. package/dist/react/i18n/index.d.ts.map +1 -1
  228. package/dist/react/i18n/index.js +116 -12
  229. package/dist/react/i18n/index.js.map +1 -1
  230. package/dist/react/intro/index.d.ts +1 -4
  231. package/dist/react/intro/index.d.ts.map +1 -1
  232. package/dist/react/intro/index.js.map +1 -1
  233. package/dist/react/router/index.browser.js +188 -29
  234. package/dist/react/router/index.browser.js.map +1 -1
  235. package/dist/react/router/index.d.ts +480 -372
  236. package/dist/react/router/index.d.ts.map +1 -1
  237. package/dist/react/router/index.js +261 -47
  238. package/dist/react/router/index.js.map +1 -1
  239. package/dist/react/sitemap/index.browser.js +35 -0
  240. package/dist/react/sitemap/index.browser.js.map +1 -0
  241. package/dist/react/sitemap/index.d.ts +91 -0
  242. package/dist/react/sitemap/index.d.ts.map +1 -0
  243. package/dist/react/sitemap/index.js +131 -0
  244. package/dist/react/sitemap/index.js.map +1 -0
  245. package/dist/react/testing/index.d.ts +4 -5
  246. package/dist/react/testing/index.d.ts.map +1 -1
  247. package/dist/react/testing/index.js +41 -15
  248. package/dist/react/testing/index.js.map +1 -1
  249. package/dist/react/testing/{chunk-BpyX8vjI.js → rolldown-runtime-Bl3dcgcQ.js} +1 -1
  250. package/dist/react/ui/index.d.ts +35 -34
  251. package/dist/react/ui/index.d.ts.map +1 -1
  252. package/dist/react/ui/index.js +12 -17
  253. package/dist/react/ui/index.js.map +1 -1
  254. package/dist/redis/index.bun.js +5 -5
  255. package/dist/redis/index.bun.js.map +1 -1
  256. package/dist/redis/index.d.ts +6 -7
  257. package/dist/redis/index.d.ts.map +1 -1
  258. package/dist/redis/index.js +6 -6
  259. package/dist/redis/index.js.map +1 -1
  260. package/dist/retry/index.d.ts +0 -1
  261. package/dist/retry/index.d.ts.map +1 -1
  262. package/dist/retry/index.js.map +1 -1
  263. package/dist/router/index.d.ts.map +1 -1
  264. package/dist/router/index.js.map +1 -1
  265. package/dist/scheduler/index.d.ts +4 -12
  266. package/dist/scheduler/index.d.ts.map +1 -1
  267. package/dist/scheduler/index.js +9 -4
  268. package/dist/scheduler/index.js.map +1 -1
  269. package/dist/scheduler/index.workerd.js +9 -4
  270. package/dist/scheduler/index.workerd.js.map +1 -1
  271. package/dist/security/index.browser.js +30 -28
  272. package/dist/security/index.browser.js.map +1 -1
  273. package/dist/security/index.d.ts +113 -44
  274. package/dist/security/index.d.ts.map +1 -1
  275. package/dist/security/index.js +452 -37
  276. package/dist/security/index.js.map +1 -1
  277. package/dist/server/auth/index.browser.js +19 -19
  278. package/dist/server/auth/index.browser.js.map +1 -1
  279. package/dist/server/auth/index.d.ts +215 -187
  280. package/dist/server/auth/index.d.ts.map +1 -1
  281. package/dist/server/auth/index.js +1145 -86
  282. package/dist/server/auth/index.js.map +1 -1
  283. package/dist/server/cookies/index.browser.js +103 -5
  284. package/dist/server/cookies/index.browser.js.map +1 -1
  285. package/dist/server/cookies/index.d.ts +86 -4
  286. package/dist/server/cookies/index.d.ts.map +1 -1
  287. package/dist/server/cookies/index.js +121 -8
  288. package/dist/server/cookies/index.js.map +1 -1
  289. package/dist/server/core/index.browser.js +22 -27
  290. package/dist/server/core/index.browser.js.map +1 -1
  291. package/dist/server/core/index.d.ts +134 -85
  292. package/dist/server/core/index.d.ts.map +1 -1
  293. package/dist/server/core/index.js +130 -118
  294. package/dist/server/core/index.js.map +1 -1
  295. package/dist/server/cors/index.d.ts +9 -10
  296. package/dist/server/cors/index.d.ts.map +1 -1
  297. package/dist/server/cors/index.js +14 -26
  298. package/dist/server/cors/index.js.map +1 -1
  299. package/dist/server/etag/index.d.ts +2 -3
  300. package/dist/server/etag/index.d.ts.map +1 -1
  301. package/dist/server/etag/index.js.map +1 -1
  302. package/dist/server/health/index.d.ts +18 -19
  303. package/dist/server/health/index.d.ts.map +1 -1
  304. package/dist/server/health/index.js +6 -6
  305. package/dist/server/health/index.js.map +1 -1
  306. package/dist/server/links/index.browser.js +15 -18
  307. package/dist/server/links/index.browser.js.map +1 -1
  308. package/dist/server/links/index.d.ts +66 -67
  309. package/dist/server/links/index.d.ts.map +1 -1
  310. package/dist/server/links/index.js +29 -32
  311. package/dist/server/links/index.js.map +1 -1
  312. package/dist/server/metrics/index.d.ts +0 -1
  313. package/dist/server/metrics/index.d.ts.map +1 -1
  314. package/dist/server/metrics/index.js +10 -3
  315. package/dist/server/metrics/index.js.map +1 -1
  316. package/dist/server/proxy/index.d.ts +0 -1
  317. package/dist/server/proxy/index.d.ts.map +1 -1
  318. package/dist/server/proxy/index.js.map +1 -1
  319. package/dist/server/rate-limit/index.d.ts +8 -9
  320. package/dist/server/rate-limit/index.d.ts.map +1 -1
  321. package/dist/server/rate-limit/index.js +6 -12
  322. package/dist/server/rate-limit/index.js.map +1 -1
  323. package/dist/server/static/index.d.ts +0 -1
  324. package/dist/server/static/index.d.ts.map +1 -1
  325. package/dist/server/static/index.js.map +1 -1
  326. package/dist/server/swagger/index.d.ts +4 -4
  327. package/dist/server/swagger/index.d.ts.map +1 -1
  328. package/dist/server/swagger/index.js +37 -25
  329. package/dist/server/swagger/index.js.map +1 -1
  330. package/dist/sms/index.d.ts +0 -1
  331. package/dist/sms/index.d.ts.map +1 -1
  332. package/dist/sms/index.js.map +1 -1
  333. package/dist/system/index.browser.js.map +1 -1
  334. package/dist/system/index.d.ts +0 -1
  335. package/dist/system/index.d.ts.map +1 -1
  336. package/dist/system/index.js +2 -1
  337. package/dist/system/index.js.map +1 -1
  338. package/dist/system/index.workerd.js.map +1 -1
  339. package/dist/topic/core/index.d.ts +19 -20
  340. package/dist/topic/core/index.d.ts.map +1 -1
  341. package/dist/topic/core/index.js +8 -8
  342. package/dist/topic/core/index.js.map +1 -1
  343. package/dist/topic/redis/index.d.ts +3 -4
  344. package/dist/topic/redis/index.d.ts.map +1 -1
  345. package/dist/topic/redis/index.js +2 -2
  346. package/dist/topic/redis/index.js.map +1 -1
  347. package/package.json +36 -36
  348. package/src/api/audits/__tests__/AuditJobs.spec.ts +83 -0
  349. package/src/api/audits/__tests__/AuditService.spec.ts +139 -1
  350. package/src/api/audits/controllers/AdminAuditController.ts +41 -41
  351. package/src/api/audits/entities/audits.ts +22 -22
  352. package/src/api/audits/index.ts +6 -1
  353. package/src/api/audits/jobs/AuditJobs.ts +29 -0
  354. package/src/api/audits/parameters/AuditParameters.ts +51 -0
  355. package/src/api/audits/primitives/$audit.ts +24 -0
  356. package/src/api/audits/schemas/auditQuerySchema.ts +13 -13
  357. package/src/api/audits/schemas/createAuditSchema.ts +18 -18
  358. package/src/api/audits/services/AuditService.ts +79 -7
  359. package/src/api/files/__tests__/FileController.spec.ts +5 -5
  360. package/src/api/files/__tests__/FileJobs.spec.ts +6 -0
  361. package/src/api/files/controllers/FileController.ts +25 -25
  362. package/src/api/files/entities/files.ts +15 -15
  363. package/src/api/files/schemas/fileCreatorSummarySchema.ts +7 -7
  364. package/src/api/files/schemas/fileQuerySchema.ts +9 -9
  365. package/src/api/files/schemas/fileResourceSchema.ts +7 -9
  366. package/src/api/files/schemas/storageStatsSchema.ts +13 -13
  367. package/src/api/jobs/__tests__/$job-hardening.spec.ts +289 -0
  368. package/src/api/jobs/__tests__/$job.spec.ts +18 -18
  369. package/src/api/jobs/controllers/AdminJobController.ts +8 -8
  370. package/src/api/jobs/entities/jobExecutionEntity.ts +29 -19
  371. package/src/api/jobs/providers/JobProvider.ts +128 -12
  372. package/src/api/jobs/providers/JobQueueProvider.ts +2 -2
  373. package/src/api/jobs/schemas/jobConfigAtom.ts +22 -22
  374. package/src/api/jobs/schemas/jobExecutionQuerySchema.ts +6 -6
  375. package/src/api/jobs/schemas/jobExecutionResourceSchema.ts +11 -15
  376. package/src/api/jobs/schemas/jobRegistrationSchema.ts +20 -19
  377. package/src/api/jobs/schemas/triggerJobSchema.ts +3 -3
  378. package/src/api/jobs/services/JobService.ts +6 -6
  379. package/src/api/keys/__tests__/ApiKeyService.spec.ts +2 -2
  380. package/src/api/keys/controllers/AdminApiKeyController.ts +10 -10
  381. package/src/api/keys/entities/apiKeyEntity.ts +15 -15
  382. package/src/api/keys/schemas/adminApiKeyQuerySchema.ts +4 -4
  383. package/src/api/keys/schemas/adminApiKeyResourceSchema.ts +15 -15
  384. package/src/api/keys/schemas/createApiKeyBodySchema.ts +5 -5
  385. package/src/api/keys/schemas/createApiKeyResponseSchema.ts +9 -9
  386. package/src/api/keys/schemas/listApiKeyResponseSchema.ts +13 -13
  387. package/src/api/keys/schemas/revokeApiKeyParamsSchema.ts +3 -3
  388. package/src/api/keys/schemas/revokeApiKeyResponseSchema.ts +3 -3
  389. package/src/api/notifications/__tests__/AlephaApiNotifications.spec.ts +2 -2
  390. package/src/api/notifications/controllers/AdminNotificationController.ts +38 -14
  391. package/src/api/notifications/jobs/NotificationJobs.ts +7 -6
  392. package/src/api/notifications/primitives/$notification.ts +43 -4
  393. package/src/api/notifications/schemas/notificationContactPreferencesSchema.ts +4 -4
  394. package/src/api/notifications/schemas/notificationContactSchema.ts +7 -7
  395. package/src/api/notifications/schemas/notificationDetailResourceSchema.ts +9 -11
  396. package/src/api/notifications/schemas/notificationPayloadSchema.ts +11 -9
  397. package/src/api/notifications/schemas/notificationQuerySchema.ts +6 -6
  398. package/src/api/notifications/schemas/notificationResourceSchema.ts +14 -14
  399. package/src/api/notifications/services/NotificationSenderService.ts +38 -2
  400. package/src/api/oauth/__tests__/OAuthClientService.spec.ts +42 -0
  401. package/src/api/oauth/__tests__/OAuthController.spec.ts +128 -0
  402. package/src/api/oauth/controllers/OAuthController.ts +132 -12
  403. package/src/api/oauth/entities/oauthClientEntity.ts +29 -12
  404. package/src/api/oauth/helpers/oauthMetadata.ts +1 -0
  405. package/src/api/oauth/helpers/oidcMetadata.ts +19 -0
  406. package/src/api/oauth/index.ts +1 -0
  407. package/src/api/oauth/schemas/authorizeDecisionBodySchema.ts +12 -11
  408. package/src/api/oauth/schemas/authorizeQuerySchema.ts +12 -10
  409. package/src/api/oauth/schemas/registerClientBodySchema.ts +10 -11
  410. package/src/api/oauth/schemas/tokenRequestBodySchema.ts +9 -8
  411. package/src/api/oauth/services/OAuthClientService.ts +228 -15
  412. package/src/api/organizations/controllers/AdminOrganizationController.ts +8 -8
  413. package/src/api/organizations/entities/organizations.ts +6 -6
  414. package/src/api/organizations/schemas/createOrganizationSchema.ts +5 -5
  415. package/src/api/organizations/schemas/organizationQuerySchema.ts +4 -4
  416. package/src/api/organizations/schemas/updateOrganizationSchema.ts +1 -2
  417. package/src/api/parameters/__tests__/$parameter.spec.ts +229 -50
  418. package/src/api/parameters/controllers/AdminParameterController.ts +9 -12
  419. package/src/api/parameters/entities/parameters.ts +29 -16
  420. package/src/api/parameters/primitives/$parameter.ts +3 -3
  421. package/src/api/parameters/schemas/activateParameterBodySchema.ts +4 -4
  422. package/src/api/parameters/schemas/createParameterVersionBodySchema.ts +15 -15
  423. package/src/api/parameters/schemas/parameterCreatorSummarySchema.ts +7 -7
  424. package/src/api/parameters/schemas/parameterCurrentResponseSchema.ts +7 -7
  425. package/src/api/parameters/schemas/parameterHistoryResponseSchema.ts +3 -3
  426. package/src/api/parameters/schemas/parameterNameParamSchema.ts +2 -3
  427. package/src/api/parameters/schemas/parameterNamesResponseSchema.ts +3 -3
  428. package/src/api/parameters/schemas/parameterResponseSchema.ts +3 -3
  429. package/src/api/parameters/schemas/parameterStatusSchema.ts +2 -2
  430. package/src/api/parameters/schemas/parameterTreeNodeSchema.ts +6 -6
  431. package/src/api/parameters/schemas/parameterVersionParamSchema.ts +5 -6
  432. package/src/api/parameters/schemas/parameterVersionResponseSchema.ts +3 -3
  433. package/src/api/parameters/schemas/rollbackParameterBodySchema.ts +6 -9
  434. package/src/api/parameters/services/ParameterProvider.ts +152 -65
  435. package/src/api/payments/controllers/AdminPaymentController.ts +7 -7
  436. package/src/api/payments/controllers/MockCheckoutController.ts +79 -28
  437. package/src/api/payments/controllers/PaymentController.ts +4 -4
  438. package/src/api/payments/entities/paymentIntents.ts +11 -11
  439. package/src/api/payments/entities/paymentMethods.ts +11 -11
  440. package/src/api/payments/entities/refunds.ts +9 -9
  441. package/src/api/payments/providers/PaymentProvider.ts +18 -0
  442. package/src/api/payments/schemas/intentSchemas.ts +25 -25
  443. package/src/api/payments/schemas/paymentMethodSchemas.ts +3 -3
  444. package/src/api/payments/services/PaymentService.ts +60 -3
  445. package/src/api/subscriptions/__tests__/SubscriptionService.spec.ts +14 -0
  446. package/src/api/subscriptions/controllers/AdminSubscriptionController.ts +11 -11
  447. package/src/api/subscriptions/controllers/SubscriptionController.ts +5 -4
  448. package/src/api/subscriptions/entities/subscriptionEvents.ts +15 -15
  449. package/src/api/subscriptions/entities/subscriptions.ts +22 -22
  450. package/src/api/subscriptions/notifications/SubscriptionNotifications.ts +22 -22
  451. package/src/api/subscriptions/schemas/cancelSubscriptionSchema.ts +4 -4
  452. package/src/api/subscriptions/schemas/changePlanSchema.ts +5 -5
  453. package/src/api/subscriptions/schemas/createSubscriptionSchema.ts +14 -7
  454. package/src/api/subscriptions/schemas/entitlementsSchema.ts +10 -10
  455. package/src/api/subscriptions/schemas/mrrSchema.ts +9 -9
  456. package/src/api/subscriptions/schemas/planDefinitionSchema.ts +21 -21
  457. package/src/api/subscriptions/schemas/planResourceSchema.ts +19 -19
  458. package/src/api/subscriptions/schemas/subscriptionQuerySchema.ts +8 -8
  459. package/src/api/subscriptions/schemas/subscriptionSettingsSchema.ts +7 -7
  460. package/src/api/subscriptions/schemas/subscriptionStatsSchema.ts +17 -17
  461. package/src/api/subscriptions/services/SubscriptionConfig.ts +2 -2
  462. package/src/api/users/__tests__/ApiKeys-integration.spec.ts +11 -11
  463. package/src/api/users/atoms/realmAuthSettingsAtom.ts +111 -119
  464. package/src/api/users/controllers/AdminIdentityController.ts +29 -19
  465. package/src/api/users/controllers/AdminSessionController.ts +37 -27
  466. package/src/api/users/controllers/AdminUserController.ts +75 -53
  467. package/src/api/users/controllers/MySessionController.ts +125 -0
  468. package/src/api/users/controllers/RealmController.ts +9 -9
  469. package/src/api/users/controllers/UserController.ts +55 -55
  470. package/src/api/users/entities/identities.ts +8 -8
  471. package/src/api/users/entities/sessions.ts +17 -17
  472. package/src/api/users/entities/users.ts +17 -17
  473. package/src/api/users/index.ts +3 -0
  474. package/src/api/users/notifications/UserNotifications.ts +76 -24
  475. package/src/api/users/primitives/$realm.ts +12 -0
  476. package/src/api/users/schemas/completePasswordResetRequestSchema.ts +8 -12
  477. package/src/api/users/schemas/completeRegistrationRequestSchema.ts +17 -20
  478. package/src/api/users/schemas/createUserSchema.ts +2 -2
  479. package/src/api/users/schemas/identityQuerySchema.ts +4 -4
  480. package/src/api/users/schemas/identityResourceSchema.ts +3 -2
  481. package/src/api/users/schemas/loginSchema.ts +4 -4
  482. package/src/api/users/schemas/passwordResetIntentResponseSchema.ts +6 -8
  483. package/src/api/users/schemas/realmConfigSchema.ts +10 -10
  484. package/src/api/users/schemas/registerQuerySchema.ts +6 -6
  485. package/src/api/users/schemas/registerRequestSchema.ts +21 -43
  486. package/src/api/users/schemas/registerResponseSchema.ts +2 -2
  487. package/src/api/users/schemas/registerSchema.ts +13 -31
  488. package/src/api/users/schemas/registrationIntentResponseSchema.ts +19 -23
  489. package/src/api/users/schemas/resetPasswordSchema.ts +10 -17
  490. package/src/api/users/schemas/sessionQuerySchema.ts +3 -3
  491. package/src/api/users/schemas/sessionResourceSchema.ts +25 -25
  492. package/src/api/users/schemas/updateUserSchema.ts +3 -4
  493. package/src/api/users/schemas/userQuerySchema.ts +7 -7
  494. package/src/api/users/services/RegistrationService.ts +25 -11
  495. package/src/api/users/services/UserService.ts +12 -6
  496. package/src/api/verifications/__tests__/CodeVerification.spec.ts +63 -101
  497. package/src/api/verifications/__tests__/LinkVerification.spec.ts +23 -92
  498. package/src/api/verifications/controllers/VerificationController.ts +13 -27
  499. package/src/api/verifications/entities/verifications.ts +13 -14
  500. package/src/api/verifications/schemas/requestVerificationCodeResponseSchema.ts +17 -14
  501. package/src/api/verifications/schemas/validateVerificationCodeResponseSchema.ts +9 -10
  502. package/src/api/verifications/schemas/verificationSettingsSchema.ts +65 -66
  503. package/src/api/verifications/schemas/verificationTypeEnumSchema.ts +2 -2
  504. package/src/api/verifications/services/VerificationService.ts +12 -0
  505. package/src/background/__tests__/BackgroundTaskProvider.spec.ts +36 -0
  506. package/src/batch/__tests__/$batch.spec.ts +22 -22
  507. package/src/batch/__tests__/startup-buffering.spec.ts +9 -9
  508. package/src/bin/index.ts +1 -1
  509. package/src/bucket/__tests__/TenantScopedBucket.spec.ts +60 -0
  510. package/src/bucket/providers/LocalFileStorageProvider.ts +14 -6
  511. package/src/bucket/providers/MemoryFileStorageProvider.ts +18 -7
  512. package/src/bucket/providers/R2FileStorageProvider.ts +14 -6
  513. package/src/bucket/providers/S3FileStorageProvider.ts +31 -15
  514. package/src/cache/core/primitives/$cache.ts +7 -10
  515. package/src/cache/database/entities/cacheEntries.ts +16 -27
  516. package/src/cache/database/providers/DatabaseCacheProvider.ts +15 -13
  517. package/src/cache/redis/providers/RedisCacheProvider.ts +6 -6
  518. package/src/captcha/providers/TurnstileCaptchaProvider.ts +4 -4
  519. package/src/cli/core/__tests__/BuildCloudflareTask.spec.ts +178 -0
  520. package/src/cli/core/__tests__/BuildDockerTask.spec.ts +0 -2
  521. package/src/cli/core/__tests__/DbCommand.spec.ts +108 -0
  522. package/src/cli/core/atoms/appEntryOptions.ts +5 -5
  523. package/src/cli/core/atoms/buildOptions.ts +76 -88
  524. package/src/cli/core/atoms/changelogOptions.ts +3 -3
  525. package/src/cli/core/atoms/devOptions.ts +3 -3
  526. package/src/cli/core/commands/build.ts +36 -49
  527. package/src/cli/core/commands/db.ts +171 -83
  528. package/src/cli/core/commands/dev.ts +8 -8
  529. package/src/cli/core/commands/gen/changelog.ts +12 -14
  530. package/src/cli/core/commands/gen/env.ts +6 -6
  531. package/src/cli/core/commands/gen/openapi.ts +6 -6
  532. package/src/cli/core/commands/init.ts +28 -33
  533. package/src/cli/core/commands/pack.ts +10 -10
  534. package/src/cli/core/commands/root.ts +7 -8
  535. package/src/cli/core/commands/test.ts +17 -20
  536. package/src/cli/core/index.ts +0 -3
  537. package/src/cli/core/tasks/BuildCloudflareTask.ts +138 -245
  538. package/src/cli/core/tasks/BuildPrerenderTask.ts +44 -7
  539. package/src/cli/core/templates/apiHelloResponseSchemaTs.ts +4 -4
  540. package/src/cli/devtools/atoms/devtoolsOptions.ts +6 -6
  541. package/src/cli/i18n/__tests__/I18nCheckService.spec.ts +48 -0
  542. package/src/cli/i18n/atoms/i18nOptions.ts +8 -8
  543. package/src/cli/i18n/services/I18nCheckService.ts +65 -11
  544. package/src/cli/platform/commands/SecretsCommand.ts +24 -25
  545. package/src/cli/platform/commands/platform.ts +40 -46
  546. package/src/cli/platform/index.ts +1 -1
  547. package/src/cli/platform-lib/__tests__/CloudflareAdapter.spec.ts +0 -4
  548. package/src/cli/platform-lib/__tests__/PlatformInspector.spec.ts +10 -3
  549. package/src/cli/platform-lib/__tests__/VercelAdapter.spec.ts +0 -2
  550. package/src/cli/platform-lib/adapters/CloudflareAdapter.ts +141 -50
  551. package/src/cli/platform-lib/adapters/VercelAdapter.ts +6 -11
  552. package/src/cli/platform-lib/atoms/platformOptions.ts +46 -25
  553. package/src/cli/platform-lib/schemas/cloudflare.ts +87 -87
  554. package/src/cli/platform-lib/schemas/platform.ts +51 -51
  555. package/src/cli/platform-lib/schemas/vercel.ts +28 -28
  556. package/src/cli/platform-lib/services/CloudflareApi.ts +5 -5
  557. package/src/cli/platform-lib/services/VercelApi.ts +4 -4
  558. package/src/cli/platform-lib/services/VercelCli.ts +6 -15
  559. package/src/cli/platform-lib/services/WranglerApi.ts +6 -15
  560. package/src/cli/vendor/atoms/vendorOptions.ts +9 -9
  561. package/src/cli/vendor/commands/VendorCommand.ts +11 -12
  562. package/src/command/__tests__/$command.spec.ts +82 -83
  563. package/src/command/__tests__/Asker.spec.ts +6 -13
  564. package/src/command/__tests__/CliProvider.spec.ts +136 -149
  565. package/src/command/__tests__/Runner.spec.ts +86 -110
  566. package/src/command/helpers/Asker.ts +23 -58
  567. package/src/command/helpers/Runner.ts +34 -110
  568. package/src/command/index.ts +4 -9
  569. package/src/command/primitives/$command.ts +10 -10
  570. package/src/command/providers/CliProvider.ts +183 -161
  571. package/src/core/Alepha.ts +62 -13
  572. package/src/core/__tests__/$atom.spec.ts +52 -3
  573. package/src/core/__tests__/$computed.spec.ts +129 -0
  574. package/src/core/__tests__/$env.spec.ts +13 -13
  575. package/src/core/__tests__/$inject.spec.ts +4 -4
  576. package/src/core/__tests__/$module.spec.ts +1 -1
  577. package/src/core/__tests__/Alepha-graph.spec.ts +1 -1
  578. package/src/core/__tests__/Alepha-parseEnv.spec.ts +32 -32
  579. package/src/core/__tests__/Alepha-start.spec.ts +3 -3
  580. package/src/core/__tests__/CodecManager.spec.ts +113 -94
  581. package/src/core/__tests__/KeylessJsonSchemaCodec.spec.ts +138 -138
  582. package/src/core/__tests__/SchemaValidator.spec.ts +43 -44
  583. package/src/core/__tests__/StateManager.persist.browser.spec.ts +87 -0
  584. package/src/core/__tests__/StateManager.spec.ts +423 -0
  585. package/src/core/__tests__/TypeBox-usecases.spec.ts +8 -8
  586. package/src/core/__tests__/TypeProvider.spec.ts +314 -309
  587. package/src/core/__tests__/dump.spec.ts +6 -6
  588. package/src/core/__tests__/pagination.spec.ts +4 -4
  589. package/src/core/__tests__/scoped-after-start.spec.ts +43 -0
  590. package/src/core/errors/TypeBoxError.ts +10 -4
  591. package/src/core/helpers/FileLike.ts +5 -5
  592. package/src/core/helpers/coerceStrings.ts +70 -0
  593. package/src/core/helpers/jsonSchemaToZod.ts +112 -0
  594. package/src/core/index.shared.ts +4 -1
  595. package/src/core/primitives/$atom.ts +95 -27
  596. package/src/core/primitives/$computed.ts +148 -0
  597. package/src/core/primitives/$env.ts +5 -5
  598. package/src/core/primitives/$state.ts +1 -1
  599. package/src/core/providers/AlsProvider.ts +26 -0
  600. package/src/core/providers/CodecManager.ts +1 -2
  601. package/src/core/providers/JsonSchemaCodec.ts +1 -2
  602. package/src/core/providers/KeylessJsonSchemaCodec.ts +82 -57
  603. package/src/core/providers/SchemaValidator.ts +48 -158
  604. package/src/core/providers/StateManager.ts +383 -9
  605. package/src/core/providers/TypeProvider.ts +80 -734
  606. package/src/core/providers/ZodProvider.ts +380 -0
  607. package/src/core/providers/zodAugment.ts +69 -0
  608. package/src/core/schemas/pageQuerySchema.ts +19 -21
  609. package/src/core/schemas/pageSchema.ts +47 -57
  610. package/src/crypto/__tests__/SecretProvider.spec.ts +31 -0
  611. package/src/crypto/providers/SecretProvider.ts +16 -10
  612. package/src/datetime/primitives/$throttle.ts +6 -1
  613. package/src/email/brevo/providers/BrevoEmailProvider.ts +4 -4
  614. package/src/email/cloudflare/providers/CloudflareEmailProvider.ts +6 -6
  615. package/src/email/core/index.ts +7 -2
  616. package/src/email/core/primitives/$email.ts +5 -1
  617. package/src/email/core/providers/LocalEmailProvider.ts +5 -5
  618. package/src/email/smtp/providers/NodemailerEmailProvider.ts +41 -52
  619. package/src/fake/__tests__/FakeProvider.spec.ts +110 -110
  620. package/src/fake/__tests__/keyName.example.ts +21 -21
  621. package/src/fake/__tests__/keyName.spec.ts +41 -41
  622. package/src/fake/__tests__/module.example.ts +8 -8
  623. package/src/fake/providers/FakeProvider.ts +122 -121
  624. package/src/lock/core/__tests__/lock-mutual-exclusion.spec.ts +44 -0
  625. package/src/lock/core/index.ts +1 -0
  626. package/src/lock/core/primitives/$lock.ts +13 -6
  627. package/src/logger/__tests__/CliFormatterProvider.spec.ts +76 -0
  628. package/src/logger/__tests__/Logger.spec.ts +49 -0
  629. package/src/logger/index.ts +25 -18
  630. package/src/logger/providers/CliFormatterProvider.ts +75 -0
  631. package/src/logger/schemas/logEntrySchema.ts +10 -10
  632. package/src/logger/services/Logger.ts +18 -0
  633. package/src/mcp/__tests__/$prompt.spec.ts +21 -23
  634. package/src/mcp/__tests__/$tool.spec.ts +22 -22
  635. package/src/mcp/__tests__/McpServerProvider.spec.ts +82 -11
  636. package/src/mcp/__tests__/StreamableHttpMcpTransport.spec.ts +2 -2
  637. package/src/mcp/__tests__/integration.spec.ts +33 -33
  638. package/src/mcp/primitives/$prompt.ts +11 -9
  639. package/src/mcp/primitives/$tool.ts +27 -86
  640. package/src/mcp/providers/McpServerProvider.ts +55 -0
  641. package/src/mcp/transports/StreamableHttpMcpTransport.ts +7 -7
  642. package/src/orm/__tests__/$repository-tests.ts +133 -27
  643. package/src/orm/__tests__/$repository.spec.ts +23 -0
  644. package/src/orm/__tests__/$transactional-tests.ts +4 -4
  645. package/src/orm/__tests__/ModelBuilder-tests.ts +28 -28
  646. package/src/orm/__tests__/ModelBuilder.spec.ts +124 -106
  647. package/src/orm/__tests__/QueryManager.spec.ts +5 -5
  648. package/src/orm/__tests__/delete-returning-tests.ts +11 -11
  649. package/src/orm/__tests__/deletedAt-tests.ts +3 -3
  650. package/src/orm/__tests__/enums.spec.ts +29 -25
  651. package/src/orm/__tests__/execute-tests.ts +8 -8
  652. package/src/orm/__tests__/fixtures/bigEntitySchema.ts +48 -48
  653. package/src/orm/__tests__/fixtures/userEntitySchema.ts +6 -6
  654. package/src/orm/__tests__/joins-tests.ts +30 -30
  655. package/src/orm/__tests__/organization-tests.ts +105 -3
  656. package/src/orm/__tests__/organization.spec.ts +46 -0
  657. package/src/orm/__tests__/orm-errors.spec.ts +10 -10
  658. package/src/orm/__tests__/orm-next-tests.ts +29 -29
  659. package/src/orm/__tests__/orm-showcase-tests.ts +9 -9
  660. package/src/orm/__tests__/primaryKey.spec.ts +4 -4
  661. package/src/orm/__tests__/query-date-encoding-tests.ts +17 -17
  662. package/src/orm/__tests__/ref-auto-onDelete.spec.ts +14 -14
  663. package/src/orm/__tests__/references.spec.ts +7 -7
  664. package/src/orm/__tests__/security.spec.ts +17 -17
  665. package/src/orm/__tests__/string-operators-tests.ts +5 -5
  666. package/src/orm/__tests__/timestamps-tests.ts +14 -14
  667. package/src/orm/__tests__/validation-tests.ts +5 -5
  668. package/src/orm/core/__tests__/BunSqliteProvider.bun.spec.ts +8 -8
  669. package/src/orm/core/constants/PG_SYMBOLS.ts +13 -1
  670. package/src/orm/core/entities/alephaSequences.ts +8 -8
  671. package/src/orm/core/helpers/pgAttr.ts +4 -4
  672. package/src/orm/core/primitives/$entity.ts +10 -6
  673. package/src/orm/core/providers/DatabaseTypeProvider.ts +46 -53
  674. package/src/orm/core/providers/drivers/BunSqliteProvider.ts +8 -8
  675. package/src/orm/core/providers/drivers/CloudflareD1Provider.ts +3 -5
  676. package/src/orm/core/providers/drivers/NodeSqliteProvider.ts +8 -8
  677. package/src/orm/core/schemas/databaseEnvSchema.ts +4 -4
  678. package/src/orm/core/schemas/insertSchema.ts +14 -18
  679. package/src/orm/core/schemas/legacyIdSchema.ts +2 -2
  680. package/src/orm/core/schemas/updateSchema.ts +10 -16
  681. package/src/orm/core/services/PgRelationManager.ts +2 -2
  682. package/src/orm/core/services/QueryManager.ts +13 -2
  683. package/src/orm/core/services/Repository.ts +88 -33
  684. package/src/orm/core/services/SqliteModelBuilder.ts +38 -45
  685. package/src/orm/postgres/__tests__/BunPostgresProvider.bun.spec.ts +4 -4
  686. package/src/orm/postgres/providers/CloudflareHyperdriveProvider.ts +4 -6
  687. package/src/orm/postgres/schemas/postgresEnvSchema.ts +6 -6
  688. package/src/orm/postgres/services/PostgresModelBuilder.ts +127 -114
  689. package/src/queue/core/__tests__/$consumer.spec.ts +4 -4
  690. package/src/queue/core/__tests__/WorkerProvider.spec.ts +4 -4
  691. package/src/queue/core/__tests__/shared.ts +6 -6
  692. package/src/queue/core/primitives/$consumer.ts +5 -5
  693. package/src/queue/core/primitives/$queue.ts +44 -61
  694. package/src/queue/core/providers/CloudflareQueueProvider.ts +6 -0
  695. package/src/queue/core/providers/WorkerProvider.ts +17 -14
  696. package/src/queue/redis/providers/RedisQueueProvider.ts +3 -3
  697. package/src/react/core/__tests__/ErrorBoundary.browser.spec.tsx +92 -0
  698. package/src/react/core/__tests__/Router.spec.tsx +4 -4
  699. package/src/react/core/__tests__/useComputed.browser.spec.tsx +192 -0
  700. package/src/react/core/__tests__/useQuery.browser.spec.tsx +48 -0
  701. package/src/react/core/__tests__/useSelector.browser.spec.tsx +105 -0
  702. package/src/react/core/__tests__/useStore.browser.spec.tsx +113 -0
  703. package/src/react/core/components/ErrorBoundary.tsx +42 -0
  704. package/src/react/core/hooks/useAction.ts +56 -19
  705. package/src/react/core/hooks/useComputed.ts +91 -0
  706. package/src/react/core/hooks/useSelector.ts +67 -0
  707. package/src/react/core/hooks/useStore.ts +36 -17
  708. package/src/react/core/index.ts +3 -0
  709. package/src/react/core/utils/shallowEqual.ts +36 -0
  710. package/src/react/form/__tests__/FormModel-submit-loading.spec.ts +71 -0
  711. package/src/react/form/__tests__/form-submitting-reactive.browser.spec.tsx +96 -0
  712. package/src/react/form/__tests__/useForm.browser.spec.tsx +54 -54
  713. package/src/react/form/errors/FormValidationError.ts +0 -2
  714. package/src/react/form/hooks/useForm.ts +19 -4
  715. package/src/react/form/hooks/useFormQuerySync.ts +0 -0
  716. package/src/react/form/services/FormModel.ts +124 -86
  717. package/src/react/form/services/parseField.ts +66 -27
  718. package/src/react/i18n/__tests__/I18nProvider.spec.ts +89 -0
  719. package/src/react/i18n/__tests__/Localize.spec.tsx +1 -1
  720. package/src/react/i18n/__tests__/locale-routing.spec.ts +107 -0
  721. package/src/react/i18n/providers/I18nProvider.ts +173 -14
  722. package/src/react/router/__tests__/$page.browser.spec.tsx +19 -19
  723. package/src/react/router/__tests__/$page.spec.tsx +21 -21
  724. package/src/react/router/__tests__/ReactBrowserProvider.browser.spec.ts +87 -1
  725. package/src/react/router/__tests__/RouterLocaleProvider.spec.ts +127 -0
  726. package/src/react/router/atoms/ssrManifestAtom.ts +16 -16
  727. package/src/react/router/components/NestedView.tsx +11 -3
  728. package/src/react/router/hooks/useQueryParams.ts +1 -1
  729. package/src/react/router/index.browser.ts +4 -0
  730. package/src/react/router/index.shared.ts +1 -0
  731. package/src/react/router/index.ts +9 -0
  732. package/src/react/router/primitives/$page.ts +2 -2
  733. package/src/react/router/providers/ReactBrowserProvider.ts +64 -13
  734. package/src/react/router/providers/ReactBrowserRouterProvider.ts +15 -1
  735. package/src/react/router/providers/ReactPageProvider.ts +38 -13
  736. package/src/react/router/providers/ReactServerProvider.ts +105 -16
  737. package/src/react/router/providers/RootComponentsProvider.ts +13 -0
  738. package/src/react/router/providers/RouterLocaleProvider.ts +125 -0
  739. package/src/react/router/providers/__tests__/RootComponentsProvider.spec.ts +15 -0
  740. package/src/react/router/providers/__tests__/rootComponents.ssr.browser.spec.tsx +67 -0
  741. package/src/react/sitemap/__tests__/$sitemap.spec.ts +131 -0
  742. package/src/react/sitemap/index.browser.ts +21 -0
  743. package/src/react/sitemap/index.ts +25 -0
  744. package/src/react/sitemap/primitives/$sitemap.browser.ts +26 -0
  745. package/src/react/sitemap/primitives/$sitemap.ts +196 -0
  746. package/src/react/ui/atoms/uiAtom.ts +6 -6
  747. package/src/react/ui/atoms/uiThemeListAtom.ts +7 -7
  748. package/src/react/ui/hooks/useColorMode.ts +11 -4
  749. package/src/react/ui/services/SchemaControl.ts +4 -4
  750. package/src/redis/providers/BunRedisProvider.ts +19 -11
  751. package/src/redis/providers/NodeRedisProvider.ts +3 -3
  752. package/src/scheduler/primitives/$scheduler.ts +21 -8
  753. package/src/security/__tests__/assertRealmScope.spec.ts +29 -0
  754. package/src/security/atoms/currentTenantAtom.ts +6 -6
  755. package/src/security/atoms/currentUserAtom.ts +2 -2
  756. package/src/security/primitives/$issuer.ts +24 -2
  757. package/src/security/providers/JwtProvider.ts +100 -0
  758. package/src/security/providers/SecurityProvider.ts +43 -2
  759. package/src/security/schemas/permissionSchema.ts +19 -19
  760. package/src/security/schemas/roleSchema.ts +22 -28
  761. package/src/security/schemas/userAccountInfoSchema.ts +47 -37
  762. package/src/server/auth/__tests__/appleClientSecret.spec.ts +34 -0
  763. package/src/server/auth/__tests__/federationRedirectReplay.spec.ts +44 -0
  764. package/src/server/auth/helpers/appleClientSecret.ts +24 -0
  765. package/src/server/auth/helpers/jtiReplayGuard.ts +41 -0
  766. package/src/server/auth/helpers/safeRedirectPath.ts +19 -0
  767. package/src/server/auth/index.ts +1 -0
  768. package/src/server/auth/primitives/$auth.ts +6 -6
  769. package/src/server/auth/primitives/$authApple.ts +10 -10
  770. package/src/server/auth/primitives/$authFacebook.ts +10 -10
  771. package/src/server/auth/primitives/$authFranceConnect.ts +10 -10
  772. package/src/server/auth/primitives/$authGithub.ts +10 -10
  773. package/src/server/auth/primitives/$authGoogle.ts +10 -10
  774. package/src/server/auth/primitives/$authMicrosoft.ts +14 -14
  775. package/src/server/auth/providers/ServerAuthProvider.ts +67 -52
  776. package/src/server/auth/schemas/authenticationProviderSchema.ts +9 -12
  777. package/src/server/auth/schemas/tokenResponseSchema.ts +2 -2
  778. package/src/server/auth/schemas/tokensSchema.ts +16 -16
  779. package/src/server/auth/schemas/userinfoResponseSchema.ts +3 -3
  780. package/src/server/cookies/__tests__/AtomCookieBinding.spec.ts +80 -0
  781. package/src/server/cookies/__tests__/AtomCookiePersistence.browser.spec.ts +169 -0
  782. package/src/server/cookies/__tests__/AtomCookiePersistence.spec.ts +294 -0
  783. package/src/server/cookies/__tests__/ServerCookiesProvider.spec.ts +82 -12
  784. package/src/server/cookies/index.browser.ts +4 -1
  785. package/src/server/cookies/index.ts +8 -1
  786. package/src/server/cookies/primitives/$cookie.browser.ts +1 -1
  787. package/src/server/cookies/primitives/$cookie.ts +16 -0
  788. package/src/server/cookies/providers/AtomCookiePersistence.browser.ts +133 -0
  789. package/src/server/cookies/providers/AtomCookiePersistence.ts +148 -0
  790. package/src/server/cookies/providers/ServerCookiesProvider.ts +32 -4
  791. package/src/server/cookies/services/CookieParser.ts +6 -1
  792. package/src/server/core/__tests__/$action.spec.ts +13 -13
  793. package/src/server/core/__tests__/$middleware.spec.ts +10 -10
  794. package/src/server/core/__tests__/$route.spec.ts +9 -9
  795. package/src/server/core/__tests__/$sse.spec.ts +26 -26
  796. package/src/server/core/__tests__/BunHttpServerProvider.bun.spec.ts +6 -6
  797. package/src/server/core/__tests__/HttpClient.spec.ts +2 -2
  798. package/src/server/core/__tests__/NodeHttpServerProvider.spec.ts +15 -3
  799. package/src/server/core/__tests__/ServerBodyParserProvider.spec.ts +14 -14
  800. package/src/server/core/__tests__/ServerMultipartProvider.spec.ts +33 -33
  801. package/src/server/core/__tests__/ServerRouterProvider-errorHandler.spec.ts +5 -1
  802. package/src/server/core/__tests__/ServerRouterProvider-getRoutes.spec.ts +25 -25
  803. package/src/server/core/__tests__/ServerRouterProvider-serializationError.spec.ts +9 -5
  804. package/src/server/core/__tests__/ServerRouterProvider-validationError.spec.ts +56 -24
  805. package/src/server/core/errors/ValidationError.ts +6 -3
  806. package/src/server/core/helpers/isMultipart.ts +2 -1
  807. package/src/server/core/index.ts +5 -2
  808. package/src/server/core/interfaces/ServerRequest.ts +14 -14
  809. package/src/server/core/primitives/$action.ts +26 -36
  810. package/src/server/core/primitives/$route.ts +27 -0
  811. package/src/server/core/primitives/$sse.ts +3 -3
  812. package/src/server/core/providers/BunHttpServerProvider.ts +8 -9
  813. package/src/server/core/providers/NodeHttpServerProvider.ts +8 -9
  814. package/src/server/core/providers/ServerBodyParserProvider.ts +20 -12
  815. package/src/server/core/providers/ServerCompressProvider.ts +9 -10
  816. package/src/server/core/providers/ServerHelmetProvider.ts +26 -27
  817. package/src/server/core/providers/ServerMultipartProvider.ts +19 -19
  818. package/src/server/core/providers/ServerRouterProvider.ts +44 -15
  819. package/src/server/core/schemas/errorSchema.ts +20 -25
  820. package/src/server/core/schemas/okSchema.ts +9 -14
  821. package/src/server/core/services/HttpClient.ts +13 -1
  822. package/src/server/core/services/ServerRequestParser.ts +6 -6
  823. package/src/server/cors/providers/ServerCorsProvider.ts +26 -28
  824. package/src/server/health/schemas/healthSchema.ts +6 -6
  825. package/src/server/links/__tests__/$remote.spec.ts +3 -3
  826. package/src/server/links/__tests__/BatchEndpoint.spec.ts +9 -9
  827. package/src/server/links/__tests__/LinkProvider.spec.ts +9 -9
  828. package/src/server/links/__tests__/ServerLinksProvider.spec.ts +23 -23
  829. package/src/server/links/__tests__/fixtures/CrudApp.ts +21 -21
  830. package/src/server/links/atoms/apiLinksAtom.ts +2 -2
  831. package/src/server/links/atoms/linkOptionsAtom.ts +6 -6
  832. package/src/server/links/primitives/$remote.ts +3 -3
  833. package/src/server/links/providers/LinkProvider.ts +3 -3
  834. package/src/server/links/providers/ServerLinksProvider.ts +20 -20
  835. package/src/server/links/schemas/apiLinksResponseSchema.ts +23 -23
  836. package/src/server/proxy/__tests__/$proxy.spec.ts +9 -9
  837. package/src/server/proxy/providers/ServerProxyProvider.ts +5 -0
  838. package/src/server/rate-limit/providers/ServerRateLimitProvider.ts +18 -20
  839. package/src/server/swagger/__tests__/$swagger.spec.ts +91 -94
  840. package/src/server/swagger/__tests__/ui.spec.ts +3 -3
  841. package/src/server/swagger/providers/ServerSwaggerProvider.ts +66 -43
  842. package/src/system/__tests__/NodeShellProvider.spec.ts +46 -0
  843. package/src/system/providers/NodeShellProvider.ts +4 -1
  844. package/src/topic/core/__tests__/$subscriber.spec.ts +4 -4
  845. package/src/topic/core/__tests__/shared.ts +8 -8
  846. package/src/topic/core/primitives/$subscriber.ts +5 -5
  847. package/src/topic/core/primitives/$topic.ts +14 -14
  848. package/src/topic/redis/providers/RedisTopicProvider.ts +3 -3
  849. package/dist/container/core/index.d.ts +0 -236
  850. package/dist/container/core/index.d.ts.map +0 -1
  851. package/dist/container/core/index.js +0 -231
  852. package/dist/container/core/index.js.map +0 -1
  853. package/dist/container/core/index.workerd.js +0 -192
  854. package/dist/container/core/index.workerd.js.map +0 -1
  855. package/src/cli/core/tasks/BuildSitemapTask.ts +0 -130
  856. package/src/command/helpers/PrettyAsker.ts +0 -256
  857. package/src/command/helpers/PrettyPrint.ts +0 -332
  858. package/src/container/core/__tests__/$container.spec.ts +0 -83
  859. package/src/container/core/index.ts +0 -50
  860. package/src/container/core/index.workerd.ts +0 -53
  861. package/src/container/core/interfaces/ContainerOptions.ts +0 -69
  862. package/src/container/core/primitives/$container.ts +0 -100
  863. package/src/container/core/providers/CloudflareContainerProvider.ts +0 -70
  864. package/src/container/core/providers/ContainerProvider.ts +0 -92
  865. package/src/container/core/providers/MockContainerProvider.ts +0 -62
  866. package/src/container/core/providers/NodeContainerProvider.ts +0 -53
  867. package/src/core/__tests__/jsonSchemaToTypeBox.spec.ts +0 -771
  868. package/src/core/helpers/jsonSchemaToTypeBox.ts +0 -359
  869. /package/dist/orm/core/{chunk-B4FMCO8f.js → rolldown-runtime-B4FMCO8f.js} +0 -0
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../../src/batch/providers/BatchProvider.ts","../../src/batch/primitives/$batch.ts","../../src/batch/index.ts"],"sourcesContent":["import { $hook, $inject, type Alepha, AlephaError } from \"alepha\";\nimport { CryptoProvider } from \"alepha/crypto\";\nimport { DateTimeProvider, type DurationLike } from \"alepha/datetime\";\nimport { $logger } from \"alepha/logger\";\nimport { type RetryBackoffOptions, RetryProvider } from \"alepha/retry\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport interface BatchOptions<TItem, TResponse = any> {\n /**\n * The batch processing handler function that processes arrays of validated items.\n */\n handler: (items: TItem[]) => TResponse;\n\n /**\n * Maximum number of items to collect before automatically flushing the batch.\n *\n * @default 10\n */\n maxSize?: number;\n\n /**\n * Maximum number of items that can be queued in a single partition.\n * If exceeded, push() will throw an error.\n */\n maxQueueSize?: number;\n\n /**\n * Maximum time to wait before flushing a batch, even if it hasn't reached maxSize.\n *\n * @default [1, \"second\"]\n */\n maxDuration?: DurationLike;\n\n /**\n * Function to determine partition keys for grouping items into separate batches.\n */\n partitionBy?: (item: TItem) => string;\n\n /**\n * Maximum number of batch handlers that can execute simultaneously.\n *\n * @default 1\n */\n concurrency?: number;\n\n /**\n * Retry configuration for failed batch processing operations.\n */\n retry?: {\n /**\n * The maximum number of attempts.\n *\n * @default 3\n */\n max?: number;\n\n /**\n * The backoff strategy for delays between retries.\n * Can be a fixed number (in ms) or a configuration object for exponential backoff.\n *\n * @default { initial: 200, factor: 2, jitter: true }\n */\n backoff?: number | RetryBackoffOptions;\n\n /**\n * An overall time limit for all retry attempts combined.\n *\n * e.g., `[5, 'seconds']`\n */\n maxDuration?: DurationLike;\n\n /**\n * A function that determines if a retry should be attempted based on the error.\n *\n * @default (error) => true (retries on any error)\n */\n when?: (error: Error) => boolean;\n\n /**\n * A custom callback for when a retry attempt fails.\n * This is called before the delay.\n */\n onError?: (error: Error, attempt: number) => void;\n };\n}\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport type BatchItemStatus = \"pending\" | \"processing\" | \"completed\" | \"failed\";\n\nexport interface BatchItemState<TItem, TResponse> {\n id: string;\n item: TItem;\n partitionKey: string;\n status: BatchItemStatus;\n result?: TResponse;\n error?: Error;\n promise?: Promise<TResponse>;\n resolve?: (value: TResponse) => void;\n reject?: (error: Error) => void;\n}\n\nexport interface PartitionState {\n itemIds: string[];\n timeout?: { clear: () => void };\n flushing: boolean;\n}\n\n/**\n * Context object that holds all state for a batch processor instance.\n */\nexport interface BatchContext<TItem, TResponse> {\n options: BatchOptions<TItem, TResponse>;\n itemStates: Map<string, BatchItemState<TItem, TResponse>>;\n partitions: Map<string, PartitionState>;\n activeHandlers: PromiseWithResolvers<void>[];\n isShuttingDown: boolean;\n isReady: boolean;\n alepha: Alepha;\n}\n\n// ---------------------------------------------------------------------------------------------------------------------\n\n/**\n * Service for batch processing operations.\n * Provides methods to manage batches of items with automatic flushing based on size or time.\n */\nexport class BatchProvider {\n protected readonly log = $logger();\n protected readonly dateTime = $inject(DateTimeProvider);\n protected readonly retryProvider = $inject(RetryProvider);\n protected readonly crypto = $inject(CryptoProvider);\n\n /**\n * All active batch contexts managed by this provider.\n */\n protected readonly contexts = new Set<BatchContext<any, any>>();\n\n /**\n * Creates a new batch context with the given options.\n */\n createContext<TItem, TResponse>(\n alepha: Alepha,\n options: BatchOptions<TItem, TResponse>,\n ): BatchContext<TItem, TResponse> {\n const context: BatchContext<TItem, TResponse> = {\n options,\n itemStates: new Map(),\n partitions: new Map(),\n activeHandlers: [],\n isShuttingDown: false,\n isReady: false,\n alepha,\n };\n this.contexts.add(context);\n return context;\n }\n\n /**\n * Shutdown hook - flushes all batch contexts on application stop.\n */\n protected readonly onStop = $hook({\n on: \"stop\",\n priority: \"first\",\n handler: async () => {\n if (this.contexts.size === 0) {\n return;\n }\n this.log.debug(`Shutting down ${this.contexts.size} batch context(s)...`);\n const promises: Promise<void>[] = [];\n for (const context of this.contexts) {\n promises.push(this.shutdown(context));\n }\n await Promise.all(promises);\n this.log.debug(\"All batch contexts shut down\");\n },\n });\n\n /**\n * Get the effective maxSize for a context.\n */\n protected getMaxSize<TItem, TResponse>(\n context: BatchContext<TItem, TResponse>,\n ): number {\n return context.options.maxSize ?? 10;\n }\n\n /**\n * Get the effective concurrency for a context.\n */\n protected getConcurrency<TItem, TResponse>(\n context: BatchContext<TItem, TResponse>,\n ): number {\n return context.options.concurrency ?? 1;\n }\n\n /**\n * Get the effective maxDuration for a context.\n */\n protected getMaxDuration<TItem, TResponse>(\n context: BatchContext<TItem, TResponse>,\n ): DurationLike {\n return context.options.maxDuration ?? [1, \"second\"];\n }\n\n /**\n * Pushes an item into the batch and returns immediately with a unique ID.\n * The item will be processed asynchronously with other items when the batch is flushed.\n * Use wait(id) to get the processing result.\n *\n * @throws Error if maxQueueSize is exceeded\n */\n push<TItem, TResponse>(\n context: BatchContext<TItem, TResponse>,\n item: TItem,\n ): string {\n // 1. Generate unique ID\n const id = this.crypto.randomUUID();\n\n // 2. Determine the partition key (with error handling)\n let partitionKey: string;\n try {\n partitionKey = context.options.partitionBy\n ? context.options.partitionBy(item)\n : \"default\";\n } catch (error) {\n this.log.warn(\n \"partitionBy function threw an error, using 'default' partition\",\n { error },\n );\n partitionKey = \"default\";\n }\n\n // 3. Create item state\n const itemState: BatchItemState<TItem, TResponse> = {\n id,\n item,\n partitionKey,\n status: \"pending\",\n };\n\n // CAUTION: Do not log.debug/info here as it may cause infinite loops if logging is batched\n\n context.itemStates.set(id, itemState);\n\n // 4. Get or create the partition state\n if (!context.partitions.has(partitionKey)) {\n context.partitions.set(partitionKey, {\n itemIds: [],\n flushing: false,\n });\n }\n const partition = context.partitions.get(partitionKey)!;\n\n // 5. Check maxQueueSize before adding\n if (\n context.options.maxQueueSize !== undefined &&\n partition.itemIds.length >= context.options.maxQueueSize\n ) {\n throw new AlephaError(\n `Batch queue size exceeded for partition '${partitionKey}' (max: ${context.options.maxQueueSize})`,\n );\n }\n\n // 6. Add item ID to partition\n partition.itemIds.push(id);\n\n const maxSize = this.getMaxSize(context);\n const maxDuration = this.getMaxDuration(context);\n\n // 7. Only start processing if the app is ready (after \"ready\" hook)\n // During startup, items are just buffered in memory\n if (context.isReady) {\n // Check if the batch is full\n if (partition.itemIds.length >= maxSize) {\n this.log.trace(\n `Batch partition '${partitionKey}' is full, flushing...`,\n );\n this.flushPartition(context, partitionKey).catch((error) =>\n this.log.error(\n `Failed to flush batch partition '${partitionKey}' on max size`,\n error,\n ),\n );\n } else if (!partition.timeout && !partition.flushing) {\n // 8. Start the timeout if it's not already running for this partition and not currently flushing\n partition.timeout = this.dateTime.createTimeout(() => {\n this.log.trace(\n `Batch partition '${partitionKey}' timed out, flushing...`,\n );\n this.flushPartition(context, partitionKey).catch((error) =>\n this.log.error(\n `Failed to flush batch partition '${partitionKey}' on timeout`,\n error,\n ),\n );\n }, maxDuration);\n }\n } else {\n // Not ready yet - just buffer items, no size checks or timeouts\n this.log.trace(\n `Buffering item in partition '${partitionKey}' (app not ready yet, ${partition.itemIds.length} items buffered)`,\n );\n }\n\n // 9. Return ID immediately\n return id;\n }\n\n /**\n * Wait for a specific item to be processed and get its result.\n * @param id The item ID returned from push()\n * @returns The processing result\n * @throws If the item doesn't exist or processing failed\n */\n async wait<TItem, TResponse>(\n context: BatchContext<TItem, TResponse>,\n id: string,\n ): Promise<TResponse> {\n const itemState = context.itemStates.get(id);\n if (!itemState) {\n throw new AlephaError(`Item with id '${id}' not found`);\n }\n\n // If already completed or failed, return immediately\n if (itemState.status === \"completed\") {\n return itemState.result!;\n }\n if (itemState.status === \"failed\") {\n throw itemState.error!;\n }\n\n // Create promise on-demand if not already created\n if (!itemState.promise) {\n itemState.promise = new Promise<TResponse>((resolve, reject) => {\n itemState.resolve = resolve;\n itemState.reject = reject;\n });\n }\n\n return itemState.promise;\n }\n\n /**\n * Get the current status of an item.\n * @param id The item ID returned from push()\n * @returns Status information or undefined if item doesn't exist\n */\n status<TItem, TResponse>(\n context: BatchContext<TItem, TResponse>,\n id: string,\n ):\n | { status: \"pending\" | \"processing\" }\n | { status: \"completed\"; result: TResponse }\n | { status: \"failed\"; error: Error }\n | undefined {\n const itemState = context.itemStates.get(id);\n if (!itemState) {\n return undefined;\n }\n\n if (itemState.status === \"completed\") {\n return { status: \"completed\", result: itemState.result! };\n }\n if (itemState.status === \"failed\") {\n return { status: \"failed\", error: itemState.error! };\n }\n return { status: itemState.status };\n }\n\n /**\n * Clears completed and failed items from the context to free memory.\n * Returns the number of items cleared.\n *\n * @param context The batch context\n * @param status Optional: only clear items with this specific status ('completed' or 'failed')\n * @returns The number of items cleared\n */\n clearCompleted<TItem, TResponse>(\n context: BatchContext<TItem, TResponse>,\n status?: \"completed\" | \"failed\",\n ): number {\n let count = 0;\n for (const [id, state] of context.itemStates) {\n if (status) {\n if (state.status === status) {\n context.itemStates.delete(id);\n count++;\n }\n } else if (state.status === \"completed\" || state.status === \"failed\") {\n context.itemStates.delete(id);\n count++;\n }\n }\n return count;\n }\n\n /**\n * Flush all partitions or a specific partition.\n */\n async flush<TItem, TResponse>(\n context: BatchContext<TItem, TResponse>,\n partitionKey?: string,\n ): Promise<void> {\n const promises: Promise<void>[] = [];\n if (partitionKey) {\n if (context.partitions.has(partitionKey)) {\n promises.push(this.flushPartition(context, partitionKey));\n }\n } else {\n for (const key of context.partitions.keys()) {\n promises.push(this.flushPartition(context, key));\n }\n }\n await Promise.all(promises);\n }\n\n /**\n * Flush a specific partition.\n */\n protected async flushPartition<TItem, TResponse>(\n context: BatchContext<TItem, TResponse>,\n partitionKey: string,\n limit?: number,\n ): Promise<void> {\n const partition = context.partitions.get(partitionKey);\n if (!partition || partition.itemIds.length === 0) {\n context.partitions.delete(partitionKey);\n return;\n }\n\n // Clear the timeout and grab the item IDs (up to limit if specified)\n partition.timeout?.clear();\n partition.timeout = undefined;\n const itemsToTake =\n limit !== undefined\n ? Math.min(limit, partition.itemIds.length)\n : partition.itemIds.length;\n const itemIdsToProcess = partition.itemIds.splice(0, itemsToTake);\n\n // Mark partition as flushing to prevent race conditions\n partition.flushing = true;\n\n // Get the items and mark them as processing\n const itemsToProcess: TItem[] = [];\n for (const id of itemIdsToProcess) {\n const itemState = context.itemStates.get(id);\n if (itemState) {\n itemState.status = \"processing\";\n itemsToProcess.push(itemState.item);\n }\n }\n\n const concurrency = this.getConcurrency(context);\n const maxDuration = this.getMaxDuration(context);\n\n // Wait until there's a free slot (if at concurrency limit)\n while (context.activeHandlers.length >= concurrency) {\n this.log.trace(\n `Batch handler is at concurrency limit, waiting for a slot...`,\n );\n // Wait for any single handler to complete, not all of them\n await Promise.race(context.activeHandlers.map((it) => it.promise));\n }\n\n const promise = Promise.withResolvers<void>();\n context.activeHandlers.push(promise);\n let result: any;\n try {\n result = await context.alepha.context.run(() =>\n // during shutdown, call handler directly to avoid retry cancellation\n context.isShuttingDown\n ? context.options.handler(itemsToProcess)\n : this.retryProvider.retry(\n {\n ...context.options.retry,\n handler: context.options.handler,\n },\n itemsToProcess,\n ),\n );\n\n // Mark all items as completed and resolve their promises\n for (const id of itemIdsToProcess) {\n const itemState = context.itemStates.get(id);\n if (itemState) {\n itemState.status = \"completed\";\n itemState.result = result;\n // Only resolve if someone is waiting\n itemState.resolve?.(result);\n }\n }\n } catch (error) {\n this.log.error(`Batch handler failed`, error);\n\n // Mark all items as failed and reject their promises\n for (const id of itemIdsToProcess) {\n const itemState = context.itemStates.get(id);\n if (itemState) {\n itemState.status = \"failed\";\n itemState.error = error as Error;\n // Only reject if someone is waiting (promise was created)\n itemState.reject?.(error as Error);\n }\n }\n } finally {\n promise.resolve();\n context.activeHandlers = context.activeHandlers.filter(\n (it) => it !== promise,\n );\n\n // Only delete partition if no new items arrived during processing\n const currentPartition = context.partitions.get(partitionKey);\n if (currentPartition?.flushing && currentPartition.itemIds.length === 0) {\n context.partitions.delete(partitionKey);\n } else if (currentPartition) {\n // Reset flushing flag if partition still exists with items\n currentPartition.flushing = false;\n\n // Restart timeout for items that arrived during flush\n if (currentPartition.itemIds.length > 0 && !currentPartition.timeout) {\n currentPartition.timeout = this.dateTime.createTimeout(() => {\n this.log.trace(\n `Batch partition '${partitionKey}' timed out, flushing...`,\n );\n this.flushPartition(context, partitionKey).catch((error) =>\n this.log.error(\n `Failed to flush batch partition '${partitionKey}' on timeout`,\n error,\n ),\n );\n }, maxDuration);\n }\n }\n }\n }\n\n /**\n * Mark the context as ready and start processing buffered items.\n * Called after the \"ready\" hook.\n */\n async markReady<TItem, TResponse>(\n context: BatchContext<TItem, TResponse>,\n ): Promise<void> {\n this.log.debug(\n \"Batch processor is now ready, starting to process buffered items...\",\n );\n context.isReady = true;\n await this.startProcessing(context);\n }\n\n /**\n * Mark the context as shutting down and flush all remaining items.\n */\n async shutdown<TItem, TResponse>(\n context: BatchContext<TItem, TResponse>,\n ): Promise<void> {\n this.log.debug(\"Flushing all remaining batch partitions on shutdown...\");\n context.isShuttingDown = true;\n await this.flush(context);\n this.log.debug(\"All batch partitions flushed\");\n }\n\n /**\n * Called after the \"ready\" hook to start processing buffered items that were\n * pushed during startup. This checks all partitions and starts timeouts/flushes\n * for items that were accumulated before the app was ready.\n */\n protected async startProcessing<TItem, TResponse>(\n context: BatchContext<TItem, TResponse>,\n ): Promise<void> {\n const maxSize = this.getMaxSize(context);\n const maxDuration = this.getMaxDuration(context);\n\n for (const [partitionKey, partition] of context.partitions.entries()) {\n if (partition.itemIds.length === 0) {\n continue;\n }\n\n this.log.trace(\n `Starting processing for partition '${partitionKey}' with ${partition.itemIds.length} buffered items`,\n );\n\n // Flush batches of maxSize while we have items >= maxSize\n while (partition.itemIds.length >= maxSize) {\n this.log.trace(\n `Partition '${partitionKey}' has ${partition.itemIds.length} items, flushing batch of ${maxSize}...`,\n );\n await this.flushPartition(context, partitionKey, maxSize);\n }\n\n // After flushing full batches, start timeout for any remaining items\n if (\n partition.itemIds.length > 0 &&\n !partition.timeout &&\n !partition.flushing\n ) {\n this.log.trace(\n `Starting timeout for partition '${partitionKey}' with ${partition.itemIds.length} remaining items`,\n );\n partition.timeout = this.dateTime.createTimeout(() => {\n this.log.trace(\n `Batch partition '${partitionKey}' timed out, flushing...`,\n );\n this.flushPartition(context, partitionKey).catch((error) =>\n this.log.error(\n `Failed to flush partition '${partitionKey}' on timeout after startup`,\n error,\n ),\n );\n }, maxDuration);\n }\n }\n }\n}\n","import {\n $hook,\n $inject,\n createPrimitive,\n KIND,\n Primitive,\n type Static,\n type TSchema,\n} from \"alepha\";\nimport type { DurationLike } from \"alepha/datetime\";\nimport type { RetryPrimitiveOptions } from \"alepha/retry\";\nimport {\n type BatchContext,\n type BatchItemState,\n type BatchItemStatus,\n BatchProvider,\n} from \"../providers/BatchProvider.ts\";\n\n/**\n * Creates a batch processing primitive for efficient grouping and processing of multiple operations.\n */\nexport const $batch = <TItem extends TSchema, TResponse>(\n options: BatchPrimitiveOptions<TItem, TResponse>,\n): BatchPrimitive<TItem, TResponse> =>\n createPrimitive(BatchPrimitive<TItem, TResponse>, options);\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport interface BatchPrimitiveOptions<TItem extends TSchema, TResponse = any> {\n /**\n * TypeBox schema for validating each item added to the batch.\n */\n schema: TItem;\n\n /**\n * The batch processing handler function that processes arrays of validated items.\n */\n handler: (items: Static<TItem>[]) => TResponse;\n\n /**\n * Maximum number of items to collect before automatically flushing the batch.\n */\n maxSize?: number;\n\n /**\n * Maximum number of items that can be queued in a single partition.\n * If exceeded, push() will throw an error.\n */\n maxQueueSize?: number;\n\n /**\n * Maximum time to wait before flushing a batch, even if it hasn't reached maxSize.\n */\n maxDuration?: DurationLike;\n\n /**\n * Function to determine partition keys for grouping items into separate batches.\n */\n partitionBy?: (item: Static<TItem>) => string;\n\n /**\n * Maximum number of batch handlers that can execute simultaneously.\n */\n concurrency?: number;\n\n /**\n * Retry configuration for failed batch processing operations.\n */\n retry?: Omit<RetryPrimitiveOptions<() => Array<Static<TItem>>>, \"handler\">;\n}\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport type { BatchItemState, BatchItemStatus };\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport class BatchPrimitive<\n TItem extends TSchema,\n TResponse = any,\n> extends Primitive<BatchPrimitiveOptions<TItem, TResponse>> {\n protected readonly batchProvider = $inject(BatchProvider);\n protected readonly context: BatchContext<Static<TItem>, TResponse>;\n\n constructor(\n ...args: ConstructorParameters<\n typeof Primitive<BatchPrimitiveOptions<TItem, TResponse>>\n >\n ) {\n super(...args);\n this.context = this.batchProvider.createContext(this.alepha, {\n handler: this.options.handler,\n maxSize: this.options.maxSize,\n maxQueueSize: this.options.maxQueueSize,\n maxDuration: this.options.maxDuration,\n partitionBy: this.options.partitionBy,\n concurrency: this.options.concurrency,\n retry: this.options.retry,\n });\n }\n\n /**\n * Pushes an item into the batch and returns immediately with a unique ID.\n * The item will be processed asynchronously with other items when the batch is flushed.\n * Use wait(id) to get the processing result.\n */\n public async push(item: Static<TItem>): Promise<string> {\n // Validate the item against the schema\n const validatedItem = this.alepha.codec.validate(this.options.schema, item);\n return this.batchProvider.push(this.context, validatedItem);\n }\n\n /**\n * Wait for a specific item to be processed and get its result.\n * @param id The item ID returned from push()\n * @returns The processing result\n * @throws If the item doesn't exist or processing failed\n */\n public async wait(id: string): Promise<TResponse> {\n return this.batchProvider.wait(this.context, id);\n }\n\n /**\n * Get the current status of an item.\n * @param id The item ID returned from push()\n * @returns Status information or undefined if item doesn't exist\n */\n public status(\n id: string,\n ):\n | { status: \"pending\" | \"processing\" }\n | { status: \"completed\"; result: TResponse }\n | { status: \"failed\"; error: Error }\n | undefined {\n return this.batchProvider.status(this.context, id);\n }\n\n /**\n * Flush all partitions or a specific partition.\n */\n public async flush(partitionKey?: string): Promise<void> {\n return this.batchProvider.flush(this.context, partitionKey);\n }\n\n /**\n * Clears completed and failed items from memory.\n * Call this periodically in long-running applications to prevent memory leaks.\n *\n * @param status Optional: only clear items with this specific status ('completed' or 'failed')\n * @returns The number of items cleared\n */\n public clearCompleted(status?: \"completed\" | \"failed\"): number {\n return this.batchProvider.clearCompleted(this.context, status);\n }\n\n protected readonly onReady = $hook({\n on: \"ready\",\n handler: async () => {\n await this.batchProvider.markReady(this.context);\n },\n });\n}\n\n$batch[KIND] = BatchPrimitive;\n","import { $module } from \"alepha\";\nimport { $batch } from \"./primitives/$batch.ts\";\nimport { BatchProvider } from \"./providers/BatchProvider.ts\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport * from \"./primitives/$batch.ts\";\nexport * from \"./providers/BatchProvider.ts\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\n/**\n * Batch accumulation and processing.\n *\n * **Features:**\n * - Batch accumulator with handler\n * - Configurable batch size\n * - Time-based triggers\n * - Status tracking\n *\n * @module alepha.batch\n */\nexport const AlephaBatch = $module({\n name: \"alepha.batch\",\n primitives: [$batch],\n services: [BatchProvider],\n});\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","names":[],"sources":["../../src/batch/providers/BatchProvider.ts","../../src/batch/primitives/$batch.ts","../../src/batch/index.ts"],"sourcesContent":["import { $hook, $inject, type Alepha, AlephaError } from \"alepha\";\nimport { CryptoProvider } from \"alepha/crypto\";\nimport { DateTimeProvider, type DurationLike } from \"alepha/datetime\";\nimport { $logger } from \"alepha/logger\";\nimport { type RetryBackoffOptions, RetryProvider } from \"alepha/retry\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport interface BatchOptions<TItem, TResponse = any> {\n /**\n * The batch processing handler function that processes arrays of validated items.\n */\n handler: (items: TItem[]) => TResponse;\n\n /**\n * Maximum number of items to collect before automatically flushing the batch.\n *\n * @default 10\n */\n maxSize?: number;\n\n /**\n * Maximum number of items that can be queued in a single partition.\n * If exceeded, push() will throw an error.\n */\n maxQueueSize?: number;\n\n /**\n * Maximum time to wait before flushing a batch, even if it hasn't reached maxSize.\n *\n * @default [1, \"second\"]\n */\n maxDuration?: DurationLike;\n\n /**\n * Function to determine partition keys for grouping items into separate batches.\n */\n partitionBy?: (item: TItem) => string;\n\n /**\n * Maximum number of batch handlers that can execute simultaneously.\n *\n * @default 1\n */\n concurrency?: number;\n\n /**\n * Retry configuration for failed batch processing operations.\n */\n retry?: {\n /**\n * The maximum number of attempts.\n *\n * @default 3\n */\n max?: number;\n\n /**\n * The backoff strategy for delays between retries.\n * Can be a fixed number (in ms) or a configuration object for exponential backoff.\n *\n * @default { initial: 200, factor: 2, jitter: true }\n */\n backoff?: number | RetryBackoffOptions;\n\n /**\n * An overall time limit for all retry attempts combined.\n *\n * e.g., `[5, 'seconds']`\n */\n maxDuration?: DurationLike;\n\n /**\n * A function that determines if a retry should be attempted based on the error.\n *\n * @default (error) => true (retries on any error)\n */\n when?: (error: Error) => boolean;\n\n /**\n * A custom callback for when a retry attempt fails.\n * This is called before the delay.\n */\n onError?: (error: Error, attempt: number) => void;\n };\n}\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport type BatchItemStatus = \"pending\" | \"processing\" | \"completed\" | \"failed\";\n\nexport interface BatchItemState<TItem, TResponse> {\n id: string;\n item: TItem;\n partitionKey: string;\n status: BatchItemStatus;\n result?: TResponse;\n error?: Error;\n promise?: Promise<TResponse>;\n resolve?: (value: TResponse) => void;\n reject?: (error: Error) => void;\n}\n\nexport interface PartitionState {\n itemIds: string[];\n timeout?: { clear: () => void };\n flushing: boolean;\n}\n\n/**\n * Context object that holds all state for a batch processor instance.\n */\nexport interface BatchContext<TItem, TResponse> {\n options: BatchOptions<TItem, TResponse>;\n itemStates: Map<string, BatchItemState<TItem, TResponse>>;\n partitions: Map<string, PartitionState>;\n activeHandlers: PromiseWithResolvers<void>[];\n isShuttingDown: boolean;\n isReady: boolean;\n alepha: Alepha;\n}\n\n// ---------------------------------------------------------------------------------------------------------------------\n\n/**\n * Service for batch processing operations.\n * Provides methods to manage batches of items with automatic flushing based on size or time.\n */\nexport class BatchProvider {\n protected readonly log = $logger();\n protected readonly dateTime = $inject(DateTimeProvider);\n protected readonly retryProvider = $inject(RetryProvider);\n protected readonly crypto = $inject(CryptoProvider);\n\n /**\n * All active batch contexts managed by this provider.\n */\n protected readonly contexts = new Set<BatchContext<any, any>>();\n\n /**\n * Creates a new batch context with the given options.\n */\n createContext<TItem, TResponse>(\n alepha: Alepha,\n options: BatchOptions<TItem, TResponse>,\n ): BatchContext<TItem, TResponse> {\n const context: BatchContext<TItem, TResponse> = {\n options,\n itemStates: new Map(),\n partitions: new Map(),\n activeHandlers: [],\n isShuttingDown: false,\n isReady: false,\n alepha,\n };\n this.contexts.add(context);\n return context;\n }\n\n /**\n * Shutdown hook - flushes all batch contexts on application stop.\n */\n protected readonly onStop = $hook({\n on: \"stop\",\n priority: \"first\",\n handler: async () => {\n if (this.contexts.size === 0) {\n return;\n }\n this.log.debug(`Shutting down ${this.contexts.size} batch context(s)...`);\n const promises: Promise<void>[] = [];\n for (const context of this.contexts) {\n promises.push(this.shutdown(context));\n }\n await Promise.all(promises);\n this.log.debug(\"All batch contexts shut down\");\n },\n });\n\n /**\n * Get the effective maxSize for a context.\n */\n protected getMaxSize<TItem, TResponse>(\n context: BatchContext<TItem, TResponse>,\n ): number {\n return context.options.maxSize ?? 10;\n }\n\n /**\n * Get the effective concurrency for a context.\n */\n protected getConcurrency<TItem, TResponse>(\n context: BatchContext<TItem, TResponse>,\n ): number {\n return context.options.concurrency ?? 1;\n }\n\n /**\n * Get the effective maxDuration for a context.\n */\n protected getMaxDuration<TItem, TResponse>(\n context: BatchContext<TItem, TResponse>,\n ): DurationLike {\n return context.options.maxDuration ?? [1, \"second\"];\n }\n\n /**\n * Pushes an item into the batch and returns immediately with a unique ID.\n * The item will be processed asynchronously with other items when the batch is flushed.\n * Use wait(id) to get the processing result.\n *\n * @throws Error if maxQueueSize is exceeded\n */\n push<TItem, TResponse>(\n context: BatchContext<TItem, TResponse>,\n item: TItem,\n ): string {\n // 1. Generate unique ID\n const id = this.crypto.randomUUID();\n\n // 2. Determine the partition key (with error handling)\n let partitionKey: string;\n try {\n partitionKey = context.options.partitionBy\n ? context.options.partitionBy(item)\n : \"default\";\n } catch (error) {\n this.log.warn(\n \"partitionBy function threw an error, using 'default' partition\",\n { error },\n );\n partitionKey = \"default\";\n }\n\n // 3. Create item state\n const itemState: BatchItemState<TItem, TResponse> = {\n id,\n item,\n partitionKey,\n status: \"pending\",\n };\n\n // CAUTION: Do not log.debug/info here as it may cause infinite loops if logging is batched\n\n context.itemStates.set(id, itemState);\n\n // 4. Get or create the partition state\n if (!context.partitions.has(partitionKey)) {\n context.partitions.set(partitionKey, {\n itemIds: [],\n flushing: false,\n });\n }\n const partition = context.partitions.get(partitionKey)!;\n\n // 5. Check maxQueueSize before adding\n if (\n context.options.maxQueueSize !== undefined &&\n partition.itemIds.length >= context.options.maxQueueSize\n ) {\n throw new AlephaError(\n `Batch queue size exceeded for partition '${partitionKey}' (max: ${context.options.maxQueueSize})`,\n );\n }\n\n // 6. Add item ID to partition\n partition.itemIds.push(id);\n\n const maxSize = this.getMaxSize(context);\n const maxDuration = this.getMaxDuration(context);\n\n // 7. Only start processing if the app is ready (after \"ready\" hook)\n // During startup, items are just buffered in memory\n if (context.isReady) {\n // Check if the batch is full\n if (partition.itemIds.length >= maxSize) {\n this.log.trace(\n `Batch partition '${partitionKey}' is full, flushing...`,\n );\n this.flushPartition(context, partitionKey).catch((error) =>\n this.log.error(\n `Failed to flush batch partition '${partitionKey}' on max size`,\n error,\n ),\n );\n } else if (!partition.timeout && !partition.flushing) {\n // 8. Start the timeout if it's not already running for this partition and not currently flushing\n partition.timeout = this.dateTime.createTimeout(() => {\n this.log.trace(\n `Batch partition '${partitionKey}' timed out, flushing...`,\n );\n this.flushPartition(context, partitionKey).catch((error) =>\n this.log.error(\n `Failed to flush batch partition '${partitionKey}' on timeout`,\n error,\n ),\n );\n }, maxDuration);\n }\n } else {\n // Not ready yet - just buffer items, no size checks or timeouts\n this.log.trace(\n `Buffering item in partition '${partitionKey}' (app not ready yet, ${partition.itemIds.length} items buffered)`,\n );\n }\n\n // 9. Return ID immediately\n return id;\n }\n\n /**\n * Wait for a specific item to be processed and get its result.\n * @param id The item ID returned from push()\n * @returns The processing result\n * @throws If the item doesn't exist or processing failed\n */\n async wait<TItem, TResponse>(\n context: BatchContext<TItem, TResponse>,\n id: string,\n ): Promise<TResponse> {\n const itemState = context.itemStates.get(id);\n if (!itemState) {\n throw new AlephaError(`Item with id '${id}' not found`);\n }\n\n // If already completed or failed, return immediately\n if (itemState.status === \"completed\") {\n return itemState.result!;\n }\n if (itemState.status === \"failed\") {\n throw itemState.error!;\n }\n\n // Create promise on-demand if not already created\n if (!itemState.promise) {\n itemState.promise = new Promise<TResponse>((resolve, reject) => {\n itemState.resolve = resolve;\n itemState.reject = reject;\n });\n }\n\n return itemState.promise;\n }\n\n /**\n * Get the current status of an item.\n * @param id The item ID returned from push()\n * @returns Status information or undefined if item doesn't exist\n */\n status<TItem, TResponse>(\n context: BatchContext<TItem, TResponse>,\n id: string,\n ):\n | { status: \"pending\" | \"processing\" }\n | { status: \"completed\"; result: TResponse }\n | { status: \"failed\"; error: Error }\n | undefined {\n const itemState = context.itemStates.get(id);\n if (!itemState) {\n return undefined;\n }\n\n if (itemState.status === \"completed\") {\n return { status: \"completed\", result: itemState.result! };\n }\n if (itemState.status === \"failed\") {\n return { status: \"failed\", error: itemState.error! };\n }\n return { status: itemState.status };\n }\n\n /**\n * Clears completed and failed items from the context to free memory.\n * Returns the number of items cleared.\n *\n * @param context The batch context\n * @param status Optional: only clear items with this specific status ('completed' or 'failed')\n * @returns The number of items cleared\n */\n clearCompleted<TItem, TResponse>(\n context: BatchContext<TItem, TResponse>,\n status?: \"completed\" | \"failed\",\n ): number {\n let count = 0;\n for (const [id, state] of context.itemStates) {\n if (status) {\n if (state.status === status) {\n context.itemStates.delete(id);\n count++;\n }\n } else if (state.status === \"completed\" || state.status === \"failed\") {\n context.itemStates.delete(id);\n count++;\n }\n }\n return count;\n }\n\n /**\n * Flush all partitions or a specific partition.\n */\n async flush<TItem, TResponse>(\n context: BatchContext<TItem, TResponse>,\n partitionKey?: string,\n ): Promise<void> {\n const promises: Promise<void>[] = [];\n if (partitionKey) {\n if (context.partitions.has(partitionKey)) {\n promises.push(this.flushPartition(context, partitionKey));\n }\n } else {\n for (const key of context.partitions.keys()) {\n promises.push(this.flushPartition(context, key));\n }\n }\n await Promise.all(promises);\n }\n\n /**\n * Flush a specific partition.\n */\n protected async flushPartition<TItem, TResponse>(\n context: BatchContext<TItem, TResponse>,\n partitionKey: string,\n limit?: number,\n ): Promise<void> {\n const partition = context.partitions.get(partitionKey);\n if (!partition || partition.itemIds.length === 0) {\n context.partitions.delete(partitionKey);\n return;\n }\n\n // Clear the timeout and grab the item IDs (up to limit if specified)\n partition.timeout?.clear();\n partition.timeout = undefined;\n const itemsToTake =\n limit !== undefined\n ? Math.min(limit, partition.itemIds.length)\n : partition.itemIds.length;\n const itemIdsToProcess = partition.itemIds.splice(0, itemsToTake);\n\n // Mark partition as flushing to prevent race conditions\n partition.flushing = true;\n\n // Get the items and mark them as processing\n const itemsToProcess: TItem[] = [];\n for (const id of itemIdsToProcess) {\n const itemState = context.itemStates.get(id);\n if (itemState) {\n itemState.status = \"processing\";\n itemsToProcess.push(itemState.item);\n }\n }\n\n const concurrency = this.getConcurrency(context);\n const maxDuration = this.getMaxDuration(context);\n\n // Wait until there's a free slot (if at concurrency limit)\n while (context.activeHandlers.length >= concurrency) {\n this.log.trace(\n `Batch handler is at concurrency limit, waiting for a slot...`,\n );\n // Wait for any single handler to complete, not all of them\n await Promise.race(context.activeHandlers.map((it) => it.promise));\n }\n\n const promise = Promise.withResolvers<void>();\n context.activeHandlers.push(promise);\n let result: any;\n try {\n result = await context.alepha.context.run(() =>\n // during shutdown, call handler directly to avoid retry cancellation\n context.isShuttingDown\n ? context.options.handler(itemsToProcess)\n : this.retryProvider.retry(\n {\n ...context.options.retry,\n handler: context.options.handler,\n },\n itemsToProcess,\n ),\n );\n\n // Mark all items as completed and resolve their promises\n for (const id of itemIdsToProcess) {\n const itemState = context.itemStates.get(id);\n if (itemState) {\n itemState.status = \"completed\";\n itemState.result = result;\n // Only resolve if someone is waiting\n itemState.resolve?.(result);\n }\n }\n } catch (error) {\n this.log.error(`Batch handler failed`, error);\n\n // Mark all items as failed and reject their promises\n for (const id of itemIdsToProcess) {\n const itemState = context.itemStates.get(id);\n if (itemState) {\n itemState.status = \"failed\";\n itemState.error = error as Error;\n // Only reject if someone is waiting (promise was created)\n itemState.reject?.(error as Error);\n }\n }\n } finally {\n promise.resolve();\n context.activeHandlers = context.activeHandlers.filter(\n (it) => it !== promise,\n );\n\n // Only delete partition if no new items arrived during processing\n const currentPartition = context.partitions.get(partitionKey);\n if (currentPartition?.flushing && currentPartition.itemIds.length === 0) {\n context.partitions.delete(partitionKey);\n } else if (currentPartition) {\n // Reset flushing flag if partition still exists with items\n currentPartition.flushing = false;\n\n // Restart timeout for items that arrived during flush\n if (currentPartition.itemIds.length > 0 && !currentPartition.timeout) {\n currentPartition.timeout = this.dateTime.createTimeout(() => {\n this.log.trace(\n `Batch partition '${partitionKey}' timed out, flushing...`,\n );\n this.flushPartition(context, partitionKey).catch((error) =>\n this.log.error(\n `Failed to flush batch partition '${partitionKey}' on timeout`,\n error,\n ),\n );\n }, maxDuration);\n }\n }\n }\n }\n\n /**\n * Mark the context as ready and start processing buffered items.\n * Called after the \"ready\" hook.\n */\n async markReady<TItem, TResponse>(\n context: BatchContext<TItem, TResponse>,\n ): Promise<void> {\n this.log.debug(\n \"Batch processor is now ready, starting to process buffered items...\",\n );\n context.isReady = true;\n await this.startProcessing(context);\n }\n\n /**\n * Mark the context as shutting down and flush all remaining items.\n */\n async shutdown<TItem, TResponse>(\n context: BatchContext<TItem, TResponse>,\n ): Promise<void> {\n this.log.debug(\"Flushing all remaining batch partitions on shutdown...\");\n context.isShuttingDown = true;\n await this.flush(context);\n this.log.debug(\"All batch partitions flushed\");\n }\n\n /**\n * Called after the \"ready\" hook to start processing buffered items that were\n * pushed during startup. This checks all partitions and starts timeouts/flushes\n * for items that were accumulated before the app was ready.\n */\n protected async startProcessing<TItem, TResponse>(\n context: BatchContext<TItem, TResponse>,\n ): Promise<void> {\n const maxSize = this.getMaxSize(context);\n const maxDuration = this.getMaxDuration(context);\n\n for (const [partitionKey, partition] of context.partitions.entries()) {\n if (partition.itemIds.length === 0) {\n continue;\n }\n\n this.log.trace(\n `Starting processing for partition '${partitionKey}' with ${partition.itemIds.length} buffered items`,\n );\n\n // Flush batches of maxSize while we have items >= maxSize\n while (partition.itemIds.length >= maxSize) {\n this.log.trace(\n `Partition '${partitionKey}' has ${partition.itemIds.length} items, flushing batch of ${maxSize}...`,\n );\n await this.flushPartition(context, partitionKey, maxSize);\n }\n\n // After flushing full batches, start timeout for any remaining items\n if (\n partition.itemIds.length > 0 &&\n !partition.timeout &&\n !partition.flushing\n ) {\n this.log.trace(\n `Starting timeout for partition '${partitionKey}' with ${partition.itemIds.length} remaining items`,\n );\n partition.timeout = this.dateTime.createTimeout(() => {\n this.log.trace(\n `Batch partition '${partitionKey}' timed out, flushing...`,\n );\n this.flushPartition(context, partitionKey).catch((error) =>\n this.log.error(\n `Failed to flush partition '${partitionKey}' on timeout after startup`,\n error,\n ),\n );\n }, maxDuration);\n }\n }\n }\n}\n","import {\n $hook,\n $inject,\n createPrimitive,\n KIND,\n Primitive,\n type Static,\n type TSchema,\n} from \"alepha\";\nimport type { DurationLike } from \"alepha/datetime\";\nimport type { RetryPrimitiveOptions } from \"alepha/retry\";\nimport {\n type BatchContext,\n type BatchItemState,\n type BatchItemStatus,\n BatchProvider,\n} from \"../providers/BatchProvider.ts\";\n\n/**\n * Creates a batch processing primitive for efficient grouping and processing of multiple operations.\n */\nexport const $batch = <TItem extends TSchema, TResponse>(\n options: BatchPrimitiveOptions<TItem, TResponse>,\n): BatchPrimitive<TItem, TResponse> =>\n createPrimitive(BatchPrimitive<TItem, TResponse>, options);\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport interface BatchPrimitiveOptions<TItem extends TSchema, TResponse = any> {\n /**\n * TypeBox schema for validating each item added to the batch.\n */\n schema: TItem;\n\n /**\n * The batch processing handler function that processes arrays of validated items.\n */\n handler: (items: Static<TItem>[]) => TResponse;\n\n /**\n * Maximum number of items to collect before automatically flushing the batch.\n */\n maxSize?: number;\n\n /**\n * Maximum number of items that can be queued in a single partition.\n * If exceeded, push() will throw an error.\n */\n maxQueueSize?: number;\n\n /**\n * Maximum time to wait before flushing a batch, even if it hasn't reached maxSize.\n */\n maxDuration?: DurationLike;\n\n /**\n * Function to determine partition keys for grouping items into separate batches.\n */\n partitionBy?: (item: Static<TItem>) => string;\n\n /**\n * Maximum number of batch handlers that can execute simultaneously.\n */\n concurrency?: number;\n\n /**\n * Retry configuration for failed batch processing operations.\n */\n retry?: Omit<RetryPrimitiveOptions<() => Array<Static<TItem>>>, \"handler\">;\n}\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport type { BatchItemState, BatchItemStatus };\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport class BatchPrimitive<\n TItem extends TSchema,\n TResponse = any,\n> extends Primitive<BatchPrimitiveOptions<TItem, TResponse>> {\n protected readonly batchProvider = $inject(BatchProvider);\n protected readonly context: BatchContext<Static<TItem>, TResponse>;\n\n constructor(\n ...args: ConstructorParameters<\n typeof Primitive<BatchPrimitiveOptions<TItem, TResponse>>\n >\n ) {\n super(...args);\n this.context = this.batchProvider.createContext(this.alepha, {\n handler: this.options.handler,\n maxSize: this.options.maxSize,\n maxQueueSize: this.options.maxQueueSize,\n maxDuration: this.options.maxDuration,\n partitionBy: this.options.partitionBy,\n concurrency: this.options.concurrency,\n retry: this.options.retry,\n });\n }\n\n /**\n * Pushes an item into the batch and returns immediately with a unique ID.\n * The item will be processed asynchronously with other items when the batch is flushed.\n * Use wait(id) to get the processing result.\n */\n public async push(item: Static<TItem>): Promise<string> {\n // Validate the item against the schema\n const validatedItem = this.alepha.codec.validate(this.options.schema, item);\n return this.batchProvider.push(this.context, validatedItem);\n }\n\n /**\n * Wait for a specific item to be processed and get its result.\n * @param id The item ID returned from push()\n * @returns The processing result\n * @throws If the item doesn't exist or processing failed\n */\n public async wait(id: string): Promise<TResponse> {\n return this.batchProvider.wait(this.context, id);\n }\n\n /**\n * Get the current status of an item.\n * @param id The item ID returned from push()\n * @returns Status information or undefined if item doesn't exist\n */\n public status(\n id: string,\n ):\n | { status: \"pending\" | \"processing\" }\n | { status: \"completed\"; result: TResponse }\n | { status: \"failed\"; error: Error }\n | undefined {\n return this.batchProvider.status(this.context, id);\n }\n\n /**\n * Flush all partitions or a specific partition.\n */\n public async flush(partitionKey?: string): Promise<void> {\n return this.batchProvider.flush(this.context, partitionKey);\n }\n\n /**\n * Clears completed and failed items from memory.\n * Call this periodically in long-running applications to prevent memory leaks.\n *\n * @param status Optional: only clear items with this specific status ('completed' or 'failed')\n * @returns The number of items cleared\n */\n public clearCompleted(status?: \"completed\" | \"failed\"): number {\n return this.batchProvider.clearCompleted(this.context, status);\n }\n\n protected readonly onReady = $hook({\n on: \"ready\",\n handler: async () => {\n await this.batchProvider.markReady(this.context);\n },\n });\n}\n\n$batch[KIND] = BatchPrimitive;\n","import { $module } from \"alepha\";\nimport { $batch } from \"./primitives/$batch.ts\";\nimport { BatchProvider } from \"./providers/BatchProvider.ts\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport * from \"./primitives/$batch.ts\";\nexport * from \"./providers/BatchProvider.ts\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\n/**\n * Batch accumulation and processing.\n *\n * **Features:**\n * - Batch accumulator with handler\n * - Configurable batch size\n * - Time-based triggers\n * - Status tracking\n *\n * @module alepha.batch\n */\nexport const AlephaBatch = $module({\n name: \"alepha.batch\",\n primitives: [$batch],\n services: [BatchProvider],\n});\n"],"mappings":"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"}
package/dist/bin/index.js CHANGED
@@ -6,7 +6,7 @@ const alepha = Alepha.create({ env: {
6
6
  APP_NAME: "CLI",
7
7
  CLI_NAME: "alepha",
8
8
  CLI_DESCRIPTION: `Alepha CLI v${version} - Create and manage Alepha projects.`,
9
- LOG_FORMAT: process.env.LOG_FORMAT ?? "raw",
9
+ LOG_FORMAT: process.env.LOG_FORMAT ?? "cli",
10
10
  LOG_LEVEL: process.env.LOG_LEVEL ?? "alepha.core:warn,info"
11
11
  } });
12
12
  alepha.with(AlephaCli);
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../../src/bin/index.ts"],"sourcesContent":["#!/usr/bin/env node\nimport { Alepha, run } from \"alepha\";\nimport { AlephaCli, version } from \"alepha/cli\";\n\nconst alepha = Alepha.create({\n env: {\n APP_NAME: \"CLI\",\n CLI_NAME: \"alepha\",\n CLI_DESCRIPTION: `Alepha CLI v${version} - Create and manage Alepha projects.`,\n LOG_FORMAT: (process.env.LOG_FORMAT ?? \"raw\") as any,\n LOG_LEVEL: process.env.LOG_LEVEL ?? \"alepha.core:warn,info\",\n },\n});\n\nalepha.with(AlephaCli);\n\nrun(alepha);\n"],"mappings":";;;;AAIA,MAAM,SAAS,OAAO,OAAO,EAC3B,KAAK;CACH,UAAU;CACV,UAAU;CACV,iBAAiB,eAAe,QAAQ;CACxC,YAAa,QAAQ,IAAI,cAAc;CACvC,WAAW,QAAQ,IAAI,aAAa;AACtC,EACF,CAAC;AAED,OAAO,KAAK,SAAS;AAErB,IAAI,MAAM"}
1
+ {"version":3,"file":"index.js","names":[],"sources":["../../src/bin/index.ts"],"sourcesContent":["#!/usr/bin/env node\nimport { Alepha, run } from \"alepha\";\nimport { AlephaCli, version } from \"alepha/cli\";\n\nconst alepha = Alepha.create({\n env: {\n APP_NAME: \"CLI\",\n CLI_NAME: \"alepha\",\n CLI_DESCRIPTION: `Alepha CLI v${version} - Create and manage Alepha projects.`,\n LOG_FORMAT: (process.env.LOG_FORMAT ?? \"cli\") as any,\n LOG_LEVEL: process.env.LOG_LEVEL ?? \"alepha.core:warn,info\",\n },\n});\n\nalepha.with(AlephaCli);\n\nrun(alepha);\n"],"mappings":";;;;AAIA,MAAM,SAAS,OAAO,OAAO,EAC3B,KAAK;CACH,UAAU;CACV,UAAU;CACV,iBAAiB,eAAe,QAAQ;CACxC,YAAa,QAAQ,IAAI,cAAc;CACvC,WAAW,QAAQ,IAAI,aAAa;AACtC,EACF,CAAC;AAED,OAAO,KAAK,SAAS;AAErB,IAAI,MAAM"}
@@ -4,7 +4,6 @@ import { CryptoProvider } from "alepha/crypto";
4
4
  import * as fs from "node:fs";
5
5
  import { S3mini } from "s3mini";
6
6
  import { R2Bucket } from "@cloudflare/workers-types";
7
-
8
7
  //#region ../../src/bucket/providers/FileStorageProvider.d.ts
9
8
  declare abstract class FileStorageProvider {
10
9
  /**
@@ -70,6 +69,7 @@ interface StoredFile {
70
69
  }
71
70
  declare class MemoryFileStorageProvider implements FileStorageProvider {
72
71
  readonly files: Record<string, StoredFile>;
72
+ protected readonly alepha: Alepha;
73
73
  protected readonly fileSystem: FileSystemProvider;
74
74
  protected readonly fileDetector: FileDetector;
75
75
  protected readonly crypto: CryptoProvider;
@@ -79,6 +79,11 @@ declare class MemoryFileStorageProvider implements FileStorageProvider {
79
79
  delete(bucketName: string, fileId: string): Promise<void>;
80
80
  deleteMany(bucketName: string, fileIds: string[]): Promise<void>;
81
81
  list(bucketName: string): Promise<string[]>;
82
+ /**
83
+ * In-memory key, tenant-scoped when a tenant is active (`currentTenantAtom`),
84
+ * mirroring the R2/Local/S3 layout: `{tenantId}/{bucket}/{fileId}`.
85
+ */
86
+ protected key(bucket: string, fileId?: string): string;
82
87
  protected createId(): string;
83
88
  }
84
89
  //#endregion
@@ -331,9 +336,9 @@ declare class FileNotFoundError extends AlephaError {
331
336
  /**
332
337
  * Local file storage configuration atom
333
338
  */
334
- declare const localFileStorageOptions: import("alepha").Atom<import("typebox").TObject<{
335
- storagePath: import("typebox").TString;
336
- }>, "alepha.bucket.local.options">;
339
+ declare const localFileStorageOptions: import("alepha").Atom<import("zod").ZodObject<{
340
+ storagePath: import("zod").ZodString;
341
+ }, import("zod/v4/core").$strip>, "alepha.bucket.local.options">;
337
342
  type LocalFileStorageProviderOptions = Static<typeof localFileStorageOptions.schema>;
338
343
  declare module "alepha" {
339
344
  interface State {
@@ -435,7 +440,12 @@ declare class R2FileStorageProvider implements FileStorageProvider {
435
440
  deleteMany(bucketName: string, fileIds: string[]): Promise<void>;
436
441
  list(bucketName: string): Promise<string[]>;
437
442
  /**
438
- * Build the full R2 key: {prefix}/{bucketName}/{fileId}
443
+ * Build the full R2 key: {prefix}/{tenantId}/{bucketName}/{fileId}
444
+ *
445
+ * When a tenant is active on the current request/job (`currentTenantAtom`),
446
+ * its id is inserted as a directory so a pooled multi-tenant worker keeps
447
+ * each tenant's objects isolated. No tenant → the historical
448
+ * `{prefix}/{bucketName}/{fileId}` layout is unchanged.
439
449
  */
440
450
  protected key(bucketName: string, fileId: string): string;
441
451
  protected getR2(): R2Bucket;
@@ -443,32 +453,12 @@ declare class R2FileStorageProvider implements FileStorageProvider {
443
453
  }
444
454
  //#endregion
445
455
  //#region ../../src/bucket/providers/S3FileStorageProvider.d.ts
446
- declare const envSchema: import("alepha").TObject<{
447
- /**
448
- * S3 endpoint URL. The bucket name is appended (path-style) per request.
449
- *
450
- * Examples:
451
- * - AWS S3: `https://s3.us-east-1.amazonaws.com`
452
- * - Cloudflare R2: `https://<account-id>.r2.cloudflarestorage.com`
453
- * - MinIO: `http://localhost:9000`
454
- * - DigitalOcean Spaces: `https://<region>.digitaloceanspaces.com`
455
- */
456
- S3_ENDPOINT: import("alepha").TString;
457
- /**
458
- * AWS region or "auto" for R2.
459
- *
460
- * @default "auto"
461
- */
462
- S3_REGION: import("alepha").TOptional<import("alepha").TString>;
463
- /**
464
- * Access key ID for S3 authentication.
465
- */
466
- S3_ACCESS_KEY_ID: import("alepha").TString;
467
- /**
468
- * Secret access key for S3 authentication.
469
- */
470
- S3_SECRET_ACCESS_KEY: import("alepha").TString;
471
- }>;
456
+ declare const envSchema: import("zod").ZodObject<{
457
+ S3_ENDPOINT: import("zod").ZodString;
458
+ S3_REGION: import("zod").ZodOptional<import("zod").ZodString>;
459
+ S3_ACCESS_KEY_ID: import("zod").ZodString;
460
+ S3_SECRET_ACCESS_KEY: import("zod").ZodString;
461
+ }, import("zod/v4/core").$strip>;
472
462
  declare module "alepha" {
473
463
  interface Env extends Partial<Static<typeof envSchema>> {}
474
464
  }
@@ -483,8 +473,8 @@ declare module "alepha" {
483
473
  declare class S3FileStorageProvider implements FileStorageProvider {
484
474
  protected readonly log: import("alepha/logger").Logger;
485
475
  protected readonly env: {
486
- S3_REGION?: string | undefined;
487
476
  S3_ENDPOINT: string;
477
+ S3_REGION?: string | undefined;
488
478
  S3_ACCESS_KEY_ID: string;
489
479
  S3_SECRET_ACCESS_KEY: string;
490
480
  };
@@ -500,6 +490,11 @@ declare class S3FileStorageProvider implements FileStorageProvider {
500
490
  convertName(name: string): string;
501
491
  protected getClient(bucketName: string): S3mini;
502
492
  protected readonly onStart: import("alepha").HookPrimitive<"start">;
493
+ /**
494
+ * Object key, tenant-scoped when a tenant is active (`currentTenantAtom`):
495
+ * `{tenantId}/{fileId}` within the per-bucket S3 bucket. No tenant → `{fileId}`.
496
+ */
497
+ protected key(fileId: string): string;
503
498
  protected createId(mimeType: string): string;
504
499
  upload(bucketName: string, file: FileLike, fileId?: string): Promise<string>;
505
500
  download(bucketName: string, fileId: string): Promise<FileLike>;
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","names":[],"sources":["../../src/bucket/providers/FileStorageProvider.ts","../../src/bucket/providers/MemoryFileStorageProvider.ts","../../src/bucket/primitives/$bucket.ts","../../src/bucket/errors/FileNotFoundError.ts","../../src/bucket/providers/LocalFileStorageProvider.ts","../../src/bucket/providers/R2FileStorageProvider.ts","../../src/bucket/providers/S3FileStorageProvider.ts","../../src/bucket/index.ts"],"mappings":";;;;;;;;uBAEsB,mBAAA;;;;;;;AAAtB;;WASW,MAAA,CACP,UAAA,UACA,IAAA,EAAM,QAAA,EACN,MAAA,YACC,OAAA;EAFK;;;;;;;EAAA,SAWC,QAAA,CAAS,UAAA,UAAoB,MAAA,WAAiB,OAAA,CAAQ,QAAA;EAuCrB;;;;;;;EAAA,SA9BjC,MAAA,CAAO,UAAA,UAAoB,MAAA,WAAiB,OAAA;EAT5C;;;;;;EAAA,SAiBA,MAAA,CAAO,UAAA,UAAoB,MAAA,WAAiB,OAAA;EARjB;;;;;;;EAAA,SAiB3B,UAAA,CAAW,UAAA,UAAoB,OAAA,aAAoB,OAAA;EAApB;;;;;;AAaE;;;;AC3DwB;ED8C1B,SAa/B,IAAA,CAAK,UAAA,WAAqB,OAAA;AAAA;;;UCzD3B,UAAA;EACR,MAAA,EAAQ,MAAM;EACd,IAAA;EACA,IAAA;EACA,IAAA;AAAA;AAAA,cAGW,yBAAA,YAAqC,mBAAA;EAAA,SAChC,KAAA,EAAO,MAAA,SAAe,UAAA;EAAA,mBACnB,UAAA,EAAU,kBAAA;EAAA,mBACV,YAAA,EAAY,YAAA;EAAA,mBACZ,MAAA,EAAM,cAAA;EAEZ,MAAA,CACX,UAAA,UACA,IAAA,EAAM,QAAA,EACN,MAAA,YACC,OAAA;EAoBU,QAAA,CAAS,UAAA,UAAoB,MAAA,WAAiB,OAAA,CAAQ,QAAA;EAiBtD,MAAA,CAAO,UAAA,UAAoB,MAAA,WAAiB,OAAA;EAI5C,MAAA,CAAO,UAAA,UAAoB,MAAA,WAAiB,OAAA;EAS5C,UAAA,CACX,UAAA,UACA,OAAA,aACC,OAAA;EAMU,IAAA,CAAK,UAAA,WAAqB,OAAA;EAAA,UAO7B,QAAA;AAAA;;;;;;;ADvFZ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA6D4C;;;;AC3DwB;cC0DvD,OAAA;EAAA,UAAoB,sBAAA,GAAsB,eAAA;EAAA;;UAGtC,sBAAA,SAA+B,iBAAA;EDzD9C;;;;AAEI;AAGN;;;;;;;;;;;;;;;;;;;;;EC+EE,QAAA,GAAW,OAAA,CAAQ,mBAAA;ED9EmB;;;;;;;;;;;;;;;;;;;;;;;ECuGtC,IAAA;AAAA;AAAA,UAKe,iBAAA;EDjDF;;;;;;;;;AAgBK;;;;AC3BpB;;;;;;;EAiFE,WAAA;;;;;;;;;AA9EF;;;;;;;;;;;;;;AAoDM;AAKN;;EAgDE,SAAA;EAhDgC;;;;;AA+EzB;AAKT;;;;;;;;;;;;;;;;;;;;;;;EALE,OAAA;AAAA;AAAA,cAKW,eAAA,SAAwB,SAAA,CAAU,sBAAA;EAAA,SAC7B,QAAA,EAAQ,mBAAA,GAAA,yBAAA;EAAA,mBACL,UAAA,EAAU,kBAAA;EAAA,IAElB,IAAA;EAOE;;;EAAA,MAAA,CACX,IAAA,EAAM,QAAA,EACN,OAAA,GAAU,iBAAA,GACT,OAAA;EADD;;;EA8DW,MAAA,CAAO,MAAA,UAAgB,QAAA,aAAmB,OAAA;EAAnB;;;;;EAkBvB,UAAA,CAAW,OAAA,YAAmB,QAAA,aAAmB,OAAA;EAmBjD;;;EAAA,MAAA,CAAO,MAAA,WAAiB,OAAA;EAYhB;;;;;;;;EAAR,IAAA,IAAQ,OAAA;EAmBF;AAerB;;EA3Be,QAAA,CAAS,MAAA,WAAiB,OAAA,CAAQ,QAAA;EAAA,UAYrC,SAAA,IAAS,mBAAA,GAAA,yBAAA;AAAA;AAAA,UAeJ,iBAAA;EAgBf;;AAAO;EAZP,WAAA;;;AC9WF;EDmXE,SAAA;;;AClXsB;;;EDyXtB,OAAA;AAAA;;;cC1XW,iBAAA,SAA0B,WAAW;EAAA,SAChC,MAAM;AAAA;;;;;;cCyBX,uBAAA,mBAAuB,IAAA,mBAAA,OAAA;;;KAYxB,+BAAA,GAAkC,MAAM,QAC3C,uBAAA,CAAwB,MAAA;AAAA;EAAA,UAIrB,KAAA;IAAA,CACP,uBAAA,CAAwB,GAAG,GAAG,+BAAA;EAAA;AAAA;AAAA,cAMtB,wBAAA,YAAoC,mBAAA;EAAA,mBAC5B,MAAA,EAAM,MAAA;EAAA,mBACN,GAAA,0BAAG,MAAA;EAAA,mBACH,YAAA,EAAY,YAAA;EAAA,mBACZ,kBAAA,EAAkB,kBAAA;EAAA,mBAClB,MAAA,EAAM,cAAA;EAAA,mBACN,OAAA,EAAO,QAAA;;;gBAEZ,WAAA;EAAA,mBAIK,WAAA,mBAAW,aAAA;EAAA,mBAcX,OAAA,mBAAO,aAAA;EAqBb,MAAA,CACX,UAAA,UACA,IAAA,EAAM,QAAA,EACN,MAAA,YACC,OAAA;EAaU,QAAA,CAAS,UAAA,UAAoB,MAAA,WAAiB,OAAA,CAAQ,QAAA;EAqBtD,MAAA,CAAO,UAAA,UAAoB,MAAA,WAAiB,OAAA;EAY5C,MAAA,CAAO,UAAA,UAAoB,MAAA,WAAiB,OAAA;EAW5C,UAAA,CACX,UAAA,UACA,OAAA,aACC,OAAA;EAWU,IAAA,CAAK,UAAA,WAAqB,OAAA;EAAA,UAgB7B,IAAA,CAAK,MAAA,UAAgB,MAAA,WAAiB,OAAA,CAAQ,EAAA,CAAG,KAAA;EAAA,UAIjD,QAAA,CAAS,QAAA;EAAA,UAKT,IAAA,CAAK,MAAA,UAAgB,MAAA;EAAA,UAIrB,cAAA,CAAe,KAAA;AAAA;;;;;;;AJzM3B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA6D4C;cKA/B,qBAAA,YAAiC,mBAAA;EAAA,mBACzB,MAAA,EAAM,MAAA;EAAA,mBACN,GAAA,0BAAG,MAAA;EAAA,mBACH,MAAA,EAAM,cAAA;EAAA,mBACN,GAAA;;;YAWT,EAAA,GAAK,QAAA;EJvEP;;;EAAA,II4EG,UAAA;EJzEP;AAAA;AAGN;;EAHM,IIiFO,MAAA;EAAA,mBAIQ,OAAA,mBAAO,aAAA;EAqCb,MAAA,CACX,UAAA,UACA,IAAA,EAAM,QAAA,EACN,MAAA,YACC,OAAA;EAsBU,QAAA,CAAS,UAAA,UAAoB,MAAA,WAAiB,OAAA,CAAQ,QAAA;EA4BtD,MAAA,CAAO,UAAA,UAAoB,MAAA,WAAiB,OAAA;EAU5C,MAAA,CAAO,UAAA,UAAoB,MAAA,WAAiB,OAAA;EAkB5C,UAAA,CACX,UAAA,UACA,OAAA,aACC,OAAA;EAYU,IAAA,CAAK,UAAA,WAAqB,OAAA;EJ1L4B;;;EAAA,UIsMzD,GAAA,CAAI,UAAA,UAAoB,MAAA;EAAA,UAQxB,KAAA,IAAS,QAAA;EAAA,UAOT,QAAA,CAAS,QAAA;AAAA;;;cC3Of,SAAA,mBAAS,OAAA;;;ANnBf;;;;;;;;EAuCuD;;;;;;EA7BnD;;;;EAGC;;;;;;YMqCO,GAAA,SAAY,OAAA,CAAQ,MAAA,QAAc,SAAA;AAAA;;;;;;;;;cAWjC,qBAAA,YAAiC,mBAAA;EAAA,mBACzB,GAAA,0BAAG,MAAA;EAAA,mBACH,GAAA;;;;;;qBACA,MAAA,EAAM,MAAA;EAAA,mBACN,UAAA,EAAU,kBAAA;EAAA,mBACV,YAAA,EAAY,YAAA;EAAA,mBACZ,MAAA,EAAM,cAAA;EAAA,mBACN,OAAA,EAAS,GAAA,SAAY,MAAA;EL/DxC;;;;EKqEO,WAAA,CAAY,IAAA;EAAA,UAIT,SAAA,CAAU,UAAA,WAAqB,MAAA;EAAA,mBAgBtB,OAAA,mBAAO,aAAA;EAAA,UA2BhB,QAAA,CAAS,QAAA;EAKN,MAAA,CACX,UAAA,UACA,IAAA,EAAM,QAAA,EACN,MAAA,YACC,OAAA;EAiCU,QAAA,CAAS,UAAA,UAAoB,MAAA,WAAiB,OAAA,CAAQ,QAAA;EA0CtD,MAAA,CAAO,UAAA,UAAoB,MAAA,WAAiB,OAAA;EAU5C,MAAA,CAAO,UAAA,UAAoB,MAAA,WAAiB,OAAA;EAgB5C,IAAA,CAAK,UAAA,WAAqB,OAAA;EAS1B,UAAA,CACX,UAAA,UACA,OAAA,aACC,OAAA;AAAA;;;;YC7NO,KAAA;IPXF;;;;IOgBN,sBAAA;MACE,EAAA;MACA,IAAA,EAAM,QAAA;MACN,MAAA,EAAQ,eAAA;MACR,OAAA,EAAS,iBAAA;IAAA;IPtBJ;;;IO2BP,qBAAA;MACE,EAAA;MACA,MAAA,EAAQ,eAAA;IAAA;IPhBM;;;IOqBhB,wBAAA;MACE,EAAA;MACA,IAAA,EAAM,QAAA;MACN,MAAA,EAAQ,eAAA;IAAA;EAAA;AAAA;;;;;;;;;;;APe8B;;;;cOM/B,YAAA,mBAAY,OAAA,kBAAA,MAAA"}
1
+ {"version":3,"file":"index.d.ts","names":[],"sources":["../../src/bucket/providers/FileStorageProvider.ts","../../src/bucket/providers/MemoryFileStorageProvider.ts","../../src/bucket/primitives/$bucket.ts","../../src/bucket/errors/FileNotFoundError.ts","../../src/bucket/providers/LocalFileStorageProvider.ts","../../src/bucket/providers/R2FileStorageProvider.ts","../../src/bucket/providers/S3FileStorageProvider.ts","../../src/bucket/index.ts"],"mappings":"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"}
@@ -1,6 +1,7 @@
1
- import { $atom, $env, $hook, $inject, $module, $state, Alepha, AlephaError, KIND, Primitive, createPrimitive, t } from "alepha";
1
+ import { $atom, $env, $hook, $inject, $module, $state, Alepha, AlephaError, KIND, Primitive, createPrimitive, z } from "alepha";
2
2
  import { FileDetector, FileSystemProvider } from "alepha/system";
3
3
  import { CryptoProvider } from "alepha/crypto";
4
+ import { currentTenantAtom } from "alepha/security";
4
5
  import { createReadStream } from "node:fs";
5
6
  import { mkdir, readdir, stat, unlink } from "node:fs/promises";
6
7
  import { tmpdir } from "node:os";
@@ -25,6 +26,7 @@ var FileNotFoundError = class extends AlephaError {
25
26
  //#region ../../src/bucket/providers/MemoryFileStorageProvider.ts
26
27
  var MemoryFileStorageProvider = class {
27
28
  files = {};
29
+ alepha = $inject(Alepha);
28
30
  fileSystem = $inject(FileSystemProvider);
29
31
  fileDetector = $inject(FileDetector);
30
32
  crypto = $inject(CryptoProvider);
@@ -33,7 +35,7 @@ var MemoryFileStorageProvider = class {
33
35
  const chunks = [];
34
36
  for await (const chunk of file.stream()) chunks.push(chunk);
35
37
  const buffer = Buffer.concat(chunks);
36
- this.files[`${bucketName}/${fileId}`] = {
38
+ this.files[this.key(bucketName, fileId)] = {
37
39
  buffer,
38
40
  name: file.name,
39
41
  type: file.type,
@@ -42,7 +44,7 @@ var MemoryFileStorageProvider = class {
42
44
  return fileId;
43
45
  }
44
46
  async download(bucketName, fileId) {
45
- const fileKey = `${bucketName}/${fileId}`;
47
+ const fileKey = this.key(bucketName, fileId);
46
48
  const stored = this.files[fileKey];
47
49
  if (!stored) throw new FileNotFoundError(`File with ID ${fileId} not found.`);
48
50
  return this.fileSystem.createFile({
@@ -53,20 +55,28 @@ var MemoryFileStorageProvider = class {
53
55
  });
54
56
  }
55
57
  async exists(bucketName, fileId) {
56
- return `${bucketName}/${fileId}` in this.files;
58
+ return this.key(bucketName, fileId) in this.files;
57
59
  }
58
60
  async delete(bucketName, fileId) {
59
- const fileKey = `${bucketName}/${fileId}`;
61
+ const fileKey = this.key(bucketName, fileId);
60
62
  if (!(fileKey in this.files)) throw new FileNotFoundError(`File with ID ${fileId} not found.`);
61
63
  delete this.files[fileKey];
62
64
  }
63
65
  async deleteMany(bucketName, fileIds) {
64
- for (const id of fileIds) delete this.files[`${bucketName}/${id}`];
66
+ for (const id of fileIds) delete this.files[this.key(bucketName, id)];
65
67
  }
66
68
  async list(bucketName) {
67
- const prefix = `${bucketName}/`;
69
+ const prefix = this.key(bucketName, "");
68
70
  return Object.keys(this.files).filter((key) => key.startsWith(prefix)).map((key) => key.slice(prefix.length));
69
71
  }
72
+ /**
73
+ * In-memory key, tenant-scoped when a tenant is active (`currentTenantAtom`),
74
+ * mirroring the R2/Local/S3 layout: `{tenantId}/{bucket}/{fileId}`.
75
+ */
76
+ key(bucket, fileId = "") {
77
+ const tenantId = this.alepha.store.get(currentTenantAtom)?.id;
78
+ return tenantId ? `${tenantId}/${bucket}/${fileId}` : `${bucket}/${fileId}`;
79
+ }
70
80
  createId() {
71
81
  return this.crypto.randomUUID();
72
82
  }
@@ -227,7 +237,7 @@ $bucket[KIND] = BucketPrimitive;
227
237
  */
228
238
  const localFileStorageOptions = $atom({
229
239
  name: "alepha.bucket.local.options",
230
- schema: t.object({ storagePath: t.string({ description: "Directory path where files will be stored" }) }),
240
+ schema: z.object({ storagePath: z.string().describe("Directory path where files will be stored") }),
231
241
  default: { storagePath: "node_modules/.alepha/buckets" }
232
242
  });
233
243
  var LocalFileStorageProvider = class {
@@ -262,6 +272,7 @@ var LocalFileStorageProvider = class {
262
272
  async upload(bucketName, file, fileId) {
263
273
  fileId ??= this.createId(file.type);
264
274
  this.log.trace(`Uploading file to ${bucketName}`);
275
+ await mkdir(this.path(bucketName), { recursive: true });
265
276
  await this.fileSystemProvider.writeFile(this.path(bucketName, fileId), file);
266
277
  return fileId;
267
278
  }
@@ -320,7 +331,8 @@ var LocalFileStorageProvider = class {
320
331
  return `${this.crypto.randomUUID()}.${ext}`;
321
332
  }
322
333
  path(bucket, fileId = "") {
323
- return join(this.storagePath, bucket, fileId);
334
+ const tenantId = this.alepha.store.get(currentTenantAtom)?.id;
335
+ return tenantId ? join(this.storagePath, tenantId, bucket, fileId) : join(this.storagePath, bucket, fileId);
324
336
  }
325
337
  isErrorNoEntry(error) {
326
338
  return error instanceof Error && "code" in error && error.code === "ENOENT";
@@ -328,7 +340,7 @@ var LocalFileStorageProvider = class {
328
340
  };
329
341
  //#endregion
330
342
  //#region ../../src/bucket/providers/S3FileStorageProvider.ts
331
- const envSchema = t.object({
343
+ const envSchema = z.object({
332
344
  /**
333
345
  * S3 endpoint URL. The bucket name is appended (path-style) per request.
334
346
  *
@@ -338,21 +350,21 @@ const envSchema = t.object({
338
350
  * - MinIO: `http://localhost:9000`
339
351
  * - DigitalOcean Spaces: `https://<region>.digitaloceanspaces.com`
340
352
  */
341
- S3_ENDPOINT: t.string(),
353
+ S3_ENDPOINT: z.string(),
342
354
  /**
343
355
  * AWS region or "auto" for R2.
344
356
  *
345
357
  * @default "auto"
346
358
  */
347
- S3_REGION: t.optional(t.string()),
359
+ S3_REGION: z.string().optional(),
348
360
  /**
349
361
  * Access key ID for S3 authentication.
350
362
  */
351
- S3_ACCESS_KEY_ID: t.string(),
363
+ S3_ACCESS_KEY_ID: z.string(),
352
364
  /**
353
365
  * Secret access key for S3 authentication.
354
366
  */
355
- S3_SECRET_ACCESS_KEY: t.string()
367
+ S3_SECRET_ACCESS_KEY: z.string()
356
368
  });
357
369
  /**
358
370
  * S3-compatible file storage provider for Node.js.
@@ -408,6 +420,14 @@ var S3FileStorageProvider = class {
408
420
  }
409
421
  }
410
422
  });
423
+ /**
424
+ * Object key, tenant-scoped when a tenant is active (`currentTenantAtom`):
425
+ * `{tenantId}/{fileId}` within the per-bucket S3 bucket. No tenant → `{fileId}`.
426
+ */
427
+ key(fileId) {
428
+ const tenantId = this.alepha.store.get(currentTenantAtom)?.id;
429
+ return tenantId ? `${tenantId}/${fileId}` : fileId;
430
+ }
411
431
  createId(mimeType) {
412
432
  const ext = this.fileDetector.getExtensionFromMimeType(mimeType);
413
433
  return `${this.crypto.randomUUID()}.${ext}`;
@@ -418,7 +438,7 @@ var S3FileStorageProvider = class {
418
438
  const client = this.getClient(bucketName);
419
439
  try {
420
440
  const buffer = new Uint8Array(await file.arrayBuffer());
421
- await client.putObject(fileId, buffer, file.type || "application/octet-stream", void 0, { "x-amz-meta-name": encodeURIComponent(file.name) }, file.size);
441
+ await client.putObject(this.key(fileId), buffer, file.type || "application/octet-stream", void 0, { "x-amz-meta-name": encodeURIComponent(file.name) }, file.size);
422
442
  this.log.trace(`File uploaded successfully: ${fileId}`);
423
443
  return fileId;
424
444
  } catch (error) {
@@ -429,7 +449,7 @@ var S3FileStorageProvider = class {
429
449
  }
430
450
  async download(bucketName, fileId) {
431
451
  this.log.trace(`Downloading file '${fileId}' from bucket '${bucketName}'...`);
432
- const response = await this.getClient(bucketName).getObjectResponse(fileId);
452
+ const response = await this.getClient(bucketName).getObjectResponse(this.key(fileId));
433
453
  if (!response) throw new FileNotFoundError(`File '${fileId}' not found in bucket '${bucketName}'`);
434
454
  const mimeType = response.headers.get("content-type") || this.fileDetector.getContentType(fileId);
435
455
  const metaName = response.headers.get("x-amz-meta-name");
@@ -450,13 +470,13 @@ var S3FileStorageProvider = class {
450
470
  }
451
471
  async exists(bucketName, fileId) {
452
472
  this.log.trace(`Checking existence of file '${fileId}' in bucket '${bucketName}'...`);
453
- return await this.getClient(bucketName).objectExists(fileId) === true;
473
+ return await this.getClient(bucketName).objectExists(this.key(fileId)) === true;
454
474
  }
455
475
  async delete(bucketName, fileId) {
456
476
  this.log.trace(`Deleting file '${fileId}' from bucket '${bucketName}'...`);
457
477
  const client = this.getClient(bucketName);
458
478
  try {
459
- await client.deleteObject(fileId);
479
+ await client.deleteObject(this.key(fileId));
460
480
  } catch (error) {
461
481
  this.log.error(`Failed to delete file: ${error}`);
462
482
  if (error instanceof Error) throw new FileNotFoundError("Error deleting file", { cause: error });
@@ -465,20 +485,23 @@ var S3FileStorageProvider = class {
465
485
  }
466
486
  async list(bucketName) {
467
487
  this.log.trace(`Listing files in bucket '${bucketName}'...`);
468
- const objects = await this.getClient(bucketName).listObjects();
488
+ const client = this.getClient(bucketName);
489
+ const tenantId = this.alepha.store.get(currentTenantAtom)?.id;
490
+ const prefix = tenantId ? `${tenantId}/` : void 0;
491
+ const objects = await client.listObjects(void 0, prefix);
469
492
  if (!objects) return [];
470
- return objects.map((object) => object.Key);
493
+ return objects.map((object) => prefix && object.Key.startsWith(prefix) ? object.Key.slice(prefix.length) : object.Key);
471
494
  }
472
495
  async deleteMany(bucketName, fileIds) {
473
496
  if (fileIds.length === 0) return;
474
497
  this.log.trace(`Deleting ${fileIds.length} files from bucket '${bucketName}'...`);
475
498
  const client = this.getClient(bucketName);
476
499
  for (let i = 0; i < fileIds.length; i += 1e3) {
477
- const chunk = fileIds.slice(i, i + 1e3);
500
+ const keys = fileIds.slice(i, i + 1e3).map((id) => this.key(id));
478
501
  try {
479
502
  const batch = client.deleteObjects;
480
- if (typeof batch === "function") await batch.call(client, chunk);
481
- else await Promise.all(chunk.map((id) => client.deleteObject(id)));
503
+ if (typeof batch === "function") await batch.call(client, keys);
504
+ else await Promise.all(keys.map((key) => client.deleteObject(key)));
482
505
  } catch (error) {
483
506
  this.log.error(`Failed to delete files: ${error}`);
484
507
  if (error instanceof Error) throw new FileNotFoundError("Error deleting files", { cause: error });
@@ -538,11 +561,11 @@ var R2FileStorageProvider = class {
538
561
  alepha = $inject(Alepha);
539
562
  log = $logger();
540
563
  crypto = $inject(CryptoProvider);
541
- env = $env(t.object({
564
+ env = $env(z.object({
542
565
  /**
543
566
  * The actual R2 bucket name in Cloudflare.
544
567
  */
545
- R2_BUCKET_NAME: t.string({ description: "R2 bucket name in Cloudflare" }) }));
568
+ R2_BUCKET_NAME: z.string().describe("R2 bucket name in Cloudflare") }));
546
569
  r2;
547
570
  /**
548
571
  * Get the R2 bucket name from environment.
@@ -631,10 +654,17 @@ R2_BUCKET_NAME: t.string({ description: "R2 bucket name in Cloudflare" }) }));
631
654
  return (await r2.list({ prefix: keyPrefix })).objects.map((object) => object.key.slice(keyPrefix.length));
632
655
  }
633
656
  /**
634
- * Build the full R2 key: {prefix}/{bucketName}/{fileId}
657
+ * Build the full R2 key: {prefix}/{tenantId}/{bucketName}/{fileId}
658
+ *
659
+ * When a tenant is active on the current request/job (`currentTenantAtom`),
660
+ * its id is inserted as a directory so a pooled multi-tenant worker keeps
661
+ * each tenant's objects isolated. No tenant → the historical
662
+ * `{prefix}/{bucketName}/{fileId}` layout is unchanged.
635
663
  */
636
664
  key(bucketName, fileId) {
637
665
  const parts = [bucketName, fileId];
666
+ const tenantId = this.alepha.store.get(currentTenantAtom)?.id;
667
+ if (tenantId) parts.unshift(tenantId);
638
668
  if (this.prefix) parts.unshift(this.prefix);
639
669
  return parts.join("/");
640
670
  }