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.
- package/assets/devtools-ui/200.html +2 -2
- package/assets/devtools-ui/200.html.br +0 -0
- package/assets/devtools-ui/404.html +2 -2
- package/assets/devtools-ui/404.html.br +0 -0
- package/assets/devtools-ui/{asset.BfSBZ5Dd.css → asset.hG_f8HuK.css} +1 -1
- package/assets/devtools-ui/asset.hG_f8HuK.css.br +0 -0
- package/assets/devtools-ui/chunk.B3au4Lhg.js +1 -0
- package/assets/devtools-ui/chunk.B3au4Lhg.js.br +0 -0
- package/assets/devtools-ui/chunk.BLOrlnMB.js +1 -0
- package/assets/devtools-ui/chunk.BLOrlnMB.js.br +0 -0
- package/assets/devtools-ui/chunk.BLR01ljW.js +1 -0
- package/assets/devtools-ui/chunk.BLR01ljW.js.br +0 -0
- package/assets/devtools-ui/chunk.BTXaIUlA.js +1 -0
- package/assets/devtools-ui/chunk.BTXaIUlA.js.br +0 -0
- package/assets/devtools-ui/{chunk.lJL-lgnW.js → chunk.BhJaxmm8.js} +1 -1
- package/assets/devtools-ui/chunk.BhJaxmm8.js.br +0 -0
- package/assets/devtools-ui/chunk.BtoNxFuL.js +1 -0
- package/assets/devtools-ui/chunk.BtoNxFuL.js.br +0 -0
- package/assets/devtools-ui/chunk.C8YUV2Wd.js +1 -0
- package/assets/devtools-ui/chunk.C8YUV2Wd.js.br +0 -0
- package/assets/devtools-ui/{chunk.M6wyKO_3.js → chunk.CBbIgDzE.js} +2 -2
- package/assets/devtools-ui/chunk.CBbIgDzE.js.br +0 -0
- package/assets/devtools-ui/chunk.CFqIniwA.js +1 -0
- package/assets/devtools-ui/chunk.CFqIniwA.js.br +0 -0
- package/assets/devtools-ui/chunk.CLFF7f7-.js +1 -0
- package/assets/devtools-ui/chunk.CLFF7f7-.js.br +0 -0
- package/assets/devtools-ui/chunk.CRsBbA10.js +1 -0
- package/assets/devtools-ui/chunk.CRsBbA10.js.br +0 -0
- package/assets/devtools-ui/{chunk.DbEH1oOB.js → chunk.CZPo6v95.js} +1 -1
- package/assets/devtools-ui/chunk.CZPo6v95.js.br +0 -0
- package/assets/devtools-ui/chunk.D0fWgNos.js +1 -0
- package/assets/devtools-ui/chunk.D0fWgNos.js.br +1 -0
- package/assets/devtools-ui/chunk.D7-0ziQ6.js +1 -0
- package/assets/devtools-ui/chunk.D7-0ziQ6.js.br +0 -0
- package/assets/devtools-ui/chunk.DAewe0vm.js +1 -0
- package/assets/devtools-ui/chunk.DAewe0vm.js.br +0 -0
- package/assets/devtools-ui/chunk.DJRQEYqK.js +1 -0
- package/assets/devtools-ui/chunk.DJRQEYqK.js.br +0 -0
- package/assets/devtools-ui/{chunk.CZl6J9DF.js → chunk.DMAxv14p.js} +1 -1
- package/assets/devtools-ui/chunk.DMAxv14p.js.br +0 -0
- package/assets/devtools-ui/{chunk.BT2IiBkZ.js → chunk.DMImnNjU.js} +1 -1
- package/assets/devtools-ui/chunk.DMImnNjU.js.br +0 -0
- package/assets/devtools-ui/chunk.DeeQsidk.js +9 -0
- package/assets/devtools-ui/chunk.DeeQsidk.js.br +0 -0
- package/assets/devtools-ui/chunk.DqEwn9Vj.js +7 -0
- package/assets/devtools-ui/chunk.DqEwn9Vj.js.br +0 -0
- package/assets/devtools-ui/chunk.Dt8OsQey.js +1 -0
- package/assets/devtools-ui/chunk.Dt8OsQey.js.br +0 -0
- package/assets/devtools-ui/{chunk.B9pX3zit.js → chunk.Dtp8oa_f.js} +1 -1
- package/assets/devtools-ui/chunk.Dtp8oa_f.js.br +0 -0
- package/assets/devtools-ui/chunk.Dx3JzAYM.js +1 -0
- package/assets/devtools-ui/chunk.Dx3JzAYM.js.br +0 -0
- package/assets/devtools-ui/chunk.GCOj1-5E.js +1 -0
- package/assets/devtools-ui/chunk.GCOj1-5E.js.br +0 -0
- package/assets/devtools-ui/chunk.IC1LD8BH.js +1 -0
- package/assets/devtools-ui/chunk.IC1LD8BH.js.br +0 -0
- package/assets/devtools-ui/chunk.IwuB_TqW.js +1 -0
- package/assets/devtools-ui/chunk.IwuB_TqW.js.br +0 -0
- package/assets/devtools-ui/chunk.Qqapj2zq.js +1 -0
- package/assets/devtools-ui/chunk.Qqapj2zq.js.br +0 -0
- package/assets/devtools-ui/{chunk.C79YouPp.js → chunk.TKKKndOy.js} +1 -1
- package/assets/devtools-ui/chunk.TKKKndOy.js.br +0 -0
- package/assets/devtools-ui/chunk.YHTVhFQT.js +1 -0
- package/assets/devtools-ui/chunk.YHTVhFQT.js.br +0 -0
- package/assets/devtools-ui/chunk.fnod6uEi.js +1 -0
- package/assets/devtools-ui/chunk.fnod6uEi.js.br +0 -0
- package/assets/devtools-ui/chunk.mOCRmXjo.js +1 -0
- package/assets/devtools-ui/chunk.mOCRmXjo.js.br +0 -0
- package/assets/devtools-ui/chunk.qZTNEAK0.js +1 -0
- package/assets/devtools-ui/chunk.qZTNEAK0.js.br +0 -0
- package/assets/devtools-ui/chunk.rc9m0y4-.js +1 -0
- package/assets/devtools-ui/chunk.rc9m0y4-.js.br +0 -0
- package/assets/devtools-ui/entry.Cxc5QLCU.js +80 -0
- package/assets/devtools-ui/entry.Cxc5QLCU.js.br +0 -0
- package/assets/devtools-ui/index.html +2 -2
- package/assets/devtools-ui/index.html.br +0 -0
- package/assets/swagger-ui/swagger-ui-bundle.js +1 -1
- package/assets/swagger-ui/swagger-ui.css +1 -1
- package/dist/api/audits/index.d.ts +61 -5
- package/dist/api/audits/index.d.ts.map +1 -1
- package/dist/api/files/index.d.ts +61 -5
- package/dist/api/files/index.d.ts.map +1 -1
- package/dist/api/jobs/index.d.ts +61 -5
- package/dist/api/jobs/index.d.ts.map +1 -1
- package/dist/api/jobs/index.js +4 -2
- package/dist/api/jobs/index.js.map +1 -1
- package/dist/api/keys/index.d.ts +5 -5
- package/dist/api/notifications/index.browser.js +44 -1
- package/dist/api/notifications/index.browser.js.map +1 -1
- package/dist/api/notifications/index.d.ts +187 -2
- package/dist/api/notifications/index.d.ts.map +1 -1
- package/dist/api/notifications/index.js +143 -8
- package/dist/api/notifications/index.js.map +1 -1
- package/dist/api/parameters/index.d.ts +61 -5
- package/dist/api/parameters/index.d.ts.map +1 -1
- package/dist/api/users/index.d.ts +330 -93
- package/dist/api/users/index.d.ts.map +1 -1
- package/dist/api/users/index.js +27 -36
- package/dist/api/users/index.js.map +1 -1
- package/dist/cli/config/index.d.ts +46 -0
- package/dist/cli/config/index.d.ts.map +1 -0
- package/dist/cli/config/index.js +20 -0
- package/dist/cli/config/index.js.map +1 -0
- package/dist/cli/core/index.d.ts +69 -66
- package/dist/cli/core/index.d.ts.map +1 -1
- package/dist/cli/core/index.js +329 -196
- package/dist/cli/core/index.js.map +1 -1
- package/dist/cli/platform/index.d.ts +302 -63
- package/dist/cli/platform/index.d.ts.map +1 -1
- package/dist/cli/platform/index.js +455 -25
- package/dist/cli/platform/index.js.map +1 -1
- package/dist/core/index.browser.js +125 -87
- package/dist/core/index.browser.js.map +1 -1
- package/dist/core/index.d.ts +62 -53
- package/dist/core/index.d.ts.map +1 -1
- package/dist/core/index.js +125 -87
- package/dist/core/index.js.map +1 -1
- package/dist/core/index.native.js +125 -87
- package/dist/core/index.native.js.map +1 -1
- package/dist/core/index.workerd.js +125 -87
- package/dist/core/index.workerd.js.map +1 -1
- package/dist/crypto/index.d.ts +18 -1
- package/dist/crypto/index.d.ts.map +1 -1
- package/dist/crypto/index.js +29 -3
- package/dist/crypto/index.js.map +1 -1
- package/dist/devtools/index.js +3 -12
- package/dist/devtools/index.js.map +1 -1
- package/dist/logger/index.d.ts +10 -1
- package/dist/logger/index.d.ts.map +1 -1
- package/dist/logger/index.js +19 -9
- package/dist/logger/index.js.map +1 -1
- package/dist/orm/core/index.browser.js +57 -1
- package/dist/orm/core/index.browser.js.map +1 -1
- package/dist/orm/core/index.bun.js +378 -19
- package/dist/orm/core/index.bun.js.map +1 -1
- package/dist/orm/core/index.d.ts +328 -9
- package/dist/orm/core/index.d.ts.map +1 -1
- package/dist/orm/core/index.js +384 -21
- package/dist/orm/core/index.js.map +1 -1
- package/dist/orm/postgres/index.bun.js +49 -17
- package/dist/orm/postgres/index.bun.js.map +1 -1
- package/dist/orm/postgres/index.d.ts +47 -21
- package/dist/orm/postgres/index.d.ts.map +1 -1
- package/dist/orm/postgres/index.js +52 -17
- package/dist/orm/postgres/index.js.map +1 -1
- package/dist/react/core/index.d.ts +1 -1
- package/dist/react/core/index.d.ts.map +1 -1
- package/dist/react/core/index.js +6 -1
- package/dist/react/core/index.js.map +1 -1
- package/dist/react/form/index.d.ts +28 -18
- package/dist/react/form/index.d.ts.map +1 -1
- package/dist/react/form/index.js +92 -56
- package/dist/react/form/index.js.map +1 -1
- package/dist/react/router/index.browser.js +448 -116
- package/dist/react/router/index.browser.js.map +1 -1
- package/dist/react/router/index.d.ts +102 -40
- package/dist/react/router/index.d.ts.map +1 -1
- package/dist/react/router/index.js +453 -92
- package/dist/react/router/index.js.map +1 -1
- package/dist/security/index.d.ts +3 -11
- package/dist/security/index.d.ts.map +1 -1
- package/dist/security/index.js +6 -11
- package/dist/security/index.js.map +1 -1
- package/dist/server/auth/index.d.ts +22 -24
- package/dist/server/auth/index.d.ts.map +1 -1
- package/dist/server/auth/index.js +102 -82
- package/dist/server/auth/index.js.map +1 -1
- package/dist/server/cookies/index.d.ts +7 -4
- package/dist/server/cookies/index.d.ts.map +1 -1
- package/dist/server/cookies/index.js +13 -12
- package/dist/server/cookies/index.js.map +1 -1
- package/dist/server/core/index.d.ts +288 -4
- package/dist/server/core/index.d.ts.map +1 -1
- package/dist/server/core/index.js +375 -2
- package/dist/server/core/index.js.map +1 -1
- package/dist/server/links/index.browser.js +10 -71
- package/dist/server/links/index.browser.js.map +1 -1
- package/dist/server/links/index.d.ts +32 -49
- package/dist/server/links/index.d.ts.map +1 -1
- package/dist/server/links/index.js +73 -100
- package/dist/server/links/index.js.map +1 -1
- package/dist/system/index.browser.js +221 -2
- package/dist/system/index.browser.js.map +1 -1
- package/dist/system/index.d.ts +63 -1
- package/dist/system/index.d.ts.map +1 -1
- package/dist/system/index.js +221 -1
- package/dist/system/index.js.map +1 -1
- package/dist/system/index.workerd.js +224 -4
- package/dist/system/index.workerd.js.map +1 -1
- package/package.json +10 -5
- package/src/api/jobs/providers/JobProvider.ts +6 -3
- package/src/api/notifications/controllers/AdminNotificationController.ts +83 -0
- package/src/api/notifications/index.browser.ts +3 -0
- package/src/api/notifications/index.ts +14 -2
- package/src/api/notifications/jobs/NotificationJobs.ts +11 -2
- package/src/api/notifications/schemas/notificationDetailResourceSchema.ts +20 -0
- package/src/api/notifications/schemas/notificationQuerySchema.ts +19 -0
- package/src/api/notifications/schemas/notificationResourceSchema.ts +18 -0
- package/src/api/notifications/services/NotificationSenderService.ts +15 -2
- package/src/api/users/atoms/realmAuthSettingsAtom.ts +28 -32
- package/src/api/users/buckets/UserBuckets.ts +1 -1
- package/src/api/users/jobs/UserJobs.ts +1 -1
- package/src/api/users/primitives/$realm.ts +8 -49
- package/src/api/users/providers/RealmProvider.ts +2 -3
- package/src/api/users/services/RegistrationService.spec.ts +7 -7
- package/src/api/users/services/RegistrationService.ts +3 -3
- package/src/api/users/services/SessionService.spec.ts +4 -4
- package/src/api/users/services/SessionService.ts +3 -3
- package/src/cli/{core → config}/defineConfig.ts +14 -20
- package/src/cli/config/index.ts +1 -0
- package/src/cli/core/commands/db.ts +65 -1
- package/src/cli/core/commands/dev.ts +1 -0
- package/src/cli/core/commands/init.ts +2 -192
- package/src/cli/core/index.ts +34 -11
- package/src/cli/core/providers/ViteDevServerProvider.ts +52 -13
- package/src/cli/core/services/PackageManagerUtils.ts +43 -21
- package/src/cli/core/services/ProjectScaffolder.ts +214 -2
- package/src/cli/core/services/ViteUtils.ts +57 -0
- package/src/cli/core/tasks/BuildClientTask.ts +7 -2
- package/src/cli/core/tasks/BuildCloudflareTask.ts +4 -12
- package/src/cli/core/tasks/BuildServerTask.ts +2 -0
- package/src/cli/core/tasks/BuildVercelTask.ts +165 -168
- package/src/cli/core/templates/alephaConfigTs.ts +1 -1
- package/src/cli/core/templates/apiAppSecurityTs.ts +5 -8
- package/src/cli/core/templates/tsconfigJson.ts +6 -1
- package/src/cli/platform/adapters/CloudflareAdapter.spec.ts +1 -1
- package/src/cli/platform/adapters/CloudflareAdapter.ts +30 -29
- package/src/cli/platform/atoms/platformOptions.ts +21 -0
- package/src/cli/platform/commands/SecretsCommand.spec.ts +298 -0
- package/src/cli/platform/commands/SecretsCommand.ts +283 -0
- package/src/cli/platform/commands/platform.ts +12 -0
- package/src/cli/platform/index.ts +14 -28
- package/src/cli/platform/providers/GitHubSecretStore.spec.ts +153 -0
- package/src/cli/platform/providers/GitHubSecretStore.ts +112 -0
- package/src/cli/platform/providers/MemorySecretStore.ts +114 -0
- package/src/cli/platform/providers/SecretStoreProvider.ts +39 -0
- package/src/cli/platform/schemas/cloudflare.ts +2 -0
- package/src/cli/platform/services/CloudflareApi.ts +5 -2
- package/src/cli/platform/services/DockerComposeGenerator.spec.ts +115 -0
- package/src/cli/platform/services/DockerComposeGenerator.ts +46 -1
- package/src/cli/platform/services/SecretFilterService.spec.ts +111 -0
- package/src/cli/platform/services/SecretFilterService.ts +54 -0
- package/src/core/Alepha.ts +94 -25
- package/src/core/__tests__/Alepha-parseEnv.spec.ts +20 -0
- package/src/core/primitives/$memoize.ts +38 -26
- package/src/core/providers/AlsProvider.ts +2 -0
- package/src/core/providers/EventManager.ts +4 -0
- package/src/core/providers/KeylessJsonSchemaCodec.spec.ts +1 -4
- package/src/core/providers/KeylessJsonSchemaCodec.ts +19 -125
- package/src/core/providers/SchemaValidator.spec.ts +36 -0
- package/src/core/providers/SchemaValidator.ts +9 -0
- package/src/crypto/index.ts +6 -1
- package/src/crypto/providers/SecretProvider.ts +36 -0
- package/src/devtools/providers/DevToolsProvider.ts +3 -12
- package/src/logger/index.ts +33 -6
- package/src/logger/providers/PrettyFormatterProvider.ts +5 -3
- package/src/orm/__tests__/orm-next-tests.ts +492 -0
- package/src/orm/__tests__/orm-next.spec.ts +140 -0
- package/src/orm/core/constants/PG_SYMBOLS.ts +17 -0
- package/src/orm/core/index.bun.ts +3 -6
- package/src/orm/core/index.shared-server.ts +2 -0
- package/src/orm/core/index.shared.ts +2 -0
- package/src/orm/core/index.ts +5 -7
- package/src/orm/core/interfaces/AggregateQuery.ts +103 -0
- package/src/orm/core/interfaces/PgQueryWhere.ts +7 -0
- package/src/orm/core/primitives/$entity.ts +8 -0
- package/src/orm/core/primitives/$repository.ts +6 -3
- package/src/orm/core/primitives/$view.ts +88 -0
- package/src/orm/core/providers/DbCacheProvider.ts +66 -0
- package/src/orm/core/providers/DrizzleKitProvider.ts +42 -0
- package/src/orm/core/providers/drivers/BunSqliteProvider.ts +2 -3
- package/src/orm/core/providers/drivers/CloudflareD1Provider.ts +12 -0
- package/src/orm/core/providers/drivers/DatabaseProvider.ts +39 -0
- package/src/orm/core/providers/drivers/NodeSqliteProvider.ts +2 -3
- package/src/orm/core/schemas/databaseEnvSchema.ts +31 -0
- package/src/orm/core/schemas/insertSchema.ts +13 -3
- package/src/orm/core/schemas/updateSchema.ts +14 -3
- package/src/orm/core/services/ModelBuilder.ts +26 -14
- package/src/orm/core/services/QueryManager.ts +13 -0
- package/src/orm/core/services/Repository.ts +307 -5
- package/src/orm/core/services/SqliteModelBuilder.ts +38 -0
- package/src/orm/postgres/index.bun.ts +4 -7
- package/src/orm/postgres/index.ts +4 -7
- package/src/orm/postgres/providers/BunPostgresProvider.ts +12 -2
- package/src/orm/postgres/providers/NodePostgresProvider.ts +7 -0
- package/src/orm/postgres/providers/PglitePostgresProvider.ts +10 -17
- package/src/orm/postgres/providers/PostgresProvider.ts +7 -36
- package/src/orm/postgres/schemas/postgresEnvSchema.ts +32 -0
- package/src/orm/postgres/services/PostgresModelBuilder.ts +40 -0
- package/src/react/core/components/ErrorBoundary.tsx +5 -2
- package/src/react/form/hooks/useFieldValue.ts +34 -0
- package/src/react/form/hooks/useForm.browser.spec.tsx +94 -9
- package/src/react/form/hooks/useForm.ts +14 -2
- package/src/react/form/hooks/useFormState.ts +10 -10
- package/src/react/form/hooks/useFormValues.ts +29 -0
- package/src/react/form/index.ts +3 -1
- package/src/react/form/services/FormModel.ts +53 -122
- package/src/react/router/components/ErrorViewer.tsx +333 -34
- package/src/react/router/components/NestedView.tsx +10 -3
- package/src/react/router/primitives/$page.browser.spec.tsx +34 -0
- package/src/react/router/primitives/$page.spec.tsx +20 -0
- package/src/react/router/primitives/$page.ts +24 -0
- package/src/react/router/providers/ReactBrowserRouterProvider.ts +14 -2
- package/src/react/router/providers/ReactPageProvider.ts +156 -73
- package/src/react/router/providers/ReactServerProvider.ts +40 -2
- package/src/react/router/providers/ReactServerTemplateProvider.ts +13 -1
- package/src/security/providers/SecurityProvider.ts +5 -27
- package/src/server/auth/primitives/$auth.ts +52 -19
- package/src/server/auth/providers/ServerAuthProvider.ts +145 -139
- package/src/server/cookies/providers/ServerCookiesProvider.ts +12 -24
- package/src/server/core/index.ts +3 -1
- package/src/server/core/primitives/$sse.spec.ts +315 -0
- package/src/server/core/primitives/$sse.ts +715 -0
- package/src/server/links/index.browser.ts +1 -3
- package/src/server/links/index.ts +0 -3
- package/src/server/links/providers/LinkProvider.spec.ts +12 -21
- package/src/server/links/providers/LinkProvider.ts +20 -52
- package/src/server/links/providers/ServerLinksProvider.spec.ts +106 -0
- package/src/server/links/providers/ServerLinksProvider.ts +113 -73
- package/src/server/links/schemas/apiLinksResponseSchema.ts +4 -21
- package/src/server/links/services/BatchCollector.ts +5 -3
- package/src/system/index.browser.ts +1 -0
- package/src/system/index.ts +3 -0
- package/src/system/index.workerd.ts +39 -1
- package/src/system/providers/WorkerdFileSystemProvider.ts +365 -0
- package/assets/devtools-ui/asset.BfSBZ5Dd.css.br +0 -0
- package/assets/devtools-ui/chunk.2NYaoqWt.js +0 -1
- package/assets/devtools-ui/chunk.2NYaoqWt.js.br +0 -0
- package/assets/devtools-ui/chunk.B052Z_xQ.js +0 -1
- package/assets/devtools-ui/chunk.B052Z_xQ.js.br +0 -0
- package/assets/devtools-ui/chunk.B4kVY90C.js +0 -1
- package/assets/devtools-ui/chunk.B4kVY90C.js.br +0 -0
- package/assets/devtools-ui/chunk.B7QJXctB.js +0 -1
- package/assets/devtools-ui/chunk.B7QJXctB.js.br +0 -0
- package/assets/devtools-ui/chunk.B9pX3zit.js.br +0 -0
- package/assets/devtools-ui/chunk.BKF9JxIo.js +0 -1
- package/assets/devtools-ui/chunk.BKF9JxIo.js.br +0 -0
- package/assets/devtools-ui/chunk.BOHgdTP-.js +0 -1
- package/assets/devtools-ui/chunk.BOHgdTP-.js.br +0 -0
- package/assets/devtools-ui/chunk.BOVFxkYC.js +0 -1
- package/assets/devtools-ui/chunk.BOVFxkYC.js.br +0 -0
- package/assets/devtools-ui/chunk.BR842zj5.js +0 -1
- package/assets/devtools-ui/chunk.BR842zj5.js.br +0 -0
- package/assets/devtools-ui/chunk.BT2IiBkZ.js.br +0 -0
- package/assets/devtools-ui/chunk.C79YouPp.js.br +0 -0
- package/assets/devtools-ui/chunk.C8mlBrjW.js +0 -9
- package/assets/devtools-ui/chunk.C8mlBrjW.js.br +0 -0
- package/assets/devtools-ui/chunk.CK0ow3AZ.js +0 -1
- package/assets/devtools-ui/chunk.CK0ow3AZ.js.br +0 -0
- package/assets/devtools-ui/chunk.CZl6J9DF.js.br +0 -0
- package/assets/devtools-ui/chunk.CdNr0YzS.js +0 -1
- package/assets/devtools-ui/chunk.CdNr0YzS.js.br +0 -0
- package/assets/devtools-ui/chunk.Ce6_6iIF.js +0 -1
- package/assets/devtools-ui/chunk.Ce6_6iIF.js.br +0 -0
- package/assets/devtools-ui/chunk.CpyDMr6O.js +0 -1
- package/assets/devtools-ui/chunk.CpyDMr6O.js.br +0 -0
- package/assets/devtools-ui/chunk.CyPmvPnY.js +0 -1
- package/assets/devtools-ui/chunk.CyPmvPnY.js.br +0 -0
- package/assets/devtools-ui/chunk.DTI_geWu.js +0 -1
- package/assets/devtools-ui/chunk.DTI_geWu.js.br +0 -0
- package/assets/devtools-ui/chunk.DbEH1oOB.js.br +0 -0
- package/assets/devtools-ui/chunk.Ddeqj5gv.js +0 -1
- package/assets/devtools-ui/chunk.Ddeqj5gv.js.br +0 -0
- package/assets/devtools-ui/chunk.DpRnB4vJ.js +0 -1
- package/assets/devtools-ui/chunk.DpRnB4vJ.js.br +0 -0
- package/assets/devtools-ui/chunk.DxPGTlsg.js +0 -1
- package/assets/devtools-ui/chunk.DxPGTlsg.js.br +0 -0
- package/assets/devtools-ui/chunk.G7_MMBJS.js +0 -1
- package/assets/devtools-ui/chunk.G7_MMBJS.js.br +0 -0
- package/assets/devtools-ui/chunk.M6wyKO_3.js.br +0 -0
- package/assets/devtools-ui/chunk.OUxNGmQ6.js +0 -1
- package/assets/devtools-ui/chunk.OUxNGmQ6.js.br +0 -0
- package/assets/devtools-ui/chunk.T1kle-fF.js +0 -1
- package/assets/devtools-ui/chunk.T1kle-fF.js.br +0 -0
- package/assets/devtools-ui/chunk.WjpsbQAv.js +0 -1
- package/assets/devtools-ui/chunk.WjpsbQAv.js.br +0 -0
- package/assets/devtools-ui/chunk.c6YgVx86.js +0 -1
- package/assets/devtools-ui/chunk.c6YgVx86.js.br +0 -0
- package/assets/devtools-ui/chunk.dwU3E_MU.js +0 -1
- package/assets/devtools-ui/chunk.dwU3E_MU.js.br +0 -0
- package/assets/devtools-ui/chunk.lJL-lgnW.js.br +0 -0
- package/assets/devtools-ui/chunk.lPWRmvA-.js +0 -7
- package/assets/devtools-ui/chunk.lPWRmvA-.js.br +0 -0
- package/assets/devtools-ui/chunk.p3HJvugM.js +0 -1
- package/assets/devtools-ui/chunk.p3HJvugM.js.br +0 -0
- package/assets/devtools-ui/chunk.r_Xoa_CI.js +0 -1
- package/assets/devtools-ui/chunk.r_Xoa_CI.js.br +0 -0
- package/assets/devtools-ui/chunk.sRNuTYXb.js +0 -1
- package/assets/devtools-ui/chunk.sRNuTYXb.js.br +0 -0
- package/assets/devtools-ui/chunk.tUjcyX5C.js +0 -1
- package/assets/devtools-ui/chunk.tUjcyX5C.js.br +0 -0
- package/assets/devtools-ui/chunk.thjBxvCA.js +0 -1
- package/assets/devtools-ui/chunk.thjBxvCA.js.br +0 -0
- package/assets/devtools-ui/entry.GYhBVRpC.js +0 -78
- package/assets/devtools-ui/entry.GYhBVRpC.js.br +0 -0
- package/src/server/links/services/DefinitionsPool.spec.ts +0 -86
- 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
|
-
|
|
115
|
-
|
|
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
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
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 {
|
|
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
|
-
|
|
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.
|
|
240
|
-
this.log.warn(
|
|
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
|
-
|
|
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
|
|
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
|
|