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
|
@@ -0,0 +1,492 @@
|
|
|
1
|
+
import { type Alepha, AlephaError, t } from "alepha";
|
|
2
|
+
import { sql } from "drizzle-orm";
|
|
3
|
+
import { expect } from "vitest";
|
|
4
|
+
import { PG_GENERATED } from "../core/constants/PG_SYMBOLS.ts";
|
|
5
|
+
import { $entity, $repository, $view, db, pgAttr } from "../core/index.ts";
|
|
6
|
+
|
|
7
|
+
// ============================================================================
|
|
8
|
+
// Shared entity definitions
|
|
9
|
+
// ============================================================================
|
|
10
|
+
|
|
11
|
+
const orderEntity = $entity({
|
|
12
|
+
name: "orders",
|
|
13
|
+
schema: t.object({
|
|
14
|
+
id: db.primaryKey(),
|
|
15
|
+
category: t.text(),
|
|
16
|
+
amount: t.number(),
|
|
17
|
+
status: t.text(),
|
|
18
|
+
}),
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
// ============================================================================
|
|
22
|
+
// Feature 1: Partial Indexes
|
|
23
|
+
// ============================================================================
|
|
24
|
+
|
|
25
|
+
export const testPartialIndex = async (alepha: Alepha) => {
|
|
26
|
+
const entity = $entity({
|
|
27
|
+
name: "items_partial_idx",
|
|
28
|
+
schema: t.object({
|
|
29
|
+
id: db.primaryKey(),
|
|
30
|
+
email: t.text(),
|
|
31
|
+
active: t.boolean(),
|
|
32
|
+
}),
|
|
33
|
+
indexes: [
|
|
34
|
+
{
|
|
35
|
+
column: "email",
|
|
36
|
+
unique: true,
|
|
37
|
+
where: sql`active = true`,
|
|
38
|
+
},
|
|
39
|
+
],
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
class App {
|
|
43
|
+
repo = $repository(entity);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const app = alepha.inject(App);
|
|
47
|
+
await alepha.start();
|
|
48
|
+
|
|
49
|
+
// Create an active item — unique constraint applies
|
|
50
|
+
const a = await app.repo.create({ email: "alice@test.com", active: true });
|
|
51
|
+
expect(a.email).toBe("alice@test.com");
|
|
52
|
+
|
|
53
|
+
// Deactivate it — no longer covered by partial index
|
|
54
|
+
await app.repo.updateById(a.id, { active: false });
|
|
55
|
+
|
|
56
|
+
// Insert same email as active — allowed because first one is inactive
|
|
57
|
+
const b = await app.repo.create({ email: "alice@test.com", active: true });
|
|
58
|
+
expect(b.email).toBe("alice@test.com");
|
|
59
|
+
expect(b.id).not.toBe(a.id);
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
export const testPartialCompositeIndex = async (alepha: Alepha) => {
|
|
63
|
+
const entity = $entity({
|
|
64
|
+
name: "items_partial_comp_idx",
|
|
65
|
+
schema: t.object({
|
|
66
|
+
id: db.primaryKey(),
|
|
67
|
+
category: t.text(),
|
|
68
|
+
name: t.text(),
|
|
69
|
+
active: t.boolean(),
|
|
70
|
+
}),
|
|
71
|
+
indexes: [
|
|
72
|
+
{
|
|
73
|
+
columns: ["category", "name"],
|
|
74
|
+
unique: true,
|
|
75
|
+
where: sql`active = true`,
|
|
76
|
+
},
|
|
77
|
+
],
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
class App {
|
|
81
|
+
repo = $repository(entity);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
const app = alepha.inject(App);
|
|
85
|
+
await alepha.start();
|
|
86
|
+
|
|
87
|
+
await app.repo.create({ category: "A", name: "foo", active: true });
|
|
88
|
+
|
|
89
|
+
// Same category+name but inactive — allowed by partial index
|
|
90
|
+
await app.repo.create({ category: "A", name: "foo", active: false });
|
|
91
|
+
|
|
92
|
+
const all = await app.repo.findMany();
|
|
93
|
+
expect(all).toHaveLength(2);
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
// ============================================================================
|
|
97
|
+
// Feature 2: Subqueries (exists / notExists)
|
|
98
|
+
// ============================================================================
|
|
99
|
+
|
|
100
|
+
export const testExistsSubquery = async (alepha: Alepha) => {
|
|
101
|
+
const parentEntity = $entity({
|
|
102
|
+
name: "parents_exist",
|
|
103
|
+
schema: t.object({
|
|
104
|
+
id: db.primaryKey(),
|
|
105
|
+
name: t.text(),
|
|
106
|
+
}),
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
const childEntity = $entity({
|
|
110
|
+
name: "children_exist",
|
|
111
|
+
schema: t.object({
|
|
112
|
+
id: db.primaryKey(),
|
|
113
|
+
parentId: t.integer(),
|
|
114
|
+
label: t.text(),
|
|
115
|
+
}),
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
class App {
|
|
119
|
+
parents = $repository(parentEntity);
|
|
120
|
+
children = $repository(childEntity);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
const app = alepha.inject(App);
|
|
124
|
+
await alepha.start();
|
|
125
|
+
|
|
126
|
+
const p1 = await app.parents.create({ name: "P1" });
|
|
127
|
+
const p2 = await app.parents.create({ name: "P2" });
|
|
128
|
+
await app.children.create({ parentId: p1.id, label: "C1" });
|
|
129
|
+
|
|
130
|
+
// parents that have at least one child
|
|
131
|
+
const withChildren = await app.parents.findMany({
|
|
132
|
+
where: {
|
|
133
|
+
exists: sql`(SELECT 1 FROM children_exist WHERE children_exist.parent_id = parents_exist.id)`,
|
|
134
|
+
},
|
|
135
|
+
});
|
|
136
|
+
expect(withChildren).toHaveLength(1);
|
|
137
|
+
expect(withChildren[0].name).toBe("P1");
|
|
138
|
+
|
|
139
|
+
// parents that have NO children
|
|
140
|
+
const withoutChildren = await app.parents.findMany({
|
|
141
|
+
where: {
|
|
142
|
+
notExists: sql`(SELECT 1 FROM children_exist WHERE children_exist.parent_id = parents_exist.id)`,
|
|
143
|
+
},
|
|
144
|
+
});
|
|
145
|
+
expect(withoutChildren).toHaveLength(1);
|
|
146
|
+
expect(withoutChildren[0].name).toBe("P2");
|
|
147
|
+
};
|
|
148
|
+
|
|
149
|
+
// ============================================================================
|
|
150
|
+
// Feature 4: Aggregate Functions
|
|
151
|
+
// ============================================================================
|
|
152
|
+
|
|
153
|
+
export const testAggregateBasic = async (alepha: Alepha) => {
|
|
154
|
+
class App {
|
|
155
|
+
orders = $repository(orderEntity);
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
const app = alepha.inject(App);
|
|
159
|
+
await alepha.start();
|
|
160
|
+
|
|
161
|
+
await app.orders.createMany([
|
|
162
|
+
{ category: "A", amount: 100, status: "paid" },
|
|
163
|
+
{ category: "A", amount: 200, status: "paid" },
|
|
164
|
+
{ category: "B", amount: 50, status: "paid" },
|
|
165
|
+
{ category: "B", amount: 150, status: "pending" },
|
|
166
|
+
]);
|
|
167
|
+
|
|
168
|
+
// Basic grouped aggregation
|
|
169
|
+
const result = await app.orders.aggregate({
|
|
170
|
+
select: { category: true, amount: { sum: true, avg: true } },
|
|
171
|
+
groupBy: ["category"],
|
|
172
|
+
orderBy: "category",
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
expect(result).toHaveLength(2);
|
|
176
|
+
expect(result[0].category).toBe("A");
|
|
177
|
+
expect(result[0].amount.sum).toBe(300);
|
|
178
|
+
expect(result[0].amount.avg).toBe(150);
|
|
179
|
+
expect(result[1].category).toBe("B");
|
|
180
|
+
expect(result[1].amount.sum).toBe(200);
|
|
181
|
+
};
|
|
182
|
+
|
|
183
|
+
export const testAggregateMinMaxCount = async (alepha: Alepha) => {
|
|
184
|
+
class App {
|
|
185
|
+
orders = $repository(orderEntity);
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
const app = alepha.inject(App);
|
|
189
|
+
await alepha.start();
|
|
190
|
+
|
|
191
|
+
await app.orders.createMany([
|
|
192
|
+
{ category: "X", amount: 10, status: "paid" },
|
|
193
|
+
{ category: "X", amount: 30, status: "paid" },
|
|
194
|
+
{ category: "X", amount: 50, status: "paid" },
|
|
195
|
+
]);
|
|
196
|
+
|
|
197
|
+
const result = await app.orders.aggregate({
|
|
198
|
+
select: {
|
|
199
|
+
category: true,
|
|
200
|
+
amount: { min: true, max: true, count: true },
|
|
201
|
+
},
|
|
202
|
+
groupBy: ["category"],
|
|
203
|
+
});
|
|
204
|
+
|
|
205
|
+
expect(result).toHaveLength(1);
|
|
206
|
+
expect(result[0].amount.min).toBe(10);
|
|
207
|
+
expect(result[0].amount.max).toBe(50);
|
|
208
|
+
expect(result[0].amount.count).toBe(3);
|
|
209
|
+
};
|
|
210
|
+
|
|
211
|
+
export const testAggregateHaving = async (alepha: Alepha) => {
|
|
212
|
+
class App {
|
|
213
|
+
orders = $repository(orderEntity);
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
const app = alepha.inject(App);
|
|
217
|
+
await alepha.start();
|
|
218
|
+
|
|
219
|
+
await app.orders.createMany([
|
|
220
|
+
{ category: "A", amount: 10, status: "paid" },
|
|
221
|
+
{ category: "A", amount: 20, status: "paid" },
|
|
222
|
+
{ category: "B", amount: 500, status: "paid" },
|
|
223
|
+
{ category: "B", amount: 600, status: "paid" },
|
|
224
|
+
]);
|
|
225
|
+
|
|
226
|
+
// Only groups where sum > 100
|
|
227
|
+
const result = await app.orders.aggregate({
|
|
228
|
+
select: { category: true, amount: { sum: true } },
|
|
229
|
+
groupBy: ["category"],
|
|
230
|
+
having: { amount: { sum: { gt: 100 } } },
|
|
231
|
+
});
|
|
232
|
+
|
|
233
|
+
expect(result).toHaveLength(1);
|
|
234
|
+
expect(result[0].category).toBe("B");
|
|
235
|
+
expect(result[0].amount.sum).toBe(1100);
|
|
236
|
+
};
|
|
237
|
+
|
|
238
|
+
export const testAggregateOrderByDotNotation = async (alepha: Alepha) => {
|
|
239
|
+
class App {
|
|
240
|
+
orders = $repository(orderEntity);
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
const app = alepha.inject(App);
|
|
244
|
+
await alepha.start();
|
|
245
|
+
|
|
246
|
+
await app.orders.createMany([
|
|
247
|
+
{ category: "A", amount: 500, status: "paid" },
|
|
248
|
+
{ category: "B", amount: 100, status: "paid" },
|
|
249
|
+
{ category: "C", amount: 300, status: "paid" },
|
|
250
|
+
]);
|
|
251
|
+
|
|
252
|
+
const result = await app.orders.aggregate({
|
|
253
|
+
select: { category: true, amount: { sum: true } },
|
|
254
|
+
groupBy: ["category"],
|
|
255
|
+
orderBy: { column: "amount.sum", direction: "desc" },
|
|
256
|
+
});
|
|
257
|
+
|
|
258
|
+
expect(result).toHaveLength(3);
|
|
259
|
+
expect(result[0].category).toBe("A");
|
|
260
|
+
expect(result[1].category).toBe("C");
|
|
261
|
+
expect(result[2].category).toBe("B");
|
|
262
|
+
};
|
|
263
|
+
|
|
264
|
+
// ============================================================================
|
|
265
|
+
// Feature 6: Generated Columns (SQLite only — virtual)
|
|
266
|
+
// ============================================================================
|
|
267
|
+
|
|
268
|
+
export const testGeneratedColumnSqlite = async (alepha: Alepha) => {
|
|
269
|
+
const entity = $entity({
|
|
270
|
+
name: "generated_col",
|
|
271
|
+
schema: t.object({
|
|
272
|
+
id: db.primaryKey(),
|
|
273
|
+
firstName: t.text(),
|
|
274
|
+
lastName: t.text(),
|
|
275
|
+
fullName: pgAttr(t.text(), PG_GENERATED, {
|
|
276
|
+
expression: sql`first_name || ' ' || last_name`,
|
|
277
|
+
mode: "virtual",
|
|
278
|
+
}),
|
|
279
|
+
}),
|
|
280
|
+
});
|
|
281
|
+
|
|
282
|
+
class App {
|
|
283
|
+
repo = $repository(entity);
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
const app = alepha.inject(App);
|
|
287
|
+
await alepha.start();
|
|
288
|
+
|
|
289
|
+
// Insert without fullName — it's computed
|
|
290
|
+
const created = await app.repo.create({
|
|
291
|
+
firstName: "John",
|
|
292
|
+
lastName: "Doe",
|
|
293
|
+
} as any);
|
|
294
|
+
|
|
295
|
+
expect(created.fullName).toBe("John Doe");
|
|
296
|
+
|
|
297
|
+
// Verify findMany also returns the computed value
|
|
298
|
+
const all = await app.repo.findMany();
|
|
299
|
+
expect(all).toHaveLength(1);
|
|
300
|
+
expect(all[0].fullName).toBe("John Doe");
|
|
301
|
+
};
|
|
302
|
+
|
|
303
|
+
export const testGeneratedColumnPostgres = async (alepha: Alepha) => {
|
|
304
|
+
const entity = $entity({
|
|
305
|
+
name: "generated_col_pg",
|
|
306
|
+
schema: t.object({
|
|
307
|
+
id: db.primaryKey(),
|
|
308
|
+
firstName: t.text(),
|
|
309
|
+
lastName: t.text(),
|
|
310
|
+
fullName: pgAttr(t.text(), PG_GENERATED, {
|
|
311
|
+
expression: sql`first_name || ' ' || last_name`,
|
|
312
|
+
mode: "stored",
|
|
313
|
+
}),
|
|
314
|
+
}),
|
|
315
|
+
});
|
|
316
|
+
|
|
317
|
+
class App {
|
|
318
|
+
repo = $repository(entity);
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
const app = alepha.inject(App);
|
|
322
|
+
await alepha.start();
|
|
323
|
+
|
|
324
|
+
const created = await app.repo.create({
|
|
325
|
+
firstName: "Jane",
|
|
326
|
+
lastName: "Smith",
|
|
327
|
+
} as any);
|
|
328
|
+
|
|
329
|
+
expect(created.fullName).toBe("Jane Smith");
|
|
330
|
+
|
|
331
|
+
const all = await app.repo.findMany();
|
|
332
|
+
expect(all).toHaveLength(1);
|
|
333
|
+
expect(all[0].fullName).toBe("Jane Smith");
|
|
334
|
+
};
|
|
335
|
+
|
|
336
|
+
export const testGeneratedColumnExcludedFromInsertSchema = async (
|
|
337
|
+
alepha: Alepha,
|
|
338
|
+
) => {
|
|
339
|
+
const entity = $entity({
|
|
340
|
+
name: "generated_schema_test",
|
|
341
|
+
schema: t.object({
|
|
342
|
+
id: db.primaryKey(),
|
|
343
|
+
a: t.text(),
|
|
344
|
+
b: t.text(),
|
|
345
|
+
computed: pgAttr(t.text(), PG_GENERATED, {
|
|
346
|
+
expression: sql`a || b`,
|
|
347
|
+
mode: "virtual",
|
|
348
|
+
}),
|
|
349
|
+
}),
|
|
350
|
+
});
|
|
351
|
+
|
|
352
|
+
// Verify insertSchema does not contain generated column
|
|
353
|
+
const insertProps = Object.keys(entity.insertSchema.properties);
|
|
354
|
+
expect(insertProps).toContain("a");
|
|
355
|
+
expect(insertProps).toContain("b");
|
|
356
|
+
expect(insertProps).not.toContain("computed");
|
|
357
|
+
|
|
358
|
+
// Verify updateSchema does not contain generated column
|
|
359
|
+
const updateProps = Object.keys(entity.updateSchema.properties);
|
|
360
|
+
expect(updateProps).toContain("a");
|
|
361
|
+
expect(updateProps).toContain("b");
|
|
362
|
+
expect(updateProps).not.toContain("computed");
|
|
363
|
+
};
|
|
364
|
+
|
|
365
|
+
// ============================================================================
|
|
366
|
+
// Feature 7: Query Caching
|
|
367
|
+
// ============================================================================
|
|
368
|
+
|
|
369
|
+
export const testQueryCache = async (alepha: Alepha) => {
|
|
370
|
+
class App {
|
|
371
|
+
orders = $repository(orderEntity);
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
const app = alepha.inject(App);
|
|
375
|
+
await alepha.start();
|
|
376
|
+
|
|
377
|
+
await app.orders.create({ category: "A", amount: 100, status: "paid" });
|
|
378
|
+
|
|
379
|
+
// First call — hits DB
|
|
380
|
+
const first = await app.orders.findMany(
|
|
381
|
+
{ where: { category: { eq: "A" } } },
|
|
382
|
+
{ cache: { ttl: 60_000 } },
|
|
383
|
+
);
|
|
384
|
+
expect(first).toHaveLength(1);
|
|
385
|
+
|
|
386
|
+
// Insert another — won't be in cache (cache was invalidated by write)
|
|
387
|
+
await app.orders.create({ category: "A", amount: 200, status: "paid" });
|
|
388
|
+
|
|
389
|
+
// This should hit DB again (cache was invalidated by the create above)
|
|
390
|
+
const afterInsert = await app.orders.findMany(
|
|
391
|
+
{ where: { category: { eq: "A" } } },
|
|
392
|
+
{ cache: { ttl: 60_000 } },
|
|
393
|
+
);
|
|
394
|
+
expect(afterInsert).toHaveLength(2);
|
|
395
|
+
|
|
396
|
+
// This should return cached result (no writes since last query)
|
|
397
|
+
const cached = await app.orders.findMany(
|
|
398
|
+
{ where: { category: { eq: "A" } } },
|
|
399
|
+
{ cache: { ttl: 60_000 } },
|
|
400
|
+
);
|
|
401
|
+
expect(cached).toHaveLength(2);
|
|
402
|
+
};
|
|
403
|
+
|
|
404
|
+
export const testQueryCacheCustomKey = async (alepha: Alepha) => {
|
|
405
|
+
class App {
|
|
406
|
+
orders = $repository(orderEntity);
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
const app = alepha.inject(App);
|
|
410
|
+
await alepha.start();
|
|
411
|
+
|
|
412
|
+
await app.orders.create({ category: "A", amount: 100, status: "paid" });
|
|
413
|
+
|
|
414
|
+
// Use custom cache key
|
|
415
|
+
const result = await app.orders.findMany(
|
|
416
|
+
{},
|
|
417
|
+
{ cache: { ttl: 60_000, key: "all-orders" } },
|
|
418
|
+
);
|
|
419
|
+
expect(result).toHaveLength(1);
|
|
420
|
+
|
|
421
|
+
// Same custom key returns cached result
|
|
422
|
+
const cached = await app.orders.findMany(
|
|
423
|
+
{},
|
|
424
|
+
{ cache: { ttl: 60_000, key: "all-orders" } },
|
|
425
|
+
);
|
|
426
|
+
expect(cached).toHaveLength(1);
|
|
427
|
+
};
|
|
428
|
+
|
|
429
|
+
// ============================================================================
|
|
430
|
+
// Feature 8: Database Views
|
|
431
|
+
// ============================================================================
|
|
432
|
+
|
|
433
|
+
export const testViewReadOnly = async (alepha: Alepha) => {
|
|
434
|
+
// Create the underlying table
|
|
435
|
+
const itemEntity = $entity({
|
|
436
|
+
name: "view_items",
|
|
437
|
+
schema: t.object({
|
|
438
|
+
id: db.primaryKey(),
|
|
439
|
+
name: t.text(),
|
|
440
|
+
price: t.number(),
|
|
441
|
+
}),
|
|
442
|
+
});
|
|
443
|
+
|
|
444
|
+
// Create a view
|
|
445
|
+
const itemView = $view({
|
|
446
|
+
name: "view_items_summary",
|
|
447
|
+
schema: t.object({
|
|
448
|
+
id: t.integer(),
|
|
449
|
+
name: t.text(),
|
|
450
|
+
price: t.number(),
|
|
451
|
+
}),
|
|
452
|
+
query: sql`SELECT id, name, price FROM view_items`,
|
|
453
|
+
});
|
|
454
|
+
|
|
455
|
+
class App {
|
|
456
|
+
items = $repository(itemEntity);
|
|
457
|
+
summary = $repository(itemView);
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
const app = alepha.inject(App);
|
|
461
|
+
await alepha.start();
|
|
462
|
+
|
|
463
|
+
// Verify the repository detects it's a view
|
|
464
|
+
expect(app.summary.isReadOnly).toBe(true);
|
|
465
|
+
expect(app.items.isReadOnly).toBe(false);
|
|
466
|
+
|
|
467
|
+
// Write operations should throw on views
|
|
468
|
+
await expect(
|
|
469
|
+
app.summary.create({ id: 1, name: "test", price: 10 } as any),
|
|
470
|
+
).rejects.toThrow(AlephaError);
|
|
471
|
+
};
|
|
472
|
+
|
|
473
|
+
export const testViewRefreshThrowsForNonMaterialized = async (
|
|
474
|
+
alepha: Alepha,
|
|
475
|
+
) => {
|
|
476
|
+
const view = $view({
|
|
477
|
+
name: "non_mat_view",
|
|
478
|
+
schema: t.object({
|
|
479
|
+
id: t.integer(),
|
|
480
|
+
}),
|
|
481
|
+
query: sql`SELECT 1 as id`,
|
|
482
|
+
});
|
|
483
|
+
|
|
484
|
+
class App {
|
|
485
|
+
repo = $repository(view);
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
const app = alepha.inject(App);
|
|
489
|
+
await alepha.start();
|
|
490
|
+
|
|
491
|
+
await expect(app.repo.refresh()).rejects.toThrow(AlephaError);
|
|
492
|
+
};
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
import { Alepha } from "alepha";
|
|
2
|
+
import { describe, it } from "vitest";
|
|
3
|
+
import { AlephaOrmPostgres } from "../postgres/index.ts";
|
|
4
|
+
import {
|
|
5
|
+
testAggregateBasic,
|
|
6
|
+
testAggregateHaving,
|
|
7
|
+
testAggregateMinMaxCount,
|
|
8
|
+
testAggregateOrderByDotNotation,
|
|
9
|
+
testExistsSubquery,
|
|
10
|
+
testGeneratedColumnExcludedFromInsertSchema,
|
|
11
|
+
testGeneratedColumnPostgres,
|
|
12
|
+
testGeneratedColumnSqlite,
|
|
13
|
+
testPartialCompositeIndex,
|
|
14
|
+
testPartialIndex,
|
|
15
|
+
testQueryCache,
|
|
16
|
+
testQueryCacheCustomKey,
|
|
17
|
+
testViewReadOnly,
|
|
18
|
+
testViewRefreshThrowsForNonMaterialized,
|
|
19
|
+
} from "./orm-next-tests.ts";
|
|
20
|
+
|
|
21
|
+
const sqlite = () =>
|
|
22
|
+
Alepha.create({ env: { DATABASE_URL: "sqlite://:memory:" } });
|
|
23
|
+
const postgres = () => Alepha.create().with(AlephaOrmPostgres);
|
|
24
|
+
|
|
25
|
+
// =============================================================================
|
|
26
|
+
// Feature 1: Partial Indexes
|
|
27
|
+
// =============================================================================
|
|
28
|
+
|
|
29
|
+
describe("partial indexes", () => {
|
|
30
|
+
it("should support partial unique index (sqlite)", async () => {
|
|
31
|
+
await testPartialIndex(sqlite());
|
|
32
|
+
});
|
|
33
|
+
it("should support partial unique index (postgres)", async () => {
|
|
34
|
+
await testPartialIndex(postgres());
|
|
35
|
+
});
|
|
36
|
+
it("should support partial composite index (sqlite)", async () => {
|
|
37
|
+
await testPartialCompositeIndex(sqlite());
|
|
38
|
+
});
|
|
39
|
+
it("should support partial composite index (postgres)", async () => {
|
|
40
|
+
await testPartialCompositeIndex(postgres());
|
|
41
|
+
});
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
// =============================================================================
|
|
45
|
+
// Feature 2: Subqueries (exists / notExists)
|
|
46
|
+
// =============================================================================
|
|
47
|
+
|
|
48
|
+
describe("subqueries", () => {
|
|
49
|
+
it("should support exists and notExists (sqlite)", async () => {
|
|
50
|
+
await testExistsSubquery(sqlite());
|
|
51
|
+
});
|
|
52
|
+
it("should support exists and notExists (postgres)", async () => {
|
|
53
|
+
await testExistsSubquery(postgres());
|
|
54
|
+
});
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
// =============================================================================
|
|
58
|
+
// Feature 4: Aggregate Functions
|
|
59
|
+
// =============================================================================
|
|
60
|
+
|
|
61
|
+
describe("aggregates", () => {
|
|
62
|
+
it("should compute sum and avg grouped by column (sqlite)", async () => {
|
|
63
|
+
await testAggregateBasic(sqlite());
|
|
64
|
+
});
|
|
65
|
+
it("should compute sum and avg grouped by column (postgres)", async () => {
|
|
66
|
+
await testAggregateBasic(postgres());
|
|
67
|
+
});
|
|
68
|
+
it("should compute min, max, and count (sqlite)", async () => {
|
|
69
|
+
await testAggregateMinMaxCount(sqlite());
|
|
70
|
+
});
|
|
71
|
+
it("should compute min, max, and count (postgres)", async () => {
|
|
72
|
+
await testAggregateMinMaxCount(postgres());
|
|
73
|
+
});
|
|
74
|
+
it("should filter groups with having clause (sqlite)", async () => {
|
|
75
|
+
await testAggregateHaving(sqlite());
|
|
76
|
+
});
|
|
77
|
+
it("should filter groups with having clause (postgres)", async () => {
|
|
78
|
+
await testAggregateHaving(postgres());
|
|
79
|
+
});
|
|
80
|
+
it("should order by aggregate with dot notation (sqlite)", async () => {
|
|
81
|
+
await testAggregateOrderByDotNotation(sqlite());
|
|
82
|
+
});
|
|
83
|
+
it("should order by aggregate with dot notation (postgres)", async () => {
|
|
84
|
+
await testAggregateOrderByDotNotation(postgres());
|
|
85
|
+
});
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
// =============================================================================
|
|
89
|
+
// Feature 6: Generated Columns
|
|
90
|
+
// =============================================================================
|
|
91
|
+
|
|
92
|
+
describe("generated columns", () => {
|
|
93
|
+
it("should compute virtual generated column (sqlite)", async () => {
|
|
94
|
+
await testGeneratedColumnSqlite(sqlite());
|
|
95
|
+
});
|
|
96
|
+
it("should compute stored generated column (postgres)", async () => {
|
|
97
|
+
await testGeneratedColumnPostgres(postgres());
|
|
98
|
+
});
|
|
99
|
+
it("should exclude generated columns from insert and update schemas", async () => {
|
|
100
|
+
await testGeneratedColumnExcludedFromInsertSchema(sqlite());
|
|
101
|
+
});
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
// =============================================================================
|
|
105
|
+
// Feature 7: Query Caching
|
|
106
|
+
// =============================================================================
|
|
107
|
+
|
|
108
|
+
describe("query caching", () => {
|
|
109
|
+
it("should cache and invalidate on write (sqlite)", async () => {
|
|
110
|
+
await testQueryCache(sqlite());
|
|
111
|
+
});
|
|
112
|
+
it("should cache and invalidate on write (postgres)", async () => {
|
|
113
|
+
await testQueryCache(postgres());
|
|
114
|
+
});
|
|
115
|
+
it("should support custom cache keys (sqlite)", async () => {
|
|
116
|
+
await testQueryCacheCustomKey(sqlite());
|
|
117
|
+
});
|
|
118
|
+
it("should support custom cache keys (postgres)", async () => {
|
|
119
|
+
await testQueryCacheCustomKey(postgres());
|
|
120
|
+
});
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
// =============================================================================
|
|
124
|
+
// Feature 8: Database Views
|
|
125
|
+
// =============================================================================
|
|
126
|
+
|
|
127
|
+
describe("database views", () => {
|
|
128
|
+
it("should block writes on view repositories (sqlite)", async () => {
|
|
129
|
+
await testViewReadOnly(sqlite());
|
|
130
|
+
});
|
|
131
|
+
it("should block writes on view repositories (postgres)", async () => {
|
|
132
|
+
await testViewReadOnly(postgres());
|
|
133
|
+
});
|
|
134
|
+
it("should throw on refresh for non-materialized view (sqlite)", async () => {
|
|
135
|
+
await testViewRefreshThrowsForNonMaterialized(sqlite());
|
|
136
|
+
});
|
|
137
|
+
it("should throw on refresh for non-materialized view (postgres)", async () => {
|
|
138
|
+
await testViewRefreshThrowsForNonMaterialized(postgres());
|
|
139
|
+
});
|
|
140
|
+
});
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { SQL } from "drizzle-orm";
|
|
1
2
|
import type {
|
|
2
3
|
PgSequenceOptions,
|
|
3
4
|
UpdateDeleteAction,
|
|
@@ -13,6 +14,7 @@ export const PG_VERSION = Symbol.for("Alepha.Postgres.Version");
|
|
|
13
14
|
export const PG_IDENTITY = Symbol.for("Alepha.Postgres.Identity");
|
|
14
15
|
export const PG_ENUM = Symbol.for("Alepha.Postgres.Enum");
|
|
15
16
|
export const PG_REF = Symbol.for("Alepha.Postgres.Ref");
|
|
17
|
+
export const PG_GENERATED = Symbol.for("Alepha.Postgres.Generated");
|
|
16
18
|
|
|
17
19
|
/**
|
|
18
20
|
* @deprecated Use `PG_IDENTITY` instead.
|
|
@@ -33,6 +35,7 @@ export type PgSymbols = {
|
|
|
33
35
|
[PG_IDENTITY]: PgIdentityOptions;
|
|
34
36
|
[PG_REF]: PgRefOptions;
|
|
35
37
|
[PG_ENUM]: PgEnumOptions;
|
|
38
|
+
[PG_GENERATED]: PgGeneratedOptions;
|
|
36
39
|
|
|
37
40
|
/**
|
|
38
41
|
* @deprecated Use `PG_IDENTITY` instead.
|
|
@@ -53,6 +56,20 @@ export interface PgEnumOptions {
|
|
|
53
56
|
description?: string;
|
|
54
57
|
}
|
|
55
58
|
|
|
59
|
+
export interface PgGeneratedOptions {
|
|
60
|
+
/**
|
|
61
|
+
* SQL expression for the generated column.
|
|
62
|
+
*/
|
|
63
|
+
expression: SQL;
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Storage mode for the generated column.
|
|
67
|
+
* - `"stored"` — value is computed on write and stored on disk (default for PostgreSQL).
|
|
68
|
+
* - `"virtual"` — value is computed on read (default for SQLite).
|
|
69
|
+
*/
|
|
70
|
+
mode?: "stored" | "virtual";
|
|
71
|
+
}
|
|
72
|
+
|
|
56
73
|
export interface PgRefOptions {
|
|
57
74
|
ref: () => {
|
|
58
75
|
name: string;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { $module, type Alepha
|
|
1
|
+
import { $module, type Alepha } from "alepha";
|
|
2
2
|
import { AlephaDateTime } from "alepha/datetime";
|
|
3
3
|
import { $entity } from "./primitives/$entity.ts";
|
|
4
4
|
import { $sequence } from "./primitives/$sequence.ts";
|
|
@@ -7,6 +7,7 @@ import { BunSqliteProvider } from "./providers/drivers/BunSqliteProvider.ts";
|
|
|
7
7
|
import { CloudflareD1Provider } from "./providers/drivers/CloudflareD1Provider.ts";
|
|
8
8
|
import { DatabaseProvider } from "./providers/drivers/DatabaseProvider.ts";
|
|
9
9
|
import { RepositoryProvider } from "./providers/RepositoryProvider.ts";
|
|
10
|
+
import { databaseEnvSchema } from "./schemas/databaseEnvSchema.ts";
|
|
10
11
|
import { PgRelationManager } from "./services/PgRelationManager.ts";
|
|
11
12
|
import { QueryManager } from "./services/QueryManager.ts";
|
|
12
13
|
import { Repository } from "./services/Repository.ts";
|
|
@@ -33,11 +34,7 @@ export const AlephaOrm = $module({
|
|
|
33
34
|
QueryManager,
|
|
34
35
|
],
|
|
35
36
|
register: (alepha: Alepha) => {
|
|
36
|
-
const env = alepha.parseEnv(
|
|
37
|
-
t.object({
|
|
38
|
-
DATABASE_URL: t.optional(t.text()),
|
|
39
|
-
}),
|
|
40
|
-
);
|
|
37
|
+
const env = alepha.parseEnv(databaseEnvSchema);
|
|
41
38
|
|
|
42
39
|
alepha.with(DrizzleKitProvider);
|
|
43
40
|
alepha.with(RepositoryProvider);
|