alepha 0.21.2 → 0.23.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/README.md +0 -1
- package/dist/api/audits/index.browser.js.map +1 -1
- package/dist/api/audits/index.d.ts +393 -403
- package/dist/api/audits/index.d.ts.map +1 -1
- package/dist/api/audits/index.js +25 -56
- package/dist/api/audits/index.js.map +1 -1
- package/dist/api/files/index.browser.js +31 -1
- package/dist/api/files/index.browser.js.map +1 -1
- package/dist/api/files/index.d.ts +313 -208
- package/dist/api/files/index.d.ts.map +1 -1
- package/dist/api/files/index.js +152 -42
- package/dist/api/files/index.js.map +1 -1
- package/dist/api/jobs/index.browser.js +2 -2
- package/dist/api/jobs/index.browser.js.map +1 -1
- package/dist/api/jobs/index.d.ts +282 -285
- package/dist/api/jobs/index.d.ts.map +1 -1
- package/dist/api/jobs/index.js +39 -33
- package/dist/api/jobs/index.js.map +1 -1
- package/dist/api/keys/index.d.ts +217 -222
- package/dist/api/keys/index.d.ts.map +1 -1
- package/dist/api/keys/index.js.map +1 -1
- package/dist/api/notifications/index.browser.js.map +1 -1
- package/dist/api/notifications/index.d.ts +188 -195
- package/dist/api/notifications/index.d.ts.map +1 -1
- package/dist/api/notifications/index.js.map +1 -1
- package/dist/api/oauth/index.d.ts +71 -76
- package/dist/api/oauth/index.d.ts.map +1 -1
- package/dist/api/oauth/index.js.map +1 -1
- package/dist/api/organizations/index.browser.js.map +1 -1
- package/dist/api/organizations/index.d.ts +104 -109
- package/dist/api/organizations/index.d.ts.map +1 -1
- package/dist/api/organizations/index.js.map +1 -1
- package/dist/api/parameters/index.browser.js +43 -16
- package/dist/api/parameters/index.browser.js.map +1 -1
- package/dist/api/parameters/index.d.ts +488 -344
- package/dist/api/parameters/index.d.ts.map +1 -1
- package/dist/api/parameters/index.js +175 -35
- package/dist/api/parameters/index.js.map +1 -1
- package/dist/api/payments/index.d.ts +396 -402
- package/dist/api/payments/index.d.ts.map +1 -1
- package/dist/api/payments/index.js.map +1 -1
- package/dist/api/subscriptions/index.d.ts +644 -652
- package/dist/api/subscriptions/index.d.ts.map +1 -1
- package/dist/api/subscriptions/index.js +1 -1
- package/dist/api/subscriptions/index.js.map +1 -1
- package/dist/api/users/index.browser.js +7 -0
- package/dist/api/users/index.browser.js.map +1 -1
- package/dist/api/users/index.d.ts +1106 -1005
- package/dist/api/users/index.d.ts.map +1 -1
- package/dist/api/users/index.js +307 -64
- package/dist/api/users/index.js.map +1 -1
- package/dist/api/verifications/index.browser.js.map +1 -1
- package/dist/api/verifications/index.d.ts +137 -143
- package/dist/api/verifications/index.d.ts.map +1 -1
- package/dist/api/verifications/index.js.map +1 -1
- package/dist/background/index.d.ts +95 -0
- package/dist/background/index.d.ts.map +1 -0
- package/dist/background/index.js +121 -0
- package/dist/background/index.js.map +1 -0
- package/dist/background/index.workerd.js +110 -0
- package/dist/background/index.workerd.js.map +1 -0
- package/dist/batch/index.d.ts +5 -7
- package/dist/batch/index.d.ts.map +1 -1
- package/dist/batch/index.js.map +1 -1
- package/dist/bin/index.js.map +1 -1
- package/dist/bucket/index.d.ts +76 -54
- package/dist/bucket/index.d.ts.map +1 -1
- package/dist/bucket/index.js +58 -11
- package/dist/bucket/index.js.map +1 -1
- package/dist/bucket/index.workerd.js +200 -5
- package/dist/bucket/index.workerd.js.map +1 -1
- package/dist/cache/core/index.d.ts +7 -10
- package/dist/cache/core/index.d.ts.map +1 -1
- package/dist/cache/core/index.js.map +1 -1
- package/dist/cache/core/index.workerd.js.map +1 -1
- package/dist/cache/database/index.d.ts +22 -26
- package/dist/cache/database/index.d.ts.map +1 -1
- package/dist/cache/database/index.js.map +1 -1
- package/dist/cache/redis/index.d.ts +4 -7
- package/dist/cache/redis/index.d.ts.map +1 -1
- package/dist/cache/redis/index.js.map +1 -1
- package/dist/captcha/index.d.ts +3 -6
- package/dist/captcha/index.d.ts.map +1 -1
- package/dist/captcha/index.js.map +1 -1
- package/dist/cli/config/index.d.ts.map +1 -1
- package/dist/cli/config/index.js.map +1 -1
- package/dist/cli/core/index.d.ts +458 -249
- package/dist/cli/core/index.d.ts.map +1 -1
- package/dist/cli/core/index.js +372 -660
- package/dist/cli/core/index.js.map +1 -1
- package/dist/cli/devtools/index.d.ts +3 -5
- package/dist/cli/devtools/index.d.ts.map +1 -1
- package/dist/cli/devtools/index.js.map +1 -1
- package/dist/cli/i18n/index.d.ts +20 -17
- package/dist/cli/i18n/index.d.ts.map +1 -1
- package/dist/cli/i18n/index.js +45 -11
- package/dist/cli/i18n/index.js.map +1 -1
- package/dist/cli/platform/index.d.ts +126 -1342
- package/dist/cli/platform/index.d.ts.map +1 -1
- package/dist/cli/platform/index.js +136 -2374
- package/dist/cli/platform/index.js.map +1 -1
- package/dist/cli/platform-lib/index.d.ts +1472 -0
- package/dist/cli/platform-lib/index.d.ts.map +1 -0
- package/dist/cli/platform-lib/index.js +2660 -0
- package/dist/cli/platform-lib/index.js.map +1 -0
- package/dist/cli/vendor/index.d.ts +17 -21
- package/dist/cli/vendor/index.d.ts.map +1 -1
- package/dist/cli/vendor/index.js.map +1 -1
- package/dist/command/index.d.ts +20 -19
- package/dist/command/index.d.ts.map +1 -1
- package/dist/command/index.js +39 -10
- package/dist/command/index.js.map +1 -1
- package/dist/{containers → container}/core/index.d.ts +13 -15
- package/dist/container/core/index.d.ts.map +1 -0
- package/dist/{containers → container}/core/index.js +23 -14
- package/dist/container/core/index.js.map +1 -0
- package/dist/{containers → container}/core/index.workerd.js +37 -22
- package/dist/container/core/index.workerd.js.map +1 -0
- package/dist/core/index.browser.js +27 -1
- package/dist/core/index.browser.js.map +1 -1
- package/dist/core/index.d.ts +48 -24
- package/dist/core/index.d.ts.map +1 -1
- package/dist/core/index.js +27 -1
- package/dist/core/index.js.map +1 -1
- package/dist/core/index.native.js +27 -1
- package/dist/core/index.native.js.map +1 -1
- package/dist/core/index.workerd.js +27 -1
- package/dist/core/index.workerd.js.map +1 -1
- package/dist/crypto/index.browser.js.map +1 -1
- package/dist/crypto/index.d.ts +5 -8
- package/dist/crypto/index.d.ts.map +1 -1
- package/dist/crypto/index.js.map +1 -1
- package/dist/datetime/index.d.ts +3 -4
- package/dist/datetime/index.d.ts.map +1 -1
- package/dist/datetime/index.js.map +1 -1
- package/dist/email/brevo/index.d.ts +2 -4
- package/dist/email/brevo/index.d.ts.map +1 -1
- package/dist/email/brevo/index.js.map +1 -1
- package/dist/email/cloudflare/index.d.ts +20 -7
- package/dist/email/cloudflare/index.d.ts.map +1 -1
- package/dist/email/cloudflare/index.js +46 -9
- package/dist/email/cloudflare/index.js.map +1 -1
- package/dist/email/core/index.d.ts +6 -9
- package/dist/email/core/index.d.ts.map +1 -1
- package/dist/email/core/index.js.map +1 -1
- package/dist/email/core/index.workerd.js.map +1 -1
- package/dist/email/smtp/index.d.ts +10 -13
- package/dist/email/smtp/index.d.ts.map +1 -1
- package/dist/email/smtp/index.js +107 -32
- package/dist/email/smtp/index.js.map +1 -1
- package/dist/fake/index.d.ts +1 -2
- package/dist/fake/index.d.ts.map +1 -1
- package/dist/fake/index.js.map +1 -1
- package/dist/lock/core/index.d.ts +9 -14
- package/dist/lock/core/index.d.ts.map +1 -1
- package/dist/lock/core/index.js.map +1 -1
- package/dist/lock/redis/index.d.ts +2 -4
- 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 +105 -76
- package/dist/logger/index.d.ts.map +1 -1
- package/dist/logger/index.js +196 -174
- package/dist/logger/index.js.map +1 -1
- package/dist/mcp/index.d.ts +25 -20
- package/dist/mcp/index.d.ts.map +1 -1
- package/dist/mcp/index.js +23 -0
- package/dist/mcp/index.js.map +1 -1
- package/dist/orm/core/index.browser.js.map +1 -1
- package/dist/orm/core/index.bun.js +19 -1
- package/dist/orm/core/index.bun.js.map +1 -1
- package/dist/orm/core/index.d.ts +76 -62
- package/dist/orm/core/index.d.ts.map +1 -1
- package/dist/orm/core/index.js +20 -2
- package/dist/orm/core/index.js.map +1 -1
- package/dist/orm/postgres/index.bun.js.map +1 -1
- package/dist/orm/postgres/index.d.ts +28 -20
- package/dist/orm/postgres/index.d.ts.map +1 -1
- package/dist/orm/postgres/index.js.map +1 -1
- package/dist/queue/core/index.d.ts +12 -15
- package/dist/queue/core/index.d.ts.map +1 -1
- package/dist/queue/core/index.js.map +1 -1
- package/dist/queue/core/index.workerd.js.map +1 -1
- package/dist/queue/redis/index.d.ts +3 -5
- package/dist/queue/redis/index.d.ts.map +1 -1
- package/dist/queue/redis/index.js.map +1 -1
- package/dist/react/auth/index.browser.js +9 -2
- package/dist/react/auth/index.browser.js.map +1 -1
- package/dist/react/auth/index.d.ts +14 -9
- package/dist/react/auth/index.d.ts.map +1 -1
- package/dist/react/auth/index.js +9 -2
- package/dist/react/auth/index.js.map +1 -1
- package/dist/react/core/index.d.ts +7 -8
- package/dist/react/core/index.d.ts.map +1 -1
- package/dist/react/core/index.js +6 -3
- package/dist/react/core/index.js.map +1 -1
- package/dist/react/form/index.d.ts +2 -5
- package/dist/react/form/index.d.ts.map +1 -1
- package/dist/react/form/index.js +16 -15
- 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 +2 -4
- 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 +90 -11
- package/dist/react/i18n/index.d.ts.map +1 -1
- package/dist/react/i18n/index.js +147 -11
- package/dist/react/i18n/index.js.map +1 -1
- package/dist/react/intro/index.d.ts +1 -2
- package/dist/react/intro/index.d.ts.map +1 -1
- package/dist/react/intro/index.js +2 -2
- package/dist/react/intro/index.js.map +1 -1
- package/dist/react/router/index.browser.js +193 -24
- package/dist/react/router/index.browser.js.map +1 -1
- package/dist/react/router/index.d.ts +434 -222
- package/dist/react/router/index.d.ts.map +1 -1
- package/dist/react/router/index.js +249 -35
- 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 +92 -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 +1 -2
- package/dist/react/testing/index.d.ts.map +1 -1
- package/dist/react/testing/index.js +16 -17
- package/dist/react/testing/index.js.map +1 -1
- package/dist/react/ui/index.d.ts +20 -25
- package/dist/react/ui/index.d.ts.map +1 -1
- package/dist/react/ui/index.js.map +1 -1
- package/dist/redis/index.bun.js.map +1 -1
- package/dist/redis/index.d.ts +17 -19
- package/dist/redis/index.d.ts.map +1 -1
- package/dist/redis/index.js.map +1 -1
- package/dist/retry/index.d.ts +2 -4
- 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 +10 -13
- package/dist/scheduler/index.d.ts.map +1 -1
- package/dist/scheduler/index.js.map +1 -1
- package/dist/scheduler/index.workerd.js.map +1 -1
- package/dist/security/index.browser.js.map +1 -1
- package/dist/security/index.d.ts +45 -48
- package/dist/security/index.d.ts.map +1 -1
- package/dist/security/index.js.map +1 -1
- package/dist/server/auth/index.browser.js.map +1 -1
- package/dist/server/auth/index.d.ts +272 -173
- package/dist/server/auth/index.d.ts.map +1 -1
- package/dist/server/auth/index.js +1608 -15
- package/dist/server/auth/index.js.map +1 -1
- package/dist/server/cookies/index.browser.js.map +1 -1
- package/dist/server/cookies/index.d.ts +20 -7
- package/dist/server/cookies/index.d.ts.map +1 -1
- package/dist/server/cookies/index.js +22 -3
- package/dist/server/cookies/index.js.map +1 -1
- package/dist/server/core/index.browser.js.map +1 -1
- package/dist/server/core/index.d.ts +106 -73
- package/dist/server/core/index.d.ts.map +1 -1
- package/dist/server/core/index.js +44 -0
- package/dist/server/core/index.js.map +1 -1
- package/dist/server/cors/index.d.ts +11 -14
- package/dist/server/cors/index.d.ts.map +1 -1
- package/dist/server/cors/index.js.map +1 -1
- package/dist/server/etag/index.d.ts +6 -9
- 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 -21
- package/dist/server/health/index.d.ts.map +1 -1
- package/dist/server/health/index.js.map +1 -1
- package/dist/server/links/index.browser.js +2 -0
- package/dist/server/links/index.browser.js.map +1 -1
- package/dist/server/links/index.d.ts +63 -67
- package/dist/server/links/index.d.ts.map +1 -1
- package/dist/server/links/index.js +2 -0
- package/dist/server/links/index.js.map +1 -1
- package/dist/server/metrics/index.d.ts +5 -7
- package/dist/server/metrics/index.d.ts.map +1 -1
- package/dist/server/metrics/index.js.map +1 -1
- package/dist/server/proxy/index.d.ts +3 -5
- 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 +10 -13
- package/dist/server/rate-limit/index.d.ts.map +1 -1
- package/dist/server/rate-limit/index.js.map +1 -1
- package/dist/server/static/index.d.ts +3 -5
- 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 +5 -8
- package/dist/server/swagger/index.d.ts.map +1 -1
- package/dist/server/swagger/index.js.map +1 -1
- package/dist/sms/index.d.ts +3 -5
- 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 +2 -4
- package/dist/system/index.d.ts.map +1 -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 +4 -6
- package/dist/topic/core/index.d.ts.map +1 -1
- package/dist/topic/core/index.js.map +1 -1
- package/dist/topic/redis/index.d.ts +5 -8
- package/dist/topic/redis/index.d.ts.map +1 -1
- package/dist/topic/redis/index.js.map +1 -1
- package/package.json +59 -23
- package/src/api/audits/__tests__/AuditService.spec.ts +18 -110
- package/src/api/audits/controllers/AdminAuditController.ts +14 -0
- package/src/api/audits/services/AuditService.ts +21 -88
- package/src/api/files/__tests__/FileService.spec.ts +207 -2
- package/src/api/files/index.ts +3 -0
- package/src/api/files/schemas/fileCreatorSummarySchema.ts +22 -0
- package/src/api/files/schemas/fileResourceSchema.ts +10 -1
- package/src/api/files/services/FileService.ts +170 -72
- package/src/api/jobs/__tests__/$job.spec.ts +24 -1
- package/src/api/jobs/index.ts +4 -3
- package/src/api/jobs/primitives/$job.ts +7 -3
- package/src/api/jobs/providers/DirectJobDispatcher.ts +17 -36
- package/src/api/jobs/providers/JobProvider.ts +53 -24
- package/src/api/jobs/schemas/jobConfigAtom.ts +1 -1
- package/src/api/jobs/schemas/jobExecutionResourceSchema.ts +4 -1
- package/src/api/keys/schemas/adminApiKeyResourceSchema.ts +3 -1
- package/src/api/parameters/__tests__/$parameter.spec.ts +19 -2
- package/src/api/parameters/audits/ParameterAudits.ts +17 -0
- package/src/api/parameters/controllers/AdminParameterController.ts +95 -19
- package/src/api/parameters/index.ts +3 -0
- package/src/api/parameters/schemas/activateParameterBodySchema.ts +3 -3
- package/src/api/parameters/schemas/createParameterVersionBodySchema.ts +3 -2
- package/src/api/parameters/schemas/parameterCreatorSummarySchema.ts +25 -0
- package/src/api/parameters/schemas/parameterResponseSchema.ts +5 -0
- package/src/api/parameters/schemas/rollbackParameterBodySchema.ts +4 -2
- package/src/api/parameters/services/ParameterProvider.ts +69 -6
- package/src/api/subscriptions/jobs/SubscriptionJobs.ts +1 -1
- package/src/api/users/__tests__/AdminSessionController.spec.ts +37 -0
- package/src/api/users/audits/SessionAudits.ts +33 -0
- package/src/api/users/audits/UserAudits.ts +19 -43
- package/src/api/users/controllers/AdminUserController.ts +66 -1
- package/src/api/users/controllers/RealmController.ts +1 -0
- package/src/api/users/entities/sessions.ts +6 -0
- package/src/api/users/entities/users.ts +2 -0
- package/src/api/users/index.ts +9 -1
- package/src/api/users/primitives/$realm.ts +29 -0
- package/src/api/users/providers/RealmProvider.ts +15 -0
- package/src/api/users/schemas/realmConfigSchema.ts +14 -0
- package/src/api/users/schemas/sessionResourceSchema.ts +16 -0
- package/src/api/users/schemas/updateUserSchema.ts +1 -8
- package/src/api/users/schemas/userQuerySchema.ts +7 -0
- package/src/api/users/services/CredentialService.ts +15 -6
- package/src/api/users/services/IdentityService.ts +2 -1
- package/src/api/users/services/RegistrationService.ts +2 -1
- package/src/api/users/services/SessionCrudService.ts +19 -2
- package/src/api/users/services/SessionService.ts +39 -19
- package/src/api/users/services/UserService.ts +106 -8
- package/src/background/__tests__/BackgroundTaskProvider.spec.ts +96 -0
- package/src/background/index.ts +37 -0
- package/src/background/index.workerd.ts +28 -0
- package/src/background/providers/BackgroundTaskProvider.ts +70 -0
- package/src/background/providers/WorkerdBackgroundTaskProvider.ts +43 -0
- package/src/bucket/__tests__/$bucket.spec.ts +18 -0
- package/src/bucket/__tests__/LocalFileStorageProvider.spec.ts +5 -0
- package/src/bucket/__tests__/MemoryFileStorageProvider.spec.ts +5 -0
- package/src/bucket/__tests__/NodeS3BucketProvider.spec.ts +23 -4
- package/src/bucket/__tests__/shared.ts +30 -0
- package/src/bucket/index.ts +5 -5
- package/src/bucket/index.workerd.ts +11 -4
- package/src/bucket/primitives/$bucket.ts +27 -0
- package/src/bucket/providers/FileStorageProvider.ts +13 -0
- package/src/bucket/providers/LocalFileStorageProvider.ts +17 -1
- package/src/bucket/providers/MemoryFileStorageProvider.ts +7 -0
- package/src/bucket/providers/{CloudflareR2Provider.ts → R2FileStorageProvider.ts} +10 -1
- package/src/bucket/providers/{NodeS3BucketProvider.ts → S3FileStorageProvider.ts} +27 -5
- package/src/cli/core/__tests__/BuildDockerTask.spec.ts +25 -1
- package/src/cli/core/__tests__/init.spec.ts +0 -219
- package/src/cli/core/atoms/buildOptions.ts +0 -12
- package/src/cli/core/commands/__tests__/BuildCommand.spec.ts +43 -0
- package/src/cli/core/commands/build.ts +105 -37
- package/src/cli/core/commands/init.ts +0 -12
- package/src/cli/core/commands/pack.ts +133 -0
- package/src/cli/core/index.ts +3 -3
- package/src/cli/core/providers/ViteDevServerProvider.ts +40 -16
- package/src/cli/core/services/PackageManagerUtils.ts +0 -16
- package/src/cli/core/services/ProjectScaffolder.ts +29 -291
- package/src/cli/core/tasks/BuildCloudflareTask.ts +382 -56
- package/src/cli/core/tasks/BuildDockerTask.ts +33 -3
- package/src/cli/core/tasks/BuildPrerenderTask.ts +44 -7
- package/src/cli/core/tasks/BuildTask.ts +34 -0
- package/src/cli/core/templates/apiIndexTs.ts +1 -22
- package/src/cli/core/templates/mainCss.ts +0 -1
- package/src/cli/core/templates/webAppRouterTs.ts +0 -99
- package/src/cli/core/templates/webIndexTs.ts +1 -22
- package/src/cli/i18n/__tests__/I18nCheckService.spec.ts +48 -0
- package/src/cli/i18n/services/I18nCheckService.ts +65 -11
- package/src/cli/platform/__tests__/SecretsCommand.spec.ts +5 -3
- package/src/cli/platform/commands/SecretsCommand.ts +8 -6
- package/src/cli/platform/commands/platform.ts +192 -46
- package/src/cli/platform/index.ts +12 -52
- package/src/cli/{platform → platform-lib}/__tests__/CloudflareAdapter.spec.ts +426 -169
- package/src/cli/{platform → platform-lib}/__tests__/NamingService.spec.ts +91 -4
- package/src/cli/{platform → platform-lib}/__tests__/VercelAdapter.spec.ts +56 -85
- package/src/cli/{platform → platform-lib}/adapters/CloudflareAdapter.ts +519 -190
- package/src/cli/{platform → platform-lib}/adapters/PlatformAdapter.ts +62 -35
- package/src/cli/{platform → platform-lib}/adapters/VercelAdapter.ts +6 -10
- package/src/cli/{platform → platform-lib}/atoms/platformOptions.ts +34 -1
- package/src/cli/platform-lib/index.ts +67 -0
- package/src/cli/platform-lib/services/NamingService.ts +136 -0
- package/src/cli/{platform → platform-lib}/services/PlatformInspector.ts +60 -13
- package/src/cli/{platform → platform-lib}/services/PlatformOrchestrator.ts +54 -43
- package/src/cli/{platform → platform-lib}/services/WranglerApi.ts +4 -2
- package/src/command/__tests__/Runner.spec.ts +20 -0
- package/src/command/helpers/EnvUtils.ts +19 -3
- package/src/command/helpers/Runner.ts +12 -2
- package/src/command/providers/CliProvider.ts +34 -1
- package/src/{containers → container}/core/__tests__/$container.spec.ts +5 -5
- package/src/{containers → container}/core/index.ts +4 -4
- package/src/{containers → container}/core/index.workerd.ts +19 -3
- package/src/{containers → container}/core/primitives/$container.ts +1 -1
- package/src/{containers → container}/core/providers/CloudflareContainerProvider.ts +17 -19
- package/src/{containers → container}/core/providers/ContainerProvider.ts +16 -2
- package/src/{containers → container}/core/providers/MockContainerProvider.ts +1 -1
- package/src/core/Alepha.ts +49 -1
- package/src/core/__tests__/$env.spec.ts +42 -0
- package/src/core/__tests__/dump.spec.ts +47 -0
- package/src/email/cloudflare/__tests__/CloudflareEmailProvider.spec.ts +42 -10
- package/src/email/cloudflare/index.ts +14 -5
- package/src/email/cloudflare/providers/CloudflareEmailProvider.ts +54 -9
- package/src/logger/__tests__/Logger.spec.ts +55 -0
- package/src/logger/index.ts +13 -0
- package/src/logger/services/Logger.ts +31 -1
- package/src/mcp/__tests__/McpServerProvider.spec.ts +71 -0
- package/src/mcp/providers/McpServerProvider.ts +55 -0
- package/src/orm/__tests__/orm-showcase-tests.ts +27 -0
- package/src/orm/__tests__/orm-showcase.spec.ts +12 -0
- package/src/orm/core/interfaces/PgQuery.ts +4 -1
- package/src/orm/core/services/Repository.ts +27 -11
- package/src/react/auth/hooks/useAuth.ts +10 -5
- package/src/react/core/__tests__/useQuery.browser.spec.tsx +25 -0
- package/src/react/core/hooks/useAction.ts +14 -3
- package/src/react/core/hooks/useQuery.ts +24 -4
- 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/services/FormModel.ts +57 -39
- package/src/react/i18n/__tests__/I18nProvider.spec.ts +89 -0
- package/src/react/i18n/__tests__/locale-routing.spec.ts +107 -0
- package/src/react/i18n/components/Translate.tsx +47 -0
- package/src/react/i18n/index.ts +2 -0
- package/src/react/i18n/providers/I18nProvider.ts +171 -12
- package/src/react/intro/components/GettingStartedAdminSlide.tsx +2 -2
- package/src/react/router/__tests__/$page.spec.tsx +3 -2
- package/src/react/router/__tests__/RouterLocaleProvider.spec.ts +127 -0
- package/src/react/router/__tests__/page-can.spec.ts +18 -13
- package/src/react/router/hooks/useQueryParams.ts +114 -14
- 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 +85 -4
- package/src/react/router/providers/ReactBrowserRouterProvider.ts +18 -8
- package/src/react/router/providers/ReactPageProvider.ts +12 -1
- package/src/react/router/providers/ReactServerProvider.ts +96 -14
- 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/services/SchemaControl.ts +3 -4
- package/src/server/auth/__tests__/appleClientSecret.spec.ts +34 -0
- package/src/server/auth/__tests__/authFederationClient.spec.ts +40 -0
- package/src/server/auth/__tests__/federationAssertion.spec.ts +146 -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/federationAssertion.ts +74 -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 +4 -0
- package/src/server/auth/primitives/$authFederationBroker.ts +273 -0
- package/src/server/auth/primitives/$authFederationClient.ts +89 -0
- package/src/server/auth/providers/ServerAuthProvider.ts +18 -4
- package/src/server/cookies/__tests__/ServerCookiesProvider.spec.ts +70 -0
- package/src/server/cookies/providers/ServerCookiesProvider.ts +23 -3
- package/src/server/core/interfaces/ServerRequest.ts +8 -0
- package/src/server/core/primitives/$route.ts +27 -0
- package/src/server/core/providers/ServerMultipartProvider.ts +19 -0
- package/src/server/links/providers/LinkProvider.ts +10 -0
- package/dist/containers/core/index.d.ts.map +0 -1
- package/dist/containers/core/index.js.map +0 -1
- package/dist/containers/core/index.workerd.js.map +0 -1
- package/src/cli/core/tasks/BuildSitemapTask.ts +0 -130
- package/src/cli/core/templates/componentsJsonTs.ts +0 -39
- package/src/cli/core/templates/saasAdminLayoutTsx.ts +0 -77
- package/src/cli/core/templates/saasAdminPagesTsx.ts +0 -26
- package/src/cli/core/templates/saasAuthLayoutTsx.ts +0 -22
- package/src/cli/core/templates/saasAuthPagesTsx.ts +0 -62
- package/src/cli/core/templates/saasRealmProviderTs.ts +0 -52
- package/src/cli/platform/services/NamingService.ts +0 -54
- /package/dist/orm/core/{chunk-o8xxKEmq.js → chunk-B4FMCO8f.js} +0 -0
- /package/dist/react/testing/{chunk-6Ep1yQYe.js → chunk-BpyX8vjI.js} +0 -0
- /package/src/cli/{platform → platform-lib}/__tests__/GitHubSecretStore.spec.ts +0 -0
- /package/src/cli/{platform → platform-lib}/__tests__/PlatformCacheProvider.spec.ts +0 -0
- /package/src/cli/{platform → platform-lib}/__tests__/PlatformInspector.spec.ts +0 -0
- /package/src/cli/{platform → platform-lib}/__tests__/PlatformOrchestrator.spec.ts +0 -0
- /package/src/cli/{platform → platform-lib}/__tests__/SecretFilterService.spec.ts +0 -0
- /package/src/cli/{platform → platform-lib}/__tests__/detectResources.spec.ts +0 -0
- /package/src/cli/{platform → platform-lib}/providers/GitHubSecretStore.ts +0 -0
- /package/src/cli/{platform → platform-lib}/providers/MemorySecretStore.ts +0 -0
- /package/src/cli/{platform → platform-lib}/providers/PlatformCacheProvider.ts +0 -0
- /package/src/cli/{platform → platform-lib}/providers/SecretStoreProvider.ts +0 -0
- /package/src/cli/{platform → platform-lib}/schemas/cloudflare.ts +0 -0
- /package/src/cli/{platform → platform-lib}/schemas/platform.ts +0 -0
- /package/src/cli/{platform → platform-lib}/schemas/vercel.ts +0 -0
- /package/src/cli/{platform → platform-lib}/services/CloudflareApi.ts +0 -0
- /package/src/cli/{platform → platform-lib}/services/SecretFilterService.ts +0 -0
- /package/src/cli/{platform → platform-lib}/services/VercelApi.ts +0 -0
- /package/src/cli/{platform → platform-lib}/services/VercelCli.ts +0 -0
- /package/src/{containers → container}/core/interfaces/ContainerOptions.ts +0 -0
- /package/src/{containers → container}/core/providers/NodeContainerProvider.ts +0 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":["sql","sql","sql","sql","sql","migrate"],"sources":["../../../src/orm/postgres/schemas/postgresEnvSchema.ts","../../../src/orm/postgres/types/byte.ts","../../../src/orm/postgres/services/PostgresModelBuilder.ts","../../../src/orm/postgres/providers/PostgresProvider.ts","../../../src/orm/postgres/providers/BunPostgresProvider.ts","../../../src/orm/postgres/providers/CloudflareHyperdriveProvider.ts","../../../src/orm/postgres/providers/NodePostgresProvider.ts","../../../src/orm/postgres/providers/PglitePostgresProvider.ts","../../../src/orm/postgres/index.ts"],"sourcesContent":["import { type Static, t } from \"alepha\";\n\n/**\n * PostgreSQL-specific environment schema.\n *\n * Additional env vars for PostgreSQL providers on top of `databaseEnvSchema`.\n */\nexport const postgresEnvSchema = t.object({\n /**\n * PostgreSQL schema name (defaults to `\"public\"` when unset).\n */\n POSTGRES_SCHEMA: t.optional(t.text()),\n\n /**\n * Maximum number of connections in the pool.\n */\n POOL_MAX: t.optional(t.integer()),\n\n /**\n * Seconds a connection can be idle before being closed.\n */\n POOL_IDLE_TIMEOUT: t.optional(t.integer()),\n\n /**\n * Seconds to wait when establishing a new connection.\n */\n POOL_CONNECT_TIMEOUT: t.optional(t.integer()),\n});\n\ndeclare module \"alepha\" {\n interface Env extends Partial<Static<typeof postgresEnvSchema>> {}\n}\n","import { customType } from \"drizzle-orm/pg-core\";\n\n/**\n * Postgres bytea type.\n */\nexport const byte = customType<{\n data: Buffer;\n}>({\n dataType: () => \"bytea\",\n});\n","import { AlephaError, type TObject, type TSchema, t } from \"alepha\";\nimport {\n type EntityPrimitive,\n type FromSchema,\n ModelBuilder,\n PG_CREATED_AT,\n PG_GENERATED,\n PG_IDENTITY,\n PG_PRIMARY_KEY,\n PG_REF,\n PG_SERIAL,\n PG_UPDATED_AT,\n type PgGeneratedOptions,\n type PgIdentityOptions,\n type PgRefOptions,\n type SequencePrimitive,\n schema,\n sql,\n} from \"alepha/orm\";\nimport type { BuildExtraConfigColumns } from \"drizzle-orm\";\nimport * as pg from \"drizzle-orm/pg-core\";\nimport {\n check,\n foreignKey,\n index,\n type PgEnum,\n type PgSchema,\n type PgTableExtraConfigValue,\n type PgTableWithColumns,\n pgEnum,\n pgSchema,\n pgSequence,\n pgTable,\n unique,\n uniqueIndex,\n} from \"drizzle-orm/pg-core\";\nimport { byte } from \"../types/byte.ts\";\n\nexport class PostgresModelBuilder extends ModelBuilder {\n protected schemas = new Map<string, PgSchema>();\n\n /**\n * Create a primary key column with UUID v7\n */\n protected getPrimaryKeyUUID(key: string) {\n return pg.uuid(key).default(sql`uuidv7()`);\n }\n\n protected getPgSchema(name: string) {\n if (!this.schemas.has(name) && name !== \"public\") {\n this.schemas.set(name, pgSchema(name));\n }\n\n const nsp =\n name !== \"public\"\n ? this.schemas.get(name)\n : ({\n enum: pgEnum,\n table: pgTable,\n sequence: pgSequence,\n } as any);\n\n if (!nsp) {\n throw new AlephaError(`Postgres schema ${name} not found`);\n }\n\n return nsp;\n }\n\n public buildTable(\n entity: EntityPrimitive<any>,\n options: {\n tables: Map<string, unknown>;\n enums: Map<string, unknown>;\n schemas: Map<string, unknown>;\n schema: string;\n },\n ) {\n const tableName = entity.name;\n if (options.tables.has(tableName)) {\n return;\n }\n\n const nsp = this.getPgSchema(options.schema);\n\n // Register PgSchema so drizzle-kit knows the schema is declared in code.\n // Without this, pushSchema diffs \"schema exists in DB but not in code\" → DROP.\n if (options.schema !== \"public\" && !options.schemas.has(options.schema)) {\n options.schemas.set(options.schema, nsp);\n }\n\n const columns = this.schemaToPgColumns(\n tableName,\n entity.schema,\n nsp,\n options.enums,\n options.tables,\n );\n\n // Build the config function that includes indexes, foreign keys, constraints, and custom config\n const configFn = this.getTableConfig(entity, options.tables);\n\n const table = nsp.table(tableName, columns, configFn);\n\n options.tables.set(tableName, table);\n }\n\n public buildSequence(\n sequence: SequencePrimitive,\n options: {\n sequences: Map<string, unknown>;\n schema: string;\n },\n ) {\n const sequenceName = sequence.name;\n if (options.sequences.has(sequenceName)) {\n return;\n }\n\n const nsp = this.getPgSchema(options.schema);\n\n options.sequences.set(\n sequenceName,\n nsp.sequence(sequenceName, sequence.options),\n );\n }\n\n // -------------------------------------------------------------------------------------------------------------------\n\n /**\n * Get PostgreSQL-specific config builder for the table.\n */\n protected getTableConfig(\n entity: EntityPrimitive,\n tables: Map<string, unknown>,\n ):\n | ((\n self: BuildExtraConfigColumns<string, any, \"pg\">,\n ) => PgTableExtraConfigValue[])\n | undefined {\n // PostgreSQL-specific builders\n const pgBuilders = {\n index,\n uniqueIndex,\n unique,\n check,\n foreignKey,\n };\n\n // Table resolver function\n const tableResolver = (entityName: string) => {\n return tables.get(entityName) as any;\n };\n\n return this.buildTableConfig<\n PgTableExtraConfigValue,\n BuildExtraConfigColumns<string, any, \"pg\">\n >(entity, pgBuilders as any, tableResolver);\n }\n\n schemaToPgColumns = <T extends TObject>(\n tableName: string,\n schema: T,\n nsp: PgSchema,\n enums: Map<string, unknown>,\n tables: Map<string, unknown>,\n ): FromSchema<T> => {\n return Object.entries(schema.properties).reduce<Partial<FromSchema<T>>>(\n (columns, [key, value]) => {\n let col = this.mapFieldToColumn(tableName, key, value, nsp, enums);\n\n if (\"default\" in value && value.default != null) {\n col = col.default(value.default as any);\n }\n\n if (PG_PRIMARY_KEY in value) {\n col = col.primaryKey();\n }\n\n if (PG_REF in value) {\n const config = value[PG_REF] as PgRefOptions;\n col = col.references(() => {\n const ref = config.ref();\n const table = tables.get(\n ref.entity.name,\n ) as PgTableWithColumns<any>;\n\n if (!table) {\n throw new AlephaError(\n `Referenced table ${ref.entity.name} not found for ${tableName}.${key}`,\n );\n }\n\n const target = table[ref.name];\n if (!target) {\n throw new AlephaError(\n `Referenced column ${ref.name} not found in table ${ref.entity.name} for ${tableName}.${key}`,\n );\n }\n\n return target;\n }, config.actions);\n }\n\n if (PG_GENERATED in value) {\n const gen = value[PG_GENERATED] as PgGeneratedOptions;\n col = col.generatedAlwaysAs(gen.expression);\n }\n\n if (schema.required?.includes(key)) {\n col = col.notNull();\n }\n\n (columns as Record<string, unknown>)[key] = col;\n return columns;\n },\n {},\n ) as FromSchema<T>;\n };\n\n mapFieldToColumn = (\n tableName: string,\n fieldName: string,\n value: TSchema,\n nsp: PgSchema,\n enums: Map<string, any>,\n ) => {\n const key = this.toColumnName(fieldName);\n\n if (\n // is nullish ?\n \"anyOf\" in value &&\n Array.isArray(value.anyOf) &&\n value.anyOf.length === 2 &&\n value.anyOf.some((it: TSchema) => t.schema.isNull(it))\n ) {\n // then, remove nullish\n value = value.anyOf.find((it: TSchema) => !t.schema.isNull(it))!;\n }\n\n if (t.schema.isInteger(value)) {\n if (PG_SERIAL in value) {\n return pg.serial(key);\n }\n\n if (PG_IDENTITY in value) {\n const options = value[PG_IDENTITY] as PgIdentityOptions;\n if (options.mode === \"byDefault\") {\n return pg.integer(key).generatedByDefaultAsIdentity(options);\n }\n return pg.integer(key).generatedAlwaysAsIdentity(options);\n }\n\n return pg.integer(key);\n }\n\n if (t.schema.isBigInt(value)) {\n if (PG_IDENTITY in value) {\n const options = value[PG_IDENTITY] as PgIdentityOptions;\n if (options.mode === \"byDefault\") {\n return pg\n .bigint(key, { mode: \"bigint\" })\n .generatedByDefaultAsIdentity(options);\n }\n return pg\n .bigint(key, { mode: \"bigint\" })\n .generatedAlwaysAsIdentity(options);\n }\n\n return pg.bigint(key, { mode: \"bigint\" });\n }\n\n if (t.schema.isNumber(value)) {\n if (PG_IDENTITY in value) {\n const options = value[PG_IDENTITY] as PgIdentityOptions;\n if (options.mode === \"byDefault\") {\n return pg\n .bigint(key, { mode: \"number\" })\n .generatedByDefaultAsIdentity(options);\n }\n return pg\n .bigint(key, { mode: \"number\" })\n .generatedAlwaysAsIdentity(options);\n }\n\n if (value.format === \"int64\") {\n return pg.bigint(key, { mode: \"number\" });\n }\n\n return pg.numeric(key);\n }\n\n const isTypeEnum = (value: any): value is { enum: any[] } =>\n t.schema.isUnsafe(value) &&\n \"type\" in value &&\n value.type === \"string\" &&\n \"enum\" in value &&\n Array.isArray(value.enum);\n\n if (t.schema.isString(value) && !isTypeEnum(value)) {\n return this.mapStringToColumn(key, value);\n }\n\n if (t.schema.isBoolean(value)) {\n return pg.boolean(key);\n }\n\n if (t.schema.isObject(value)) {\n return schema(key, value);\n }\n\n if (t.schema.isRecord(value)) {\n return schema(key, value);\n }\n\n if (t.schema.isArray(value)) {\n if (t.schema.isObject(value.items)) {\n return schema(key, value);\n }\n if (t.schema.isRecord(value.items)) {\n return schema(key, value);\n }\n if (t.schema.isString(value.items)) {\n return pg.text(key).array();\n }\n if (t.schema.isInteger(value.items)) {\n return pg.integer(key).array();\n }\n if (t.schema.isNumber(value.items)) {\n return pg.numeric(key).array();\n }\n if (t.schema.isBoolean(value.items)) {\n return pg.boolean(key).array();\n }\n if (isTypeEnum(value.items)) {\n return pg.text(key).array();\n }\n }\n\n // Enum handling\n if (isTypeEnum(value)) {\n if (!value.enum.every((it) => typeof it === \"string\")) {\n throw new AlephaError(\n `Enum for ${fieldName} must be an array of strings, got ${JSON.stringify(\n value.enum,\n )}`,\n );\n }\n\n // SQL Enum (default for t.enum unless mode: \"text\")\n if ((value as any).mode !== \"text\") {\n const enumName = (value as any).enumName ?? `${tableName}_${key}_enum`;\n\n if (enums.has(enumName)) {\n const values = (\n enums.get(enumName) as PgEnum<[string]>\n ).enumValues.join(\",\");\n const newValues = value.enum.join(\",\");\n if (values !== newValues) {\n throw new AlephaError(\n `Enum name conflict for ${enumName}: [${values}] vs [${newValues}]`,\n );\n }\n }\n\n enums.set(enumName, nsp.enum(enumName, value.enum as [string]));\n\n return enums.get(enumName)(key);\n }\n\n // else, map to TEXT\n return this.mapStringToColumn(key, value);\n }\n\n throw new AlephaError(\n `Unsupported schema type for ${fieldName} as ${JSON.stringify(value)}`,\n );\n };\n\n /**\n * Map a string to a PG column.\n *\n * @param key The key of the field.\n * @param value The value of the field.\n */\n mapStringToColumn = (key: string, value: TSchema) => {\n if (\"format\" in value) {\n if (value.format === \"uuid\") {\n if (PG_PRIMARY_KEY in value) {\n return this.getPrimaryKeyUUID(key);\n }\n\n return pg.uuid(key);\n }\n\n if (value.format === \"byte\") {\n return byte(key);\n }\n\n if (value.format === \"date-time\") {\n if (PG_CREATED_AT in value) {\n return pg\n .timestamp(key, { mode: \"string\", withTimezone: true })\n .defaultNow();\n }\n if (PG_UPDATED_AT in value) {\n return pg\n .timestamp(key, { mode: \"string\", withTimezone: true })\n .defaultNow();\n }\n return pg.timestamp(key, { mode: \"string\", withTimezone: true });\n }\n\n if (value.format === \"date\") {\n return pg.date(key, { mode: \"string\" });\n }\n }\n\n return pg.text(key);\n };\n}\n","import { $env, $hook, $inject, $pipeline, AlephaError } from \"alepha\";\nimport { $lock } from \"alepha/lock\";\nimport {\n DatabaseProvider,\n DbMigrationError,\n databaseEnvSchema,\n type SQLLike,\n} from \"alepha/orm\";\nimport { sql } from \"drizzle-orm\";\nimport type { PgDatabase } from \"drizzle-orm/pg-core\";\nimport { postgresEnvSchema } from \"../schemas/postgresEnvSchema.ts\";\nimport { PostgresModelBuilder } from \"../services/PostgresModelBuilder.ts\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\n/**\n * Abstract base class for PostgreSQL database providers.\n *\n * Provides shared logic for Node.js and Bun PostgreSQL providers:\n * - Environment variable handling (DATABASE_URL, POSTGRES_SCHEMA)\n * - Schema name resolution (with test schema generation)\n * - SQL execution with error wrapping\n * - Lifecycle hooks (start with migration lock, stop with test cleanup)\n *\n * Subclasses must implement `connect()`, `close()`, and `executeMigrations()`.\n */\nexport abstract class PostgresProvider extends DatabaseProvider {\n protected readonly env = $env(databaseEnvSchema);\n protected readonly pgEnv = $env(postgresEnvSchema);\n protected readonly builder = $inject(PostgresModelBuilder);\n\n public override readonly dialect = \"postgresql\";\n\n public get name() {\n return \"postgres\";\n }\n\n /**\n * In testing mode, the schema name will be generated and deleted after the test.\n */\n protected schemaForTesting = this.alepha.isTest()\n ? this.generateTestSchemaName()\n : undefined;\n\n public override get url() {\n if (!this.env.DATABASE_URL) {\n throw new AlephaError(\"DATABASE_URL is not defined in the environment\");\n }\n\n return this.env.DATABASE_URL;\n }\n\n /**\n * Execute a SQL statement.\n */\n public override async execute(\n statement: SQLLike,\n ): Promise<Array<Record<string, unknown>>> {\n return await this.db.execute(statement);\n }\n\n /**\n * Get Postgres schema used by this provider.\n */\n public override get schema(): string {\n if (this.schemaForTesting) {\n return this.schemaForTesting;\n }\n\n if (this.pgEnv.POSTGRES_SCHEMA) {\n return this.pgEnv.POSTGRES_SCHEMA;\n }\n\n return \"public\";\n }\n\n public abstract override get db(): PgDatabase<any>;\n\n /**\n * Establish the database connection.\n */\n public abstract connect(): Promise<void>;\n\n /**\n * Close the database connection.\n */\n public abstract close(): Promise<void>;\n\n // -------------------------------------------------------------------------------------------------------------------\n\n protected readonly onStart = $hook({\n on: \"start\",\n handler: async () => {\n await this.connect();\n await this.generateTestSchema();\n\n // never migrate in serverless mode (vercel, netlify, ...)\n if (!this.alepha.isServerless()) {\n try {\n await this.migrateLock.run();\n } catch (error) {\n throw new DbMigrationError(error);\n }\n }\n },\n });\n\n protected readonly onStop = $hook({\n on: \"stop\",\n handler: async () => {\n await this.dropTestSchema();\n await this.close();\n },\n });\n\n protected migrateLock = $pipeline({\n use: [$lock({ name: \"postgres:migrate\" })],\n handler: async () => {\n await this.migrate();\n },\n });\n\n // -------------------------------------------------------------------------------------------------------------------\n // Create unique schema for tests and clean up after. Format: test_alepha_{epoch_seconds}_{random8}\n // -------------------------------------------------------------------------------------------------------------------\n\n protected async generateTestSchema() {\n if (\n this.alepha.isTest() &&\n this.schemaForTesting?.startsWith(\"test_alepha_\")\n ) {\n // Self-healing: drop stale schemas left behind by crashed/failed test runs\n await this.cleanupStaleTestSchemas();\n\n await this.execute(\n sql`CREATE SCHEMA IF NOT EXISTS ${sql.raw(this.schemaForTesting)}`,\n );\n }\n }\n\n /**\n * Drop the current test schema if applicable.\n */\n protected async dropTestSchema(): Promise<void> {\n if (\n this.alepha.isTest() &&\n this.schemaForTesting?.startsWith(\"test_alepha_\")\n ) {\n this.log.info(`Deleting test schema '${this.schemaForTesting}' ...`);\n await this.execute(\n sql`DROP SCHEMA IF EXISTS ${sql.raw(this.schemaForTesting)} CASCADE`,\n );\n this.log.info(`Test schema '${this.schemaForTesting}' deleted`);\n }\n }\n\n /**\n * Remove stale test schemas older than 1 hour.\n *\n * Parses the embedded epoch from schema names (format: test_alepha_{epoch}_{random8})\n * and drops any that exceed the TTL. This handles schemas left behind by crashed tests,\n * killed processes, or failed startups where stop() never fired.\n *\n * Uses a PG advisory lock so only one concurrent test process performs the cleanup.\n */\n protected async cleanupStaleTestSchemas(): Promise<void> {\n try {\n // Non-blocking advisory lock — first process in cleans, others skip\n const [lock] = await this.execute(\n sql`SELECT pg_try_advisory_lock(hashtext('alepha:test:schema:cleanup')) AS acquired`,\n );\n\n if (!lock?.acquired) {\n return;\n }\n\n try {\n const result = await this.execute(\n sql`SELECT schema_name FROM information_schema.schemata WHERE schema_name LIKE 'test_alepha_%'`,\n );\n\n const now = this.dateTime.nowMillis();\n const maxAge = 60 * 60 * 1000; // 1 hour\n\n for (const row of result) {\n const name = row.schema_name as string;\n\n // Skip our own schema (it was just created or is about to be)\n if (name === this.schemaForTesting) {\n continue;\n }\n\n const age = this.parseTestSchemaAge(name, now);\n if (age !== undefined && age > maxAge) {\n this.log.warn(\n `Dropping stale test schema '${name}' (age: ${Math.round(age / 60_000)}min) ...`,\n );\n await this.execute(\n sql`DROP SCHEMA IF EXISTS ${sql.raw(name)} CASCADE`,\n );\n }\n }\n } finally {\n await this.execute(\n sql`SELECT pg_advisory_unlock(hashtext('alepha:test:schema:cleanup'))`,\n );\n }\n } catch (error) {\n // Don't fail test setup if stale cleanup fails\n this.log.warn(\"Failed to clean up stale test schemas\", { error });\n }\n }\n\n /**\n * Parse the age in milliseconds from a test schema name.\n * Format: test_alepha_{epoch_seconds}_{random8}\n * Returns undefined if the name doesn't match the expected format.\n */\n protected parseTestSchemaAge(name: string, now: number): number | undefined {\n const parts = name.split(\"_\");\n // test_alepha_{epoch}_{random} → [\"test\", \"alepha\", epoch, random]\n if (parts.length !== 4 || parts[0] !== \"test\" || parts[1] !== \"alepha\") {\n return undefined;\n }\n\n const epoch = Number(parts[2]);\n if (!Number.isFinite(epoch) || epoch <= 0) {\n return undefined;\n }\n\n return now - epoch * 1000;\n }\n}\n","import { AlephaError } from \"alepha\";\nimport { sql } from \"drizzle-orm\";\nimport type { BunSQLDatabase } from \"drizzle-orm/bun-sql\";\nimport type { PgDatabase } from \"drizzle-orm/pg-core\";\nimport { PostgresProvider } from \"./PostgresProvider.ts\";\n\n/**\n * Bun PostgreSQL provider using Drizzle ORM with Bun's native SQL client.\n *\n * This provider uses Bun's built-in SQL class for PostgreSQL connections,\n * which provides excellent performance on the Bun runtime.\n *\n * @example\n * ```ts\n * // Set DATABASE_URL environment variable\n * // DATABASE_URL=postgres://user:password@localhost:5432/database\n *\n * // Or configure programmatically\n * alepha.with({\n * provide: DatabaseProvider,\n * use: BunPostgresProvider,\n * });\n * ```\n */\nexport class BunPostgresProvider extends PostgresProvider {\n protected client?: Bun.SQL;\n protected bunDb?: BunSQLDatabase;\n\n /**\n * Get the Drizzle Postgres database instance.\n */\n public override get db(): PgDatabase<any> {\n if (!this.bunDb) {\n throw new AlephaError(\"Database not initialized\");\n }\n\n return this.bunDb as unknown as PgDatabase<any>;\n }\n\n protected override async executeMigrations(\n migrationsFolder: string,\n ): Promise<void> {\n if (this.schema !== \"public\") {\n await this.db.execute(\n sql.raw(`SET search_path TO ${this.schema}, public`),\n );\n }\n const { migrate } = await import(\"drizzle-orm/bun-sql/migrator\");\n await migrate(this.bunDb!, {\n migrationsFolder,\n migrationsTable: this.migrationsTable,\n });\n }\n\n // -------------------------------------------------------------------------------------------------------------------\n\n public async connect(): Promise<void> {\n this.log.debug(\"Connect ..\");\n\n // Check if we're running in Bun\n if (typeof Bun === \"undefined\") {\n throw new AlephaError(\n \"BunPostgresProvider requires the Bun runtime. Use NodePostgresProvider for Node.js.\",\n );\n }\n\n const { drizzle } = await import(\"drizzle-orm/bun-sql\");\n\n // Create Bun SQL client with pool options\n // Set search_path via connection URL so all pool connections use the correct schema\n let connectionUrl = this.url;\n if (this.schema !== \"public\") {\n const separator = connectionUrl.includes(\"?\") ? \"&\" : \"?\";\n connectionUrl += `${separator}search_path=${this.schema},public`;\n }\n const bunOptions: Record<string, any> = { url: connectionUrl };\n if (this.pgEnv.POOL_MAX != null) {\n bunOptions.max = this.pgEnv.POOL_MAX;\n }\n if (this.pgEnv.POOL_IDLE_TIMEOUT != null) {\n bunOptions.idleTimeout = this.pgEnv.POOL_IDLE_TIMEOUT;\n }\n if (this.pgEnv.POOL_CONNECT_TIMEOUT != null) {\n bunOptions.connectionTimeout = this.pgEnv.POOL_CONNECT_TIMEOUT;\n }\n this.client = new Bun.SQL(bunOptions);\n\n // Test connection\n await this.client.unsafe(\"SELECT 1\");\n\n this.bunDb = drizzle({\n client: this.client,\n logger: {\n logQuery: (query: string, params: unknown[]) => {\n this.log.trace(query, { params });\n },\n },\n });\n\n this.log.info(\"Connection OK\");\n }\n\n public async close(): Promise<void> {\n if (this.client) {\n this.log.debug(\"Close...\");\n\n await this.client.close();\n\n this.client = undefined;\n this.bunDb = undefined;\n\n this.log.info(\"Connection closed\");\n }\n }\n}\n","import { $env, $hook, $inject, AlephaError, t } from \"alepha\";\nimport { DatabaseProvider, type SQLLike } from \"alepha/orm\";\nimport { sql } from \"drizzle-orm\";\nimport type { PgDatabase } from \"drizzle-orm/pg-core\";\nimport { PostgresModelBuilder } from \"../services/PostgresModelBuilder.ts\";\n\n/**\n * Cloudflare Hyperdrive PostgreSQL provider using Drizzle ORM.\n *\n * Connects to an external PostgreSQL database through Cloudflare Hyperdrive,\n * which provides connection pooling and caching at the edge.\n *\n * Creates a fresh connection per request, since Cloudflare Workers\n * cannot reuse I/O objects across request contexts.\n *\n * URL format: hyperdrive://BINDING_NAME\n */\nexport class CloudflareHyperdriveProvider extends DatabaseProvider {\n protected readonly builder = $inject(PostgresModelBuilder);\n protected readonly env = $env(\n t.object({\n DATABASE_URL: t.string({\n description: \"Expect to be 'hyperdrive://BINDING'\",\n }),\n POSTGRES_SCHEMA: t.optional(t.text()),\n }),\n );\n\n public override get schema(): string {\n return this.env.POSTGRES_SCHEMA ?? \"public\";\n }\n\n protected postgresFn?: any;\n protected drizzleFn?: any;\n protected bindingName?: string;\n\n public get name() {\n return \"postgres\";\n }\n\n public get driver() {\n return \"hyperdrive\";\n }\n\n public override readonly dialect = \"postgresql\";\n\n public override get url(): string {\n return this.env.DATABASE_URL;\n }\n\n /**\n * Get a fresh Drizzle instance per request.\n *\n * Reads the current Hyperdrive binding from `cloudflare.env`\n * and creates a new postgres client each time, avoiding the\n * \"Cannot perform I/O on behalf of a different request\" error.\n */\n public override get db(): PgDatabase<any> {\n if (!this.postgresFn || !this.drizzleFn || !this.bindingName) {\n throw new AlephaError(\"Hyperdrive database not initialized\");\n }\n\n const cloudflareEnv = this.alepha.get(\"cloudflare.env\") as\n | Record<string, unknown>\n | undefined;\n if (!cloudflareEnv) {\n throw new AlephaError(\n \"Cloudflare Workers environment not found in Alepha store under 'cloudflare.env'.\",\n );\n }\n\n const binding = cloudflareEnv[this.bindingName] as\n | { connectionString: string }\n | undefined;\n if (!binding?.connectionString) {\n throw new AlephaError(\n `Hyperdrive binding '${this.bindingName}' not found in Cloudflare Workers environment.`,\n );\n }\n\n const pgOptions: Record<string, any> = { prepare: false };\n\n // Set search_path so schema-free migration SQL resolves to the correct schema.\n if (this.schema !== \"public\") {\n pgOptions.connection = { search_path: `${this.schema}, public` };\n }\n\n const client = this.postgresFn(binding.connectionString, pgOptions);\n return this.drizzleFn(client as any) as unknown as PgDatabase<any>;\n }\n\n public override async execute(\n query: SQLLike,\n ): Promise<Array<Record<string, unknown>>> {\n return this.db.execute(query);\n }\n\n protected readonly onStart = $hook({\n on: \"start\",\n handler: async () => {\n this.bindingName = this.env.DATABASE_URL.replace(\"hyperdrive://\", \"\");\n\n // Pre-load modules so db getter is synchronous\n const pgModule = await import(\"postgres\");\n const drizzleModule = await import(\"drizzle-orm/postgres-js\");\n this.postgresFn = pgModule.default ?? pgModule;\n this.drizzleFn = drizzleModule.drizzle;\n\n this.log.info(\"Using Cloudflare Hyperdrive (PostgreSQL)\");\n },\n });\n\n protected async executeMigrations(migrationsFolder: string): Promise<void> {\n this.log.debug(`Running Postgres migrations from '${migrationsFolder}'...`);\n try {\n if (this.schema !== \"public\") {\n await this.db.execute(\n sql.raw(`SET search_path TO ${this.schema}, public`),\n );\n }\n const { migrate } = await import(\"drizzle-orm/postgres-js/migrator\");\n await migrate(this.db as any, {\n migrationsFolder,\n migrationsTable: this.migrationsTable,\n });\n this.log.debug(\"Postgres migrations completed successfully\");\n } catch (error) {\n const errorMessage =\n error instanceof Error\n ? `${error.name}: ${error.message}`\n : String(error);\n throw new AlephaError(\n `Postgres migration failed from '${migrationsFolder}': ${errorMessage}`,\n { cause: error },\n );\n }\n }\n}\n","import { AlephaError } from \"alepha\";\nimport { sql } from \"drizzle-orm\";\nimport type { PostgresJsDatabase } from \"drizzle-orm/postgres-js\";\nimport { drizzle } from \"drizzle-orm/postgres-js\";\nimport { migrate } from \"drizzle-orm/postgres-js/migrator\";\nimport postgres from \"postgres\";\nimport { PostgresProvider } from \"./PostgresProvider.ts\";\n\nexport class NodePostgresProvider extends PostgresProvider {\n static readonly SSL_MODES = [\n \"require\",\n \"allow\",\n \"prefer\",\n \"verify-full\",\n ] as const;\n\n protected client?: postgres.Sql;\n protected pg?: PostgresJsDatabase;\n\n /**\n * Get the Drizzle Postgres database instance.\n */\n public override get db(): PostgresJsDatabase {\n if (!this.pg) {\n throw new AlephaError(\"Database not initialized\");\n }\n\n return this.pg;\n }\n\n protected override async executeMigrations(\n migrationsFolder: string,\n ): Promise<void> {\n // Set search_path so schema-free migration SQL resolves to the correct schema.\n // postgres.js doesn't support the `connection` startup parameter with pooled\n // connections (e.g. Neon), so we SET it explicitly before running migrations.\n if (this.schema !== \"public\") {\n await this.db.execute(\n sql.raw(`SET search_path TO ${this.schema}, public`),\n );\n }\n await migrate(this.db, {\n migrationsFolder,\n migrationsTable: this.migrationsTable,\n });\n }\n\n // -------------------------------------------------------------------------------------------------------------------\n\n public async connect(): Promise<void> {\n const options = this.getClientOptions();\n\n this.log.debug(\"Connect ..\", {\n ...options,\n password: options.password ? \"****\" : undefined, // hide password\n });\n\n const client = postgres(options);\n await client`SELECT 1`; // test connection\n\n this.client = client;\n this.pg = drizzle(client, {\n logger: {\n // forward logs\n logQuery: (query: string, params: unknown[]) => {\n this.log.trace(query, { params });\n },\n },\n });\n\n this.log.info(\"Connection OK\");\n }\n\n public async close(): Promise<void> {\n if (this.client) {\n this.log.debug(\"Close...\");\n\n await this.client.end();\n\n this.client = undefined;\n this.pg = undefined;\n\n this.log.info(\"Connection closed\");\n }\n }\n\n // -------------------------------------------------------------------------------------------------------------------\n\n /**\n * Map the DATABASE_URL to postgres client options.\n */\n protected getClientOptions(): postgres.Options<any> {\n const url = new URL(this.url);\n\n const options: postgres.Options<any> = {\n host: url.hostname,\n user: decodeURIComponent(url.username),\n database: decodeURIComponent(url.pathname.replace(\"/\", \"\")),\n password: decodeURIComponent(url.password),\n port: Number(url.port || 5432),\n ssl: this.ssl(url),\n onnotice: () => {\n // let drizzle handle logs\n },\n };\n\n // Pool options — only set when explicitly configured via env vars\n if (this.pgEnv.POOL_MAX != null) options.max = this.pgEnv.POOL_MAX;\n if (this.pgEnv.POOL_IDLE_TIMEOUT != null)\n options.idle_timeout = this.pgEnv.POOL_IDLE_TIMEOUT;\n if (this.pgEnv.POOL_CONNECT_TIMEOUT != null)\n options.connect_timeout = this.pgEnv.POOL_CONNECT_TIMEOUT;\n\n // Set search_path at connection level so schema-free migration SQL\n // resolves to the correct PostgreSQL schema across all pool connections.\n if (this.schema !== \"public\") {\n options.connection = { search_path: `${this.schema}, public` };\n }\n\n return options;\n }\n\n protected ssl(\n url: URL,\n ): \"require\" | \"allow\" | \"prefer\" | \"verify-full\" | undefined {\n const mode = url.searchParams.get(\"sslmode\");\n for (const it of NodePostgresProvider.SSL_MODES) {\n if (mode === it) {\n return it;\n }\n }\n }\n}\n","import { mkdir } from \"node:fs/promises\";\nimport { createRequire } from \"node:module\";\nimport type { PGlite } from \"@electric-sql/pglite\";\nimport { $env, $hook, $inject, AlephaError } from \"alepha\";\nimport { DatabaseProvider, databaseEnvSchema, type SQLLike } from \"alepha/orm\";\nimport { sql } from \"drizzle-orm\";\nimport type { PgliteDatabase } from \"drizzle-orm/pglite\";\nimport { migrate } from \"drizzle-orm/pglite/migrator\";\nimport { postgresEnvSchema } from \"../schemas/postgresEnvSchema.ts\";\nimport { PostgresModelBuilder } from \"../services/PostgresModelBuilder.ts\";\n\nexport interface PgLiteModule {\n PGlite: typeof PGlite;\n}\n\nexport class PglitePostgresProvider extends DatabaseProvider {\n public static importPglite(): PgLiteModule | undefined {\n try {\n return createRequire(import.meta.url)(\"@electric-sql/pglite\");\n } catch {\n // ignored\n }\n }\n\n public override get schema(): string {\n return this.pgEnv.POSTGRES_SCHEMA ?? \"public\";\n }\n\n protected readonly env = $env(databaseEnvSchema);\n protected readonly pgEnv = $env(postgresEnvSchema);\n protected readonly builder = $inject(PostgresModelBuilder);\n\n protected client?: PGlite;\n protected pglite?: PgliteDatabase;\n\n public get name() {\n return \"postgres\";\n }\n\n public get driver() {\n return \"pglite\";\n }\n\n public override readonly dialect = \"postgresql\";\n\n public override get supportsTransactions(): boolean {\n return false;\n }\n\n public override get url(): string {\n let path = this.env.DATABASE_URL;\n\n if (!path) {\n if (this.alepha.isTest()) {\n path = \":memory:\"; // use in-memory database for tests by default\n } else {\n path = \"node_modules/.alepha/pglite\"; // default path for dev\n }\n } else {\n if (path.includes(\":memory:\")) {\n // like postgres://:memory: or pglite://:memory:\n path = \":memory:\";\n } else if (path.startsWith(\"file://\")) {\n path = path.replace(\"file://\", \"\");\n }\n }\n\n return path;\n }\n\n public override get db(): PgliteDatabase {\n if (!this.pglite) {\n throw new AlephaError(\"Database not initialized\");\n }\n\n return this.pglite;\n }\n\n public override async execute(\n statement: SQLLike,\n ): Promise<Array<Record<string, unknown>>> {\n const { rows } = await this.db.execute(statement);\n return rows;\n }\n\n protected readonly onStart = $hook({\n on: \"start\",\n handler: async () => {\n if (Object.keys(this.kit.getModels(this)).length === 0) {\n return;\n }\n\n const module = PglitePostgresProvider.importPglite();\n if (!module) {\n throw new AlephaError(\n \"@electric-sql/pglite is not installed. Please install it to use the pglite driver.\",\n );\n }\n\n const { drizzle } = createRequire(import.meta.url)(\"drizzle-orm/pglite\");\n const path = this.url;\n\n if (path !== \":memory:\") {\n await mkdir(path, { recursive: true }).catch(() => null);\n this.client = new module.PGlite(path);\n } else {\n this.client = new module.PGlite();\n }\n\n this.pglite = drizzle({\n client: this.client,\n });\n\n await this.migrate();\n\n this.log.info(`Using PGlite database at ${path}`);\n },\n });\n\n protected readonly onStop = $hook({\n on: \"stop\",\n handler: async () => {\n if (this.client) {\n this.log.debug(\"Closing PGlite connection...\");\n await this.client.close();\n this.client = undefined;\n this.pglite = undefined;\n this.log.info(\"PGlite connection closed\");\n }\n },\n });\n\n protected async executeMigrations(migrationsFolder: string): Promise<void> {\n // Set search_path so schema-free migration SQL resolves to the correct schema.\n // PGlite uses a single connection, so SET persists through the migration.\n if (this.schema !== \"public\") {\n await this.db.execute(\n sql.raw(`SET search_path TO ${this.schema}, public`),\n );\n }\n await migrate(this.db, {\n migrationsFolder,\n migrationsTable: this.migrationsTable,\n });\n }\n}\n","import { $module, type Alepha } from \"alepha\";\nimport { AlephaOrm, DatabaseProvider, databaseEnvSchema } from \"alepha/orm\";\nimport { BunPostgresProvider } from \"./providers/BunPostgresProvider.ts\";\nimport { CloudflareHyperdriveProvider } from \"./providers/CloudflareHyperdriveProvider.ts\";\nimport { NodePostgresProvider } from \"./providers/NodePostgresProvider.ts\";\nimport { PglitePostgresProvider } from \"./providers/PglitePostgresProvider.ts\";\nimport { PostgresProvider } from \"./providers/PostgresProvider.ts\";\nimport { PostgresModelBuilder } from \"./services/PostgresModelBuilder.ts\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport * from \"./providers/BunPostgresProvider.ts\";\nexport * from \"./providers/CloudflareHyperdriveProvider.ts\";\nexport * from \"./providers/NodePostgresProvider.ts\";\nexport * from \"./providers/PglitePostgresProvider.ts\";\nexport * from \"./providers/PostgresProvider.ts\";\nexport * from \"./schemas/postgresEnvSchema.ts\";\nexport * from \"./services/PostgresModelBuilder.ts\";\nexport * from \"./types/byte.ts\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport const AlephaOrmPostgres = $module({\n name: \"alepha.orm.postgres\",\n services: [PostgresModelBuilder],\n variants: [\n PostgresProvider,\n CloudflareHyperdriveProvider,\n NodePostgresProvider,\n BunPostgresProvider,\n PglitePostgresProvider,\n ],\n register: (alepha: Alepha) => {\n const env = alepha.parseEnv(databaseEnvSchema);\n\n const url = env.DATABASE_URL;\n const isBun = alepha.isBun();\n\n if (url?.startsWith(\"hyperdrive:\")) {\n alepha.with({\n optional: true,\n provide: DatabaseProvider,\n use: CloudflareHyperdriveProvider,\n });\n } else if (url?.startsWith(\"pglite:\")) {\n alepha.with({\n optional: true,\n provide: DatabaseProvider,\n use: PglitePostgresProvider,\n });\n } else if (url?.startsWith(\"postgres:\")) {\n alepha.with({\n optional: true,\n provide: DatabaseProvider,\n use: isBun ? BunPostgresProvider : NodePostgresProvider,\n });\n }\n\n // Chain core ORM module AFTER substitution so its own SQLite default\n // doesn't preempt our Postgres-specific provider choice.\n alepha.with(AlephaOrm);\n },\n});\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","names":["sql","sql","sql","sql","sql","migrate"],"sources":["../../../src/orm/postgres/schemas/postgresEnvSchema.ts","../../../src/orm/postgres/types/byte.ts","../../../src/orm/postgres/services/PostgresModelBuilder.ts","../../../src/orm/postgres/providers/PostgresProvider.ts","../../../src/orm/postgres/providers/BunPostgresProvider.ts","../../../src/orm/postgres/providers/CloudflareHyperdriveProvider.ts","../../../src/orm/postgres/providers/NodePostgresProvider.ts","../../../src/orm/postgres/providers/PglitePostgresProvider.ts","../../../src/orm/postgres/index.ts"],"sourcesContent":["import { type Static, t } from \"alepha\";\n\n/**\n * PostgreSQL-specific environment schema.\n *\n * Additional env vars for PostgreSQL providers on top of `databaseEnvSchema`.\n */\nexport const postgresEnvSchema = t.object({\n /**\n * PostgreSQL schema name (defaults to `\"public\"` when unset).\n */\n POSTGRES_SCHEMA: t.optional(t.text()),\n\n /**\n * Maximum number of connections in the pool.\n */\n POOL_MAX: t.optional(t.integer()),\n\n /**\n * Seconds a connection can be idle before being closed.\n */\n POOL_IDLE_TIMEOUT: t.optional(t.integer()),\n\n /**\n * Seconds to wait when establishing a new connection.\n */\n POOL_CONNECT_TIMEOUT: t.optional(t.integer()),\n});\n\ndeclare module \"alepha\" {\n interface Env extends Partial<Static<typeof postgresEnvSchema>> {}\n}\n","import { customType } from \"drizzle-orm/pg-core\";\n\n/**\n * Postgres bytea type.\n */\nexport const byte = customType<{\n data: Buffer;\n}>({\n dataType: () => \"bytea\",\n});\n","import { AlephaError, type TObject, type TSchema, t } from \"alepha\";\nimport {\n type EntityPrimitive,\n type FromSchema,\n ModelBuilder,\n PG_CREATED_AT,\n PG_GENERATED,\n PG_IDENTITY,\n PG_PRIMARY_KEY,\n PG_REF,\n PG_SERIAL,\n PG_UPDATED_AT,\n type PgGeneratedOptions,\n type PgIdentityOptions,\n type PgRefOptions,\n type SequencePrimitive,\n schema,\n sql,\n} from \"alepha/orm\";\nimport type { BuildExtraConfigColumns } from \"drizzle-orm\";\nimport * as pg from \"drizzle-orm/pg-core\";\nimport {\n check,\n foreignKey,\n index,\n type PgEnum,\n type PgSchema,\n type PgTableExtraConfigValue,\n type PgTableWithColumns,\n pgEnum,\n pgSchema,\n pgSequence,\n pgTable,\n unique,\n uniqueIndex,\n} from \"drizzle-orm/pg-core\";\nimport { byte } from \"../types/byte.ts\";\n\nexport class PostgresModelBuilder extends ModelBuilder {\n protected schemas = new Map<string, PgSchema>();\n\n /**\n * Create a primary key column with UUID v7\n */\n protected getPrimaryKeyUUID(key: string) {\n return pg.uuid(key).default(sql`uuidv7()`);\n }\n\n protected getPgSchema(name: string) {\n if (!this.schemas.has(name) && name !== \"public\") {\n this.schemas.set(name, pgSchema(name));\n }\n\n const nsp =\n name !== \"public\"\n ? this.schemas.get(name)\n : ({\n enum: pgEnum,\n table: pgTable,\n sequence: pgSequence,\n } as any);\n\n if (!nsp) {\n throw new AlephaError(`Postgres schema ${name} not found`);\n }\n\n return nsp;\n }\n\n public buildTable(\n entity: EntityPrimitive<any>,\n options: {\n tables: Map<string, unknown>;\n enums: Map<string, unknown>;\n schemas: Map<string, unknown>;\n schema: string;\n },\n ) {\n const tableName = entity.name;\n if (options.tables.has(tableName)) {\n return;\n }\n\n const nsp = this.getPgSchema(options.schema);\n\n // Register PgSchema so drizzle-kit knows the schema is declared in code.\n // Without this, pushSchema diffs \"schema exists in DB but not in code\" → DROP.\n if (options.schema !== \"public\" && !options.schemas.has(options.schema)) {\n options.schemas.set(options.schema, nsp);\n }\n\n const columns = this.schemaToPgColumns(\n tableName,\n entity.schema,\n nsp,\n options.enums,\n options.tables,\n );\n\n // Build the config function that includes indexes, foreign keys, constraints, and custom config\n const configFn = this.getTableConfig(entity, options.tables);\n\n const table = nsp.table(tableName, columns, configFn);\n\n options.tables.set(tableName, table);\n }\n\n public buildSequence(\n sequence: SequencePrimitive,\n options: {\n sequences: Map<string, unknown>;\n schema: string;\n },\n ) {\n const sequenceName = sequence.name;\n if (options.sequences.has(sequenceName)) {\n return;\n }\n\n const nsp = this.getPgSchema(options.schema);\n\n options.sequences.set(\n sequenceName,\n nsp.sequence(sequenceName, sequence.options),\n );\n }\n\n // -------------------------------------------------------------------------------------------------------------------\n\n /**\n * Get PostgreSQL-specific config builder for the table.\n */\n protected getTableConfig(\n entity: EntityPrimitive,\n tables: Map<string, unknown>,\n ):\n | ((\n self: BuildExtraConfigColumns<string, any, \"pg\">,\n ) => PgTableExtraConfigValue[])\n | undefined {\n // PostgreSQL-specific builders\n const pgBuilders = {\n index,\n uniqueIndex,\n unique,\n check,\n foreignKey,\n };\n\n // Table resolver function\n const tableResolver = (entityName: string) => {\n return tables.get(entityName) as any;\n };\n\n return this.buildTableConfig<\n PgTableExtraConfigValue,\n BuildExtraConfigColumns<string, any, \"pg\">\n >(entity, pgBuilders as any, tableResolver);\n }\n\n schemaToPgColumns = <T extends TObject>(\n tableName: string,\n schema: T,\n nsp: PgSchema,\n enums: Map<string, unknown>,\n tables: Map<string, unknown>,\n ): FromSchema<T> => {\n return Object.entries(schema.properties).reduce<Partial<FromSchema<T>>>(\n (columns, [key, value]) => {\n let col = this.mapFieldToColumn(tableName, key, value, nsp, enums);\n\n if (\"default\" in value && value.default != null) {\n col = col.default(value.default as any);\n }\n\n if (PG_PRIMARY_KEY in value) {\n col = col.primaryKey();\n }\n\n if (PG_REF in value) {\n const config = value[PG_REF] as PgRefOptions;\n col = col.references(() => {\n const ref = config.ref();\n const table = tables.get(\n ref.entity.name,\n ) as PgTableWithColumns<any>;\n\n if (!table) {\n throw new AlephaError(\n `Referenced table ${ref.entity.name} not found for ${tableName}.${key}`,\n );\n }\n\n const target = table[ref.name];\n if (!target) {\n throw new AlephaError(\n `Referenced column ${ref.name} not found in table ${ref.entity.name} for ${tableName}.${key}`,\n );\n }\n\n return target;\n }, config.actions);\n }\n\n if (PG_GENERATED in value) {\n const gen = value[PG_GENERATED] as PgGeneratedOptions;\n col = col.generatedAlwaysAs(gen.expression);\n }\n\n if (schema.required?.includes(key)) {\n col = col.notNull();\n }\n\n (columns as Record<string, unknown>)[key] = col;\n return columns;\n },\n {},\n ) as FromSchema<T>;\n };\n\n mapFieldToColumn = (\n tableName: string,\n fieldName: string,\n value: TSchema,\n nsp: PgSchema,\n enums: Map<string, any>,\n ) => {\n const key = this.toColumnName(fieldName);\n\n if (\n // is nullish ?\n \"anyOf\" in value &&\n Array.isArray(value.anyOf) &&\n value.anyOf.length === 2 &&\n value.anyOf.some((it: TSchema) => t.schema.isNull(it))\n ) {\n // then, remove nullish\n value = value.anyOf.find((it: TSchema) => !t.schema.isNull(it))!;\n }\n\n if (t.schema.isInteger(value)) {\n if (PG_SERIAL in value) {\n return pg.serial(key);\n }\n\n if (PG_IDENTITY in value) {\n const options = value[PG_IDENTITY] as PgIdentityOptions;\n if (options.mode === \"byDefault\") {\n return pg.integer(key).generatedByDefaultAsIdentity(options);\n }\n return pg.integer(key).generatedAlwaysAsIdentity(options);\n }\n\n return pg.integer(key);\n }\n\n if (t.schema.isBigInt(value)) {\n if (PG_IDENTITY in value) {\n const options = value[PG_IDENTITY] as PgIdentityOptions;\n if (options.mode === \"byDefault\") {\n return pg\n .bigint(key, { mode: \"bigint\" })\n .generatedByDefaultAsIdentity(options);\n }\n return pg\n .bigint(key, { mode: \"bigint\" })\n .generatedAlwaysAsIdentity(options);\n }\n\n return pg.bigint(key, { mode: \"bigint\" });\n }\n\n if (t.schema.isNumber(value)) {\n if (PG_IDENTITY in value) {\n const options = value[PG_IDENTITY] as PgIdentityOptions;\n if (options.mode === \"byDefault\") {\n return pg\n .bigint(key, { mode: \"number\" })\n .generatedByDefaultAsIdentity(options);\n }\n return pg\n .bigint(key, { mode: \"number\" })\n .generatedAlwaysAsIdentity(options);\n }\n\n if (value.format === \"int64\") {\n return pg.bigint(key, { mode: \"number\" });\n }\n\n return pg.numeric(key);\n }\n\n const isTypeEnum = (value: any): value is { enum: any[] } =>\n t.schema.isUnsafe(value) &&\n \"type\" in value &&\n value.type === \"string\" &&\n \"enum\" in value &&\n Array.isArray(value.enum);\n\n if (t.schema.isString(value) && !isTypeEnum(value)) {\n return this.mapStringToColumn(key, value);\n }\n\n if (t.schema.isBoolean(value)) {\n return pg.boolean(key);\n }\n\n if (t.schema.isObject(value)) {\n return schema(key, value);\n }\n\n if (t.schema.isRecord(value)) {\n return schema(key, value);\n }\n\n if (t.schema.isArray(value)) {\n if (t.schema.isObject(value.items)) {\n return schema(key, value);\n }\n if (t.schema.isRecord(value.items)) {\n return schema(key, value);\n }\n if (t.schema.isString(value.items)) {\n return pg.text(key).array();\n }\n if (t.schema.isInteger(value.items)) {\n return pg.integer(key).array();\n }\n if (t.schema.isNumber(value.items)) {\n return pg.numeric(key).array();\n }\n if (t.schema.isBoolean(value.items)) {\n return pg.boolean(key).array();\n }\n if (isTypeEnum(value.items)) {\n return pg.text(key).array();\n }\n }\n\n // Enum handling\n if (isTypeEnum(value)) {\n if (!value.enum.every((it) => typeof it === \"string\")) {\n throw new AlephaError(\n `Enum for ${fieldName} must be an array of strings, got ${JSON.stringify(\n value.enum,\n )}`,\n );\n }\n\n // SQL Enum (default for t.enum unless mode: \"text\")\n if ((value as any).mode !== \"text\") {\n const enumName = (value as any).enumName ?? `${tableName}_${key}_enum`;\n\n if (enums.has(enumName)) {\n const values = (\n enums.get(enumName) as PgEnum<[string]>\n ).enumValues.join(\",\");\n const newValues = value.enum.join(\",\");\n if (values !== newValues) {\n throw new AlephaError(\n `Enum name conflict for ${enumName}: [${values}] vs [${newValues}]`,\n );\n }\n }\n\n enums.set(enumName, nsp.enum(enumName, value.enum as [string]));\n\n return enums.get(enumName)(key);\n }\n\n // else, map to TEXT\n return this.mapStringToColumn(key, value);\n }\n\n throw new AlephaError(\n `Unsupported schema type for ${fieldName} as ${JSON.stringify(value)}`,\n );\n };\n\n /**\n * Map a string to a PG column.\n *\n * @param key The key of the field.\n * @param value The value of the field.\n */\n mapStringToColumn = (key: string, value: TSchema) => {\n if (\"format\" in value) {\n if (value.format === \"uuid\") {\n if (PG_PRIMARY_KEY in value) {\n return this.getPrimaryKeyUUID(key);\n }\n\n return pg.uuid(key);\n }\n\n if (value.format === \"byte\") {\n return byte(key);\n }\n\n if (value.format === \"date-time\") {\n if (PG_CREATED_AT in value) {\n return pg\n .timestamp(key, { mode: \"string\", withTimezone: true })\n .defaultNow();\n }\n if (PG_UPDATED_AT in value) {\n return pg\n .timestamp(key, { mode: \"string\", withTimezone: true })\n .defaultNow();\n }\n return pg.timestamp(key, { mode: \"string\", withTimezone: true });\n }\n\n if (value.format === \"date\") {\n return pg.date(key, { mode: \"string\" });\n }\n }\n\n return pg.text(key);\n };\n}\n","import { $env, $hook, $inject, $pipeline, AlephaError } from \"alepha\";\nimport { $lock } from \"alepha/lock\";\nimport {\n DatabaseProvider,\n DbMigrationError,\n databaseEnvSchema,\n type SQLLike,\n} from \"alepha/orm\";\nimport { sql } from \"drizzle-orm\";\nimport type { PgDatabase } from \"drizzle-orm/pg-core\";\nimport { postgresEnvSchema } from \"../schemas/postgresEnvSchema.ts\";\nimport { PostgresModelBuilder } from \"../services/PostgresModelBuilder.ts\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\n/**\n * Abstract base class for PostgreSQL database providers.\n *\n * Provides shared logic for Node.js and Bun PostgreSQL providers:\n * - Environment variable handling (DATABASE_URL, POSTGRES_SCHEMA)\n * - Schema name resolution (with test schema generation)\n * - SQL execution with error wrapping\n * - Lifecycle hooks (start with migration lock, stop with test cleanup)\n *\n * Subclasses must implement `connect()`, `close()`, and `executeMigrations()`.\n */\nexport abstract class PostgresProvider extends DatabaseProvider {\n protected readonly env = $env(databaseEnvSchema);\n protected readonly pgEnv = $env(postgresEnvSchema);\n protected readonly builder = $inject(PostgresModelBuilder);\n\n public override readonly dialect = \"postgresql\";\n\n public get name() {\n return \"postgres\";\n }\n\n /**\n * In testing mode, the schema name will be generated and deleted after the test.\n */\n protected schemaForTesting = this.alepha.isTest()\n ? this.generateTestSchemaName()\n : undefined;\n\n public override get url() {\n if (!this.env.DATABASE_URL) {\n throw new AlephaError(\"DATABASE_URL is not defined in the environment\");\n }\n\n return this.env.DATABASE_URL;\n }\n\n /**\n * Execute a SQL statement.\n */\n public override async execute(\n statement: SQLLike,\n ): Promise<Array<Record<string, unknown>>> {\n return await this.db.execute(statement);\n }\n\n /**\n * Get Postgres schema used by this provider.\n */\n public override get schema(): string {\n if (this.schemaForTesting) {\n return this.schemaForTesting;\n }\n\n if (this.pgEnv.POSTGRES_SCHEMA) {\n return this.pgEnv.POSTGRES_SCHEMA;\n }\n\n return \"public\";\n }\n\n public abstract override get db(): PgDatabase<any>;\n\n /**\n * Establish the database connection.\n */\n public abstract connect(): Promise<void>;\n\n /**\n * Close the database connection.\n */\n public abstract close(): Promise<void>;\n\n // -------------------------------------------------------------------------------------------------------------------\n\n protected readonly onStart = $hook({\n on: \"start\",\n handler: async () => {\n await this.connect();\n await this.generateTestSchema();\n\n // never migrate in serverless mode (vercel, netlify, ...)\n if (!this.alepha.isServerless()) {\n try {\n await this.migrateLock.run();\n } catch (error) {\n throw new DbMigrationError(error);\n }\n }\n },\n });\n\n protected readonly onStop = $hook({\n on: \"stop\",\n handler: async () => {\n await this.dropTestSchema();\n await this.close();\n },\n });\n\n protected migrateLock = $pipeline({\n use: [$lock({ name: \"postgres:migrate\" })],\n handler: async () => {\n await this.migrate();\n },\n });\n\n // -------------------------------------------------------------------------------------------------------------------\n // Create unique schema for tests and clean up after. Format: test_alepha_{epoch_seconds}_{random8}\n // -------------------------------------------------------------------------------------------------------------------\n\n protected async generateTestSchema() {\n if (\n this.alepha.isTest() &&\n this.schemaForTesting?.startsWith(\"test_alepha_\")\n ) {\n // Self-healing: drop stale schemas left behind by crashed/failed test runs\n await this.cleanupStaleTestSchemas();\n\n await this.execute(\n sql`CREATE SCHEMA IF NOT EXISTS ${sql.raw(this.schemaForTesting)}`,\n );\n }\n }\n\n /**\n * Drop the current test schema if applicable.\n */\n protected async dropTestSchema(): Promise<void> {\n if (\n this.alepha.isTest() &&\n this.schemaForTesting?.startsWith(\"test_alepha_\")\n ) {\n this.log.info(`Deleting test schema '${this.schemaForTesting}' ...`);\n await this.execute(\n sql`DROP SCHEMA IF EXISTS ${sql.raw(this.schemaForTesting)} CASCADE`,\n );\n this.log.info(`Test schema '${this.schemaForTesting}' deleted`);\n }\n }\n\n /**\n * Remove stale test schemas older than 1 hour.\n *\n * Parses the embedded epoch from schema names (format: test_alepha_{epoch}_{random8})\n * and drops any that exceed the TTL. This handles schemas left behind by crashed tests,\n * killed processes, or failed startups where stop() never fired.\n *\n * Uses a PG advisory lock so only one concurrent test process performs the cleanup.\n */\n protected async cleanupStaleTestSchemas(): Promise<void> {\n try {\n // Non-blocking advisory lock — first process in cleans, others skip\n const [lock] = await this.execute(\n sql`SELECT pg_try_advisory_lock(hashtext('alepha:test:schema:cleanup')) AS acquired`,\n );\n\n if (!lock?.acquired) {\n return;\n }\n\n try {\n const result = await this.execute(\n sql`SELECT schema_name FROM information_schema.schemata WHERE schema_name LIKE 'test_alepha_%'`,\n );\n\n const now = this.dateTime.nowMillis();\n const maxAge = 60 * 60 * 1000; // 1 hour\n\n for (const row of result) {\n const name = row.schema_name as string;\n\n // Skip our own schema (it was just created or is about to be)\n if (name === this.schemaForTesting) {\n continue;\n }\n\n const age = this.parseTestSchemaAge(name, now);\n if (age !== undefined && age > maxAge) {\n this.log.warn(\n `Dropping stale test schema '${name}' (age: ${Math.round(age / 60_000)}min) ...`,\n );\n await this.execute(\n sql`DROP SCHEMA IF EXISTS ${sql.raw(name)} CASCADE`,\n );\n }\n }\n } finally {\n await this.execute(\n sql`SELECT pg_advisory_unlock(hashtext('alepha:test:schema:cleanup'))`,\n );\n }\n } catch (error) {\n // Don't fail test setup if stale cleanup fails\n this.log.warn(\"Failed to clean up stale test schemas\", { error });\n }\n }\n\n /**\n * Parse the age in milliseconds from a test schema name.\n * Format: test_alepha_{epoch_seconds}_{random8}\n * Returns undefined if the name doesn't match the expected format.\n */\n protected parseTestSchemaAge(name: string, now: number): number | undefined {\n const parts = name.split(\"_\");\n // test_alepha_{epoch}_{random} → [\"test\", \"alepha\", epoch, random]\n if (parts.length !== 4 || parts[0] !== \"test\" || parts[1] !== \"alepha\") {\n return undefined;\n }\n\n const epoch = Number(parts[2]);\n if (!Number.isFinite(epoch) || epoch <= 0) {\n return undefined;\n }\n\n return now - epoch * 1000;\n }\n}\n","import { AlephaError } from \"alepha\";\nimport { sql } from \"drizzle-orm\";\nimport type { BunSQLDatabase } from \"drizzle-orm/bun-sql\";\nimport type { PgDatabase } from \"drizzle-orm/pg-core\";\nimport { PostgresProvider } from \"./PostgresProvider.ts\";\n\n/**\n * Bun PostgreSQL provider using Drizzle ORM with Bun's native SQL client.\n *\n * This provider uses Bun's built-in SQL class for PostgreSQL connections,\n * which provides excellent performance on the Bun runtime.\n *\n * @example\n * ```ts\n * // Set DATABASE_URL environment variable\n * // DATABASE_URL=postgres://user:password@localhost:5432/database\n *\n * // Or configure programmatically\n * alepha.with({\n * provide: DatabaseProvider,\n * use: BunPostgresProvider,\n * });\n * ```\n */\nexport class BunPostgresProvider extends PostgresProvider {\n protected client?: Bun.SQL;\n protected bunDb?: BunSQLDatabase;\n\n /**\n * Get the Drizzle Postgres database instance.\n */\n public override get db(): PgDatabase<any> {\n if (!this.bunDb) {\n throw new AlephaError(\"Database not initialized\");\n }\n\n return this.bunDb as unknown as PgDatabase<any>;\n }\n\n protected override async executeMigrations(\n migrationsFolder: string,\n ): Promise<void> {\n if (this.schema !== \"public\") {\n await this.db.execute(\n sql.raw(`SET search_path TO ${this.schema}, public`),\n );\n }\n const { migrate } = await import(\"drizzle-orm/bun-sql/migrator\");\n await migrate(this.bunDb!, {\n migrationsFolder,\n migrationsTable: this.migrationsTable,\n });\n }\n\n // -------------------------------------------------------------------------------------------------------------------\n\n public async connect(): Promise<void> {\n this.log.debug(\"Connect ..\");\n\n // Check if we're running in Bun\n if (typeof Bun === \"undefined\") {\n throw new AlephaError(\n \"BunPostgresProvider requires the Bun runtime. Use NodePostgresProvider for Node.js.\",\n );\n }\n\n const { drizzle } = await import(\"drizzle-orm/bun-sql\");\n\n // Create Bun SQL client with pool options\n // Set search_path via connection URL so all pool connections use the correct schema\n let connectionUrl = this.url;\n if (this.schema !== \"public\") {\n const separator = connectionUrl.includes(\"?\") ? \"&\" : \"?\";\n connectionUrl += `${separator}search_path=${this.schema},public`;\n }\n const bunOptions: Record<string, any> = { url: connectionUrl };\n if (this.pgEnv.POOL_MAX != null) {\n bunOptions.max = this.pgEnv.POOL_MAX;\n }\n if (this.pgEnv.POOL_IDLE_TIMEOUT != null) {\n bunOptions.idleTimeout = this.pgEnv.POOL_IDLE_TIMEOUT;\n }\n if (this.pgEnv.POOL_CONNECT_TIMEOUT != null) {\n bunOptions.connectionTimeout = this.pgEnv.POOL_CONNECT_TIMEOUT;\n }\n this.client = new Bun.SQL(bunOptions);\n\n // Test connection\n await this.client.unsafe(\"SELECT 1\");\n\n this.bunDb = drizzle({\n client: this.client,\n logger: {\n logQuery: (query: string, params: unknown[]) => {\n this.log.trace(query, { params });\n },\n },\n });\n\n this.log.info(\"Connection OK\");\n }\n\n public async close(): Promise<void> {\n if (this.client) {\n this.log.debug(\"Close...\");\n\n await this.client.close();\n\n this.client = undefined;\n this.bunDb = undefined;\n\n this.log.info(\"Connection closed\");\n }\n }\n}\n","import { $env, $hook, $inject, AlephaError, t } from \"alepha\";\nimport { DatabaseProvider, type SQLLike } from \"alepha/orm\";\nimport { sql } from \"drizzle-orm\";\nimport type { PgDatabase } from \"drizzle-orm/pg-core\";\nimport { PostgresModelBuilder } from \"../services/PostgresModelBuilder.ts\";\n\n/**\n * Cloudflare Hyperdrive PostgreSQL provider using Drizzle ORM.\n *\n * Connects to an external PostgreSQL database through Cloudflare Hyperdrive,\n * which provides connection pooling and caching at the edge.\n *\n * Creates a fresh connection per request, since Cloudflare Workers\n * cannot reuse I/O objects across request contexts.\n *\n * URL format: hyperdrive://BINDING_NAME\n */\nexport class CloudflareHyperdriveProvider extends DatabaseProvider {\n protected readonly builder = $inject(PostgresModelBuilder);\n protected readonly env = $env(\n t.object({\n DATABASE_URL: t.string({\n description: \"Expect to be 'hyperdrive://BINDING'\",\n }),\n POSTGRES_SCHEMA: t.optional(t.text()),\n }),\n );\n\n public override get schema(): string {\n return this.env.POSTGRES_SCHEMA ?? \"public\";\n }\n\n protected postgresFn?: any;\n protected drizzleFn?: any;\n protected bindingName?: string;\n\n public get name() {\n return \"postgres\";\n }\n\n public get driver() {\n return \"hyperdrive\";\n }\n\n public override readonly dialect = \"postgresql\";\n\n public override get url(): string {\n return this.env.DATABASE_URL;\n }\n\n /**\n * Get a fresh Drizzle instance per request.\n *\n * Reads the current Hyperdrive binding from `cloudflare.env`\n * and creates a new postgres client each time, avoiding the\n * \"Cannot perform I/O on behalf of a different request\" error.\n */\n public override get db(): PgDatabase<any> {\n if (!this.postgresFn || !this.drizzleFn || !this.bindingName) {\n throw new AlephaError(\"Hyperdrive database not initialized\");\n }\n\n const cloudflareEnv = this.alepha.get(\"cloudflare.env\") as\n | Record<string, unknown>\n | undefined;\n if (!cloudflareEnv) {\n throw new AlephaError(\n \"Cloudflare Workers environment not found in Alepha store under 'cloudflare.env'.\",\n );\n }\n\n const binding = cloudflareEnv[this.bindingName] as\n | { connectionString: string }\n | undefined;\n if (!binding?.connectionString) {\n throw new AlephaError(\n `Hyperdrive binding '${this.bindingName}' not found in Cloudflare Workers environment.`,\n );\n }\n\n const pgOptions: Record<string, any> = { prepare: false };\n\n // Set search_path so schema-free migration SQL resolves to the correct schema.\n if (this.schema !== \"public\") {\n pgOptions.connection = { search_path: `${this.schema}, public` };\n }\n\n const client = this.postgresFn(binding.connectionString, pgOptions);\n return this.drizzleFn(client as any) as unknown as PgDatabase<any>;\n }\n\n public override async execute(\n query: SQLLike,\n ): Promise<Array<Record<string, unknown>>> {\n return this.db.execute(query);\n }\n\n protected readonly onStart = $hook({\n on: \"start\",\n handler: async () => {\n this.bindingName = this.env.DATABASE_URL.replace(\"hyperdrive://\", \"\");\n\n // Pre-load modules so db getter is synchronous\n const pgModule = await import(\"postgres\");\n const drizzleModule = await import(\"drizzle-orm/postgres-js\");\n this.postgresFn = pgModule.default ?? pgModule;\n this.drizzleFn = drizzleModule.drizzle;\n\n this.log.info(\"Using Cloudflare Hyperdrive (PostgreSQL)\");\n },\n });\n\n protected async executeMigrations(migrationsFolder: string): Promise<void> {\n this.log.debug(`Running Postgres migrations from '${migrationsFolder}'...`);\n try {\n if (this.schema !== \"public\") {\n await this.db.execute(\n sql.raw(`SET search_path TO ${this.schema}, public`),\n );\n }\n const { migrate } = await import(\"drizzle-orm/postgres-js/migrator\");\n await migrate(this.db as any, {\n migrationsFolder,\n migrationsTable: this.migrationsTable,\n });\n this.log.debug(\"Postgres migrations completed successfully\");\n } catch (error) {\n const errorMessage =\n error instanceof Error\n ? `${error.name}: ${error.message}`\n : String(error);\n throw new AlephaError(\n `Postgres migration failed from '${migrationsFolder}': ${errorMessage}`,\n { cause: error },\n );\n }\n }\n}\n","import { AlephaError } from \"alepha\";\nimport { sql } from \"drizzle-orm\";\nimport type { PostgresJsDatabase } from \"drizzle-orm/postgres-js\";\nimport { drizzle } from \"drizzle-orm/postgres-js\";\nimport { migrate } from \"drizzle-orm/postgres-js/migrator\";\nimport postgres from \"postgres\";\nimport { PostgresProvider } from \"./PostgresProvider.ts\";\n\nexport class NodePostgresProvider extends PostgresProvider {\n static readonly SSL_MODES = [\n \"require\",\n \"allow\",\n \"prefer\",\n \"verify-full\",\n ] as const;\n\n protected client?: postgres.Sql;\n protected pg?: PostgresJsDatabase;\n\n /**\n * Get the Drizzle Postgres database instance.\n */\n public override get db(): PostgresJsDatabase {\n if (!this.pg) {\n throw new AlephaError(\"Database not initialized\");\n }\n\n return this.pg;\n }\n\n protected override async executeMigrations(\n migrationsFolder: string,\n ): Promise<void> {\n // Set search_path so schema-free migration SQL resolves to the correct schema.\n // postgres.js doesn't support the `connection` startup parameter with pooled\n // connections (e.g. Neon), so we SET it explicitly before running migrations.\n if (this.schema !== \"public\") {\n await this.db.execute(\n sql.raw(`SET search_path TO ${this.schema}, public`),\n );\n }\n await migrate(this.db, {\n migrationsFolder,\n migrationsTable: this.migrationsTable,\n });\n }\n\n // -------------------------------------------------------------------------------------------------------------------\n\n public async connect(): Promise<void> {\n const options = this.getClientOptions();\n\n this.log.debug(\"Connect ..\", {\n ...options,\n password: options.password ? \"****\" : undefined, // hide password\n });\n\n const client = postgres(options);\n await client`SELECT 1`; // test connection\n\n this.client = client;\n this.pg = drizzle(client, {\n logger: {\n // forward logs\n logQuery: (query: string, params: unknown[]) => {\n this.log.trace(query, { params });\n },\n },\n });\n\n this.log.info(\"Connection OK\");\n }\n\n public async close(): Promise<void> {\n if (this.client) {\n this.log.debug(\"Close...\");\n\n await this.client.end();\n\n this.client = undefined;\n this.pg = undefined;\n\n this.log.info(\"Connection closed\");\n }\n }\n\n // -------------------------------------------------------------------------------------------------------------------\n\n /**\n * Map the DATABASE_URL to postgres client options.\n */\n protected getClientOptions(): postgres.Options<any> {\n const url = new URL(this.url);\n\n const options: postgres.Options<any> = {\n host: url.hostname,\n user: decodeURIComponent(url.username),\n database: decodeURIComponent(url.pathname.replace(\"/\", \"\")),\n password: decodeURIComponent(url.password),\n port: Number(url.port || 5432),\n ssl: this.ssl(url),\n onnotice: () => {\n // let drizzle handle logs\n },\n };\n\n // Pool options — only set when explicitly configured via env vars\n if (this.pgEnv.POOL_MAX != null) options.max = this.pgEnv.POOL_MAX;\n if (this.pgEnv.POOL_IDLE_TIMEOUT != null)\n options.idle_timeout = this.pgEnv.POOL_IDLE_TIMEOUT;\n if (this.pgEnv.POOL_CONNECT_TIMEOUT != null)\n options.connect_timeout = this.pgEnv.POOL_CONNECT_TIMEOUT;\n\n // Set search_path at connection level so schema-free migration SQL\n // resolves to the correct PostgreSQL schema across all pool connections.\n if (this.schema !== \"public\") {\n options.connection = { search_path: `${this.schema}, public` };\n }\n\n return options;\n }\n\n protected ssl(\n url: URL,\n ): \"require\" | \"allow\" | \"prefer\" | \"verify-full\" | undefined {\n const mode = url.searchParams.get(\"sslmode\");\n for (const it of NodePostgresProvider.SSL_MODES) {\n if (mode === it) {\n return it;\n }\n }\n }\n}\n","import { mkdir } from \"node:fs/promises\";\nimport { createRequire } from \"node:module\";\nimport type { PGlite } from \"@electric-sql/pglite\";\nimport { $env, $hook, $inject, AlephaError } from \"alepha\";\nimport { DatabaseProvider, databaseEnvSchema, type SQLLike } from \"alepha/orm\";\nimport { sql } from \"drizzle-orm\";\nimport type { PgliteDatabase } from \"drizzle-orm/pglite\";\nimport { migrate } from \"drizzle-orm/pglite/migrator\";\nimport { postgresEnvSchema } from \"../schemas/postgresEnvSchema.ts\";\nimport { PostgresModelBuilder } from \"../services/PostgresModelBuilder.ts\";\n\nexport interface PgLiteModule {\n PGlite: typeof PGlite;\n}\n\nexport class PglitePostgresProvider extends DatabaseProvider {\n public static importPglite(): PgLiteModule | undefined {\n try {\n return createRequire(import.meta.url)(\"@electric-sql/pglite\");\n } catch {\n // ignored\n }\n }\n\n public override get schema(): string {\n return this.pgEnv.POSTGRES_SCHEMA ?? \"public\";\n }\n\n protected readonly env = $env(databaseEnvSchema);\n protected readonly pgEnv = $env(postgresEnvSchema);\n protected readonly builder = $inject(PostgresModelBuilder);\n\n protected client?: PGlite;\n protected pglite?: PgliteDatabase;\n\n public get name() {\n return \"postgres\";\n }\n\n public get driver() {\n return \"pglite\";\n }\n\n public override readonly dialect = \"postgresql\";\n\n public override get supportsTransactions(): boolean {\n return false;\n }\n\n public override get url(): string {\n let path = this.env.DATABASE_URL;\n\n if (!path) {\n if (this.alepha.isTest()) {\n path = \":memory:\"; // use in-memory database for tests by default\n } else {\n path = \"node_modules/.alepha/pglite\"; // default path for dev\n }\n } else {\n if (path.includes(\":memory:\")) {\n // like postgres://:memory: or pglite://:memory:\n path = \":memory:\";\n } else if (path.startsWith(\"file://\")) {\n path = path.replace(\"file://\", \"\");\n }\n }\n\n return path;\n }\n\n public override get db(): PgliteDatabase {\n if (!this.pglite) {\n throw new AlephaError(\"Database not initialized\");\n }\n\n return this.pglite;\n }\n\n public override async execute(\n statement: SQLLike,\n ): Promise<Array<Record<string, unknown>>> {\n const { rows } = await this.db.execute(statement);\n return rows;\n }\n\n protected readonly onStart = $hook({\n on: \"start\",\n handler: async () => {\n if (Object.keys(this.kit.getModels(this)).length === 0) {\n return;\n }\n\n const module = PglitePostgresProvider.importPglite();\n if (!module) {\n throw new AlephaError(\n \"@electric-sql/pglite is not installed. Please install it to use the pglite driver.\",\n );\n }\n\n const { drizzle } = createRequire(import.meta.url)(\"drizzle-orm/pglite\");\n const path = this.url;\n\n if (path !== \":memory:\") {\n await mkdir(path, { recursive: true }).catch(() => null);\n this.client = new module.PGlite(path);\n } else {\n this.client = new module.PGlite();\n }\n\n this.pglite = drizzle({\n client: this.client,\n });\n\n await this.migrate();\n\n this.log.info(`Using PGlite database at ${path}`);\n },\n });\n\n protected readonly onStop = $hook({\n on: \"stop\",\n handler: async () => {\n if (this.client) {\n this.log.debug(\"Closing PGlite connection...\");\n await this.client.close();\n this.client = undefined;\n this.pglite = undefined;\n this.log.info(\"PGlite connection closed\");\n }\n },\n });\n\n protected async executeMigrations(migrationsFolder: string): Promise<void> {\n // Set search_path so schema-free migration SQL resolves to the correct schema.\n // PGlite uses a single connection, so SET persists through the migration.\n if (this.schema !== \"public\") {\n await this.db.execute(\n sql.raw(`SET search_path TO ${this.schema}, public`),\n );\n }\n await migrate(this.db, {\n migrationsFolder,\n migrationsTable: this.migrationsTable,\n });\n }\n}\n","import { $module, type Alepha } from \"alepha\";\nimport { AlephaOrm, DatabaseProvider, databaseEnvSchema } from \"alepha/orm\";\nimport { BunPostgresProvider } from \"./providers/BunPostgresProvider.ts\";\nimport { CloudflareHyperdriveProvider } from \"./providers/CloudflareHyperdriveProvider.ts\";\nimport { NodePostgresProvider } from \"./providers/NodePostgresProvider.ts\";\nimport { PglitePostgresProvider } from \"./providers/PglitePostgresProvider.ts\";\nimport { PostgresProvider } from \"./providers/PostgresProvider.ts\";\nimport { PostgresModelBuilder } from \"./services/PostgresModelBuilder.ts\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport * from \"./providers/BunPostgresProvider.ts\";\nexport * from \"./providers/CloudflareHyperdriveProvider.ts\";\nexport * from \"./providers/NodePostgresProvider.ts\";\nexport * from \"./providers/PglitePostgresProvider.ts\";\nexport * from \"./providers/PostgresProvider.ts\";\nexport * from \"./schemas/postgresEnvSchema.ts\";\nexport * from \"./services/PostgresModelBuilder.ts\";\nexport * from \"./types/byte.ts\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport const AlephaOrmPostgres = $module({\n name: \"alepha.orm.postgres\",\n services: [PostgresModelBuilder],\n variants: [\n PostgresProvider,\n CloudflareHyperdriveProvider,\n NodePostgresProvider,\n BunPostgresProvider,\n PglitePostgresProvider,\n ],\n register: (alepha: Alepha) => {\n const env = alepha.parseEnv(databaseEnvSchema);\n\n const url = env.DATABASE_URL;\n const isBun = alepha.isBun();\n\n if (url?.startsWith(\"hyperdrive:\")) {\n alepha.with({\n optional: true,\n provide: DatabaseProvider,\n use: CloudflareHyperdriveProvider,\n });\n } else if (url?.startsWith(\"pglite:\")) {\n alepha.with({\n optional: true,\n provide: DatabaseProvider,\n use: PglitePostgresProvider,\n });\n } else if (url?.startsWith(\"postgres:\")) {\n alepha.with({\n optional: true,\n provide: DatabaseProvider,\n use: isBun ? BunPostgresProvider : NodePostgresProvider,\n });\n }\n\n // Chain core ORM module AFTER substitution so its own SQLite default\n // doesn't preempt our Postgres-specific provider choice.\n alepha.with(AlephaOrm);\n },\n});\n"],"mappings":"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"}
|
|
@@ -1,8 +1,5 @@
|
|
|
1
|
-
import * as _$alepha from "alepha";
|
|
2
1
|
import { Alepha, KIND, PipelinePrimitive, PipelinePrimitiveOptions, Primitive, Service, Static, TSchema } from "alepha";
|
|
3
|
-
import * as _$alepha_logger0 from "alepha/logger";
|
|
4
2
|
import { DateTimeProvider } from "alepha/datetime";
|
|
5
|
-
import * as _$typebox from "typebox";
|
|
6
3
|
|
|
7
4
|
//#region ../../src/queue/core/providers/QueueProvider.d.ts
|
|
8
5
|
/**
|
|
@@ -30,7 +27,7 @@ declare abstract class QueueProvider {
|
|
|
30
27
|
//#endregion
|
|
31
28
|
//#region ../../src/queue/core/providers/MemoryQueueProvider.d.ts
|
|
32
29
|
declare class MemoryQueueProvider implements QueueProvider {
|
|
33
|
-
protected readonly log:
|
|
30
|
+
protected readonly log: import("alepha/logger").Logger;
|
|
34
31
|
protected queueList: Record<string, string[]>;
|
|
35
32
|
push(queue: string, ...messages: string[]): Promise<void>;
|
|
36
33
|
pop(queue: string): Promise<string | undefined>;
|
|
@@ -40,10 +37,10 @@ declare class MemoryQueueProvider implements QueueProvider {
|
|
|
40
37
|
/**
|
|
41
38
|
* Queue worker configuration atom.
|
|
42
39
|
*/
|
|
43
|
-
declare const queueWorkerOptions:
|
|
44
|
-
interval:
|
|
45
|
-
maxInterval:
|
|
46
|
-
concurrency:
|
|
40
|
+
declare const queueWorkerOptions: import("alepha").Atom<import("typebox").TObject<{
|
|
41
|
+
interval: import("typebox").TInteger;
|
|
42
|
+
maxInterval: import("typebox").TInteger;
|
|
43
|
+
concurrency: import("typebox").TInteger;
|
|
47
44
|
}>, "alepha.queue.worker.options">;
|
|
48
45
|
type QueueWorkerOptions = Static<typeof queueWorkerOptions.schema>;
|
|
49
46
|
declare module "alepha" {
|
|
@@ -52,7 +49,7 @@ declare module "alepha" {
|
|
|
52
49
|
}
|
|
53
50
|
}
|
|
54
51
|
declare class WorkerProvider {
|
|
55
|
-
protected readonly log:
|
|
52
|
+
protected readonly log: import("alepha/logger").Logger;
|
|
56
53
|
protected readonly options: Readonly<{
|
|
57
54
|
interval: number;
|
|
58
55
|
maxInterval: number;
|
|
@@ -68,13 +65,13 @@ declare class WorkerProvider {
|
|
|
68
65
|
protected consumers: Array<Consumer>;
|
|
69
66
|
protected nextConsumerIndex: number;
|
|
70
67
|
get isRunning(): boolean;
|
|
71
|
-
protected readonly start:
|
|
68
|
+
protected readonly start: import("alepha").HookPrimitive<"start">;
|
|
72
69
|
/**
|
|
73
70
|
* Start the workers.
|
|
74
71
|
* This method will create an endless loop that will check for new messages!
|
|
75
72
|
*/
|
|
76
73
|
protected startWorkers(): void;
|
|
77
|
-
protected readonly stop:
|
|
74
|
+
protected readonly stop: import("alepha").HookPrimitive<"stop">;
|
|
78
75
|
/**
|
|
79
76
|
* Wait for the next message, where `n` is the worker number.
|
|
80
77
|
*
|
|
@@ -349,7 +346,7 @@ interface QueuePrimitiveOptions<T extends TSchema> {
|
|
|
349
346
|
handler?: (message: QueueMessage<T>) => Promise<void>;
|
|
350
347
|
}
|
|
351
348
|
declare class QueuePrimitive<T extends TSchema> extends Primitive<QueuePrimitiveOptions<T>> {
|
|
352
|
-
protected readonly log:
|
|
349
|
+
protected readonly log: import("alepha/logger").Logger;
|
|
353
350
|
protected readonly workerProvider: WorkerProvider;
|
|
354
351
|
readonly provider: QueueProvider | MemoryQueueProvider;
|
|
355
352
|
push(...payloads: Array<Static<T>>): Promise<void>;
|
|
@@ -561,9 +558,9 @@ declare const QUEUE_DEFAULT_BINDING = "JOBS_QUEUE";
|
|
|
561
558
|
*/
|
|
562
559
|
declare class CloudflareQueueProvider extends QueueProvider {
|
|
563
560
|
protected readonly alepha: Alepha;
|
|
564
|
-
protected readonly log:
|
|
561
|
+
protected readonly log: import("alepha/logger").Logger;
|
|
565
562
|
protected queue?: CloudflareQueue;
|
|
566
|
-
protected readonly onStart:
|
|
563
|
+
protected readonly onStart: import("alepha").HookPrimitive<"start">;
|
|
567
564
|
push(queue: string, message: string): Promise<void>;
|
|
568
565
|
/**
|
|
569
566
|
* Not used on Cloudflare — queue consumption is push-based via the `queue` handler.
|
|
@@ -588,7 +585,7 @@ declare class CloudflareQueueProvider extends QueueProvider {
|
|
|
588
585
|
*
|
|
589
586
|
* @module alepha.queue
|
|
590
587
|
*/
|
|
591
|
-
declare const AlephaQueue:
|
|
588
|
+
declare const AlephaQueue: import("alepha").Service<import("alepha").Module>;
|
|
592
589
|
//#endregion
|
|
593
590
|
export { $consumer, $queue, AlephaQueue, CloudflareQueue, CloudflareQueueProvider, Consumer, ConsumerPrimitive, ConsumerPrimitiveOptions, MemoryQueueProvider, NextMessage, QUEUE_DEFAULT_BINDING, QueueMessage, QueueMessageSchema, QueuePrimitive, QueuePrimitiveOptions, QueueProvider, QueueWorkerOptions, WorkerProvider, queueWorkerOptions };
|
|
594
591
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","names":[],"sources":["../../../src/queue/core/providers/QueueProvider.ts","../../../src/queue/core/providers/MemoryQueueProvider.ts","../../../src/queue/core/providers/WorkerProvider.ts","../../../src/queue/core/primitives/$queue.ts","../../../src/queue/core/primitives/$consumer.ts","../../../src/queue/core/providers/CloudflareQueueProvider.ts","../../../src/queue/core/index.ts"],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.d.ts","names":[],"sources":["../../../src/queue/core/providers/QueueProvider.ts","../../../src/queue/core/providers/MemoryQueueProvider.ts","../../../src/queue/core/providers/WorkerProvider.ts","../../../src/queue/core/primitives/$queue.ts","../../../src/queue/core/primitives/$consumer.ts","../../../src/queue/core/providers/CloudflareQueueProvider.ts","../../../src/queue/core/index.ts"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":[],"sources":["../../../src/queue/core/primitives/$consumer.ts","../../../src/queue/core/providers/MemoryQueueProvider.ts","../../../src/queue/core/providers/QueueProvider.ts","../../../src/queue/core/providers/WorkerProvider.ts","../../../src/queue/core/primitives/$queue.ts","../../../src/queue/core/providers/CloudflareQueueProvider.ts","../../../src/queue/core/index.ts"],"sourcesContent":["import {\n createPrimitive,\n KIND,\n PipelinePrimitive,\n type PipelinePrimitiveOptions,\n type Static,\n type TSchema,\n} from \"alepha\";\nimport type { QueuePrimitive } from \"./$queue.ts\";\n\n/**\n * Creates a consumer primitive to process messages from a specific queue.\n *\n * Provides a dedicated message consumer that connects to a queue and processes messages\n * with custom handler logic, enabling scalable architectures where multiple consumers\n * can process messages from the same queue.\n *\n * **Key Features**\n * - Seamless integration with any $queue primitive\n * - Full type safety inherited from queue schema\n * - Automatic worker management for background processing\n * - Built-in error handling and retry mechanisms\n * - Support for multiple consumers per queue for horizontal scaling\n *\n * **Common Use Cases**\n * - Email sending and notification services\n * - Image and media processing workers\n * - Data synchronization and background jobs\n *\n * @example\n * ```ts\n * class EmailService {\n * emailQueue = $queue({\n * name: \"emails\",\n * schema: t.object({\n * to: t.text(),\n * subject: t.text(),\n * body: t.text()\n * })\n * });\n *\n * emailConsumer = $consumer({\n * queue: this.emailQueue,\n * handler: async (message) => {\n * const { to, subject, body } = message.payload;\n * await this.sendEmail(to, subject, body);\n * }\n * });\n *\n * async sendWelcomeEmail(userEmail: string) {\n * await this.emailQueue.push({\n * to: userEmail,\n * subject: \"Welcome!\",\n * body: \"Thanks for joining.\"\n * });\n * }\n * }\n * ```\n */\nexport const $consumer = <T extends TSchema>(\n options: ConsumerPrimitiveOptions<T>,\n): ConsumerPrimitive<T> => {\n return createPrimitive(ConsumerPrimitive<T>, options);\n};\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport interface ConsumerPrimitiveOptions<T extends TSchema>\n extends PipelinePrimitiveOptions {\n /**\n * The queue primitive that this consumer will process messages from.\n *\n * This establishes the connection between the consumer and its source queue:\n * - The consumer inherits the queue's message schema for type safety\n * - Messages pushed to the queue will be automatically routed to this consumer\n * - Multiple consumers can be attached to the same queue for parallel processing\n * - The consumer will use the queue's provider and configuration settings\n *\n * **Queue Integration Benefits**:\n * - Type safety: Consumer handler gets fully typed message payloads\n * - Schema validation: Messages are validated before reaching the consumer\n * - Error handling: Failed messages can be retried or moved to dead letter queues\n * - Monitoring: Queue metrics include consumer processing statistics\n *\n * @example\n * ```ts\n * // First, define a queue\n * emailQueue = $queue({\n * name: \"emails\",\n * schema: t.object({ to: t.text(), subject: t.text() })\n * });\n *\n * // Then, create a consumer for that queue\n * emailConsumer = $consumer({\n * queue: this.emailQueue, // Reference the queue primitive\n * handler: async (message) => { } // process email\n * });\n * ```\n */\n queue: QueuePrimitive<T>;\n\n /**\n * Message handler function that processes individual messages from the queue.\n *\n * This function:\n * - Receives fully typed and validated message payloads from the connected queue\n * - Runs in the background worker system for non-blocking operation\n * - Should implement the core business logic for processing this message type\n * - Can throw errors to trigger the queue's retry mechanisms\n * - Has access to the full Alepha dependency injection container\n * - Should be idempotent to handle potential duplicate deliveries\n *\n * **Handler Design Guidelines**:\n * - Keep handlers focused on a single responsibility\n * - Use proper error handling and meaningful error messages\n * - Log important processing steps for debugging and monitoring\n * - Consider transaction boundaries for data consistency\n * - Make operations idempotent when possible\n * - Validate business rules within the handler logic\n *\n * **Error Handling Strategy**:\n * - Throw errors for temporary failures that should be retried\n * - Log and handle permanent failures gracefully\n * - Use specific error types to control retry behavior\n * - Consider implementing circuit breakers for external service calls\n *\n * @param message - The queue message containing the validated payload\n * @param message.payload - The typed message data based on the queue's schema\n * @returns Promise that resolves when processing is complete\n *\n * @example\n * ```ts\n * handler: async (message) => {\n * const { userId, action, data } = message.payload;\n *\n * try {\n * // Log processing start\n * this.logger.info(`Processing ${action} for user ${userId}`);\n *\n * // Validate business rules\n * if (!await this.userService.exists(userId)) {\n * throw new Error(`User ${userId} not found`);\n * }\n *\n * // Perform the main processing logic\n * switch (action) {\n * case \"create\":\n * await this.processCreation(userId, data);\n * break;\n * case \"update\":\n * await this.processUpdate(userId, data);\n * break;\n * default:\n * throw new Error(`Unknown action: ${action}`);\n * }\n *\n * // Log successful completion\n * this.logger.info(`Successfully processed ${action} for user ${userId}`);\n *\n * } catch (error) {\n * // Log error with context\n * this.logger.error(`Failed to process ${action} for user ${userId}`, {\n * error: error.message,\n * userId,\n * action,\n * data\n * });\n *\n * // Re-throw to trigger queue retry mechanism\n * throw error;\n * }\n * }\n * ```\n */\n handler: (message: { payload: Static<T> }) => Promise<void>;\n}\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport class ConsumerPrimitive<T extends TSchema> extends PipelinePrimitive<\n ConsumerPrimitiveOptions<T>\n> {}\n\n$consumer[KIND] = ConsumerPrimitive;\n","import { $logger } from \"alepha/logger\";\nimport type { QueueProvider } from \"./QueueProvider.ts\";\n\nexport class MemoryQueueProvider implements QueueProvider {\n protected readonly log = $logger();\n protected queueList: Record<string, string[]> = {};\n\n public async push(queue: string, ...messages: string[]): Promise<void> {\n if (this.queueList[queue] == null) {\n this.queueList[queue] = [];\n }\n\n this.queueList[queue].push(...messages);\n }\n\n public async pop(queue: string): Promise<string | undefined> {\n return this.queueList[queue]?.shift();\n }\n}\n","/**\n * Minimalist Queue interface.\n *\n * Will be probably enhanced in the future to support more advanced features. But for now, it's enough!\n */\nexport abstract class QueueProvider {\n /**\n * Push a message to the queue.\n *\n * @param queue Name of the queue to push the message to.\n * @param message String message to be pushed to the queue. Buffer messages are not supported for now.\n */\n public abstract push(queue: string, message: string): Promise<void>;\n\n /**\n * Pop a message from the queue.\n *\n * @param queue Name of the queue to pop the message from.\n *\n * @returns The message popped or `undefined` if the queue is empty.\n */\n public abstract pop(queue: string): Promise<string | undefined>;\n}\n","import {\n $atom,\n $hook,\n $inject,\n $state,\n Alepha,\n type Static,\n type TSchema,\n t,\n} from \"alepha\";\nimport { DateTimeProvider } from \"alepha/datetime\";\nimport { $logger } from \"alepha/logger\";\nimport { $consumer } from \"../primitives/$consumer.ts\";\nimport {\n $queue,\n type QueueMessage,\n type QueuePrimitive,\n} from \"../primitives/$queue.ts\";\nimport { QueueProvider } from \"./QueueProvider.ts\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\n/**\n * Queue worker configuration atom.\n */\nexport const queueWorkerOptions = $atom({\n name: \"alepha.queue.worker.options\",\n schema: t.object({\n interval: t.integer({\n default: 1000,\n description:\n \"Interval in milliseconds to wait before checking for new messages.\",\n }),\n maxInterval: t.integer({\n default: 32000,\n description:\n \"Maximum interval in milliseconds to wait before checking for new messages.\",\n }),\n concurrency: t.integer({\n default: 1,\n description:\n \"Number of workers to run concurrently. Useful only if you are doing a lot of I/O.\",\n }),\n }),\n default: {\n interval: 1000,\n maxInterval: 32000,\n concurrency: 1,\n },\n});\n\nexport type QueueWorkerOptions = Static<typeof queueWorkerOptions.schema>;\n\ndeclare module \"alepha\" {\n interface State {\n [queueWorkerOptions.key]: QueueWorkerOptions;\n }\n}\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport class WorkerProvider {\n protected readonly log = $logger();\n protected readonly options = $state(queueWorkerOptions);\n protected readonly alepha = $inject(Alepha);\n protected readonly queueProvider = $inject(QueueProvider);\n protected readonly dateTimeProvider = $inject(DateTimeProvider);\n\n protected workerPromises: Array<Promise<void>> = [];\n protected workersRunning = 0;\n protected abortController: AbortController | undefined;\n protected workerIntervals: Record<number, number> = {};\n protected consumers: Array<Consumer> = [];\n protected nextConsumerIndex = 0;\n\n public get isRunning(): boolean {\n return this.workersRunning > 0;\n }\n\n protected readonly start = $hook({\n on: \"start\",\n priority: \"last\",\n handler: () => {\n for (const queue of this.alepha.primitives($queue)) {\n const handler = queue.options.handler;\n if (handler) {\n this.consumers.push({\n handler,\n queue,\n });\n }\n }\n\n for (const consumer of this.alepha.primitives($consumer)) {\n this.consumers.push({\n queue: consumer.options.queue,\n handler: (msg) => consumer.handler.run(msg),\n });\n }\n\n if (this.consumers.length > 0) {\n this.startWorkers();\n this.log.debug(\n `Watching for ${this.consumers.length} queue${this.consumers.length > 1 ? \"s\" : \"\"} with ${this.options.concurrency} worker${\n this.options.concurrency > 1 ? \"s\" : \"\"\n }.`,\n );\n }\n },\n });\n\n // -------------------------------------------------------------------------------------------------------------------\n\n // Engine part - this is the part that will run the workers and process the messages\n\n /**\n * Start the workers.\n * This method will create an endless loop that will check for new messages!\n */\n protected startWorkers(): void {\n this.abortController ??= new AbortController();\n const workerToStart = this.options.concurrency - this.workersRunning;\n\n for (let i = 0; i < workerToStart; i++) {\n this.workersRunning += 1;\n this.log.debug(`Starting worker n-${i}`);\n\n const workerLoop = async () => {\n while (this.workersRunning > 0) {\n this.log.trace(`Worker n-${i} is checking for new messages`);\n const next = await this.getNextMessage();\n if (next) {\n this.workerIntervals[i] = 0;\n await this.processMessage(next);\n } else {\n await this.waitForNextMessage(i);\n }\n }\n this.log.info(`Worker n-${i} has stopped`);\n // Only decrement if we're not already at 0 (shutdown case)\n if (this.workersRunning > 0) {\n this.workersRunning -= 1;\n }\n };\n\n this.workerPromises.push(\n workerLoop().catch((e) => {\n this.log.error(`Worker n-${i} has crashed`, e);\n // Always decrement on crash, regardless of shutdown state\n this.workersRunning = Math.max(0, this.workersRunning - 1);\n }),\n );\n }\n }\n\n protected readonly stop = $hook({\n on: \"stop\",\n handler: async () => {\n if (this.consumers.length > 0) {\n await this.stopWorkers();\n }\n },\n });\n\n /**\n * Wait for the next message, where `n` is the worker number.\n *\n * This method will wait for a certain amount of time, increasing the wait time again if no message is found.\n */\n protected async waitForNextMessage(n: number): Promise<void> {\n const intervals = this.workerIntervals;\n const milliseconds = intervals[n] || this.options.interval;\n\n this.log.trace(`Worker n-${n} is waiting for ${milliseconds}ms.`);\n\n if (this.abortController?.signal.aborted) {\n this.log.warn(`Worker n-${n} aborted.`);\n return;\n }\n\n await this.dateTimeProvider.wait(milliseconds, {\n signal: this.abortController?.signal,\n });\n\n if (intervals[n]) {\n if (intervals[n] < this.options.maxInterval) {\n intervals[n] = intervals[n] * 2;\n }\n } else {\n intervals[n] = milliseconds;\n }\n }\n\n /**\n * Get the next message.\n */\n protected async getNextMessage(): Promise<undefined | NextMessage> {\n const len = this.consumers.length;\n for (let i = 0; i < len; i++) {\n const idx = (this.nextConsumerIndex + i) % len;\n const consumer = this.consumers[idx];\n const provider = consumer.queue.provider;\n const message = await provider.pop(consumer.queue.name);\n if (message) {\n this.nextConsumerIndex = (idx + 1) % len;\n return { message, consumer };\n }\n }\n }\n\n /**\n * Process a message from a queue.\n */\n protected async processMessage(response: {\n message: any;\n consumer: Consumer;\n }) {\n const { message, consumer } = response;\n\n try {\n const json = JSON.parse(message);\n const payload = this.alepha.codec.decode(\n consumer.queue.options.schema,\n json.payload,\n );\n await this.alepha.context.run(() => consumer.handler({ payload }));\n } catch (e) {\n this.log.error(\"Failed to process message\", e);\n }\n }\n\n /**\n * Stop the workers.\n *\n * This method will stop the workers and wait for them to finish processing.\n */\n protected async stopWorkers() {\n this.workersRunning = 0;\n\n this.log.trace(\"Stopping workers...\");\n this.abortController?.abort();\n\n this.log.trace(\"Waiting for workers to finish...\");\n await Promise.all(this.workerPromises);\n }\n\n /**\n * Force the workers to get back to work.\n */\n public wakeUp(): void {\n this.log.debug(\"Waking up workers...\");\n this.abortController?.abort();\n this.abortController = new AbortController();\n\n // if no workers are running, start them, (should not happen, but just in case)\n this.startWorkers();\n }\n}\n\nexport interface Consumer<T extends TSchema = TSchema> {\n queue: QueuePrimitive<T>;\n handler: (message: QueueMessage<T>) => Promise<void>;\n}\n\nexport interface NextMessage {\n consumer: Consumer;\n message: string;\n}\n","import {\n $inject,\n createPrimitive,\n KIND,\n Primitive,\n type Service,\n type Static,\n type TSchema,\n} from \"alepha\";\nimport { $logger } from \"alepha/logger\";\nimport { MemoryQueueProvider } from \"../providers/MemoryQueueProvider.ts\";\nimport { QueueProvider } from \"../providers/QueueProvider.ts\";\nimport { WorkerProvider } from \"../providers/WorkerProvider.ts\";\n\n/**\n * Creates a queue primitive for asynchronous message processing with background workers.\n *\n * The $queue primitive enables powerful asynchronous communication patterns in your application.\n * It provides type-safe message queuing with automatic worker processing, making it perfect for\n * decoupling components and handling background tasks efficiently.\n *\n * **Background Processing**\n * - Automatic worker threads for non-blocking message processing\n * - Built-in retry mechanisms and error handling\n * - Dead letter queues for failed message handling\n * - Graceful shutdown and worker lifecycle management\n *\n * **Type Safety**\n * - Full TypeScript support with schema validation using TypeBox\n * - Type-safe message payloads with automatic inference\n * - Runtime validation of all queued messages\n * - Compile-time errors for invalid message structures\n *\n * **Storage Flexibility**\n * - Memory provider for development and testing\n * - Redis provider for production scalability and persistence\n * - Custom provider support for specialized backends\n * - Automatic failover and connection pooling\n *\n * **Performance & Scalability**\n * - Batch processing support for high-throughput scenarios\n * - Horizontal scaling with distributed queue backends\n * - Configurable concurrency and worker pools\n * - Efficient serialization and message routing\n *\n * **Reliability**\n * - Message persistence across application restarts\n * - Automatic retry with exponential backoff\n * - Dead letter handling for permanently failed messages\n * - Comprehensive logging and monitoring integration\n *\n * @example Basic notification queue\n * ```typescript\n * const emailQueue = $queue({\n * name: \"email-notifications\",\n * schema: t.object({\n * to: t.text(),\n * subject: t.text(),\n * body: t.text(),\n * priority: t.optional(t.enum([\"high\", \"normal\"]))\n * }),\n * handler: async (message) => {\n * await emailService.send(message.payload);\n * console.log(`Email sent to ${message.payload.to}`);\n * }\n * });\n *\n * // Push messages for background processing\n * await emailQueue.push({\n * to: \"user@example.com\",\n * subject: \"Welcome!\",\n * body: \"Welcome to our platform\",\n * priority: \"high\"\n * });\n * ```\n *\n * @example Batch processing with Redis\n * ```typescript\n * const imageQueue = $queue({\n * name: \"image-processing\",\n * provider: RedisQueueProvider,\n * schema: t.object({\n * imageId: t.text(),\n * operations: t.array(t.enum([\"resize\", \"compress\", \"thumbnail\"]))\n * }),\n * handler: async (message) => {\n * for (const op of message.payload.operations) {\n * await processImage(message.payload.imageId, op);\n * }\n * }\n * });\n *\n * // Batch processing multiple images\n * await imageQueue.push(\n * { imageId: \"img1\", operations: [\"resize\", \"thumbnail\"] },\n * { imageId: \"img2\", operations: [\"compress\"] },\n * { imageId: \"img3\", operations: [\"resize\", \"compress\", \"thumbnail\"] }\n * );\n * ```\n *\n * @example Development with memory provider\n * ```typescript\n * const taskQueue = $queue({\n * name: \"dev-tasks\",\n * provider: \"memory\",\n * schema: t.object({\n * taskType: t.enum([\"cleanup\", \"backup\", \"report\"]),\n * data: t.record(t.text(), t.any())\n * }),\n * handler: async (message) => {\n * switch (message.payload.taskType) {\n * case \"cleanup\":\n * await performCleanup(message.payload.data);\n * break;\n * case \"backup\":\n * await createBackup(message.payload.data);\n * break;\n * case \"report\":\n * await generateReport(message.payload.data);\n * break;\n * }\n * }\n * });\n * ```\n */\nexport const $queue = <T extends TSchema>(\n options: QueuePrimitiveOptions<T>,\n): QueuePrimitive<T> => {\n return createPrimitive(QueuePrimitive<T>, options);\n};\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport interface QueuePrimitiveOptions<T extends TSchema> {\n /**\n * Unique name for the queue.\n *\n * This name is used for:\n * - Queue identification across the system\n * - Storage backend key generation\n * - Logging and monitoring\n * - Worker assignment and routing\n *\n * If not provided, defaults to the property key where the queue is declared.\n *\n * @example \"email-notifications\"\n * @example \"image-processing\"\n * @example \"order-fulfillment\"\n */\n name?: string;\n\n /**\n * Human-readable description of the queue's purpose.\n *\n * Used for:\n * - Documentation generation\n * - Monitoring dashboards\n * - Development team communication\n * - Queue management interfaces\n *\n * @example \"Process user registration emails and welcome sequences\"\n * @example \"Handle image uploads, resizing, and thumbnail generation\"\n * @example \"Manage order processing, payment, and shipping workflows\"\n */\n description?: string;\n\n /**\n * Queue storage provider configuration.\n *\n * Options:\n * - **\"memory\"**: In-memory queue (default for development, lost on restart)\n * - **Service<QueueProvider>**: Custom provider class (e.g., RedisQueueProvider)\n * - **undefined**: Uses the default queue provider from dependency injection\n *\n * **Provider Selection Guidelines**:\n * - Development: Use \"memory\" for fast, simple testing\n * - Production: Use Redis or database-backed providers for persistence\n * - High-throughput: Use specialized providers with connection pooling\n * - Distributed systems: Use Redis or message brokers for scalability\n *\n * @default Uses injected QueueProvider\n * @example \"memory\"\n * @example RedisQueueProvider\n * @example DatabaseQueueProvider\n */\n provider?: \"memory\" | Service<QueueProvider>;\n\n /**\n * TypeBox schema defining the structure of messages in this queue.\n *\n * This schema:\n * - Validates all messages pushed to the queue\n * - Provides full TypeScript type inference\n * - Ensures type safety between producers and consumers\n * - Enables automatic serialization/deserialization\n *\n * **Schema Design Best Practices**:\n * - Keep schemas simple and focused on the specific task\n * - Use optional fields for data that might not always be available\n * - Include version fields for schema evolution\n * - Use union types for different message types in the same queue\n *\n * @example\n * ```ts\n * t.object({\n * userId: t.text(),\n * action: t.enum([\"create\", \"update\"]),\n * data: t.record(t.text(), t.any()),\n * timestamp: t.optional(t.number())\n * })\n * ```\n */\n schema: T;\n\n /**\n * Message handler function that processes queue messages.\n *\n * This function:\n * - Runs in background worker threads for non-blocking processing\n * - Receives type-safe message payloads based on the schema\n * - Should be idempotent to handle potential retries\n * - Can throw errors to trigger retry mechanisms\n * - Has access to the full Alepha dependency injection container\n *\n * **Handler Best Practices**:\n * - Keep handlers focused on a single responsibility\n * - Use proper error handling and logging\n * - Make operations idempotent when possible\n * - Validate critical business logic within handlers\n * - Consider using transactions for data consistency\n *\n * @param message - The queue message with validated payload\n * @returns Promise that resolves when processing is complete\n *\n * @example\n * ```ts\n * handler: async (message) => {\n * const { userId, email, template } = message.payload;\n *\n * try {\n * await this.emailService.send({\n * to: email,\n * template,\n * data: { userId }\n * });\n *\n * await this.userService.markEmailSent(userId, template);\n * } catch (error) {\n * // Log error and let the queue system handle retries\n * this.logger.error(`Failed to send email to ${email}`, error);\n * throw error;\n * }\n * }\n * ```\n */\n handler?: (message: QueueMessage<T>) => Promise<void>;\n}\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport class QueuePrimitive<T extends TSchema> extends Primitive<\n QueuePrimitiveOptions<T>\n> {\n protected readonly log = $logger();\n protected readonly workerProvider = $inject(WorkerProvider);\n public readonly provider = this.$provider();\n\n public async push(...payloads: Array<Static<T>>) {\n await Promise.all(\n payloads.map((payload) =>\n this.provider.push(\n this.name,\n JSON.stringify({\n headers: {},\n payload: this.alepha.codec.decode(this.options.schema, payload),\n }),\n ),\n ),\n );\n\n this.log.debug(`Pushed to queue ${this.name}`, payloads);\n this.workerProvider.wakeUp();\n }\n\n public get name() {\n return this.options.name || this.config.propertyKey;\n }\n\n protected $provider() {\n if (!this.options.provider) {\n return this.alepha.inject(QueueProvider);\n }\n if (this.options.provider === \"memory\") {\n return this.alepha.inject(MemoryQueueProvider);\n }\n return this.alepha.inject(this.options.provider);\n }\n}\n\n$queue[KIND] = QueuePrimitive;\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport interface QueueMessageSchema {\n payload: TSchema;\n}\n\nexport interface QueueMessage<T extends TSchema> {\n payload: Static<T>;\n}\n","import { $hook, $inject, Alepha, AlephaError } from \"alepha\";\nimport { $logger } from \"alepha/logger\";\nimport { QueueProvider } from \"./QueueProvider.ts\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\n/**\n * Cloudflare Queue interface matching the CF Workers Queue API.\n */\nexport interface CloudflareQueue {\n send(message: unknown): Promise<void>;\n sendBatch(messages: Array<{ body: unknown }>): Promise<void>;\n}\n\n// ---------------------------------------------------------------------------------------------------------------------\n\n/**\n * Default queue binding name used in wrangler configuration.\n */\nexport const QUEUE_DEFAULT_BINDING = \"JOBS_QUEUE\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\n/**\n * Cloudflare Queue provider.\n *\n * Uses a Queue binding for message dispatch. Messages are wrapped with the\n * logical queue name so the consumer can route them to the correct handler.\n *\n * **Required Cloudflare binding:**\n * - `JOBS_QUEUE` - A Queue binding in wrangler configuration\n *\n * @example\n * ```toml\n * # wrangler.toml - automatically generated by alepha build\n * [[queues.producers]]\n * binding = \"JOBS_QUEUE\"\n * queue = \"my-app-queue\"\n * ```\n */\nexport class CloudflareQueueProvider extends QueueProvider {\n protected readonly alepha = $inject(Alepha);\n protected readonly log = $logger();\n\n protected queue?: CloudflareQueue;\n\n protected readonly onStart = $hook({\n on: \"start\",\n handler: async () => {\n const cloudflareEnv = this.alepha.store.get(\"cloudflare.env\") as\n | Record<string, unknown>\n | undefined;\n if (!cloudflareEnv) {\n throw new AlephaError(\n \"Cloudflare Workers environment not found in Alepha store under 'cloudflare.env'.\",\n );\n }\n\n const binding = cloudflareEnv[QUEUE_DEFAULT_BINDING] as\n | CloudflareQueue\n | undefined;\n if (!binding) {\n throw new AlephaError(\n `Queue binding '${QUEUE_DEFAULT_BINDING}' not found in Cloudflare Workers environment.`,\n );\n }\n\n this.queue = binding;\n this.log.info(\"Cloudflare Queue ready\");\n },\n });\n\n public async push(queue: string, message: string): Promise<void> {\n await this.getQueue().send({ queue, message });\n }\n\n /**\n * Not used on Cloudflare — queue consumption is push-based via the `queue` handler.\n */\n public async pop(_queue: string): Promise<string | undefined> {\n return undefined;\n }\n\n protected getQueue(): CloudflareQueue {\n if (!this.queue) {\n throw new AlephaError(\n \"Queue binding not initialized. Call start() first.\",\n );\n }\n return this.queue;\n }\n}\n","import { $module, type Alepha } from \"alepha\";\nimport { $consumer } from \"./primitives/$consumer.ts\";\nimport { $queue } from \"./primitives/$queue.ts\";\nimport { MemoryQueueProvider } from \"./providers/MemoryQueueProvider.ts\";\nimport { QueueProvider } from \"./providers/QueueProvider.ts\";\nimport { WorkerProvider } from \"./providers/WorkerProvider.ts\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport * from \"./primitives/$consumer.ts\";\nexport * from \"./primitives/$queue.ts\";\nexport * from \"./providers/CloudflareQueueProvider.ts\";\nexport * from \"./providers/MemoryQueueProvider.ts\";\nexport * from \"./providers/QueueProvider.ts\";\nexport * from \"./providers/WorkerProvider.ts\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\n/**\n * Asynchronous message processing with automatic worker management.\n *\n * **Features:**\n * - Background job queues with type-safe payloads\n * - Queue consumer handlers\n * - Automatic worker threads for non-blocking processing\n * - Retry mechanisms with exponential backoff\n * - Dead letter queues for failed messages\n * - Batch processing support\n * - Configurable concurrency and worker pools\n * - Providers: Memory (dev), Redis (production)\n *\n * @module alepha.queue\n */\nexport const AlephaQueue = $module({\n name: \"alepha.queue\",\n primitives: [$queue, $consumer],\n services: [QueueProvider, WorkerProvider],\n variants: [MemoryQueueProvider],\n register: (alepha: Alepha) =>\n alepha\n .with({\n optional: true,\n provide: QueueProvider,\n use: MemoryQueueProvider,\n })\n .with(WorkerProvider),\n});\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../../../src/queue/core/primitives/$consumer.ts","../../../src/queue/core/providers/MemoryQueueProvider.ts","../../../src/queue/core/providers/QueueProvider.ts","../../../src/queue/core/providers/WorkerProvider.ts","../../../src/queue/core/primitives/$queue.ts","../../../src/queue/core/providers/CloudflareQueueProvider.ts","../../../src/queue/core/index.ts"],"sourcesContent":["import {\n createPrimitive,\n KIND,\n PipelinePrimitive,\n type PipelinePrimitiveOptions,\n type Static,\n type TSchema,\n} from \"alepha\";\nimport type { QueuePrimitive } from \"./$queue.ts\";\n\n/**\n * Creates a consumer primitive to process messages from a specific queue.\n *\n * Provides a dedicated message consumer that connects to a queue and processes messages\n * with custom handler logic, enabling scalable architectures where multiple consumers\n * can process messages from the same queue.\n *\n * **Key Features**\n * - Seamless integration with any $queue primitive\n * - Full type safety inherited from queue schema\n * - Automatic worker management for background processing\n * - Built-in error handling and retry mechanisms\n * - Support for multiple consumers per queue for horizontal scaling\n *\n * **Common Use Cases**\n * - Email sending and notification services\n * - Image and media processing workers\n * - Data synchronization and background jobs\n *\n * @example\n * ```ts\n * class EmailService {\n * emailQueue = $queue({\n * name: \"emails\",\n * schema: t.object({\n * to: t.text(),\n * subject: t.text(),\n * body: t.text()\n * })\n * });\n *\n * emailConsumer = $consumer({\n * queue: this.emailQueue,\n * handler: async (message) => {\n * const { to, subject, body } = message.payload;\n * await this.sendEmail(to, subject, body);\n * }\n * });\n *\n * async sendWelcomeEmail(userEmail: string) {\n * await this.emailQueue.push({\n * to: userEmail,\n * subject: \"Welcome!\",\n * body: \"Thanks for joining.\"\n * });\n * }\n * }\n * ```\n */\nexport const $consumer = <T extends TSchema>(\n options: ConsumerPrimitiveOptions<T>,\n): ConsumerPrimitive<T> => {\n return createPrimitive(ConsumerPrimitive<T>, options);\n};\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport interface ConsumerPrimitiveOptions<T extends TSchema>\n extends PipelinePrimitiveOptions {\n /**\n * The queue primitive that this consumer will process messages from.\n *\n * This establishes the connection between the consumer and its source queue:\n * - The consumer inherits the queue's message schema for type safety\n * - Messages pushed to the queue will be automatically routed to this consumer\n * - Multiple consumers can be attached to the same queue for parallel processing\n * - The consumer will use the queue's provider and configuration settings\n *\n * **Queue Integration Benefits**:\n * - Type safety: Consumer handler gets fully typed message payloads\n * - Schema validation: Messages are validated before reaching the consumer\n * - Error handling: Failed messages can be retried or moved to dead letter queues\n * - Monitoring: Queue metrics include consumer processing statistics\n *\n * @example\n * ```ts\n * // First, define a queue\n * emailQueue = $queue({\n * name: \"emails\",\n * schema: t.object({ to: t.text(), subject: t.text() })\n * });\n *\n * // Then, create a consumer for that queue\n * emailConsumer = $consumer({\n * queue: this.emailQueue, // Reference the queue primitive\n * handler: async (message) => { } // process email\n * });\n * ```\n */\n queue: QueuePrimitive<T>;\n\n /**\n * Message handler function that processes individual messages from the queue.\n *\n * This function:\n * - Receives fully typed and validated message payloads from the connected queue\n * - Runs in the background worker system for non-blocking operation\n * - Should implement the core business logic for processing this message type\n * - Can throw errors to trigger the queue's retry mechanisms\n * - Has access to the full Alepha dependency injection container\n * - Should be idempotent to handle potential duplicate deliveries\n *\n * **Handler Design Guidelines**:\n * - Keep handlers focused on a single responsibility\n * - Use proper error handling and meaningful error messages\n * - Log important processing steps for debugging and monitoring\n * - Consider transaction boundaries for data consistency\n * - Make operations idempotent when possible\n * - Validate business rules within the handler logic\n *\n * **Error Handling Strategy**:\n * - Throw errors for temporary failures that should be retried\n * - Log and handle permanent failures gracefully\n * - Use specific error types to control retry behavior\n * - Consider implementing circuit breakers for external service calls\n *\n * @param message - The queue message containing the validated payload\n * @param message.payload - The typed message data based on the queue's schema\n * @returns Promise that resolves when processing is complete\n *\n * @example\n * ```ts\n * handler: async (message) => {\n * const { userId, action, data } = message.payload;\n *\n * try {\n * // Log processing start\n * this.logger.info(`Processing ${action} for user ${userId}`);\n *\n * // Validate business rules\n * if (!await this.userService.exists(userId)) {\n * throw new Error(`User ${userId} not found`);\n * }\n *\n * // Perform the main processing logic\n * switch (action) {\n * case \"create\":\n * await this.processCreation(userId, data);\n * break;\n * case \"update\":\n * await this.processUpdate(userId, data);\n * break;\n * default:\n * throw new Error(`Unknown action: ${action}`);\n * }\n *\n * // Log successful completion\n * this.logger.info(`Successfully processed ${action} for user ${userId}`);\n *\n * } catch (error) {\n * // Log error with context\n * this.logger.error(`Failed to process ${action} for user ${userId}`, {\n * error: error.message,\n * userId,\n * action,\n * data\n * });\n *\n * // Re-throw to trigger queue retry mechanism\n * throw error;\n * }\n * }\n * ```\n */\n handler: (message: { payload: Static<T> }) => Promise<void>;\n}\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport class ConsumerPrimitive<T extends TSchema> extends PipelinePrimitive<\n ConsumerPrimitiveOptions<T>\n> {}\n\n$consumer[KIND] = ConsumerPrimitive;\n","import { $logger } from \"alepha/logger\";\nimport type { QueueProvider } from \"./QueueProvider.ts\";\n\nexport class MemoryQueueProvider implements QueueProvider {\n protected readonly log = $logger();\n protected queueList: Record<string, string[]> = {};\n\n public async push(queue: string, ...messages: string[]): Promise<void> {\n if (this.queueList[queue] == null) {\n this.queueList[queue] = [];\n }\n\n this.queueList[queue].push(...messages);\n }\n\n public async pop(queue: string): Promise<string | undefined> {\n return this.queueList[queue]?.shift();\n }\n}\n","/**\n * Minimalist Queue interface.\n *\n * Will be probably enhanced in the future to support more advanced features. But for now, it's enough!\n */\nexport abstract class QueueProvider {\n /**\n * Push a message to the queue.\n *\n * @param queue Name of the queue to push the message to.\n * @param message String message to be pushed to the queue. Buffer messages are not supported for now.\n */\n public abstract push(queue: string, message: string): Promise<void>;\n\n /**\n * Pop a message from the queue.\n *\n * @param queue Name of the queue to pop the message from.\n *\n * @returns The message popped or `undefined` if the queue is empty.\n */\n public abstract pop(queue: string): Promise<string | undefined>;\n}\n","import {\n $atom,\n $hook,\n $inject,\n $state,\n Alepha,\n type Static,\n type TSchema,\n t,\n} from \"alepha\";\nimport { DateTimeProvider } from \"alepha/datetime\";\nimport { $logger } from \"alepha/logger\";\nimport { $consumer } from \"../primitives/$consumer.ts\";\nimport {\n $queue,\n type QueueMessage,\n type QueuePrimitive,\n} from \"../primitives/$queue.ts\";\nimport { QueueProvider } from \"./QueueProvider.ts\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\n/**\n * Queue worker configuration atom.\n */\nexport const queueWorkerOptions = $atom({\n name: \"alepha.queue.worker.options\",\n schema: t.object({\n interval: t.integer({\n default: 1000,\n description:\n \"Interval in milliseconds to wait before checking for new messages.\",\n }),\n maxInterval: t.integer({\n default: 32000,\n description:\n \"Maximum interval in milliseconds to wait before checking for new messages.\",\n }),\n concurrency: t.integer({\n default: 1,\n description:\n \"Number of workers to run concurrently. Useful only if you are doing a lot of I/O.\",\n }),\n }),\n default: {\n interval: 1000,\n maxInterval: 32000,\n concurrency: 1,\n },\n});\n\nexport type QueueWorkerOptions = Static<typeof queueWorkerOptions.schema>;\n\ndeclare module \"alepha\" {\n interface State {\n [queueWorkerOptions.key]: QueueWorkerOptions;\n }\n}\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport class WorkerProvider {\n protected readonly log = $logger();\n protected readonly options = $state(queueWorkerOptions);\n protected readonly alepha = $inject(Alepha);\n protected readonly queueProvider = $inject(QueueProvider);\n protected readonly dateTimeProvider = $inject(DateTimeProvider);\n\n protected workerPromises: Array<Promise<void>> = [];\n protected workersRunning = 0;\n protected abortController: AbortController | undefined;\n protected workerIntervals: Record<number, number> = {};\n protected consumers: Array<Consumer> = [];\n protected nextConsumerIndex = 0;\n\n public get isRunning(): boolean {\n return this.workersRunning > 0;\n }\n\n protected readonly start = $hook({\n on: \"start\",\n priority: \"last\",\n handler: () => {\n for (const queue of this.alepha.primitives($queue)) {\n const handler = queue.options.handler;\n if (handler) {\n this.consumers.push({\n handler,\n queue,\n });\n }\n }\n\n for (const consumer of this.alepha.primitives($consumer)) {\n this.consumers.push({\n queue: consumer.options.queue,\n handler: (msg) => consumer.handler.run(msg),\n });\n }\n\n if (this.consumers.length > 0) {\n this.startWorkers();\n this.log.debug(\n `Watching for ${this.consumers.length} queue${this.consumers.length > 1 ? \"s\" : \"\"} with ${this.options.concurrency} worker${\n this.options.concurrency > 1 ? \"s\" : \"\"\n }.`,\n );\n }\n },\n });\n\n // -------------------------------------------------------------------------------------------------------------------\n\n // Engine part - this is the part that will run the workers and process the messages\n\n /**\n * Start the workers.\n * This method will create an endless loop that will check for new messages!\n */\n protected startWorkers(): void {\n this.abortController ??= new AbortController();\n const workerToStart = this.options.concurrency - this.workersRunning;\n\n for (let i = 0; i < workerToStart; i++) {\n this.workersRunning += 1;\n this.log.debug(`Starting worker n-${i}`);\n\n const workerLoop = async () => {\n while (this.workersRunning > 0) {\n this.log.trace(`Worker n-${i} is checking for new messages`);\n const next = await this.getNextMessage();\n if (next) {\n this.workerIntervals[i] = 0;\n await this.processMessage(next);\n } else {\n await this.waitForNextMessage(i);\n }\n }\n this.log.info(`Worker n-${i} has stopped`);\n // Only decrement if we're not already at 0 (shutdown case)\n if (this.workersRunning > 0) {\n this.workersRunning -= 1;\n }\n };\n\n this.workerPromises.push(\n workerLoop().catch((e) => {\n this.log.error(`Worker n-${i} has crashed`, e);\n // Always decrement on crash, regardless of shutdown state\n this.workersRunning = Math.max(0, this.workersRunning - 1);\n }),\n );\n }\n }\n\n protected readonly stop = $hook({\n on: \"stop\",\n handler: async () => {\n if (this.consumers.length > 0) {\n await this.stopWorkers();\n }\n },\n });\n\n /**\n * Wait for the next message, where `n` is the worker number.\n *\n * This method will wait for a certain amount of time, increasing the wait time again if no message is found.\n */\n protected async waitForNextMessage(n: number): Promise<void> {\n const intervals = this.workerIntervals;\n const milliseconds = intervals[n] || this.options.interval;\n\n this.log.trace(`Worker n-${n} is waiting for ${milliseconds}ms.`);\n\n if (this.abortController?.signal.aborted) {\n this.log.warn(`Worker n-${n} aborted.`);\n return;\n }\n\n await this.dateTimeProvider.wait(milliseconds, {\n signal: this.abortController?.signal,\n });\n\n if (intervals[n]) {\n if (intervals[n] < this.options.maxInterval) {\n intervals[n] = intervals[n] * 2;\n }\n } else {\n intervals[n] = milliseconds;\n }\n }\n\n /**\n * Get the next message.\n */\n protected async getNextMessage(): Promise<undefined | NextMessage> {\n const len = this.consumers.length;\n for (let i = 0; i < len; i++) {\n const idx = (this.nextConsumerIndex + i) % len;\n const consumer = this.consumers[idx];\n const provider = consumer.queue.provider;\n const message = await provider.pop(consumer.queue.name);\n if (message) {\n this.nextConsumerIndex = (idx + 1) % len;\n return { message, consumer };\n }\n }\n }\n\n /**\n * Process a message from a queue.\n */\n protected async processMessage(response: {\n message: any;\n consumer: Consumer;\n }) {\n const { message, consumer } = response;\n\n try {\n const json = JSON.parse(message);\n const payload = this.alepha.codec.decode(\n consumer.queue.options.schema,\n json.payload,\n );\n await this.alepha.context.run(() => consumer.handler({ payload }));\n } catch (e) {\n this.log.error(\"Failed to process message\", e);\n }\n }\n\n /**\n * Stop the workers.\n *\n * This method will stop the workers and wait for them to finish processing.\n */\n protected async stopWorkers() {\n this.workersRunning = 0;\n\n this.log.trace(\"Stopping workers...\");\n this.abortController?.abort();\n\n this.log.trace(\"Waiting for workers to finish...\");\n await Promise.all(this.workerPromises);\n }\n\n /**\n * Force the workers to get back to work.\n */\n public wakeUp(): void {\n this.log.debug(\"Waking up workers...\");\n this.abortController?.abort();\n this.abortController = new AbortController();\n\n // if no workers are running, start them, (should not happen, but just in case)\n this.startWorkers();\n }\n}\n\nexport interface Consumer<T extends TSchema = TSchema> {\n queue: QueuePrimitive<T>;\n handler: (message: QueueMessage<T>) => Promise<void>;\n}\n\nexport interface NextMessage {\n consumer: Consumer;\n message: string;\n}\n","import {\n $inject,\n createPrimitive,\n KIND,\n Primitive,\n type Service,\n type Static,\n type TSchema,\n} from \"alepha\";\nimport { $logger } from \"alepha/logger\";\nimport { MemoryQueueProvider } from \"../providers/MemoryQueueProvider.ts\";\nimport { QueueProvider } from \"../providers/QueueProvider.ts\";\nimport { WorkerProvider } from \"../providers/WorkerProvider.ts\";\n\n/**\n * Creates a queue primitive for asynchronous message processing with background workers.\n *\n * The $queue primitive enables powerful asynchronous communication patterns in your application.\n * It provides type-safe message queuing with automatic worker processing, making it perfect for\n * decoupling components and handling background tasks efficiently.\n *\n * **Background Processing**\n * - Automatic worker threads for non-blocking message processing\n * - Built-in retry mechanisms and error handling\n * - Dead letter queues for failed message handling\n * - Graceful shutdown and worker lifecycle management\n *\n * **Type Safety**\n * - Full TypeScript support with schema validation using TypeBox\n * - Type-safe message payloads with automatic inference\n * - Runtime validation of all queued messages\n * - Compile-time errors for invalid message structures\n *\n * **Storage Flexibility**\n * - Memory provider for development and testing\n * - Redis provider for production scalability and persistence\n * - Custom provider support for specialized backends\n * - Automatic failover and connection pooling\n *\n * **Performance & Scalability**\n * - Batch processing support for high-throughput scenarios\n * - Horizontal scaling with distributed queue backends\n * - Configurable concurrency and worker pools\n * - Efficient serialization and message routing\n *\n * **Reliability**\n * - Message persistence across application restarts\n * - Automatic retry with exponential backoff\n * - Dead letter handling for permanently failed messages\n * - Comprehensive logging and monitoring integration\n *\n * @example Basic notification queue\n * ```typescript\n * const emailQueue = $queue({\n * name: \"email-notifications\",\n * schema: t.object({\n * to: t.text(),\n * subject: t.text(),\n * body: t.text(),\n * priority: t.optional(t.enum([\"high\", \"normal\"]))\n * }),\n * handler: async (message) => {\n * await emailService.send(message.payload);\n * console.log(`Email sent to ${message.payload.to}`);\n * }\n * });\n *\n * // Push messages for background processing\n * await emailQueue.push({\n * to: \"user@example.com\",\n * subject: \"Welcome!\",\n * body: \"Welcome to our platform\",\n * priority: \"high\"\n * });\n * ```\n *\n * @example Batch processing with Redis\n * ```typescript\n * const imageQueue = $queue({\n * name: \"image-processing\",\n * provider: RedisQueueProvider,\n * schema: t.object({\n * imageId: t.text(),\n * operations: t.array(t.enum([\"resize\", \"compress\", \"thumbnail\"]))\n * }),\n * handler: async (message) => {\n * for (const op of message.payload.operations) {\n * await processImage(message.payload.imageId, op);\n * }\n * }\n * });\n *\n * // Batch processing multiple images\n * await imageQueue.push(\n * { imageId: \"img1\", operations: [\"resize\", \"thumbnail\"] },\n * { imageId: \"img2\", operations: [\"compress\"] },\n * { imageId: \"img3\", operations: [\"resize\", \"compress\", \"thumbnail\"] }\n * );\n * ```\n *\n * @example Development with memory provider\n * ```typescript\n * const taskQueue = $queue({\n * name: \"dev-tasks\",\n * provider: \"memory\",\n * schema: t.object({\n * taskType: t.enum([\"cleanup\", \"backup\", \"report\"]),\n * data: t.record(t.text(), t.any())\n * }),\n * handler: async (message) => {\n * switch (message.payload.taskType) {\n * case \"cleanup\":\n * await performCleanup(message.payload.data);\n * break;\n * case \"backup\":\n * await createBackup(message.payload.data);\n * break;\n * case \"report\":\n * await generateReport(message.payload.data);\n * break;\n * }\n * }\n * });\n * ```\n */\nexport const $queue = <T extends TSchema>(\n options: QueuePrimitiveOptions<T>,\n): QueuePrimitive<T> => {\n return createPrimitive(QueuePrimitive<T>, options);\n};\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport interface QueuePrimitiveOptions<T extends TSchema> {\n /**\n * Unique name for the queue.\n *\n * This name is used for:\n * - Queue identification across the system\n * - Storage backend key generation\n * - Logging and monitoring\n * - Worker assignment and routing\n *\n * If not provided, defaults to the property key where the queue is declared.\n *\n * @example \"email-notifications\"\n * @example \"image-processing\"\n * @example \"order-fulfillment\"\n */\n name?: string;\n\n /**\n * Human-readable description of the queue's purpose.\n *\n * Used for:\n * - Documentation generation\n * - Monitoring dashboards\n * - Development team communication\n * - Queue management interfaces\n *\n * @example \"Process user registration emails and welcome sequences\"\n * @example \"Handle image uploads, resizing, and thumbnail generation\"\n * @example \"Manage order processing, payment, and shipping workflows\"\n */\n description?: string;\n\n /**\n * Queue storage provider configuration.\n *\n * Options:\n * - **\"memory\"**: In-memory queue (default for development, lost on restart)\n * - **Service<QueueProvider>**: Custom provider class (e.g., RedisQueueProvider)\n * - **undefined**: Uses the default queue provider from dependency injection\n *\n * **Provider Selection Guidelines**:\n * - Development: Use \"memory\" for fast, simple testing\n * - Production: Use Redis or database-backed providers for persistence\n * - High-throughput: Use specialized providers with connection pooling\n * - Distributed systems: Use Redis or message brokers for scalability\n *\n * @default Uses injected QueueProvider\n * @example \"memory\"\n * @example RedisQueueProvider\n * @example DatabaseQueueProvider\n */\n provider?: \"memory\" | Service<QueueProvider>;\n\n /**\n * TypeBox schema defining the structure of messages in this queue.\n *\n * This schema:\n * - Validates all messages pushed to the queue\n * - Provides full TypeScript type inference\n * - Ensures type safety between producers and consumers\n * - Enables automatic serialization/deserialization\n *\n * **Schema Design Best Practices**:\n * - Keep schemas simple and focused on the specific task\n * - Use optional fields for data that might not always be available\n * - Include version fields for schema evolution\n * - Use union types for different message types in the same queue\n *\n * @example\n * ```ts\n * t.object({\n * userId: t.text(),\n * action: t.enum([\"create\", \"update\"]),\n * data: t.record(t.text(), t.any()),\n * timestamp: t.optional(t.number())\n * })\n * ```\n */\n schema: T;\n\n /**\n * Message handler function that processes queue messages.\n *\n * This function:\n * - Runs in background worker threads for non-blocking processing\n * - Receives type-safe message payloads based on the schema\n * - Should be idempotent to handle potential retries\n * - Can throw errors to trigger retry mechanisms\n * - Has access to the full Alepha dependency injection container\n *\n * **Handler Best Practices**:\n * - Keep handlers focused on a single responsibility\n * - Use proper error handling and logging\n * - Make operations idempotent when possible\n * - Validate critical business logic within handlers\n * - Consider using transactions for data consistency\n *\n * @param message - The queue message with validated payload\n * @returns Promise that resolves when processing is complete\n *\n * @example\n * ```ts\n * handler: async (message) => {\n * const { userId, email, template } = message.payload;\n *\n * try {\n * await this.emailService.send({\n * to: email,\n * template,\n * data: { userId }\n * });\n *\n * await this.userService.markEmailSent(userId, template);\n * } catch (error) {\n * // Log error and let the queue system handle retries\n * this.logger.error(`Failed to send email to ${email}`, error);\n * throw error;\n * }\n * }\n * ```\n */\n handler?: (message: QueueMessage<T>) => Promise<void>;\n}\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport class QueuePrimitive<T extends TSchema> extends Primitive<\n QueuePrimitiveOptions<T>\n> {\n protected readonly log = $logger();\n protected readonly workerProvider = $inject(WorkerProvider);\n public readonly provider = this.$provider();\n\n public async push(...payloads: Array<Static<T>>) {\n await Promise.all(\n payloads.map((payload) =>\n this.provider.push(\n this.name,\n JSON.stringify({\n headers: {},\n payload: this.alepha.codec.decode(this.options.schema, payload),\n }),\n ),\n ),\n );\n\n this.log.debug(`Pushed to queue ${this.name}`, payloads);\n this.workerProvider.wakeUp();\n }\n\n public get name() {\n return this.options.name || this.config.propertyKey;\n }\n\n protected $provider() {\n if (!this.options.provider) {\n return this.alepha.inject(QueueProvider);\n }\n if (this.options.provider === \"memory\") {\n return this.alepha.inject(MemoryQueueProvider);\n }\n return this.alepha.inject(this.options.provider);\n }\n}\n\n$queue[KIND] = QueuePrimitive;\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport interface QueueMessageSchema {\n payload: TSchema;\n}\n\nexport interface QueueMessage<T extends TSchema> {\n payload: Static<T>;\n}\n","import { $hook, $inject, Alepha, AlephaError } from \"alepha\";\nimport { $logger } from \"alepha/logger\";\nimport { QueueProvider } from \"./QueueProvider.ts\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\n/**\n * Cloudflare Queue interface matching the CF Workers Queue API.\n */\nexport interface CloudflareQueue {\n send(message: unknown): Promise<void>;\n sendBatch(messages: Array<{ body: unknown }>): Promise<void>;\n}\n\n// ---------------------------------------------------------------------------------------------------------------------\n\n/**\n * Default queue binding name used in wrangler configuration.\n */\nexport const QUEUE_DEFAULT_BINDING = \"JOBS_QUEUE\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\n/**\n * Cloudflare Queue provider.\n *\n * Uses a Queue binding for message dispatch. Messages are wrapped with the\n * logical queue name so the consumer can route them to the correct handler.\n *\n * **Required Cloudflare binding:**\n * - `JOBS_QUEUE` - A Queue binding in wrangler configuration\n *\n * @example\n * ```toml\n * # wrangler.toml - automatically generated by alepha build\n * [[queues.producers]]\n * binding = \"JOBS_QUEUE\"\n * queue = \"my-app-queue\"\n * ```\n */\nexport class CloudflareQueueProvider extends QueueProvider {\n protected readonly alepha = $inject(Alepha);\n protected readonly log = $logger();\n\n protected queue?: CloudflareQueue;\n\n protected readonly onStart = $hook({\n on: \"start\",\n handler: async () => {\n const cloudflareEnv = this.alepha.store.get(\"cloudflare.env\") as\n | Record<string, unknown>\n | undefined;\n if (!cloudflareEnv) {\n throw new AlephaError(\n \"Cloudflare Workers environment not found in Alepha store under 'cloudflare.env'.\",\n );\n }\n\n const binding = cloudflareEnv[QUEUE_DEFAULT_BINDING] as\n | CloudflareQueue\n | undefined;\n if (!binding) {\n throw new AlephaError(\n `Queue binding '${QUEUE_DEFAULT_BINDING}' not found in Cloudflare Workers environment.`,\n );\n }\n\n this.queue = binding;\n this.log.info(\"Cloudflare Queue ready\");\n },\n });\n\n public async push(queue: string, message: string): Promise<void> {\n await this.getQueue().send({ queue, message });\n }\n\n /**\n * Not used on Cloudflare — queue consumption is push-based via the `queue` handler.\n */\n public async pop(_queue: string): Promise<string | undefined> {\n return undefined;\n }\n\n protected getQueue(): CloudflareQueue {\n if (!this.queue) {\n throw new AlephaError(\n \"Queue binding not initialized. Call start() first.\",\n );\n }\n return this.queue;\n }\n}\n","import { $module, type Alepha } from \"alepha\";\nimport { $consumer } from \"./primitives/$consumer.ts\";\nimport { $queue } from \"./primitives/$queue.ts\";\nimport { MemoryQueueProvider } from \"./providers/MemoryQueueProvider.ts\";\nimport { QueueProvider } from \"./providers/QueueProvider.ts\";\nimport { WorkerProvider } from \"./providers/WorkerProvider.ts\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport * from \"./primitives/$consumer.ts\";\nexport * from \"./primitives/$queue.ts\";\nexport * from \"./providers/CloudflareQueueProvider.ts\";\nexport * from \"./providers/MemoryQueueProvider.ts\";\nexport * from \"./providers/QueueProvider.ts\";\nexport * from \"./providers/WorkerProvider.ts\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\n/**\n * Asynchronous message processing with automatic worker management.\n *\n * **Features:**\n * - Background job queues with type-safe payloads\n * - Queue consumer handlers\n * - Automatic worker threads for non-blocking processing\n * - Retry mechanisms with exponential backoff\n * - Dead letter queues for failed messages\n * - Batch processing support\n * - Configurable concurrency and worker pools\n * - Providers: Memory (dev), Redis (production)\n *\n * @module alepha.queue\n */\nexport const AlephaQueue = $module({\n name: \"alepha.queue\",\n primitives: [$queue, $consumer],\n services: [QueueProvider, WorkerProvider],\n variants: [MemoryQueueProvider],\n register: (alepha: Alepha) =>\n alepha\n .with({\n optional: true,\n provide: QueueProvider,\n use: MemoryQueueProvider,\n })\n .with(WorkerProvider),\n});\n"],"mappings":"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"}
|