alepha 0.18.2 → 0.18.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/assets/devtools-ui/200.html +2 -2
- package/assets/devtools-ui/200.html.br +0 -0
- package/assets/devtools-ui/404.html +2 -2
- package/assets/devtools-ui/404.html.br +0 -0
- package/assets/devtools-ui/{asset.BfSBZ5Dd.css → asset.hG_f8HuK.css} +1 -1
- package/assets/devtools-ui/asset.hG_f8HuK.css.br +0 -0
- package/assets/devtools-ui/chunk.B3au4Lhg.js +1 -0
- package/assets/devtools-ui/chunk.B3au4Lhg.js.br +0 -0
- package/assets/devtools-ui/chunk.BLOrlnMB.js +1 -0
- package/assets/devtools-ui/chunk.BLOrlnMB.js.br +0 -0
- package/assets/devtools-ui/chunk.BLR01ljW.js +1 -0
- package/assets/devtools-ui/chunk.BLR01ljW.js.br +0 -0
- package/assets/devtools-ui/chunk.BTXaIUlA.js +1 -0
- package/assets/devtools-ui/chunk.BTXaIUlA.js.br +0 -0
- package/assets/devtools-ui/{chunk.lJL-lgnW.js → chunk.BhJaxmm8.js} +1 -1
- package/assets/devtools-ui/chunk.BhJaxmm8.js.br +0 -0
- package/assets/devtools-ui/chunk.BtoNxFuL.js +1 -0
- package/assets/devtools-ui/chunk.BtoNxFuL.js.br +0 -0
- package/assets/devtools-ui/chunk.C8YUV2Wd.js +1 -0
- package/assets/devtools-ui/chunk.C8YUV2Wd.js.br +0 -0
- package/assets/devtools-ui/{chunk.M6wyKO_3.js → chunk.CBbIgDzE.js} +2 -2
- package/assets/devtools-ui/chunk.CBbIgDzE.js.br +0 -0
- package/assets/devtools-ui/chunk.CFqIniwA.js +1 -0
- package/assets/devtools-ui/chunk.CFqIniwA.js.br +0 -0
- package/assets/devtools-ui/chunk.CLFF7f7-.js +1 -0
- package/assets/devtools-ui/chunk.CLFF7f7-.js.br +0 -0
- package/assets/devtools-ui/chunk.CRsBbA10.js +1 -0
- package/assets/devtools-ui/chunk.CRsBbA10.js.br +0 -0
- package/assets/devtools-ui/{chunk.DbEH1oOB.js → chunk.CZPo6v95.js} +1 -1
- package/assets/devtools-ui/chunk.CZPo6v95.js.br +0 -0
- package/assets/devtools-ui/chunk.D0fWgNos.js +1 -0
- package/assets/devtools-ui/chunk.D0fWgNos.js.br +1 -0
- package/assets/devtools-ui/chunk.D7-0ziQ6.js +1 -0
- package/assets/devtools-ui/chunk.D7-0ziQ6.js.br +0 -0
- package/assets/devtools-ui/chunk.DAewe0vm.js +1 -0
- package/assets/devtools-ui/chunk.DAewe0vm.js.br +0 -0
- package/assets/devtools-ui/chunk.DJRQEYqK.js +1 -0
- package/assets/devtools-ui/chunk.DJRQEYqK.js.br +0 -0
- package/assets/devtools-ui/{chunk.CZl6J9DF.js → chunk.DMAxv14p.js} +1 -1
- package/assets/devtools-ui/chunk.DMAxv14p.js.br +0 -0
- package/assets/devtools-ui/{chunk.BT2IiBkZ.js → chunk.DMImnNjU.js} +1 -1
- package/assets/devtools-ui/chunk.DMImnNjU.js.br +0 -0
- package/assets/devtools-ui/chunk.DeeQsidk.js +9 -0
- package/assets/devtools-ui/chunk.DeeQsidk.js.br +0 -0
- package/assets/devtools-ui/chunk.DqEwn9Vj.js +7 -0
- package/assets/devtools-ui/chunk.DqEwn9Vj.js.br +0 -0
- package/assets/devtools-ui/chunk.Dt8OsQey.js +1 -0
- package/assets/devtools-ui/chunk.Dt8OsQey.js.br +0 -0
- package/assets/devtools-ui/{chunk.B9pX3zit.js → chunk.Dtp8oa_f.js} +1 -1
- package/assets/devtools-ui/chunk.Dtp8oa_f.js.br +0 -0
- package/assets/devtools-ui/chunk.Dx3JzAYM.js +1 -0
- package/assets/devtools-ui/chunk.Dx3JzAYM.js.br +0 -0
- package/assets/devtools-ui/chunk.GCOj1-5E.js +1 -0
- package/assets/devtools-ui/chunk.GCOj1-5E.js.br +0 -0
- package/assets/devtools-ui/chunk.IC1LD8BH.js +1 -0
- package/assets/devtools-ui/chunk.IC1LD8BH.js.br +0 -0
- package/assets/devtools-ui/chunk.IwuB_TqW.js +1 -0
- package/assets/devtools-ui/chunk.IwuB_TqW.js.br +0 -0
- package/assets/devtools-ui/chunk.Qqapj2zq.js +1 -0
- package/assets/devtools-ui/chunk.Qqapj2zq.js.br +0 -0
- package/assets/devtools-ui/{chunk.C79YouPp.js → chunk.TKKKndOy.js} +1 -1
- package/assets/devtools-ui/chunk.TKKKndOy.js.br +0 -0
- package/assets/devtools-ui/chunk.YHTVhFQT.js +1 -0
- package/assets/devtools-ui/chunk.YHTVhFQT.js.br +0 -0
- package/assets/devtools-ui/chunk.fnod6uEi.js +1 -0
- package/assets/devtools-ui/chunk.fnod6uEi.js.br +0 -0
- package/assets/devtools-ui/chunk.mOCRmXjo.js +1 -0
- package/assets/devtools-ui/chunk.mOCRmXjo.js.br +0 -0
- package/assets/devtools-ui/chunk.qZTNEAK0.js +1 -0
- package/assets/devtools-ui/chunk.qZTNEAK0.js.br +0 -0
- package/assets/devtools-ui/chunk.rc9m0y4-.js +1 -0
- package/assets/devtools-ui/chunk.rc9m0y4-.js.br +0 -0
- package/assets/devtools-ui/entry.Cxc5QLCU.js +80 -0
- package/assets/devtools-ui/entry.Cxc5QLCU.js.br +0 -0
- package/assets/devtools-ui/index.html +2 -2
- package/assets/devtools-ui/index.html.br +0 -0
- package/assets/swagger-ui/swagger-ui-bundle.js +1 -1
- package/assets/swagger-ui/swagger-ui.css +1 -1
- package/dist/api/audits/index.d.ts +61 -5
- package/dist/api/audits/index.d.ts.map +1 -1
- package/dist/api/files/index.d.ts +61 -5
- package/dist/api/files/index.d.ts.map +1 -1
- package/dist/api/jobs/index.d.ts +61 -5
- package/dist/api/jobs/index.d.ts.map +1 -1
- package/dist/api/jobs/index.js +4 -2
- package/dist/api/jobs/index.js.map +1 -1
- package/dist/api/keys/index.d.ts +5 -5
- package/dist/api/notifications/index.browser.js +44 -1
- package/dist/api/notifications/index.browser.js.map +1 -1
- package/dist/api/notifications/index.d.ts +187 -2
- package/dist/api/notifications/index.d.ts.map +1 -1
- package/dist/api/notifications/index.js +143 -8
- package/dist/api/notifications/index.js.map +1 -1
- package/dist/api/parameters/index.d.ts +61 -5
- package/dist/api/parameters/index.d.ts.map +1 -1
- package/dist/api/users/index.d.ts +330 -93
- package/dist/api/users/index.d.ts.map +1 -1
- package/dist/api/users/index.js +27 -36
- package/dist/api/users/index.js.map +1 -1
- package/dist/cli/config/index.d.ts +46 -0
- package/dist/cli/config/index.d.ts.map +1 -0
- package/dist/cli/config/index.js +20 -0
- package/dist/cli/config/index.js.map +1 -0
- package/dist/cli/core/index.d.ts +69 -66
- package/dist/cli/core/index.d.ts.map +1 -1
- package/dist/cli/core/index.js +329 -196
- package/dist/cli/core/index.js.map +1 -1
- package/dist/cli/platform/index.d.ts +302 -63
- package/dist/cli/platform/index.d.ts.map +1 -1
- package/dist/cli/platform/index.js +455 -25
- package/dist/cli/platform/index.js.map +1 -1
- package/dist/core/index.browser.js +125 -87
- package/dist/core/index.browser.js.map +1 -1
- package/dist/core/index.d.ts +62 -53
- package/dist/core/index.d.ts.map +1 -1
- package/dist/core/index.js +125 -87
- package/dist/core/index.js.map +1 -1
- package/dist/core/index.native.js +125 -87
- package/dist/core/index.native.js.map +1 -1
- package/dist/core/index.workerd.js +125 -87
- package/dist/core/index.workerd.js.map +1 -1
- package/dist/crypto/index.d.ts +18 -1
- package/dist/crypto/index.d.ts.map +1 -1
- package/dist/crypto/index.js +29 -3
- package/dist/crypto/index.js.map +1 -1
- package/dist/devtools/index.js +3 -12
- package/dist/devtools/index.js.map +1 -1
- package/dist/logger/index.d.ts +10 -1
- package/dist/logger/index.d.ts.map +1 -1
- package/dist/logger/index.js +19 -9
- package/dist/logger/index.js.map +1 -1
- package/dist/orm/core/index.browser.js +57 -1
- package/dist/orm/core/index.browser.js.map +1 -1
- package/dist/orm/core/index.bun.js +378 -19
- package/dist/orm/core/index.bun.js.map +1 -1
- package/dist/orm/core/index.d.ts +328 -9
- package/dist/orm/core/index.d.ts.map +1 -1
- package/dist/orm/core/index.js +384 -21
- package/dist/orm/core/index.js.map +1 -1
- package/dist/orm/postgres/index.bun.js +49 -17
- package/dist/orm/postgres/index.bun.js.map +1 -1
- package/dist/orm/postgres/index.d.ts +47 -21
- package/dist/orm/postgres/index.d.ts.map +1 -1
- package/dist/orm/postgres/index.js +52 -17
- package/dist/orm/postgres/index.js.map +1 -1
- package/dist/react/core/index.d.ts +1 -1
- package/dist/react/core/index.d.ts.map +1 -1
- package/dist/react/core/index.js +6 -1
- package/dist/react/core/index.js.map +1 -1
- package/dist/react/form/index.d.ts +28 -18
- package/dist/react/form/index.d.ts.map +1 -1
- package/dist/react/form/index.js +92 -56
- package/dist/react/form/index.js.map +1 -1
- package/dist/react/router/index.browser.js +448 -116
- package/dist/react/router/index.browser.js.map +1 -1
- package/dist/react/router/index.d.ts +102 -40
- package/dist/react/router/index.d.ts.map +1 -1
- package/dist/react/router/index.js +453 -92
- package/dist/react/router/index.js.map +1 -1
- package/dist/security/index.d.ts +3 -11
- package/dist/security/index.d.ts.map +1 -1
- package/dist/security/index.js +6 -11
- package/dist/security/index.js.map +1 -1
- package/dist/server/auth/index.d.ts +22 -24
- package/dist/server/auth/index.d.ts.map +1 -1
- package/dist/server/auth/index.js +102 -82
- package/dist/server/auth/index.js.map +1 -1
- package/dist/server/cookies/index.d.ts +7 -4
- package/dist/server/cookies/index.d.ts.map +1 -1
- package/dist/server/cookies/index.js +13 -12
- package/dist/server/cookies/index.js.map +1 -1
- package/dist/server/core/index.d.ts +288 -4
- package/dist/server/core/index.d.ts.map +1 -1
- package/dist/server/core/index.js +375 -2
- package/dist/server/core/index.js.map +1 -1
- package/dist/server/links/index.browser.js +10 -71
- package/dist/server/links/index.browser.js.map +1 -1
- package/dist/server/links/index.d.ts +32 -49
- package/dist/server/links/index.d.ts.map +1 -1
- package/dist/server/links/index.js +73 -100
- package/dist/server/links/index.js.map +1 -1
- package/dist/system/index.browser.js +221 -2
- package/dist/system/index.browser.js.map +1 -1
- package/dist/system/index.d.ts +63 -1
- package/dist/system/index.d.ts.map +1 -1
- package/dist/system/index.js +221 -1
- package/dist/system/index.js.map +1 -1
- package/dist/system/index.workerd.js +224 -4
- package/dist/system/index.workerd.js.map +1 -1
- package/package.json +10 -5
- package/src/api/jobs/providers/JobProvider.ts +6 -3
- package/src/api/notifications/controllers/AdminNotificationController.ts +83 -0
- package/src/api/notifications/index.browser.ts +3 -0
- package/src/api/notifications/index.ts +14 -2
- package/src/api/notifications/jobs/NotificationJobs.ts +11 -2
- package/src/api/notifications/schemas/notificationDetailResourceSchema.ts +20 -0
- package/src/api/notifications/schemas/notificationQuerySchema.ts +19 -0
- package/src/api/notifications/schemas/notificationResourceSchema.ts +18 -0
- package/src/api/notifications/services/NotificationSenderService.ts +15 -2
- package/src/api/users/atoms/realmAuthSettingsAtom.ts +28 -32
- package/src/api/users/buckets/UserBuckets.ts +1 -1
- package/src/api/users/jobs/UserJobs.ts +1 -1
- package/src/api/users/primitives/$realm.ts +8 -49
- package/src/api/users/providers/RealmProvider.ts +2 -3
- package/src/api/users/services/RegistrationService.spec.ts +7 -7
- package/src/api/users/services/RegistrationService.ts +3 -3
- package/src/api/users/services/SessionService.spec.ts +4 -4
- package/src/api/users/services/SessionService.ts +3 -3
- package/src/cli/{core → config}/defineConfig.ts +14 -20
- package/src/cli/config/index.ts +1 -0
- package/src/cli/core/commands/db.ts +65 -1
- package/src/cli/core/commands/dev.ts +1 -0
- package/src/cli/core/commands/init.ts +2 -192
- package/src/cli/core/index.ts +34 -11
- package/src/cli/core/providers/ViteDevServerProvider.ts +52 -13
- package/src/cli/core/services/PackageManagerUtils.ts +43 -21
- package/src/cli/core/services/ProjectScaffolder.ts +214 -2
- package/src/cli/core/services/ViteUtils.ts +57 -0
- package/src/cli/core/tasks/BuildClientTask.ts +7 -2
- package/src/cli/core/tasks/BuildCloudflareTask.ts +4 -12
- package/src/cli/core/tasks/BuildServerTask.ts +2 -0
- package/src/cli/core/tasks/BuildVercelTask.ts +165 -168
- package/src/cli/core/templates/alephaConfigTs.ts +1 -1
- package/src/cli/core/templates/apiAppSecurityTs.ts +5 -8
- package/src/cli/core/templates/tsconfigJson.ts +6 -1
- package/src/cli/platform/adapters/CloudflareAdapter.spec.ts +1 -1
- package/src/cli/platform/adapters/CloudflareAdapter.ts +30 -29
- package/src/cli/platform/atoms/platformOptions.ts +21 -0
- package/src/cli/platform/commands/SecretsCommand.spec.ts +298 -0
- package/src/cli/platform/commands/SecretsCommand.ts +283 -0
- package/src/cli/platform/commands/platform.ts +12 -0
- package/src/cli/platform/index.ts +14 -28
- package/src/cli/platform/providers/GitHubSecretStore.spec.ts +153 -0
- package/src/cli/platform/providers/GitHubSecretStore.ts +112 -0
- package/src/cli/platform/providers/MemorySecretStore.ts +114 -0
- package/src/cli/platform/providers/SecretStoreProvider.ts +39 -0
- package/src/cli/platform/schemas/cloudflare.ts +2 -0
- package/src/cli/platform/services/CloudflareApi.ts +5 -2
- package/src/cli/platform/services/DockerComposeGenerator.spec.ts +115 -0
- package/src/cli/platform/services/DockerComposeGenerator.ts +46 -1
- package/src/cli/platform/services/SecretFilterService.spec.ts +111 -0
- package/src/cli/platform/services/SecretFilterService.ts +54 -0
- package/src/core/Alepha.ts +94 -25
- package/src/core/__tests__/Alepha-parseEnv.spec.ts +20 -0
- package/src/core/primitives/$memoize.ts +38 -26
- package/src/core/providers/AlsProvider.ts +2 -0
- package/src/core/providers/EventManager.ts +4 -0
- package/src/core/providers/KeylessJsonSchemaCodec.spec.ts +1 -4
- package/src/core/providers/KeylessJsonSchemaCodec.ts +19 -125
- package/src/core/providers/SchemaValidator.spec.ts +36 -0
- package/src/core/providers/SchemaValidator.ts +9 -0
- package/src/crypto/index.ts +6 -1
- package/src/crypto/providers/SecretProvider.ts +36 -0
- package/src/devtools/providers/DevToolsProvider.ts +3 -12
- package/src/logger/index.ts +33 -6
- package/src/logger/providers/PrettyFormatterProvider.ts +5 -3
- package/src/orm/__tests__/orm-next-tests.ts +492 -0
- package/src/orm/__tests__/orm-next.spec.ts +140 -0
- package/src/orm/core/constants/PG_SYMBOLS.ts +17 -0
- package/src/orm/core/index.bun.ts +3 -6
- package/src/orm/core/index.shared-server.ts +2 -0
- package/src/orm/core/index.shared.ts +2 -0
- package/src/orm/core/index.ts +5 -7
- package/src/orm/core/interfaces/AggregateQuery.ts +103 -0
- package/src/orm/core/interfaces/PgQueryWhere.ts +7 -0
- package/src/orm/core/primitives/$entity.ts +8 -0
- package/src/orm/core/primitives/$repository.ts +6 -3
- package/src/orm/core/primitives/$view.ts +88 -0
- package/src/orm/core/providers/DbCacheProvider.ts +66 -0
- package/src/orm/core/providers/DrizzleKitProvider.ts +42 -0
- package/src/orm/core/providers/drivers/BunSqliteProvider.ts +2 -3
- package/src/orm/core/providers/drivers/CloudflareD1Provider.ts +12 -0
- package/src/orm/core/providers/drivers/DatabaseProvider.ts +39 -0
- package/src/orm/core/providers/drivers/NodeSqliteProvider.ts +2 -3
- package/src/orm/core/schemas/databaseEnvSchema.ts +31 -0
- package/src/orm/core/schemas/insertSchema.ts +13 -3
- package/src/orm/core/schemas/updateSchema.ts +14 -3
- package/src/orm/core/services/ModelBuilder.ts +26 -14
- package/src/orm/core/services/QueryManager.ts +13 -0
- package/src/orm/core/services/Repository.ts +307 -5
- package/src/orm/core/services/SqliteModelBuilder.ts +38 -0
- package/src/orm/postgres/index.bun.ts +4 -7
- package/src/orm/postgres/index.ts +4 -7
- package/src/orm/postgres/providers/BunPostgresProvider.ts +12 -2
- package/src/orm/postgres/providers/NodePostgresProvider.ts +7 -0
- package/src/orm/postgres/providers/PglitePostgresProvider.ts +10 -17
- package/src/orm/postgres/providers/PostgresProvider.ts +7 -36
- package/src/orm/postgres/schemas/postgresEnvSchema.ts +32 -0
- package/src/orm/postgres/services/PostgresModelBuilder.ts +40 -0
- package/src/react/core/components/ErrorBoundary.tsx +5 -2
- package/src/react/form/hooks/useFieldValue.ts +34 -0
- package/src/react/form/hooks/useForm.browser.spec.tsx +94 -9
- package/src/react/form/hooks/useForm.ts +14 -2
- package/src/react/form/hooks/useFormState.ts +10 -10
- package/src/react/form/hooks/useFormValues.ts +29 -0
- package/src/react/form/index.ts +3 -1
- package/src/react/form/services/FormModel.ts +53 -122
- package/src/react/router/components/ErrorViewer.tsx +333 -34
- package/src/react/router/components/NestedView.tsx +10 -3
- package/src/react/router/primitives/$page.browser.spec.tsx +34 -0
- package/src/react/router/primitives/$page.spec.tsx +20 -0
- package/src/react/router/primitives/$page.ts +24 -0
- package/src/react/router/providers/ReactBrowserRouterProvider.ts +14 -2
- package/src/react/router/providers/ReactPageProvider.ts +156 -73
- package/src/react/router/providers/ReactServerProvider.ts +40 -2
- package/src/react/router/providers/ReactServerTemplateProvider.ts +13 -1
- package/src/security/providers/SecurityProvider.ts +5 -27
- package/src/server/auth/primitives/$auth.ts +52 -19
- package/src/server/auth/providers/ServerAuthProvider.ts +145 -139
- package/src/server/cookies/providers/ServerCookiesProvider.ts +12 -24
- package/src/server/core/index.ts +3 -1
- package/src/server/core/primitives/$sse.spec.ts +315 -0
- package/src/server/core/primitives/$sse.ts +715 -0
- package/src/server/links/index.browser.ts +1 -3
- package/src/server/links/index.ts +0 -3
- package/src/server/links/providers/LinkProvider.spec.ts +12 -21
- package/src/server/links/providers/LinkProvider.ts +20 -52
- package/src/server/links/providers/ServerLinksProvider.spec.ts +106 -0
- package/src/server/links/providers/ServerLinksProvider.ts +113 -73
- package/src/server/links/schemas/apiLinksResponseSchema.ts +4 -21
- package/src/server/links/services/BatchCollector.ts +5 -3
- package/src/system/index.browser.ts +1 -0
- package/src/system/index.ts +3 -0
- package/src/system/index.workerd.ts +39 -1
- package/src/system/providers/WorkerdFileSystemProvider.ts +365 -0
- package/assets/devtools-ui/asset.BfSBZ5Dd.css.br +0 -0
- package/assets/devtools-ui/chunk.2NYaoqWt.js +0 -1
- package/assets/devtools-ui/chunk.2NYaoqWt.js.br +0 -0
- package/assets/devtools-ui/chunk.B052Z_xQ.js +0 -1
- package/assets/devtools-ui/chunk.B052Z_xQ.js.br +0 -0
- package/assets/devtools-ui/chunk.B4kVY90C.js +0 -1
- package/assets/devtools-ui/chunk.B4kVY90C.js.br +0 -0
- package/assets/devtools-ui/chunk.B7QJXctB.js +0 -1
- package/assets/devtools-ui/chunk.B7QJXctB.js.br +0 -0
- package/assets/devtools-ui/chunk.B9pX3zit.js.br +0 -0
- package/assets/devtools-ui/chunk.BKF9JxIo.js +0 -1
- package/assets/devtools-ui/chunk.BKF9JxIo.js.br +0 -0
- package/assets/devtools-ui/chunk.BOHgdTP-.js +0 -1
- package/assets/devtools-ui/chunk.BOHgdTP-.js.br +0 -0
- package/assets/devtools-ui/chunk.BOVFxkYC.js +0 -1
- package/assets/devtools-ui/chunk.BOVFxkYC.js.br +0 -0
- package/assets/devtools-ui/chunk.BR842zj5.js +0 -1
- package/assets/devtools-ui/chunk.BR842zj5.js.br +0 -0
- package/assets/devtools-ui/chunk.BT2IiBkZ.js.br +0 -0
- package/assets/devtools-ui/chunk.C79YouPp.js.br +0 -0
- package/assets/devtools-ui/chunk.C8mlBrjW.js +0 -9
- package/assets/devtools-ui/chunk.C8mlBrjW.js.br +0 -0
- package/assets/devtools-ui/chunk.CK0ow3AZ.js +0 -1
- package/assets/devtools-ui/chunk.CK0ow3AZ.js.br +0 -0
- package/assets/devtools-ui/chunk.CZl6J9DF.js.br +0 -0
- package/assets/devtools-ui/chunk.CdNr0YzS.js +0 -1
- package/assets/devtools-ui/chunk.CdNr0YzS.js.br +0 -0
- package/assets/devtools-ui/chunk.Ce6_6iIF.js +0 -1
- package/assets/devtools-ui/chunk.Ce6_6iIF.js.br +0 -0
- package/assets/devtools-ui/chunk.CpyDMr6O.js +0 -1
- package/assets/devtools-ui/chunk.CpyDMr6O.js.br +0 -0
- package/assets/devtools-ui/chunk.CyPmvPnY.js +0 -1
- package/assets/devtools-ui/chunk.CyPmvPnY.js.br +0 -0
- package/assets/devtools-ui/chunk.DTI_geWu.js +0 -1
- package/assets/devtools-ui/chunk.DTI_geWu.js.br +0 -0
- package/assets/devtools-ui/chunk.DbEH1oOB.js.br +0 -0
- package/assets/devtools-ui/chunk.Ddeqj5gv.js +0 -1
- package/assets/devtools-ui/chunk.Ddeqj5gv.js.br +0 -0
- package/assets/devtools-ui/chunk.DpRnB4vJ.js +0 -1
- package/assets/devtools-ui/chunk.DpRnB4vJ.js.br +0 -0
- package/assets/devtools-ui/chunk.DxPGTlsg.js +0 -1
- package/assets/devtools-ui/chunk.DxPGTlsg.js.br +0 -0
- package/assets/devtools-ui/chunk.G7_MMBJS.js +0 -1
- package/assets/devtools-ui/chunk.G7_MMBJS.js.br +0 -0
- package/assets/devtools-ui/chunk.M6wyKO_3.js.br +0 -0
- package/assets/devtools-ui/chunk.OUxNGmQ6.js +0 -1
- package/assets/devtools-ui/chunk.OUxNGmQ6.js.br +0 -0
- package/assets/devtools-ui/chunk.T1kle-fF.js +0 -1
- package/assets/devtools-ui/chunk.T1kle-fF.js.br +0 -0
- package/assets/devtools-ui/chunk.WjpsbQAv.js +0 -1
- package/assets/devtools-ui/chunk.WjpsbQAv.js.br +0 -0
- package/assets/devtools-ui/chunk.c6YgVx86.js +0 -1
- package/assets/devtools-ui/chunk.c6YgVx86.js.br +0 -0
- package/assets/devtools-ui/chunk.dwU3E_MU.js +0 -1
- package/assets/devtools-ui/chunk.dwU3E_MU.js.br +0 -0
- package/assets/devtools-ui/chunk.lJL-lgnW.js.br +0 -0
- package/assets/devtools-ui/chunk.lPWRmvA-.js +0 -7
- package/assets/devtools-ui/chunk.lPWRmvA-.js.br +0 -0
- package/assets/devtools-ui/chunk.p3HJvugM.js +0 -1
- package/assets/devtools-ui/chunk.p3HJvugM.js.br +0 -0
- package/assets/devtools-ui/chunk.r_Xoa_CI.js +0 -1
- package/assets/devtools-ui/chunk.r_Xoa_CI.js.br +0 -0
- package/assets/devtools-ui/chunk.sRNuTYXb.js +0 -1
- package/assets/devtools-ui/chunk.sRNuTYXb.js.br +0 -0
- package/assets/devtools-ui/chunk.tUjcyX5C.js +0 -1
- package/assets/devtools-ui/chunk.tUjcyX5C.js.br +0 -0
- package/assets/devtools-ui/chunk.thjBxvCA.js +0 -1
- package/assets/devtools-ui/chunk.thjBxvCA.js.br +0 -0
- package/assets/devtools-ui/entry.GYhBVRpC.js +0 -78
- package/assets/devtools-ui/entry.GYhBVRpC.js.br +0 -0
- package/src/server/links/services/DefinitionsPool.spec.ts +0 -86
- package/src/server/links/services/DefinitionsPool.ts +0 -43
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { $atom, $inject, $module, $use, Alepha, AlephaError, KIND, Primitive, createPrimitive,
|
|
1
|
+
import { $atom, $inject, $module, $use, Alepha, AlephaError, KIND, Primitive, createPrimitive, t } from "alepha";
|
|
2
2
|
import { $logger } from "alepha/logger";
|
|
3
3
|
import { HttpClient, HttpError, ServerReply, UnauthorizedError } from "alepha/server";
|
|
4
4
|
|
|
@@ -6,14 +6,12 @@ import { HttpClient, HttpError, ServerReply, UnauthorizedError } from "alepha/se
|
|
|
6
6
|
const apiActionSchema = t.object({
|
|
7
7
|
path: t.text({ description: "Pathname used to access the action." }),
|
|
8
8
|
method: t.optional(t.text({ description: "HTTP method. Omitted when GET (the default for ~75% of actions)." })),
|
|
9
|
-
body: t.optional(t.text({ description: "Reference to a definitions pool entry (e.g. '$0') for the request body JSON Schema." })),
|
|
10
|
-
response: t.optional(t.text({ description: "Reference to a definitions pool entry (e.g. '$0') for the response JSON Schema." })),
|
|
11
9
|
contentType: t.optional(t.text({ description: "Content type for the request body. Only present for non-JSON types (e.g. 'multipart/form-data'). When absent, defaults to application/json." })),
|
|
10
|
+
kind: t.optional(t.text({ description: "Action kind. Used to distinguish special action types (e.g. 'sse' for Server-Sent Events streams)." })),
|
|
12
11
|
service: t.optional(t.text({ description: "Service name associated with the action, used for service discovery and routing." }))
|
|
13
12
|
});
|
|
14
13
|
const apiRegistryResponseSchema = t.object({
|
|
15
14
|
prefix: t.optional(t.text()),
|
|
16
|
-
definitions: t.optional(t.record(t.text(), t.string({ description: "Pre-stringified JSON Schema. Values can be 2KB+, so t.string() (no maxLength) is used instead of t.text()." }))),
|
|
17
15
|
actions: t.record(t.text(), apiActionSchema),
|
|
18
16
|
permissions: t.optional(t.array(t.text()))
|
|
19
17
|
});
|
|
@@ -56,6 +54,7 @@ const linkOptionsAtom = $atom({
|
|
|
56
54
|
*/
|
|
57
55
|
var BatchCollector = class BatchCollector {
|
|
58
56
|
static MAX_BATCH_SIZE = 20;
|
|
57
|
+
log = $logger();
|
|
59
58
|
httpClient = $inject(HttpClient);
|
|
60
59
|
pending = [];
|
|
61
60
|
scheduled = false;
|
|
@@ -71,10 +70,10 @@ var BatchCollector = class BatchCollector {
|
|
|
71
70
|
});
|
|
72
71
|
if (!this.scheduled) {
|
|
73
72
|
this.scheduled = true;
|
|
74
|
-
|
|
73
|
+
setTimeout(() => {
|
|
75
74
|
this.scheduled = false;
|
|
76
|
-
this.flush();
|
|
77
|
-
});
|
|
75
|
+
this.flush().catch((err) => this.log.error(err));
|
|
76
|
+
}, 10);
|
|
78
77
|
}
|
|
79
78
|
});
|
|
80
79
|
}
|
|
@@ -163,7 +162,6 @@ var LinkProvider = class LinkProvider {
|
|
|
163
162
|
serverLinkMap = /* @__PURE__ */ new Map();
|
|
164
163
|
actionMap = /* @__PURE__ */ new Map();
|
|
165
164
|
permissions = /* @__PURE__ */ new Set();
|
|
166
|
-
definitions = {};
|
|
167
165
|
lastLoadedRegistry = null;
|
|
168
166
|
batchCollector;
|
|
169
167
|
options = $use(linkOptionsAtom);
|
|
@@ -196,17 +194,15 @@ var LinkProvider = class LinkProvider {
|
|
|
196
194
|
*/
|
|
197
195
|
loadRegistry(registry) {
|
|
198
196
|
this.lastLoadedRegistry = registry;
|
|
199
|
-
this.definitions = registry.definitions ?? {};
|
|
200
197
|
this.permissions.clear();
|
|
201
198
|
this.actionMap.clear();
|
|
202
199
|
for (const [name, action] of Object.entries(registry.actions)) this.actionMap.set(name, {
|
|
203
200
|
name,
|
|
204
201
|
path: action.path,
|
|
202
|
+
kind: action.kind,
|
|
205
203
|
method: action.method,
|
|
206
204
|
contentType: action.contentType,
|
|
207
|
-
service: action.service
|
|
208
|
-
bodyRef: action.body,
|
|
209
|
-
responseRef: action.response
|
|
205
|
+
service: action.service
|
|
210
206
|
});
|
|
211
207
|
if (registry.permissions) for (const p of registry.permissions) this.permissions.add(p);
|
|
212
208
|
}
|
|
@@ -340,16 +336,6 @@ var LinkProvider = class LinkProvider {
|
|
|
340
336
|
$.can = () => {
|
|
341
337
|
return this.can(name);
|
|
342
338
|
};
|
|
343
|
-
$.schema = () => {
|
|
344
|
-
const link = this.links.find((l) => l.name === name);
|
|
345
|
-
if (!link) throw new AlephaError(`Link ${name} not found.`);
|
|
346
|
-
if (link.schema) return link.schema;
|
|
347
|
-
const resolved = {};
|
|
348
|
-
if (link.bodyRef && this.definitions[link.bodyRef]) resolved.body = jsonSchemaToTypeBox(JSON.parse(this.definitions[link.bodyRef]));
|
|
349
|
-
if (link.responseRef && this.definitions[link.responseRef]) resolved.response = jsonSchemaToTypeBox(JSON.parse(this.definitions[link.responseRef]));
|
|
350
|
-
link.schema = resolved;
|
|
351
|
-
return resolved;
|
|
352
|
-
};
|
|
353
339
|
return $;
|
|
354
340
|
}
|
|
355
341
|
async followRemote(link, config = {}, options = {}) {
|
|
@@ -426,62 +412,15 @@ var RemotePrimitive = class extends Primitive {
|
|
|
426
412
|
};
|
|
427
413
|
$remote[KIND] = RemotePrimitive;
|
|
428
414
|
|
|
429
|
-
//#endregion
|
|
430
|
-
//#region ../../src/server/links/services/DefinitionsPool.ts
|
|
431
|
-
/**
|
|
432
|
-
* Builds a deduplicated pool of pre-stringified JSON Schema definitions.
|
|
433
|
-
*
|
|
434
|
-
* Each unique schema is stored once and assigned a `$N` reference key.
|
|
435
|
-
* Actions reference schemas via these keys instead of inlining the full JSON.
|
|
436
|
-
*
|
|
437
|
-
* Runs at startup (registration time), not on the hot path.
|
|
438
|
-
*/
|
|
439
|
-
var DefinitionsPool = class {
|
|
440
|
-
pool = /* @__PURE__ */ new Map();
|
|
441
|
-
counter = 0;
|
|
442
|
-
/**
|
|
443
|
-
* Register a schema and return its `$N` reference key.
|
|
444
|
-
* If the same schema was already registered, returns the existing key.
|
|
445
|
-
*/
|
|
446
|
-
ref(schema) {
|
|
447
|
-
if (!schema) return void 0;
|
|
448
|
-
const json = JSON.stringify(schema);
|
|
449
|
-
const existing = this.pool.get(json);
|
|
450
|
-
if (existing) return existing;
|
|
451
|
-
const key = `$${this.counter++}`;
|
|
452
|
-
this.pool.set(json, key);
|
|
453
|
-
return key;
|
|
454
|
-
}
|
|
455
|
-
/**
|
|
456
|
-
* Returns the definitions as `{ "$0": "json string", "$1": "..." }`.
|
|
457
|
-
* Keys are `$N` refs, values are pre-stringified JSON Schema.
|
|
458
|
-
*/
|
|
459
|
-
toJSON() {
|
|
460
|
-
const result = {};
|
|
461
|
-
for (const [json, key] of this.pool) result[key] = json;
|
|
462
|
-
return result;
|
|
463
|
-
}
|
|
464
|
-
/**
|
|
465
|
-
* Number of unique definitions in the pool.
|
|
466
|
-
*/
|
|
467
|
-
get size() {
|
|
468
|
-
return this.pool.size;
|
|
469
|
-
}
|
|
470
|
-
};
|
|
471
|
-
|
|
472
415
|
//#endregion
|
|
473
416
|
//#region ../../src/server/links/index.browser.ts
|
|
474
417
|
const AlephaServerLinks = $module({
|
|
475
418
|
name: "alepha.server.links",
|
|
476
419
|
atoms: [apiLinksAtom, linkOptionsAtom],
|
|
477
420
|
primitives: [$remote, $client],
|
|
478
|
-
services: [
|
|
479
|
-
LinkProvider,
|
|
480
|
-
BatchCollector,
|
|
481
|
-
DefinitionsPool
|
|
482
|
-
]
|
|
421
|
+
services: [LinkProvider, BatchCollector]
|
|
483
422
|
});
|
|
484
423
|
|
|
485
424
|
//#endregion
|
|
486
|
-
export { $client, $remote, AlephaServerLinks, BatchCollector,
|
|
425
|
+
export { $client, $remote, AlephaServerLinks, BatchCollector, LinkProvider, RemotePrimitive, apiActionSchema, apiLinksAtom, apiLinksResponseSchema, apiRegistryResponseSchema, linkOptionsAtom };
|
|
487
426
|
//# sourceMappingURL=index.browser.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.browser.js","names":[],"sources":["../../../src/server/links/schemas/apiLinksResponseSchema.ts","../../../src/server/links/atoms/apiLinksAtom.ts","../../../src/server/links/atoms/linkOptionsAtom.ts","../../../src/server/links/services/BatchCollector.ts","../../../src/server/links/providers/LinkProvider.ts","../../../src/server/links/primitives/$client.ts","../../../src/server/links/primitives/$remote.ts","../../../src/server/links/services/DefinitionsPool.ts","../../../src/server/links/index.browser.ts"],"sourcesContent":["import type { Static } from \"alepha\";\nimport { t } from \"alepha\";\n\nexport const apiActionSchema = t.object({\n path: t.text({\n description: \"Pathname used to access the action.\",\n }),\n\n method: t.optional(\n t.text({\n description:\n \"HTTP method. Omitted when GET (the default for ~75% of actions).\",\n }),\n ),\n\n body: t.optional(\n t.text({\n description:\n \"Reference to a definitions pool entry (e.g. '$0') for the request body JSON Schema.\",\n }),\n ),\n\n response: t.optional(\n t.text({\n description:\n \"Reference to a definitions pool entry (e.g. '$0') for the response JSON Schema.\",\n }),\n ),\n\n contentType: t.optional(\n t.text({\n description:\n \"Content type for the request body. Only present for non-JSON types (e.g. 'multipart/form-data'). When absent, defaults to application/json.\",\n }),\n ),\n\n service: t.optional(\n t.text({\n description:\n \"Service name associated with the action, used for service discovery and routing.\",\n }),\n ),\n});\n\nexport const apiRegistryResponseSchema = t.object({\n prefix: t.optional(t.text()),\n\n definitions: t.optional(\n t.record(\n t.text(),\n t.string({\n description:\n \"Pre-stringified JSON Schema. Values can be 2KB+, so t.string() (no maxLength) is used instead of t.text().\",\n }),\n ),\n ),\n\n actions: t.record(t.text(), apiActionSchema),\n\n permissions: t.optional(t.array(t.text())),\n});\n\nexport type ApiRegistryResponse = Static<typeof apiRegistryResponseSchema>;\nexport type ApiAction = Static<typeof apiActionSchema>;\n\n/**\n * @deprecated Use `apiRegistryResponseSchema` and `ApiRegistryResponse` instead.\n */\nexport const apiLinksResponseSchema = apiRegistryResponseSchema;\n\n/**\n * @deprecated Use `ApiRegistryResponse` instead.\n */\nexport type ApiLinksResponse = ApiRegistryResponse;\n\n/**\n * @deprecated Use `ApiAction` instead.\n */\nexport type ApiLink = ApiAction;\n","import { $atom, t } from \"alepha\";\nimport { apiRegistryResponseSchema } from \"../schemas/apiLinksResponseSchema.ts\";\n\nexport const apiLinksAtom = $atom({\n name: \"alepha.server.request.apiLinks\",\n schema: t.optional(apiRegistryResponseSchema),\n});\n","import { $atom, t } from \"alepha\";\n\nexport const linkOptionsAtom = $atom({\n name: \"alepha.server.links.options\",\n description: \"Configuration options for the links module.\",\n schema: t.object({\n batch: t.boolean({\n description: \"Enable batch collection for browser-side calls.\",\n default: true,\n }),\n }),\n default: {\n batch: true,\n },\n});\n","import { $inject } from \"alepha\";\nimport { HttpClient, HttpError } from \"alepha/server\";\n\n/**\n * Collects browser-side action calls within a microtask and\n * sends them as a single `POST /api/_batch` request.\n *\n * Key behaviors:\n * - Single call in the window → direct HTTP call (no batch overhead)\n * - Multiple calls → coalesced into one batch request\n * - Same action + same params/query/body → deduplicated, result shared\n * - Exceeding MAX_BATCH_SIZE → split into multiple batch calls\n * - Transport failure → all pending promises reject\n */\nexport class BatchCollector {\n protected static readonly MAX_BATCH_SIZE = 20;\n\n protected readonly httpClient = $inject(HttpClient);\n\n protected pending: PendingBatchEntry[] = [];\n protected scheduled = false;\n\n /**\n * Add an action call to the batch. Returns the result when the batch resolves.\n */\n public add(entry: BatchEntry): Promise<any> {\n return new Promise((resolve, reject) => {\n this.pending.push({ entry, resolve, reject });\n\n if (!this.scheduled) {\n this.scheduled = true;\n queueMicrotask(() => {\n this.scheduled = false;\n this.flush();\n });\n }\n });\n }\n\n protected async flush(): Promise<void> {\n const batch = this.pending.splice(0);\n\n if (batch.length === 0) return;\n\n // Single request — skip batching, call directly via follow\n if (batch.length === 1) {\n const item = batch[0];\n try {\n const result = await item.entry.directCall();\n item.resolve(result);\n } catch (error) {\n item.reject(error);\n }\n return;\n }\n\n // Deduplicate: same action + same params → share result\n const { unique, indexMap } = this.dedupe(batch);\n\n // Split into chunks of MAX_BATCH_SIZE\n const chunks = this.chunk(unique, BatchCollector.MAX_BATCH_SIZE);\n\n try {\n const allResults = (\n await Promise.all(\n chunks.map((chunk) => {\n const actions = [...new Set(chunk.map((b) => b.entry.action))].join(\n \",\",\n );\n\n return this.httpClient\n .fetch(`/api/_batch?actions=${actions}`, {\n method: \"POST\",\n headers: { \"content-type\": \"application/json\" },\n body: JSON.stringify(\n chunk.map((b) => ({\n action: b.entry.action,\n params: b.entry.params,\n query: b.entry.query,\n body: b.entry.body,\n })),\n ),\n })\n .then((res) => res.data as BatchResponse[]);\n }),\n )\n ).flat();\n\n // Distribute results back (including deduped slots)\n for (let i = 0; i < batch.length; i++) {\n const result = allResults[indexMap[i]];\n if (result.status >= 400) {\n batch[i].reject(\n new HttpError({\n message:\n result.error ?? `${result.action} failed (${result.status})`,\n status: result.status,\n }),\n );\n } else {\n batch[i].resolve(result.data);\n }\n }\n } catch (error) {\n // Transport-level failure — reject all pending promises\n for (const item of batch) {\n item.reject(error);\n }\n }\n }\n\n protected dedupe(batch: PendingBatchEntry[]): {\n unique: PendingBatchEntry[];\n indexMap: number[];\n } {\n const seen = new Map<string, number>();\n const unique: PendingBatchEntry[] = [];\n const indexMap: number[] = [];\n\n for (const item of batch) {\n const key = `${item.entry.action}:${JSON.stringify({\n params: item.entry.params,\n query: item.entry.query,\n body: item.entry.body,\n })}`;\n\n const existing = seen.get(key);\n if (existing !== undefined) {\n indexMap.push(existing);\n } else {\n const idx = unique.length;\n seen.set(key, idx);\n unique.push(item);\n indexMap.push(idx);\n }\n }\n\n return { unique, indexMap };\n }\n\n protected chunk<T>(arr: T[], size: number): T[][] {\n const chunks: T[][] = [];\n for (let i = 0; i < arr.length; i += size) {\n chunks.push(arr.slice(i, i + size));\n }\n return chunks;\n }\n}\n\n// ---\n\nexport interface BatchEntry {\n action: string;\n params?: Record<string, any>;\n query?: Record<string, any>;\n body?: Record<string, any>;\n directCall: () => Promise<any>;\n}\n\ninterface PendingBatchEntry {\n entry: BatchEntry;\n resolve: (value: any) => void;\n reject: (reason: any) => void;\n}\n\ninterface BatchResponse {\n action: string;\n status: number;\n data?: any;\n error?: string;\n}\n","import {\n $inject,\n $use,\n Alepha,\n AlephaError,\n type Async,\n jsonSchemaToTypeBox,\n t,\n} from \"alepha\";\nimport { $logger } from \"alepha/logger\";\nimport type { SecureOptions } from \"alepha/security\";\nimport {\n type ActionPrimitive,\n type ClientRequestEntry,\n type ClientRequestOptions,\n type ClientRequestResponse,\n type FetchResponse,\n HttpClient,\n type RequestConfigSchema,\n ServerReply,\n type ServerRequest,\n type ServerRequestConfigEntry,\n type ServerResponseBody,\n type TRequestBody,\n UnauthorizedError,\n} from \"alepha/server\";\nimport { linkOptionsAtom } from \"../atoms/linkOptionsAtom.ts\";\nimport {\n type ApiRegistryResponse,\n apiRegistryResponseSchema,\n} from \"../schemas/apiLinksResponseSchema.ts\";\nimport { BatchCollector } from \"../services/BatchCollector.ts\";\n\n/**\n * Browser, SSR friendly, service to handle links.\n */\nexport class LinkProvider {\n static path = {\n apiLinks: \"/api/_links\",\n };\n\n protected readonly log = $logger();\n protected readonly alepha = $inject(Alepha);\n protected readonly httpClient = $inject(HttpClient);\n\n // Server-side: all registered links (local + remote), keyed by name\n protected serverLinkMap = new Map<string, HttpClientLink>();\n\n // Browser/SSR: parsed from the registry response\n protected actionMap = new Map<string, HttpClientLink>();\n protected permissions = new Set<string>();\n protected definitions: Record<string, string> = {};\n protected lastLoadedRegistry: ApiRegistryResponse | null = null;\n\n // Browser-only: batch collector for coalescing multiple calls\n protected batchCollector?: BatchCollector;\n\n protected readonly options = $use(linkOptionsAtom);\n\n /**\n * Get applicative links registered on the server.\n * This does not include lazy-loaded remote links.\n */\n public getServerLinks(): HttpClientLink[] {\n if (this.alepha.isBrowser()) {\n this.log.warn(\n \"Getting server links in the browser is not supported. Use `fetchLinks` to get links from the server.\",\n );\n return [];\n }\n\n return [...this.serverLinkMap.values()];\n }\n\n /**\n * Register a new link for the application.\n */\n public registerLink(link: HttpClientLink): void {\n if (this.alepha.isBrowser()) {\n this.log.warn(\n \"Registering links in the browser is not supported. Use `fetchLinks` to get links from the server.\",\n );\n return;\n }\n\n if (!link.handler && !link.host) {\n throw new AlephaError(\n \"Can't create link - 'handler' or 'host' is required\",\n );\n }\n\n // Detect duplicate local actions (programming error)\n const existing = this.serverLinkMap.get(link.name);\n if (existing?.handler && link.handler) {\n throw new AlephaError(\n `Duplicate action name \"${link.name}\". Each action must have a unique name.`,\n );\n }\n\n this.serverLinkMap.set(link.name, link);\n }\n\n /**\n * Load the registry response into internal stores (actionMap, permissions, definitions).\n * Called when storing from atom/fetch/SSR.\n */\n protected loadRegistry(registry: ApiRegistryResponse): void {\n this.lastLoadedRegistry = registry;\n this.definitions = registry.definitions ?? {};\n this.permissions.clear();\n this.actionMap.clear();\n\n for (const [name, action] of Object.entries(registry.actions)) {\n this.actionMap.set(name, {\n name,\n path: action.path,\n method: action.method,\n contentType: action.contentType,\n service: action.service,\n // Store definition refs for lazy schema resolution\n bodyRef: action.body,\n responseRef: action.response,\n });\n }\n\n if (registry.permissions) {\n for (const p of registry.permissions) {\n this.permissions.add(p);\n }\n }\n }\n\n public get links(): HttpClientLink[] {\n const registry = this.alepha.store.get(\"alepha.server.request.apiLinks\");\n\n if (registry) {\n if (this.alepha.isBrowser()) {\n // Browser side: use the parsed action map\n // Reload when registry changes (e.g. after login provides new authenticated links)\n if (this.actionMap.size === 0 || registry !== this.lastLoadedRegistry) {\n this.loadRegistry(registry);\n }\n return [...this.actionMap.values()];\n }\n\n // SSR side: map registry actions back to full server links\n const links: HttpClientLink[] = [];\n for (const name of Object.keys(registry.actions)) {\n const originalLink = this.serverLinkMap.get(name);\n if (originalLink) {\n links.push(originalLink);\n }\n }\n return links;\n }\n\n return [...this.serverLinkMap.values()];\n }\n\n /**\n * Force browser to refresh links from the server.\n */\n public async fetchLinks(): Promise<HttpClientLink[]> {\n const { data } = await this.httpClient.fetch(\n `${LinkProvider.path.apiLinks}`,\n {\n method: \"GET\",\n schema: {\n response: apiRegistryResponseSchema,\n },\n },\n );\n\n this.alepha.store.set(\"alepha.server.request.apiLinks\", data);\n this.loadRegistry(data);\n\n return [...this.actionMap.values()];\n }\n\n /**\n * Create a virtual client that can be used to call actions.\n *\n * Use js Proxy under the hood.\n */\n public client<T extends object>(\n scope: ClientScope = {},\n ): HttpVirtualClient<T> {\n return new Proxy<HttpVirtualClient<T>>({} as HttpVirtualClient<T>, {\n get: (_, prop) => {\n if (typeof prop !== \"string\") {\n return;\n }\n\n return this.createVirtualAction<RequestConfigSchema>(prop, scope);\n },\n });\n }\n\n /**\n * Check if a link with the given name exists or a permission matches.\n *\n * Action names never contain colons. Permission names always do.\n * - `can(\"getUsers\")` → O(1) map lookup\n * - `can(\"admin:*\")` → wildcard match against permissions set\n * - `can(\"admin:user:read\")` → O(1) set lookup\n */\n public can(name: string): boolean {\n // Action check — O(1) map lookup\n if (this.actionMap.size > 0) {\n if (this.actionMap.has(name)) return true;\n } else {\n // Fallback for server-side where actionMap may not be populated\n if (this.serverLinkMap.has(name)) return true;\n // Also check links getter (for SSR with atom)\n if (this.links.some((link) => link.name === name)) return true;\n }\n\n // Permission check — wildcard matching\n if (name.includes(\":\")) {\n if (name.endsWith(\"*\")) {\n const prefix = name.slice(0, -1);\n for (const p of this.permissions) {\n if (p.startsWith(prefix)) return true;\n }\n return false;\n }\n return this.permissions.has(name);\n }\n\n return false;\n }\n\n /**\n * Resolve a link by its name and call it.\n * - If link is local, it will call the local handler.\n * - If link is remote, it will make a fetch request to the remote server.\n */\n public async follow(\n name: string,\n config: Partial<ServerRequestConfigEntry> = {},\n options: ClientRequestOptions & ClientScope = {},\n ): Promise<any> {\n this.log.trace(\"Following link\", { name, config, options });\n const link = await this.getLinkByName(name, options);\n\n // if a handler is defined, use it (ssr)\n if (link.handler && !options.request) {\n this.log.trace(\"Local link found\", { name });\n return link.handler(\n {\n method: link.method,\n url: new URL(`http://localhost${link.path}`),\n query: config.query ?? {},\n body: config.body ?? {},\n params: config.params ?? {},\n headers: config.headers ?? {},\n metadata: {},\n reply: new ServerReply(),\n } as Partial<ServerRequest> as ServerRequest,\n options,\n );\n }\n\n this.log.trace(\"Remote link found\", {\n name,\n host: link.host,\n service: link.service,\n });\n\n // Browser-only: use batch collector for calls without explicit host\n if (this.options.batch && this.alepha.isBrowser() && !link.host) {\n this.batchCollector ??= this.alepha.inject(BatchCollector);\n return this.batchCollector.add({\n action: name,\n params: config.params as any,\n query: config.query as any,\n body: config.body as any,\n directCall: () =>\n this.followRemote(link, config, options).then((r) => r.data),\n });\n }\n\n return this.followRemote(link, config, options).then(\n (response) => response.data,\n );\n }\n\n protected createVirtualAction<T extends RequestConfigSchema>(\n name: string,\n scope: ClientScope = {},\n ): VirtualAction<T> {\n const $: VirtualAction<T> = async (\n config: any = {},\n options: ClientRequestOptions = {},\n ) => {\n return this.follow(name, config, {\n ...scope,\n ...options,\n });\n };\n\n Object.defineProperty($, \"name\", {\n value: name,\n writable: false,\n });\n\n $.run = async (config: any = {}, options: ClientRequestOptions = {}) => {\n return this.follow(name, config, {\n ...scope,\n ...options,\n });\n };\n\n $.fetch = async (config: any = {}, options: ClientRequestOptions = {}) => {\n const link = await this.getLinkByName(name, scope);\n return this.followRemote(link, config, options);\n };\n\n $.can = () => {\n return this.can(name);\n };\n\n $.schema = () => {\n const link = this.links.find((l) => l.name === name);\n if (!link) {\n throw new AlephaError(`Link ${name} not found.`);\n }\n\n // If schema is already resolved (server-side), return it\n if (link.schema) {\n return link.schema as { body: any; response: any };\n }\n\n // Lazy resolve from definition refs (browser-side)\n const resolved: RequestConfigSchema = {};\n if (link.bodyRef && this.definitions[link.bodyRef]) {\n resolved.body = jsonSchemaToTypeBox(\n JSON.parse(this.definitions[link.bodyRef]),\n ) as TRequestBody;\n }\n if (link.responseRef && this.definitions[link.responseRef]) {\n resolved.response = jsonSchemaToTypeBox(\n JSON.parse(this.definitions[link.responseRef]),\n ) as TRequestBody;\n }\n\n // Cache for next access\n link.schema = resolved;\n\n return resolved as { body: any; response: any };\n };\n\n return $;\n }\n\n protected async followRemote(\n link: HttpClientLink,\n config: Partial<ServerRequestConfigEntry> = {},\n options: ClientRequestOptions = {},\n ): Promise<FetchResponse> {\n options.request ??= {};\n options.request.headers = new Headers(options.request.headers);\n\n const als = this.alepha.store.get(\"alepha.http.request\");\n if (als?.headers.authorization) {\n options.request.headers.set(\"authorization\", als.headers.authorization);\n }\n\n const context = this.alepha.context.get(\"context\");\n if (typeof context === \"string\") {\n options.request.headers.set(\"x-request-id\", context);\n }\n\n const action = {\n ...link,\n // schema is not used in the client,\n // we assume that TypeScript will check\n schema: {\n body: t.any(),\n response: t.any(),\n },\n };\n\n // prefix with service when host is not defined (e.g. browser)\n if (!link.host && link.service) {\n action.path = `/${link.service}${action.path}`;\n }\n\n action.path = `${action.prefix ?? \"/api\"}${action.path}`;\n action.prefix = undefined; // prefix is not used in the client\n\n // else, make a request\n return this.httpClient.fetchAction({\n host: link.host,\n config,\n options,\n action: action as any, // schema.body TAny is not accepted\n });\n }\n\n protected async getLinkByName(\n name: string,\n options: ClientScope = {},\n ): Promise<HttpClientLink> {\n if (\n this.alepha.isBrowser() &&\n !this.alepha.store.get(\"alepha.server.request.apiLinks\")\n ) {\n await this.fetchLinks();\n }\n\n const link = this.links.find(\n (a) =>\n a.name === name && (!options.service || options.service === a.service),\n );\n\n if (!link) {\n const error = new UnauthorizedError(`Action ${name} not found.`);\n // mimic http error handling\n await this.alepha.events.emit(\"client:onError\", {\n route: link,\n error,\n });\n throw error;\n }\n\n if (options.hostname) {\n return {\n ...link,\n host: options.hostname,\n };\n }\n\n return link;\n }\n}\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport interface HttpClientLink {\n name: string;\n path: string;\n method?: string;\n contentType?: string;\n service?: string;\n secured?: boolean | SecureOptions;\n prefix?: string;\n group?: string;\n // -- server only --\n host?: string;\n schema?: RequestConfigSchema;\n handler?: (\n request: ServerRequest,\n options: ClientRequestOptions,\n ) => Async<ServerResponseBody>;\n // -- browser only (definition refs for lazy schema resolution) --\n bodyRef?: string;\n responseRef?: string;\n}\n\nexport interface ClientScope {\n service?: string;\n hostname?: string;\n}\n\nexport type HttpVirtualClient<T> = {\n [K in keyof T as T[K] extends ActionPrimitive<RequestConfigSchema>\n ? K\n : never]: T[K] extends ActionPrimitive<infer Schema>\n ? VirtualAction<Schema>\n : never;\n};\n\nexport interface VirtualAction<T extends RequestConfigSchema>\n extends Pick<ActionPrimitive<T>, \"name\" | \"run\" | \"fetch\"> {\n (\n config?: ClientRequestEntry<T>,\n opts?: ClientRequestOptions,\n ): Promise<ClientRequestResponse<T>>;\n can: () => boolean;\n schema: () => {\n body: T[\"body\"];\n response: T[\"response\"];\n };\n}\n","import { $inject, KIND } from \"alepha\";\nimport {\n type ClientScope,\n type HttpVirtualClient,\n LinkProvider,\n} from \"../providers/LinkProvider.ts\";\n\n/**\n * Create a new client.\n */\nexport const $client = <T extends object>(\n scope?: ClientScope,\n): HttpVirtualClient<T> => {\n return $inject(LinkProvider).client<T>(scope);\n};\n\n$client[KIND] = \"$client\";\n","import { createPrimitive, KIND, Primitive } from \"alepha\";\nimport type { ServiceAccountPrimitive } from \"alepha/security\";\nimport type { ProxyPrimitiveOptions } from \"alepha/server/proxy\";\n\n/**\n * $remote is a primitive that allows you to define remote service access.\n *\n * Use it only when you have 2 or more services that need to communicate with each other.\n *\n * All remote services can be exposed as actions, ... or not.\n *\n * You can add a service account if you want to use a security layer.\n */\nexport const $remote = (options: RemotePrimitiveOptions) => {\n return createPrimitive(RemotePrimitive, options);\n};\n\nexport interface RemotePrimitiveOptions {\n /**\n * The URL of the remote service.\n * You can use a function to generate the URL dynamically.\n * You probably should use $env(env) to get the URL from the environment.\n *\n * @example\n * ```ts\n * import { $remote } from \"alepha/server\";\n * import { $inject, t } from \"alepha\";\n *\n * class App {\n * env = $env(t.object({\n * REMOTE_URL: t.text({default: \"http://localhost:3000\"}),\n * }));\n * remote = $remote({\n * url: this.env.REMOTE_URL,\n * });\n * }\n * ```\n */\n url: string | (() => string);\n\n /**\n * The name of the remote service.\n *\n * @default Member of the class containing the remote service.\n */\n name?: string;\n\n /**\n * If true, all methods of the remote service will be exposed as actions in this context.\n * > Note: Proxy will never use the service account, it just... proxies the request.\n */\n proxy?:\n | boolean\n | Partial<\n ProxyPrimitiveOptions & {\n /**\n * If true, the remote service won't be available internally, only through the proxy.\n */\n noInternal: boolean;\n }\n >;\n\n /**\n * For communication between the server and the remote service with a security layer.\n * This will be used for internal communication and will not be exposed to the client.\n */\n serviceAccount?: ServiceAccountPrimitive;\n}\n\nexport class RemotePrimitive extends Primitive<RemotePrimitiveOptions> {\n public get name(): string {\n return this.options.name ?? this.config.propertyKey;\n }\n}\n\n$remote[KIND] = RemotePrimitive;\n","/**\n * Builds a deduplicated pool of pre-stringified JSON Schema definitions.\n *\n * Each unique schema is stored once and assigned a `$N` reference key.\n * Actions reference schemas via these keys instead of inlining the full JSON.\n *\n * Runs at startup (registration time), not on the hot path.\n */\nexport class DefinitionsPool {\n protected pool = new Map<string, string>();\n protected counter = 0;\n\n /**\n * Register a schema and return its `$N` reference key.\n * If the same schema was already registered, returns the existing key.\n */\n ref(schema: object | undefined): string | undefined {\n if (!schema) return undefined;\n const json = JSON.stringify(schema);\n const existing = this.pool.get(json);\n if (existing) return existing;\n const key = `$${this.counter++}`;\n this.pool.set(json, key);\n return key;\n }\n\n /**\n * Returns the definitions as `{ \"$0\": \"json string\", \"$1\": \"...\" }`.\n * Keys are `$N` refs, values are pre-stringified JSON Schema.\n */\n toJSON(): Record<string, string> {\n const result: Record<string, string> = {};\n for (const [json, key] of this.pool) result[key] = json;\n return result;\n }\n\n /**\n * Number of unique definitions in the pool.\n */\n get size(): number {\n return this.pool.size;\n }\n}\n","import { $module } from \"alepha\";\nimport { apiLinksAtom } from \"./atoms/apiLinksAtom.ts\";\nimport { linkOptionsAtom } from \"./atoms/linkOptionsAtom.ts\";\nimport { $client } from \"./primitives/$client.ts\";\nimport { $remote } from \"./primitives/$remote.ts\";\nimport { LinkProvider } from \"./providers/LinkProvider.ts\";\nimport { BatchCollector } from \"./services/BatchCollector.ts\";\nimport { DefinitionsPool } from \"./services/DefinitionsPool.ts\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport * from \"./atoms/apiLinksAtom.ts\";\nexport * from \"./atoms/linkOptionsAtom.ts\";\nexport * from \"./primitives/$client.ts\";\nexport * from \"./primitives/$remote.ts\";\nexport * from \"./providers/LinkProvider.ts\";\nexport * from \"./schemas/apiLinksResponseSchema.ts\";\nexport * from \"./services/BatchCollector.ts\";\nexport * from \"./services/DefinitionsPool.ts\";\n\n// ---------------------------------------------------------------- -----------------------------------------------------\n\nexport const AlephaServerLinks = $module({\n name: \"alepha.server.links\",\n atoms: [apiLinksAtom, linkOptionsAtom],\n primitives: [$remote, $client],\n services: [LinkProvider, BatchCollector, DefinitionsPool],\n});\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.browser.js","names":[],"sources":["../../../src/server/links/schemas/apiLinksResponseSchema.ts","../../../src/server/links/atoms/apiLinksAtom.ts","../../../src/server/links/atoms/linkOptionsAtom.ts","../../../src/server/links/services/BatchCollector.ts","../../../src/server/links/providers/LinkProvider.ts","../../../src/server/links/primitives/$client.ts","../../../src/server/links/primitives/$remote.ts","../../../src/server/links/index.browser.ts"],"sourcesContent":["import type { Static } from \"alepha\";\nimport { t } from \"alepha\";\n\nexport const apiActionSchema = t.object({\n path: t.text({\n description: \"Pathname used to access the action.\",\n }),\n\n method: t.optional(\n t.text({\n description:\n \"HTTP method. Omitted when GET (the default for ~75% of actions).\",\n }),\n ),\n\n contentType: t.optional(\n t.text({\n description:\n \"Content type for the request body. Only present for non-JSON types (e.g. 'multipart/form-data'). When absent, defaults to application/json.\",\n }),\n ),\n\n kind: t.optional(\n t.text({\n description:\n \"Action kind. Used to distinguish special action types (e.g. 'sse' for Server-Sent Events streams).\",\n }),\n ),\n\n service: t.optional(\n t.text({\n description:\n \"Service name associated with the action, used for service discovery and routing.\",\n }),\n ),\n});\n\nexport const apiRegistryResponseSchema = t.object({\n prefix: t.optional(t.text()),\n\n actions: t.record(t.text(), apiActionSchema),\n\n permissions: t.optional(t.array(t.text())),\n});\n\nexport type ApiRegistryResponse = Static<typeof apiRegistryResponseSchema>;\nexport type ApiAction = Static<typeof apiActionSchema>;\n\n/**\n * @deprecated Use `apiRegistryResponseSchema` and `ApiRegistryResponse` instead.\n */\nexport const apiLinksResponseSchema = apiRegistryResponseSchema;\n\n/**\n * @deprecated Use `ApiRegistryResponse` instead.\n */\nexport type ApiLinksResponse = ApiRegistryResponse;\n\n/**\n * @deprecated Use `ApiAction` instead.\n */\nexport type ApiLink = ApiAction;\n","import { $atom, t } from \"alepha\";\nimport { apiRegistryResponseSchema } from \"../schemas/apiLinksResponseSchema.ts\";\n\nexport const apiLinksAtom = $atom({\n name: \"alepha.server.request.apiLinks\",\n schema: t.optional(apiRegistryResponseSchema),\n});\n","import { $atom, t } from \"alepha\";\n\nexport const linkOptionsAtom = $atom({\n name: \"alepha.server.links.options\",\n description: \"Configuration options for the links module.\",\n schema: t.object({\n batch: t.boolean({\n description: \"Enable batch collection for browser-side calls.\",\n default: true,\n }),\n }),\n default: {\n batch: true,\n },\n});\n","import { $inject } from \"alepha\";\nimport { $logger } from \"alepha/logger\";\nimport { HttpClient, HttpError } from \"alepha/server\";\n\n/**\n * Collects browser-side action calls within a microtask and\n * sends them as a single `POST /api/_batch` request.\n *\n * Key behaviors:\n * - Single call in the window → direct HTTP call (no batch overhead)\n * - Multiple calls → coalesced into one batch request\n * - Same action + same params/query/body → deduplicated, result shared\n * - Exceeding MAX_BATCH_SIZE → split into multiple batch calls\n * - Transport failure → all pending promises reject\n */\nexport class BatchCollector {\n protected static readonly MAX_BATCH_SIZE = 20;\n\n protected readonly log = $logger();\n protected readonly httpClient = $inject(HttpClient);\n\n protected pending: PendingBatchEntry[] = [];\n protected scheduled = false;\n\n /**\n * Add an action call to the batch. Returns the result when the batch resolves.\n */\n public add(entry: BatchEntry): Promise<any> {\n return new Promise((resolve, reject) => {\n this.pending.push({ entry, resolve, reject });\n\n if (!this.scheduled) {\n this.scheduled = true;\n setTimeout(() => {\n this.scheduled = false;\n this.flush().catch((err) => this.log.error(err));\n }, 10);\n }\n });\n }\n\n protected async flush(): Promise<void> {\n const batch = this.pending.splice(0);\n\n if (batch.length === 0) return;\n\n // Single request — skip batching, call directly via follow\n if (batch.length === 1) {\n const item = batch[0];\n try {\n const result = await item.entry.directCall();\n item.resolve(result);\n } catch (error) {\n item.reject(error);\n }\n return;\n }\n\n // Deduplicate: same action + same params → share result\n const { unique, indexMap } = this.dedupe(batch);\n\n // Split into chunks of MAX_BATCH_SIZE\n const chunks = this.chunk(unique, BatchCollector.MAX_BATCH_SIZE);\n\n try {\n const allResults = (\n await Promise.all(\n chunks.map((chunk) => {\n const actions = [...new Set(chunk.map((b) => b.entry.action))].join(\n \",\",\n );\n\n return this.httpClient\n .fetch(`/api/_batch?actions=${actions}`, {\n method: \"POST\",\n headers: { \"content-type\": \"application/json\" },\n body: JSON.stringify(\n chunk.map((b) => ({\n action: b.entry.action,\n params: b.entry.params,\n query: b.entry.query,\n body: b.entry.body,\n })),\n ),\n })\n .then((res) => res.data as BatchResponse[]);\n }),\n )\n ).flat();\n\n // Distribute results back (including deduped slots)\n for (let i = 0; i < batch.length; i++) {\n const result = allResults[indexMap[i]];\n if (result.status >= 400) {\n batch[i].reject(\n new HttpError({\n message:\n result.error ?? `${result.action} failed (${result.status})`,\n status: result.status,\n }),\n );\n } else {\n batch[i].resolve(result.data);\n }\n }\n } catch (error) {\n // Transport-level failure — reject all pending promises\n for (const item of batch) {\n item.reject(error);\n }\n }\n }\n\n protected dedupe(batch: PendingBatchEntry[]): {\n unique: PendingBatchEntry[];\n indexMap: number[];\n } {\n const seen = new Map<string, number>();\n const unique: PendingBatchEntry[] = [];\n const indexMap: number[] = [];\n\n for (const item of batch) {\n const key = `${item.entry.action}:${JSON.stringify({\n params: item.entry.params,\n query: item.entry.query,\n body: item.entry.body,\n })}`;\n\n const existing = seen.get(key);\n if (existing !== undefined) {\n indexMap.push(existing);\n } else {\n const idx = unique.length;\n seen.set(key, idx);\n unique.push(item);\n indexMap.push(idx);\n }\n }\n\n return { unique, indexMap };\n }\n\n protected chunk<T>(arr: T[], size: number): T[][] {\n const chunks: T[][] = [];\n for (let i = 0; i < arr.length; i += size) {\n chunks.push(arr.slice(i, i + size));\n }\n return chunks;\n }\n}\n\n// ---\n\nexport interface BatchEntry {\n action: string;\n params?: Record<string, any>;\n query?: Record<string, any>;\n body?: Record<string, any>;\n directCall: () => Promise<any>;\n}\n\ninterface PendingBatchEntry {\n entry: BatchEntry;\n resolve: (value: any) => void;\n reject: (reason: any) => void;\n}\n\ninterface BatchResponse {\n action: string;\n status: number;\n data?: any;\n error?: string;\n}\n","import { $inject, $use, Alepha, AlephaError, type Async, t } from \"alepha\";\nimport { $logger } from \"alepha/logger\";\nimport type { SecureOptions } from \"alepha/security\";\nimport {\n type ActionPrimitive,\n type ClientRequestEntry,\n type ClientRequestOptions,\n type ClientRequestResponse,\n type FetchResponse,\n HttpClient,\n type RequestConfigSchema,\n ServerReply,\n type ServerRequest,\n type ServerRequestConfigEntry,\n type ServerResponseBody,\n type SseConfigSchema,\n type SseEventData,\n type SsePrimitive,\n type SseRequestEntry,\n type SseStream,\n UnauthorizedError,\n} from \"alepha/server\";\nimport { linkOptionsAtom } from \"../atoms/linkOptionsAtom.ts\";\nimport {\n type ApiRegistryResponse,\n apiRegistryResponseSchema,\n} from \"../schemas/apiLinksResponseSchema.ts\";\nimport { BatchCollector } from \"../services/BatchCollector.ts\";\n\n/**\n * Browser, SSR friendly, service to handle links.\n */\nexport class LinkProvider {\n static path = {\n apiLinks: \"/api/_links\",\n };\n\n protected readonly log = $logger();\n protected readonly alepha = $inject(Alepha);\n protected readonly httpClient = $inject(HttpClient);\n\n // Server-side: all registered links (local + remote), keyed by name\n protected serverLinkMap = new Map<string, HttpClientLink>();\n\n // Browser/SSR: parsed from the registry response\n protected actionMap = new Map<string, HttpClientLink>();\n protected permissions = new Set<string>();\n protected lastLoadedRegistry: ApiRegistryResponse | null = null;\n\n // Browser-only: batch collector for coalescing multiple calls\n protected batchCollector?: BatchCollector;\n\n protected readonly options = $use(linkOptionsAtom);\n\n /**\n * Get applicative links registered on the server.\n * This does not include lazy-loaded remote links.\n */\n public getServerLinks(): HttpClientLink[] {\n if (this.alepha.isBrowser()) {\n this.log.warn(\n \"Getting server links in the browser is not supported. Use `fetchLinks` to get links from the server.\",\n );\n return [];\n }\n\n return [...this.serverLinkMap.values()];\n }\n\n /**\n * Register a new link for the application.\n */\n public registerLink(link: HttpClientLink): void {\n if (this.alepha.isBrowser()) {\n this.log.warn(\n \"Registering links in the browser is not supported. Use `fetchLinks` to get links from the server.\",\n );\n return;\n }\n\n if (!link.handler && !link.host) {\n throw new AlephaError(\n \"Can't create link - 'handler' or 'host' is required\",\n );\n }\n\n // Detect duplicate local actions (programming error)\n const existing = this.serverLinkMap.get(link.name);\n if (existing?.handler && link.handler) {\n throw new AlephaError(\n `Duplicate action name \"${link.name}\". Each action must have a unique name.`,\n );\n }\n\n this.serverLinkMap.set(link.name, link);\n }\n\n /**\n * Load the registry response into internal stores (actionMap, permissions, definitions).\n * Called when storing from atom/fetch/SSR.\n */\n protected loadRegistry(registry: ApiRegistryResponse): void {\n this.lastLoadedRegistry = registry;\n this.permissions.clear();\n this.actionMap.clear();\n\n for (const [name, action] of Object.entries(registry.actions)) {\n this.actionMap.set(name, {\n name,\n path: action.path,\n kind: action.kind,\n method: action.method,\n contentType: action.contentType,\n service: action.service,\n });\n }\n\n if (registry.permissions) {\n for (const p of registry.permissions) {\n this.permissions.add(p);\n }\n }\n }\n\n public get links(): HttpClientLink[] {\n const registry = this.alepha.store.get(\"alepha.server.request.apiLinks\");\n\n if (registry) {\n if (this.alepha.isBrowser()) {\n // Browser side: use the parsed action map\n // Reload when registry changes (e.g. after login provides new authenticated links)\n if (this.actionMap.size === 0 || registry !== this.lastLoadedRegistry) {\n this.loadRegistry(registry);\n }\n return [...this.actionMap.values()];\n }\n\n // SSR side: map registry actions back to full server links\n const links: HttpClientLink[] = [];\n for (const name of Object.keys(registry.actions)) {\n const originalLink = this.serverLinkMap.get(name);\n if (originalLink) {\n links.push(originalLink);\n }\n }\n return links;\n }\n\n return [...this.serverLinkMap.values()];\n }\n\n /**\n * Force browser to refresh links from the server.\n */\n public async fetchLinks(): Promise<HttpClientLink[]> {\n const { data } = await this.httpClient.fetch(\n `${LinkProvider.path.apiLinks}`,\n {\n method: \"GET\",\n schema: {\n response: apiRegistryResponseSchema,\n },\n },\n );\n\n this.alepha.store.set(\"alepha.server.request.apiLinks\", data);\n this.loadRegistry(data);\n\n return [...this.actionMap.values()];\n }\n\n /**\n * Create a virtual client that can be used to call actions.\n *\n * Use js Proxy under the hood.\n */\n public client<T extends object>(\n scope: ClientScope = {},\n ): HttpVirtualClient<T> {\n return new Proxy<HttpVirtualClient<T>>({} as HttpVirtualClient<T>, {\n get: (_, prop) => {\n if (typeof prop !== \"string\") {\n return;\n }\n\n return this.createVirtualAction<RequestConfigSchema>(prop, scope);\n },\n });\n }\n\n /**\n * Check if a link with the given name exists or a permission matches.\n *\n * Action names never contain colons. Permission names always do.\n * - `can(\"getUsers\")` → O(1) map lookup\n * - `can(\"admin:*\")` → wildcard match against permissions set\n * - `can(\"admin:user:read\")` → O(1) set lookup\n */\n public can(name: string): boolean {\n // Action check — O(1) map lookup\n if (this.actionMap.size > 0) {\n if (this.actionMap.has(name)) return true;\n } else {\n // Fallback for server-side where actionMap may not be populated\n if (this.serverLinkMap.has(name)) return true;\n // Also check links getter (for SSR with atom)\n if (this.links.some((link) => link.name === name)) return true;\n }\n\n // Permission check — wildcard matching\n if (name.includes(\":\")) {\n if (name.endsWith(\"*\")) {\n const prefix = name.slice(0, -1);\n for (const p of this.permissions) {\n if (p.startsWith(prefix)) return true;\n }\n return false;\n }\n return this.permissions.has(name);\n }\n\n return false;\n }\n\n /**\n * Resolve a link by its name and call it.\n * - If link is local, it will call the local handler.\n * - If link is remote, it will make a fetch request to the remote server.\n */\n public async follow(\n name: string,\n config: Partial<ServerRequestConfigEntry> = {},\n options: ClientRequestOptions & ClientScope = {},\n ): Promise<any> {\n this.log.trace(\"Following link\", { name, config, options });\n const link = await this.getLinkByName(name, options);\n\n // if a handler is defined, use it (ssr)\n if (link.handler && !options.request) {\n this.log.trace(\"Local link found\", { name });\n return link.handler(\n {\n method: link.method,\n url: new URL(`http://localhost${link.path}`),\n query: config.query ?? {},\n body: config.body ?? {},\n params: config.params ?? {},\n headers: config.headers ?? {},\n metadata: {},\n reply: new ServerReply(),\n } as Partial<ServerRequest> as ServerRequest,\n options,\n );\n }\n\n this.log.trace(\"Remote link found\", {\n name,\n host: link.host,\n service: link.service,\n });\n\n // Browser-only: use batch collector for calls without explicit host\n if (this.options.batch && this.alepha.isBrowser() && !link.host) {\n this.batchCollector ??= this.alepha.inject(BatchCollector);\n return this.batchCollector.add({\n action: name,\n params: config.params as any,\n query: config.query as any,\n body: config.body as any,\n directCall: () =>\n this.followRemote(link, config, options).then((r) => r.data),\n });\n }\n\n return this.followRemote(link, config, options).then(\n (response) => response.data,\n );\n }\n\n protected createVirtualAction<T extends RequestConfigSchema>(\n name: string,\n scope: ClientScope = {},\n ): VirtualAction<T> {\n const $: VirtualAction<T> = async (\n config: any = {},\n options: ClientRequestOptions = {},\n ) => {\n return this.follow(name, config, {\n ...scope,\n ...options,\n });\n };\n\n Object.defineProperty($, \"name\", {\n value: name,\n writable: false,\n });\n\n $.run = async (config: any = {}, options: ClientRequestOptions = {}) => {\n return this.follow(name, config, {\n ...scope,\n ...options,\n });\n };\n\n $.fetch = async (config: any = {}, options: ClientRequestOptions = {}) => {\n const link = await this.getLinkByName(name, scope);\n return this.followRemote(link, config, options);\n };\n\n $.can = () => {\n return this.can(name);\n };\n\n return $;\n }\n\n protected async followRemote(\n link: HttpClientLink,\n config: Partial<ServerRequestConfigEntry> = {},\n options: ClientRequestOptions = {},\n ): Promise<FetchResponse> {\n options.request ??= {};\n options.request.headers = new Headers(options.request.headers);\n\n const als = this.alepha.store.get(\"alepha.http.request\");\n if (als?.headers.authorization) {\n options.request.headers.set(\"authorization\", als.headers.authorization);\n }\n\n const context = this.alepha.context.get(\"context\");\n if (typeof context === \"string\") {\n options.request.headers.set(\"x-request-id\", context);\n }\n\n const action = {\n ...link,\n // schema is not used in the client,\n // we assume that TypeScript will check\n schema: {\n body: t.any(),\n response: t.any(),\n },\n };\n\n // prefix with service when host is not defined (e.g. browser)\n if (!link.host && link.service) {\n action.path = `/${link.service}${action.path}`;\n }\n\n action.path = `${action.prefix ?? \"/api\"}${action.path}`;\n action.prefix = undefined; // prefix is not used in the client\n\n // else, make a request\n return this.httpClient.fetchAction({\n host: link.host,\n config,\n options,\n action: action as any, // schema.body TAny is not accepted\n });\n }\n\n protected async getLinkByName(\n name: string,\n options: ClientScope = {},\n ): Promise<HttpClientLink> {\n if (\n this.alepha.isBrowser() &&\n !this.alepha.store.get(\"alepha.server.request.apiLinks\")\n ) {\n await this.fetchLinks();\n }\n\n const link = this.links.find(\n (a) =>\n a.name === name && (!options.service || options.service === a.service),\n );\n\n if (!link) {\n const error = new UnauthorizedError(`Action ${name} not found.`);\n // mimic http error handling\n await this.alepha.events.emit(\"client:onError\", {\n route: link,\n error,\n });\n throw error;\n }\n\n if (options.hostname) {\n return {\n ...link,\n host: options.hostname,\n };\n }\n\n return link;\n }\n}\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport interface HttpClientLink {\n name: string;\n path: string;\n method?: string;\n kind?: string;\n contentType?: string;\n service?: string;\n secured?: boolean | SecureOptions;\n prefix?: string;\n group?: string;\n // -- server only --\n host?: string;\n schema?: RequestConfigSchema;\n handler?: (\n request: ServerRequest,\n options: ClientRequestOptions,\n ) => Async<ServerResponseBody>;\n}\n\nexport interface ClientScope {\n service?: string;\n hostname?: string;\n}\n\nexport type HttpVirtualClient<T> = {\n [K in keyof T as T[K] extends ActionPrimitive<RequestConfigSchema>\n ? K\n : never]: T[K] extends ActionPrimitive<infer Schema>\n ? VirtualAction<Schema>\n : never;\n} & {\n [K in keyof T as T[K] extends SsePrimitive<SseConfigSchema>\n ? K\n : never]: T[K] extends SsePrimitive<infer Schema>\n ? VirtualSse<Schema>\n : never;\n};\n\nexport interface VirtualAction<T extends RequestConfigSchema>\n extends Pick<ActionPrimitive<T>, \"name\" | \"run\" | \"fetch\"> {\n (\n config?: ClientRequestEntry<T>,\n opts?: ClientRequestOptions,\n ): Promise<ClientRequestResponse<T>>;\n can: () => boolean;\n}\n\nexport interface VirtualSse<T extends SseConfigSchema> {\n (config?: SseRequestEntry<T>): Promise<SseStream<SseEventData<T>>>;\n name: string;\n can: () => boolean;\n}\n","import { $inject, KIND } from \"alepha\";\nimport {\n type ClientScope,\n type HttpVirtualClient,\n LinkProvider,\n} from \"../providers/LinkProvider.ts\";\n\n/**\n * Create a new client.\n */\nexport const $client = <T extends object>(\n scope?: ClientScope,\n): HttpVirtualClient<T> => {\n return $inject(LinkProvider).client<T>(scope);\n};\n\n$client[KIND] = \"$client\";\n","import { createPrimitive, KIND, Primitive } from \"alepha\";\nimport type { ServiceAccountPrimitive } from \"alepha/security\";\nimport type { ProxyPrimitiveOptions } from \"alepha/server/proxy\";\n\n/**\n * $remote is a primitive that allows you to define remote service access.\n *\n * Use it only when you have 2 or more services that need to communicate with each other.\n *\n * All remote services can be exposed as actions, ... or not.\n *\n * You can add a service account if you want to use a security layer.\n */\nexport const $remote = (options: RemotePrimitiveOptions) => {\n return createPrimitive(RemotePrimitive, options);\n};\n\nexport interface RemotePrimitiveOptions {\n /**\n * The URL of the remote service.\n * You can use a function to generate the URL dynamically.\n * You probably should use $env(env) to get the URL from the environment.\n *\n * @example\n * ```ts\n * import { $remote } from \"alepha/server\";\n * import { $inject, t } from \"alepha\";\n *\n * class App {\n * env = $env(t.object({\n * REMOTE_URL: t.text({default: \"http://localhost:3000\"}),\n * }));\n * remote = $remote({\n * url: this.env.REMOTE_URL,\n * });\n * }\n * ```\n */\n url: string | (() => string);\n\n /**\n * The name of the remote service.\n *\n * @default Member of the class containing the remote service.\n */\n name?: string;\n\n /**\n * If true, all methods of the remote service will be exposed as actions in this context.\n * > Note: Proxy will never use the service account, it just... proxies the request.\n */\n proxy?:\n | boolean\n | Partial<\n ProxyPrimitiveOptions & {\n /**\n * If true, the remote service won't be available internally, only through the proxy.\n */\n noInternal: boolean;\n }\n >;\n\n /**\n * For communication between the server and the remote service with a security layer.\n * This will be used for internal communication and will not be exposed to the client.\n */\n serviceAccount?: ServiceAccountPrimitive;\n}\n\nexport class RemotePrimitive extends Primitive<RemotePrimitiveOptions> {\n public get name(): string {\n return this.options.name ?? this.config.propertyKey;\n }\n}\n\n$remote[KIND] = RemotePrimitive;\n","import { $module } from \"alepha\";\nimport { apiLinksAtom } from \"./atoms/apiLinksAtom.ts\";\nimport { linkOptionsAtom } from \"./atoms/linkOptionsAtom.ts\";\nimport { $client } from \"./primitives/$client.ts\";\nimport { $remote } from \"./primitives/$remote.ts\";\nimport { LinkProvider } from \"./providers/LinkProvider.ts\";\nimport { BatchCollector } from \"./services/BatchCollector.ts\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport * from \"./atoms/apiLinksAtom.ts\";\nexport * from \"./atoms/linkOptionsAtom.ts\";\nexport * from \"./primitives/$client.ts\";\nexport * from \"./primitives/$remote.ts\";\nexport * from \"./providers/LinkProvider.ts\";\nexport * from \"./schemas/apiLinksResponseSchema.ts\";\nexport * from \"./services/BatchCollector.ts\";\n\n// ---------------------------------------------------------------- -----------------------------------------------------\n\nexport const AlephaServerLinks = $module({\n name: \"alepha.server.links\",\n atoms: [apiLinksAtom, linkOptionsAtom],\n primitives: [$remote, $client],\n services: [LinkProvider, BatchCollector],\n});\n"],"mappings":";;;;;AAGA,MAAa,kBAAkB,EAAE,OAAO;CACtC,MAAM,EAAE,KAAK,EACX,aAAa,uCACd,CAAC;CAEF,QAAQ,EAAE,SACR,EAAE,KAAK,EACL,aACE,oEACH,CAAC,CACH;CAED,aAAa,EAAE,SACb,EAAE,KAAK,EACL,aACE,+IACH,CAAC,CACH;CAED,MAAM,EAAE,SACN,EAAE,KAAK,EACL,aACE,sGACH,CAAC,CACH;CAED,SAAS,EAAE,SACT,EAAE,KAAK,EACL,aACE,oFACH,CAAC,CACH;CACF,CAAC;AAEF,MAAa,4BAA4B,EAAE,OAAO;CAChD,QAAQ,EAAE,SAAS,EAAE,MAAM,CAAC;CAE5B,SAAS,EAAE,OAAO,EAAE,MAAM,EAAE,gBAAgB;CAE5C,aAAa,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;CAC3C,CAAC;;;;AAQF,MAAa,yBAAyB;;;;AChDtC,MAAa,eAAe,MAAM;CAChC,MAAM;CACN,QAAQ,EAAE,SAAS,0BAA0B;CAC9C,CAAC;;;;ACJF,MAAa,kBAAkB,MAAM;CACnC,MAAM;CACN,aAAa;CACb,QAAQ,EAAE,OAAO,EACf,OAAO,EAAE,QAAQ;EACf,aAAa;EACb,SAAS;EACV,CAAC,EACH,CAAC;CACF,SAAS,EACP,OAAO,MACR;CACF,CAAC;;;;;;;;;;;;;;;ACCF,IAAa,iBAAb,MAAa,eAAe;CAC1B,OAA0B,iBAAiB;CAE3C,AAAmB,MAAM,SAAS;CAClC,AAAmB,aAAa,QAAQ,WAAW;CAEnD,AAAU,UAA+B,EAAE;CAC3C,AAAU,YAAY;;;;CAKtB,AAAO,IAAI,OAAiC;AAC1C,SAAO,IAAI,SAAS,SAAS,WAAW;AACtC,QAAK,QAAQ,KAAK;IAAE;IAAO;IAAS;IAAQ,CAAC;AAE7C,OAAI,CAAC,KAAK,WAAW;AACnB,SAAK,YAAY;AACjB,qBAAiB;AACf,UAAK,YAAY;AACjB,UAAK,OAAO,CAAC,OAAO,QAAQ,KAAK,IAAI,MAAM,IAAI,CAAC;OAC/C,GAAG;;IAER;;CAGJ,MAAgB,QAAuB;EACrC,MAAM,QAAQ,KAAK,QAAQ,OAAO,EAAE;AAEpC,MAAI,MAAM,WAAW,EAAG;AAGxB,MAAI,MAAM,WAAW,GAAG;GACtB,MAAM,OAAO,MAAM;AACnB,OAAI;IACF,MAAM,SAAS,MAAM,KAAK,MAAM,YAAY;AAC5C,SAAK,QAAQ,OAAO;YACb,OAAO;AACd,SAAK,OAAO,MAAM;;AAEpB;;EAIF,MAAM,EAAE,QAAQ,aAAa,KAAK,OAAO,MAAM;EAG/C,MAAM,SAAS,KAAK,MAAM,QAAQ,eAAe,eAAe;AAEhE,MAAI;GACF,MAAM,cACJ,MAAM,QAAQ,IACZ,OAAO,KAAK,UAAU;IACpB,MAAM,UAAU,CAAC,GAAG,IAAI,IAAI,MAAM,KAAK,MAAM,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,KAC7D,IACD;AAED,WAAO,KAAK,WACT,MAAM,uBAAuB,WAAW;KACvC,QAAQ;KACR,SAAS,EAAE,gBAAgB,oBAAoB;KAC/C,MAAM,KAAK,UACT,MAAM,KAAK,OAAO;MAChB,QAAQ,EAAE,MAAM;MAChB,QAAQ,EAAE,MAAM;MAChB,OAAO,EAAE,MAAM;MACf,MAAM,EAAE,MAAM;MACf,EAAE,CACJ;KACF,CAAC,CACD,MAAM,QAAQ,IAAI,KAAwB;KAC7C,CACH,EACD,MAAM;AAGR,QAAK,IAAI,IAAI,GAAG,IAAI,MAAM,QAAQ,KAAK;IACrC,MAAM,SAAS,WAAW,SAAS;AACnC,QAAI,OAAO,UAAU,IACnB,OAAM,GAAG,OACP,IAAI,UAAU;KACZ,SACE,OAAO,SAAS,GAAG,OAAO,OAAO,WAAW,OAAO,OAAO;KAC5D,QAAQ,OAAO;KAChB,CAAC,CACH;QAED,OAAM,GAAG,QAAQ,OAAO,KAAK;;WAG1B,OAAO;AAEd,QAAK,MAAM,QAAQ,MACjB,MAAK,OAAO,MAAM;;;CAKxB,AAAU,OAAO,OAGf;EACA,MAAM,uBAAO,IAAI,KAAqB;EACtC,MAAM,SAA8B,EAAE;EACtC,MAAM,WAAqB,EAAE;AAE7B,OAAK,MAAM,QAAQ,OAAO;GACxB,MAAM,MAAM,GAAG,KAAK,MAAM,OAAO,GAAG,KAAK,UAAU;IACjD,QAAQ,KAAK,MAAM;IACnB,OAAO,KAAK,MAAM;IAClB,MAAM,KAAK,MAAM;IAClB,CAAC;GAEF,MAAM,WAAW,KAAK,IAAI,IAAI;AAC9B,OAAI,aAAa,OACf,UAAS,KAAK,SAAS;QAClB;IACL,MAAM,MAAM,OAAO;AACnB,SAAK,IAAI,KAAK,IAAI;AAClB,WAAO,KAAK,KAAK;AACjB,aAAS,KAAK,IAAI;;;AAItB,SAAO;GAAE;GAAQ;GAAU;;CAG7B,AAAU,MAAS,KAAU,MAAqB;EAChD,MAAM,SAAgB,EAAE;AACxB,OAAK,IAAI,IAAI,GAAG,IAAI,IAAI,QAAQ,KAAK,KACnC,QAAO,KAAK,IAAI,MAAM,GAAG,IAAI,KAAK,CAAC;AAErC,SAAO;;;;;;;;;ACnHX,IAAa,eAAb,MAAa,aAAa;CACxB,OAAO,OAAO,EACZ,UAAU,eACX;CAED,AAAmB,MAAM,SAAS;CAClC,AAAmB,SAAS,QAAQ,OAAO;CAC3C,AAAmB,aAAa,QAAQ,WAAW;CAGnD,AAAU,gCAAgB,IAAI,KAA6B;CAG3D,AAAU,4BAAY,IAAI,KAA6B;CACvD,AAAU,8BAAc,IAAI,KAAa;CACzC,AAAU,qBAAiD;CAG3D,AAAU;CAEV,AAAmB,UAAU,KAAK,gBAAgB;;;;;CAMlD,AAAO,iBAAmC;AACxC,MAAI,KAAK,OAAO,WAAW,EAAE;AAC3B,QAAK,IAAI,KACP,uGACD;AACD,UAAO,EAAE;;AAGX,SAAO,CAAC,GAAG,KAAK,cAAc,QAAQ,CAAC;;;;;CAMzC,AAAO,aAAa,MAA4B;AAC9C,MAAI,KAAK,OAAO,WAAW,EAAE;AAC3B,QAAK,IAAI,KACP,oGACD;AACD;;AAGF,MAAI,CAAC,KAAK,WAAW,CAAC,KAAK,KACzB,OAAM,IAAI,YACR,sDACD;AAKH,MADiB,KAAK,cAAc,IAAI,KAAK,KAAK,EACpC,WAAW,KAAK,QAC5B,OAAM,IAAI,YACR,0BAA0B,KAAK,KAAK,yCACrC;AAGH,OAAK,cAAc,IAAI,KAAK,MAAM,KAAK;;;;;;CAOzC,AAAU,aAAa,UAAqC;AAC1D,OAAK,qBAAqB;AAC1B,OAAK,YAAY,OAAO;AACxB,OAAK,UAAU,OAAO;AAEtB,OAAK,MAAM,CAAC,MAAM,WAAW,OAAO,QAAQ,SAAS,QAAQ,CAC3D,MAAK,UAAU,IAAI,MAAM;GACvB;GACA,MAAM,OAAO;GACb,MAAM,OAAO;GACb,QAAQ,OAAO;GACf,aAAa,OAAO;GACpB,SAAS,OAAO;GACjB,CAAC;AAGJ,MAAI,SAAS,YACX,MAAK,MAAM,KAAK,SAAS,YACvB,MAAK,YAAY,IAAI,EAAE;;CAK7B,IAAW,QAA0B;EACnC,MAAM,WAAW,KAAK,OAAO,MAAM,IAAI,iCAAiC;AAExE,MAAI,UAAU;AACZ,OAAI,KAAK,OAAO,WAAW,EAAE;AAG3B,QAAI,KAAK,UAAU,SAAS,KAAK,aAAa,KAAK,mBACjD,MAAK,aAAa,SAAS;AAE7B,WAAO,CAAC,GAAG,KAAK,UAAU,QAAQ,CAAC;;GAIrC,MAAM,QAA0B,EAAE;AAClC,QAAK,MAAM,QAAQ,OAAO,KAAK,SAAS,QAAQ,EAAE;IAChD,MAAM,eAAe,KAAK,cAAc,IAAI,KAAK;AACjD,QAAI,aACF,OAAM,KAAK,aAAa;;AAG5B,UAAO;;AAGT,SAAO,CAAC,GAAG,KAAK,cAAc,QAAQ,CAAC;;;;;CAMzC,MAAa,aAAwC;EACnD,MAAM,EAAE,SAAS,MAAM,KAAK,WAAW,MACrC,GAAG,aAAa,KAAK,YACrB;GACE,QAAQ;GACR,QAAQ,EACN,UAAU,2BACX;GACF,CACF;AAED,OAAK,OAAO,MAAM,IAAI,kCAAkC,KAAK;AAC7D,OAAK,aAAa,KAAK;AAEvB,SAAO,CAAC,GAAG,KAAK,UAAU,QAAQ,CAAC;;;;;;;CAQrC,AAAO,OACL,QAAqB,EAAE,EACD;AACtB,SAAO,IAAI,MAA4B,EAAE,EAA0B,EACjE,MAAM,GAAG,SAAS;AAChB,OAAI,OAAO,SAAS,SAClB;AAGF,UAAO,KAAK,oBAAyC,MAAM,MAAM;KAEpE,CAAC;;;;;;;;;;CAWJ,AAAO,IAAI,MAAuB;AAEhC,MAAI,KAAK,UAAU,OAAO,GACxB;OAAI,KAAK,UAAU,IAAI,KAAK,CAAE,QAAO;SAChC;AAEL,OAAI,KAAK,cAAc,IAAI,KAAK,CAAE,QAAO;AAEzC,OAAI,KAAK,MAAM,MAAM,SAAS,KAAK,SAAS,KAAK,CAAE,QAAO;;AAI5D,MAAI,KAAK,SAAS,IAAI,EAAE;AACtB,OAAI,KAAK,SAAS,IAAI,EAAE;IACtB,MAAM,SAAS,KAAK,MAAM,GAAG,GAAG;AAChC,SAAK,MAAM,KAAK,KAAK,YACnB,KAAI,EAAE,WAAW,OAAO,CAAE,QAAO;AAEnC,WAAO;;AAET,UAAO,KAAK,YAAY,IAAI,KAAK;;AAGnC,SAAO;;;;;;;CAQT,MAAa,OACX,MACA,SAA4C,EAAE,EAC9C,UAA8C,EAAE,EAClC;AACd,OAAK,IAAI,MAAM,kBAAkB;GAAE;GAAM;GAAQ;GAAS,CAAC;EAC3D,MAAM,OAAO,MAAM,KAAK,cAAc,MAAM,QAAQ;AAGpD,MAAI,KAAK,WAAW,CAAC,QAAQ,SAAS;AACpC,QAAK,IAAI,MAAM,oBAAoB,EAAE,MAAM,CAAC;AAC5C,UAAO,KAAK,QACV;IACE,QAAQ,KAAK;IACb,KAAK,IAAI,IAAI,mBAAmB,KAAK,OAAO;IAC5C,OAAO,OAAO,SAAS,EAAE;IACzB,MAAM,OAAO,QAAQ,EAAE;IACvB,QAAQ,OAAO,UAAU,EAAE;IAC3B,SAAS,OAAO,WAAW,EAAE;IAC7B,UAAU,EAAE;IACZ,OAAO,IAAI,aAAa;IACzB,EACD,QACD;;AAGH,OAAK,IAAI,MAAM,qBAAqB;GAClC;GACA,MAAM,KAAK;GACX,SAAS,KAAK;GACf,CAAC;AAGF,MAAI,KAAK,QAAQ,SAAS,KAAK,OAAO,WAAW,IAAI,CAAC,KAAK,MAAM;AAC/D,QAAK,mBAAmB,KAAK,OAAO,OAAO,eAAe;AAC1D,UAAO,KAAK,eAAe,IAAI;IAC7B,QAAQ;IACR,QAAQ,OAAO;IACf,OAAO,OAAO;IACd,MAAM,OAAO;IACb,kBACE,KAAK,aAAa,MAAM,QAAQ,QAAQ,CAAC,MAAM,MAAM,EAAE,KAAK;IAC/D,CAAC;;AAGJ,SAAO,KAAK,aAAa,MAAM,QAAQ,QAAQ,CAAC,MAC7C,aAAa,SAAS,KACxB;;CAGH,AAAU,oBACR,MACA,QAAqB,EAAE,EACL;EAClB,MAAM,IAAsB,OAC1B,SAAc,EAAE,EAChB,UAAgC,EAAE,KAC/B;AACH,UAAO,KAAK,OAAO,MAAM,QAAQ;IAC/B,GAAG;IACH,GAAG;IACJ,CAAC;;AAGJ,SAAO,eAAe,GAAG,QAAQ;GAC/B,OAAO;GACP,UAAU;GACX,CAAC;AAEF,IAAE,MAAM,OAAO,SAAc,EAAE,EAAE,UAAgC,EAAE,KAAK;AACtE,UAAO,KAAK,OAAO,MAAM,QAAQ;IAC/B,GAAG;IACH,GAAG;IACJ,CAAC;;AAGJ,IAAE,QAAQ,OAAO,SAAc,EAAE,EAAE,UAAgC,EAAE,KAAK;GACxE,MAAM,OAAO,MAAM,KAAK,cAAc,MAAM,MAAM;AAClD,UAAO,KAAK,aAAa,MAAM,QAAQ,QAAQ;;AAGjD,IAAE,YAAY;AACZ,UAAO,KAAK,IAAI,KAAK;;AAGvB,SAAO;;CAGT,MAAgB,aACd,MACA,SAA4C,EAAE,EAC9C,UAAgC,EAAE,EACV;AACxB,UAAQ,YAAY,EAAE;AACtB,UAAQ,QAAQ,UAAU,IAAI,QAAQ,QAAQ,QAAQ,QAAQ;EAE9D,MAAM,MAAM,KAAK,OAAO,MAAM,IAAI,sBAAsB;AACxD,MAAI,KAAK,QAAQ,cACf,SAAQ,QAAQ,QAAQ,IAAI,iBAAiB,IAAI,QAAQ,cAAc;EAGzE,MAAM,UAAU,KAAK,OAAO,QAAQ,IAAI,UAAU;AAClD,MAAI,OAAO,YAAY,SACrB,SAAQ,QAAQ,QAAQ,IAAI,gBAAgB,QAAQ;EAGtD,MAAM,SAAS;GACb,GAAG;GAGH,QAAQ;IACN,MAAM,EAAE,KAAK;IACb,UAAU,EAAE,KAAK;IAClB;GACF;AAGD,MAAI,CAAC,KAAK,QAAQ,KAAK,QACrB,QAAO,OAAO,IAAI,KAAK,UAAU,OAAO;AAG1C,SAAO,OAAO,GAAG,OAAO,UAAU,SAAS,OAAO;AAClD,SAAO,SAAS;AAGhB,SAAO,KAAK,WAAW,YAAY;GACjC,MAAM,KAAK;GACX;GACA;GACQ;GACT,CAAC;;CAGJ,MAAgB,cACd,MACA,UAAuB,EAAE,EACA;AACzB,MACE,KAAK,OAAO,WAAW,IACvB,CAAC,KAAK,OAAO,MAAM,IAAI,iCAAiC,CAExD,OAAM,KAAK,YAAY;EAGzB,MAAM,OAAO,KAAK,MAAM,MACrB,MACC,EAAE,SAAS,SAAS,CAAC,QAAQ,WAAW,QAAQ,YAAY,EAAE,SACjE;AAED,MAAI,CAAC,MAAM;GACT,MAAM,QAAQ,IAAI,kBAAkB,UAAU,KAAK,aAAa;AAEhE,SAAM,KAAK,OAAO,OAAO,KAAK,kBAAkB;IAC9C,OAAO;IACP;IACD,CAAC;AACF,SAAM;;AAGR,MAAI,QAAQ,SACV,QAAO;GACL,GAAG;GACH,MAAM,QAAQ;GACf;AAGH,SAAO;;;;;;;;;ACjYX,MAAa,WACX,UACyB;AACzB,QAAO,QAAQ,aAAa,CAAC,OAAU,MAAM;;AAG/C,QAAQ,QAAQ;;;;;;;;;;;;;ACHhB,MAAa,WAAW,YAAoC;AAC1D,QAAO,gBAAgB,iBAAiB,QAAQ;;AAuDlD,IAAa,kBAAb,cAAqC,UAAkC;CACrE,IAAW,OAAe;AACxB,SAAO,KAAK,QAAQ,QAAQ,KAAK,OAAO;;;AAI5C,QAAQ,QAAQ;;;;ACvDhB,MAAa,oBAAoB,QAAQ;CACvC,MAAM;CACN,OAAO,CAAC,cAAc,gBAAgB;CACtC,YAAY,CAAC,SAAS,QAAQ;CAC9B,UAAU,CAAC,cAAc,eAAe;CACzC,CAAC"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import * as alepha from "alepha";
|
|
2
2
|
import { Alepha, Async, KIND, Primitive, Static } from "alepha";
|
|
3
3
|
import * as alepha_server0 from "alepha/server";
|
|
4
|
-
import { ActionPrimitive, ClientRequestEntry, ClientRequestOptions, ClientRequestResponse, FetchResponse, HttpClient, RequestConfigSchema, ServerRequest, ServerRequestConfigEntry, ServerResponseBody, ServerTimingProvider } from "alepha/server";
|
|
4
|
+
import { ActionPrimitive, ClientRequestEntry, ClientRequestOptions, ClientRequestResponse, FetchResponse, HttpClient, RequestConfigSchema, ServerRequest, ServerRequestConfigEntry, ServerResponseBody, ServerTimingProvider, SseConfigSchema, SseEventData, SsePrimitive, SseRequestEntry, SseStream } from "alepha/server";
|
|
5
5
|
import * as alepha_logger0 from "alepha/logger";
|
|
6
6
|
import { ProxyPrimitiveOptions, ServerProxyProvider } from "alepha/server/proxy";
|
|
7
7
|
import { SecureOptions, SecurityProvider, ServiceAccountPrimitive, UserAccountToken } from "alepha/security";
|
|
@@ -10,20 +10,17 @@ import { SecureOptions, SecurityProvider, ServiceAccountPrimitive, UserAccountTo
|
|
|
10
10
|
declare const apiActionSchema: alepha.TObject<{
|
|
11
11
|
path: alepha.TString;
|
|
12
12
|
method: alepha.TOptional<alepha.TString>;
|
|
13
|
-
body: alepha.TOptional<alepha.TString>;
|
|
14
|
-
response: alepha.TOptional<alepha.TString>;
|
|
15
13
|
contentType: alepha.TOptional<alepha.TString>;
|
|
14
|
+
kind: alepha.TOptional<alepha.TString>;
|
|
16
15
|
service: alepha.TOptional<alepha.TString>;
|
|
17
16
|
}>;
|
|
18
17
|
declare const apiRegistryResponseSchema: alepha.TObject<{
|
|
19
18
|
prefix: alepha.TOptional<alepha.TString>;
|
|
20
|
-
definitions: alepha.TOptional<alepha.TRecord<"^.*$", alepha.TString>>;
|
|
21
19
|
actions: alepha.TRecord<"^.*$", alepha.TObject<{
|
|
22
20
|
path: alepha.TString;
|
|
23
21
|
method: alepha.TOptional<alepha.TString>;
|
|
24
|
-
body: alepha.TOptional<alepha.TString>;
|
|
25
|
-
response: alepha.TOptional<alepha.TString>;
|
|
26
22
|
contentType: alepha.TOptional<alepha.TString>;
|
|
23
|
+
kind: alepha.TOptional<alepha.TString>;
|
|
27
24
|
service: alepha.TOptional<alepha.TString>;
|
|
28
25
|
}>>;
|
|
29
26
|
permissions: alepha.TOptional<alepha.TArray<alepha.TString>>;
|
|
@@ -35,13 +32,11 @@ type ApiAction = Static<typeof apiActionSchema>;
|
|
|
35
32
|
*/
|
|
36
33
|
declare const apiLinksResponseSchema: alepha.TObject<{
|
|
37
34
|
prefix: alepha.TOptional<alepha.TString>;
|
|
38
|
-
definitions: alepha.TOptional<alepha.TRecord<"^.*$", alepha.TString>>;
|
|
39
35
|
actions: alepha.TRecord<"^.*$", alepha.TObject<{
|
|
40
36
|
path: alepha.TString;
|
|
41
37
|
method: alepha.TOptional<alepha.TString>;
|
|
42
|
-
body: alepha.TOptional<alepha.TString>;
|
|
43
|
-
response: alepha.TOptional<alepha.TString>;
|
|
44
38
|
contentType: alepha.TOptional<alepha.TString>;
|
|
39
|
+
kind: alepha.TOptional<alepha.TString>;
|
|
45
40
|
service: alepha.TOptional<alepha.TString>;
|
|
46
41
|
}>>;
|
|
47
42
|
permissions: alepha.TOptional<alepha.TArray<alepha.TString>>;
|
|
@@ -58,13 +53,11 @@ type ApiLink = ApiAction;
|
|
|
58
53
|
//#region ../../src/server/links/atoms/apiLinksAtom.d.ts
|
|
59
54
|
declare const apiLinksAtom: alepha.Atom<alepha.TOptional<alepha.TObject<{
|
|
60
55
|
prefix: alepha.TOptional<alepha.TString>;
|
|
61
|
-
definitions: alepha.TOptional<alepha.TRecord<"^.*$", alepha.TString>>;
|
|
62
56
|
actions: alepha.TRecord<"^.*$", alepha.TObject<{
|
|
63
57
|
path: alepha.TString;
|
|
64
58
|
method: alepha.TOptional<alepha.TString>;
|
|
65
|
-
body: alepha.TOptional<alepha.TString>;
|
|
66
|
-
response: alepha.TOptional<alepha.TString>;
|
|
67
59
|
contentType: alepha.TOptional<alepha.TString>;
|
|
60
|
+
kind: alepha.TOptional<alepha.TString>;
|
|
68
61
|
service: alepha.TOptional<alepha.TString>;
|
|
69
62
|
}>>;
|
|
70
63
|
permissions: alepha.TOptional<alepha.TArray<alepha.TString>>;
|
|
@@ -89,6 +82,7 @@ declare const linkOptionsAtom: alepha.Atom<alepha.TObject<{
|
|
|
89
82
|
*/
|
|
90
83
|
declare class BatchCollector {
|
|
91
84
|
protected static readonly MAX_BATCH_SIZE = 20;
|
|
85
|
+
protected readonly log: alepha_logger0.Logger;
|
|
92
86
|
protected readonly httpClient: HttpClient;
|
|
93
87
|
protected pending: PendingBatchEntry[];
|
|
94
88
|
protected scheduled: boolean;
|
|
@@ -130,7 +124,6 @@ declare class LinkProvider {
|
|
|
130
124
|
protected serverLinkMap: Map<string, HttpClientLink>;
|
|
131
125
|
protected actionMap: Map<string, HttpClientLink>;
|
|
132
126
|
protected permissions: Set<string>;
|
|
133
|
-
protected definitions: Record<string, string>;
|
|
134
127
|
protected lastLoadedRegistry: ApiRegistryResponse | null;
|
|
135
128
|
protected batchCollector?: BatchCollector;
|
|
136
129
|
protected readonly options: Readonly<{
|
|
@@ -184,6 +177,7 @@ interface HttpClientLink {
|
|
|
184
177
|
name: string;
|
|
185
178
|
path: string;
|
|
186
179
|
method?: string;
|
|
180
|
+
kind?: string;
|
|
187
181
|
contentType?: string;
|
|
188
182
|
service?: string;
|
|
189
183
|
secured?: boolean | SecureOptions;
|
|
@@ -192,21 +186,20 @@ interface HttpClientLink {
|
|
|
192
186
|
host?: string;
|
|
193
187
|
schema?: RequestConfigSchema;
|
|
194
188
|
handler?: (request: ServerRequest, options: ClientRequestOptions) => Async<ServerResponseBody>;
|
|
195
|
-
bodyRef?: string;
|
|
196
|
-
responseRef?: string;
|
|
197
189
|
}
|
|
198
190
|
interface ClientScope {
|
|
199
191
|
service?: string;
|
|
200
192
|
hostname?: string;
|
|
201
193
|
}
|
|
202
|
-
type HttpVirtualClient<T> = { [K in keyof T as T[K] extends ActionPrimitive<RequestConfigSchema> ? K : never]: T[K] extends ActionPrimitive<infer Schema> ? VirtualAction<Schema> : never };
|
|
194
|
+
type HttpVirtualClient<T> = { [K in keyof T as T[K] extends ActionPrimitive<RequestConfigSchema> ? K : never]: T[K] extends ActionPrimitive<infer Schema> ? VirtualAction<Schema> : never } & { [K in keyof T as T[K] extends SsePrimitive<SseConfigSchema> ? K : never]: T[K] extends SsePrimitive<infer Schema> ? VirtualSse<Schema> : never };
|
|
203
195
|
interface VirtualAction<T extends RequestConfigSchema> extends Pick<ActionPrimitive<T>, "name" | "run" | "fetch"> {
|
|
204
196
|
(config?: ClientRequestEntry<T>, opts?: ClientRequestOptions): Promise<ClientRequestResponse<T>>;
|
|
205
197
|
can: () => boolean;
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
198
|
+
}
|
|
199
|
+
interface VirtualSse<T extends SseConfigSchema> {
|
|
200
|
+
(config?: SseRequestEntry<T>): Promise<SseStream<SseEventData<T>>>;
|
|
201
|
+
name: string;
|
|
202
|
+
can: () => boolean;
|
|
210
203
|
}
|
|
211
204
|
//#endregion
|
|
212
205
|
//#region ../../src/server/links/primitives/$client.d.ts
|
|
@@ -379,6 +372,20 @@ declare class ServerLinksProvider {
|
|
|
379
372
|
* Cached by role set with ETag support.
|
|
380
373
|
*/
|
|
381
374
|
readonly links: alepha_server0.RoutePrimitive<RequestConfigSchema>;
|
|
375
|
+
/**
|
|
376
|
+
* On-demand schemas endpoint — returns JSON Schemas for requested actions.
|
|
377
|
+
*
|
|
378
|
+
* Schemas are filtered by the user's permissions (same logic as the registry).
|
|
379
|
+
*/
|
|
380
|
+
readonly schemas: alepha_server0.RoutePrimitive<{
|
|
381
|
+
body: alepha.TObject<{
|
|
382
|
+
actions: alepha.TArray<alepha.TString>;
|
|
383
|
+
}>;
|
|
384
|
+
response: alepha.TRecord<"^.*$", alepha.TObject<{
|
|
385
|
+
body: alepha.TOptional<alepha.TString>;
|
|
386
|
+
response: alepha.TOptional<alepha.TString>;
|
|
387
|
+
}>>;
|
|
388
|
+
}>;
|
|
382
389
|
/**
|
|
383
390
|
* Batch endpoint — execute multiple actions in a single HTTP request.
|
|
384
391
|
* Each sub-request is independent: errors in one don't affect others.
|
|
@@ -403,6 +410,10 @@ declare class ServerLinksProvider {
|
|
|
403
410
|
* Will check on local links and remote links.
|
|
404
411
|
*/
|
|
405
412
|
getUserApiLinks(options: GetApiLinksOptions): Promise<ApiRegistryResponse>;
|
|
413
|
+
/**
|
|
414
|
+
* Check if a link is accessible by the given user based on security rules.
|
|
415
|
+
*/
|
|
416
|
+
protected isLinkAccessible(link: HttpClientLink, user?: UserAccountToken): boolean;
|
|
406
417
|
}
|
|
407
418
|
interface GetApiLinksOptions {
|
|
408
419
|
user?: UserAccountToken;
|
|
@@ -413,34 +424,6 @@ interface RegistryCacheEntry {
|
|
|
413
424
|
etag: string;
|
|
414
425
|
}
|
|
415
426
|
//#endregion
|
|
416
|
-
//#region ../../src/server/links/services/DefinitionsPool.d.ts
|
|
417
|
-
/**
|
|
418
|
-
* Builds a deduplicated pool of pre-stringified JSON Schema definitions.
|
|
419
|
-
*
|
|
420
|
-
* Each unique schema is stored once and assigned a `$N` reference key.
|
|
421
|
-
* Actions reference schemas via these keys instead of inlining the full JSON.
|
|
422
|
-
*
|
|
423
|
-
* Runs at startup (registration time), not on the hot path.
|
|
424
|
-
*/
|
|
425
|
-
declare class DefinitionsPool {
|
|
426
|
-
protected pool: Map<string, string>;
|
|
427
|
-
protected counter: number;
|
|
428
|
-
/**
|
|
429
|
-
* Register a schema and return its `$N` reference key.
|
|
430
|
-
* If the same schema was already registered, returns the existing key.
|
|
431
|
-
*/
|
|
432
|
-
ref(schema: object | undefined): string | undefined;
|
|
433
|
-
/**
|
|
434
|
-
* Returns the definitions as `{ "$0": "json string", "$1": "..." }`.
|
|
435
|
-
* Keys are `$N` refs, values are pre-stringified JSON Schema.
|
|
436
|
-
*/
|
|
437
|
-
toJSON(): Record<string, string>;
|
|
438
|
-
/**
|
|
439
|
-
* Number of unique definitions in the pool.
|
|
440
|
-
*/
|
|
441
|
-
get size(): number;
|
|
442
|
-
}
|
|
443
|
-
//#endregion
|
|
444
427
|
//#region ../../src/server/links/index.d.ts
|
|
445
428
|
declare module "alepha" {
|
|
446
429
|
interface State {
|
|
@@ -473,5 +456,5 @@ declare module "alepha" {
|
|
|
473
456
|
*/
|
|
474
457
|
declare const AlephaServerLinks: alepha.Service<alepha.Module>;
|
|
475
458
|
//#endregion
|
|
476
|
-
export { $client, $remote, AlephaServerLinks, ApiAction, ApiLink, ApiLinksResponse, ApiRegistryResponse, BatchCollector, BatchEntry, ClientScope,
|
|
459
|
+
export { $client, $remote, AlephaServerLinks, ApiAction, ApiLink, ApiLinksResponse, ApiRegistryResponse, BatchCollector, BatchEntry, ClientScope, FetchLinksOptions, GetApiLinksOptions, HttpClientLink, HttpVirtualClient, LinkProvider, RemotePrimitive, RemotePrimitiveOptions, RemotePrimitiveProvider, ServerLinksProvider, ServerRemote, VirtualAction, VirtualSse, apiActionSchema, apiLinksAtom, apiLinksResponseSchema, apiRegistryResponseSchema, linkOptionsAtom };
|
|
477
460
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","names":[],"sources":["../../../src/server/links/schemas/apiLinksResponseSchema.ts","../../../src/server/links/atoms/apiLinksAtom.ts","../../../src/server/links/atoms/linkOptionsAtom.ts","../../../src/server/links/services/BatchCollector.ts","../../../src/server/links/providers/LinkProvider.ts","../../../src/server/links/primitives/$client.ts","../../../src/server/links/primitives/$remote.ts","../../../src/server/links/providers/RemotePrimitiveProvider.ts","../../../src/server/links/providers/ServerLinksProvider.ts","../../../src/server/links/
|
|
1
|
+
{"version":3,"file":"index.d.ts","names":[],"sources":["../../../src/server/links/schemas/apiLinksResponseSchema.ts","../../../src/server/links/atoms/apiLinksAtom.ts","../../../src/server/links/atoms/linkOptionsAtom.ts","../../../src/server/links/services/BatchCollector.ts","../../../src/server/links/providers/LinkProvider.ts","../../../src/server/links/primitives/$client.ts","../../../src/server/links/primitives/$remote.ts","../../../src/server/links/providers/RemotePrimitiveProvider.ts","../../../src/server/links/providers/ServerLinksProvider.ts","../../../src/server/links/index.ts"],"mappings":"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"}
|