alepha 0.15.1 → 0.15.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/README.md +68 -80
- package/dist/api/audits/index.d.ts +10 -33
- package/dist/api/audits/index.d.ts.map +1 -1
- package/dist/api/audits/index.js +10 -33
- package/dist/api/audits/index.js.map +1 -1
- package/dist/api/files/index.d.ts +10 -3
- package/dist/api/files/index.d.ts.map +1 -1
- package/dist/api/files/index.js +10 -3
- package/dist/api/files/index.js.map +1 -1
- package/dist/api/jobs/index.d.ts +162 -155
- package/dist/api/jobs/index.d.ts.map +1 -1
- package/dist/api/jobs/index.js +10 -3
- package/dist/api/jobs/index.js.map +1 -1
- package/dist/api/keys/index.d.ts +413 -0
- package/dist/api/keys/index.d.ts.map +1 -0
- package/dist/api/keys/index.js +476 -0
- package/dist/api/keys/index.js.map +1 -0
- package/dist/api/notifications/index.d.ts +10 -4
- package/dist/api/notifications/index.d.ts.map +1 -1
- package/dist/api/notifications/index.js +10 -4
- package/dist/api/notifications/index.js.map +1 -1
- package/dist/api/parameters/index.d.ts +43 -50
- package/dist/api/parameters/index.d.ts.map +1 -1
- package/dist/api/parameters/index.js +30 -37
- package/dist/api/parameters/index.js.map +1 -1
- package/dist/api/users/index.d.ts +1081 -760
- package/dist/api/users/index.d.ts.map +1 -1
- package/dist/api/users/index.js +2539 -218
- package/dist/api/users/index.js.map +1 -1
- package/dist/api/verifications/index.d.ts +138 -132
- package/dist/api/verifications/index.d.ts.map +1 -1
- package/dist/api/verifications/index.js +12 -4
- package/dist/api/verifications/index.js.map +1 -1
- package/dist/batch/index.d.ts +20 -40
- package/dist/batch/index.d.ts.map +1 -1
- package/dist/batch/index.js +31 -44
- package/dist/batch/index.js.map +1 -1
- package/dist/bucket/index.d.ts +440 -8
- package/dist/bucket/index.d.ts.map +1 -1
- package/dist/bucket/index.js +1861 -12
- package/dist/bucket/index.js.map +1 -1
- package/dist/cache/core/index.d.ts +179 -7
- package/dist/cache/core/index.d.ts.map +1 -1
- package/dist/cache/core/index.js +213 -7
- package/dist/cache/core/index.js.map +1 -1
- package/dist/cache/redis/index.d.ts +1 -0
- package/dist/cache/redis/index.d.ts.map +1 -1
- package/dist/cache/redis/index.js +4 -0
- package/dist/cache/redis/index.js.map +1 -1
- package/dist/cli/index.d.ts +638 -5645
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +2550 -368
- package/dist/cli/index.js.map +1 -1
- package/dist/command/index.d.ts +203 -45
- package/dist/command/index.d.ts.map +1 -1
- package/dist/command/index.js +2060 -71
- package/dist/command/index.js.map +1 -1
- package/dist/core/index.browser.js +70 -40
- package/dist/core/index.browser.js.map +1 -1
- package/dist/core/index.d.ts +34 -13
- package/dist/core/index.d.ts.map +1 -1
- package/dist/core/index.js +90 -40
- package/dist/core/index.js.map +1 -1
- package/dist/core/index.native.js +70 -40
- package/dist/core/index.native.js.map +1 -1
- package/dist/datetime/index.d.ts +15 -0
- package/dist/datetime/index.d.ts.map +1 -1
- package/dist/datetime/index.js +15 -0
- package/dist/datetime/index.js.map +1 -1
- package/dist/email/index.d.ts +323 -20
- package/dist/email/index.d.ts.map +1 -1
- package/dist/email/index.js +1857 -7
- package/dist/email/index.js.map +1 -1
- package/dist/fake/index.d.ts +90 -8
- package/dist/fake/index.d.ts.map +1 -1
- package/dist/fake/index.js +91 -20
- package/dist/fake/index.js.map +1 -1
- package/dist/lock/core/index.d.ts +11 -4
- package/dist/lock/core/index.d.ts.map +1 -1
- package/dist/lock/core/index.js +11 -4
- package/dist/lock/core/index.js.map +1 -1
- package/dist/logger/index.d.ts +17 -66
- package/dist/logger/index.d.ts.map +1 -1
- package/dist/logger/index.js +14 -63
- package/dist/logger/index.js.map +1 -1
- package/dist/mcp/index.d.ts +10 -30
- package/dist/mcp/index.d.ts.map +1 -1
- package/dist/mcp/index.js +12 -35
- package/dist/mcp/index.js.map +1 -1
- package/dist/orm/index.browser.js +3 -3
- package/dist/orm/index.browser.js.map +1 -1
- package/dist/orm/index.bun.js +39 -20
- package/dist/orm/index.bun.js.map +1 -1
- package/dist/orm/index.d.ts +517 -540
- package/dist/orm/index.d.ts.map +1 -1
- package/dist/orm/index.js +58 -71
- package/dist/orm/index.js.map +1 -1
- package/dist/queue/core/index.d.ts +18 -10
- package/dist/queue/core/index.d.ts.map +1 -1
- package/dist/queue/core/index.js +14 -6
- package/dist/queue/core/index.js.map +1 -1
- package/dist/react/auth/index.browser.js +108 -0
- package/dist/react/auth/index.browser.js.map +1 -0
- package/dist/react/auth/index.d.ts +100 -0
- package/dist/react/auth/index.d.ts.map +1 -0
- package/dist/react/auth/index.js +145 -0
- package/dist/react/auth/index.js.map +1 -0
- package/dist/react/core/index.d.ts +469 -0
- package/dist/react/core/index.d.ts.map +1 -0
- package/dist/react/core/index.js +464 -0
- package/dist/react/core/index.js.map +1 -0
- package/dist/react/form/index.d.ts +232 -0
- package/dist/react/form/index.d.ts.map +1 -0
- package/dist/react/form/index.js +432 -0
- package/dist/react/form/index.js.map +1 -0
- package/dist/react/head/index.browser.js +423 -0
- package/dist/react/head/index.browser.js.map +1 -0
- package/dist/react/head/index.d.ts +288 -0
- package/dist/react/head/index.d.ts.map +1 -0
- package/dist/react/head/index.js +465 -0
- package/dist/react/head/index.js.map +1 -0
- package/dist/react/i18n/index.d.ts +175 -0
- package/dist/react/i18n/index.d.ts.map +1 -0
- package/dist/react/i18n/index.js +224 -0
- package/dist/react/i18n/index.js.map +1 -0
- package/dist/react/router/index.browser.js +1974 -0
- package/dist/react/router/index.browser.js.map +1 -0
- package/dist/react/router/index.d.ts +1956 -0
- package/dist/react/router/index.d.ts.map +1 -0
- package/dist/react/router/index.js +4722 -0
- package/dist/react/router/index.js.map +1 -0
- package/dist/react/websocket/index.d.ts +117 -0
- package/dist/react/websocket/index.d.ts.map +1 -0
- package/dist/react/websocket/index.js +107 -0
- package/dist/react/websocket/index.js.map +1 -0
- package/dist/redis/index.bun.js +4 -0
- package/dist/redis/index.bun.js.map +1 -1
- package/dist/redis/index.d.ts +41 -44
- package/dist/redis/index.d.ts.map +1 -1
- package/dist/redis/index.js +16 -25
- package/dist/redis/index.js.map +1 -1
- package/dist/retry/index.d.ts +11 -2
- package/dist/retry/index.d.ts.map +1 -1
- package/dist/retry/index.js +11 -2
- package/dist/retry/index.js.map +1 -1
- package/dist/scheduler/index.d.ts +11 -2
- package/dist/scheduler/index.d.ts.map +1 -1
- package/dist/scheduler/index.js +11 -2
- package/dist/scheduler/index.js.map +1 -1
- package/dist/security/index.d.ts +140 -49
- package/dist/security/index.d.ts.map +1 -1
- package/dist/security/index.js +164 -32
- package/dist/security/index.js.map +1 -1
- package/dist/server/auth/index.d.ts +12 -7
- package/dist/server/auth/index.d.ts.map +1 -1
- package/dist/server/auth/index.js +12 -7
- package/dist/server/auth/index.js.map +1 -1
- package/dist/server/cache/index.d.ts +7 -22
- package/dist/server/cache/index.d.ts.map +1 -1
- package/dist/server/cache/index.js +7 -22
- package/dist/server/cache/index.js.map +1 -1
- package/dist/server/compress/index.d.ts +10 -2
- package/dist/server/compress/index.d.ts.map +1 -1
- package/dist/server/compress/index.js +10 -2
- package/dist/server/compress/index.js.map +1 -1
- package/dist/server/cookies/index.d.ts +40 -16
- package/dist/server/cookies/index.d.ts.map +1 -1
- package/dist/server/cookies/index.js +7 -5
- package/dist/server/cookies/index.js.map +1 -1
- package/dist/server/core/index.d.ts +124 -23
- package/dist/server/core/index.d.ts.map +1 -1
- package/dist/server/core/index.js +231 -14
- package/dist/server/core/index.js.map +1 -1
- package/dist/server/cors/index.d.ts +13 -23
- package/dist/server/cors/index.d.ts.map +1 -1
- package/dist/server/cors/index.js +7 -21
- package/dist/server/cors/index.js.map +1 -1
- package/dist/server/health/index.d.ts +8 -2
- package/dist/server/health/index.d.ts.map +1 -1
- package/dist/server/health/index.js +8 -2
- package/dist/server/health/index.js.map +1 -1
- package/dist/server/helmet/index.d.ts +11 -3
- package/dist/server/helmet/index.d.ts.map +1 -1
- package/dist/server/helmet/index.js +11 -3
- package/dist/server/helmet/index.js.map +1 -1
- package/dist/server/links/index.d.ts +11 -6
- package/dist/server/links/index.d.ts.map +1 -1
- package/dist/server/links/index.js +11 -6
- package/dist/server/links/index.js.map +1 -1
- package/dist/server/metrics/index.d.ts +10 -3
- package/dist/server/metrics/index.d.ts.map +1 -1
- package/dist/server/metrics/index.js +10 -3
- package/dist/server/metrics/index.js.map +1 -1
- package/dist/server/multipart/index.d.ts +9 -3
- package/dist/server/multipart/index.d.ts.map +1 -1
- package/dist/server/multipart/index.js +9 -3
- package/dist/server/multipart/index.js.map +1 -1
- package/dist/server/proxy/index.d.ts +8 -2
- package/dist/server/proxy/index.d.ts.map +1 -1
- package/dist/server/proxy/index.js +8 -2
- package/dist/server/proxy/index.js.map +1 -1
- package/dist/server/rate-limit/index.d.ts +30 -35
- package/dist/server/rate-limit/index.d.ts.map +1 -1
- package/dist/server/rate-limit/index.js +18 -55
- package/dist/server/rate-limit/index.js.map +1 -1
- package/dist/server/static/index.d.ts +137 -4
- package/dist/server/static/index.d.ts.map +1 -1
- package/dist/server/static/index.js +1853 -5
- package/dist/server/static/index.js.map +1 -1
- package/dist/server/swagger/index.d.ts +309 -6
- package/dist/server/swagger/index.d.ts.map +1 -1
- package/dist/server/swagger/index.js +1854 -6
- package/dist/server/swagger/index.js.map +1 -1
- package/dist/sms/index.d.ts +309 -7
- package/dist/sms/index.d.ts.map +1 -1
- package/dist/sms/index.js +1856 -7
- package/dist/sms/index.js.map +1 -1
- package/dist/system/index.browser.js +1218 -0
- package/dist/system/index.browser.js.map +1 -0
- package/dist/{file → system}/index.d.ts +343 -16
- package/dist/system/index.d.ts.map +1 -0
- package/dist/{file → system}/index.js +419 -22
- package/dist/system/index.js.map +1 -0
- package/dist/thread/index.d.ts +11 -2
- package/dist/thread/index.d.ts.map +1 -1
- package/dist/thread/index.js +11 -2
- package/dist/thread/index.js.map +1 -1
- package/dist/topic/core/index.d.ts +12 -5
- package/dist/topic/core/index.d.ts.map +1 -1
- package/dist/topic/core/index.js +12 -5
- package/dist/topic/core/index.js.map +1 -1
- package/dist/vite/index.d.ts +5 -6272
- package/dist/vite/index.d.ts.map +1 -1
- package/dist/vite/index.js +23 -10
- package/dist/vite/index.js.map +1 -1
- package/dist/websocket/index.d.ts +12 -8
- package/dist/websocket/index.d.ts.map +1 -1
- package/dist/websocket/index.js +12 -8
- package/dist/websocket/index.js.map +1 -1
- package/package.json +82 -11
- package/src/api/audits/index.ts +10 -33
- package/src/api/files/__tests__/$bucket.spec.ts +1 -1
- package/src/api/files/controllers/AdminFileStatsController.spec.ts +1 -1
- package/src/api/files/controllers/FileController.spec.ts +1 -1
- package/src/api/files/index.ts +10 -3
- package/src/api/files/jobs/FileJobs.spec.ts +1 -1
- package/src/api/files/services/FileService.spec.ts +1 -1
- package/src/api/jobs/index.ts +10 -3
- package/src/api/keys/controllers/AdminApiKeyController.ts +75 -0
- package/src/api/keys/controllers/ApiKeyController.ts +103 -0
- package/src/api/keys/entities/apiKeyEntity.ts +41 -0
- package/src/api/keys/index.ts +49 -0
- package/src/api/keys/schemas/adminApiKeyQuerySchema.ts +7 -0
- package/src/api/keys/schemas/adminApiKeyResourceSchema.ts +17 -0
- package/src/api/keys/schemas/createApiKeyBodySchema.ts +7 -0
- package/src/api/keys/schemas/createApiKeyResponseSchema.ts +11 -0
- package/src/api/keys/schemas/listApiKeyResponseSchema.ts +15 -0
- package/src/api/keys/schemas/revokeApiKeyParamsSchema.ts +5 -0
- package/src/api/keys/schemas/revokeApiKeyResponseSchema.ts +5 -0
- package/src/api/keys/services/ApiKeyService.spec.ts +553 -0
- package/src/api/keys/services/ApiKeyService.ts +306 -0
- package/src/api/logs/TODO.md +55 -0
- package/src/api/notifications/index.ts +10 -4
- package/src/api/parameters/index.ts +9 -30
- package/src/api/parameters/primitives/$config.ts +12 -4
- package/src/api/parameters/services/ConfigStore.ts +9 -3
- package/src/api/users/__tests__/ApiKeys-integration.spec.ts +1035 -0
- package/src/api/users/__tests__/ApiKeys.spec.ts +401 -0
- package/src/api/users/index.ts +14 -3
- package/src/api/users/primitives/$realm.ts +33 -5
- package/src/api/users/providers/RealmProvider.ts +1 -12
- package/src/api/users/services/SessionService.ts +1 -1
- package/src/api/verifications/controllers/VerificationController.ts +2 -0
- package/src/api/verifications/index.ts +10 -4
- package/src/batch/index.ts +9 -36
- package/src/batch/primitives/$batch.ts +0 -8
- package/src/batch/providers/BatchProvider.ts +29 -2
- package/src/bucket/__tests__/shared.ts +1 -1
- package/src/bucket/index.ts +13 -6
- package/src/bucket/primitives/$bucket.ts +1 -1
- package/src/bucket/providers/LocalFileStorageProvider.ts +1 -1
- package/src/bucket/providers/MemoryFileStorageProvider.ts +1 -1
- package/src/cache/core/__tests__/shared.ts +30 -0
- package/src/cache/core/index.ts +11 -6
- package/src/cache/core/primitives/$cache.spec.ts +5 -0
- package/src/cache/core/providers/CacheProvider.ts +17 -0
- package/src/cache/core/providers/MemoryCacheProvider.ts +300 -1
- package/src/cache/redis/__tests__/cache-redis.spec.ts +5 -0
- package/src/cache/redis/providers/RedisCacheProvider.ts +9 -0
- package/src/cli/apps/AlephaCli.ts +1 -14
- package/src/cli/apps/AlephaPackageBuilderCli.ts +10 -1
- package/src/cli/atoms/buildOptions.ts +99 -9
- package/src/cli/commands/build.ts +150 -37
- package/src/cli/commands/db.ts +22 -18
- package/src/cli/commands/deploy.ts +1 -1
- package/src/cli/commands/dev.ts +1 -20
- package/src/cli/commands/gen/env.ts +5 -2
- package/src/cli/commands/gen/openapi.ts +5 -2
- package/src/cli/commands/init.spec.ts +588 -0
- package/src/cli/commands/init.ts +115 -58
- package/src/cli/commands/lint.ts +7 -1
- package/src/cli/commands/typecheck.ts +11 -0
- package/src/cli/providers/AppEntryProvider.ts +1 -1
- package/src/cli/providers/ViteBuildProvider.ts +8 -50
- package/src/cli/providers/ViteDevServerProvider.ts +35 -16
- package/src/cli/services/AlephaCliUtils.ts +52 -121
- package/src/cli/services/PackageManagerUtils.ts +129 -11
- package/src/cli/services/ProjectScaffolder.spec.ts +97 -0
- package/src/cli/services/ProjectScaffolder.ts +148 -81
- package/src/cli/services/ViteUtils.ts +82 -0
- package/src/cli/{assets/claudeMd.ts → templates/agentMd.ts} +37 -24
- package/src/cli/templates/apiAppSecurityTs.ts +11 -0
- package/src/cli/templates/apiIndexTs.ts +30 -0
- package/src/cli/templates/gitignore.ts +39 -0
- package/src/cli/{assets → templates}/mainCss.ts +11 -2
- package/src/cli/templates/mainServerTs.ts +33 -0
- package/src/cli/templates/webAppRouterTs.ts +74 -0
- package/src/cli/templates/webHelloComponentTsx.ts +30 -0
- package/src/command/helpers/Runner.spec.ts +139 -0
- package/src/command/helpers/Runner.ts +7 -22
- package/src/command/index.ts +12 -4
- package/src/command/providers/CliProvider.spec.ts +1392 -0
- package/src/command/providers/CliProvider.ts +320 -47
- package/src/core/Alepha.ts +34 -27
- package/src/core/__tests__/Alepha-start.spec.ts +4 -4
- package/src/core/helpers/jsonSchemaToTypeBox.spec.ts +771 -0
- package/src/core/helpers/jsonSchemaToTypeBox.ts +62 -10
- package/src/core/index.shared.ts +1 -0
- package/src/core/index.ts +20 -0
- package/src/core/providers/EventManager.spec.ts +0 -71
- package/src/core/providers/EventManager.ts +3 -15
- package/src/core/providers/Json.ts +2 -14
- package/src/datetime/index.ts +15 -0
- package/src/email/index.ts +10 -5
- package/src/email/providers/LocalEmailProvider.spec.ts +1 -1
- package/src/email/providers/LocalEmailProvider.ts +1 -1
- package/src/fake/__tests__/keyName.example.ts +1 -1
- package/src/fake/__tests__/keyName.spec.ts +5 -5
- package/src/fake/index.ts +9 -6
- package/src/fake/providers/FakeProvider.spec.ts +258 -40
- package/src/fake/providers/FakeProvider.ts +133 -19
- package/src/lock/core/index.ts +11 -4
- package/src/logger/index.ts +17 -66
- package/src/mcp/index.ts +10 -27
- package/src/mcp/transports/SseMcpTransport.ts +0 -11
- package/src/orm/__tests__/PostgresProvider.spec.ts +2 -2
- package/src/orm/index.browser.ts +2 -2
- package/src/orm/index.bun.ts +5 -3
- package/src/orm/index.ts +23 -53
- package/src/orm/providers/drivers/BunSqliteProvider.ts +5 -1
- package/src/orm/providers/drivers/CloudflareD1Provider.ts +57 -30
- package/src/orm/providers/drivers/DatabaseProvider.ts +9 -1
- package/src/orm/providers/drivers/NodeSqliteProvider.ts +4 -1
- package/src/orm/services/Repository.ts +7 -3
- package/src/queue/core/index.ts +14 -6
- package/src/react/auth/__tests__/$auth.spec.ts +202 -0
- package/src/react/auth/hooks/useAuth.ts +32 -0
- package/src/react/auth/index.browser.ts +13 -0
- package/src/react/auth/index.shared.ts +2 -0
- package/src/react/auth/index.ts +48 -0
- package/src/react/auth/providers/ReactAuthProvider.ts +16 -0
- package/src/react/auth/services/ReactAuth.ts +135 -0
- package/src/react/core/__tests__/Router.spec.tsx +169 -0
- package/src/react/core/components/ClientOnly.tsx +49 -0
- package/src/react/core/components/ErrorBoundary.tsx +73 -0
- package/src/react/core/contexts/AlephaContext.ts +7 -0
- package/src/react/core/contexts/AlephaProvider.tsx +42 -0
- package/src/react/core/hooks/useAction.browser.spec.tsx +569 -0
- package/src/react/core/hooks/useAction.ts +480 -0
- package/src/react/core/hooks/useAlepha.ts +26 -0
- package/src/react/core/hooks/useClient.ts +17 -0
- package/src/react/core/hooks/useEvents.ts +51 -0
- package/src/react/core/hooks/useInject.ts +12 -0
- package/src/react/core/hooks/useStore.ts +52 -0
- package/src/react/core/index.ts +90 -0
- package/src/react/form/components/FormState.tsx +17 -0
- package/src/react/form/errors/FormValidationError.ts +18 -0
- package/src/react/form/hooks/useForm.browser.spec.tsx +366 -0
- package/src/react/form/hooks/useForm.ts +47 -0
- package/src/react/form/hooks/useFormState.ts +130 -0
- package/src/react/form/index.ts +44 -0
- package/src/react/form/services/FormModel.ts +614 -0
- package/src/react/head/helpers/SeoExpander.spec.ts +203 -0
- package/src/react/head/helpers/SeoExpander.ts +142 -0
- package/src/react/head/hooks/useHead.spec.tsx +288 -0
- package/src/react/head/hooks/useHead.ts +62 -0
- package/src/react/head/index.browser.ts +26 -0
- package/src/react/head/index.ts +44 -0
- package/src/react/head/interfaces/Head.ts +105 -0
- package/src/react/head/primitives/$head.ts +25 -0
- package/src/react/head/providers/BrowserHeadProvider.browser.spec.ts +196 -0
- package/src/react/head/providers/BrowserHeadProvider.ts +212 -0
- package/src/react/head/providers/HeadProvider.ts +168 -0
- package/src/react/head/providers/ServerHeadProvider.ts +31 -0
- package/src/react/i18n/__tests__/integration.spec.tsx +239 -0
- package/src/react/i18n/components/Localize.spec.tsx +357 -0
- package/src/react/i18n/components/Localize.tsx +35 -0
- package/src/react/i18n/hooks/useI18n.browser.spec.tsx +438 -0
- package/src/react/i18n/hooks/useI18n.ts +18 -0
- package/src/react/i18n/index.ts +41 -0
- package/src/react/i18n/primitives/$dictionary.ts +69 -0
- package/src/react/i18n/providers/I18nProvider.spec.ts +389 -0
- package/src/react/i18n/providers/I18nProvider.ts +278 -0
- package/src/react/router/__tests__/page-head-browser.browser.spec.ts +95 -0
- package/src/react/router/__tests__/page-head.spec.ts +48 -0
- package/src/react/router/__tests__/seo-head.spec.ts +125 -0
- package/src/react/router/atoms/ssrManifestAtom.ts +58 -0
- package/src/react/router/components/ErrorViewer.tsx +872 -0
- package/src/react/router/components/Link.tsx +23 -0
- package/src/react/router/components/NestedView.tsx +223 -0
- package/src/react/router/components/NotFound.tsx +30 -0
- package/src/react/router/constants/PAGE_PRELOAD_KEY.ts +6 -0
- package/src/react/router/contexts/RouterLayerContext.ts +12 -0
- package/src/react/router/errors/Redirection.ts +28 -0
- package/src/react/router/hooks/useActive.ts +52 -0
- package/src/react/router/hooks/useQueryParams.ts +63 -0
- package/src/react/router/hooks/useRouter.ts +20 -0
- package/src/react/router/hooks/useRouterState.ts +11 -0
- package/src/react/router/index.browser.ts +45 -0
- package/src/react/router/index.shared.ts +19 -0
- package/src/react/router/index.ts +146 -0
- package/src/react/router/primitives/$page.browser.spec.tsx +851 -0
- package/src/react/router/primitives/$page.spec.tsx +676 -0
- package/src/react/router/primitives/$page.ts +489 -0
- package/src/react/router/providers/ReactBrowserProvider.ts +312 -0
- package/src/react/router/providers/ReactBrowserRendererProvider.ts +25 -0
- package/src/react/router/providers/ReactBrowserRouterProvider.ts +168 -0
- package/src/react/router/providers/ReactPageProvider.ts +726 -0
- package/src/react/router/providers/ReactPreloadProvider.spec.ts +142 -0
- package/src/react/router/providers/ReactPreloadProvider.ts +85 -0
- package/src/react/router/providers/ReactServerProvider.spec.tsx +316 -0
- package/src/react/router/providers/ReactServerProvider.ts +487 -0
- package/src/react/router/providers/ReactServerTemplateProvider.spec.ts +210 -0
- package/src/react/router/providers/ReactServerTemplateProvider.ts +542 -0
- package/src/react/router/providers/SSRManifestProvider.ts +334 -0
- package/src/react/router/services/ReactPageServerService.ts +48 -0
- package/src/react/router/services/ReactPageService.ts +27 -0
- package/src/react/router/services/ReactRouter.ts +262 -0
- package/src/react/websocket/hooks/useRoom.tsx +242 -0
- package/src/react/websocket/index.ts +7 -0
- package/src/redis/__tests__/redis.spec.ts +13 -0
- package/src/redis/index.ts +9 -25
- package/src/redis/providers/BunRedisProvider.ts +9 -0
- package/src/redis/providers/NodeRedisProvider.ts +8 -0
- package/src/redis/providers/RedisProvider.ts +16 -0
- package/src/retry/index.ts +11 -2
- package/src/router/index.ts +15 -0
- package/src/scheduler/index.ts +11 -2
- package/src/security/__tests__/BasicAuth.spec.ts +2 -0
- package/src/security/__tests__/ServerSecurityProvider.spec.ts +90 -5
- package/src/security/index.ts +15 -10
- package/src/security/interfaces/IssuerResolver.ts +27 -0
- package/src/security/primitives/$issuer.ts +55 -0
- package/src/security/providers/SecurityProvider.ts +179 -0
- package/src/security/providers/ServerBasicAuthProvider.ts +6 -2
- package/src/security/providers/ServerSecurityProvider.ts +63 -41
- package/src/server/auth/index.ts +12 -7
- package/src/server/cache/index.ts +7 -22
- package/src/server/compress/index.ts +10 -2
- package/src/server/cookies/index.ts +7 -5
- package/src/server/cookies/primitives/$cookie.ts +33 -11
- package/src/server/core/index.ts +16 -6
- package/src/server/core/interfaces/ServerRequest.ts +83 -1
- package/src/server/core/primitives/$action.spec.ts +1 -1
- package/src/server/core/primitives/$action.ts +8 -3
- package/src/server/core/providers/NodeHttpServerProvider.spec.ts +9 -3
- package/src/server/core/providers/NodeHttpServerProvider.ts +9 -3
- package/src/server/core/services/ServerRequestParser.spec.ts +520 -0
- package/src/server/core/services/ServerRequestParser.ts +306 -13
- package/src/server/cors/index.ts +7 -21
- package/src/server/cors/primitives/$cors.ts +6 -2
- package/src/server/health/index.ts +8 -2
- package/src/server/helmet/index.ts +11 -3
- package/src/server/links/index.ts +11 -6
- package/src/server/metrics/index.ts +10 -3
- package/src/server/multipart/index.ts +9 -3
- package/src/server/proxy/index.ts +8 -2
- package/src/server/rate-limit/index.ts +21 -25
- package/src/server/rate-limit/primitives/$rateLimit.ts +6 -2
- package/src/server/rate-limit/providers/ServerRateLimitProvider.spec.ts +38 -14
- package/src/server/rate-limit/providers/ServerRateLimitProvider.ts +22 -56
- package/src/server/static/index.ts +8 -2
- package/src/server/static/providers/ServerStaticProvider.ts +1 -1
- package/src/server/swagger/index.ts +9 -4
- package/src/server/swagger/providers/ServerSwaggerProvider.ts +1 -1
- package/src/sms/index.ts +9 -5
- package/src/sms/providers/LocalSmsProvider.spec.ts +1 -1
- package/src/sms/providers/LocalSmsProvider.ts +1 -1
- package/src/system/index.browser.ts +36 -0
- package/src/system/index.ts +62 -0
- package/src/system/index.workerd.ts +1 -0
- package/src/{file → system}/providers/FileSystemProvider.ts +24 -0
- package/src/{file → system}/providers/MemoryFileSystemProvider.ts +116 -3
- package/src/system/providers/MemoryShellProvider.ts +164 -0
- package/src/{file → system}/providers/NodeFileSystemProvider.spec.ts +2 -2
- package/src/{file → system}/providers/NodeFileSystemProvider.ts +47 -2
- package/src/system/providers/NodeShellProvider.ts +184 -0
- package/src/system/providers/ShellProvider.ts +74 -0
- package/src/{file → system}/services/FileDetector.spec.ts +2 -2
- package/src/thread/index.ts +11 -2
- package/src/topic/core/index.ts +12 -5
- package/src/vite/tasks/buildClient.ts +2 -7
- package/src/vite/tasks/buildServer.ts +19 -13
- package/src/vite/tasks/generateCloudflare.ts +10 -7
- package/src/vite/tasks/generateDocker.ts +4 -0
- package/src/websocket/index.ts +12 -8
- package/dist/file/index.d.ts.map +0 -1
- package/dist/file/index.js.map +0 -1
- package/src/cli/assets/apiIndexTs.ts +0 -16
- package/src/cli/assets/mainServerTs.ts +0 -24
- package/src/cli/assets/webAppRouterTs.ts +0 -16
- package/src/cli/assets/webHelloComponentTsx.ts +0 -20
- package/src/cli/providers/ViteTemplateProvider.ts +0 -27
- package/src/file/index.ts +0 -43
- /package/src/cli/{assets → templates}/apiHelloControllerTs.ts +0 -0
- /package/src/cli/{assets → templates}/biomeJson.ts +0 -0
- /package/src/cli/{assets → templates}/dummySpecTs.ts +0 -0
- /package/src/cli/{assets → templates}/editorconfig.ts +0 -0
- /package/src/cli/{assets → templates}/mainBrowserTs.ts +0 -0
- /package/src/cli/{assets → templates}/tsconfigJson.ts +0 -0
- /package/src/cli/{assets → templates}/webIndexTs.ts +0 -0
- /package/src/{file → system}/errors/FileError.ts +0 -0
- /package/src/{file → system}/services/FileDetector.ts +0 -0
|
@@ -25,9 +25,13 @@ declare const $cors: {
|
|
|
25
25
|
[KIND]: typeof CorsPrimitive;
|
|
26
26
|
};
|
|
27
27
|
interface CorsPrimitiveConfig extends Partial<CorsOptions> {
|
|
28
|
-
/**
|
|
28
|
+
/**
|
|
29
|
+
* Name identifier for this CORS config (default: property key).
|
|
30
|
+
*/
|
|
29
31
|
name?: string;
|
|
30
|
-
/**
|
|
32
|
+
/**
|
|
33
|
+
* Path patterns to match (supports wildcards like /api/*).
|
|
34
|
+
*/
|
|
31
35
|
paths?: string[];
|
|
32
36
|
}
|
|
33
37
|
interface AbstractCorsPrimitive {
|
|
@@ -108,30 +112,16 @@ declare module "alepha/server" {
|
|
|
108
112
|
}
|
|
109
113
|
}
|
|
110
114
|
/**
|
|
111
|
-
*
|
|
115
|
+
* | type | quality | stability |
|
|
116
|
+
* |------|---------|-----------|
|
|
117
|
+
* | backend | standard | stable |
|
|
112
118
|
*
|
|
113
|
-
*
|
|
114
|
-
* ```ts
|
|
115
|
-
* import { Alepha, $route } from "alepha";
|
|
116
|
-
* import { AlephaServerCors, $cors } from "alepha/server-cors";
|
|
119
|
+
* Cross-Origin Resource Sharing configuration.
|
|
117
120
|
*
|
|
118
|
-
*
|
|
119
|
-
*
|
|
121
|
+
* **Features:**
|
|
122
|
+
* - CORS policy definition
|
|
120
123
|
*
|
|
121
|
-
*
|
|
122
|
-
* apiCors = $cors({
|
|
123
|
-
* paths: ["/api/*"],
|
|
124
|
-
* origin: "https://app.example.com",
|
|
125
|
-
* credentials: true,
|
|
126
|
-
* });
|
|
127
|
-
*
|
|
128
|
-
* route = $route({
|
|
129
|
-
* path: "/api/data",
|
|
130
|
-
* method: "POST",
|
|
131
|
-
* handler: () => ({ data: "hello" }),
|
|
132
|
-
* });
|
|
133
|
-
* }
|
|
134
|
-
* ```
|
|
124
|
+
* @module alepha.server.cors
|
|
135
125
|
*/
|
|
136
126
|
declare const AlephaServerCors: alepha1.Service<alepha1.Module>;
|
|
137
127
|
//#endregion
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","names":[],"sources":["../../../src/server/cors/primitives/$cors.ts","../../../src/server/cors/providers/ServerCorsProvider.ts","../../../src/server/cors/index.ts"],"mappings":";;;;;;;;;AAoBA;;;;;;;;;;;;;cAAa,KAAA;EAAA,UAAkB,mBAAA,GAAsB,qBAAA;EAAA;;UAMpC,mBAAA,SAA4B,OAAA,CAAQ,WAAA
|
|
1
|
+
{"version":3,"file":"index.d.ts","names":[],"sources":["../../../src/server/cors/primitives/$cors.ts","../../../src/server/cors/providers/ServerCorsProvider.ts","../../../src/server/cors/index.ts"],"mappings":";;;;;;;;;AAoBA;;;;;;;;;;;;;cAAa,KAAA;EAAA,UAAkB,mBAAA,GAAsB,qBAAA;EAAA;;UAMpC,mBAAA,SAA4B,OAAA,CAAQ,WAAA;;;;EAInD,IAAA;EAAA;;;EAIA,KAAA;AAAA;AAAA,UAKe,qBAAA;EAAA,SACN,IAAA;EAAA,SACA,OAAA,EAAS,mBAAA;AAAA;AAAA,cAGP,aAAA,SACH,SAAA,CAAU,mBAAA,aACP,qBAAA;EAAA,mBAEQ,kBAAA,EAAkB,kBAAA;EAAA,IAE1B,IAAA,CAAA;EAAA,UAID,MAAA,CAAA;AAAA;;;;;;cC5CC,WAAA,EAAW,OAAA,CAAA,IAAA,SAAA,OAAA;4BAoCtB,OAAA,CAAA,OAAA;;;;;;KAEU,WAAA,GAAc,MAAA,QAAc,WAAA,CAAY,MAAA;AAAA;EAAA,UAGxC,KAAA;IAAA,CACP,WAAA,CAAY,GAAA,GAAM,WAAA;EAAA;AAAA;AAAA,cAMV,kBAAA;EAAA,mBACQ,GAAA,EADU,cAAA,CACP,MAAA;EAAA,mBACH,oBAAA,EAAoB,oBAAA;EAAA,mBACpB,aAAA,EAAa,QAAA;;;;;;;ED3BhC;;;EAAA,SCgCgB,iBAAA,EAAmB,mBAAA;ED3BC;;;ECgC7B,YAAA,CAAa,MAAA,EAAQ,mBAAA;EAAA,mBAIT,OAAA,EAJ4B,OAAA,CAIrB,aAAA;EAAA,mBAuBP,SAAA,EAvBO,OAAA,CAuBE,aAAA;EAAA,mBAwBT,SAAA,EAxBS,OAAA,CAwBA,aAAA;EDjFS;AAGvC;;EAHuC,UC6F3B,gBAAA,CAAiB,MAAA,EAAQ,mBAAA,GAAsB,WAAA;EDzFvC;;;EAAA,UCsGR,gBAAA,CACR,OAAA;IACE,OAAA;MAAW,MAAA;IAAA;IACX,KAAA;MAAS,SAAA,GAAY,IAAA,UAAc,KAAA;IAAA;EAAA,GAErC,OAAA,EAAS,WAAA;EAqBJ,eAAA,CACL,MAAA,sBACA,OAAA,EAAS,WAAA;AAAA;AAAA,KAWD,yBAAA,GAA4B,WAAA;;;;YC3K5B,WAAA;;AFKZ;;;IEAI,IAAA,GAAO,WAAA;EAAA;AAAA;;;;;;;;;;;AFMX;;cEYa,gBAAA,EAAgB,OAAA,CAAA,OAAA,CAI3B,OAAA,CAJ2B,MAAA"}
|
|
@@ -161,30 +161,16 @@ $cors[KIND] = CorsPrimitive;
|
|
|
161
161
|
//#endregion
|
|
162
162
|
//#region ../../src/server/cors/index.ts
|
|
163
163
|
/**
|
|
164
|
-
*
|
|
164
|
+
* | type | quality | stability |
|
|
165
|
+
* |------|---------|-----------|
|
|
166
|
+
* | backend | standard | stable |
|
|
165
167
|
*
|
|
166
|
-
*
|
|
167
|
-
* ```ts
|
|
168
|
-
* import { Alepha, $route } from "alepha";
|
|
169
|
-
* import { AlephaServerCors, $cors } from "alepha/server-cors";
|
|
168
|
+
* Cross-Origin Resource Sharing configuration.
|
|
170
169
|
*
|
|
171
|
-
*
|
|
172
|
-
*
|
|
170
|
+
* **Features:**
|
|
171
|
+
* - CORS policy definition
|
|
173
172
|
*
|
|
174
|
-
*
|
|
175
|
-
* apiCors = $cors({
|
|
176
|
-
* paths: ["/api/*"],
|
|
177
|
-
* origin: "https://app.example.com",
|
|
178
|
-
* credentials: true,
|
|
179
|
-
* });
|
|
180
|
-
*
|
|
181
|
-
* route = $route({
|
|
182
|
-
* path: "/api/data",
|
|
183
|
-
* method: "POST",
|
|
184
|
-
* handler: () => ({ data: "hello" }),
|
|
185
|
-
* });
|
|
186
|
-
* }
|
|
187
|
-
* ```
|
|
173
|
+
* @module alepha.server.cors
|
|
188
174
|
*/
|
|
189
175
|
const AlephaServerCors = $module({
|
|
190
176
|
name: "alepha.server.cors",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":[],"sources":["../../../src/server/cors/providers/ServerCorsProvider.ts","../../../src/server/cors/primitives/$cors.ts","../../../src/server/cors/index.ts"],"sourcesContent":["import { $atom, $hook, $inject, $use, type Static, t } from \"alepha\";\nimport { $logger } from \"alepha/logger\";\nimport { ServerRouterProvider } from \"alepha/server\";\nimport type { CorsPrimitiveConfig } from \"../primitives/$cors.ts\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\n/**\n * CORS configuration atom (global defaults)\n */\nexport const corsOptions = $atom({\n name: \"alepha.server.cors.options\",\n schema: t.object({\n origin: t.optional(\n t.string({\n description:\n \"Allowed origins (* for all, string for single, comma-separated for multiple)\",\n default: \"*\",\n }),\n ),\n methods: t.array(t.string(), {\n description: \"Allowed HTTP methods\",\n default: [\"GET\", \"POST\", \"PUT\", \"PATCH\", \"DELETE\", \"OPTIONS\"],\n }),\n headers: t.array(t.string(), {\n description: \"Allowed headers\",\n default: [\"Content-Type\", \"Authorization\"],\n }),\n credentials: t.optional(\n t.boolean({\n description: \"Allow credentials\",\n default: true,\n }),\n ),\n maxAge: t.optional(\n t.number({\n description: \"Preflight cache duration in seconds\",\n }),\n ),\n }),\n default: {\n origin: \"*\",\n methods: [\"GET\", \"POST\", \"PUT\", \"PATCH\", \"DELETE\", \"OPTIONS\"],\n headers: [\"Content-Type\", \"Authorization\"],\n credentials: true,\n },\n});\n\nexport type CorsOptions = Static<typeof corsOptions.schema>;\n\ndeclare module \"alepha\" {\n interface State {\n [corsOptions.key]: CorsOptions;\n }\n}\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport class ServerCorsProvider {\n protected readonly log = $logger();\n protected readonly serverRouterProvider = $inject(ServerRouterProvider);\n protected readonly globalOptions = $use(corsOptions);\n\n /**\n * Registered CORS configurations with their path patterns\n */\n public readonly registeredConfigs: CorsPrimitiveConfig[] = [];\n\n /**\n * Register a CORS configuration (called by primitives)\n */\n public registerCors(config: CorsPrimitiveConfig): void {\n this.registeredConfigs.push(config);\n }\n\n protected readonly onStart = $hook({\n on: \"start\",\n handler: async () => {\n // Apply path-specific CORS configs to routes\n for (const config of this.registeredConfigs) {\n if (config.paths) {\n for (const pattern of config.paths) {\n const matchedRoutes = this.serverRouterProvider.getRoutes(pattern);\n for (const route of matchedRoutes) {\n route.cors = this.buildCorsOptions(config);\n }\n }\n }\n }\n\n if (this.registeredConfigs.length > 0) {\n this.log.info(\n `Initialized with ${this.registeredConfigs.length} registered CORS configurations.`,\n );\n }\n },\n });\n\n protected readonly configure = $hook({\n on: \"configure\",\n handler: () => {\n const routes = this.serverRouterProvider.getRoutes();\n for (const route of routes) {\n if (\n !route.method ||\n route.method === \"GET\" ||\n route.method === \"OPTIONS\"\n ) {\n continue;\n }\n\n this.serverRouterProvider.createRoute({\n path: route.path,\n method: \"OPTIONS\",\n handler: ({ reply }) => {\n reply.setStatus(204);\n },\n });\n }\n },\n });\n\n protected readonly onRequest = $hook({\n on: \"server:onRequest\",\n handler: ({ route, request }) => {\n // Use route-specific CORS if defined, otherwise use global options\n const corsConfig = route.cors ?? this.globalOptions;\n this.applyCorsHeaders(request, corsConfig);\n },\n });\n\n /**\n * Build complete CORS options by merging with global defaults\n */\n protected buildCorsOptions(config: CorsPrimitiveConfig): CorsOptions {\n return {\n origin: config.origin ?? this.globalOptions.origin,\n methods: config.methods ?? this.globalOptions.methods,\n headers: config.headers ?? this.globalOptions.headers,\n credentials: config.credentials ?? this.globalOptions.credentials,\n maxAge: config.maxAge ?? this.globalOptions.maxAge,\n };\n }\n\n /**\n * Apply CORS headers to the response\n */\n protected applyCorsHeaders(\n request: {\n headers: { origin?: string };\n reply: { setHeader: (name: string, value: string) => void };\n },\n options: CorsOptions,\n ): void {\n const reqOrigin = request.headers.origin;\n const { origin, methods, headers, credentials, maxAge } = options;\n\n if (reqOrigin && this.isOriginAllowed(reqOrigin, origin)) {\n request.reply.setHeader(\"Access-Control-Allow-Origin\", reqOrigin);\n }\n\n if (credentials) {\n request.reply.setHeader(\"Access-Control-Allow-Credentials\", \"true\");\n }\n\n request.reply.setHeader(\"Access-Control-Allow-Methods\", methods.join(\", \"));\n request.reply.setHeader(\"Access-Control-Allow-Headers\", headers.join(\", \"));\n\n if (maxAge != null) {\n request.reply.setHeader(\"Access-Control-Max-Age\", String(maxAge));\n }\n }\n\n public isOriginAllowed(\n origin: string | undefined,\n allowed: CorsOptions[\"origin\"],\n ): boolean {\n if (!allowed) return false;\n if (allowed === \"*\") return true;\n return allowed\n .split(\",\")\n .map((o) => o.trim())\n .includes(origin ?? \"\");\n }\n}\n\nexport type ServerCorsProviderOptions = CorsOptions;\n","import { $inject, createPrimitive, KIND, Primitive } from \"alepha\";\nimport type { CorsOptions } from \"../providers/ServerCorsProvider.ts\";\nimport { ServerCorsProvider } from \"../providers/ServerCorsProvider.ts\";\n\n/**\n * Declares CORS configuration for specific server routes.\n * This primitive provides path-based CORS configuration.\n *\n * @example\n * ```ts\n * class ApiService {\n * // Apply specific CORS to API routes\n * cors = $cors({\n * paths: [\"/api/*\"],\n * origin: \"https://app.example.com\",\n * credentials: true,\n * });\n * }\n * ```\n */\nexport const $cors = (options: CorsPrimitiveConfig): AbstractCorsPrimitive => {\n return createPrimitive(CorsPrimitive, options);\n};\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport interface CorsPrimitiveConfig extends Partial<CorsOptions> {\n /** Name identifier for this CORS config (default: property key) */\n name?: string;\n /** Path patterns to match (supports wildcards like /api/*) */\n paths?: string[];\n}\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport interface AbstractCorsPrimitive {\n readonly name: string;\n readonly options: CorsPrimitiveConfig;\n}\n\nexport class CorsPrimitive\n extends Primitive<CorsPrimitiveConfig>\n implements AbstractCorsPrimitive\n{\n protected readonly serverCorsProvider = $inject(ServerCorsProvider);\n\n public get name(): string {\n return this.options.name ?? `${this.config.propertyKey}`;\n }\n\n protected onInit() {\n // Register this CORS configuration with the provider\n this.serverCorsProvider.registerCors(this.options);\n }\n}\n\n$cors[KIND] = CorsPrimitive;\n","import { $module } from \"alepha\";\nimport { $cors } from \"./primitives/$cors.ts\";\nimport {\n type CorsOptions,\n ServerCorsProvider,\n} from \"./providers/ServerCorsProvider.ts\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport * from \"./primitives/$cors.ts\";\nexport * from \"./providers/ServerCorsProvider.ts\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\ndeclare module \"alepha/server\" {\n interface ServerRoute {\n /**\n * Route-specific CORS configuration.\n * If set, overrides the global CORS options for this route.\n */\n cors?: CorsOptions;\n }\n}\n\n// ---------------------------------------------------------------------------------------------------------------------\n\n/**\n * Plugin for configuring CORS on the Alepha server.\n *\n * @example\n * ```ts\n * import { Alepha, $route } from \"alepha\";\n * import { AlephaServerCors, $cors } from \"alepha/server-cors\";\n *\n * class ApiService {\n * // Global CORS is applied via corsOptions atom\n *\n * // Path-specific CORS for API routes\n * apiCors = $cors({\n * paths: [\"/api/*\"],\n * origin: \"https://app.example.com\",\n * credentials: true,\n * });\n *\n * route = $route({\n * path: \"/api/data\",\n * method: \"POST\",\n * handler: () => ({ data: \"hello\" }),\n * });\n * }\n * ```\n */\nexport const AlephaServerCors = $module({\n name: \"alepha.server.cors\",\n primitives: [$cors],\n services: [ServerCorsProvider],\n});\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../../../src/server/cors/providers/ServerCorsProvider.ts","../../../src/server/cors/primitives/$cors.ts","../../../src/server/cors/index.ts"],"sourcesContent":["import { $atom, $hook, $inject, $use, type Static, t } from \"alepha\";\nimport { $logger } from \"alepha/logger\";\nimport { ServerRouterProvider } from \"alepha/server\";\nimport type { CorsPrimitiveConfig } from \"../primitives/$cors.ts\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\n/**\n * CORS configuration atom (global defaults)\n */\nexport const corsOptions = $atom({\n name: \"alepha.server.cors.options\",\n schema: t.object({\n origin: t.optional(\n t.string({\n description:\n \"Allowed origins (* for all, string for single, comma-separated for multiple)\",\n default: \"*\",\n }),\n ),\n methods: t.array(t.string(), {\n description: \"Allowed HTTP methods\",\n default: [\"GET\", \"POST\", \"PUT\", \"PATCH\", \"DELETE\", \"OPTIONS\"],\n }),\n headers: t.array(t.string(), {\n description: \"Allowed headers\",\n default: [\"Content-Type\", \"Authorization\"],\n }),\n credentials: t.optional(\n t.boolean({\n description: \"Allow credentials\",\n default: true,\n }),\n ),\n maxAge: t.optional(\n t.number({\n description: \"Preflight cache duration in seconds\",\n }),\n ),\n }),\n default: {\n origin: \"*\",\n methods: [\"GET\", \"POST\", \"PUT\", \"PATCH\", \"DELETE\", \"OPTIONS\"],\n headers: [\"Content-Type\", \"Authorization\"],\n credentials: true,\n },\n});\n\nexport type CorsOptions = Static<typeof corsOptions.schema>;\n\ndeclare module \"alepha\" {\n interface State {\n [corsOptions.key]: CorsOptions;\n }\n}\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport class ServerCorsProvider {\n protected readonly log = $logger();\n protected readonly serverRouterProvider = $inject(ServerRouterProvider);\n protected readonly globalOptions = $use(corsOptions);\n\n /**\n * Registered CORS configurations with their path patterns\n */\n public readonly registeredConfigs: CorsPrimitiveConfig[] = [];\n\n /**\n * Register a CORS configuration (called by primitives)\n */\n public registerCors(config: CorsPrimitiveConfig): void {\n this.registeredConfigs.push(config);\n }\n\n protected readonly onStart = $hook({\n on: \"start\",\n handler: async () => {\n // Apply path-specific CORS configs to routes\n for (const config of this.registeredConfigs) {\n if (config.paths) {\n for (const pattern of config.paths) {\n const matchedRoutes = this.serverRouterProvider.getRoutes(pattern);\n for (const route of matchedRoutes) {\n route.cors = this.buildCorsOptions(config);\n }\n }\n }\n }\n\n if (this.registeredConfigs.length > 0) {\n this.log.info(\n `Initialized with ${this.registeredConfigs.length} registered CORS configurations.`,\n );\n }\n },\n });\n\n protected readonly configure = $hook({\n on: \"configure\",\n handler: () => {\n const routes = this.serverRouterProvider.getRoutes();\n for (const route of routes) {\n if (\n !route.method ||\n route.method === \"GET\" ||\n route.method === \"OPTIONS\"\n ) {\n continue;\n }\n\n this.serverRouterProvider.createRoute({\n path: route.path,\n method: \"OPTIONS\",\n handler: ({ reply }) => {\n reply.setStatus(204);\n },\n });\n }\n },\n });\n\n protected readonly onRequest = $hook({\n on: \"server:onRequest\",\n handler: ({ route, request }) => {\n // Use route-specific CORS if defined, otherwise use global options\n const corsConfig = route.cors ?? this.globalOptions;\n this.applyCorsHeaders(request, corsConfig);\n },\n });\n\n /**\n * Build complete CORS options by merging with global defaults\n */\n protected buildCorsOptions(config: CorsPrimitiveConfig): CorsOptions {\n return {\n origin: config.origin ?? this.globalOptions.origin,\n methods: config.methods ?? this.globalOptions.methods,\n headers: config.headers ?? this.globalOptions.headers,\n credentials: config.credentials ?? this.globalOptions.credentials,\n maxAge: config.maxAge ?? this.globalOptions.maxAge,\n };\n }\n\n /**\n * Apply CORS headers to the response\n */\n protected applyCorsHeaders(\n request: {\n headers: { origin?: string };\n reply: { setHeader: (name: string, value: string) => void };\n },\n options: CorsOptions,\n ): void {\n const reqOrigin = request.headers.origin;\n const { origin, methods, headers, credentials, maxAge } = options;\n\n if (reqOrigin && this.isOriginAllowed(reqOrigin, origin)) {\n request.reply.setHeader(\"Access-Control-Allow-Origin\", reqOrigin);\n }\n\n if (credentials) {\n request.reply.setHeader(\"Access-Control-Allow-Credentials\", \"true\");\n }\n\n request.reply.setHeader(\"Access-Control-Allow-Methods\", methods.join(\", \"));\n request.reply.setHeader(\"Access-Control-Allow-Headers\", headers.join(\", \"));\n\n if (maxAge != null) {\n request.reply.setHeader(\"Access-Control-Max-Age\", String(maxAge));\n }\n }\n\n public isOriginAllowed(\n origin: string | undefined,\n allowed: CorsOptions[\"origin\"],\n ): boolean {\n if (!allowed) return false;\n if (allowed === \"*\") return true;\n return allowed\n .split(\",\")\n .map((o) => o.trim())\n .includes(origin ?? \"\");\n }\n}\n\nexport type ServerCorsProviderOptions = CorsOptions;\n","import { $inject, createPrimitive, KIND, Primitive } from \"alepha\";\nimport type { CorsOptions } from \"../providers/ServerCorsProvider.ts\";\nimport { ServerCorsProvider } from \"../providers/ServerCorsProvider.ts\";\n\n/**\n * Declares CORS configuration for specific server routes.\n * This primitive provides path-based CORS configuration.\n *\n * @example\n * ```ts\n * class ApiService {\n * // Apply specific CORS to API routes\n * cors = $cors({\n * paths: [\"/api/*\"],\n * origin: \"https://app.example.com\",\n * credentials: true,\n * });\n * }\n * ```\n */\nexport const $cors = (options: CorsPrimitiveConfig): AbstractCorsPrimitive => {\n return createPrimitive(CorsPrimitive, options);\n};\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport interface CorsPrimitiveConfig extends Partial<CorsOptions> {\n /**\n * Name identifier for this CORS config (default: property key).\n */\n name?: string;\n /**\n * Path patterns to match (supports wildcards like /api/*).\n */\n paths?: string[];\n}\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport interface AbstractCorsPrimitive {\n readonly name: string;\n readonly options: CorsPrimitiveConfig;\n}\n\nexport class CorsPrimitive\n extends Primitive<CorsPrimitiveConfig>\n implements AbstractCorsPrimitive\n{\n protected readonly serverCorsProvider = $inject(ServerCorsProvider);\n\n public get name(): string {\n return this.options.name ?? `${this.config.propertyKey}`;\n }\n\n protected onInit() {\n // Register this CORS configuration with the provider\n this.serverCorsProvider.registerCors(this.options);\n }\n}\n\n$cors[KIND] = CorsPrimitive;\n","import { $module } from \"alepha\";\nimport { $cors } from \"./primitives/$cors.ts\";\nimport {\n type CorsOptions,\n ServerCorsProvider,\n} from \"./providers/ServerCorsProvider.ts\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport * from \"./primitives/$cors.ts\";\nexport * from \"./providers/ServerCorsProvider.ts\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\ndeclare module \"alepha/server\" {\n interface ServerRoute {\n /**\n * Route-specific CORS configuration.\n * If set, overrides the global CORS options for this route.\n */\n cors?: CorsOptions;\n }\n}\n\n// ---------------------------------------------------------------------------------------------------------------------\n\n/**\n * | type | quality | stability |\n * |------|---------|-----------|\n * | backend | standard | stable |\n *\n * Cross-Origin Resource Sharing configuration.\n *\n * **Features:**\n * - CORS policy definition\n *\n * @module alepha.server.cors\n */\nexport const AlephaServerCors = $module({\n name: \"alepha.server.cors\",\n primitives: [$cors],\n services: [ServerCorsProvider],\n});\n"],"mappings":"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"}
|
|
@@ -46,9 +46,15 @@ declare const healthSchema: alepha6.TObject<{
|
|
|
46
46
|
//#endregion
|
|
47
47
|
//#region ../../src/server/health/index.d.ts
|
|
48
48
|
/**
|
|
49
|
-
*
|
|
49
|
+
* | type | quality | stability |
|
|
50
|
+
* |------|---------|-----------|
|
|
51
|
+
* | devops | standard | stable |
|
|
52
|
+
*
|
|
53
|
+
* Application health monitoring endpoints.
|
|
54
|
+
*
|
|
55
|
+
* **Features:**
|
|
56
|
+
* - `GET /health` endpoint
|
|
50
57
|
*
|
|
51
|
-
* @see {@link ServerHealthProvider}
|
|
52
58
|
* @module alepha.server.health
|
|
53
59
|
*/
|
|
54
60
|
declare const AlephaServerHealth: alepha6.Service<alepha6.Module>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","names":[],"sources":["../../../src/server/health/providers/ServerHealthProvider.ts","../../../src/server/health/schemas/healthSchema.ts","../../../src/server/health/index.ts"],"mappings":";;;;;;;;;;AAUA;cAAa,oBAAA;EAAA,mBACQ,IAAA,EAAM,gBAAA;EAAA,mBACN,MAAA,EAAM,MAAA;EAAA,SAET,MAAA,iBAAM,cAAA;;eAFG,OAAA,CAAA,OAAA;;;;;;WAWT,OAAA,iBAAO,cAAA;;eATD,OAAA,CAAA,OAAA;;;;;;YAkBZ,WAAA,CAAA;;;;;;;;;cC9BC,YAAA,UAAY,OAAA;WAKvB,OAAA,CAAA,OAAA;;;;;;;;;;;ADGF
|
|
1
|
+
{"version":3,"file":"index.d.ts","names":[],"sources":["../../../src/server/health/providers/ServerHealthProvider.ts","../../../src/server/health/schemas/healthSchema.ts","../../../src/server/health/index.ts"],"mappings":";;;;;;;;;;AAUA;cAAa,oBAAA;EAAA,mBACQ,IAAA,EAAM,gBAAA;EAAA,mBACN,MAAA,EAAM,MAAA;EAAA,SAET,MAAA,iBAAM,cAAA;;eAFG,OAAA,CAAA,OAAA;;;;;;WAWT,OAAA,iBAAO,cAAA;;eATD,OAAA,CAAA,OAAA;;;;;;YAkBZ,WAAA,CAAA;;;;;;;;;cC9BC,YAAA,UAAY,OAAA;WAKvB,OAAA,CAAA,OAAA;;;;;;;;;;;ADGF;;;;;;;;cEaa,kBAAA,EAAkB,OAAA,CAAA,OAAA,CAG7B,OAAA,CAH6B,MAAA"}
|
|
@@ -45,9 +45,15 @@ var ServerHealthProvider = class {
|
|
|
45
45
|
//#endregion
|
|
46
46
|
//#region ../../src/server/health/index.ts
|
|
47
47
|
/**
|
|
48
|
-
*
|
|
48
|
+
* | type | quality | stability |
|
|
49
|
+
* |------|---------|-----------|
|
|
50
|
+
* | devops | standard | stable |
|
|
51
|
+
*
|
|
52
|
+
* Application health monitoring endpoints.
|
|
53
|
+
*
|
|
54
|
+
* **Features:**
|
|
55
|
+
* - `GET /health` endpoint
|
|
49
56
|
*
|
|
50
|
-
* @see {@link ServerHealthProvider}
|
|
51
57
|
* @module alepha.server.health
|
|
52
58
|
*/
|
|
53
59
|
const AlephaServerHealth = $module({
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":[],"sources":["../../../src/server/health/schemas/healthSchema.ts","../../../src/server/health/providers/ServerHealthProvider.ts","../../../src/server/health/index.ts"],"sourcesContent":["import { t } from \"alepha\";\n\nexport const healthSchema = t.object({\n message: t.text(),\n uptime: t.number(),\n date: t.datetime(),\n ready: t.boolean(),\n});\n","import { $inject, Alepha } from \"alepha\";\nimport { DateTimeProvider } from \"alepha/datetime\";\nimport { $route } from \"alepha/server\";\nimport { healthSchema } from \"../schemas/healthSchema.ts\";\n\n/**\n * Register `/health` & `/healthz` endpoint.\n *\n * - Provides basic health information about the server.\n */\nexport class ServerHealthProvider {\n protected readonly time: DateTimeProvider = $inject(DateTimeProvider);\n protected readonly alepha = $inject(Alepha);\n\n public readonly health = $route({\n path: \"/health\",\n schema: {\n response: healthSchema,\n },\n silent: true,\n handler: () => this.healthCheck(),\n });\n\n public readonly healthz = $route({\n path: \"/healthz\",\n schema: {\n response: healthSchema,\n },\n silent: true,\n handler: () => this.healthCheck(),\n });\n\n protected healthCheck() {\n return {\n message: \"OK\",\n uptime: Math.floor(process.uptime()),\n date: this.time.nowISOString(),\n ready: this.alepha.isReady(),\n };\n }\n}\n","import { $module } from \"alepha\";\nimport { AlephaServer } from \"alepha/server\";\nimport { ServerHealthProvider } from \"./providers/ServerHealthProvider.ts\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport * from \"./providers/ServerHealthProvider.ts\";\nexport * from \"./schemas/healthSchema.ts\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\n/**\n *
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../../../src/server/health/schemas/healthSchema.ts","../../../src/server/health/providers/ServerHealthProvider.ts","../../../src/server/health/index.ts"],"sourcesContent":["import { t } from \"alepha\";\n\nexport const healthSchema = t.object({\n message: t.text(),\n uptime: t.number(),\n date: t.datetime(),\n ready: t.boolean(),\n});\n","import { $inject, Alepha } from \"alepha\";\nimport { DateTimeProvider } from \"alepha/datetime\";\nimport { $route } from \"alepha/server\";\nimport { healthSchema } from \"../schemas/healthSchema.ts\";\n\n/**\n * Register `/health` & `/healthz` endpoint.\n *\n * - Provides basic health information about the server.\n */\nexport class ServerHealthProvider {\n protected readonly time: DateTimeProvider = $inject(DateTimeProvider);\n protected readonly alepha = $inject(Alepha);\n\n public readonly health = $route({\n path: \"/health\",\n schema: {\n response: healthSchema,\n },\n silent: true,\n handler: () => this.healthCheck(),\n });\n\n public readonly healthz = $route({\n path: \"/healthz\",\n schema: {\n response: healthSchema,\n },\n silent: true,\n handler: () => this.healthCheck(),\n });\n\n protected healthCheck() {\n return {\n message: \"OK\",\n uptime: Math.floor(process.uptime()),\n date: this.time.nowISOString(),\n ready: this.alepha.isReady(),\n };\n }\n}\n","import { $module } from \"alepha\";\nimport { AlephaServer } from \"alepha/server\";\nimport { ServerHealthProvider } from \"./providers/ServerHealthProvider.ts\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport * from \"./providers/ServerHealthProvider.ts\";\nexport * from \"./schemas/healthSchema.ts\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\n/**\n * | type | quality | stability |\n * |------|---------|-----------|\n * | devops | standard | stable |\n *\n * Application health monitoring endpoints.\n *\n * **Features:**\n * - `GET /health` endpoint\n *\n * @module alepha.server.health\n */\nexport const AlephaServerHealth = $module({\n name: \"alepha.server.health\",\n services: [AlephaServer, ServerHealthProvider],\n});\n"],"mappings":";;;;;AAEA,MAAa,eAAe,EAAE,OAAO;CACnC,SAAS,EAAE,MAAM;CACjB,QAAQ,EAAE,QAAQ;CAClB,MAAM,EAAE,UAAU;CAClB,OAAO,EAAE,SAAS;CACnB,CAAC;;;;;;;;;ACGF,IAAa,uBAAb,MAAkC;CAChC,AAAmB,OAAyB,QAAQ,iBAAiB;CACrE,AAAmB,SAAS,QAAQ,OAAO;CAE3C,AAAgB,SAAS,OAAO;EAC9B,MAAM;EACN,QAAQ,EACN,UAAU,cACX;EACD,QAAQ;EACR,eAAe,KAAK,aAAa;EAClC,CAAC;CAEF,AAAgB,UAAU,OAAO;EAC/B,MAAM;EACN,QAAQ,EACN,UAAU,cACX;EACD,QAAQ;EACR,eAAe,KAAK,aAAa;EAClC,CAAC;CAEF,AAAU,cAAc;AACtB,SAAO;GACL,SAAS;GACT,QAAQ,KAAK,MAAM,QAAQ,QAAQ,CAAC;GACpC,MAAM,KAAK,KAAK,cAAc;GAC9B,OAAO,KAAK,OAAO,SAAS;GAC7B;;;;;;;;;;;;;;;;;;ACfL,MAAa,qBAAqB,QAAQ;CACxC,MAAM;CACN,UAAU,CAAC,cAAc,qBAAqB;CAC/C,CAAC"}
|
|
@@ -88,10 +88,18 @@ declare class ServerHelmetProvider {
|
|
|
88
88
|
//#endregion
|
|
89
89
|
//#region ../../src/server/helmet/index.d.ts
|
|
90
90
|
/**
|
|
91
|
-
*
|
|
92
|
-
*
|
|
91
|
+
* | type | quality | stability |
|
|
92
|
+
* |------|---------|-----------|
|
|
93
|
+
* | backend | standard | stable |
|
|
94
|
+
*
|
|
95
|
+
* HTTP security headers.
|
|
96
|
+
*
|
|
97
|
+
* **Features:**
|
|
98
|
+
* - X-Frame-Options
|
|
99
|
+
* - X-Content-Type-Options
|
|
100
|
+
* - Content-Security-Policy
|
|
101
|
+
* - Other security headers
|
|
93
102
|
*
|
|
94
|
-
* @see {@link ServerHelmetProvider}
|
|
95
103
|
* @module alepha.server.helmet
|
|
96
104
|
*/
|
|
97
105
|
declare const AlephaServerHelmet: alepha1.Service<alepha1.Module>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","names":[],"sources":["../../../src/server/helmet/providers/ServerHelmetProvider.ts","../../../src/server/helmet/index.ts"],"mappings":";;;;;;;;cAOa,aAAA,EAAa,OAAA,CAAA,IAAA,SAAA,OAAA;8BAsCxB,OAAA,CAAA,QAAA;;;;;;;;;;;;;;KAEU,aAAA,GAAgB,MAAA,QAAc,aAAA,CAAc,MAAA;AAAA;EAAA,UAG5C,KAAA;IAAA,CACP,aAAA,CAAc,GAAA,GAAM,aAAA;EAAA;AAAA;AAAA,KAMpB,YAAA;AAAA,UAEY,aAAA;EACf,aAAA,GAAgB,YAAA;EAChB,YAAA,GAAe,YAAA;EACf,WAAA,GAAc,YAAA;EACd,SAAA,GAAY,YAAA;EACZ,aAAA,GAAgB,YAAA;EAChB,UAAA,GAAa,YAAA;EACb,YAAA,GAAe,YAAA;EACf,WAAA,GAAc,YAAA;EACd,WAAA,GAAc,YAAA;EACd,OAAA,GAAU,YAAA;EACV,YAAA;EACA,WAAA,GAAc,YAAA;EACd,aAAA,GAAgB,YAAA;EAChB,iBAAA,GAAoB,YAAA;EACpB,cAAA,GAAiB,YAAA;EACjB,UAAA,GAAa,YAAA;EAAA,CACZ,GAAA,WAAc,YAAA;AAAA;AAAA,UAGA,UAAA;EACf,UAAA,EAAY,aAAA;AAAA;AAAA,UAGG,WAAA;EACf,MAAA;EACA,iBAAA;EACA,OAAA;AAAA;;;;;cAOW,oBAAA;EAAA,mBACQ,MAAA,EAAM,MAAA;;;;qBAKN,OAAA,EAAO,QAAA;;;;;;;;;;;;4CALD,OAAA,CAAA,IAAA;MAAA;;;;YAOf,oBAAA,CAAA,GAAwB,aAAA;EAAA,UAgBxB,YAAA,CAAA,GAAgB,MAAA;EAAA,mBAiEP,UAAA,EAjEa,OAAA,CAiEH,aAAA;AAAA;;;;;;;AA/K/B
|
|
1
|
+
{"version":3,"file":"index.d.ts","names":[],"sources":["../../../src/server/helmet/providers/ServerHelmetProvider.ts","../../../src/server/helmet/index.ts"],"mappings":";;;;;;;;cAOa,aAAA,EAAa,OAAA,CAAA,IAAA,SAAA,OAAA;8BAsCxB,OAAA,CAAA,QAAA;;;;;;;;;;;;;;KAEU,aAAA,GAAgB,MAAA,QAAc,aAAA,CAAc,MAAA;AAAA;EAAA,UAG5C,KAAA;IAAA,CACP,aAAA,CAAc,GAAA,GAAM,aAAA;EAAA;AAAA;AAAA,KAMpB,YAAA;AAAA,UAEY,aAAA;EACf,aAAA,GAAgB,YAAA;EAChB,YAAA,GAAe,YAAA;EACf,WAAA,GAAc,YAAA;EACd,SAAA,GAAY,YAAA;EACZ,aAAA,GAAgB,YAAA;EAChB,UAAA,GAAa,YAAA;EACb,YAAA,GAAe,YAAA;EACf,WAAA,GAAc,YAAA;EACd,WAAA,GAAc,YAAA;EACd,OAAA,GAAU,YAAA;EACV,YAAA;EACA,WAAA,GAAc,YAAA;EACd,aAAA,GAAgB,YAAA;EAChB,iBAAA,GAAoB,YAAA;EACpB,cAAA,GAAiB,YAAA;EACjB,UAAA,GAAa,YAAA;EAAA,CACZ,GAAA,WAAc,YAAA;AAAA;AAAA,UAGA,UAAA;EACf,UAAA,EAAY,aAAA;AAAA;AAAA,UAGG,WAAA;EACf,MAAA;EACA,iBAAA;EACA,OAAA;AAAA;;;;;cAOW,oBAAA;EAAA,mBACQ,MAAA,EAAM,MAAA;;;;qBAKN,OAAA,EAAO,QAAA;;;;;;;;;;;;4CALD,OAAA,CAAA,IAAA;MAAA;;;;YAOf,oBAAA,CAAA,GAAwB,aAAA;EAAA,UAgBxB,YAAA,CAAA,GAAgB,MAAA;EAAA,mBAiEP,UAAA,EAjEa,OAAA,CAiEH,aAAA;AAAA;;;;;;;AA/K/B;;;;;;;;;;;cCkBa,kBAAA,EAAkB,OAAA,CAAA,OAAA,CAG7B,OAAA,CAH6B,MAAA"}
|
|
@@ -113,10 +113,18 @@ var ServerHelmetProvider = class {
|
|
|
113
113
|
//#endregion
|
|
114
114
|
//#region ../../src/server/helmet/index.ts
|
|
115
115
|
/**
|
|
116
|
-
*
|
|
117
|
-
*
|
|
116
|
+
* | type | quality | stability |
|
|
117
|
+
* |------|---------|-----------|
|
|
118
|
+
* | backend | standard | stable |
|
|
119
|
+
*
|
|
120
|
+
* HTTP security headers.
|
|
121
|
+
*
|
|
122
|
+
* **Features:**
|
|
123
|
+
* - X-Frame-Options
|
|
124
|
+
* - X-Content-Type-Options
|
|
125
|
+
* - Content-Security-Policy
|
|
126
|
+
* - Other security headers
|
|
118
127
|
*
|
|
119
|
-
* @see {@link ServerHelmetProvider}
|
|
120
128
|
* @module alepha.server.helmet
|
|
121
129
|
*/
|
|
122
130
|
const AlephaServerHelmet = $module({
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":[],"sources":["../../../src/server/helmet/providers/ServerHelmetProvider.ts","../../../src/server/helmet/index.ts"],"sourcesContent":["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 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 const headers: Record<string, string> = {};\n const {\n strictTransportSecurity: hsts,\n xContentTypeOptions,\n xFrameOptions,\n xXssProtection,\n contentSecurityPolicy: csp,\n referrerPolicy,\n } = this.options;\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 // 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 { $module } from \"alepha\";\nimport { AlephaServer } from \"alepha/server\";\nimport { ServerHelmetProvider } from \"./providers/ServerHelmetProvider.ts\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport * from \"./providers/ServerHelmetProvider.ts\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\n/**\n *
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../../../src/server/helmet/providers/ServerHelmetProvider.ts","../../../src/server/helmet/index.ts"],"sourcesContent":["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 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 const headers: Record<string, string> = {};\n const {\n strictTransportSecurity: hsts,\n xContentTypeOptions,\n xFrameOptions,\n xXssProtection,\n contentSecurityPolicy: csp,\n referrerPolicy,\n } = this.options;\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 // 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 { $module } from \"alepha\";\nimport { AlephaServer } from \"alepha/server\";\nimport { ServerHelmetProvider } from \"./providers/ServerHelmetProvider.ts\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport * from \"./providers/ServerHelmetProvider.ts\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\n/**\n * | type | quality | stability |\n * |------|---------|-----------|\n * | backend | standard | stable |\n *\n * HTTP security headers.\n *\n * **Features:**\n * - X-Frame-Options\n * - X-Content-Type-Options\n * - Content-Security-Policy\n * - Other security headers\n *\n * @module alepha.server.helmet\n */\nexport const AlephaServerHelmet = $module({\n name: \"alepha.server.helmet\",\n services: [AlephaServer, ServerHelmetProvider],\n});\n"],"mappings":"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"}
|
|
@@ -306,14 +306,19 @@ declare module "alepha" {
|
|
|
306
306
|
}
|
|
307
307
|
}
|
|
308
308
|
/**
|
|
309
|
-
*
|
|
309
|
+
* | type | quality | stability |
|
|
310
|
+
* |------|---------|-----------|
|
|
311
|
+
* | backend | standard | stable |
|
|
310
312
|
*
|
|
311
|
-
*
|
|
312
|
-
*
|
|
313
|
-
*
|
|
313
|
+
* Type-safe API client with request deduplication.
|
|
314
|
+
*
|
|
315
|
+
* **Features:**
|
|
316
|
+
* - Virtual HTTP client for type-safe API calls
|
|
317
|
+
* - Remote action definitions
|
|
318
|
+
* - Type inference from action schemas
|
|
319
|
+
* - Request deduplication
|
|
320
|
+
* - Automatic error handling
|
|
314
321
|
*
|
|
315
|
-
* @see {@link $remote}
|
|
316
|
-
* @see {@link $client}
|
|
317
322
|
* @module alepha.server.links
|
|
318
323
|
*/
|
|
319
324
|
declare const AlephaServerLinks: alepha23.Service<alepha23.Module>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","names":[],"sources":["../../../src/server/links/schemas/apiLinksResponseSchema.ts","../../../src/server/links/providers/LinkProvider.ts","../../../src/server/links/primitives/$client.ts","../../../src/server/links/primitives/$remote.ts","../../../src/server/links/providers/RemotePrimitiveProvider.ts","../../../src/server/links/providers/ServerLinksProvider.ts","../../../src/server/links/index.ts"],"mappings":"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
|
|
1
|
+
{"version":3,"file":"index.d.ts","names":[],"sources":["../../../src/server/links/schemas/apiLinksResponseSchema.ts","../../../src/server/links/providers/LinkProvider.ts","../../../src/server/links/primitives/$client.ts","../../../src/server/links/primitives/$remote.ts","../../../src/server/links/providers/RemotePrimitiveProvider.ts","../../../src/server/links/providers/ServerLinksProvider.ts","../../../src/server/links/index.ts"],"mappings":"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"}
|
|
@@ -468,14 +468,19 @@ var ServerLinksProvider = class {
|
|
|
468
468
|
//#endregion
|
|
469
469
|
//#region ../../src/server/links/index.ts
|
|
470
470
|
/**
|
|
471
|
-
*
|
|
471
|
+
* | type | quality | stability |
|
|
472
|
+
* |------|---------|-----------|
|
|
473
|
+
* | backend | standard | stable |
|
|
472
474
|
*
|
|
473
|
-
*
|
|
474
|
-
*
|
|
475
|
-
*
|
|
475
|
+
* Type-safe API client with request deduplication.
|
|
476
|
+
*
|
|
477
|
+
* **Features:**
|
|
478
|
+
* - Virtual HTTP client for type-safe API calls
|
|
479
|
+
* - Remote action definitions
|
|
480
|
+
* - Type inference from action schemas
|
|
481
|
+
* - Request deduplication
|
|
482
|
+
* - Automatic error handling
|
|
476
483
|
*
|
|
477
|
-
* @see {@link $remote}
|
|
478
|
-
* @see {@link $client}
|
|
479
484
|
* @module alepha.server.links
|
|
480
485
|
*/
|
|
481
486
|
const AlephaServerLinks = $module({
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":["envSchema"],"sources":["../../../src/server/links/schemas/apiLinksResponseSchema.ts","../../../src/server/links/atoms/apiLinksAtom.ts","../../../src/server/links/providers/LinkProvider.ts","../../../src/server/links/primitives/$client.ts","../../../src/server/links/primitives/$remote.ts","../../../src/server/links/providers/RemotePrimitiveProvider.ts","../../../src/server/links/providers/ServerLinksProvider.ts","../../../src/server/links/index.ts"],"sourcesContent":["import type { Static } from \"alepha\";\nimport { t } from \"alepha\";\n\nexport const apiLinkSchema = t.object({\n name: t.text({\n description: \"Name of the API link, used for identification.\",\n }),\n\n group: t.optional(\n t.text({\n description:\n \"Group to which the API link belongs, used for categorization.\",\n }),\n ),\n\n path: t.text({\n description: \"Pathname used to access the API link.\",\n }),\n\n method: t.optional(\n t.text({\n description:\n \"HTTP method used for the API link, e.g., GET, POST, etc. If not specified, defaults to GET.\",\n }),\n ),\n\n requestBodyType: t.optional(\n t.text({\n description:\n \"Type of the request body for the API link. Default is application/json for POST/PUT/PATCH, null for others.\",\n }),\n ),\n\n service: t.optional(\n t.text({\n description:\n \"Service name associated with the API link, used for service discovery.\",\n }),\n ),\n\n rawSchema: t.optional(\n t.object({\n body: t.optional(t.string()),\n response: t.optional(t.string()),\n }),\n ),\n});\n\nexport const apiLinksResponseSchema = t.object({\n prefix: t.optional(t.text()),\n links: t.array(apiLinkSchema),\n});\n\nexport type ApiLinksResponse = Static<typeof apiLinksResponseSchema>;\nexport type ApiLink = Static<typeof apiLinkSchema>;\n","import { $atom, t } from \"alepha\";\nimport { apiLinksResponseSchema } from \"../schemas/apiLinksResponseSchema.ts\";\n\nexport const apiLinksAtom = $atom({\n name: \"alepha.server.request.apiLinks\",\n schema: t.optional(apiLinksResponseSchema),\n});\n","import {\n $inject,\n Alepha,\n AlephaError,\n type Async,\n jsonSchemaToTypeBox,\n t,\n} from \"alepha\";\nimport { $logger } from \"alepha/logger\";\nimport type { ServerRouteSecure } from \"alepha/security\";\nimport {\n type ActionPrimitive,\n type ClientRequestEntry,\n type ClientRequestOptions,\n type ClientRequestResponse,\n type FetchResponse,\n HttpClient,\n type RequestConfigSchema,\n ServerReply,\n type ServerRequest,\n type ServerRequestConfigEntry,\n type ServerResponseBody,\n type TRequestBody,\n UnauthorizedError,\n} from \"alepha/server\";\nimport {\n type ApiLink,\n apiLinksResponseSchema,\n} from \"../schemas/apiLinksResponseSchema.ts\";\n\n/**\n * Browser, SSR friendly, service to handle links.\n */\nexport class LinkProvider {\n static path = {\n apiLinks: \"/api/_links\",\n };\n\n protected readonly log = $logger();\n protected readonly alepha = $inject(Alepha);\n protected readonly httpClient = $inject(HttpClient);\n\n // all server links (local + remote)\n // THIS IS NOT USER LINKS! (which are filtered by permissions)\n protected serverLinks: Array<HttpClientLink> = [];\n\n /**\n * Get applicative links registered on the server.\n * This does not include lazy-loaded remote links.\n */\n public getServerLinks(): HttpClientLink[] {\n if (this.alepha.isBrowser()) {\n this.log.warn(\n \"Getting server links in the browser is not supported. Use `fetchLinks` to get links from the server.\",\n );\n return [];\n }\n\n return this.serverLinks;\n }\n\n /**\n * Register a new link for the application.\n */\n public registerLink(link: HttpClientLink): void {\n if (this.alepha.isBrowser()) {\n this.log.warn(\n \"Registering links in the browser is not supported. Use `fetchLinks` to get links from the server.\",\n );\n return;\n }\n\n if (!link.handler && !link.host) {\n throw new AlephaError(\n \"Can't create link - 'handler' or 'host' is required\",\n );\n }\n\n if (this.serverLinks.some((l) => l.name === link.name)) {\n // remove existing link with the same name\n this.serverLinks = this.serverLinks.filter((l) => l.name !== link.name);\n }\n\n if (!link.rawSchema) {\n link.rawSchema = {};\n if (link.schema?.body)\n link.rawSchema.body = JSON.stringify(link.schema.body);\n if (link.schema?.response)\n link.rawSchema.response = JSON.stringify(link.schema.response);\n }\n\n this.serverLinks.push(link);\n }\n\n public get links(): HttpClientLink[] {\n // TODO: not performant at all, use a map instead for ServerLinks\n const apiLinks = this.alepha.store.get(\n \"alepha.server.request.apiLinks\",\n )?.links;\n\n if (apiLinks) {\n if (this.alepha.isBrowser()) {\n return apiLinks;\n }\n\n const links = [];\n for (const link of apiLinks) {\n const originalLink = this.serverLinks.find((l) => l.name === link.name);\n if (originalLink) {\n links.push(originalLink);\n }\n }\n return links;\n }\n\n return this.serverLinks ?? [];\n }\n\n /**\n * Force browser to refresh links from the server.\n */\n public async fetchLinks(): Promise<HttpClientLink[]> {\n const { data } = await this.httpClient.fetch(\n `${LinkProvider.path.apiLinks}`,\n {\n method: \"GET\",\n schema: {\n response: apiLinksResponseSchema,\n },\n },\n );\n\n this.alepha.store.set(\"alepha.server.request.apiLinks\", data);\n\n return data.links;\n }\n\n /**\n * Create a virtual client that can be used to call actions.\n *\n * Use js Proxy under the hood.\n */\n public client<T extends object>(\n scope: ClientScope = {},\n ): HttpVirtualClient<T> {\n return new Proxy<HttpVirtualClient<T>>({} as HttpVirtualClient<T>, {\n get: (_, prop) => {\n if (typeof prop !== \"string\") {\n return;\n }\n\n return this.createVirtualAction<RequestConfigSchema>(prop, scope);\n },\n });\n }\n\n /**\n * Check if a link with the given name exists.\n * @param name\n */\n public can(name: string): boolean {\n return this.links.some((link) => link.name === name);\n }\n\n /**\n * Resolve a link by its name and call it.\n * - If link is local, it will call the local handler.\n * - If link is remote, it will make a fetch request to the remote server.\n */\n public async follow(\n name: string,\n config: Partial<ServerRequestConfigEntry> = {},\n options: ClientRequestOptions & ClientScope = {},\n ): Promise<any> {\n this.log.trace(\"Following link\", { name, config, options });\n const link = await this.getLinkByName(name, options);\n\n // if a handler is defined, use it (ssr)\n if (link.handler && !options.request) {\n this.log.trace(\"Local link found\", { name });\n return link.handler(\n {\n method: link.method,\n url: new URL(`http://localhost${link.path}`),\n query: config.query ?? {},\n body: config.body ?? {},\n params: config.params ?? {},\n headers: config.headers ?? {},\n metadata: {},\n reply: new ServerReply(),\n } as Partial<ServerRequest> as ServerRequest,\n options,\n );\n }\n\n this.log.trace(\"Remote link found\", {\n name,\n host: link.host,\n service: link.service,\n });\n\n return this.followRemote(link, config, options).then(\n (response) => response.data,\n );\n }\n\n protected createVirtualAction<T extends RequestConfigSchema>(\n name: string,\n scope: ClientScope = {},\n ): VirtualAction<T> {\n const $: VirtualAction<T> = async (\n config: any = {},\n options: ClientRequestOptions = {},\n ) => {\n return this.follow(name, config, {\n ...scope,\n ...options,\n });\n };\n\n Object.defineProperty($, \"name\", {\n value: name,\n writable: false,\n });\n\n $.run = async (config: any = {}, options: ClientRequestOptions = {}) => {\n return this.follow(name, config, {\n ...scope,\n ...options,\n });\n };\n\n $.fetch = async (config: any = {}, options: ClientRequestOptions = {}) => {\n const link = await this.getLinkByName(name, scope);\n return this.followRemote(link, config, options);\n };\n\n $.can = () => {\n return this.can(name);\n };\n\n $.schema = () => {\n const link = this.links.find((l) => l.name === name);\n if (!link) {\n throw new AlephaError(`Link ${name} not found.`);\n }\n\n if (link.rawSchema && !link.schema) {\n link.schema = {};\n link.schema.body = link.rawSchema?.body\n ? (jsonSchemaToTypeBox(\n JSON.parse(link.rawSchema.body),\n ) as TRequestBody)\n : undefined;\n link.schema.response = link.rawSchema?.response\n ? (jsonSchemaToTypeBox(\n JSON.parse(link.rawSchema.response),\n ) as TRequestBody)\n : undefined;\n }\n\n return link.schema as {\n body: any;\n response: any;\n };\n };\n\n return $;\n }\n\n protected async followRemote(\n link: HttpClientLink,\n config: Partial<ServerRequestConfigEntry> = {},\n options: ClientRequestOptions = {},\n ): Promise<FetchResponse> {\n options.request ??= {};\n options.request.headers = new Headers(options.request.headers);\n\n const als = this.alepha.context.get<ServerRequest>(\"request\");\n if (als?.headers.authorization) {\n options.request.headers.set(\"authorization\", als.headers.authorization);\n }\n\n const context = this.alepha.context.get(\"context\");\n if (typeof context === \"string\") {\n options.request.headers.set(\"x-request-id\", context);\n }\n\n const action = {\n ...link,\n // schema is not used in the client,\n // we assume that TypeScript will check\n schema: {\n body: t.any(),\n response: t.any(),\n },\n };\n\n // prefix with service when host is not defined (e.g. browser)\n if (!link.host && link.service) {\n action.path = `/${link.service}${action.path}`;\n }\n\n action.path = `${action.prefix ?? \"/api\"}${action.path}`;\n action.prefix = undefined; // prefix is not used in the client\n\n // else, make a request\n return this.httpClient.fetchAction({\n host: link.host,\n config,\n options,\n action: action as any, // schema.body TAny is not accepted\n });\n }\n\n protected async getLinkByName(\n name: string,\n options: ClientScope = {},\n ): Promise<HttpClientLink> {\n if (\n this.alepha.isBrowser() &&\n !this.alepha.store.get(\"alepha.server.request.apiLinks\")\n ) {\n await this.fetchLinks();\n }\n\n const link = this.links.find(\n (a) =>\n a.name === name &&\n (!options.group || a.group === options.group) &&\n (!options.service || options.service === a.service),\n );\n\n if (!link) {\n const error = new UnauthorizedError(`Action ${name} not found.`);\n // mimic http error handling\n await this.alepha.events.emit(\"client:onError\", {\n route: link,\n error,\n });\n throw error;\n }\n\n if (options.hostname) {\n return {\n ...link,\n host: options.hostname,\n };\n }\n\n return link;\n }\n}\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport interface HttpClientLink extends ApiLink {\n secured?: boolean | ServerRouteSecure;\n prefix?: string;\n // -- server only --\n // only for remote actions\n host?: string;\n service?: string;\n // used only for local actions, not for remote actions\n schema?: RequestConfigSchema;\n handler?: (\n request: ServerRequest,\n options: ClientRequestOptions,\n ) => Async<ServerResponseBody>;\n}\n\nexport interface ClientScope {\n group?: string;\n service?: string;\n hostname?: string;\n}\n\nexport type HttpVirtualClient<T> = {\n [K in keyof T as T[K] extends ActionPrimitive<RequestConfigSchema>\n ? K\n : never]: T[K] extends ActionPrimitive<infer Schema>\n ? VirtualAction<Schema>\n : never;\n};\n\nexport interface VirtualAction<T extends RequestConfigSchema>\n extends Pick<ActionPrimitive<T>, \"name\" | \"run\" | \"fetch\"> {\n (\n config?: ClientRequestEntry<T>,\n opts?: ClientRequestOptions,\n ): Promise<ClientRequestResponse<T>>;\n can: () => boolean;\n schema: () => {\n body: T[\"body\"];\n response: T[\"response\"];\n };\n}\n","import { $inject, KIND } from \"alepha\";\nimport {\n type ClientScope,\n type HttpVirtualClient,\n LinkProvider,\n} from \"../providers/LinkProvider.ts\";\n\n/**\n * Create a new client.\n */\nexport const $client = <T extends object>(\n scope?: ClientScope,\n): HttpVirtualClient<T> => {\n return $inject(LinkProvider).client<T>(scope);\n};\n\n$client[KIND] = \"$client\";\n","import { createPrimitive, KIND, Primitive } from \"alepha\";\nimport type { ServiceAccountPrimitive } from \"alepha/security\";\nimport type { ProxyPrimitiveOptions } from \"alepha/server/proxy\";\n\n/**\n * $remote is a primitive that allows you to define remote service access.\n *\n * Use it only when you have 2 or more services that need to communicate with each other.\n *\n * All remote services can be exposed as actions, ... or not.\n *\n * You can add a service account if you want to use a security layer.\n */\nexport const $remote = (options: RemotePrimitiveOptions) => {\n return createPrimitive(RemotePrimitive, options);\n};\n\nexport interface RemotePrimitiveOptions {\n /**\n * The URL of the remote service.\n * You can use a function to generate the URL dynamically.\n * You probably should use $env(env) to get the URL from the environment.\n *\n * @example\n * ```ts\n * import { $remote } from \"alepha/server\";\n * import { $inject, t } from \"alepha\";\n *\n * class App {\n * env = $env(t.object({\n * REMOTE_URL: t.text({default: \"http://localhost:3000\"}),\n * }));\n * remote = $remote({\n * url: this.env.REMOTE_URL,\n * });\n * }\n * ```\n */\n url: string | (() => string);\n\n /**\n * The name of the remote service.\n *\n * @default Member of the class containing the remote service.\n */\n name?: string;\n\n /**\n * If true, all methods of the remote service will be exposed as actions in this context.\n * > Note: Proxy will never use the service account, it just... proxies the request.\n */\n proxy?:\n | boolean\n | Partial<\n ProxyPrimitiveOptions & {\n /**\n * If true, the remote service won't be available internally, only through the proxy.\n */\n noInternal: boolean;\n }\n >;\n\n /**\n * For communication between the server and the remote service with a security layer.\n * This will be used for internal communication and will not be exposed to the client.\n */\n serviceAccount?: ServiceAccountPrimitive;\n}\n\nexport class RemotePrimitive extends Primitive<RemotePrimitiveOptions> {\n public get name(): string {\n return this.options.name ?? this.config.propertyKey;\n }\n}\n\n$remote[KIND] = RemotePrimitive;\n","import { $env, $hook, $inject, Alepha, t } from \"alepha\";\nimport { $logger } from \"alepha/logger\";\nimport { $retry } from \"alepha/retry\";\nimport type { ServiceAccountPrimitive } from \"alepha/security\";\nimport { ServerProxyProvider } from \"alepha/server/proxy\";\nimport { $remote, type RemotePrimitive } from \"../primitives/$remote.ts\";\nimport {\n type ApiLinksResponse,\n apiLinksResponseSchema,\n} from \"../schemas/apiLinksResponseSchema.ts\";\nimport { LinkProvider } from \"./LinkProvider.ts\";\n\nconst envSchema = t.object({\n SERVER_API_PREFIX: t.text({\n description: \"Prefix for all API routes (e.g. $action).\",\n default: \"/api\",\n }),\n});\n\nexport class RemotePrimitiveProvider {\n protected readonly env = $env(envSchema);\n protected readonly alepha = $inject(Alepha);\n protected readonly proxyProvider = $inject(ServerProxyProvider);\n protected readonly linkProvider = $inject(LinkProvider);\n protected readonly remotes: Array<ServerRemote> = [];\n protected readonly log = $logger();\n\n public getRemotes(): ServerRemote[] {\n return this.remotes;\n }\n\n public readonly configure = $hook({\n on: \"configure\",\n handler: async () => {\n const remotes = this.alepha.primitives($remote);\n for (const remote of remotes) {\n await this.registerRemote(remote);\n }\n },\n });\n\n public readonly start = $hook({\n on: \"start\",\n handler: async () => {\n for (const remote of this.remotes) {\n const token =\n typeof remote.serviceAccount?.token === \"function\"\n ? await remote.serviceAccount.token()\n : undefined;\n\n if (!remote.internal) {\n continue; // skip download links for remotes that are not internal\n }\n\n const { links } = await remote.links({ authorization: token });\n\n for (const link of links) {\n let path = link.path.replace(remote.prefix, \"\");\n if (link.service) {\n path = `/${link.service}${path}`;\n }\n\n this.linkProvider.registerLink({\n ...link,\n prefix: remote.prefix,\n path,\n method: link.method ?? \"GET\",\n host: remote.url,\n service: remote.name,\n });\n }\n\n this.log.info(`Remote '${remote.name}' OK`, {\n links: remote.links.length,\n prefix: remote.prefix,\n });\n }\n },\n });\n\n public async registerRemote(value: RemotePrimitive): Promise<void> {\n const options = value.options;\n const url = typeof options.url === \"string\" ? options.url : options.url();\n const linkPath = LinkProvider.path.apiLinks;\n const name = value.name;\n const proxy = typeof options.proxy === \"object\" ? options.proxy : {};\n\n const remote: ServerRemote = {\n url,\n name,\n prefix: \"/api\",\n serviceAccount: options.serviceAccount,\n proxy: !!options.proxy,\n internal: !proxy.noInternal,\n schema: async (opts) => {\n const { authorization, name } = opts;\n return await fetch(`${url}${linkPath}/${name}/schema`, {\n headers: new Headers(\n authorization\n ? {\n authorization,\n }\n : {},\n ),\n }).then((it) => it.json()); // TODO: use schema validation for response\n },\n links: async (opts) => {\n const { authorization } = opts;\n const remoteApi = await this.fetchLinks.run({\n service: name,\n url: `${url}${linkPath}`,\n authorization,\n });\n\n if (remoteApi.prefix != null) {\n remote.prefix = remoteApi.prefix; // monkey patch the prefix, not ideal but works\n }\n\n return remoteApi;\n },\n };\n\n this.remotes.push(remote);\n\n if (options.proxy) {\n this.proxyProvider.createProxy({\n path: `${this.env.SERVER_API_PREFIX}/${name}/*`,\n target: url,\n rewrite: (url) => {\n url.pathname = url.pathname.replace(\n `${this.env.SERVER_API_PREFIX}/${name}`,\n remote.prefix,\n );\n },\n ...proxy,\n });\n }\n }\n\n protected readonly fetchLinks = $retry({\n max: 10,\n backoff: {\n initial: 1000,\n },\n onError: (_, attempt, { service, url }) => {\n this.log.warn(`Failed to fetch links, retry (${attempt})...`, {\n service,\n url,\n });\n },\n handler: async (opts: FetchLinksOptions): Promise<ApiLinksResponse> => {\n const { url, authorization } = opts;\n const response = await fetch(url, {\n headers: new Headers(\n authorization\n ? {\n authorization,\n }\n : {},\n ),\n });\n\n if (!response.ok) {\n throw new Error(`Failed to fetch links from ${url}`);\n }\n\n return this.alepha.codec.decode(\n apiLinksResponseSchema,\n await response.json(),\n );\n },\n });\n}\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport interface FetchLinksOptions {\n /**\n * Name of the remote service.\n */\n service: string;\n\n /**\n * URL to fetch links from.\n */\n url: string;\n\n /**\n * Authorization header containing access token.\n */\n authorization?: string;\n}\n\nexport interface ServerRemote {\n /**\n * URL of the remote service.\n */\n url: string;\n\n /**\n * Name of the remote service.\n */\n name: string;\n\n /**\n * Expose links as endpoint. It's not only internal.\n */\n proxy: boolean;\n\n /**\n * It's only used inside the application.\n */\n internal: boolean;\n\n /**\n * Links fetcher.\n */\n links: (args: { authorization?: string }) => Promise<ApiLinksResponse>;\n\n /**\n * Fetches schema for the remote service.\n */\n schema: (args: { name: string; authorization?: string }) => Promise<any>;\n\n /**\n * Force a default access token provider when not provided.\n */\n serviceAccount?: ServiceAccountPrimitive;\n\n /**\n * Prefix for the remote service links.\n */\n prefix: string;\n}\n","import { $env, $hook, $inject, Alepha, t } from \"alepha\";\nimport {\n type Permission,\n SecurityProvider,\n type UserAccountToken,\n} from \"alepha/security\";\nimport {\n $action,\n $route,\n type ClientRequestEntry,\n type ClientRequestOptions,\n type RequestConfigSchema,\n ServerTimingProvider,\n} from \"alepha/server\";\nimport {\n type ApiLink,\n type ApiLinksResponse,\n apiLinksResponseSchema,\n} from \"../schemas/apiLinksResponseSchema.ts\";\nimport { LinkProvider } from \"./LinkProvider.ts\";\nimport { RemotePrimitiveProvider } from \"./RemotePrimitiveProvider.ts\";\n\nconst envSchema = t.object({\n SERVER_API_PREFIX: t.text({\n description: \"Prefix for all API routes (e.g. $action).\",\n default: \"/api\",\n }),\n});\n\nexport class ServerLinksProvider {\n protected readonly env = $env(envSchema);\n protected readonly alepha = $inject(Alepha);\n protected readonly linkProvider = $inject(LinkProvider);\n protected readonly remoteProvider = $inject(RemotePrimitiveProvider);\n protected readonly serverTimingProvider = $inject(ServerTimingProvider);\n\n public get prefix() {\n return this.env.SERVER_API_PREFIX;\n }\n\n public readonly onRoute = $hook({\n on: \"configure\",\n handler: () => {\n // convert all $action to local links\n for (const action of this.alepha.primitives($action)) {\n this.linkProvider.registerLink({\n name: action.name,\n group: action.group,\n schema: action.options.schema,\n requestBodyType: action.getBodyContentType(),\n secured: action.options.secure ?? true,\n method: action.method === \"GET\" ? undefined : action.method,\n prefix: action.prefix,\n path: action.path,\n // by local, we mean that it can be called directly via the handler\n handler: (\n config: ClientRequestEntry<RequestConfigSchema>,\n options: ClientRequestOptions = {},\n ) => action.run(config, options),\n });\n }\n },\n });\n\n /**\n * First API - Get all API links for the user.\n *\n * This is based on the user's permissions.\n */\n public readonly links = $route({\n path: LinkProvider.path.apiLinks,\n schema: {\n response: apiLinksResponseSchema,\n },\n handler: ({ user, headers }) => {\n return this.getUserApiLinks({\n user,\n authorization: headers.authorization,\n });\n },\n });\n\n /**\n * Retrieves API links for the user based on their permissions.\n * Will check on local links and remote links.\n */\n public async getUserApiLinks(\n options: GetApiLinksOptions,\n ): Promise<ApiLinksResponse> {\n const { user } = options;\n let permissions: Permission[] | undefined;\n let permissionMap: Map<string, Permission> | undefined;\n const hasSecurity = this.alepha.has(SecurityProvider);\n if (hasSecurity && user) {\n permissions = this.alepha.inject(SecurityProvider).getPermissions(user);\n permissionMap = new Map(\n permissions.map((it) => [`${it.group}:${it.name}`, it]),\n );\n }\n\n const userLinks: ApiLink[] = [];\n\n // bonus: add permissions not related to $action\n for (const permission of permissions ?? []) {\n if (\n !permission.path &&\n !permission.method &&\n permission.name &&\n permission.group\n ) {\n userLinks.push({\n path: \"\", // this is a placeholder for links without specific path\n name: permission.name,\n group: permission.group,\n });\n }\n }\n\n // add local links\n for (const link of this.linkProvider.getServerLinks()) {\n // SKIP REMOTE LINKS, remote links are handled separately for security\n if (link.host) continue;\n\n if (hasSecurity && link.secured) {\n // skip secured links if user is not provided\n if (!user) {\n continue;\n }\n\n if (typeof link.secured === \"object\" && link.secured.realm) {\n // realm check\n if (user.realm !== link.secured.realm) {\n continue;\n }\n } else if (permissionMap) {\n // small permissions check, can be optimized later ... :')\n\n if (!permissionMap.has(`${link.group}:${link.name}`)) {\n continue;\n }\n }\n }\n\n userLinks.push({\n name: link.name,\n group: link.group,\n requestBodyType: link.requestBodyType,\n method: link.method,\n path: link.path,\n rawSchema: link.rawSchema,\n });\n }\n\n this.serverTimingProvider.beginTiming(\"fetchRemoteLinks\");\n // this does not scale well, but it's working for now\n // TODO: remote links can be cached by user.roles\n const promises = this.remoteProvider\n .getRemotes()\n .filter((it) => it.proxy) // add only \"proxy\" remotes\n .map(async (remote) => {\n const { links, prefix } = await remote.links(options);\n return links.map((link) => {\n let path = link.path.replace(prefix ?? \"/api\", \"\");\n if (link.service) {\n path = `/${link.service}${path}`;\n }\n\n return {\n ...link,\n path,\n proxy: true,\n service: remote.name,\n };\n });\n });\n\n userLinks.push(...(await Promise.all(promises)).flat());\n this.serverTimingProvider.endTiming(\"fetchRemoteLinks\");\n\n return {\n prefix: this.env.SERVER_API_PREFIX,\n links: userLinks,\n };\n }\n}\n\nexport interface GetApiLinksOptions {\n user?: UserAccountToken;\n authorization?: string;\n}\n","import \"alepha/security\";\nimport { $module } from \"alepha\";\nimport { AlephaServer } from \"alepha/server\";\nimport { apiLinksAtom } from \"./atoms/apiLinksAtom.ts\";\nimport { $client } from \"./primitives/$client.ts\";\nimport { $remote } from \"./primitives/$remote.ts\";\nimport { LinkProvider } from \"./providers/LinkProvider.ts\";\nimport { RemotePrimitiveProvider } from \"./providers/RemotePrimitiveProvider.ts\";\nimport { ServerLinksProvider } from \"./providers/ServerLinksProvider.ts\";\nimport type { ApiLinksResponse } from \"./schemas/apiLinksResponseSchema.ts\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport * from \"./primitives/$client.ts\";\nexport * from \"./primitives/$remote.ts\";\nexport * from \"./providers/LinkProvider.ts\";\nexport * from \"./providers/RemotePrimitiveProvider.ts\";\nexport * from \"./providers/ServerLinksProvider.ts\";\nexport * from \"./schemas/apiLinksResponseSchema.ts\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\ndeclare module \"alepha\" {\n interface State {\n /**\n * API links attached to the server request state.\n *\n * @see {@link ApiLinksResponse}\n * @internal\n */\n \"alepha.server.request.apiLinks\"?: ApiLinksResponse;\n }\n}\n\n// ---------------------------------------------------------------------------------------------------------------------\n\n/**\n * Provides server-side link management and remote capabilities for client-server interactions.\n *\n * The server-links module enables declarative link definitions using `$remote` and `$client` primitives,\n * facilitating seamless API endpoint management and client-server communication. It integrates with server\n * security features to ensure safe and controlled access to resources.\n *\n * @see {@link $remote}\n * @see {@link $client}\n * @module alepha.server.links\n */\nexport const AlephaServerLinks = $module({\n name: \"alepha.server.links\",\n atoms: [apiLinksAtom],\n primitives: [$remote, $client],\n services: [\n AlephaServer,\n ServerLinksProvider,\n RemotePrimitiveProvider,\n LinkProvider,\n ],\n});\n"],"mappings":";;;;;;;;AAGA,MAAa,gBAAgB,EAAE,OAAO;CACpC,MAAM,EAAE,KAAK,EACX,aAAa,kDACd,CAAC;CAEF,OAAO,EAAE,SACP,EAAE,KAAK,EACL,aACE,iEACH,CAAC,CACH;CAED,MAAM,EAAE,KAAK,EACX,aAAa,yCACd,CAAC;CAEF,QAAQ,EAAE,SACR,EAAE,KAAK,EACL,aACE,+FACH,CAAC,CACH;CAED,iBAAiB,EAAE,SACjB,EAAE,KAAK,EACL,aACE,+GACH,CAAC,CACH;CAED,SAAS,EAAE,SACT,EAAE,KAAK,EACL,aACE,0EACH,CAAC,CACH;CAED,WAAW,EAAE,SACX,EAAE,OAAO;EACP,MAAM,EAAE,SAAS,EAAE,QAAQ,CAAC;EAC5B,UAAU,EAAE,SAAS,EAAE,QAAQ,CAAC;EACjC,CAAC,CACH;CACF,CAAC;AAEF,MAAa,yBAAyB,EAAE,OAAO;CAC7C,QAAQ,EAAE,SAAS,EAAE,MAAM,CAAC;CAC5B,OAAO,EAAE,MAAM,cAAc;CAC9B,CAAC;;;;AChDF,MAAa,eAAe,MAAM;CAChC,MAAM;CACN,QAAQ,EAAE,SAAS,uBAAuB;CAC3C,CAAC;;;;;;;AC2BF,IAAa,eAAb,MAAa,aAAa;CACxB,OAAO,OAAO,EACZ,UAAU,eACX;CAED,AAAmB,MAAM,SAAS;CAClC,AAAmB,SAAS,QAAQ,OAAO;CAC3C,AAAmB,aAAa,QAAQ,WAAW;CAInD,AAAU,cAAqC,EAAE;;;;;CAMjD,AAAO,iBAAmC;AACxC,MAAI,KAAK,OAAO,WAAW,EAAE;AAC3B,QAAK,IAAI,KACP,uGACD;AACD,UAAO,EAAE;;AAGX,SAAO,KAAK;;;;;CAMd,AAAO,aAAa,MAA4B;AAC9C,MAAI,KAAK,OAAO,WAAW,EAAE;AAC3B,QAAK,IAAI,KACP,oGACD;AACD;;AAGF,MAAI,CAAC,KAAK,WAAW,CAAC,KAAK,KACzB,OAAM,IAAI,YACR,sDACD;AAGH,MAAI,KAAK,YAAY,MAAM,MAAM,EAAE,SAAS,KAAK,KAAK,CAEpD,MAAK,cAAc,KAAK,YAAY,QAAQ,MAAM,EAAE,SAAS,KAAK,KAAK;AAGzE,MAAI,CAAC,KAAK,WAAW;AACnB,QAAK,YAAY,EAAE;AACnB,OAAI,KAAK,QAAQ,KACf,MAAK,UAAU,OAAO,KAAK,UAAU,KAAK,OAAO,KAAK;AACxD,OAAI,KAAK,QAAQ,SACf,MAAK,UAAU,WAAW,KAAK,UAAU,KAAK,OAAO,SAAS;;AAGlE,OAAK,YAAY,KAAK,KAAK;;CAG7B,IAAW,QAA0B;EAEnC,MAAM,WAAW,KAAK,OAAO,MAAM,IACjC,iCACD,EAAE;AAEH,MAAI,UAAU;AACZ,OAAI,KAAK,OAAO,WAAW,CACzB,QAAO;GAGT,MAAM,QAAQ,EAAE;AAChB,QAAK,MAAM,QAAQ,UAAU;IAC3B,MAAM,eAAe,KAAK,YAAY,MAAM,MAAM,EAAE,SAAS,KAAK,KAAK;AACvE,QAAI,aACF,OAAM,KAAK,aAAa;;AAG5B,UAAO;;AAGT,SAAO,KAAK,eAAe,EAAE;;;;;CAM/B,MAAa,aAAwC;EACnD,MAAM,EAAE,SAAS,MAAM,KAAK,WAAW,MACrC,GAAG,aAAa,KAAK,YACrB;GACE,QAAQ;GACR,QAAQ,EACN,UAAU,wBACX;GACF,CACF;AAED,OAAK,OAAO,MAAM,IAAI,kCAAkC,KAAK;AAE7D,SAAO,KAAK;;;;;;;CAQd,AAAO,OACL,QAAqB,EAAE,EACD;AACtB,SAAO,IAAI,MAA4B,EAAE,EAA0B,EACjE,MAAM,GAAG,SAAS;AAChB,OAAI,OAAO,SAAS,SAClB;AAGF,UAAO,KAAK,oBAAyC,MAAM,MAAM;KAEpE,CAAC;;;;;;CAOJ,AAAO,IAAI,MAAuB;AAChC,SAAO,KAAK,MAAM,MAAM,SAAS,KAAK,SAAS,KAAK;;;;;;;CAQtD,MAAa,OACX,MACA,SAA4C,EAAE,EAC9C,UAA8C,EAAE,EAClC;AACd,OAAK,IAAI,MAAM,kBAAkB;GAAE;GAAM;GAAQ;GAAS,CAAC;EAC3D,MAAM,OAAO,MAAM,KAAK,cAAc,MAAM,QAAQ;AAGpD,MAAI,KAAK,WAAW,CAAC,QAAQ,SAAS;AACpC,QAAK,IAAI,MAAM,oBAAoB,EAAE,MAAM,CAAC;AAC5C,UAAO,KAAK,QACV;IACE,QAAQ,KAAK;IACb,KAAK,IAAI,IAAI,mBAAmB,KAAK,OAAO;IAC5C,OAAO,OAAO,SAAS,EAAE;IACzB,MAAM,OAAO,QAAQ,EAAE;IACvB,QAAQ,OAAO,UAAU,EAAE;IAC3B,SAAS,OAAO,WAAW,EAAE;IAC7B,UAAU,EAAE;IACZ,OAAO,IAAI,aAAa;IACzB,EACD,QACD;;AAGH,OAAK,IAAI,MAAM,qBAAqB;GAClC;GACA,MAAM,KAAK;GACX,SAAS,KAAK;GACf,CAAC;AAEF,SAAO,KAAK,aAAa,MAAM,QAAQ,QAAQ,CAAC,MAC7C,aAAa,SAAS,KACxB;;CAGH,AAAU,oBACR,MACA,QAAqB,EAAE,EACL;EAClB,MAAM,IAAsB,OAC1B,SAAc,EAAE,EAChB,UAAgC,EAAE,KAC/B;AACH,UAAO,KAAK,OAAO,MAAM,QAAQ;IAC/B,GAAG;IACH,GAAG;IACJ,CAAC;;AAGJ,SAAO,eAAe,GAAG,QAAQ;GAC/B,OAAO;GACP,UAAU;GACX,CAAC;AAEF,IAAE,MAAM,OAAO,SAAc,EAAE,EAAE,UAAgC,EAAE,KAAK;AACtE,UAAO,KAAK,OAAO,MAAM,QAAQ;IAC/B,GAAG;IACH,GAAG;IACJ,CAAC;;AAGJ,IAAE,QAAQ,OAAO,SAAc,EAAE,EAAE,UAAgC,EAAE,KAAK;GACxE,MAAM,OAAO,MAAM,KAAK,cAAc,MAAM,MAAM;AAClD,UAAO,KAAK,aAAa,MAAM,QAAQ,QAAQ;;AAGjD,IAAE,YAAY;AACZ,UAAO,KAAK,IAAI,KAAK;;AAGvB,IAAE,eAAe;GACf,MAAM,OAAO,KAAK,MAAM,MAAM,MAAM,EAAE,SAAS,KAAK;AACpD,OAAI,CAAC,KACH,OAAM,IAAI,YAAY,QAAQ,KAAK,aAAa;AAGlD,OAAI,KAAK,aAAa,CAAC,KAAK,QAAQ;AAClC,SAAK,SAAS,EAAE;AAChB,SAAK,OAAO,OAAO,KAAK,WAAW,OAC9B,oBACC,KAAK,MAAM,KAAK,UAAU,KAAK,CAChC,GACD;AACJ,SAAK,OAAO,WAAW,KAAK,WAAW,WAClC,oBACC,KAAK,MAAM,KAAK,UAAU,SAAS,CACpC,GACD;;AAGN,UAAO,KAAK;;AAMd,SAAO;;CAGT,MAAgB,aACd,MACA,SAA4C,EAAE,EAC9C,UAAgC,EAAE,EACV;AACxB,UAAQ,YAAY,EAAE;AACtB,UAAQ,QAAQ,UAAU,IAAI,QAAQ,QAAQ,QAAQ,QAAQ;EAE9D,MAAM,MAAM,KAAK,OAAO,QAAQ,IAAmB,UAAU;AAC7D,MAAI,KAAK,QAAQ,cACf,SAAQ,QAAQ,QAAQ,IAAI,iBAAiB,IAAI,QAAQ,cAAc;EAGzE,MAAM,UAAU,KAAK,OAAO,QAAQ,IAAI,UAAU;AAClD,MAAI,OAAO,YAAY,SACrB,SAAQ,QAAQ,QAAQ,IAAI,gBAAgB,QAAQ;EAGtD,MAAM,SAAS;GACb,GAAG;GAGH,QAAQ;IACN,MAAM,EAAE,KAAK;IACb,UAAU,EAAE,KAAK;IAClB;GACF;AAGD,MAAI,CAAC,KAAK,QAAQ,KAAK,QACrB,QAAO,OAAO,IAAI,KAAK,UAAU,OAAO;AAG1C,SAAO,OAAO,GAAG,OAAO,UAAU,SAAS,OAAO;AAClD,SAAO,SAAS;AAGhB,SAAO,KAAK,WAAW,YAAY;GACjC,MAAM,KAAK;GACX;GACA;GACQ;GACT,CAAC;;CAGJ,MAAgB,cACd,MACA,UAAuB,EAAE,EACA;AACzB,MACE,KAAK,OAAO,WAAW,IACvB,CAAC,KAAK,OAAO,MAAM,IAAI,iCAAiC,CAExD,OAAM,KAAK,YAAY;EAGzB,MAAM,OAAO,KAAK,MAAM,MACrB,MACC,EAAE,SAAS,SACV,CAAC,QAAQ,SAAS,EAAE,UAAU,QAAQ,WACtC,CAAC,QAAQ,WAAW,QAAQ,YAAY,EAAE,SAC9C;AAED,MAAI,CAAC,MAAM;GACT,MAAM,QAAQ,IAAI,kBAAkB,UAAU,KAAK,aAAa;AAEhE,SAAM,KAAK,OAAO,OAAO,KAAK,kBAAkB;IAC9C,OAAO;IACP;IACD,CAAC;AACF,SAAM;;AAGR,MAAI,QAAQ,SACV,QAAO;GACL,GAAG;GACH,MAAM,QAAQ;GACf;AAGH,SAAO;;;;;;;;;ACpVX,MAAa,WACX,UACyB;AACzB,QAAO,QAAQ,aAAa,CAAC,OAAU,MAAM;;AAG/C,QAAQ,QAAQ;;;;;;;;;;;;;ACHhB,MAAa,WAAW,YAAoC;AAC1D,QAAO,gBAAgB,iBAAiB,QAAQ;;AAuDlD,IAAa,kBAAb,cAAqC,UAAkC;CACrE,IAAW,OAAe;AACxB,SAAO,KAAK,QAAQ,QAAQ,KAAK,OAAO;;;AAI5C,QAAQ,QAAQ;;;;AC/DhB,MAAMA,cAAY,EAAE,OAAO,EACzB,mBAAmB,EAAE,KAAK;CACxB,aAAa;CACb,SAAS;CACV,CAAC,EACH,CAAC;AAEF,IAAa,0BAAb,MAAqC;CACnC,AAAmB,MAAM,KAAKA,YAAU;CACxC,AAAmB,SAAS,QAAQ,OAAO;CAC3C,AAAmB,gBAAgB,QAAQ,oBAAoB;CAC/D,AAAmB,eAAe,QAAQ,aAAa;CACvD,AAAmB,UAA+B,EAAE;CACpD,AAAmB,MAAM,SAAS;CAElC,AAAO,aAA6B;AAClC,SAAO,KAAK;;CAGd,AAAgB,YAAY,MAAM;EAChC,IAAI;EACJ,SAAS,YAAY;GACnB,MAAM,UAAU,KAAK,OAAO,WAAW,QAAQ;AAC/C,QAAK,MAAM,UAAU,QACnB,OAAM,KAAK,eAAe,OAAO;;EAGtC,CAAC;CAEF,AAAgB,QAAQ,MAAM;EAC5B,IAAI;EACJ,SAAS,YAAY;AACnB,QAAK,MAAM,UAAU,KAAK,SAAS;IACjC,MAAM,QACJ,OAAO,OAAO,gBAAgB,UAAU,aACpC,MAAM,OAAO,eAAe,OAAO,GACnC;AAEN,QAAI,CAAC,OAAO,SACV;IAGF,MAAM,EAAE,UAAU,MAAM,OAAO,MAAM,EAAE,eAAe,OAAO,CAAC;AAE9D,SAAK,MAAM,QAAQ,OAAO;KACxB,IAAI,OAAO,KAAK,KAAK,QAAQ,OAAO,QAAQ,GAAG;AAC/C,SAAI,KAAK,QACP,QAAO,IAAI,KAAK,UAAU;AAG5B,UAAK,aAAa,aAAa;MAC7B,GAAG;MACH,QAAQ,OAAO;MACf;MACA,QAAQ,KAAK,UAAU;MACvB,MAAM,OAAO;MACb,SAAS,OAAO;MACjB,CAAC;;AAGJ,SAAK,IAAI,KAAK,WAAW,OAAO,KAAK,OAAO;KAC1C,OAAO,OAAO,MAAM;KACpB,QAAQ,OAAO;KAChB,CAAC;;;EAGP,CAAC;CAEF,MAAa,eAAe,OAAuC;EACjE,MAAM,UAAU,MAAM;EACtB,MAAM,MAAM,OAAO,QAAQ,QAAQ,WAAW,QAAQ,MAAM,QAAQ,KAAK;EACzE,MAAM,WAAW,aAAa,KAAK;EACnC,MAAM,OAAO,MAAM;EACnB,MAAM,QAAQ,OAAO,QAAQ,UAAU,WAAW,QAAQ,QAAQ,EAAE;EAEpE,MAAM,SAAuB;GAC3B;GACA;GACA,QAAQ;GACR,gBAAgB,QAAQ;GACxB,OAAO,CAAC,CAAC,QAAQ;GACjB,UAAU,CAAC,MAAM;GACjB,QAAQ,OAAO,SAAS;IACtB,MAAM,EAAE,eAAe,SAAS;AAChC,WAAO,MAAM,MAAM,GAAG,MAAM,SAAS,GAAG,KAAK,UAAU,EACrD,SAAS,IAAI,QACX,gBACI,EACE,eACD,GACD,EAAE,CACP,EACF,CAAC,CAAC,MAAM,OAAO,GAAG,MAAM,CAAC;;GAE5B,OAAO,OAAO,SAAS;IACrB,MAAM,EAAE,kBAAkB;IAC1B,MAAM,YAAY,MAAM,KAAK,WAAW,IAAI;KAC1C,SAAS;KACT,KAAK,GAAG,MAAM;KACd;KACD,CAAC;AAEF,QAAI,UAAU,UAAU,KACtB,QAAO,SAAS,UAAU;AAG5B,WAAO;;GAEV;AAED,OAAK,QAAQ,KAAK,OAAO;AAEzB,MAAI,QAAQ,MACV,MAAK,cAAc,YAAY;GAC7B,MAAM,GAAG,KAAK,IAAI,kBAAkB,GAAG,KAAK;GAC5C,QAAQ;GACR,UAAU,QAAQ;AAChB,QAAI,WAAW,IAAI,SAAS,QAC1B,GAAG,KAAK,IAAI,kBAAkB,GAAG,QACjC,OAAO,OACR;;GAEH,GAAG;GACJ,CAAC;;CAIN,AAAmB,aAAa,OAAO;EACrC,KAAK;EACL,SAAS,EACP,SAAS,KACV;EACD,UAAU,GAAG,SAAS,EAAE,SAAS,UAAU;AACzC,QAAK,IAAI,KAAK,iCAAiC,QAAQ,OAAO;IAC5D;IACA;IACD,CAAC;;EAEJ,SAAS,OAAO,SAAuD;GACrE,MAAM,EAAE,KAAK,kBAAkB;GAC/B,MAAM,WAAW,MAAM,MAAM,KAAK,EAChC,SAAS,IAAI,QACX,gBACI,EACE,eACD,GACD,EAAE,CACP,EACF,CAAC;AAEF,OAAI,CAAC,SAAS,GACZ,OAAM,IAAI,MAAM,8BAA8B,MAAM;AAGtD,UAAO,KAAK,OAAO,MAAM,OACvB,wBACA,MAAM,SAAS,MAAM,CACtB;;EAEJ,CAAC;;;;;ACrJJ,MAAM,YAAY,EAAE,OAAO,EACzB,mBAAmB,EAAE,KAAK;CACxB,aAAa;CACb,SAAS;CACV,CAAC,EACH,CAAC;AAEF,IAAa,sBAAb,MAAiC;CAC/B,AAAmB,MAAM,KAAK,UAAU;CACxC,AAAmB,SAAS,QAAQ,OAAO;CAC3C,AAAmB,eAAe,QAAQ,aAAa;CACvD,AAAmB,iBAAiB,QAAQ,wBAAwB;CACpE,AAAmB,uBAAuB,QAAQ,qBAAqB;CAEvE,IAAW,SAAS;AAClB,SAAO,KAAK,IAAI;;CAGlB,AAAgB,UAAU,MAAM;EAC9B,IAAI;EACJ,eAAe;AAEb,QAAK,MAAM,UAAU,KAAK,OAAO,WAAW,QAAQ,CAClD,MAAK,aAAa,aAAa;IAC7B,MAAM,OAAO;IACb,OAAO,OAAO;IACd,QAAQ,OAAO,QAAQ;IACvB,iBAAiB,OAAO,oBAAoB;IAC5C,SAAS,OAAO,QAAQ,UAAU;IAClC,QAAQ,OAAO,WAAW,QAAQ,SAAY,OAAO;IACrD,QAAQ,OAAO;IACf,MAAM,OAAO;IAEb,UACE,QACA,UAAgC,EAAE,KAC/B,OAAO,IAAI,QAAQ,QAAQ;IACjC,CAAC;;EAGP,CAAC;;;;;;CAOF,AAAgB,QAAQ,OAAO;EAC7B,MAAM,aAAa,KAAK;EACxB,QAAQ,EACN,UAAU,wBACX;EACD,UAAU,EAAE,MAAM,cAAc;AAC9B,UAAO,KAAK,gBAAgB;IAC1B;IACA,eAAe,QAAQ;IACxB,CAAC;;EAEL,CAAC;;;;;CAMF,MAAa,gBACX,SAC2B;EAC3B,MAAM,EAAE,SAAS;EACjB,IAAI;EACJ,IAAI;EACJ,MAAM,cAAc,KAAK,OAAO,IAAI,iBAAiB;AACrD,MAAI,eAAe,MAAM;AACvB,iBAAc,KAAK,OAAO,OAAO,iBAAiB,CAAC,eAAe,KAAK;AACvE,mBAAgB,IAAI,IAClB,YAAY,KAAK,OAAO,CAAC,GAAG,GAAG,MAAM,GAAG,GAAG,QAAQ,GAAG,CAAC,CACxD;;EAGH,MAAM,YAAuB,EAAE;AAG/B,OAAK,MAAM,cAAc,eAAe,EAAE,CACxC,KACE,CAAC,WAAW,QACZ,CAAC,WAAW,UACZ,WAAW,QACX,WAAW,MAEX,WAAU,KAAK;GACb,MAAM;GACN,MAAM,WAAW;GACjB,OAAO,WAAW;GACnB,CAAC;AAKN,OAAK,MAAM,QAAQ,KAAK,aAAa,gBAAgB,EAAE;AAErD,OAAI,KAAK,KAAM;AAEf,OAAI,eAAe,KAAK,SAAS;AAE/B,QAAI,CAAC,KACH;AAGF,QAAI,OAAO,KAAK,YAAY,YAAY,KAAK,QAAQ,OAEnD;SAAI,KAAK,UAAU,KAAK,QAAQ,MAC9B;eAEO,eAGT;SAAI,CAAC,cAAc,IAAI,GAAG,KAAK,MAAM,GAAG,KAAK,OAAO,CAClD;;;AAKN,aAAU,KAAK;IACb,MAAM,KAAK;IACX,OAAO,KAAK;IACZ,iBAAiB,KAAK;IACtB,QAAQ,KAAK;IACb,MAAM,KAAK;IACX,WAAW,KAAK;IACjB,CAAC;;AAGJ,OAAK,qBAAqB,YAAY,mBAAmB;EAGzD,MAAM,WAAW,KAAK,eACnB,YAAY,CACZ,QAAQ,OAAO,GAAG,MAAM,CACxB,IAAI,OAAO,WAAW;GACrB,MAAM,EAAE,OAAO,WAAW,MAAM,OAAO,MAAM,QAAQ;AACrD,UAAO,MAAM,KAAK,SAAS;IACzB,IAAI,OAAO,KAAK,KAAK,QAAQ,UAAU,QAAQ,GAAG;AAClD,QAAI,KAAK,QACP,QAAO,IAAI,KAAK,UAAU;AAG5B,WAAO;KACL,GAAG;KACH;KACA,OAAO;KACP,SAAS,OAAO;KACjB;KACD;IACF;AAEJ,YAAU,KAAK,IAAI,MAAM,QAAQ,IAAI,SAAS,EAAE,MAAM,CAAC;AACvD,OAAK,qBAAqB,UAAU,mBAAmB;AAEvD,SAAO;GACL,QAAQ,KAAK,IAAI;GACjB,OAAO;GACR;;;;;;;;;;;;;;;;;ACvIL,MAAa,oBAAoB,QAAQ;CACvC,MAAM;CACN,OAAO,CAAC,aAAa;CACrB,YAAY,CAAC,SAAS,QAAQ;CAC9B,UAAU;EACR;EACA;EACA;EACA;EACD;CACF,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.js","names":["envSchema"],"sources":["../../../src/server/links/schemas/apiLinksResponseSchema.ts","../../../src/server/links/atoms/apiLinksAtom.ts","../../../src/server/links/providers/LinkProvider.ts","../../../src/server/links/primitives/$client.ts","../../../src/server/links/primitives/$remote.ts","../../../src/server/links/providers/RemotePrimitiveProvider.ts","../../../src/server/links/providers/ServerLinksProvider.ts","../../../src/server/links/index.ts"],"sourcesContent":["import type { Static } from \"alepha\";\nimport { t } from \"alepha\";\n\nexport const apiLinkSchema = t.object({\n name: t.text({\n description: \"Name of the API link, used for identification.\",\n }),\n\n group: t.optional(\n t.text({\n description:\n \"Group to which the API link belongs, used for categorization.\",\n }),\n ),\n\n path: t.text({\n description: \"Pathname used to access the API link.\",\n }),\n\n method: t.optional(\n t.text({\n description:\n \"HTTP method used for the API link, e.g., GET, POST, etc. If not specified, defaults to GET.\",\n }),\n ),\n\n requestBodyType: t.optional(\n t.text({\n description:\n \"Type of the request body for the API link. Default is application/json for POST/PUT/PATCH, null for others.\",\n }),\n ),\n\n service: t.optional(\n t.text({\n description:\n \"Service name associated with the API link, used for service discovery.\",\n }),\n ),\n\n rawSchema: t.optional(\n t.object({\n body: t.optional(t.string()),\n response: t.optional(t.string()),\n }),\n ),\n});\n\nexport const apiLinksResponseSchema = t.object({\n prefix: t.optional(t.text()),\n links: t.array(apiLinkSchema),\n});\n\nexport type ApiLinksResponse = Static<typeof apiLinksResponseSchema>;\nexport type ApiLink = Static<typeof apiLinkSchema>;\n","import { $atom, t } from \"alepha\";\nimport { apiLinksResponseSchema } from \"../schemas/apiLinksResponseSchema.ts\";\n\nexport const apiLinksAtom = $atom({\n name: \"alepha.server.request.apiLinks\",\n schema: t.optional(apiLinksResponseSchema),\n});\n","import {\n $inject,\n Alepha,\n AlephaError,\n type Async,\n jsonSchemaToTypeBox,\n t,\n} from \"alepha\";\nimport { $logger } from \"alepha/logger\";\nimport type { ServerRouteSecure } from \"alepha/security\";\nimport {\n type ActionPrimitive,\n type ClientRequestEntry,\n type ClientRequestOptions,\n type ClientRequestResponse,\n type FetchResponse,\n HttpClient,\n type RequestConfigSchema,\n ServerReply,\n type ServerRequest,\n type ServerRequestConfigEntry,\n type ServerResponseBody,\n type TRequestBody,\n UnauthorizedError,\n} from \"alepha/server\";\nimport {\n type ApiLink,\n apiLinksResponseSchema,\n} from \"../schemas/apiLinksResponseSchema.ts\";\n\n/**\n * Browser, SSR friendly, service to handle links.\n */\nexport class LinkProvider {\n static path = {\n apiLinks: \"/api/_links\",\n };\n\n protected readonly log = $logger();\n protected readonly alepha = $inject(Alepha);\n protected readonly httpClient = $inject(HttpClient);\n\n // all server links (local + remote)\n // THIS IS NOT USER LINKS! (which are filtered by permissions)\n protected serverLinks: Array<HttpClientLink> = [];\n\n /**\n * Get applicative links registered on the server.\n * This does not include lazy-loaded remote links.\n */\n public getServerLinks(): HttpClientLink[] {\n if (this.alepha.isBrowser()) {\n this.log.warn(\n \"Getting server links in the browser is not supported. Use `fetchLinks` to get links from the server.\",\n );\n return [];\n }\n\n return this.serverLinks;\n }\n\n /**\n * Register a new link for the application.\n */\n public registerLink(link: HttpClientLink): void {\n if (this.alepha.isBrowser()) {\n this.log.warn(\n \"Registering links in the browser is not supported. Use `fetchLinks` to get links from the server.\",\n );\n return;\n }\n\n if (!link.handler && !link.host) {\n throw new AlephaError(\n \"Can't create link - 'handler' or 'host' is required\",\n );\n }\n\n if (this.serverLinks.some((l) => l.name === link.name)) {\n // remove existing link with the same name\n this.serverLinks = this.serverLinks.filter((l) => l.name !== link.name);\n }\n\n if (!link.rawSchema) {\n link.rawSchema = {};\n if (link.schema?.body)\n link.rawSchema.body = JSON.stringify(link.schema.body);\n if (link.schema?.response)\n link.rawSchema.response = JSON.stringify(link.schema.response);\n }\n\n this.serverLinks.push(link);\n }\n\n public get links(): HttpClientLink[] {\n // TODO: not performant at all, use a map instead for ServerLinks\n const apiLinks = this.alepha.store.get(\n \"alepha.server.request.apiLinks\",\n )?.links;\n\n if (apiLinks) {\n if (this.alepha.isBrowser()) {\n return apiLinks;\n }\n\n const links = [];\n for (const link of apiLinks) {\n const originalLink = this.serverLinks.find((l) => l.name === link.name);\n if (originalLink) {\n links.push(originalLink);\n }\n }\n return links;\n }\n\n return this.serverLinks ?? [];\n }\n\n /**\n * Force browser to refresh links from the server.\n */\n public async fetchLinks(): Promise<HttpClientLink[]> {\n const { data } = await this.httpClient.fetch(\n `${LinkProvider.path.apiLinks}`,\n {\n method: \"GET\",\n schema: {\n response: apiLinksResponseSchema,\n },\n },\n );\n\n this.alepha.store.set(\"alepha.server.request.apiLinks\", data);\n\n return data.links;\n }\n\n /**\n * Create a virtual client that can be used to call actions.\n *\n * Use js Proxy under the hood.\n */\n public client<T extends object>(\n scope: ClientScope = {},\n ): HttpVirtualClient<T> {\n return new Proxy<HttpVirtualClient<T>>({} as HttpVirtualClient<T>, {\n get: (_, prop) => {\n if (typeof prop !== \"string\") {\n return;\n }\n\n return this.createVirtualAction<RequestConfigSchema>(prop, scope);\n },\n });\n }\n\n /**\n * Check if a link with the given name exists.\n * @param name\n */\n public can(name: string): boolean {\n return this.links.some((link) => link.name === name);\n }\n\n /**\n * Resolve a link by its name and call it.\n * - If link is local, it will call the local handler.\n * - If link is remote, it will make a fetch request to the remote server.\n */\n public async follow(\n name: string,\n config: Partial<ServerRequestConfigEntry> = {},\n options: ClientRequestOptions & ClientScope = {},\n ): Promise<any> {\n this.log.trace(\"Following link\", { name, config, options });\n const link = await this.getLinkByName(name, options);\n\n // if a handler is defined, use it (ssr)\n if (link.handler && !options.request) {\n this.log.trace(\"Local link found\", { name });\n return link.handler(\n {\n method: link.method,\n url: new URL(`http://localhost${link.path}`),\n query: config.query ?? {},\n body: config.body ?? {},\n params: config.params ?? {},\n headers: config.headers ?? {},\n metadata: {},\n reply: new ServerReply(),\n } as Partial<ServerRequest> as ServerRequest,\n options,\n );\n }\n\n this.log.trace(\"Remote link found\", {\n name,\n host: link.host,\n service: link.service,\n });\n\n return this.followRemote(link, config, options).then(\n (response) => response.data,\n );\n }\n\n protected createVirtualAction<T extends RequestConfigSchema>(\n name: string,\n scope: ClientScope = {},\n ): VirtualAction<T> {\n const $: VirtualAction<T> = async (\n config: any = {},\n options: ClientRequestOptions = {},\n ) => {\n return this.follow(name, config, {\n ...scope,\n ...options,\n });\n };\n\n Object.defineProperty($, \"name\", {\n value: name,\n writable: false,\n });\n\n $.run = async (config: any = {}, options: ClientRequestOptions = {}) => {\n return this.follow(name, config, {\n ...scope,\n ...options,\n });\n };\n\n $.fetch = async (config: any = {}, options: ClientRequestOptions = {}) => {\n const link = await this.getLinkByName(name, scope);\n return this.followRemote(link, config, options);\n };\n\n $.can = () => {\n return this.can(name);\n };\n\n $.schema = () => {\n const link = this.links.find((l) => l.name === name);\n if (!link) {\n throw new AlephaError(`Link ${name} not found.`);\n }\n\n if (link.rawSchema && !link.schema) {\n link.schema = {};\n link.schema.body = link.rawSchema?.body\n ? (jsonSchemaToTypeBox(\n JSON.parse(link.rawSchema.body),\n ) as TRequestBody)\n : undefined;\n link.schema.response = link.rawSchema?.response\n ? (jsonSchemaToTypeBox(\n JSON.parse(link.rawSchema.response),\n ) as TRequestBody)\n : undefined;\n }\n\n return link.schema as {\n body: any;\n response: any;\n };\n };\n\n return $;\n }\n\n protected async followRemote(\n link: HttpClientLink,\n config: Partial<ServerRequestConfigEntry> = {},\n options: ClientRequestOptions = {},\n ): Promise<FetchResponse> {\n options.request ??= {};\n options.request.headers = new Headers(options.request.headers);\n\n const als = this.alepha.context.get<ServerRequest>(\"request\");\n if (als?.headers.authorization) {\n options.request.headers.set(\"authorization\", als.headers.authorization);\n }\n\n const context = this.alepha.context.get(\"context\");\n if (typeof context === \"string\") {\n options.request.headers.set(\"x-request-id\", context);\n }\n\n const action = {\n ...link,\n // schema is not used in the client,\n // we assume that TypeScript will check\n schema: {\n body: t.any(),\n response: t.any(),\n },\n };\n\n // prefix with service when host is not defined (e.g. browser)\n if (!link.host && link.service) {\n action.path = `/${link.service}${action.path}`;\n }\n\n action.path = `${action.prefix ?? \"/api\"}${action.path}`;\n action.prefix = undefined; // prefix is not used in the client\n\n // else, make a request\n return this.httpClient.fetchAction({\n host: link.host,\n config,\n options,\n action: action as any, // schema.body TAny is not accepted\n });\n }\n\n protected async getLinkByName(\n name: string,\n options: ClientScope = {},\n ): Promise<HttpClientLink> {\n if (\n this.alepha.isBrowser() &&\n !this.alepha.store.get(\"alepha.server.request.apiLinks\")\n ) {\n await this.fetchLinks();\n }\n\n const link = this.links.find(\n (a) =>\n a.name === name &&\n (!options.group || a.group === options.group) &&\n (!options.service || options.service === a.service),\n );\n\n if (!link) {\n const error = new UnauthorizedError(`Action ${name} not found.`);\n // mimic http error handling\n await this.alepha.events.emit(\"client:onError\", {\n route: link,\n error,\n });\n throw error;\n }\n\n if (options.hostname) {\n return {\n ...link,\n host: options.hostname,\n };\n }\n\n return link;\n }\n}\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport interface HttpClientLink extends ApiLink {\n secured?: boolean | ServerRouteSecure;\n prefix?: string;\n // -- server only --\n // only for remote actions\n host?: string;\n service?: string;\n // used only for local actions, not for remote actions\n schema?: RequestConfigSchema;\n handler?: (\n request: ServerRequest,\n options: ClientRequestOptions,\n ) => Async<ServerResponseBody>;\n}\n\nexport interface ClientScope {\n group?: string;\n service?: string;\n hostname?: string;\n}\n\nexport type HttpVirtualClient<T> = {\n [K in keyof T as T[K] extends ActionPrimitive<RequestConfigSchema>\n ? K\n : never]: T[K] extends ActionPrimitive<infer Schema>\n ? VirtualAction<Schema>\n : never;\n};\n\nexport interface VirtualAction<T extends RequestConfigSchema>\n extends Pick<ActionPrimitive<T>, \"name\" | \"run\" | \"fetch\"> {\n (\n config?: ClientRequestEntry<T>,\n opts?: ClientRequestOptions,\n ): Promise<ClientRequestResponse<T>>;\n can: () => boolean;\n schema: () => {\n body: T[\"body\"];\n response: T[\"response\"];\n };\n}\n","import { $inject, KIND } from \"alepha\";\nimport {\n type ClientScope,\n type HttpVirtualClient,\n LinkProvider,\n} from \"../providers/LinkProvider.ts\";\n\n/**\n * Create a new client.\n */\nexport const $client = <T extends object>(\n scope?: ClientScope,\n): HttpVirtualClient<T> => {\n return $inject(LinkProvider).client<T>(scope);\n};\n\n$client[KIND] = \"$client\";\n","import { createPrimitive, KIND, Primitive } from \"alepha\";\nimport type { ServiceAccountPrimitive } from \"alepha/security\";\nimport type { ProxyPrimitiveOptions } from \"alepha/server/proxy\";\n\n/**\n * $remote is a primitive that allows you to define remote service access.\n *\n * Use it only when you have 2 or more services that need to communicate with each other.\n *\n * All remote services can be exposed as actions, ... or not.\n *\n * You can add a service account if you want to use a security layer.\n */\nexport const $remote = (options: RemotePrimitiveOptions) => {\n return createPrimitive(RemotePrimitive, options);\n};\n\nexport interface RemotePrimitiveOptions {\n /**\n * The URL of the remote service.\n * You can use a function to generate the URL dynamically.\n * You probably should use $env(env) to get the URL from the environment.\n *\n * @example\n * ```ts\n * import { $remote } from \"alepha/server\";\n * import { $inject, t } from \"alepha\";\n *\n * class App {\n * env = $env(t.object({\n * REMOTE_URL: t.text({default: \"http://localhost:3000\"}),\n * }));\n * remote = $remote({\n * url: this.env.REMOTE_URL,\n * });\n * }\n * ```\n */\n url: string | (() => string);\n\n /**\n * The name of the remote service.\n *\n * @default Member of the class containing the remote service.\n */\n name?: string;\n\n /**\n * If true, all methods of the remote service will be exposed as actions in this context.\n * > Note: Proxy will never use the service account, it just... proxies the request.\n */\n proxy?:\n | boolean\n | Partial<\n ProxyPrimitiveOptions & {\n /**\n * If true, the remote service won't be available internally, only through the proxy.\n */\n noInternal: boolean;\n }\n >;\n\n /**\n * For communication between the server and the remote service with a security layer.\n * This will be used for internal communication and will not be exposed to the client.\n */\n serviceAccount?: ServiceAccountPrimitive;\n}\n\nexport class RemotePrimitive extends Primitive<RemotePrimitiveOptions> {\n public get name(): string {\n return this.options.name ?? this.config.propertyKey;\n }\n}\n\n$remote[KIND] = RemotePrimitive;\n","import { $env, $hook, $inject, Alepha, t } from \"alepha\";\nimport { $logger } from \"alepha/logger\";\nimport { $retry } from \"alepha/retry\";\nimport type { ServiceAccountPrimitive } from \"alepha/security\";\nimport { ServerProxyProvider } from \"alepha/server/proxy\";\nimport { $remote, type RemotePrimitive } from \"../primitives/$remote.ts\";\nimport {\n type ApiLinksResponse,\n apiLinksResponseSchema,\n} from \"../schemas/apiLinksResponseSchema.ts\";\nimport { LinkProvider } from \"./LinkProvider.ts\";\n\nconst envSchema = t.object({\n SERVER_API_PREFIX: t.text({\n description: \"Prefix for all API routes (e.g. $action).\",\n default: \"/api\",\n }),\n});\n\nexport class RemotePrimitiveProvider {\n protected readonly env = $env(envSchema);\n protected readonly alepha = $inject(Alepha);\n protected readonly proxyProvider = $inject(ServerProxyProvider);\n protected readonly linkProvider = $inject(LinkProvider);\n protected readonly remotes: Array<ServerRemote> = [];\n protected readonly log = $logger();\n\n public getRemotes(): ServerRemote[] {\n return this.remotes;\n }\n\n public readonly configure = $hook({\n on: \"configure\",\n handler: async () => {\n const remotes = this.alepha.primitives($remote);\n for (const remote of remotes) {\n await this.registerRemote(remote);\n }\n },\n });\n\n public readonly start = $hook({\n on: \"start\",\n handler: async () => {\n for (const remote of this.remotes) {\n const token =\n typeof remote.serviceAccount?.token === \"function\"\n ? await remote.serviceAccount.token()\n : undefined;\n\n if (!remote.internal) {\n continue; // skip download links for remotes that are not internal\n }\n\n const { links } = await remote.links({ authorization: token });\n\n for (const link of links) {\n let path = link.path.replace(remote.prefix, \"\");\n if (link.service) {\n path = `/${link.service}${path}`;\n }\n\n this.linkProvider.registerLink({\n ...link,\n prefix: remote.prefix,\n path,\n method: link.method ?? \"GET\",\n host: remote.url,\n service: remote.name,\n });\n }\n\n this.log.info(`Remote '${remote.name}' OK`, {\n links: remote.links.length,\n prefix: remote.prefix,\n });\n }\n },\n });\n\n public async registerRemote(value: RemotePrimitive): Promise<void> {\n const options = value.options;\n const url = typeof options.url === \"string\" ? options.url : options.url();\n const linkPath = LinkProvider.path.apiLinks;\n const name = value.name;\n const proxy = typeof options.proxy === \"object\" ? options.proxy : {};\n\n const remote: ServerRemote = {\n url,\n name,\n prefix: \"/api\",\n serviceAccount: options.serviceAccount,\n proxy: !!options.proxy,\n internal: !proxy.noInternal,\n schema: async (opts) => {\n const { authorization, name } = opts;\n return await fetch(`${url}${linkPath}/${name}/schema`, {\n headers: new Headers(\n authorization\n ? {\n authorization,\n }\n : {},\n ),\n }).then((it) => it.json()); // TODO: use schema validation for response\n },\n links: async (opts) => {\n const { authorization } = opts;\n const remoteApi = await this.fetchLinks.run({\n service: name,\n url: `${url}${linkPath}`,\n authorization,\n });\n\n if (remoteApi.prefix != null) {\n remote.prefix = remoteApi.prefix; // monkey patch the prefix, not ideal but works\n }\n\n return remoteApi;\n },\n };\n\n this.remotes.push(remote);\n\n if (options.proxy) {\n this.proxyProvider.createProxy({\n path: `${this.env.SERVER_API_PREFIX}/${name}/*`,\n target: url,\n rewrite: (url) => {\n url.pathname = url.pathname.replace(\n `${this.env.SERVER_API_PREFIX}/${name}`,\n remote.prefix,\n );\n },\n ...proxy,\n });\n }\n }\n\n protected readonly fetchLinks = $retry({\n max: 10,\n backoff: {\n initial: 1000,\n },\n onError: (_, attempt, { service, url }) => {\n this.log.warn(`Failed to fetch links, retry (${attempt})...`, {\n service,\n url,\n });\n },\n handler: async (opts: FetchLinksOptions): Promise<ApiLinksResponse> => {\n const { url, authorization } = opts;\n const response = await fetch(url, {\n headers: new Headers(\n authorization\n ? {\n authorization,\n }\n : {},\n ),\n });\n\n if (!response.ok) {\n throw new Error(`Failed to fetch links from ${url}`);\n }\n\n return this.alepha.codec.decode(\n apiLinksResponseSchema,\n await response.json(),\n );\n },\n });\n}\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport interface FetchLinksOptions {\n /**\n * Name of the remote service.\n */\n service: string;\n\n /**\n * URL to fetch links from.\n */\n url: string;\n\n /**\n * Authorization header containing access token.\n */\n authorization?: string;\n}\n\nexport interface ServerRemote {\n /**\n * URL of the remote service.\n */\n url: string;\n\n /**\n * Name of the remote service.\n */\n name: string;\n\n /**\n * Expose links as endpoint. It's not only internal.\n */\n proxy: boolean;\n\n /**\n * It's only used inside the application.\n */\n internal: boolean;\n\n /**\n * Links fetcher.\n */\n links: (args: { authorization?: string }) => Promise<ApiLinksResponse>;\n\n /**\n * Fetches schema for the remote service.\n */\n schema: (args: { name: string; authorization?: string }) => Promise<any>;\n\n /**\n * Force a default access token provider when not provided.\n */\n serviceAccount?: ServiceAccountPrimitive;\n\n /**\n * Prefix for the remote service links.\n */\n prefix: string;\n}\n","import { $env, $hook, $inject, Alepha, t } from \"alepha\";\nimport {\n type Permission,\n SecurityProvider,\n type UserAccountToken,\n} from \"alepha/security\";\nimport {\n $action,\n $route,\n type ClientRequestEntry,\n type ClientRequestOptions,\n type RequestConfigSchema,\n ServerTimingProvider,\n} from \"alepha/server\";\nimport {\n type ApiLink,\n type ApiLinksResponse,\n apiLinksResponseSchema,\n} from \"../schemas/apiLinksResponseSchema.ts\";\nimport { LinkProvider } from \"./LinkProvider.ts\";\nimport { RemotePrimitiveProvider } from \"./RemotePrimitiveProvider.ts\";\n\nconst envSchema = t.object({\n SERVER_API_PREFIX: t.text({\n description: \"Prefix for all API routes (e.g. $action).\",\n default: \"/api\",\n }),\n});\n\nexport class ServerLinksProvider {\n protected readonly env = $env(envSchema);\n protected readonly alepha = $inject(Alepha);\n protected readonly linkProvider = $inject(LinkProvider);\n protected readonly remoteProvider = $inject(RemotePrimitiveProvider);\n protected readonly serverTimingProvider = $inject(ServerTimingProvider);\n\n public get prefix() {\n return this.env.SERVER_API_PREFIX;\n }\n\n public readonly onRoute = $hook({\n on: \"configure\",\n handler: () => {\n // convert all $action to local links\n for (const action of this.alepha.primitives($action)) {\n this.linkProvider.registerLink({\n name: action.name,\n group: action.group,\n schema: action.options.schema,\n requestBodyType: action.getBodyContentType(),\n secured: action.options.secure ?? true,\n method: action.method === \"GET\" ? undefined : action.method,\n prefix: action.prefix,\n path: action.path,\n // by local, we mean that it can be called directly via the handler\n handler: (\n config: ClientRequestEntry<RequestConfigSchema>,\n options: ClientRequestOptions = {},\n ) => action.run(config, options),\n });\n }\n },\n });\n\n /**\n * First API - Get all API links for the user.\n *\n * This is based on the user's permissions.\n */\n public readonly links = $route({\n path: LinkProvider.path.apiLinks,\n schema: {\n response: apiLinksResponseSchema,\n },\n handler: ({ user, headers }) => {\n return this.getUserApiLinks({\n user,\n authorization: headers.authorization,\n });\n },\n });\n\n /**\n * Retrieves API links for the user based on their permissions.\n * Will check on local links and remote links.\n */\n public async getUserApiLinks(\n options: GetApiLinksOptions,\n ): Promise<ApiLinksResponse> {\n const { user } = options;\n let permissions: Permission[] | undefined;\n let permissionMap: Map<string, Permission> | undefined;\n const hasSecurity = this.alepha.has(SecurityProvider);\n if (hasSecurity && user) {\n permissions = this.alepha.inject(SecurityProvider).getPermissions(user);\n permissionMap = new Map(\n permissions.map((it) => [`${it.group}:${it.name}`, it]),\n );\n }\n\n const userLinks: ApiLink[] = [];\n\n // bonus: add permissions not related to $action\n for (const permission of permissions ?? []) {\n if (\n !permission.path &&\n !permission.method &&\n permission.name &&\n permission.group\n ) {\n userLinks.push({\n path: \"\", // this is a placeholder for links without specific path\n name: permission.name,\n group: permission.group,\n });\n }\n }\n\n // add local links\n for (const link of this.linkProvider.getServerLinks()) {\n // SKIP REMOTE LINKS, remote links are handled separately for security\n if (link.host) continue;\n\n if (hasSecurity && link.secured) {\n // skip secured links if user is not provided\n if (!user) {\n continue;\n }\n\n if (typeof link.secured === \"object\" && link.secured.realm) {\n // realm check\n if (user.realm !== link.secured.realm) {\n continue;\n }\n } else if (permissionMap) {\n // small permissions check, can be optimized later ... :')\n\n if (!permissionMap.has(`${link.group}:${link.name}`)) {\n continue;\n }\n }\n }\n\n userLinks.push({\n name: link.name,\n group: link.group,\n requestBodyType: link.requestBodyType,\n method: link.method,\n path: link.path,\n rawSchema: link.rawSchema,\n });\n }\n\n this.serverTimingProvider.beginTiming(\"fetchRemoteLinks\");\n // this does not scale well, but it's working for now\n // TODO: remote links can be cached by user.roles\n const promises = this.remoteProvider\n .getRemotes()\n .filter((it) => it.proxy) // add only \"proxy\" remotes\n .map(async (remote) => {\n const { links, prefix } = await remote.links(options);\n return links.map((link) => {\n let path = link.path.replace(prefix ?? \"/api\", \"\");\n if (link.service) {\n path = `/${link.service}${path}`;\n }\n\n return {\n ...link,\n path,\n proxy: true,\n service: remote.name,\n };\n });\n });\n\n userLinks.push(...(await Promise.all(promises)).flat());\n this.serverTimingProvider.endTiming(\"fetchRemoteLinks\");\n\n return {\n prefix: this.env.SERVER_API_PREFIX,\n links: userLinks,\n };\n }\n}\n\nexport interface GetApiLinksOptions {\n user?: UserAccountToken;\n authorization?: string;\n}\n","import \"alepha/security\";\nimport { $module } from \"alepha\";\nimport { AlephaServer } from \"alepha/server\";\nimport { apiLinksAtom } from \"./atoms/apiLinksAtom.ts\";\nimport { $client } from \"./primitives/$client.ts\";\nimport { $remote } from \"./primitives/$remote.ts\";\nimport { LinkProvider } from \"./providers/LinkProvider.ts\";\nimport { RemotePrimitiveProvider } from \"./providers/RemotePrimitiveProvider.ts\";\nimport { ServerLinksProvider } from \"./providers/ServerLinksProvider.ts\";\nimport type { ApiLinksResponse } from \"./schemas/apiLinksResponseSchema.ts\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport * from \"./primitives/$client.ts\";\nexport * from \"./primitives/$remote.ts\";\nexport * from \"./providers/LinkProvider.ts\";\nexport * from \"./providers/RemotePrimitiveProvider.ts\";\nexport * from \"./providers/ServerLinksProvider.ts\";\nexport * from \"./schemas/apiLinksResponseSchema.ts\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\ndeclare module \"alepha\" {\n interface State {\n /**\n * API links attached to the server request state.\n *\n * @see {@link ApiLinksResponse}\n * @internal\n */\n \"alepha.server.request.apiLinks\"?: ApiLinksResponse;\n }\n}\n\n// ---------------------------------------------------------------------------------------------------------------------\n\n/**\n * | type | quality | stability |\n * |------|---------|-----------|\n * | backend | standard | stable |\n *\n * Type-safe API client with request deduplication.\n *\n * **Features:**\n * - Virtual HTTP client for type-safe API calls\n * - Remote action definitions\n * - Type inference from action schemas\n * - Request deduplication\n * - Automatic error handling\n *\n * @module alepha.server.links\n */\nexport const AlephaServerLinks = $module({\n name: \"alepha.server.links\",\n atoms: [apiLinksAtom],\n primitives: [$remote, $client],\n services: [\n AlephaServer,\n ServerLinksProvider,\n RemotePrimitiveProvider,\n LinkProvider,\n ],\n});\n"],"mappings":"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"}
|