alepha 0.20.5 → 0.20.7

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 (367) hide show
  1. package/AGENTS.md +0 -1
  2. package/CLAUDE.md +0 -1
  3. package/assets/agents-template.md +0 -1
  4. package/dist/api/audits/index.browser.js +1 -0
  5. package/dist/api/audits/index.browser.js.map +1 -1
  6. package/dist/api/audits/index.d.ts +701 -654
  7. package/dist/api/audits/index.d.ts.map +1 -1
  8. package/dist/api/audits/index.js +24 -1
  9. package/dist/api/audits/index.js.map +1 -1
  10. package/dist/api/files/index.browser.js +1 -0
  11. package/dist/api/files/index.browser.js.map +1 -1
  12. package/dist/api/files/index.d.ts +193 -166
  13. package/dist/api/files/index.d.ts.map +1 -1
  14. package/dist/api/files/index.js +52 -0
  15. package/dist/api/files/index.js.map +1 -1
  16. package/dist/api/jobs/index.browser.js +40 -14
  17. package/dist/api/jobs/index.browser.js.map +1 -1
  18. package/dist/api/jobs/index.d.ts +639 -333
  19. package/dist/api/jobs/index.d.ts.map +1 -1
  20. package/dist/api/jobs/index.js +495 -162
  21. package/dist/api/jobs/index.js.map +1 -1
  22. package/dist/api/keys/index.d.ts +222 -188
  23. package/dist/api/keys/index.d.ts.map +1 -1
  24. package/dist/api/keys/index.js +54 -0
  25. package/dist/api/keys/index.js.map +1 -1
  26. package/dist/api/notifications/index.d.ts +265 -236
  27. package/dist/api/notifications/index.d.ts.map +1 -1
  28. package/dist/api/notifications/index.js +55 -13
  29. package/dist/api/notifications/index.js.map +1 -1
  30. package/dist/api/organizations/index.d.ts +100 -97
  31. package/dist/api/organizations/index.d.ts.map +1 -1
  32. package/dist/api/organizations/index.js.map +1 -1
  33. package/dist/api/parameters/index.d.ts +332 -314
  34. package/dist/api/parameters/index.d.ts.map +1 -1
  35. package/dist/api/parameters/index.js +37 -0
  36. package/dist/api/parameters/index.js.map +1 -1
  37. package/dist/api/payments/index.d.ts +431 -376
  38. package/dist/api/payments/index.d.ts.map +1 -1
  39. package/dist/api/payments/index.js +202 -87
  40. package/dist/api/payments/index.js.map +1 -1
  41. package/dist/api/subscriptions/index.d.ts +1695 -0
  42. package/dist/api/subscriptions/index.d.ts.map +1 -0
  43. package/dist/api/subscriptions/index.js +1919 -0
  44. package/dist/api/subscriptions/index.js.map +1 -0
  45. package/dist/api/users/index.d.ts +1001 -844
  46. package/dist/api/users/index.d.ts.map +1 -1
  47. package/dist/api/users/index.js +237 -28
  48. package/dist/api/users/index.js.map +1 -1
  49. package/dist/api/verifications/index.d.ts +123 -122
  50. package/dist/api/verifications/index.d.ts.map +1 -1
  51. package/dist/api/verifications/index.js.map +1 -1
  52. package/dist/batch/index.js.map +1 -1
  53. package/dist/bucket/index.d.ts +21 -2
  54. package/dist/bucket/index.d.ts.map +1 -1
  55. package/dist/bucket/index.js +47 -0
  56. package/dist/bucket/index.js.map +1 -1
  57. package/dist/bucket/index.workerd.js +24 -0
  58. package/dist/bucket/index.workerd.js.map +1 -1
  59. package/dist/cache/core/index.d.ts +134 -7
  60. package/dist/cache/core/index.d.ts.map +1 -1
  61. package/dist/cache/core/index.js +181 -15
  62. package/dist/cache/core/index.js.map +1 -1
  63. package/dist/cache/core/index.workerd.js +181 -15
  64. package/dist/cache/core/index.workerd.js.map +1 -1
  65. package/dist/cache/database/index.d.ts +156 -0
  66. package/dist/cache/database/index.d.ts.map +1 -0
  67. package/dist/cache/database/index.js +266 -0
  68. package/dist/cache/database/index.js.map +1 -0
  69. package/dist/cache/redis/index.d.ts +3 -2
  70. package/dist/cache/redis/index.d.ts.map +1 -1
  71. package/dist/cache/redis/index.js.map +1 -1
  72. package/dist/captcha/index.js.map +1 -1
  73. package/dist/cli/config/index.js.map +1 -1
  74. package/dist/cli/core/index.d.ts +142 -128
  75. package/dist/cli/core/index.d.ts.map +1 -1
  76. package/dist/cli/core/index.js +160 -13
  77. package/dist/cli/core/index.js.map +1 -1
  78. package/dist/cli/devtools/index.d.ts +3 -2
  79. package/dist/cli/devtools/index.d.ts.map +1 -1
  80. package/dist/cli/devtools/index.js.map +1 -1
  81. package/dist/cli/platform/index.d.ts +346 -290
  82. package/dist/cli/platform/index.d.ts.map +1 -1
  83. package/dist/cli/platform/index.js +106 -7
  84. package/dist/cli/platform/index.js.map +1 -1
  85. package/dist/cli/vendor/index.d.ts +12 -11
  86. package/dist/cli/vendor/index.d.ts.map +1 -1
  87. package/dist/cli/vendor/index.js.map +1 -1
  88. package/dist/command/index.d.ts +6 -5
  89. package/dist/command/index.d.ts.map +1 -1
  90. package/dist/command/index.js.map +1 -1
  91. package/dist/core/index.browser.js +1 -1
  92. package/dist/core/index.browser.js.map +1 -1
  93. package/dist/core/index.d.ts +119 -118
  94. package/dist/core/index.d.ts.map +1 -1
  95. package/dist/core/index.js +1 -1
  96. package/dist/core/index.js.map +1 -1
  97. package/dist/core/index.native.js +1 -1
  98. package/dist/core/index.native.js.map +1 -1
  99. package/dist/core/index.workerd.js +1 -1
  100. package/dist/core/index.workerd.js.map +1 -1
  101. package/dist/crypto/index.browser.js.map +1 -1
  102. package/dist/crypto/index.d.ts +3 -2
  103. package/dist/crypto/index.d.ts.map +1 -1
  104. package/dist/crypto/index.js.map +1 -1
  105. package/dist/datetime/index.js.map +1 -1
  106. package/dist/email/brevo/index.js.map +1 -1
  107. package/dist/email/core/index.d.ts +3 -2
  108. package/dist/email/core/index.d.ts.map +1 -1
  109. package/dist/email/core/index.js.map +1 -1
  110. package/dist/email/core/index.workerd.js.map +1 -1
  111. package/dist/email/smtp/index.d.ts +7 -6
  112. package/dist/email/smtp/index.d.ts.map +1 -1
  113. package/dist/email/smtp/index.js.map +1 -1
  114. package/dist/fake/index.js.map +1 -1
  115. package/dist/lock/core/index.d.ts +5 -4
  116. package/dist/lock/core/index.d.ts.map +1 -1
  117. package/dist/lock/core/index.js.map +1 -1
  118. package/dist/lock/redis/index.js.map +1 -1
  119. package/dist/logger/index.d.ts +10 -9
  120. package/dist/logger/index.d.ts.map +1 -1
  121. package/dist/logger/index.js.map +1 -1
  122. package/dist/mcp/index.d.ts +9 -8
  123. package/dist/mcp/index.d.ts.map +1 -1
  124. package/dist/mcp/index.js +1 -1
  125. package/dist/mcp/index.js.map +1 -1
  126. package/dist/orm/core/index.browser.js +9 -3
  127. package/dist/orm/core/index.browser.js.map +1 -1
  128. package/dist/orm/core/index.bun.js +31 -10
  129. package/dist/orm/core/index.bun.js.map +1 -1
  130. package/dist/orm/core/index.d.ts +33 -14
  131. package/dist/orm/core/index.d.ts.map +1 -1
  132. package/dist/orm/core/index.js +31 -10
  133. package/dist/orm/core/index.js.map +1 -1
  134. package/dist/orm/postgres/index.bun.js.map +1 -1
  135. package/dist/orm/postgres/index.d.ts +6 -5
  136. package/dist/orm/postgres/index.d.ts.map +1 -1
  137. package/dist/orm/postgres/index.js.map +1 -1
  138. package/dist/queue/core/index.d.ts +5 -4
  139. package/dist/queue/core/index.d.ts.map +1 -1
  140. package/dist/queue/core/index.js.map +1 -1
  141. package/dist/queue/core/index.workerd.js.map +1 -1
  142. package/dist/queue/redis/index.d.ts +3 -2
  143. package/dist/queue/redis/index.d.ts.map +1 -1
  144. package/dist/queue/redis/index.js.map +1 -1
  145. package/dist/react/auth/index.browser.js.map +1 -1
  146. package/dist/react/auth/index.js.map +1 -1
  147. package/dist/react/core/index.js.map +1 -1
  148. package/dist/react/form/index.d.ts +5 -0
  149. package/dist/react/form/index.d.ts.map +1 -1
  150. package/dist/react/form/index.js +8 -4
  151. package/dist/react/form/index.js.map +1 -1
  152. package/dist/react/head/index.browser.js.map +1 -1
  153. package/dist/react/head/index.js.map +1 -1
  154. package/dist/react/i18n/index.d.ts +2 -1
  155. package/dist/react/i18n/index.d.ts.map +1 -1
  156. package/dist/react/i18n/index.js.map +1 -1
  157. package/dist/react/intro/index.js.map +1 -1
  158. package/dist/react/router/index.browser.js.map +1 -1
  159. package/dist/react/router/index.d.ts +206 -205
  160. package/dist/react/router/index.d.ts.map +1 -1
  161. package/dist/react/router/index.js.map +1 -1
  162. package/dist/react/testing/index.js.map +1 -1
  163. package/dist/react/ui/index.d.ts +11 -11
  164. package/dist/react/ui/index.d.ts.map +1 -1
  165. package/dist/react/ui/index.js.map +1 -1
  166. package/dist/redis/index.bun.js.map +1 -1
  167. package/dist/redis/index.js.map +1 -1
  168. package/dist/retry/index.js.map +1 -1
  169. package/dist/router/index.js.map +1 -1
  170. package/dist/scheduler/index.d.ts +25 -2
  171. package/dist/scheduler/index.d.ts.map +1 -1
  172. package/dist/scheduler/index.js +12 -0
  173. package/dist/scheduler/index.js.map +1 -1
  174. package/dist/scheduler/index.workerd.js +12 -0
  175. package/dist/scheduler/index.workerd.js.map +1 -1
  176. package/dist/security/index.browser.js +29 -1
  177. package/dist/security/index.browser.js.map +1 -1
  178. package/dist/security/index.d.ts +82 -35
  179. package/dist/security/index.d.ts.map +1 -1
  180. package/dist/security/index.js +56 -3
  181. package/dist/security/index.js.map +1 -1
  182. package/dist/server/auth/index.d.ts +163 -158
  183. package/dist/server/auth/index.d.ts.map +1 -1
  184. package/dist/server/auth/index.js +16 -4
  185. package/dist/server/auth/index.js.map +1 -1
  186. package/dist/server/cookies/index.browser.js.map +1 -1
  187. package/dist/server/cookies/index.js.map +1 -1
  188. package/dist/server/core/index.browser.js.map +1 -1
  189. package/dist/server/core/index.d.ts +35 -34
  190. package/dist/server/core/index.d.ts.map +1 -1
  191. package/dist/server/core/index.js.map +1 -1
  192. package/dist/server/cors/index.d.ts +7 -6
  193. package/dist/server/cors/index.d.ts.map +1 -1
  194. package/dist/server/cors/index.js.map +1 -1
  195. package/dist/server/etag/index.js.map +1 -1
  196. package/dist/server/health/index.d.ts +16 -15
  197. package/dist/server/health/index.d.ts.map +1 -1
  198. package/dist/server/health/index.js.map +1 -1
  199. package/dist/server/links/index.browser.js.map +1 -1
  200. package/dist/server/links/index.d.ts +51 -50
  201. package/dist/server/links/index.d.ts.map +1 -1
  202. package/dist/server/links/index.js.map +1 -1
  203. package/dist/server/metrics/index.js.map +1 -1
  204. package/dist/server/proxy/index.js.map +1 -1
  205. package/dist/server/rate-limit/index.d.ts +6 -5
  206. package/dist/server/rate-limit/index.d.ts.map +1 -1
  207. package/dist/server/rate-limit/index.js.map +1 -1
  208. package/dist/server/static/index.js.map +1 -1
  209. package/dist/server/swagger/index.d.ts +2 -1
  210. package/dist/server/swagger/index.d.ts.map +1 -1
  211. package/dist/server/swagger/index.js.map +1 -1
  212. package/dist/sms/index.js.map +1 -1
  213. package/dist/system/index.browser.js.map +1 -1
  214. package/dist/system/index.js.map +1 -1
  215. package/dist/system/index.workerd.js.map +1 -1
  216. package/dist/topic/core/index.js.map +1 -1
  217. package/dist/topic/redis/index.d.ts +3 -2
  218. package/dist/topic/redis/index.d.ts.map +1 -1
  219. package/dist/topic/redis/index.js.map +1 -1
  220. package/package.json +33 -39
  221. package/src/api/audits/controllers/AdminAuditController.ts +29 -0
  222. package/src/api/audits/entities/audits.ts +1 -0
  223. package/src/api/files/controllers/FileController.ts +24 -0
  224. package/src/api/files/entities/files.ts +1 -0
  225. package/src/api/files/services/FileService.ts +41 -0
  226. package/src/api/jobs/__tests__/$job.spec.ts +501 -24
  227. package/src/api/jobs/entities/jobExecutionEntity.ts +4 -3
  228. package/src/api/jobs/index.ts +47 -10
  229. package/src/api/jobs/primitives/$job.ts +22 -9
  230. package/src/api/jobs/providers/DirectJobDispatcher.ts +71 -0
  231. package/src/api/jobs/providers/JobDispatcher.ts +49 -0
  232. package/src/api/jobs/providers/JobProvider.ts +385 -147
  233. package/src/api/jobs/providers/JobQueueProvider.ts +43 -18
  234. package/src/api/jobs/schemas/jobConfigAtom.ts +9 -3
  235. package/src/api/jobs/schemas/jobExecutionResourceSchema.ts +11 -0
  236. package/src/api/jobs/schemas/jobRegistrationSchema.ts +4 -2
  237. package/src/api/jobs/services/JobService.ts +21 -11
  238. package/src/api/keys/controllers/AdminApiKeyController.ts +23 -0
  239. package/src/api/keys/entities/apiKeyEntity.ts +1 -0
  240. package/src/api/keys/services/ApiKeyService.ts +42 -0
  241. package/src/api/notifications/__tests__/AlephaApiNotifications.spec.ts +63 -0
  242. package/src/api/notifications/controllers/AdminNotificationController.ts +48 -1
  243. package/src/api/notifications/index.ts +13 -3
  244. package/src/api/notifications/jobs/NotificationJobs.ts +0 -6
  245. package/src/api/parameters/controllers/AdminParameterController.ts +26 -0
  246. package/src/api/parameters/services/ParameterProvider.ts +18 -0
  247. package/src/api/payments/controllers/MockCheckoutController.ts +146 -0
  248. package/src/api/payments/index.ts +3 -0
  249. package/src/api/payments/providers/MemoryPaymentProvider.ts +9 -4
  250. package/src/api/payments/providers/PaymentProvider.ts +25 -9
  251. package/src/api/payments/services/PaymentService.ts +3 -0
  252. package/src/api/subscriptions/__tests__/BillingService.spec.ts +218 -0
  253. package/src/api/subscriptions/__tests__/SubscriptionService.spec.ts +278 -0
  254. package/src/api/subscriptions/controllers/AdminSubscriptionController.ts +212 -0
  255. package/src/api/subscriptions/controllers/SubscriptionController.ts +189 -0
  256. package/src/api/subscriptions/entities/subscriptionEvents.ts +54 -0
  257. package/src/api/subscriptions/entities/subscriptions.ts +68 -0
  258. package/src/api/subscriptions/index.ts +133 -0
  259. package/src/api/subscriptions/jobs/SubscriptionJobs.ts +382 -0
  260. package/src/api/subscriptions/middleware/$requireLimit.ts +50 -0
  261. package/src/api/subscriptions/middleware/$requirePlan.ts +49 -0
  262. package/src/api/subscriptions/notifications/SubscriptionNotifications.ts +110 -0
  263. package/src/api/subscriptions/schemas/cancelSubscriptionSchema.ts +8 -0
  264. package/src/api/subscriptions/schemas/changePlanSchema.ts +9 -0
  265. package/src/api/subscriptions/schemas/createSubscriptionSchema.ts +11 -0
  266. package/src/api/subscriptions/schemas/entitlementsSchema.ts +21 -0
  267. package/src/api/subscriptions/schemas/mrrSchema.ts +13 -0
  268. package/src/api/subscriptions/schemas/planDefinitionSchema.ts +71 -0
  269. package/src/api/subscriptions/schemas/planResourceSchema.ts +25 -0
  270. package/src/api/subscriptions/schemas/subscriptionEventResourceSchema.ts +8 -0
  271. package/src/api/subscriptions/schemas/subscriptionQuerySchema.ts +19 -0
  272. package/src/api/subscriptions/schemas/subscriptionResourceSchema.ts +6 -0
  273. package/src/api/subscriptions/schemas/subscriptionSettingsSchema.ts +32 -0
  274. package/src/api/subscriptions/schemas/subscriptionStatsSchema.ts +23 -0
  275. package/src/api/subscriptions/services/BillingService.ts +437 -0
  276. package/src/api/subscriptions/services/SubscriptionConfig.ts +56 -0
  277. package/src/api/subscriptions/services/SubscriptionService.ts +867 -0
  278. package/src/api/subscriptions/services/UsageService.ts +118 -0
  279. package/src/api/users/__tests__/Registration-emailMode.spec.ts +203 -0
  280. package/src/api/users/__tests__/UsernameSlugger.spec.ts +138 -0
  281. package/src/api/users/atoms/realmAuthSettingsAtom.ts +41 -3
  282. package/src/api/users/controllers/AdminSessionController.ts +29 -0
  283. package/src/api/users/controllers/AdminUserController.ts +32 -0
  284. package/src/api/users/index.ts +3 -0
  285. package/src/api/users/services/CredentialService.ts +5 -0
  286. package/src/api/users/services/RegistrationService.ts +49 -1
  287. package/src/api/users/services/SessionCrudService.ts +16 -0
  288. package/src/api/users/services/SessionService.ts +17 -59
  289. package/src/api/users/services/UsernameSlugger.ts +195 -0
  290. package/src/bucket/primitives/$bucket.ts +21 -0
  291. package/src/bucket/providers/CloudflareR2Provider.ts +15 -0
  292. package/src/bucket/providers/FileStorageProvider.ts +9 -0
  293. package/src/bucket/providers/LocalFileStorageProvider.ts +14 -0
  294. package/src/bucket/providers/MemoryFileStorageProvider.ts +9 -0
  295. package/src/bucket/providers/NodeS3BucketProvider.ts +35 -0
  296. package/src/cache/core/__tests__/$cache.memory.spec.ts +450 -0
  297. package/src/cache/core/__tests__/$cache.swr.spec.ts +394 -0
  298. package/src/cache/core/index.ts +16 -0
  299. package/src/cache/core/primitives/$cache.ts +367 -24
  300. package/src/cache/database/__tests__/DatabaseCacheProvider.behavior.spec.ts +203 -0
  301. package/src/cache/database/__tests__/DatabaseCacheProvider.spec.ts +110 -0
  302. package/src/cache/database/entities/cacheEntries.ts +55 -0
  303. package/src/cache/database/index.ts +36 -0
  304. package/src/cache/database/providers/DatabaseCacheProvider.ts +348 -0
  305. package/src/cli/core/services/ProjectScaffolder.ts +0 -2
  306. package/src/cli/core/tasks/BuildCloudflareTask.ts +33 -3
  307. package/src/cli/core/tasks/BuildSitemapTask.ts +7 -0
  308. package/src/cli/core/tasks/BuildVercelTask.ts +82 -3
  309. package/src/cli/core/templates/agentMd.ts +39 -4
  310. package/src/cli/core/templates/biomeJson.ts +25 -1
  311. package/src/cli/core/templates/saasAdminLayoutTsx.ts +2 -2
  312. package/src/cli/platform/__tests__/CloudflareAdapter.spec.ts +117 -0
  313. package/src/cli/platform/__tests__/detectResources.spec.ts +96 -0
  314. package/src/cli/platform/adapters/CloudflareAdapter.ts +104 -7
  315. package/src/cli/platform/atoms/platformOptions.ts +13 -0
  316. package/src/cli/platform/commands/platform.ts +7 -1
  317. package/src/cli/platform/schemas/platform.ts +1 -0
  318. package/src/cli/platform/services/CloudflareApi.ts +61 -0
  319. package/src/cli/platform/services/PlatformOrchestrator.ts +9 -4
  320. package/src/core/__tests__/$module.spec.ts +2 -2
  321. package/src/core/primitives/$module.ts +4 -4
  322. package/src/mcp/providers/McpServerProvider.ts +1 -1
  323. package/src/orm/core/providers/DatabaseTypeProvider.ts +9 -3
  324. package/src/orm/core/providers/drivers/DatabaseProvider.ts +1 -1
  325. package/src/orm/core/schemas/insertSchema.ts +10 -2
  326. package/src/orm/core/services/Repository.ts +27 -7
  327. package/src/react/form/hooks/useFormState.ts +8 -1
  328. package/src/react/form/index.ts +10 -1
  329. package/src/react/form/services/FormModel.ts +9 -3
  330. package/src/scheduler/index.ts +14 -0
  331. package/src/scheduler/providers/CronProvider.ts +13 -0
  332. package/src/security/atoms/currentTenantAtom.ts +34 -0
  333. package/src/security/index.browser.ts +1 -0
  334. package/src/security/index.ts +12 -1
  335. package/src/security/primitives/$issuer.ts +17 -1
  336. package/src/security/providers/SecurityProvider.ts +37 -0
  337. package/src/server/auth/__tests__/validateRedirectUri.spec.ts +78 -0
  338. package/src/server/auth/providers/ServerAuthProvider.ts +21 -5
  339. package/tsconfig.base.json +2 -1
  340. package/dist/react/websocket/index.d.ts +0 -117
  341. package/dist/react/websocket/index.d.ts.map +0 -1
  342. package/dist/react/websocket/index.js +0 -108
  343. package/dist/react/websocket/index.js.map +0 -1
  344. package/dist/websocket/index.browser.js +0 -844
  345. package/dist/websocket/index.browser.js.map +0 -1
  346. package/dist/websocket/index.d.ts +0 -876
  347. package/dist/websocket/index.d.ts.map +0 -1
  348. package/dist/websocket/index.js +0 -1175
  349. package/dist/websocket/index.js.map +0 -1
  350. package/src/react/websocket/hooks/useRoom.tsx +0 -251
  351. package/src/react/websocket/index.ts +0 -7
  352. package/src/websocket/__tests__/$channel.spec.ts +0 -30
  353. package/src/websocket/__tests__/$websocket-new.spec.ts +0 -195
  354. package/src/websocket/__tests__/RoomManager.spec.ts +0 -146
  355. package/src/websocket/__tests__/websocket-integration.spec.ts +0 -951
  356. package/src/websocket/errors/WebSocketError.ts +0 -34
  357. package/src/websocket/index.browser.ts +0 -25
  358. package/src/websocket/index.shared.ts +0 -8
  359. package/src/websocket/index.ts +0 -85
  360. package/src/websocket/interfaces/WebSocketInterfaces.ts +0 -252
  361. package/src/websocket/primitives/$channel.ts +0 -131
  362. package/src/websocket/primitives/$websocket.ts +0 -107
  363. package/src/websocket/providers/NodeWebSocketServerProvider.ts +0 -617
  364. package/src/websocket/providers/WebSocketServerProvider.ts +0 -56
  365. package/src/websocket/services/RoomManager.ts +0 -160
  366. package/src/websocket/services/WebSocketClient.ts +0 -642
  367. package/src/websocket/services/WebSocketTopicService.ts +0 -108
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../../../src/topic/redis/providers/RedisTopicProvider.ts","../../../src/topic/redis/index.ts"],"sourcesContent":["import { $atom, $hook, $inject, $state, Alepha, type Static, t } from \"alepha\";\nimport { $logger } from \"alepha/logger\";\nimport { RedisProvider, RedisSubscriberProvider } from \"alepha/redis\";\nimport {\n type SubscribeCallback,\n TopicProvider,\n type TopicPublishOptions,\n type TopicSubscribeOptions,\n type UnSubscribeFn,\n} from \"alepha/topic\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\n/**\n * Redis topic configuration atom.\n */\nexport const redisTopicOptions = $atom({\n name: \"alepha.topic.redis.options\",\n schema: t.object({\n prefix: t.text({\n default: \"topic\",\n description: \"Prefix for all topic channels in Redis.\",\n }),\n }),\n default: {\n prefix: \"topic\",\n },\n});\n\nexport type RedisTopicOptions = Static<typeof redisTopicOptions.schema>;\n\ndeclare module \"alepha\" {\n interface State {\n [redisTopicOptions.key]: RedisTopicOptions;\n }\n}\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport class RedisTopicProvider extends TopicProvider {\n protected readonly options = $state(redisTopicOptions);\n protected readonly alepha = $inject(Alepha);\n protected readonly redisProvider = $inject(RedisProvider);\n protected readonly redisSubscriberProvider = $inject(RedisSubscriberProvider);\n\n protected readonly log = $logger();\n\n protected readonly start = $hook({\n on: \"start\",\n handler: async () => {\n const subscribers = this.subscribers();\n if (subscribers.length) {\n await Promise.all(subscribers.map((fn) => fn()));\n for (const subscriber of subscribers) {\n this.log.debug(`Subscribed to topic '${subscriber.name}'`);\n }\n }\n },\n });\n\n protected override wildcardChar(): string {\n return \"*\";\n }\n\n public prefix(queue: string): string {\n return `${this.options.prefix}:${queue}`;\n }\n\n /**\n * Publish a message to a topic.\n */\n public async publish(\n topic: string,\n message: string,\n options?: TopicPublishOptions,\n ): Promise<void> {\n if (options?.retain) {\n await this.redisProvider.set(`${this.prefix(topic)}:retained`, message);\n }\n await this.redisProvider.publish(this.prefix(topic), message);\n }\n\n /**\n * Subscribe to a topic.\n */\n public async subscribe(\n name: string,\n callback: SubscribeCallback,\n _options?: TopicSubscribeOptions,\n ): Promise<UnSubscribeFn> {\n const topic = this.prefix(name);\n\n // Subscribe first to avoid message loss window\n await this.redisSubscriberProvider.subscribe(topic, callback);\n\n // Then deliver retained message if exists\n const retained = await this.redisProvider.get(`${topic}:retained`);\n if (retained) {\n await callback(retained.toString());\n }\n\n return () => this.unsubscribe(name, callback);\n }\n\n /**\n * Unsubscribe from a topic.\n */\n public async unsubscribe(\n name: string,\n callback?: SubscribeCallback,\n ): Promise<void> {\n const topic = this.prefix(name);\n\n await this.redisSubscriberProvider.unsubscribe(topic, callback);\n }\n}\n","import { $module, type Alepha } from \"alepha\";\nimport { AlephaTopic, TopicProvider } from \"alepha/topic\";\nimport { RedisTopicProvider } from \"./providers/RedisTopicProvider.ts\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport * from \"./providers/RedisTopicProvider.ts\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\n/**\n * Plugin for Alepha Topic that provides Redis pub/sub capabilities.\n *\n * @see {@link RedisTopicProvider}\n * @module alepha.topic.redis\n */\nexport const AlephaTopicRedis = $module({\n name: \"alepha.topic.redis\",\n services: [RedisTopicProvider],\n register: (alepha: Alepha): Alepha =>\n alepha\n .with({\n optional: true,\n provide: TopicProvider,\n use: RedisTopicProvider,\n })\n .with(AlephaTopic),\n});\n"],"mappings":";;;;;;;;AAgBA,MAAa,oBAAoB,MAAM;CACrC,MAAM;CACN,QAAQ,EAAE,OAAO,EACf,QAAQ,EAAE,KAAK;EACb,SAAS;EACT,aAAa;EACd,CAAC,EACH,CAAC;CACF,SAAS,EACP,QAAQ,SACT;CACF,CAAC;AAYF,IAAa,qBAAb,cAAwC,cAAc;CACpD,UAA6B,OAAO,kBAAkB;CACtD,SAA4B,QAAQ,OAAO;CAC3C,gBAAmC,QAAQ,cAAc;CACzD,0BAA6C,QAAQ,wBAAwB;CAE7E,MAAyB,SAAS;CAElC,QAA2B,MAAM;EAC/B,IAAI;EACJ,SAAS,YAAY;GACnB,MAAM,cAAc,KAAK,aAAa;AACtC,OAAI,YAAY,QAAQ;AACtB,UAAM,QAAQ,IAAI,YAAY,KAAK,OAAO,IAAI,CAAC,CAAC;AAChD,SAAK,MAAM,cAAc,YACvB,MAAK,IAAI,MAAM,wBAAwB,WAAW,KAAK,GAAG;;;EAIjE,CAAC;CAEF,eAA0C;AACxC,SAAO;;CAGT,OAAc,OAAuB;AACnC,SAAO,GAAG,KAAK,QAAQ,OAAO,GAAG;;;;;CAMnC,MAAa,QACX,OACA,SACA,SACe;AACf,MAAI,SAAS,OACX,OAAM,KAAK,cAAc,IAAI,GAAG,KAAK,OAAO,MAAM,CAAC,YAAY,QAAQ;AAEzE,QAAM,KAAK,cAAc,QAAQ,KAAK,OAAO,MAAM,EAAE,QAAQ;;;;;CAM/D,MAAa,UACX,MACA,UACA,UACwB;EACxB,MAAM,QAAQ,KAAK,OAAO,KAAK;AAG/B,QAAM,KAAK,wBAAwB,UAAU,OAAO,SAAS;EAG7D,MAAM,WAAW,MAAM,KAAK,cAAc,IAAI,GAAG,MAAM,WAAW;AAClE,MAAI,SACF,OAAM,SAAS,SAAS,UAAU,CAAC;AAGrC,eAAa,KAAK,YAAY,MAAM,SAAS;;;;;CAM/C,MAAa,YACX,MACA,UACe;EACf,MAAM,QAAQ,KAAK,OAAO,KAAK;AAE/B,QAAM,KAAK,wBAAwB,YAAY,OAAO,SAAS;;;;;;;;;;;ACjGnE,MAAa,mBAAmB,QAAQ;CACtC,MAAM;CACN,UAAU,CAAC,mBAAmB;CAC9B,WAAW,WACT,OACG,KAAK;EACJ,UAAU;EACV,SAAS;EACT,KAAK;EACN,CAAC,CACD,KAAK,YAAY;CACvB,CAAC"}
1
+ {"version":3,"file":"index.js","names":[],"sources":["../../../src/topic/redis/providers/RedisTopicProvider.ts","../../../src/topic/redis/index.ts"],"sourcesContent":["import { $atom, $hook, $inject, $state, Alepha, type Static, t } from \"alepha\";\nimport { $logger } from \"alepha/logger\";\nimport { RedisProvider, RedisSubscriberProvider } from \"alepha/redis\";\nimport {\n type SubscribeCallback,\n TopicProvider,\n type TopicPublishOptions,\n type TopicSubscribeOptions,\n type UnSubscribeFn,\n} from \"alepha/topic\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\n/**\n * Redis topic configuration atom.\n */\nexport const redisTopicOptions = $atom({\n name: \"alepha.topic.redis.options\",\n schema: t.object({\n prefix: t.text({\n default: \"topic\",\n description: \"Prefix for all topic channels in Redis.\",\n }),\n }),\n default: {\n prefix: \"topic\",\n },\n});\n\nexport type RedisTopicOptions = Static<typeof redisTopicOptions.schema>;\n\ndeclare module \"alepha\" {\n interface State {\n [redisTopicOptions.key]: RedisTopicOptions;\n }\n}\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport class RedisTopicProvider extends TopicProvider {\n protected readonly options = $state(redisTopicOptions);\n protected readonly alepha = $inject(Alepha);\n protected readonly redisProvider = $inject(RedisProvider);\n protected readonly redisSubscriberProvider = $inject(RedisSubscriberProvider);\n\n protected readonly log = $logger();\n\n protected readonly start = $hook({\n on: \"start\",\n handler: async () => {\n const subscribers = this.subscribers();\n if (subscribers.length) {\n await Promise.all(subscribers.map((fn) => fn()));\n for (const subscriber of subscribers) {\n this.log.debug(`Subscribed to topic '${subscriber.name}'`);\n }\n }\n },\n });\n\n protected override wildcardChar(): string {\n return \"*\";\n }\n\n public prefix(queue: string): string {\n return `${this.options.prefix}:${queue}`;\n }\n\n /**\n * Publish a message to a topic.\n */\n public async publish(\n topic: string,\n message: string,\n options?: TopicPublishOptions,\n ): Promise<void> {\n if (options?.retain) {\n await this.redisProvider.set(`${this.prefix(topic)}:retained`, message);\n }\n await this.redisProvider.publish(this.prefix(topic), message);\n }\n\n /**\n * Subscribe to a topic.\n */\n public async subscribe(\n name: string,\n callback: SubscribeCallback,\n _options?: TopicSubscribeOptions,\n ): Promise<UnSubscribeFn> {\n const topic = this.prefix(name);\n\n // Subscribe first to avoid message loss window\n await this.redisSubscriberProvider.subscribe(topic, callback);\n\n // Then deliver retained message if exists\n const retained = await this.redisProvider.get(`${topic}:retained`);\n if (retained) {\n await callback(retained.toString());\n }\n\n return () => this.unsubscribe(name, callback);\n }\n\n /**\n * Unsubscribe from a topic.\n */\n public async unsubscribe(\n name: string,\n callback?: SubscribeCallback,\n ): Promise<void> {\n const topic = this.prefix(name);\n\n await this.redisSubscriberProvider.unsubscribe(topic, callback);\n }\n}\n","import { $module, type Alepha } from \"alepha\";\nimport { AlephaTopic, TopicProvider } from \"alepha/topic\";\nimport { RedisTopicProvider } from \"./providers/RedisTopicProvider.ts\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport * from \"./providers/RedisTopicProvider.ts\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\n/**\n * Plugin for Alepha Topic that provides Redis pub/sub capabilities.\n *\n * @see {@link RedisTopicProvider}\n * @module alepha.topic.redis\n */\nexport const AlephaTopicRedis = $module({\n name: \"alepha.topic.redis\",\n services: [RedisTopicProvider],\n register: (alepha: Alepha): Alepha =>\n alepha\n .with({\n optional: true,\n provide: TopicProvider,\n use: RedisTopicProvider,\n })\n .with(AlephaTopic),\n});\n"],"mappings":";;;;;;;;AAgBA,MAAa,oBAAoB,MAAM;CACrC,MAAM;CACN,QAAQ,EAAE,OAAO,EACf,QAAQ,EAAE,KAAK;EACb,SAAS;EACT,aAAa;EACd,CAAC,EACH,CAAC;CACF,SAAS,EACP,QAAQ,SACT;CACF,CAAC;AAYF,IAAa,qBAAb,cAAwC,cAAc;CACpD,UAA6B,OAAO,kBAAkB;CACtD,SAA4B,QAAQ,OAAO;CAC3C,gBAAmC,QAAQ,cAAc;CACzD,0BAA6C,QAAQ,wBAAwB;CAE7E,MAAyB,SAAS;CAElC,QAA2B,MAAM;EAC/B,IAAI;EACJ,SAAS,YAAY;GACnB,MAAM,cAAc,KAAK,aAAa;GACtC,IAAI,YAAY,QAAQ;IACtB,MAAM,QAAQ,IAAI,YAAY,KAAK,OAAO,IAAI,CAAC,CAAC;IAChD,KAAK,MAAM,cAAc,aACvB,KAAK,IAAI,MAAM,wBAAwB,WAAW,KAAK,GAAG;;;EAIjE,CAAC;CAEF,eAA0C;EACxC,OAAO;;CAGT,OAAc,OAAuB;EACnC,OAAO,GAAG,KAAK,QAAQ,OAAO,GAAG;;;;;CAMnC,MAAa,QACX,OACA,SACA,SACe;EACf,IAAI,SAAS,QACX,MAAM,KAAK,cAAc,IAAI,GAAG,KAAK,OAAO,MAAM,CAAC,YAAY,QAAQ;EAEzE,MAAM,KAAK,cAAc,QAAQ,KAAK,OAAO,MAAM,EAAE,QAAQ;;;;;CAM/D,MAAa,UACX,MACA,UACA,UACwB;EACxB,MAAM,QAAQ,KAAK,OAAO,KAAK;EAG/B,MAAM,KAAK,wBAAwB,UAAU,OAAO,SAAS;EAG7D,MAAM,WAAW,MAAM,KAAK,cAAc,IAAI,GAAG,MAAM,WAAW;EAClE,IAAI,UACF,MAAM,SAAS,SAAS,UAAU,CAAC;EAGrC,aAAa,KAAK,YAAY,MAAM,SAAS;;;;;CAM/C,MAAa,YACX,MACA,UACe;EACf,MAAM,QAAQ,KAAK,OAAO,KAAK;EAE/B,MAAM,KAAK,wBAAwB,YAAY,OAAO,SAAS;;;;;;;;;;;ACjGnE,MAAa,mBAAmB,QAAQ;CACtC,MAAM;CACN,UAAU,CAAC,mBAAmB;CAC9B,WAAW,WACT,OACG,KAAK;EACJ,UAAU;EACV,SAAS;EACT,KAAK;EACN,CAAC,CACD,KAAK,YAAY;CACvB,CAAC"}
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "alepha",
3
3
  "description": "Easy-to-use modern TypeScript framework for building many kind of applications.",
4
4
  "author": "Feunard",
5
- "version": "0.20.5",
5
+ "version": "0.20.7",
6
6
  "type": "module",
7
7
  "engines": {
8
8
  "node": ">=22.0.0"
@@ -27,25 +27,23 @@
27
27
  "postgres": "^3.4.9",
28
28
  "s3mini": "^0.9.4",
29
29
  "tsx": "^4.21.0",
30
- "typebox": "^1.1.37",
30
+ "typebox": "^1.1.38",
31
31
  "typescript": "^6.0.3",
32
- "vite-bundle-analyzer": "^1.3.8",
33
- "ws": "^8.20.0"
32
+ "vite-bundle-analyzer": "^1.3.8"
34
33
  },
35
34
  "devDependencies": {
36
- "@biomejs/biome": "^2.4.14",
37
- "@cloudflare/workers-types": "^4.20260503.1",
35
+ "@biomejs/biome": "^2.4.15",
36
+ "@cloudflare/workers-types": "^4.20260510.1",
38
37
  "@electric-sql/pglite": "^0.4.5",
39
38
  "@faker-js/faker": "^10.4.0",
40
- "@tailwindcss/vite": "^4.2.4",
39
+ "@tailwindcss/vite": "^4.3.0",
41
40
  "@testing-library/dom": "^10.4.1",
42
41
  "@testing-library/react": "^16.3.2",
43
42
  "@types/bun": "^1.3.13",
44
- "@types/node": "^25.6.0",
43
+ "@types/node": "^25.6.2",
45
44
  "@types/nodemailer": "^8.0.0",
46
45
  "@types/react": "^19.2.14",
47
46
  "@types/react-dom": "^19.2.3",
48
- "@types/ws": "^8.18.1",
49
47
  "cron-schedule": "^6.0.0",
50
48
  "drizzle-kit": "^0.31.10",
51
49
  "jose": "^6.2.3",
@@ -53,13 +51,13 @@
53
51
  "nodemailer": "^8.0.7",
54
52
  "openid-client": "^6.8.4",
55
53
  "prom-client": "^15.1.3",
56
- "react": "^19.2.5",
57
- "react-dom": "^19.2.5",
58
- "shadcn": "^4.6.0",
54
+ "react": "^19.2.6",
55
+ "react-dom": "^19.2.6",
56
+ "shadcn": "^4.7.0",
59
57
  "swagger-ui-dist": "^5.32.5",
60
- "tailwindcss": "^4.2.4",
61
- "tsdown": "^0.21.10",
62
- "vite": "^8.0.10",
58
+ "tailwindcss": "^4.3.0",
59
+ "tsdown": "^0.22.0",
60
+ "vite": "^8.0.11",
63
61
  "vitest": "^4.1.5"
64
62
  },
65
63
  "peerDependencies": {
@@ -151,6 +149,11 @@
151
149
  "import": "./dist/api/payments/index.js",
152
150
  "default": "./dist/api/payments/index.js"
153
151
  },
152
+ "./api/subscriptions": {
153
+ "types": "./dist/api/subscriptions/index.d.ts",
154
+ "import": "./dist/api/subscriptions/index.js",
155
+ "default": "./dist/api/subscriptions/index.js"
156
+ },
154
157
  "./api/users": {
155
158
  "types": "./dist/api/users/index.d.ts",
156
159
  "react-native": "./dist/api/users/index.browser.js",
@@ -187,6 +190,11 @@
187
190
  "import": "./dist/cache/core/index.js",
188
191
  "default": "./dist/cache/core/index.js"
189
192
  },
193
+ "./cache/database": {
194
+ "types": "./dist/cache/database/index.d.ts",
195
+ "import": "./dist/cache/database/index.js",
196
+ "default": "./dist/cache/database/index.js"
197
+ },
190
198
  "./cache/redis": {
191
199
  "types": "./dist/cache/redis/index.d.ts",
192
200
  "import": "./dist/cache/redis/index.js",
@@ -364,11 +372,6 @@
364
372
  "import": "./dist/react/ui/index.js",
365
373
  "default": "./dist/react/ui/index.js"
366
374
  },
367
- "./react/websocket": {
368
- "types": "./dist/react/websocket/index.d.ts",
369
- "import": "./dist/react/websocket/index.js",
370
- "default": "./dist/react/websocket/index.js"
371
- },
372
375
  "./redis": {
373
376
  "types": "./dist/redis/index.d.ts",
374
377
  "bun": "./dist/redis/index.bun.js",
@@ -489,13 +492,6 @@
489
492
  "import": "./dist/topic/redis/index.js",
490
493
  "default": "./dist/topic/redis/index.js"
491
494
  },
492
- "./websocket": {
493
- "types": "./dist/websocket/index.d.ts",
494
- "react-native": "./dist/websocket/index.browser.js",
495
- "browser": "./dist/websocket/index.browser.js",
496
- "import": "./dist/websocket/index.js",
497
- "default": "./dist/websocket/index.js"
498
- },
499
495
  "./tsconfig.base": "./tsconfig.base.json",
500
496
  "./package.json": "./package.json"
501
497
  },
@@ -556,6 +552,11 @@
556
552
  "import": "./dist/api/payments/index.js",
557
553
  "default": "./dist/api/payments/index.js"
558
554
  },
555
+ "./api/subscriptions": {
556
+ "types": "./dist/api/subscriptions/index.d.ts",
557
+ "import": "./dist/api/subscriptions/index.js",
558
+ "default": "./dist/api/subscriptions/index.js"
559
+ },
559
560
  "./api/users": {
560
561
  "types": "./dist/api/users/index.d.ts",
561
562
  "react-native": "./dist/api/users/index.browser.js",
@@ -592,6 +593,11 @@
592
593
  "import": "./dist/cache/core/index.js",
593
594
  "default": "./dist/cache/core/index.js"
594
595
  },
596
+ "./cache/database": {
597
+ "types": "./dist/cache/database/index.d.ts",
598
+ "import": "./dist/cache/database/index.js",
599
+ "default": "./dist/cache/database/index.js"
600
+ },
595
601
  "./cache/redis": {
596
602
  "types": "./dist/cache/redis/index.d.ts",
597
603
  "import": "./dist/cache/redis/index.js",
@@ -769,11 +775,6 @@
769
775
  "import": "./dist/react/ui/index.js",
770
776
  "default": "./dist/react/ui/index.js"
771
777
  },
772
- "./react/websocket": {
773
- "types": "./dist/react/websocket/index.d.ts",
774
- "import": "./dist/react/websocket/index.js",
775
- "default": "./dist/react/websocket/index.js"
776
- },
777
778
  "./redis": {
778
779
  "types": "./dist/redis/index.d.ts",
779
780
  "bun": "./dist/redis/index.bun.js",
@@ -894,13 +895,6 @@
894
895
  "import": "./dist/topic/redis/index.js",
895
896
  "default": "./dist/topic/redis/index.js"
896
897
  },
897
- "./websocket": {
898
- "types": "./dist/websocket/index.d.ts",
899
- "react-native": "./dist/websocket/index.browser.js",
900
- "browser": "./dist/websocket/index.browser.js",
901
- "import": "./dist/websocket/index.js",
902
- "default": "./dist/websocket/index.js"
903
- },
904
898
  "./tsconfig.base": "./tsconfig.base.json",
905
899
  "./package.json": "./package.json"
906
900
  }
@@ -1,6 +1,8 @@
1
1
  import { $inject, t } from "alepha";
2
+ import { $repository } from "alepha/orm";
2
3
  import { $secure } from "alepha/security";
3
4
  import { $action } from "alepha/server";
5
+ import { audits } from "../entities/audits.ts";
4
6
  import { auditQuerySchema } from "../schemas/auditQuerySchema.ts";
5
7
  import { auditResourceSchema } from "../schemas/auditResourceSchema.ts";
6
8
  import { createAuditSchema } from "../schemas/createAuditSchema.ts";
@@ -19,6 +21,7 @@ export class AdminAuditController {
19
21
  protected readonly url = "/audits";
20
22
  protected readonly group = "admin:audits";
21
23
  protected readonly auditService = $inject(AuditService);
24
+ protected readonly repo = $repository(audits);
22
25
 
23
26
  /**
24
27
  * Find audit entries with filtering and pagination.
@@ -52,6 +55,32 @@ export class AdminAuditController {
52
55
  handler: ({ params }) => this.auditService.getById(params.id),
53
56
  });
54
57
 
58
+ /**
59
+ * Delete many audit entries by id in one repository call. Use with care —
60
+ * audit logs are usually retained for compliance reasons.
61
+ */
62
+ public readonly deleteAudits = $action({
63
+ method: "POST",
64
+ path: `${this.url}/delete`,
65
+ group: this.group,
66
+ use: [$secure({ permissions: ["admin:audit:delete"] })],
67
+ description: "Delete many audit entries",
68
+ schema: {
69
+ body: t.object({
70
+ ids: t.array(t.text(), { minItems: 1, maxItems: 1000 }),
71
+ }),
72
+ response: t.object({
73
+ deleted: t.array(t.text()),
74
+ }),
75
+ },
76
+ handler: async ({ body }) => {
77
+ const deleted = await this.repo.deleteMany({
78
+ id: { inArray: body.ids },
79
+ });
80
+ return { deleted: deleted.map(String) };
81
+ },
82
+ });
83
+
55
84
  /**
56
85
  * Create a new audit entry.
57
86
  * System-only — this permission should never be assigned to human roles.
@@ -26,6 +26,7 @@ export const audits = $entity({
26
26
  schema: t.object({
27
27
  id: db.primaryKey(t.bigint()),
28
28
  createdAt: db.createdAt(),
29
+ organizationId: db.organization(),
29
30
 
30
31
  /**
31
32
  * Audit event type (e.g., "auth", "user", "payment", "system").
@@ -50,6 +50,30 @@ export class FileController {
50
50
  handler: ({ params }) => this.fileService.deleteFile(params.id),
51
51
  });
52
52
 
53
+ /**
54
+ * POST /files/delete - Delete many files in one request, batching the
55
+ * underlying bucket calls per bucket (R2/S3 batch where supported).
56
+ */
57
+ public readonly deleteFiles = $action({
58
+ method: "POST",
59
+ path: `${this.url}/delete`,
60
+ group: `admin:${this.group}`,
61
+ use: [$secure({ permissions: ["admin:file:delete"] })],
62
+ description: "Delete many files",
63
+ schema: {
64
+ body: t.object({
65
+ ids: t.array(t.uuid(), { minItems: 1, maxItems: 1000 }),
66
+ }),
67
+ response: t.object({
68
+ deleted: t.array(t.string()),
69
+ }),
70
+ },
71
+ handler: async ({ body }) => {
72
+ const deleted = await this.fileService.deleteFiles(body.ids);
73
+ return { deleted };
74
+ },
75
+ });
76
+
53
77
  /**
54
78
  * POST /files - Uploads a new file to storage.
55
79
  * Creates a database record with metadata and calculates checksum.
@@ -8,6 +8,7 @@ export const files = $entity({
8
8
  version: db.version(),
9
9
  createdAt: db.createdAt(),
10
10
  updatedAt: db.updatedAt(),
11
+ organizationId: db.organization(),
11
12
  blobId: t.text(),
12
13
  creator: t.optional(t.uuid()),
13
14
  creatorRealm: t.optional(t.string()),
@@ -324,6 +324,47 @@ export class FileService {
324
324
  return { ok: true, id: String(file.id) };
325
325
  }
326
326
 
327
+ /**
328
+ * Delete many files in one round-trip per bucket. The database rows are
329
+ * removed in a single `deleteMany`, and each affected bucket gets a single
330
+ * `bucket.deleteMany` call (R2/S3 batch where supported).
331
+ */
332
+ public async deleteFiles(ids: string[]): Promise<string[]> {
333
+ if (ids.length === 0) return [];
334
+
335
+ const files = await this.fileRepository.findMany({
336
+ where: { id: { inArray: ids } },
337
+ columns: ["id", "bucket", "blobId"],
338
+ });
339
+ if (files.length === 0) return [];
340
+
341
+ const dbDeleted = await this.fileRepository.deleteMany({
342
+ id: { inArray: files.map((f) => f.id) },
343
+ });
344
+
345
+ const blobsByBucket = new Map<string, string[]>();
346
+ for (const f of files) {
347
+ const list = blobsByBucket.get(f.bucket) ?? [];
348
+ list.push(f.blobId);
349
+ blobsByBucket.set(f.bucket, list);
350
+ }
351
+
352
+ for (const [bucketName, blobIds] of blobsByBucket) {
353
+ try {
354
+ await this.bucket(bucketName).deleteMany(blobIds, true);
355
+ } catch (e) {
356
+ // DB rows already gone — log and continue. Orphaned blobs are
357
+ // recoverable; orphaned DB rows would be worse.
358
+ this.log.warn(
359
+ `Failed to bulk-delete ${blobIds.length} files from bucket ${bucketName}`,
360
+ e,
361
+ );
362
+ }
363
+ }
364
+
365
+ return dbDeleted.map(String);
366
+ }
367
+
327
368
  /**
328
369
  * Retrieves a file entity by its ID.
329
370
  * If already an entity object, returns it as-is (convenience method).