alepha 0.18.2 → 0.18.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/assets/devtools-ui/200.html +2 -2
- package/assets/devtools-ui/200.html.br +0 -0
- package/assets/devtools-ui/404.html +2 -2
- package/assets/devtools-ui/404.html.br +0 -0
- package/assets/devtools-ui/{asset.BfSBZ5Dd.css → asset.hG_f8HuK.css} +1 -1
- package/assets/devtools-ui/asset.hG_f8HuK.css.br +0 -0
- package/assets/devtools-ui/chunk.B3au4Lhg.js +1 -0
- package/assets/devtools-ui/chunk.B3au4Lhg.js.br +0 -0
- package/assets/devtools-ui/chunk.BLOrlnMB.js +1 -0
- package/assets/devtools-ui/chunk.BLOrlnMB.js.br +0 -0
- package/assets/devtools-ui/chunk.BLR01ljW.js +1 -0
- package/assets/devtools-ui/chunk.BLR01ljW.js.br +0 -0
- package/assets/devtools-ui/chunk.BTXaIUlA.js +1 -0
- package/assets/devtools-ui/chunk.BTXaIUlA.js.br +0 -0
- package/assets/devtools-ui/{chunk.lJL-lgnW.js → chunk.BhJaxmm8.js} +1 -1
- package/assets/devtools-ui/chunk.BhJaxmm8.js.br +0 -0
- package/assets/devtools-ui/chunk.BtoNxFuL.js +1 -0
- package/assets/devtools-ui/chunk.BtoNxFuL.js.br +0 -0
- package/assets/devtools-ui/chunk.C8YUV2Wd.js +1 -0
- package/assets/devtools-ui/chunk.C8YUV2Wd.js.br +0 -0
- package/assets/devtools-ui/{chunk.M6wyKO_3.js → chunk.CBbIgDzE.js} +2 -2
- package/assets/devtools-ui/chunk.CBbIgDzE.js.br +0 -0
- package/assets/devtools-ui/chunk.CFqIniwA.js +1 -0
- package/assets/devtools-ui/chunk.CFqIniwA.js.br +0 -0
- package/assets/devtools-ui/chunk.CLFF7f7-.js +1 -0
- package/assets/devtools-ui/chunk.CLFF7f7-.js.br +0 -0
- package/assets/devtools-ui/chunk.CRsBbA10.js +1 -0
- package/assets/devtools-ui/chunk.CRsBbA10.js.br +0 -0
- package/assets/devtools-ui/{chunk.DbEH1oOB.js → chunk.CZPo6v95.js} +1 -1
- package/assets/devtools-ui/chunk.CZPo6v95.js.br +0 -0
- package/assets/devtools-ui/chunk.D0fWgNos.js +1 -0
- package/assets/devtools-ui/chunk.D0fWgNos.js.br +1 -0
- package/assets/devtools-ui/chunk.D7-0ziQ6.js +1 -0
- package/assets/devtools-ui/chunk.D7-0ziQ6.js.br +0 -0
- package/assets/devtools-ui/chunk.DAewe0vm.js +1 -0
- package/assets/devtools-ui/chunk.DAewe0vm.js.br +0 -0
- package/assets/devtools-ui/chunk.DJRQEYqK.js +1 -0
- package/assets/devtools-ui/chunk.DJRQEYqK.js.br +0 -0
- package/assets/devtools-ui/{chunk.CZl6J9DF.js → chunk.DMAxv14p.js} +1 -1
- package/assets/devtools-ui/chunk.DMAxv14p.js.br +0 -0
- package/assets/devtools-ui/{chunk.BT2IiBkZ.js → chunk.DMImnNjU.js} +1 -1
- package/assets/devtools-ui/chunk.DMImnNjU.js.br +0 -0
- package/assets/devtools-ui/chunk.DeeQsidk.js +9 -0
- package/assets/devtools-ui/chunk.DeeQsidk.js.br +0 -0
- package/assets/devtools-ui/chunk.DqEwn9Vj.js +7 -0
- package/assets/devtools-ui/chunk.DqEwn9Vj.js.br +0 -0
- package/assets/devtools-ui/chunk.Dt8OsQey.js +1 -0
- package/assets/devtools-ui/chunk.Dt8OsQey.js.br +0 -0
- package/assets/devtools-ui/{chunk.B9pX3zit.js → chunk.Dtp8oa_f.js} +1 -1
- package/assets/devtools-ui/chunk.Dtp8oa_f.js.br +0 -0
- package/assets/devtools-ui/chunk.Dx3JzAYM.js +1 -0
- package/assets/devtools-ui/chunk.Dx3JzAYM.js.br +0 -0
- package/assets/devtools-ui/chunk.GCOj1-5E.js +1 -0
- package/assets/devtools-ui/chunk.GCOj1-5E.js.br +0 -0
- package/assets/devtools-ui/chunk.IC1LD8BH.js +1 -0
- package/assets/devtools-ui/chunk.IC1LD8BH.js.br +0 -0
- package/assets/devtools-ui/chunk.IwuB_TqW.js +1 -0
- package/assets/devtools-ui/chunk.IwuB_TqW.js.br +0 -0
- package/assets/devtools-ui/chunk.Qqapj2zq.js +1 -0
- package/assets/devtools-ui/chunk.Qqapj2zq.js.br +0 -0
- package/assets/devtools-ui/{chunk.C79YouPp.js → chunk.TKKKndOy.js} +1 -1
- package/assets/devtools-ui/chunk.TKKKndOy.js.br +0 -0
- package/assets/devtools-ui/chunk.YHTVhFQT.js +1 -0
- package/assets/devtools-ui/chunk.YHTVhFQT.js.br +0 -0
- package/assets/devtools-ui/chunk.fnod6uEi.js +1 -0
- package/assets/devtools-ui/chunk.fnod6uEi.js.br +0 -0
- package/assets/devtools-ui/chunk.mOCRmXjo.js +1 -0
- package/assets/devtools-ui/chunk.mOCRmXjo.js.br +0 -0
- package/assets/devtools-ui/chunk.qZTNEAK0.js +1 -0
- package/assets/devtools-ui/chunk.qZTNEAK0.js.br +0 -0
- package/assets/devtools-ui/chunk.rc9m0y4-.js +1 -0
- package/assets/devtools-ui/chunk.rc9m0y4-.js.br +0 -0
- package/assets/devtools-ui/entry.Cxc5QLCU.js +80 -0
- package/assets/devtools-ui/entry.Cxc5QLCU.js.br +0 -0
- package/assets/devtools-ui/index.html +2 -2
- package/assets/devtools-ui/index.html.br +0 -0
- package/assets/swagger-ui/swagger-ui-bundle.js +1 -1
- package/assets/swagger-ui/swagger-ui.css +1 -1
- package/dist/api/audits/index.d.ts +61 -5
- package/dist/api/audits/index.d.ts.map +1 -1
- package/dist/api/files/index.d.ts +61 -5
- package/dist/api/files/index.d.ts.map +1 -1
- package/dist/api/jobs/index.d.ts +61 -5
- package/dist/api/jobs/index.d.ts.map +1 -1
- package/dist/api/jobs/index.js +4 -2
- package/dist/api/jobs/index.js.map +1 -1
- package/dist/api/keys/index.d.ts +5 -5
- package/dist/api/notifications/index.browser.js +44 -1
- package/dist/api/notifications/index.browser.js.map +1 -1
- package/dist/api/notifications/index.d.ts +187 -2
- package/dist/api/notifications/index.d.ts.map +1 -1
- package/dist/api/notifications/index.js +143 -8
- package/dist/api/notifications/index.js.map +1 -1
- package/dist/api/parameters/index.d.ts +61 -5
- package/dist/api/parameters/index.d.ts.map +1 -1
- package/dist/api/users/index.d.ts +330 -93
- package/dist/api/users/index.d.ts.map +1 -1
- package/dist/api/users/index.js +27 -36
- package/dist/api/users/index.js.map +1 -1
- package/dist/cli/config/index.d.ts +46 -0
- package/dist/cli/config/index.d.ts.map +1 -0
- package/dist/cli/config/index.js +20 -0
- package/dist/cli/config/index.js.map +1 -0
- package/dist/cli/core/index.d.ts +69 -66
- package/dist/cli/core/index.d.ts.map +1 -1
- package/dist/cli/core/index.js +329 -196
- package/dist/cli/core/index.js.map +1 -1
- package/dist/cli/platform/index.d.ts +302 -63
- package/dist/cli/platform/index.d.ts.map +1 -1
- package/dist/cli/platform/index.js +455 -25
- package/dist/cli/platform/index.js.map +1 -1
- package/dist/core/index.browser.js +125 -87
- package/dist/core/index.browser.js.map +1 -1
- package/dist/core/index.d.ts +62 -53
- package/dist/core/index.d.ts.map +1 -1
- package/dist/core/index.js +125 -87
- package/dist/core/index.js.map +1 -1
- package/dist/core/index.native.js +125 -87
- package/dist/core/index.native.js.map +1 -1
- package/dist/core/index.workerd.js +125 -87
- package/dist/core/index.workerd.js.map +1 -1
- package/dist/crypto/index.d.ts +18 -1
- package/dist/crypto/index.d.ts.map +1 -1
- package/dist/crypto/index.js +29 -3
- package/dist/crypto/index.js.map +1 -1
- package/dist/devtools/index.js +3 -12
- package/dist/devtools/index.js.map +1 -1
- package/dist/logger/index.d.ts +10 -1
- package/dist/logger/index.d.ts.map +1 -1
- package/dist/logger/index.js +19 -9
- package/dist/logger/index.js.map +1 -1
- package/dist/orm/core/index.browser.js +57 -1
- package/dist/orm/core/index.browser.js.map +1 -1
- package/dist/orm/core/index.bun.js +378 -19
- package/dist/orm/core/index.bun.js.map +1 -1
- package/dist/orm/core/index.d.ts +328 -9
- package/dist/orm/core/index.d.ts.map +1 -1
- package/dist/orm/core/index.js +384 -21
- package/dist/orm/core/index.js.map +1 -1
- package/dist/orm/postgres/index.bun.js +49 -17
- package/dist/orm/postgres/index.bun.js.map +1 -1
- package/dist/orm/postgres/index.d.ts +47 -21
- package/dist/orm/postgres/index.d.ts.map +1 -1
- package/dist/orm/postgres/index.js +52 -17
- package/dist/orm/postgres/index.js.map +1 -1
- package/dist/react/core/index.d.ts +1 -1
- package/dist/react/core/index.d.ts.map +1 -1
- package/dist/react/core/index.js +6 -1
- package/dist/react/core/index.js.map +1 -1
- package/dist/react/form/index.d.ts +28 -18
- package/dist/react/form/index.d.ts.map +1 -1
- package/dist/react/form/index.js +92 -56
- package/dist/react/form/index.js.map +1 -1
- package/dist/react/router/index.browser.js +448 -116
- package/dist/react/router/index.browser.js.map +1 -1
- package/dist/react/router/index.d.ts +102 -40
- package/dist/react/router/index.d.ts.map +1 -1
- package/dist/react/router/index.js +453 -92
- package/dist/react/router/index.js.map +1 -1
- package/dist/security/index.d.ts +3 -11
- package/dist/security/index.d.ts.map +1 -1
- package/dist/security/index.js +6 -11
- package/dist/security/index.js.map +1 -1
- package/dist/server/auth/index.d.ts +22 -24
- package/dist/server/auth/index.d.ts.map +1 -1
- package/dist/server/auth/index.js +102 -82
- package/dist/server/auth/index.js.map +1 -1
- package/dist/server/cookies/index.d.ts +7 -4
- package/dist/server/cookies/index.d.ts.map +1 -1
- package/dist/server/cookies/index.js +13 -12
- package/dist/server/cookies/index.js.map +1 -1
- package/dist/server/core/index.d.ts +288 -4
- package/dist/server/core/index.d.ts.map +1 -1
- package/dist/server/core/index.js +375 -2
- package/dist/server/core/index.js.map +1 -1
- package/dist/server/links/index.browser.js +10 -71
- package/dist/server/links/index.browser.js.map +1 -1
- package/dist/server/links/index.d.ts +32 -49
- package/dist/server/links/index.d.ts.map +1 -1
- package/dist/server/links/index.js +73 -100
- package/dist/server/links/index.js.map +1 -1
- package/dist/system/index.browser.js +221 -2
- package/dist/system/index.browser.js.map +1 -1
- package/dist/system/index.d.ts +63 -1
- package/dist/system/index.d.ts.map +1 -1
- package/dist/system/index.js +221 -1
- package/dist/system/index.js.map +1 -1
- package/dist/system/index.workerd.js +224 -4
- package/dist/system/index.workerd.js.map +1 -1
- package/package.json +10 -5
- package/src/api/jobs/providers/JobProvider.ts +6 -3
- package/src/api/notifications/controllers/AdminNotificationController.ts +83 -0
- package/src/api/notifications/index.browser.ts +3 -0
- package/src/api/notifications/index.ts +14 -2
- package/src/api/notifications/jobs/NotificationJobs.ts +11 -2
- package/src/api/notifications/schemas/notificationDetailResourceSchema.ts +20 -0
- package/src/api/notifications/schemas/notificationQuerySchema.ts +19 -0
- package/src/api/notifications/schemas/notificationResourceSchema.ts +18 -0
- package/src/api/notifications/services/NotificationSenderService.ts +15 -2
- package/src/api/users/atoms/realmAuthSettingsAtom.ts +28 -32
- package/src/api/users/buckets/UserBuckets.ts +1 -1
- package/src/api/users/jobs/UserJobs.ts +1 -1
- package/src/api/users/primitives/$realm.ts +8 -49
- package/src/api/users/providers/RealmProvider.ts +2 -3
- package/src/api/users/services/RegistrationService.spec.ts +7 -7
- package/src/api/users/services/RegistrationService.ts +3 -3
- package/src/api/users/services/SessionService.spec.ts +4 -4
- package/src/api/users/services/SessionService.ts +3 -3
- package/src/cli/{core → config}/defineConfig.ts +14 -20
- package/src/cli/config/index.ts +1 -0
- package/src/cli/core/commands/db.ts +65 -1
- package/src/cli/core/commands/dev.ts +1 -0
- package/src/cli/core/commands/init.ts +2 -192
- package/src/cli/core/index.ts +34 -11
- package/src/cli/core/providers/ViteDevServerProvider.ts +52 -13
- package/src/cli/core/services/PackageManagerUtils.ts +43 -21
- package/src/cli/core/services/ProjectScaffolder.ts +214 -2
- package/src/cli/core/services/ViteUtils.ts +57 -0
- package/src/cli/core/tasks/BuildClientTask.ts +7 -2
- package/src/cli/core/tasks/BuildCloudflareTask.ts +4 -12
- package/src/cli/core/tasks/BuildServerTask.ts +2 -0
- package/src/cli/core/tasks/BuildVercelTask.ts +165 -168
- package/src/cli/core/templates/alephaConfigTs.ts +1 -1
- package/src/cli/core/templates/apiAppSecurityTs.ts +5 -8
- package/src/cli/core/templates/tsconfigJson.ts +6 -1
- package/src/cli/platform/adapters/CloudflareAdapter.spec.ts +1 -1
- package/src/cli/platform/adapters/CloudflareAdapter.ts +30 -29
- package/src/cli/platform/atoms/platformOptions.ts +21 -0
- package/src/cli/platform/commands/SecretsCommand.spec.ts +298 -0
- package/src/cli/platform/commands/SecretsCommand.ts +283 -0
- package/src/cli/platform/commands/platform.ts +12 -0
- package/src/cli/platform/index.ts +14 -28
- package/src/cli/platform/providers/GitHubSecretStore.spec.ts +153 -0
- package/src/cli/platform/providers/GitHubSecretStore.ts +112 -0
- package/src/cli/platform/providers/MemorySecretStore.ts +114 -0
- package/src/cli/platform/providers/SecretStoreProvider.ts +39 -0
- package/src/cli/platform/schemas/cloudflare.ts +2 -0
- package/src/cli/platform/services/CloudflareApi.ts +5 -2
- package/src/cli/platform/services/DockerComposeGenerator.spec.ts +115 -0
- package/src/cli/platform/services/DockerComposeGenerator.ts +46 -1
- package/src/cli/platform/services/SecretFilterService.spec.ts +111 -0
- package/src/cli/platform/services/SecretFilterService.ts +54 -0
- package/src/core/Alepha.ts +94 -25
- package/src/core/__tests__/Alepha-parseEnv.spec.ts +20 -0
- package/src/core/primitives/$memoize.ts +38 -26
- package/src/core/providers/AlsProvider.ts +2 -0
- package/src/core/providers/EventManager.ts +4 -0
- package/src/core/providers/KeylessJsonSchemaCodec.spec.ts +1 -4
- package/src/core/providers/KeylessJsonSchemaCodec.ts +19 -125
- package/src/core/providers/SchemaValidator.spec.ts +36 -0
- package/src/core/providers/SchemaValidator.ts +9 -0
- package/src/crypto/index.ts +6 -1
- package/src/crypto/providers/SecretProvider.ts +36 -0
- package/src/devtools/providers/DevToolsProvider.ts +3 -12
- package/src/logger/index.ts +33 -6
- package/src/logger/providers/PrettyFormatterProvider.ts +5 -3
- package/src/orm/__tests__/orm-next-tests.ts +492 -0
- package/src/orm/__tests__/orm-next.spec.ts +140 -0
- package/src/orm/core/constants/PG_SYMBOLS.ts +17 -0
- package/src/orm/core/index.bun.ts +3 -6
- package/src/orm/core/index.shared-server.ts +2 -0
- package/src/orm/core/index.shared.ts +2 -0
- package/src/orm/core/index.ts +5 -7
- package/src/orm/core/interfaces/AggregateQuery.ts +103 -0
- package/src/orm/core/interfaces/PgQueryWhere.ts +7 -0
- package/src/orm/core/primitives/$entity.ts +8 -0
- package/src/orm/core/primitives/$repository.ts +6 -3
- package/src/orm/core/primitives/$view.ts +88 -0
- package/src/orm/core/providers/DbCacheProvider.ts +66 -0
- package/src/orm/core/providers/DrizzleKitProvider.ts +42 -0
- package/src/orm/core/providers/drivers/BunSqliteProvider.ts +2 -3
- package/src/orm/core/providers/drivers/CloudflareD1Provider.ts +12 -0
- package/src/orm/core/providers/drivers/DatabaseProvider.ts +39 -0
- package/src/orm/core/providers/drivers/NodeSqliteProvider.ts +2 -3
- package/src/orm/core/schemas/databaseEnvSchema.ts +31 -0
- package/src/orm/core/schemas/insertSchema.ts +13 -3
- package/src/orm/core/schemas/updateSchema.ts +14 -3
- package/src/orm/core/services/ModelBuilder.ts +26 -14
- package/src/orm/core/services/QueryManager.ts +13 -0
- package/src/orm/core/services/Repository.ts +307 -5
- package/src/orm/core/services/SqliteModelBuilder.ts +38 -0
- package/src/orm/postgres/index.bun.ts +4 -7
- package/src/orm/postgres/index.ts +4 -7
- package/src/orm/postgres/providers/BunPostgresProvider.ts +12 -2
- package/src/orm/postgres/providers/NodePostgresProvider.ts +7 -0
- package/src/orm/postgres/providers/PglitePostgresProvider.ts +10 -17
- package/src/orm/postgres/providers/PostgresProvider.ts +7 -36
- package/src/orm/postgres/schemas/postgresEnvSchema.ts +32 -0
- package/src/orm/postgres/services/PostgresModelBuilder.ts +40 -0
- package/src/react/core/components/ErrorBoundary.tsx +5 -2
- package/src/react/form/hooks/useFieldValue.ts +34 -0
- package/src/react/form/hooks/useForm.browser.spec.tsx +94 -9
- package/src/react/form/hooks/useForm.ts +14 -2
- package/src/react/form/hooks/useFormState.ts +10 -10
- package/src/react/form/hooks/useFormValues.ts +29 -0
- package/src/react/form/index.ts +3 -1
- package/src/react/form/services/FormModel.ts +53 -122
- package/src/react/router/components/ErrorViewer.tsx +333 -34
- package/src/react/router/components/NestedView.tsx +10 -3
- package/src/react/router/primitives/$page.browser.spec.tsx +34 -0
- package/src/react/router/primitives/$page.spec.tsx +20 -0
- package/src/react/router/primitives/$page.ts +24 -0
- package/src/react/router/providers/ReactBrowserRouterProvider.ts +14 -2
- package/src/react/router/providers/ReactPageProvider.ts +156 -73
- package/src/react/router/providers/ReactServerProvider.ts +40 -2
- package/src/react/router/providers/ReactServerTemplateProvider.ts +13 -1
- package/src/security/providers/SecurityProvider.ts +5 -27
- package/src/server/auth/primitives/$auth.ts +52 -19
- package/src/server/auth/providers/ServerAuthProvider.ts +145 -139
- package/src/server/cookies/providers/ServerCookiesProvider.ts +12 -24
- package/src/server/core/index.ts +3 -1
- package/src/server/core/primitives/$sse.spec.ts +315 -0
- package/src/server/core/primitives/$sse.ts +715 -0
- package/src/server/links/index.browser.ts +1 -3
- package/src/server/links/index.ts +0 -3
- package/src/server/links/providers/LinkProvider.spec.ts +12 -21
- package/src/server/links/providers/LinkProvider.ts +20 -52
- package/src/server/links/providers/ServerLinksProvider.spec.ts +106 -0
- package/src/server/links/providers/ServerLinksProvider.ts +113 -73
- package/src/server/links/schemas/apiLinksResponseSchema.ts +4 -21
- package/src/server/links/services/BatchCollector.ts +5 -3
- package/src/system/index.browser.ts +1 -0
- package/src/system/index.ts +3 -0
- package/src/system/index.workerd.ts +39 -1
- package/src/system/providers/WorkerdFileSystemProvider.ts +365 -0
- package/assets/devtools-ui/asset.BfSBZ5Dd.css.br +0 -0
- package/assets/devtools-ui/chunk.2NYaoqWt.js +0 -1
- package/assets/devtools-ui/chunk.2NYaoqWt.js.br +0 -0
- package/assets/devtools-ui/chunk.B052Z_xQ.js +0 -1
- package/assets/devtools-ui/chunk.B052Z_xQ.js.br +0 -0
- package/assets/devtools-ui/chunk.B4kVY90C.js +0 -1
- package/assets/devtools-ui/chunk.B4kVY90C.js.br +0 -0
- package/assets/devtools-ui/chunk.B7QJXctB.js +0 -1
- package/assets/devtools-ui/chunk.B7QJXctB.js.br +0 -0
- package/assets/devtools-ui/chunk.B9pX3zit.js.br +0 -0
- package/assets/devtools-ui/chunk.BKF9JxIo.js +0 -1
- package/assets/devtools-ui/chunk.BKF9JxIo.js.br +0 -0
- package/assets/devtools-ui/chunk.BOHgdTP-.js +0 -1
- package/assets/devtools-ui/chunk.BOHgdTP-.js.br +0 -0
- package/assets/devtools-ui/chunk.BOVFxkYC.js +0 -1
- package/assets/devtools-ui/chunk.BOVFxkYC.js.br +0 -0
- package/assets/devtools-ui/chunk.BR842zj5.js +0 -1
- package/assets/devtools-ui/chunk.BR842zj5.js.br +0 -0
- package/assets/devtools-ui/chunk.BT2IiBkZ.js.br +0 -0
- package/assets/devtools-ui/chunk.C79YouPp.js.br +0 -0
- package/assets/devtools-ui/chunk.C8mlBrjW.js +0 -9
- package/assets/devtools-ui/chunk.C8mlBrjW.js.br +0 -0
- package/assets/devtools-ui/chunk.CK0ow3AZ.js +0 -1
- package/assets/devtools-ui/chunk.CK0ow3AZ.js.br +0 -0
- package/assets/devtools-ui/chunk.CZl6J9DF.js.br +0 -0
- package/assets/devtools-ui/chunk.CdNr0YzS.js +0 -1
- package/assets/devtools-ui/chunk.CdNr0YzS.js.br +0 -0
- package/assets/devtools-ui/chunk.Ce6_6iIF.js +0 -1
- package/assets/devtools-ui/chunk.Ce6_6iIF.js.br +0 -0
- package/assets/devtools-ui/chunk.CpyDMr6O.js +0 -1
- package/assets/devtools-ui/chunk.CpyDMr6O.js.br +0 -0
- package/assets/devtools-ui/chunk.CyPmvPnY.js +0 -1
- package/assets/devtools-ui/chunk.CyPmvPnY.js.br +0 -0
- package/assets/devtools-ui/chunk.DTI_geWu.js +0 -1
- package/assets/devtools-ui/chunk.DTI_geWu.js.br +0 -0
- package/assets/devtools-ui/chunk.DbEH1oOB.js.br +0 -0
- package/assets/devtools-ui/chunk.Ddeqj5gv.js +0 -1
- package/assets/devtools-ui/chunk.Ddeqj5gv.js.br +0 -0
- package/assets/devtools-ui/chunk.DpRnB4vJ.js +0 -1
- package/assets/devtools-ui/chunk.DpRnB4vJ.js.br +0 -0
- package/assets/devtools-ui/chunk.DxPGTlsg.js +0 -1
- package/assets/devtools-ui/chunk.DxPGTlsg.js.br +0 -0
- package/assets/devtools-ui/chunk.G7_MMBJS.js +0 -1
- package/assets/devtools-ui/chunk.G7_MMBJS.js.br +0 -0
- package/assets/devtools-ui/chunk.M6wyKO_3.js.br +0 -0
- package/assets/devtools-ui/chunk.OUxNGmQ6.js +0 -1
- package/assets/devtools-ui/chunk.OUxNGmQ6.js.br +0 -0
- package/assets/devtools-ui/chunk.T1kle-fF.js +0 -1
- package/assets/devtools-ui/chunk.T1kle-fF.js.br +0 -0
- package/assets/devtools-ui/chunk.WjpsbQAv.js +0 -1
- package/assets/devtools-ui/chunk.WjpsbQAv.js.br +0 -0
- package/assets/devtools-ui/chunk.c6YgVx86.js +0 -1
- package/assets/devtools-ui/chunk.c6YgVx86.js.br +0 -0
- package/assets/devtools-ui/chunk.dwU3E_MU.js +0 -1
- package/assets/devtools-ui/chunk.dwU3E_MU.js.br +0 -0
- package/assets/devtools-ui/chunk.lJL-lgnW.js.br +0 -0
- package/assets/devtools-ui/chunk.lPWRmvA-.js +0 -7
- package/assets/devtools-ui/chunk.lPWRmvA-.js.br +0 -0
- package/assets/devtools-ui/chunk.p3HJvugM.js +0 -1
- package/assets/devtools-ui/chunk.p3HJvugM.js.br +0 -0
- package/assets/devtools-ui/chunk.r_Xoa_CI.js +0 -1
- package/assets/devtools-ui/chunk.r_Xoa_CI.js.br +0 -0
- package/assets/devtools-ui/chunk.sRNuTYXb.js +0 -1
- package/assets/devtools-ui/chunk.sRNuTYXb.js.br +0 -0
- package/assets/devtools-ui/chunk.tUjcyX5C.js +0 -1
- package/assets/devtools-ui/chunk.tUjcyX5C.js.br +0 -0
- package/assets/devtools-ui/chunk.thjBxvCA.js +0 -1
- package/assets/devtools-ui/chunk.thjBxvCA.js.br +0 -0
- package/assets/devtools-ui/entry.GYhBVRpC.js +0 -78
- package/assets/devtools-ui/entry.GYhBVRpC.js.br +0 -0
- package/src/server/links/services/DefinitionsPool.spec.ts +0 -86
- package/src/server/links/services/DefinitionsPool.ts +0 -43
|
@@ -0,0 +1,715 @@
|
|
|
1
|
+
import {
|
|
2
|
+
$inject,
|
|
3
|
+
$use,
|
|
4
|
+
AlephaError,
|
|
5
|
+
type Async,
|
|
6
|
+
createPrimitive,
|
|
7
|
+
KIND,
|
|
8
|
+
PipelinePrimitive,
|
|
9
|
+
type PipelinePrimitiveOptions,
|
|
10
|
+
type Static,
|
|
11
|
+
type TObject,
|
|
12
|
+
type TSchema,
|
|
13
|
+
} from "alepha";
|
|
14
|
+
import { $logger } from "alepha/logger";
|
|
15
|
+
import type { RouteMethod } from "../constants/routeMethods.ts";
|
|
16
|
+
import { ServerReply } from "../helpers/ServerReply.ts";
|
|
17
|
+
import type {
|
|
18
|
+
RequestConfigSchema,
|
|
19
|
+
ServerRequest,
|
|
20
|
+
} from "../interfaces/ServerRequest.ts";
|
|
21
|
+
import { ServerProvider } from "../providers/ServerProvider.ts";
|
|
22
|
+
import { ServerRouterProvider } from "../providers/ServerRouterProvider.ts";
|
|
23
|
+
import { serverApiOptions } from "./$action.ts";
|
|
24
|
+
|
|
25
|
+
// ----------------------------------------------------------------------------------------------------------
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Schema configuration for an SSE endpoint.
|
|
29
|
+
*/
|
|
30
|
+
export interface SseConfigSchema {
|
|
31
|
+
/**
|
|
32
|
+
* Request body schema.
|
|
33
|
+
*/
|
|
34
|
+
body?: TObject;
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Path parameters schema.
|
|
38
|
+
*/
|
|
39
|
+
params?: TObject;
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Query parameters schema.
|
|
43
|
+
*/
|
|
44
|
+
query?: TObject;
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Request headers schema.
|
|
48
|
+
*/
|
|
49
|
+
headers?: TObject;
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Schema for the data payload of each SSE event.
|
|
53
|
+
*/
|
|
54
|
+
data?: TSchema;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// ----------------------------------------------------------------------------------------------------------
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Context object passed to the SSE handler function.
|
|
61
|
+
*/
|
|
62
|
+
export interface SseHandlerContext<TConfig extends SseConfigSchema> {
|
|
63
|
+
/**
|
|
64
|
+
* Parsed request body.
|
|
65
|
+
*/
|
|
66
|
+
body: TConfig["body"] extends TObject ? Static<TConfig["body"]> : any;
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Parsed path parameters.
|
|
70
|
+
*/
|
|
71
|
+
params: TConfig["params"] extends TObject
|
|
72
|
+
? Static<TConfig["params"]>
|
|
73
|
+
: Record<string, string>;
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Parsed query parameters.
|
|
77
|
+
*/
|
|
78
|
+
query: TConfig["query"] extends TObject
|
|
79
|
+
? Partial<Static<TConfig["query"]>>
|
|
80
|
+
: Record<string, any>;
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Parsed request headers.
|
|
84
|
+
*/
|
|
85
|
+
headers: TConfig["headers"] extends TObject
|
|
86
|
+
? Static<TConfig["headers"]>
|
|
87
|
+
: Record<string, string>;
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* The underlying server request object.
|
|
91
|
+
*/
|
|
92
|
+
request: ServerRequest;
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Emit an SSE event to the client.
|
|
96
|
+
*/
|
|
97
|
+
emit: (
|
|
98
|
+
data: TConfig["data"] extends TSchema ? Static<TConfig["data"]> : any,
|
|
99
|
+
) => void;
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Close the SSE stream.
|
|
103
|
+
*/
|
|
104
|
+
close: () => void;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Handler function type for SSE endpoints.
|
|
109
|
+
*/
|
|
110
|
+
export type SseHandler<TConfig extends SseConfigSchema = SseConfigSchema> = (
|
|
111
|
+
context: SseHandlerContext<TConfig>,
|
|
112
|
+
) => Async<void>;
|
|
113
|
+
|
|
114
|
+
// ----------------------------------------------------------------------------------------------------------
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Options for the $sse primitive.
|
|
118
|
+
*/
|
|
119
|
+
export interface SsePrimitiveOptions<TConfig extends SseConfigSchema>
|
|
120
|
+
extends PipelinePrimitiveOptions {
|
|
121
|
+
/**
|
|
122
|
+
* Name of the SSE endpoint.
|
|
123
|
+
*/
|
|
124
|
+
name?: string;
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Group SSE endpoints together.
|
|
128
|
+
*/
|
|
129
|
+
group?: string;
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Pathname of the route. If not provided, property key is used.
|
|
133
|
+
*/
|
|
134
|
+
path?: string;
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* The config schema for the SSE endpoint.
|
|
138
|
+
*/
|
|
139
|
+
schema?: TConfig;
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* A short description of the endpoint. Used for documentation purposes.
|
|
143
|
+
*/
|
|
144
|
+
description?: string;
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Disable the SSE endpoint.
|
|
148
|
+
*/
|
|
149
|
+
disabled?: boolean;
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Main SSE handler. Receives context with emit/close functions.
|
|
153
|
+
*/
|
|
154
|
+
handler: SseHandler<TConfig>;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// ----------------------------------------------------------------------------------------------------------
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* Async iterable stream of SSE events.
|
|
161
|
+
*
|
|
162
|
+
* Supports push-based event delivery via `push()`, error propagation
|
|
163
|
+
* via `fail()`, and clean termination via `end()`.
|
|
164
|
+
*/
|
|
165
|
+
export class SseStream<T> implements AsyncIterable<T> {
|
|
166
|
+
protected queue: T[] = [];
|
|
167
|
+
protected error: Error | null = null;
|
|
168
|
+
protected done = false;
|
|
169
|
+
protected resolve: (() => void) | null = null;
|
|
170
|
+
protected listeners: Array<(data: T) => void> = [];
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* Push a new event into the stream.
|
|
174
|
+
*/
|
|
175
|
+
public push(data: T): void {
|
|
176
|
+
if (this.done) return;
|
|
177
|
+
this.queue.push(data);
|
|
178
|
+
for (const listener of this.listeners) {
|
|
179
|
+
listener(data);
|
|
180
|
+
}
|
|
181
|
+
if (this.resolve) {
|
|
182
|
+
this.resolve();
|
|
183
|
+
this.resolve = null;
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
/**
|
|
188
|
+
* Signal an error on the stream.
|
|
189
|
+
*/
|
|
190
|
+
public fail(error: Error): void {
|
|
191
|
+
this.error = error;
|
|
192
|
+
this.done = true;
|
|
193
|
+
if (this.resolve) {
|
|
194
|
+
this.resolve();
|
|
195
|
+
this.resolve = null;
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
* End the stream gracefully.
|
|
201
|
+
*/
|
|
202
|
+
public end(): void {
|
|
203
|
+
this.done = true;
|
|
204
|
+
if (this.resolve) {
|
|
205
|
+
this.resolve();
|
|
206
|
+
this.resolve = null;
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
/**
|
|
211
|
+
* Subscribe to new events as they arrive.
|
|
212
|
+
*/
|
|
213
|
+
public subscribe(listener: (data: T) => void): () => void {
|
|
214
|
+
this.listeners.push(listener);
|
|
215
|
+
return () => {
|
|
216
|
+
const idx = this.listeners.indexOf(listener);
|
|
217
|
+
if (idx >= 0) this.listeners.splice(idx, 1);
|
|
218
|
+
};
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
async *[Symbol.asyncIterator](): AsyncIterator<T> {
|
|
222
|
+
while (true) {
|
|
223
|
+
while (this.queue.length > 0) {
|
|
224
|
+
yield this.queue.shift()!;
|
|
225
|
+
}
|
|
226
|
+
if (this.error) {
|
|
227
|
+
throw this.error;
|
|
228
|
+
}
|
|
229
|
+
if (this.done) {
|
|
230
|
+
return;
|
|
231
|
+
}
|
|
232
|
+
await new Promise<void>((r) => {
|
|
233
|
+
this.resolve = r;
|
|
234
|
+
});
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
// ----------------------------------------------------------------------------------------------------------
|
|
240
|
+
|
|
241
|
+
/**
|
|
242
|
+
* Response wrapper for SSE fetch requests.
|
|
243
|
+
*
|
|
244
|
+
* Wraps a standard `Response` and parses the `text/event-stream` body
|
|
245
|
+
* into an async iterable of typed events.
|
|
246
|
+
*/
|
|
247
|
+
export class SseFetchResponse<T> implements AsyncIterable<T> {
|
|
248
|
+
public readonly response: Response;
|
|
249
|
+
|
|
250
|
+
constructor(response: Response) {
|
|
251
|
+
this.response = response;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
/**
|
|
255
|
+
* HTTP status code of the response.
|
|
256
|
+
*/
|
|
257
|
+
public get status(): number {
|
|
258
|
+
return this.response.status;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
/**
|
|
262
|
+
* HTTP status text of the response.
|
|
263
|
+
*/
|
|
264
|
+
public get statusText(): string {
|
|
265
|
+
return this.response.statusText;
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
/**
|
|
269
|
+
* Response headers.
|
|
270
|
+
*/
|
|
271
|
+
public get headers(): Headers {
|
|
272
|
+
return this.response.headers;
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
async *[Symbol.asyncIterator](): AsyncIterator<T> {
|
|
276
|
+
const reader = this.response.body?.getReader();
|
|
277
|
+
if (!reader) return;
|
|
278
|
+
|
|
279
|
+
const decoder = new TextDecoder();
|
|
280
|
+
let buffer = "";
|
|
281
|
+
|
|
282
|
+
try {
|
|
283
|
+
while (true) {
|
|
284
|
+
const { done, value } = await reader.read();
|
|
285
|
+
if (done) break;
|
|
286
|
+
|
|
287
|
+
buffer += decoder.decode(value, { stream: true });
|
|
288
|
+
|
|
289
|
+
const lines = buffer.split("\n");
|
|
290
|
+
buffer = lines.pop() ?? "";
|
|
291
|
+
|
|
292
|
+
for (const line of lines) {
|
|
293
|
+
if (line.startsWith("data: ")) {
|
|
294
|
+
const data = line.slice(6);
|
|
295
|
+
try {
|
|
296
|
+
yield JSON.parse(data) as T;
|
|
297
|
+
} catch {
|
|
298
|
+
// skip non-JSON data lines
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
// process remaining buffer
|
|
305
|
+
if (buffer.startsWith("data: ")) {
|
|
306
|
+
const data = buffer.slice(6);
|
|
307
|
+
try {
|
|
308
|
+
yield JSON.parse(data) as T;
|
|
309
|
+
} catch {
|
|
310
|
+
// skip non-JSON data lines
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
} finally {
|
|
314
|
+
reader.releaseLock();
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
// ----------------------------------------------------------------------------------------------------------
|
|
320
|
+
|
|
321
|
+
/**
|
|
322
|
+
* Creates a Server-Sent Events (SSE) primitive for streaming typed events to clients.
|
|
323
|
+
*
|
|
324
|
+
* SSE endpoints provide a unidirectional stream from server to client over HTTP,
|
|
325
|
+
* with full type safety for event data. The handler receives `emit()` and `close()`
|
|
326
|
+
* functions to control the stream.
|
|
327
|
+
*
|
|
328
|
+
* **Key Features**
|
|
329
|
+
* - Full TypeScript inference for event data types
|
|
330
|
+
* - Automatic schema validation using TypeBox
|
|
331
|
+
* - Convention-based URL generation with customizable paths
|
|
332
|
+
* - Direct invocation (`run()`) returns an `SseStream` async iterable
|
|
333
|
+
* - HTTP requests (`fetch()`) returns an `SseFetchResponse` async iterable
|
|
334
|
+
* - Built-in `text/event-stream` content-type handling
|
|
335
|
+
*
|
|
336
|
+
* **URL Generation**
|
|
337
|
+
*
|
|
338
|
+
* All `$sse` paths are automatically prefixed with `/api`.
|
|
339
|
+
*
|
|
340
|
+
* ```ts
|
|
341
|
+
* $sse({ path: "/events" }) // POST /api/events
|
|
342
|
+
* $sse({ path: "/feed/:id" }) // POST /api/feed/:id
|
|
343
|
+
* ```
|
|
344
|
+
*
|
|
345
|
+
* The HTTP method is always POST.
|
|
346
|
+
*
|
|
347
|
+
* @example
|
|
348
|
+
* ```ts
|
|
349
|
+
* class NotificationController {
|
|
350
|
+
* events = $sse({
|
|
351
|
+
* schema: {
|
|
352
|
+
* data: t.object({
|
|
353
|
+
* type: t.text(),
|
|
354
|
+
* message: t.text(),
|
|
355
|
+
* }),
|
|
356
|
+
* },
|
|
357
|
+
* handler: async ({ emit, close }) => {
|
|
358
|
+
* emit({ type: "welcome", message: "Connected!" });
|
|
359
|
+
* // ... stream events ...
|
|
360
|
+
* close();
|
|
361
|
+
* },
|
|
362
|
+
* });
|
|
363
|
+
* }
|
|
364
|
+
* ```
|
|
365
|
+
*/
|
|
366
|
+
export const $sse = <TConfig extends SseConfigSchema>(
|
|
367
|
+
options: SsePrimitiveOptions<TConfig>,
|
|
368
|
+
): SsePrimitiveFn<TConfig> => {
|
|
369
|
+
const instance = createPrimitive(SsePrimitive<TConfig>, options);
|
|
370
|
+
const fn = (
|
|
371
|
+
config?: SseRequestEntry<TConfig>,
|
|
372
|
+
): SseStream<SseEventData<TConfig>> => {
|
|
373
|
+
return instance.run(config);
|
|
374
|
+
};
|
|
375
|
+
Object.defineProperty(fn, "name", {
|
|
376
|
+
get(): string {
|
|
377
|
+
return instance.options.name || instance.config.propertyKey;
|
|
378
|
+
},
|
|
379
|
+
});
|
|
380
|
+
return Object.setPrototypeOf(fn, instance) as SsePrimitiveFn<TConfig>;
|
|
381
|
+
};
|
|
382
|
+
|
|
383
|
+
// ----------------------------------------------------------------------------------------------------------
|
|
384
|
+
|
|
385
|
+
/**
|
|
386
|
+
* The SSE primitive class extending PipelinePrimitive.
|
|
387
|
+
*
|
|
388
|
+
* Registers a POST route that streams `text/event-stream` responses.
|
|
389
|
+
* Supports direct invocation via `run()` and HTTP via `fetch()`.
|
|
390
|
+
*/
|
|
391
|
+
export class SsePrimitive<
|
|
392
|
+
TConfig extends SseConfigSchema,
|
|
393
|
+
> extends PipelinePrimitive<SsePrimitiveOptions<TConfig>> {
|
|
394
|
+
protected readonly log = $logger();
|
|
395
|
+
protected readonly settings = $use(serverApiOptions);
|
|
396
|
+
protected readonly serverProvider = $inject(ServerProvider);
|
|
397
|
+
protected readonly serverRouterProvider = $inject(ServerRouterProvider);
|
|
398
|
+
|
|
399
|
+
protected onInit() {
|
|
400
|
+
if (this.options.disabled) {
|
|
401
|
+
this.log.debug(
|
|
402
|
+
`SSE endpoint '${this.name}' is disabled. It won't be available in the API.`,
|
|
403
|
+
);
|
|
404
|
+
return;
|
|
405
|
+
}
|
|
406
|
+
this.serverRouterProvider.createRoute({
|
|
407
|
+
method: this.method,
|
|
408
|
+
path: `${this.prefix}${this.path}`,
|
|
409
|
+
schema: this.requestConfigSchema,
|
|
410
|
+
handler: (request: ServerRequest) => this.httpHandler(request),
|
|
411
|
+
});
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
/**
|
|
415
|
+
* Returns the /api prefix.
|
|
416
|
+
*/
|
|
417
|
+
public get prefix(): string {
|
|
418
|
+
return this.settings.prefix;
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
/**
|
|
422
|
+
* Returns the name of the SSE endpoint.
|
|
423
|
+
*/
|
|
424
|
+
public get name(): string {
|
|
425
|
+
return this.options.name || this.config.propertyKey;
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
/**
|
|
429
|
+
* Returns the group of the SSE endpoint.
|
|
430
|
+
*/
|
|
431
|
+
public get group(): string {
|
|
432
|
+
return this.options.group || this.config.service.name;
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
/**
|
|
436
|
+
* Returns the HTTP method. SSE always uses POST.
|
|
437
|
+
*/
|
|
438
|
+
public get method(): RouteMethod {
|
|
439
|
+
return "POST";
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
/**
|
|
443
|
+
* Returns the path of the SSE endpoint.
|
|
444
|
+
*/
|
|
445
|
+
public get path(): string {
|
|
446
|
+
if (this.options.path) {
|
|
447
|
+
return this.options.path;
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
let path = `/${this.name}`;
|
|
451
|
+
|
|
452
|
+
if (this.options.schema?.params) {
|
|
453
|
+
for (const [key] of Object.entries(
|
|
454
|
+
this.options.schema.params.properties,
|
|
455
|
+
)) {
|
|
456
|
+
path += `/:${key}`;
|
|
457
|
+
}
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
return path;
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
/**
|
|
464
|
+
* Returns the schema configuration.
|
|
465
|
+
*/
|
|
466
|
+
public get schema(): TConfig | undefined {
|
|
467
|
+
return this.options.schema;
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
/**
|
|
471
|
+
* Constructs a RequestConfigSchema from the SSE config for route registration.
|
|
472
|
+
*/
|
|
473
|
+
protected get requestConfigSchema(): RequestConfigSchema | undefined {
|
|
474
|
+
if (!this.options.schema) return undefined;
|
|
475
|
+
return {
|
|
476
|
+
body: this.options.schema.body,
|
|
477
|
+
params: this.options.schema.params,
|
|
478
|
+
query: this.options.schema.query,
|
|
479
|
+
headers: this.options.schema.headers,
|
|
480
|
+
};
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
/**
|
|
484
|
+
* Call the SSE handler directly and return a typed async iterable stream.
|
|
485
|
+
* There is no HTTP layer involved.
|
|
486
|
+
*/
|
|
487
|
+
public run(
|
|
488
|
+
config?: SseRequestEntry<TConfig>,
|
|
489
|
+
): SseStream<SseEventData<TConfig>> {
|
|
490
|
+
if (this.options.disabled) {
|
|
491
|
+
throw new AlephaError(`SSE endpoint '${this.name}' is disabled.`);
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
const stream = new SseStream<SseEventData<TConfig>>();
|
|
495
|
+
const {
|
|
496
|
+
body,
|
|
497
|
+
params = {},
|
|
498
|
+
query = {},
|
|
499
|
+
headers = {},
|
|
500
|
+
} = (config ?? {}) as SseRequestEntryContainer<SseConfigSchema>;
|
|
501
|
+
|
|
502
|
+
const url = new URL(`http://localhost${this.path ?? ""}`);
|
|
503
|
+
const reply = new ServerReply();
|
|
504
|
+
|
|
505
|
+
const serverRequest: Partial<ServerRequest> = {
|
|
506
|
+
method: this.method,
|
|
507
|
+
url,
|
|
508
|
+
body,
|
|
509
|
+
params,
|
|
510
|
+
query,
|
|
511
|
+
headers,
|
|
512
|
+
reply,
|
|
513
|
+
metadata: {
|
|
514
|
+
routePath: `${this.prefix}${this.path}`,
|
|
515
|
+
routeMethod: this.method,
|
|
516
|
+
},
|
|
517
|
+
};
|
|
518
|
+
|
|
519
|
+
const context: SseHandlerContext<TConfig> = {
|
|
520
|
+
body: body as any,
|
|
521
|
+
params: params as any,
|
|
522
|
+
query: query as any,
|
|
523
|
+
headers: headers as any,
|
|
524
|
+
request: serverRequest as ServerRequest,
|
|
525
|
+
emit: (data: SseEventData<TConfig>) => stream.push(data),
|
|
526
|
+
close: () => stream.end(),
|
|
527
|
+
};
|
|
528
|
+
|
|
529
|
+
const handlerFn = this.handler.run.bind(this.handler);
|
|
530
|
+
|
|
531
|
+
// run handler async, errors propagate to stream
|
|
532
|
+
Promise.resolve()
|
|
533
|
+
.then(() => handlerFn(context))
|
|
534
|
+
.then(() => {
|
|
535
|
+
// auto-close stream when handler finishes without calling close()
|
|
536
|
+
stream.end();
|
|
537
|
+
})
|
|
538
|
+
.catch((error: Error) => {
|
|
539
|
+
stream.fail(error);
|
|
540
|
+
});
|
|
541
|
+
|
|
542
|
+
return stream;
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
/**
|
|
546
|
+
* Works like `run`, but always fetches (http request) the route.
|
|
547
|
+
* Returns an `SseFetchResponse` that can be async-iterated for typed events.
|
|
548
|
+
*/
|
|
549
|
+
public async fetch(
|
|
550
|
+
config?: SseRequestEntry<TConfig>,
|
|
551
|
+
): Promise<SseFetchResponse<SseEventData<TConfig>>> {
|
|
552
|
+
const host = this.serverProvider.hostname;
|
|
553
|
+
const url = this.buildFetchUrl(host, config);
|
|
554
|
+
const { body, headers: configHeaders = {} } = (config ??
|
|
555
|
+
{}) as SseRequestEntryContainer<SseConfigSchema>;
|
|
556
|
+
|
|
557
|
+
const requestInit: RequestInit = {
|
|
558
|
+
method: "POST",
|
|
559
|
+
headers: {
|
|
560
|
+
accept: "text/event-stream",
|
|
561
|
+
"content-type": "application/json",
|
|
562
|
+
...configHeaders,
|
|
563
|
+
},
|
|
564
|
+
};
|
|
565
|
+
|
|
566
|
+
if (body !== undefined) {
|
|
567
|
+
requestInit.body = JSON.stringify(body);
|
|
568
|
+
}
|
|
569
|
+
|
|
570
|
+
const response = await fetch(url, requestInit);
|
|
571
|
+
return new SseFetchResponse<SseEventData<TConfig>>(response);
|
|
572
|
+
}
|
|
573
|
+
|
|
574
|
+
/**
|
|
575
|
+
* HTTP handler for the registered route.
|
|
576
|
+
* Returns a ReadableStream with SSE-formatted events.
|
|
577
|
+
*/
|
|
578
|
+
protected httpHandler(request: ServerRequest): ReadableStream {
|
|
579
|
+
const reply = request.reply;
|
|
580
|
+
reply.setHeader("content-type", "text/event-stream");
|
|
581
|
+
reply.setHeader("cache-control", "no-cache");
|
|
582
|
+
reply.setHeader("connection", "keep-alive");
|
|
583
|
+
|
|
584
|
+
const handlerFn = this.handler.run.bind(this.handler);
|
|
585
|
+
|
|
586
|
+
return new ReadableStream({
|
|
587
|
+
start: (controller) => {
|
|
588
|
+
const encoder = new TextEncoder();
|
|
589
|
+
const context: SseHandlerContext<TConfig> = {
|
|
590
|
+
body: request.body as any,
|
|
591
|
+
params: request.params as any,
|
|
592
|
+
query: request.query as any,
|
|
593
|
+
headers: request.headers as any,
|
|
594
|
+
request,
|
|
595
|
+
emit: (data: SseEventData<TConfig>) => {
|
|
596
|
+
try {
|
|
597
|
+
controller.enqueue(
|
|
598
|
+
encoder.encode(`data: ${JSON.stringify(data)}\n\n`),
|
|
599
|
+
);
|
|
600
|
+
} catch {
|
|
601
|
+
// stream may already be closed
|
|
602
|
+
}
|
|
603
|
+
},
|
|
604
|
+
close: () => {
|
|
605
|
+
try {
|
|
606
|
+
controller.close();
|
|
607
|
+
} catch {
|
|
608
|
+
// stream may already be closed
|
|
609
|
+
}
|
|
610
|
+
},
|
|
611
|
+
};
|
|
612
|
+
|
|
613
|
+
Promise.resolve()
|
|
614
|
+
.then(() => handlerFn(context))
|
|
615
|
+
.then(() => {
|
|
616
|
+
try {
|
|
617
|
+
controller.close();
|
|
618
|
+
} catch {
|
|
619
|
+
// already closed
|
|
620
|
+
}
|
|
621
|
+
})
|
|
622
|
+
.catch((error: Error) => {
|
|
623
|
+
try {
|
|
624
|
+
controller.error(error);
|
|
625
|
+
} catch {
|
|
626
|
+
// already closed
|
|
627
|
+
}
|
|
628
|
+
});
|
|
629
|
+
},
|
|
630
|
+
});
|
|
631
|
+
}
|
|
632
|
+
|
|
633
|
+
/**
|
|
634
|
+
* Build the fetch URL with path variables and query params.
|
|
635
|
+
*/
|
|
636
|
+
protected buildFetchUrl(
|
|
637
|
+
host: string,
|
|
638
|
+
config?: SseRequestEntry<TConfig>,
|
|
639
|
+
): string {
|
|
640
|
+
let url = `${host}${this.prefix}${this.path}`;
|
|
641
|
+
|
|
642
|
+
const { params, query } = (config ??
|
|
643
|
+
{}) as SseRequestEntryContainer<SseConfigSchema>;
|
|
644
|
+
|
|
645
|
+
if (params && typeof params === "object") {
|
|
646
|
+
for (const [key, value] of Object.entries(params)) {
|
|
647
|
+
url = url.replace(`:${key}`, String(value));
|
|
648
|
+
}
|
|
649
|
+
}
|
|
650
|
+
|
|
651
|
+
if (query && typeof query === "object") {
|
|
652
|
+
const searchParams = new URLSearchParams();
|
|
653
|
+
for (const [key, value] of Object.entries(query)) {
|
|
654
|
+
if (value !== undefined) {
|
|
655
|
+
searchParams.set(key, String(value));
|
|
656
|
+
}
|
|
657
|
+
}
|
|
658
|
+
const qs = searchParams.toString();
|
|
659
|
+
if (qs) {
|
|
660
|
+
url += `?${qs}`;
|
|
661
|
+
}
|
|
662
|
+
}
|
|
663
|
+
|
|
664
|
+
return url;
|
|
665
|
+
}
|
|
666
|
+
}
|
|
667
|
+
|
|
668
|
+
// ----------------------------------------------------------------------------------------------------------
|
|
669
|
+
|
|
670
|
+
/**
|
|
671
|
+
* Combined callable + SsePrimitive interface.
|
|
672
|
+
*/
|
|
673
|
+
export interface SsePrimitiveFn<TConfig extends SseConfigSchema>
|
|
674
|
+
extends SsePrimitive<TConfig> {
|
|
675
|
+
(config?: SseRequestEntry<TConfig>): SseStream<SseEventData<TConfig>>;
|
|
676
|
+
}
|
|
677
|
+
|
|
678
|
+
$sse[KIND] = SsePrimitive;
|
|
679
|
+
|
|
680
|
+
// ----------------------------------------------------------------------------------------------------------
|
|
681
|
+
|
|
682
|
+
/**
|
|
683
|
+
* Infer the event data type from an SSE config schema.
|
|
684
|
+
*/
|
|
685
|
+
export type SseEventData<TConfig extends SseConfigSchema> =
|
|
686
|
+
TConfig["data"] extends TSchema ? Static<TConfig["data"]> : any;
|
|
687
|
+
|
|
688
|
+
/**
|
|
689
|
+
* Request entry type for SSE endpoints (body, params, query, headers).
|
|
690
|
+
*/
|
|
691
|
+
export type SseRequestEntry<
|
|
692
|
+
TConfig extends SseConfigSchema,
|
|
693
|
+
T = SseRequestEntryContainer<TConfig>,
|
|
694
|
+
> = {
|
|
695
|
+
[K in keyof T as T[K] extends undefined ? never : K]: T[K];
|
|
696
|
+
};
|
|
697
|
+
|
|
698
|
+
/**
|
|
699
|
+
* Full container type for SSE request entries.
|
|
700
|
+
*/
|
|
701
|
+
export type SseRequestEntryContainer<TConfig extends SseConfigSchema> = {
|
|
702
|
+
body: TConfig["body"] extends TObject ? Static<TConfig["body"]> : undefined;
|
|
703
|
+
|
|
704
|
+
params: TConfig["params"] extends TObject
|
|
705
|
+
? Static<TConfig["params"]>
|
|
706
|
+
: undefined;
|
|
707
|
+
|
|
708
|
+
headers?: TConfig["headers"] extends TObject
|
|
709
|
+
? Static<TConfig["headers"]>
|
|
710
|
+
: Record<string, string>;
|
|
711
|
+
|
|
712
|
+
query?: TConfig["query"] extends TObject
|
|
713
|
+
? Partial<Static<TConfig["query"]>>
|
|
714
|
+
: Record<string, string>;
|
|
715
|
+
};
|