alepha 0.21.2 → 0.22.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 +289 -292
- 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 +211 -216
- 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 +1073 -1006
- package/dist/api/users/index.d.ts.map +1 -1
- package/dist/api/users/index.js +283 -61
- 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 +134 -140
- 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 +417 -214
- package/dist/cli/core/index.d.ts.map +1 -1
- package/dist/cli/core/index.js +325 -563
- 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 +8 -12
- package/dist/cli/i18n/index.d.ts.map +1 -1
- 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 +1446 -0
- package/dist/cli/platform-lib/index.d.ts.map +1 -0
- package/dist/cli/platform-lib/index.js +2597 -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 +21 -20
- 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 +16 -20
- package/dist/mcp/index.d.ts.map +1 -1
- 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 -4
- package/dist/react/form/index.d.ts.map +1 -1
- 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 +47 -11
- package/dist/react/i18n/index.d.ts.map +1 -1
- package/dist/react/i18n/index.js +33 -1
- 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 +65 -19
- package/dist/react/router/index.browser.js.map +1 -1
- package/dist/react/router/index.d.ts +327 -222
- package/dist/react/router/index.d.ts.map +1 -1
- package/dist/react/router/index.js +65 -29
- package/dist/react/router/index.js.map +1 -1
- 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 +167 -172
- package/dist/server/auth/index.d.ts.map +1 -1
- package/dist/server/auth/index.js +4 -8
- 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 +5 -7
- package/dist/server/cookies/index.d.ts.map +1 -1
- 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 +88 -73
- package/dist/server/core/index.d.ts.map +1 -1
- package/dist/server/core/index.js +19 -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 +45 -22
- 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/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 +3 -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/commands/__tests__/BuildCommand.spec.ts +43 -0
- package/src/cli/core/commands/build.ts +108 -30
- 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 -0
- 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 +353 -47
- package/src/cli/core/tasks/BuildDockerTask.ts +33 -3
- 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/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 +402 -165
- 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/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/i18n/components/Translate.tsx +47 -0
- package/src/react/i18n/index.ts +2 -0
- package/src/react/intro/components/GettingStartedAdminSlide.tsx +2 -2
- package/src/react/router/__tests__/$page.spec.tsx +3 -2
- package/src/react/router/__tests__/page-can.spec.ts +18 -13
- package/src/react/router/hooks/useQueryParams.ts +114 -14
- package/src/react/router/primitives/$page.ts +85 -4
- package/src/react/router/providers/ReactBrowserRouterProvider.ts +3 -7
- package/src/react/router/providers/ReactServerProvider.ts +4 -13
- package/src/react/ui/services/SchemaControl.ts +3 -4
- 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/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
|
@@ -11,8 +11,14 @@ import { $logger } from "alepha/logger";
|
|
|
11
11
|
const SEND_EMAIL_DEFAULT_BINDING = "SEND_EMAIL";
|
|
12
12
|
/**
|
|
13
13
|
* Environment variables for Cloudflare email configuration.
|
|
14
|
+
*
|
|
15
|
+
* `EMAIL_FROM` is `optional` at the schema level so the provider can be
|
|
16
|
+
* registered (and constructed) in non-Workers contexts — Node `yarn
|
|
17
|
+
* start`, build-time introspection, etc. — without forcing every dev to
|
|
18
|
+
* set it. `send()` re-checks at call time and throws a clear error if
|
|
19
|
+
* it's missing then.
|
|
14
20
|
*/
|
|
15
|
-
const envSchema = t.object({ EMAIL_FROM: t.text({ description: "Default sender
|
|
21
|
+
const envSchema = t.object({ EMAIL_FROM: t.optional(t.text({ description: "Default sender (a verified sender address). Accepts a bare address or an RFC 5322 display-name form, e.g. `Lore <noreply@lore.alepha.dev>`." })) });
|
|
16
22
|
/**
|
|
17
23
|
* Email provider using Cloudflare's Email Sending API via a Workers binding.
|
|
18
24
|
*
|
|
@@ -49,22 +55,29 @@ var CloudflareEmailProvider = class {
|
|
|
49
55
|
on: "start",
|
|
50
56
|
handler: () => {
|
|
51
57
|
const cloudflareEnv = this.alepha.get("cloudflare.env");
|
|
52
|
-
if (!cloudflareEnv)
|
|
58
|
+
if (!cloudflareEnv) {
|
|
59
|
+
this.log.warn("Cloudflare Email Sending inert: 'cloudflare.env' not set (not running on Workers).");
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
53
62
|
const binding = cloudflareEnv[SEND_EMAIL_DEFAULT_BINDING];
|
|
54
|
-
if (!binding)
|
|
63
|
+
if (!binding) {
|
|
64
|
+
this.log.warn(`Cloudflare Email Sending inert: binding '${SEND_EMAIL_DEFAULT_BINDING}' not found in Workers environment.`);
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
55
67
|
this.binding = binding;
|
|
56
68
|
this.log.info("Cloudflare Email Sending OK");
|
|
57
69
|
}
|
|
58
70
|
});
|
|
59
71
|
async send(options) {
|
|
60
72
|
const { to, subject, body } = options;
|
|
73
|
+
if (!this.env.EMAIL_FROM) throw new EmailError("Cannot send email via Cloudflare: EMAIL_FROM env var is not set.");
|
|
61
74
|
this.log.info("Sending email via Cloudflare", {
|
|
62
75
|
to,
|
|
63
76
|
subject
|
|
64
77
|
});
|
|
65
78
|
const message = {
|
|
66
79
|
to: Array.isArray(to) ? to : [to],
|
|
67
|
-
from: this.env.EMAIL_FROM,
|
|
80
|
+
from: this.resolveFrom(this.env.EMAIL_FROM),
|
|
68
81
|
subject,
|
|
69
82
|
html: body
|
|
70
83
|
};
|
|
@@ -87,6 +100,27 @@ var CloudflareEmailProvider = class {
|
|
|
87
100
|
throw new EmailError(message, error instanceof Error ? error : void 0);
|
|
88
101
|
}
|
|
89
102
|
}
|
|
103
|
+
/**
|
|
104
|
+
* Map the configured `EMAIL_FROM` to Cloudflare's `from` shape. An RFC 5322
|
|
105
|
+
* display-name form — `Name <addr@host>` (the name may be quoted) — is split
|
|
106
|
+
* into `{ email, name }` so Cloudflare renders the sender's display name
|
|
107
|
+
* (e.g. `Lore <noreply@lore.alepha.dev>` → "Lore"). A bare address is passed
|
|
108
|
+
* through unchanged.
|
|
109
|
+
*
|
|
110
|
+
* The object key is `email` (not `address`) — that is the field the
|
|
111
|
+
* Cloudflare Email Service Workers API expects. Sending `{ address, name }`
|
|
112
|
+
* makes the API reject the message with an opaque "internal error".
|
|
113
|
+
*/
|
|
114
|
+
resolveFrom(value) {
|
|
115
|
+
const match = value.match(/^\s*(.*?)\s*<([^>]+)>\s*$/);
|
|
116
|
+
if (!match) return value.trim();
|
|
117
|
+
const name = match[1].replace(/^"|"$/g, "").trim();
|
|
118
|
+
const email = match[2].trim();
|
|
119
|
+
return name ? {
|
|
120
|
+
email,
|
|
121
|
+
name
|
|
122
|
+
} : { email };
|
|
123
|
+
}
|
|
90
124
|
getBinding() {
|
|
91
125
|
if (!this.binding) throw new AlephaError("Cloudflare Email binding not initialized. Call start() first.");
|
|
92
126
|
return this.binding;
|
|
@@ -104,11 +138,14 @@ var CloudflareEmailProvider = class {
|
|
|
104
138
|
const AlephaEmailCloudflare = $module({
|
|
105
139
|
name: "alepha.email.cloudflare",
|
|
106
140
|
services: [CloudflareEmailProvider],
|
|
107
|
-
register: (alepha) =>
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
141
|
+
register: (alepha) => {
|
|
142
|
+
if (alepha.isServerless()) alepha.with({
|
|
143
|
+
optional: true,
|
|
144
|
+
provide: EmailProvider,
|
|
145
|
+
use: CloudflareEmailProvider
|
|
146
|
+
});
|
|
147
|
+
return alepha.with(AlephaEmail);
|
|
148
|
+
}
|
|
112
149
|
});
|
|
113
150
|
//#endregion
|
|
114
151
|
export { AlephaEmailCloudflare, CloudflareEmailProvider, SEND_EMAIL_DEFAULT_BINDING };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":[],"sources":["../../../src/email/cloudflare/providers/CloudflareEmailProvider.ts","../../../src/email/cloudflare/index.ts"],"sourcesContent":["import { $env, $hook, $inject, Alepha, AlephaError, t } from \"alepha\";\nimport {\n EmailError,\n type EmailProvider,\n type EmailSendOptions,\n} from \"alepha/email\";\nimport { $logger } from \"alepha/logger\";\n\n/**\n * Default Cloudflare Email Sending binding name.\n *\n * Matches the convention used by other Alepha Cloudflare providers\n * (e.g. `KV_CACHE` for {@link CloudflareKVProvider}).\n */\nexport const SEND_EMAIL_DEFAULT_BINDING = \"SEND_EMAIL\";\n\n/**\n * Environment variables for Cloudflare email configuration.\n */\nconst envSchema = t.object({\n EMAIL_FROM: t.text({\n description: \"Default sender email address (must be a verified sender)\",\n }),\n});\n\n/**\n * Shape of the Cloudflare Email Sending binding (public beta, 2026-04-16).\n *\n * @see https://developers.cloudflare.com/email-service/\n */\nexport interface CloudflareEmailBinding {\n send(message: CloudflareEmailSendMessage): Promise<CloudflareEmailSendResult>;\n}\n\nexport interface CloudflareEmailSendMessage {\n to: string | string[];\n from: string | { address: string; name?: string };\n subject: string;\n html?: string;\n text?: string;\n cc?: string | string[];\n bcc?: string | string[];\n reply_to?: string | string[];\n headers?: Record<string, string>;\n}\n\nexport interface CloudflareEmailSendResult {\n id?: string;\n status?: \"queued\" | \"sent\" | \"bounced\" | string;\n}\n\n/**\n * Email provider using Cloudflare's Email Sending API via a Workers binding.\n *\n * Requires the Workers Paid plan and a verified sender address on the\n * `EMAIL_FROM` domain.\n *\n * **Required Cloudflare binding:**\n * - `SEND_EMAIL` — an Email Sending binding in wrangler configuration\n *\n * Configuration is provided via environment variables:\n * - `EMAIL_FROM`: Default sender email address\n *\n * @example\n * ```toml\n * # wrangler.toml\n * [[send_email]]\n * binding = \"SEND_EMAIL\"\n * ```\n *\n * @example\n * ```typescript\n * // app.ts\n * import { AlephaEmailCloudflare } from \"alepha/email/cloudflare\";\n *\n * const app = Alepha.create().with(AlephaEmailCloudflare);\n * ```\n */\nexport class CloudflareEmailProvider implements EmailProvider {\n protected readonly alepha = $inject(Alepha);\n protected readonly env = $env(envSchema);\n protected readonly log = $logger();\n\n protected binding?: CloudflareEmailBinding;\n\n protected readonly onStart = $hook({\n on: \"start\",\n handler: () => {\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[SEND_EMAIL_DEFAULT_BINDING] as\n | CloudflareEmailBinding\n | undefined;\n if (!binding) {\n throw new AlephaError(\n `Cloudflare Email binding '${SEND_EMAIL_DEFAULT_BINDING}' not found in Workers environment.`,\n );\n }\n\n this.binding = binding;\n this.log.info(\"Cloudflare Email Sending OK\");\n },\n });\n\n public async send(options: EmailSendOptions): Promise<void> {\n const { to, subject, body } = options;\n this.log.info(\"Sending email via Cloudflare\", { to, subject });\n\n const message: CloudflareEmailSendMessage = {\n to: Array.isArray(to) ? to : [to],\n from: this.env.EMAIL_FROM,\n subject,\n html: body,\n };\n\n try {\n const result = await this.getBinding().send(message);\n\n if (result?.status === \"bounced\") {\n throw new EmailError(\n `Cloudflare email bounced (id=${result.id ?? \"unknown\"})`,\n );\n }\n\n this.log.info(\"Email sent successfully via Cloudflare\", {\n to,\n subject,\n id: result?.id,\n status: result?.status,\n });\n } catch (error) {\n if (error instanceof EmailError) {\n throw error;\n }\n const status = (error as { status?: number })?.status;\n const message =\n status === 429\n ? `Cloudflare email rate limit hit (429): ${error instanceof Error ? error.message : String(error)}`\n : `Failed to send email via Cloudflare: ${error instanceof Error ? error.message : String(error)}`;\n this.log.error(message, { to, subject });\n throw new EmailError(message, error instanceof Error ? error : undefined);\n }\n }\n\n protected getBinding(): CloudflareEmailBinding {\n if (!this.binding) {\n throw new AlephaError(\n \"Cloudflare Email binding not initialized. Call start() first.\",\n );\n }\n return this.binding;\n }\n}\n","import { $module, type Alepha } from \"alepha\";\nimport { AlephaEmail, EmailProvider } from \"alepha/email\";\nimport { CloudflareEmailProvider } from \"./providers/CloudflareEmailProvider.ts\";\n\n// Exports\nexport * from \"./providers/CloudflareEmailProvider.ts\";\n\n/**\n * Plugin for Alepha Email that sends through Cloudflare's Email Sending API\n * via a Workers binding.\n *\n * @see {@link CloudflareEmailProvider}\n * @module alepha.email.cloudflare\n */\nexport const AlephaEmailCloudflare = $module({\n name: \"alepha.email.cloudflare\",\n services: [CloudflareEmailProvider],\n register: (alepha: Alepha) =>\n alepha\n .with({\n optional: true,\n provide: EmailProvider,\n use: CloudflareEmailProvider,\n })\n .with(AlephaEmail),\n});\n"],"mappings":";;;;;;;;;;AAcA,MAAa,6BAA6B;;;;AAK1C,MAAM,YAAY,EAAE,OAAO,EACzB,YAAY,EAAE,KAAK,EACjB,aAAa,4DACd,CAAC,EACH,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAuDF,IAAa,0BAAb,MAA8D;CAC5D,SAA4B,QAAQ,OAAO;CAC3C,MAAyB,KAAK,UAAU;CACxC,MAAyB,SAAS;CAElC;CAEA,UAA6B,MAAM;EACjC,IAAI;EACJ,eAAe;GACb,MAAM,gBAAgB,KAAK,OAAO,IAAI,iBAAiB;GAGvD,IAAI,CAAC,eACH,MAAM,IAAI,YACR,mFACD;GAGH,MAAM,UAAU,cAAc;GAG9B,IAAI,CAAC,SACH,MAAM,IAAI,YACR,6BAA6B,2BAA2B,qCACzD;GAGH,KAAK,UAAU;GACf,KAAK,IAAI,KAAK,8BAA8B;;EAE/C,CAAC;CAEF,MAAa,KAAK,SAA0C;EAC1D,MAAM,EAAE,IAAI,SAAS,SAAS;EAC9B,KAAK,IAAI,KAAK,gCAAgC;GAAE;GAAI;GAAS,CAAC;EAE9D,MAAM,UAAsC;GAC1C,IAAI,MAAM,QAAQ,GAAG,GAAG,KAAK,CAAC,GAAG;GACjC,MAAM,KAAK,IAAI;GACf;GACA,MAAM;GACP;EAED,IAAI;GACF,MAAM,SAAS,MAAM,KAAK,YAAY,CAAC,KAAK,QAAQ;GAEpD,IAAI,QAAQ,WAAW,WACrB,MAAM,IAAI,WACR,gCAAgC,OAAO,MAAM,UAAU,GACxD;GAGH,KAAK,IAAI,KAAK,0CAA0C;IACtD;IACA;IACA,IAAI,QAAQ;IACZ,QAAQ,QAAQ;IACjB,CAAC;WACK,OAAO;GACd,IAAI,iBAAiB,YACnB,MAAM;GAGR,MAAM,UADU,OAA+B,WAElC,MACP,0CAA0C,iBAAiB,QAAQ,MAAM,UAAU,OAAO,MAAM,KAChG,wCAAwC,iBAAiB,QAAQ,MAAM,UAAU,OAAO,MAAM;GACpG,KAAK,IAAI,MAAM,SAAS;IAAE;IAAI;IAAS,CAAC;GACxC,MAAM,IAAI,WAAW,SAAS,iBAAiB,QAAQ,QAAQ,KAAA,EAAU;;;CAI7E,aAA+C;EAC7C,IAAI,CAAC,KAAK,SACR,MAAM,IAAI,YACR,gEACD;EAEH,OAAO,KAAK;;;;;;;;;;;;AC/IhB,MAAa,wBAAwB,QAAQ;CAC3C,MAAM;CACN,UAAU,CAAC,wBAAwB;CACnC,WAAW,WACT,OACG,KAAK;EACJ,UAAU;EACV,SAAS;EACT,KAAK;EACN,CAAC,CACD,KAAK,YAAY;CACvB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../../../src/email/cloudflare/providers/CloudflareEmailProvider.ts","../../../src/email/cloudflare/index.ts"],"sourcesContent":["import { $env, $hook, $inject, Alepha, AlephaError, t } from \"alepha\";\nimport {\n EmailError,\n type EmailProvider,\n type EmailSendOptions,\n} from \"alepha/email\";\nimport { $logger } from \"alepha/logger\";\n\n/**\n * Default Cloudflare Email Sending binding name.\n *\n * Matches the convention used by other Alepha Cloudflare providers\n * (e.g. `KV_CACHE` for {@link CloudflareKVProvider}).\n */\nexport const SEND_EMAIL_DEFAULT_BINDING = \"SEND_EMAIL\";\n\n/**\n * Environment variables for Cloudflare email configuration.\n *\n * `EMAIL_FROM` is `optional` at the schema level so the provider can be\n * registered (and constructed) in non-Workers contexts — Node `yarn\n * start`, build-time introspection, etc. — without forcing every dev to\n * set it. `send()` re-checks at call time and throws a clear error if\n * it's missing then.\n */\nconst envSchema = t.object({\n EMAIL_FROM: t.optional(\n t.text({\n description:\n \"Default sender (a verified sender address). Accepts a bare address or an RFC 5322 display-name form, e.g. `Lore <noreply@lore.alepha.dev>`.\",\n }),\n ),\n});\n\n/**\n * Shape of the Cloudflare Email Sending binding (public beta, 2026-04-16).\n *\n * @see https://developers.cloudflare.com/email-service/\n */\nexport interface CloudflareEmailBinding {\n send(message: CloudflareEmailSendMessage): Promise<CloudflareEmailSendResult>;\n}\n\nexport interface CloudflareEmailSendMessage {\n to: string | string[];\n from: string | { email: string; name?: string };\n subject: string;\n html?: string;\n text?: string;\n cc?: string | string[];\n bcc?: string | string[];\n reply_to?: string | string[];\n headers?: Record<string, string>;\n}\n\nexport interface CloudflareEmailSendResult {\n id?: string;\n status?: \"queued\" | \"sent\" | \"bounced\" | string;\n}\n\n/**\n * Email provider using Cloudflare's Email Sending API via a Workers binding.\n *\n * Requires the Workers Paid plan and a verified sender address on the\n * `EMAIL_FROM` domain.\n *\n * **Required Cloudflare binding:**\n * - `SEND_EMAIL` — an Email Sending binding in wrangler configuration\n *\n * Configuration is provided via environment variables:\n * - `EMAIL_FROM`: Default sender email address\n *\n * @example\n * ```toml\n * # wrangler.toml\n * [[send_email]]\n * binding = \"SEND_EMAIL\"\n * ```\n *\n * @example\n * ```typescript\n * // app.ts\n * import { AlephaEmailCloudflare } from \"alepha/email/cloudflare\";\n *\n * const app = Alepha.create().with(AlephaEmailCloudflare);\n * ```\n */\nexport class CloudflareEmailProvider implements EmailProvider {\n protected readonly alepha = $inject(Alepha);\n protected readonly env = $env(envSchema);\n protected readonly log = $logger();\n\n protected binding?: CloudflareEmailBinding;\n\n protected readonly onStart = $hook({\n on: \"start\",\n handler: () => {\n // Tolerate boot off-Workers. The provider has to be registered\n // unconditionally so the CF build task can see it and emit the\n // `send_email` binding into wrangler.jsonc — but actually starting\n // on Node (yarn start / yarn dev) would crash because no binding\n // is wired. Treat that as \"inert provider\": warn, don't throw.\n // `send()` later will surface the real error if it's ever called.\n const cloudflareEnv = this.alepha.get(\"cloudflare.env\") as\n | Record<string, unknown>\n | undefined;\n if (!cloudflareEnv) {\n this.log.warn(\n \"Cloudflare Email Sending inert: 'cloudflare.env' not set (not running on Workers).\",\n );\n return;\n }\n\n const binding = cloudflareEnv[SEND_EMAIL_DEFAULT_BINDING] as\n | CloudflareEmailBinding\n | undefined;\n if (!binding) {\n this.log.warn(\n `Cloudflare Email Sending inert: binding '${SEND_EMAIL_DEFAULT_BINDING}' not found in Workers environment.`,\n );\n return;\n }\n\n this.binding = binding;\n this.log.info(\"Cloudflare Email Sending OK\");\n },\n });\n\n public async send(options: EmailSendOptions): Promise<void> {\n const { to, subject, body } = options;\n if (!this.env.EMAIL_FROM) {\n throw new EmailError(\n \"Cannot send email via Cloudflare: EMAIL_FROM env var is not set.\",\n );\n }\n this.log.info(\"Sending email via Cloudflare\", { to, subject });\n\n const message: CloudflareEmailSendMessage = {\n to: Array.isArray(to) ? to : [to],\n from: this.resolveFrom(this.env.EMAIL_FROM),\n subject,\n html: body,\n };\n\n try {\n const result = await this.getBinding().send(message);\n\n if (result?.status === \"bounced\") {\n throw new EmailError(\n `Cloudflare email bounced (id=${result.id ?? \"unknown\"})`,\n );\n }\n\n this.log.info(\"Email sent successfully via Cloudflare\", {\n to,\n subject,\n id: result?.id,\n status: result?.status,\n });\n } catch (error) {\n if (error instanceof EmailError) {\n throw error;\n }\n const status = (error as { status?: number })?.status;\n const message =\n status === 429\n ? `Cloudflare email rate limit hit (429): ${error instanceof Error ? error.message : String(error)}`\n : `Failed to send email via Cloudflare: ${error instanceof Error ? error.message : String(error)}`;\n this.log.error(message, { to, subject });\n throw new EmailError(message, error instanceof Error ? error : undefined);\n }\n }\n\n /**\n * Map the configured `EMAIL_FROM` to Cloudflare's `from` shape. An RFC 5322\n * display-name form — `Name <addr@host>` (the name may be quoted) — is split\n * into `{ email, name }` so Cloudflare renders the sender's display name\n * (e.g. `Lore <noreply@lore.alepha.dev>` → \"Lore\"). A bare address is passed\n * through unchanged.\n *\n * The object key is `email` (not `address`) — that is the field the\n * Cloudflare Email Service Workers API expects. Sending `{ address, name }`\n * makes the API reject the message with an opaque \"internal error\".\n */\n protected resolveFrom(\n value: string,\n ): string | { email: string; name?: string } {\n const match = value.match(/^\\s*(.*?)\\s*<([^>]+)>\\s*$/);\n if (!match) {\n return value.trim();\n }\n const name = match[1].replace(/^\"|\"$/g, \"\").trim();\n const email = match[2].trim();\n return name ? { email, name } : { email };\n }\n\n protected getBinding(): CloudflareEmailBinding {\n if (!this.binding) {\n throw new AlephaError(\n \"Cloudflare Email binding not initialized. Call start() first.\",\n );\n }\n return this.binding;\n }\n}\n","import { $module, type Alepha } from \"alepha\";\nimport { AlephaEmail, EmailProvider } from \"alepha/email\";\nimport { CloudflareEmailProvider } from \"./providers/CloudflareEmailProvider.ts\";\n\n// Exports\nexport * from \"./providers/CloudflareEmailProvider.ts\";\n\n/**\n * Plugin for Alepha Email that sends through Cloudflare's Email Sending API\n * via a Workers binding.\n *\n * @see {@link CloudflareEmailProvider}\n * @module alepha.email.cloudflare\n */\nexport const AlephaEmailCloudflare = $module({\n name: \"alepha.email.cloudflare\",\n services: [CloudflareEmailProvider],\n // Gate the substitution on `isServerless()` so apps can register this\n // module unconditionally — the build path needs to see the service to\n // emit the wrangler `send_email` binding, but the runtime substitution\n // must only kick in on Workers. Off-Workers we fall through to\n // AlephaEmail's defaults (LocalEmailProvider for dev, MemoryEmailProvider\n // for tests), so `yarn start` / `yarn dev` / e2e keep working without\n // a real binding.\n register: (alepha: Alepha) => {\n if (alepha.isServerless()) {\n alepha.with({\n optional: true,\n provide: EmailProvider,\n use: CloudflareEmailProvider,\n });\n }\n return alepha.with(AlephaEmail);\n },\n});\n"],"mappings":";;;;;;;;;;AAcA,MAAa,6BAA6B;;;;;;;;;;AAW1C,MAAM,YAAY,EAAE,OAAO,EACzB,YAAY,EAAE,SACZ,EAAE,KAAK,EACL,aACE,8IACJ,CAAC,CACH,EACF,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAuDD,IAAa,0BAAb,MAA8D;CAC5D,SAA4B,QAAQ,MAAM;CAC1C,MAAyB,KAAK,SAAS;CACvC,MAAyB,QAAQ;CAEjC;CAEA,UAA6B,MAAM;EACjC,IAAI;EACJ,eAAe;GAOb,MAAM,gBAAgB,KAAK,OAAO,IAAI,gBAAgB;GAGtD,IAAI,CAAC,eAAe;IAClB,KAAK,IAAI,KACP,oFACF;IACA;GACF;GAEA,MAAM,UAAU,cAAc;GAG9B,IAAI,CAAC,SAAS;IACZ,KAAK,IAAI,KACP,4CAA4C,2BAA2B,oCACzE;IACA;GACF;GAEA,KAAK,UAAU;GACf,KAAK,IAAI,KAAK,6BAA6B;EAC7C;CACF,CAAC;CAED,MAAa,KAAK,SAA0C;EAC1D,MAAM,EAAE,IAAI,SAAS,SAAS;EAC9B,IAAI,CAAC,KAAK,IAAI,YACZ,MAAM,IAAI,WACR,kEACF;EAEF,KAAK,IAAI,KAAK,gCAAgC;GAAE;GAAI;EAAQ,CAAC;EAE7D,MAAM,UAAsC;GAC1C,IAAI,MAAM,QAAQ,EAAE,IAAI,KAAK,CAAC,EAAE;GAChC,MAAM,KAAK,YAAY,KAAK,IAAI,UAAU;GAC1C;GACA,MAAM;EACR;EAEA,IAAI;GACF,MAAM,SAAS,MAAM,KAAK,WAAW,EAAE,KAAK,OAAO;GAEnD,IAAI,QAAQ,WAAW,WACrB,MAAM,IAAI,WACR,gCAAgC,OAAO,MAAM,UAAU,EACzD;GAGF,KAAK,IAAI,KAAK,0CAA0C;IACtD;IACA;IACA,IAAI,QAAQ;IACZ,QAAQ,QAAQ;GAClB,CAAC;EACH,SAAS,OAAO;GACd,IAAI,iBAAiB,YACnB,MAAM;GAGR,MAAM,UADU,OAA+B,WAElC,MACP,0CAA0C,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,MAC/F,wCAAwC,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;GACnG,KAAK,IAAI,MAAM,SAAS;IAAE;IAAI;GAAQ,CAAC;GACvC,MAAM,IAAI,WAAW,SAAS,iBAAiB,QAAQ,QAAQ,KAAA,CAAS;EAC1E;CACF;;;;;;;;;;;;CAaA,YACE,OAC2C;EAC3C,MAAM,QAAQ,MAAM,MAAM,2BAA2B;EACrD,IAAI,CAAC,OACH,OAAO,MAAM,KAAK;EAEpB,MAAM,OAAO,MAAM,GAAG,QAAQ,UAAU,EAAE,EAAE,KAAK;EACjD,MAAM,QAAQ,MAAM,GAAG,KAAK;EAC5B,OAAO,OAAO;GAAE;GAAO;EAAK,IAAI,EAAE,MAAM;CAC1C;CAEA,aAA+C;EAC7C,IAAI,CAAC,KAAK,SACR,MAAM,IAAI,YACR,+DACF;EAEF,OAAO,KAAK;CACd;AACF;;;;;;;;;;AC9LA,MAAa,wBAAwB,QAAQ;CAC3C,MAAM;CACN,UAAU,CAAC,uBAAuB;CAQlC,WAAW,WAAmB;EAC5B,IAAI,OAAO,aAAa,GACtB,OAAO,KAAK;GACV,UAAU;GACV,SAAS;GACT,KAAK;EACP,CAAC;EAEH,OAAO,OAAO,KAAK,WAAW;CAChC;AACF,CAAC"}
|
|
@@ -1,8 +1,5 @@
|
|
|
1
|
-
import * as _$alepha from "alepha";
|
|
2
1
|
import { AlephaError, InstantiableClass, KIND, Primitive, Static } from "alepha";
|
|
3
|
-
import * as _$alepha_logger0 from "alepha/logger";
|
|
4
2
|
import { FileSystemProvider } from "alepha/system";
|
|
5
|
-
import * as _$typebox from "typebox";
|
|
6
3
|
|
|
7
4
|
//#region ../../src/email/core/providers/EmailProvider.d.ts
|
|
8
5
|
/**
|
|
@@ -70,8 +67,8 @@ declare class EmailPrimitive extends Primitive<EmailPrimitiveOptions> {
|
|
|
70
67
|
/**
|
|
71
68
|
* Local email provider configuration atom
|
|
72
69
|
*/
|
|
73
|
-
declare const localEmailOptions:
|
|
74
|
-
directory:
|
|
70
|
+
declare const localEmailOptions: import("alepha").Atom<import("typebox").TObject<{
|
|
71
|
+
directory: import("typebox").TString;
|
|
75
72
|
}>, "alepha.email.local.options">;
|
|
76
73
|
type LocalEmailProviderOptions = Static<typeof localEmailOptions.schema>;
|
|
77
74
|
declare module "alepha" {
|
|
@@ -80,13 +77,13 @@ declare module "alepha" {
|
|
|
80
77
|
}
|
|
81
78
|
}
|
|
82
79
|
declare class LocalEmailProvider implements EmailProvider {
|
|
83
|
-
protected readonly log:
|
|
80
|
+
protected readonly log: import("alepha/logger").Logger;
|
|
84
81
|
protected readonly fs: FileSystemProvider;
|
|
85
82
|
protected readonly options: Readonly<{
|
|
86
83
|
directory: string;
|
|
87
84
|
}>;
|
|
88
85
|
protected get directory(): string;
|
|
89
|
-
protected onStart:
|
|
86
|
+
protected onStart: import("alepha").HookPrimitive<"start">;
|
|
90
87
|
send(options: EmailSendOptions): Promise<void>;
|
|
91
88
|
createEmailJson(options: {
|
|
92
89
|
to: string;
|
|
@@ -108,7 +105,7 @@ interface EmailRecord {
|
|
|
108
105
|
sentAt: Date;
|
|
109
106
|
}
|
|
110
107
|
declare class MemoryEmailProvider implements EmailProvider {
|
|
111
|
-
protected readonly log:
|
|
108
|
+
protected readonly log: import("alepha/logger").Logger;
|
|
112
109
|
records: EmailRecord[];
|
|
113
110
|
send(options: EmailSendOptions): Promise<void>;
|
|
114
111
|
/**
|
|
@@ -148,7 +145,7 @@ declare module "alepha" {
|
|
|
148
145
|
*
|
|
149
146
|
* @module alepha.email
|
|
150
147
|
*/
|
|
151
|
-
declare const AlephaEmail:
|
|
148
|
+
declare const AlephaEmail: import("alepha").Service<import("alepha").Module>;
|
|
152
149
|
//#endregion
|
|
153
150
|
export { $email, AlephaEmail, EmailError, EmailPrimitive, EmailPrimitiveOptions, EmailProvider, EmailRecord, EmailSendOptions, LocalEmailProvider, LocalEmailProviderOptions, MemoryEmailProvider, localEmailOptions };
|
|
154
151
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","names":[],"sources":["../../../src/email/core/providers/EmailProvider.ts","../../../src/email/core/errors/EmailError.ts","../../../src/email/core/primitives/$email.ts","../../../src/email/core/providers/LocalEmailProvider.ts","../../../src/email/core/providers/MemoryEmailProvider.ts","../../../src/email/core/index.ts"],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.d.ts","names":[],"sources":["../../../src/email/core/providers/EmailProvider.ts","../../../src/email/core/errors/EmailError.ts","../../../src/email/core/primitives/$email.ts","../../../src/email/core/providers/LocalEmailProvider.ts","../../../src/email/core/providers/MemoryEmailProvider.ts","../../../src/email/core/index.ts"],"mappings":";;;;;;;;AAKA;uBAAsB,aAAA;;;;;;WAMJ,IAAA,CAAK,OAAA,EAAS,gBAAA,GAAmB,OAAO;AAAA;AAAA,KAG9C,gBAAA;EACV,EAAA;EACA,OAAA;EACA,IAAA;AAAA;;;cCfW,UAAA,SAAmB,WAAW;cAC7B,OAAA,UAAiB,KAAA,GAAQ,KAAA;AAAA;;;cCU1B,MAAA;EAAA,WAAmB,qBAAA,GAAqB,cAAA;EAAA;;UAKpC,qBAAA;EACf,IAAA;EACA,QAAA,GAAW,iBAAiB,CAAC,aAAA;AAAA;;;AFT2B;AAG1D;;;;;;;;AAGM;;;;ACfN;;;cCyCa,cAAA,SAAuB,SAAA,CAAU,qBAAA;EAAA,mBACzB,QAAA,EAAQ,aAAA;EAAA,IAEhB,IAAA;ED3CC;;;ECkDC,IAAA,CAAK,OAAA,EAAS,gBAAA,GAAmB,OAAA;EAAA,UAoBpC,SAAA,IAAa,aAAA;AAAA;;;;AFpEzB;;cGMa,iBAAA,mBAAiB,IAAA,mBAAA,OAAA;;;KAYlB,yBAAA,GAA4B,MAAM,QAAQ,iBAAA,CAAkB,MAAA;AAAA;EAAA,UAG5D,KAAA;IAAA,CACP,iBAAA,CAAkB,GAAG,GAAG,yBAAA;EAAA;AAAA;AAAA,cAMhB,kBAAA,YAA8B,aAAA;EAAA,mBACtB,GAAA,0BAAG,MAAA;EAAA,mBACH,EAAA,EAAE,kBAAA;EAAA,mBACF,OAAA,EAAO,QAAA;;;gBAEZ,SAAA;EAAA,UAIJ,OAAA,mBAAO,aAAA;EAmBJ,IAAA,CAAK,OAAA,EAAS,gBAAA,GAAmB,OAAA;EA4BvC,eAAA,CAAgB,OAAA;IACrB,EAAA;IACA,OAAA;IACA,IAAA;EAAA;IACI,EAAA;IAAY,OAAA;IAAiB,IAAA;IAAc,MAAA;EAAA;AAAA;;;UC1FlC,WAAA;EACf,EAAA;EACA,OAAA;EACA,IAAA;EACA,MAAA,EAAQ,IAAI;AAAA;AAAA,cAGD,mBAAA,YAA+B,aAAA;EAAA,mBACvB,GAAA,0BAAG,MAAA;EACf,OAAA,EAAS,WAAA;EAEH,IAAA,CAAK,OAAA,EAAS,gBAAA,GAAmB,OAAA;EJHG;;AAAO;EAAP,IIoBtC,IAAA,IAAQ,WAAA;AAAA;;;;YChBT,KAAA;IACR,eAAA;MACE,EAAA;MACA,QAAA;MACA,SAAA,EAAW,MAAA;MACX,QAAA,EAAU,aAAA;MACV,KAAA;IAAA;IAEF,YAAA;MACE,EAAA;MACA,QAAA;MACA,QAAA,EAAU,aAAA;IAAA;EAAA;AAAA;ALTV;;;;ACfN;;;;;;;;;;ADeM,cK4BO,WAAA,mBAAW,OAAA,kBAAA,MAAA"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":[],"sources":["../../../src/email/core/providers/EmailProvider.ts","../../../src/email/core/providers/MemoryEmailProvider.ts","../../../src/email/core/primitives/$email.ts","../../../src/email/core/errors/EmailError.ts","../../../src/email/core/providers/LocalEmailProvider.ts","../../../src/email/core/index.ts"],"sourcesContent":["/**\n * Email provider interface.\n *\n * All methods are asynchronous and return promises.\n */\nexport abstract class EmailProvider {\n /**\n * Send an email.\n *\n * @return Promise that resolves when the email is sent.\n */\n public abstract send(options: EmailSendOptions): Promise<void>;\n}\n\nexport type EmailSendOptions = {\n to: string | string[];\n subject: string;\n body: string;\n};\n","import { $logger } from \"alepha/logger\";\nimport type { EmailProvider, EmailSendOptions } from \"./EmailProvider.ts\";\n\nexport interface EmailRecord {\n to: string;\n subject: string;\n body: string;\n sentAt: Date;\n}\n\nexport class MemoryEmailProvider implements EmailProvider {\n protected readonly log = $logger();\n public records: EmailRecord[] = [];\n\n public async send(options: EmailSendOptions): Promise<void> {\n const { to, subject, body } = options;\n this.log.debug(\"Sending email to memory store\", { to, subject });\n\n for (const recipient of Array.isArray(to) ? to : [to]) {\n this.records.push({\n to: recipient,\n subject,\n body,\n sentAt: new Date(),\n });\n }\n }\n\n /**\n * Get the last email sent (for testing purposes).\n */\n public get last(): EmailRecord | undefined {\n return this.records[this.records.length - 1];\n }\n}\n","import {\n AlephaError,\n createPrimitive,\n type InstantiableClass,\n KIND,\n Primitive,\n} from \"alepha\";\nimport type { EmailSendOptions } from \"../providers/EmailProvider.ts\";\nimport { EmailProvider } from \"../providers/EmailProvider.ts\";\nimport { MemoryEmailProvider } from \"../providers/MemoryEmailProvider.ts\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport const $email = (options: EmailPrimitiveOptions = {}) =>\n createPrimitive(EmailPrimitive, options);\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport interface EmailPrimitiveOptions {\n name?: string;\n provider?: InstantiableClass<EmailProvider> | \"memory\";\n}\n\n// ---------------------------------------------------------------------------------------------------------------------\n\n/**\n * Email primitive for sending emails through various providers.\n *\n * Usage:\n * ```typescript\n * class MyService {\n * private readonly welcomeEmail = $email({ name: \"welcome\" });\n *\n * async sendWelcome(userEmail: string, userName: string) {\n * await this.welcomeEmail.send({\n * to: userEmail,\n * subject: \"Welcome!\",\n * body: `<p>Hello ${userName}!</p>`\n * });\n * }\n * }\n * ```\n */\nexport class EmailPrimitive extends Primitive<EmailPrimitiveOptions> {\n protected readonly provider = this.$provider();\n\n public get name() {\n return this.options.name ?? `${this.config.propertyKey}`;\n }\n\n /**\n * Send an email using the configured provider.\n */\n public async send(options: EmailSendOptions): Promise<void> {\n await this.alepha.events.emit(\"email:sending\", {\n to: options.to,\n template: this.name,\n variables: {},\n provider: this.provider,\n abort: () => {\n throw new AlephaError(\"Email sending aborted by hook\");\n },\n });\n\n await this.provider.send(options);\n\n await this.alepha.events.emit(\"email:sent\", {\n to: options.to,\n template: this.name,\n provider: this.provider,\n });\n }\n\n protected $provider(): EmailProvider {\n if (!this.options.provider) {\n return this.alepha.inject(EmailProvider);\n }\n if (this.options.provider === \"memory\") {\n return this.alepha.inject(MemoryEmailProvider);\n }\n return this.alepha.inject(this.options.provider);\n }\n}\n\n// ---------------------------------------------------------------------------------------------------------------------\n\n$email[KIND] = EmailPrimitive;\n","import { AlephaError } from \"alepha\";\n\nexport class EmailError extends AlephaError {\n constructor(message: string, cause?: Error) {\n super(message);\n this.name = \"EmailError\";\n this.cause = cause;\n }\n}\n","import { $atom, $hook, $inject, $state, type Static, t } from \"alepha\";\nimport { $logger } from \"alepha/logger\";\nimport { FileSystemProvider } from \"alepha/system\";\nimport { EmailError } from \"../errors/EmailError.ts\";\nimport type { EmailProvider, EmailSendOptions } from \"./EmailProvider.ts\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\n/**\n * Local email provider configuration atom\n */\nexport const localEmailOptions = $atom({\n name: \"alepha.email.local.options\",\n schema: t.object({\n directory: t.string({\n description: \"Directory path where email files will be stored\",\n }),\n }),\n default: {\n directory: \"node_modules/.alepha/emails\",\n },\n});\n\nexport type LocalEmailProviderOptions = Static<typeof localEmailOptions.schema>;\n\ndeclare module \"alepha\" {\n interface State {\n [localEmailOptions.key]: LocalEmailProviderOptions;\n }\n}\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport class LocalEmailProvider implements EmailProvider {\n protected readonly log = $logger();\n protected readonly fs = $inject(FileSystemProvider);\n protected readonly options = $state(localEmailOptions);\n\n protected get directory(): string {\n return this.options.directory;\n }\n\n protected onStart = $hook({\n on: \"start\",\n handler: async () => {\n try {\n await this.fs.mkdir(this.directory, { recursive: true });\n this.log.info(\"Email directory OK\", {\n at: this.directory,\n });\n } catch (error) {\n const message = `Failed to create email directory: ${error instanceof Error ? error.message : String(error)}`;\n this.log.error(message, { at: this.directory });\n throw new EmailError(\n message,\n error instanceof Error ? error : undefined,\n );\n }\n },\n });\n\n public async send(options: EmailSendOptions): Promise<void> {\n const { to, subject, body } = options;\n\n this.log.debug(\"Sending email to local file\", {\n to,\n subject,\n directory: this.directory,\n });\n\n try {\n const timestamp = new Date().toISOString().replace(/[:.]/g, \"-\");\n for (const recipient of Array.isArray(to) ? to : [to]) {\n const sanitizedEmail = recipient.replace(/[^a-zA-Z0-9@.-]/g, \"_\");\n const filename = `${sanitizedEmail},${timestamp}.eml.json`;\n const filepath = this.fs.join(this.directory, filename);\n\n const content = this.createEmailJson({ to: recipient, subject, body });\n await this.fs.writeFile(filepath, JSON.stringify(content, null, 2));\n\n this.log.info(\"Email saved to local file\", { filepath, to, subject });\n }\n } catch (error) {\n const message = `Failed to save email to local file: ${error instanceof Error ? error.message : String(error)}`;\n this.log.error(message, { to, subject, directory: this.directory });\n throw new EmailError(message, error instanceof Error ? error : undefined);\n }\n }\n\n public createEmailJson(options: {\n to: string;\n subject: string;\n body: string;\n }): { to: string; subject: string; body: string; sentAt: string } {\n return {\n to: options.to,\n subject: options.subject,\n body: options.body,\n sentAt: new Date().toISOString(),\n };\n }\n}\n","import { $module } from \"alepha\";\nimport { $email } from \"./primitives/$email.ts\";\nimport { EmailProvider } from \"./providers/EmailProvider.ts\";\nimport { LocalEmailProvider } from \"./providers/LocalEmailProvider.ts\";\nimport { MemoryEmailProvider } from \"./providers/MemoryEmailProvider.ts\";\n\n// Exports\nexport * from \"./errors/EmailError.ts\";\nexport * from \"./primitives/$email.ts\";\nexport * from \"./providers/EmailProvider.ts\";\nexport * from \"./providers/LocalEmailProvider.ts\";\nexport * from \"./providers/MemoryEmailProvider.ts\";\n\n// Hook declarations\ndeclare module \"alepha\" {\n interface Hooks {\n \"email:sending\": {\n to: string | string[];\n template: string;\n variables: Record<string, unknown>;\n provider: EmailProvider;\n abort(): void;\n };\n \"email:sent\": {\n to: string | string[];\n template: string;\n provider: EmailProvider;\n };\n }\n}\n\n/**\n * Email delivery with template support.\n *\n * **Features:**\n * - Send emails with templates\n * - Multiple recipients\n * - Local file provider for development\n * - Memory provider for testing\n *\n * For SMTP support, use `AlephaEmailSmtp` from `alepha/email/smtp`.\n * For Brevo support, use `AlephaEmailBrevo` from `alepha/email/brevo`.\n *\n * @module alepha.email\n */\nexport const AlephaEmail = $module({\n name: \"alepha.email\",\n primitives: [$email],\n services: [EmailProvider],\n variants: [MemoryEmailProvider, LocalEmailProvider],\n register: (alepha) => {\n if (alepha.isTest()) {\n alepha.with({\n optional: true,\n provide: EmailProvider,\n use: MemoryEmailProvider,\n });\n } else {\n alepha.with({\n optional: true,\n provide: EmailProvider,\n use: LocalEmailProvider,\n });\n }\n },\n});\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../../../src/email/core/providers/EmailProvider.ts","../../../src/email/core/providers/MemoryEmailProvider.ts","../../../src/email/core/primitives/$email.ts","../../../src/email/core/errors/EmailError.ts","../../../src/email/core/providers/LocalEmailProvider.ts","../../../src/email/core/index.ts"],"sourcesContent":["/**\n * Email provider interface.\n *\n * All methods are asynchronous and return promises.\n */\nexport abstract class EmailProvider {\n /**\n * Send an email.\n *\n * @return Promise that resolves when the email is sent.\n */\n public abstract send(options: EmailSendOptions): Promise<void>;\n}\n\nexport type EmailSendOptions = {\n to: string | string[];\n subject: string;\n body: string;\n};\n","import { $logger } from \"alepha/logger\";\nimport type { EmailProvider, EmailSendOptions } from \"./EmailProvider.ts\";\n\nexport interface EmailRecord {\n to: string;\n subject: string;\n body: string;\n sentAt: Date;\n}\n\nexport class MemoryEmailProvider implements EmailProvider {\n protected readonly log = $logger();\n public records: EmailRecord[] = [];\n\n public async send(options: EmailSendOptions): Promise<void> {\n const { to, subject, body } = options;\n this.log.debug(\"Sending email to memory store\", { to, subject });\n\n for (const recipient of Array.isArray(to) ? to : [to]) {\n this.records.push({\n to: recipient,\n subject,\n body,\n sentAt: new Date(),\n });\n }\n }\n\n /**\n * Get the last email sent (for testing purposes).\n */\n public get last(): EmailRecord | undefined {\n return this.records[this.records.length - 1];\n }\n}\n","import {\n AlephaError,\n createPrimitive,\n type InstantiableClass,\n KIND,\n Primitive,\n} from \"alepha\";\nimport type { EmailSendOptions } from \"../providers/EmailProvider.ts\";\nimport { EmailProvider } from \"../providers/EmailProvider.ts\";\nimport { MemoryEmailProvider } from \"../providers/MemoryEmailProvider.ts\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport const $email = (options: EmailPrimitiveOptions = {}) =>\n createPrimitive(EmailPrimitive, options);\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport interface EmailPrimitiveOptions {\n name?: string;\n provider?: InstantiableClass<EmailProvider> | \"memory\";\n}\n\n// ---------------------------------------------------------------------------------------------------------------------\n\n/**\n * Email primitive for sending emails through various providers.\n *\n * Usage:\n * ```typescript\n * class MyService {\n * private readonly welcomeEmail = $email({ name: \"welcome\" });\n *\n * async sendWelcome(userEmail: string, userName: string) {\n * await this.welcomeEmail.send({\n * to: userEmail,\n * subject: \"Welcome!\",\n * body: `<p>Hello ${userName}!</p>`\n * });\n * }\n * }\n * ```\n */\nexport class EmailPrimitive extends Primitive<EmailPrimitiveOptions> {\n protected readonly provider = this.$provider();\n\n public get name() {\n return this.options.name ?? `${this.config.propertyKey}`;\n }\n\n /**\n * Send an email using the configured provider.\n */\n public async send(options: EmailSendOptions): Promise<void> {\n await this.alepha.events.emit(\"email:sending\", {\n to: options.to,\n template: this.name,\n variables: {},\n provider: this.provider,\n abort: () => {\n throw new AlephaError(\"Email sending aborted by hook\");\n },\n });\n\n await this.provider.send(options);\n\n await this.alepha.events.emit(\"email:sent\", {\n to: options.to,\n template: this.name,\n provider: this.provider,\n });\n }\n\n protected $provider(): EmailProvider {\n if (!this.options.provider) {\n return this.alepha.inject(EmailProvider);\n }\n if (this.options.provider === \"memory\") {\n return this.alepha.inject(MemoryEmailProvider);\n }\n return this.alepha.inject(this.options.provider);\n }\n}\n\n// ---------------------------------------------------------------------------------------------------------------------\n\n$email[KIND] = EmailPrimitive;\n","import { AlephaError } from \"alepha\";\n\nexport class EmailError extends AlephaError {\n constructor(message: string, cause?: Error) {\n super(message);\n this.name = \"EmailError\";\n this.cause = cause;\n }\n}\n","import { $atom, $hook, $inject, $state, type Static, t } from \"alepha\";\nimport { $logger } from \"alepha/logger\";\nimport { FileSystemProvider } from \"alepha/system\";\nimport { EmailError } from \"../errors/EmailError.ts\";\nimport type { EmailProvider, EmailSendOptions } from \"./EmailProvider.ts\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\n/**\n * Local email provider configuration atom\n */\nexport const localEmailOptions = $atom({\n name: \"alepha.email.local.options\",\n schema: t.object({\n directory: t.string({\n description: \"Directory path where email files will be stored\",\n }),\n }),\n default: {\n directory: \"node_modules/.alepha/emails\",\n },\n});\n\nexport type LocalEmailProviderOptions = Static<typeof localEmailOptions.schema>;\n\ndeclare module \"alepha\" {\n interface State {\n [localEmailOptions.key]: LocalEmailProviderOptions;\n }\n}\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport class LocalEmailProvider implements EmailProvider {\n protected readonly log = $logger();\n protected readonly fs = $inject(FileSystemProvider);\n protected readonly options = $state(localEmailOptions);\n\n protected get directory(): string {\n return this.options.directory;\n }\n\n protected onStart = $hook({\n on: \"start\",\n handler: async () => {\n try {\n await this.fs.mkdir(this.directory, { recursive: true });\n this.log.info(\"Email directory OK\", {\n at: this.directory,\n });\n } catch (error) {\n const message = `Failed to create email directory: ${error instanceof Error ? error.message : String(error)}`;\n this.log.error(message, { at: this.directory });\n throw new EmailError(\n message,\n error instanceof Error ? error : undefined,\n );\n }\n },\n });\n\n public async send(options: EmailSendOptions): Promise<void> {\n const { to, subject, body } = options;\n\n this.log.debug(\"Sending email to local file\", {\n to,\n subject,\n directory: this.directory,\n });\n\n try {\n const timestamp = new Date().toISOString().replace(/[:.]/g, \"-\");\n for (const recipient of Array.isArray(to) ? to : [to]) {\n const sanitizedEmail = recipient.replace(/[^a-zA-Z0-9@.-]/g, \"_\");\n const filename = `${sanitizedEmail},${timestamp}.eml.json`;\n const filepath = this.fs.join(this.directory, filename);\n\n const content = this.createEmailJson({ to: recipient, subject, body });\n await this.fs.writeFile(filepath, JSON.stringify(content, null, 2));\n\n this.log.info(\"Email saved to local file\", { filepath, to, subject });\n }\n } catch (error) {\n const message = `Failed to save email to local file: ${error instanceof Error ? error.message : String(error)}`;\n this.log.error(message, { to, subject, directory: this.directory });\n throw new EmailError(message, error instanceof Error ? error : undefined);\n }\n }\n\n public createEmailJson(options: {\n to: string;\n subject: string;\n body: string;\n }): { to: string; subject: string; body: string; sentAt: string } {\n return {\n to: options.to,\n subject: options.subject,\n body: options.body,\n sentAt: new Date().toISOString(),\n };\n }\n}\n","import { $module } from \"alepha\";\nimport { $email } from \"./primitives/$email.ts\";\nimport { EmailProvider } from \"./providers/EmailProvider.ts\";\nimport { LocalEmailProvider } from \"./providers/LocalEmailProvider.ts\";\nimport { MemoryEmailProvider } from \"./providers/MemoryEmailProvider.ts\";\n\n// Exports\nexport * from \"./errors/EmailError.ts\";\nexport * from \"./primitives/$email.ts\";\nexport * from \"./providers/EmailProvider.ts\";\nexport * from \"./providers/LocalEmailProvider.ts\";\nexport * from \"./providers/MemoryEmailProvider.ts\";\n\n// Hook declarations\ndeclare module \"alepha\" {\n interface Hooks {\n \"email:sending\": {\n to: string | string[];\n template: string;\n variables: Record<string, unknown>;\n provider: EmailProvider;\n abort(): void;\n };\n \"email:sent\": {\n to: string | string[];\n template: string;\n provider: EmailProvider;\n };\n }\n}\n\n/**\n * Email delivery with template support.\n *\n * **Features:**\n * - Send emails with templates\n * - Multiple recipients\n * - Local file provider for development\n * - Memory provider for testing\n *\n * For SMTP support, use `AlephaEmailSmtp` from `alepha/email/smtp`.\n * For Brevo support, use `AlephaEmailBrevo` from `alepha/email/brevo`.\n *\n * @module alepha.email\n */\nexport const AlephaEmail = $module({\n name: \"alepha.email\",\n primitives: [$email],\n services: [EmailProvider],\n variants: [MemoryEmailProvider, LocalEmailProvider],\n register: (alepha) => {\n if (alepha.isTest()) {\n alepha.with({\n optional: true,\n provide: EmailProvider,\n use: MemoryEmailProvider,\n });\n } else {\n alepha.with({\n optional: true,\n provide: EmailProvider,\n use: LocalEmailProvider,\n });\n }\n },\n});\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.workerd.js","names":[],"sources":["../../../src/email/core/providers/EmailProvider.ts","../../../src/email/core/providers/MemoryEmailProvider.ts","../../../src/email/core/primitives/$email.ts","../../../src/email/core/errors/EmailError.ts","../../../src/email/core/index.workerd.ts"],"sourcesContent":["/**\n * Email provider interface.\n *\n * All methods are asynchronous and return promises.\n */\nexport abstract class EmailProvider {\n /**\n * Send an email.\n *\n * @return Promise that resolves when the email is sent.\n */\n public abstract send(options: EmailSendOptions): Promise<void>;\n}\n\nexport type EmailSendOptions = {\n to: string | string[];\n subject: string;\n body: string;\n};\n","import { $logger } from \"alepha/logger\";\nimport type { EmailProvider, EmailSendOptions } from \"./EmailProvider.ts\";\n\nexport interface EmailRecord {\n to: string;\n subject: string;\n body: string;\n sentAt: Date;\n}\n\nexport class MemoryEmailProvider implements EmailProvider {\n protected readonly log = $logger();\n public records: EmailRecord[] = [];\n\n public async send(options: EmailSendOptions): Promise<void> {\n const { to, subject, body } = options;\n this.log.debug(\"Sending email to memory store\", { to, subject });\n\n for (const recipient of Array.isArray(to) ? to : [to]) {\n this.records.push({\n to: recipient,\n subject,\n body,\n sentAt: new Date(),\n });\n }\n }\n\n /**\n * Get the last email sent (for testing purposes).\n */\n public get last(): EmailRecord | undefined {\n return this.records[this.records.length - 1];\n }\n}\n","import {\n AlephaError,\n createPrimitive,\n type InstantiableClass,\n KIND,\n Primitive,\n} from \"alepha\";\nimport type { EmailSendOptions } from \"../providers/EmailProvider.ts\";\nimport { EmailProvider } from \"../providers/EmailProvider.ts\";\nimport { MemoryEmailProvider } from \"../providers/MemoryEmailProvider.ts\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport const $email = (options: EmailPrimitiveOptions = {}) =>\n createPrimitive(EmailPrimitive, options);\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport interface EmailPrimitiveOptions {\n name?: string;\n provider?: InstantiableClass<EmailProvider> | \"memory\";\n}\n\n// ---------------------------------------------------------------------------------------------------------------------\n\n/**\n * Email primitive for sending emails through various providers.\n *\n * Usage:\n * ```typescript\n * class MyService {\n * private readonly welcomeEmail = $email({ name: \"welcome\" });\n *\n * async sendWelcome(userEmail: string, userName: string) {\n * await this.welcomeEmail.send({\n * to: userEmail,\n * subject: \"Welcome!\",\n * body: `<p>Hello ${userName}!</p>`\n * });\n * }\n * }\n * ```\n */\nexport class EmailPrimitive extends Primitive<EmailPrimitiveOptions> {\n protected readonly provider = this.$provider();\n\n public get name() {\n return this.options.name ?? `${this.config.propertyKey}`;\n }\n\n /**\n * Send an email using the configured provider.\n */\n public async send(options: EmailSendOptions): Promise<void> {\n await this.alepha.events.emit(\"email:sending\", {\n to: options.to,\n template: this.name,\n variables: {},\n provider: this.provider,\n abort: () => {\n throw new AlephaError(\"Email sending aborted by hook\");\n },\n });\n\n await this.provider.send(options);\n\n await this.alepha.events.emit(\"email:sent\", {\n to: options.to,\n template: this.name,\n provider: this.provider,\n });\n }\n\n protected $provider(): EmailProvider {\n if (!this.options.provider) {\n return this.alepha.inject(EmailProvider);\n }\n if (this.options.provider === \"memory\") {\n return this.alepha.inject(MemoryEmailProvider);\n }\n return this.alepha.inject(this.options.provider);\n }\n}\n\n// ---------------------------------------------------------------------------------------------------------------------\n\n$email[KIND] = EmailPrimitive;\n","import { AlephaError } from \"alepha\";\n\nexport class EmailError extends AlephaError {\n constructor(message: string, cause?: Error) {\n super(message);\n this.name = \"EmailError\";\n this.cause = cause;\n }\n}\n","import { $module } from \"alepha\";\nimport { $email } from \"./primitives/$email.ts\";\nimport { EmailProvider } from \"./providers/EmailProvider.ts\";\nimport { MemoryEmailProvider } from \"./providers/MemoryEmailProvider.ts\";\n\n// Exports\nexport * from \"./errors/EmailError.ts\";\nexport * from \"./primitives/$email.ts\";\nexport * from \"./providers/EmailProvider.ts\";\nexport * from \"./providers/MemoryEmailProvider.ts\";\n\n/**\n * Email delivery for Cloudflare Workers.\n *\n * Uses Memory provider by default. For production email delivery,\n * add `AlephaEmailBrevo` from `alepha/email/brevo`.\n *\n * @module alepha.email\n */\nexport const AlephaEmail = $module({\n name: \"alepha.email\",\n primitives: [$email],\n services: [EmailProvider, MemoryEmailProvider],\n register: (alepha) => {\n alepha.with({\n optional: true,\n provide: EmailProvider,\n use: MemoryEmailProvider,\n });\n },\n});\n"],"mappings":";;;;;;;;AAKA,IAAsB,gBAAtB,MAAoC;;;
|
|
1
|
+
{"version":3,"file":"index.workerd.js","names":[],"sources":["../../../src/email/core/providers/EmailProvider.ts","../../../src/email/core/providers/MemoryEmailProvider.ts","../../../src/email/core/primitives/$email.ts","../../../src/email/core/errors/EmailError.ts","../../../src/email/core/index.workerd.ts"],"sourcesContent":["/**\n * Email provider interface.\n *\n * All methods are asynchronous and return promises.\n */\nexport abstract class EmailProvider {\n /**\n * Send an email.\n *\n * @return Promise that resolves when the email is sent.\n */\n public abstract send(options: EmailSendOptions): Promise<void>;\n}\n\nexport type EmailSendOptions = {\n to: string | string[];\n subject: string;\n body: string;\n};\n","import { $logger } from \"alepha/logger\";\nimport type { EmailProvider, EmailSendOptions } from \"./EmailProvider.ts\";\n\nexport interface EmailRecord {\n to: string;\n subject: string;\n body: string;\n sentAt: Date;\n}\n\nexport class MemoryEmailProvider implements EmailProvider {\n protected readonly log = $logger();\n public records: EmailRecord[] = [];\n\n public async send(options: EmailSendOptions): Promise<void> {\n const { to, subject, body } = options;\n this.log.debug(\"Sending email to memory store\", { to, subject });\n\n for (const recipient of Array.isArray(to) ? to : [to]) {\n this.records.push({\n to: recipient,\n subject,\n body,\n sentAt: new Date(),\n });\n }\n }\n\n /**\n * Get the last email sent (for testing purposes).\n */\n public get last(): EmailRecord | undefined {\n return this.records[this.records.length - 1];\n }\n}\n","import {\n AlephaError,\n createPrimitive,\n type InstantiableClass,\n KIND,\n Primitive,\n} from \"alepha\";\nimport type { EmailSendOptions } from \"../providers/EmailProvider.ts\";\nimport { EmailProvider } from \"../providers/EmailProvider.ts\";\nimport { MemoryEmailProvider } from \"../providers/MemoryEmailProvider.ts\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport const $email = (options: EmailPrimitiveOptions = {}) =>\n createPrimitive(EmailPrimitive, options);\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport interface EmailPrimitiveOptions {\n name?: string;\n provider?: InstantiableClass<EmailProvider> | \"memory\";\n}\n\n// ---------------------------------------------------------------------------------------------------------------------\n\n/**\n * Email primitive for sending emails through various providers.\n *\n * Usage:\n * ```typescript\n * class MyService {\n * private readonly welcomeEmail = $email({ name: \"welcome\" });\n *\n * async sendWelcome(userEmail: string, userName: string) {\n * await this.welcomeEmail.send({\n * to: userEmail,\n * subject: \"Welcome!\",\n * body: `<p>Hello ${userName}!</p>`\n * });\n * }\n * }\n * ```\n */\nexport class EmailPrimitive extends Primitive<EmailPrimitiveOptions> {\n protected readonly provider = this.$provider();\n\n public get name() {\n return this.options.name ?? `${this.config.propertyKey}`;\n }\n\n /**\n * Send an email using the configured provider.\n */\n public async send(options: EmailSendOptions): Promise<void> {\n await this.alepha.events.emit(\"email:sending\", {\n to: options.to,\n template: this.name,\n variables: {},\n provider: this.provider,\n abort: () => {\n throw new AlephaError(\"Email sending aborted by hook\");\n },\n });\n\n await this.provider.send(options);\n\n await this.alepha.events.emit(\"email:sent\", {\n to: options.to,\n template: this.name,\n provider: this.provider,\n });\n }\n\n protected $provider(): EmailProvider {\n if (!this.options.provider) {\n return this.alepha.inject(EmailProvider);\n }\n if (this.options.provider === \"memory\") {\n return this.alepha.inject(MemoryEmailProvider);\n }\n return this.alepha.inject(this.options.provider);\n }\n}\n\n// ---------------------------------------------------------------------------------------------------------------------\n\n$email[KIND] = EmailPrimitive;\n","import { AlephaError } from \"alepha\";\n\nexport class EmailError extends AlephaError {\n constructor(message: string, cause?: Error) {\n super(message);\n this.name = \"EmailError\";\n this.cause = cause;\n }\n}\n","import { $module } from \"alepha\";\nimport { $email } from \"./primitives/$email.ts\";\nimport { EmailProvider } from \"./providers/EmailProvider.ts\";\nimport { MemoryEmailProvider } from \"./providers/MemoryEmailProvider.ts\";\n\n// Exports\nexport * from \"./errors/EmailError.ts\";\nexport * from \"./primitives/$email.ts\";\nexport * from \"./providers/EmailProvider.ts\";\nexport * from \"./providers/MemoryEmailProvider.ts\";\n\n/**\n * Email delivery for Cloudflare Workers.\n *\n * Uses Memory provider by default. For production email delivery,\n * add `AlephaEmailBrevo` from `alepha/email/brevo`.\n *\n * @module alepha.email\n */\nexport const AlephaEmail = $module({\n name: \"alepha.email\",\n primitives: [$email],\n services: [EmailProvider, MemoryEmailProvider],\n register: (alepha) => {\n alepha.with({\n optional: true,\n provide: EmailProvider,\n use: MemoryEmailProvider,\n });\n },\n});\n"],"mappings":";;;;;;;;AAKA,IAAsB,gBAAtB,MAAoC,CAOpC;;;ACFA,IAAa,sBAAb,MAA0D;CACxD,MAAyB,QAAQ;CACjC,UAAgC,CAAC;CAEjC,MAAa,KAAK,SAA0C;EAC1D,MAAM,EAAE,IAAI,SAAS,SAAS;EAC9B,KAAK,IAAI,MAAM,iCAAiC;GAAE;GAAI;EAAQ,CAAC;EAE/D,KAAK,MAAM,aAAa,MAAM,QAAQ,EAAE,IAAI,KAAK,CAAC,EAAE,GAClD,KAAK,QAAQ,KAAK;GAChB,IAAI;GACJ;GACA;GACA,wBAAQ,IAAI,KAAK;EACnB,CAAC;CAEL;;;;CAKA,IAAW,OAAgC;EACzC,OAAO,KAAK,QAAQ,KAAK,QAAQ,SAAS;CAC5C;AACF;;;ACrBA,MAAa,UAAU,UAAiC,CAAC,MACvD,gBAAgB,gBAAgB,OAAO;;;;;;;;;;;;;;;;;;;AA6BzC,IAAa,iBAAb,cAAoC,UAAiC;CACnE,WAA8B,KAAK,UAAU;CAE7C,IAAW,OAAO;EAChB,OAAO,KAAK,QAAQ,QAAQ,GAAG,KAAK,OAAO;CAC7C;;;;CAKA,MAAa,KAAK,SAA0C;EAC1D,MAAM,KAAK,OAAO,OAAO,KAAK,iBAAiB;GAC7C,IAAI,QAAQ;GACZ,UAAU,KAAK;GACf,WAAW,CAAC;GACZ,UAAU,KAAK;GACf,aAAa;IACX,MAAM,IAAI,YAAY,+BAA+B;GACvD;EACF,CAAC;EAED,MAAM,KAAK,SAAS,KAAK,OAAO;EAEhC,MAAM,KAAK,OAAO,OAAO,KAAK,cAAc;GAC1C,IAAI,QAAQ;GACZ,UAAU,KAAK;GACf,UAAU,KAAK;EACjB,CAAC;CACH;CAEA,YAAqC;EACnC,IAAI,CAAC,KAAK,QAAQ,UAChB,OAAO,KAAK,OAAO,OAAO,aAAa;EAEzC,IAAI,KAAK,QAAQ,aAAa,UAC5B,OAAO,KAAK,OAAO,OAAO,mBAAmB;EAE/C,OAAO,KAAK,OAAO,OAAO,KAAK,QAAQ,QAAQ;CACjD;AACF;AAIA,OAAO,QAAQ;;;ACpFf,IAAa,aAAb,cAAgC,YAAY;CAC1C,YAAY,SAAiB,OAAe;EAC1C,MAAM,OAAO;EACb,KAAK,OAAO;EACZ,KAAK,QAAQ;CACf;AACF;;;;;;;;;;;ACWA,MAAa,cAAc,QAAQ;CACjC,MAAM;CACN,YAAY,CAAC,MAAM;CACnB,UAAU,CAAC,eAAe,mBAAmB;CAC7C,WAAW,WAAW;EACpB,OAAO,KAAK;GACV,UAAU;GACV,SAAS;GACT,KAAK;EACP,CAAC;CACH;AACF,CAAC"}
|
|
@@ -1,20 +1,17 @@
|
|
|
1
|
-
import * as _$alepha from "alepha";
|
|
2
1
|
import { Static } from "alepha";
|
|
3
2
|
import { EmailProvider, EmailSendOptions } from "alepha/email";
|
|
4
|
-
import * as _$alepha_logger0 from "alepha/logger";
|
|
5
|
-
import * as _$typebox from "typebox";
|
|
6
3
|
import { Transporter } from "nodemailer";
|
|
7
4
|
|
|
8
5
|
//#region ../../src/email/smtp/providers/NodemailerEmailProvider.d.ts
|
|
9
6
|
/**
|
|
10
7
|
* Nodemailer connection pooling and rate limiting options
|
|
11
8
|
*/
|
|
12
|
-
declare const nodemailerEmailOptions:
|
|
13
|
-
pool:
|
|
14
|
-
maxConnections:
|
|
15
|
-
maxMessages:
|
|
16
|
-
rateDelta:
|
|
17
|
-
rateLimit:
|
|
9
|
+
declare const nodemailerEmailOptions: import("alepha").Atom<import("typebox").TObject<{
|
|
10
|
+
pool: import("typebox").TOptional<import("typebox").TBoolean>;
|
|
11
|
+
maxConnections: import("typebox").TOptional<import("typebox").TNumber>;
|
|
12
|
+
maxMessages: import("typebox").TOptional<import("typebox").TNumber>;
|
|
13
|
+
rateDelta: import("typebox").TOptional<import("typebox").TNumber>;
|
|
14
|
+
rateLimit: import("typebox").TOptional<import("typebox").TNumber>;
|
|
18
15
|
}>, "alepha.email.nodemailer.options">;
|
|
19
16
|
type NodemailerEmailProviderOptions = Static<typeof nodemailerEmailOptions.schema>;
|
|
20
17
|
declare module "alepha" {
|
|
@@ -62,7 +59,7 @@ declare class NodemailerEmailProvider implements EmailProvider {
|
|
|
62
59
|
EMAIL_PORT: number;
|
|
63
60
|
EMAIL_SECURE: boolean;
|
|
64
61
|
};
|
|
65
|
-
protected readonly log:
|
|
62
|
+
protected readonly log: import("alepha/logger").Logger;
|
|
66
63
|
protected readonly options: Readonly<{
|
|
67
64
|
pool?: boolean | undefined;
|
|
68
65
|
maxConnections?: number | undefined;
|
|
@@ -88,8 +85,8 @@ declare class NodemailerEmailProvider implements EmailProvider {
|
|
|
88
85
|
* Close the transporter connection.
|
|
89
86
|
*/
|
|
90
87
|
close(): void;
|
|
91
|
-
protected readonly onStart:
|
|
92
|
-
protected readonly onStop:
|
|
88
|
+
protected readonly onStart: import("alepha").HookPrimitive<"start">;
|
|
89
|
+
protected readonly onStop: import("alepha").HookPrimitive<"stop">;
|
|
93
90
|
}
|
|
94
91
|
//#endregion
|
|
95
92
|
//#region ../../src/email/smtp/index.d.ts
|
|
@@ -99,7 +96,7 @@ declare class NodemailerEmailProvider implements EmailProvider {
|
|
|
99
96
|
* @see {@link NodemailerEmailProvider}
|
|
100
97
|
* @module alepha.email.smtp
|
|
101
98
|
*/
|
|
102
|
-
declare const AlephaEmailSmtp:
|
|
99
|
+
declare const AlephaEmailSmtp: import("alepha").Service<import("alepha").Module>;
|
|
103
100
|
//#endregion
|
|
104
101
|
export { AlephaEmailSmtp, NodemailerEmailProvider, NodemailerEmailProviderOptions, nodemailerEmailOptions };
|
|
105
102
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","names":[],"sources":["../../../src/email/smtp/providers/NodemailerEmailProvider.ts","../../../src/email/smtp/index.ts"],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.d.ts","names":[],"sources":["../../../src/email/smtp/providers/NodemailerEmailProvider.ts","../../../src/email/smtp/index.ts"],"mappings":";;;;;;;AAmDA;cAAa,sBAAA,mBAAsB,IAAA,mBAAA,OAAA;;;;;;;KAgCvB,8BAAA,GAAiC,MAAM,QAC1C,sBAAA,CAAuB,MAAA;AAAA;EAAA,UAIpB,KAAA;IAAA,CACP,sBAAA,CAAuB,GAAG,GAAG,8BAAA;EAAA;AAAA;;;;;;;;;;AANlC;;;;;;;;AACsC;AACpC;;;;;;;;;;;AAI8D;AAqChE;cAAa,uBAAA,YAAmC,aAAA;EAAA,mBAC3B,GAAA;;;;;;;;qBACA,GAAA,0BAAG,MAAA;EAAA,mBACH,OAAA,EAAO,QAAA;;;;;;;YAChB,WAAA,EAAa,WAAA;EAAA,cAET,IAAA;EAAA,cAUA,IAAA;EAAA,cAIA,MAAA;EAAA,cAIA,IAAA;EAAA,cAIA,IAAA;EAAA,cAIA,WAAA;EAAA,UAUJ,cAAA,IAAkB,WAAA;EAOf,IAAA,CAAK,OAAA,EAAS,gBAAA,GAAmB,OAAA;EAAA,UAyBpC,iBAAA,IAAqB,WAAA;;;;EAiClB,MAAA,IAAU,OAAA;EArGT;;;EAmHP,KAAA;EAAA,mBAOY,OAAA,mBAAO,aAAA;EAAA,mBAKP,MAAA,mBAAM,aAAA;AAAA;;;;;;;AAhN3B;;cCtCa,eAAA,mBAAe,OAAA,kBAAA,MAAA"}
|