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.d.ts","names":[],"sources":["../../../src/api/files/entities/files.ts","../../../src/api/files/schemas/fileQuerySchema.ts","../../../src/orm/core/schemas/insertSchema.ts","../../../src/orm/core/schemas/updateSchema.ts","../../../src/orm/core/primitives/$entity.ts","../../../src/orm/core/constants/PG_SYMBOLS.ts","../../../src/orm/core/helpers/pgAttr.ts","../../../src/orm/core/schemas/databaseEnvSchema.ts","../../../src/api/files/schemas/fileResourceSchema.ts","../../../src/api/files/schemas/storageStatsSchema.ts","../../../src/api/files/services/FileService.ts","../../../src/api/files/controllers/AdminFileStatsController.ts","../../../src/api/files/providers/FileAccessProvider.ts","../../../src/api/files/controllers/FileController.ts","../../../src/api/files/jobs/FileJobs.ts","../../../src/api/files/schemas/fileCreatorSummarySchema.ts","../../../src/api/files/index.ts"],"mappings":";;;;;;;;;;cAGa,KAAA,uBAAK,eAAA,mBAAA,OAAA;;;;;;;;;;;;;;;;AAAlB;;;EAuDE;;;;;;;;;;;;;;;;;;;;;;;;KAEU,UAAA,GAAa,MAAM,QAAQ,KAAA,CAAM,MAAA;;;cCxDhC,eAAA,oBAAe,OAAA;;;;;;;;;;;;KAUhB,SAAA,GAAY,MAAM,QAAQ,eAAA;;;;;;;;;;;;KCG1B,aAAA,WAAwB,OAAA,IAAW,OAAA,eACjC,CAAA,kBAAmB,CAAA,eAAgB,CAAA;EAAA,CAC5C,YAAA;AAAA,YAGC,CAAA,GAAI,CAAA,eAAgB,CAAA;EAAA,CACjB,UAAA;AAAA;EAAA,CACA,eAAA;AAAA;EACD,WAAA;AAAA,IACF,SAAA,CAAU,CAAA,eAAgB,CAAA,KAC1B,CAAA,eAAgB,CAAA;;;;;;;;;;;;KCTV,aAAA,WAAwB,OAAA,IAAW,OAAA,eACjC,CAAA,kBAAmB,CAAA,eAAgB,CAAA;EAAA,CAC5C,YAAA;AAAA,YAGC,CAAA,GAAI,CAAA,eAAgB,CAAA,UAAW,SAAA,YAC/B,SAAA,CAAU,MAAA,EAAQ,CAAA,EAAG,KAAA,MACrB,CAAA,eAAgB,CAAA;;;UCWL,sBAAA,WACL,OAAA,eACG,MAAA,CAAO,CAAA;;;;;EAMpB,IAAA;;;;EAKA,MAAA,EAAQ,CAAA;;;;EAKR,OAAA,IACI,IAAA;;;;IAKE,MAAA,EAAQ,IAAA;;;;IAIR,MAAA;;;;IAIA,IAAA;;;;IAIA,KAAA,GAAQ,GAAA;EAAA;;;;IAMR,OAAA,EAAS,IAAA;;;;IAIT,MAAA;;;;IAIA,IAAA;;;;IAIA,KAAA,GAAQ,GAAA;EAAA;;;;;;;;;;;AJ9BhB;;;;;;IIiDQ,WAAA,GAAc,IAAA,EAAM,MAAA,CAAO,IAAA,qBAAyB,GAAA;IJjDT;AAAA;;IIqD3C,MAAA;;AH7GR;;IGiHQ,IAAA;IHzGN;;;IG6GM,KAAA,GAAQ,GAAA;EAAA;;;;EAOd,WAAA,GAAc,KAAA;;;;IAIZ,IAAA;;;;IAIA,OAAA,EAAS,KAAA,OAAY,MAAA,CAAO,CAAA;;;;;IAK5B,cAAA,EAAgB,KAAA,OAAY,YAAA;EAAA;;;;;;;;;;;;;AH/HhC;;;;AAAqD;;;;ACGrD;;;;;;;;;;;EE8JE,WAAA,GAAc,KAAA;IFvJP;;;IE2JL,OAAA,EAAS,KAAA,OAAY,MAAA,CAAO,CAAA;IFxJ1B;;;IE4JF,IAAA;IFtKkD;;;IE0KlD,MAAA;IFzKD;;;IE6KC,KAAA,GAAQ,GAAA;EAAA;EFzKN;;;EE+KJ,MAAA,IACE,IAAA,EAAM,uBAAA,SAAgC,UAAA,CAAW,CAAA,aAC9C,uBAAA;AAAA;AAAA,cAKM,eAAA,WAA0B,OAAA,GAAU,OAAA;EAAA,SAC/B,OAAA,EAAS,sBAAA,CAAuB,CAAA;cAEpC,OAAA,EAAS,sBAAA,CAAuB,CAAA;EAI5C,KAAA,CAAM,KAAA;EAAA,IAYF,IAAA,IAAQ,aAAA,CAAc,CAAA;EAAA,IActB,IAAA;EAAA,IAIA,MAAA,IAAU,CAAA;EAAA,IAIV,YAAA,IAAgB,aAAA,CAAc,CAAA;EAAA,IAI9B,YAAA,IAAgB,aAAA,CAAc,CAAA;AAAA;;ADvOpC;;KCmPY,UAAA,WAAqB,OAAA,oBACjB,CAAA,iBAAkB,mBAAA;AAAA,KAYtB,YAAA,WAAuB,OAAA;EACjC,IAAA;EACA,MAAA,EAAQ,eAAA,CAAgB,CAAA;AAAA;AAAA,KAGd,aAAA,WAAwB,OAAA,oBACpB,CAAA,iBAAkB,YAAA,CAAa,CAAA;;;cCjRlC,UAAA;AAAA,cACA,cAAA;AAAA,cACA,aAAA;AAAA,cACA,aAAA;AAAA,cACA,aAAA;AAAA,cACA,UAAA;AAAA,cACA,WAAA;AAAA,cACA,OAAA;AAAA,cACA,MAAA;AAAA,cACA,YAAA;AAAA,cACA,eAAA;;;;cAKA,SAAA;AAAA,KAMD,SAAA;EAAA,CACT,UAAA;EAAA,CACA,cAAA;EAAA,CACA,aAAA;EAAA,CACA,aAAA;EAAA,CACA,aAAA;EAAA,CACA,UAAA;EAAA,CACA,WAAA,GAAc,iBAAA;EAAA,CACd,MAAA,GAAS,YAAA;EAAA,CACT,OAAA,GAAU,aAAA;EAAA,CACV,YAAA,GAAe,kBAAA;EAAA,CACf,eAAA;;;;GAKA,SAAA;AAAA;AAAA,KAGS,YAAA,SAAqB,SAAS;AAAA,KAE9B,iBAAA;EACV,IAAA;AAAA,IACE,iBAAiB;EACjB,IAAA;AAAA;AAAA,UAGa,aAAA;EACf,IAAA;EACA,WAAW;AAAA;AAAA,UAGI,kBAAA;;;;EAIf,UAAA,EAAY,GAAG;;;;;;EAOf,IAAA;AAAA;AAAA,UAGe,YAAA;EACf,GAAA;IACE,IAAA;IACA,MAAA,EAAQ,eAAA;EAAA;EAEV,OAAA;IACE,QAAA,GAAW,kBAAA;IACX,QAAA,GAAW,kBAAA;EAAA;AAAA;;;;;;KC5BH,MAAA,WAAiB,OAAA,gBAAuB,YAAA,IAAgB,CAAA,WAC5D,KAAA,GAAQ,SAAA,CAAU,CAAA;;;;;;;;;;;;;;;;cCvCb,iBAAA,oBAAiB,OAAA;;;APZ9B;;;;;;;;;YO0BY,GAAA,SAAY,OAAA,CAAQ,MAAA,QAAc,iBAAA;AAAA;;;cCzBjC,kBAAA,oBAAkB,OAAA;sDAe9B,cAAA;;;;;;;;;;;;;;;;;;;;;;;;;KAEW,YAAA,GAAe,MAAM,QAAQ,kBAAA;;;cClB5B,iBAAA,oBAAiB,OAAA;;;;;cAMjB,mBAAA,oBAAmB,OAAA;;;;cAKnB,kBAAA,oBAAkB,OAAA;;;;;;;;;;;;;KAOnB,WAAA,GAAc,MAAM,QAAQ,iBAAA;AAAA,KAC5B,aAAA,GAAgB,MAAM,QAAQ,mBAAA;AAAA,KAC9B,YAAA,GAAe,MAAM,QAAQ,kBAAA;;;cCA5B,WAAA;EAAA,mBACQ,MAAA,EAAM,MAAA;EAAA,mBACN,GAAA,0BAAG,MAAA;EAAA,mBACH,gBAAA,EAAgB,gBAAA;EAAA,mBAChB,kBAAA,EAAkB,kBAAA;EAAA,mBAClB,UAAA,EAAU,kBAAA;EAAA,mBACV,aAAA,EAAa,eAAA;EAAA,SAChB,cAAA,uBAAc,UAAA,mBAAA,OAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;YAepB,kBAAA;;;;QAUkD,IAAA;MAAA;IAAA;EAAA;EAAA,UAKlD,YAAA,mBAAY,aAAA;EAAA,UA0BZ,kBAAA,mBAAkB,aAAA;;;;;;;;;;;;;YAwBZ,iBAAA,CAAkB,IAAA,EAAM,QAAA,GAAW,OAAA;;;;;;;;YAWzC,UAAA,CAAW,IAAA,EAAM,WAAA;;;;;AV7D7B;;;EUwES,MAAA,CAAO,UAAA,YAA+C,eAAA;EVxEtC;;;;AAA0B;;;EU6FpC,SAAA,CAAU,CAAA,GAAG,SAAA,GAAiB,OAAA,CAAQ,IAAA,CAAK,UAAA;ETrJ7C;;;;;;ESkNE,gBAAA,IAAoB,OAAA,CAAQ,UAAA;;;;;;;;YAgB/B,iBAAA,CAAkB,GAAA,GAAM,YAAA;;;;;;;;;;;;;;EAsBrB,UAAA,CACX,IAAA,EAAM,QAAA,EACN,OAAA;IACE,cAAA,YAA0B,QAAA;IAC1B,MAAA;IACA,IAAA,GAAO,gBAAA;IACP,IAAA;EAAA,IAED,OAAA,CAAQ,UAAA;;;;;ATtPb;;;;AAAqD;;;;ACGrD;;;;;;YQmTkB,mBAAA,CACd,MAAA,EAAQ,eAAA,EACR,MAAA,UACA,MAAA,QAAc,OAAA,CAAQ,UAAA,IACrB,OAAA,CAAQ,UAAA;ERrTR;;;;;;;;;;EQ6UU,UAAA,CAAW,EAAA,WAAa,UAAA,GAAa,OAAA,CAAQ,QAAA;ER/Ub;;;;;;;;;;;;EQkWhC,UAAA,CACX,EAAA,UACA,IAAA;IACE,IAAA;IACA,IAAA;IACA,cAAA,GAAiB,QAAA;EAAA,IAElB,OAAA,CAAQ,UAAA;ERhWP;;;;;;AACiB;;;EQ8XR,UAAA,CAAW,EAAA,WAAa,OAAA,CAAQ,EAAA;EPvYnC;;;;;EOuaG,WAAA,CAAY,GAAA,aAAgB,OAAA;EPtaM;;;;;;;;EOkdlC,WAAA,CAAY,EAAA,WAAa,UAAA,GAAa,OAAA,CAAQ,UAAA;EP7cvD;;;;;;;;;;;EOgeS,eAAA,IAAmB,OAAA,CAAQ,YAAA;EPreO;;;;;;;EO2gBxC,gBAAA,CAAiB,MAAA,EAAQ,UAAA,GAAa,YAAA;AAAA;;;;;;;cCphBlC,wBAAA;EAAA,mBACQ,GAAA;EAAA,mBACA,KAAA;EAAA,mBACA,WAAA,EAAW,WAAA;;;;;;WAOd,YAAA,0BAAY,iBAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AXjB9B;;;;;;;;cYwBa,kBAAA;;;;;;EAML,cAAA,CACJ,IAAA,EAAM,UAAA,EACN,IAAA,EAAM,gBAAA,eACL,OAAA;;;;;;;;;;;;;;;;;;;EAkCG,YAAA,CAAa,IAAA,EAAM,UAAA,GAAa,OAAA;AAAA;;;;;;;cCzD3B,cAAA;EAAA,mBACQ,GAAA;EAAA,mBACA,KAAA;EAAA,mBACA,WAAA,EAAW,WAAA;EAAA,mBACX,UAAA,EAAU,kBAAA;;;;;WAMb,SAAA,0BAAS,iBAAA;;;;;;;;;;;;;;0DANI,cAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;WAsBb,UAAA,0BAAU,iBAAA;;;;;;;;;;;;;;WAmBV,WAAA,0BAAW,iBAAA;;;;;;;;;;;;AbE7B;WauBkB,UAAA,0BAAU,iBAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;WA0BV,UAAA,0BAAU,iBAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EX5FmB;;;;;;;;;;;EAAA,SW2H7B,UAAA,0BAAU,iBAAA;;;;;;EXlHtB;;;;;;AACiB;;;;ACTvB;;;;EDQM,SW2JY,gBAAA,0BAAgB,iBAAA;;;;;;;;;cCjLrB,QAAA;EAAA,mBACQ,WAAA,EAAW,WAAA;EAAA,SAEd,UAAA,6BAAU,kBAAA;AAAA;;;;;;;;;;;;;;cCMf,wBAAA,oBAAwB,OAAA;;;;;;;KAQzB,kBAAA,GAAqB,MAAM,QAAQ,wBAAA;;;;YCMnC,iBAAA;;;;IAIR,GAAA,GAAM,YAAA;IhB2BR;;;IgBtBE,IAAA;IhBjCc;;;IgBsCd,IAAA,GAAO,gBAAgB;;;;;;IAOvB,OAAA;EAAA;AAAA;;;;;;;;;;;;cAiBS,cAAA,mBAAc,OAAA,kBAAA,MAAA"}
1
+ {"version":3,"file":"index.d.ts","names":[],"sources":["../../../src/api/files/entities/files.ts","../../../src/api/files/schemas/fileQuerySchema.ts","../../../src/orm/core/schemas/insertSchema.ts","../../../src/orm/core/schemas/updateSchema.ts","../../../src/orm/core/primitives/$entity.ts","../../../src/orm/core/constants/PG_SYMBOLS.ts","../../../src/orm/core/helpers/pgAttr.ts","../../../src/orm/core/schemas/databaseEnvSchema.ts","../../../src/api/files/schemas/fileResourceSchema.ts","../../../src/api/files/schemas/storageStatsSchema.ts","../../../src/api/files/services/FileService.ts","../../../src/api/files/controllers/AdminFileStatsController.ts","../../../src/api/files/providers/FileAccessProvider.ts","../../../src/api/files/controllers/FileController.ts","../../../src/api/files/jobs/FileJobs.ts","../../../src/api/files/schemas/fileCreatorSummarySchema.ts","../../../src/api/files/index.ts"],"mappings":"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"}
@@ -1,4 +1,4 @@
1
- import { $hook, $inject, $module, Alepha, t } from "alepha";
1
+ import { $hook, $inject, $module, Alepha, z } from "alepha";
2
2
  import { $bucket, AlephaBucket, FileNotFoundError } from "alepha/bucket";
3
3
  import { $etag, AlephaServerEtag } from "alepha/server/etag";
4
4
  import { $secure } from "alepha/security";
@@ -10,44 +10,44 @@ import { $entity, $repository, RepositoryProvider, db, pageQuerySchema } from "a
10
10
  import { FileSystemProvider } from "alepha/system";
11
11
  import { $scheduler } from "alepha/scheduler";
12
12
  //#region ../../src/api/files/schemas/storageStatsSchema.ts
13
- const bucketStatsSchema = t.object({
14
- bucket: t.string(),
15
- totalSize: t.number(),
16
- fileCount: t.number()
13
+ const bucketStatsSchema = z.object({
14
+ bucket: z.string(),
15
+ totalSize: z.number(),
16
+ fileCount: z.number()
17
17
  });
18
- const mimeTypeStatsSchema = t.object({
19
- mimeType: t.string(),
20
- fileCount: t.number()
18
+ const mimeTypeStatsSchema = z.object({
19
+ mimeType: z.string(),
20
+ fileCount: z.number()
21
21
  });
22
- const storageStatsSchema = t.object({
23
- totalSize: t.number(),
24
- totalFiles: t.number(),
25
- byBucket: t.array(bucketStatsSchema),
26
- byMimeType: t.array(mimeTypeStatsSchema)
22
+ const storageStatsSchema = z.object({
23
+ totalSize: z.number(),
24
+ totalFiles: z.number(),
25
+ byBucket: z.array(bucketStatsSchema),
26
+ byMimeType: z.array(mimeTypeStatsSchema)
27
27
  });
28
28
  //#endregion
29
29
  //#region ../../src/api/files/entities/files.ts
30
30
  const files = $entity({
31
31
  name: "files",
32
- schema: t.object({
33
- id: db.primaryKey(t.uuid()),
32
+ schema: z.object({
33
+ id: db.primaryKey(z.uuid()),
34
34
  version: db.version(),
35
35
  createdAt: db.createdAt(),
36
36
  updatedAt: db.updatedAt(),
37
37
  organizationId: db.organization(),
38
- blobId: t.text(),
39
- creator: t.optional(t.uuid()),
40
- creatorRealm: t.optional(t.string()),
41
- creatorName: t.optional(t.string()),
42
- bucket: t.text(),
43
- expirationDate: t.optional(t.datetime()),
38
+ blobId: z.text(),
39
+ creator: z.uuid().optional(),
40
+ creatorRealm: z.string().optional(),
41
+ creatorName: z.string().optional(),
42
+ bucket: z.text(),
43
+ expirationDate: z.datetime().optional(),
44
44
  /**
45
45
  * Display name. Mutable via `FileService.updateFile` — can be renamed
46
46
  * by callers (e.g. an Archive UI letting users rename uploaded blobs).
47
47
  * Initialized to the uploader's filename at upload time. Use
48
48
  * `originalName` if you need the as-uploaded filename after a rename.
49
49
  */
50
- name: t.text(),
50
+ name: z.text(),
51
51
  /**
52
52
  * Immutable record of the uploader's filename at upload time. Set once
53
53
  * by `FileService.uploadFile` and the `bucket:file:uploaded` hook;
@@ -55,22 +55,22 @@ const files = $entity({
55
55
  * headers (Content-Disposition), audit trails, and any UI that wants
56
56
  * to show "originally uploaded as X" alongside a renamed file.
57
57
  */
58
- originalName: t.optional(t.string()),
59
- size: t.number(),
60
- mimeType: t.string(),
58
+ originalName: z.string().optional(),
59
+ size: z.number(),
60
+ mimeType: z.string(),
61
61
  /**
62
62
  * Free-form taxonomy. Normalized server-side (trim/lowercase/dedupe is
63
63
  * caller's responsibility for now). Use with `FileQuery.tags` to
64
64
  * filter via array-contains.
65
65
  */
66
- tags: t.optional(t.array(t.text())),
66
+ tags: z.array(z.text()).optional(),
67
67
  /**
68
68
  * SHA-256 hex digest of the file content (64 lowercase hex chars).
69
69
  * Computed at upload time by `FileService.calculateChecksum`. Stable
70
70
  * — never recomputed after upload. Use for integrity verification,
71
71
  * content-based dedup, or downstream virus-scan correlation.
72
72
  */
73
- checksum: t.optional(t.string())
73
+ checksum: z.string().optional()
74
74
  }),
75
75
  indexes: [
76
76
  "expirationDate",
@@ -540,14 +540,14 @@ var FileAccessProvider = class {
540
540
  };
541
541
  //#endregion
542
542
  //#region ../../src/api/files/schemas/fileQuerySchema.ts
543
- const fileQuerySchema = t.extend(pageQuerySchema, {
544
- bucket: t.optional(t.string()),
545
- tags: t.optional(t.array(t.string())),
546
- name: t.optional(t.string()),
547
- mimeType: t.optional(t.string()),
548
- creator: t.optional(t.uuid()),
549
- createdAfter: t.optional(t.datetime()),
550
- createdBefore: t.optional(t.datetime())
543
+ const fileQuerySchema = pageQuerySchema.extend({
544
+ bucket: z.string().optional(),
545
+ tags: z.array(z.string()).optional(),
546
+ name: z.string().optional(),
547
+ mimeType: z.string().optional(),
548
+ creator: z.uuid().optional(),
549
+ createdAfter: z.datetime().optional(),
550
+ createdBefore: z.datetime().optional()
551
551
  });
552
552
  //#endregion
553
553
  //#region ../../src/api/files/schemas/fileCreatorSummarySchema.ts
@@ -562,26 +562,26 @@ const fileQuerySchema = t.extend(pageQuerySchema, {
562
562
  * comes back with `user` undefined. Callers must fall back to `creatorName`
563
563
  * or the raw `creator` id.
564
564
  */
565
- const fileCreatorSummarySchema = t.object({
566
- id: t.uuid(),
567
- email: t.optional(t.string({ format: "email" })),
568
- username: t.optional(t.shortText({
565
+ const fileCreatorSummarySchema = z.object({
566
+ id: z.uuid(),
567
+ email: z.string().meta({ format: "email" }).optional(),
568
+ username: z.shortText({
569
569
  minLength: 3,
570
570
  maxLength: 30
571
- })),
572
- firstName: t.optional(t.string()),
573
- lastName: t.optional(t.string())
571
+ }).optional(),
572
+ firstName: z.string().optional(),
573
+ lastName: z.string().optional()
574
574
  });
575
575
  //#endregion
576
576
  //#region ../../src/api/files/schemas/fileResourceSchema.ts
577
- const fileResourceSchema = t.extend(files.schema, {
577
+ const fileResourceSchema = files.schema.extend({
578
578
  /**
579
579
  * Uploader summary, embedded by the admin listing via a best-effort
580
580
  * left join. Optional — see `fileCreatorSummarySchema`. Distinct from
581
581
  * the denormalized `creatorName`, which is set only on direct uploads
582
582
  * and never carries an email.
583
583
  */
584
- user: t.optional(fileCreatorSummarySchema) }, {
584
+ user: fileCreatorSummarySchema.optional() }).meta({
585
585
  title: "FileResource",
586
586
  description: "A file resource representing a file stored in the system."
587
587
  });
@@ -607,7 +607,7 @@ var FileController = class {
607
607
  description: "List files with filtering and pagination",
608
608
  schema: {
609
609
  query: fileQuerySchema,
610
- response: t.page(fileResourceSchema)
610
+ response: z.page(fileResourceSchema)
611
611
  },
612
612
  handler: ({ query }) => this.fileService.findFiles(query)
613
613
  });
@@ -622,7 +622,7 @@ var FileController = class {
622
622
  use: [$secure({ permissions: ["admin:file:delete"] })],
623
623
  description: "Delete a file",
624
624
  schema: {
625
- params: t.object({ id: t.uuid() }),
625
+ params: z.object({ id: z.uuid() }),
626
626
  response: okSchema
627
627
  },
628
628
  handler: ({ params }) => this.fileService.deleteFile(params.id)
@@ -638,11 +638,8 @@ var FileController = class {
638
638
  use: [$secure({ permissions: ["admin:file:delete"] })],
639
639
  description: "Delete many files",
640
640
  schema: {
641
- body: t.object({ ids: t.array(t.uuid(), {
642
- minItems: 1,
643
- maxItems: 1e3
644
- }) }),
645
- response: t.object({ deleted: t.array(t.string()) })
641
+ body: z.object({ ids: z.array(z.uuid()).min(1).max(1e3) }),
642
+ response: z.object({ deleted: z.array(z.string()) })
646
643
  },
647
644
  handler: async ({ body }) => {
648
645
  return { deleted: await this.fileService.deleteFiles(body.ids) };
@@ -659,10 +656,10 @@ var FileController = class {
659
656
  use: [$secure({ permissions: ["file:create"] })],
660
657
  description: "Upload a new file",
661
658
  schema: {
662
- body: t.object({ file: t.file() }),
663
- query: t.object({
664
- expirationDate: t.optional(t.datetime()),
665
- bucket: t.optional(t.string())
659
+ body: z.object({ file: z.file() }),
660
+ query: z.object({
661
+ expirationDate: z.datetime().optional(),
662
+ bucket: z.string().optional()
666
663
  }),
667
664
  response: fileResourceSchema
668
665
  },
@@ -682,11 +679,11 @@ var FileController = class {
682
679
  use: [$secure({ permissions: ["admin:file:update"] })],
683
680
  description: "Update file metadata",
684
681
  schema: {
685
- params: t.object({ id: t.uuid() }),
686
- body: t.object({
687
- name: t.optional(t.string()),
688
- tags: t.optional(t.array(t.string())),
689
- expirationDate: t.optional(t.datetime())
682
+ params: z.object({ id: z.uuid() }),
683
+ body: z.object({
684
+ name: z.string().optional(),
685
+ tags: z.array(z.string()).optional(),
686
+ expirationDate: z.datetime().optional()
690
687
  }),
691
688
  response: fileResourceSchema
692
689
  },
@@ -713,8 +710,8 @@ var FileController = class {
713
710
  immutable: true
714
711
  } })],
715
712
  schema: {
716
- params: t.object({ id: t.uuid() }),
717
- response: t.file()
713
+ params: z.object({ id: z.uuid() }),
714
+ response: z.file()
718
715
  },
719
716
  handler: async ({ params, user }) => {
720
717
  const file = await this.fileService.getFileById(params.id);
@@ -746,8 +743,8 @@ var FileController = class {
746
743
  immutable: true
747
744
  } })],
748
745
  schema: {
749
- params: t.object({ id: t.uuid() }),
750
- response: t.file()
746
+ params: z.object({ id: z.uuid() }),
747
+ response: z.file()
751
748
  },
752
749
  handler: async ({ params }) => {
753
750
  const file = await this.fileService.getFileById(params.id);
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../../../src/api/files/schemas/storageStatsSchema.ts","../../../src/api/files/entities/files.ts","../../../src/api/files/services/FileService.ts","../../../src/api/files/controllers/AdminFileStatsController.ts","../../../src/api/files/providers/FileAccessProvider.ts","../../../src/api/files/schemas/fileQuerySchema.ts","../../../src/api/files/schemas/fileCreatorSummarySchema.ts","../../../src/api/files/schemas/fileResourceSchema.ts","../../../src/api/files/controllers/FileController.ts","../../../src/api/files/jobs/FileJobs.ts","../../../src/api/files/index.ts"],"sourcesContent":["import type { Static } from \"alepha\";\nimport { t } from \"alepha\";\n\nexport const bucketStatsSchema = t.object({\n bucket: t.string(),\n totalSize: t.number(),\n fileCount: t.number(),\n});\n\nexport const mimeTypeStatsSchema = t.object({\n mimeType: t.string(),\n fileCount: t.number(),\n});\n\nexport const storageStatsSchema = t.object({\n totalSize: t.number(),\n totalFiles: t.number(),\n byBucket: t.array(bucketStatsSchema),\n byMimeType: t.array(mimeTypeStatsSchema),\n});\n\nexport type BucketStats = Static<typeof bucketStatsSchema>;\nexport type MimeTypeStats = Static<typeof mimeTypeStatsSchema>;\nexport type StorageStats = Static<typeof storageStatsSchema>;\n","import { type Static, t } from \"alepha\";\nimport { $entity, db } from \"alepha/orm\";\n\nexport const files = $entity({\n name: \"files\",\n schema: t.object({\n id: db.primaryKey(t.uuid()),\n version: db.version(),\n createdAt: db.createdAt(),\n updatedAt: db.updatedAt(),\n organizationId: db.organization(),\n blobId: t.text(),\n creator: t.optional(t.uuid()),\n creatorRealm: t.optional(t.string()),\n creatorName: t.optional(t.string()),\n bucket: t.text(),\n expirationDate: t.optional(t.datetime()),\n /**\n * Display name. Mutable via `FileService.updateFile` — can be renamed\n * by callers (e.g. an Archive UI letting users rename uploaded blobs).\n * Initialized to the uploader's filename at upload time. Use\n * `originalName` if you need the as-uploaded filename after a rename.\n */\n name: t.text(),\n /**\n * Immutable record of the uploader's filename at upload time. Set once\n * by `FileService.uploadFile` and the `bucket:file:uploaded` hook;\n * `FileService.updateFile` never touches it. Useful for download\n * headers (Content-Disposition), audit trails, and any UI that wants\n * to show \"originally uploaded as X\" alongside a renamed file.\n */\n originalName: t.optional(t.string()),\n size: t.number(),\n mimeType: t.string(),\n /**\n * Free-form taxonomy. Normalized server-side (trim/lowercase/dedupe is\n * caller's responsibility for now). Use with `FileQuery.tags` to\n * filter via array-contains.\n */\n tags: t.optional(t.array(t.text())),\n /**\n * SHA-256 hex digest of the file content (64 lowercase hex chars).\n * Computed at upload time by `FileService.calculateChecksum`. Stable\n * — never recomputed after upload. Use for integrity verification,\n * content-based dedup, or downstream virus-scan correlation.\n */\n checksum: t.optional(t.string()),\n }),\n indexes: [\n \"expirationDate\",\n \"bucket\",\n \"creator\",\n \"createdAt\",\n \"mimeType\",\n {\n columns: [\"bucket\", \"createdAt\"],\n },\n ],\n});\n\nexport type FileEntity = Static<typeof files.schema>;\n","import { createHash } from \"node:crypto\";\nimport { $hook, $inject, Alepha, type FileLike } from \"alepha\";\nimport {\n $bucket,\n type BucketPrimitive,\n FileNotFoundError,\n} from \"alepha/bucket\";\nimport {\n type DateTime,\n DateTimeProvider,\n type DurationLike,\n} from \"alepha/datetime\";\nimport { $logger } from \"alepha/logger\";\nimport { $repository, type Page, RepositoryProvider } from \"alepha/orm\";\nimport type { UserAccountToken } from \"alepha/security\";\nimport type { Ok } from \"alepha/server\";\nimport { NotFoundError } from \"alepha/server\";\nimport { FileSystemProvider } from \"alepha/system\";\nimport { type FileEntity, files } from \"../entities/files.ts\";\nimport type { FileQuery } from \"../schemas/fileQuerySchema.ts\";\nimport type { FileResource } from \"../schemas/fileResourceSchema.ts\";\nimport type { StorageStats } from \"../schemas/storageStatsSchema.ts\";\n\nexport class FileService {\n protected readonly alepha = $inject(Alepha);\n protected readonly log = $logger();\n protected readonly dateTimeProvider = $inject(DateTimeProvider);\n protected readonly repositoryProvider = $inject(RepositoryProvider);\n protected readonly fileSystem = $inject(FileSystemProvider);\n protected readonly defaultBucket = $bucket({ name: \"default\" });\n public readonly fileRepository = $repository(files);\n\n /**\n * Best-effort left join embedding the uploader on every file row, so the\n * admin UI can render `user.email` instead of the bare `creator` UUID.\n * Joins `files.creator` → `users.id`. Only applied when the `users` table is\n * actually registered in the app (see `findFiles`) — the files module stays\n * usable standalone, without `alepha/api/users`. Targets the default `users`\n * table, so creators in non-default realms come back unmatched (`user`\n * undefined), which the UI handles by falling back to `creatorName`.\n *\n * The `users` entity is resolved from the repository registry at runtime\n * rather than imported: the users module already depends on the files module\n * (avatars), so a compile-time import here would form a circular dependency.\n */\n protected resolveCreatorJoin() {\n const usersEntity = this.repositoryProvider\n .getRepositories()\n .find((repo) => repo.entity.name === \"users\")?.entity;\n if (!usersEntity) {\n return undefined;\n }\n return {\n user: {\n join: usersEntity,\n on: [\"creator\", usersEntity.cols.id] as [\"creator\", { name: string }],\n },\n };\n }\n\n protected onUploadFile = $hook({\n on: \"bucket:file:uploaded\",\n handler: async ({ file, bucket, options, id }) => {\n if (options.persist === false) {\n return;\n }\n\n const checksum = await this.calculateChecksum(file);\n\n await this.persistBlobMetadata(bucket, id, () =>\n this.fileRepository.create({\n blobId: id,\n mimeType: file.type,\n name: file.name,\n originalName: file.name,\n size: file.size,\n creator: options.user?.id,\n creatorRealm: options.user?.realm,\n expirationDate: this.getExpirationDate(options.ttl),\n bucket: bucket.name,\n checksum,\n }),\n );\n },\n });\n\n protected onDeleteBucketFile = $hook({\n on: \"bucket:file:deleted\",\n handler: async ({ bucket, id }) => {\n await this.fileRepository.deleteMany({\n blobId: { eq: id },\n bucket: { eq: bucket.name },\n });\n },\n });\n\n // -------------------------------------------------------------------------------------------------------------------\n\n /**\n * Calculates SHA-256 checksum of a file.\n *\n * Reads the whole file into memory. Callers that already hold the bytes\n * should use {@link hashBuffer} instead to avoid re-reading — re-reading a\n * one-shot stream either yields the wrong hash or drains a stream another\n * step still needs.\n *\n * @param file - The file to calculate checksum for\n * @returns Hexadecimal string representation of the SHA-256 hash\n * @protected\n */\n protected async calculateChecksum(file: FileLike): Promise<string> {\n return this.hashBuffer(await file.arrayBuffer());\n }\n\n /**\n * Calculates the SHA-256 checksum of an already-read body.\n *\n * @param data - The file bytes\n * @returns Hexadecimal string representation of the SHA-256 hash\n * @protected\n */\n protected hashBuffer(data: ArrayBuffer): string {\n return createHash(\"sha256\").update(Buffer.from(data)).digest(\"hex\");\n }\n\n /**\n * Gets a bucket primitive by name.\n *\n * @param bucketName - The name of the bucket to retrieve (defaults to \"default\")\n * @returns The bucket primitive\n * @throws {NotFoundError} If the bucket is not found\n */\n public bucket(bucketName: string = this.defaultBucket.name): BucketPrimitive {\n const bucket = this.alepha\n .primitives($bucket)\n .find((it) => it.name === bucketName);\n\n if (!bucket) {\n throw new NotFoundError(`Bucket '${bucketName}' not found.`);\n }\n\n return bucket;\n }\n\n // -------------------------------------------------------------------------------------------------------------------\n\n /**\n * Finds files matching the given query criteria with pagination support.\n * Supports filtering by bucket, tags, name, mimeType, creator, and date range.\n *\n * @param q - Query parameters including bucket, tags, name, mimeType, creator, date range, pagination, and sorting\n * @returns Paginated list of file entities\n */\n public async findFiles(q: FileQuery = {}): Promise<Page<FileEntity>> {\n q.sort ??= \"-createdAt\";\n\n const where = this.fileRepository.createQueryWhere();\n\n if (q.bucket) {\n where.bucket = { eq: q.bucket };\n }\n\n if (q.tags) {\n where.tags = { arrayContains: q.tags };\n }\n\n if (q.name) {\n where.name = { ilike: `%${q.name}%` };\n }\n\n if (q.mimeType) {\n where.mimeType = { eq: q.mimeType };\n }\n\n if (q.creator) {\n where.creator = { eq: q.creator };\n }\n\n if (q.createdAfter && q.createdBefore) {\n where.createdAt = {\n gte: q.createdAfter,\n lte: q.createdBefore,\n };\n } else if (q.createdAfter) {\n where.createdAt = { gte: q.createdAfter };\n } else if (q.createdBefore) {\n where.createdAt = { lte: q.createdBefore };\n }\n\n // The creator join requires the `users` table. Only opt in when a users\n // repository is registered (i.e. `alepha/api/users` is loaded) so the\n // files module — and its standalone tests — keep working without it.\n const withCreator = this.resolveCreatorJoin();\n\n return await this.fileRepository\n .paginate(\n q,\n { where, ...(withCreator ? { with: withCreator } : {}) },\n { count: true },\n )\n .then((page) => {\n return {\n ...page,\n content: page.content.map((it) => this.entityToResource(it)),\n };\n });\n }\n\n /**\n * Finds files that have expired based on their expiration date.\n * Limited to 1000 files per call to prevent memory issues.\n *\n * @returns Array of expired file entities\n */\n public async findExpiredFiles(): Promise<FileEntity[]> {\n return await this.fileRepository.findMany({\n limit: 1000,\n where: {\n expirationDate: { lte: this.dateTimeProvider.nowISOString() },\n },\n });\n }\n\n /**\n * Calculates an expiration date based on a TTL (time to live) duration.\n *\n * @param ttl - Duration like \"1 day\", \"2 hours\", etc.\n * @returns DateTime representation of the expiration date, or undefined if no TTL provided\n * @protected\n */\n protected getExpirationDate(ttl?: DurationLike): string | undefined {\n return ttl\n ? this.dateTimeProvider\n .now()\n .add(this.dateTimeProvider.duration(ttl))\n .toISOString()\n : undefined;\n }\n\n /**\n * Uploads a file to a bucket and creates a database record with metadata.\n * Automatically calculates and stores the file checksum (SHA-256).\n *\n * @param file - The file to upload\n * @param options - Upload options including bucket, expiration, user, and tags\n * @param options.bucket - Target bucket name (defaults to \"default\")\n * @param options.expirationDate - When the file should expire\n * @param options.user - User performing the upload (for audit trail)\n * @param options.tags - Tags to associate with the file\n * @returns The created file entity with all metadata\n * @throws {NotFoundError} If the specified bucket doesn't exist\n */\n public async uploadFile(\n file: FileLike,\n options: {\n expirationDate?: string | DateTime;\n bucket?: string;\n user?: UserAccountToken;\n tags?: string[];\n } = {},\n ): Promise<FileEntity> {\n const bucket = this.bucket(options.bucket);\n\n // Read the source exactly once. The checksum and the stored bytes are\n // both derived from this single buffer, so a one-shot stream is never\n // read twice — the previous code checksummed the file and then let\n // bucket.upload read it again, which drained the stream and stored an\n // empty blob. Uploads are size-capped (bucket maxSize / multipart\n // limits), so buffering here is intentional and bounded.\n const data = await file.arrayBuffer();\n const checksum = this.hashBuffer(data);\n file = this.fileSystem.createFile({\n arrayBuffer: data,\n name: file.name,\n type: file.type,\n });\n\n const blobId = await bucket.upload(file, { persist: false });\n\n let expirationDate: string | undefined;\n if (options.expirationDate) {\n expirationDate = this.dateTimeProvider\n .of(options.expirationDate)\n .toISOString();\n } else if (bucket.options.ttl) {\n expirationDate = this.getExpirationDate(bucket.options.ttl);\n }\n\n return await this.persistBlobMetadata(bucket, blobId, () =>\n this.fileRepository.create({\n blobId: blobId,\n mimeType: file.type,\n name: file.name,\n originalName: file.name,\n size: file.size,\n creator: options.user?.id,\n creatorRealm: options.user?.realm,\n creatorName: options.user?.name,\n expirationDate,\n bucket: bucket.name,\n tags: options.tags,\n checksum,\n }),\n );\n }\n\n /**\n * Persists the metadata row for an already-uploaded blob, deleting the\n * blob if the insert fails. Uploads are not atomic: the blob is written to\n * storage first (the row needs the returned `blobId`), so a failed insert\n * would otherwise leak the blob — an orphaned blob with no DB row. This\n * compensates by removing the blob, favouring the recoverable failure\n * (a missing blob) over the worse one (a row pointing at nothing).\n *\n * Best-effort: cleanup runs with `skipHook` so it neither re-emits\n * `bucket:file:deleted` nor touches the (non-existent) DB row, and a failed\n * cleanup is logged rather than thrown. The original write error is always\n * rethrown so callers still see the real failure.\n *\n * @param bucket - The bucket the blob was uploaded to\n * @param blobId - The id returned by `bucket.upload`\n * @param insert - Thunk performing the metadata insert\n * @returns The created file entity\n */\n protected async persistBlobMetadata(\n bucket: BucketPrimitive,\n blobId: string,\n insert: () => Promise<FileEntity>,\n ): Promise<FileEntity> {\n try {\n return await insert();\n } catch (error) {\n await bucket.delete(blobId, true).catch((cleanupError) => {\n this.log.warn(\n `Failed to remove orphaned blob ${blobId} from bucket ${bucket.name} after a metadata write failure`,\n cleanupError,\n );\n });\n throw error;\n }\n }\n\n /**\n * Streams a file from storage by its database ID, or directly from an\n * already-loaded `FileEntity` to avoid a duplicate DB roundtrip when the\n * caller has already fetched the row (e.g. after an access check).\n *\n * @param id - The database ID (UUID) of the file, or the entity itself\n * @returns The file object ready for streaming/downloading\n * @throws {NotFoundError} If the file doesn't exist in the database\n * @throws {FileNotFoundError} If the file exists in database but not in storage\n */\n public async streamFile(id: string | FileEntity): Promise<FileLike> {\n const entity = await this.getFileById(id);\n const bucket = this.bucket(entity.bucket);\n\n return await bucket.download(entity.blobId);\n }\n\n /**\n * Updates file metadata (name, tags, expiration date).\n * Does not modify the actual file content in storage.\n *\n * @param id - The database ID (UUID) of the file to update\n * @param data - Partial file data to update\n * @param data.name - New file name\n * @param data.tags - New tags array\n * @param data.expirationDate - New expiration date\n * @returns The updated file entity\n * @throws {NotFoundError} If the file doesn't exist in the database\n */\n public async updateFile(\n id: string,\n data: {\n name?: string;\n tags?: string[];\n expirationDate?: DateTime | string;\n },\n ): Promise<FileEntity> {\n const file = await this.getFileById(id);\n\n const updateData: Partial<FileEntity> = {};\n\n if (data.name !== undefined) {\n updateData.name = data.name;\n }\n\n if (data.tags !== undefined) {\n updateData.tags = data.tags;\n }\n\n if (data.expirationDate !== undefined) {\n updateData.expirationDate = this.dateTimeProvider\n .of(data.expirationDate)\n .toISOString();\n }\n\n return await this.fileRepository.updateById(file.id, updateData);\n }\n\n /**\n * Deletes a file from both storage and database.\n * Handles cases where file is already deleted from storage gracefully.\n * Always ensures database record is removed even if storage deletion fails.\n *\n * @param id - The database ID (UUID) of the file to delete\n * @returns Success response with the deleted file ID\n * @throws {NotFoundError} If the file doesn't exist in the database\n */\n public async deleteFile(id: string): Promise<Ok> {\n const file = await this.getFileById(id);\n const bucket = this.bucket(file.bucket);\n\n // Always delete the database record\n await this.fileRepository.deleteById(file.id);\n\n try {\n await bucket.delete(file.blobId, true);\n } catch (e) {\n if (e instanceof FileNotFoundError) {\n // File is already deleted in the bucket, this is okay\n this.log.debug(\n `File ${file.blobId} not found in bucket ${bucket.name}, cleaning up database record`,\n );\n } else {\n // Other errors (permission, network, etc.) - log but continue to clean up database\n this.log.warn(\n `Failed to delete file ${file.blobId} from bucket ${bucket.name}`,\n e,\n );\n }\n }\n\n return { ok: true, id: String(file.id) };\n }\n\n /**\n * Delete many files in one round-trip per bucket. The database rows are\n * removed in a single `deleteMany`, and each affected bucket gets a single\n * `bucket.deleteMany` call (R2/S3 batch where supported).\n */\n public async deleteFiles(ids: string[]): Promise<string[]> {\n if (ids.length === 0) return [];\n\n const files = await this.fileRepository.findMany({\n where: { id: { inArray: ids } },\n columns: [\"id\", \"bucket\", \"blobId\"],\n });\n if (files.length === 0) return [];\n\n const dbDeleted = await this.fileRepository.deleteMany({\n id: { inArray: files.map((f) => f.id) },\n });\n\n const blobsByBucket = new Map<string, string[]>();\n for (const f of files) {\n const list = blobsByBucket.get(f.bucket) ?? [];\n list.push(f.blobId);\n blobsByBucket.set(f.bucket, list);\n }\n\n for (const [bucketName, blobIds] of blobsByBucket) {\n try {\n await this.bucket(bucketName).deleteMany(blobIds, true);\n } catch (e) {\n // DB rows already gone — log and continue. Orphaned blobs are\n // recoverable; orphaned DB rows would be worse.\n this.log.warn(\n `Failed to bulk-delete ${blobIds.length} files from bucket ${bucketName}`,\n e,\n );\n }\n }\n\n return dbDeleted.map(String);\n }\n\n /**\n * Retrieves a file entity by its ID.\n * If already an entity object, returns it as-is (convenience method).\n *\n * @param id - Either a UUID string or an existing FileEntity object\n * @returns The file entity\n * @throws {NotFoundError} If the file doesn't exist in the database\n */\n public async getFileById(id: string | FileEntity): Promise<FileEntity> {\n if (typeof id === \"object\") {\n return id;\n }\n\n return await this.fileRepository.getById(id);\n }\n\n /**\n * Gets storage statistics including total size, file count, and breakdowns by bucket and MIME type.\n *\n * Aggregated in SQL (`SUM`/`COUNT` + `GROUP BY`) rather than by loading\n * every row into memory — the table can hold millions of files, and this\n * endpoint must stay O(groups), not O(rows). Totals are derived from the\n * per-bucket groups (every row has exactly one bucket), so no extra query\n * is needed.\n *\n * @returns Storage statistics with aggregated data\n */\n public async getStorageStats(): Promise<StorageStats> {\n const [byBucketRows, byMimeTypeRows] = await Promise.all([\n this.fileRepository.aggregate({\n select: { bucket: true, size: { sum: true, count: true } },\n groupBy: [\"bucket\"],\n }),\n this.fileRepository.aggregate({\n select: { mimeType: true, size: { count: true } },\n groupBy: [\"mimeType\"],\n }),\n ]);\n\n const byBucket = byBucketRows.map((row) => ({\n bucket: row.bucket,\n totalSize: row.size.sum,\n fileCount: row.size.count,\n }));\n\n const byMimeType = byMimeTypeRows.map((row) => ({\n mimeType: row.mimeType,\n fileCount: row.size.count,\n }));\n\n return {\n totalSize: byBucket.reduce((sum, b) => sum + b.totalSize, 0),\n totalFiles: byBucket.reduce((sum, b) => sum + b.fileCount, 0),\n byBucket,\n byMimeType,\n };\n }\n\n /**\n * Converts a file entity to a file resource (API response format).\n * Currently a pass-through, but allows for future transformation logic.\n *\n * @param entity - The file entity to convert\n * @returns The file resource for API responses\n */\n public entityToResource(entity: FileEntity): FileResource {\n return entity;\n }\n}\n","import { $inject } from \"alepha\";\nimport { $secure } from \"alepha/security\";\nimport { $action } from \"alepha/server\";\nimport { storageStatsSchema } from \"../schemas/storageStatsSchema.ts\";\nimport { FileService } from \"../services/FileService.ts\";\n\n/**\n * REST API controller for storage analytics and statistics.\n * Provides endpoints for viewing storage usage metrics.\n */\nexport class AdminFileStatsController {\n protected readonly url = \"/files/stats\";\n protected readonly group = \"admin:files\";\n protected readonly fileService = $inject(FileService);\n\n /**\n * GET /files/stats - Gets storage statistics.\n * Returns aggregated data including total size, file count,\n * and breakdowns by bucket and MIME type.\n */\n public readonly getFileStats = $action({\n path: this.url,\n group: this.group,\n use: [$secure({ permissions: [\"admin:file:read\"] })],\n description: \"Get storage statistics\",\n schema: {\n response: storageStatsSchema,\n },\n handler: () => this.fileService.getStorageStats(),\n });\n}\n","import type { UserAccountToken } from \"alepha/security\";\nimport { ForbiddenError, NotFoundError } from \"alepha/server\";\nimport type { FileEntity } from \"../entities/files.ts\";\n\n/**\n * Authorization policy for file reads served through `FileController.streamFile`.\n *\n * Default: the caller must be the uploader (`file.creator === user.id`). Any\n * other access path — public buckets, shared attachments, avatars — must be\n * opted in by overriding this provider in the consuming app:\n *\n * ```ts\n * class MyAccess extends FileAccessProvider {\n * async assertReadable(file, user) {\n * if (file.bucket === \"avatars\") return; // public\n * if (file.bucket === \"campaign-icons\") return this.checkCampaignVisible(file, user);\n * return super.assertReadable(file, user);\n * }\n * }\n * Alepha.create().with({ provide: FileAccessProvider, use: MyAccess });\n * ```\n *\n * Why this exists: prior to introducing this gate, `streamFile` only required\n * the framework-wide `file:read` permission. The default `user` role grants\n * `*`, so every authenticated user could download any file by UUID — turning\n * the 128-bit id into the sole security boundary across tenants.\n */\nexport class FileAccessProvider {\n /**\n * Throws `ForbiddenError` when `user` may not read `file`. Override to\n * implement bucket-aware or relationship-aware policies. The default\n * implementation is strict: only the uploader passes.\n */\n async assertReadable(\n file: FileEntity,\n user: UserAccountToken | undefined,\n ): Promise<void> {\n if (!user) {\n throw new ForbiddenError(\"File access requires authentication\");\n }\n // Privileged identities (admin without narrow ownership scope) bypass\n // the per-file check — they need to be able to read any file via the\n // admin UI/inspector regardless of who uploaded it.\n if (user.ownership === false) {\n return;\n }\n if (file.creator && file.creator === user.id) {\n return;\n }\n throw new ForbiddenError(\"File access denied\");\n }\n\n /**\n * Throws when `file` may not be served through the anonymous\n * `/public/files/:id` route. The default is deny-all: nothing is public\n * unless this method is overridden. Throws `NotFoundError` (not 403) so the\n * endpoint doesn't leak the existence of private file ids.\n *\n * Typical override matches on bucket name:\n *\n * ```ts\n * class MyAccess extends FileAccessProvider {\n * async assertPublic(file) {\n * if (file.bucket === \"avatars\") return;\n * if (file.bucket === \"campaign-icons\") return;\n * return super.assertPublic(file);\n * }\n * }\n * ```\n */\n async assertPublic(file: FileEntity): Promise<void> {\n throw new NotFoundError(`File '${file.id}' not found`);\n }\n}\n","import type { Static } from \"alepha\";\nimport { t } from \"alepha\";\nimport { pageQuerySchema } from \"alepha/orm\";\n\nexport const fileQuerySchema = t.extend(pageQuerySchema, {\n bucket: t.optional(t.string()),\n tags: t.optional(t.array(t.string())),\n name: t.optional(t.string()),\n mimeType: t.optional(t.string()),\n creator: t.optional(t.uuid()),\n createdAfter: t.optional(t.datetime()),\n createdBefore: t.optional(t.datetime()),\n});\n\nexport type FileQuery = Static<typeof fileQuerySchema>;\n","import { type Static, t } from \"alepha\";\n\n/**\n * Slim view of a file's uploader, embedded by the admin listing via a\n * best-effort left join (`files.creator` → `users.id`) so the UI can render\n * a human-readable identifier instead of a bare UUID.\n *\n * Optional end-to-end: the join only runs when the `users` entity is\n * registered in the running app (see `FileService.findFiles`), and even then\n * a file whose creator was deleted — or who lives in a non-default realm —\n * comes back with `user` undefined. Callers must fall back to `creatorName`\n * or the raw `creator` id.\n */\nexport const fileCreatorSummarySchema = t.object({\n id: t.uuid(),\n email: t.optional(t.string({ format: \"email\" })),\n username: t.optional(t.shortText({ minLength: 3, maxLength: 30 })),\n firstName: t.optional(t.string()),\n lastName: t.optional(t.string()),\n});\n\nexport type FileCreatorSummary = Static<typeof fileCreatorSummarySchema>;\n","import { type Static, t } from \"alepha\";\nimport { files } from \"../entities/files.ts\";\nimport { fileCreatorSummarySchema } from \"./fileCreatorSummarySchema.ts\";\n\nexport const fileResourceSchema = t.extend(\n files.schema,\n {\n /**\n * Uploader summary, embedded by the admin listing via a best-effort\n * left join. Optional — see `fileCreatorSummarySchema`. Distinct from\n * the denormalized `creatorName`, which is set only on direct uploads\n * and never carries an email.\n */\n user: t.optional(fileCreatorSummarySchema),\n },\n {\n title: \"FileResource\",\n description: \"A file resource representing a file stored in the system.\",\n },\n);\n\nexport type FileResource = Static<typeof fileResourceSchema>;\n","import { $inject, t } from \"alepha\";\nimport { $secure } from \"alepha/security\";\nimport { $action, okSchema } from \"alepha/server\";\nimport { $etag } from \"alepha/server/etag\";\nimport { FileAccessProvider } from \"../providers/FileAccessProvider.ts\";\nimport { fileQuerySchema } from \"../schemas/fileQuerySchema.ts\";\nimport { fileResourceSchema } from \"../schemas/fileResourceSchema.ts\";\nimport { FileService } from \"../services/FileService.ts\";\n\n/**\n * REST API controller for file management operations.\n * Provides endpoints for uploading, downloading, listing, and deleting files.\n */\nexport class FileController {\n protected readonly url = \"/files\";\n protected readonly group = \"files\";\n protected readonly fileService = $inject(FileService);\n protected readonly fileAccess = $inject(FileAccessProvider);\n\n /**\n * GET /files - Lists files with optional filtering and pagination.\n * Supports filtering by bucket and tags.\n */\n public readonly findFiles = $action({\n path: this.url,\n group: `admin:${this.group}`,\n use: [$secure({ permissions: [\"admin:file:read\"] })],\n description: \"List files with filtering and pagination\",\n schema: {\n query: fileQuerySchema,\n response: t.page(fileResourceSchema),\n },\n handler: ({ query }) => this.fileService.findFiles(query),\n });\n\n /**\n * DELETE /files/:id - Deletes a file from both storage and database.\n * Removes the file from the bucket and cleans up the database record.\n */\n public readonly deleteFile = $action({\n method: \"DELETE\",\n path: `${this.url}/:id`,\n group: `admin:${this.group}`,\n use: [$secure({ permissions: [\"admin:file:delete\"] })],\n description: \"Delete a file\",\n schema: {\n params: t.object({\n id: t.uuid(),\n }),\n response: okSchema,\n },\n handler: ({ params }) => this.fileService.deleteFile(params.id),\n });\n\n /**\n * POST /files/delete - Delete many files in one request, batching the\n * underlying bucket calls per bucket (R2/S3 batch where supported).\n */\n public readonly deleteFiles = $action({\n method: \"POST\",\n path: `${this.url}/delete`,\n group: `admin:${this.group}`,\n use: [$secure({ permissions: [\"admin:file:delete\"] })],\n description: \"Delete many files\",\n schema: {\n body: t.object({\n ids: t.array(t.uuid(), { minItems: 1, maxItems: 1000 }),\n }),\n response: t.object({\n deleted: t.array(t.string()),\n }),\n },\n handler: async ({ body }) => {\n const deleted = await this.fileService.deleteFiles(body.ids);\n return { deleted };\n },\n });\n\n /**\n * POST /files - Uploads a new file to storage.\n * Creates a database record with metadata and calculates checksum.\n * Optionally specify bucket and expiration date.\n */\n public readonly uploadFile = $action({\n path: this.url,\n group: this.group,\n use: [$secure({ permissions: [\"file:create\"] })],\n description: \"Upload a new file\",\n schema: {\n body: t.object({\n file: t.file(),\n }),\n query: t.object({\n expirationDate: t.optional(t.datetime()),\n bucket: t.optional(t.string()),\n }),\n response: fileResourceSchema,\n },\n handler: async ({ body, user, query }) =>\n this.fileService.uploadFile(body.file, {\n user,\n ...query,\n }),\n });\n\n /**\n * PATCH /files/:id - Updates file metadata.\n * Allows updating name, tags, and expiration date without modifying file content.\n */\n public readonly updateFile = $action({\n method: \"PATCH\",\n path: `${this.url}/:id`,\n group: `admin:${this.group}`,\n use: [$secure({ permissions: [\"admin:file:update\"] })],\n description: \"Update file metadata\",\n schema: {\n params: t.object({\n id: t.uuid(),\n }),\n body: t.object({\n name: t.optional(t.string()),\n tags: t.optional(t.array(t.string())),\n expirationDate: t.optional(t.datetime()),\n }),\n response: fileResourceSchema,\n },\n handler: ({ params, body }) => this.fileService.updateFile(params.id, body),\n });\n\n /**\n * GET /files/:id - Streams/downloads a file by its ID.\n * Returns the file content with appropriate Content-Type header.\n *\n * Authorization is delegated to `FileAccessProvider.assertReadable`. The\n * default policy is creator-only — override the provider via DI to widen\n * access (e.g. avatars, shared attachments). See `FileAccessProvider`.\n *\n * Cache-Control is `private` because the per-user authorization decision\n * cannot be cached by shared proxies/CDNs. Client-side ETag still works.\n */\n public readonly streamFile = $action({\n path: `${this.url}/:id`,\n group: this.group,\n description: \"Download a file\",\n use: [\n $secure({ permissions: [\"file:read\"] }),\n $etag({\n control: {\n private: true,\n maxAge: [1, \"year\"],\n immutable: true,\n },\n }),\n ],\n schema: {\n params: t.object({\n id: t.uuid(),\n }),\n response: t.file(),\n },\n handler: async ({ params, user }) => {\n const file = await this.fileService.getFileById(params.id);\n await this.fileAccess.assertReadable(file, user);\n return await this.fileService.streamFile(file);\n },\n });\n\n /**\n * GET /public/files/:id - Anonymous, edge-cacheable download.\n *\n * Authorization is delegated to `FileAccessProvider.assertPublic`. The\n * default policy is deny-all (throws `NotFoundError`), so consuming apps\n * must override the provider to opt files in — typically by bucket name\n * (avatars, campaign icons, etc.).\n *\n * Cache-Control is `public, immutable, max-age=1y` so Cloudflare's edge\n * cache and any intermediary proxy can serve subsequent hits without\n * touching the Worker. The split URL prefix (vs `/files/:id`) is what\n * makes this safe: edge cache is URL-keyed, so public and private files\n * live in separate cache lanes.\n */\n public readonly streamPublicFile = $action({\n path: \"/public/files/:id\",\n group: this.group,\n description: \"Download a public file (anonymous, edge-cacheable)\",\n use: [\n $etag({\n control: {\n public: true,\n maxAge: [1, \"year\"],\n immutable: true,\n },\n }),\n ],\n schema: {\n params: t.object({\n id: t.uuid(),\n }),\n response: t.file(),\n },\n handler: async ({ params }) => {\n const file = await this.fileService.getFileById(params.id);\n await this.fileAccess.assertPublic(file);\n return await this.fileService.streamFile(file);\n },\n });\n}\n","import { $inject } from \"alepha\";\nimport { $scheduler } from \"alepha/scheduler\";\nimport { FileService } from \"../services/FileService.ts\";\n\nexport class FileJobs {\n protected readonly fileService = $inject(FileService);\n\n public readonly purgeFiles = $scheduler({\n name: \"api:files:purgeFiles\",\n description: \"Purge files that are marked for deletion\",\n cron: \"0 * * * *\", // Hourly at minute 0\n handler: async () => {\n const files = await this.fileService.findExpiredFiles();\n\n await Promise.all(\n files.map((file) => this.fileService.deleteFile(file.id)),\n );\n },\n });\n}\n","import { $module } from \"alepha\";\nimport { AlephaBucket } from \"alepha/bucket\";\nimport type { DurationLike } from \"alepha/datetime\";\nimport type { UserAccountToken } from \"alepha/security\";\nimport { AlephaServerEtag } from \"alepha/server/etag\";\nimport { AdminFileStatsController } from \"./controllers/AdminFileStatsController.ts\";\nimport { FileController } from \"./controllers/FileController.ts\";\nimport { FileJobs } from \"./jobs/FileJobs.ts\";\nimport { FileAccessProvider } from \"./providers/FileAccessProvider.ts\";\nimport { FileService } from \"./services/FileService.ts\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport * from \"./controllers/AdminFileStatsController.ts\";\nexport * from \"./controllers/FileController.ts\";\nexport * from \"./entities/files.ts\";\nexport * from \"./jobs/FileJobs.ts\";\nexport * from \"./providers/FileAccessProvider.ts\";\nexport * from \"./schemas/fileCreatorSummarySchema.ts\";\nexport * from \"./schemas/fileQuerySchema.ts\";\nexport * from \"./schemas/fileResourceSchema.ts\";\nexport * from \"./schemas/storageStatsSchema.ts\";\nexport * from \"./services/FileService.ts\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\ndeclare module \"alepha/bucket\" {\n interface BucketFileOptions {\n /**\n * Time to live for the files in the bucket.\n */\n ttl?: DurationLike;\n\n /**\n * Tags for the bucket.\n */\n tags?: string[];\n\n /**\n * User performing the operation.\n */\n user?: UserAccountToken;\n\n /**\n * Whether to persist the file metadata in the database.\n *\n * @default true\n */\n persist?: boolean;\n }\n}\n\n// ---------------------------------------------------------------------------------------------------------------------\n\n/**\n * File management endpoints.\n *\n * **Features:**\n * - Upload/download endpoints\n * - File metadata storage\n * - TTL-based expiration\n * - Storage statistics\n *\n * @module alepha.api.files\n */\nexport const AlephaApiFiles = $module({\n name: \"alepha.api.files\",\n services: [\n FileController,\n AdminFileStatsController,\n FileJobs,\n FileService,\n FileAccessProvider,\n ],\n imports: [AlephaBucket, AlephaServerEtag],\n});\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","names":[],"sources":["../../../src/api/files/schemas/storageStatsSchema.ts","../../../src/api/files/entities/files.ts","../../../src/api/files/services/FileService.ts","../../../src/api/files/controllers/AdminFileStatsController.ts","../../../src/api/files/providers/FileAccessProvider.ts","../../../src/api/files/schemas/fileQuerySchema.ts","../../../src/api/files/schemas/fileCreatorSummarySchema.ts","../../../src/api/files/schemas/fileResourceSchema.ts","../../../src/api/files/controllers/FileController.ts","../../../src/api/files/jobs/FileJobs.ts","../../../src/api/files/index.ts"],"sourcesContent":["import type { Static } from \"alepha\";\nimport { z } from \"alepha\";\n\nexport const bucketStatsSchema = z.object({\n bucket: z.string(),\n totalSize: z.number(),\n fileCount: z.number(),\n});\n\nexport const mimeTypeStatsSchema = z.object({\n mimeType: z.string(),\n fileCount: z.number(),\n});\n\nexport const storageStatsSchema = z.object({\n totalSize: z.number(),\n totalFiles: z.number(),\n byBucket: z.array(bucketStatsSchema),\n byMimeType: z.array(mimeTypeStatsSchema),\n});\n\nexport type BucketStats = Static<typeof bucketStatsSchema>;\nexport type MimeTypeStats = Static<typeof mimeTypeStatsSchema>;\nexport type StorageStats = Static<typeof storageStatsSchema>;\n","import { type Static, z } from \"alepha\";\nimport { $entity, db } from \"alepha/orm\";\n\nexport const files = $entity({\n name: \"files\",\n schema: z.object({\n id: db.primaryKey(z.uuid()),\n version: db.version(),\n createdAt: db.createdAt(),\n updatedAt: db.updatedAt(),\n organizationId: db.organization(),\n blobId: z.text(),\n creator: z.uuid().optional(),\n creatorRealm: z.string().optional(),\n creatorName: z.string().optional(),\n bucket: z.text(),\n expirationDate: z.datetime().optional(),\n /**\n * Display name. Mutable via `FileService.updateFile` — can be renamed\n * by callers (e.g. an Archive UI letting users rename uploaded blobs).\n * Initialized to the uploader's filename at upload time. Use\n * `originalName` if you need the as-uploaded filename after a rename.\n */\n name: z.text(),\n /**\n * Immutable record of the uploader's filename at upload time. Set once\n * by `FileService.uploadFile` and the `bucket:file:uploaded` hook;\n * `FileService.updateFile` never touches it. Useful for download\n * headers (Content-Disposition), audit trails, and any UI that wants\n * to show \"originally uploaded as X\" alongside a renamed file.\n */\n originalName: z.string().optional(),\n size: z.number(),\n mimeType: z.string(),\n /**\n * Free-form taxonomy. Normalized server-side (trim/lowercase/dedupe is\n * caller's responsibility for now). Use with `FileQuery.tags` to\n * filter via array-contains.\n */\n tags: z.array(z.text()).optional(),\n /**\n * SHA-256 hex digest of the file content (64 lowercase hex chars).\n * Computed at upload time by `FileService.calculateChecksum`. Stable\n * — never recomputed after upload. Use for integrity verification,\n * content-based dedup, or downstream virus-scan correlation.\n */\n checksum: z.string().optional(),\n }),\n indexes: [\n \"expirationDate\",\n \"bucket\",\n \"creator\",\n \"createdAt\",\n \"mimeType\",\n {\n columns: [\"bucket\", \"createdAt\"],\n },\n ],\n});\n\nexport type FileEntity = Static<typeof files.schema>;\n","import { createHash } from \"node:crypto\";\nimport { $hook, $inject, Alepha, type FileLike } from \"alepha\";\nimport {\n $bucket,\n type BucketPrimitive,\n FileNotFoundError,\n} from \"alepha/bucket\";\nimport {\n type DateTime,\n DateTimeProvider,\n type DurationLike,\n} from \"alepha/datetime\";\nimport { $logger } from \"alepha/logger\";\nimport { $repository, type Page, RepositoryProvider } from \"alepha/orm\";\nimport type { UserAccountToken } from \"alepha/security\";\nimport type { Ok } from \"alepha/server\";\nimport { NotFoundError } from \"alepha/server\";\nimport { FileSystemProvider } from \"alepha/system\";\nimport { type FileEntity, files } from \"../entities/files.ts\";\nimport type { FileQuery } from \"../schemas/fileQuerySchema.ts\";\nimport type { FileResource } from \"../schemas/fileResourceSchema.ts\";\nimport type { StorageStats } from \"../schemas/storageStatsSchema.ts\";\n\nexport class FileService {\n protected readonly alepha = $inject(Alepha);\n protected readonly log = $logger();\n protected readonly dateTimeProvider = $inject(DateTimeProvider);\n protected readonly repositoryProvider = $inject(RepositoryProvider);\n protected readonly fileSystem = $inject(FileSystemProvider);\n protected readonly defaultBucket = $bucket({ name: \"default\" });\n public readonly fileRepository = $repository(files);\n\n /**\n * Best-effort left join embedding the uploader on every file row, so the\n * admin UI can render `user.email` instead of the bare `creator` UUID.\n * Joins `files.creator` → `users.id`. Only applied when the `users` table is\n * actually registered in the app (see `findFiles`) — the files module stays\n * usable standalone, without `alepha/api/users`. Targets the default `users`\n * table, so creators in non-default realms come back unmatched (`user`\n * undefined), which the UI handles by falling back to `creatorName`.\n *\n * The `users` entity is resolved from the repository registry at runtime\n * rather than imported: the users module already depends on the files module\n * (avatars), so a compile-time import here would form a circular dependency.\n */\n protected resolveCreatorJoin() {\n const usersEntity = this.repositoryProvider\n .getRepositories()\n .find((repo) => repo.entity.name === \"users\")?.entity;\n if (!usersEntity) {\n return undefined;\n }\n return {\n user: {\n join: usersEntity,\n on: [\"creator\", usersEntity.cols.id] as [\"creator\", { name: string }],\n },\n };\n }\n\n protected onUploadFile = $hook({\n on: \"bucket:file:uploaded\",\n handler: async ({ file, bucket, options, id }) => {\n if (options.persist === false) {\n return;\n }\n\n const checksum = await this.calculateChecksum(file);\n\n await this.persistBlobMetadata(bucket, id, () =>\n this.fileRepository.create({\n blobId: id,\n mimeType: file.type,\n name: file.name,\n originalName: file.name,\n size: file.size,\n creator: options.user?.id,\n creatorRealm: options.user?.realm,\n expirationDate: this.getExpirationDate(options.ttl),\n bucket: bucket.name,\n checksum,\n }),\n );\n },\n });\n\n protected onDeleteBucketFile = $hook({\n on: \"bucket:file:deleted\",\n handler: async ({ bucket, id }) => {\n await this.fileRepository.deleteMany({\n blobId: { eq: id },\n bucket: { eq: bucket.name },\n });\n },\n });\n\n // -------------------------------------------------------------------------------------------------------------------\n\n /**\n * Calculates SHA-256 checksum of a file.\n *\n * Reads the whole file into memory. Callers that already hold the bytes\n * should use {@link hashBuffer} instead to avoid re-reading — re-reading a\n * one-shot stream either yields the wrong hash or drains a stream another\n * step still needs.\n *\n * @param file - The file to calculate checksum for\n * @returns Hexadecimal string representation of the SHA-256 hash\n * @protected\n */\n protected async calculateChecksum(file: FileLike): Promise<string> {\n return this.hashBuffer(await file.arrayBuffer());\n }\n\n /**\n * Calculates the SHA-256 checksum of an already-read body.\n *\n * @param data - The file bytes\n * @returns Hexadecimal string representation of the SHA-256 hash\n * @protected\n */\n protected hashBuffer(data: ArrayBuffer): string {\n return createHash(\"sha256\").update(Buffer.from(data)).digest(\"hex\");\n }\n\n /**\n * Gets a bucket primitive by name.\n *\n * @param bucketName - The name of the bucket to retrieve (defaults to \"default\")\n * @returns The bucket primitive\n * @throws {NotFoundError} If the bucket is not found\n */\n public bucket(bucketName: string = this.defaultBucket.name): BucketPrimitive {\n const bucket = this.alepha\n .primitives($bucket)\n .find((it) => it.name === bucketName);\n\n if (!bucket) {\n throw new NotFoundError(`Bucket '${bucketName}' not found.`);\n }\n\n return bucket;\n }\n\n // -------------------------------------------------------------------------------------------------------------------\n\n /**\n * Finds files matching the given query criteria with pagination support.\n * Supports filtering by bucket, tags, name, mimeType, creator, and date range.\n *\n * @param q - Query parameters including bucket, tags, name, mimeType, creator, date range, pagination, and sorting\n * @returns Paginated list of file entities\n */\n public async findFiles(q: FileQuery = {}): Promise<Page<FileEntity>> {\n q.sort ??= \"-createdAt\";\n\n const where = this.fileRepository.createQueryWhere();\n\n if (q.bucket) {\n where.bucket = { eq: q.bucket };\n }\n\n if (q.tags) {\n where.tags = { arrayContains: q.tags };\n }\n\n if (q.name) {\n where.name = { ilike: `%${q.name}%` };\n }\n\n if (q.mimeType) {\n where.mimeType = { eq: q.mimeType };\n }\n\n if (q.creator) {\n where.creator = { eq: q.creator };\n }\n\n if (q.createdAfter && q.createdBefore) {\n where.createdAt = {\n gte: q.createdAfter,\n lte: q.createdBefore,\n };\n } else if (q.createdAfter) {\n where.createdAt = { gte: q.createdAfter };\n } else if (q.createdBefore) {\n where.createdAt = { lte: q.createdBefore };\n }\n\n // The creator join requires the `users` table. Only opt in when a users\n // repository is registered (i.e. `alepha/api/users` is loaded) so the\n // files module — and its standalone tests — keep working without it.\n const withCreator = this.resolveCreatorJoin();\n\n return await this.fileRepository\n .paginate(\n q,\n { where, ...(withCreator ? { with: withCreator } : {}) },\n { count: true },\n )\n .then((page) => {\n return {\n ...page,\n content: page.content.map((it) => this.entityToResource(it)),\n };\n });\n }\n\n /**\n * Finds files that have expired based on their expiration date.\n * Limited to 1000 files per call to prevent memory issues.\n *\n * @returns Array of expired file entities\n */\n public async findExpiredFiles(): Promise<FileEntity[]> {\n return await this.fileRepository.findMany({\n limit: 1000,\n where: {\n expirationDate: { lte: this.dateTimeProvider.nowISOString() },\n },\n });\n }\n\n /**\n * Calculates an expiration date based on a TTL (time to live) duration.\n *\n * @param ttl - Duration like \"1 day\", \"2 hours\", etc.\n * @returns DateTime representation of the expiration date, or undefined if no TTL provided\n * @protected\n */\n protected getExpirationDate(ttl?: DurationLike): string | undefined {\n return ttl\n ? this.dateTimeProvider\n .now()\n .add(this.dateTimeProvider.duration(ttl))\n .toISOString()\n : undefined;\n }\n\n /**\n * Uploads a file to a bucket and creates a database record with metadata.\n * Automatically calculates and stores the file checksum (SHA-256).\n *\n * @param file - The file to upload\n * @param options - Upload options including bucket, expiration, user, and tags\n * @param options.bucket - Target bucket name (defaults to \"default\")\n * @param options.expirationDate - When the file should expire\n * @param options.user - User performing the upload (for audit trail)\n * @param options.tags - Tags to associate with the file\n * @returns The created file entity with all metadata\n * @throws {NotFoundError} If the specified bucket doesn't exist\n */\n public async uploadFile(\n file: FileLike,\n options: {\n expirationDate?: string | DateTime;\n bucket?: string;\n user?: UserAccountToken;\n tags?: string[];\n } = {},\n ): Promise<FileEntity> {\n const bucket = this.bucket(options.bucket);\n\n // Read the source exactly once. The checksum and the stored bytes are\n // both derived from this single buffer, so a one-shot stream is never\n // read twice — the previous code checksummed the file and then let\n // bucket.upload read it again, which drained the stream and stored an\n // empty blob. Uploads are size-capped (bucket maxSize / multipart\n // limits), so buffering here is intentional and bounded.\n const data = await file.arrayBuffer();\n const checksum = this.hashBuffer(data);\n file = this.fileSystem.createFile({\n arrayBuffer: data,\n name: file.name,\n type: file.type,\n });\n\n const blobId = await bucket.upload(file, { persist: false });\n\n let expirationDate: string | undefined;\n if (options.expirationDate) {\n expirationDate = this.dateTimeProvider\n .of(options.expirationDate)\n .toISOString();\n } else if (bucket.options.ttl) {\n expirationDate = this.getExpirationDate(bucket.options.ttl);\n }\n\n return await this.persistBlobMetadata(bucket, blobId, () =>\n this.fileRepository.create({\n blobId: blobId,\n mimeType: file.type,\n name: file.name,\n originalName: file.name,\n size: file.size,\n creator: options.user?.id,\n creatorRealm: options.user?.realm,\n creatorName: options.user?.name,\n expirationDate,\n bucket: bucket.name,\n tags: options.tags,\n checksum,\n }),\n );\n }\n\n /**\n * Persists the metadata row for an already-uploaded blob, deleting the\n * blob if the insert fails. Uploads are not atomic: the blob is written to\n * storage first (the row needs the returned `blobId`), so a failed insert\n * would otherwise leak the blob — an orphaned blob with no DB row. This\n * compensates by removing the blob, favouring the recoverable failure\n * (a missing blob) over the worse one (a row pointing at nothing).\n *\n * Best-effort: cleanup runs with `skipHook` so it neither re-emits\n * `bucket:file:deleted` nor touches the (non-existent) DB row, and a failed\n * cleanup is logged rather than thrown. The original write error is always\n * rethrown so callers still see the real failure.\n *\n * @param bucket - The bucket the blob was uploaded to\n * @param blobId - The id returned by `bucket.upload`\n * @param insert - Thunk performing the metadata insert\n * @returns The created file entity\n */\n protected async persistBlobMetadata(\n bucket: BucketPrimitive,\n blobId: string,\n insert: () => Promise<FileEntity>,\n ): Promise<FileEntity> {\n try {\n return await insert();\n } catch (error) {\n await bucket.delete(blobId, true).catch((cleanupError) => {\n this.log.warn(\n `Failed to remove orphaned blob ${blobId} from bucket ${bucket.name} after a metadata write failure`,\n cleanupError,\n );\n });\n throw error;\n }\n }\n\n /**\n * Streams a file from storage by its database ID, or directly from an\n * already-loaded `FileEntity` to avoid a duplicate DB roundtrip when the\n * caller has already fetched the row (e.g. after an access check).\n *\n * @param id - The database ID (UUID) of the file, or the entity itself\n * @returns The file object ready for streaming/downloading\n * @throws {NotFoundError} If the file doesn't exist in the database\n * @throws {FileNotFoundError} If the file exists in database but not in storage\n */\n public async streamFile(id: string | FileEntity): Promise<FileLike> {\n const entity = await this.getFileById(id);\n const bucket = this.bucket(entity.bucket);\n\n return await bucket.download(entity.blobId);\n }\n\n /**\n * Updates file metadata (name, tags, expiration date).\n * Does not modify the actual file content in storage.\n *\n * @param id - The database ID (UUID) of the file to update\n * @param data - Partial file data to update\n * @param data.name - New file name\n * @param data.tags - New tags array\n * @param data.expirationDate - New expiration date\n * @returns The updated file entity\n * @throws {NotFoundError} If the file doesn't exist in the database\n */\n public async updateFile(\n id: string,\n data: {\n name?: string;\n tags?: string[];\n expirationDate?: DateTime | string;\n },\n ): Promise<FileEntity> {\n const file = await this.getFileById(id);\n\n const updateData: Partial<FileEntity> = {};\n\n if (data.name !== undefined) {\n updateData.name = data.name;\n }\n\n if (data.tags !== undefined) {\n updateData.tags = data.tags;\n }\n\n if (data.expirationDate !== undefined) {\n updateData.expirationDate = this.dateTimeProvider\n .of(data.expirationDate)\n .toISOString();\n }\n\n return await this.fileRepository.updateById(file.id, updateData);\n }\n\n /**\n * Deletes a file from both storage and database.\n * Handles cases where file is already deleted from storage gracefully.\n * Always ensures database record is removed even if storage deletion fails.\n *\n * @param id - The database ID (UUID) of the file to delete\n * @returns Success response with the deleted file ID\n * @throws {NotFoundError} If the file doesn't exist in the database\n */\n public async deleteFile(id: string): Promise<Ok> {\n const file = await this.getFileById(id);\n const bucket = this.bucket(file.bucket);\n\n // Always delete the database record\n await this.fileRepository.deleteById(file.id);\n\n try {\n await bucket.delete(file.blobId, true);\n } catch (e) {\n if (e instanceof FileNotFoundError) {\n // File is already deleted in the bucket, this is okay\n this.log.debug(\n `File ${file.blobId} not found in bucket ${bucket.name}, cleaning up database record`,\n );\n } else {\n // Other errors (permission, network, etc.) - log but continue to clean up database\n this.log.warn(\n `Failed to delete file ${file.blobId} from bucket ${bucket.name}`,\n e,\n );\n }\n }\n\n return { ok: true, id: String(file.id) };\n }\n\n /**\n * Delete many files in one round-trip per bucket. The database rows are\n * removed in a single `deleteMany`, and each affected bucket gets a single\n * `bucket.deleteMany` call (R2/S3 batch where supported).\n */\n public async deleteFiles(ids: string[]): Promise<string[]> {\n if (ids.length === 0) return [];\n\n const files = await this.fileRepository.findMany({\n where: { id: { inArray: ids } },\n columns: [\"id\", \"bucket\", \"blobId\"],\n });\n if (files.length === 0) return [];\n\n const dbDeleted = await this.fileRepository.deleteMany({\n id: { inArray: files.map((f) => f.id) },\n });\n\n const blobsByBucket = new Map<string, string[]>();\n for (const f of files) {\n const list = blobsByBucket.get(f.bucket) ?? [];\n list.push(f.blobId);\n blobsByBucket.set(f.bucket, list);\n }\n\n for (const [bucketName, blobIds] of blobsByBucket) {\n try {\n await this.bucket(bucketName).deleteMany(blobIds, true);\n } catch (e) {\n // DB rows already gone — log and continue. Orphaned blobs are\n // recoverable; orphaned DB rows would be worse.\n this.log.warn(\n `Failed to bulk-delete ${blobIds.length} files from bucket ${bucketName}`,\n e,\n );\n }\n }\n\n return dbDeleted.map(String);\n }\n\n /**\n * Retrieves a file entity by its ID.\n * If already an entity object, returns it as-is (convenience method).\n *\n * @param id - Either a UUID string or an existing FileEntity object\n * @returns The file entity\n * @throws {NotFoundError} If the file doesn't exist in the database\n */\n public async getFileById(id: string | FileEntity): Promise<FileEntity> {\n if (typeof id === \"object\") {\n return id;\n }\n\n return await this.fileRepository.getById(id);\n }\n\n /**\n * Gets storage statistics including total size, file count, and breakdowns by bucket and MIME type.\n *\n * Aggregated in SQL (`SUM`/`COUNT` + `GROUP BY`) rather than by loading\n * every row into memory — the table can hold millions of files, and this\n * endpoint must stay O(groups), not O(rows). Totals are derived from the\n * per-bucket groups (every row has exactly one bucket), so no extra query\n * is needed.\n *\n * @returns Storage statistics with aggregated data\n */\n public async getStorageStats(): Promise<StorageStats> {\n const [byBucketRows, byMimeTypeRows] = await Promise.all([\n this.fileRepository.aggregate({\n select: { bucket: true, size: { sum: true, count: true } },\n groupBy: [\"bucket\"],\n }),\n this.fileRepository.aggregate({\n select: { mimeType: true, size: { count: true } },\n groupBy: [\"mimeType\"],\n }),\n ]);\n\n const byBucket = byBucketRows.map((row) => ({\n bucket: row.bucket,\n totalSize: row.size.sum,\n fileCount: row.size.count,\n }));\n\n const byMimeType = byMimeTypeRows.map((row) => ({\n mimeType: row.mimeType,\n fileCount: row.size.count,\n }));\n\n return {\n totalSize: byBucket.reduce((sum, b) => sum + b.totalSize, 0),\n totalFiles: byBucket.reduce((sum, b) => sum + b.fileCount, 0),\n byBucket,\n byMimeType,\n };\n }\n\n /**\n * Converts a file entity to a file resource (API response format).\n * Currently a pass-through, but allows for future transformation logic.\n *\n * @param entity - The file entity to convert\n * @returns The file resource for API responses\n */\n public entityToResource(entity: FileEntity): FileResource {\n return entity;\n }\n}\n","import { $inject } from \"alepha\";\nimport { $secure } from \"alepha/security\";\nimport { $action } from \"alepha/server\";\nimport { storageStatsSchema } from \"../schemas/storageStatsSchema.ts\";\nimport { FileService } from \"../services/FileService.ts\";\n\n/**\n * REST API controller for storage analytics and statistics.\n * Provides endpoints for viewing storage usage metrics.\n */\nexport class AdminFileStatsController {\n protected readonly url = \"/files/stats\";\n protected readonly group = \"admin:files\";\n protected readonly fileService = $inject(FileService);\n\n /**\n * GET /files/stats - Gets storage statistics.\n * Returns aggregated data including total size, file count,\n * and breakdowns by bucket and MIME type.\n */\n public readonly getFileStats = $action({\n path: this.url,\n group: this.group,\n use: [$secure({ permissions: [\"admin:file:read\"] })],\n description: \"Get storage statistics\",\n schema: {\n response: storageStatsSchema,\n },\n handler: () => this.fileService.getStorageStats(),\n });\n}\n","import type { UserAccountToken } from \"alepha/security\";\nimport { ForbiddenError, NotFoundError } from \"alepha/server\";\nimport type { FileEntity } from \"../entities/files.ts\";\n\n/**\n * Authorization policy for file reads served through `FileController.streamFile`.\n *\n * Default: the caller must be the uploader (`file.creator === user.id`). Any\n * other access path — public buckets, shared attachments, avatars — must be\n * opted in by overriding this provider in the consuming app:\n *\n * ```ts\n * class MyAccess extends FileAccessProvider {\n * async assertReadable(file, user) {\n * if (file.bucket === \"avatars\") return; // public\n * if (file.bucket === \"campaign-icons\") return this.checkCampaignVisible(file, user);\n * return super.assertReadable(file, user);\n * }\n * }\n * Alepha.create().with({ provide: FileAccessProvider, use: MyAccess });\n * ```\n *\n * Why this exists: prior to introducing this gate, `streamFile` only required\n * the framework-wide `file:read` permission. The default `user` role grants\n * `*`, so every authenticated user could download any file by UUID — turning\n * the 128-bit id into the sole security boundary across tenants.\n */\nexport class FileAccessProvider {\n /**\n * Throws `ForbiddenError` when `user` may not read `file`. Override to\n * implement bucket-aware or relationship-aware policies. The default\n * implementation is strict: only the uploader passes.\n */\n async assertReadable(\n file: FileEntity,\n user: UserAccountToken | undefined,\n ): Promise<void> {\n if (!user) {\n throw new ForbiddenError(\"File access requires authentication\");\n }\n // Privileged identities (admin without narrow ownership scope) bypass\n // the per-file check — they need to be able to read any file via the\n // admin UI/inspector regardless of who uploaded it.\n if (user.ownership === false) {\n return;\n }\n if (file.creator && file.creator === user.id) {\n return;\n }\n throw new ForbiddenError(\"File access denied\");\n }\n\n /**\n * Throws when `file` may not be served through the anonymous\n * `/public/files/:id` route. The default is deny-all: nothing is public\n * unless this method is overridden. Throws `NotFoundError` (not 403) so the\n * endpoint doesn't leak the existence of private file ids.\n *\n * Typical override matches on bucket name:\n *\n * ```ts\n * class MyAccess extends FileAccessProvider {\n * async assertPublic(file) {\n * if (file.bucket === \"avatars\") return;\n * if (file.bucket === \"campaign-icons\") return;\n * return super.assertPublic(file);\n * }\n * }\n * ```\n */\n async assertPublic(file: FileEntity): Promise<void> {\n throw new NotFoundError(`File '${file.id}' not found`);\n }\n}\n","import type { Static } from \"alepha\";\nimport { z } from \"alepha\";\nimport { pageQuerySchema } from \"alepha/orm\";\n\nexport const fileQuerySchema = pageQuerySchema.extend({\n bucket: z.string().optional(),\n tags: z.array(z.string()).optional(),\n name: z.string().optional(),\n mimeType: z.string().optional(),\n creator: z.uuid().optional(),\n createdAfter: z.datetime().optional(),\n createdBefore: z.datetime().optional(),\n});\n\nexport type FileQuery = Static<typeof fileQuerySchema>;\n","import { type Static, z } from \"alepha\";\n\n/**\n * Slim view of a file's uploader, embedded by the admin listing via a\n * best-effort left join (`files.creator` → `users.id`) so the UI can render\n * a human-readable identifier instead of a bare UUID.\n *\n * Optional end-to-end: the join only runs when the `users` entity is\n * registered in the running app (see `FileService.findFiles`), and even then\n * a file whose creator was deleted — or who lives in a non-default realm —\n * comes back with `user` undefined. Callers must fall back to `creatorName`\n * or the raw `creator` id.\n */\nexport const fileCreatorSummarySchema = z.object({\n id: z.uuid(),\n email: z.string().meta({ format: \"email\" }).optional(),\n username: z.shortText({ minLength: 3, maxLength: 30 }).optional(),\n firstName: z.string().optional(),\n lastName: z.string().optional(),\n});\n\nexport type FileCreatorSummary = Static<typeof fileCreatorSummarySchema>;\n","import type { Static } from \"alepha\";\nimport { files } from \"../entities/files.ts\";\nimport { fileCreatorSummarySchema } from \"./fileCreatorSummarySchema.ts\";\n\nexport const fileResourceSchema = files.schema\n .extend({\n /**\n * Uploader summary, embedded by the admin listing via a best-effort\n * left join. Optional — see `fileCreatorSummarySchema`. Distinct from\n * the denormalized `creatorName`, which is set only on direct uploads\n * and never carries an email.\n */\n user: fileCreatorSummarySchema.optional(),\n })\n .meta({\n title: \"FileResource\",\n description: \"A file resource representing a file stored in the system.\",\n });\n\nexport type FileResource = Static<typeof fileResourceSchema>;\n","import { $inject, z } from \"alepha\";\nimport { $secure } from \"alepha/security\";\nimport { $action, okSchema } from \"alepha/server\";\nimport { $etag } from \"alepha/server/etag\";\nimport { FileAccessProvider } from \"../providers/FileAccessProvider.ts\";\nimport { fileQuerySchema } from \"../schemas/fileQuerySchema.ts\";\nimport { fileResourceSchema } from \"../schemas/fileResourceSchema.ts\";\nimport { FileService } from \"../services/FileService.ts\";\n\n/**\n * REST API controller for file management operations.\n * Provides endpoints for uploading, downloading, listing, and deleting files.\n */\nexport class FileController {\n protected readonly url = \"/files\";\n protected readonly group = \"files\";\n protected readonly fileService = $inject(FileService);\n protected readonly fileAccess = $inject(FileAccessProvider);\n\n /**\n * GET /files - Lists files with optional filtering and pagination.\n * Supports filtering by bucket and tags.\n */\n public readonly findFiles = $action({\n path: this.url,\n group: `admin:${this.group}`,\n use: [$secure({ permissions: [\"admin:file:read\"] })],\n description: \"List files with filtering and pagination\",\n schema: {\n query: fileQuerySchema,\n response: z.page(fileResourceSchema),\n },\n handler: ({ query }) => this.fileService.findFiles(query),\n });\n\n /**\n * DELETE /files/:id - Deletes a file from both storage and database.\n * Removes the file from the bucket and cleans up the database record.\n */\n public readonly deleteFile = $action({\n method: \"DELETE\",\n path: `${this.url}/:id`,\n group: `admin:${this.group}`,\n use: [$secure({ permissions: [\"admin:file:delete\"] })],\n description: \"Delete a file\",\n schema: {\n params: z.object({\n id: z.uuid(),\n }),\n response: okSchema,\n },\n handler: ({ params }) => this.fileService.deleteFile(params.id),\n });\n\n /**\n * POST /files/delete - Delete many files in one request, batching the\n * underlying bucket calls per bucket (R2/S3 batch where supported).\n */\n public readonly deleteFiles = $action({\n method: \"POST\",\n path: `${this.url}/delete`,\n group: `admin:${this.group}`,\n use: [$secure({ permissions: [\"admin:file:delete\"] })],\n description: \"Delete many files\",\n schema: {\n body: z.object({\n ids: z.array(z.uuid()).min(1).max(1000),\n }),\n response: z.object({\n deleted: z.array(z.string()),\n }),\n },\n handler: async ({ body }) => {\n const deleted = await this.fileService.deleteFiles(body.ids);\n return { deleted };\n },\n });\n\n /**\n * POST /files - Uploads a new file to storage.\n * Creates a database record with metadata and calculates checksum.\n * Optionally specify bucket and expiration date.\n */\n public readonly uploadFile = $action({\n path: this.url,\n group: this.group,\n use: [$secure({ permissions: [\"file:create\"] })],\n description: \"Upload a new file\",\n schema: {\n body: z.object({\n file: z.file(),\n }),\n query: z.object({\n expirationDate: z.datetime().optional(),\n bucket: z.string().optional(),\n }),\n response: fileResourceSchema,\n },\n handler: async ({ body, user, query }) =>\n this.fileService.uploadFile(body.file, {\n user,\n ...query,\n }),\n });\n\n /**\n * PATCH /files/:id - Updates file metadata.\n * Allows updating name, tags, and expiration date without modifying file content.\n */\n public readonly updateFile = $action({\n method: \"PATCH\",\n path: `${this.url}/:id`,\n group: `admin:${this.group}`,\n use: [$secure({ permissions: [\"admin:file:update\"] })],\n description: \"Update file metadata\",\n schema: {\n params: z.object({\n id: z.uuid(),\n }),\n body: z.object({\n name: z.string().optional(),\n tags: z.array(z.string()).optional(),\n expirationDate: z.datetime().optional(),\n }),\n response: fileResourceSchema,\n },\n handler: ({ params, body }) => this.fileService.updateFile(params.id, body),\n });\n\n /**\n * GET /files/:id - Streams/downloads a file by its ID.\n * Returns the file content with appropriate Content-Type header.\n *\n * Authorization is delegated to `FileAccessProvider.assertReadable`. The\n * default policy is creator-only — override the provider via DI to widen\n * access (e.g. avatars, shared attachments). See `FileAccessProvider`.\n *\n * Cache-Control is `private` because the per-user authorization decision\n * cannot be cached by shared proxies/CDNs. Client-side ETag still works.\n */\n public readonly streamFile = $action({\n path: `${this.url}/:id`,\n group: this.group,\n description: \"Download a file\",\n use: [\n $secure({ permissions: [\"file:read\"] }),\n $etag({\n control: {\n private: true,\n maxAge: [1, \"year\"],\n immutable: true,\n },\n }),\n ],\n schema: {\n params: z.object({\n id: z.uuid(),\n }),\n response: z.file(),\n },\n handler: async ({ params, user }) => {\n const file = await this.fileService.getFileById(params.id);\n await this.fileAccess.assertReadable(file, user);\n return await this.fileService.streamFile(file);\n },\n });\n\n /**\n * GET /public/files/:id - Anonymous, edge-cacheable download.\n *\n * Authorization is delegated to `FileAccessProvider.assertPublic`. The\n * default policy is deny-all (throws `NotFoundError`), so consuming apps\n * must override the provider to opt files in — typically by bucket name\n * (avatars, campaign icons, etc.).\n *\n * Cache-Control is `public, immutable, max-age=1y` so Cloudflare's edge\n * cache and any intermediary proxy can serve subsequent hits without\n * touching the Worker. The split URL prefix (vs `/files/:id`) is what\n * makes this safe: edge cache is URL-keyed, so public and private files\n * live in separate cache lanes.\n */\n public readonly streamPublicFile = $action({\n path: \"/public/files/:id\",\n group: this.group,\n description: \"Download a public file (anonymous, edge-cacheable)\",\n use: [\n $etag({\n control: {\n public: true,\n maxAge: [1, \"year\"],\n immutable: true,\n },\n }),\n ],\n schema: {\n params: z.object({\n id: z.uuid(),\n }),\n response: z.file(),\n },\n handler: async ({ params }) => {\n const file = await this.fileService.getFileById(params.id);\n await this.fileAccess.assertPublic(file);\n return await this.fileService.streamFile(file);\n },\n });\n}\n","import { $inject } from \"alepha\";\nimport { $scheduler } from \"alepha/scheduler\";\nimport { FileService } from \"../services/FileService.ts\";\n\nexport class FileJobs {\n protected readonly fileService = $inject(FileService);\n\n public readonly purgeFiles = $scheduler({\n name: \"api:files:purgeFiles\",\n description: \"Purge files that are marked for deletion\",\n cron: \"0 * * * *\", // Hourly at minute 0\n handler: async () => {\n const files = await this.fileService.findExpiredFiles();\n\n await Promise.all(\n files.map((file) => this.fileService.deleteFile(file.id)),\n );\n },\n });\n}\n","import { $module } from \"alepha\";\nimport { AlephaBucket } from \"alepha/bucket\";\nimport type { DurationLike } from \"alepha/datetime\";\nimport type { UserAccountToken } from \"alepha/security\";\nimport { AlephaServerEtag } from \"alepha/server/etag\";\nimport { AdminFileStatsController } from \"./controllers/AdminFileStatsController.ts\";\nimport { FileController } from \"./controllers/FileController.ts\";\nimport { FileJobs } from \"./jobs/FileJobs.ts\";\nimport { FileAccessProvider } from \"./providers/FileAccessProvider.ts\";\nimport { FileService } from \"./services/FileService.ts\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport * from \"./controllers/AdminFileStatsController.ts\";\nexport * from \"./controllers/FileController.ts\";\nexport * from \"./entities/files.ts\";\nexport * from \"./jobs/FileJobs.ts\";\nexport * from \"./providers/FileAccessProvider.ts\";\nexport * from \"./schemas/fileCreatorSummarySchema.ts\";\nexport * from \"./schemas/fileQuerySchema.ts\";\nexport * from \"./schemas/fileResourceSchema.ts\";\nexport * from \"./schemas/storageStatsSchema.ts\";\nexport * from \"./services/FileService.ts\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\ndeclare module \"alepha/bucket\" {\n interface BucketFileOptions {\n /**\n * Time to live for the files in the bucket.\n */\n ttl?: DurationLike;\n\n /**\n * Tags for the bucket.\n */\n tags?: string[];\n\n /**\n * User performing the operation.\n */\n user?: UserAccountToken;\n\n /**\n * Whether to persist the file metadata in the database.\n *\n * @default true\n */\n persist?: boolean;\n }\n}\n\n// ---------------------------------------------------------------------------------------------------------------------\n\n/**\n * File management endpoints.\n *\n * **Features:**\n * - Upload/download endpoints\n * - File metadata storage\n * - TTL-based expiration\n * - Storage statistics\n *\n * @module alepha.api.files\n */\nexport const AlephaApiFiles = $module({\n name: \"alepha.api.files\",\n services: [\n FileController,\n AdminFileStatsController,\n FileJobs,\n FileService,\n FileAccessProvider,\n ],\n imports: [AlephaBucket, AlephaServerEtag],\n});\n"],"mappings":"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"}