alepha 0.22.0 → 0.24.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/assets/swagger-ui/swagger-ui-bundle.js +1 -1
- package/assets/swagger-ui/swagger-ui-standalone-preset.js +1 -1
- package/dist/api/audits/index.browser.js +49 -52
- package/dist/api/audits/index.browser.js.map +1 -1
- package/dist/api/audits/index.d.ts +629 -565
- package/dist/api/audits/index.d.ts.map +1 -1
- package/dist/api/audits/index.js +206 -98
- package/dist/api/audits/index.js.map +1 -1
- package/dist/api/files/index.browser.js +44 -44
- package/dist/api/files/index.browser.js.map +1 -1
- package/dist/api/files/index.d.ts +292 -293
- package/dist/api/files/index.d.ts.map +1 -1
- package/dist/api/files/index.js +61 -64
- package/dist/api/files/index.js.map +1 -1
- package/dist/api/jobs/index.browser.js +61 -59
- package/dist/api/jobs/index.browser.js.map +1 -1
- package/dist/api/jobs/index.d.ts +546 -347
- package/dist/api/jobs/index.d.ts.map +1 -1
- package/dist/api/jobs/index.js +165 -86
- package/dist/api/jobs/index.js.map +1 -1
- package/dist/api/keys/index.d.ts +242 -230
- package/dist/api/keys/index.d.ts.map +1 -1
- package/dist/api/keys/index.js +63 -66
- package/dist/api/keys/index.js.map +1 -1
- package/dist/api/notifications/index.browser.js +40 -38
- package/dist/api/notifications/index.browser.js.map +1 -1
- package/dist/api/notifications/index.d.ts +339 -209
- package/dist/api/notifications/index.d.ts.map +1 -1
- package/dist/api/notifications/index.js +108 -60
- package/dist/api/notifications/index.js.map +1 -1
- package/dist/api/oauth/index.d.ts +190 -78
- package/dist/api/oauth/index.d.ts.map +1 -1
- package/dist/api/oauth/index.js +290 -68
- package/dist/api/oauth/index.js.map +1 -1
- package/dist/api/organizations/index.browser.js +14 -14
- package/dist/api/organizations/index.browser.js.map +1 -1
- package/dist/api/organizations/index.d.ts +158 -134
- package/dist/api/organizations/index.d.ts.map +1 -1
- package/dist/api/organizations/index.js +18 -18
- package/dist/api/organizations/index.js.map +1 -1
- package/dist/api/parameters/index.browser.js +74 -53
- package/dist/api/parameters/index.browser.js.map +1 -1
- package/dist/api/parameters/index.d.ts +607 -513
- package/dist/api/parameters/index.d.ts.map +1 -1
- package/dist/api/parameters/index.js +205 -127
- package/dist/api/parameters/index.js.map +1 -1
- package/dist/api/payments/index.d.ts +652 -449
- package/dist/api/payments/index.d.ts.map +1 -1
- package/dist/api/payments/index.js +159 -93
- package/dist/api/payments/index.js.map +1 -1
- package/dist/api/subscriptions/index.d.ts +1032 -762
- package/dist/api/subscriptions/index.d.ts.map +1 -1
- package/dist/api/subscriptions/index.js +166 -185
- package/dist/api/subscriptions/index.js.map +1 -1
- package/dist/api/users/index.browser.js +48 -71
- package/dist/api/users/index.browser.js.map +1 -1
- package/dist/api/users/index.d.ts +1297 -1109
- package/dist/api/users/index.d.ts.map +1 -1
- package/dist/api/users/index.js +454 -369
- package/dist/api/users/index.js.map +1 -1
- package/dist/api/verifications/index.browser.js +17 -17
- package/dist/api/verifications/index.browser.js.map +1 -1
- package/dist/api/verifications/index.d.ts +169 -157
- package/dist/api/verifications/index.d.ts.map +1 -1
- package/dist/api/verifications/index.js +47 -95
- package/dist/api/verifications/index.js.map +1 -1
- package/dist/background/index.d.ts +0 -1
- package/dist/background/index.d.ts.map +1 -1
- package/dist/background/index.js.map +1 -1
- package/dist/background/index.workerd.js.map +1 -1
- package/dist/batch/index.d.ts +0 -1
- package/dist/batch/index.d.ts.map +1 -1
- package/dist/batch/index.js.map +1 -1
- package/dist/bin/index.js +1 -1
- package/dist/bin/index.js.map +1 -1
- package/dist/bucket/index.d.ts +27 -32
- package/dist/bucket/index.d.ts.map +1 -1
- package/dist/bucket/index.js +56 -26
- package/dist/bucket/index.js.map +1 -1
- package/dist/bucket/index.workerd.js +52 -24
- package/dist/bucket/index.workerd.js.map +1 -1
- package/dist/cache/core/index.d.ts +4 -6
- package/dist/cache/core/index.d.ts.map +1 -1
- package/dist/cache/core/index.js +4 -10
- package/dist/cache/core/index.js.map +1 -1
- package/dist/cache/core/index.workerd.js +4 -10
- package/dist/cache/core/index.workerd.js.map +1 -1
- package/dist/cache/database/index.d.ts +22 -23
- package/dist/cache/database/index.d.ts.map +1 -1
- package/dist/cache/database/index.js +11 -20
- package/dist/cache/database/index.js.map +1 -1
- package/dist/cache/redis/index.d.ts +3 -4
- package/dist/cache/redis/index.d.ts.map +1 -1
- package/dist/cache/redis/index.js +2 -2
- package/dist/cache/redis/index.js.map +1 -1
- package/dist/captcha/index.d.ts.map +1 -1
- package/dist/captcha/index.js +4 -4
- package/dist/captcha/index.js.map +1 -1
- package/dist/cli/config/index.d.ts +0 -1
- package/dist/cli/config/index.d.ts.map +1 -1
- package/dist/cli/core/index.d.ts +244 -475
- package/dist/cli/core/index.d.ts.map +1 -1
- package/dist/cli/core/index.js +308 -383
- package/dist/cli/core/index.js.map +1 -1
- package/dist/cli/devtools/index.d.ts +3 -9
- package/dist/cli/devtools/index.d.ts.map +1 -1
- package/dist/cli/devtools/index.js +3 -3
- package/dist/cli/devtools/index.js.map +1 -1
- package/dist/cli/i18n/index.d.ts +20 -39
- package/dist/cli/i18n/index.d.ts.map +1 -1
- package/dist/cli/i18n/index.js +51 -17
- package/dist/cli/i18n/index.js.map +1 -1
- package/dist/cli/platform/index.d.ts +89 -82
- package/dist/cli/platform/index.d.ts.map +1 -1
- package/dist/cli/platform/index.js +27 -33
- package/dist/cli/platform/index.js.map +1 -1
- package/dist/cli/platform-lib/index.d.ts +383 -410
- package/dist/cli/platform-lib/index.d.ts.map +1 -1
- package/dist/cli/platform-lib/index.js +300 -258
- package/dist/cli/platform-lib/index.js.map +1 -1
- package/dist/cli/vendor/index.d.ts +17 -41
- package/dist/cli/vendor/index.d.ts.map +1 -1
- package/dist/cli/vendor/index.js +10 -13
- package/dist/cli/vendor/index.js.map +1 -1
- package/dist/command/index.d.ts +82 -194
- package/dist/command/index.d.ts.map +1 -1
- package/dist/command/index.js +160 -563
- package/dist/command/index.js.map +1 -1
- package/dist/core/index.browser.js +845 -859
- package/dist/core/index.browser.js.map +1 -1
- package/dist/core/index.d.ts +787 -527
- package/dist/core/index.d.ts.map +1 -1
- package/dist/core/index.js +845 -859
- package/dist/core/index.js.map +1 -1
- package/dist/core/index.native.js +845 -859
- package/dist/core/index.native.js.map +1 -1
- package/dist/core/index.workerd.js +845 -859
- package/dist/core/index.workerd.js.map +1 -1
- package/dist/crypto/index.browser.js +1 -1
- package/dist/crypto/index.browser.js.map +1 -1
- package/dist/crypto/index.d.ts +3 -4
- package/dist/crypto/index.d.ts.map +1 -1
- package/dist/crypto/index.js +6 -3
- package/dist/crypto/index.js.map +1 -1
- package/dist/datetime/index.d.ts +12 -2
- package/dist/datetime/index.d.ts.map +1 -1
- package/dist/datetime/index.js +6 -1
- package/dist/datetime/index.js.map +1 -1
- package/dist/email/brevo/index.d.ts +0 -1
- package/dist/email/brevo/index.d.ts.map +1 -1
- package/dist/email/brevo/index.js +4 -4
- package/dist/email/brevo/index.js.map +1 -1
- package/dist/email/cloudflare/index.d.ts +0 -1
- package/dist/email/cloudflare/index.d.ts.map +1 -1
- package/dist/email/cloudflare/index.js +2 -2
- package/dist/email/cloudflare/index.js.map +1 -1
- package/dist/email/core/index.d.ts +15 -7
- package/dist/email/core/index.d.ts.map +1 -1
- package/dist/email/core/index.js +14 -5
- package/dist/email/core/index.js.map +1 -1
- package/dist/email/core/index.workerd.js +5 -1
- package/dist/email/core/index.workerd.js.map +1 -1
- package/dist/email/smtp/index.d.ts +8 -9
- package/dist/email/smtp/index.d.ts.map +1 -1
- package/dist/email/smtp/index.js +702 -479
- package/dist/email/smtp/index.js.map +1 -1
- package/dist/fake/index.d.ts +1580 -1238
- package/dist/fake/index.d.ts.map +1 -1
- package/dist/fake/index.js +11728 -32131
- package/dist/fake/index.js.map +1 -1
- package/dist/lock/core/index.d.ts +13 -9
- package/dist/lock/core/index.d.ts.map +1 -1
- package/dist/lock/core/index.js +5 -5
- package/dist/lock/core/index.js.map +1 -1
- package/dist/lock/redis/index.d.ts +0 -1
- package/dist/lock/redis/index.d.ts.map +1 -1
- package/dist/lock/redis/index.js.map +1 -1
- package/dist/logger/index.d.ts +81 -69
- package/dist/logger/index.d.ts.map +1 -1
- package/dist/logger/index.js +123 -56
- package/dist/logger/index.js.map +1 -1
- package/dist/mcp/index.d.ts +36 -90
- package/dist/mcp/index.d.ts.map +1 -1
- package/dist/mcp/index.js +65 -70
- package/dist/mcp/index.js.map +1 -1
- package/dist/orm/core/index.browser.js +54 -40
- package/dist/orm/core/index.browser.js.map +1 -1
- package/dist/orm/core/index.bun.js +140 -106
- package/dist/orm/core/index.bun.js.map +1 -1
- package/dist/orm/core/index.d.ts +145 -150
- package/dist/orm/core/index.d.ts.map +1 -1
- package/dist/orm/core/index.js +144 -109
- package/dist/orm/core/index.js.map +1 -1
- package/dist/orm/postgres/index.bun.js +64 -54
- package/dist/orm/postgres/index.bun.js.map +1 -1
- package/dist/orm/postgres/index.d.ts +36 -76
- package/dist/orm/postgres/index.d.ts.map +1 -1
- package/dist/orm/postgres/index.js +67 -57
- package/dist/orm/postgres/index.js.map +1 -1
- package/dist/queue/core/index.d.ts +62 -75
- package/dist/queue/core/index.d.ts.map +1 -1
- package/dist/queue/core/index.js +48 -71
- package/dist/queue/core/index.js.map +1 -1
- package/dist/queue/core/index.workerd.js +48 -71
- package/dist/queue/core/index.workerd.js.map +1 -1
- package/dist/queue/redis/index.d.ts +3 -4
- package/dist/queue/redis/index.d.ts.map +1 -1
- package/dist/queue/redis/index.js +2 -2
- package/dist/queue/redis/index.js.map +1 -1
- package/dist/react/auth/index.browser.js.map +1 -1
- package/dist/react/auth/index.d.ts +7 -4
- package/dist/react/auth/index.d.ts.map +1 -1
- package/dist/react/auth/index.js.map +1 -1
- package/dist/react/core/index.d.ts +87 -5
- package/dist/react/core/index.d.ts.map +1 -1
- package/dist/react/core/index.js +170 -23
- package/dist/react/core/index.js.map +1 -1
- package/dist/react/form/index.d.ts +4 -6
- package/dist/react/form/index.d.ts.map +1 -1
- package/dist/react/form/index.js +107 -71
- package/dist/react/form/index.js.map +1 -1
- package/dist/react/head/index.browser.js.map +1 -1
- package/dist/react/head/index.d.ts +0 -1
- package/dist/react/head/index.d.ts.map +1 -1
- package/dist/react/head/index.js.map +1 -1
- package/dist/react/i18n/index.d.ts +47 -5
- package/dist/react/i18n/index.d.ts.map +1 -1
- package/dist/react/i18n/index.js +116 -12
- package/dist/react/i18n/index.js.map +1 -1
- package/dist/react/intro/index.d.ts +1 -4
- package/dist/react/intro/index.d.ts.map +1 -1
- package/dist/react/intro/index.js.map +1 -1
- package/dist/react/router/index.browser.js +188 -29
- package/dist/react/router/index.browser.js.map +1 -1
- package/dist/react/router/index.d.ts +480 -372
- package/dist/react/router/index.d.ts.map +1 -1
- package/dist/react/router/index.js +261 -47
- package/dist/react/router/index.js.map +1 -1
- package/dist/react/sitemap/index.browser.js +35 -0
- package/dist/react/sitemap/index.browser.js.map +1 -0
- package/dist/react/sitemap/index.d.ts +91 -0
- package/dist/react/sitemap/index.d.ts.map +1 -0
- package/dist/react/sitemap/index.js +131 -0
- package/dist/react/sitemap/index.js.map +1 -0
- package/dist/react/testing/index.d.ts +4 -5
- package/dist/react/testing/index.d.ts.map +1 -1
- package/dist/react/testing/index.js +41 -15
- package/dist/react/testing/index.js.map +1 -1
- package/dist/react/testing/{chunk-BpyX8vjI.js → rolldown-runtime-Bl3dcgcQ.js} +1 -1
- package/dist/react/ui/index.d.ts +35 -34
- package/dist/react/ui/index.d.ts.map +1 -1
- package/dist/react/ui/index.js +12 -17
- package/dist/react/ui/index.js.map +1 -1
- package/dist/redis/index.bun.js +5 -5
- package/dist/redis/index.bun.js.map +1 -1
- package/dist/redis/index.d.ts +6 -7
- package/dist/redis/index.d.ts.map +1 -1
- package/dist/redis/index.js +6 -6
- package/dist/redis/index.js.map +1 -1
- package/dist/retry/index.d.ts +0 -1
- package/dist/retry/index.d.ts.map +1 -1
- package/dist/retry/index.js.map +1 -1
- package/dist/router/index.d.ts.map +1 -1
- package/dist/router/index.js.map +1 -1
- package/dist/scheduler/index.d.ts +4 -12
- package/dist/scheduler/index.d.ts.map +1 -1
- package/dist/scheduler/index.js +9 -4
- package/dist/scheduler/index.js.map +1 -1
- package/dist/scheduler/index.workerd.js +9 -4
- package/dist/scheduler/index.workerd.js.map +1 -1
- package/dist/security/index.browser.js +30 -28
- package/dist/security/index.browser.js.map +1 -1
- package/dist/security/index.d.ts +113 -44
- package/dist/security/index.d.ts.map +1 -1
- package/dist/security/index.js +452 -37
- package/dist/security/index.js.map +1 -1
- package/dist/server/auth/index.browser.js +19 -19
- package/dist/server/auth/index.browser.js.map +1 -1
- package/dist/server/auth/index.d.ts +215 -187
- package/dist/server/auth/index.d.ts.map +1 -1
- package/dist/server/auth/index.js +1145 -86
- package/dist/server/auth/index.js.map +1 -1
- package/dist/server/cookies/index.browser.js +103 -5
- package/dist/server/cookies/index.browser.js.map +1 -1
- package/dist/server/cookies/index.d.ts +86 -4
- package/dist/server/cookies/index.d.ts.map +1 -1
- package/dist/server/cookies/index.js +121 -8
- package/dist/server/cookies/index.js.map +1 -1
- package/dist/server/core/index.browser.js +22 -27
- package/dist/server/core/index.browser.js.map +1 -1
- package/dist/server/core/index.d.ts +134 -85
- package/dist/server/core/index.d.ts.map +1 -1
- package/dist/server/core/index.js +130 -118
- package/dist/server/core/index.js.map +1 -1
- package/dist/server/cors/index.d.ts +9 -10
- package/dist/server/cors/index.d.ts.map +1 -1
- package/dist/server/cors/index.js +14 -26
- package/dist/server/cors/index.js.map +1 -1
- package/dist/server/etag/index.d.ts +2 -3
- package/dist/server/etag/index.d.ts.map +1 -1
- package/dist/server/etag/index.js.map +1 -1
- package/dist/server/health/index.d.ts +18 -19
- package/dist/server/health/index.d.ts.map +1 -1
- package/dist/server/health/index.js +6 -6
- package/dist/server/health/index.js.map +1 -1
- package/dist/server/links/index.browser.js +15 -18
- package/dist/server/links/index.browser.js.map +1 -1
- package/dist/server/links/index.d.ts +66 -67
- package/dist/server/links/index.d.ts.map +1 -1
- package/dist/server/links/index.js +29 -32
- package/dist/server/links/index.js.map +1 -1
- package/dist/server/metrics/index.d.ts +0 -1
- 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/proxy/index.d.ts +0 -1
- package/dist/server/proxy/index.d.ts.map +1 -1
- package/dist/server/proxy/index.js.map +1 -1
- package/dist/server/rate-limit/index.d.ts +8 -9
- package/dist/server/rate-limit/index.d.ts.map +1 -1
- package/dist/server/rate-limit/index.js +6 -12
- package/dist/server/rate-limit/index.js.map +1 -1
- package/dist/server/static/index.d.ts +0 -1
- package/dist/server/static/index.d.ts.map +1 -1
- package/dist/server/static/index.js.map +1 -1
- package/dist/server/swagger/index.d.ts +4 -4
- package/dist/server/swagger/index.d.ts.map +1 -1
- package/dist/server/swagger/index.js +37 -25
- package/dist/server/swagger/index.js.map +1 -1
- package/dist/sms/index.d.ts +0 -1
- package/dist/sms/index.d.ts.map +1 -1
- package/dist/sms/index.js.map +1 -1
- package/dist/system/index.browser.js.map +1 -1
- package/dist/system/index.d.ts +0 -1
- package/dist/system/index.d.ts.map +1 -1
- package/dist/system/index.js +2 -1
- package/dist/system/index.js.map +1 -1
- package/dist/system/index.workerd.js.map +1 -1
- package/dist/topic/core/index.d.ts +19 -20
- package/dist/topic/core/index.d.ts.map +1 -1
- package/dist/topic/core/index.js +8 -8
- package/dist/topic/core/index.js.map +1 -1
- package/dist/topic/redis/index.d.ts +3 -4
- package/dist/topic/redis/index.d.ts.map +1 -1
- package/dist/topic/redis/index.js +2 -2
- package/dist/topic/redis/index.js.map +1 -1
- package/package.json +36 -36
- package/src/api/audits/__tests__/AuditJobs.spec.ts +83 -0
- package/src/api/audits/__tests__/AuditService.spec.ts +139 -1
- package/src/api/audits/controllers/AdminAuditController.ts +41 -41
- package/src/api/audits/entities/audits.ts +22 -22
- package/src/api/audits/index.ts +6 -1
- package/src/api/audits/jobs/AuditJobs.ts +29 -0
- package/src/api/audits/parameters/AuditParameters.ts +51 -0
- package/src/api/audits/primitives/$audit.ts +24 -0
- package/src/api/audits/schemas/auditQuerySchema.ts +13 -13
- package/src/api/audits/schemas/createAuditSchema.ts +18 -18
- package/src/api/audits/services/AuditService.ts +79 -7
- package/src/api/files/__tests__/FileController.spec.ts +5 -5
- package/src/api/files/__tests__/FileJobs.spec.ts +6 -0
- package/src/api/files/controllers/FileController.ts +25 -25
- package/src/api/files/entities/files.ts +15 -15
- package/src/api/files/schemas/fileCreatorSummarySchema.ts +7 -7
- package/src/api/files/schemas/fileQuerySchema.ts +9 -9
- package/src/api/files/schemas/fileResourceSchema.ts +7 -9
- package/src/api/files/schemas/storageStatsSchema.ts +13 -13
- package/src/api/jobs/__tests__/$job-hardening.spec.ts +289 -0
- package/src/api/jobs/__tests__/$job.spec.ts +18 -18
- package/src/api/jobs/controllers/AdminJobController.ts +8 -8
- package/src/api/jobs/entities/jobExecutionEntity.ts +29 -19
- package/src/api/jobs/providers/JobProvider.ts +128 -12
- package/src/api/jobs/providers/JobQueueProvider.ts +2 -2
- package/src/api/jobs/schemas/jobConfigAtom.ts +22 -22
- package/src/api/jobs/schemas/jobExecutionQuerySchema.ts +6 -6
- package/src/api/jobs/schemas/jobExecutionResourceSchema.ts +11 -15
- package/src/api/jobs/schemas/jobRegistrationSchema.ts +20 -19
- package/src/api/jobs/schemas/triggerJobSchema.ts +3 -3
- package/src/api/jobs/services/JobService.ts +6 -6
- package/src/api/keys/__tests__/ApiKeyService.spec.ts +2 -2
- package/src/api/keys/controllers/AdminApiKeyController.ts +10 -10
- package/src/api/keys/entities/apiKeyEntity.ts +15 -15
- package/src/api/keys/schemas/adminApiKeyQuerySchema.ts +4 -4
- package/src/api/keys/schemas/adminApiKeyResourceSchema.ts +15 -15
- package/src/api/keys/schemas/createApiKeyBodySchema.ts +5 -5
- package/src/api/keys/schemas/createApiKeyResponseSchema.ts +9 -9
- package/src/api/keys/schemas/listApiKeyResponseSchema.ts +13 -13
- package/src/api/keys/schemas/revokeApiKeyParamsSchema.ts +3 -3
- package/src/api/keys/schemas/revokeApiKeyResponseSchema.ts +3 -3
- package/src/api/notifications/__tests__/AlephaApiNotifications.spec.ts +2 -2
- package/src/api/notifications/controllers/AdminNotificationController.ts +38 -14
- package/src/api/notifications/jobs/NotificationJobs.ts +7 -6
- package/src/api/notifications/primitives/$notification.ts +43 -4
- package/src/api/notifications/schemas/notificationContactPreferencesSchema.ts +4 -4
- package/src/api/notifications/schemas/notificationContactSchema.ts +7 -7
- package/src/api/notifications/schemas/notificationDetailResourceSchema.ts +9 -11
- package/src/api/notifications/schemas/notificationPayloadSchema.ts +11 -9
- package/src/api/notifications/schemas/notificationQuerySchema.ts +6 -6
- package/src/api/notifications/schemas/notificationResourceSchema.ts +14 -14
- package/src/api/notifications/services/NotificationSenderService.ts +38 -2
- package/src/api/oauth/__tests__/OAuthClientService.spec.ts +42 -0
- package/src/api/oauth/__tests__/OAuthController.spec.ts +128 -0
- package/src/api/oauth/controllers/OAuthController.ts +132 -12
- package/src/api/oauth/entities/oauthClientEntity.ts +29 -12
- package/src/api/oauth/helpers/oauthMetadata.ts +1 -0
- package/src/api/oauth/helpers/oidcMetadata.ts +19 -0
- package/src/api/oauth/index.ts +1 -0
- package/src/api/oauth/schemas/authorizeDecisionBodySchema.ts +12 -11
- package/src/api/oauth/schemas/authorizeQuerySchema.ts +12 -10
- package/src/api/oauth/schemas/registerClientBodySchema.ts +10 -11
- package/src/api/oauth/schemas/tokenRequestBodySchema.ts +9 -8
- package/src/api/oauth/services/OAuthClientService.ts +228 -15
- package/src/api/organizations/controllers/AdminOrganizationController.ts +8 -8
- package/src/api/organizations/entities/organizations.ts +6 -6
- package/src/api/organizations/schemas/createOrganizationSchema.ts +5 -5
- package/src/api/organizations/schemas/organizationQuerySchema.ts +4 -4
- package/src/api/organizations/schemas/updateOrganizationSchema.ts +1 -2
- package/src/api/parameters/__tests__/$parameter.spec.ts +229 -50
- package/src/api/parameters/controllers/AdminParameterController.ts +9 -12
- package/src/api/parameters/entities/parameters.ts +29 -16
- package/src/api/parameters/primitives/$parameter.ts +3 -3
- package/src/api/parameters/schemas/activateParameterBodySchema.ts +4 -4
- package/src/api/parameters/schemas/createParameterVersionBodySchema.ts +15 -15
- package/src/api/parameters/schemas/parameterCreatorSummarySchema.ts +7 -7
- package/src/api/parameters/schemas/parameterCurrentResponseSchema.ts +7 -7
- package/src/api/parameters/schemas/parameterHistoryResponseSchema.ts +3 -3
- package/src/api/parameters/schemas/parameterNameParamSchema.ts +2 -3
- package/src/api/parameters/schemas/parameterNamesResponseSchema.ts +3 -3
- package/src/api/parameters/schemas/parameterResponseSchema.ts +3 -3
- package/src/api/parameters/schemas/parameterStatusSchema.ts +2 -2
- package/src/api/parameters/schemas/parameterTreeNodeSchema.ts +6 -6
- package/src/api/parameters/schemas/parameterVersionParamSchema.ts +5 -6
- package/src/api/parameters/schemas/parameterVersionResponseSchema.ts +3 -3
- package/src/api/parameters/schemas/rollbackParameterBodySchema.ts +6 -9
- package/src/api/parameters/services/ParameterProvider.ts +152 -65
- package/src/api/payments/controllers/AdminPaymentController.ts +7 -7
- package/src/api/payments/controllers/MockCheckoutController.ts +79 -28
- package/src/api/payments/controllers/PaymentController.ts +4 -4
- package/src/api/payments/entities/paymentIntents.ts +11 -11
- package/src/api/payments/entities/paymentMethods.ts +11 -11
- package/src/api/payments/entities/refunds.ts +9 -9
- package/src/api/payments/providers/PaymentProvider.ts +18 -0
- package/src/api/payments/schemas/intentSchemas.ts +25 -25
- package/src/api/payments/schemas/paymentMethodSchemas.ts +3 -3
- package/src/api/payments/services/PaymentService.ts +60 -3
- package/src/api/subscriptions/__tests__/SubscriptionService.spec.ts +14 -0
- package/src/api/subscriptions/controllers/AdminSubscriptionController.ts +11 -11
- package/src/api/subscriptions/controllers/SubscriptionController.ts +5 -4
- package/src/api/subscriptions/entities/subscriptionEvents.ts +15 -15
- package/src/api/subscriptions/entities/subscriptions.ts +22 -22
- package/src/api/subscriptions/notifications/SubscriptionNotifications.ts +22 -22
- package/src/api/subscriptions/schemas/cancelSubscriptionSchema.ts +4 -4
- package/src/api/subscriptions/schemas/changePlanSchema.ts +5 -5
- package/src/api/subscriptions/schemas/createSubscriptionSchema.ts +14 -7
- package/src/api/subscriptions/schemas/entitlementsSchema.ts +10 -10
- package/src/api/subscriptions/schemas/mrrSchema.ts +9 -9
- package/src/api/subscriptions/schemas/planDefinitionSchema.ts +21 -21
- package/src/api/subscriptions/schemas/planResourceSchema.ts +19 -19
- package/src/api/subscriptions/schemas/subscriptionQuerySchema.ts +8 -8
- package/src/api/subscriptions/schemas/subscriptionSettingsSchema.ts +7 -7
- package/src/api/subscriptions/schemas/subscriptionStatsSchema.ts +17 -17
- package/src/api/subscriptions/services/SubscriptionConfig.ts +2 -2
- package/src/api/users/__tests__/ApiKeys-integration.spec.ts +11 -11
- package/src/api/users/atoms/realmAuthSettingsAtom.ts +111 -119
- package/src/api/users/controllers/AdminIdentityController.ts +29 -19
- package/src/api/users/controllers/AdminSessionController.ts +37 -27
- package/src/api/users/controllers/AdminUserController.ts +75 -53
- package/src/api/users/controllers/MySessionController.ts +125 -0
- package/src/api/users/controllers/RealmController.ts +9 -9
- package/src/api/users/controllers/UserController.ts +55 -55
- package/src/api/users/entities/identities.ts +8 -8
- package/src/api/users/entities/sessions.ts +17 -17
- package/src/api/users/entities/users.ts +17 -17
- package/src/api/users/index.ts +3 -0
- package/src/api/users/notifications/UserNotifications.ts +76 -24
- package/src/api/users/primitives/$realm.ts +12 -0
- package/src/api/users/schemas/completePasswordResetRequestSchema.ts +8 -12
- package/src/api/users/schemas/completeRegistrationRequestSchema.ts +17 -20
- package/src/api/users/schemas/createUserSchema.ts +2 -2
- package/src/api/users/schemas/identityQuerySchema.ts +4 -4
- package/src/api/users/schemas/identityResourceSchema.ts +3 -2
- package/src/api/users/schemas/loginSchema.ts +4 -4
- package/src/api/users/schemas/passwordResetIntentResponseSchema.ts +6 -8
- package/src/api/users/schemas/realmConfigSchema.ts +10 -10
- package/src/api/users/schemas/registerQuerySchema.ts +6 -6
- package/src/api/users/schemas/registerRequestSchema.ts +21 -43
- package/src/api/users/schemas/registerResponseSchema.ts +2 -2
- package/src/api/users/schemas/registerSchema.ts +13 -31
- package/src/api/users/schemas/registrationIntentResponseSchema.ts +19 -23
- package/src/api/users/schemas/resetPasswordSchema.ts +10 -17
- package/src/api/users/schemas/sessionQuerySchema.ts +3 -3
- package/src/api/users/schemas/sessionResourceSchema.ts +25 -25
- package/src/api/users/schemas/updateUserSchema.ts +3 -4
- package/src/api/users/schemas/userQuerySchema.ts +7 -7
- package/src/api/users/services/RegistrationService.ts +25 -11
- package/src/api/users/services/UserService.ts +12 -6
- package/src/api/verifications/__tests__/CodeVerification.spec.ts +63 -101
- package/src/api/verifications/__tests__/LinkVerification.spec.ts +23 -92
- package/src/api/verifications/controllers/VerificationController.ts +13 -27
- package/src/api/verifications/entities/verifications.ts +13 -14
- package/src/api/verifications/schemas/requestVerificationCodeResponseSchema.ts +17 -14
- package/src/api/verifications/schemas/validateVerificationCodeResponseSchema.ts +9 -10
- package/src/api/verifications/schemas/verificationSettingsSchema.ts +65 -66
- package/src/api/verifications/schemas/verificationTypeEnumSchema.ts +2 -2
- package/src/api/verifications/services/VerificationService.ts +12 -0
- package/src/background/__tests__/BackgroundTaskProvider.spec.ts +36 -0
- package/src/batch/__tests__/$batch.spec.ts +22 -22
- package/src/batch/__tests__/startup-buffering.spec.ts +9 -9
- package/src/bin/index.ts +1 -1
- package/src/bucket/__tests__/TenantScopedBucket.spec.ts +60 -0
- package/src/bucket/providers/LocalFileStorageProvider.ts +14 -6
- package/src/bucket/providers/MemoryFileStorageProvider.ts +18 -7
- package/src/bucket/providers/R2FileStorageProvider.ts +14 -6
- package/src/bucket/providers/S3FileStorageProvider.ts +31 -15
- package/src/cache/core/primitives/$cache.ts +7 -10
- package/src/cache/database/entities/cacheEntries.ts +16 -27
- package/src/cache/database/providers/DatabaseCacheProvider.ts +15 -13
- package/src/cache/redis/providers/RedisCacheProvider.ts +6 -6
- package/src/captcha/providers/TurnstileCaptchaProvider.ts +4 -4
- package/src/cli/core/__tests__/BuildCloudflareTask.spec.ts +178 -0
- package/src/cli/core/__tests__/BuildDockerTask.spec.ts +0 -2
- package/src/cli/core/__tests__/DbCommand.spec.ts +108 -0
- package/src/cli/core/atoms/appEntryOptions.ts +5 -5
- package/src/cli/core/atoms/buildOptions.ts +76 -88
- package/src/cli/core/atoms/changelogOptions.ts +3 -3
- package/src/cli/core/atoms/devOptions.ts +3 -3
- package/src/cli/core/commands/build.ts +36 -49
- package/src/cli/core/commands/db.ts +171 -83
- package/src/cli/core/commands/dev.ts +8 -8
- package/src/cli/core/commands/gen/changelog.ts +12 -14
- package/src/cli/core/commands/gen/env.ts +6 -6
- package/src/cli/core/commands/gen/openapi.ts +6 -6
- package/src/cli/core/commands/init.ts +28 -33
- package/src/cli/core/commands/pack.ts +10 -10
- package/src/cli/core/commands/root.ts +7 -8
- package/src/cli/core/commands/test.ts +17 -20
- package/src/cli/core/index.ts +0 -3
- package/src/cli/core/tasks/BuildCloudflareTask.ts +138 -245
- package/src/cli/core/tasks/BuildPrerenderTask.ts +44 -7
- package/src/cli/core/templates/apiHelloResponseSchemaTs.ts +4 -4
- package/src/cli/devtools/atoms/devtoolsOptions.ts +6 -6
- package/src/cli/i18n/__tests__/I18nCheckService.spec.ts +48 -0
- package/src/cli/i18n/atoms/i18nOptions.ts +8 -8
- package/src/cli/i18n/services/I18nCheckService.ts +65 -11
- package/src/cli/platform/commands/SecretsCommand.ts +24 -25
- package/src/cli/platform/commands/platform.ts +40 -46
- package/src/cli/platform/index.ts +1 -1
- package/src/cli/platform-lib/__tests__/CloudflareAdapter.spec.ts +0 -4
- package/src/cli/platform-lib/__tests__/PlatformInspector.spec.ts +10 -3
- package/src/cli/platform-lib/__tests__/VercelAdapter.spec.ts +0 -2
- package/src/cli/platform-lib/adapters/CloudflareAdapter.ts +141 -50
- package/src/cli/platform-lib/adapters/VercelAdapter.ts +6 -11
- package/src/cli/platform-lib/atoms/platformOptions.ts +46 -25
- package/src/cli/platform-lib/schemas/cloudflare.ts +87 -87
- package/src/cli/platform-lib/schemas/platform.ts +51 -51
- package/src/cli/platform-lib/schemas/vercel.ts +28 -28
- package/src/cli/platform-lib/services/CloudflareApi.ts +5 -5
- package/src/cli/platform-lib/services/VercelApi.ts +4 -4
- package/src/cli/platform-lib/services/VercelCli.ts +6 -15
- package/src/cli/platform-lib/services/WranglerApi.ts +6 -15
- package/src/cli/vendor/atoms/vendorOptions.ts +9 -9
- package/src/cli/vendor/commands/VendorCommand.ts +11 -12
- package/src/command/__tests__/$command.spec.ts +82 -83
- package/src/command/__tests__/Asker.spec.ts +6 -13
- package/src/command/__tests__/CliProvider.spec.ts +136 -149
- package/src/command/__tests__/Runner.spec.ts +86 -110
- package/src/command/helpers/Asker.ts +23 -58
- package/src/command/helpers/Runner.ts +34 -110
- package/src/command/index.ts +4 -9
- package/src/command/primitives/$command.ts +10 -10
- package/src/command/providers/CliProvider.ts +183 -161
- package/src/core/Alepha.ts +62 -13
- package/src/core/__tests__/$atom.spec.ts +52 -3
- package/src/core/__tests__/$computed.spec.ts +129 -0
- package/src/core/__tests__/$env.spec.ts +13 -13
- package/src/core/__tests__/$inject.spec.ts +4 -4
- package/src/core/__tests__/$module.spec.ts +1 -1
- package/src/core/__tests__/Alepha-graph.spec.ts +1 -1
- package/src/core/__tests__/Alepha-parseEnv.spec.ts +32 -32
- package/src/core/__tests__/Alepha-start.spec.ts +3 -3
- package/src/core/__tests__/CodecManager.spec.ts +113 -94
- package/src/core/__tests__/KeylessJsonSchemaCodec.spec.ts +138 -138
- package/src/core/__tests__/SchemaValidator.spec.ts +43 -44
- package/src/core/__tests__/StateManager.persist.browser.spec.ts +87 -0
- package/src/core/__tests__/StateManager.spec.ts +423 -0
- package/src/core/__tests__/TypeBox-usecases.spec.ts +8 -8
- package/src/core/__tests__/TypeProvider.spec.ts +314 -309
- package/src/core/__tests__/dump.spec.ts +6 -6
- package/src/core/__tests__/pagination.spec.ts +4 -4
- package/src/core/__tests__/scoped-after-start.spec.ts +43 -0
- package/src/core/errors/TypeBoxError.ts +10 -4
- package/src/core/helpers/FileLike.ts +5 -5
- package/src/core/helpers/coerceStrings.ts +70 -0
- package/src/core/helpers/jsonSchemaToZod.ts +112 -0
- package/src/core/index.shared.ts +4 -1
- package/src/core/primitives/$atom.ts +95 -27
- package/src/core/primitives/$computed.ts +148 -0
- package/src/core/primitives/$env.ts +5 -5
- package/src/core/primitives/$state.ts +1 -1
- package/src/core/providers/AlsProvider.ts +26 -0
- package/src/core/providers/CodecManager.ts +1 -2
- package/src/core/providers/JsonSchemaCodec.ts +1 -2
- package/src/core/providers/KeylessJsonSchemaCodec.ts +82 -57
- package/src/core/providers/SchemaValidator.ts +48 -158
- package/src/core/providers/StateManager.ts +383 -9
- package/src/core/providers/TypeProvider.ts +80 -734
- package/src/core/providers/ZodProvider.ts +380 -0
- package/src/core/providers/zodAugment.ts +69 -0
- package/src/core/schemas/pageQuerySchema.ts +19 -21
- package/src/core/schemas/pageSchema.ts +47 -57
- package/src/crypto/__tests__/SecretProvider.spec.ts +31 -0
- package/src/crypto/providers/SecretProvider.ts +16 -10
- package/src/datetime/primitives/$throttle.ts +6 -1
- package/src/email/brevo/providers/BrevoEmailProvider.ts +4 -4
- package/src/email/cloudflare/providers/CloudflareEmailProvider.ts +6 -6
- package/src/email/core/index.ts +7 -2
- package/src/email/core/primitives/$email.ts +5 -1
- package/src/email/core/providers/LocalEmailProvider.ts +5 -5
- package/src/email/smtp/providers/NodemailerEmailProvider.ts +41 -52
- package/src/fake/__tests__/FakeProvider.spec.ts +110 -110
- package/src/fake/__tests__/keyName.example.ts +21 -21
- package/src/fake/__tests__/keyName.spec.ts +41 -41
- package/src/fake/__tests__/module.example.ts +8 -8
- package/src/fake/providers/FakeProvider.ts +122 -121
- package/src/lock/core/__tests__/lock-mutual-exclusion.spec.ts +44 -0
- package/src/lock/core/index.ts +1 -0
- package/src/lock/core/primitives/$lock.ts +13 -6
- package/src/logger/__tests__/CliFormatterProvider.spec.ts +76 -0
- package/src/logger/__tests__/Logger.spec.ts +49 -0
- package/src/logger/index.ts +25 -18
- package/src/logger/providers/CliFormatterProvider.ts +75 -0
- package/src/logger/schemas/logEntrySchema.ts +10 -10
- package/src/logger/services/Logger.ts +18 -0
- package/src/mcp/__tests__/$prompt.spec.ts +21 -23
- package/src/mcp/__tests__/$tool.spec.ts +22 -22
- package/src/mcp/__tests__/McpServerProvider.spec.ts +82 -11
- package/src/mcp/__tests__/StreamableHttpMcpTransport.spec.ts +2 -2
- package/src/mcp/__tests__/integration.spec.ts +33 -33
- package/src/mcp/primitives/$prompt.ts +11 -9
- package/src/mcp/primitives/$tool.ts +27 -86
- package/src/mcp/providers/McpServerProvider.ts +55 -0
- package/src/mcp/transports/StreamableHttpMcpTransport.ts +7 -7
- package/src/orm/__tests__/$repository-tests.ts +133 -27
- package/src/orm/__tests__/$repository.spec.ts +23 -0
- package/src/orm/__tests__/$transactional-tests.ts +4 -4
- package/src/orm/__tests__/ModelBuilder-tests.ts +28 -28
- package/src/orm/__tests__/ModelBuilder.spec.ts +124 -106
- package/src/orm/__tests__/QueryManager.spec.ts +5 -5
- package/src/orm/__tests__/delete-returning-tests.ts +11 -11
- package/src/orm/__tests__/deletedAt-tests.ts +3 -3
- package/src/orm/__tests__/enums.spec.ts +29 -25
- package/src/orm/__tests__/execute-tests.ts +8 -8
- package/src/orm/__tests__/fixtures/bigEntitySchema.ts +48 -48
- package/src/orm/__tests__/fixtures/userEntitySchema.ts +6 -6
- package/src/orm/__tests__/joins-tests.ts +30 -30
- package/src/orm/__tests__/organization-tests.ts +105 -3
- package/src/orm/__tests__/organization.spec.ts +46 -0
- package/src/orm/__tests__/orm-errors.spec.ts +10 -10
- package/src/orm/__tests__/orm-next-tests.ts +29 -29
- package/src/orm/__tests__/orm-showcase-tests.ts +9 -9
- package/src/orm/__tests__/primaryKey.spec.ts +4 -4
- package/src/orm/__tests__/query-date-encoding-tests.ts +17 -17
- package/src/orm/__tests__/ref-auto-onDelete.spec.ts +14 -14
- package/src/orm/__tests__/references.spec.ts +7 -7
- package/src/orm/__tests__/security.spec.ts +17 -17
- package/src/orm/__tests__/string-operators-tests.ts +5 -5
- package/src/orm/__tests__/timestamps-tests.ts +14 -14
- package/src/orm/__tests__/validation-tests.ts +5 -5
- package/src/orm/core/__tests__/BunSqliteProvider.bun.spec.ts +8 -8
- package/src/orm/core/constants/PG_SYMBOLS.ts +13 -1
- package/src/orm/core/entities/alephaSequences.ts +8 -8
- package/src/orm/core/helpers/pgAttr.ts +4 -4
- package/src/orm/core/primitives/$entity.ts +10 -6
- package/src/orm/core/providers/DatabaseTypeProvider.ts +46 -53
- package/src/orm/core/providers/drivers/BunSqliteProvider.ts +8 -8
- package/src/orm/core/providers/drivers/CloudflareD1Provider.ts +3 -5
- package/src/orm/core/providers/drivers/NodeSqliteProvider.ts +8 -8
- package/src/orm/core/schemas/databaseEnvSchema.ts +4 -4
- package/src/orm/core/schemas/insertSchema.ts +14 -18
- package/src/orm/core/schemas/legacyIdSchema.ts +2 -2
- package/src/orm/core/schemas/updateSchema.ts +10 -16
- package/src/orm/core/services/PgRelationManager.ts +2 -2
- package/src/orm/core/services/QueryManager.ts +13 -2
- package/src/orm/core/services/Repository.ts +88 -33
- package/src/orm/core/services/SqliteModelBuilder.ts +38 -45
- package/src/orm/postgres/__tests__/BunPostgresProvider.bun.spec.ts +4 -4
- package/src/orm/postgres/providers/CloudflareHyperdriveProvider.ts +4 -6
- package/src/orm/postgres/schemas/postgresEnvSchema.ts +6 -6
- package/src/orm/postgres/services/PostgresModelBuilder.ts +127 -114
- package/src/queue/core/__tests__/$consumer.spec.ts +4 -4
- package/src/queue/core/__tests__/WorkerProvider.spec.ts +4 -4
- package/src/queue/core/__tests__/shared.ts +6 -6
- package/src/queue/core/primitives/$consumer.ts +5 -5
- package/src/queue/core/primitives/$queue.ts +44 -61
- package/src/queue/core/providers/CloudflareQueueProvider.ts +6 -0
- package/src/queue/core/providers/WorkerProvider.ts +17 -14
- package/src/queue/redis/providers/RedisQueueProvider.ts +3 -3
- package/src/react/core/__tests__/ErrorBoundary.browser.spec.tsx +92 -0
- package/src/react/core/__tests__/Router.spec.tsx +4 -4
- package/src/react/core/__tests__/useComputed.browser.spec.tsx +192 -0
- package/src/react/core/__tests__/useQuery.browser.spec.tsx +48 -0
- package/src/react/core/__tests__/useSelector.browser.spec.tsx +105 -0
- package/src/react/core/__tests__/useStore.browser.spec.tsx +113 -0
- package/src/react/core/components/ErrorBoundary.tsx +42 -0
- package/src/react/core/hooks/useAction.ts +56 -19
- package/src/react/core/hooks/useComputed.ts +91 -0
- package/src/react/core/hooks/useSelector.ts +67 -0
- package/src/react/core/hooks/useStore.ts +36 -17
- package/src/react/core/index.ts +3 -0
- package/src/react/core/utils/shallowEqual.ts +36 -0
- package/src/react/form/__tests__/FormModel-submit-loading.spec.ts +71 -0
- package/src/react/form/__tests__/form-submitting-reactive.browser.spec.tsx +96 -0
- package/src/react/form/__tests__/useForm.browser.spec.tsx +54 -54
- package/src/react/form/errors/FormValidationError.ts +0 -2
- package/src/react/form/hooks/useForm.ts +19 -4
- package/src/react/form/hooks/useFormQuerySync.ts +0 -0
- package/src/react/form/services/FormModel.ts +124 -86
- package/src/react/form/services/parseField.ts +66 -27
- package/src/react/i18n/__tests__/I18nProvider.spec.ts +89 -0
- package/src/react/i18n/__tests__/Localize.spec.tsx +1 -1
- package/src/react/i18n/__tests__/locale-routing.spec.ts +107 -0
- package/src/react/i18n/providers/I18nProvider.ts +173 -14
- package/src/react/router/__tests__/$page.browser.spec.tsx +19 -19
- package/src/react/router/__tests__/$page.spec.tsx +21 -21
- package/src/react/router/__tests__/ReactBrowserProvider.browser.spec.ts +87 -1
- package/src/react/router/__tests__/RouterLocaleProvider.spec.ts +127 -0
- package/src/react/router/atoms/ssrManifestAtom.ts +16 -16
- package/src/react/router/components/NestedView.tsx +11 -3
- package/src/react/router/hooks/useQueryParams.ts +1 -1
- package/src/react/router/index.browser.ts +4 -0
- package/src/react/router/index.shared.ts +1 -0
- package/src/react/router/index.ts +9 -0
- package/src/react/router/primitives/$page.ts +2 -2
- package/src/react/router/providers/ReactBrowserProvider.ts +64 -13
- package/src/react/router/providers/ReactBrowserRouterProvider.ts +15 -1
- package/src/react/router/providers/ReactPageProvider.ts +38 -13
- package/src/react/router/providers/ReactServerProvider.ts +105 -16
- package/src/react/router/providers/RootComponentsProvider.ts +13 -0
- package/src/react/router/providers/RouterLocaleProvider.ts +125 -0
- package/src/react/router/providers/__tests__/RootComponentsProvider.spec.ts +15 -0
- package/src/react/router/providers/__tests__/rootComponents.ssr.browser.spec.tsx +67 -0
- package/src/react/sitemap/__tests__/$sitemap.spec.ts +131 -0
- package/src/react/sitemap/index.browser.ts +21 -0
- package/src/react/sitemap/index.ts +25 -0
- package/src/react/sitemap/primitives/$sitemap.browser.ts +26 -0
- package/src/react/sitemap/primitives/$sitemap.ts +196 -0
- package/src/react/ui/atoms/uiAtom.ts +6 -6
- package/src/react/ui/atoms/uiThemeListAtom.ts +7 -7
- package/src/react/ui/hooks/useColorMode.ts +11 -4
- package/src/react/ui/services/SchemaControl.ts +4 -4
- package/src/redis/providers/BunRedisProvider.ts +19 -11
- package/src/redis/providers/NodeRedisProvider.ts +3 -3
- package/src/scheduler/primitives/$scheduler.ts +21 -8
- package/src/security/__tests__/assertRealmScope.spec.ts +29 -0
- package/src/security/atoms/currentTenantAtom.ts +6 -6
- package/src/security/atoms/currentUserAtom.ts +2 -2
- package/src/security/primitives/$issuer.ts +24 -2
- package/src/security/providers/JwtProvider.ts +100 -0
- package/src/security/providers/SecurityProvider.ts +43 -2
- package/src/security/schemas/permissionSchema.ts +19 -19
- package/src/security/schemas/roleSchema.ts +22 -28
- package/src/security/schemas/userAccountInfoSchema.ts +47 -37
- package/src/server/auth/__tests__/appleClientSecret.spec.ts +34 -0
- package/src/server/auth/__tests__/federationRedirectReplay.spec.ts +44 -0
- package/src/server/auth/helpers/appleClientSecret.ts +24 -0
- package/src/server/auth/helpers/jtiReplayGuard.ts +41 -0
- package/src/server/auth/helpers/safeRedirectPath.ts +19 -0
- package/src/server/auth/index.ts +1 -0
- package/src/server/auth/primitives/$auth.ts +6 -6
- package/src/server/auth/primitives/$authApple.ts +10 -10
- package/src/server/auth/primitives/$authFacebook.ts +10 -10
- package/src/server/auth/primitives/$authFranceConnect.ts +10 -10
- package/src/server/auth/primitives/$authGithub.ts +10 -10
- package/src/server/auth/primitives/$authGoogle.ts +10 -10
- package/src/server/auth/primitives/$authMicrosoft.ts +14 -14
- package/src/server/auth/providers/ServerAuthProvider.ts +67 -52
- package/src/server/auth/schemas/authenticationProviderSchema.ts +9 -12
- package/src/server/auth/schemas/tokenResponseSchema.ts +2 -2
- package/src/server/auth/schemas/tokensSchema.ts +16 -16
- package/src/server/auth/schemas/userinfoResponseSchema.ts +3 -3
- package/src/server/cookies/__tests__/AtomCookieBinding.spec.ts +80 -0
- package/src/server/cookies/__tests__/AtomCookiePersistence.browser.spec.ts +169 -0
- package/src/server/cookies/__tests__/AtomCookiePersistence.spec.ts +294 -0
- package/src/server/cookies/__tests__/ServerCookiesProvider.spec.ts +82 -12
- package/src/server/cookies/index.browser.ts +4 -1
- package/src/server/cookies/index.ts +8 -1
- package/src/server/cookies/primitives/$cookie.browser.ts +1 -1
- package/src/server/cookies/primitives/$cookie.ts +16 -0
- package/src/server/cookies/providers/AtomCookiePersistence.browser.ts +133 -0
- package/src/server/cookies/providers/AtomCookiePersistence.ts +148 -0
- package/src/server/cookies/providers/ServerCookiesProvider.ts +32 -4
- package/src/server/cookies/services/CookieParser.ts +6 -1
- package/src/server/core/__tests__/$action.spec.ts +13 -13
- package/src/server/core/__tests__/$middleware.spec.ts +10 -10
- package/src/server/core/__tests__/$route.spec.ts +9 -9
- package/src/server/core/__tests__/$sse.spec.ts +26 -26
- package/src/server/core/__tests__/BunHttpServerProvider.bun.spec.ts +6 -6
- package/src/server/core/__tests__/HttpClient.spec.ts +2 -2
- package/src/server/core/__tests__/NodeHttpServerProvider.spec.ts +15 -3
- package/src/server/core/__tests__/ServerBodyParserProvider.spec.ts +14 -14
- package/src/server/core/__tests__/ServerMultipartProvider.spec.ts +33 -33
- package/src/server/core/__tests__/ServerRouterProvider-errorHandler.spec.ts +5 -1
- package/src/server/core/__tests__/ServerRouterProvider-getRoutes.spec.ts +25 -25
- package/src/server/core/__tests__/ServerRouterProvider-serializationError.spec.ts +9 -5
- package/src/server/core/__tests__/ServerRouterProvider-validationError.spec.ts +56 -24
- package/src/server/core/errors/ValidationError.ts +6 -3
- package/src/server/core/helpers/isMultipart.ts +2 -1
- package/src/server/core/index.ts +5 -2
- package/src/server/core/interfaces/ServerRequest.ts +14 -14
- package/src/server/core/primitives/$action.ts +26 -36
- package/src/server/core/primitives/$route.ts +27 -0
- package/src/server/core/primitives/$sse.ts +3 -3
- package/src/server/core/providers/BunHttpServerProvider.ts +8 -9
- package/src/server/core/providers/NodeHttpServerProvider.ts +8 -9
- package/src/server/core/providers/ServerBodyParserProvider.ts +20 -12
- package/src/server/core/providers/ServerCompressProvider.ts +9 -10
- package/src/server/core/providers/ServerHelmetProvider.ts +26 -27
- package/src/server/core/providers/ServerMultipartProvider.ts +19 -19
- package/src/server/core/providers/ServerRouterProvider.ts +44 -15
- package/src/server/core/schemas/errorSchema.ts +20 -25
- package/src/server/core/schemas/okSchema.ts +9 -14
- package/src/server/core/services/HttpClient.ts +13 -1
- package/src/server/core/services/ServerRequestParser.ts +6 -6
- package/src/server/cors/providers/ServerCorsProvider.ts +26 -28
- package/src/server/health/schemas/healthSchema.ts +6 -6
- package/src/server/links/__tests__/$remote.spec.ts +3 -3
- package/src/server/links/__tests__/BatchEndpoint.spec.ts +9 -9
- package/src/server/links/__tests__/LinkProvider.spec.ts +9 -9
- package/src/server/links/__tests__/ServerLinksProvider.spec.ts +23 -23
- package/src/server/links/__tests__/fixtures/CrudApp.ts +21 -21
- package/src/server/links/atoms/apiLinksAtom.ts +2 -2
- package/src/server/links/atoms/linkOptionsAtom.ts +6 -6
- package/src/server/links/primitives/$remote.ts +3 -3
- package/src/server/links/providers/LinkProvider.ts +3 -3
- package/src/server/links/providers/ServerLinksProvider.ts +20 -20
- package/src/server/links/schemas/apiLinksResponseSchema.ts +23 -23
- package/src/server/proxy/__tests__/$proxy.spec.ts +9 -9
- package/src/server/proxy/providers/ServerProxyProvider.ts +5 -0
- package/src/server/rate-limit/providers/ServerRateLimitProvider.ts +18 -20
- package/src/server/swagger/__tests__/$swagger.spec.ts +91 -94
- package/src/server/swagger/__tests__/ui.spec.ts +3 -3
- package/src/server/swagger/providers/ServerSwaggerProvider.ts +66 -43
- package/src/system/__tests__/NodeShellProvider.spec.ts +46 -0
- package/src/system/providers/NodeShellProvider.ts +4 -1
- package/src/topic/core/__tests__/$subscriber.spec.ts +4 -4
- package/src/topic/core/__tests__/shared.ts +8 -8
- package/src/topic/core/primitives/$subscriber.ts +5 -5
- package/src/topic/core/primitives/$topic.ts +14 -14
- package/src/topic/redis/providers/RedisTopicProvider.ts +3 -3
- package/dist/container/core/index.d.ts +0 -236
- package/dist/container/core/index.d.ts.map +0 -1
- package/dist/container/core/index.js +0 -231
- package/dist/container/core/index.js.map +0 -1
- package/dist/container/core/index.workerd.js +0 -192
- package/dist/container/core/index.workerd.js.map +0 -1
- package/src/cli/core/tasks/BuildSitemapTask.ts +0 -130
- package/src/command/helpers/PrettyAsker.ts +0 -256
- package/src/command/helpers/PrettyPrint.ts +0 -332
- package/src/container/core/__tests__/$container.spec.ts +0 -83
- package/src/container/core/index.ts +0 -50
- package/src/container/core/index.workerd.ts +0 -53
- package/src/container/core/interfaces/ContainerOptions.ts +0 -69
- package/src/container/core/primitives/$container.ts +0 -100
- package/src/container/core/providers/CloudflareContainerProvider.ts +0 -70
- package/src/container/core/providers/ContainerProvider.ts +0 -92
- package/src/container/core/providers/MockContainerProvider.ts +0 -62
- package/src/container/core/providers/NodeContainerProvider.ts +0 -53
- package/src/core/__tests__/jsonSchemaToTypeBox.spec.ts +0 -771
- package/src/core/helpers/jsonSchemaToTypeBox.ts +0 -359
- /package/dist/orm/core/{chunk-B4FMCO8f.js → rolldown-runtime-B4FMCO8f.js} +0 -0
|
@@ -1,17 +1,16 @@
|
|
|
1
1
|
import { Middleware, Static } from "alepha";
|
|
2
2
|
import { ServerRouterProvider } from "alepha/server";
|
|
3
|
-
|
|
4
3
|
//#region ../../src/server/cors/providers/ServerCorsProvider.d.ts
|
|
5
4
|
/**
|
|
6
5
|
* CORS configuration atom (global defaults)
|
|
7
6
|
*/
|
|
8
|
-
declare const corsOptions: import("alepha").Atom<import("
|
|
9
|
-
origin: import("
|
|
10
|
-
methods: import("
|
|
11
|
-
headers: import("
|
|
12
|
-
credentials: import("
|
|
13
|
-
maxAge: import("
|
|
14
|
-
}>, "alepha.server.cors.options">;
|
|
7
|
+
declare const corsOptions: import("alepha").Atom<import("zod").ZodObject<{
|
|
8
|
+
origin: import("zod").ZodOptional<import("zod").ZodDefault<import("zod").ZodString>>;
|
|
9
|
+
methods: import("zod").ZodDefault<import("zod").ZodArray<import("zod").ZodString>>;
|
|
10
|
+
headers: import("zod").ZodDefault<import("zod").ZodArray<import("zod").ZodString>>;
|
|
11
|
+
credentials: import("zod").ZodOptional<import("zod").ZodDefault<import("zod").ZodBoolean>>;
|
|
12
|
+
maxAge: import("zod").ZodOptional<import("zod").ZodNumber>;
|
|
13
|
+
}, import("zod/v4/core").$strip>, "alepha.server.cors.options">;
|
|
15
14
|
type CorsOptions = Static<typeof corsOptions.schema>;
|
|
16
15
|
declare module "alepha" {
|
|
17
16
|
interface State {
|
|
@@ -33,10 +32,10 @@ declare class ServerCorsProvider {
|
|
|
33
32
|
protected readonly serverRouterProvider: ServerRouterProvider;
|
|
34
33
|
protected readonly globalOptions: Readonly<{
|
|
35
34
|
origin?: string | undefined;
|
|
36
|
-
credentials?: boolean | undefined;
|
|
37
|
-
maxAge?: number | undefined;
|
|
38
35
|
methods: string[];
|
|
39
36
|
headers: string[];
|
|
37
|
+
credentials?: boolean | undefined;
|
|
38
|
+
maxAge?: number | undefined;
|
|
40
39
|
}>;
|
|
41
40
|
/**
|
|
42
41
|
* Registered CORS configurations with their path patterns
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","names":[],"sources":["../../../src/server/cors/providers/ServerCorsProvider.ts","../../../src/server/cors/primitives/$cors.ts","../../../src/server/cors/index.ts"],"mappings":";;;;;;
|
|
1
|
+
{"version":3,"file":"index.d.ts","names":[],"sources":["../../../src/server/cors/providers/ServerCorsProvider.ts","../../../src/server/cors/primitives/$cors.ts","../../../src/server/cors/index.ts"],"mappings":";;;;;;cASa,8BAAW,mBAAA;;;;;;;KAoCZ,cAAc,cAAc,YAAY;;YAGxC;KACP,YAAY,MAAM;;;UAMN,yBAAyB,QAAQ;;;;EAIhD;;;;EAIA;;cAGW;qBACQ,6BAAG;qBACH,sBAAoB;qBACpB,eAAa;;;;;;;;;;WAKhB,mBAAmB;;;;EAK5B,aAAa,QAAQ;qBAIT,0BAAO;;;;EA0BnB,iBAAiB,QAAQ,QAAQ,eAAe;;;;EAahD,iBACL;IACE;MAAW;;IACX;MAAS,YAAY,cAAc;;KAErC,SAAS;qBAqBQ,4BAAS;qBAwBT,4BAAS;EASrB,gBACL,4BACA,SAAS;;KAWD,4BAA4B;;;;;;;;;;;;;;;;;;;;;;;;cCvK3B,QAAK,UAAc,QAAQ,iBAAe;;;;YCb3C;;;;;IAKR,OAAO;;;;;;;;;;;cAcE,mCAAgB,yBAAA"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { $atom, $hook, $inject, $module, $state, AlephaError, createMiddleware,
|
|
1
|
+
import { $atom, $hook, $inject, $module, $state, AlephaError, createMiddleware, z } from "alepha";
|
|
2
2
|
import { $logger } from "alepha/logger";
|
|
3
3
|
import { ServerRouterProvider } from "alepha/server";
|
|
4
4
|
//#region ../../src/server/cors/providers/ServerCorsProvider.ts
|
|
@@ -7,31 +7,19 @@ import { ServerRouterProvider } from "alepha/server";
|
|
|
7
7
|
*/
|
|
8
8
|
const corsOptions = $atom({
|
|
9
9
|
name: "alepha.server.cors.options",
|
|
10
|
-
schema:
|
|
11
|
-
origin:
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
"OPTIONS"
|
|
24
|
-
]
|
|
25
|
-
}),
|
|
26
|
-
headers: t.array(t.string(), {
|
|
27
|
-
description: "Allowed headers",
|
|
28
|
-
default: ["Content-Type", "Authorization"]
|
|
29
|
-
}),
|
|
30
|
-
credentials: t.optional(t.boolean({
|
|
31
|
-
description: "Allow credentials",
|
|
32
|
-
default: false
|
|
33
|
-
})),
|
|
34
|
-
maxAge: t.optional(t.number({ description: "Preflight cache duration in seconds" }))
|
|
10
|
+
schema: z.object({
|
|
11
|
+
origin: z.string().describe("Allowed origins (* for all, string for single, comma-separated for multiple)").default("*").optional(),
|
|
12
|
+
methods: z.array(z.string()).describe("Allowed HTTP methods").default([
|
|
13
|
+
"GET",
|
|
14
|
+
"POST",
|
|
15
|
+
"PUT",
|
|
16
|
+
"PATCH",
|
|
17
|
+
"DELETE",
|
|
18
|
+
"OPTIONS"
|
|
19
|
+
]),
|
|
20
|
+
headers: z.array(z.string()).describe("Allowed headers").default(["Content-Type", "Authorization"]),
|
|
21
|
+
credentials: z.boolean().describe("Allow credentials").default(false).optional(),
|
|
22
|
+
maxAge: z.number().describe("Preflight cache duration in seconds").optional()
|
|
35
23
|
}),
|
|
36
24
|
default: {
|
|
37
25
|
origin: "*",
|
|
@@ -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, $state, type Static, t } from \"alepha\";\nimport { $logger } from \"alepha/logger\";\nimport { ServerRouterProvider } from \"alepha/server\";\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: false,\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: false,\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 interface CorsRegistration extends Partial<CorsOptions> {\n /**\n * Name identifier for this CORS config.\n */\n name?: string;\n /**\n * Path patterns to match (supports wildcards like /api/*).\n */\n paths?: string[];\n}\n\nexport class ServerCorsProvider {\n protected readonly log = $logger();\n protected readonly serverRouterProvider = $inject(ServerRouterProvider);\n protected readonly globalOptions = $state(corsOptions);\n\n /**\n * Registered CORS configurations with their path patterns\n */\n public readonly registeredConfigs: CorsRegistration[] = [];\n\n /**\n * Register a CORS configuration (called by primitives)\n */\n public registerCors(config: CorsRegistration): 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 /**\n * Build complete CORS options by merging with global defaults\n */\n public buildCorsOptions(config: Partial<CorsOptions>): 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 public 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 protected readonly configure = $hook({\n on: \"start\",\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 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 { AlephaError, createMiddleware, type Middleware } from \"alepha\";\nimport {\n type CorsOptions,\n ServerCorsProvider,\n} from \"../providers/ServerCorsProvider.ts\";\n\n/**\n * Middleware that applies CORS headers to the response and handles OPTIONS preflight.\n *\n * Reads the request from the ALS context and applies the configured\n * CORS headers via `ServerCorsProvider`. Options are merged with\n * global CORS defaults.\n *\n * For OPTIONS preflight requests, the middleware short-circuits with a 204 response\n * and skips the handler entirely.\n *\n * **Route middleware** — requires a request context (`$action`). Throws if used outside one.\n *\n * ```typescript\n * class ApiController {\n * getOrders = $action({\n * use: [$cors({ origin: \"https://app.example.com\", credentials: true })],\n * handler: async ({ query }) => { ... },\n * });\n * }\n * ```\n */\nexport const $cors = (options?: Partial<CorsOptions>): Middleware => {\n return createMiddleware({\n name: \"$cors\",\n options: options as unknown as Record<string, unknown>,\n handler: ({ alepha, next }) => {\n const corsProvider = alepha.inject(ServerCorsProvider);\n\n return async (...args) => {\n const request = alepha.get(\"alepha.http.request\");\n\n if (!request) {\n throw new AlephaError(\n \"$cors requires a request context (use inside $action)\",\n );\n }\n\n const corsConfig = corsProvider.buildCorsOptions(options ?? {});\n corsProvider.applyCorsHeaders(request, corsConfig);\n\n // OPTIONS preflight → respond immediately, skip handler\n if (request.method === \"OPTIONS\") {\n request.reply.setStatus(204);\n return;\n }\n\n return next(...args);\n };\n },\n });\n};\n","import { $module } from \"alepha\";\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 * 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 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, $state, type Static, z } from \"alepha\";\nimport { $logger } from \"alepha/logger\";\nimport { ServerRouterProvider } from \"alepha/server\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\n/**\n * CORS configuration atom (global defaults)\n */\nexport const corsOptions = $atom({\n name: \"alepha.server.cors.options\",\n schema: z.object({\n origin: z\n .string()\n .describe(\n \"Allowed origins (* for all, string for single, comma-separated for multiple)\",\n )\n .default(\"*\")\n .optional(),\n methods: z\n .array(z.string())\n .describe(\"Allowed HTTP methods\")\n .default([\"GET\", \"POST\", \"PUT\", \"PATCH\", \"DELETE\", \"OPTIONS\"]),\n headers: z\n .array(z.string())\n .describe(\"Allowed headers\")\n .default([\"Content-Type\", \"Authorization\"]),\n credentials: z\n .boolean()\n .describe(\"Allow credentials\")\n .default(false)\n .optional(),\n maxAge: z\n .number()\n .describe(\"Preflight cache duration in seconds\")\n .optional(),\n }),\n default: {\n origin: \"*\",\n methods: [\"GET\", \"POST\", \"PUT\", \"PATCH\", \"DELETE\", \"OPTIONS\"],\n headers: [\"Content-Type\", \"Authorization\"],\n credentials: false,\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 interface CorsRegistration extends Partial<CorsOptions> {\n /**\n * Name identifier for this CORS config.\n */\n name?: string;\n /**\n * Path patterns to match (supports wildcards like /api/*).\n */\n paths?: string[];\n}\n\nexport class ServerCorsProvider {\n protected readonly log = $logger();\n protected readonly serverRouterProvider = $inject(ServerRouterProvider);\n protected readonly globalOptions = $state(corsOptions);\n\n /**\n * Registered CORS configurations with their path patterns\n */\n public readonly registeredConfigs: CorsRegistration[] = [];\n\n /**\n * Register a CORS configuration (called by primitives)\n */\n public registerCors(config: CorsRegistration): 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 /**\n * Build complete CORS options by merging with global defaults\n */\n public buildCorsOptions(config: Partial<CorsOptions>): 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 public 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 protected readonly configure = $hook({\n on: \"start\",\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 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 { AlephaError, createMiddleware, type Middleware } from \"alepha\";\nimport {\n type CorsOptions,\n ServerCorsProvider,\n} from \"../providers/ServerCorsProvider.ts\";\n\n/**\n * Middleware that applies CORS headers to the response and handles OPTIONS preflight.\n *\n * Reads the request from the ALS context and applies the configured\n * CORS headers via `ServerCorsProvider`. Options are merged with\n * global CORS defaults.\n *\n * For OPTIONS preflight requests, the middleware short-circuits with a 204 response\n * and skips the handler entirely.\n *\n * **Route middleware** — requires a request context (`$action`). Throws if used outside one.\n *\n * ```typescript\n * class ApiController {\n * getOrders = $action({\n * use: [$cors({ origin: \"https://app.example.com\", credentials: true })],\n * handler: async ({ query }) => { ... },\n * });\n * }\n * ```\n */\nexport const $cors = (options?: Partial<CorsOptions>): Middleware => {\n return createMiddleware({\n name: \"$cors\",\n options: options as unknown as Record<string, unknown>,\n handler: ({ alepha, next }) => {\n const corsProvider = alepha.inject(ServerCorsProvider);\n\n return async (...args) => {\n const request = alepha.get(\"alepha.http.request\");\n\n if (!request) {\n throw new AlephaError(\n \"$cors requires a request context (use inside $action)\",\n );\n }\n\n const corsConfig = corsProvider.buildCorsOptions(options ?? {});\n corsProvider.applyCorsHeaders(request, corsConfig);\n\n // OPTIONS preflight → respond immediately, skip handler\n if (request.method === \"OPTIONS\") {\n request.reply.setStatus(204);\n return;\n }\n\n return next(...args);\n };\n },\n });\n};\n","import { $module } from \"alepha\";\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 * 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 services: [ServerCorsProvider],\n});\n"],"mappings":"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"}
|
|
@@ -3,7 +3,6 @@ import { CachePrimitiveOptions } from "alepha/cache";
|
|
|
3
3
|
import { CryptoProvider } from "alepha/crypto";
|
|
4
4
|
import { DateTimeProvider, DurationLike } from "alepha/datetime";
|
|
5
5
|
import { ServerRequest, ServerRoute } from "alepha/server";
|
|
6
|
-
|
|
7
6
|
//#region ../../src/server/etag/primitives/$etag.d.ts
|
|
8
7
|
/**
|
|
9
8
|
* Middleware that enables ETag-based response caching per-route.
|
|
@@ -45,11 +44,11 @@ type EtagMiddlewareOptions =
|
|
|
45
44
|
/**
|
|
46
45
|
* If true, enables both store and etag.
|
|
47
46
|
*/
|
|
48
|
-
true
|
|
47
|
+
true |
|
|
49
48
|
/**
|
|
50
49
|
* Object configuration for fine-grained control.
|
|
51
50
|
*/
|
|
52
|
-
|
|
51
|
+
{
|
|
53
52
|
/**
|
|
54
53
|
* If true, enables storing cached responses. (in-memory, Redis, @see alepha/cache for other providers)
|
|
55
54
|
* If a DurationLike is provided, it will be used as the TTL for the cache.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","names":[],"sources":["../../../src/server/etag/primitives/$etag.ts","../../../src/server/etag/providers/ServerEtagProvider.ts","../../../src/server/etag/index.ts"],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.d.ts","names":[],"sources":["../../../src/server/etag/primitives/$etag.ts","../../../src/server/etag/providers/ServerEtagProvider.ts","../../../src/server/etag/index.ts"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;cAyCa,QAAK,UAAc,0BAAwB;iBAsCxC,mBACd,UAAU,wBACT;KAcS;;;;;;;;;;;;;;;;EAgBN,eAAe,eAAe;;;;;;EAO9B;;;;;;EAOA;;;;IAOM;;;;IAIA;;;;IAIA;;;;IAIA;;;;;IAKA,kBAAkB;;;;;IAKlB,mBAAmB;;;;IAInB;;;;IAIA;;;;IAIA;;;;;;IAMA,gCAAgC;;;UAI3B;EACf,eAAe,eAAe;EAC9B;EACA;IAIM;IACA;IACA;IACA;IACA,kBAAkB;IAClB,mBAAmB;IACnB;IACA;IACA;IACA,gCAAgC;;;;;cCtL3B;qBACQ,6BAAG;qBACH,QAAM;qBACN,QAAM;qBACN,MAAI;qBACJ,8BAAK,kBAAA;EAKjB,aAAa,kBAAkB;EAKzB,WAAW,OAAO,cAAW;;;;;EAQ7B,WACX,SAAS,eACT,SAAS,gCACR;;;;qBA8EgB,mCAAgB;;;;;qBAyDhB,yBAAM;;;;qBA2CN,6BAAU;EA0GtB,wBACL,UAAU;EA6DL,YAAY,UAAU;EAMtB,cAAc,UAAU;YAUrB,kBAAkB,mBAAmB;YAQrC,eAAe,OAAO,aAAa,SAAS;;;;;YAgBtC,sBACd,QAAQ,eAAe,aACvB,aACA,4BACA,iCACA,wBACC;;UAwCK;EACR;EACA;EACA;EACA;EACA;;;;;;;;;;;;;;;cClcW,mCAAgB,yBAAA"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":[],"sources":["../../../src/server/etag/providers/ServerEtagProvider.ts","../../../src/server/etag/primitives/$etag.ts","../../../src/server/etag/index.ts"],"sourcesContent":["import { $hook, $inject, Alepha } from \"alepha\";\nimport { $cache } from \"alepha/cache\";\nimport { CryptoProvider } from \"alepha/crypto\";\nimport { DateTimeProvider, type DurationLike } from \"alepha/datetime\";\nimport { $logger } from \"alepha/logger\";\nimport type { ServerRequest, ServerRoute } from \"alepha/server\";\nimport type { EtagMiddlewareOptionsResolved } from \"../primitives/$etag.ts\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport class ServerEtagProvider {\n protected readonly log = $logger();\n protected readonly alepha = $inject(Alepha);\n protected readonly crypto = $inject(CryptoProvider);\n protected readonly time = $inject(DateTimeProvider);\n protected readonly cache = $cache<RouteCacheEntry>({\n provider: \"memory\",\n name: \"http:server\",\n });\n\n public generateETag(content: string | Buffer): string {\n const data = typeof content === \"string\" ? content : content.toString();\n return `\"${this.crypto.hash(data, \"md5\")}\"`;\n }\n\n public async invalidate(route: ServerRoute) {\n await this.cache.invalidate(this.createCacheKey(route));\n }\n\n /**\n * Check cache for a stored response. Returns the cached body if found, undefined otherwise.\n * Called by the $etag() middleware before the handler runs.\n */\n public async checkCache(\n request: ServerRequest,\n options: EtagMiddlewareOptionsResolved,\n ): Promise<any> {\n if (!this.shouldStore(options)) {\n return undefined;\n }\n\n // Build key from route metadata (set by ServerRouterProvider before handler)\n // or from action request context (for .run() direct calls)\n const actionRequest = this.alepha.store.get(\"alepha.action.request\");\n\n const keySource = {\n method:\n request.metadata?.routeMethod ??\n actionRequest?.method ??\n request.method ??\n \"GET\",\n path:\n request.metadata?.routePath ??\n String(actionRequest?.url ?? request.url ?? \"\"),\n } as ServerRoute;\n\n const key = this.createCacheKey(keySource, actionRequest ?? request);\n const cached = await this.cache.get(key);\n\n if (!cached) {\n this.log.trace(\"Cache miss\", { key });\n return undefined;\n }\n\n this.log.trace(\"Cache hit\", { key });\n\n // Mark as cache hit in request metadata\n request.metadata ??= {} as any;\n request.metadata.etagHit = true;\n\n // For HTTP routes, set reply headers\n if (request.reply) {\n // Check if client has matching ETag - return 304\n if (\n request.headers?.[\"if-none-match\"] === cached.hash ||\n request.headers?.[\"if-modified-since\"] === cached.lastModified\n ) {\n request.reply.status = 304;\n request.reply.setHeader(\"etag\", cached.hash);\n request.reply.setHeader(\"last-modified\", cached.lastModified);\n this.log.trace(\"ETag match, returning 304\", {\n key,\n etag: cached.hash,\n });\n return request.reply.body;\n }\n\n request.reply.body = cached.body;\n request.reply.status = cached.status ?? 200;\n\n if (cached.contentType) {\n request.reply.setHeader(\"Content-Type\", cached.contentType);\n }\n\n request.reply.setHeader(\"etag\", cached.hash);\n request.reply.setHeader(\"last-modified\", cached.lastModified);\n }\n\n // For action direct invocations, return body\n const body =\n cached.contentType === \"application/json\"\n ? JSON.parse(cached.body)\n : cached.body;\n\n return body;\n }\n\n // -------------------------------------------------------------------------------------------------------------------\n // Hooks (post-handler, read from request.metadata)\n // -------------------------------------------------------------------------------------------------------------------\n\n /**\n * After an action response, store it in cache if store is enabled.\n */\n protected readonly onActionResponse = $hook({\n on: \"action:onResponse\",\n handler: async ({ action, request, response }) => {\n const options = request.metadata\n ?.etagOptions as EtagMiddlewareOptionsResolved;\n if (!options || !this.shouldStore(options)) {\n return;\n }\n\n // Skip if this was a cache hit (don't re-store)\n if (request.metadata?.etagHit) {\n return;\n }\n\n // Don't cache error responses (status >= 400)\n if (request.reply.status && request.reply.status >= 400) {\n return;\n }\n\n if (!response) {\n return;\n }\n\n const contentType =\n typeof response === \"string\" ? \"text/plain\" : \"application/json\";\n const body =\n contentType === \"text/plain\" ? response : JSON.stringify(response);\n\n const generatedEtag = this.generateETag(body);\n const lastModified = this.time.toISOString();\n\n const key = this.createCacheKey(action.route, request);\n\n this.log.trace(\"Storing action response\", {\n key,\n action: action.name,\n });\n\n await this.cache.set(key, {\n body: body,\n lastModified,\n contentType: contentType,\n hash: generatedEtag,\n });\n\n // Set Cache-Control header if configured\n const cacheControl = this.buildCacheControlHeader(options);\n if (cacheControl) {\n request.reply.setHeader(\"cache-control\", cacheControl);\n }\n },\n });\n\n /**\n * Before sending the response, check ETag for etag-only routes.\n * This handles the case where etag is enabled but store is not.\n */\n protected readonly onSend = $hook({\n on: \"server:onSend\",\n handler: ({ request }) => {\n const options = request.metadata\n ?.etagOptions as EtagMiddlewareOptionsResolved;\n if (!options) {\n return;\n }\n\n const shouldStore = this.shouldStore(options);\n const shouldUseEtag = this.shouldUseEtag(options);\n\n if (request.reply.headers.etag) {\n // ETag already set, skip\n return;\n }\n\n if (\n !shouldStore &&\n shouldUseEtag &&\n request.reply.body != null &&\n (typeof request.reply.body === \"string\" ||\n Buffer.isBuffer(request.reply.body))\n ) {\n const generatedEtag = this.generateETag(request.reply.body);\n\n if (request.headers[\"if-none-match\"] === generatedEtag) {\n request.reply.status = 304;\n request.reply.body = undefined;\n request.reply.setHeader(\"etag\", generatedEtag);\n this.log.trace(\"ETag match on send, returning 304\", {\n route: request.url,\n etag: generatedEtag,\n });\n return;\n }\n }\n },\n });\n\n /**\n * After the response is generated, store it and set ETag headers.\n */\n protected readonly onResponse = $hook({\n on: \"server:onResponse\",\n priority: \"first\",\n handler: async ({ route, request, response }) => {\n const options = request.metadata\n ?.etagOptions as EtagMiddlewareOptionsResolved;\n if (!options) {\n return;\n }\n\n // Set Cache-Control header if configured\n const cacheControl = this.buildCacheControlHeader(options);\n if (cacheControl) {\n response.headers[\"cache-control\"] = cacheControl;\n }\n\n const shouldStore = this.shouldStore(options);\n const shouldUseEtag = this.shouldUseEtag(options);\n\n // Skip if neither cache nor etag is enabled\n if (!shouldStore && !shouldUseEtag) {\n return;\n }\n\n // Skip if this was a cache hit (don't re-store)\n if (request.metadata?.etagHit) {\n return;\n }\n\n // Don't cache error responses (status >= 400)\n if (response.status && response.status >= 400) {\n return;\n }\n\n // Initialize headers if not present\n response.headers ??= {};\n\n const key = this.createCacheKey(route, request);\n\n // Handle ReadableStream responses (e.g., SSR streaming)\n if (response.body instanceof ReadableStream && shouldStore) {\n // Tee the stream: one for client, one for cache collection\n const [clientStream, cacheStream] = (\n response.body as ReadableStream<Uint8Array>\n ).tee();\n\n // Replace response body with client stream (continues streaming to client)\n response.body = clientStream as typeof response.body;\n\n // Collect cache stream in background (non-blocking)\n this.collectStreamForCache(\n cacheStream,\n key,\n response.status,\n response.headers?.[\"content-type\"],\n shouldUseEtag,\n )\n .then((hash) => {\n if (shouldUseEtag && hash) {\n this.log.trace(\"Stream cached with hash\", { key, hash });\n }\n })\n .catch((err) => {\n this.log.warn(\"Failed to cache stream\", { key, error: err });\n });\n\n return;\n }\n\n // Only process string responses (text, html, json, etc.)\n if (typeof response.body !== \"string\") {\n return;\n }\n\n const generatedEtag = this.generateETag(response.body);\n const lastModified = this.time.toISOString();\n\n // Store response if storing is enabled\n if (shouldStore) {\n this.log.trace(\"Storing response\", {\n key,\n route: route.path,\n etag: shouldUseEtag,\n });\n\n await this.cache.set(key, {\n body: response.body,\n status: response.status,\n contentType: response.headers?.[\"content-type\"],\n lastModified,\n hash: generatedEtag,\n });\n }\n\n // Set ETag headers if etag is enabled\n if (shouldUseEtag) {\n response.headers.etag = generatedEtag;\n response.headers[\"last-modified\"] = lastModified;\n }\n },\n });\n\n // -------------------------------------------------------------------------------------------------------------------\n // Public helpers\n // -------------------------------------------------------------------------------------------------------------------\n\n public buildCacheControlHeader(\n options?: EtagMiddlewareOptionsResolved,\n ): string | undefined {\n if (!options) {\n return undefined;\n }\n\n const control = options.control;\n if (!control) {\n return undefined;\n }\n\n // If control is a string, return it directly\n if (typeof control === \"string\") {\n return control;\n }\n\n // If control is true, return default Cache-Control\n if (control === true) {\n return \"public, max-age=300\";\n }\n\n // Build Cache-Control from object directives\n const directives: string[] = [];\n\n if (control.public) {\n directives.push(\"public\");\n }\n if (control.private) {\n directives.push(\"private\");\n }\n if (control.noCache) {\n directives.push(\"no-cache\");\n }\n if (control.noStore) {\n directives.push(\"no-store\");\n }\n if (control.maxAge !== undefined) {\n const maxAgeSeconds = this.durationToSeconds(control.maxAge);\n directives.push(`max-age=${maxAgeSeconds}`);\n }\n if (control.sMaxAge !== undefined) {\n const sMaxAgeSeconds = this.durationToSeconds(control.sMaxAge);\n directives.push(`s-maxage=${sMaxAgeSeconds}`);\n }\n if (control.mustRevalidate) {\n directives.push(\"must-revalidate\");\n }\n if (control.proxyRevalidate) {\n directives.push(\"proxy-revalidate\");\n }\n if (control.immutable) {\n directives.push(\"immutable\");\n }\n if (control.staleWhileRevalidate !== undefined) {\n const seconds = this.durationToSeconds(control.staleWhileRevalidate);\n directives.push(`stale-while-revalidate=${seconds}`);\n }\n\n return directives.length > 0 ? directives.join(\", \") : undefined;\n }\n\n public shouldStore(options?: EtagMiddlewareOptionsResolved): boolean {\n if (!options) return false;\n if (options.store) return true;\n return false;\n }\n\n public shouldUseEtag(options?: EtagMiddlewareOptionsResolved): boolean {\n if (!options) return false;\n if (options.etag) return true;\n return false;\n }\n\n // -------------------------------------------------------------------------------------------------------------------\n // Protected helpers\n // -------------------------------------------------------------------------------------------------------------------\n\n protected durationToSeconds(duration: number | DurationLike): number {\n if (typeof duration === \"number\") {\n return duration;\n }\n\n return this.time.duration(duration).asSeconds();\n }\n\n protected createCacheKey(route: ServerRoute, config?: ServerRequest): string {\n const params: string[] = [];\n for (const [key, value] of Object.entries(config?.params ?? {})) {\n params.push(`${key}=${value}`);\n }\n for (const [key, value] of Object.entries(config?.query ?? {})) {\n params.push(`${key}=${value}`);\n }\n\n return `${route.method}:${(route.path ?? \"\").replaceAll(\":\", \"\")}:${params.join(\",\").replaceAll(\":\", \"\")}`;\n }\n\n /**\n * Collect a ReadableStream into a string and store it in the cache.\n * This runs in the background while the original stream is sent to the client.\n */\n protected async collectStreamForCache(\n stream: ReadableStream<Uint8Array>,\n key: string,\n status: number | undefined,\n contentType: string | undefined,\n generateEtag: boolean,\n ): Promise<string | undefined> {\n const chunks: Uint8Array[] = [];\n const reader = stream.getReader();\n\n try {\n while (true) {\n const { done, value } = await reader.read();\n if (done) break;\n chunks.push(value);\n }\n\n // Combine chunks into a single string\n const decoder = new TextDecoder();\n const body =\n chunks\n .map((chunk) => decoder.decode(chunk, { stream: true }))\n .join(\"\") + decoder.decode(); // Flush remaining\n\n const hash = this.generateETag(body);\n const lastModified = this.time.toISOString();\n\n this.log.trace(\"Storing streamed response\", { key });\n\n await this.cache.set(key, {\n body,\n status,\n contentType,\n lastModified,\n hash,\n });\n\n return generateEtag ? hash : undefined;\n } finally {\n reader.releaseLock();\n }\n }\n}\n\n// ---------------------------------------------------------------------------------------------------------------------\n\ninterface RouteCacheEntry {\n contentType?: string;\n body: any;\n status?: number;\n lastModified: string;\n hash: string;\n}\n","import { createMiddleware, type Middleware } from \"alepha\";\nimport type { CachePrimitiveOptions } from \"alepha/cache\";\nimport type { DurationLike } from \"alepha/datetime\";\nimport { ServerEtagProvider } from \"../providers/ServerEtagProvider.ts\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\n/**\n * Middleware that enables ETag-based response caching per-route.\n *\n * Sets per-request etag options in the ALS context.\n * The global `ServerEtagProvider` hooks read these\n * to generate ETags, handle 304s, and optionally store responses.\n *\n * When `store` is enabled, the middleware also checks the cache before\n * calling the handler, short-circuiting on cache hits.\n *\n * **Route middleware** — works inside `$action`, `$page`, or any pipeline.\n *\n * ```typescript\n * class UserController {\n * // ETag only (no response caching)\n * getUser = $action({\n * use: [$etag()],\n * handler: async ({ params }) => { ... },\n * });\n *\n * // ETag + response caching (store)\n * getProfile = $action({\n * use: [$etag(true)],\n * handler: async ({ params }) => { ... },\n * });\n *\n * // Fine-grained control\n * getStats = $action({\n * use: [$etag({ store: { ttl: [5, \"minutes\"] }, control: { public: true, maxAge: 300 } })],\n * handler: async ({ params }) => { ... },\n * });\n * }\n * ```\n */\nexport const $etag = (options?: EtagMiddlewareOptions): Middleware => {\n const resolved = resolveEtagOptions(options);\n\n return createMiddleware({\n name: \"$etag\",\n options: resolved as unknown as Record<string, unknown>,\n handler: ({ alepha, next }) => {\n const etagProvider = alepha.inject(ServerEtagProvider);\n\n return async (...args) => {\n const request = alepha.get(\"alepha.http.request\") ?? args[0];\n\n // Set etag options on request metadata for hooks to read\n if (request?.metadata) {\n request.metadata.etagOptions = resolved;\n }\n\n // If store is enabled, check cache before handler\n if (etagProvider.shouldStore(resolved)) {\n if (request) {\n const cached = await etagProvider.checkCache(request, resolved);\n\n // checkCache sets request.metadata.etagHit on cache hit\n // cached may be undefined for 304 responses (no body)\n if (cached !== undefined || request.metadata?.etagHit) {\n return cached;\n }\n }\n }\n\n return next(...args);\n };\n },\n });\n};\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport function resolveEtagOptions(\n options?: EtagMiddlewareOptions,\n): EtagMiddlewareOptionsResolved {\n if (options === true) {\n return { store: true, etag: true };\n }\n\n if (!options) {\n return { etag: true };\n }\n\n return { etag: true, ...options };\n}\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport type EtagMiddlewareOptions =\n /**\n * If true, enables both store and etag.\n */\n | true\n /**\n * Object configuration for fine-grained control.\n */\n | {\n /**\n * If true, enables storing cached responses. (in-memory, Redis, @see alepha/cache for other providers)\n * If a DurationLike is provided, it will be used as the TTL for the cache.\n * If CachePrimitiveOptions is provided, it will be used to configure the cache storage.\n *\n * @default false\n */\n store?: true | DurationLike | CachePrimitiveOptions;\n\n /**\n * If true, enables ETag support for the cached responses.\n *\n * @default true (always true when using $etag)\n */\n etag?: true;\n\n /**\n * - If true, sets Cache-Control to \"public, max-age=300\" (5 minutes).\n * - If string, sets Cache-Control to the provided value directly.\n * - If object, configures Cache-Control directives.\n */\n control?:\n | true\n | string\n | {\n /**\n * Indicates that the response may be cached by any cache.\n */\n public?: boolean;\n /**\n * Indicates that the response is intended for a single user and must not be stored by a shared cache.\n */\n private?: boolean;\n /**\n * Forces caches to submit the request to the origin server for validation before releasing a cached copy.\n */\n noCache?: boolean;\n /**\n * Instructs caches not to store the response.\n */\n noStore?: boolean;\n /**\n * Maximum amount of time a resource is considered fresh.\n * Can be specified as a number (seconds) or as a DurationLike object.\n */\n maxAge?: number | DurationLike;\n /**\n * Overrides max-age for shared caches (e.g., CDNs).\n * Can be specified as a number (seconds) or as a DurationLike object.\n */\n sMaxAge?: number | DurationLike;\n /**\n * Indicates that once a resource becomes stale, caches must not use it without successful validation.\n */\n mustRevalidate?: boolean;\n /**\n * Similar to must-revalidate, but only for shared caches.\n */\n proxyRevalidate?: boolean;\n /**\n * Indicates that the response can be stored but must be revalidated before each use.\n */\n immutable?: boolean;\n /**\n * Time window (in seconds or DurationLike) during which a stale response may be served\n * while a fresh one is fetched in the background.\n * Supported by Cloudflare, modern browsers, and most CDNs.\n */\n staleWhileRevalidate?: number | DurationLike;\n };\n };\n\nexport interface EtagMiddlewareOptionsResolved {\n store?: true | DurationLike | CachePrimitiveOptions;\n etag?: true;\n control?:\n | true\n | string\n | {\n public?: boolean;\n private?: boolean;\n noCache?: boolean;\n noStore?: boolean;\n maxAge?: number | DurationLike;\n sMaxAge?: number | DurationLike;\n mustRevalidate?: boolean;\n proxyRevalidate?: boolean;\n immutable?: boolean;\n staleWhileRevalidate?: number | DurationLike;\n };\n}\n","import { $module } from \"alepha\";\nimport { AlephaCache } from \"alepha/cache\";\nimport { AlephaCrypto } from \"alepha/crypto\";\nimport { ServerEtagProvider } from \"./providers/ServerEtagProvider.ts\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport * from \"./primitives/$etag.ts\";\nexport * from \"./providers/ServerEtagProvider.ts\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\n/**\n * ETag-based response caching.\n *\n * **Features:**\n * - ETag generation and validation\n * - Conditional request handling (304 Not Modified)\n * - Optional response caching (store)\n * - Cache-Control header support\n *\n * @module alepha.server.etag\n */\nexport const AlephaServerEtag = $module({\n name: \"alepha.server.etag\",\n services: [AlephaCache, AlephaCrypto, ServerEtagProvider],\n});\n"],"mappings":";;;;;;AAUA,IAAa,qBAAb,MAAgC;CAC9B,MAAyB,QAAQ;CACjC,SAA4B,QAAQ,MAAM;CAC1C,SAA4B,QAAQ,cAAc;CAClD,OAA0B,QAAQ,gBAAgB;CAClD,QAA2B,OAAwB;EACjD,UAAU;EACV,MAAM;CACR,CAAC;CAED,aAAoB,SAAkC;EACpD,MAAM,OAAO,OAAO,YAAY,WAAW,UAAU,QAAQ,SAAS;EACtE,OAAO,IAAI,KAAK,OAAO,KAAK,MAAM,KAAK,EAAE;CAC3C;CAEA,MAAa,WAAW,OAAoB;EAC1C,MAAM,KAAK,MAAM,WAAW,KAAK,eAAe,KAAK,CAAC;CACxD;;;;;CAMA,MAAa,WACX,SACA,SACc;EACd,IAAI,CAAC,KAAK,YAAY,OAAO,GAC3B;EAKF,MAAM,gBAAgB,KAAK,OAAO,MAAM,IAAI,uBAAuB;EAEnE,MAAM,YAAY;GAChB,QACE,QAAQ,UAAU,eAClB,eAAe,UACf,QAAQ,UACR;GACF,MACE,QAAQ,UAAU,aAClB,OAAO,eAAe,OAAO,QAAQ,OAAO,EAAE;EAClD;EAEA,MAAM,MAAM,KAAK,eAAe,WAAW,iBAAiB,OAAO;EACnE,MAAM,SAAS,MAAM,KAAK,MAAM,IAAI,GAAG;EAEvC,IAAI,CAAC,QAAQ;GACX,KAAK,IAAI,MAAM,cAAc,EAAE,IAAI,CAAC;GACpC;EACF;EAEA,KAAK,IAAI,MAAM,aAAa,EAAE,IAAI,CAAC;EAGnC,QAAQ,aAAa,CAAC;EACtB,QAAQ,SAAS,UAAU;EAG3B,IAAI,QAAQ,OAAO;GAEjB,IACE,QAAQ,UAAU,qBAAqB,OAAO,QAC9C,QAAQ,UAAU,yBAAyB,OAAO,cAClD;IACA,QAAQ,MAAM,SAAS;IACvB,QAAQ,MAAM,UAAU,QAAQ,OAAO,IAAI;IAC3C,QAAQ,MAAM,UAAU,iBAAiB,OAAO,YAAY;IAC5D,KAAK,IAAI,MAAM,6BAA6B;KAC1C;KACA,MAAM,OAAO;IACf,CAAC;IACD,OAAO,QAAQ,MAAM;GACvB;GAEA,QAAQ,MAAM,OAAO,OAAO;GAC5B,QAAQ,MAAM,SAAS,OAAO,UAAU;GAExC,IAAI,OAAO,aACT,QAAQ,MAAM,UAAU,gBAAgB,OAAO,WAAW;GAG5D,QAAQ,MAAM,UAAU,QAAQ,OAAO,IAAI;GAC3C,QAAQ,MAAM,UAAU,iBAAiB,OAAO,YAAY;EAC9D;EAQA,OAJE,OAAO,gBAAgB,qBACnB,KAAK,MAAM,OAAO,IAAI,IACtB,OAAO;CAGf;;;;CASA,mBAAsC,MAAM;EAC1C,IAAI;EACJ,SAAS,OAAO,EAAE,QAAQ,SAAS,eAAe;GAChD,MAAM,UAAU,QAAQ,UACpB;GACJ,IAAI,CAAC,WAAW,CAAC,KAAK,YAAY,OAAO,GACvC;GAIF,IAAI,QAAQ,UAAU,SACpB;GAIF,IAAI,QAAQ,MAAM,UAAU,QAAQ,MAAM,UAAU,KAClD;GAGF,IAAI,CAAC,UACH;GAGF,MAAM,cACJ,OAAO,aAAa,WAAW,eAAe;GAChD,MAAM,OACJ,gBAAgB,eAAe,WAAW,KAAK,UAAU,QAAQ;GAEnE,MAAM,gBAAgB,KAAK,aAAa,IAAI;GAC5C,MAAM,eAAe,KAAK,KAAK,YAAY;GAE3C,MAAM,MAAM,KAAK,eAAe,OAAO,OAAO,OAAO;GAErD,KAAK,IAAI,MAAM,2BAA2B;IACxC;IACA,QAAQ,OAAO;GACjB,CAAC;GAED,MAAM,KAAK,MAAM,IAAI,KAAK;IAClB;IACN;IACa;IACb,MAAM;GACR,CAAC;GAGD,MAAM,eAAe,KAAK,wBAAwB,OAAO;GACzD,IAAI,cACF,QAAQ,MAAM,UAAU,iBAAiB,YAAY;EAEzD;CACF,CAAC;;;;;CAMD,SAA4B,MAAM;EAChC,IAAI;EACJ,UAAU,EAAE,cAAc;GACxB,MAAM,UAAU,QAAQ,UACpB;GACJ,IAAI,CAAC,SACH;GAGF,MAAM,cAAc,KAAK,YAAY,OAAO;GAC5C,MAAM,gBAAgB,KAAK,cAAc,OAAO;GAEhD,IAAI,QAAQ,MAAM,QAAQ,MAExB;GAGF,IACE,CAAC,eACD,iBACA,QAAQ,MAAM,QAAQ,SACrB,OAAO,QAAQ,MAAM,SAAS,YAC7B,OAAO,SAAS,QAAQ,MAAM,IAAI,IACpC;IACA,MAAM,gBAAgB,KAAK,aAAa,QAAQ,MAAM,IAAI;IAE1D,IAAI,QAAQ,QAAQ,qBAAqB,eAAe;KACtD,QAAQ,MAAM,SAAS;KACvB,QAAQ,MAAM,OAAO,KAAA;KACrB,QAAQ,MAAM,UAAU,QAAQ,aAAa;KAC7C,KAAK,IAAI,MAAM,qCAAqC;MAClD,OAAO,QAAQ;MACf,MAAM;KACR,CAAC;KACD;IACF;GACF;EACF;CACF,CAAC;;;;CAKD,aAAgC,MAAM;EACpC,IAAI;EACJ,UAAU;EACV,SAAS,OAAO,EAAE,OAAO,SAAS,eAAe;GAC/C,MAAM,UAAU,QAAQ,UACpB;GACJ,IAAI,CAAC,SACH;GAIF,MAAM,eAAe,KAAK,wBAAwB,OAAO;GACzD,IAAI,cACF,SAAS,QAAQ,mBAAmB;GAGtC,MAAM,cAAc,KAAK,YAAY,OAAO;GAC5C,MAAM,gBAAgB,KAAK,cAAc,OAAO;GAGhD,IAAI,CAAC,eAAe,CAAC,eACnB;GAIF,IAAI,QAAQ,UAAU,SACpB;GAIF,IAAI,SAAS,UAAU,SAAS,UAAU,KACxC;GAIF,SAAS,YAAY,CAAC;GAEtB,MAAM,MAAM,KAAK,eAAe,OAAO,OAAO;GAG9C,IAAI,SAAS,gBAAgB,kBAAkB,aAAa;IAE1D,MAAM,CAAC,cAAc,eACnB,SAAS,KACT,IAAI;IAGN,SAAS,OAAO;IAGhB,KAAK,sBACH,aACA,KACA,SAAS,QACT,SAAS,UAAU,iBACnB,aACF,EACG,MAAM,SAAS;KACd,IAAI,iBAAiB,MACnB,KAAK,IAAI,MAAM,2BAA2B;MAAE;MAAK;KAAK,CAAC;IAE3D,CAAC,EACA,OAAO,QAAQ;KACd,KAAK,IAAI,KAAK,0BAA0B;MAAE;MAAK,OAAO;KAAI,CAAC;IAC7D,CAAC;IAEH;GACF;GAGA,IAAI,OAAO,SAAS,SAAS,UAC3B;GAGF,MAAM,gBAAgB,KAAK,aAAa,SAAS,IAAI;GACrD,MAAM,eAAe,KAAK,KAAK,YAAY;GAG3C,IAAI,aAAa;IACf,KAAK,IAAI,MAAM,oBAAoB;KACjC;KACA,OAAO,MAAM;KACb,MAAM;IACR,CAAC;IAED,MAAM,KAAK,MAAM,IAAI,KAAK;KACxB,MAAM,SAAS;KACf,QAAQ,SAAS;KACjB,aAAa,SAAS,UAAU;KAChC;KACA,MAAM;IACR,CAAC;GACH;GAGA,IAAI,eAAe;IACjB,SAAS,QAAQ,OAAO;IACxB,SAAS,QAAQ,mBAAmB;GACtC;EACF;CACF,CAAC;CAMD,wBACE,SACoB;EACpB,IAAI,CAAC,SACH;EAGF,MAAM,UAAU,QAAQ;EACxB,IAAI,CAAC,SACH;EAIF,IAAI,OAAO,YAAY,UACrB,OAAO;EAIT,IAAI,YAAY,MACd,OAAO;EAIT,MAAM,aAAuB,CAAC;EAE9B,IAAI,QAAQ,QACV,WAAW,KAAK,QAAQ;EAE1B,IAAI,QAAQ,SACV,WAAW,KAAK,SAAS;EAE3B,IAAI,QAAQ,SACV,WAAW,KAAK,UAAU;EAE5B,IAAI,QAAQ,SACV,WAAW,KAAK,UAAU;EAE5B,IAAI,QAAQ,WAAW,KAAA,GAAW;GAChC,MAAM,gBAAgB,KAAK,kBAAkB,QAAQ,MAAM;GAC3D,WAAW,KAAK,WAAW,eAAe;EAC5C;EACA,IAAI,QAAQ,YAAY,KAAA,GAAW;GACjC,MAAM,iBAAiB,KAAK,kBAAkB,QAAQ,OAAO;GAC7D,WAAW,KAAK,YAAY,gBAAgB;EAC9C;EACA,IAAI,QAAQ,gBACV,WAAW,KAAK,iBAAiB;EAEnC,IAAI,QAAQ,iBACV,WAAW,KAAK,kBAAkB;EAEpC,IAAI,QAAQ,WACV,WAAW,KAAK,WAAW;EAE7B,IAAI,QAAQ,yBAAyB,KAAA,GAAW;GAC9C,MAAM,UAAU,KAAK,kBAAkB,QAAQ,oBAAoB;GACnE,WAAW,KAAK,0BAA0B,SAAS;EACrD;EAEA,OAAO,WAAW,SAAS,IAAI,WAAW,KAAK,IAAI,IAAI,KAAA;CACzD;CAEA,YAAmB,SAAkD;EACnE,IAAI,CAAC,SAAS,OAAO;EACrB,IAAI,QAAQ,OAAO,OAAO;EAC1B,OAAO;CACT;CAEA,cAAqB,SAAkD;EACrE,IAAI,CAAC,SAAS,OAAO;EACrB,IAAI,QAAQ,MAAM,OAAO;EACzB,OAAO;CACT;CAMA,kBAA4B,UAAyC;EACnE,IAAI,OAAO,aAAa,UACtB,OAAO;EAGT,OAAO,KAAK,KAAK,SAAS,QAAQ,EAAE,UAAU;CAChD;CAEA,eAAyB,OAAoB,QAAgC;EAC3E,MAAM,SAAmB,CAAC;EAC1B,KAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,QAAQ,UAAU,CAAC,CAAC,GAC5D,OAAO,KAAK,GAAG,IAAI,GAAG,OAAO;EAE/B,KAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,QAAQ,SAAS,CAAC,CAAC,GAC3D,OAAO,KAAK,GAAG,IAAI,GAAG,OAAO;EAG/B,OAAO,GAAG,MAAM,OAAO,IAAI,MAAM,QAAQ,IAAI,WAAW,KAAK,EAAE,EAAE,GAAG,OAAO,KAAK,GAAG,EAAE,WAAW,KAAK,EAAE;CACzG;;;;;CAMA,MAAgB,sBACd,QACA,KACA,QACA,aACA,cAC6B;EAC7B,MAAM,SAAuB,CAAC;EAC9B,MAAM,SAAS,OAAO,UAAU;EAEhC,IAAI;GACF,OAAO,MAAM;IACX,MAAM,EAAE,MAAM,UAAU,MAAM,OAAO,KAAK;IAC1C,IAAI,MAAM;IACV,OAAO,KAAK,KAAK;GACnB;GAGA,MAAM,UAAU,IAAI,YAAY;GAChC,MAAM,OACJ,OACG,KAAK,UAAU,QAAQ,OAAO,OAAO,EAAE,QAAQ,KAAK,CAAC,CAAC,EACtD,KAAK,EAAE,IAAI,QAAQ,OAAO;GAE/B,MAAM,OAAO,KAAK,aAAa,IAAI;GACnC,MAAM,eAAe,KAAK,KAAK,YAAY;GAE3C,KAAK,IAAI,MAAM,6BAA6B,EAAE,IAAI,CAAC;GAEnD,MAAM,KAAK,MAAM,IAAI,KAAK;IACxB;IACA;IACA;IACA;IACA;GACF,CAAC;GAED,OAAO,eAAe,OAAO,KAAA;EAC/B,UAAU;GACR,OAAO,YAAY;EACrB;CACF;AACF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;ACvaA,MAAa,SAAS,YAAgD;CACpE,MAAM,WAAW,mBAAmB,OAAO;CAE3C,OAAO,iBAAiB;EACtB,MAAM;EACN,SAAS;EACT,UAAU,EAAE,QAAQ,WAAW;GAC7B,MAAM,eAAe,OAAO,OAAO,kBAAkB;GAErD,OAAO,OAAO,GAAG,SAAS;IACxB,MAAM,UAAU,OAAO,IAAI,qBAAqB,KAAK,KAAK;IAG1D,IAAI,SAAS,UACX,QAAQ,SAAS,cAAc;IAIjC,IAAI,aAAa,YAAY,QAAQ;SAC/B,SAAS;MACX,MAAM,SAAS,MAAM,aAAa,WAAW,SAAS,QAAQ;MAI9D,IAAI,WAAW,KAAA,KAAa,QAAQ,UAAU,SAC5C,OAAO;KAEX;;IAGF,OAAO,KAAK,GAAG,IAAI;GACrB;EACF;CACF,CAAC;AACH;AAIA,SAAgB,mBACd,SAC+B;CAC/B,IAAI,YAAY,MACd,OAAO;EAAE,OAAO;EAAM,MAAM;CAAK;CAGnC,IAAI,CAAC,SACH,OAAO,EAAE,MAAM,KAAK;CAGtB,OAAO;EAAE,MAAM;EAAM,GAAG;CAAQ;AAClC;;;;;;;;;;;;;;ACpEA,MAAa,mBAAmB,QAAQ;CACtC,MAAM;CACN,UAAU;EAAC;EAAa;EAAc;CAAkB;AAC1D,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../../../src/server/etag/providers/ServerEtagProvider.ts","../../../src/server/etag/primitives/$etag.ts","../../../src/server/etag/index.ts"],"sourcesContent":["import { $hook, $inject, Alepha } from \"alepha\";\nimport { $cache } from \"alepha/cache\";\nimport { CryptoProvider } from \"alepha/crypto\";\nimport { DateTimeProvider, type DurationLike } from \"alepha/datetime\";\nimport { $logger } from \"alepha/logger\";\nimport type { ServerRequest, ServerRoute } from \"alepha/server\";\nimport type { EtagMiddlewareOptionsResolved } from \"../primitives/$etag.ts\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport class ServerEtagProvider {\n protected readonly log = $logger();\n protected readonly alepha = $inject(Alepha);\n protected readonly crypto = $inject(CryptoProvider);\n protected readonly time = $inject(DateTimeProvider);\n protected readonly cache = $cache<RouteCacheEntry>({\n provider: \"memory\",\n name: \"http:server\",\n });\n\n public generateETag(content: string | Buffer): string {\n const data = typeof content === \"string\" ? content : content.toString();\n return `\"${this.crypto.hash(data, \"md5\")}\"`;\n }\n\n public async invalidate(route: ServerRoute) {\n await this.cache.invalidate(this.createCacheKey(route));\n }\n\n /**\n * Check cache for a stored response. Returns the cached body if found, undefined otherwise.\n * Called by the $etag() middleware before the handler runs.\n */\n public async checkCache(\n request: ServerRequest,\n options: EtagMiddlewareOptionsResolved,\n ): Promise<any> {\n if (!this.shouldStore(options)) {\n return undefined;\n }\n\n // Build key from route metadata (set by ServerRouterProvider before handler)\n // or from action request context (for .run() direct calls)\n const actionRequest = this.alepha.store.get(\"alepha.action.request\");\n\n const keySource = {\n method:\n request.metadata?.routeMethod ??\n actionRequest?.method ??\n request.method ??\n \"GET\",\n path:\n request.metadata?.routePath ??\n String(actionRequest?.url ?? request.url ?? \"\"),\n } as ServerRoute;\n\n const key = this.createCacheKey(keySource, actionRequest ?? request);\n const cached = await this.cache.get(key);\n\n if (!cached) {\n this.log.trace(\"Cache miss\", { key });\n return undefined;\n }\n\n this.log.trace(\"Cache hit\", { key });\n\n // Mark as cache hit in request metadata\n request.metadata ??= {} as any;\n request.metadata.etagHit = true;\n\n // For HTTP routes, set reply headers\n if (request.reply) {\n // Check if client has matching ETag - return 304\n if (\n request.headers?.[\"if-none-match\"] === cached.hash ||\n request.headers?.[\"if-modified-since\"] === cached.lastModified\n ) {\n request.reply.status = 304;\n request.reply.setHeader(\"etag\", cached.hash);\n request.reply.setHeader(\"last-modified\", cached.lastModified);\n this.log.trace(\"ETag match, returning 304\", {\n key,\n etag: cached.hash,\n });\n return request.reply.body;\n }\n\n request.reply.body = cached.body;\n request.reply.status = cached.status ?? 200;\n\n if (cached.contentType) {\n request.reply.setHeader(\"Content-Type\", cached.contentType);\n }\n\n request.reply.setHeader(\"etag\", cached.hash);\n request.reply.setHeader(\"last-modified\", cached.lastModified);\n }\n\n // For action direct invocations, return body\n const body =\n cached.contentType === \"application/json\"\n ? JSON.parse(cached.body)\n : cached.body;\n\n return body;\n }\n\n // -------------------------------------------------------------------------------------------------------------------\n // Hooks (post-handler, read from request.metadata)\n // -------------------------------------------------------------------------------------------------------------------\n\n /**\n * After an action response, store it in cache if store is enabled.\n */\n protected readonly onActionResponse = $hook({\n on: \"action:onResponse\",\n handler: async ({ action, request, response }) => {\n const options = request.metadata\n ?.etagOptions as EtagMiddlewareOptionsResolved;\n if (!options || !this.shouldStore(options)) {\n return;\n }\n\n // Skip if this was a cache hit (don't re-store)\n if (request.metadata?.etagHit) {\n return;\n }\n\n // Don't cache error responses (status >= 400)\n if (request.reply.status && request.reply.status >= 400) {\n return;\n }\n\n if (!response) {\n return;\n }\n\n const contentType =\n typeof response === \"string\" ? \"text/plain\" : \"application/json\";\n const body =\n contentType === \"text/plain\" ? response : JSON.stringify(response);\n\n const generatedEtag = this.generateETag(body);\n const lastModified = this.time.toISOString();\n\n const key = this.createCacheKey(action.route, request);\n\n this.log.trace(\"Storing action response\", {\n key,\n action: action.name,\n });\n\n await this.cache.set(key, {\n body: body,\n lastModified,\n contentType: contentType,\n hash: generatedEtag,\n });\n\n // Set Cache-Control header if configured\n const cacheControl = this.buildCacheControlHeader(options);\n if (cacheControl) {\n request.reply.setHeader(\"cache-control\", cacheControl);\n }\n },\n });\n\n /**\n * Before sending the response, check ETag for etag-only routes.\n * This handles the case where etag is enabled but store is not.\n */\n protected readonly onSend = $hook({\n on: \"server:onSend\",\n handler: ({ request }) => {\n const options = request.metadata\n ?.etagOptions as EtagMiddlewareOptionsResolved;\n if (!options) {\n return;\n }\n\n const shouldStore = this.shouldStore(options);\n const shouldUseEtag = this.shouldUseEtag(options);\n\n if (request.reply.headers.etag) {\n // ETag already set, skip\n return;\n }\n\n if (\n !shouldStore &&\n shouldUseEtag &&\n request.reply.body != null &&\n (typeof request.reply.body === \"string\" ||\n Buffer.isBuffer(request.reply.body))\n ) {\n const generatedEtag = this.generateETag(request.reply.body);\n\n if (request.headers[\"if-none-match\"] === generatedEtag) {\n request.reply.status = 304;\n request.reply.body = undefined;\n request.reply.setHeader(\"etag\", generatedEtag);\n this.log.trace(\"ETag match on send, returning 304\", {\n route: request.url,\n etag: generatedEtag,\n });\n return;\n }\n }\n },\n });\n\n /**\n * After the response is generated, store it and set ETag headers.\n */\n protected readonly onResponse = $hook({\n on: \"server:onResponse\",\n priority: \"first\",\n handler: async ({ route, request, response }) => {\n const options = request.metadata\n ?.etagOptions as EtagMiddlewareOptionsResolved;\n if (!options) {\n return;\n }\n\n // Set Cache-Control header if configured\n const cacheControl = this.buildCacheControlHeader(options);\n if (cacheControl) {\n response.headers[\"cache-control\"] = cacheControl;\n }\n\n const shouldStore = this.shouldStore(options);\n const shouldUseEtag = this.shouldUseEtag(options);\n\n // Skip if neither cache nor etag is enabled\n if (!shouldStore && !shouldUseEtag) {\n return;\n }\n\n // Skip if this was a cache hit (don't re-store)\n if (request.metadata?.etagHit) {\n return;\n }\n\n // Don't cache error responses (status >= 400)\n if (response.status && response.status >= 400) {\n return;\n }\n\n // Initialize headers if not present\n response.headers ??= {};\n\n const key = this.createCacheKey(route, request);\n\n // Handle ReadableStream responses (e.g., SSR streaming)\n if (response.body instanceof ReadableStream && shouldStore) {\n // Tee the stream: one for client, one for cache collection\n const [clientStream, cacheStream] = (\n response.body as ReadableStream<Uint8Array>\n ).tee();\n\n // Replace response body with client stream (continues streaming to client)\n response.body = clientStream as typeof response.body;\n\n // Collect cache stream in background (non-blocking)\n this.collectStreamForCache(\n cacheStream,\n key,\n response.status,\n response.headers?.[\"content-type\"],\n shouldUseEtag,\n )\n .then((hash) => {\n if (shouldUseEtag && hash) {\n this.log.trace(\"Stream cached with hash\", { key, hash });\n }\n })\n .catch((err) => {\n this.log.warn(\"Failed to cache stream\", { key, error: err });\n });\n\n return;\n }\n\n // Only process string responses (text, html, json, etc.)\n if (typeof response.body !== \"string\") {\n return;\n }\n\n const generatedEtag = this.generateETag(response.body);\n const lastModified = this.time.toISOString();\n\n // Store response if storing is enabled\n if (shouldStore) {\n this.log.trace(\"Storing response\", {\n key,\n route: route.path,\n etag: shouldUseEtag,\n });\n\n await this.cache.set(key, {\n body: response.body,\n status: response.status,\n contentType: response.headers?.[\"content-type\"],\n lastModified,\n hash: generatedEtag,\n });\n }\n\n // Set ETag headers if etag is enabled\n if (shouldUseEtag) {\n response.headers.etag = generatedEtag;\n response.headers[\"last-modified\"] = lastModified;\n }\n },\n });\n\n // -------------------------------------------------------------------------------------------------------------------\n // Public helpers\n // -------------------------------------------------------------------------------------------------------------------\n\n public buildCacheControlHeader(\n options?: EtagMiddlewareOptionsResolved,\n ): string | undefined {\n if (!options) {\n return undefined;\n }\n\n const control = options.control;\n if (!control) {\n return undefined;\n }\n\n // If control is a string, return it directly\n if (typeof control === \"string\") {\n return control;\n }\n\n // If control is true, return default Cache-Control\n if (control === true) {\n return \"public, max-age=300\";\n }\n\n // Build Cache-Control from object directives\n const directives: string[] = [];\n\n if (control.public) {\n directives.push(\"public\");\n }\n if (control.private) {\n directives.push(\"private\");\n }\n if (control.noCache) {\n directives.push(\"no-cache\");\n }\n if (control.noStore) {\n directives.push(\"no-store\");\n }\n if (control.maxAge !== undefined) {\n const maxAgeSeconds = this.durationToSeconds(control.maxAge);\n directives.push(`max-age=${maxAgeSeconds}`);\n }\n if (control.sMaxAge !== undefined) {\n const sMaxAgeSeconds = this.durationToSeconds(control.sMaxAge);\n directives.push(`s-maxage=${sMaxAgeSeconds}`);\n }\n if (control.mustRevalidate) {\n directives.push(\"must-revalidate\");\n }\n if (control.proxyRevalidate) {\n directives.push(\"proxy-revalidate\");\n }\n if (control.immutable) {\n directives.push(\"immutable\");\n }\n if (control.staleWhileRevalidate !== undefined) {\n const seconds = this.durationToSeconds(control.staleWhileRevalidate);\n directives.push(`stale-while-revalidate=${seconds}`);\n }\n\n return directives.length > 0 ? directives.join(\", \") : undefined;\n }\n\n public shouldStore(options?: EtagMiddlewareOptionsResolved): boolean {\n if (!options) return false;\n if (options.store) return true;\n return false;\n }\n\n public shouldUseEtag(options?: EtagMiddlewareOptionsResolved): boolean {\n if (!options) return false;\n if (options.etag) return true;\n return false;\n }\n\n // -------------------------------------------------------------------------------------------------------------------\n // Protected helpers\n // -------------------------------------------------------------------------------------------------------------------\n\n protected durationToSeconds(duration: number | DurationLike): number {\n if (typeof duration === \"number\") {\n return duration;\n }\n\n return this.time.duration(duration).asSeconds();\n }\n\n protected createCacheKey(route: ServerRoute, config?: ServerRequest): string {\n const params: string[] = [];\n for (const [key, value] of Object.entries(config?.params ?? {})) {\n params.push(`${key}=${value}`);\n }\n for (const [key, value] of Object.entries(config?.query ?? {})) {\n params.push(`${key}=${value}`);\n }\n\n return `${route.method}:${(route.path ?? \"\").replaceAll(\":\", \"\")}:${params.join(\",\").replaceAll(\":\", \"\")}`;\n }\n\n /**\n * Collect a ReadableStream into a string and store it in the cache.\n * This runs in the background while the original stream is sent to the client.\n */\n protected async collectStreamForCache(\n stream: ReadableStream<Uint8Array>,\n key: string,\n status: number | undefined,\n contentType: string | undefined,\n generateEtag: boolean,\n ): Promise<string | undefined> {\n const chunks: Uint8Array[] = [];\n const reader = stream.getReader();\n\n try {\n while (true) {\n const { done, value } = await reader.read();\n if (done) break;\n chunks.push(value);\n }\n\n // Combine chunks into a single string\n const decoder = new TextDecoder();\n const body =\n chunks\n .map((chunk) => decoder.decode(chunk, { stream: true }))\n .join(\"\") + decoder.decode(); // Flush remaining\n\n const hash = this.generateETag(body);\n const lastModified = this.time.toISOString();\n\n this.log.trace(\"Storing streamed response\", { key });\n\n await this.cache.set(key, {\n body,\n status,\n contentType,\n lastModified,\n hash,\n });\n\n return generateEtag ? hash : undefined;\n } finally {\n reader.releaseLock();\n }\n }\n}\n\n// ---------------------------------------------------------------------------------------------------------------------\n\ninterface RouteCacheEntry {\n contentType?: string;\n body: any;\n status?: number;\n lastModified: string;\n hash: string;\n}\n","import { createMiddleware, type Middleware } from \"alepha\";\nimport type { CachePrimitiveOptions } from \"alepha/cache\";\nimport type { DurationLike } from \"alepha/datetime\";\nimport { ServerEtagProvider } from \"../providers/ServerEtagProvider.ts\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\n/**\n * Middleware that enables ETag-based response caching per-route.\n *\n * Sets per-request etag options in the ALS context.\n * The global `ServerEtagProvider` hooks read these\n * to generate ETags, handle 304s, and optionally store responses.\n *\n * When `store` is enabled, the middleware also checks the cache before\n * calling the handler, short-circuiting on cache hits.\n *\n * **Route middleware** — works inside `$action`, `$page`, or any pipeline.\n *\n * ```typescript\n * class UserController {\n * // ETag only (no response caching)\n * getUser = $action({\n * use: [$etag()],\n * handler: async ({ params }) => { ... },\n * });\n *\n * // ETag + response caching (store)\n * getProfile = $action({\n * use: [$etag(true)],\n * handler: async ({ params }) => { ... },\n * });\n *\n * // Fine-grained control\n * getStats = $action({\n * use: [$etag({ store: { ttl: [5, \"minutes\"] }, control: { public: true, maxAge: 300 } })],\n * handler: async ({ params }) => { ... },\n * });\n * }\n * ```\n */\nexport const $etag = (options?: EtagMiddlewareOptions): Middleware => {\n const resolved = resolveEtagOptions(options);\n\n return createMiddleware({\n name: \"$etag\",\n options: resolved as unknown as Record<string, unknown>,\n handler: ({ alepha, next }) => {\n const etagProvider = alepha.inject(ServerEtagProvider);\n\n return async (...args) => {\n const request = alepha.get(\"alepha.http.request\") ?? args[0];\n\n // Set etag options on request metadata for hooks to read\n if (request?.metadata) {\n request.metadata.etagOptions = resolved;\n }\n\n // If store is enabled, check cache before handler\n if (etagProvider.shouldStore(resolved)) {\n if (request) {\n const cached = await etagProvider.checkCache(request, resolved);\n\n // checkCache sets request.metadata.etagHit on cache hit\n // cached may be undefined for 304 responses (no body)\n if (cached !== undefined || request.metadata?.etagHit) {\n return cached;\n }\n }\n }\n\n return next(...args);\n };\n },\n });\n};\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport function resolveEtagOptions(\n options?: EtagMiddlewareOptions,\n): EtagMiddlewareOptionsResolved {\n if (options === true) {\n return { store: true, etag: true };\n }\n\n if (!options) {\n return { etag: true };\n }\n\n return { etag: true, ...options };\n}\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport type EtagMiddlewareOptions =\n /**\n * If true, enables both store and etag.\n */\n | true\n /**\n * Object configuration for fine-grained control.\n */\n | {\n /**\n * If true, enables storing cached responses. (in-memory, Redis, @see alepha/cache for other providers)\n * If a DurationLike is provided, it will be used as the TTL for the cache.\n * If CachePrimitiveOptions is provided, it will be used to configure the cache storage.\n *\n * @default false\n */\n store?: true | DurationLike | CachePrimitiveOptions;\n\n /**\n * If true, enables ETag support for the cached responses.\n *\n * @default true (always true when using $etag)\n */\n etag?: true;\n\n /**\n * - If true, sets Cache-Control to \"public, max-age=300\" (5 minutes).\n * - If string, sets Cache-Control to the provided value directly.\n * - If object, configures Cache-Control directives.\n */\n control?:\n | true\n | string\n | {\n /**\n * Indicates that the response may be cached by any cache.\n */\n public?: boolean;\n /**\n * Indicates that the response is intended for a single user and must not be stored by a shared cache.\n */\n private?: boolean;\n /**\n * Forces caches to submit the request to the origin server for validation before releasing a cached copy.\n */\n noCache?: boolean;\n /**\n * Instructs caches not to store the response.\n */\n noStore?: boolean;\n /**\n * Maximum amount of time a resource is considered fresh.\n * Can be specified as a number (seconds) or as a DurationLike object.\n */\n maxAge?: number | DurationLike;\n /**\n * Overrides max-age for shared caches (e.g., CDNs).\n * Can be specified as a number (seconds) or as a DurationLike object.\n */\n sMaxAge?: number | DurationLike;\n /**\n * Indicates that once a resource becomes stale, caches must not use it without successful validation.\n */\n mustRevalidate?: boolean;\n /**\n * Similar to must-revalidate, but only for shared caches.\n */\n proxyRevalidate?: boolean;\n /**\n * Indicates that the response can be stored but must be revalidated before each use.\n */\n immutable?: boolean;\n /**\n * Time window (in seconds or DurationLike) during which a stale response may be served\n * while a fresh one is fetched in the background.\n * Supported by Cloudflare, modern browsers, and most CDNs.\n */\n staleWhileRevalidate?: number | DurationLike;\n };\n };\n\nexport interface EtagMiddlewareOptionsResolved {\n store?: true | DurationLike | CachePrimitiveOptions;\n etag?: true;\n control?:\n | true\n | string\n | {\n public?: boolean;\n private?: boolean;\n noCache?: boolean;\n noStore?: boolean;\n maxAge?: number | DurationLike;\n sMaxAge?: number | DurationLike;\n mustRevalidate?: boolean;\n proxyRevalidate?: boolean;\n immutable?: boolean;\n staleWhileRevalidate?: number | DurationLike;\n };\n}\n","import { $module } from \"alepha\";\nimport { AlephaCache } from \"alepha/cache\";\nimport { AlephaCrypto } from \"alepha/crypto\";\nimport { ServerEtagProvider } from \"./providers/ServerEtagProvider.ts\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport * from \"./primitives/$etag.ts\";\nexport * from \"./providers/ServerEtagProvider.ts\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\n/**\n * ETag-based response caching.\n *\n * **Features:**\n * - ETag generation and validation\n * - Conditional request handling (304 Not Modified)\n * - Optional response caching (store)\n * - Cache-Control header support\n *\n * @module alepha.server.etag\n */\nexport const AlephaServerEtag = $module({\n name: \"alepha.server.etag\",\n services: [AlephaCache, AlephaCrypto, ServerEtagProvider],\n});\n"],"mappings":"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"}
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { Alepha } from "alepha";
|
|
2
2
|
import { DateTimeProvider } from "alepha/datetime";
|
|
3
|
-
|
|
4
3
|
//#region ../../src/server/health/providers/ServerHealthProvider.d.ts
|
|
5
4
|
/**
|
|
6
5
|
* Register `/health` & `/healthz` endpoint.
|
|
@@ -11,20 +10,20 @@ declare class ServerHealthProvider {
|
|
|
11
10
|
protected readonly time: DateTimeProvider;
|
|
12
11
|
protected readonly alepha: Alepha;
|
|
13
12
|
readonly health: import("alepha/server").RoutePrimitive<{
|
|
14
|
-
response: import("
|
|
15
|
-
message: import("
|
|
16
|
-
uptime: import("
|
|
17
|
-
date: import("
|
|
18
|
-
ready: import("
|
|
19
|
-
}>;
|
|
13
|
+
response: import("zod").ZodObject<{
|
|
14
|
+
message: import("zod").ZodString;
|
|
15
|
+
uptime: import("zod").ZodNumber;
|
|
16
|
+
date: import("zod").ZodString;
|
|
17
|
+
ready: import("zod").ZodBoolean;
|
|
18
|
+
}, import("zod/v4/core").$strip>;
|
|
20
19
|
}>;
|
|
21
20
|
readonly healthz: import("alepha/server").RoutePrimitive<{
|
|
22
|
-
response: import("
|
|
23
|
-
message: import("
|
|
24
|
-
uptime: import("
|
|
25
|
-
date: import("
|
|
26
|
-
ready: import("
|
|
27
|
-
}>;
|
|
21
|
+
response: import("zod").ZodObject<{
|
|
22
|
+
message: import("zod").ZodString;
|
|
23
|
+
uptime: import("zod").ZodNumber;
|
|
24
|
+
date: import("zod").ZodString;
|
|
25
|
+
ready: import("zod").ZodBoolean;
|
|
26
|
+
}, import("zod/v4/core").$strip>;
|
|
28
27
|
}>;
|
|
29
28
|
protected healthCheck(): {
|
|
30
29
|
message: string;
|
|
@@ -35,12 +34,12 @@ declare class ServerHealthProvider {
|
|
|
35
34
|
}
|
|
36
35
|
//#endregion
|
|
37
36
|
//#region ../../src/server/health/schemas/healthSchema.d.ts
|
|
38
|
-
declare const healthSchema: import("
|
|
39
|
-
message: import("
|
|
40
|
-
uptime: import("
|
|
41
|
-
date: import("
|
|
42
|
-
ready: import("
|
|
43
|
-
}>;
|
|
37
|
+
declare const healthSchema: import("zod").ZodObject<{
|
|
38
|
+
message: import("zod").ZodString;
|
|
39
|
+
uptime: import("zod").ZodNumber;
|
|
40
|
+
date: import("zod").ZodString;
|
|
41
|
+
ready: import("zod").ZodBoolean;
|
|
42
|
+
}, import("zod/v4/core").$strip>;
|
|
44
43
|
//#endregion
|
|
45
44
|
//#region ../../src/server/health/index.d.ts
|
|
46
45
|
/**
|
|
@@ -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":"
|
|
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":";;;;;;;;cAUa;qBACQ,MAAM;qBACN,QAAM;WAET,gCAAM;;;;;;;;WASN,iCAAO;;;;;;;;YASb;IAEN;IACA;IACA;IACA;;;;;cCnCO,4BAAY;;;;;;;;;;;;;;;;cCiBZ,qCAAkB,yBAAA"}
|
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
import { $inject, $module, Alepha,
|
|
1
|
+
import { $inject, $module, Alepha, z } from "alepha";
|
|
2
2
|
import { $route, AlephaServer } from "alepha/server";
|
|
3
3
|
import { DateTimeProvider } from "alepha/datetime";
|
|
4
4
|
//#region ../../src/server/health/schemas/healthSchema.ts
|
|
5
|
-
const healthSchema =
|
|
6
|
-
message:
|
|
7
|
-
uptime:
|
|
8
|
-
date:
|
|
9
|
-
ready:
|
|
5
|
+
const healthSchema = z.object({
|
|
6
|
+
message: z.text(),
|
|
7
|
+
uptime: z.number(),
|
|
8
|
+
date: z.datetime(),
|
|
9
|
+
ready: z.boolean()
|
|
10
10
|
});
|
|
11
11
|
//#endregion
|
|
12
12
|
//#region ../../src/server/health/providers/ServerHealthProvider.ts
|
|
@@ -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 {
|
|
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 { z } from \"alepha\";\n\nexport const healthSchema = z.object({\n message: z.text(),\n uptime: z.number(),\n date: z.datetime(),\n ready: z.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 * 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,KAAK;CAChB,QAAQ,EAAE,OAAO;CACjB,MAAM,EAAE,SAAS;CACjB,OAAO,EAAE,QAAQ;AACnB,CAAC;;;;;;;;ACGD,IAAa,uBAAb,MAAkC;CAChC,OAA4C,QAAQ,gBAAgB;CACpE,SAA4B,QAAQ,MAAM;CAE1C,SAAyB,OAAO;EAC9B,MAAM;EACN,QAAQ,EACN,UAAU,aACZ;EACA,QAAQ;EACR,eAAe,KAAK,YAAY;CAClC,CAAC;CAED,UAA0B,OAAO;EAC/B,MAAM;EACN,QAAQ,EACN,UAAU,aACZ;EACA,QAAQ;EACR,eAAe,KAAK,YAAY;CAClC,CAAC;CAED,cAAwB;EACtB,OAAO;GACL,SAAS;GACT,QAAQ,KAAK,MAAM,QAAQ,OAAO,CAAC;GACnC,MAAM,KAAK,KAAK,aAAa;GAC7B,OAAO,KAAK,OAAO,QAAQ;EAC7B;CACF;AACF;;;;;;;;;;;ACrBA,MAAa,qBAAqB,QAAQ;CACxC,MAAM;CACN,UAAU,CAAC,cAAc,oBAAoB;AAC/C,CAAC"}
|
|
@@ -1,18 +1,18 @@
|
|
|
1
|
-
import { $atom, $inject, $module, $state, Alepha, AlephaError, KIND, Primitive, createPrimitive,
|
|
1
|
+
import { $atom, $inject, $module, $state, Alepha, AlephaError, KIND, Primitive, createPrimitive, z } from "alepha";
|
|
2
2
|
import { $logger } from "alepha/logger";
|
|
3
3
|
import { HttpClient, HttpError, ServerReply, UnauthorizedError } from "alepha/server";
|
|
4
4
|
//#region ../../src/server/links/schemas/apiLinksResponseSchema.ts
|
|
5
|
-
const apiActionSchema =
|
|
6
|
-
path:
|
|
7
|
-
method:
|
|
8
|
-
contentType:
|
|
9
|
-
kind:
|
|
10
|
-
service:
|
|
5
|
+
const apiActionSchema = z.object({
|
|
6
|
+
path: z.text({ description: "Pathname used to access the action." }),
|
|
7
|
+
method: z.text({ description: "HTTP method. Omitted when GET (the default for ~75% of actions)." }).optional(),
|
|
8
|
+
contentType: z.text({ description: "Content type for the request body. Only present for non-JSON types (e.g. 'multipart/form-data'). When absent, defaults to application/json." }).optional(),
|
|
9
|
+
kind: z.text({ description: "Action kind. Used to distinguish special action types (e.g. 'sse' for Server-Sent Events streams)." }).optional(),
|
|
10
|
+
service: z.text({ description: "Service name associated with the action, used for service discovery and routing." }).optional()
|
|
11
11
|
});
|
|
12
|
-
const apiRegistryResponseSchema =
|
|
13
|
-
prefix:
|
|
14
|
-
actions:
|
|
15
|
-
permissions:
|
|
12
|
+
const apiRegistryResponseSchema = z.object({
|
|
13
|
+
prefix: z.text().optional(),
|
|
14
|
+
actions: z.record(z.text(), apiActionSchema),
|
|
15
|
+
permissions: z.array(z.text()).optional()
|
|
16
16
|
});
|
|
17
17
|
/**
|
|
18
18
|
* @deprecated Use `apiRegistryResponseSchema` and `ApiRegistryResponse` instead.
|
|
@@ -22,17 +22,14 @@ const apiLinksResponseSchema = apiRegistryResponseSchema;
|
|
|
22
22
|
//#region ../../src/server/links/atoms/apiLinksAtom.ts
|
|
23
23
|
const apiLinksAtom = $atom({
|
|
24
24
|
name: "alepha.server.request.apiLinks",
|
|
25
|
-
schema:
|
|
25
|
+
schema: apiRegistryResponseSchema.optional()
|
|
26
26
|
});
|
|
27
27
|
//#endregion
|
|
28
28
|
//#region ../../src/server/links/atoms/linkOptionsAtom.ts
|
|
29
29
|
const linkOptionsAtom = $atom({
|
|
30
30
|
name: "alepha.server.links.options",
|
|
31
31
|
description: "Configuration options for the links module.",
|
|
32
|
-
schema:
|
|
33
|
-
description: "Enable batch collection for browser-side calls.",
|
|
34
|
-
default: true
|
|
35
|
-
}) }),
|
|
32
|
+
schema: z.object({ batch: z.boolean().describe("Enable batch collection for browser-side calls.").default(true) }),
|
|
36
33
|
default: { batch: true }
|
|
37
34
|
});
|
|
38
35
|
//#endregion
|
|
@@ -361,8 +358,8 @@ var LinkProvider = class LinkProvider {
|
|
|
361
358
|
const action = {
|
|
362
359
|
...link,
|
|
363
360
|
schema: {
|
|
364
|
-
body:
|
|
365
|
-
response:
|
|
361
|
+
body: z.any(),
|
|
362
|
+
response: z.any()
|
|
366
363
|
}
|
|
367
364
|
};
|
|
368
365
|
if (!link.host && link.service) action.path = `/${link.service}${action.path}`;
|