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
|
@@ -5,7 +5,6 @@ import { $client } from "./primitives/$client.ts";
|
|
|
5
5
|
import { $remote } from "./primitives/$remote.ts";
|
|
6
6
|
import { LinkProvider } from "./providers/LinkProvider.ts";
|
|
7
7
|
import { BatchCollector } from "./services/BatchCollector.ts";
|
|
8
|
-
import { DefinitionsPool } from "./services/DefinitionsPool.ts";
|
|
9
8
|
|
|
10
9
|
// ---------------------------------------------------------------------------------------------------------------------
|
|
11
10
|
|
|
@@ -16,7 +15,6 @@ export * from "./primitives/$remote.ts";
|
|
|
16
15
|
export * from "./providers/LinkProvider.ts";
|
|
17
16
|
export * from "./schemas/apiLinksResponseSchema.ts";
|
|
18
17
|
export * from "./services/BatchCollector.ts";
|
|
19
|
-
export * from "./services/DefinitionsPool.ts";
|
|
20
18
|
|
|
21
19
|
// ---------------------------------------------------------------- -----------------------------------------------------
|
|
22
20
|
|
|
@@ -24,5 +22,5 @@ export const AlephaServerLinks = $module({
|
|
|
24
22
|
name: "alepha.server.links",
|
|
25
23
|
atoms: [apiLinksAtom, linkOptionsAtom],
|
|
26
24
|
primitives: [$remote, $client],
|
|
27
|
-
services: [LinkProvider, BatchCollector
|
|
25
|
+
services: [LinkProvider, BatchCollector],
|
|
28
26
|
});
|
|
@@ -9,7 +9,6 @@ import { RemotePrimitiveProvider } from "./providers/RemotePrimitiveProvider.ts"
|
|
|
9
9
|
import { ServerLinksProvider } from "./providers/ServerLinksProvider.ts";
|
|
10
10
|
import type { ApiRegistryResponse } from "./schemas/apiLinksResponseSchema.ts";
|
|
11
11
|
import { BatchCollector } from "./services/BatchCollector.ts";
|
|
12
|
-
import { DefinitionsPool } from "./services/DefinitionsPool.ts";
|
|
13
12
|
|
|
14
13
|
// ---------------------------------------------------------------------------------------------------------------------
|
|
15
14
|
|
|
@@ -22,7 +21,6 @@ export * from "./providers/RemotePrimitiveProvider.ts";
|
|
|
22
21
|
export * from "./providers/ServerLinksProvider.ts";
|
|
23
22
|
export * from "./schemas/apiLinksResponseSchema.ts";
|
|
24
23
|
export * from "./services/BatchCollector.ts";
|
|
25
|
-
export * from "./services/DefinitionsPool.ts";
|
|
26
24
|
|
|
27
25
|
// ---------------------------------------------------------------------------------------------------------------------
|
|
28
26
|
|
|
@@ -69,6 +67,5 @@ export const AlephaServerLinks = $module({
|
|
|
69
67
|
RemotePrimitiveProvider,
|
|
70
68
|
LinkProvider,
|
|
71
69
|
BatchCollector,
|
|
72
|
-
DefinitionsPool,
|
|
73
70
|
],
|
|
74
71
|
});
|
|
@@ -40,13 +40,9 @@ describe("LinkProvider", () => {
|
|
|
40
40
|
|
|
41
41
|
expect(data).toStrictEqual({
|
|
42
42
|
prefix: "/api",
|
|
43
|
-
definitions: {
|
|
44
|
-
$0: '{"type":"object","required":["pong"],"properties":{"pong":{"type":"boolean"}},"additionalProperties":false}',
|
|
45
|
-
},
|
|
46
43
|
actions: {
|
|
47
44
|
ping: {
|
|
48
45
|
path: "/ping",
|
|
49
|
-
response: "$0",
|
|
50
46
|
},
|
|
51
47
|
},
|
|
52
48
|
});
|
|
@@ -114,13 +110,13 @@ describe("LinkProvider", () => {
|
|
|
114
110
|
expect(data.actions.createUser.method).toBe("POST");
|
|
115
111
|
});
|
|
116
112
|
|
|
117
|
-
it("should
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
class
|
|
113
|
+
it("should not include definitions or schema refs in registry", async ({
|
|
114
|
+
expect,
|
|
115
|
+
}) => {
|
|
116
|
+
class SchemaApp {
|
|
121
117
|
getUser = $action({
|
|
122
118
|
path: "/users/:id",
|
|
123
|
-
schema: { response:
|
|
119
|
+
schema: { response: t.object({ id: t.integer(), name: t.text() }) },
|
|
124
120
|
handler: () => ({ id: 1, name: "John" }),
|
|
125
121
|
});
|
|
126
122
|
updateUser = $action({
|
|
@@ -128,13 +124,13 @@ describe("LinkProvider", () => {
|
|
|
128
124
|
path: "/users/:id",
|
|
129
125
|
schema: {
|
|
130
126
|
body: t.object({ name: t.text() }),
|
|
131
|
-
response:
|
|
127
|
+
response: t.object({ id: t.integer(), name: t.text() }),
|
|
132
128
|
},
|
|
133
129
|
handler: () => ({ id: 1, name: "John" }),
|
|
134
130
|
});
|
|
135
131
|
}
|
|
136
132
|
|
|
137
|
-
const alepha = Alepha.create().with(
|
|
133
|
+
const alepha = Alepha.create().with(SchemaApp).with(ServerLinksProvider);
|
|
138
134
|
await alepha.start();
|
|
139
135
|
|
|
140
136
|
const res = await fetch(
|
|
@@ -142,16 +138,11 @@ describe("LinkProvider", () => {
|
|
|
142
138
|
);
|
|
143
139
|
const data = await res.json();
|
|
144
140
|
|
|
145
|
-
|
|
146
|
-
expect(data.actions.getUser.
|
|
147
|
-
|
|
148
|
-
);
|
|
149
|
-
|
|
150
|
-
// The body schema is different, so it gets a separate definition
|
|
151
|
-
expect(data.actions.updateUser.body).toBeDefined();
|
|
152
|
-
expect(data.actions.updateUser.body).not.toBe(
|
|
153
|
-
data.actions.updateUser.response,
|
|
154
|
-
);
|
|
141
|
+
expect(data.definitions).toBeUndefined();
|
|
142
|
+
expect(data.actions.getUser.body).toBeUndefined();
|
|
143
|
+
expect(data.actions.getUser.response).toBeUndefined();
|
|
144
|
+
expect(data.actions.updateUser.body).toBeUndefined();
|
|
145
|
+
expect(data.actions.updateUser.response).toBeUndefined();
|
|
155
146
|
});
|
|
156
147
|
|
|
157
148
|
it("should not include group or rawSchema in wire format", async ({
|
|
@@ -1,12 +1,4 @@
|
|
|
1
|
-
import {
|
|
2
|
-
$inject,
|
|
3
|
-
$use,
|
|
4
|
-
Alepha,
|
|
5
|
-
AlephaError,
|
|
6
|
-
type Async,
|
|
7
|
-
jsonSchemaToTypeBox,
|
|
8
|
-
t,
|
|
9
|
-
} from "alepha";
|
|
1
|
+
import { $inject, $use, Alepha, AlephaError, type Async, t } from "alepha";
|
|
10
2
|
import { $logger } from "alepha/logger";
|
|
11
3
|
import type { SecureOptions } from "alepha/security";
|
|
12
4
|
import {
|
|
@@ -21,7 +13,11 @@ import {
|
|
|
21
13
|
type ServerRequest,
|
|
22
14
|
type ServerRequestConfigEntry,
|
|
23
15
|
type ServerResponseBody,
|
|
24
|
-
type
|
|
16
|
+
type SseConfigSchema,
|
|
17
|
+
type SseEventData,
|
|
18
|
+
type SsePrimitive,
|
|
19
|
+
type SseRequestEntry,
|
|
20
|
+
type SseStream,
|
|
25
21
|
UnauthorizedError,
|
|
26
22
|
} from "alepha/server";
|
|
27
23
|
import { linkOptionsAtom } from "../atoms/linkOptionsAtom.ts";
|
|
@@ -49,7 +45,6 @@ export class LinkProvider {
|
|
|
49
45
|
// Browser/SSR: parsed from the registry response
|
|
50
46
|
protected actionMap = new Map<string, HttpClientLink>();
|
|
51
47
|
protected permissions = new Set<string>();
|
|
52
|
-
protected definitions: Record<string, string> = {};
|
|
53
48
|
protected lastLoadedRegistry: ApiRegistryResponse | null = null;
|
|
54
49
|
|
|
55
50
|
// Browser-only: batch collector for coalescing multiple calls
|
|
@@ -106,7 +101,6 @@ export class LinkProvider {
|
|
|
106
101
|
*/
|
|
107
102
|
protected loadRegistry(registry: ApiRegistryResponse): void {
|
|
108
103
|
this.lastLoadedRegistry = registry;
|
|
109
|
-
this.definitions = registry.definitions ?? {};
|
|
110
104
|
this.permissions.clear();
|
|
111
105
|
this.actionMap.clear();
|
|
112
106
|
|
|
@@ -114,12 +108,10 @@ export class LinkProvider {
|
|
|
114
108
|
this.actionMap.set(name, {
|
|
115
109
|
name,
|
|
116
110
|
path: action.path,
|
|
111
|
+
kind: action.kind,
|
|
117
112
|
method: action.method,
|
|
118
113
|
contentType: action.contentType,
|
|
119
114
|
service: action.service,
|
|
120
|
-
// Store definition refs for lazy schema resolution
|
|
121
|
-
bodyRef: action.body,
|
|
122
|
-
responseRef: action.response,
|
|
123
115
|
});
|
|
124
116
|
}
|
|
125
117
|
|
|
@@ -320,36 +312,6 @@ export class LinkProvider {
|
|
|
320
312
|
return this.can(name);
|
|
321
313
|
};
|
|
322
314
|
|
|
323
|
-
$.schema = () => {
|
|
324
|
-
const link = this.links.find((l) => l.name === name);
|
|
325
|
-
if (!link) {
|
|
326
|
-
throw new AlephaError(`Link ${name} not found.`);
|
|
327
|
-
}
|
|
328
|
-
|
|
329
|
-
// If schema is already resolved (server-side), return it
|
|
330
|
-
if (link.schema) {
|
|
331
|
-
return link.schema as { body: any; response: any };
|
|
332
|
-
}
|
|
333
|
-
|
|
334
|
-
// Lazy resolve from definition refs (browser-side)
|
|
335
|
-
const resolved: RequestConfigSchema = {};
|
|
336
|
-
if (link.bodyRef && this.definitions[link.bodyRef]) {
|
|
337
|
-
resolved.body = jsonSchemaToTypeBox(
|
|
338
|
-
JSON.parse(this.definitions[link.bodyRef]),
|
|
339
|
-
) as TRequestBody;
|
|
340
|
-
}
|
|
341
|
-
if (link.responseRef && this.definitions[link.responseRef]) {
|
|
342
|
-
resolved.response = jsonSchemaToTypeBox(
|
|
343
|
-
JSON.parse(this.definitions[link.responseRef]),
|
|
344
|
-
) as TRequestBody;
|
|
345
|
-
}
|
|
346
|
-
|
|
347
|
-
// Cache for next access
|
|
348
|
-
link.schema = resolved;
|
|
349
|
-
|
|
350
|
-
return resolved as { body: any; response: any };
|
|
351
|
-
};
|
|
352
|
-
|
|
353
315
|
return $;
|
|
354
316
|
}
|
|
355
317
|
|
|
@@ -441,6 +403,7 @@ export interface HttpClientLink {
|
|
|
441
403
|
name: string;
|
|
442
404
|
path: string;
|
|
443
405
|
method?: string;
|
|
406
|
+
kind?: string;
|
|
444
407
|
contentType?: string;
|
|
445
408
|
service?: string;
|
|
446
409
|
secured?: boolean | SecureOptions;
|
|
@@ -453,9 +416,6 @@ export interface HttpClientLink {
|
|
|
453
416
|
request: ServerRequest,
|
|
454
417
|
options: ClientRequestOptions,
|
|
455
418
|
) => Async<ServerResponseBody>;
|
|
456
|
-
// -- browser only (definition refs for lazy schema resolution) --
|
|
457
|
-
bodyRef?: string;
|
|
458
|
-
responseRef?: string;
|
|
459
419
|
}
|
|
460
420
|
|
|
461
421
|
export interface ClientScope {
|
|
@@ -469,6 +429,12 @@ export type HttpVirtualClient<T> = {
|
|
|
469
429
|
: never]: T[K] extends ActionPrimitive<infer Schema>
|
|
470
430
|
? VirtualAction<Schema>
|
|
471
431
|
: never;
|
|
432
|
+
} & {
|
|
433
|
+
[K in keyof T as T[K] extends SsePrimitive<SseConfigSchema>
|
|
434
|
+
? K
|
|
435
|
+
: never]: T[K] extends SsePrimitive<infer Schema>
|
|
436
|
+
? VirtualSse<Schema>
|
|
437
|
+
: never;
|
|
472
438
|
};
|
|
473
439
|
|
|
474
440
|
export interface VirtualAction<T extends RequestConfigSchema>
|
|
@@ -478,8 +444,10 @@ export interface VirtualAction<T extends RequestConfigSchema>
|
|
|
478
444
|
opts?: ClientRequestOptions,
|
|
479
445
|
): Promise<ClientRequestResponse<T>>;
|
|
480
446
|
can: () => boolean;
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
export interface VirtualSse<T extends SseConfigSchema> {
|
|
450
|
+
(config?: SseRequestEntry<T>): Promise<SseStream<SseEventData<T>>>;
|
|
451
|
+
name: string;
|
|
452
|
+
can: () => boolean;
|
|
485
453
|
}
|
|
@@ -366,6 +366,112 @@ describe("ServerLinksProvider", () => {
|
|
|
366
366
|
});
|
|
367
367
|
});
|
|
368
368
|
|
|
369
|
+
describe("schemas endpoint", () => {
|
|
370
|
+
it("should return schemas for requested actions", async ({ expect }) => {
|
|
371
|
+
class App {
|
|
372
|
+
ping = $action({
|
|
373
|
+
schema: {
|
|
374
|
+
body: t.object({ message: t.text() }),
|
|
375
|
+
response: t.object({ pong: t.boolean() }),
|
|
376
|
+
},
|
|
377
|
+
handler: () => ({ pong: true }),
|
|
378
|
+
});
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
const alepha = Alepha.create().with(App).with(ServerLinksProvider);
|
|
382
|
+
await alepha.start();
|
|
383
|
+
|
|
384
|
+
const res = await fetch(
|
|
385
|
+
`${alepha.inject(ServerProvider).hostname}/api/_links/schemas`,
|
|
386
|
+
{
|
|
387
|
+
method: "POST",
|
|
388
|
+
headers: { "content-type": "application/json" },
|
|
389
|
+
body: JSON.stringify({ actions: ["ping"] }),
|
|
390
|
+
},
|
|
391
|
+
);
|
|
392
|
+
|
|
393
|
+
const data = await res.json();
|
|
394
|
+
|
|
395
|
+
expect(data.ping).toBeDefined();
|
|
396
|
+
expect(data.ping.body).toBeDefined();
|
|
397
|
+
expect(data.ping.response).toBeDefined();
|
|
398
|
+
expect(JSON.parse(data.ping.body).type).toBe("object");
|
|
399
|
+
expect(JSON.parse(data.ping.response).type).toBe("object");
|
|
400
|
+
});
|
|
401
|
+
|
|
402
|
+
it("should return empty for unknown actions", async ({ expect }) => {
|
|
403
|
+
class App {
|
|
404
|
+
ping = $action({
|
|
405
|
+
schema: { response: t.text() },
|
|
406
|
+
handler: () => "pong",
|
|
407
|
+
});
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
const alepha = Alepha.create().with(App).with(ServerLinksProvider);
|
|
411
|
+
await alepha.start();
|
|
412
|
+
|
|
413
|
+
const res = await fetch(
|
|
414
|
+
`${alepha.inject(ServerProvider).hostname}/api/_links/schemas`,
|
|
415
|
+
{
|
|
416
|
+
method: "POST",
|
|
417
|
+
headers: { "content-type": "application/json" },
|
|
418
|
+
body: JSON.stringify({ actions: ["nonexistent"] }),
|
|
419
|
+
},
|
|
420
|
+
);
|
|
421
|
+
|
|
422
|
+
const data = await res.json();
|
|
423
|
+
|
|
424
|
+
expect(data.nonexistent).toBeUndefined();
|
|
425
|
+
});
|
|
426
|
+
|
|
427
|
+
it("should filter based on user permissions", async ({ expect }) => {
|
|
428
|
+
class App {
|
|
429
|
+
publicAction = $action({
|
|
430
|
+
schema: {
|
|
431
|
+
body: t.object({ name: t.text() }),
|
|
432
|
+
response: t.text(),
|
|
433
|
+
},
|
|
434
|
+
handler: () => "PUBLIC",
|
|
435
|
+
});
|
|
436
|
+
securedAction = $action({
|
|
437
|
+
use: [$secure()],
|
|
438
|
+
schema: {
|
|
439
|
+
body: t.object({ secret: t.text() }),
|
|
440
|
+
response: t.text(),
|
|
441
|
+
},
|
|
442
|
+
handler: () => "SECURED",
|
|
443
|
+
});
|
|
444
|
+
issuer = $issuer({
|
|
445
|
+
secret: "test",
|
|
446
|
+
roles: [{ name: "user", permissions: [{ name: "*" }] }],
|
|
447
|
+
});
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
const alepha = Alepha.create()
|
|
451
|
+
.with(App)
|
|
452
|
+
.with(ServerLinksProvider)
|
|
453
|
+
.with(AlephaSecurity);
|
|
454
|
+
await alepha.start();
|
|
455
|
+
|
|
456
|
+
// Unauthenticated request — should only get public schema
|
|
457
|
+
const res = await fetch(
|
|
458
|
+
`${alepha.inject(ServerProvider).hostname}/api/_links/schemas`,
|
|
459
|
+
{
|
|
460
|
+
method: "POST",
|
|
461
|
+
headers: { "content-type": "application/json" },
|
|
462
|
+
body: JSON.stringify({
|
|
463
|
+
actions: ["publicAction", "securedAction"],
|
|
464
|
+
}),
|
|
465
|
+
},
|
|
466
|
+
);
|
|
467
|
+
|
|
468
|
+
const data = await res.json();
|
|
469
|
+
|
|
470
|
+
expect(data.publicAction).toBeDefined();
|
|
471
|
+
expect(data.securedAction).toBeUndefined();
|
|
472
|
+
});
|
|
473
|
+
});
|
|
474
|
+
|
|
369
475
|
describe("caching and ETag", () => {
|
|
370
476
|
it("should return ETag header on first request", async ({ expect }) => {
|
|
371
477
|
class App {
|
|
@@ -5,6 +5,7 @@ import type { SecurityProvider, UserAccountToken } from "alepha/security";
|
|
|
5
5
|
import {
|
|
6
6
|
$action,
|
|
7
7
|
$route,
|
|
8
|
+
$sse,
|
|
8
9
|
type ClientRequestEntry,
|
|
9
10
|
type ClientRequestOptions,
|
|
10
11
|
type RequestConfigSchema,
|
|
@@ -12,8 +13,7 @@ import {
|
|
|
12
13
|
serverApiOptions,
|
|
13
14
|
} from "alepha/server";
|
|
14
15
|
import type { ApiRegistryResponse } from "../schemas/apiLinksResponseSchema.ts";
|
|
15
|
-
import {
|
|
16
|
-
import { LinkProvider } from "./LinkProvider.ts";
|
|
16
|
+
import { type HttpClientLink, LinkProvider } from "./LinkProvider.ts";
|
|
17
17
|
import { RemotePrimitiveProvider } from "./RemotePrimitiveProvider.ts";
|
|
18
18
|
|
|
19
19
|
export class ServerLinksProvider {
|
|
@@ -68,6 +68,24 @@ export class ServerLinksProvider {
|
|
|
68
68
|
) => action.run(config, options),
|
|
69
69
|
});
|
|
70
70
|
}
|
|
71
|
+
|
|
72
|
+
// convert all $sse to local links
|
|
73
|
+
for (const sse of this.alepha.primitives($sse)) {
|
|
74
|
+
this.linkProvider.registerLink({
|
|
75
|
+
name: sse.name,
|
|
76
|
+
group: sse.group,
|
|
77
|
+
kind: "sse",
|
|
78
|
+
schema: {
|
|
79
|
+
body: sse.schema?.body,
|
|
80
|
+
},
|
|
81
|
+
method: "POST",
|
|
82
|
+
prefix: sse.prefix,
|
|
83
|
+
path: sse.path,
|
|
84
|
+
handler: async (config) => {
|
|
85
|
+
return sse.run(config as any) as any;
|
|
86
|
+
},
|
|
87
|
+
});
|
|
88
|
+
}
|
|
71
89
|
},
|
|
72
90
|
});
|
|
73
91
|
|
|
@@ -127,6 +145,51 @@ export class ServerLinksProvider {
|
|
|
127
145
|
},
|
|
128
146
|
});
|
|
129
147
|
|
|
148
|
+
/**
|
|
149
|
+
* On-demand schemas endpoint — returns JSON Schemas for requested actions.
|
|
150
|
+
*
|
|
151
|
+
* Schemas are filtered by the user's permissions (same logic as the registry).
|
|
152
|
+
*/
|
|
153
|
+
public readonly schemas = $route({
|
|
154
|
+
method: "POST",
|
|
155
|
+
path: "/api/_links/schemas",
|
|
156
|
+
schema: {
|
|
157
|
+
body: t.object({
|
|
158
|
+
actions: t.array(t.text()),
|
|
159
|
+
}),
|
|
160
|
+
response: t.record(
|
|
161
|
+
t.text(),
|
|
162
|
+
t.object({
|
|
163
|
+
body: t.optional(t.string()),
|
|
164
|
+
response: t.optional(t.string()),
|
|
165
|
+
}),
|
|
166
|
+
),
|
|
167
|
+
},
|
|
168
|
+
handler: async ({ body, user }) => {
|
|
169
|
+
const result: Record<string, { body?: string; response?: string }> = {};
|
|
170
|
+
|
|
171
|
+
for (const name of body.actions) {
|
|
172
|
+
const link = this.linkProvider
|
|
173
|
+
.getServerLinks()
|
|
174
|
+
.find((l) => l.name === name && !l.host);
|
|
175
|
+
|
|
176
|
+
if (!link) continue;
|
|
177
|
+
if (!this.isLinkAccessible(link, user)) continue;
|
|
178
|
+
|
|
179
|
+
const entry: { body?: string; response?: string } = {};
|
|
180
|
+
if (link.schema?.body) {
|
|
181
|
+
entry.body = JSON.stringify(link.schema.body);
|
|
182
|
+
}
|
|
183
|
+
if (link.schema?.response) {
|
|
184
|
+
entry.response = JSON.stringify(link.schema.response);
|
|
185
|
+
}
|
|
186
|
+
result[name] = entry;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
return result as any;
|
|
190
|
+
},
|
|
191
|
+
});
|
|
192
|
+
|
|
130
193
|
/**
|
|
131
194
|
* Batch endpoint — execute multiple actions in a single HTTP request.
|
|
132
195
|
* Each sub-request is independent: errors in one don't affect others.
|
|
@@ -208,7 +271,6 @@ export class ServerLinksProvider {
|
|
|
208
271
|
? securityProvider.getPermissions(user)
|
|
209
272
|
: undefined;
|
|
210
273
|
|
|
211
|
-
const pool = new DefinitionsPool();
|
|
212
274
|
const actions: Record<string, any> = {};
|
|
213
275
|
const permissions: string[] = [];
|
|
214
276
|
|
|
@@ -228,56 +290,12 @@ export class ServerLinksProvider {
|
|
|
228
290
|
for (const link of this.linkProvider.getServerLinks()) {
|
|
229
291
|
// SKIP REMOTE LINKS, remote links are handled separately for security
|
|
230
292
|
if (link.host) continue;
|
|
231
|
-
|
|
232
|
-
if (securityProvider && link.secured) {
|
|
233
|
-
// skip secured links if user is not provided
|
|
234
|
-
if (!user) {
|
|
235
|
-
continue;
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
if (typeof link.secured === "object") {
|
|
239
|
-
// issuer check
|
|
240
|
-
if (
|
|
241
|
-
link.secured.issuers?.length &&
|
|
242
|
-
(!user.realm || !link.secured.issuers.includes(user.realm))
|
|
243
|
-
) {
|
|
244
|
-
continue;
|
|
245
|
-
}
|
|
246
|
-
|
|
247
|
-
// role check
|
|
248
|
-
if (link.secured.roles?.length) {
|
|
249
|
-
const hasRole = link.secured.roles.some((role: string) =>
|
|
250
|
-
user.roles?.includes(role),
|
|
251
|
-
);
|
|
252
|
-
if (!hasRole) continue;
|
|
253
|
-
}
|
|
254
|
-
|
|
255
|
-
// explicit permission check
|
|
256
|
-
if (link.secured.permissions?.length) {
|
|
257
|
-
const perms = link.secured.permissions;
|
|
258
|
-
|
|
259
|
-
let allowed = true;
|
|
260
|
-
for (const perm of perms) {
|
|
261
|
-
const result = securityProvider.checkPermission(
|
|
262
|
-
perm,
|
|
263
|
-
...(user.roles ?? []),
|
|
264
|
-
);
|
|
265
|
-
if (!result.isAuthorized) {
|
|
266
|
-
allowed = false;
|
|
267
|
-
break;
|
|
268
|
-
}
|
|
269
|
-
}
|
|
270
|
-
if (!allowed) continue;
|
|
271
|
-
}
|
|
272
|
-
}
|
|
273
|
-
// link.secured === true → auth only, user is already checked above
|
|
274
|
-
}
|
|
293
|
+
if (!this.isLinkAccessible(link, user)) continue;
|
|
275
294
|
|
|
276
295
|
actions[link.name] = {
|
|
277
296
|
path: link.path,
|
|
278
297
|
method: link.method || undefined,
|
|
279
|
-
|
|
280
|
-
response: pool.ref(link.schema?.response),
|
|
298
|
+
kind: link.kind,
|
|
281
299
|
contentType: link.contentType,
|
|
282
300
|
service: link.service,
|
|
283
301
|
};
|
|
@@ -298,20 +316,6 @@ export class ServerLinksProvider {
|
|
|
298
316
|
for (const { remote, registry } of remoteResults) {
|
|
299
317
|
const remotePrefix = registry.prefix ?? "/api";
|
|
300
318
|
|
|
301
|
-
// Merge remote definitions into our pool
|
|
302
|
-
const remoteDefMap = new Map<string, string>();
|
|
303
|
-
if (registry.definitions) {
|
|
304
|
-
for (const [refKey, jsonString] of Object.entries(
|
|
305
|
-
registry.definitions,
|
|
306
|
-
)) {
|
|
307
|
-
const schema = JSON.parse(jsonString);
|
|
308
|
-
const newRef = pool.ref(schema);
|
|
309
|
-
if (newRef) {
|
|
310
|
-
remoteDefMap.set(refKey, newRef);
|
|
311
|
-
}
|
|
312
|
-
}
|
|
313
|
-
}
|
|
314
|
-
|
|
315
319
|
// Merge remote actions
|
|
316
320
|
for (const [name, action] of Object.entries(registry.actions)) {
|
|
317
321
|
let path = action.path.replace(remotePrefix, "");
|
|
@@ -322,12 +326,6 @@ export class ServerLinksProvider {
|
|
|
322
326
|
actions[name] = {
|
|
323
327
|
path,
|
|
324
328
|
method: action.method,
|
|
325
|
-
body: action.body
|
|
326
|
-
? (remoteDefMap.get(action.body) ?? action.body)
|
|
327
|
-
: undefined,
|
|
328
|
-
response: action.response
|
|
329
|
-
? (remoteDefMap.get(action.response) ?? action.response)
|
|
330
|
-
: undefined,
|
|
331
329
|
contentType: action.contentType,
|
|
332
330
|
service: remote.name,
|
|
333
331
|
};
|
|
@@ -341,16 +339,58 @@ export class ServerLinksProvider {
|
|
|
341
339
|
|
|
342
340
|
this.serverTimingProvider.endTiming("fetchRemoteLinks");
|
|
343
341
|
|
|
344
|
-
const definitions = pool.toJSON();
|
|
345
|
-
|
|
346
342
|
return {
|
|
347
343
|
prefix: this.serverApi.prefix,
|
|
348
|
-
definitions:
|
|
349
|
-
Object.keys(definitions).length > 0 ? definitions : undefined,
|
|
350
344
|
actions,
|
|
351
345
|
permissions: permissions.length > 0 ? permissions : undefined,
|
|
352
346
|
};
|
|
353
347
|
}
|
|
348
|
+
|
|
349
|
+
/**
|
|
350
|
+
* Check if a link is accessible by the given user based on security rules.
|
|
351
|
+
*/
|
|
352
|
+
protected isLinkAccessible(
|
|
353
|
+
link: HttpClientLink,
|
|
354
|
+
user?: UserAccountToken,
|
|
355
|
+
): boolean {
|
|
356
|
+
const { securityProvider } = this;
|
|
357
|
+
|
|
358
|
+
if (securityProvider && link.secured) {
|
|
359
|
+
if (!user) return false;
|
|
360
|
+
|
|
361
|
+
if (typeof link.secured === "object") {
|
|
362
|
+
// issuer check
|
|
363
|
+
if (
|
|
364
|
+
link.secured.issuers?.length &&
|
|
365
|
+
(!user.realm || !link.secured.issuers.includes(user.realm))
|
|
366
|
+
) {
|
|
367
|
+
return false;
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
// role check
|
|
371
|
+
if (link.secured.roles?.length) {
|
|
372
|
+
const hasRole = link.secured.roles.some((role: string) =>
|
|
373
|
+
user.roles?.includes(role),
|
|
374
|
+
);
|
|
375
|
+
if (!hasRole) return false;
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
// explicit permission check
|
|
379
|
+
if (link.secured.permissions?.length) {
|
|
380
|
+
for (const perm of link.secured.permissions) {
|
|
381
|
+
const result = securityProvider.checkPermission(
|
|
382
|
+
perm,
|
|
383
|
+
...(user.roles ?? []),
|
|
384
|
+
);
|
|
385
|
+
if (!result.isAuthorized) return false;
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
// link.secured === true → auth only, user is already checked above
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
return true;
|
|
393
|
+
}
|
|
354
394
|
}
|
|
355
395
|
|
|
356
396
|
export interface GetApiLinksOptions {
|
|
@@ -13,24 +13,17 @@ export const apiActionSchema = t.object({
|
|
|
13
13
|
}),
|
|
14
14
|
),
|
|
15
15
|
|
|
16
|
-
|
|
17
|
-
t.text({
|
|
18
|
-
description:
|
|
19
|
-
"Reference to a definitions pool entry (e.g. '$0') for the request body JSON Schema.",
|
|
20
|
-
}),
|
|
21
|
-
),
|
|
22
|
-
|
|
23
|
-
response: t.optional(
|
|
16
|
+
contentType: t.optional(
|
|
24
17
|
t.text({
|
|
25
18
|
description:
|
|
26
|
-
"
|
|
19
|
+
"Content type for the request body. Only present for non-JSON types (e.g. 'multipart/form-data'). When absent, defaults to application/json.",
|
|
27
20
|
}),
|
|
28
21
|
),
|
|
29
22
|
|
|
30
|
-
|
|
23
|
+
kind: t.optional(
|
|
31
24
|
t.text({
|
|
32
25
|
description:
|
|
33
|
-
"
|
|
26
|
+
"Action kind. Used to distinguish special action types (e.g. 'sse' for Server-Sent Events streams).",
|
|
34
27
|
}),
|
|
35
28
|
),
|
|
36
29
|
|
|
@@ -45,16 +38,6 @@ export const apiActionSchema = t.object({
|
|
|
45
38
|
export const apiRegistryResponseSchema = t.object({
|
|
46
39
|
prefix: t.optional(t.text()),
|
|
47
40
|
|
|
48
|
-
definitions: t.optional(
|
|
49
|
-
t.record(
|
|
50
|
-
t.text(),
|
|
51
|
-
t.string({
|
|
52
|
-
description:
|
|
53
|
-
"Pre-stringified JSON Schema. Values can be 2KB+, so t.string() (no maxLength) is used instead of t.text().",
|
|
54
|
-
}),
|
|
55
|
-
),
|
|
56
|
-
),
|
|
57
|
-
|
|
58
41
|
actions: t.record(t.text(), apiActionSchema),
|
|
59
42
|
|
|
60
43
|
permissions: t.optional(t.array(t.text())),
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { $inject } from "alepha";
|
|
2
|
+
import { $logger } from "alepha/logger";
|
|
2
3
|
import { HttpClient, HttpError } from "alepha/server";
|
|
3
4
|
|
|
4
5
|
/**
|
|
@@ -15,6 +16,7 @@ import { HttpClient, HttpError } from "alepha/server";
|
|
|
15
16
|
export class BatchCollector {
|
|
16
17
|
protected static readonly MAX_BATCH_SIZE = 20;
|
|
17
18
|
|
|
19
|
+
protected readonly log = $logger();
|
|
18
20
|
protected readonly httpClient = $inject(HttpClient);
|
|
19
21
|
|
|
20
22
|
protected pending: PendingBatchEntry[] = [];
|
|
@@ -29,10 +31,10 @@ export class BatchCollector {
|
|
|
29
31
|
|
|
30
32
|
if (!this.scheduled) {
|
|
31
33
|
this.scheduled = true;
|
|
32
|
-
|
|
34
|
+
setTimeout(() => {
|
|
33
35
|
this.scheduled = false;
|
|
34
|
-
this.flush();
|
|
35
|
-
});
|
|
36
|
+
this.flush().catch((err) => this.log.error(err));
|
|
37
|
+
}, 10);
|
|
36
38
|
}
|
|
37
39
|
});
|
|
38
40
|
}
|