alepha 0.18.2 → 0.18.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (397) hide show
  1. package/assets/devtools-ui/200.html +2 -2
  2. package/assets/devtools-ui/200.html.br +0 -0
  3. package/assets/devtools-ui/404.html +2 -2
  4. package/assets/devtools-ui/404.html.br +0 -0
  5. package/assets/devtools-ui/{asset.BfSBZ5Dd.css → asset.hG_f8HuK.css} +1 -1
  6. package/assets/devtools-ui/asset.hG_f8HuK.css.br +0 -0
  7. package/assets/devtools-ui/chunk.B3au4Lhg.js +1 -0
  8. package/assets/devtools-ui/chunk.B3au4Lhg.js.br +0 -0
  9. package/assets/devtools-ui/chunk.BLOrlnMB.js +1 -0
  10. package/assets/devtools-ui/chunk.BLOrlnMB.js.br +0 -0
  11. package/assets/devtools-ui/chunk.BLR01ljW.js +1 -0
  12. package/assets/devtools-ui/chunk.BLR01ljW.js.br +0 -0
  13. package/assets/devtools-ui/chunk.BTXaIUlA.js +1 -0
  14. package/assets/devtools-ui/chunk.BTXaIUlA.js.br +0 -0
  15. package/assets/devtools-ui/{chunk.lJL-lgnW.js → chunk.BhJaxmm8.js} +1 -1
  16. package/assets/devtools-ui/chunk.BhJaxmm8.js.br +0 -0
  17. package/assets/devtools-ui/chunk.BtoNxFuL.js +1 -0
  18. package/assets/devtools-ui/chunk.BtoNxFuL.js.br +0 -0
  19. package/assets/devtools-ui/chunk.C8YUV2Wd.js +1 -0
  20. package/assets/devtools-ui/chunk.C8YUV2Wd.js.br +0 -0
  21. package/assets/devtools-ui/{chunk.M6wyKO_3.js → chunk.CBbIgDzE.js} +2 -2
  22. package/assets/devtools-ui/chunk.CBbIgDzE.js.br +0 -0
  23. package/assets/devtools-ui/chunk.CFqIniwA.js +1 -0
  24. package/assets/devtools-ui/chunk.CFqIniwA.js.br +0 -0
  25. package/assets/devtools-ui/chunk.CLFF7f7-.js +1 -0
  26. package/assets/devtools-ui/chunk.CLFF7f7-.js.br +0 -0
  27. package/assets/devtools-ui/chunk.CRsBbA10.js +1 -0
  28. package/assets/devtools-ui/chunk.CRsBbA10.js.br +0 -0
  29. package/assets/devtools-ui/{chunk.DbEH1oOB.js → chunk.CZPo6v95.js} +1 -1
  30. package/assets/devtools-ui/chunk.CZPo6v95.js.br +0 -0
  31. package/assets/devtools-ui/chunk.D0fWgNos.js +1 -0
  32. package/assets/devtools-ui/chunk.D0fWgNos.js.br +1 -0
  33. package/assets/devtools-ui/chunk.D7-0ziQ6.js +1 -0
  34. package/assets/devtools-ui/chunk.D7-0ziQ6.js.br +0 -0
  35. package/assets/devtools-ui/chunk.DAewe0vm.js +1 -0
  36. package/assets/devtools-ui/chunk.DAewe0vm.js.br +0 -0
  37. package/assets/devtools-ui/chunk.DJRQEYqK.js +1 -0
  38. package/assets/devtools-ui/chunk.DJRQEYqK.js.br +0 -0
  39. package/assets/devtools-ui/{chunk.CZl6J9DF.js → chunk.DMAxv14p.js} +1 -1
  40. package/assets/devtools-ui/chunk.DMAxv14p.js.br +0 -0
  41. package/assets/devtools-ui/{chunk.BT2IiBkZ.js → chunk.DMImnNjU.js} +1 -1
  42. package/assets/devtools-ui/chunk.DMImnNjU.js.br +0 -0
  43. package/assets/devtools-ui/chunk.DeeQsidk.js +9 -0
  44. package/assets/devtools-ui/chunk.DeeQsidk.js.br +0 -0
  45. package/assets/devtools-ui/chunk.DqEwn9Vj.js +7 -0
  46. package/assets/devtools-ui/chunk.DqEwn9Vj.js.br +0 -0
  47. package/assets/devtools-ui/chunk.Dt8OsQey.js +1 -0
  48. package/assets/devtools-ui/chunk.Dt8OsQey.js.br +0 -0
  49. package/assets/devtools-ui/{chunk.B9pX3zit.js → chunk.Dtp8oa_f.js} +1 -1
  50. package/assets/devtools-ui/chunk.Dtp8oa_f.js.br +0 -0
  51. package/assets/devtools-ui/chunk.Dx3JzAYM.js +1 -0
  52. package/assets/devtools-ui/chunk.Dx3JzAYM.js.br +0 -0
  53. package/assets/devtools-ui/chunk.GCOj1-5E.js +1 -0
  54. package/assets/devtools-ui/chunk.GCOj1-5E.js.br +0 -0
  55. package/assets/devtools-ui/chunk.IC1LD8BH.js +1 -0
  56. package/assets/devtools-ui/chunk.IC1LD8BH.js.br +0 -0
  57. package/assets/devtools-ui/chunk.IwuB_TqW.js +1 -0
  58. package/assets/devtools-ui/chunk.IwuB_TqW.js.br +0 -0
  59. package/assets/devtools-ui/chunk.Qqapj2zq.js +1 -0
  60. package/assets/devtools-ui/chunk.Qqapj2zq.js.br +0 -0
  61. package/assets/devtools-ui/{chunk.C79YouPp.js → chunk.TKKKndOy.js} +1 -1
  62. package/assets/devtools-ui/chunk.TKKKndOy.js.br +0 -0
  63. package/assets/devtools-ui/chunk.YHTVhFQT.js +1 -0
  64. package/assets/devtools-ui/chunk.YHTVhFQT.js.br +0 -0
  65. package/assets/devtools-ui/chunk.fnod6uEi.js +1 -0
  66. package/assets/devtools-ui/chunk.fnod6uEi.js.br +0 -0
  67. package/assets/devtools-ui/chunk.mOCRmXjo.js +1 -0
  68. package/assets/devtools-ui/chunk.mOCRmXjo.js.br +0 -0
  69. package/assets/devtools-ui/chunk.qZTNEAK0.js +1 -0
  70. package/assets/devtools-ui/chunk.qZTNEAK0.js.br +0 -0
  71. package/assets/devtools-ui/chunk.rc9m0y4-.js +1 -0
  72. package/assets/devtools-ui/chunk.rc9m0y4-.js.br +0 -0
  73. package/assets/devtools-ui/entry.Cxc5QLCU.js +80 -0
  74. package/assets/devtools-ui/entry.Cxc5QLCU.js.br +0 -0
  75. package/assets/devtools-ui/index.html +2 -2
  76. package/assets/devtools-ui/index.html.br +0 -0
  77. package/assets/swagger-ui/swagger-ui-bundle.js +1 -1
  78. package/assets/swagger-ui/swagger-ui.css +1 -1
  79. package/dist/api/audits/index.d.ts +61 -5
  80. package/dist/api/audits/index.d.ts.map +1 -1
  81. package/dist/api/files/index.d.ts +61 -5
  82. package/dist/api/files/index.d.ts.map +1 -1
  83. package/dist/api/jobs/index.d.ts +61 -5
  84. package/dist/api/jobs/index.d.ts.map +1 -1
  85. package/dist/api/jobs/index.js +4 -2
  86. package/dist/api/jobs/index.js.map +1 -1
  87. package/dist/api/keys/index.d.ts +5 -5
  88. package/dist/api/notifications/index.browser.js +44 -1
  89. package/dist/api/notifications/index.browser.js.map +1 -1
  90. package/dist/api/notifications/index.d.ts +187 -2
  91. package/dist/api/notifications/index.d.ts.map +1 -1
  92. package/dist/api/notifications/index.js +143 -8
  93. package/dist/api/notifications/index.js.map +1 -1
  94. package/dist/api/parameters/index.d.ts +61 -5
  95. package/dist/api/parameters/index.d.ts.map +1 -1
  96. package/dist/api/users/index.d.ts +330 -93
  97. package/dist/api/users/index.d.ts.map +1 -1
  98. package/dist/api/users/index.js +27 -36
  99. package/dist/api/users/index.js.map +1 -1
  100. package/dist/cli/config/index.d.ts +46 -0
  101. package/dist/cli/config/index.d.ts.map +1 -0
  102. package/dist/cli/config/index.js +20 -0
  103. package/dist/cli/config/index.js.map +1 -0
  104. package/dist/cli/core/index.d.ts +69 -66
  105. package/dist/cli/core/index.d.ts.map +1 -1
  106. package/dist/cli/core/index.js +329 -196
  107. package/dist/cli/core/index.js.map +1 -1
  108. package/dist/cli/platform/index.d.ts +302 -63
  109. package/dist/cli/platform/index.d.ts.map +1 -1
  110. package/dist/cli/platform/index.js +455 -25
  111. package/dist/cli/platform/index.js.map +1 -1
  112. package/dist/core/index.browser.js +125 -87
  113. package/dist/core/index.browser.js.map +1 -1
  114. package/dist/core/index.d.ts +62 -53
  115. package/dist/core/index.d.ts.map +1 -1
  116. package/dist/core/index.js +125 -87
  117. package/dist/core/index.js.map +1 -1
  118. package/dist/core/index.native.js +125 -87
  119. package/dist/core/index.native.js.map +1 -1
  120. package/dist/core/index.workerd.js +125 -87
  121. package/dist/core/index.workerd.js.map +1 -1
  122. package/dist/crypto/index.d.ts +18 -1
  123. package/dist/crypto/index.d.ts.map +1 -1
  124. package/dist/crypto/index.js +29 -3
  125. package/dist/crypto/index.js.map +1 -1
  126. package/dist/devtools/index.js +3 -12
  127. package/dist/devtools/index.js.map +1 -1
  128. package/dist/logger/index.d.ts +10 -1
  129. package/dist/logger/index.d.ts.map +1 -1
  130. package/dist/logger/index.js +19 -9
  131. package/dist/logger/index.js.map +1 -1
  132. package/dist/orm/core/index.browser.js +57 -1
  133. package/dist/orm/core/index.browser.js.map +1 -1
  134. package/dist/orm/core/index.bun.js +378 -19
  135. package/dist/orm/core/index.bun.js.map +1 -1
  136. package/dist/orm/core/index.d.ts +328 -9
  137. package/dist/orm/core/index.d.ts.map +1 -1
  138. package/dist/orm/core/index.js +384 -21
  139. package/dist/orm/core/index.js.map +1 -1
  140. package/dist/orm/postgres/index.bun.js +49 -17
  141. package/dist/orm/postgres/index.bun.js.map +1 -1
  142. package/dist/orm/postgres/index.d.ts +47 -21
  143. package/dist/orm/postgres/index.d.ts.map +1 -1
  144. package/dist/orm/postgres/index.js +52 -17
  145. package/dist/orm/postgres/index.js.map +1 -1
  146. package/dist/react/core/index.d.ts +1 -1
  147. package/dist/react/core/index.d.ts.map +1 -1
  148. package/dist/react/core/index.js +6 -1
  149. package/dist/react/core/index.js.map +1 -1
  150. package/dist/react/form/index.d.ts +28 -18
  151. package/dist/react/form/index.d.ts.map +1 -1
  152. package/dist/react/form/index.js +92 -56
  153. package/dist/react/form/index.js.map +1 -1
  154. package/dist/react/router/index.browser.js +448 -116
  155. package/dist/react/router/index.browser.js.map +1 -1
  156. package/dist/react/router/index.d.ts +102 -40
  157. package/dist/react/router/index.d.ts.map +1 -1
  158. package/dist/react/router/index.js +453 -92
  159. package/dist/react/router/index.js.map +1 -1
  160. package/dist/security/index.d.ts +3 -11
  161. package/dist/security/index.d.ts.map +1 -1
  162. package/dist/security/index.js +6 -11
  163. package/dist/security/index.js.map +1 -1
  164. package/dist/server/auth/index.d.ts +22 -24
  165. package/dist/server/auth/index.d.ts.map +1 -1
  166. package/dist/server/auth/index.js +102 -82
  167. package/dist/server/auth/index.js.map +1 -1
  168. package/dist/server/cookies/index.d.ts +7 -4
  169. package/dist/server/cookies/index.d.ts.map +1 -1
  170. package/dist/server/cookies/index.js +13 -12
  171. package/dist/server/cookies/index.js.map +1 -1
  172. package/dist/server/core/index.d.ts +288 -4
  173. package/dist/server/core/index.d.ts.map +1 -1
  174. package/dist/server/core/index.js +375 -2
  175. package/dist/server/core/index.js.map +1 -1
  176. package/dist/server/links/index.browser.js +10 -71
  177. package/dist/server/links/index.browser.js.map +1 -1
  178. package/dist/server/links/index.d.ts +32 -49
  179. package/dist/server/links/index.d.ts.map +1 -1
  180. package/dist/server/links/index.js +73 -100
  181. package/dist/server/links/index.js.map +1 -1
  182. package/dist/system/index.browser.js +221 -2
  183. package/dist/system/index.browser.js.map +1 -1
  184. package/dist/system/index.d.ts +63 -1
  185. package/dist/system/index.d.ts.map +1 -1
  186. package/dist/system/index.js +221 -1
  187. package/dist/system/index.js.map +1 -1
  188. package/dist/system/index.workerd.js +224 -4
  189. package/dist/system/index.workerd.js.map +1 -1
  190. package/package.json +10 -5
  191. package/src/api/jobs/providers/JobProvider.ts +6 -3
  192. package/src/api/notifications/controllers/AdminNotificationController.ts +83 -0
  193. package/src/api/notifications/index.browser.ts +3 -0
  194. package/src/api/notifications/index.ts +14 -2
  195. package/src/api/notifications/jobs/NotificationJobs.ts +11 -2
  196. package/src/api/notifications/schemas/notificationDetailResourceSchema.ts +20 -0
  197. package/src/api/notifications/schemas/notificationQuerySchema.ts +19 -0
  198. package/src/api/notifications/schemas/notificationResourceSchema.ts +18 -0
  199. package/src/api/notifications/services/NotificationSenderService.ts +15 -2
  200. package/src/api/users/atoms/realmAuthSettingsAtom.ts +28 -32
  201. package/src/api/users/buckets/UserBuckets.ts +1 -1
  202. package/src/api/users/jobs/UserJobs.ts +1 -1
  203. package/src/api/users/primitives/$realm.ts +8 -49
  204. package/src/api/users/providers/RealmProvider.ts +2 -3
  205. package/src/api/users/services/RegistrationService.spec.ts +7 -7
  206. package/src/api/users/services/RegistrationService.ts +3 -3
  207. package/src/api/users/services/SessionService.spec.ts +4 -4
  208. package/src/api/users/services/SessionService.ts +3 -3
  209. package/src/cli/{core → config}/defineConfig.ts +14 -20
  210. package/src/cli/config/index.ts +1 -0
  211. package/src/cli/core/commands/db.ts +65 -1
  212. package/src/cli/core/commands/dev.ts +1 -0
  213. package/src/cli/core/commands/init.ts +2 -192
  214. package/src/cli/core/index.ts +34 -11
  215. package/src/cli/core/providers/ViteDevServerProvider.ts +52 -13
  216. package/src/cli/core/services/PackageManagerUtils.ts +43 -21
  217. package/src/cli/core/services/ProjectScaffolder.ts +214 -2
  218. package/src/cli/core/services/ViteUtils.ts +57 -0
  219. package/src/cli/core/tasks/BuildClientTask.ts +7 -2
  220. package/src/cli/core/tasks/BuildCloudflareTask.ts +4 -12
  221. package/src/cli/core/tasks/BuildServerTask.ts +2 -0
  222. package/src/cli/core/tasks/BuildVercelTask.ts +165 -168
  223. package/src/cli/core/templates/alephaConfigTs.ts +1 -1
  224. package/src/cli/core/templates/apiAppSecurityTs.ts +5 -8
  225. package/src/cli/core/templates/tsconfigJson.ts +6 -1
  226. package/src/cli/platform/adapters/CloudflareAdapter.spec.ts +1 -1
  227. package/src/cli/platform/adapters/CloudflareAdapter.ts +30 -29
  228. package/src/cli/platform/atoms/platformOptions.ts +21 -0
  229. package/src/cli/platform/commands/SecretsCommand.spec.ts +298 -0
  230. package/src/cli/platform/commands/SecretsCommand.ts +283 -0
  231. package/src/cli/platform/commands/platform.ts +12 -0
  232. package/src/cli/platform/index.ts +14 -28
  233. package/src/cli/platform/providers/GitHubSecretStore.spec.ts +153 -0
  234. package/src/cli/platform/providers/GitHubSecretStore.ts +112 -0
  235. package/src/cli/platform/providers/MemorySecretStore.ts +114 -0
  236. package/src/cli/platform/providers/SecretStoreProvider.ts +39 -0
  237. package/src/cli/platform/schemas/cloudflare.ts +2 -0
  238. package/src/cli/platform/services/CloudflareApi.ts +5 -2
  239. package/src/cli/platform/services/DockerComposeGenerator.spec.ts +115 -0
  240. package/src/cli/platform/services/DockerComposeGenerator.ts +46 -1
  241. package/src/cli/platform/services/SecretFilterService.spec.ts +111 -0
  242. package/src/cli/platform/services/SecretFilterService.ts +54 -0
  243. package/src/core/Alepha.ts +94 -25
  244. package/src/core/__tests__/Alepha-parseEnv.spec.ts +20 -0
  245. package/src/core/primitives/$memoize.ts +38 -26
  246. package/src/core/providers/AlsProvider.ts +2 -0
  247. package/src/core/providers/EventManager.ts +4 -0
  248. package/src/core/providers/KeylessJsonSchemaCodec.spec.ts +1 -4
  249. package/src/core/providers/KeylessJsonSchemaCodec.ts +19 -125
  250. package/src/core/providers/SchemaValidator.spec.ts +36 -0
  251. package/src/core/providers/SchemaValidator.ts +9 -0
  252. package/src/crypto/index.ts +6 -1
  253. package/src/crypto/providers/SecretProvider.ts +36 -0
  254. package/src/devtools/providers/DevToolsProvider.ts +3 -12
  255. package/src/logger/index.ts +33 -6
  256. package/src/logger/providers/PrettyFormatterProvider.ts +5 -3
  257. package/src/orm/__tests__/orm-next-tests.ts +492 -0
  258. package/src/orm/__tests__/orm-next.spec.ts +140 -0
  259. package/src/orm/core/constants/PG_SYMBOLS.ts +17 -0
  260. package/src/orm/core/index.bun.ts +3 -6
  261. package/src/orm/core/index.shared-server.ts +2 -0
  262. package/src/orm/core/index.shared.ts +2 -0
  263. package/src/orm/core/index.ts +5 -7
  264. package/src/orm/core/interfaces/AggregateQuery.ts +103 -0
  265. package/src/orm/core/interfaces/PgQueryWhere.ts +7 -0
  266. package/src/orm/core/primitives/$entity.ts +8 -0
  267. package/src/orm/core/primitives/$repository.ts +6 -3
  268. package/src/orm/core/primitives/$view.ts +88 -0
  269. package/src/orm/core/providers/DbCacheProvider.ts +66 -0
  270. package/src/orm/core/providers/DrizzleKitProvider.ts +42 -0
  271. package/src/orm/core/providers/drivers/BunSqliteProvider.ts +2 -3
  272. package/src/orm/core/providers/drivers/CloudflareD1Provider.ts +12 -0
  273. package/src/orm/core/providers/drivers/DatabaseProvider.ts +39 -0
  274. package/src/orm/core/providers/drivers/NodeSqliteProvider.ts +2 -3
  275. package/src/orm/core/schemas/databaseEnvSchema.ts +31 -0
  276. package/src/orm/core/schemas/insertSchema.ts +13 -3
  277. package/src/orm/core/schemas/updateSchema.ts +14 -3
  278. package/src/orm/core/services/ModelBuilder.ts +26 -14
  279. package/src/orm/core/services/QueryManager.ts +13 -0
  280. package/src/orm/core/services/Repository.ts +307 -5
  281. package/src/orm/core/services/SqliteModelBuilder.ts +38 -0
  282. package/src/orm/postgres/index.bun.ts +4 -7
  283. package/src/orm/postgres/index.ts +4 -7
  284. package/src/orm/postgres/providers/BunPostgresProvider.ts +12 -2
  285. package/src/orm/postgres/providers/NodePostgresProvider.ts +7 -0
  286. package/src/orm/postgres/providers/PglitePostgresProvider.ts +10 -17
  287. package/src/orm/postgres/providers/PostgresProvider.ts +7 -36
  288. package/src/orm/postgres/schemas/postgresEnvSchema.ts +32 -0
  289. package/src/orm/postgres/services/PostgresModelBuilder.ts +40 -0
  290. package/src/react/core/components/ErrorBoundary.tsx +5 -2
  291. package/src/react/form/hooks/useFieldValue.ts +34 -0
  292. package/src/react/form/hooks/useForm.browser.spec.tsx +94 -9
  293. package/src/react/form/hooks/useForm.ts +14 -2
  294. package/src/react/form/hooks/useFormState.ts +10 -10
  295. package/src/react/form/hooks/useFormValues.ts +29 -0
  296. package/src/react/form/index.ts +3 -1
  297. package/src/react/form/services/FormModel.ts +53 -122
  298. package/src/react/router/components/ErrorViewer.tsx +333 -34
  299. package/src/react/router/components/NestedView.tsx +10 -3
  300. package/src/react/router/primitives/$page.browser.spec.tsx +34 -0
  301. package/src/react/router/primitives/$page.spec.tsx +20 -0
  302. package/src/react/router/primitives/$page.ts +24 -0
  303. package/src/react/router/providers/ReactBrowserRouterProvider.ts +14 -2
  304. package/src/react/router/providers/ReactPageProvider.ts +156 -73
  305. package/src/react/router/providers/ReactServerProvider.ts +40 -2
  306. package/src/react/router/providers/ReactServerTemplateProvider.ts +13 -1
  307. package/src/security/providers/SecurityProvider.ts +5 -27
  308. package/src/server/auth/primitives/$auth.ts +52 -19
  309. package/src/server/auth/providers/ServerAuthProvider.ts +145 -139
  310. package/src/server/cookies/providers/ServerCookiesProvider.ts +12 -24
  311. package/src/server/core/index.ts +3 -1
  312. package/src/server/core/primitives/$sse.spec.ts +315 -0
  313. package/src/server/core/primitives/$sse.ts +715 -0
  314. package/src/server/links/index.browser.ts +1 -3
  315. package/src/server/links/index.ts +0 -3
  316. package/src/server/links/providers/LinkProvider.spec.ts +12 -21
  317. package/src/server/links/providers/LinkProvider.ts +20 -52
  318. package/src/server/links/providers/ServerLinksProvider.spec.ts +106 -0
  319. package/src/server/links/providers/ServerLinksProvider.ts +113 -73
  320. package/src/server/links/schemas/apiLinksResponseSchema.ts +4 -21
  321. package/src/server/links/services/BatchCollector.ts +5 -3
  322. package/src/system/index.browser.ts +1 -0
  323. package/src/system/index.ts +3 -0
  324. package/src/system/index.workerd.ts +39 -1
  325. package/src/system/providers/WorkerdFileSystemProvider.ts +365 -0
  326. package/assets/devtools-ui/asset.BfSBZ5Dd.css.br +0 -0
  327. package/assets/devtools-ui/chunk.2NYaoqWt.js +0 -1
  328. package/assets/devtools-ui/chunk.2NYaoqWt.js.br +0 -0
  329. package/assets/devtools-ui/chunk.B052Z_xQ.js +0 -1
  330. package/assets/devtools-ui/chunk.B052Z_xQ.js.br +0 -0
  331. package/assets/devtools-ui/chunk.B4kVY90C.js +0 -1
  332. package/assets/devtools-ui/chunk.B4kVY90C.js.br +0 -0
  333. package/assets/devtools-ui/chunk.B7QJXctB.js +0 -1
  334. package/assets/devtools-ui/chunk.B7QJXctB.js.br +0 -0
  335. package/assets/devtools-ui/chunk.B9pX3zit.js.br +0 -0
  336. package/assets/devtools-ui/chunk.BKF9JxIo.js +0 -1
  337. package/assets/devtools-ui/chunk.BKF9JxIo.js.br +0 -0
  338. package/assets/devtools-ui/chunk.BOHgdTP-.js +0 -1
  339. package/assets/devtools-ui/chunk.BOHgdTP-.js.br +0 -0
  340. package/assets/devtools-ui/chunk.BOVFxkYC.js +0 -1
  341. package/assets/devtools-ui/chunk.BOVFxkYC.js.br +0 -0
  342. package/assets/devtools-ui/chunk.BR842zj5.js +0 -1
  343. package/assets/devtools-ui/chunk.BR842zj5.js.br +0 -0
  344. package/assets/devtools-ui/chunk.BT2IiBkZ.js.br +0 -0
  345. package/assets/devtools-ui/chunk.C79YouPp.js.br +0 -0
  346. package/assets/devtools-ui/chunk.C8mlBrjW.js +0 -9
  347. package/assets/devtools-ui/chunk.C8mlBrjW.js.br +0 -0
  348. package/assets/devtools-ui/chunk.CK0ow3AZ.js +0 -1
  349. package/assets/devtools-ui/chunk.CK0ow3AZ.js.br +0 -0
  350. package/assets/devtools-ui/chunk.CZl6J9DF.js.br +0 -0
  351. package/assets/devtools-ui/chunk.CdNr0YzS.js +0 -1
  352. package/assets/devtools-ui/chunk.CdNr0YzS.js.br +0 -0
  353. package/assets/devtools-ui/chunk.Ce6_6iIF.js +0 -1
  354. package/assets/devtools-ui/chunk.Ce6_6iIF.js.br +0 -0
  355. package/assets/devtools-ui/chunk.CpyDMr6O.js +0 -1
  356. package/assets/devtools-ui/chunk.CpyDMr6O.js.br +0 -0
  357. package/assets/devtools-ui/chunk.CyPmvPnY.js +0 -1
  358. package/assets/devtools-ui/chunk.CyPmvPnY.js.br +0 -0
  359. package/assets/devtools-ui/chunk.DTI_geWu.js +0 -1
  360. package/assets/devtools-ui/chunk.DTI_geWu.js.br +0 -0
  361. package/assets/devtools-ui/chunk.DbEH1oOB.js.br +0 -0
  362. package/assets/devtools-ui/chunk.Ddeqj5gv.js +0 -1
  363. package/assets/devtools-ui/chunk.Ddeqj5gv.js.br +0 -0
  364. package/assets/devtools-ui/chunk.DpRnB4vJ.js +0 -1
  365. package/assets/devtools-ui/chunk.DpRnB4vJ.js.br +0 -0
  366. package/assets/devtools-ui/chunk.DxPGTlsg.js +0 -1
  367. package/assets/devtools-ui/chunk.DxPGTlsg.js.br +0 -0
  368. package/assets/devtools-ui/chunk.G7_MMBJS.js +0 -1
  369. package/assets/devtools-ui/chunk.G7_MMBJS.js.br +0 -0
  370. package/assets/devtools-ui/chunk.M6wyKO_3.js.br +0 -0
  371. package/assets/devtools-ui/chunk.OUxNGmQ6.js +0 -1
  372. package/assets/devtools-ui/chunk.OUxNGmQ6.js.br +0 -0
  373. package/assets/devtools-ui/chunk.T1kle-fF.js +0 -1
  374. package/assets/devtools-ui/chunk.T1kle-fF.js.br +0 -0
  375. package/assets/devtools-ui/chunk.WjpsbQAv.js +0 -1
  376. package/assets/devtools-ui/chunk.WjpsbQAv.js.br +0 -0
  377. package/assets/devtools-ui/chunk.c6YgVx86.js +0 -1
  378. package/assets/devtools-ui/chunk.c6YgVx86.js.br +0 -0
  379. package/assets/devtools-ui/chunk.dwU3E_MU.js +0 -1
  380. package/assets/devtools-ui/chunk.dwU3E_MU.js.br +0 -0
  381. package/assets/devtools-ui/chunk.lJL-lgnW.js.br +0 -0
  382. package/assets/devtools-ui/chunk.lPWRmvA-.js +0 -7
  383. package/assets/devtools-ui/chunk.lPWRmvA-.js.br +0 -0
  384. package/assets/devtools-ui/chunk.p3HJvugM.js +0 -1
  385. package/assets/devtools-ui/chunk.p3HJvugM.js.br +0 -0
  386. package/assets/devtools-ui/chunk.r_Xoa_CI.js +0 -1
  387. package/assets/devtools-ui/chunk.r_Xoa_CI.js.br +0 -0
  388. package/assets/devtools-ui/chunk.sRNuTYXb.js +0 -1
  389. package/assets/devtools-ui/chunk.sRNuTYXb.js.br +0 -0
  390. package/assets/devtools-ui/chunk.tUjcyX5C.js +0 -1
  391. package/assets/devtools-ui/chunk.tUjcyX5C.js.br +0 -0
  392. package/assets/devtools-ui/chunk.thjBxvCA.js +0 -1
  393. package/assets/devtools-ui/chunk.thjBxvCA.js.br +0 -0
  394. package/assets/devtools-ui/entry.GYhBVRpC.js +0 -78
  395. package/assets/devtools-ui/entry.GYhBVRpC.js.br +0 -0
  396. package/src/server/links/services/DefinitionsPool.spec.ts +0 -86
  397. package/src/server/links/services/DefinitionsPool.ts +0 -43
@@ -1,6 +1,7 @@
1
1
  import type { SQL } from "drizzle-orm";
2
2
  import type { EntityPrimitive } from "../primitives/$entity.ts";
3
3
  import type { SequencePrimitive } from "../primitives/$sequence.ts";
4
+ import type { ViewPrimitive } from "../primitives/$view.ts";
4
5
 
5
6
  /**
6
7
  * Database-specific table configuration functions
@@ -41,6 +42,17 @@ export abstract class ModelBuilder {
41
42
  },
42
43
  ): void;
43
44
 
45
+ /**
46
+ * Build a view from a view primitive.
47
+ */
48
+ abstract buildView(
49
+ view: ViewPrimitive,
50
+ options: {
51
+ tables: Map<string, unknown>;
52
+ schema: string;
53
+ },
54
+ ): void;
55
+
44
56
  /**
45
57
  * Build a sequence from a sequence primitive.
46
58
  */
@@ -111,19 +123,17 @@ export abstract class ModelBuilder {
111
123
 
112
124
  // Use original camelCase property name for lookup
113
125
  if ((self as any)[indexDef.column]) {
114
- if (indexDef.unique) {
115
- configs.push(
116
- builders
126
+ let idx = indexDef.unique
127
+ ? builders
117
128
  .uniqueIndex(indexName)
118
- .on((self as any)[indexDef.column]),
119
- );
120
- } else {
121
- configs.push(
122
- builders
129
+ .on((self as any)[indexDef.column])
130
+ : builders
123
131
  .index(indexName)
124
- .on((self as any)[indexDef.column]),
125
- );
132
+ .on((self as any)[indexDef.column]);
133
+ if ("where" in indexDef && indexDef.where) {
134
+ idx = (idx as any).where(indexDef.where);
126
135
  }
136
+ configs.push(idx);
127
137
  }
128
138
  } else if ("columns" in indexDef) {
129
139
  const columnNames = indexDef.columns.map((col: any) =>
@@ -138,11 +148,13 @@ export abstract class ModelBuilder {
138
148
  .filter(Boolean);
139
149
 
140
150
  if (cols.length === indexDef.columns.length) {
141
- if (indexDef.unique) {
142
- configs.push(builders.uniqueIndex(indexName).on(...cols));
143
- } else {
144
- configs.push(builders.index(indexName).on(...cols));
151
+ let idx = indexDef.unique
152
+ ? builders.uniqueIndex(indexName).on(...cols)
153
+ : builders.index(indexName).on(...cols);
154
+ if ("where" in indexDef && indexDef.where) {
155
+ idx = (idx as any).where(indexDef.where);
145
156
  }
157
+ configs.push(idx);
146
158
  }
147
159
  }
148
160
  }
@@ -12,6 +12,7 @@ import {
12
12
  arrayOverlaps,
13
13
  between,
14
14
  eq,
15
+ exists,
15
16
  gt,
16
17
  gte,
17
18
  ilike,
@@ -25,11 +26,13 @@ import {
25
26
  ne,
26
27
  not,
27
28
  notBetween,
29
+ notExists,
28
30
  notIlike,
29
31
  notInArray,
30
32
  notLike,
31
33
  or,
32
34
  type SQL,
35
+ type SQLWrapper,
33
36
  sql,
34
37
  } from "drizzle-orm";
35
38
  import type { PgColumn } from "drizzle-orm/pg-core";
@@ -154,6 +157,16 @@ export class QueryManager {
154
157
  }
155
158
  }
156
159
 
160
+ if (key === "exists") {
161
+ conditions.push(exists(operator as SQLWrapper));
162
+ continue;
163
+ }
164
+
165
+ if (key === "notExists") {
166
+ conditions.push(notExists(operator as SQLWrapper));
167
+ continue;
168
+ }
169
+
157
170
  if (operator) {
158
171
  const column = col(key);
159
172
  const sql = this.mapOperatorToSql(
@@ -12,7 +12,23 @@ import {
12
12
  } from "alepha";
13
13
  import { type DateTime, DateTimeProvider } from "alepha/datetime";
14
14
  import { $logger } from "alepha/logger";
15
- import { asc, desc, type SQL } from "drizzle-orm";
15
+ import {
16
+ asc,
17
+ avg,
18
+ count,
19
+ desc,
20
+ and as drizzleAnd,
21
+ eq as drizzleEq,
22
+ gt,
23
+ gte,
24
+ lt,
25
+ lte,
26
+ max,
27
+ min,
28
+ ne,
29
+ type SQL,
30
+ sum,
31
+ } from "drizzle-orm";
16
32
  import type {
17
33
  LockConfig,
18
34
  LockStrength,
@@ -41,6 +57,12 @@ import { DbNotNullError } from "../errors/DbNotNullError.ts";
41
57
  import { DbTableNotFoundError } from "../errors/DbTableNotFoundError.ts";
42
58
  import { DbVersionMismatchError } from "../errors/DbVersionMismatchError.ts";
43
59
  import { getAttrFields, type PgAttrField } from "../helpers/pgAttr.ts";
60
+ import type {
61
+ AggregateOp,
62
+ AggregateQuery,
63
+ AggregateResult,
64
+ AggregateSelect,
65
+ } from "../interfaces/AggregateQuery.ts";
44
66
  import type {
45
67
  PgQuery,
46
68
  PgQueryRelations,
@@ -55,6 +77,7 @@ import type {
55
77
  EntityPrimitive,
56
78
  SchemaToTableConfig,
57
79
  } from "../primitives/$entity.ts";
80
+ import { DbCacheProvider } from "../providers/DbCacheProvider.ts";
58
81
  import {
59
82
  DatabaseProvider,
60
83
  type SQLLike,
@@ -72,6 +95,7 @@ export abstract class Repository<T extends TObject> {
72
95
  protected readonly relationManager = $inject(PgRelationManager);
73
96
  protected readonly queryManager = $inject(QueryManager);
74
97
  protected readonly dateTimeProvider = $inject(DateTimeProvider);
98
+ protected readonly dbCache = new DbCacheProvider();
75
99
  protected readonly alepha = $inject(Alepha);
76
100
 
77
101
  static of<T extends TObject>(
@@ -88,7 +112,11 @@ export abstract class Repository<T extends TObject> {
88
112
  constructor(entity: EntityPrimitive<T>, provider = DatabaseProvider) {
89
113
  this.entity = entity;
90
114
  this.provider = this.alepha.inject(provider);
91
- this.provider.registerEntity(entity as EntityPrimitive);
115
+ if ((entity as any).isView) {
116
+ this.provider.registerView(entity as any);
117
+ } else {
118
+ this.provider.registerEntity(entity as EntityPrimitive);
119
+ }
92
120
  }
93
121
 
94
122
  /**
@@ -120,6 +148,13 @@ export abstract class Repository<T extends TObject> {
120
148
  return this.entity.name;
121
149
  }
122
150
 
151
+ /**
152
+ * Whether this repository is backed by a view (read-only).
153
+ */
154
+ public get isReadOnly(): boolean {
155
+ return (this.entity as any).isView === true;
156
+ }
157
+
123
158
  /**
124
159
  * Getter for the database connection from the database provider.
125
160
  *
@@ -179,6 +214,17 @@ export abstract class Repository<T extends TObject> {
179
214
  throw this.handleError(error, "Custom query has failed");
180
215
  }
181
216
 
217
+ if (rows == null) {
218
+ return [];
219
+ }
220
+
221
+ if (!Array.isArray(rows)) {
222
+ throw new DbError(
223
+ "Invalid query result. Expected an array of rows, but got: " +
224
+ JSON.stringify(rows),
225
+ );
226
+ }
227
+
182
228
  return rows.map((it) => {
183
229
  return this.clean(
184
230
  this.mapRawFieldsToEntity(it),
@@ -236,8 +282,10 @@ export abstract class Repository<T extends TObject> {
236
282
  ) => Promise<T>,
237
283
  config?: PgTransactionConfig,
238
284
  ): Promise<T> {
239
- if (this.provider.driver === "pglite") {
240
- this.log.warn("Transactions are not supported with pglite driver");
285
+ if (!this.provider.supportsTransactions) {
286
+ this.log.warn(
287
+ `Transactions are not supported with ${this.provider.driver} driver`,
288
+ );
241
289
  return await transaction(null as any);
242
290
  }
243
291
 
@@ -310,6 +358,16 @@ export abstract class Repository<T extends TObject> {
310
358
  query: PgQueryRelations<T, R> = {},
311
359
  opts: StatementOptions = {},
312
360
  ): Promise<PgStatic<T, R>[]> {
361
+ // Check cache
362
+ if (opts.cache) {
363
+ const cacheKey = opts.cache.key ?? this.buildCacheKey("findMany", query);
364
+ const cached = await this.dbCache.get<PgStatic<T, R>[]>(
365
+ this.tableName,
366
+ cacheKey,
367
+ );
368
+ if (cached) return cached;
369
+ }
370
+
313
371
  await this.alepha.events.emit("repository:read:before", {
314
372
  tableName: this.tableName,
315
373
  query,
@@ -414,7 +472,21 @@ export abstract class Repository<T extends TObject> {
414
472
  entities: rows,
415
473
  });
416
474
 
417
- return rows as PgStatic<T, R>[];
475
+ const result = rows as PgStatic<T, R>[];
476
+
477
+ // Store in cache
478
+ if (opts.cache) {
479
+ const cacheKey =
480
+ opts.cache.key ?? this.buildCacheKey("findMany", query);
481
+ await this.dbCache.set(
482
+ this.tableName,
483
+ cacheKey,
484
+ result,
485
+ opts.cache.ttl,
486
+ );
487
+ }
488
+
489
+ return result;
418
490
  } catch (error) {
419
491
  throw this.handleError(error, "Query select has failed");
420
492
  }
@@ -590,6 +662,7 @@ export abstract class Repository<T extends TObject> {
590
662
  data: Static<TObjectInsert<T>>,
591
663
  opts: StatementOptions = {},
592
664
  ): Promise<Static<T>> {
665
+ this.assertWritable();
593
666
  await this.alepha.events.emit("repository:create:before", {
594
667
  tableName: this.tableName,
595
668
  data,
@@ -601,6 +674,8 @@ export abstract class Repository<T extends TObject> {
601
674
  .returning(this.table)
602
675
  .then(([it]) => this.clean(it, this.entity.schema));
603
676
 
677
+ this.dbCache.invalidateTable(this.tableName).catch(() => {});
678
+
604
679
  await this.alepha.events.emit("repository:create:after", {
605
680
  tableName: this.tableName,
606
681
  data,
@@ -626,6 +701,7 @@ export abstract class Repository<T extends TObject> {
626
701
  values: Array<Static<TObjectInsert<T>>>,
627
702
  opts: StatementOptions & { batchSize?: number } = {},
628
703
  ): Promise<Static<T>[]> {
704
+ this.assertWritable();
629
705
  if (values.length === 0) {
630
706
  return [];
631
707
  }
@@ -648,6 +724,8 @@ export abstract class Repository<T extends TObject> {
648
724
  allEntities.push(...entities);
649
725
  }
650
726
 
727
+ this.dbCache.invalidateTable(this.tableName).catch(() => {});
728
+
651
729
  await this.alepha.events.emit("repository:create:after", {
652
730
  tableName: this.tableName,
653
731
  data: values,
@@ -696,6 +774,7 @@ export abstract class Repository<T extends TObject> {
696
774
  set?: WithSQL<Static<TObjectUpdate<T>>>;
697
775
  } = {},
698
776
  ): Promise<Static<T>> {
777
+ this.assertWritable();
699
778
  await this.alepha.events.emit("repository:create:before", {
700
779
  tableName: this.tableName,
701
780
  data,
@@ -741,6 +820,8 @@ export abstract class Repository<T extends TObject> {
741
820
  .returning(this.table)
742
821
  .then(([it]) => this.clean(it, this.entity.schema));
743
822
 
823
+ this.dbCache.invalidateTable(this.tableName).catch(() => {});
824
+
744
825
  await this.alepha.events.emit("repository:create:after", {
745
826
  tableName: this.tableName,
746
827
  data,
@@ -763,6 +844,7 @@ export abstract class Repository<T extends TObject> {
763
844
  data: WithSQL<Static<TObjectUpdate<T>>>,
764
845
  opts: StatementOptions = {},
765
846
  ): Promise<Static<T>> {
847
+ this.assertWritable();
766
848
  await this.alepha.events.emit("repository:update:before", {
767
849
  tableName: this.tableName,
768
850
  where,
@@ -802,6 +884,8 @@ export abstract class Repository<T extends TObject> {
802
884
  try {
803
885
  const entity = this.clean(response[0], this.entity.schema);
804
886
 
887
+ this.dbCache.invalidateTable(this.tableName).catch(() => {});
888
+
805
889
  await this.alepha.events.emit("repository:update:after", {
806
890
  tableName: this.tableName,
807
891
  where,
@@ -839,6 +923,7 @@ export abstract class Repository<T extends TObject> {
839
923
  entity: Static<T>,
840
924
  opts: StatementOptions = {},
841
925
  ): Promise<void> {
926
+ this.assertWritable();
842
927
  const row = entity as any;
843
928
 
844
929
  const id = row[this.id.key];
@@ -924,6 +1009,7 @@ export abstract class Repository<T extends TObject> {
924
1009
  data: WithSQL<Static<TObjectUpdate<T>>>,
925
1010
  opts: StatementOptions = {},
926
1011
  ): Promise<Array<number | string>> {
1012
+ this.assertWritable();
927
1013
  await this.alepha.events.emit("repository:update:before", {
928
1014
  tableName: this.tableName,
929
1015
  where,
@@ -950,6 +1036,8 @@ export abstract class Repository<T extends TObject> {
950
1036
  .where(this.toSQL(where))
951
1037
  .returning();
952
1038
 
1039
+ this.dbCache.invalidateTable(this.tableName).catch(() => {});
1040
+
953
1041
  await this.alepha.events.emit("repository:update:after", {
954
1042
  tableName: this.tableName,
955
1043
  where,
@@ -971,6 +1059,7 @@ export abstract class Repository<T extends TObject> {
971
1059
  where: PgQueryWhereOrSQL<T> = {},
972
1060
  opts: StatementOptions = {},
973
1061
  ): Promise<Array<number | string>> {
1062
+ this.assertWritable();
974
1063
  const deletedAt = this.deletedAt();
975
1064
  if (deletedAt && !opts.force) {
976
1065
  return await this.updateMany(
@@ -993,6 +1082,8 @@ export abstract class Repository<T extends TObject> {
993
1082
  .returning({ id: (this.table as any)[this.id.key] });
994
1083
  const ids = result.map((row) => row.id);
995
1084
 
1085
+ this.dbCache.invalidateTable(this.tableName).catch(() => {});
1086
+
996
1087
  await this.alepha.events.emit("repository:delete:after", {
997
1088
  tableName: this.tableName,
998
1089
  where,
@@ -1087,6 +1178,165 @@ export abstract class Repository<T extends TObject> {
1087
1178
 
1088
1179
  // -------------------------------------------------------------------------------------------------------------------
1089
1180
 
1181
+ /**
1182
+ * Execute an aggregate query with type-safe select, groupBy, and having.
1183
+ *
1184
+ * @example
1185
+ * ```ts
1186
+ * const result = await repo.aggregate({
1187
+ * select: { category: true, amount: { sum: true, avg: true } },
1188
+ * groupBy: ["category"],
1189
+ * having: { amount: { sum: { gt: 100 } } },
1190
+ * orderBy: { column: "amount.sum", direction: "desc" },
1191
+ * });
1192
+ * // result: Array<{ category: string; amount: { sum: number; avg: number } }>
1193
+ * ```
1194
+ */
1195
+ public async aggregate<S extends AggregateSelect<T>>(
1196
+ query: AggregateQuery<T, S>,
1197
+ opts: StatementOptions = {},
1198
+ ): Promise<AggregateResult<T, S>[]> {
1199
+ const AGG_SEPARATOR = "___";
1200
+
1201
+ // Build flat select fields
1202
+ const flatFields: Record<string, any> = {};
1203
+ const aggFn = (op: AggregateOp, column: any) => {
1204
+ switch (op) {
1205
+ case "count":
1206
+ return count(column);
1207
+ case "sum":
1208
+ return sum(column);
1209
+ case "avg":
1210
+ return avg(column);
1211
+ case "min":
1212
+ return min(column);
1213
+ case "max":
1214
+ return max(column);
1215
+ }
1216
+ };
1217
+
1218
+ for (const [key, select] of Object.entries(query.select)) {
1219
+ if (select === true) {
1220
+ flatFields[key] = this.col(key);
1221
+ } else if (typeof select === "object" && select !== null) {
1222
+ for (const op of Object.keys(select) as AggregateOp[]) {
1223
+ if ((select as Record<string, boolean>)[op]) {
1224
+ flatFields[`${key}${AGG_SEPARATOR}${op}`] = aggFn(
1225
+ op,
1226
+ this.col(key),
1227
+ );
1228
+ }
1229
+ }
1230
+ }
1231
+ }
1232
+
1233
+ const db = opts.tx === null ? this.provider.db : (opts.tx ?? this.db);
1234
+ let builder = db.select(flatFields).from(this.table as PgTable);
1235
+
1236
+ // WHERE
1237
+ if (query.where) {
1238
+ const where = this.withDeletedAt(query.where as any, opts);
1239
+ builder = builder.where(this.toSQL(where)) as any;
1240
+ }
1241
+
1242
+ // GROUP BY
1243
+ if (query.groupBy) {
1244
+ builder = builder.groupBy(
1245
+ ...query.groupBy.map((key) => this.col(key as string)),
1246
+ ) as any;
1247
+ }
1248
+
1249
+ // HAVING
1250
+ if (query.having) {
1251
+ const havingConditions: SQL[] = [];
1252
+ for (const [key, ops] of Object.entries(query.having)) {
1253
+ if (!ops || typeof ops !== "object") continue;
1254
+ for (const [op, comparisons] of Object.entries(ops)) {
1255
+ if (!comparisons || typeof comparisons !== "object") continue;
1256
+ const aggExpr = aggFn(op as AggregateOp, this.col(key));
1257
+ for (const [cmp, val] of Object.entries(
1258
+ comparisons as Record<string, number>,
1259
+ )) {
1260
+ switch (cmp) {
1261
+ case "gt":
1262
+ havingConditions.push(gt(aggExpr, val));
1263
+ break;
1264
+ case "gte":
1265
+ havingConditions.push(gte(aggExpr, val));
1266
+ break;
1267
+ case "lt":
1268
+ havingConditions.push(lt(aggExpr, val));
1269
+ break;
1270
+ case "lte":
1271
+ havingConditions.push(lte(aggExpr, val));
1272
+ break;
1273
+ case "eq":
1274
+ havingConditions.push(drizzleEq(aggExpr, val));
1275
+ break;
1276
+ case "ne":
1277
+ havingConditions.push(ne(aggExpr, val));
1278
+ break;
1279
+ }
1280
+ }
1281
+ }
1282
+ }
1283
+ if (havingConditions.length > 0) {
1284
+ builder = builder.having(drizzleAnd(...havingConditions)!) as any;
1285
+ }
1286
+ }
1287
+
1288
+ // ORDER BY
1289
+ if (query.orderBy) {
1290
+ const clauses = this.queryManager.normalizeOrderBy(query.orderBy);
1291
+ builder = builder.orderBy(
1292
+ ...clauses.map((clause) => {
1293
+ // Support dot notation: "amount.sum" → "amount___sum"
1294
+ const colName = clause.column.includes(".")
1295
+ ? clause.column.replace(".", AGG_SEPARATOR)
1296
+ : clause.column;
1297
+ const col = flatFields[colName];
1298
+ if (!col) {
1299
+ throw new AlephaError(
1300
+ `Invalid orderBy column '${clause.column}' in aggregate query`,
1301
+ );
1302
+ }
1303
+ return clause.direction === "desc" ? desc(col) : asc(col);
1304
+ }),
1305
+ ) as any;
1306
+ }
1307
+
1308
+ // LIMIT / OFFSET
1309
+ if (query.limit) {
1310
+ builder = builder.limit(query.limit) as any;
1311
+ }
1312
+ if (query.offset) {
1313
+ builder = builder.offset(query.offset) as any;
1314
+ }
1315
+
1316
+ try {
1317
+ const rows = await builder.execute();
1318
+
1319
+ // Re-nest flat results: { amount___sum: 500 } → { amount: { sum: 500 } }
1320
+ return rows.map((row: any) => {
1321
+ const result: Record<string, any> = {};
1322
+ for (const [flatKey, value] of Object.entries(row)) {
1323
+ if (flatKey.includes(AGG_SEPARATOR)) {
1324
+ const [col, op] = flatKey.split(AGG_SEPARATOR);
1325
+ if (!result[col]) result[col] = {};
1326
+ result[col][op] = value != null ? Number(value) : 0;
1327
+ } else {
1328
+ result[flatKey] = value;
1329
+ }
1330
+ }
1331
+ return result as AggregateResult<T, S>;
1332
+ });
1333
+ } catch (error) {
1334
+ throw this.handleError(error, "Aggregate query has failed");
1335
+ }
1336
+ }
1337
+
1338
+ // -------------------------------------------------------------------------------------------------------------------
1339
+
1090
1340
  // Error message patterns for different database errors
1091
1341
  protected errorPatterns = {
1092
1342
  // Unique constraint violations
@@ -1316,6 +1566,36 @@ export abstract class Repository<T extends TObject> {
1316
1566
  return entity as Static<T>;
1317
1567
  }
1318
1568
 
1569
+ /**
1570
+ * Throw if this repository is read-only (backed by a view).
1571
+ */
1572
+ protected assertWritable(): void {
1573
+ if (this.isReadOnly) {
1574
+ throw new AlephaError(
1575
+ `Cannot write to view '${this.tableName}'. Views are read-only.`,
1576
+ );
1577
+ }
1578
+ }
1579
+
1580
+ /**
1581
+ * Refresh a materialized view. PostgreSQL only.
1582
+ */
1583
+ public async refresh(): Promise<void> {
1584
+ if (!(this.entity as any).materialized) {
1585
+ throw new AlephaError(
1586
+ `Cannot refresh '${this.tableName}'. Only materialized views support refresh.`,
1587
+ );
1588
+ }
1589
+ await this.provider.execute(`REFRESH MATERIALIZED VIEW ${this.tableName}`);
1590
+ }
1591
+
1592
+ /**
1593
+ * Build a cache key from method name and query parameters.
1594
+ */
1595
+ protected buildCacheKey(method: string, query: any): string {
1596
+ return `${method}:${JSON.stringify(query)}`;
1597
+ }
1598
+
1319
1599
  /**
1320
1600
  * Convert a where clause to SQL.
1321
1601
  */
@@ -1399,6 +1679,28 @@ export interface StatementOptions {
1399
1679
  * Force the current time.
1400
1680
  */
1401
1681
  now?: DateTime | string;
1682
+
1683
+ /**
1684
+ * Cache configuration for query results.
1685
+ *
1686
+ * When set, results are stored in an in-memory cache keyed by query parameters.
1687
+ * Any write to this table automatically invalidates all cached queries.
1688
+ *
1689
+ * @example
1690
+ * ```ts
1691
+ * await repo.findMany(query, { cache: { ttl: 60_000 } });
1692
+ * ```
1693
+ */
1694
+ cache?: {
1695
+ /**
1696
+ * Time-to-live in milliseconds.
1697
+ */
1698
+ ttl?: number;
1699
+ /**
1700
+ * Custom cache key. If not provided, a key is derived from the query.
1701
+ */
1702
+ key?: string;
1703
+ };
1402
1704
  }
1403
1705
 
1404
1706
  type WithSQL<T> = {
@@ -16,20 +16,24 @@ import {
16
16
  type SQLiteColumnBuilderBase,
17
17
  type SQLiteTableWithColumns,
18
18
  sqliteTable,
19
+ sqliteView,
19
20
  unique,
20
21
  uniqueIndex,
21
22
  } from "drizzle-orm/sqlite-core";
22
23
  import {
23
24
  PG_CREATED_AT,
25
+ PG_GENERATED,
24
26
  PG_IDENTITY,
25
27
  PG_PRIMARY_KEY,
26
28
  PG_REF,
27
29
  PG_SERIAL,
28
30
  PG_UPDATED_AT,
31
+ type PgGeneratedOptions,
29
32
  type PgRefOptions,
30
33
  } from "../constants/PG_SYMBOLS.ts";
31
34
  import type { EntityPrimitive } from "../primitives/$entity.ts";
32
35
  import type { SequencePrimitive } from "../primitives/$sequence.ts";
36
+ import type { ViewPrimitive } from "../primitives/$view.ts";
33
37
  import { ModelBuilder } from "./ModelBuilder.ts";
34
38
 
35
39
  export class SqliteModelBuilder extends ModelBuilder {
@@ -62,6 +66,33 @@ export class SqliteModelBuilder extends ModelBuilder {
62
66
  options.tables.set(tableName, table);
63
67
  }
64
68
 
69
+ public buildView(
70
+ view: ViewPrimitive,
71
+ options: {
72
+ tables: Map<string, unknown>;
73
+ schema: string;
74
+ },
75
+ ) {
76
+ const viewName = view.name;
77
+ if (options.tables.has(viewName)) {
78
+ return;
79
+ }
80
+
81
+ if (view.materialized) {
82
+ throw new AlephaError("SQLite does not support materialized views");
83
+ }
84
+
85
+ const columns = this.schemaToSqliteColumns(
86
+ viewName,
87
+ view.schema,
88
+ new Map(),
89
+ options.tables,
90
+ );
91
+
92
+ const drizzleView = sqliteView(viewName, columns).existing();
93
+ options.tables.set(viewName, drizzleView);
94
+ }
95
+
65
96
  public buildSequence(
66
97
  sequence: SequencePrimitive,
67
98
  options: {
@@ -151,6 +182,13 @@ export class SqliteModelBuilder extends ModelBuilder {
151
182
  }, config.actions);
152
183
  }
153
184
 
185
+ if (PG_GENERATED in value) {
186
+ const gen = value[PG_GENERATED] as PgGeneratedOptions;
187
+ col = col.generatedAlwaysAs(gen.expression, {
188
+ mode: gen.mode ?? "virtual",
189
+ });
190
+ }
191
+
154
192
  if (schema.required?.includes(key)) {
155
193
  col = col.notNull();
156
194
  }
@@ -1,5 +1,5 @@
1
- import { $module, type Alepha, t } from "alepha";
2
- import { AlephaOrm, DatabaseProvider } from "alepha/orm";
1
+ import { $module, type Alepha } from "alepha";
2
+ import { AlephaOrm, DatabaseProvider, databaseEnvSchema } from "alepha/orm";
3
3
  import { BunPostgresProvider } from "./providers/BunPostgresProvider.ts";
4
4
  import { PglitePostgresProvider } from "./providers/PglitePostgresProvider.ts";
5
5
  import { PostgresProvider } from "./providers/PostgresProvider.ts";
@@ -8,6 +8,7 @@ import { PostgresModelBuilder } from "./services/PostgresModelBuilder.ts";
8
8
  export * from "./providers/BunPostgresProvider.ts";
9
9
  export * from "./providers/PglitePostgresProvider.ts";
10
10
  export * from "./providers/PostgresProvider.ts";
11
+ export * from "./schemas/postgresEnvSchema.ts";
11
12
  export * from "./services/PostgresModelBuilder.ts";
12
13
  export * from "./types/byte.ts";
13
14
 
@@ -21,11 +22,7 @@ export const AlephaOrmPostgres = $module({
21
22
  PostgresModelBuilder,
22
23
  ],
23
24
  register: (alepha: Alepha) => {
24
- const env = alepha.parseEnv(
25
- t.object({
26
- DATABASE_URL: t.optional(t.text()),
27
- }),
28
- );
25
+ const env = alepha.parseEnv(databaseEnvSchema);
29
26
 
30
27
  const url = env.DATABASE_URL;
31
28