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 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":["envSchema","NodeWebStream","NodeStream","envSchema","ReadableStream","HttpError","isMultipart"],"sources":["../../../src/server/core/helpers/isMultipart.ts","../../../src/server/core/helpers/ServerReply.ts","../../../src/server/core/errors/HttpError.ts","../../../src/server/core/errors/ValidationError.ts","../../../src/server/core/services/UserAgentParser.ts","../../../src/server/core/services/ServerRequestParser.ts","../../../src/server/core/providers/ServerTimingProvider.ts","../../../src/server/core/providers/ServerRouterProvider.ts","../../../src/server/core/providers/ServerProvider.ts","../../../src/server/core/schemas/errorSchema.ts","../../../src/server/core/services/HttpClient.ts","../../../src/server/core/primitives/$action.ts","../../../src/server/core/primitives/$middleware.ts","../../../src/server/core/primitives/$route.ts","../../../src/server/core/providers/BunHttpServerProvider.ts","../../../src/server/core/providers/NodeHttpServerProvider.ts","../../../src/server/core/providers/ServerBodyParserProvider.ts","../../../src/server/core/providers/ServerCompressProvider.ts","../../../src/server/core/providers/ServerHelmetProvider.ts","../../../src/server/core/providers/ServerLoggerProvider.ts","../../../src/server/core/providers/ServerMultipartProvider.ts","../../../src/server/core/providers/ServerNotReadyProvider.ts","../../../src/server/core/constants/routeMethods.ts","../../../src/server/core/errors/BadRequestError.ts","../../../src/server/core/errors/ConflictError.ts","../../../src/server/core/errors/ForbiddenError.ts","../../../src/server/core/errors/NotFoundError.ts","../../../src/server/core/errors/UnauthorizedError.ts","../../../src/server/core/schemas/okSchema.ts","../../../src/server/core/primitives/$circuit.ts","../../../src/server/core/index.ts"],"sourcesContent":["import type { RequestConfigSchema } from \"../interfaces/ServerRequest.ts\";\n\n/**\n * Checks if the route has multipart/form-data request body.\n */\nexport const isMultipart = (options: {\n schema?: RequestConfigSchema;\n contentType?: string;\n requestBodyType?: string;\n}): boolean => {\n if (\n options.contentType === \"multipart/form-data\" ||\n options.requestBodyType === \"multipart/form-data\"\n ) {\n return true;\n }\n\n if (options.schema?.body && \"properties\" in options.schema.body) {\n const properties: Record<string, any> = options.schema.body.properties;\n for (const key in properties) {\n if (properties[key].format === \"binary\") {\n return true;\n }\n }\n }\n\n return false;\n};\n","/**\n * Helper for building server replies.\n */\nexport class ServerReply {\n // TODO: make it private\n public headers: Record<string, string> & {\n \"set-cookie\"?: string[];\n } = {};\n\n public status?: number; // default 200, or 204 (no content)\n\n public body?: any;\n\n /**\n * Redirect to a given URL with optional status code (default 302).\n */\n public redirect(url: string, status: number = 302): void {\n this.status = status;\n this.headers.location = url;\n }\n\n // TODO: check if status / header is already set and throw an error if so (for allow to override with force flag)\n\n /**\n * Set the response status code.\n */\n public setStatus(status: number): this {\n this.status = status;\n return this;\n }\n\n /**\n * Set a response header.\n */\n public setHeader(name: string, value: string): this {\n this.headers[name.toLowerCase()] = value;\n return this;\n }\n\n /**\n * Set the response body.\n */\n public setBody(body: any): this {\n this.body = body;\n return this;\n }\n}\n","import { AlephaError } from \"alepha\";\nimport type { ErrorSchema } from \"../schemas/errorSchema.ts\";\n\nexport const isHttpError = (\n error: unknown,\n status?: number,\n): error is HttpErrorLike => {\n const isError =\n !!error &&\n typeof error === \"object\" &&\n \"message\" in error &&\n typeof error.message === \"string\" &&\n \"status\" in error &&\n typeof error.status === \"number\";\n\n if (!isError) {\n return false;\n }\n\n if (status) {\n return (error as HttpErrorLike).status === status;\n }\n\n return true;\n};\n\nexport class HttpError extends AlephaError {\n public name = \"HttpError\";\n\n static is = isHttpError;\n\n static toJSON(error: HttpError): ErrorSchema {\n const json: Record<string, unknown> = {\n error: error.error,\n status: error.status,\n message: error.message,\n };\n\n if (error.details) json.details = error.details;\n if (error.requestId) json.requestId = error.requestId;\n if (error.reason) json.cause = error.reason;\n\n return json as ErrorSchema;\n }\n\n public readonly error: string;\n public readonly status: number;\n\n public readonly requestId?: string;\n public readonly details?: string;\n public readonly reason?: {\n name: string;\n message: string;\n };\n\n constructor(options: Partial<ErrorSchema>, cause?: unknown) {\n super(options.message, {\n cause,\n });\n\n this.status = options.status ?? 500;\n this.details = options.details;\n this.requestId = options.requestId;\n\n if (typeof options.cause === \"object\") {\n this.reason = {\n name: (options.cause as { name: string }).name,\n message: (options.cause as { message: string }).message,\n };\n } else if (cause instanceof Error) {\n this.reason = {\n name: cause.name,\n message: cause.message,\n };\n }\n\n if (this.constructor.name === \"HttpError\") {\n this.error =\n options.error ?? errorNameByStatus[this.status] ?? \"HttpError\";\n } else {\n this.error = this.constructor.name;\n }\n }\n}\n\nexport const errorNameByStatus: Record<number, string> = {\n 400: \"BadRequestError\",\n 401: \"UnauthorizedError\",\n 403: \"ForbiddenError\",\n 404: \"NotFoundError\",\n 405: \"MethodNotAllowedError\",\n 409: \"ConflictError\",\n 410: \"GoneError\",\n 413: \"PayloadTooLargeError\",\n 415: \"UnsupportedMediaTypeError\",\n 429: \"TooManyRequestsError\",\n 500: \"InternalServerError\",\n 501: \"NotImplementedError\",\n 502: \"BadGatewayError\",\n 503: \"ServiceUnavailableError\",\n 504: \"GatewayTimeoutError\",\n};\n\nexport interface HttpErrorLike extends Error {\n status: number;\n}\n","import { HttpError } from \"./HttpError.ts\";\n\nexport class ValidationError extends HttpError {\n constructor(message = \"Validation has failed\", cause?: unknown) {\n super(\n {\n message,\n status: 400,\n },\n cause,\n );\n }\n}\n","export interface UserAgentInfo {\n os:\n | \"Windows\"\n | \"Android\"\n | \"Ubuntu\"\n | \"MacOS\"\n | \"iOS\"\n | \"Linux\"\n | \"FreeBSD\"\n | \"OpenBSD\"\n | \"ChromeOS\"\n | \"BlackBerry\"\n | \"Symbian\"\n | \"Windows Phone\";\n browser:\n | \"Chrome\"\n | \"Firefox\"\n | \"Safari\"\n | \"Edge\"\n | \"Opera\"\n | \"Internet Explorer\"\n | \"Brave\"\n | \"Vivaldi\"\n | \"Samsung Browser\"\n | \"UC Browser\"\n | \"Yandex\";\n device: \"MOBILE\" | \"DESKTOP\" | \"TABLET\";\n}\n\n/**\n * Simple User-Agent parser to detect OS, browser, and device type.\n * This parser is not exhaustive and may not cover all edge cases.\n *\n * Use result for non\n */\nexport class UserAgentParser {\n public parse(userAgent: string = \"\"): UserAgentInfo {\n const ua = userAgent.toLowerCase();\n\n // Default values\n let os: UserAgentInfo[\"os\"] = \"Windows\";\n let browser: UserAgentInfo[\"browser\"] = \"Chrome\";\n let device: UserAgentInfo[\"device\"] = \"DESKTOP\";\n\n // Detect OS - Order matters for specificity\n if (ua.includes(\"windows phone\")) {\n os = \"Windows Phone\";\n } else if (ua.includes(\"windows\")) {\n os = \"Windows\";\n } else if (ua.includes(\"android\")) {\n os = \"Android\";\n } else if (\n ua.includes(\"iphone\") ||\n ua.includes(\"ipad\") ||\n ua.includes(\"ipod\") ||\n (ua.includes(\"ios\") && !ua.includes(\"android\"))\n ) {\n os = \"iOS\";\n } else if (\n ua.includes(\"mac os\") ||\n ua.includes(\"macos\") ||\n ua.includes(\"macintosh\")\n ) {\n os = \"MacOS\";\n } else if (ua.includes(\"cros\") || ua.includes(\"chromeos\")) {\n os = \"ChromeOS\";\n } else if (ua.includes(\"ubuntu\")) {\n os = \"Ubuntu\";\n } else if (ua.includes(\"freebsd\")) {\n os = \"FreeBSD\";\n } else if (ua.includes(\"openbsd\")) {\n os = \"OpenBSD\";\n } else if (ua.includes(\"blackberry\") || ua.includes(\"bb10\")) {\n os = \"BlackBerry\";\n } else if (ua.includes(\"symbian\") || ua.includes(\"symbos\")) {\n os = \"Symbian\";\n } else if (ua.includes(\"linux\") || ua.includes(\"x11\")) {\n os = \"Linux\";\n }\n\n // Detect Browser/browser - Order matters, check most specific first\n if (ua.includes(\"yabrowser\") || ua.includes(\"yandex\")) {\n browser = \"Yandex\";\n } else if (ua.includes(\"brave\")) {\n browser = \"Brave\";\n } else if (ua.includes(\"vivaldi\")) {\n browser = \"Vivaldi\";\n } else if (ua.includes(\"samsungbrowser\") || ua.includes(\"samsung\")) {\n browser = \"Samsung Browser\";\n } else if (ua.includes(\"ucbrowser\") || ua.includes(\"uc browser\")) {\n browser = \"UC Browser\";\n } else if (\n ua.includes(\"opera\") ||\n ua.includes(\"opr/\") ||\n ua.includes(\"opios\")\n ) {\n browser = \"Opera\";\n } else if (\n ua.includes(\"edg/\") ||\n ua.includes(\"edge\") ||\n ua.includes(\"edgios\")\n ) {\n browser = \"Edge\";\n } else if (ua.includes(\"firefox\") && !ua.includes(\"seamonkey\")) {\n browser = \"Firefox\";\n } else if (ua.includes(\"trident\") || ua.includes(\"msie\")) {\n browser = \"Internet Explorer\";\n } else if (\n ua.includes(\"safari\") &&\n !ua.includes(\"chrome\") &&\n !ua.includes(\"chromium\")\n ) {\n browser = \"Safari\";\n } else if (\n ua.includes(\"chrome\") ||\n ua.includes(\"chromium\") ||\n ua.includes(\"crios\")\n ) {\n browser = \"Chrome\";\n }\n\n // Detect Device Type\n const mobileKeywords = [\n \"mobile\",\n \"android\",\n \"iphone\",\n \"ipod\",\n \"blackberry\",\n \"windows phone\",\n \"opera mini\",\n \"iemobile\",\n \"mobile safari\",\n \"nokia\",\n \"symbian\",\n ];\n\n const tabletKeywords = [\n \"ipad\",\n \"tablet\",\n \"kindle\",\n \"silk\",\n \"gt-p\",\n \"sm-t\",\n \"nexus 7\",\n \"nexus 10\",\n ];\n\n const isTablet = tabletKeywords.some((keyword) => ua.includes(keyword));\n const isMobile = mobileKeywords.some((keyword) => ua.includes(keyword));\n\n if (isTablet) {\n device = \"TABLET\";\n } else if (isMobile) {\n device = \"MOBILE\";\n } else {\n device = \"DESKTOP\";\n }\n\n return { os, browser, device };\n }\n}\n","import { $env, $inject, Alepha, t } from \"alepha\";\nimport { CryptoProvider } from \"alepha/crypto\";\nimport { ServerReply } from \"../helpers/ServerReply.ts\";\nimport type {\n RequestGeo,\n RequestReferer,\n ServerRequest,\n ServerRequestData,\n} from \"../interfaces/ServerRequest.ts\";\nimport { UserAgentParser } from \"./UserAgentParser.ts\";\n\nconst envSchema = t.object({\n /**\n * Trust proxy headers (X-Forwarded-For, X-Real-IP) for client IP resolution.\n *\n * Default: true (modern deployments are typically behind a reverse proxy)\n *\n * Set to false only if your server accepts direct connections without a proxy\n * and you want to use the raw connection IP.\n */\n TRUST_PROXY: t.boolean({\n default: true,\n description: \"Trust proxy headers for client IP\",\n }),\n});\n\nexport class ServerRequestParser {\n protected readonly alepha = $inject(Alepha);\n protected readonly userAgentParser = $inject(UserAgentParser);\n protected readonly cryptoProvider = $inject(CryptoProvider);\n protected readonly env = $env(envSchema);\n protected readonly rootURL = new URL(\"http://localhost/\");\n\n public createServerRequest(\n partialRawRequest: Partial<ServerRequestData>,\n ): ServerRequest {\n const rawRequest = {\n method: \"GET\",\n url: this.rootURL,\n headers: {},\n query: {},\n params: {},\n ...partialRawRequest,\n } as ServerRequestData;\n const self = this;\n return {\n method: rawRequest.method,\n url: rawRequest.url,\n raw: rawRequest.raw,\n headers: rawRequest.headers,\n query: rawRequest.query,\n params: rawRequest.params,\n // ---------------------------------------------------------------------------------------------------------------\n // body will be filled by body parser middleware\n body: null,\n // ---------------------------------------------------------------------------------------------------------------\n metadata: {},\n reply: this.alepha.inject(ServerReply, { lifetime: \"transient\" }),\n // ---------------------------------------------------------------------------------------------------------------\n get requestId() {\n return self.getRequestId(rawRequest);\n },\n get ip() {\n return self.getRequestIp(rawRequest);\n },\n get userAgent() {\n return self.getRequestUserAgent(rawRequest);\n },\n get geo() {\n return self.getRequestGeo(rawRequest);\n },\n get isBot() {\n return self.getIsBot(rawRequest);\n },\n get isMobile() {\n return self.getIsMobile(rawRequest);\n },\n get protocol() {\n return self.getProtocol(rawRequest);\n },\n get language() {\n return self.getLanguage(rawRequest);\n },\n get referer() {\n return self.getReferer(rawRequest);\n },\n } as ServerRequest;\n }\n\n public getRequestId(request: ServerRequestData): string {\n return request.headers[\"x-request-id\"] || this.cryptoProvider.randomUUID();\n }\n\n public getRequestUserAgent(request: ServerRequestData) {\n return this.userAgentParser.parse(request.headers[\"user-agent\"]);\n }\n\n public getRequestIp(request: ServerRequestData): string | undefined {\n // Only trust proxy headers when explicitly configured\n if (this.env.TRUST_PROXY) {\n const headers = request.headers;\n\n // X-Forwarded-For: standard proxy header (Cloudflare, Vercel, Nginx, etc.)\n const forwardedFor = headers[\"x-forwarded-for\"];\n if (forwardedFor) {\n return Array.isArray(forwardedFor)\n ? forwardedFor[0]\n : forwardedFor.split(\",\")[0].trim();\n }\n\n // X-Real-IP: alternative proxy header\n const xRealIP = headers[\"x-real-ip\"];\n if (xRealIP) {\n return Array.isArray(xRealIP) ? xRealIP[0] : xRealIP;\n }\n }\n\n // Default: use raw connection IP\n return this.getConnectionIp(request);\n }\n\n protected getConnectionIp(request: ServerRequestData): string | undefined {\n // Get IP from raw connection (Node.js socket)\n const nodeReq = request.raw.node?.req;\n if (nodeReq) {\n return nodeReq.socket?.remoteAddress;\n }\n return undefined;\n }\n\n // ─────────────────────────────────────────────────────────────────────────────\n // Geolocation\n // ─────────────────────────────────────────────────────────────────────────────\n\n public getRequestGeo(request: ServerRequestData): RequestGeo {\n const headers = request.headers;\n\n return {\n // Country: Cloudflare, Vercel, AWS CloudFront\n country:\n headers[\"cf-ipcountry\"] ||\n headers[\"x-vercel-ip-country\"] ||\n headers[\"cloudfront-viewer-country\"],\n\n // City: Cloudflare, Vercel\n city: headers[\"cf-ipcity\"] || headers[\"x-vercel-ip-city\"],\n\n // Region: Cloudflare, Vercel\n region:\n headers[\"cf-region\"] ||\n headers[\"cf-region-code\"] ||\n headers[\"x-vercel-ip-country-region\"],\n\n // Coordinates: Cloudflare, Vercel\n latitude: headers[\"cf-iplatitude\"] || headers[\"x-vercel-ip-latitude\"],\n longitude: headers[\"cf-iplongitude\"] || headers[\"x-vercel-ip-longitude\"],\n };\n }\n\n // ─────────────────────────────────────────────────────────────────────────────\n // Bot detection\n // ─────────────────────────────────────────────────────────────────────────────\n\n protected static readonly BOT_PATTERNS = [\n /bot/i,\n /crawl/i,\n /spider/i,\n /slurp/i,\n /googlebot/i,\n /bingbot/i,\n /yandex/i,\n /baiduspider/i,\n /facebookexternalhit/i,\n /twitterbot/i,\n /linkedinbot/i,\n /whatsapp/i,\n /telegrambot/i,\n /discordbot/i,\n /slackbot/i,\n /applebot/i,\n /duckduckbot/i,\n /semrush/i,\n /ahrefsbot/i,\n /mj12bot/i,\n /dotbot/i,\n /petalbot/i,\n /bytespider/i,\n /gptbot/i,\n /claudebot/i,\n /anthropic/i,\n /curl/i,\n /wget/i,\n /python-requests/i,\n /axios/i,\n /node-fetch/i,\n /go-http-client/i,\n /java\\//i,\n /libwww/i,\n /httpunit/i,\n /nutch/i,\n /phpcrawl/i,\n /biglotron/i,\n /teoma/i,\n /convera/i,\n /gigablast/i,\n /ia_archiver/i,\n /webmon/i,\n /httrack/i,\n /grub\\.org/i,\n /netresearchserver/i,\n /speedy/i,\n /fluffy/i,\n /findlink/i,\n /panscient/i,\n /ips-agent/i,\n /yanga/i,\n /cyberpatrol/i,\n /postrank/i,\n /page2rss/i,\n /linkdex/i,\n /ezooms/i,\n /heritrix/i,\n /findthatfile/i,\n /europarchive\\.org/i,\n /mappydata/i,\n /eright/i,\n /apercite/i,\n /aboundex/i,\n /domaincrawler/i,\n /wbsearchbot/i,\n /summify/i,\n /ccbot/i,\n /edisterbot/i,\n /seznambot/i,\n /ec2linkfinder/i,\n /gslfbot/i,\n /aihitbot/i,\n /intelium_bot/i,\n /yeti/i,\n /retrevopageanalyzer/i,\n /lb-spider/i,\n /sogou/i,\n /lssbot/i,\n /careerbot/i,\n /wotbox/i,\n /wocbot/i,\n /ichiro/i,\n /duckduckgo/i,\n /lssrocketcrawler/i,\n /drupact/i,\n /webcompanycrawler/i,\n /acoonbot/i,\n /openindexspider/i,\n /screaming frog/i,\n /pingdom/i,\n /uptimerobot/i,\n /headlesschrome/i,\n /phantomjs/i,\n /prerender/i,\n /lighthouse/i,\n /pagespeed/i,\n ];\n\n public getIsBot(request: ServerRequestData): boolean {\n const ua = request.headers[\"user-agent\"];\n if (!ua) return false;\n\n return ServerRequestParser.BOT_PATTERNS.some((pattern) => pattern.test(ua));\n }\n\n // ─────────────────────────────────────────────────────────────────────────────\n // Mobile detection\n // ─────────────────────────────────────────────────────────────────────────────\n\n protected static readonly MOBILE_PATTERNS = [\n /android/i,\n /webos/i,\n /iphone/i,\n /ipad/i,\n /ipod/i,\n /blackberry/i,\n /iemobile/i,\n /opera mini/i,\n /mobile/i,\n /tablet/i,\n /kindle/i,\n /silk/i,\n /fennec/i,\n /windows phone/i,\n /windows ce/i,\n /symbian/i,\n /palm/i,\n /webmate/i,\n ];\n\n public getIsMobile(request: ServerRequestData): boolean {\n const ua = request.headers[\"user-agent\"];\n if (!ua) return false;\n\n return ServerRequestParser.MOBILE_PATTERNS.some((pattern) =>\n pattern.test(ua),\n );\n }\n\n // ─────────────────────────────────────────────────────────────────────────────\n // Protocol detection\n // ─────────────────────────────────────────────────────────────────────────────\n\n public getProtocol(request: ServerRequestData): \"http\" | \"https\" {\n // Check proxy headers first\n const forwardedProto = request.headers[\"x-forwarded-proto\"];\n if (forwardedProto) {\n return forwardedProto.toLowerCase() === \"https\" ? \"https\" : \"http\";\n }\n\n // Cloudflare-specific header\n const cfVisitorHeader = request.headers[\"cf-visitor\"];\n if (cfVisitorHeader) {\n try {\n const parsed = JSON.parse(cfVisitorHeader);\n if (parsed.scheme === \"https\") return \"https\";\n } catch {\n // Ignore parse errors\n }\n }\n\n // Check URL scheme\n if (request.url.protocol === \"https:\") {\n return \"https\";\n }\n\n return \"http\";\n }\n\n // ─────────────────────────────────────────────────────────────────────────────\n // Language detection\n // ─────────────────────────────────────────────────────────────────────────────\n\n public getLanguage(request: ServerRequestData): string | undefined {\n const acceptLanguage = request.headers[\"accept-language\"];\n if (!acceptLanguage) return undefined;\n\n // Parse Accept-Language header\n // Format: \"en-US,en;q=0.9,fr;q=0.8\"\n const firstLang = acceptLanguage.split(\",\")[0];\n if (!firstLang) return undefined;\n\n // Remove quality value if present (e.g., \"en;q=0.9\" -> \"en\")\n const lang = firstLang.split(\";\")[0].trim();\n\n return lang || undefined;\n }\n\n // ─────────────────────────────────────────────────────────────────────────────\n // Referer parsing\n // ─────────────────────────────────────────────────────────────────────────────\n\n public getReferer(request: ServerRequestData): RequestReferer | undefined {\n const referer = request.headers.referer || request.headers.referrer;\n if (!referer) return undefined;\n\n try {\n const url = new URL(referer);\n return {\n url: referer,\n hostname: url.hostname,\n pathname: url.pathname,\n };\n } catch {\n // Invalid URL\n return undefined;\n }\n }\n}\n","import { $hook, $inject, Alepha } from \"alepha\";\nimport { $logger } from \"alepha/logger\";\n\ntype TimingMap = Record<string, [number, number]>;\n\nexport class ServerTimingProvider {\n protected readonly log = $logger();\n protected readonly alepha = $inject(Alepha);\n\n public options = {\n prefix: this.alepha.env.APP_NAME\n ? `${this.alepha.env.APP_NAME.toLowerCase()}-`\n : \"\",\n disabled: this.alepha.isProduction(),\n };\n\n public readonly onRequest = $hook({\n priority: \"first\",\n on: \"server:onRequest\",\n handler: ({ request }) => {\n if (this.options.disabled) {\n return;\n }\n\n request.metadata.timing = {};\n request.metadata.timing[this.handlerName] = [Date.now()];\n },\n });\n\n public readonly onResponse = $hook({\n priority: \"last\",\n on: \"server:onResponse\",\n handler: ({ request }) => {\n if (this.options.disabled) {\n return;\n }\n\n if (request.metadata.timing) {\n this.setDuration(this.handlerName, request.metadata.timing);\n\n let timingHeader = \"\";\n\n for (const [name, [start, duration]] of Object.entries(\n request.metadata.timing as TimingMap,\n )) {\n if (typeof start !== \"number\" || typeof duration !== \"number\") {\n this.log.warn(\n `Invalid timing for '${name}': [${start}, ${duration}]`,\n );\n continue;\n }\n\n const formattedName =\n this.options.prefix + name.replace(/[^a-zA-Z0-9-]/g, \"-\");\n timingHeader += `${formattedName};dur=${duration}, `;\n }\n\n if (request.reply.headers[\"Server-Timing\"]) {\n request.reply.headers[\"Server-Timing\"] += `, ${timingHeader}`;\n } else {\n request.reply.headers[\"Server-Timing\"] = timingHeader;\n }\n }\n },\n });\n\n protected get handlerName() {\n return `request`;\n }\n\n public beginTiming(name: string): void {\n if (this.options.disabled) {\n return;\n }\n\n const request = this.alepha.store.get(\"alepha.http.request\");\n if (!request) {\n return;\n }\n\n request.metadata ??= {};\n request.metadata.timing ??= {};\n request.metadata.timing[name] = [Date.now()];\n }\n\n public endTiming(name: string): void {\n if (this.options.disabled) {\n return;\n }\n\n const request = this.alepha.store.get(\"alepha.http.request\");\n if (!request) {\n return;\n }\n\n if (!request.metadata?.timing || !(name in request.metadata.timing)) {\n this.log.warn(`No timing found for '${name}'.`);\n return;\n }\n\n const start = request.metadata.timing[name]?.[0];\n if (typeof start !== \"number\") {\n this.log.warn(`Invalid timing start for '${name}': ${start}`);\n return;\n }\n\n this.setDuration(name, request.metadata.timing);\n }\n\n protected setDuration(name: string, timing: TimingMap): void {\n timing[name] = [timing[name][0], Date.now() - timing[name][0]];\n }\n}\n","import { randomUUID } from \"node:crypto\";\nimport { Readable as NodeStream } from \"node:stream\";\nimport { ReadableStream as NodeWebStream } from \"node:stream/web\";\nimport {\n $inject,\n Alepha,\n isFileLike,\n isTypeFile,\n type Middleware,\n PipelineHandler,\n t,\n} from \"alepha\";\nimport { $logger } from \"alepha/logger\";\nimport { RouterProvider } from \"alepha/router\";\nimport type { RouteMethod } from \"../constants/routeMethods.ts\";\nimport { errorNameByStatus, HttpError } from \"../errors/HttpError.ts\";\nimport { ValidationError } from \"../errors/ValidationError.ts\";\nimport type { ServerReply } from \"../helpers/ServerReply.ts\";\nimport type {\n RequestConfigSchema,\n ResponseKind,\n ServerRequest,\n ServerRequestConfig,\n ServerRoute,\n ServerRouteInput,\n ServerRouteMatcher,\n} from \"../interfaces/ServerRequest.ts\";\nimport { ServerRequestParser } from \"../services/ServerRequestParser.ts\";\nimport { ServerTimingProvider } from \"./ServerTimingProvider.ts\";\n\n/**\n * Main router for all routes server side.\n *\n * Reminder:\n * - $route => generic route\n * - $action => action route (for API calls)\n * - $page => React route (for React SSR)\n */\nexport class ServerRouterProvider extends RouterProvider<ServerRouteMatcher> {\n protected readonly log = $logger();\n protected readonly alepha = $inject(Alepha);\n protected readonly routes: ServerRoute[] = [];\n protected readonly serverTimingProvider = $inject(ServerTimingProvider);\n protected readonly serverRequestParser = $inject(ServerRequestParser);\n protected readonly queryKeysCache = new WeakMap<object, string[]>();\n protected readonly globalMiddlewareRegistry: GlobalMiddlewareEntry[] = [];\n\n /**\n * Get cached keys for a query schema, computing them lazily on first access.\n */\n protected getQuerySchemaKeys(schema: { properties: object }): string[] {\n let keys = this.queryKeysCache.get(schema.properties);\n if (!keys) {\n keys = Object.keys(schema.properties);\n this.queryKeysCache.set(schema.properties, keys);\n }\n return keys;\n }\n\n public pushMiddleware(\n pattern: string,\n middleware: Middleware[],\n options?: PushMiddlewareOptions,\n ): void {\n this.globalMiddlewareRegistry.push({ pattern, middleware, ...options });\n for (const route of this.getRoutes(pattern)) {\n if (this.matchesMiddlewareFilter(route, options)) {\n route.handler.use(...middleware);\n }\n }\n }\n\n /**\n * Check if a route passes the middleware filter options (method, exclude).\n */\n protected matchesMiddlewareFilter(\n route: ServerRoute,\n options?: PushMiddlewareOptions,\n ): boolean {\n if (options?.method) {\n const methods = Array.isArray(options.method)\n ? options.method\n : [options.method];\n if (\n route.method &&\n !methods.includes(route.method.toUpperCase() as RouteMethod)\n ) {\n return false;\n }\n }\n if (options?.exclude) {\n for (const exclude of options.exclude) {\n if (route.path === exclude || route.path.startsWith(`${exclude}/`)) {\n return false;\n }\n }\n }\n return true;\n }\n\n /**\n * Check if a route path matches a pattern.\n * Wildcard `*` at end = prefix match, otherwise exact match.\n */\n protected matchesPattern(routePath: string, pattern: string): boolean {\n if (pattern.endsWith(\"*\")) {\n return routePath.startsWith(pattern.slice(0, -1));\n }\n return routePath === pattern;\n }\n\n /**\n * Get all registered routes, optionally filtered by a pattern.\n *\n * Pattern accept simple wildcard '*' at the end.\n * Example: '/api/*' will match all routes starting with '/api/' but '/api/' will match only that exact route.\n */\n public getRoutes(pattern?: string): ServerRoute[] {\n if (pattern) {\n return this.routes.filter((route) =>\n this.matchesPattern(route.path, pattern),\n );\n }\n return this.routes;\n }\n\n /**\n * Create a new server route.\n *\n * Accepts both `PipelineHandler` and plain handler functions.\n * Plain functions are auto-wrapped in `PipelineHandler`.\n */\n public createRoute<TConfig extends RequestConfigSchema = RequestConfigSchema>(\n input: ServerRouteInput<TConfig>,\n ): void {\n // Auto-wrap plain function handlers in PipelineHandler\n if (!(input.handler instanceof PipelineHandler)) {\n (input as any).handler = new PipelineHandler(input.handler);\n }\n\n const route = input as unknown as ServerRoute<TConfig>;\n route.method ??= \"GET\";\n route.method = route.method.toUpperCase() as RouteMethod;\n\n this.routes.push(route);\n\n for (const entry of this.globalMiddlewareRegistry) {\n if (this.matchesPattern(route.path, entry.pattern)) {\n if (this.matchesMiddlewareFilter(route, entry)) {\n route.handler.use(...entry.middleware);\n }\n }\n }\n\n const path = `/${route.method}/${route.path}`.replace(/\\/+/g, \"/\");\n const responseKind = this.getResponseType(route.schema);\n\n this.log.trace(`Create route ${path}`);\n\n this.push({\n path,\n handler: (rawRequest) => {\n const request =\n this.serverRequestParser.createServerRequest(rawRequest);\n\n return this.alepha.context.run(\n () => this.processRequest(request, route, responseKind),\n { context: this.getContextId(rawRequest.headers) },\n );\n },\n });\n }\n\n /**\n * Get or generate a context ID from request headers.\n *\n * It first checks for common headers like 'x-request-id' or 'x-correlation-id' that may be set by proxies or clients.\n * If none of these headers are present, it generates a new UUID to ensure uniqueness.\n *\n * Note: In production environments, it's recommended to have a proxy (e.g. Nginx, Cloudflare) that sets a consistent request ID header for better traceability across services.\n */\n protected getContextId(headers: Record<string, string>): string {\n // note: we trust these headers as all our environments are behind a proxy\n return (\n headers[\"x-request-id\"] || headers[\"x-correlation-id\"] || randomUUID()\n );\n }\n\n /**\n * Process an incoming request through the full lifecycle:\n * onRequest → handler → onSend → response → onResponse\n */\n protected async processRequest(\n request: ServerRequest,\n route: ServerRoute,\n responseKind: ResponseKind,\n ) {\n try {\n await this.runRouteHandler(route, request, responseKind);\n } catch (error) {\n await this.errorHandler(route, request, error as Error);\n }\n\n const payload = { request, route, response: undefined as any };\n await this.alepha.events.emit(\"server:onSend\", payload, { catch: true });\n\n const reply = request.reply;\n const response = {\n status: reply.status ?? (reply.body ? 200 : 204), // default status: 200 if body is set, otherwise 204\n headers: reply.headers,\n body: reply.body as any,\n };\n\n payload.response = response;\n await this.alepha.events.emit(\"server:onResponse\", payload, {\n catch: true,\n });\n\n return response;\n }\n\n /**\n * Run the route handler with request validation and response serialization.\n */\n protected async runRouteHandler(\n route: ServerRoute,\n request: ServerRequest,\n responseKind: ResponseKind,\n ) {\n // Built-in hooks: body parsing, security, logging\n await this.alepha.events.emit(\"server:onRequest\", { request, route });\n\n // A hook (e.g. CORS preflight) may have already written the response\n const reply = request.reply;\n if (reply.body || (reply.status && reply.status >= 200)) {\n return;\n }\n\n // Store route path info for middleware (e.g. $secure auto-permission lookup)\n request.metadata.routePath = route.path;\n request.metadata.routeMethod = route.method;\n\n // Make the request available to handlers via alepha.context\n this.alepha.set(\"alepha.http.request\", request);\n\n const timing = this.serverTimingProvider;\n\n timing.beginTiming(\"validateRequest\");\n try {\n this.validateRequest(route, request);\n } finally {\n timing.endTiming(\"validateRequest\");\n }\n\n timing.beginTiming(\"runHandler\");\n try {\n const result = await route.handler.run(request);\n if (result) {\n reply.body = result;\n }\n } finally {\n timing.endTiming(\"runHandler\");\n }\n\n timing.beginTiming(\"serializeResponse\");\n try {\n this.serializeResponse(route, reply, responseKind);\n } finally {\n timing.endTiming(\"serializeResponse\");\n }\n }\n\n /**\n * Transform reply body based on response kind and route schema.\n */\n public serializeResponse(\n route: ServerRoute,\n reply: ServerReply,\n responseKind: ResponseKind,\n ): void {\n const headers = reply.headers;\n\n if (responseKind === \"json\" && route.schema?.response) {\n headers[\"content-type\"] = \"application/json\";\n reply.body = this.alepha.codec.encode(route.schema.response, reply.body, {\n as: \"string\" as const,\n });\n return;\n }\n\n if (responseKind === \"file\") {\n if (!isFileLike(reply.body)) {\n throw new HttpError({\n status: 500,\n message: \"Invalid response body - not a file\",\n });\n }\n headers[\"content-type\"] = reply.body.type;\n headers[\"content-disposition\"] =\n `attachment; filename=\"${reply.body.name.replaceAll('\"', \"\")}\"`;\n reply.body = reply.body.stream();\n return;\n }\n\n if (responseKind === \"text\") {\n reply.body = String(reply.body);\n // Detect HTML responses (starts with <!DOCTYPE html>)\n if (\n reply.body.length > 15 &&\n reply.body.charCodeAt(0) === 60 &&\n reply.body.startsWith(\"<!DOCTYPE html>\")\n ) {\n headers[\"content-type\"] ??= \"text/html; charset=UTF-8\";\n } else {\n headers[\"content-type\"] ??= \"text/plain\";\n }\n return;\n }\n\n if (reply.body == null || responseKind === \"void\") {\n delete headers[\"content-type\"];\n reply.body = undefined;\n return;\n }\n\n if (Buffer.isBuffer(reply.body)) {\n headers[\"content-type\"] ??= \"application/octet-stream\";\n return;\n }\n\n if (\n reply.body instanceof NodeWebStream ||\n reply.body instanceof NodeStream\n ) {\n headers[\"content-type\"] ??= \"application/octet-stream\";\n return;\n }\n\n // Plain objects/arrays → auto-serialize as JSON\n if (typeof reply.body === \"object\") {\n headers[\"content-type\"] ??= \"application/json\";\n reply.body = JSON.stringify(reply.body);\n return;\n }\n\n headers[\"content-type\"] ??= \"text/plain\";\n reply.body = String(reply.body);\n }\n\n /**\n * Determine response type based on route schema.\n */\n protected getResponseType(schema?: RequestConfigSchema): ResponseKind {\n if (schema?.response) {\n if (\n t.schema.isObject(schema.response) ||\n t.schema.isRecord(schema.response) ||\n t.schema.isArray(schema.response)\n ) {\n return \"json\";\n }\n\n if (\n t.schema.isString(schema.response) ||\n t.schema.isInteger(schema.response) ||\n t.schema.isNumber(schema.response) ||\n t.schema.isBoolean(schema.response)\n ) {\n return \"text\";\n }\n\n if (isTypeFile(schema.response)) {\n return \"file\";\n }\n\n if (t.schema.isVoid(schema.response)) {\n return \"void\";\n }\n }\n\n return \"any\";\n }\n\n /**\n * Handle errors during request processing.\n */\n protected async errorHandler(\n route: ServerRoute,\n request: ServerRequest,\n error: Error,\n ) {\n // Reset body — it's likely invalid after an error\n const reply = request.reply;\n reply.body = null;\n\n // Let error hooks handle it first (e.g. custom error pages, Sentry)\n await this.alepha.events.emit(\"server:onError\", { request, route, error });\n // |\n // |\n // --> If a hook already set the response, we're done!\n if (reply.status) return;\n\n const requestId = request.requestId;\n\n if (error instanceof HttpError) {\n reply.status = error.status;\n reply.headers[\"content-type\"] = \"application/json\";\n const errorJson = HttpError.toJSON(error);\n errorJson.requestId = requestId;\n reply.body = JSON.stringify(errorJson);\n return;\n }\n\n // Errors with a known HTTP status (e.g. from upstream libraries)\n if (\n \"status\" in error &&\n typeof error.status === \"number\" &&\n !!errorNameByStatus[error.status]\n ) {\n const status = error.status;\n reply.status = status;\n reply.headers[\"content-type\"] = \"application/json\";\n reply.body = JSON.stringify({\n status,\n error: errorNameByStatus[status],\n message: error.message,\n requestId,\n });\n return;\n }\n\n // Fallback: unknown error → 500\n reply.status = 500;\n reply.headers[\"content-type\"] = \"application/json\";\n reply.body = JSON.stringify({\n status: 500,\n error: \"InternalServerError\",\n message: error.message,\n requestId,\n });\n }\n\n /**\n * Validate incoming request against route schema.\n */\n public validateRequest(\n route: { schema?: RequestConfigSchema },\n request: ServerRequestConfig,\n ) {\n if (route.schema?.params) {\n try {\n request.params = this.alepha.codec.validate(\n route.schema.params,\n request.params,\n ) as any;\n } catch (error) {\n throw new ValidationError(\"Invalid request params\", error);\n }\n }\n\n if (route.schema?.query) {\n try {\n const schemaQuery = route.schema.query;\n const keys = this.getQuerySchemaKeys(schemaQuery);\n const query: Record<string, any> = {};\n\n for (const key of keys) {\n if (request.query[key] != null) {\n query[key] = this.alepha.codec.decode(\n schemaQuery.properties[key],\n request.query[key],\n );\n }\n }\n\n request.query = query;\n } catch (error) {\n throw new ValidationError(\"Invalid request query\", error);\n }\n }\n\n if (route.schema?.headers) {\n try {\n request.headers = this.alepha.codec.validate(\n route.schema.headers,\n request.headers,\n ) as any;\n } catch (error) {\n throw new ValidationError(\"Invalid request header\", error);\n }\n }\n\n if (route.schema?.body) {\n if (t.schema.isString(route.schema.body)) {\n if (typeof request.body !== \"string\") {\n throw new ValidationError(\"Request body is not a string\");\n }\n } else {\n try {\n request.body = this.alepha.codec.decode(\n route.schema.body,\n request.body,\n );\n } catch (error) {\n throw new ValidationError(\"Invalid request body\", error);\n }\n }\n }\n }\n}\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport interface PushMiddlewareOptions {\n method?: RouteMethod | RouteMethod[];\n exclude?: string[];\n}\n\ninterface GlobalMiddlewareEntry extends PushMiddlewareOptions {\n pattern: string;\n middleware: Middleware[];\n}\n","import { Readable } from \"node:stream\";\nimport { $hook, $inject, Alepha } from \"alepha\";\nimport { DateTimeProvider } from \"alepha/datetime\";\nimport { $logger } from \"alepha/logger\";\nimport type { Route } from \"alepha/router\";\nimport type { RouteMethod } from \"../constants/routeMethods.ts\";\nimport type {\n NodeRequestEvent,\n ServerRequestData,\n WebRequestEvent,\n} from \"../interfaces/ServerRequest.ts\";\nimport { ServerRouterProvider } from \"./ServerRouterProvider.ts\";\n\n// ============================================================================\n// Performance Constants\n// ============================================================================\n\n// Note: We cannot use frozen/shared empty objects here because downstream code\n// may mutate params/query (e.g., validation, React page rendering)\n\n// Header constants for fast property access\nconst HEADER_X_FORWARDED_PROTO = \"x-forwarded-proto\";\nconst HEADER_HOST = \"host\";\n\n// Protocol prefixes\nconst PROTO_HTTPS = \"https://\";\nconst PROTO_HTTP = \"http://\";\n\n/**\n * Base server provider to handle incoming requests and route them.\n *\n * This is the default implementation for serverless environments.\n *\n * ServerProvider supports both Node.js HTTP requests and Web (Fetch API) requests.\n */\nexport class ServerProvider {\n protected readonly log = $logger();\n protected readonly alepha = $inject(Alepha);\n protected readonly dateTimeProvider = $inject(DateTimeProvider);\n protected readonly router = $inject(ServerRouterProvider);\n\n protected readonly internalServerErrorMessage = \"Internal Server Error\";\n\n // Pre-allocated error response to avoid object creation per failed request\n protected readonly internalErrorResponse = Object.freeze({\n status: 500,\n headers: Object.freeze({ \"content-type\": \"text/plain\" }),\n body: this.internalServerErrorMessage,\n });\n\n // Pre-bound error handler to avoid function allocation per request\n protected readonly handleInternalError = () => this.internalErrorResponse;\n\n // ============================================================================\n // P1: URL Base Cache - cache protocol+host per unique host header\n // Avoids string concatenation on every request\n // ============================================================================\n protected readonly urlBaseCache = new Map<string, string>();\n\n /**\n * Get cached URL base (protocol + host) for a given host header.\n * Caches the result to avoid repeated string concatenation.\n */\n protected getUrlBase(headers: Record<string, string>): string {\n const host = headers[HEADER_HOST];\n let base = this.urlBaseCache.get(host);\n if (!base) {\n const proto =\n headers[HEADER_X_FORWARDED_PROTO] === \"https\"\n ? PROTO_HTTPS\n : PROTO_HTTP;\n base = proto + host;\n // Limit cache size to prevent memory leaks from many unique hosts\n if (this.urlBaseCache.size < 100) {\n this.urlBaseCache.set(host, base);\n }\n }\n return base;\n }\n\n // ============================================================================\n // P0: Manual Query String Parser - faster than URLSearchParams\n // Parses query string without creating URL object\n // ============================================================================\n\n /**\n * Parse query string manually - faster than new URL() + URLSearchParams.\n * Returns empty object if no query string.\n */\n protected parseQueryString(rawUrl: string): Record<string, string> {\n const qIndex = rawUrl.indexOf(\"?\");\n if (qIndex === -1) {\n return {};\n }\n\n const qs = rawUrl.slice(qIndex + 1);\n if (!qs) {\n return {};\n }\n\n const query: Record<string, string> = {};\n let start = 0;\n let eqIdx = -1;\n\n for (let i = 0; i <= qs.length; i++) {\n const char = i < qs.length ? qs.charCodeAt(i) : 38; // '&' at end\n\n if (char === 61) {\n // '='\n eqIdx = i;\n } else if (char === 38) {\n // '&'\n if (eqIdx !== -1 && eqIdx > start) {\n const key = qs.slice(start, eqIdx);\n const value = qs.slice(eqIdx + 1, i);\n // Only decode if necessary (contains % or +)\n query[this.fastDecode(key)] = this.fastDecode(value);\n }\n start = i + 1;\n eqIdx = -1;\n }\n }\n\n return query;\n }\n\n /**\n * Fast decode - only calls decodeURIComponent if needed.\n */\n protected fastDecode(str: string): string {\n // Fast path: no encoding needed\n if (str.indexOf(\"%\") === -1 && str.indexOf(\"+\") === -1) {\n return str;\n }\n // Replace + with space before decoding\n try {\n return decodeURIComponent(str.replace(/\\+/g, \" \"));\n } catch {\n return str;\n }\n }\n\n public get hostname(): string {\n if (this.alepha.isViteDev()) {\n return `http://localhost:${this.alepha.env.SERVER_PORT}`;\n }\n return \"\"; // no hostname in serverless mode\n }\n\n /**\n * When a Node.js HTTP request is received from outside. (Vercel, AWS Lambda, etc.)\n */\n protected readonly onNodeRequest = $hook({\n on: \"node:request\",\n handler: (ev) => this.handleNodeRequest(ev),\n });\n\n /**\n * When a Web (Fetch API) request is received from outside. (Netlify, Cloudflare Workers, etc.)\n */\n protected readonly onWebRequest = $hook({\n on: \"web:request\",\n handler: (ev) => {\n return this.handleWebRequest(ev);\n },\n });\n\n /**\n * Handle Node.js HTTP request event.\n *\n * Optimized to avoid expensive URL parsing when possible.\n */\n public async handleNodeRequest(\n nodeRequestEvent: NodeRequestEvent,\n ): Promise<void> {\n const { req, res } = nodeRequestEvent;\n const rawUrl = req.url!;\n const { route, params } = this.router.match(`/${req.method}${rawUrl}`);\n\n if (!route) {\n // Skip if response was already sent (e.g., by Vite middleware)\n if (res.headersSent) {\n return;\n }\n // if no route is found, return basic 404\n // note: you should not use this in production, use a custom 404 page instead by adding a route /*\n res.writeHead(404, { \"content-type\": \"text/plain\" });\n res.end(\"Not Found\");\n return;\n }\n\n const headers = (req.headers ?? {}) as Record<string, string>;\n const method = (req.method?.toUpperCase() ?? \"GET\") as RouteMethod;\n\n // P0: Use manual query parsing - much faster than new URL() + URLSearchParams\n const query = this.parseQueryString(rawUrl);\n\n // P1: Use cached URL base - avoids string concat on every request\n // Create URL object (still needed for downstream code that uses url.pathname, etc.)\n const urlBase = this.getUrlBase(headers);\n const url = new URL(rawUrl, urlBase);\n\n const request: ServerRequestData = {\n method,\n url,\n headers,\n params: params ?? {},\n query,\n raw: { node: nodeRequestEvent },\n };\n\n const response = await route\n .handler(request)\n .catch(this.handleInternalError);\n\n // Skip if response was already sent (e.g., by Vite middleware)\n if (res.headersSent) {\n return;\n }\n\n // empty body - just send status & headers\n if (!response.body) {\n res.writeHead(response.status, response.headers);\n res.end();\n return;\n }\n\n // if response.body is string or buffer\n if (typeof response.body === \"string\" || Buffer.isBuffer(response.body)) {\n res.writeHead(response.status, response.headers);\n res.end(response.body);\n return;\n }\n\n // if response.body is node stream\n if (response.body instanceof Readable) {\n res.writeHead(response.status, response.headers);\n response.body.pipe(res);\n return;\n }\n\n // if response.body is web stream\n if (response.body instanceof ReadableStream) {\n res.writeHead(response.status, response.headers);\n // Flush headers immediately and disable Nagle's algorithm for streaming\n res.flushHeaders();\n res.socket?.setNoDelay(true);\n try {\n for await (const chunk of response.body) {\n const canContinue = res.write(chunk);\n // If the internal buffer is full, wait for it to drain\n if (!canContinue) {\n await new Promise<void>((resolve) => res.once(\"drain\", resolve));\n }\n }\n } catch (error) {\n this.log.error(\"Error piping proxy response stream\", error);\n } finally {\n res.end();\n }\n return;\n }\n\n // not supported response body type\n\n this.log.error(\"Unknown response body type:\", typeof response.body);\n res.writeHead(500, { \"content-type\": \"text/plain\" });\n res.end(this.internalServerErrorMessage);\n }\n\n /**\n * Handle Web (Fetch API) request event.\n */\n public async handleWebRequest(ev: WebRequestEvent): Promise<void> {\n const req = ev.req;\n const url = new URL(req.url);\n const { route, params } = this.router.match(\n `/${req.method}${url.pathname}`,\n );\n\n if (this.isViteNotFound(req.url, route, params)) {\n return;\n }\n\n if (!route) {\n // if no route is found, return basic 404\n // note: you should not use this in production, use a custom 404 page instead by adding a route /*\n ev.res = new Response(\"Not Found\", {\n status: 404,\n headers: { \"content-type\": \"text/plain\" },\n });\n return;\n }\n\n const headers: Record<string, string> = {};\n\n req.headers.forEach((value, key) => {\n headers[key] = value;\n });\n\n // Optimize: only parse query params if there are any\n const query = url.search\n ? Object.fromEntries(url.searchParams.entries())\n : {};\n const method = (req.method.toUpperCase() ?? \"GET\") as RouteMethod;\n const request: ServerRequestData = {\n method,\n url,\n headers,\n params: params || {},\n query,\n raw: { web: ev },\n };\n\n const response = await route\n .handler(request)\n .catch(this.handleInternalError);\n\n const webHeaders = this.toWebHeaders(response.headers);\n\n // empty body - just send status & headers\n if (!response.body) {\n ev.res = new Response(null, {\n status: response.status,\n headers: webHeaders,\n });\n return;\n }\n\n // if response.body is string or buffer\n if (typeof response.body === \"string\") {\n ev.res = new Response(response.body, {\n status: response.status,\n headers: webHeaders,\n });\n return;\n }\n\n if (Buffer.isBuffer(response.body)) {\n // Use Uint8Array to avoid Buffer pooling issues where .buffer returns\n // the entire underlying ArrayBuffer which may be larger than the actual data\n ev.res = new Response(new Uint8Array(response.body), {\n status: response.status,\n headers: webHeaders,\n });\n return;\n }\n\n // if response.body is node stream\n if (response.body instanceof Readable) {\n ev.res = new Response(\n Readable.toWeb(response.body) as unknown as ReadableStream,\n {\n status: response.status,\n headers: webHeaders,\n },\n );\n return;\n }\n\n // if response.body is web stream\n if (response.body instanceof ReadableStream) {\n ev.res = new Response(response.body, {\n status: response.status,\n headers: webHeaders,\n });\n return;\n }\n\n // not supported response body type\n this.log.error(`Unknown response body type: ${typeof response.body}`);\n ev.res = new Response(this.internalServerErrorMessage, {\n status: 500,\n headers: { \"content-type\": \"text/plain\" },\n });\n }\n\n /**\n * Convert response headers to Web API Headers.\n *\n * The `set-cookie` header requires special handling because it's stored\n * as `string[]` (one entry per cookie) but the `Response` constructor\n * would comma-join arrays, which breaks cookie parsing. We use\n * `Headers.append()` to emit each cookie as a separate header.\n */\n protected toWebHeaders(headers: Record<string, string | string[]>): Headers {\n const webHeaders = new Headers();\n for (const [key, value] of Object.entries(headers)) {\n if (Array.isArray(value)) {\n for (const v of value) {\n webHeaders.append(key, v);\n }\n } else {\n webHeaders.set(key, value);\n }\n }\n return webHeaders;\n }\n\n /**\n * Helper for Vite development mode to let Vite handle (or not) 404.\n */\n protected isViteNotFound(\n url?: string,\n route?: Route,\n params?: Record<string, string>,\n ): boolean {\n if (this.alepha.isViteDev()) {\n if (!route) {\n return true;\n }\n\n url = url?.split(\"?\")[0];\n\n if (!!params?.[\"*\"] && `/${params?.[\"*\"]}` === url) {\n return true;\n }\n }\n\n return false;\n }\n}\n","import { type Static, t } from \"alepha\";\n\nexport const errorSchema = t.object(\n {\n error: t.text({ description: \"HTTP error name\" }),\n status: t.integer({\n description: \"HTTP status code\",\n }),\n message: t.text({\n description: \"Short text which describe the error\",\n size: \"rich\",\n }),\n details: t.optional(\n t.text({\n description: \"Detailed description of the error\",\n size: \"rich\",\n }),\n ),\n requestId: t.optional(t.text()),\n cause: t.optional(\n t.object({\n name: t.text(),\n message: t.text({\n description: \"Cause Error message\",\n size: \"rich\",\n }),\n }),\n ),\n },\n {\n title: \"HttpError\",\n description: \"Generic response after a failed operation\",\n },\n);\n\nexport type ErrorSchema = Static<typeof errorSchema>;\n","import {\n $inject,\n Alepha,\n type FileLike,\n isFileLike,\n type Static,\n type TObject,\n type TSchema,\n} from \"alepha\";\nimport { $cache } from \"alepha/cache\";\nimport type { DurationLike } from \"alepha/datetime\";\nimport { $logger } from \"alepha/logger\";\nimport { HttpError } from \"../errors/HttpError.ts\";\nimport { isMultipart } from \"../helpers/isMultipart.ts\";\nimport type {\n ServerRequestConfigEntry,\n TRequestBody,\n TResponseBody,\n} from \"../interfaces/ServerRequest.ts\";\nimport type { ClientRequestOptions } from \"../primitives/$action.ts\";\nimport { errorSchema } from \"../schemas/errorSchema.ts\";\n\nexport class HttpClient {\n protected readonly log = $logger();\n protected readonly alepha = $inject(Alepha);\n\n public readonly cache = $cache<HttpClientCache>({\n provider: \"memory\",\n name: \"http:client\",\n });\n\n protected readonly pendingRequests: HttpClientPendingRequests = {};\n\n public async fetchAction(args: FetchActionArgs): Promise<FetchResponse> {\n const route = args.action; // our link to fetch\n const options = args.options ?? {}; // fetch standard options, cache, etc.\n const config = args.config ?? {}; // params, query, body, etc.\n const host = args.host ?? \"\"; // remote host, e.g. \"https://api.example.com\" or empty (for browser)\n\n const request: RequestInit = {\n ...options.request,\n };\n\n const method = route.method;\n const headers: Record<string, string> = {};\n const url = this.url(host, route, config);\n\n await this.alepha.events.emit(\"client:onRequest\", {\n route,\n config,\n options,\n headers,\n request,\n });\n\n request.method ??= method;\n\n await this.body(request, headers, route, config);\n\n request.headers = {\n ...config.headers,\n ...Object.fromEntries(new Headers(request.headers).entries()),\n ...headers,\n };\n\n return await this.fetch(url, {\n ...request,\n schema: route.schema,\n ...options,\n });\n }\n\n public async fetch<T extends TSchema>(\n url: string,\n request: RequestInitWithOptions<T> = {}, // standard options\n ): Promise<FetchResponse<Static<T>>> {\n const options = {\n cache: request.localCache,\n schema: request.schema?.response,\n key: request.key,\n };\n\n request.method ??= \"GET\";\n\n this.log.trace(\"Request\", {\n url,\n method: request.method,\n body: request.body,\n headers: request.headers,\n options,\n });\n\n // Only add automatic ETag if user didn't explicitly provide headers\n const cached = await this.cache.get(url);\n if (cached && request.method === \"GET\") {\n if (cached.etag) {\n request.headers = new Headers(request.headers);\n if (!request.headers.has(\"if-none-match\")) {\n request.headers.set(\"if-none-match\", cached.etag);\n }\n } else {\n return {\n data: cached.data as Static<T>,\n status: 200,\n statusText: \"OK\",\n headers: new Headers(),\n };\n }\n }\n\n await this.alepha.events.emit(\"client:beforeFetch\", {\n url,\n options,\n request,\n });\n\n // make a key for the request\n // this will be used to check if the request is already pending\n const key =\n options.key ??\n JSON.stringify({\n url,\n method: request.method,\n body: request.body,\n });\n\n const existing = this.pendingRequests[key];\n if (existing) {\n this.log.info(\"Request already pending\", key);\n return existing;\n }\n\n this.pendingRequests[key] = fetch(url, request)\n .then(async (response) => {\n this.log.debug(\"Response\", {\n url,\n status: response.status,\n });\n\n const fetchResponse: FetchResponse = {\n data: await this.responseData(response, options),\n status: response.status,\n statusText: response.statusText,\n headers: response.headers,\n raw: response,\n };\n\n if (request.method === \"GET\") {\n if (options.cache) {\n await this.cache.set(\n url,\n { data: fetchResponse.data },\n typeof options.cache === \"boolean\" ? undefined : options.cache,\n );\n } else if (!this.alepha.isBrowser()) {\n // only cache etag on server, browser can handle etag itself\n const etag = response.headers.get(\"etag\") ?? undefined;\n if (etag) {\n await this.cache.set(url, { data: fetchResponse.data, etag });\n }\n }\n }\n\n return fetchResponse;\n })\n .finally(() => {\n delete this.pendingRequests[key];\n });\n\n return this.pendingRequests[key];\n }\n\n protected url(\n host: string,\n action: HttpAction,\n args: ServerRequestConfigEntry,\n ) {\n let url = host;\n\n if (action.prefix) {\n url += action.prefix;\n }\n\n url += action.path;\n url = this.pathVariables(url, action, args);\n url = this.queryParams(url, action, args);\n\n return url;\n }\n\n protected async body(\n init: RequestInit,\n headers: Record<string, string>,\n action: HttpAction,\n args: ServerRequestConfigEntry = {},\n ) {\n const hasHeader =\n typeof init.headers === \"object\" &&\n \"content-type\" in init.headers &&\n init.headers[\"content-type\"] === \"multipart/form-data\";\n\n if (hasHeader || isMultipart(action)) {\n if (typeof init.headers === \"object\" && \"content-type\" in init.headers) {\n delete init.headers[\"content-type\"]; // fetch() will fill this for us\n }\n\n const formData = new FormData();\n\n for (const [key, value] of Object.entries(args.body ?? {})) {\n if (typeof value === \"string\") {\n formData.append(key, value);\n continue;\n }\n if (value instanceof Blob) {\n formData.append(key, value);\n continue;\n }\n if (isFileLike(value)) {\n // FileLike must be transformed to WebFile\n formData.append(\n key,\n new File([await value.arrayBuffer()], value.name, {\n type: value.type,\n }),\n );\n }\n }\n\n init.body = formData;\n\n return;\n }\n\n if (!init.body && action.schema?.body) {\n headers[\"content-type\"] = \"application/json\";\n init.body = this.alepha.codec.encode(action.schema?.body, args.body, {\n as: \"string\",\n });\n }\n }\n\n protected async responseData(\n response: Response,\n options: FetchOptions,\n ): Promise<any> {\n if (response.status === 304) {\n let cacheKey = response.url;\n if (typeof window !== \"undefined\") {\n cacheKey = cacheKey.replace(window.location.origin, \"\");\n }\n\n const cached = await this.cache.get(cacheKey);\n if (cached) {\n return cached.data;\n }\n\n // if no cached data (etag-only routes), return empty string\n return \"\";\n }\n\n if (response.status === 204) {\n return;\n }\n\n if (this.isMaybeFile(response)) {\n return this.createFileLike(response);\n }\n\n if (response.headers.get(\"Content-Type\")?.startsWith(\"text/\")) {\n return await response.text();\n }\n\n if (response.headers.get(\"Content-Type\") === \"application/json\") {\n const json = await response.json();\n\n if (response.status >= 400) {\n const jsonError = this.alepha.codec.decode(errorSchema, json);\n const error = new HttpError(jsonError);\n\n await this.alepha.events.emit(\"client:onError\", {\n error,\n });\n\n throw error;\n }\n\n if (options.schema) {\n return this.alepha.codec.decode(options.schema, json);\n }\n\n return json;\n }\n\n if (response.status >= 400) {\n const error = new HttpError({\n status: response.status,\n message: `An error occurred while fetching the resource. (${response.statusText})`,\n });\n\n await this.alepha.events.emit(\"client:onError\", {\n error,\n });\n\n throw error;\n }\n\n return response;\n }\n\n protected isMaybeFile(response: Response): boolean {\n const contentType = response.headers.get(\"Content-Type\");\n if (!contentType) {\n return false;\n }\n\n if (response.headers.get(\"Content-Disposition\")?.includes(\"attachment\")) {\n return true; // If Content-Disposition indicates an attachment, treat it as a file\n }\n\n return (\n contentType.startsWith(\"application/octet-stream\") ||\n contentType.startsWith(\"application/pdf\") ||\n contentType.startsWith(\"application/zip\") ||\n contentType.startsWith(\"image/\") ||\n contentType.startsWith(\"video/\") ||\n contentType.startsWith(\"audio/\")\n );\n }\n\n protected createFileLike(response: Response, defaultFileName = \"\"): FileLike {\n const match = (response.headers.get(\"Content-Disposition\") ?? \"\").match(\n /filename=\"(.+)\"/,\n );\n return {\n name: match?.[1] ? match[1] : defaultFileName,\n type: response.headers.get(\"Content-Type\") ?? \"application/octet-stream\",\n size: Number(response.headers.get(\"Content-Length\") ?? 0),\n lastModified: Date.now(),\n stream: () => {\n throw new Error(\"Not implemented\");\n },\n arrayBuffer: async () => {\n return await response.arrayBuffer();\n },\n text: async () => {\n return await response.text();\n },\n };\n }\n\n public pathVariables(\n url: string,\n action: { schema?: { params?: TObject } },\n args: ServerRequestConfigEntry = {},\n ): string {\n if (typeof args.params === \"object\") {\n const params = action.schema?.params\n ? (this.alepha.codec.decode(\n action.schema.params,\n args.params,\n ) as Record<string, any>)\n : args.params;\n\n for (const key of Object.keys(params)) {\n url = url.replace(`:${key}`, params[key]);\n url = url.replace(`{${key}}`, params[key]);\n }\n }\n\n return url;\n }\n\n public queryParams(\n url: string,\n action: { schema?: { query?: TObject } },\n args: ServerRequestConfigEntry = {},\n ): string {\n if (typeof args.query === \"object\") {\n const query = action.schema?.query\n ? this.alepha.codec.decode(action.schema.query, args.query ?? {})\n : args.query;\n\n for (const key of Object.keys(query)) {\n if (query[key] === undefined) {\n delete query[key];\n }\n if (typeof query[key] === \"object\") {\n query[key] = JSON.stringify(query[key]);\n }\n }\n\n return `${url}?${new URLSearchParams(\n query as Record<string, string>,\n ).toString()}`;\n }\n return url;\n }\n}\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport interface FetchOptions<T extends TSchema = TSchema> {\n /**\n * Key to identify the request in the pending requests.\n */\n key?: string;\n\n /**\n * The schema to validate the response against.\n */\n schema?: {\n response?: T;\n };\n\n /**\n * Built-in cache options.\n */\n localCache?: boolean | number | DurationLike;\n}\n\nexport type RequestInitWithOptions<T extends TSchema = TSchema> = RequestInit &\n FetchOptions<T>;\n\nexport interface FetchResponse<T = any> {\n data: T;\n status: number;\n statusText: string;\n headers: Headers;\n raw?: Response;\n}\n\nexport type HttpClientPendingRequests = Record<\n string,\n Promise<any> | undefined\n>;\n\ninterface HttpClientCache {\n data: any;\n etag?: string;\n}\n\nexport interface FetchActionArgs {\n action: HttpAction;\n host?: string;\n config?: ServerRequestConfigEntry;\n options?: ClientRequestOptions;\n}\n\nexport interface HttpAction {\n method?: string;\n prefix?: string;\n path: string;\n contentType?: string;\n requestBodyType?: string;\n schema?: {\n params?: TObject;\n query?: TObject;\n body?: TRequestBody;\n response?: TResponseBody;\n };\n}\n","import {\n $atom,\n $inject,\n $use,\n AlephaError,\n type Async,\n createPrimitive,\n isTypeFile,\n KIND,\n PipelinePrimitive,\n type PipelinePrimitiveOptions,\n type Static,\n type TObject,\n type TSchema,\n t,\n} from \"alepha\";\nimport { $logger } from \"alepha/logger\";\nimport type { RouteMethod } from \"../constants/routeMethods.ts\";\nimport { isMultipart } from \"../helpers/isMultipart.ts\";\nimport { ServerReply } from \"../helpers/ServerReply.ts\";\nimport type {\n RequestConfigSchema,\n ServerRequest,\n ServerResponseBody,\n ServerRoute,\n TRequestBody,\n} from \"../interfaces/ServerRequest.ts\";\nimport { ServerProvider } from \"../providers/ServerProvider.ts\";\nimport { ServerRouterProvider } from \"../providers/ServerRouterProvider.ts\";\nimport {\n type FetchOptions,\n type FetchResponse,\n HttpClient,\n} from \"../services/HttpClient.ts\";\n\n/**\n * Creates a server action primitive for defining type-safe HTTP endpoints.\n *\n * Server actions are the core building blocks for REST APIs in Alepha, providing declarative\n * HTTP endpoints with full type safety, automatic validation, and OpenAPI documentation.\n *\n * **Key Features**\n * - Full TypeScript inference for request/response types\n * - Automatic schema validation using TypeBox\n * - Convention-based URL generation with customizable paths\n * - Direct invocation (`run()`) or HTTP requests (`fetch()`)\n * - Built-in authentication and authorization support\n * - Automatic content-type handling (JSON, form-data, plain text)\n *\n * **URL Generation**\n *\n * **Important:** All `$action` paths are automatically prefixed with `/api`.\n *\n * ```ts\n * $action({ path: \"/users\" }) // → GET /api/users\n * $action({ path: \"/users/:id\" }) // → GET /api/users/:id\n * $action({ path: \"/hello\" }) // → GET /api/hello\n * ```\n *\n * This prefix is configurable via the `SERVER_API_PREFIX` environment variable.\n * HTTP method defaults to GET, or POST if body schema is provided.\n *\n * **Common Use Cases**\n * - CRUD operations with type safety\n * - File upload and download endpoints\n * - Microservice communication\n *\n * @example\n * ```ts\n * class UserController {\n * getUsers = $action({\n * path: \"/users\",\n * schema: {\n * query: t.object({\n * page: t.optional(t.number({ default: 1 })),\n * limit: t.optional(t.number({ default: 10 }))\n * }),\n * response: t.object({\n * users: t.array(t.object({\n * id: t.text(),\n * name: t.text(),\n * email: t.text()\n * })),\n * total: t.number()\n * })\n * },\n * handler: async ({ query }) => {\n * const users = await this.userService.findUsers(query);\n * return { users: users.items, total: users.total };\n * }\n * });\n *\n * createUser = $action({\n * method: \"POST\",\n * path: \"/users\",\n * schema: {\n * body: t.object({\n * name: t.text(),\n * email: t.text({ format: \"email\" })\n * }),\n * response: t.object({ id: t.text(), name: t.text() })\n * },\n * handler: async ({ body }) => {\n * return await this.userService.create(body);\n * }\n * });\n * }\n * ```\n */\nexport const $action = <TConfig extends RequestConfigSchema>(\n options: ActionPrimitiveOptions<TConfig>,\n): ActionPrimitiveFn<TConfig> => {\n const instance = createPrimitive(ActionPrimitive<TConfig>, options);\n const fn = (\n config?: ClientRequestEntry<TConfig>,\n options?: ClientRequestOptions,\n ) => {\n return instance.run(config, options);\n };\n Object.defineProperty(fn, \"name\", {\n get(): string {\n return instance.options.name || instance.config.propertyKey;\n },\n });\n return Object.setPrototypeOf(fn, instance) as ActionPrimitiveFn<TConfig>;\n};\n\n// ----------------------------------------------------------------------------------------------------------\n\nexport interface ActionPrimitiveOptions<TConfig extends RequestConfigSchema>\n extends Omit<ServerRoute, \"handler\" | \"path\" | \"schema\" | \"mapParams\">,\n PipelinePrimitiveOptions {\n /**\n * Name of the action.\n *\n * - It will be used to generate the route path if `path` is not provided.\n * - It will be used to generate the permission name if `security` is enabled.\n */\n name?: string;\n\n /**\n * Group actions together.\n *\n * - If not provided, the service name containing the route will be used.\n * - It will be used as Tag for documentation purposes.\n * - It will be used for permission name generation if `security` is enabled.\n *\n * @example\n * ```ts\n * // group = \"MyController\"\n * class MyController {\n * \thello = $action({ handler: () => \"Hello World\" });\n * }\n *\n * // group = \"users\"\n * class MyOtherController {\n * group = \"users\";\n * a1 = $action({ handler: () => \"Action 1\", group: this.group });\n * a2 = $action({ handler: () => \"Action 2\", group: this.group });\n * }\n * ```\n */\n group?: string;\n\n /**\n * Pathname of the route. If not provided, property key is used.\n */\n path?: string;\n\n /**\n * The route method.\n *\n * - If not provided, it will be set to \"GET\" by default.\n * - If not provider and a body is provided, it will be set to \"POST\".\n *\n * Wildcard methods are not supported for now. (e.g. \"ALL\", \"ANY\", etc.)\n */\n method?: RouteMethod;\n\n /**\n * The config schema of the route.\n * - body: The request body schema.\n * - params: Path variables schema.\n * - query: The request query-params schema.\n * - response: The response schema.\n */\n schema?: TConfig;\n\n /**\n * A short description of the action. Used for documentation purposes.\n */\n description?: string;\n\n /**\n * Disable the route. Useful with env variables to disable one specific route.\n * Route won't be available in the API nor locally.\n */\n disabled?: boolean;\n\n /**\n * Main route handler. This is where the route logic is implemented.\n */\n handler: ServerActionHandler<TConfig>;\n}\n\n// ----------------------------------------------------------------------------------------------------------\n\n/**\n * Server API configuration atom.\n */\nexport const serverApiOptions = $atom({\n name: \"alepha.server.api.options\",\n schema: t.object({\n prefix: t.text({\n default: \"/api\",\n description: \"Prefix for all API routes (e.g. $action).\",\n }),\n }),\n default: {\n prefix: \"/api\",\n },\n});\n\nexport type ServerApiOptions = Static<typeof serverApiOptions.schema>;\n\ndeclare module \"alepha\" {\n interface State {\n [serverApiOptions.key]: ServerApiOptions;\n }\n}\n\n// ----------------------------------------------------------------------------------------------------------\n\nexport class ActionPrimitive<\n TConfig extends RequestConfigSchema,\n> extends PipelinePrimitive<ActionPrimitiveOptions<TConfig>> {\n protected readonly log = $logger();\n protected readonly settings = $use(serverApiOptions);\n protected readonly httpClient = $inject(HttpClient);\n protected readonly serverProvider = $inject(ServerProvider);\n protected readonly serverRouterProvider = $inject(ServerRouterProvider);\n\n protected onInit() {\n if (this.options.disabled) {\n this.log.debug(\n `Action '${this.name}' is disabled. It won't be available in the API.`,\n );\n return;\n }\n this.serverRouterProvider.createRoute(this.route);\n }\n\n public get prefix() {\n return this.settings.prefix;\n }\n\n public get route(): ServerRoute {\n return {\n ...(this.options as any), // TODO: fix schema.header mapping\n method: this.method,\n path: `${this.prefix}${this.path}`,\n handler: this.handler,\n } as ServerRoute;\n }\n\n /**\n * Returns the name of the action.\n */\n public get name(): string {\n return this.options.name || this.config.propertyKey;\n }\n\n /**\n * Returns the group of the action. (e.g. \"orders\", \"admin\", etc.)\n */\n public get group(): string {\n return this.options.group || this.config.service.name;\n }\n\n /**\n * Returns the HTTP method of the action.\n */\n public get method(): RouteMethod {\n return this.options.method || (this.options.schema?.body ? \"POST\" : \"GET\");\n }\n\n /**\n * Returns the path of the action.\n *\n * Path is prefixed by `/api` by default.\n */\n public get path(): string {\n if (this.options.path) {\n return this.options.path;\n }\n\n let path = `/${this.name}`;\n\n if (this.options.schema?.params) {\n for (const [key] of Object.entries(\n this.options.schema.params.properties,\n )) {\n path += `/:${key}`;\n }\n }\n\n return path;\n }\n\n public get schema(): TConfig | undefined {\n return this.options.schema;\n }\n\n public getBodyContentType(): string | undefined {\n if (this.options.schema?.body) {\n // TODO: move to `alepha.server.multipart` module ?\n if (isMultipart(this.options)) {\n return \"multipart/form-data\";\n }\n\n if (t.schema.isString(this.options.schema.body)) {\n // if body is a string, we assume it's plain text\n return \"text/plain\";\n }\n\n if (\n t.schema.isObject(this.options.schema.body) ||\n t.schema.isArray(this.options.schema.body) ||\n t.schema.isRecord(this.options.schema.body)\n )\n // if body is an object or array, we assume it's JSON\n return \"application/json\";\n }\n }\n\n /**\n * Call the action handler directly.\n * There is no HTTP layer involved.\n */\n public async run(\n config?: ClientRequestEntry<TConfig>,\n options: ClientRequestOptions = {}, // most of the options are ignored here\n ): Promise<ClientRequestResponse<TConfig>> {\n if (this.options.disabled) {\n throw new AlephaError(`Action '${this.name}' is disabled.`);\n }\n const handler = this.handler.run.bind(this.handler);\n const {\n body,\n params = {},\n query = {},\n headers = {},\n } = (config ?? {}) as ClientRequestEntryContainer<RequestConfigSchema>;\n const reply = new ServerReply();\n const method = this.method;\n\n // we use localhost as the base URL for the action\n const url = new URL(`http://localhost${this.path ?? \"\"}`);\n\n const serverActionRequest: Partial<ServerRequest> = {\n method,\n url,\n body,\n params,\n query,\n headers,\n reply,\n metadata: {\n routePath: this.route.path,\n routeMethod: this.route.method,\n },\n };\n\n const eventData: {\n action: ActionPrimitive<TConfig>;\n request: ServerRequest;\n options: ClientRequestOptions;\n context?: Record<string, any>;\n } = {\n action: this,\n request: serverActionRequest as ServerRequest,\n options,\n };\n\n await this.alepha.events.emit(\"action:onRequest\", eventData as any);\n\n if (serverActionRequest.reply?.body) {\n return serverActionRequest.reply.body as ClientRequestResponse<TConfig>;\n }\n\n const executeHandler = async (): Promise<\n ClientRequestResponse<TConfig>\n > => {\n if (serverActionRequest.query && this.options.schema?.query) {\n serverActionRequest.query = this.alepha.codec.encode(\n this.options.schema.query,\n serverActionRequest.query,\n );\n }\n\n if (serverActionRequest.headers && this.options.schema?.headers) {\n serverActionRequest.headers = this.alepha.codec.encode(\n this.options.schema.headers,\n serverActionRequest.headers,\n ) as Record<string, any>;\n }\n\n if (serverActionRequest.body && this.options.schema?.body) {\n serverActionRequest.body = this.alepha.codec.encode(\n this.options.schema.body,\n serverActionRequest.body,\n ) as unknown;\n }\n\n if (serverActionRequest.params && this.options.schema?.params) {\n serverActionRequest.params = this.alepha.codec.encode(\n this.options.schema.params,\n serverActionRequest.params,\n ) as Record<string, any>;\n }\n\n this.serverRouterProvider.validateRequest(\n this.options,\n serverActionRequest as ServerRequest,\n );\n\n let response: any = await handler(\n serverActionRequest as ServerActionRequest<TConfig>,\n );\n\n // we validate response just to remove undeclared properties from response\n if (\n this.options.schema?.response &&\n // skip validation if response is expected as file\n !isTypeFile(this.options.schema.response)\n ) {\n response = this.alepha.codec.validate(\n this.options.schema.response,\n response,\n );\n }\n\n await this.alepha.events.emit(\"action:onResponse\", {\n action: this,\n request: serverActionRequest as ServerRequest,\n options,\n response,\n });\n\n return response;\n };\n\n // Always fork to isolate each .run() call in its own ALS context.\n // The synthetic action request is separate from any real HTTP request.\n // Hook fork data (e.g. user atom) is merged into the context.\n const forkData: Record<string, any> = {\n \"alepha.action.request\": serverActionRequest,\n ...eventData.context,\n };\n\n return this.alepha.fork(executeHandler, forkData);\n }\n\n /**\n * Works like `run`, but always fetches (http request) the route.\n */\n public fetch(\n config?: ClientRequestEntry<TConfig>,\n options?: ClientRequestOptions,\n ): Promise<FetchResponse<ClientRequestResponse<TConfig>>> {\n return this.httpClient.fetchAction({\n host: this.serverProvider.hostname, // that's the trick, we just use the server hostname\n action: this,\n config,\n options,\n });\n }\n}\n\nexport interface ActionPrimitiveFn<TConfig extends RequestConfigSchema>\n extends ActionPrimitive<TConfig> {\n (\n config?: ClientRequestEntry<TConfig>,\n options?: ClientRequestOptions,\n ): Promise<ClientRequestResponse<TConfig>>;\n}\n\n$action[KIND] = ActionPrimitive;\n\n// ----------------------------------------------------------------------------------------------------------\n\nexport type ClientRequestEntry<\n TConfig extends RequestConfigSchema,\n T = ClientRequestEntryContainer<TConfig>,\n> = {\n [K in keyof T as T[K] extends undefined ? never : K]: T[K];\n};\n\nexport type ClientRequestEntryContainer<TConfig extends RequestConfigSchema> = {\n body: TConfig[\"body\"] extends TRequestBody\n ? Static<TConfig[\"body\"]>\n : undefined;\n\n params: TConfig[\"params\"] extends TObject\n ? Static<TConfig[\"params\"]>\n : undefined;\n\n headers?: TConfig[\"headers\"] extends TObject\n ? Static<TConfig[\"headers\"]>\n : Record<string, string>;\n\n query?: TConfig[\"query\"] extends TObject\n ? Partial<Static<TConfig[\"query\"]>>\n : Record<string, string>;\n};\n\nexport interface ClientRequestOptions extends FetchOptions {\n /**\n * Standard request fetch options.\n */\n request?: RequestInit;\n\n /**\n * Add query parameters to the request URL. They will be merged with any query params defined in the action schema.\n * This is useful for adding dynamic query params at runtime.\n */\n query?: Record<string, string | number | boolean>;\n}\n\nexport type ClientRequestResponse<TConfig extends RequestConfigSchema> =\n TConfig[\"response\"] extends TSchema ? Static<TConfig[\"response\"]> : any;\n\n/**\n * Specific handler for server actions.\n */\nexport type ServerActionHandler<\n TConfig extends RequestConfigSchema = RequestConfigSchema,\n> = (\n request: ServerActionRequest<TConfig>,\n) => Async<ServerResponseBody<TConfig>>;\n\n/**\n * Server Action Request Interface\n *\n * Can be extended with module augmentation to add custom properties (like `user` in Server Security).\n *\n * This is NOT Server Request, but a specific type for actions.\n */\nexport interface ServerActionRequest<TConfig extends RequestConfigSchema>\n extends ServerRequest<TConfig> {}\n","import {\n $inject,\n createPrimitive,\n KIND,\n type Middleware,\n Primitive,\n} from \"alepha\";\nimport type { RouteMethod } from \"../constants/routeMethods.ts\";\nimport { ServerRouterProvider } from \"../providers/ServerRouterProvider.ts\";\n\nexport const $middleware = (options: ServerMiddlewarePrimitiveOptions) => {\n return createPrimitive(ServerMiddlewarePrimitive, options);\n};\n\nexport interface ServerMiddlewarePrimitiveOptions {\n /**\n * Path prefix. Middleware applies to all routes starting with this path.\n *\n * @example \"/api\" — matches \"/api/users\", \"/api/orders\", etc.\n */\n path: string;\n\n /**\n * Middleware functions to apply to matching routes.\n */\n use: Middleware[];\n\n /**\n * Limit middleware to specific HTTP methods.\n * If not set, middleware applies to all methods.\n */\n method?: RouteMethod | RouteMethod[];\n\n /**\n * Exclude specific route paths from middleware application.\n */\n exclude?: string[];\n}\n\nexport class ServerMiddlewarePrimitive extends Primitive<ServerMiddlewarePrimitiveOptions> {\n protected readonly serverRouterProvider = $inject(ServerRouterProvider);\n\n protected onInit() {\n const path = this.options.path.replace(/\\/+$/, \"\");\n this.serverRouterProvider.pushMiddleware(`${path}/*`, this.options.use, {\n method: this.options.method,\n exclude: this.options.exclude,\n });\n }\n}\n\n$middleware[KIND] = ServerMiddlewarePrimitive;\n","import {\n $inject,\n createPrimitive,\n KIND,\n PipelinePrimitive,\n type PipelinePrimitiveOptions,\n} from \"alepha\";\nimport type {\n RequestConfigSchema,\n ServerHandler,\n ServerRoute,\n} from \"../interfaces/ServerRequest.ts\";\nimport { ServerRouterProvider } from \"../providers/ServerRouterProvider.ts\";\n\n/**\n * Create a basic endpoint.\n *\n * It's a low level primitive. You probably want to use `$action` instead.\n *\n * @see {@link $action}\n * @see {@link $page}\n */\nexport const $route = <TConfig extends RequestConfigSchema>(\n options: RoutePrimitiveOptions<TConfig>,\n): RoutePrimitive<TConfig> => {\n return createPrimitive(RoutePrimitive<TConfig>, options);\n};\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport interface RoutePrimitiveOptions<\n TConfig extends RequestConfigSchema = RequestConfigSchema,\n> extends Omit<ServerRoute<TConfig>, \"handler\">,\n PipelinePrimitiveOptions<ServerHandler<TConfig>> {}\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport class RoutePrimitive<\n TConfig extends RequestConfigSchema,\n> extends PipelinePrimitive<RoutePrimitiveOptions<TConfig>> {\n protected readonly serverRouterProvider = $inject(ServerRouterProvider);\n\n protected onInit() {\n this.serverRouterProvider.createRoute({\n ...(this.options as any),\n handler: this.handler,\n } as ServerRoute<TConfig>);\n }\n}\n\n$route[KIND] = RoutePrimitive;\n","import { $env, $hook, $inject, Alepha, type Static, t } from \"alepha\";\nimport { DateTimeProvider } from \"alepha/datetime\";\nimport { $logger } from \"alepha/logger\";\nimport { ServerProvider } from \"./ServerProvider.ts\";\nimport { ServerRouterProvider } from \"./ServerRouterProvider.ts\";\n\nconst envSchema = t.object({\n SERVER_PORT: t.integer({\n default: 3000,\n min: 0,\n max: 65535,\n description: \"Set 0 to listen on a random port.\",\n }),\n SERVER_HOST: t.text({\n default: \"localhost\",\n description: \"Set 0.0.0.0 to listen on all interfaces.\",\n }),\n});\n\ndeclare module \"alepha\" {\n interface Env extends Partial<Static<typeof envSchema>> {}\n}\n\nexport class BunHttpServerProvider extends ServerProvider {\n protected readonly alepha = $inject(Alepha);\n protected readonly dateTimeProvider = $inject(DateTimeProvider);\n protected readonly log = $logger();\n protected readonly env = $env(envSchema);\n protected readonly router = $inject(ServerRouterProvider);\n\n protected bunServer?: ReturnType<typeof Bun.serve>;\n\n public get hostname(): string {\n if (this.bunServer) {\n return `http://${this.bunServer.hostname}:${this.bunServer.port}`;\n }\n return `http://${this.env.SERVER_HOST}:${this.env.SERVER_PORT}`;\n }\n\n public readonly start = $hook({\n on: \"start\",\n handler: async () => {\n await this.listen();\n },\n });\n\n protected readonly stop = $hook({\n on: \"stop\",\n handler: async () => {\n if (this.alepha.isProduction()) {\n await this.close();\n return;\n }\n\n // do not await in development & test\n this.close().catch(() => {});\n },\n });\n\n protected async listen() {\n let port = this.env.SERVER_PORT;\n\n // for testing, use a random port if port is 3000 (default)\n if (this.alepha.isTest() && port === 3000) {\n port = 0;\n }\n\n try {\n this.bunServer = Bun.serve({\n port,\n hostname: this.env.SERVER_HOST,\n fetch: async (request: Request) => {\n this.log.trace(`Incoming Bun request -> ${request.url}`);\n\n // Create WebRequestEvent for handleWebRequest\n const webRequestEvent = {\n req: request,\n res: undefined as Response | undefined,\n };\n\n try {\n await this.handleWebRequest(webRequestEvent);\n\n if (!webRequestEvent.res) {\n // No response set, return 500\n return new Response(\"Internal Server Error\", {\n status: 500,\n headers: { \"content-type\": \"text/plain\" },\n });\n }\n\n return webRequestEvent.res;\n } catch (err) {\n this.log.error(\"Error handling request\", err);\n return new Response(\"Internal Server Error\", {\n status: 500,\n headers: { \"content-type\": \"text/plain\" },\n });\n }\n },\n error: (error: Error) => {\n this.log.error(\"Bun server error\", error);\n return new Response(\"Internal Server Error\", {\n status: 500,\n headers: { \"content-type\": \"text/plain\" },\n });\n },\n });\n\n this.log.info(`Server listening on ${this.hostname}/`);\n } catch (err) {\n this.log.error(\"Failed to start Bun server\", err);\n throw err;\n }\n }\n\n protected async close() {\n if (!this.bunServer) {\n return;\n }\n\n try {\n // Bun's server.stop() returns a promise that resolves when all connections are closed\n const stopPromise = this.bunServer.stop();\n\n // Race between stop completion and timeout\n await Promise.race([this.dateTimeProvider.wait(10000), stopPromise]);\n\n this.bunServer = undefined;\n this.log.info(\"Server closed\");\n } catch (err) {\n this.log.error(\"Error closing Bun server\", err);\n throw err;\n }\n }\n}\n","import {\n createServer,\n type IncomingMessage,\n type Server,\n type ServerResponse,\n} from \"node:http\";\nimport type { Socket } from \"node:net\";\nimport { $env, $hook, $inject, Alepha, type Static, t } from \"alepha\";\nimport { DateTimeProvider } from \"alepha/datetime\";\nimport { $logger } from \"alepha/logger\";\nimport { ServerProvider } from \"./ServerProvider.ts\";\nimport { ServerRouterProvider } from \"./ServerRouterProvider.ts\";\n\nconst envSchema = t.object({\n SERVER_PORT: t.integer({\n default: 3000,\n min: 0,\n max: 65535,\n description: \"Set 0 to listen on a random port.\",\n }),\n SERVER_HOST: t.text({\n default: \"localhost\",\n description: \"Set 0.0.0.0 to listen on all interfaces.\",\n }),\n});\n\ndeclare module \"alepha\" {\n interface Env extends Partial<Static<typeof envSchema>> {}\n}\n\nexport class NodeHttpServerProvider extends ServerProvider {\n protected readonly alepha = $inject(Alepha);\n protected readonly dateTimeProvider = $inject(DateTimeProvider);\n protected readonly log = $logger();\n protected readonly env = $env(envSchema);\n protected readonly router = $inject(ServerRouterProvider);\n\n /**\n * Track active connections for fast shutdown.\n */\n protected readonly connections = new Set<Socket>();\n\n /**\n * Get number of active connections.\n */\n public getConnectionsCount(): number {\n return this.connections.size;\n }\n\n /**\n * Server options.\n */\n public readonly options = {\n /**\n * Graceful shutdown timeout in ms.\n * After this, remaining connections are forcefully closed.\n * @default 30000\n */\n shutdownTimeout: 10000,\n };\n\n public get hostname(): string {\n // sometimes hostname is called before .star(), so server may not be created yet (nor listening)\n if (this.server?.listening) {\n const address = this.server.address();\n if (typeof address === \"object\" && address !== null) {\n return `http://${this.env.SERVER_HOST}:${address.port}`;\n }\n }\n\n return `http://${this.env.SERVER_HOST}:${this.env.SERVER_PORT}`;\n }\n\n // Pre-bound error handler to avoid function allocation per request\n protected readonly handleRequestError = (res: ServerResponse, err: Error) => {\n this.log.error(\"Error handling request\", err);\n res.statusCode = 500;\n res.end(\"Internal Server Error\");\n };\n\n public server!: Server;\n\n public readonly configure = $hook({\n on: \"configure\",\n handler: async () => {\n this.server = this.createHttpServer();\n },\n });\n\n public readonly start = $hook({\n on: \"start\",\n handler: async () => {\n await this.listen();\n },\n });\n\n protected requestListener = (req: IncomingMessage, res: ServerResponse) => {\n const promise = this.handleNodeRequest({ req, res });\n promise.catch((err) => this.handleRequestError(res, err));\n };\n\n protected connectionListener = (socket: Socket) => {\n this.connections.add(socket);\n socket.on(\"close\", () => this.connections.delete(socket));\n };\n\n protected createHttpServer(): Server {\n let server: Server;\n\n const existing = this.alepha.store.get(\"alepha.node.server\");\n if (this.alepha.isViteDev() && existing) {\n server = existing;\n server.removeAllListeners(\"request\");\n // --> server.removeAllListeners(\"connection\");\n } else {\n server = createServer({\n // nov 25 - keep connections alive for better performance, cuz we http/1.1 by default\n keepAlive: this.alepha.isProduction(),\n });\n }\n\n server.on(\"request\", this.requestListener);\n\n // Track connections for fast shutdown\n server.on(\"connection\", this.connectionListener);\n\n return server;\n }\n\n protected readonly stop = $hook({\n on: \"stop\",\n handler: async () => {\n await this.close();\n },\n });\n\n protected async listen() {\n if (this.alepha.store.get(\"alepha.node.server\")) {\n return;\n }\n\n let port = this.env.SERVER_PORT;\n\n // for testing, use a random port if port is 3000 (default)\n if (this.alepha.isTest() && port === 3000) {\n port = 0;\n }\n\n await new Promise<void>((resolve, reject) => {\n this.server?.listen(port, this.env.SERVER_HOST, () => {\n this.log.info(`Server listening on ${this.hostname}/`);\n resolve();\n });\n\n this.server?.on(\"error\", (err) => {\n reject(err);\n });\n });\n\n this.alepha.store.set(\"alepha.node.server\", this.server);\n }\n\n protected async close() {\n if (this.alepha.isViteDev()) {\n this.server.removeListener(\"request\", this.requestListener);\n this.server.removeListener(\"connection\", this.connectionListener);\n return;\n }\n\n // Dev/Test: instant shutdown (destroy connections immediately)\n // Production: graceful shutdown (wait for requests to complete, then close)\n if (!this.alepha.isProduction()) {\n this.destroyAllConnections();\n }\n\n // Stop accepting new connections\n const closePromise = new Promise<void>((resolve, reject) => {\n this.server?.close((err) => (err ? reject(err) : resolve()));\n });\n\n if (this.alepha.isProduction() && this.connections.size > 0) {\n // In production, wait for connections with timeout\n const timeout = this.options.shutdownTimeout;\n\n // Set up timeout to force-close connections\n const timeoutId = setTimeout(() => {\n if (this.connections.size > 0) {\n this.log.warn(\n `Shutdown timeout (${timeout}ms) reached, forcing ${this.connections.size} connections to close`,\n );\n // Destroy sockets - this triggers 'close' events which eventually resolves closePromise\n for (const socket of this.connections) {\n socket.destroy();\n }\n }\n }, timeout);\n\n // Wait for server to fully close (all connections closed)\n await closePromise;\n clearTimeout(timeoutId);\n this.connections.clear();\n } else {\n await closePromise;\n }\n\n this.log.info(\"Server closed\");\n }\n\n protected destroyAllConnections() {\n for (const socket of this.connections) {\n socket.destroy();\n }\n this.connections.clear();\n }\n}\n","import { Readable } from \"node:stream\";\nimport { createBrotliDecompress, createGunzip, createInflate } from \"node:zlib\";\nimport type { TSchema } from \"alepha\";\nimport { $atom, $hook, $inject, $use, Alepha, type Static, t } from \"alepha\";\nimport { $logger } from \"alepha/logger\";\nimport { HttpError } from \"../errors/HttpError.ts\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\n/**\n * Body parser configuration atom.\n */\nexport const bodyParserOptions = $atom({\n name: \"alepha.server.body-parser.options\",\n schema: t.object({\n inflate: t.boolean({\n default: true,\n description: \"Enable decompression of request body.\",\n }),\n limit: t.integer({\n default: 100_000, // 100KB\n min: 0,\n description: \"Maximum size of request body in bytes.\",\n }),\n }),\n default: {\n inflate: true,\n limit: 100_000,\n },\n});\n\nexport type BodyParserOptions = Static<typeof bodyParserOptions.schema>;\n\ndeclare module \"alepha\" {\n interface State {\n [bodyParserOptions.key]: BodyParserOptions;\n }\n}\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport class ServerBodyParserProvider {\n protected readonly alepha = $inject(Alepha);\n protected readonly log = $logger();\n protected readonly options = $use(bodyParserOptions);\n\n public readonly onRequest = $hook({\n on: \"server:onRequest\",\n handler: ({ route, request }) => {\n if (request.body) {\n return; // already parsed\n }\n\n let stream: ReadableStream | undefined;\n\n if (request.raw.web?.req.body) {\n stream = request.raw.web.req.body;\n } else if (request.raw.node?.req) {\n const nodeReq = request.raw.node.req as Readable & {\n body?: string | Buffer | object;\n };\n\n if (nodeReq.body !== undefined) {\n // Body was pre-consumed by the runtime (e.g., Vercel serverless).\n // The original stream is already drained — reconstruct from pre-parsed body.\n if (typeof nodeReq.body === \"string\") {\n stream = new ReadableStream({\n start(controller) {\n controller.enqueue(\n new TextEncoder().encode(nodeReq.body as string),\n );\n controller.close();\n },\n });\n } else if (Buffer.isBuffer(nodeReq.body)) {\n stream = new ReadableStream({\n start(controller) {\n controller.enqueue(new Uint8Array(nodeReq.body as Buffer));\n controller.close();\n },\n });\n } else if (\n nodeReq.body !== null &&\n typeof nodeReq.body === \"object\"\n ) {\n // Already parsed as a JSON object — assign directly\n request.body = nodeReq.body;\n return;\n }\n } else {\n stream = Readable.toWeb(\n nodeReq as Readable,\n ) as unknown as ReadableStream;\n }\n }\n\n if (!stream) {\n return;\n }\n\n if (route.schema?.body) {\n const contentLength = request.headers[\"content-length\"];\n if (contentLength) {\n const size = Number.parseInt(contentLength, 10);\n if (!Number.isNaN(size) && size > this.options.limit) {\n throw new HttpError({\n status: 413,\n message: \"Request body size limit exceeded\",\n });\n }\n }\n\n return this.parse(stream, request.headers, route.schema.body)\n .then((body) => {\n if (body) {\n request.body = body;\n }\n })\n .catch((error) => {\n if (error instanceof HttpError) {\n throw error;\n }\n throw new HttpError(\n {\n status: 400,\n message: \"Failed to parse request body\",\n },\n error,\n );\n });\n }\n },\n });\n\n public async parse(\n stream: ReadableStream,\n headers: Record<string, string>,\n schema: TSchema,\n ): Promise<object | string | undefined> {\n const contentType = headers[\"content-type\"];\n const contentEncoding = headers[\"content-encoding\"];\n\n if (!contentType) return undefined;\n\n if (contentType.startsWith(\"text/plain\") || t.schema.isString(schema)) {\n return this.parseText(stream, contentEncoding);\n }\n\n if (contentType.startsWith(\"application/json\")) {\n return this.parseJson(stream, contentEncoding);\n }\n\n if (contentType.startsWith(\"application/x-www-form-urlencoded\")) {\n return this.parseUrlEncoded(stream, contentEncoding);\n }\n\n return undefined;\n }\n\n public async parseText(\n stream: ReadableStream,\n contentEncoding?: string,\n ): Promise<string> {\n const buffer = await this.streamToBuffer(stream);\n const bufferInflated = await this.maybeDecompress(buffer, contentEncoding);\n return bufferInflated.toString(\"utf-8\");\n }\n\n public async parseUrlEncoded(\n stream: ReadableStream,\n contentEncoding?: string,\n ): Promise<object> {\n const text = await this.parseText(stream, contentEncoding);\n const params = new URLSearchParams(text);\n const result: Record<string, string | string[]> = {};\n\n for (const key of params.keys()) {\n const values = params.getAll(key);\n result[key] = values.length === 1 ? values[0] : values;\n }\n\n return result;\n }\n\n public async parseJson(\n stream: ReadableStream,\n contentEncoding?: string,\n ): Promise<object> {\n const text = await this.parseText(stream, contentEncoding);\n return JSON.parse(text);\n }\n\n protected async maybeDecompress(\n buffer: Buffer,\n encoding: string | undefined,\n ): Promise<Buffer> {\n if (!this.options.inflate && encoding) {\n throw new HttpError({\n status: 415,\n message: `Content-Encoding ${encoding} not allowed`,\n });\n }\n\n switch (encoding) {\n case \"gzip\":\n return this.decompressBuffer(buffer, createGunzip());\n case \"deflate\":\n return this.decompressBuffer(buffer, createInflate());\n case \"br\":\n return this.decompressBuffer(buffer, createBrotliDecompress());\n case undefined:\n case \"identity\":\n return buffer;\n default:\n throw new HttpError({\n status: 415,\n message: `Unsupported Content-Encoding: ${encoding}`,\n });\n }\n }\n\n protected decompressBuffer(\n buffer: Buffer,\n transform:\n | import(\"node:zlib\").BrotliDecompress\n | import(\"node:zlib\").Gunzip\n | import(\"node:zlib\").Inflate,\n ): Promise<Buffer> {\n return new Promise((resolve, reject) => {\n const chunks: Buffer[] = [];\n transform\n .on(\"data\", (chunk: Buffer) => chunks.push(chunk))\n .on(\"end\", () => resolve(Buffer.concat(chunks)))\n .on(\"error\", reject);\n transform.end(buffer);\n });\n }\n\n /**\n * Convert Web ReadableStream to Buffer, with a size limit.\n */\n protected async streamToBuffer(stream: ReadableStream): Promise<Buffer> {\n const limit = this.options.limit;\n const chunks: Uint8Array[] = [];\n let totalLength = 0;\n\n const reader = stream.getReader();\n\n try {\n while (true) {\n const { done, value } = await reader.read();\n\n if (done) {\n break;\n }\n\n if (value) {\n totalLength += value.length;\n\n if (totalLength > limit) {\n this.log.error(\n `Body size limit exceeded: ${totalLength} > ${limit}`,\n );\n\n await reader.cancel();\n\n throw new HttpError({\n status: 413,\n message: \"Request body size limit exceeded\",\n });\n }\n\n chunks.push(value);\n }\n }\n\n const combined = new Uint8Array(totalLength);\n let offset = 0;\n\n for (const chunk of chunks) {\n combined.set(chunk, offset);\n offset += chunk.length;\n }\n\n return Buffer.from(\n combined.buffer,\n combined.byteOffset,\n combined.byteLength,\n );\n } finally {\n reader.releaseLock();\n }\n }\n}\n","import { Readable, type Transform } from \"node:stream\";\nimport { ReadableStream } from \"node:stream/web\";\nimport { promisify } from \"node:util\";\nimport * as zlib from \"node:zlib\";\nimport { $atom, $hook, $inject, $use, Alepha, type Static, t } from \"alepha\";\nimport type { ServerResponse } from \"alepha/server\";\n\nconst gzip = promisify(zlib.gzip);\nconst createGzip = zlib.createGzip;\nconst brotli = promisify(zlib.brotliCompress);\nconst createBrotliCompress = zlib.createBrotliCompress;\nconst zstd = zlib.zstdCompress ? promisify(zlib.zstdCompress) : undefined;\nconst createZstdCompress = zstd ? zlib.createZstdCompress : undefined;\n\n// ---------------------------------------------------------------------------------------------------------------------\n\n/**\n * Compression configuration atom.\n */\nexport const compressOptions = $atom({\n name: \"alepha.server.compress.options\",\n schema: t.object({\n disabled: t.optional(\n t.boolean({\n description: \"Disable response compression entirely.\",\n }),\n ),\n allowedContentTypes: t.array(t.string(), {\n description: \"Content types eligible for compression.\",\n }),\n }),\n default: {\n allowedContentTypes: [\n \"application/json\",\n \"text/html\",\n \"application/javascript\",\n \"text/plain\",\n \"text/css\",\n ],\n },\n});\n\nexport type CompressOptions = Static<typeof compressOptions.schema>;\n\ndeclare module \"alepha\" {\n interface State {\n [compressOptions.key]: CompressOptions;\n }\n}\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport class ServerCompressProvider {\n static compressors: Record<\n string,\n | {\n compress: (...args: any[]) => Promise<Buffer>;\n stream: (options?: any) => Transform;\n }\n | undefined\n > = {\n gzip: {\n compress: gzip,\n stream: createGzip,\n },\n br: {\n compress: brotli,\n stream: createBrotliCompress,\n },\n zstd:\n zstd && createZstdCompress\n ? {\n compress: zstd,\n stream: createZstdCompress,\n }\n : undefined,\n };\n\n protected readonly alepha = $inject(Alepha);\n protected readonly options = $use(compressOptions);\n\n public readonly onResponse = $hook({\n on: \"server:onResponse\",\n handler: async ({ request, response }) => {\n if (this.options.disabled) {\n return;\n }\n\n // In serverless (Cloudflare Workers), skip compression entirely:\n // Cloudflare's edge network automatically compresses responses\n if (this.alepha.isServerless()) {\n return;\n }\n\n // skip if already compressed\n if (response.headers[\"content-encoding\"]) {\n return;\n }\n\n const acceptEncoding = request.headers[\"accept-encoding\"]; // skip if no accept-encoding header\n if (!acceptEncoding) {\n return;\n }\n\n // skip if not json or html (for now)\n if (!this.isAllowedContentType(response.headers[\"content-type\"])) {\n return;\n }\n\n for (const encoding of [\"zstd\", \"br\", \"gzip\"] as const) {\n if (\n acceptEncoding.includes(encoding) &&\n ServerCompressProvider.compressors[encoding]\n ) {\n await this.compress(encoding, response);\n return;\n }\n }\n },\n });\n\n protected isAllowedContentType(contentType: string | undefined): boolean {\n if (!contentType) {\n return false;\n }\n\n const lowerContentType = contentType.toLowerCase();\n\n return !!this.options.allowedContentTypes.find((it) =>\n lowerContentType.includes(it),\n );\n }\n\n protected async compress(\n encoding: keyof typeof ServerCompressProvider.compressors,\n response: ServerResponse,\n ): Promise<void> {\n const body = response.body; // can be string or Buffer or ArrayBuffer or Readable\n\n const compressor = ServerCompressProvider.compressors[encoding];\n if (!compressor) {\n return;\n }\n\n const params = this.getParams(encoding);\n\n if (\n typeof body === \"string\" ||\n Buffer.isBuffer(body) ||\n body instanceof ArrayBuffer\n ) {\n const compressed = await compressor.compress(body, {\n params,\n });\n this.setHeaders(response, encoding);\n response.headers[\"content-length\"] = compressed.length.toString();\n response.body = compressed;\n return;\n }\n\n if (typeof body === \"object\" && body instanceof Readable) {\n this.setHeaders(response, encoding);\n response.body = body.pipe(compressor.stream({ params }));\n return;\n }\n\n if (typeof body === \"object\" && body instanceof ReadableStream) {\n this.setHeaders(response, encoding);\n // For streaming responses, use flush mode to avoid buffering\n response.body = this.createFlushingCompressStream(\n body,\n compressor.stream,\n encoding,\n params,\n );\n }\n }\n\n /**\n * Create a compressed stream that flushes after each chunk.\n * This is essential for streaming SSR - ensures each chunk is sent immediately.\n */\n protected createFlushingCompressStream(\n input: ReadableStream,\n createCompressor: (options?: any) => Transform,\n encoding: string,\n params: Record<number, any>,\n ): ReadableStream<Uint8Array> {\n const compressor = createCompressor({\n params,\n flush:\n encoding === \"br\"\n ? zlib.constants.BROTLI_OPERATION_FLUSH\n : zlib.constants.Z_SYNC_FLUSH,\n });\n const reader = Readable.fromWeb(input);\n\n return new ReadableStream<Uint8Array>({\n start(controller) {\n compressor.on(\"data\", (chunk: Buffer) => {\n controller.enqueue(new Uint8Array(chunk));\n });\n\n compressor.on(\"end\", () => {\n controller.close();\n });\n\n compressor.on(\"error\", (err) => {\n controller.error(err);\n });\n\n reader.on(\"data\", (chunk: Buffer) => {\n compressor.write(chunk);\n // Force flush after each chunk for streaming\n // Cast to any because flush() exists on zlib streams but not in Transform type\n const zlibStream = compressor as any;\n if (encoding === \"gzip\") {\n zlibStream.flush(zlib.constants.Z_SYNC_FLUSH);\n } else if (encoding === \"br\") {\n zlibStream.flush(zlib.constants.BROTLI_OPERATION_FLUSH);\n } else if (encoding === \"zstd\") {\n zlibStream.flush();\n }\n });\n\n reader.on(\"end\", () => {\n compressor.end();\n });\n\n reader.on(\"error\", (err) => {\n controller.error(err);\n });\n },\n });\n }\n\n protected getParams(\n encoding: keyof typeof ServerCompressProvider.compressors,\n ): Record<number, any> {\n if (encoding === \"zstd\") {\n return {\n [zlib.constants.ZSTD_c_compressionLevel]: 3, // default compression level for zstd\n };\n }\n if (encoding === \"br\") {\n return {};\n }\n if (encoding === \"gzip\") {\n return {};\n }\n return {};\n }\n\n protected setHeaders(\n response: ServerResponse,\n encoding: keyof typeof ServerCompressProvider.compressors,\n ): void {\n response.headers.vary = \"content-encoding\";\n response.headers[\"content-encoding\"] = encoding;\n }\n}\n","import { $atom, $hook, $inject, $use, Alepha, type Static, t } from \"alepha\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\n/**\n * Helmet security headers configuration atom\n */\nexport const helmetOptions = $atom({\n name: \"alepha.server.helmet.options\",\n schema: t.object({\n disabled: t.optional(\n t.boolean({\n description: \"Disable security headers entirely.\",\n }),\n ),\n isSecure: t.optional(t.boolean()),\n strictTransportSecurity: t.optional(\n t.object({\n maxAge: t.optional(t.number()),\n includeSubDomains: t.optional(t.boolean()),\n preload: t.optional(t.boolean()),\n }),\n ),\n xContentTypeOptions: t.optional(t.boolean()),\n xFrameOptions: t.optional(t.enum([\"DENY\", \"SAMEORIGIN\"])),\n xXssProtection: t.optional(t.boolean()),\n contentSecurityPolicy: t.optional(\n t.object({\n directives: t.record(t.string(), t.any()),\n }),\n ),\n referrerPolicy: t.optional(\n t.enum([\n \"no-referrer\",\n \"no-referrer-when-downgrade\",\n \"origin\",\n \"origin-when-cross-origin\",\n \"same-origin\",\n \"strict-origin\",\n \"strict-origin-when-cross-origin\",\n \"unsafe-url\",\n ]),\n ),\n }),\n default: {\n strictTransportSecurity: { maxAge: 15552000, includeSubDomains: true },\n xFrameOptions: \"SAMEORIGIN\",\n xXssProtection: false,\n referrerPolicy: \"strict-origin-when-cross-origin\",\n },\n});\n\nexport type HelmetOptions = Static<typeof helmetOptions.schema>;\n\ndeclare module \"alepha\" {\n interface State {\n [helmetOptions.key]: HelmetOptions;\n }\n}\n\n// ---------------------------------------------------------------------------------------------------------------------\n\ntype CspDirective = string | string[];\n\nexport interface CspDirectives {\n \"default-src\"?: CspDirective;\n \"script-src\"?: CspDirective;\n \"style-src\"?: CspDirective;\n \"img-src\"?: CspDirective;\n \"connect-src\"?: CspDirective;\n \"font-src\"?: CspDirective;\n \"object-src\"?: CspDirective;\n \"media-src\"?: CspDirective;\n \"frame-src\"?: CspDirective;\n sandbox?: CspDirective | boolean;\n \"report-uri\"?: string;\n \"child-src\"?: CspDirective;\n \"form-action\"?: CspDirective;\n \"frame-ancestors\"?: CspDirective;\n \"plugin-types\"?: CspDirective;\n \"base-uri\"?: CspDirective;\n [key: string]: CspDirective | undefined | boolean;\n}\n\nexport interface CspOptions {\n directives: CspDirectives;\n}\n\nexport interface HstsOptions {\n maxAge?: number;\n includeSubDomains?: boolean;\n preload?: boolean;\n}\n\n/**\n * Provides a configurable way to apply essential HTTP security headers\n * to every server response, without external dependencies.\n */\nexport class ServerHelmetProvider {\n protected readonly alepha = $inject(Alepha);\n\n /**\n * The configuration options loaded from the atom.\n */\n protected readonly options = $use(helmetOptions);\n\n protected defaultCspDirectives(): CspDirectives {\n return {\n \"default-src\": [\"'self'\"],\n \"base-uri\": [\"'self'\"],\n \"font-src\": [\"'self'\", \"https:\", \"data:\"],\n \"form-action\": [\"'self'\"],\n \"frame-ancestors\": [\"'self'\"],\n \"img-src\": [\"'self'\", \"data:\"],\n \"object-src\": [\"'none'\"],\n \"script-src\": [\"'self'\"],\n \"script-src-attr\": [\"'none'\"],\n \"style-src\": [\"'self'\", \"https:\", \"'unsafe-inline'\"],\n \"upgrade-insecure-requests\": [],\n };\n }\n\n protected buildHeaders(): Record<string, string> {\n return this.buildHeadersFromConfig(this.options);\n }\n\n protected buildHeadersFromConfig(\n config: HelmetOptions,\n ): Record<string, string> {\n const headers: Record<string, string> = {};\n const {\n strictTransportSecurity: hsts,\n xContentTypeOptions,\n xFrameOptions,\n xXssProtection,\n contentSecurityPolicy: csp,\n referrerPolicy,\n } = config;\n\n // Strict-Transport-Security\n if (hsts) {\n let value = `max-age=${hsts.maxAge ?? 15552000}`;\n if (hsts.includeSubDomains) value += \"; includeSubDomains\";\n if (hsts.preload) value += \"; preload\";\n headers[\"strict-transport-security\"] = value;\n }\n\n // X-Content-Type-Options\n if (xContentTypeOptions !== false) {\n headers[\"x-content-type-options\"] = \"nosniff\";\n }\n\n // X-Frame-Options\n if (xFrameOptions) {\n headers[\"x-frame-options\"] = xFrameOptions;\n }\n\n // X-XSS-Protection\n if (xXssProtection !== false) {\n headers[\"x-xss-protection\"] = \"1; mode=block\";\n }\n\n // Referrer-Policy\n if (referrerPolicy) {\n headers[\"referrer-policy\"] = referrerPolicy;\n }\n\n // Content-Security-Policy\n if (csp) {\n const directives =\n Object.keys(csp).length === 0\n ? this.defaultCspDirectives()\n : csp.directives;\n headers[\"content-security-policy\"] = Object.entries(directives)\n .map(([key, value]) => {\n const kebabKey = key.replace(\n /[A-Z]/g,\n (letter) => `-${letter.toLowerCase()}`,\n );\n if (Array.isArray(value)) {\n return `${kebabKey} ${value.join(\" \")}`;\n }\n if (typeof value === \"boolean\" && value) {\n return kebabKey;\n }\n return `${kebabKey} ${value}`;\n })\n .join(\"; \");\n }\n\n return headers;\n }\n\n protected readonly onResponse = $hook({\n on: \"server:onResponse\",\n priority: \"first\",\n handler: ({ response }) => {\n if (this.options.disabled) {\n return;\n }\n\n // this check is important. Only add HSTS on HTTPS requests.\n const isSecure =\n response.headers[\"x-forwarded-proto\"] === \"https\" ||\n this.options.isSecure ||\n this.alepha.isProduction();\n\n const headersToSet = this.buildHeaders();\n\n for (const [key, value] of Object.entries(headersToSet)) {\n if (key === \"strict-transport-security\" && !isSecure) {\n continue;\n }\n // don't overwrite headers if they are already set\n if (!response.headers[key]) {\n response.headers[key] = value;\n }\n }\n },\n });\n}\n","import { $hook, $inject, Alepha } from \"alepha\";\nimport { $logger } from \"alepha/logger\";\n\nexport class ServerLoggerProvider {\n protected readonly log = $logger();\n protected readonly alepha = $inject(Alepha);\n\n public readonly onRequest = $hook({\n on: \"server:onRequest\",\n priority: \"first\",\n handler: ({ route, request }) => {\n if (route.silent || request.metadata.vite) {\n return;\n }\n\n request.metadata.now = Date.now();\n\n const search = request.url.search;\n const data: Record<string, string> = {\n method: request.method,\n path: search\n ? `${request.url.pathname}${search}`\n : request.url.pathname,\n };\n\n if (this.alepha.isProduction()) {\n data.agent = request.headers[\"user-agent\"];\n const ip = request.ip;\n if (ip) {\n data.ip = ip;\n }\n }\n\n this.log.info(\"Incoming request\", data);\n },\n });\n\n public readonly onError = $hook({\n on: \"server:onError\",\n priority: \"last\",\n handler: ({ error }) => {\n this.log.error(\"Request has failed\", error);\n },\n });\n\n public readonly onResponse = $hook({\n on: \"server:onResponse\",\n priority: \"last\",\n handler: ({ route, request, response }) => {\n if (route.silent || request.metadata.vite) {\n return;\n }\n\n const ms = Date.now() - request.metadata.now;\n const search = request.url.search;\n this.log.info(\"Request completed\", {\n method: request.method,\n path: search\n ? `${request.url.pathname}${search}`\n : request.url.pathname,\n status: response.status,\n duration: ms,\n });\n },\n });\n}\n","import { Readable } from \"node:stream\";\nimport {\n $atom,\n $hook,\n $inject,\n $use,\n Alepha,\n type FileLike,\n isTypeFile,\n type Static,\n t,\n} from \"alepha\";\nimport { $logger } from \"alepha/logger\";\nimport { HttpError, isMultipart, type ServerRoute } from \"alepha/server\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\n/**\n * Multipart configuration atom.\n */\nexport const multipartOptions = $atom({\n name: \"alepha.server.multipart.options\",\n schema: t.object({\n limit: t.integer({\n default: 10_000_000, // 10MB total\n min: 0,\n description: \"Maximum total size of multipart request body in bytes.\",\n }),\n fileLimit: t.integer({\n default: 5_000_000, // 5MB per file\n min: 0,\n description: \"Maximum size of a single file in bytes.\",\n }),\n fileCount: t.integer({\n default: 10,\n min: 1,\n description: \"Maximum number of files allowed in a single request.\",\n }),\n }),\n default: {\n limit: 10_000_000,\n fileLimit: 5_000_000,\n fileCount: 10,\n },\n});\n\nexport type MultipartOptions = Static<typeof multipartOptions.schema>;\n\ndeclare module \"alepha\" {\n interface State {\n [multipartOptions.key]: MultipartOptions;\n }\n}\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport class ServerMultipartProvider {\n protected readonly alepha = $inject(Alepha);\n protected readonly log = $logger();\n protected readonly options = $use(multipartOptions);\n\n public readonly onRequest = $hook({\n on: \"server:onRequest\",\n handler: async ({ route, request }) => {\n if (request.body) {\n return;\n }\n\n if (!route.schema?.body) {\n return;\n }\n\n let webRequest: Request | undefined;\n\n if (request.raw.web?.req) {\n webRequest = request.raw.web.req;\n } else if (request.raw.node?.req) {\n webRequest = new Request(request.url, {\n method: request.method,\n headers: request.headers,\n body: Readable.toWeb(\n request.raw.node.req as Readable,\n ) as unknown as ReadableStream,\n duplex: \"half\",\n } as RequestInit & { duplex: \"half\" });\n }\n\n if (!webRequest) {\n return;\n }\n\n const contentType = request.headers[\"content-type\"];\n\n // Check content-length before processing to fail fast on oversized requests\n const contentLength = request.headers[\"content-length\"];\n if (contentLength) {\n const size = Number.parseInt(contentLength, 10);\n if (!Number.isNaN(size) && size > this.options.limit) {\n this.log.error(\n `Multipart request size limit exceeded: ${size} > ${this.options.limit}`,\n );\n throw new HttpError({\n status: 413,\n message: `Request body size limit exceeded. Maximum allowed: ${this.options.limit} bytes`,\n });\n }\n }\n\n if (!contentType?.startsWith(\"multipart/form-data\")) {\n if (!isMultipart(route)) {\n return;\n }\n\n throw new HttpError({\n status: 415,\n message: `Invalid content-type: ${contentType} - only \"multipart/form-data\" is accepted`,\n });\n }\n\n request.body = await this.parseMultipart(route, webRequest);\n },\n });\n\n public async parseMultipart(\n route: ServerRoute,\n request: Request,\n ): Promise<Record<string, unknown>> {\n let formData: FormData;\n\n try {\n formData = await request.formData();\n } catch (error) {\n throw new HttpError(\n {\n status: 400,\n message: \"Malformed multipart/form-data\",\n },\n error,\n );\n }\n\n const body: Record<string, any> = {};\n let fileCount = 0;\n let totalSize = 0;\n\n if (route.schema?.body && t.schema.isObject(route.schema.body)) {\n for (const [key, value] of Object.entries(route.schema.body.properties)) {\n if (!t.schema.isSchema(value)) {\n continue;\n }\n\n if (isTypeFile(value)) {\n const file = formData.get(key);\n if (file && typeof file === \"object\" && \"arrayBuffer\" in file) {\n const blob = file as Blob;\n\n fileCount++;\n if (fileCount > this.options.fileCount) {\n this.log.error(\n `Too many files in multipart request: ${fileCount} > ${this.options.fileCount}`,\n );\n throw new HttpError({\n status: 413,\n message: `Too many files. Maximum allowed: ${this.options.fileCount}`,\n });\n }\n\n if (blob.size > this.options.fileLimit) {\n this.log.error(\n `File \"${key}\" exceeds size limit: ${blob.size} > ${this.options.fileLimit}`,\n );\n throw new HttpError({\n status: 413,\n message: `File \"${key}\" exceeds size limit. Maximum allowed: ${this.options.fileLimit} bytes`,\n });\n }\n\n totalSize += blob.size;\n if (totalSize > this.options.limit) {\n this.log.error(\n `Total multipart size exceeds limit: ${totalSize} > ${this.options.limit}`,\n );\n throw new HttpError({\n status: 413,\n message: `Total request size exceeds limit. Maximum allowed: ${this.options.limit} bytes`,\n });\n }\n\n body[key] = this.blobToFileLike(blob, key);\n }\n } else {\n const fieldValue = formData.get(key);\n if (fieldValue !== null) {\n const stringValue =\n typeof fieldValue === \"string\" ? fieldValue : \"\";\n body[key] = this.alepha.codec.decode(value, stringValue);\n }\n }\n }\n }\n\n return body;\n }\n\n protected blobToFileLike(blob: Blob, fieldName: string): FileLike {\n const isFile = blob instanceof File;\n\n return {\n name: isFile ? blob.name : `${fieldName}_${Date.now()}`,\n type: blob.type || \"application/octet-stream\",\n size: blob.size,\n lastModified: isFile ? blob.lastModified : Date.now(),\n\n stream() {\n return blob.stream() as unknown as ReadableStream;\n },\n\n arrayBuffer() {\n return blob.arrayBuffer();\n },\n\n text() {\n return blob.text();\n },\n };\n }\n}\n","import { $hook, $inject, Alepha } from \"alepha\";\nimport { HttpError } from \"../errors/HttpError.ts\";\n\n/**\n * On every request, this provider checks if the server is ready.\n *\n * If the server is not ready, it responds with a 503 status code and a message indicating that the server is not ready yet.\n *\n * The response also includes a `Retry-After` header indicating that the client should retry after 5 seconds.\n */\nexport class ServerNotReadyProvider {\n protected readonly alepha = $inject(Alepha);\n\n public readonly onRequest = $hook({\n on: \"server:onRequest\",\n priority: \"first\",\n handler: ({ request: { reply } }) => {\n if (this.alepha.isReady()) {\n return;\n }\n\n reply.headers[\"Retry-After\"] = \"5\"; // Retry after 5 seconds\n\n throw new HttpError({\n status: 503,\n message: \"Server is not ready yet. Please try again later.\",\n });\n },\n });\n}\n","export const routeMethods = [\n // list of supported http methods\n \"GET\",\n \"POST\",\n \"PUT\",\n \"PATCH\",\n \"DELETE\",\n \"HEAD\",\n \"OPTIONS\",\n \"CONNECT\",\n \"TRACE\",\n] as const;\n\nexport type RouteMethod = (typeof routeMethods)[number];\n","import { HttpError } from \"./HttpError.ts\";\n\nexport class BadRequestError extends HttpError {\n constructor(message = \"Invalid request body\", cause?: unknown) {\n super(\n {\n message,\n status: 400,\n },\n cause,\n );\n }\n}\n","import { HttpError } from \"./HttpError.ts\";\n\nexport class ConflictError extends HttpError {\n constructor(message = \"Entity already exists\", cause?: unknown) {\n super(\n {\n message,\n status: 409,\n },\n cause,\n );\n }\n}\n","import { HttpError } from \"./HttpError.ts\";\n\nexport class ForbiddenError extends HttpError {\n constructor(\n message = \"No permission to access this resource\",\n cause?: unknown,\n ) {\n super(\n {\n message,\n status: 403,\n },\n cause,\n );\n }\n}\n","import { HttpError } from \"./HttpError.ts\";\n\nexport class NotFoundError extends HttpError {\n constructor(message = \"Resource not found\", cause?: unknown) {\n super(\n {\n message,\n status: 404,\n },\n cause,\n );\n }\n}\n","import { HttpError } from \"./HttpError.ts\";\n\nexport class UnauthorizedError extends HttpError {\n readonly name = \"UnauthorizedError\";\n\n constructor(\n message = \"Not allowed to access this resource\",\n cause?: unknown,\n ) {\n super(\n {\n message,\n status: 401,\n },\n cause,\n );\n }\n}\n","import type { Static } from \"alepha\";\nimport { t } from \"alepha\";\n\nexport const okSchema = t.object(\n {\n ok: t.boolean({ description: \"True when operation succeed\" }),\n id: t.optional(t.union([t.text(), t.integer()])),\n count: t.optional(\n t.number({ description: \"Number of resources affected\" }),\n ),\n },\n {\n title: \"Ok\",\n description: \"Generic response after a successful operation on a resource\",\n },\n);\n\nexport type Ok = Static<typeof okSchema>;\n","import { AlephaError, createMiddleware, type Middleware } from \"alepha\";\nimport { DateTimeProvider, type DurationLike } from \"alepha/datetime\";\n\nexport interface CircuitBreakerOptions {\n /**\n * Consecutive failures before opening the circuit.\n */\n threshold: number;\n\n /**\n * Cooldown before transitioning from open to half-open.\n */\n reset: DurationLike;\n}\n\n/**\n * Middleware that implements the circuit breaker pattern.\n *\n * Three states:\n * - **Closed** (normal) — calls pass through. Failures are counted.\n * - **Open** (tripped) — calls are immediately rejected. No handler execution.\n * - **Half-open** (probing) — one call is allowed. Success closes, failure re-opens.\n *\n * ```typescript\n * class PaymentController {\n * charge = $action({\n * use: [$circuit({ threshold: 5, reset: [30, \"seconds\"] })],\n * handler: async ({ body }) => this.paymentGateway.charge(body),\n * });\n * }\n * ```\n */\nexport const $circuit = (options: CircuitBreakerOptions): Middleware => {\n return createMiddleware({\n name: \"$circuit\",\n options,\n handler: ({ alepha, next }) => {\n const dateTimeProvider = alepha.inject(DateTimeProvider);\n const resetMs = dateTimeProvider.duration(options.reset).asMilliseconds();\n\n let state: \"closed\" | \"open\" | \"half-open\" = \"closed\";\n let failures = 0;\n let openedAt = 0;\n\n return async (...args) => {\n if (state === \"open\") {\n if (dateTimeProvider.nowMillis() - openedAt >= resetMs) {\n state = \"half-open\";\n } else {\n throw new AlephaError(\"$circuit: circuit is open\");\n }\n }\n\n try {\n const result = await next(...args);\n failures = 0;\n state = \"closed\";\n return result;\n } catch (error) {\n failures++;\n if (failures >= options.threshold) {\n state = \"open\";\n openedAt = dateTimeProvider.nowMillis();\n }\n throw error;\n }\n };\n },\n });\n};\n","import type { Server } from \"node:http\";\nimport { $module, type Alepha } from \"alepha\";\nimport type { HttpError } from \"./errors/HttpError.ts\";\nimport type {\n NodeRequestEvent,\n RequestConfigSchema,\n ServerRequest,\n ServerRequestConfigEntry,\n ServerResponse,\n ServerRoute,\n WebRequestEvent,\n} from \"./interfaces/ServerRequest.ts\";\nimport {\n $action,\n type ActionPrimitive,\n type ClientRequestOptions,\n} from \"./primitives/$action.ts\";\nimport { $middleware } from \"./primitives/$middleware.ts\";\nimport { $route } from \"./primitives/$route.ts\";\nimport { BunHttpServerProvider } from \"./providers/BunHttpServerProvider.ts\";\nimport { NodeHttpServerProvider } from \"./providers/NodeHttpServerProvider.ts\";\nimport { ServerBodyParserProvider } from \"./providers/ServerBodyParserProvider.ts\";\nimport { ServerCompressProvider } from \"./providers/ServerCompressProvider.ts\";\nimport { ServerHelmetProvider } from \"./providers/ServerHelmetProvider.ts\";\nimport { ServerLoggerProvider } from \"./providers/ServerLoggerProvider.ts\";\nimport { ServerMultipartProvider } from \"./providers/ServerMultipartProvider.ts\";\nimport { ServerNotReadyProvider } from \"./providers/ServerNotReadyProvider.ts\";\nimport { ServerProvider } from \"./providers/ServerProvider.ts\";\nimport { ServerRouterProvider } from \"./providers/ServerRouterProvider.ts\";\nimport { ServerTimingProvider } from \"./providers/ServerTimingProvider.ts\";\nimport type { FetchOptions, HttpAction } from \"./services/HttpClient.ts\";\nimport { HttpClient } from \"./services/HttpClient.ts\";\nimport { ServerRequestParser } from \"./services/ServerRequestParser.ts\";\nimport { UserAgentParser } from \"./services/UserAgentParser.ts\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\ndeclare module \"alepha\" {\n interface State {\n \"alepha.node.server\"?: Server;\n \"alepha.http.request\"?: ServerRequest;\n \"alepha.action.request\"?: ServerRequest;\n }\n interface Hooks {\n // -----------------------------------------------------------------------------------------------------------------\n // Local Actions hooks\n \"action:onRequest\": {\n action: ActionPrimitive<RequestConfigSchema>;\n request: ServerRequest;\n options: ClientRequestOptions;\n context?: Record<string, any>;\n };\n \"action:onResponse\": {\n action: ActionPrimitive<RequestConfigSchema>;\n request: ServerRequest;\n options: ClientRequestOptions;\n response: any;\n };\n // -----------------------------------------------------------------------------------------------------------------\n // Server hooks\n \"server:onRequest\": {\n route: ServerRoute;\n request: ServerRequest;\n };\n \"server:onError\": {\n route: ServerRoute;\n request: ServerRequest;\n error: Error;\n };\n // last chance to modify the response -\n // TODO: probably not really needed, we can also update the response in the onResponse hook...\n \"server:onSend\": {\n route: ServerRoute;\n request: ServerRequest;\n };\n // response is ready\n \"server:onResponse\": {\n route: ServerRoute;\n request: ServerRequest;\n response: ServerResponse;\n };\n // -----------------------------------------------------------------------------------------------------------------\n // Http client hooks\n \"client:onRequest\": {\n route: HttpAction;\n config: ServerRequestConfigEntry;\n options: ClientRequestOptions;\n headers: Record<string, string>;\n request: RequestInit;\n };\n \"client:beforeFetch\": {\n url: string;\n options: FetchOptions;\n request: RequestInit;\n };\n \"client:onError\": {\n route?: HttpAction;\n error: HttpError;\n };\n // -----------------------------------------------------------------------------------------------------------------\n // Internal hooks\n \"node:request\": NodeRequestEvent;\n \"web:request\": WebRequestEvent;\n }\n}\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport * from \"./index.shared.ts\";\nexport * from \"./primitives/$action.ts\";\nexport * from \"./primitives/$circuit.ts\";\nexport * from \"./primitives/$middleware.ts\";\nexport * from \"./primitives/$route.ts\";\nexport * from \"./providers/BunHttpServerProvider.ts\";\nexport * from \"./providers/NodeHttpServerProvider.ts\";\nexport * from \"./providers/ServerCompressProvider.ts\";\nexport * from \"./providers/ServerHelmetProvider.ts\";\nexport * from \"./providers/ServerLoggerProvider.ts\";\nexport * from \"./providers/ServerMultipartProvider.ts\";\nexport * from \"./providers/ServerNotReadyProvider.ts\";\nexport * from \"./providers/ServerProvider.ts\";\nexport * from \"./providers/ServerRouterProvider.ts\";\nexport * from \"./providers/ServerTimingProvider.ts\";\nexport * from \"./services/UserAgentParser.ts\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\n/**\n * Convention-driven HTTP server with automatic validation and type inference.\n *\n * **Features:**\n * - Type-safe API endpoints with schema validation\n * - Lower-level HTTP route definitions\n * - Automatic request/response validation via TypeBox\n * - Convention-based URL generation (`/api/{ActionName}`)\n * - Direct invocation (`run()`) or HTTP (`fetch()`)\n * - Built-in authentication integration\n * - Multipart file upload handling\n * - Response compression (gzip, brotli, zstd)\n * - Security headers (HSTS, CSP, X-Frame-Options, etc.)\n * - Content-type auto-negotiation (JSON, form-data, text)\n * - HTTP methods: GET, POST, PUT, DELETE, PATCH, HEAD, OPTIONS\n * - Error handling: BadRequestError, ValidationError, ForbiddenError, UnauthorizedError, ConflictError, NotFoundError\n *\n * @module alepha.server\n */\nexport const AlephaServer = $module({\n name: \"alepha.server\",\n primitives: [$route, $action, $middleware],\n services: [\n ServerProvider,\n BunHttpServerProvider,\n NodeHttpServerProvider,\n ServerBodyParserProvider,\n ServerCompressProvider,\n ServerHelmetProvider,\n ServerMultipartProvider,\n ServerLoggerProvider,\n ServerNotReadyProvider,\n ServerTimingProvider,\n HttpClient,\n UserAgentParser,\n ServerRequestParser,\n ServerRouterProvider,\n ],\n register: (alepha: Alepha) => {\n // In Vite dev mode, Vite owns the HTTP server - just use base ServerProvider\n // In serverless mode, no HTTP server needed - just use base ServerProvider\n if (!alepha.isServerless() && !alepha.isViteDev()) {\n if (alepha.isBun()) {\n alepha.with({\n optional: true,\n provide: ServerProvider,\n use: BunHttpServerProvider,\n });\n } else {\n alepha.with({\n optional: true,\n provide: ServerProvider,\n use: NodeHttpServerProvider,\n });\n }\n } else {\n alepha.with(ServerProvider);\n }\n\n alepha.with(ServerBodyParserProvider);\n alepha.with(ServerCompressProvider);\n alepha.with(ServerHelmetProvider);\n alepha.with(ServerMultipartProvider);\n\n if (!alepha.isTest()) {\n alepha.with(ServerLoggerProvider);\n alepha.with(ServerNotReadyProvider);\n }\n\n if (!alepha.isProduction()) {\n alepha.with(ServerTimingProvider);\n }\n },\n});\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","names":["envSchema","NodeWebStream","NodeStream","envSchema","ReadableStream","HttpError","isMultipart"],"sources":["../../../src/server/core/helpers/isMultipart.ts","../../../src/server/core/helpers/ServerReply.ts","../../../src/server/core/errors/HttpError.ts","../../../src/server/core/errors/ValidationError.ts","../../../src/server/core/services/UserAgentParser.ts","../../../src/server/core/services/ServerRequestParser.ts","../../../src/server/core/providers/ServerTimingProvider.ts","../../../src/server/core/providers/ServerRouterProvider.ts","../../../src/server/core/providers/ServerProvider.ts","../../../src/server/core/schemas/errorSchema.ts","../../../src/server/core/services/HttpClient.ts","../../../src/server/core/primitives/$action.ts","../../../src/server/core/primitives/$middleware.ts","../../../src/server/core/primitives/$route.ts","../../../src/server/core/primitives/$sse.ts","../../../src/server/core/providers/BunHttpServerProvider.ts","../../../src/server/core/providers/NodeHttpServerProvider.ts","../../../src/server/core/providers/ServerBodyParserProvider.ts","../../../src/server/core/providers/ServerCompressProvider.ts","../../../src/server/core/providers/ServerHelmetProvider.ts","../../../src/server/core/providers/ServerLoggerProvider.ts","../../../src/server/core/providers/ServerMultipartProvider.ts","../../../src/server/core/providers/ServerNotReadyProvider.ts","../../../src/server/core/constants/routeMethods.ts","../../../src/server/core/errors/BadRequestError.ts","../../../src/server/core/errors/ConflictError.ts","../../../src/server/core/errors/ForbiddenError.ts","../../../src/server/core/errors/NotFoundError.ts","../../../src/server/core/errors/UnauthorizedError.ts","../../../src/server/core/schemas/okSchema.ts","../../../src/server/core/primitives/$circuit.ts","../../../src/server/core/index.ts"],"sourcesContent":["import type { RequestConfigSchema } from \"../interfaces/ServerRequest.ts\";\n\n/**\n * Checks if the route has multipart/form-data request body.\n */\nexport const isMultipart = (options: {\n schema?: RequestConfigSchema;\n contentType?: string;\n requestBodyType?: string;\n}): boolean => {\n if (\n options.contentType === \"multipart/form-data\" ||\n options.requestBodyType === \"multipart/form-data\"\n ) {\n return true;\n }\n\n if (options.schema?.body && \"properties\" in options.schema.body) {\n const properties: Record<string, any> = options.schema.body.properties;\n for (const key in properties) {\n if (properties[key].format === \"binary\") {\n return true;\n }\n }\n }\n\n return false;\n};\n","/**\n * Helper for building server replies.\n */\nexport class ServerReply {\n // TODO: make it private\n public headers: Record<string, string> & {\n \"set-cookie\"?: string[];\n } = {};\n\n public status?: number; // default 200, or 204 (no content)\n\n public body?: any;\n\n /**\n * Redirect to a given URL with optional status code (default 302).\n */\n public redirect(url: string, status: number = 302): void {\n this.status = status;\n this.headers.location = url;\n }\n\n // TODO: check if status / header is already set and throw an error if so (for allow to override with force flag)\n\n /**\n * Set the response status code.\n */\n public setStatus(status: number): this {\n this.status = status;\n return this;\n }\n\n /**\n * Set a response header.\n */\n public setHeader(name: string, value: string): this {\n this.headers[name.toLowerCase()] = value;\n return this;\n }\n\n /**\n * Set the response body.\n */\n public setBody(body: any): this {\n this.body = body;\n return this;\n }\n}\n","import { AlephaError } from \"alepha\";\nimport type { ErrorSchema } from \"../schemas/errorSchema.ts\";\n\nexport const isHttpError = (\n error: unknown,\n status?: number,\n): error is HttpErrorLike => {\n const isError =\n !!error &&\n typeof error === \"object\" &&\n \"message\" in error &&\n typeof error.message === \"string\" &&\n \"status\" in error &&\n typeof error.status === \"number\";\n\n if (!isError) {\n return false;\n }\n\n if (status) {\n return (error as HttpErrorLike).status === status;\n }\n\n return true;\n};\n\nexport class HttpError extends AlephaError {\n public name = \"HttpError\";\n\n static is = isHttpError;\n\n static toJSON(error: HttpError): ErrorSchema {\n const json: Record<string, unknown> = {\n error: error.error,\n status: error.status,\n message: error.message,\n };\n\n if (error.details) json.details = error.details;\n if (error.requestId) json.requestId = error.requestId;\n if (error.reason) json.cause = error.reason;\n\n return json as ErrorSchema;\n }\n\n public readonly error: string;\n public readonly status: number;\n\n public readonly requestId?: string;\n public readonly details?: string;\n public readonly reason?: {\n name: string;\n message: string;\n };\n\n constructor(options: Partial<ErrorSchema>, cause?: unknown) {\n super(options.message, {\n cause,\n });\n\n this.status = options.status ?? 500;\n this.details = options.details;\n this.requestId = options.requestId;\n\n if (typeof options.cause === \"object\") {\n this.reason = {\n name: (options.cause as { name: string }).name,\n message: (options.cause as { message: string }).message,\n };\n } else if (cause instanceof Error) {\n this.reason = {\n name: cause.name,\n message: cause.message,\n };\n }\n\n if (this.constructor.name === \"HttpError\") {\n this.error =\n options.error ?? errorNameByStatus[this.status] ?? \"HttpError\";\n } else {\n this.error = this.constructor.name;\n }\n }\n}\n\nexport const errorNameByStatus: Record<number, string> = {\n 400: \"BadRequestError\",\n 401: \"UnauthorizedError\",\n 403: \"ForbiddenError\",\n 404: \"NotFoundError\",\n 405: \"MethodNotAllowedError\",\n 409: \"ConflictError\",\n 410: \"GoneError\",\n 413: \"PayloadTooLargeError\",\n 415: \"UnsupportedMediaTypeError\",\n 429: \"TooManyRequestsError\",\n 500: \"InternalServerError\",\n 501: \"NotImplementedError\",\n 502: \"BadGatewayError\",\n 503: \"ServiceUnavailableError\",\n 504: \"GatewayTimeoutError\",\n};\n\nexport interface HttpErrorLike extends Error {\n status: number;\n}\n","import { HttpError } from \"./HttpError.ts\";\n\nexport class ValidationError extends HttpError {\n constructor(message = \"Validation has failed\", cause?: unknown) {\n super(\n {\n message,\n status: 400,\n },\n cause,\n );\n }\n}\n","export interface UserAgentInfo {\n os:\n | \"Windows\"\n | \"Android\"\n | \"Ubuntu\"\n | \"MacOS\"\n | \"iOS\"\n | \"Linux\"\n | \"FreeBSD\"\n | \"OpenBSD\"\n | \"ChromeOS\"\n | \"BlackBerry\"\n | \"Symbian\"\n | \"Windows Phone\";\n browser:\n | \"Chrome\"\n | \"Firefox\"\n | \"Safari\"\n | \"Edge\"\n | \"Opera\"\n | \"Internet Explorer\"\n | \"Brave\"\n | \"Vivaldi\"\n | \"Samsung Browser\"\n | \"UC Browser\"\n | \"Yandex\";\n device: \"MOBILE\" | \"DESKTOP\" | \"TABLET\";\n}\n\n/**\n * Simple User-Agent parser to detect OS, browser, and device type.\n * This parser is not exhaustive and may not cover all edge cases.\n *\n * Use result for non\n */\nexport class UserAgentParser {\n public parse(userAgent: string = \"\"): UserAgentInfo {\n const ua = userAgent.toLowerCase();\n\n // Default values\n let os: UserAgentInfo[\"os\"] = \"Windows\";\n let browser: UserAgentInfo[\"browser\"] = \"Chrome\";\n let device: UserAgentInfo[\"device\"] = \"DESKTOP\";\n\n // Detect OS - Order matters for specificity\n if (ua.includes(\"windows phone\")) {\n os = \"Windows Phone\";\n } else if (ua.includes(\"windows\")) {\n os = \"Windows\";\n } else if (ua.includes(\"android\")) {\n os = \"Android\";\n } else if (\n ua.includes(\"iphone\") ||\n ua.includes(\"ipad\") ||\n ua.includes(\"ipod\") ||\n (ua.includes(\"ios\") && !ua.includes(\"android\"))\n ) {\n os = \"iOS\";\n } else if (\n ua.includes(\"mac os\") ||\n ua.includes(\"macos\") ||\n ua.includes(\"macintosh\")\n ) {\n os = \"MacOS\";\n } else if (ua.includes(\"cros\") || ua.includes(\"chromeos\")) {\n os = \"ChromeOS\";\n } else if (ua.includes(\"ubuntu\")) {\n os = \"Ubuntu\";\n } else if (ua.includes(\"freebsd\")) {\n os = \"FreeBSD\";\n } else if (ua.includes(\"openbsd\")) {\n os = \"OpenBSD\";\n } else if (ua.includes(\"blackberry\") || ua.includes(\"bb10\")) {\n os = \"BlackBerry\";\n } else if (ua.includes(\"symbian\") || ua.includes(\"symbos\")) {\n os = \"Symbian\";\n } else if (ua.includes(\"linux\") || ua.includes(\"x11\")) {\n os = \"Linux\";\n }\n\n // Detect Browser/browser - Order matters, check most specific first\n if (ua.includes(\"yabrowser\") || ua.includes(\"yandex\")) {\n browser = \"Yandex\";\n } else if (ua.includes(\"brave\")) {\n browser = \"Brave\";\n } else if (ua.includes(\"vivaldi\")) {\n browser = \"Vivaldi\";\n } else if (ua.includes(\"samsungbrowser\") || ua.includes(\"samsung\")) {\n browser = \"Samsung Browser\";\n } else if (ua.includes(\"ucbrowser\") || ua.includes(\"uc browser\")) {\n browser = \"UC Browser\";\n } else if (\n ua.includes(\"opera\") ||\n ua.includes(\"opr/\") ||\n ua.includes(\"opios\")\n ) {\n browser = \"Opera\";\n } else if (\n ua.includes(\"edg/\") ||\n ua.includes(\"edge\") ||\n ua.includes(\"edgios\")\n ) {\n browser = \"Edge\";\n } else if (ua.includes(\"firefox\") && !ua.includes(\"seamonkey\")) {\n browser = \"Firefox\";\n } else if (ua.includes(\"trident\") || ua.includes(\"msie\")) {\n browser = \"Internet Explorer\";\n } else if (\n ua.includes(\"safari\") &&\n !ua.includes(\"chrome\") &&\n !ua.includes(\"chromium\")\n ) {\n browser = \"Safari\";\n } else if (\n ua.includes(\"chrome\") ||\n ua.includes(\"chromium\") ||\n ua.includes(\"crios\")\n ) {\n browser = \"Chrome\";\n }\n\n // Detect Device Type\n const mobileKeywords = [\n \"mobile\",\n \"android\",\n \"iphone\",\n \"ipod\",\n \"blackberry\",\n \"windows phone\",\n \"opera mini\",\n \"iemobile\",\n \"mobile safari\",\n \"nokia\",\n \"symbian\",\n ];\n\n const tabletKeywords = [\n \"ipad\",\n \"tablet\",\n \"kindle\",\n \"silk\",\n \"gt-p\",\n \"sm-t\",\n \"nexus 7\",\n \"nexus 10\",\n ];\n\n const isTablet = tabletKeywords.some((keyword) => ua.includes(keyword));\n const isMobile = mobileKeywords.some((keyword) => ua.includes(keyword));\n\n if (isTablet) {\n device = \"TABLET\";\n } else if (isMobile) {\n device = \"MOBILE\";\n } else {\n device = \"DESKTOP\";\n }\n\n return { os, browser, device };\n }\n}\n","import { $env, $inject, Alepha, t } from \"alepha\";\nimport { CryptoProvider } from \"alepha/crypto\";\nimport { ServerReply } from \"../helpers/ServerReply.ts\";\nimport type {\n RequestGeo,\n RequestReferer,\n ServerRequest,\n ServerRequestData,\n} from \"../interfaces/ServerRequest.ts\";\nimport { UserAgentParser } from \"./UserAgentParser.ts\";\n\nconst envSchema = t.object({\n /**\n * Trust proxy headers (X-Forwarded-For, X-Real-IP) for client IP resolution.\n *\n * Default: true (modern deployments are typically behind a reverse proxy)\n *\n * Set to false only if your server accepts direct connections without a proxy\n * and you want to use the raw connection IP.\n */\n TRUST_PROXY: t.boolean({\n default: true,\n description: \"Trust proxy headers for client IP\",\n }),\n});\n\nexport class ServerRequestParser {\n protected readonly alepha = $inject(Alepha);\n protected readonly userAgentParser = $inject(UserAgentParser);\n protected readonly cryptoProvider = $inject(CryptoProvider);\n protected readonly env = $env(envSchema);\n protected readonly rootURL = new URL(\"http://localhost/\");\n\n public createServerRequest(\n partialRawRequest: Partial<ServerRequestData>,\n ): ServerRequest {\n const rawRequest = {\n method: \"GET\",\n url: this.rootURL,\n headers: {},\n query: {},\n params: {},\n ...partialRawRequest,\n } as ServerRequestData;\n const self = this;\n return {\n method: rawRequest.method,\n url: rawRequest.url,\n raw: rawRequest.raw,\n headers: rawRequest.headers,\n query: rawRequest.query,\n params: rawRequest.params,\n // ---------------------------------------------------------------------------------------------------------------\n // body will be filled by body parser middleware\n body: null,\n // ---------------------------------------------------------------------------------------------------------------\n metadata: {},\n reply: this.alepha.inject(ServerReply, { lifetime: \"transient\" }),\n // ---------------------------------------------------------------------------------------------------------------\n get requestId() {\n return self.getRequestId(rawRequest);\n },\n get ip() {\n return self.getRequestIp(rawRequest);\n },\n get userAgent() {\n return self.getRequestUserAgent(rawRequest);\n },\n get geo() {\n return self.getRequestGeo(rawRequest);\n },\n get isBot() {\n return self.getIsBot(rawRequest);\n },\n get isMobile() {\n return self.getIsMobile(rawRequest);\n },\n get protocol() {\n return self.getProtocol(rawRequest);\n },\n get language() {\n return self.getLanguage(rawRequest);\n },\n get referer() {\n return self.getReferer(rawRequest);\n },\n } as ServerRequest;\n }\n\n public getRequestId(request: ServerRequestData): string {\n return request.headers[\"x-request-id\"] || this.cryptoProvider.randomUUID();\n }\n\n public getRequestUserAgent(request: ServerRequestData) {\n return this.userAgentParser.parse(request.headers[\"user-agent\"]);\n }\n\n public getRequestIp(request: ServerRequestData): string | undefined {\n // Only trust proxy headers when explicitly configured\n if (this.env.TRUST_PROXY) {\n const headers = request.headers;\n\n // X-Forwarded-For: standard proxy header (Cloudflare, Vercel, Nginx, etc.)\n const forwardedFor = headers[\"x-forwarded-for\"];\n if (forwardedFor) {\n return Array.isArray(forwardedFor)\n ? forwardedFor[0]\n : forwardedFor.split(\",\")[0].trim();\n }\n\n // X-Real-IP: alternative proxy header\n const xRealIP = headers[\"x-real-ip\"];\n if (xRealIP) {\n return Array.isArray(xRealIP) ? xRealIP[0] : xRealIP;\n }\n }\n\n // Default: use raw connection IP\n return this.getConnectionIp(request);\n }\n\n protected getConnectionIp(request: ServerRequestData): string | undefined {\n // Get IP from raw connection (Node.js socket)\n const nodeReq = request.raw.node?.req;\n if (nodeReq) {\n return nodeReq.socket?.remoteAddress;\n }\n return undefined;\n }\n\n // ─────────────────────────────────────────────────────────────────────────────\n // Geolocation\n // ─────────────────────────────────────────────────────────────────────────────\n\n public getRequestGeo(request: ServerRequestData): RequestGeo {\n const headers = request.headers;\n\n return {\n // Country: Cloudflare, Vercel, AWS CloudFront\n country:\n headers[\"cf-ipcountry\"] ||\n headers[\"x-vercel-ip-country\"] ||\n headers[\"cloudfront-viewer-country\"],\n\n // City: Cloudflare, Vercel\n city: headers[\"cf-ipcity\"] || headers[\"x-vercel-ip-city\"],\n\n // Region: Cloudflare, Vercel\n region:\n headers[\"cf-region\"] ||\n headers[\"cf-region-code\"] ||\n headers[\"x-vercel-ip-country-region\"],\n\n // Coordinates: Cloudflare, Vercel\n latitude: headers[\"cf-iplatitude\"] || headers[\"x-vercel-ip-latitude\"],\n longitude: headers[\"cf-iplongitude\"] || headers[\"x-vercel-ip-longitude\"],\n };\n }\n\n // ─────────────────────────────────────────────────────────────────────────────\n // Bot detection\n // ─────────────────────────────────────────────────────────────────────────────\n\n protected static readonly BOT_PATTERNS = [\n /bot/i,\n /crawl/i,\n /spider/i,\n /slurp/i,\n /googlebot/i,\n /bingbot/i,\n /yandex/i,\n /baiduspider/i,\n /facebookexternalhit/i,\n /twitterbot/i,\n /linkedinbot/i,\n /whatsapp/i,\n /telegrambot/i,\n /discordbot/i,\n /slackbot/i,\n /applebot/i,\n /duckduckbot/i,\n /semrush/i,\n /ahrefsbot/i,\n /mj12bot/i,\n /dotbot/i,\n /petalbot/i,\n /bytespider/i,\n /gptbot/i,\n /claudebot/i,\n /anthropic/i,\n /curl/i,\n /wget/i,\n /python-requests/i,\n /axios/i,\n /node-fetch/i,\n /go-http-client/i,\n /java\\//i,\n /libwww/i,\n /httpunit/i,\n /nutch/i,\n /phpcrawl/i,\n /biglotron/i,\n /teoma/i,\n /convera/i,\n /gigablast/i,\n /ia_archiver/i,\n /webmon/i,\n /httrack/i,\n /grub\\.org/i,\n /netresearchserver/i,\n /speedy/i,\n /fluffy/i,\n /findlink/i,\n /panscient/i,\n /ips-agent/i,\n /yanga/i,\n /cyberpatrol/i,\n /postrank/i,\n /page2rss/i,\n /linkdex/i,\n /ezooms/i,\n /heritrix/i,\n /findthatfile/i,\n /europarchive\\.org/i,\n /mappydata/i,\n /eright/i,\n /apercite/i,\n /aboundex/i,\n /domaincrawler/i,\n /wbsearchbot/i,\n /summify/i,\n /ccbot/i,\n /edisterbot/i,\n /seznambot/i,\n /ec2linkfinder/i,\n /gslfbot/i,\n /aihitbot/i,\n /intelium_bot/i,\n /yeti/i,\n /retrevopageanalyzer/i,\n /lb-spider/i,\n /sogou/i,\n /lssbot/i,\n /careerbot/i,\n /wotbox/i,\n /wocbot/i,\n /ichiro/i,\n /duckduckgo/i,\n /lssrocketcrawler/i,\n /drupact/i,\n /webcompanycrawler/i,\n /acoonbot/i,\n /openindexspider/i,\n /screaming frog/i,\n /pingdom/i,\n /uptimerobot/i,\n /headlesschrome/i,\n /phantomjs/i,\n /prerender/i,\n /lighthouse/i,\n /pagespeed/i,\n ];\n\n public getIsBot(request: ServerRequestData): boolean {\n const ua = request.headers[\"user-agent\"];\n if (!ua) return false;\n\n return ServerRequestParser.BOT_PATTERNS.some((pattern) => pattern.test(ua));\n }\n\n // ─────────────────────────────────────────────────────────────────────────────\n // Mobile detection\n // ─────────────────────────────────────────────────────────────────────────────\n\n protected static readonly MOBILE_PATTERNS = [\n /android/i,\n /webos/i,\n /iphone/i,\n /ipad/i,\n /ipod/i,\n /blackberry/i,\n /iemobile/i,\n /opera mini/i,\n /mobile/i,\n /tablet/i,\n /kindle/i,\n /silk/i,\n /fennec/i,\n /windows phone/i,\n /windows ce/i,\n /symbian/i,\n /palm/i,\n /webmate/i,\n ];\n\n public getIsMobile(request: ServerRequestData): boolean {\n const ua = request.headers[\"user-agent\"];\n if (!ua) return false;\n\n return ServerRequestParser.MOBILE_PATTERNS.some((pattern) =>\n pattern.test(ua),\n );\n }\n\n // ─────────────────────────────────────────────────────────────────────────────\n // Protocol detection\n // ─────────────────────────────────────────────────────────────────────────────\n\n public getProtocol(request: ServerRequestData): \"http\" | \"https\" {\n // Check proxy headers first\n const forwardedProto = request.headers[\"x-forwarded-proto\"];\n if (forwardedProto) {\n return forwardedProto.toLowerCase() === \"https\" ? \"https\" : \"http\";\n }\n\n // Cloudflare-specific header\n const cfVisitorHeader = request.headers[\"cf-visitor\"];\n if (cfVisitorHeader) {\n try {\n const parsed = JSON.parse(cfVisitorHeader);\n if (parsed.scheme === \"https\") return \"https\";\n } catch {\n // Ignore parse errors\n }\n }\n\n // Check URL scheme\n if (request.url.protocol === \"https:\") {\n return \"https\";\n }\n\n return \"http\";\n }\n\n // ─────────────────────────────────────────────────────────────────────────────\n // Language detection\n // ─────────────────────────────────────────────────────────────────────────────\n\n public getLanguage(request: ServerRequestData): string | undefined {\n const acceptLanguage = request.headers[\"accept-language\"];\n if (!acceptLanguage) return undefined;\n\n // Parse Accept-Language header\n // Format: \"en-US,en;q=0.9,fr;q=0.8\"\n const firstLang = acceptLanguage.split(\",\")[0];\n if (!firstLang) return undefined;\n\n // Remove quality value if present (e.g., \"en;q=0.9\" -> \"en\")\n const lang = firstLang.split(\";\")[0].trim();\n\n return lang || undefined;\n }\n\n // ─────────────────────────────────────────────────────────────────────────────\n // Referer parsing\n // ─────────────────────────────────────────────────────────────────────────────\n\n public getReferer(request: ServerRequestData): RequestReferer | undefined {\n const referer = request.headers.referer || request.headers.referrer;\n if (!referer) return undefined;\n\n try {\n const url = new URL(referer);\n return {\n url: referer,\n hostname: url.hostname,\n pathname: url.pathname,\n };\n } catch {\n // Invalid URL\n return undefined;\n }\n }\n}\n","import { $hook, $inject, Alepha } from \"alepha\";\nimport { $logger } from \"alepha/logger\";\n\ntype TimingMap = Record<string, [number, number]>;\n\nexport class ServerTimingProvider {\n protected readonly log = $logger();\n protected readonly alepha = $inject(Alepha);\n\n public options = {\n prefix: this.alepha.env.APP_NAME\n ? `${this.alepha.env.APP_NAME.toLowerCase()}-`\n : \"\",\n disabled: this.alepha.isProduction(),\n };\n\n public readonly onRequest = $hook({\n priority: \"first\",\n on: \"server:onRequest\",\n handler: ({ request }) => {\n if (this.options.disabled) {\n return;\n }\n\n request.metadata.timing = {};\n request.metadata.timing[this.handlerName] = [Date.now()];\n },\n });\n\n public readonly onResponse = $hook({\n priority: \"last\",\n on: \"server:onResponse\",\n handler: ({ request }) => {\n if (this.options.disabled) {\n return;\n }\n\n if (request.metadata.timing) {\n this.setDuration(this.handlerName, request.metadata.timing);\n\n let timingHeader = \"\";\n\n for (const [name, [start, duration]] of Object.entries(\n request.metadata.timing as TimingMap,\n )) {\n if (typeof start !== \"number\" || typeof duration !== \"number\") {\n this.log.warn(\n `Invalid timing for '${name}': [${start}, ${duration}]`,\n );\n continue;\n }\n\n const formattedName =\n this.options.prefix + name.replace(/[^a-zA-Z0-9-]/g, \"-\");\n timingHeader += `${formattedName};dur=${duration}, `;\n }\n\n if (request.reply.headers[\"Server-Timing\"]) {\n request.reply.headers[\"Server-Timing\"] += `, ${timingHeader}`;\n } else {\n request.reply.headers[\"Server-Timing\"] = timingHeader;\n }\n }\n },\n });\n\n protected get handlerName() {\n return `request`;\n }\n\n public beginTiming(name: string): void {\n if (this.options.disabled) {\n return;\n }\n\n const request = this.alepha.store.get(\"alepha.http.request\");\n if (!request) {\n return;\n }\n\n request.metadata ??= {};\n request.metadata.timing ??= {};\n request.metadata.timing[name] = [Date.now()];\n }\n\n public endTiming(name: string): void {\n if (this.options.disabled) {\n return;\n }\n\n const request = this.alepha.store.get(\"alepha.http.request\");\n if (!request) {\n return;\n }\n\n if (!request.metadata?.timing || !(name in request.metadata.timing)) {\n this.log.warn(`No timing found for '${name}'.`);\n return;\n }\n\n const start = request.metadata.timing[name]?.[0];\n if (typeof start !== \"number\") {\n this.log.warn(`Invalid timing start for '${name}': ${start}`);\n return;\n }\n\n this.setDuration(name, request.metadata.timing);\n }\n\n protected setDuration(name: string, timing: TimingMap): void {\n timing[name] = [timing[name][0], Date.now() - timing[name][0]];\n }\n}\n","import { randomUUID } from \"node:crypto\";\nimport { Readable as NodeStream } from \"node:stream\";\nimport { ReadableStream as NodeWebStream } from \"node:stream/web\";\nimport {\n $inject,\n Alepha,\n isFileLike,\n isTypeFile,\n type Middleware,\n PipelineHandler,\n t,\n} from \"alepha\";\nimport { $logger } from \"alepha/logger\";\nimport { RouterProvider } from \"alepha/router\";\nimport type { RouteMethod } from \"../constants/routeMethods.ts\";\nimport { errorNameByStatus, HttpError } from \"../errors/HttpError.ts\";\nimport { ValidationError } from \"../errors/ValidationError.ts\";\nimport type { ServerReply } from \"../helpers/ServerReply.ts\";\nimport type {\n RequestConfigSchema,\n ResponseKind,\n ServerRequest,\n ServerRequestConfig,\n ServerRoute,\n ServerRouteInput,\n ServerRouteMatcher,\n} from \"../interfaces/ServerRequest.ts\";\nimport { ServerRequestParser } from \"../services/ServerRequestParser.ts\";\nimport { ServerTimingProvider } from \"./ServerTimingProvider.ts\";\n\n/**\n * Main router for all routes server side.\n *\n * Reminder:\n * - $route => generic route\n * - $action => action route (for API calls)\n * - $page => React route (for React SSR)\n */\nexport class ServerRouterProvider extends RouterProvider<ServerRouteMatcher> {\n protected readonly log = $logger();\n protected readonly alepha = $inject(Alepha);\n protected readonly routes: ServerRoute[] = [];\n protected readonly serverTimingProvider = $inject(ServerTimingProvider);\n protected readonly serverRequestParser = $inject(ServerRequestParser);\n protected readonly queryKeysCache = new WeakMap<object, string[]>();\n protected readonly globalMiddlewareRegistry: GlobalMiddlewareEntry[] = [];\n\n /**\n * Get cached keys for a query schema, computing them lazily on first access.\n */\n protected getQuerySchemaKeys(schema: { properties: object }): string[] {\n let keys = this.queryKeysCache.get(schema.properties);\n if (!keys) {\n keys = Object.keys(schema.properties);\n this.queryKeysCache.set(schema.properties, keys);\n }\n return keys;\n }\n\n public pushMiddleware(\n pattern: string,\n middleware: Middleware[],\n options?: PushMiddlewareOptions,\n ): void {\n this.globalMiddlewareRegistry.push({ pattern, middleware, ...options });\n for (const route of this.getRoutes(pattern)) {\n if (this.matchesMiddlewareFilter(route, options)) {\n route.handler.use(...middleware);\n }\n }\n }\n\n /**\n * Check if a route passes the middleware filter options (method, exclude).\n */\n protected matchesMiddlewareFilter(\n route: ServerRoute,\n options?: PushMiddlewareOptions,\n ): boolean {\n if (options?.method) {\n const methods = Array.isArray(options.method)\n ? options.method\n : [options.method];\n if (\n route.method &&\n !methods.includes(route.method.toUpperCase() as RouteMethod)\n ) {\n return false;\n }\n }\n if (options?.exclude) {\n for (const exclude of options.exclude) {\n if (route.path === exclude || route.path.startsWith(`${exclude}/`)) {\n return false;\n }\n }\n }\n return true;\n }\n\n /**\n * Check if a route path matches a pattern.\n * Wildcard `*` at end = prefix match, otherwise exact match.\n */\n protected matchesPattern(routePath: string, pattern: string): boolean {\n if (pattern.endsWith(\"*\")) {\n return routePath.startsWith(pattern.slice(0, -1));\n }\n return routePath === pattern;\n }\n\n /**\n * Get all registered routes, optionally filtered by a pattern.\n *\n * Pattern accept simple wildcard '*' at the end.\n * Example: '/api/*' will match all routes starting with '/api/' but '/api/' will match only that exact route.\n */\n public getRoutes(pattern?: string): ServerRoute[] {\n if (pattern) {\n return this.routes.filter((route) =>\n this.matchesPattern(route.path, pattern),\n );\n }\n return this.routes;\n }\n\n /**\n * Create a new server route.\n *\n * Accepts both `PipelineHandler` and plain handler functions.\n * Plain functions are auto-wrapped in `PipelineHandler`.\n */\n public createRoute<TConfig extends RequestConfigSchema = RequestConfigSchema>(\n input: ServerRouteInput<TConfig>,\n ): void {\n // Auto-wrap plain function handlers in PipelineHandler\n if (!(input.handler instanceof PipelineHandler)) {\n (input as any).handler = new PipelineHandler(input.handler);\n }\n\n const route = input as unknown as ServerRoute<TConfig>;\n route.method ??= \"GET\";\n route.method = route.method.toUpperCase() as RouteMethod;\n\n this.routes.push(route);\n\n for (const entry of this.globalMiddlewareRegistry) {\n if (this.matchesPattern(route.path, entry.pattern)) {\n if (this.matchesMiddlewareFilter(route, entry)) {\n route.handler.use(...entry.middleware);\n }\n }\n }\n\n const path = `/${route.method}/${route.path}`.replace(/\\/+/g, \"/\");\n const responseKind = this.getResponseType(route.schema);\n\n this.log.trace(`Create route ${path}`);\n\n this.push({\n path,\n handler: (rawRequest) => {\n const request =\n this.serverRequestParser.createServerRequest(rawRequest);\n\n return this.alepha.context.run(\n () => this.processRequest(request, route, responseKind),\n { context: this.getContextId(rawRequest.headers) },\n );\n },\n });\n }\n\n /**\n * Get or generate a context ID from request headers.\n *\n * It first checks for common headers like 'x-request-id' or 'x-correlation-id' that may be set by proxies or clients.\n * If none of these headers are present, it generates a new UUID to ensure uniqueness.\n *\n * Note: In production environments, it's recommended to have a proxy (e.g. Nginx, Cloudflare) that sets a consistent request ID header for better traceability across services.\n */\n protected getContextId(headers: Record<string, string>): string {\n // note: we trust these headers as all our environments are behind a proxy\n return (\n headers[\"x-request-id\"] || headers[\"x-correlation-id\"] || randomUUID()\n );\n }\n\n /**\n * Process an incoming request through the full lifecycle:\n * onRequest → handler → onSend → response → onResponse\n */\n protected async processRequest(\n request: ServerRequest,\n route: ServerRoute,\n responseKind: ResponseKind,\n ) {\n try {\n await this.runRouteHandler(route, request, responseKind);\n } catch (error) {\n await this.errorHandler(route, request, error as Error);\n }\n\n const payload = { request, route, response: undefined as any };\n await this.alepha.events.emit(\"server:onSend\", payload, { catch: true });\n\n const reply = request.reply;\n const response = {\n status: reply.status ?? (reply.body ? 200 : 204), // default status: 200 if body is set, otherwise 204\n headers: reply.headers,\n body: reply.body as any,\n };\n\n payload.response = response;\n await this.alepha.events.emit(\"server:onResponse\", payload, {\n catch: true,\n });\n\n return response;\n }\n\n /**\n * Run the route handler with request validation and response serialization.\n */\n protected async runRouteHandler(\n route: ServerRoute,\n request: ServerRequest,\n responseKind: ResponseKind,\n ) {\n // Built-in hooks: body parsing, security, logging\n await this.alepha.events.emit(\"server:onRequest\", { request, route });\n\n // A hook (e.g. CORS preflight) may have already written the response\n const reply = request.reply;\n if (reply.body || (reply.status && reply.status >= 200)) {\n return;\n }\n\n // Store route path info for middleware (e.g. $secure auto-permission lookup)\n request.metadata.routePath = route.path;\n request.metadata.routeMethod = route.method;\n\n // Make the request available to handlers via alepha.context\n this.alepha.set(\"alepha.http.request\", request);\n\n const timing = this.serverTimingProvider;\n\n timing.beginTiming(\"validateRequest\");\n try {\n this.validateRequest(route, request);\n } finally {\n timing.endTiming(\"validateRequest\");\n }\n\n timing.beginTiming(\"runHandler\");\n try {\n const result = await route.handler.run(request);\n if (result) {\n reply.body = result;\n }\n } finally {\n timing.endTiming(\"runHandler\");\n }\n\n timing.beginTiming(\"serializeResponse\");\n try {\n this.serializeResponse(route, reply, responseKind);\n } finally {\n timing.endTiming(\"serializeResponse\");\n }\n }\n\n /**\n * Transform reply body based on response kind and route schema.\n */\n public serializeResponse(\n route: ServerRoute,\n reply: ServerReply,\n responseKind: ResponseKind,\n ): void {\n const headers = reply.headers;\n\n if (responseKind === \"json\" && route.schema?.response) {\n headers[\"content-type\"] = \"application/json\";\n reply.body = this.alepha.codec.encode(route.schema.response, reply.body, {\n as: \"string\" as const,\n });\n return;\n }\n\n if (responseKind === \"file\") {\n if (!isFileLike(reply.body)) {\n throw new HttpError({\n status: 500,\n message: \"Invalid response body - not a file\",\n });\n }\n headers[\"content-type\"] = reply.body.type;\n headers[\"content-disposition\"] =\n `attachment; filename=\"${reply.body.name.replaceAll('\"', \"\")}\"`;\n reply.body = reply.body.stream();\n return;\n }\n\n if (responseKind === \"text\") {\n reply.body = String(reply.body);\n // Detect HTML responses (starts with <!DOCTYPE html>)\n if (\n reply.body.length > 15 &&\n reply.body.charCodeAt(0) === 60 &&\n reply.body.startsWith(\"<!DOCTYPE html>\")\n ) {\n headers[\"content-type\"] ??= \"text/html; charset=UTF-8\";\n } else {\n headers[\"content-type\"] ??= \"text/plain\";\n }\n return;\n }\n\n if (reply.body == null || responseKind === \"void\") {\n delete headers[\"content-type\"];\n reply.body = undefined;\n return;\n }\n\n if (Buffer.isBuffer(reply.body)) {\n headers[\"content-type\"] ??= \"application/octet-stream\";\n return;\n }\n\n if (\n reply.body instanceof NodeWebStream ||\n reply.body instanceof NodeStream\n ) {\n headers[\"content-type\"] ??= \"application/octet-stream\";\n return;\n }\n\n // Plain objects/arrays → auto-serialize as JSON\n if (typeof reply.body === \"object\") {\n headers[\"content-type\"] ??= \"application/json\";\n reply.body = JSON.stringify(reply.body);\n return;\n }\n\n headers[\"content-type\"] ??= \"text/plain\";\n reply.body = String(reply.body);\n }\n\n /**\n * Determine response type based on route schema.\n */\n protected getResponseType(schema?: RequestConfigSchema): ResponseKind {\n if (schema?.response) {\n if (\n t.schema.isObject(schema.response) ||\n t.schema.isRecord(schema.response) ||\n t.schema.isArray(schema.response)\n ) {\n return \"json\";\n }\n\n if (\n t.schema.isString(schema.response) ||\n t.schema.isInteger(schema.response) ||\n t.schema.isNumber(schema.response) ||\n t.schema.isBoolean(schema.response)\n ) {\n return \"text\";\n }\n\n if (isTypeFile(schema.response)) {\n return \"file\";\n }\n\n if (t.schema.isVoid(schema.response)) {\n return \"void\";\n }\n }\n\n return \"any\";\n }\n\n /**\n * Handle errors during request processing.\n */\n protected async errorHandler(\n route: ServerRoute,\n request: ServerRequest,\n error: Error,\n ) {\n // Reset body — it's likely invalid after an error\n const reply = request.reply;\n reply.body = null;\n\n // Let error hooks handle it first (e.g. custom error pages, Sentry)\n await this.alepha.events.emit(\"server:onError\", { request, route, error });\n // |\n // |\n // --> If a hook already set the response, we're done!\n if (reply.status) return;\n\n const requestId = request.requestId;\n\n if (error instanceof HttpError) {\n reply.status = error.status;\n reply.headers[\"content-type\"] = \"application/json\";\n const errorJson = HttpError.toJSON(error);\n errorJson.requestId = requestId;\n reply.body = JSON.stringify(errorJson);\n return;\n }\n\n // Errors with a known HTTP status (e.g. from upstream libraries)\n if (\n \"status\" in error &&\n typeof error.status === \"number\" &&\n !!errorNameByStatus[error.status]\n ) {\n const status = error.status;\n reply.status = status;\n reply.headers[\"content-type\"] = \"application/json\";\n reply.body = JSON.stringify({\n status,\n error: errorNameByStatus[status],\n message: error.message,\n requestId,\n });\n return;\n }\n\n // Fallback: unknown error → 500\n reply.status = 500;\n reply.headers[\"content-type\"] = \"application/json\";\n reply.body = JSON.stringify({\n status: 500,\n error: \"InternalServerError\",\n message: error.message,\n requestId,\n });\n }\n\n /**\n * Validate incoming request against route schema.\n */\n public validateRequest(\n route: { schema?: RequestConfigSchema },\n request: ServerRequestConfig,\n ) {\n if (route.schema?.params) {\n try {\n request.params = this.alepha.codec.validate(\n route.schema.params,\n request.params,\n ) as any;\n } catch (error) {\n throw new ValidationError(\"Invalid request params\", error);\n }\n }\n\n if (route.schema?.query) {\n try {\n const schemaQuery = route.schema.query;\n const keys = this.getQuerySchemaKeys(schemaQuery);\n const query: Record<string, any> = {};\n\n for (const key of keys) {\n if (request.query[key] != null) {\n query[key] = this.alepha.codec.decode(\n schemaQuery.properties[key],\n request.query[key],\n );\n }\n }\n\n request.query = query;\n } catch (error) {\n throw new ValidationError(\"Invalid request query\", error);\n }\n }\n\n if (route.schema?.headers) {\n try {\n request.headers = this.alepha.codec.validate(\n route.schema.headers,\n request.headers,\n ) as any;\n } catch (error) {\n throw new ValidationError(\"Invalid request header\", error);\n }\n }\n\n if (route.schema?.body) {\n if (t.schema.isString(route.schema.body)) {\n if (typeof request.body !== \"string\") {\n throw new ValidationError(\"Request body is not a string\");\n }\n } else {\n try {\n request.body = this.alepha.codec.decode(\n route.schema.body,\n request.body,\n );\n } catch (error) {\n throw new ValidationError(\"Invalid request body\", error);\n }\n }\n }\n }\n}\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport interface PushMiddlewareOptions {\n method?: RouteMethod | RouteMethod[];\n exclude?: string[];\n}\n\ninterface GlobalMiddlewareEntry extends PushMiddlewareOptions {\n pattern: string;\n middleware: Middleware[];\n}\n","import { Readable } from \"node:stream\";\nimport { $hook, $inject, Alepha } from \"alepha\";\nimport { DateTimeProvider } from \"alepha/datetime\";\nimport { $logger } from \"alepha/logger\";\nimport type { Route } from \"alepha/router\";\nimport type { RouteMethod } from \"../constants/routeMethods.ts\";\nimport type {\n NodeRequestEvent,\n ServerRequestData,\n WebRequestEvent,\n} from \"../interfaces/ServerRequest.ts\";\nimport { ServerRouterProvider } from \"./ServerRouterProvider.ts\";\n\n// ============================================================================\n// Performance Constants\n// ============================================================================\n\n// Note: We cannot use frozen/shared empty objects here because downstream code\n// may mutate params/query (e.g., validation, React page rendering)\n\n// Header constants for fast property access\nconst HEADER_X_FORWARDED_PROTO = \"x-forwarded-proto\";\nconst HEADER_HOST = \"host\";\n\n// Protocol prefixes\nconst PROTO_HTTPS = \"https://\";\nconst PROTO_HTTP = \"http://\";\n\n/**\n * Base server provider to handle incoming requests and route them.\n *\n * This is the default implementation for serverless environments.\n *\n * ServerProvider supports both Node.js HTTP requests and Web (Fetch API) requests.\n */\nexport class ServerProvider {\n protected readonly log = $logger();\n protected readonly alepha = $inject(Alepha);\n protected readonly dateTimeProvider = $inject(DateTimeProvider);\n protected readonly router = $inject(ServerRouterProvider);\n\n protected readonly internalServerErrorMessage = \"Internal Server Error\";\n\n // Pre-allocated error response to avoid object creation per failed request\n protected readonly internalErrorResponse = Object.freeze({\n status: 500,\n headers: Object.freeze({ \"content-type\": \"text/plain\" }),\n body: this.internalServerErrorMessage,\n });\n\n // Pre-bound error handler to avoid function allocation per request\n protected readonly handleInternalError = () => this.internalErrorResponse;\n\n // ============================================================================\n // P1: URL Base Cache - cache protocol+host per unique host header\n // Avoids string concatenation on every request\n // ============================================================================\n protected readonly urlBaseCache = new Map<string, string>();\n\n /**\n * Get cached URL base (protocol + host) for a given host header.\n * Caches the result to avoid repeated string concatenation.\n */\n protected getUrlBase(headers: Record<string, string>): string {\n const host = headers[HEADER_HOST];\n let base = this.urlBaseCache.get(host);\n if (!base) {\n const proto =\n headers[HEADER_X_FORWARDED_PROTO] === \"https\"\n ? PROTO_HTTPS\n : PROTO_HTTP;\n base = proto + host;\n // Limit cache size to prevent memory leaks from many unique hosts\n if (this.urlBaseCache.size < 100) {\n this.urlBaseCache.set(host, base);\n }\n }\n return base;\n }\n\n // ============================================================================\n // P0: Manual Query String Parser - faster than URLSearchParams\n // Parses query string without creating URL object\n // ============================================================================\n\n /**\n * Parse query string manually - faster than new URL() + URLSearchParams.\n * Returns empty object if no query string.\n */\n protected parseQueryString(rawUrl: string): Record<string, string> {\n const qIndex = rawUrl.indexOf(\"?\");\n if (qIndex === -1) {\n return {};\n }\n\n const qs = rawUrl.slice(qIndex + 1);\n if (!qs) {\n return {};\n }\n\n const query: Record<string, string> = {};\n let start = 0;\n let eqIdx = -1;\n\n for (let i = 0; i <= qs.length; i++) {\n const char = i < qs.length ? qs.charCodeAt(i) : 38; // '&' at end\n\n if (char === 61) {\n // '='\n eqIdx = i;\n } else if (char === 38) {\n // '&'\n if (eqIdx !== -1 && eqIdx > start) {\n const key = qs.slice(start, eqIdx);\n const value = qs.slice(eqIdx + 1, i);\n // Only decode if necessary (contains % or +)\n query[this.fastDecode(key)] = this.fastDecode(value);\n }\n start = i + 1;\n eqIdx = -1;\n }\n }\n\n return query;\n }\n\n /**\n * Fast decode - only calls decodeURIComponent if needed.\n */\n protected fastDecode(str: string): string {\n // Fast path: no encoding needed\n if (str.indexOf(\"%\") === -1 && str.indexOf(\"+\") === -1) {\n return str;\n }\n // Replace + with space before decoding\n try {\n return decodeURIComponent(str.replace(/\\+/g, \" \"));\n } catch {\n return str;\n }\n }\n\n public get hostname(): string {\n if (this.alepha.isViteDev()) {\n return `http://localhost:${this.alepha.env.SERVER_PORT}`;\n }\n return \"\"; // no hostname in serverless mode\n }\n\n /**\n * When a Node.js HTTP request is received from outside. (Vercel, AWS Lambda, etc.)\n */\n protected readonly onNodeRequest = $hook({\n on: \"node:request\",\n handler: (ev) => this.handleNodeRequest(ev),\n });\n\n /**\n * When a Web (Fetch API) request is received from outside. (Netlify, Cloudflare Workers, etc.)\n */\n protected readonly onWebRequest = $hook({\n on: \"web:request\",\n handler: (ev) => {\n return this.handleWebRequest(ev);\n },\n });\n\n /**\n * Handle Node.js HTTP request event.\n *\n * Optimized to avoid expensive URL parsing when possible.\n */\n public async handleNodeRequest(\n nodeRequestEvent: NodeRequestEvent,\n ): Promise<void> {\n const { req, res } = nodeRequestEvent;\n const rawUrl = req.url!;\n const { route, params } = this.router.match(`/${req.method}${rawUrl}`);\n\n if (!route) {\n // Skip if response was already sent (e.g., by Vite middleware)\n if (res.headersSent) {\n return;\n }\n // if no route is found, return basic 404\n // note: you should not use this in production, use a custom 404 page instead by adding a route /*\n res.writeHead(404, { \"content-type\": \"text/plain\" });\n res.end(\"Not Found\");\n return;\n }\n\n const headers = (req.headers ?? {}) as Record<string, string>;\n const method = (req.method?.toUpperCase() ?? \"GET\") as RouteMethod;\n\n // P0: Use manual query parsing - much faster than new URL() + URLSearchParams\n const query = this.parseQueryString(rawUrl);\n\n // P1: Use cached URL base - avoids string concat on every request\n // Create URL object (still needed for downstream code that uses url.pathname, etc.)\n const urlBase = this.getUrlBase(headers);\n const url = new URL(rawUrl, urlBase);\n\n const request: ServerRequestData = {\n method,\n url,\n headers,\n params: params ?? {},\n query,\n raw: { node: nodeRequestEvent },\n };\n\n const response = await route\n .handler(request)\n .catch(this.handleInternalError);\n\n // Skip if response was already sent (e.g., by Vite middleware)\n if (res.headersSent) {\n return;\n }\n\n // empty body - just send status & headers\n if (!response.body) {\n res.writeHead(response.status, response.headers);\n res.end();\n return;\n }\n\n // if response.body is string or buffer\n if (typeof response.body === \"string\" || Buffer.isBuffer(response.body)) {\n res.writeHead(response.status, response.headers);\n res.end(response.body);\n return;\n }\n\n // if response.body is node stream\n if (response.body instanceof Readable) {\n res.writeHead(response.status, response.headers);\n response.body.pipe(res);\n return;\n }\n\n // if response.body is web stream\n if (response.body instanceof ReadableStream) {\n res.writeHead(response.status, response.headers);\n // Flush headers immediately and disable Nagle's algorithm for streaming\n res.flushHeaders();\n res.socket?.setNoDelay(true);\n try {\n for await (const chunk of response.body) {\n const canContinue = res.write(chunk);\n // If the internal buffer is full, wait for it to drain\n if (!canContinue) {\n await new Promise<void>((resolve) => res.once(\"drain\", resolve));\n }\n }\n } catch (error) {\n this.log.error(\"Error piping proxy response stream\", error);\n } finally {\n res.end();\n }\n return;\n }\n\n // not supported response body type\n\n this.log.error(\"Unknown response body type:\", typeof response.body);\n res.writeHead(500, { \"content-type\": \"text/plain\" });\n res.end(this.internalServerErrorMessage);\n }\n\n /**\n * Handle Web (Fetch API) request event.\n */\n public async handleWebRequest(ev: WebRequestEvent): Promise<void> {\n const req = ev.req;\n const url = new URL(req.url);\n const { route, params } = this.router.match(\n `/${req.method}${url.pathname}`,\n );\n\n if (this.isViteNotFound(req.url, route, params)) {\n return;\n }\n\n if (!route) {\n // if no route is found, return basic 404\n // note: you should not use this in production, use a custom 404 page instead by adding a route /*\n ev.res = new Response(\"Not Found\", {\n status: 404,\n headers: { \"content-type\": \"text/plain\" },\n });\n return;\n }\n\n const headers: Record<string, string> = {};\n\n req.headers.forEach((value, key) => {\n headers[key] = value;\n });\n\n // Optimize: only parse query params if there are any\n const query = url.search\n ? Object.fromEntries(url.searchParams.entries())\n : {};\n const method = (req.method.toUpperCase() ?? \"GET\") as RouteMethod;\n const request: ServerRequestData = {\n method,\n url,\n headers,\n params: params || {},\n query,\n raw: { web: ev },\n };\n\n const response = await route\n .handler(request)\n .catch(this.handleInternalError);\n\n const webHeaders = this.toWebHeaders(response.headers);\n\n // empty body - just send status & headers\n if (!response.body) {\n ev.res = new Response(null, {\n status: response.status,\n headers: webHeaders,\n });\n return;\n }\n\n // if response.body is string or buffer\n if (typeof response.body === \"string\") {\n ev.res = new Response(response.body, {\n status: response.status,\n headers: webHeaders,\n });\n return;\n }\n\n if (Buffer.isBuffer(response.body)) {\n // Use Uint8Array to avoid Buffer pooling issues where .buffer returns\n // the entire underlying ArrayBuffer which may be larger than the actual data\n ev.res = new Response(new Uint8Array(response.body), {\n status: response.status,\n headers: webHeaders,\n });\n return;\n }\n\n // if response.body is node stream\n if (response.body instanceof Readable) {\n ev.res = new Response(\n Readable.toWeb(response.body) as unknown as ReadableStream,\n {\n status: response.status,\n headers: webHeaders,\n },\n );\n return;\n }\n\n // if response.body is web stream\n if (response.body instanceof ReadableStream) {\n ev.res = new Response(response.body, {\n status: response.status,\n headers: webHeaders,\n });\n return;\n }\n\n // not supported response body type\n this.log.error(`Unknown response body type: ${typeof response.body}`);\n ev.res = new Response(this.internalServerErrorMessage, {\n status: 500,\n headers: { \"content-type\": \"text/plain\" },\n });\n }\n\n /**\n * Convert response headers to Web API Headers.\n *\n * The `set-cookie` header requires special handling because it's stored\n * as `string[]` (one entry per cookie) but the `Response` constructor\n * would comma-join arrays, which breaks cookie parsing. We use\n * `Headers.append()` to emit each cookie as a separate header.\n */\n protected toWebHeaders(headers: Record<string, string | string[]>): Headers {\n const webHeaders = new Headers();\n for (const [key, value] of Object.entries(headers)) {\n if (Array.isArray(value)) {\n for (const v of value) {\n webHeaders.append(key, v);\n }\n } else {\n webHeaders.set(key, value);\n }\n }\n return webHeaders;\n }\n\n /**\n * Helper for Vite development mode to let Vite handle (or not) 404.\n */\n protected isViteNotFound(\n url?: string,\n route?: Route,\n params?: Record<string, string>,\n ): boolean {\n if (this.alepha.isViteDev()) {\n if (!route) {\n return true;\n }\n\n url = url?.split(\"?\")[0];\n\n if (!!params?.[\"*\"] && `/${params?.[\"*\"]}` === url) {\n return true;\n }\n }\n\n return false;\n }\n}\n","import { type Static, t } from \"alepha\";\n\nexport const errorSchema = t.object(\n {\n error: t.text({ description: \"HTTP error name\" }),\n status: t.integer({\n description: \"HTTP status code\",\n }),\n message: t.text({\n description: \"Short text which describe the error\",\n size: \"rich\",\n }),\n details: t.optional(\n t.text({\n description: \"Detailed description of the error\",\n size: \"rich\",\n }),\n ),\n requestId: t.optional(t.text()),\n cause: t.optional(\n t.object({\n name: t.text(),\n message: t.text({\n description: \"Cause Error message\",\n size: \"rich\",\n }),\n }),\n ),\n },\n {\n title: \"HttpError\",\n description: \"Generic response after a failed operation\",\n },\n);\n\nexport type ErrorSchema = Static<typeof errorSchema>;\n","import {\n $inject,\n Alepha,\n type FileLike,\n isFileLike,\n type Static,\n type TObject,\n type TSchema,\n} from \"alepha\";\nimport { $cache } from \"alepha/cache\";\nimport type { DurationLike } from \"alepha/datetime\";\nimport { $logger } from \"alepha/logger\";\nimport { HttpError } from \"../errors/HttpError.ts\";\nimport { isMultipart } from \"../helpers/isMultipart.ts\";\nimport type {\n ServerRequestConfigEntry,\n TRequestBody,\n TResponseBody,\n} from \"../interfaces/ServerRequest.ts\";\nimport type { ClientRequestOptions } from \"../primitives/$action.ts\";\nimport { errorSchema } from \"../schemas/errorSchema.ts\";\n\nexport class HttpClient {\n protected readonly log = $logger();\n protected readonly alepha = $inject(Alepha);\n\n public readonly cache = $cache<HttpClientCache>({\n provider: \"memory\",\n name: \"http:client\",\n });\n\n protected readonly pendingRequests: HttpClientPendingRequests = {};\n\n public async fetchAction(args: FetchActionArgs): Promise<FetchResponse> {\n const route = args.action; // our link to fetch\n const options = args.options ?? {}; // fetch standard options, cache, etc.\n const config = args.config ?? {}; // params, query, body, etc.\n const host = args.host ?? \"\"; // remote host, e.g. \"https://api.example.com\" or empty (for browser)\n\n const request: RequestInit = {\n ...options.request,\n };\n\n const method = route.method;\n const headers: Record<string, string> = {};\n const url = this.url(host, route, config);\n\n await this.alepha.events.emit(\"client:onRequest\", {\n route,\n config,\n options,\n headers,\n request,\n });\n\n request.method ??= method;\n\n await this.body(request, headers, route, config);\n\n request.headers = {\n ...config.headers,\n ...Object.fromEntries(new Headers(request.headers).entries()),\n ...headers,\n };\n\n return await this.fetch(url, {\n ...request,\n schema: route.schema,\n ...options,\n });\n }\n\n public async fetch<T extends TSchema>(\n url: string,\n request: RequestInitWithOptions<T> = {}, // standard options\n ): Promise<FetchResponse<Static<T>>> {\n const options = {\n cache: request.localCache,\n schema: request.schema?.response,\n key: request.key,\n };\n\n request.method ??= \"GET\";\n\n this.log.trace(\"Request\", {\n url,\n method: request.method,\n body: request.body,\n headers: request.headers,\n options,\n });\n\n // Only add automatic ETag if user didn't explicitly provide headers\n const cached = await this.cache.get(url);\n if (cached && request.method === \"GET\") {\n if (cached.etag) {\n request.headers = new Headers(request.headers);\n if (!request.headers.has(\"if-none-match\")) {\n request.headers.set(\"if-none-match\", cached.etag);\n }\n } else {\n return {\n data: cached.data as Static<T>,\n status: 200,\n statusText: \"OK\",\n headers: new Headers(),\n };\n }\n }\n\n await this.alepha.events.emit(\"client:beforeFetch\", {\n url,\n options,\n request,\n });\n\n // make a key for the request\n // this will be used to check if the request is already pending\n const key =\n options.key ??\n JSON.stringify({\n url,\n method: request.method,\n body: request.body,\n });\n\n const existing = this.pendingRequests[key];\n if (existing) {\n this.log.info(\"Request already pending\", key);\n return existing;\n }\n\n this.pendingRequests[key] = fetch(url, request)\n .then(async (response) => {\n this.log.debug(\"Response\", {\n url,\n status: response.status,\n });\n\n const fetchResponse: FetchResponse = {\n data: await this.responseData(response, options),\n status: response.status,\n statusText: response.statusText,\n headers: response.headers,\n raw: response,\n };\n\n if (request.method === \"GET\") {\n if (options.cache) {\n await this.cache.set(\n url,\n { data: fetchResponse.data },\n typeof options.cache === \"boolean\" ? undefined : options.cache,\n );\n } else if (!this.alepha.isBrowser()) {\n // only cache etag on server, browser can handle etag itself\n const etag = response.headers.get(\"etag\") ?? undefined;\n if (etag) {\n await this.cache.set(url, { data: fetchResponse.data, etag });\n }\n }\n }\n\n return fetchResponse;\n })\n .finally(() => {\n delete this.pendingRequests[key];\n });\n\n return this.pendingRequests[key];\n }\n\n protected url(\n host: string,\n action: HttpAction,\n args: ServerRequestConfigEntry,\n ) {\n let url = host;\n\n if (action.prefix) {\n url += action.prefix;\n }\n\n url += action.path;\n url = this.pathVariables(url, action, args);\n url = this.queryParams(url, action, args);\n\n return url;\n }\n\n protected async body(\n init: RequestInit,\n headers: Record<string, string>,\n action: HttpAction,\n args: ServerRequestConfigEntry = {},\n ) {\n const hasHeader =\n typeof init.headers === \"object\" &&\n \"content-type\" in init.headers &&\n init.headers[\"content-type\"] === \"multipart/form-data\";\n\n if (hasHeader || isMultipart(action)) {\n if (typeof init.headers === \"object\" && \"content-type\" in init.headers) {\n delete init.headers[\"content-type\"]; // fetch() will fill this for us\n }\n\n const formData = new FormData();\n\n for (const [key, value] of Object.entries(args.body ?? {})) {\n if (typeof value === \"string\") {\n formData.append(key, value);\n continue;\n }\n if (value instanceof Blob) {\n formData.append(key, value);\n continue;\n }\n if (isFileLike(value)) {\n // FileLike must be transformed to WebFile\n formData.append(\n key,\n new File([await value.arrayBuffer()], value.name, {\n type: value.type,\n }),\n );\n }\n }\n\n init.body = formData;\n\n return;\n }\n\n if (!init.body && action.schema?.body) {\n headers[\"content-type\"] = \"application/json\";\n init.body = this.alepha.codec.encode(action.schema?.body, args.body, {\n as: \"string\",\n });\n }\n }\n\n protected async responseData(\n response: Response,\n options: FetchOptions,\n ): Promise<any> {\n if (response.status === 304) {\n let cacheKey = response.url;\n if (typeof window !== \"undefined\") {\n cacheKey = cacheKey.replace(window.location.origin, \"\");\n }\n\n const cached = await this.cache.get(cacheKey);\n if (cached) {\n return cached.data;\n }\n\n // if no cached data (etag-only routes), return empty string\n return \"\";\n }\n\n if (response.status === 204) {\n return;\n }\n\n if (this.isMaybeFile(response)) {\n return this.createFileLike(response);\n }\n\n if (response.headers.get(\"Content-Type\")?.startsWith(\"text/\")) {\n return await response.text();\n }\n\n if (response.headers.get(\"Content-Type\") === \"application/json\") {\n const json = await response.json();\n\n if (response.status >= 400) {\n const jsonError = this.alepha.codec.decode(errorSchema, json);\n const error = new HttpError(jsonError);\n\n await this.alepha.events.emit(\"client:onError\", {\n error,\n });\n\n throw error;\n }\n\n if (options.schema) {\n return this.alepha.codec.decode(options.schema, json);\n }\n\n return json;\n }\n\n if (response.status >= 400) {\n const error = new HttpError({\n status: response.status,\n message: `An error occurred while fetching the resource. (${response.statusText})`,\n });\n\n await this.alepha.events.emit(\"client:onError\", {\n error,\n });\n\n throw error;\n }\n\n return response;\n }\n\n protected isMaybeFile(response: Response): boolean {\n const contentType = response.headers.get(\"Content-Type\");\n if (!contentType) {\n return false;\n }\n\n if (response.headers.get(\"Content-Disposition\")?.includes(\"attachment\")) {\n return true; // If Content-Disposition indicates an attachment, treat it as a file\n }\n\n return (\n contentType.startsWith(\"application/octet-stream\") ||\n contentType.startsWith(\"application/pdf\") ||\n contentType.startsWith(\"application/zip\") ||\n contentType.startsWith(\"image/\") ||\n contentType.startsWith(\"video/\") ||\n contentType.startsWith(\"audio/\")\n );\n }\n\n protected createFileLike(response: Response, defaultFileName = \"\"): FileLike {\n const match = (response.headers.get(\"Content-Disposition\") ?? \"\").match(\n /filename=\"(.+)\"/,\n );\n return {\n name: match?.[1] ? match[1] : defaultFileName,\n type: response.headers.get(\"Content-Type\") ?? \"application/octet-stream\",\n size: Number(response.headers.get(\"Content-Length\") ?? 0),\n lastModified: Date.now(),\n stream: () => {\n throw new Error(\"Not implemented\");\n },\n arrayBuffer: async () => {\n return await response.arrayBuffer();\n },\n text: async () => {\n return await response.text();\n },\n };\n }\n\n public pathVariables(\n url: string,\n action: { schema?: { params?: TObject } },\n args: ServerRequestConfigEntry = {},\n ): string {\n if (typeof args.params === \"object\") {\n const params = action.schema?.params\n ? (this.alepha.codec.decode(\n action.schema.params,\n args.params,\n ) as Record<string, any>)\n : args.params;\n\n for (const key of Object.keys(params)) {\n url = url.replace(`:${key}`, params[key]);\n url = url.replace(`{${key}}`, params[key]);\n }\n }\n\n return url;\n }\n\n public queryParams(\n url: string,\n action: { schema?: { query?: TObject } },\n args: ServerRequestConfigEntry = {},\n ): string {\n if (typeof args.query === \"object\") {\n const query = action.schema?.query\n ? this.alepha.codec.decode(action.schema.query, args.query ?? {})\n : args.query;\n\n for (const key of Object.keys(query)) {\n if (query[key] === undefined) {\n delete query[key];\n }\n if (typeof query[key] === \"object\") {\n query[key] = JSON.stringify(query[key]);\n }\n }\n\n return `${url}?${new URLSearchParams(\n query as Record<string, string>,\n ).toString()}`;\n }\n return url;\n }\n}\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport interface FetchOptions<T extends TSchema = TSchema> {\n /**\n * Key to identify the request in the pending requests.\n */\n key?: string;\n\n /**\n * The schema to validate the response against.\n */\n schema?: {\n response?: T;\n };\n\n /**\n * Built-in cache options.\n */\n localCache?: boolean | number | DurationLike;\n}\n\nexport type RequestInitWithOptions<T extends TSchema = TSchema> = RequestInit &\n FetchOptions<T>;\n\nexport interface FetchResponse<T = any> {\n data: T;\n status: number;\n statusText: string;\n headers: Headers;\n raw?: Response;\n}\n\nexport type HttpClientPendingRequests = Record<\n string,\n Promise<any> | undefined\n>;\n\ninterface HttpClientCache {\n data: any;\n etag?: string;\n}\n\nexport interface FetchActionArgs {\n action: HttpAction;\n host?: string;\n config?: ServerRequestConfigEntry;\n options?: ClientRequestOptions;\n}\n\nexport interface HttpAction {\n method?: string;\n prefix?: string;\n path: string;\n contentType?: string;\n requestBodyType?: string;\n schema?: {\n params?: TObject;\n query?: TObject;\n body?: TRequestBody;\n response?: TResponseBody;\n };\n}\n","import {\n $atom,\n $inject,\n $use,\n AlephaError,\n type Async,\n createPrimitive,\n isTypeFile,\n KIND,\n PipelinePrimitive,\n type PipelinePrimitiveOptions,\n type Static,\n type TObject,\n type TSchema,\n t,\n} from \"alepha\";\nimport { $logger } from \"alepha/logger\";\nimport type { RouteMethod } from \"../constants/routeMethods.ts\";\nimport { isMultipart } from \"../helpers/isMultipart.ts\";\nimport { ServerReply } from \"../helpers/ServerReply.ts\";\nimport type {\n RequestConfigSchema,\n ServerRequest,\n ServerResponseBody,\n ServerRoute,\n TRequestBody,\n} from \"../interfaces/ServerRequest.ts\";\nimport { ServerProvider } from \"../providers/ServerProvider.ts\";\nimport { ServerRouterProvider } from \"../providers/ServerRouterProvider.ts\";\nimport {\n type FetchOptions,\n type FetchResponse,\n HttpClient,\n} from \"../services/HttpClient.ts\";\n\n/**\n * Creates a server action primitive for defining type-safe HTTP endpoints.\n *\n * Server actions are the core building blocks for REST APIs in Alepha, providing declarative\n * HTTP endpoints with full type safety, automatic validation, and OpenAPI documentation.\n *\n * **Key Features**\n * - Full TypeScript inference for request/response types\n * - Automatic schema validation using TypeBox\n * - Convention-based URL generation with customizable paths\n * - Direct invocation (`run()`) or HTTP requests (`fetch()`)\n * - Built-in authentication and authorization support\n * - Automatic content-type handling (JSON, form-data, plain text)\n *\n * **URL Generation**\n *\n * **Important:** All `$action` paths are automatically prefixed with `/api`.\n *\n * ```ts\n * $action({ path: \"/users\" }) // → GET /api/users\n * $action({ path: \"/users/:id\" }) // → GET /api/users/:id\n * $action({ path: \"/hello\" }) // → GET /api/hello\n * ```\n *\n * This prefix is configurable via the `SERVER_API_PREFIX` environment variable.\n * HTTP method defaults to GET, or POST if body schema is provided.\n *\n * **Common Use Cases**\n * - CRUD operations with type safety\n * - File upload and download endpoints\n * - Microservice communication\n *\n * @example\n * ```ts\n * class UserController {\n * getUsers = $action({\n * path: \"/users\",\n * schema: {\n * query: t.object({\n * page: t.optional(t.number({ default: 1 })),\n * limit: t.optional(t.number({ default: 10 }))\n * }),\n * response: t.object({\n * users: t.array(t.object({\n * id: t.text(),\n * name: t.text(),\n * email: t.text()\n * })),\n * total: t.number()\n * })\n * },\n * handler: async ({ query }) => {\n * const users = await this.userService.findUsers(query);\n * return { users: users.items, total: users.total };\n * }\n * });\n *\n * createUser = $action({\n * method: \"POST\",\n * path: \"/users\",\n * schema: {\n * body: t.object({\n * name: t.text(),\n * email: t.text({ format: \"email\" })\n * }),\n * response: t.object({ id: t.text(), name: t.text() })\n * },\n * handler: async ({ body }) => {\n * return await this.userService.create(body);\n * }\n * });\n * }\n * ```\n */\nexport const $action = <TConfig extends RequestConfigSchema>(\n options: ActionPrimitiveOptions<TConfig>,\n): ActionPrimitiveFn<TConfig> => {\n const instance = createPrimitive(ActionPrimitive<TConfig>, options);\n const fn = (\n config?: ClientRequestEntry<TConfig>,\n options?: ClientRequestOptions,\n ) => {\n return instance.run(config, options);\n };\n Object.defineProperty(fn, \"name\", {\n get(): string {\n return instance.options.name || instance.config.propertyKey;\n },\n });\n return Object.setPrototypeOf(fn, instance) as ActionPrimitiveFn<TConfig>;\n};\n\n// ----------------------------------------------------------------------------------------------------------\n\nexport interface ActionPrimitiveOptions<TConfig extends RequestConfigSchema>\n extends Omit<ServerRoute, \"handler\" | \"path\" | \"schema\" | \"mapParams\">,\n PipelinePrimitiveOptions {\n /**\n * Name of the action.\n *\n * - It will be used to generate the route path if `path` is not provided.\n * - It will be used to generate the permission name if `security` is enabled.\n */\n name?: string;\n\n /**\n * Group actions together.\n *\n * - If not provided, the service name containing the route will be used.\n * - It will be used as Tag for documentation purposes.\n * - It will be used for permission name generation if `security` is enabled.\n *\n * @example\n * ```ts\n * // group = \"MyController\"\n * class MyController {\n * \thello = $action({ handler: () => \"Hello World\" });\n * }\n *\n * // group = \"users\"\n * class MyOtherController {\n * group = \"users\";\n * a1 = $action({ handler: () => \"Action 1\", group: this.group });\n * a2 = $action({ handler: () => \"Action 2\", group: this.group });\n * }\n * ```\n */\n group?: string;\n\n /**\n * Pathname of the route. If not provided, property key is used.\n */\n path?: string;\n\n /**\n * The route method.\n *\n * - If not provided, it will be set to \"GET\" by default.\n * - If not provider and a body is provided, it will be set to \"POST\".\n *\n * Wildcard methods are not supported for now. (e.g. \"ALL\", \"ANY\", etc.)\n */\n method?: RouteMethod;\n\n /**\n * The config schema of the route.\n * - body: The request body schema.\n * - params: Path variables schema.\n * - query: The request query-params schema.\n * - response: The response schema.\n */\n schema?: TConfig;\n\n /**\n * A short description of the action. Used for documentation purposes.\n */\n description?: string;\n\n /**\n * Disable the route. Useful with env variables to disable one specific route.\n * Route won't be available in the API nor locally.\n */\n disabled?: boolean;\n\n /**\n * Main route handler. This is where the route logic is implemented.\n */\n handler: ServerActionHandler<TConfig>;\n}\n\n// ----------------------------------------------------------------------------------------------------------\n\n/**\n * Server API configuration atom.\n */\nexport const serverApiOptions = $atom({\n name: \"alepha.server.api.options\",\n schema: t.object({\n prefix: t.text({\n default: \"/api\",\n description: \"Prefix for all API routes (e.g. $action).\",\n }),\n }),\n default: {\n prefix: \"/api\",\n },\n});\n\nexport type ServerApiOptions = Static<typeof serverApiOptions.schema>;\n\ndeclare module \"alepha\" {\n interface State {\n [serverApiOptions.key]: ServerApiOptions;\n }\n}\n\n// ----------------------------------------------------------------------------------------------------------\n\nexport class ActionPrimitive<\n TConfig extends RequestConfigSchema,\n> extends PipelinePrimitive<ActionPrimitiveOptions<TConfig>> {\n protected readonly log = $logger();\n protected readonly settings = $use(serverApiOptions);\n protected readonly httpClient = $inject(HttpClient);\n protected readonly serverProvider = $inject(ServerProvider);\n protected readonly serverRouterProvider = $inject(ServerRouterProvider);\n\n protected onInit() {\n if (this.options.disabled) {\n this.log.debug(\n `Action '${this.name}' is disabled. It won't be available in the API.`,\n );\n return;\n }\n this.serverRouterProvider.createRoute(this.route);\n }\n\n public get prefix() {\n return this.settings.prefix;\n }\n\n public get route(): ServerRoute {\n return {\n ...(this.options as any), // TODO: fix schema.header mapping\n method: this.method,\n path: `${this.prefix}${this.path}`,\n handler: this.handler,\n } as ServerRoute;\n }\n\n /**\n * Returns the name of the action.\n */\n public get name(): string {\n return this.options.name || this.config.propertyKey;\n }\n\n /**\n * Returns the group of the action. (e.g. \"orders\", \"admin\", etc.)\n */\n public get group(): string {\n return this.options.group || this.config.service.name;\n }\n\n /**\n * Returns the HTTP method of the action.\n */\n public get method(): RouteMethod {\n return this.options.method || (this.options.schema?.body ? \"POST\" : \"GET\");\n }\n\n /**\n * Returns the path of the action.\n *\n * Path is prefixed by `/api` by default.\n */\n public get path(): string {\n if (this.options.path) {\n return this.options.path;\n }\n\n let path = `/${this.name}`;\n\n if (this.options.schema?.params) {\n for (const [key] of Object.entries(\n this.options.schema.params.properties,\n )) {\n path += `/:${key}`;\n }\n }\n\n return path;\n }\n\n public get schema(): TConfig | undefined {\n return this.options.schema;\n }\n\n public getBodyContentType(): string | undefined {\n if (this.options.schema?.body) {\n // TODO: move to `alepha.server.multipart` module ?\n if (isMultipart(this.options)) {\n return \"multipart/form-data\";\n }\n\n if (t.schema.isString(this.options.schema.body)) {\n // if body is a string, we assume it's plain text\n return \"text/plain\";\n }\n\n if (\n t.schema.isObject(this.options.schema.body) ||\n t.schema.isArray(this.options.schema.body) ||\n t.schema.isRecord(this.options.schema.body)\n )\n // if body is an object or array, we assume it's JSON\n return \"application/json\";\n }\n }\n\n /**\n * Call the action handler directly.\n * There is no HTTP layer involved.\n */\n public async run(\n config?: ClientRequestEntry<TConfig>,\n options: ClientRequestOptions = {}, // most of the options are ignored here\n ): Promise<ClientRequestResponse<TConfig>> {\n if (this.options.disabled) {\n throw new AlephaError(`Action '${this.name}' is disabled.`);\n }\n const handler = this.handler.run.bind(this.handler);\n const {\n body,\n params = {},\n query = {},\n headers = {},\n } = (config ?? {}) as ClientRequestEntryContainer<RequestConfigSchema>;\n const reply = new ServerReply();\n const method = this.method;\n\n // we use localhost as the base URL for the action\n const url = new URL(`http://localhost${this.path ?? \"\"}`);\n\n const serverActionRequest: Partial<ServerRequest> = {\n method,\n url,\n body,\n params,\n query,\n headers,\n reply,\n metadata: {\n routePath: this.route.path,\n routeMethod: this.route.method,\n },\n };\n\n const eventData: {\n action: ActionPrimitive<TConfig>;\n request: ServerRequest;\n options: ClientRequestOptions;\n context?: Record<string, any>;\n } = {\n action: this,\n request: serverActionRequest as ServerRequest,\n options,\n };\n\n await this.alepha.events.emit(\"action:onRequest\", eventData as any);\n\n if (serverActionRequest.reply?.body) {\n return serverActionRequest.reply.body as ClientRequestResponse<TConfig>;\n }\n\n const executeHandler = async (): Promise<\n ClientRequestResponse<TConfig>\n > => {\n if (serverActionRequest.query && this.options.schema?.query) {\n serverActionRequest.query = this.alepha.codec.encode(\n this.options.schema.query,\n serverActionRequest.query,\n );\n }\n\n if (serverActionRequest.headers && this.options.schema?.headers) {\n serverActionRequest.headers = this.alepha.codec.encode(\n this.options.schema.headers,\n serverActionRequest.headers,\n ) as Record<string, any>;\n }\n\n if (serverActionRequest.body && this.options.schema?.body) {\n serverActionRequest.body = this.alepha.codec.encode(\n this.options.schema.body,\n serverActionRequest.body,\n ) as unknown;\n }\n\n if (serverActionRequest.params && this.options.schema?.params) {\n serverActionRequest.params = this.alepha.codec.encode(\n this.options.schema.params,\n serverActionRequest.params,\n ) as Record<string, any>;\n }\n\n this.serverRouterProvider.validateRequest(\n this.options,\n serverActionRequest as ServerRequest,\n );\n\n let response: any = await handler(\n serverActionRequest as ServerActionRequest<TConfig>,\n );\n\n // we validate response just to remove undeclared properties from response\n if (\n this.options.schema?.response &&\n // skip validation if response is expected as file\n !isTypeFile(this.options.schema.response)\n ) {\n response = this.alepha.codec.validate(\n this.options.schema.response,\n response,\n );\n }\n\n await this.alepha.events.emit(\"action:onResponse\", {\n action: this,\n request: serverActionRequest as ServerRequest,\n options,\n response,\n });\n\n return response;\n };\n\n // Always fork to isolate each .run() call in its own ALS context.\n // The synthetic action request is separate from any real HTTP request.\n // Hook fork data (e.g. user atom) is merged into the context.\n const forkData: Record<string, any> = {\n \"alepha.action.request\": serverActionRequest,\n ...eventData.context,\n };\n\n return this.alepha.fork(executeHandler, forkData);\n }\n\n /**\n * Works like `run`, but always fetches (http request) the route.\n */\n public fetch(\n config?: ClientRequestEntry<TConfig>,\n options?: ClientRequestOptions,\n ): Promise<FetchResponse<ClientRequestResponse<TConfig>>> {\n return this.httpClient.fetchAction({\n host: this.serverProvider.hostname, // that's the trick, we just use the server hostname\n action: this,\n config,\n options,\n });\n }\n}\n\nexport interface ActionPrimitiveFn<TConfig extends RequestConfigSchema>\n extends ActionPrimitive<TConfig> {\n (\n config?: ClientRequestEntry<TConfig>,\n options?: ClientRequestOptions,\n ): Promise<ClientRequestResponse<TConfig>>;\n}\n\n$action[KIND] = ActionPrimitive;\n\n// ----------------------------------------------------------------------------------------------------------\n\nexport type ClientRequestEntry<\n TConfig extends RequestConfigSchema,\n T = ClientRequestEntryContainer<TConfig>,\n> = {\n [K in keyof T as T[K] extends undefined ? never : K]: T[K];\n};\n\nexport type ClientRequestEntryContainer<TConfig extends RequestConfigSchema> = {\n body: TConfig[\"body\"] extends TRequestBody\n ? Static<TConfig[\"body\"]>\n : undefined;\n\n params: TConfig[\"params\"] extends TObject\n ? Static<TConfig[\"params\"]>\n : undefined;\n\n headers?: TConfig[\"headers\"] extends TObject\n ? Static<TConfig[\"headers\"]>\n : Record<string, string>;\n\n query?: TConfig[\"query\"] extends TObject\n ? Partial<Static<TConfig[\"query\"]>>\n : Record<string, string>;\n};\n\nexport interface ClientRequestOptions extends FetchOptions {\n /**\n * Standard request fetch options.\n */\n request?: RequestInit;\n\n /**\n * Add query parameters to the request URL. They will be merged with any query params defined in the action schema.\n * This is useful for adding dynamic query params at runtime.\n */\n query?: Record<string, string | number | boolean>;\n}\n\nexport type ClientRequestResponse<TConfig extends RequestConfigSchema> =\n TConfig[\"response\"] extends TSchema ? Static<TConfig[\"response\"]> : any;\n\n/**\n * Specific handler for server actions.\n */\nexport type ServerActionHandler<\n TConfig extends RequestConfigSchema = RequestConfigSchema,\n> = (\n request: ServerActionRequest<TConfig>,\n) => Async<ServerResponseBody<TConfig>>;\n\n/**\n * Server Action Request Interface\n *\n * Can be extended with module augmentation to add custom properties (like `user` in Server Security).\n *\n * This is NOT Server Request, but a specific type for actions.\n */\nexport interface ServerActionRequest<TConfig extends RequestConfigSchema>\n extends ServerRequest<TConfig> {}\n","import {\n $inject,\n createPrimitive,\n KIND,\n type Middleware,\n Primitive,\n} from \"alepha\";\nimport type { RouteMethod } from \"../constants/routeMethods.ts\";\nimport { ServerRouterProvider } from \"../providers/ServerRouterProvider.ts\";\n\nexport const $middleware = (options: ServerMiddlewarePrimitiveOptions) => {\n return createPrimitive(ServerMiddlewarePrimitive, options);\n};\n\nexport interface ServerMiddlewarePrimitiveOptions {\n /**\n * Path prefix. Middleware applies to all routes starting with this path.\n *\n * @example \"/api\" — matches \"/api/users\", \"/api/orders\", etc.\n */\n path: string;\n\n /**\n * Middleware functions to apply to matching routes.\n */\n use: Middleware[];\n\n /**\n * Limit middleware to specific HTTP methods.\n * If not set, middleware applies to all methods.\n */\n method?: RouteMethod | RouteMethod[];\n\n /**\n * Exclude specific route paths from middleware application.\n */\n exclude?: string[];\n}\n\nexport class ServerMiddlewarePrimitive extends Primitive<ServerMiddlewarePrimitiveOptions> {\n protected readonly serverRouterProvider = $inject(ServerRouterProvider);\n\n protected onInit() {\n const path = this.options.path.replace(/\\/+$/, \"\");\n this.serverRouterProvider.pushMiddleware(`${path}/*`, this.options.use, {\n method: this.options.method,\n exclude: this.options.exclude,\n });\n }\n}\n\n$middleware[KIND] = ServerMiddlewarePrimitive;\n","import {\n $inject,\n createPrimitive,\n KIND,\n PipelinePrimitive,\n type PipelinePrimitiveOptions,\n} from \"alepha\";\nimport type {\n RequestConfigSchema,\n ServerHandler,\n ServerRoute,\n} from \"../interfaces/ServerRequest.ts\";\nimport { ServerRouterProvider } from \"../providers/ServerRouterProvider.ts\";\n\n/**\n * Create a basic endpoint.\n *\n * It's a low level primitive. You probably want to use `$action` instead.\n *\n * @see {@link $action}\n * @see {@link $page}\n */\nexport const $route = <TConfig extends RequestConfigSchema>(\n options: RoutePrimitiveOptions<TConfig>,\n): RoutePrimitive<TConfig> => {\n return createPrimitive(RoutePrimitive<TConfig>, options);\n};\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport interface RoutePrimitiveOptions<\n TConfig extends RequestConfigSchema = RequestConfigSchema,\n> extends Omit<ServerRoute<TConfig>, \"handler\">,\n PipelinePrimitiveOptions<ServerHandler<TConfig>> {}\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport class RoutePrimitive<\n TConfig extends RequestConfigSchema,\n> extends PipelinePrimitive<RoutePrimitiveOptions<TConfig>> {\n protected readonly serverRouterProvider = $inject(ServerRouterProvider);\n\n protected onInit() {\n this.serverRouterProvider.createRoute({\n ...(this.options as any),\n handler: this.handler,\n } as ServerRoute<TConfig>);\n }\n}\n\n$route[KIND] = RoutePrimitive;\n","import {\n $inject,\n $use,\n AlephaError,\n type Async,\n createPrimitive,\n KIND,\n PipelinePrimitive,\n type PipelinePrimitiveOptions,\n type Static,\n type TObject,\n type TSchema,\n} from \"alepha\";\nimport { $logger } from \"alepha/logger\";\nimport type { RouteMethod } from \"../constants/routeMethods.ts\";\nimport { ServerReply } from \"../helpers/ServerReply.ts\";\nimport type {\n RequestConfigSchema,\n ServerRequest,\n} from \"../interfaces/ServerRequest.ts\";\nimport { ServerProvider } from \"../providers/ServerProvider.ts\";\nimport { ServerRouterProvider } from \"../providers/ServerRouterProvider.ts\";\nimport { serverApiOptions } from \"./$action.ts\";\n\n// ----------------------------------------------------------------------------------------------------------\n\n/**\n * Schema configuration for an SSE endpoint.\n */\nexport interface SseConfigSchema {\n /**\n * Request body schema.\n */\n body?: TObject;\n\n /**\n * Path parameters schema.\n */\n params?: TObject;\n\n /**\n * Query parameters schema.\n */\n query?: TObject;\n\n /**\n * Request headers schema.\n */\n headers?: TObject;\n\n /**\n * Schema for the data payload of each SSE event.\n */\n data?: TSchema;\n}\n\n// ----------------------------------------------------------------------------------------------------------\n\n/**\n * Context object passed to the SSE handler function.\n */\nexport interface SseHandlerContext<TConfig extends SseConfigSchema> {\n /**\n * Parsed request body.\n */\n body: TConfig[\"body\"] extends TObject ? Static<TConfig[\"body\"]> : any;\n\n /**\n * Parsed path parameters.\n */\n params: TConfig[\"params\"] extends TObject\n ? Static<TConfig[\"params\"]>\n : Record<string, string>;\n\n /**\n * Parsed query parameters.\n */\n query: TConfig[\"query\"] extends TObject\n ? Partial<Static<TConfig[\"query\"]>>\n : Record<string, any>;\n\n /**\n * Parsed request headers.\n */\n headers: TConfig[\"headers\"] extends TObject\n ? Static<TConfig[\"headers\"]>\n : Record<string, string>;\n\n /**\n * The underlying server request object.\n */\n request: ServerRequest;\n\n /**\n * Emit an SSE event to the client.\n */\n emit: (\n data: TConfig[\"data\"] extends TSchema ? Static<TConfig[\"data\"]> : any,\n ) => void;\n\n /**\n * Close the SSE stream.\n */\n close: () => void;\n}\n\n/**\n * Handler function type for SSE endpoints.\n */\nexport type SseHandler<TConfig extends SseConfigSchema = SseConfigSchema> = (\n context: SseHandlerContext<TConfig>,\n) => Async<void>;\n\n// ----------------------------------------------------------------------------------------------------------\n\n/**\n * Options for the $sse primitive.\n */\nexport interface SsePrimitiveOptions<TConfig extends SseConfigSchema>\n extends PipelinePrimitiveOptions {\n /**\n * Name of the SSE endpoint.\n */\n name?: string;\n\n /**\n * Group SSE endpoints together.\n */\n group?: string;\n\n /**\n * Pathname of the route. If not provided, property key is used.\n */\n path?: string;\n\n /**\n * The config schema for the SSE endpoint.\n */\n schema?: TConfig;\n\n /**\n * A short description of the endpoint. Used for documentation purposes.\n */\n description?: string;\n\n /**\n * Disable the SSE endpoint.\n */\n disabled?: boolean;\n\n /**\n * Main SSE handler. Receives context with emit/close functions.\n */\n handler: SseHandler<TConfig>;\n}\n\n// ----------------------------------------------------------------------------------------------------------\n\n/**\n * Async iterable stream of SSE events.\n *\n * Supports push-based event delivery via `push()`, error propagation\n * via `fail()`, and clean termination via `end()`.\n */\nexport class SseStream<T> implements AsyncIterable<T> {\n protected queue: T[] = [];\n protected error: Error | null = null;\n protected done = false;\n protected resolve: (() => void) | null = null;\n protected listeners: Array<(data: T) => void> = [];\n\n /**\n * Push a new event into the stream.\n */\n public push(data: T): void {\n if (this.done) return;\n this.queue.push(data);\n for (const listener of this.listeners) {\n listener(data);\n }\n if (this.resolve) {\n this.resolve();\n this.resolve = null;\n }\n }\n\n /**\n * Signal an error on the stream.\n */\n public fail(error: Error): void {\n this.error = error;\n this.done = true;\n if (this.resolve) {\n this.resolve();\n this.resolve = null;\n }\n }\n\n /**\n * End the stream gracefully.\n */\n public end(): void {\n this.done = true;\n if (this.resolve) {\n this.resolve();\n this.resolve = null;\n }\n }\n\n /**\n * Subscribe to new events as they arrive.\n */\n public subscribe(listener: (data: T) => void): () => void {\n this.listeners.push(listener);\n return () => {\n const idx = this.listeners.indexOf(listener);\n if (idx >= 0) this.listeners.splice(idx, 1);\n };\n }\n\n async *[Symbol.asyncIterator](): AsyncIterator<T> {\n while (true) {\n while (this.queue.length > 0) {\n yield this.queue.shift()!;\n }\n if (this.error) {\n throw this.error;\n }\n if (this.done) {\n return;\n }\n await new Promise<void>((r) => {\n this.resolve = r;\n });\n }\n }\n}\n\n// ----------------------------------------------------------------------------------------------------------\n\n/**\n * Response wrapper for SSE fetch requests.\n *\n * Wraps a standard `Response` and parses the `text/event-stream` body\n * into an async iterable of typed events.\n */\nexport class SseFetchResponse<T> implements AsyncIterable<T> {\n public readonly response: Response;\n\n constructor(response: Response) {\n this.response = response;\n }\n\n /**\n * HTTP status code of the response.\n */\n public get status(): number {\n return this.response.status;\n }\n\n /**\n * HTTP status text of the response.\n */\n public get statusText(): string {\n return this.response.statusText;\n }\n\n /**\n * Response headers.\n */\n public get headers(): Headers {\n return this.response.headers;\n }\n\n async *[Symbol.asyncIterator](): AsyncIterator<T> {\n const reader = this.response.body?.getReader();\n if (!reader) return;\n\n const decoder = new TextDecoder();\n let buffer = \"\";\n\n try {\n while (true) {\n const { done, value } = await reader.read();\n if (done) break;\n\n buffer += decoder.decode(value, { stream: true });\n\n const lines = buffer.split(\"\\n\");\n buffer = lines.pop() ?? \"\";\n\n for (const line of lines) {\n if (line.startsWith(\"data: \")) {\n const data = line.slice(6);\n try {\n yield JSON.parse(data) as T;\n } catch {\n // skip non-JSON data lines\n }\n }\n }\n }\n\n // process remaining buffer\n if (buffer.startsWith(\"data: \")) {\n const data = buffer.slice(6);\n try {\n yield JSON.parse(data) as T;\n } catch {\n // skip non-JSON data lines\n }\n }\n } finally {\n reader.releaseLock();\n }\n }\n}\n\n// ----------------------------------------------------------------------------------------------------------\n\n/**\n * Creates a Server-Sent Events (SSE) primitive for streaming typed events to clients.\n *\n * SSE endpoints provide a unidirectional stream from server to client over HTTP,\n * with full type safety for event data. The handler receives `emit()` and `close()`\n * functions to control the stream.\n *\n * **Key Features**\n * - Full TypeScript inference for event data types\n * - Automatic schema validation using TypeBox\n * - Convention-based URL generation with customizable paths\n * - Direct invocation (`run()`) returns an `SseStream` async iterable\n * - HTTP requests (`fetch()`) returns an `SseFetchResponse` async iterable\n * - Built-in `text/event-stream` content-type handling\n *\n * **URL Generation**\n *\n * All `$sse` paths are automatically prefixed with `/api`.\n *\n * ```ts\n * $sse({ path: \"/events\" }) // POST /api/events\n * $sse({ path: \"/feed/:id\" }) // POST /api/feed/:id\n * ```\n *\n * The HTTP method is always POST.\n *\n * @example\n * ```ts\n * class NotificationController {\n * events = $sse({\n * schema: {\n * data: t.object({\n * type: t.text(),\n * message: t.text(),\n * }),\n * },\n * handler: async ({ emit, close }) => {\n * emit({ type: \"welcome\", message: \"Connected!\" });\n * // ... stream events ...\n * close();\n * },\n * });\n * }\n * ```\n */\nexport const $sse = <TConfig extends SseConfigSchema>(\n options: SsePrimitiveOptions<TConfig>,\n): SsePrimitiveFn<TConfig> => {\n const instance = createPrimitive(SsePrimitive<TConfig>, options);\n const fn = (\n config?: SseRequestEntry<TConfig>,\n ): SseStream<SseEventData<TConfig>> => {\n return instance.run(config);\n };\n Object.defineProperty(fn, \"name\", {\n get(): string {\n return instance.options.name || instance.config.propertyKey;\n },\n });\n return Object.setPrototypeOf(fn, instance) as SsePrimitiveFn<TConfig>;\n};\n\n// ----------------------------------------------------------------------------------------------------------\n\n/**\n * The SSE primitive class extending PipelinePrimitive.\n *\n * Registers a POST route that streams `text/event-stream` responses.\n * Supports direct invocation via `run()` and HTTP via `fetch()`.\n */\nexport class SsePrimitive<\n TConfig extends SseConfigSchema,\n> extends PipelinePrimitive<SsePrimitiveOptions<TConfig>> {\n protected readonly log = $logger();\n protected readonly settings = $use(serverApiOptions);\n protected readonly serverProvider = $inject(ServerProvider);\n protected readonly serverRouterProvider = $inject(ServerRouterProvider);\n\n protected onInit() {\n if (this.options.disabled) {\n this.log.debug(\n `SSE endpoint '${this.name}' is disabled. It won't be available in the API.`,\n );\n return;\n }\n this.serverRouterProvider.createRoute({\n method: this.method,\n path: `${this.prefix}${this.path}`,\n schema: this.requestConfigSchema,\n handler: (request: ServerRequest) => this.httpHandler(request),\n });\n }\n\n /**\n * Returns the /api prefix.\n */\n public get prefix(): string {\n return this.settings.prefix;\n }\n\n /**\n * Returns the name of the SSE endpoint.\n */\n public get name(): string {\n return this.options.name || this.config.propertyKey;\n }\n\n /**\n * Returns the group of the SSE endpoint.\n */\n public get group(): string {\n return this.options.group || this.config.service.name;\n }\n\n /**\n * Returns the HTTP method. SSE always uses POST.\n */\n public get method(): RouteMethod {\n return \"POST\";\n }\n\n /**\n * Returns the path of the SSE endpoint.\n */\n public get path(): string {\n if (this.options.path) {\n return this.options.path;\n }\n\n let path = `/${this.name}`;\n\n if (this.options.schema?.params) {\n for (const [key] of Object.entries(\n this.options.schema.params.properties,\n )) {\n path += `/:${key}`;\n }\n }\n\n return path;\n }\n\n /**\n * Returns the schema configuration.\n */\n public get schema(): TConfig | undefined {\n return this.options.schema;\n }\n\n /**\n * Constructs a RequestConfigSchema from the SSE config for route registration.\n */\n protected get requestConfigSchema(): RequestConfigSchema | undefined {\n if (!this.options.schema) return undefined;\n return {\n body: this.options.schema.body,\n params: this.options.schema.params,\n query: this.options.schema.query,\n headers: this.options.schema.headers,\n };\n }\n\n /**\n * Call the SSE handler directly and return a typed async iterable stream.\n * There is no HTTP layer involved.\n */\n public run(\n config?: SseRequestEntry<TConfig>,\n ): SseStream<SseEventData<TConfig>> {\n if (this.options.disabled) {\n throw new AlephaError(`SSE endpoint '${this.name}' is disabled.`);\n }\n\n const stream = new SseStream<SseEventData<TConfig>>();\n const {\n body,\n params = {},\n query = {},\n headers = {},\n } = (config ?? {}) as SseRequestEntryContainer<SseConfigSchema>;\n\n const url = new URL(`http://localhost${this.path ?? \"\"}`);\n const reply = new ServerReply();\n\n const serverRequest: Partial<ServerRequest> = {\n method: this.method,\n url,\n body,\n params,\n query,\n headers,\n reply,\n metadata: {\n routePath: `${this.prefix}${this.path}`,\n routeMethod: this.method,\n },\n };\n\n const context: SseHandlerContext<TConfig> = {\n body: body as any,\n params: params as any,\n query: query as any,\n headers: headers as any,\n request: serverRequest as ServerRequest,\n emit: (data: SseEventData<TConfig>) => stream.push(data),\n close: () => stream.end(),\n };\n\n const handlerFn = this.handler.run.bind(this.handler);\n\n // run handler async, errors propagate to stream\n Promise.resolve()\n .then(() => handlerFn(context))\n .then(() => {\n // auto-close stream when handler finishes without calling close()\n stream.end();\n })\n .catch((error: Error) => {\n stream.fail(error);\n });\n\n return stream;\n }\n\n /**\n * Works like `run`, but always fetches (http request) the route.\n * Returns an `SseFetchResponse` that can be async-iterated for typed events.\n */\n public async fetch(\n config?: SseRequestEntry<TConfig>,\n ): Promise<SseFetchResponse<SseEventData<TConfig>>> {\n const host = this.serverProvider.hostname;\n const url = this.buildFetchUrl(host, config);\n const { body, headers: configHeaders = {} } = (config ??\n {}) as SseRequestEntryContainer<SseConfigSchema>;\n\n const requestInit: RequestInit = {\n method: \"POST\",\n headers: {\n accept: \"text/event-stream\",\n \"content-type\": \"application/json\",\n ...configHeaders,\n },\n };\n\n if (body !== undefined) {\n requestInit.body = JSON.stringify(body);\n }\n\n const response = await fetch(url, requestInit);\n return new SseFetchResponse<SseEventData<TConfig>>(response);\n }\n\n /**\n * HTTP handler for the registered route.\n * Returns a ReadableStream with SSE-formatted events.\n */\n protected httpHandler(request: ServerRequest): ReadableStream {\n const reply = request.reply;\n reply.setHeader(\"content-type\", \"text/event-stream\");\n reply.setHeader(\"cache-control\", \"no-cache\");\n reply.setHeader(\"connection\", \"keep-alive\");\n\n const handlerFn = this.handler.run.bind(this.handler);\n\n return new ReadableStream({\n start: (controller) => {\n const encoder = new TextEncoder();\n const context: SseHandlerContext<TConfig> = {\n body: request.body as any,\n params: request.params as any,\n query: request.query as any,\n headers: request.headers as any,\n request,\n emit: (data: SseEventData<TConfig>) => {\n try {\n controller.enqueue(\n encoder.encode(`data: ${JSON.stringify(data)}\\n\\n`),\n );\n } catch {\n // stream may already be closed\n }\n },\n close: () => {\n try {\n controller.close();\n } catch {\n // stream may already be closed\n }\n },\n };\n\n Promise.resolve()\n .then(() => handlerFn(context))\n .then(() => {\n try {\n controller.close();\n } catch {\n // already closed\n }\n })\n .catch((error: Error) => {\n try {\n controller.error(error);\n } catch {\n // already closed\n }\n });\n },\n });\n }\n\n /**\n * Build the fetch URL with path variables and query params.\n */\n protected buildFetchUrl(\n host: string,\n config?: SseRequestEntry<TConfig>,\n ): string {\n let url = `${host}${this.prefix}${this.path}`;\n\n const { params, query } = (config ??\n {}) as SseRequestEntryContainer<SseConfigSchema>;\n\n if (params && typeof params === \"object\") {\n for (const [key, value] of Object.entries(params)) {\n url = url.replace(`:${key}`, String(value));\n }\n }\n\n if (query && typeof query === \"object\") {\n const searchParams = new URLSearchParams();\n for (const [key, value] of Object.entries(query)) {\n if (value !== undefined) {\n searchParams.set(key, String(value));\n }\n }\n const qs = searchParams.toString();\n if (qs) {\n url += `?${qs}`;\n }\n }\n\n return url;\n }\n}\n\n// ----------------------------------------------------------------------------------------------------------\n\n/**\n * Combined callable + SsePrimitive interface.\n */\nexport interface SsePrimitiveFn<TConfig extends SseConfigSchema>\n extends SsePrimitive<TConfig> {\n (config?: SseRequestEntry<TConfig>): SseStream<SseEventData<TConfig>>;\n}\n\n$sse[KIND] = SsePrimitive;\n\n// ----------------------------------------------------------------------------------------------------------\n\n/**\n * Infer the event data type from an SSE config schema.\n */\nexport type SseEventData<TConfig extends SseConfigSchema> =\n TConfig[\"data\"] extends TSchema ? Static<TConfig[\"data\"]> : any;\n\n/**\n * Request entry type for SSE endpoints (body, params, query, headers).\n */\nexport type SseRequestEntry<\n TConfig extends SseConfigSchema,\n T = SseRequestEntryContainer<TConfig>,\n> = {\n [K in keyof T as T[K] extends undefined ? never : K]: T[K];\n};\n\n/**\n * Full container type for SSE request entries.\n */\nexport type SseRequestEntryContainer<TConfig extends SseConfigSchema> = {\n body: TConfig[\"body\"] extends TObject ? Static<TConfig[\"body\"]> : undefined;\n\n params: TConfig[\"params\"] extends TObject\n ? Static<TConfig[\"params\"]>\n : undefined;\n\n headers?: TConfig[\"headers\"] extends TObject\n ? Static<TConfig[\"headers\"]>\n : Record<string, string>;\n\n query?: TConfig[\"query\"] extends TObject\n ? Partial<Static<TConfig[\"query\"]>>\n : Record<string, string>;\n};\n","import { $env, $hook, $inject, Alepha, type Static, t } from \"alepha\";\nimport { DateTimeProvider } from \"alepha/datetime\";\nimport { $logger } from \"alepha/logger\";\nimport { ServerProvider } from \"./ServerProvider.ts\";\nimport { ServerRouterProvider } from \"./ServerRouterProvider.ts\";\n\nconst envSchema = t.object({\n SERVER_PORT: t.integer({\n default: 3000,\n min: 0,\n max: 65535,\n description: \"Set 0 to listen on a random port.\",\n }),\n SERVER_HOST: t.text({\n default: \"localhost\",\n description: \"Set 0.0.0.0 to listen on all interfaces.\",\n }),\n});\n\ndeclare module \"alepha\" {\n interface Env extends Partial<Static<typeof envSchema>> {}\n}\n\nexport class BunHttpServerProvider extends ServerProvider {\n protected readonly alepha = $inject(Alepha);\n protected readonly dateTimeProvider = $inject(DateTimeProvider);\n protected readonly log = $logger();\n protected readonly env = $env(envSchema);\n protected readonly router = $inject(ServerRouterProvider);\n\n protected bunServer?: ReturnType<typeof Bun.serve>;\n\n public get hostname(): string {\n if (this.bunServer) {\n return `http://${this.bunServer.hostname}:${this.bunServer.port}`;\n }\n return `http://${this.env.SERVER_HOST}:${this.env.SERVER_PORT}`;\n }\n\n public readonly start = $hook({\n on: \"start\",\n handler: async () => {\n await this.listen();\n },\n });\n\n protected readonly stop = $hook({\n on: \"stop\",\n handler: async () => {\n if (this.alepha.isProduction()) {\n await this.close();\n return;\n }\n\n // do not await in development & test\n this.close().catch(() => {});\n },\n });\n\n protected async listen() {\n let port = this.env.SERVER_PORT;\n\n // for testing, use a random port if port is 3000 (default)\n if (this.alepha.isTest() && port === 3000) {\n port = 0;\n }\n\n try {\n this.bunServer = Bun.serve({\n port,\n hostname: this.env.SERVER_HOST,\n fetch: async (request: Request) => {\n this.log.trace(`Incoming Bun request -> ${request.url}`);\n\n // Create WebRequestEvent for handleWebRequest\n const webRequestEvent = {\n req: request,\n res: undefined as Response | undefined,\n };\n\n try {\n await this.handleWebRequest(webRequestEvent);\n\n if (!webRequestEvent.res) {\n // No response set, return 500\n return new Response(\"Internal Server Error\", {\n status: 500,\n headers: { \"content-type\": \"text/plain\" },\n });\n }\n\n return webRequestEvent.res;\n } catch (err) {\n this.log.error(\"Error handling request\", err);\n return new Response(\"Internal Server Error\", {\n status: 500,\n headers: { \"content-type\": \"text/plain\" },\n });\n }\n },\n error: (error: Error) => {\n this.log.error(\"Bun server error\", error);\n return new Response(\"Internal Server Error\", {\n status: 500,\n headers: { \"content-type\": \"text/plain\" },\n });\n },\n });\n\n this.log.info(`Server listening on ${this.hostname}/`);\n } catch (err) {\n this.log.error(\"Failed to start Bun server\", err);\n throw err;\n }\n }\n\n protected async close() {\n if (!this.bunServer) {\n return;\n }\n\n try {\n // Bun's server.stop() returns a promise that resolves when all connections are closed\n const stopPromise = this.bunServer.stop();\n\n // Race between stop completion and timeout\n await Promise.race([this.dateTimeProvider.wait(10000), stopPromise]);\n\n this.bunServer = undefined;\n this.log.info(\"Server closed\");\n } catch (err) {\n this.log.error(\"Error closing Bun server\", err);\n throw err;\n }\n }\n}\n","import {\n createServer,\n type IncomingMessage,\n type Server,\n type ServerResponse,\n} from \"node:http\";\nimport type { Socket } from \"node:net\";\nimport { $env, $hook, $inject, Alepha, type Static, t } from \"alepha\";\nimport { DateTimeProvider } from \"alepha/datetime\";\nimport { $logger } from \"alepha/logger\";\nimport { ServerProvider } from \"./ServerProvider.ts\";\nimport { ServerRouterProvider } from \"./ServerRouterProvider.ts\";\n\nconst envSchema = t.object({\n SERVER_PORT: t.integer({\n default: 3000,\n min: 0,\n max: 65535,\n description: \"Set 0 to listen on a random port.\",\n }),\n SERVER_HOST: t.text({\n default: \"localhost\",\n description: \"Set 0.0.0.0 to listen on all interfaces.\",\n }),\n});\n\ndeclare module \"alepha\" {\n interface Env extends Partial<Static<typeof envSchema>> {}\n}\n\nexport class NodeHttpServerProvider extends ServerProvider {\n protected readonly alepha = $inject(Alepha);\n protected readonly dateTimeProvider = $inject(DateTimeProvider);\n protected readonly log = $logger();\n protected readonly env = $env(envSchema);\n protected readonly router = $inject(ServerRouterProvider);\n\n /**\n * Track active connections for fast shutdown.\n */\n protected readonly connections = new Set<Socket>();\n\n /**\n * Get number of active connections.\n */\n public getConnectionsCount(): number {\n return this.connections.size;\n }\n\n /**\n * Server options.\n */\n public readonly options = {\n /**\n * Graceful shutdown timeout in ms.\n * After this, remaining connections are forcefully closed.\n * @default 30000\n */\n shutdownTimeout: 10000,\n };\n\n public get hostname(): string {\n // sometimes hostname is called before .star(), so server may not be created yet (nor listening)\n if (this.server?.listening) {\n const address = this.server.address();\n if (typeof address === \"object\" && address !== null) {\n return `http://${this.env.SERVER_HOST}:${address.port}`;\n }\n }\n\n return `http://${this.env.SERVER_HOST}:${this.env.SERVER_PORT}`;\n }\n\n // Pre-bound error handler to avoid function allocation per request\n protected readonly handleRequestError = (res: ServerResponse, err: Error) => {\n this.log.error(\"Error handling request\", err);\n res.statusCode = 500;\n res.end(\"Internal Server Error\");\n };\n\n public server!: Server;\n\n public readonly configure = $hook({\n on: \"configure\",\n handler: async () => {\n this.server = this.createHttpServer();\n },\n });\n\n public readonly start = $hook({\n on: \"start\",\n handler: async () => {\n await this.listen();\n },\n });\n\n protected requestListener = (req: IncomingMessage, res: ServerResponse) => {\n const promise = this.handleNodeRequest({ req, res });\n promise.catch((err) => this.handleRequestError(res, err));\n };\n\n protected connectionListener = (socket: Socket) => {\n this.connections.add(socket);\n socket.on(\"close\", () => this.connections.delete(socket));\n };\n\n protected createHttpServer(): Server {\n let server: Server;\n\n const existing = this.alepha.store.get(\"alepha.node.server\");\n if (this.alepha.isViteDev() && existing) {\n server = existing;\n server.removeAllListeners(\"request\");\n // --> server.removeAllListeners(\"connection\");\n } else {\n server = createServer({\n // nov 25 - keep connections alive for better performance, cuz we http/1.1 by default\n keepAlive: this.alepha.isProduction(),\n });\n }\n\n server.on(\"request\", this.requestListener);\n\n // Track connections for fast shutdown\n server.on(\"connection\", this.connectionListener);\n\n return server;\n }\n\n protected readonly stop = $hook({\n on: \"stop\",\n handler: async () => {\n await this.close();\n },\n });\n\n protected async listen() {\n if (this.alepha.store.get(\"alepha.node.server\")) {\n return;\n }\n\n let port = this.env.SERVER_PORT;\n\n // for testing, use a random port if port is 3000 (default)\n if (this.alepha.isTest() && port === 3000) {\n port = 0;\n }\n\n await new Promise<void>((resolve, reject) => {\n this.server?.listen(port, this.env.SERVER_HOST, () => {\n this.log.info(`Server listening on ${this.hostname}/`);\n resolve();\n });\n\n this.server?.on(\"error\", (err) => {\n reject(err);\n });\n });\n\n this.alepha.store.set(\"alepha.node.server\", this.server);\n }\n\n protected async close() {\n if (this.alepha.isViteDev()) {\n this.server.removeListener(\"request\", this.requestListener);\n this.server.removeListener(\"connection\", this.connectionListener);\n return;\n }\n\n // Dev/Test: instant shutdown (destroy connections immediately)\n // Production: graceful shutdown (wait for requests to complete, then close)\n if (!this.alepha.isProduction()) {\n this.destroyAllConnections();\n }\n\n // Stop accepting new connections\n const closePromise = new Promise<void>((resolve, reject) => {\n this.server?.close((err) => (err ? reject(err) : resolve()));\n });\n\n if (this.alepha.isProduction() && this.connections.size > 0) {\n // In production, wait for connections with timeout\n const timeout = this.options.shutdownTimeout;\n\n // Set up timeout to force-close connections\n const timeoutId = setTimeout(() => {\n if (this.connections.size > 0) {\n this.log.warn(\n `Shutdown timeout (${timeout}ms) reached, forcing ${this.connections.size} connections to close`,\n );\n // Destroy sockets - this triggers 'close' events which eventually resolves closePromise\n for (const socket of this.connections) {\n socket.destroy();\n }\n }\n }, timeout);\n\n // Wait for server to fully close (all connections closed)\n await closePromise;\n clearTimeout(timeoutId);\n this.connections.clear();\n } else {\n await closePromise;\n }\n\n this.log.info(\"Server closed\");\n }\n\n protected destroyAllConnections() {\n for (const socket of this.connections) {\n socket.destroy();\n }\n this.connections.clear();\n }\n}\n","import { Readable } from \"node:stream\";\nimport { createBrotliDecompress, createGunzip, createInflate } from \"node:zlib\";\nimport type { TSchema } from \"alepha\";\nimport { $atom, $hook, $inject, $use, Alepha, type Static, t } from \"alepha\";\nimport { $logger } from \"alepha/logger\";\nimport { HttpError } from \"../errors/HttpError.ts\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\n/**\n * Body parser configuration atom.\n */\nexport const bodyParserOptions = $atom({\n name: \"alepha.server.body-parser.options\",\n schema: t.object({\n inflate: t.boolean({\n default: true,\n description: \"Enable decompression of request body.\",\n }),\n limit: t.integer({\n default: 100_000, // 100KB\n min: 0,\n description: \"Maximum size of request body in bytes.\",\n }),\n }),\n default: {\n inflate: true,\n limit: 100_000,\n },\n});\n\nexport type BodyParserOptions = Static<typeof bodyParserOptions.schema>;\n\ndeclare module \"alepha\" {\n interface State {\n [bodyParserOptions.key]: BodyParserOptions;\n }\n}\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport class ServerBodyParserProvider {\n protected readonly alepha = $inject(Alepha);\n protected readonly log = $logger();\n protected readonly options = $use(bodyParserOptions);\n\n public readonly onRequest = $hook({\n on: \"server:onRequest\",\n handler: ({ route, request }) => {\n if (request.body) {\n return; // already parsed\n }\n\n let stream: ReadableStream | undefined;\n\n if (request.raw.web?.req.body) {\n stream = request.raw.web.req.body;\n } else if (request.raw.node?.req) {\n const nodeReq = request.raw.node.req as Readable & {\n body?: string | Buffer | object;\n };\n\n if (nodeReq.body !== undefined) {\n // Body was pre-consumed by the runtime (e.g., Vercel serverless).\n // The original stream is already drained — reconstruct from pre-parsed body.\n if (typeof nodeReq.body === \"string\") {\n stream = new ReadableStream({\n start(controller) {\n controller.enqueue(\n new TextEncoder().encode(nodeReq.body as string),\n );\n controller.close();\n },\n });\n } else if (Buffer.isBuffer(nodeReq.body)) {\n stream = new ReadableStream({\n start(controller) {\n controller.enqueue(new Uint8Array(nodeReq.body as Buffer));\n controller.close();\n },\n });\n } else if (\n nodeReq.body !== null &&\n typeof nodeReq.body === \"object\"\n ) {\n // Already parsed as a JSON object — assign directly\n request.body = nodeReq.body;\n return;\n }\n } else {\n stream = Readable.toWeb(\n nodeReq as Readable,\n ) as unknown as ReadableStream;\n }\n }\n\n if (!stream) {\n return;\n }\n\n if (route.schema?.body) {\n const contentLength = request.headers[\"content-length\"];\n if (contentLength) {\n const size = Number.parseInt(contentLength, 10);\n if (!Number.isNaN(size) && size > this.options.limit) {\n throw new HttpError({\n status: 413,\n message: \"Request body size limit exceeded\",\n });\n }\n }\n\n return this.parse(stream, request.headers, route.schema.body)\n .then((body) => {\n if (body) {\n request.body = body;\n }\n })\n .catch((error) => {\n if (error instanceof HttpError) {\n throw error;\n }\n throw new HttpError(\n {\n status: 400,\n message: \"Failed to parse request body\",\n },\n error,\n );\n });\n }\n },\n });\n\n public async parse(\n stream: ReadableStream,\n headers: Record<string, string>,\n schema: TSchema,\n ): Promise<object | string | undefined> {\n const contentType = headers[\"content-type\"];\n const contentEncoding = headers[\"content-encoding\"];\n\n if (!contentType) return undefined;\n\n if (contentType.startsWith(\"text/plain\") || t.schema.isString(schema)) {\n return this.parseText(stream, contentEncoding);\n }\n\n if (contentType.startsWith(\"application/json\")) {\n return this.parseJson(stream, contentEncoding);\n }\n\n if (contentType.startsWith(\"application/x-www-form-urlencoded\")) {\n return this.parseUrlEncoded(stream, contentEncoding);\n }\n\n return undefined;\n }\n\n public async parseText(\n stream: ReadableStream,\n contentEncoding?: string,\n ): Promise<string> {\n const buffer = await this.streamToBuffer(stream);\n const bufferInflated = await this.maybeDecompress(buffer, contentEncoding);\n return bufferInflated.toString(\"utf-8\");\n }\n\n public async parseUrlEncoded(\n stream: ReadableStream,\n contentEncoding?: string,\n ): Promise<object> {\n const text = await this.parseText(stream, contentEncoding);\n const params = new URLSearchParams(text);\n const result: Record<string, string | string[]> = {};\n\n for (const key of params.keys()) {\n const values = params.getAll(key);\n result[key] = values.length === 1 ? values[0] : values;\n }\n\n return result;\n }\n\n public async parseJson(\n stream: ReadableStream,\n contentEncoding?: string,\n ): Promise<object> {\n const text = await this.parseText(stream, contentEncoding);\n return JSON.parse(text);\n }\n\n protected async maybeDecompress(\n buffer: Buffer,\n encoding: string | undefined,\n ): Promise<Buffer> {\n if (!this.options.inflate && encoding) {\n throw new HttpError({\n status: 415,\n message: `Content-Encoding ${encoding} not allowed`,\n });\n }\n\n switch (encoding) {\n case \"gzip\":\n return this.decompressBuffer(buffer, createGunzip());\n case \"deflate\":\n return this.decompressBuffer(buffer, createInflate());\n case \"br\":\n return this.decompressBuffer(buffer, createBrotliDecompress());\n case undefined:\n case \"identity\":\n return buffer;\n default:\n throw new HttpError({\n status: 415,\n message: `Unsupported Content-Encoding: ${encoding}`,\n });\n }\n }\n\n protected decompressBuffer(\n buffer: Buffer,\n transform:\n | import(\"node:zlib\").BrotliDecompress\n | import(\"node:zlib\").Gunzip\n | import(\"node:zlib\").Inflate,\n ): Promise<Buffer> {\n return new Promise((resolve, reject) => {\n const chunks: Buffer[] = [];\n transform\n .on(\"data\", (chunk: Buffer) => chunks.push(chunk))\n .on(\"end\", () => resolve(Buffer.concat(chunks)))\n .on(\"error\", reject);\n transform.end(buffer);\n });\n }\n\n /**\n * Convert Web ReadableStream to Buffer, with a size limit.\n */\n protected async streamToBuffer(stream: ReadableStream): Promise<Buffer> {\n const limit = this.options.limit;\n const chunks: Uint8Array[] = [];\n let totalLength = 0;\n\n const reader = stream.getReader();\n\n try {\n while (true) {\n const { done, value } = await reader.read();\n\n if (done) {\n break;\n }\n\n if (value) {\n totalLength += value.length;\n\n if (totalLength > limit) {\n this.log.error(\n `Body size limit exceeded: ${totalLength} > ${limit}`,\n );\n\n await reader.cancel();\n\n throw new HttpError({\n status: 413,\n message: \"Request body size limit exceeded\",\n });\n }\n\n chunks.push(value);\n }\n }\n\n const combined = new Uint8Array(totalLength);\n let offset = 0;\n\n for (const chunk of chunks) {\n combined.set(chunk, offset);\n offset += chunk.length;\n }\n\n return Buffer.from(\n combined.buffer,\n combined.byteOffset,\n combined.byteLength,\n );\n } finally {\n reader.releaseLock();\n }\n }\n}\n","import { Readable, type Transform } from \"node:stream\";\nimport { ReadableStream } from \"node:stream/web\";\nimport { promisify } from \"node:util\";\nimport * as zlib from \"node:zlib\";\nimport { $atom, $hook, $inject, $use, Alepha, type Static, t } from \"alepha\";\nimport type { ServerResponse } from \"alepha/server\";\n\nconst gzip = promisify(zlib.gzip);\nconst createGzip = zlib.createGzip;\nconst brotli = promisify(zlib.brotliCompress);\nconst createBrotliCompress = zlib.createBrotliCompress;\nconst zstd = zlib.zstdCompress ? promisify(zlib.zstdCompress) : undefined;\nconst createZstdCompress = zstd ? zlib.createZstdCompress : undefined;\n\n// ---------------------------------------------------------------------------------------------------------------------\n\n/**\n * Compression configuration atom.\n */\nexport const compressOptions = $atom({\n name: \"alepha.server.compress.options\",\n schema: t.object({\n disabled: t.optional(\n t.boolean({\n description: \"Disable response compression entirely.\",\n }),\n ),\n allowedContentTypes: t.array(t.string(), {\n description: \"Content types eligible for compression.\",\n }),\n }),\n default: {\n allowedContentTypes: [\n \"application/json\",\n \"text/html\",\n \"application/javascript\",\n \"text/plain\",\n \"text/css\",\n ],\n },\n});\n\nexport type CompressOptions = Static<typeof compressOptions.schema>;\n\ndeclare module \"alepha\" {\n interface State {\n [compressOptions.key]: CompressOptions;\n }\n}\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport class ServerCompressProvider {\n static compressors: Record<\n string,\n | {\n compress: (...args: any[]) => Promise<Buffer>;\n stream: (options?: any) => Transform;\n }\n | undefined\n > = {\n gzip: {\n compress: gzip,\n stream: createGzip,\n },\n br: {\n compress: brotli,\n stream: createBrotliCompress,\n },\n zstd:\n zstd && createZstdCompress\n ? {\n compress: zstd,\n stream: createZstdCompress,\n }\n : undefined,\n };\n\n protected readonly alepha = $inject(Alepha);\n protected readonly options = $use(compressOptions);\n\n public readonly onResponse = $hook({\n on: \"server:onResponse\",\n handler: async ({ request, response }) => {\n if (this.options.disabled) {\n return;\n }\n\n // In serverless (Cloudflare Workers), skip compression entirely:\n // Cloudflare's edge network automatically compresses responses\n if (this.alepha.isServerless()) {\n return;\n }\n\n // skip if already compressed\n if (response.headers[\"content-encoding\"]) {\n return;\n }\n\n const acceptEncoding = request.headers[\"accept-encoding\"]; // skip if no accept-encoding header\n if (!acceptEncoding) {\n return;\n }\n\n // skip if not json or html (for now)\n if (!this.isAllowedContentType(response.headers[\"content-type\"])) {\n return;\n }\n\n for (const encoding of [\"zstd\", \"br\", \"gzip\"] as const) {\n if (\n acceptEncoding.includes(encoding) &&\n ServerCompressProvider.compressors[encoding]\n ) {\n await this.compress(encoding, response);\n return;\n }\n }\n },\n });\n\n protected isAllowedContentType(contentType: string | undefined): boolean {\n if (!contentType) {\n return false;\n }\n\n const lowerContentType = contentType.toLowerCase();\n\n return !!this.options.allowedContentTypes.find((it) =>\n lowerContentType.includes(it),\n );\n }\n\n protected async compress(\n encoding: keyof typeof ServerCompressProvider.compressors,\n response: ServerResponse,\n ): Promise<void> {\n const body = response.body; // can be string or Buffer or ArrayBuffer or Readable\n\n const compressor = ServerCompressProvider.compressors[encoding];\n if (!compressor) {\n return;\n }\n\n const params = this.getParams(encoding);\n\n if (\n typeof body === \"string\" ||\n Buffer.isBuffer(body) ||\n body instanceof ArrayBuffer\n ) {\n const compressed = await compressor.compress(body, {\n params,\n });\n this.setHeaders(response, encoding);\n response.headers[\"content-length\"] = compressed.length.toString();\n response.body = compressed;\n return;\n }\n\n if (typeof body === \"object\" && body instanceof Readable) {\n this.setHeaders(response, encoding);\n response.body = body.pipe(compressor.stream({ params }));\n return;\n }\n\n if (typeof body === \"object\" && body instanceof ReadableStream) {\n this.setHeaders(response, encoding);\n // For streaming responses, use flush mode to avoid buffering\n response.body = this.createFlushingCompressStream(\n body,\n compressor.stream,\n encoding,\n params,\n );\n }\n }\n\n /**\n * Create a compressed stream that flushes after each chunk.\n * This is essential for streaming SSR - ensures each chunk is sent immediately.\n */\n protected createFlushingCompressStream(\n input: ReadableStream,\n createCompressor: (options?: any) => Transform,\n encoding: string,\n params: Record<number, any>,\n ): ReadableStream<Uint8Array> {\n const compressor = createCompressor({\n params,\n flush:\n encoding === \"br\"\n ? zlib.constants.BROTLI_OPERATION_FLUSH\n : zlib.constants.Z_SYNC_FLUSH,\n });\n const reader = Readable.fromWeb(input);\n\n return new ReadableStream<Uint8Array>({\n start(controller) {\n compressor.on(\"data\", (chunk: Buffer) => {\n controller.enqueue(new Uint8Array(chunk));\n });\n\n compressor.on(\"end\", () => {\n controller.close();\n });\n\n compressor.on(\"error\", (err) => {\n controller.error(err);\n });\n\n reader.on(\"data\", (chunk: Buffer) => {\n compressor.write(chunk);\n // Force flush after each chunk for streaming\n // Cast to any because flush() exists on zlib streams but not in Transform type\n const zlibStream = compressor as any;\n if (encoding === \"gzip\") {\n zlibStream.flush(zlib.constants.Z_SYNC_FLUSH);\n } else if (encoding === \"br\") {\n zlibStream.flush(zlib.constants.BROTLI_OPERATION_FLUSH);\n } else if (encoding === \"zstd\") {\n zlibStream.flush();\n }\n });\n\n reader.on(\"end\", () => {\n compressor.end();\n });\n\n reader.on(\"error\", (err) => {\n controller.error(err);\n });\n },\n });\n }\n\n protected getParams(\n encoding: keyof typeof ServerCompressProvider.compressors,\n ): Record<number, any> {\n if (encoding === \"zstd\") {\n return {\n [zlib.constants.ZSTD_c_compressionLevel]: 3, // default compression level for zstd\n };\n }\n if (encoding === \"br\") {\n return {};\n }\n if (encoding === \"gzip\") {\n return {};\n }\n return {};\n }\n\n protected setHeaders(\n response: ServerResponse,\n encoding: keyof typeof ServerCompressProvider.compressors,\n ): void {\n response.headers.vary = \"content-encoding\";\n response.headers[\"content-encoding\"] = encoding;\n }\n}\n","import { $atom, $hook, $inject, $use, Alepha, type Static, t } from \"alepha\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\n/**\n * Helmet security headers configuration atom\n */\nexport const helmetOptions = $atom({\n name: \"alepha.server.helmet.options\",\n schema: t.object({\n disabled: t.optional(\n t.boolean({\n description: \"Disable security headers entirely.\",\n }),\n ),\n isSecure: t.optional(t.boolean()),\n strictTransportSecurity: t.optional(\n t.object({\n maxAge: t.optional(t.number()),\n includeSubDomains: t.optional(t.boolean()),\n preload: t.optional(t.boolean()),\n }),\n ),\n xContentTypeOptions: t.optional(t.boolean()),\n xFrameOptions: t.optional(t.enum([\"DENY\", \"SAMEORIGIN\"])),\n xXssProtection: t.optional(t.boolean()),\n contentSecurityPolicy: t.optional(\n t.object({\n directives: t.record(t.string(), t.any()),\n }),\n ),\n referrerPolicy: t.optional(\n t.enum([\n \"no-referrer\",\n \"no-referrer-when-downgrade\",\n \"origin\",\n \"origin-when-cross-origin\",\n \"same-origin\",\n \"strict-origin\",\n \"strict-origin-when-cross-origin\",\n \"unsafe-url\",\n ]),\n ),\n }),\n default: {\n strictTransportSecurity: { maxAge: 15552000, includeSubDomains: true },\n xFrameOptions: \"SAMEORIGIN\",\n xXssProtection: false,\n referrerPolicy: \"strict-origin-when-cross-origin\",\n },\n});\n\nexport type HelmetOptions = Static<typeof helmetOptions.schema>;\n\ndeclare module \"alepha\" {\n interface State {\n [helmetOptions.key]: HelmetOptions;\n }\n}\n\n// ---------------------------------------------------------------------------------------------------------------------\n\ntype CspDirective = string | string[];\n\nexport interface CspDirectives {\n \"default-src\"?: CspDirective;\n \"script-src\"?: CspDirective;\n \"style-src\"?: CspDirective;\n \"img-src\"?: CspDirective;\n \"connect-src\"?: CspDirective;\n \"font-src\"?: CspDirective;\n \"object-src\"?: CspDirective;\n \"media-src\"?: CspDirective;\n \"frame-src\"?: CspDirective;\n sandbox?: CspDirective | boolean;\n \"report-uri\"?: string;\n \"child-src\"?: CspDirective;\n \"form-action\"?: CspDirective;\n \"frame-ancestors\"?: CspDirective;\n \"plugin-types\"?: CspDirective;\n \"base-uri\"?: CspDirective;\n [key: string]: CspDirective | undefined | boolean;\n}\n\nexport interface CspOptions {\n directives: CspDirectives;\n}\n\nexport interface HstsOptions {\n maxAge?: number;\n includeSubDomains?: boolean;\n preload?: boolean;\n}\n\n/**\n * Provides a configurable way to apply essential HTTP security headers\n * to every server response, without external dependencies.\n */\nexport class ServerHelmetProvider {\n protected readonly alepha = $inject(Alepha);\n\n /**\n * The configuration options loaded from the atom.\n */\n protected readonly options = $use(helmetOptions);\n\n protected defaultCspDirectives(): CspDirectives {\n return {\n \"default-src\": [\"'self'\"],\n \"base-uri\": [\"'self'\"],\n \"font-src\": [\"'self'\", \"https:\", \"data:\"],\n \"form-action\": [\"'self'\"],\n \"frame-ancestors\": [\"'self'\"],\n \"img-src\": [\"'self'\", \"data:\"],\n \"object-src\": [\"'none'\"],\n \"script-src\": [\"'self'\"],\n \"script-src-attr\": [\"'none'\"],\n \"style-src\": [\"'self'\", \"https:\", \"'unsafe-inline'\"],\n \"upgrade-insecure-requests\": [],\n };\n }\n\n protected buildHeaders(): Record<string, string> {\n return this.buildHeadersFromConfig(this.options);\n }\n\n protected buildHeadersFromConfig(\n config: HelmetOptions,\n ): Record<string, string> {\n const headers: Record<string, string> = {};\n const {\n strictTransportSecurity: hsts,\n xContentTypeOptions,\n xFrameOptions,\n xXssProtection,\n contentSecurityPolicy: csp,\n referrerPolicy,\n } = config;\n\n // Strict-Transport-Security\n if (hsts) {\n let value = `max-age=${hsts.maxAge ?? 15552000}`;\n if (hsts.includeSubDomains) value += \"; includeSubDomains\";\n if (hsts.preload) value += \"; preload\";\n headers[\"strict-transport-security\"] = value;\n }\n\n // X-Content-Type-Options\n if (xContentTypeOptions !== false) {\n headers[\"x-content-type-options\"] = \"nosniff\";\n }\n\n // X-Frame-Options\n if (xFrameOptions) {\n headers[\"x-frame-options\"] = xFrameOptions;\n }\n\n // X-XSS-Protection\n if (xXssProtection !== false) {\n headers[\"x-xss-protection\"] = \"1; mode=block\";\n }\n\n // Referrer-Policy\n if (referrerPolicy) {\n headers[\"referrer-policy\"] = referrerPolicy;\n }\n\n // Content-Security-Policy\n if (csp) {\n const directives =\n Object.keys(csp).length === 0\n ? this.defaultCspDirectives()\n : csp.directives;\n headers[\"content-security-policy\"] = Object.entries(directives)\n .map(([key, value]) => {\n const kebabKey = key.replace(\n /[A-Z]/g,\n (letter) => `-${letter.toLowerCase()}`,\n );\n if (Array.isArray(value)) {\n return `${kebabKey} ${value.join(\" \")}`;\n }\n if (typeof value === \"boolean\" && value) {\n return kebabKey;\n }\n return `${kebabKey} ${value}`;\n })\n .join(\"; \");\n }\n\n return headers;\n }\n\n protected readonly onResponse = $hook({\n on: \"server:onResponse\",\n priority: \"first\",\n handler: ({ response }) => {\n if (this.options.disabled) {\n return;\n }\n\n // this check is important. Only add HSTS on HTTPS requests.\n const isSecure =\n response.headers[\"x-forwarded-proto\"] === \"https\" ||\n this.options.isSecure ||\n this.alepha.isProduction();\n\n const headersToSet = this.buildHeaders();\n\n for (const [key, value] of Object.entries(headersToSet)) {\n if (key === \"strict-transport-security\" && !isSecure) {\n continue;\n }\n // don't overwrite headers if they are already set\n if (!response.headers[key]) {\n response.headers[key] = value;\n }\n }\n },\n });\n}\n","import { $hook, $inject, Alepha } from \"alepha\";\nimport { $logger } from \"alepha/logger\";\n\nexport class ServerLoggerProvider {\n protected readonly log = $logger();\n protected readonly alepha = $inject(Alepha);\n\n public readonly onRequest = $hook({\n on: \"server:onRequest\",\n priority: \"first\",\n handler: ({ route, request }) => {\n if (route.silent || request.metadata.vite) {\n return;\n }\n\n request.metadata.now = Date.now();\n\n const search = request.url.search;\n const data: Record<string, string> = {\n method: request.method,\n path: search\n ? `${request.url.pathname}${search}`\n : request.url.pathname,\n };\n\n if (this.alepha.isProduction()) {\n data.agent = request.headers[\"user-agent\"];\n const ip = request.ip;\n if (ip) {\n data.ip = ip;\n }\n }\n\n this.log.info(\"Incoming request\", data);\n },\n });\n\n public readonly onError = $hook({\n on: \"server:onError\",\n priority: \"last\",\n handler: ({ error }) => {\n this.log.error(\"Request has failed\", error);\n },\n });\n\n public readonly onResponse = $hook({\n on: \"server:onResponse\",\n priority: \"last\",\n handler: ({ route, request, response }) => {\n if (route.silent || request.metadata.vite) {\n return;\n }\n\n const ms = Date.now() - request.metadata.now;\n const search = request.url.search;\n this.log.info(\"Request completed\", {\n method: request.method,\n path: search\n ? `${request.url.pathname}${search}`\n : request.url.pathname,\n status: response.status,\n duration: ms,\n });\n },\n });\n}\n","import { Readable } from \"node:stream\";\nimport {\n $atom,\n $hook,\n $inject,\n $use,\n Alepha,\n type FileLike,\n isTypeFile,\n type Static,\n t,\n} from \"alepha\";\nimport { $logger } from \"alepha/logger\";\nimport { HttpError, isMultipart, type ServerRoute } from \"alepha/server\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\n/**\n * Multipart configuration atom.\n */\nexport const multipartOptions = $atom({\n name: \"alepha.server.multipart.options\",\n schema: t.object({\n limit: t.integer({\n default: 10_000_000, // 10MB total\n min: 0,\n description: \"Maximum total size of multipart request body in bytes.\",\n }),\n fileLimit: t.integer({\n default: 5_000_000, // 5MB per file\n min: 0,\n description: \"Maximum size of a single file in bytes.\",\n }),\n fileCount: t.integer({\n default: 10,\n min: 1,\n description: \"Maximum number of files allowed in a single request.\",\n }),\n }),\n default: {\n limit: 10_000_000,\n fileLimit: 5_000_000,\n fileCount: 10,\n },\n});\n\nexport type MultipartOptions = Static<typeof multipartOptions.schema>;\n\ndeclare module \"alepha\" {\n interface State {\n [multipartOptions.key]: MultipartOptions;\n }\n}\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport class ServerMultipartProvider {\n protected readonly alepha = $inject(Alepha);\n protected readonly log = $logger();\n protected readonly options = $use(multipartOptions);\n\n public readonly onRequest = $hook({\n on: \"server:onRequest\",\n handler: async ({ route, request }) => {\n if (request.body) {\n return;\n }\n\n if (!route.schema?.body) {\n return;\n }\n\n let webRequest: Request | undefined;\n\n if (request.raw.web?.req) {\n webRequest = request.raw.web.req;\n } else if (request.raw.node?.req) {\n webRequest = new Request(request.url, {\n method: request.method,\n headers: request.headers,\n body: Readable.toWeb(\n request.raw.node.req as Readable,\n ) as unknown as ReadableStream,\n duplex: \"half\",\n } as RequestInit & { duplex: \"half\" });\n }\n\n if (!webRequest) {\n return;\n }\n\n const contentType = request.headers[\"content-type\"];\n\n // Check content-length before processing to fail fast on oversized requests\n const contentLength = request.headers[\"content-length\"];\n if (contentLength) {\n const size = Number.parseInt(contentLength, 10);\n if (!Number.isNaN(size) && size > this.options.limit) {\n this.log.error(\n `Multipart request size limit exceeded: ${size} > ${this.options.limit}`,\n );\n throw new HttpError({\n status: 413,\n message: `Request body size limit exceeded. Maximum allowed: ${this.options.limit} bytes`,\n });\n }\n }\n\n if (!contentType?.startsWith(\"multipart/form-data\")) {\n if (!isMultipart(route)) {\n return;\n }\n\n throw new HttpError({\n status: 415,\n message: `Invalid content-type: ${contentType} - only \"multipart/form-data\" is accepted`,\n });\n }\n\n request.body = await this.parseMultipart(route, webRequest);\n },\n });\n\n public async parseMultipart(\n route: ServerRoute,\n request: Request,\n ): Promise<Record<string, unknown>> {\n let formData: FormData;\n\n try {\n formData = await request.formData();\n } catch (error) {\n throw new HttpError(\n {\n status: 400,\n message: \"Malformed multipart/form-data\",\n },\n error,\n );\n }\n\n const body: Record<string, any> = {};\n let fileCount = 0;\n let totalSize = 0;\n\n if (route.schema?.body && t.schema.isObject(route.schema.body)) {\n for (const [key, value] of Object.entries(route.schema.body.properties)) {\n if (!t.schema.isSchema(value)) {\n continue;\n }\n\n if (isTypeFile(value)) {\n const file = formData.get(key);\n if (file && typeof file === \"object\" && \"arrayBuffer\" in file) {\n const blob = file as Blob;\n\n fileCount++;\n if (fileCount > this.options.fileCount) {\n this.log.error(\n `Too many files in multipart request: ${fileCount} > ${this.options.fileCount}`,\n );\n throw new HttpError({\n status: 413,\n message: `Too many files. Maximum allowed: ${this.options.fileCount}`,\n });\n }\n\n if (blob.size > this.options.fileLimit) {\n this.log.error(\n `File \"${key}\" exceeds size limit: ${blob.size} > ${this.options.fileLimit}`,\n );\n throw new HttpError({\n status: 413,\n message: `File \"${key}\" exceeds size limit. Maximum allowed: ${this.options.fileLimit} bytes`,\n });\n }\n\n totalSize += blob.size;\n if (totalSize > this.options.limit) {\n this.log.error(\n `Total multipart size exceeds limit: ${totalSize} > ${this.options.limit}`,\n );\n throw new HttpError({\n status: 413,\n message: `Total request size exceeds limit. Maximum allowed: ${this.options.limit} bytes`,\n });\n }\n\n body[key] = this.blobToFileLike(blob, key);\n }\n } else {\n const fieldValue = formData.get(key);\n if (fieldValue !== null) {\n const stringValue =\n typeof fieldValue === \"string\" ? fieldValue : \"\";\n body[key] = this.alepha.codec.decode(value, stringValue);\n }\n }\n }\n }\n\n return body;\n }\n\n protected blobToFileLike(blob: Blob, fieldName: string): FileLike {\n const isFile = blob instanceof File;\n\n return {\n name: isFile ? blob.name : `${fieldName}_${Date.now()}`,\n type: blob.type || \"application/octet-stream\",\n size: blob.size,\n lastModified: isFile ? blob.lastModified : Date.now(),\n\n stream() {\n return blob.stream() as unknown as ReadableStream;\n },\n\n arrayBuffer() {\n return blob.arrayBuffer();\n },\n\n text() {\n return blob.text();\n },\n };\n }\n}\n","import { $hook, $inject, Alepha } from \"alepha\";\nimport { HttpError } from \"../errors/HttpError.ts\";\n\n/**\n * On every request, this provider checks if the server is ready.\n *\n * If the server is not ready, it responds with a 503 status code and a message indicating that the server is not ready yet.\n *\n * The response also includes a `Retry-After` header indicating that the client should retry after 5 seconds.\n */\nexport class ServerNotReadyProvider {\n protected readonly alepha = $inject(Alepha);\n\n public readonly onRequest = $hook({\n on: \"server:onRequest\",\n priority: \"first\",\n handler: ({ request: { reply } }) => {\n if (this.alepha.isReady()) {\n return;\n }\n\n reply.headers[\"Retry-After\"] = \"5\"; // Retry after 5 seconds\n\n throw new HttpError({\n status: 503,\n message: \"Server is not ready yet. Please try again later.\",\n });\n },\n });\n}\n","export const routeMethods = [\n // list of supported http methods\n \"GET\",\n \"POST\",\n \"PUT\",\n \"PATCH\",\n \"DELETE\",\n \"HEAD\",\n \"OPTIONS\",\n \"CONNECT\",\n \"TRACE\",\n] as const;\n\nexport type RouteMethod = (typeof routeMethods)[number];\n","import { HttpError } from \"./HttpError.ts\";\n\nexport class BadRequestError extends HttpError {\n constructor(message = \"Invalid request body\", cause?: unknown) {\n super(\n {\n message,\n status: 400,\n },\n cause,\n );\n }\n}\n","import { HttpError } from \"./HttpError.ts\";\n\nexport class ConflictError extends HttpError {\n constructor(message = \"Entity already exists\", cause?: unknown) {\n super(\n {\n message,\n status: 409,\n },\n cause,\n );\n }\n}\n","import { HttpError } from \"./HttpError.ts\";\n\nexport class ForbiddenError extends HttpError {\n constructor(\n message = \"No permission to access this resource\",\n cause?: unknown,\n ) {\n super(\n {\n message,\n status: 403,\n },\n cause,\n );\n }\n}\n","import { HttpError } from \"./HttpError.ts\";\n\nexport class NotFoundError extends HttpError {\n constructor(message = \"Resource not found\", cause?: unknown) {\n super(\n {\n message,\n status: 404,\n },\n cause,\n );\n }\n}\n","import { HttpError } from \"./HttpError.ts\";\n\nexport class UnauthorizedError extends HttpError {\n readonly name = \"UnauthorizedError\";\n\n constructor(\n message = \"Not allowed to access this resource\",\n cause?: unknown,\n ) {\n super(\n {\n message,\n status: 401,\n },\n cause,\n );\n }\n}\n","import type { Static } from \"alepha\";\nimport { t } from \"alepha\";\n\nexport const okSchema = t.object(\n {\n ok: t.boolean({ description: \"True when operation succeed\" }),\n id: t.optional(t.union([t.text(), t.integer()])),\n count: t.optional(\n t.number({ description: \"Number of resources affected\" }),\n ),\n },\n {\n title: \"Ok\",\n description: \"Generic response after a successful operation on a resource\",\n },\n);\n\nexport type Ok = Static<typeof okSchema>;\n","import { AlephaError, createMiddleware, type Middleware } from \"alepha\";\nimport { DateTimeProvider, type DurationLike } from \"alepha/datetime\";\n\nexport interface CircuitBreakerOptions {\n /**\n * Consecutive failures before opening the circuit.\n */\n threshold: number;\n\n /**\n * Cooldown before transitioning from open to half-open.\n */\n reset: DurationLike;\n}\n\n/**\n * Middleware that implements the circuit breaker pattern.\n *\n * Three states:\n * - **Closed** (normal) — calls pass through. Failures are counted.\n * - **Open** (tripped) — calls are immediately rejected. No handler execution.\n * - **Half-open** (probing) — one call is allowed. Success closes, failure re-opens.\n *\n * ```typescript\n * class PaymentController {\n * charge = $action({\n * use: [$circuit({ threshold: 5, reset: [30, \"seconds\"] })],\n * handler: async ({ body }) => this.paymentGateway.charge(body),\n * });\n * }\n * ```\n */\nexport const $circuit = (options: CircuitBreakerOptions): Middleware => {\n return createMiddleware({\n name: \"$circuit\",\n options,\n handler: ({ alepha, next }) => {\n const dateTimeProvider = alepha.inject(DateTimeProvider);\n const resetMs = dateTimeProvider.duration(options.reset).asMilliseconds();\n\n let state: \"closed\" | \"open\" | \"half-open\" = \"closed\";\n let failures = 0;\n let openedAt = 0;\n\n return async (...args) => {\n if (state === \"open\") {\n if (dateTimeProvider.nowMillis() - openedAt >= resetMs) {\n state = \"half-open\";\n } else {\n throw new AlephaError(\"$circuit: circuit is open\");\n }\n }\n\n try {\n const result = await next(...args);\n failures = 0;\n state = \"closed\";\n return result;\n } catch (error) {\n failures++;\n if (failures >= options.threshold) {\n state = \"open\";\n openedAt = dateTimeProvider.nowMillis();\n }\n throw error;\n }\n };\n },\n });\n};\n","import type { Server } from \"node:http\";\nimport { $module, type Alepha } from \"alepha\";\nimport type { HttpError } from \"./errors/HttpError.ts\";\nimport type {\n NodeRequestEvent,\n RequestConfigSchema,\n ServerRequest,\n ServerRequestConfigEntry,\n ServerResponse,\n ServerRoute,\n WebRequestEvent,\n} from \"./interfaces/ServerRequest.ts\";\nimport {\n $action,\n type ActionPrimitive,\n type ClientRequestOptions,\n} from \"./primitives/$action.ts\";\nimport { $middleware } from \"./primitives/$middleware.ts\";\nimport { $route } from \"./primitives/$route.ts\";\nimport { $sse } from \"./primitives/$sse.ts\";\nimport { BunHttpServerProvider } from \"./providers/BunHttpServerProvider.ts\";\nimport { NodeHttpServerProvider } from \"./providers/NodeHttpServerProvider.ts\";\nimport { ServerBodyParserProvider } from \"./providers/ServerBodyParserProvider.ts\";\nimport { ServerCompressProvider } from \"./providers/ServerCompressProvider.ts\";\nimport { ServerHelmetProvider } from \"./providers/ServerHelmetProvider.ts\";\nimport { ServerLoggerProvider } from \"./providers/ServerLoggerProvider.ts\";\nimport { ServerMultipartProvider } from \"./providers/ServerMultipartProvider.ts\";\nimport { ServerNotReadyProvider } from \"./providers/ServerNotReadyProvider.ts\";\nimport { ServerProvider } from \"./providers/ServerProvider.ts\";\nimport { ServerRouterProvider } from \"./providers/ServerRouterProvider.ts\";\nimport { ServerTimingProvider } from \"./providers/ServerTimingProvider.ts\";\nimport type { FetchOptions, HttpAction } from \"./services/HttpClient.ts\";\nimport { HttpClient } from \"./services/HttpClient.ts\";\nimport { ServerRequestParser } from \"./services/ServerRequestParser.ts\";\nimport { UserAgentParser } from \"./services/UserAgentParser.ts\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\ndeclare module \"alepha\" {\n interface State {\n \"alepha.node.server\"?: Server;\n \"alepha.http.request\"?: ServerRequest;\n \"alepha.action.request\"?: ServerRequest;\n }\n interface Hooks {\n // -----------------------------------------------------------------------------------------------------------------\n // Local Actions hooks\n \"action:onRequest\": {\n action: ActionPrimitive<RequestConfigSchema>;\n request: ServerRequest;\n options: ClientRequestOptions;\n context?: Record<string, any>;\n };\n \"action:onResponse\": {\n action: ActionPrimitive<RequestConfigSchema>;\n request: ServerRequest;\n options: ClientRequestOptions;\n response: any;\n };\n // -----------------------------------------------------------------------------------------------------------------\n // Server hooks\n \"server:onRequest\": {\n route: ServerRoute;\n request: ServerRequest;\n };\n \"server:onError\": {\n route: ServerRoute;\n request: ServerRequest;\n error: Error;\n };\n // last chance to modify the response -\n // TODO: probably not really needed, we can also update the response in the onResponse hook...\n \"server:onSend\": {\n route: ServerRoute;\n request: ServerRequest;\n };\n // response is ready\n \"server:onResponse\": {\n route: ServerRoute;\n request: ServerRequest;\n response: ServerResponse;\n };\n // -----------------------------------------------------------------------------------------------------------------\n // Http client hooks\n \"client:onRequest\": {\n route: HttpAction;\n config: ServerRequestConfigEntry;\n options: ClientRequestOptions;\n headers: Record<string, string>;\n request: RequestInit;\n };\n \"client:beforeFetch\": {\n url: string;\n options: FetchOptions;\n request: RequestInit;\n };\n \"client:onError\": {\n route?: HttpAction;\n error: HttpError;\n };\n // -----------------------------------------------------------------------------------------------------------------\n // Internal hooks\n \"node:request\": NodeRequestEvent;\n \"web:request\": WebRequestEvent;\n }\n}\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport * from \"./index.shared.ts\";\nexport * from \"./primitives/$action.ts\";\nexport * from \"./primitives/$circuit.ts\";\nexport * from \"./primitives/$middleware.ts\";\nexport * from \"./primitives/$route.ts\";\nexport * from \"./primitives/$sse.ts\";\nexport * from \"./providers/BunHttpServerProvider.ts\";\nexport * from \"./providers/NodeHttpServerProvider.ts\";\nexport * from \"./providers/ServerCompressProvider.ts\";\nexport * from \"./providers/ServerHelmetProvider.ts\";\nexport * from \"./providers/ServerLoggerProvider.ts\";\nexport * from \"./providers/ServerMultipartProvider.ts\";\nexport * from \"./providers/ServerNotReadyProvider.ts\";\nexport * from \"./providers/ServerProvider.ts\";\nexport * from \"./providers/ServerRouterProvider.ts\";\nexport * from \"./providers/ServerTimingProvider.ts\";\nexport * from \"./services/UserAgentParser.ts\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\n/**\n * Convention-driven HTTP server with automatic validation and type inference.\n *\n * **Features:**\n * - Type-safe API endpoints with schema validation\n * - Lower-level HTTP route definitions\n * - Automatic request/response validation via TypeBox\n * - Convention-based URL generation (`/api/{ActionName}`)\n * - Direct invocation (`run()`) or HTTP (`fetch()`)\n * - Built-in authentication integration\n * - Multipart file upload handling\n * - Response compression (gzip, brotli, zstd)\n * - Security headers (HSTS, CSP, X-Frame-Options, etc.)\n * - Content-type auto-negotiation (JSON, form-data, text)\n * - HTTP methods: GET, POST, PUT, DELETE, PATCH, HEAD, OPTIONS\n * - Error handling: BadRequestError, ValidationError, ForbiddenError, UnauthorizedError, ConflictError, NotFoundError\n *\n * @module alepha.server\n */\nexport const AlephaServer = $module({\n name: \"alepha.server\",\n primitives: [$route, $action, $middleware, $sse],\n services: [\n ServerProvider,\n BunHttpServerProvider,\n NodeHttpServerProvider,\n ServerBodyParserProvider,\n ServerCompressProvider,\n ServerHelmetProvider,\n ServerMultipartProvider,\n ServerLoggerProvider,\n ServerNotReadyProvider,\n ServerTimingProvider,\n HttpClient,\n UserAgentParser,\n ServerRequestParser,\n ServerRouterProvider,\n ],\n register: (alepha: Alepha) => {\n // In Vite dev mode, Vite owns the HTTP server - just use base ServerProvider\n // In serverless mode, no HTTP server needed - just use base ServerProvider\n if (!alepha.isServerless() && !alepha.isViteDev()) {\n if (alepha.isBun()) {\n alepha.with({\n optional: true,\n provide: ServerProvider,\n use: BunHttpServerProvider,\n });\n } else {\n alepha.with({\n optional: true,\n provide: ServerProvider,\n use: NodeHttpServerProvider,\n });\n }\n } else {\n alepha.with(ServerProvider);\n }\n\n alepha.with(ServerBodyParserProvider);\n alepha.with(ServerCompressProvider);\n alepha.with(ServerHelmetProvider);\n alepha.with(ServerMultipartProvider);\n\n if (!alepha.isTest()) {\n alepha.with(ServerLoggerProvider);\n alepha.with(ServerNotReadyProvider);\n }\n\n if (!alepha.isProduction()) {\n alepha.with(ServerTimingProvider);\n }\n },\n});\n"],"mappings":"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"}
|