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
package/dist/redis/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":["envSchema"],"sources":["../../src/redis/providers/RedisProvider.ts","../../src/redis/providers/BunRedisProvider.ts","../../src/redis/providers/RedisSubscriberProvider.ts","../../src/redis/providers/BunRedisSubscriberProvider.ts","../../src/redis/providers/NodeRedisProvider.ts","../../src/redis/providers/NodeRedisSubscriberProvider.ts","../../src/redis/index.ts"],"sourcesContent":["/**\n * Abstract Redis provider interface.\n *\n * This abstract class defines the common interface for Redis operations.\n * Implementations include:\n * - {@link NodeRedisProvider} - Uses `@redis/client` for Node.js runtime\n * - {@link BunRedisProvider} - Uses Bun's native `RedisClient` for Bun runtime\n *\n * @example\n * ```ts\n * // Inject the abstract provider - runtime selects the implementation\n * const redis = alepha.inject(RedisProvider);\n *\n * // Use common operations\n * await redis.set(\"key\", \"value\");\n * const value = await redis.get(\"key\");\n * ```\n */\nexport abstract class RedisProvider {\n /**\n * Whether the Redis client is ready to accept commands.\n */\n public abstract readonly isReady: boolean;\n\n /**\n * Connect to the Redis server.\n */\n public abstract connect(): Promise<void>;\n\n /**\n * Close the connection to the Redis server.\n */\n public abstract close(): Promise<void>;\n\n /**\n * Get the value of a key.\n *\n * @param key The key to get.\n * @returns The value as a Buffer, or undefined if the key does not exist.\n */\n public abstract get(key: string): Promise<Buffer | undefined>;\n\n /**\n * Set the value of a key.\n *\n * @param key The key to set.\n * @param value The value to set (Buffer or string).\n * @param options Optional set options (EX, PX, NX, XX, etc.).\n * @returns The value as a Buffer.\n */\n public abstract set(\n key: string,\n value: Buffer | string,\n options?: RedisSetOptions,\n ): Promise<Buffer>;\n\n /**\n * Check if a key exists.\n *\n * @param key The key to check.\n * @returns True if the key exists.\n */\n public abstract has(key: string): Promise<boolean>;\n\n /**\n * Get all keys matching a pattern.\n *\n * @param pattern The glob-style pattern to match.\n * @returns Array of matching key names.\n */\n public abstract keys(pattern: string): Promise<string[]>;\n\n /**\n * Delete one or more keys.\n *\n * @param keys The keys to delete.\n */\n public abstract del(keys: string[]): Promise<void>;\n\n // ---------------------------------------------------------\n // Queue operations (for alepha/queue-redis)\n // ---------------------------------------------------------\n\n /**\n * Push a value to the left (head) of a list.\n *\n * @param key The list key.\n * @param value The value to push.\n */\n public abstract lpush(key: string, value: string): Promise<void>;\n\n /**\n * Pop a value from the right (tail) of a list.\n *\n * @param key The list key.\n * @returns The value, or undefined if the list is empty.\n */\n public abstract rpop(key: string): Promise<string | undefined>;\n\n // ---------------------------------------------------------\n // Pub/Sub operations (for alepha/topic-redis)\n // ---------------------------------------------------------\n\n /**\n * Publish a message to a channel.\n *\n * @param channel The channel name.\n * @param message The message to publish.\n */\n public abstract publish(channel: string, message: string): Promise<void>;\n\n // ---------------------------------------------------------\n // Counter operations\n // ---------------------------------------------------------\n\n /**\n * Increment the integer value of a key by the given amount.\n *\n * If the key does not exist, it is set to 0 before performing the operation.\n * This operation is atomic.\n *\n * @param key The key to increment.\n * @param amount The amount to increment by.\n * @returns The new value after incrementing.\n */\n public abstract incr(key: string, amount: number): Promise<number>;\n}\n\n/**\n * Common Redis SET command options.\n * Compatible with @redis/client SetOptions format.\n */\nexport interface RedisSetOptions {\n /**\n * Set the specified expire time, in seconds.\n */\n EX?: number;\n /**\n * Set the specified expire time, in milliseconds.\n */\n PX?: number;\n /**\n * Set the specified Unix time at which the key will expire, in seconds.\n */\n EXAT?: number;\n /**\n * Set the specified Unix time at which the key will expire, in milliseconds.\n */\n PXAT?: number;\n /**\n * Only set the key if it does not already exist.\n */\n NX?: boolean;\n /**\n * Only set the key if it already exists.\n */\n XX?: boolean;\n /**\n * Retain the time to live associated with the key.\n */\n KEEPTTL?: boolean;\n /**\n * Return the old string stored at key, or nil if key did not exist.\n */\n GET?: boolean;\n /**\n * Alternative expiration format (compatible with @redis/client).\n */\n expiration?: {\n type: \"EX\" | \"PX\" | \"EXAT\" | \"PXAT\" | \"KEEPTTL\";\n value: number;\n };\n /**\n * Alternative condition format (compatible with @redis/client).\n */\n condition?: \"NX\" | \"XX\";\n}\n","import {\n $env,\n $hook,\n $inject,\n Alepha,\n AlephaError,\n type Static,\n t,\n} from \"alepha\";\nimport { $logger } from \"alepha/logger\";\nimport { RedisProvider, type RedisSetOptions } from \"./RedisProvider.ts\";\n\nconst envSchema = t.object({\n REDIS_URL: t.text({\n default: \"redis://localhost:6379\",\n description: \"Redis connection URL\",\n }),\n});\n\ndeclare module \"alepha\" {\n interface Env extends Partial<Static<typeof envSchema>> {}\n}\n\n/**\n * Bun Redis client provider using Bun's native Redis client.\n *\n * This provider uses Bun's built-in `RedisClient` class for Redis connections,\n * which provides excellent performance (7.9x faster than ioredis) on the Bun runtime.\n *\n * @example\n * ```ts\n * // Set REDIS_URL environment variable (default: redis://localhost:6379)\n * // REDIS_URL=redis://:password@myredis.example.com:6379\n *\n * // Or configure programmatically\n * alepha.with({\n * provide: RedisProvider,\n * use: BunRedisProvider,\n * });\n * ```\n */\nexport class BunRedisProvider extends RedisProvider {\n protected readonly log = $logger();\n protected readonly alepha = $inject(Alepha);\n protected readonly env = $env(envSchema);\n protected client?: Bun.RedisClient;\n\n public get publisher(): Bun.RedisClient {\n if (!this.client?.connected) {\n throw new AlephaError(\"Redis client is not ready\");\n }\n\n return this.client;\n }\n\n public override get isReady(): boolean {\n return this.client?.connected ?? false;\n }\n\n protected readonly start = $hook({\n on: \"start\",\n handler: () => this.connect(),\n });\n\n protected readonly stop = $hook({\n on: \"stop\",\n handler: () => this.close(),\n });\n\n /**\n * Connect to the Redis server.\n */\n public override async connect(): Promise<void> {\n // Check if we're running in Bun\n if (!this.alepha.isBun()) {\n throw new AlephaError(\n \"BunRedisProvider requires the Bun runtime. Use NodeRedisProvider for Node.js.\",\n );\n }\n\n this.log.debug(\"Connecting...\");\n\n this.client = new Bun.RedisClient(this.getUrl(), {\n autoReconnect: true,\n enableAutoPipelining: true,\n });\n\n this.client.onconnect = () => {\n this.log.trace(\"Redis connected\");\n };\n\n this.client.onclose = (error) => {\n if (this.alepha.isStarted() && error) {\n this.log.error(\"Redis connection closed\", error);\n }\n };\n\n await this.client.connect();\n\n this.log.info(\"Connection OK\");\n }\n\n /**\n * Close the connection to the Redis server.\n */\n public override async close(): Promise<void> {\n if (this.client) {\n this.log.debug(\"Closing connection...\");\n this.client.close();\n this.client = undefined;\n this.log.info(\"Connection closed\");\n }\n }\n\n /**\n * Create a duplicate connection for pub/sub or other isolated operations.\n */\n public async duplicate(): Promise<Bun.RedisClient> {\n if (typeof Bun === \"undefined\") {\n throw new AlephaError(\"BunRedisProvider requires the Bun runtime.\");\n }\n\n const client = new Bun.RedisClient(this.getUrl(), {\n autoReconnect: true,\n enableAutoPipelining: true,\n });\n\n client.onclose = (error) => {\n if (this.alepha.isStarted() && error) {\n this.log.error(\"Redis duplicate connection closed\", error);\n }\n };\n\n await client.connect();\n\n return client;\n }\n\n public override async get(key: string): Promise<Buffer | undefined> {\n this.log.trace(`Getting key ${key}`);\n const resp = await this.publisher.getBuffer(key);\n\n if (resp === null) {\n return undefined;\n }\n\n return Buffer.from(resp);\n }\n\n public override async set(\n key: string,\n value: Buffer | string,\n options?: RedisSetOptions,\n ): Promise<Buffer> {\n const buf = Buffer.isBuffer(value) ? value : Buffer.from(value, \"utf-8\");\n\n // Build SET command arguments\n const args: string[] = [key, buf.toString(\"binary\")];\n\n // Handle expiration object format (from alepha/cache-redis, alepha/lock-redis)\n if (options?.expiration) {\n if (options.expiration.type === \"KEEPTTL\") {\n args.push(\"KEEPTTL\");\n } else {\n args.push(options.expiration.type, String(options.expiration.value));\n }\n }\n\n // Handle direct expiration properties\n if (options?.EX !== undefined) {\n args.push(\"EX\", String(options.EX));\n }\n if (options?.PX !== undefined) {\n args.push(\"PX\", String(options.PX));\n }\n if (options?.EXAT !== undefined) {\n args.push(\"EXAT\", String(options.EXAT));\n }\n if (options?.PXAT !== undefined) {\n args.push(\"PXAT\", String(options.PXAT));\n }\n if (options?.KEEPTTL) {\n args.push(\"KEEPTTL\");\n }\n\n // Handle condition object format\n if (options?.condition === \"NX\") {\n args.push(\"NX\");\n } else if (options?.condition === \"XX\") {\n args.push(\"XX\");\n }\n\n // Handle direct condition properties\n if (options?.NX) {\n args.push(\"NX\");\n }\n if (options?.XX) {\n args.push(\"XX\");\n }\n if (options?.GET) {\n args.push(\"GET\");\n }\n\n if (args.length === 2) {\n // Simple set without options\n await this.publisher.set(key, buf);\n } else {\n // Set with options via raw command\n await this.publisher.send(\"SET\", args);\n }\n\n return buf;\n }\n\n public override async has(key: string): Promise<boolean> {\n return this.publisher.exists(key);\n }\n\n public override async keys(pattern: string): Promise<string[]> {\n const keys = await this.publisher.send(\"KEYS\", [pattern]);\n if (!Array.isArray(keys)) {\n return [];\n }\n return keys.map((key) =>\n key instanceof Uint8Array ? Buffer.from(key).toString() : String(key),\n );\n }\n\n public override async del(keys: string[]): Promise<void> {\n if (keys.length === 0) {\n return;\n }\n\n await this.publisher.send(\"DEL\", keys);\n }\n\n // ---------------------------------------------------------\n // Queue operations\n // ---------------------------------------------------------\n\n public override async lpush(key: string, value: string): Promise<void> {\n await this.publisher.send(\"LPUSH\", [key, value]);\n }\n\n public override async rpop(key: string): Promise<string | undefined> {\n const value = await this.publisher.send(\"RPOP\", [key]);\n if (value == null) {\n return undefined;\n }\n if (value instanceof Uint8Array) {\n return Buffer.from(value).toString();\n }\n return String(value);\n }\n\n // ---------------------------------------------------------\n // Pub/Sub operations\n // ---------------------------------------------------------\n\n public override async publish(\n channel: string,\n message: string,\n ): Promise<void> {\n await this.publisher.publish(channel, message);\n }\n\n // ---------------------------------------------------------\n // Counter operations\n // ---------------------------------------------------------\n\n public override async incr(key: string, amount: number): Promise<number> {\n const result = await this.publisher.send(\"INCRBY\", [key, String(amount)]);\n return Number(result);\n }\n\n /**\n * Get the Redis connection URL.\n */\n protected getUrl(): string {\n return this.env.REDIS_URL;\n }\n}\n","/**\n * Abstract Redis subscriber provider interface.\n *\n * This abstract class defines the common interface for Redis pub/sub subscriptions.\n * Implementations include:\n * - {@link NodeRedisSubscriberProvider} - Uses `@redis/client` for Node.js runtime\n * - {@link BunRedisSubscriberProvider} - Uses Bun's native `RedisClient` for Bun runtime\n *\n * Redis requires separate connections for pub/sub operations, so this provider\n * creates a dedicated connection for subscriptions.\n *\n * @example\n * ```ts\n * // Inject the abstract provider - runtime selects the implementation\n * const subscriber = alepha.inject(RedisSubscriberProvider);\n *\n * // Subscribe to a channel\n * await subscriber.subscribe(\"my-channel\", (message, channel) => {\n * console.log(`Received: ${message} on ${channel}`);\n * });\n * ```\n */\nexport abstract class RedisSubscriberProvider {\n /**\n * Whether the Redis subscriber client is ready to accept commands.\n */\n public abstract readonly isReady: boolean;\n\n /**\n * Connect to the Redis server for subscriptions.\n */\n public abstract connect(): Promise<void>;\n\n /**\n * Close the subscriber connection.\n */\n public abstract close(): Promise<void>;\n\n /**\n * Subscribe to a channel.\n *\n * @param channel The channel name.\n * @param callback The callback to invoke when a message is received.\n */\n public abstract subscribe(\n channel: string,\n callback: SubscribeCallback,\n ): Promise<void>;\n\n /**\n * Unsubscribe from a channel.\n *\n * @param channel The channel name.\n * @param callback Optional specific callback to remove.\n */\n public abstract unsubscribe(\n channel: string,\n callback?: SubscribeCallback,\n ): Promise<void>;\n}\n\n/**\n * Callback for subscription messages.\n */\nexport type SubscribeCallback = (message: string, channel: string) => void;\n","import { $hook, $inject, Alepha, AlephaError } from \"alepha\";\nimport { $logger } from \"alepha/logger\";\nimport { BunRedisProvider } from \"./BunRedisProvider.ts\";\nimport {\n RedisSubscriberProvider,\n type SubscribeCallback,\n} from \"./RedisSubscriberProvider.ts\";\n\n/**\n * Bun Redis subscriber provider for pub/sub operations.\n *\n * This provider creates a dedicated Redis connection for subscriptions,\n * as Redis requires separate connections for pub/sub operations.\n *\n * @example\n * ```ts\n * const subscriber = alepha.inject(RedisSubscriberProvider);\n * await subscriber.subscribe(\"channel\", (message, channel) => {\n * console.log(`Received: ${message} on ${channel}`);\n * });\n * ```\n */\nexport class BunRedisSubscriberProvider extends RedisSubscriberProvider {\n protected readonly log = $logger();\n protected readonly alepha = $inject(Alepha);\n protected readonly redisProvider = $inject(BunRedisProvider);\n protected client?: Bun.RedisClient;\n\n public get subscriber(): Bun.RedisClient {\n if (!this.client?.connected) {\n throw new AlephaError(\"Redis subscriber client is not ready\");\n }\n\n return this.client;\n }\n\n public override get isReady(): boolean {\n return this.client?.connected ?? false;\n }\n\n protected readonly start = $hook({\n on: \"start\",\n handler: () => this.connect(),\n });\n\n protected readonly stop = $hook({\n on: \"stop\",\n handler: () => this.close(),\n });\n\n /**\n * Connect to the Redis server for subscriptions.\n */\n public override async connect(): Promise<void> {\n this.log.debug(\"Connecting subscriber...\");\n this.client = await this.redisProvider.duplicate();\n this.log.info(\"Subscriber connection OK\");\n }\n\n /**\n * Close the subscriber connection.\n */\n public override async close(): Promise<void> {\n if (this.client) {\n this.log.debug(\"Closing subscriber connection...\");\n this.client.close();\n this.client = undefined;\n this.log.info(\"Subscriber connection closed\");\n }\n }\n\n public override async subscribe(\n channel: string,\n callback: SubscribeCallback,\n ): Promise<void> {\n await this.subscriber.subscribe(channel, (message, ch) => {\n // Bun's callback provides Buffer or string, normalize to string\n const msg =\n typeof message === \"object\" && message !== null\n ? Buffer.from(message as Uint8Array).toString()\n : String(message);\n callback(msg, ch);\n });\n }\n\n public override async unsubscribe(\n channel: string,\n _callback?: SubscribeCallback,\n ): Promise<void> {\n // Bun's unsubscribe doesn't support callback filtering\n await this.subscriber.unsubscribe(channel);\n }\n}\n","import {\n createClient,\n RESP_TYPES,\n type RedisClientType,\n type SetOptions,\n} from \"@redis/client\";\nimport {\n $env,\n $hook,\n $inject,\n Alepha,\n AlephaError,\n type Static,\n t,\n} from \"alepha\";\nimport { $logger } from \"alepha/logger\";\nimport { RedisProvider, type RedisSetOptions } from \"./RedisProvider.ts\";\n\nconst envSchema = t.object({\n REDIS_URL: t.text({\n default: \"redis://localhost:6379\",\n description: \"Redis connection URL\",\n }),\n});\n\ndeclare module \"alepha\" {\n interface Env extends Partial<Static<typeof envSchema>> {}\n}\n\nexport type NodeRedisClient = RedisClientType<\n {},\n {},\n {},\n 3,\n { 36: BufferConstructor }\n>;\nexport type NodeRedisClientOptions = Parameters<typeof createClient>[0];\n\n/**\n * Node.js Redis client provider using `@redis/client`.\n *\n * This provider uses the official Redis client for Node.js runtime.\n *\n * @example\n * ```ts\n * // Set REDIS_URL environment variable (default: redis://localhost:6379)\n * // REDIS_URL=redis://:password@myredis.example.com:6379\n *\n * // Or configure programmatically\n * alepha.with({\n * provide: RedisProvider,\n * use: NodeRedisProvider,\n * });\n * ```\n */\nexport class NodeRedisProvider extends RedisProvider {\n protected readonly log = $logger();\n protected readonly alepha = $inject(Alepha);\n protected readonly env = $env(envSchema);\n protected readonly client = this.createClient();\n\n public get publisher(): NodeRedisClient {\n if (!this.client.isReady) {\n throw new AlephaError(\"Redis client is not ready\");\n }\n\n return this.client;\n }\n\n public override get isReady(): boolean {\n return this.client.isReady;\n }\n\n protected readonly start = $hook({\n on: \"start\",\n handler: () => this.connect(),\n });\n\n protected readonly stop = $hook({\n on: \"stop\",\n handler: () => this.close(),\n });\n\n /**\n * Connect to the Redis server.\n */\n public override async connect(): Promise<void> {\n this.log.debug(\"Connecting...\");\n await this.client.connect();\n this.log.info(\"Connection OK\");\n }\n\n /**\n * Close the connection to the Redis server.\n */\n public override async close(): Promise<void> {\n this.log.debug(\"Closing connection...\");\n await this.client.close();\n this.log.info(\"Connection closed\");\n }\n\n public duplicate(options?: Partial<NodeRedisClientOptions>): NodeRedisClient {\n return this.client\n .duplicate({\n ...options,\n RESP: 3,\n })\n .withTypeMapping({\n [RESP_TYPES.BLOB_STRING]: Buffer,\n });\n }\n\n public override async get(key: string): Promise<Buffer | undefined> {\n this.log.trace(`Getting key ${key}`);\n const resp = await this.publisher.get(key);\n\n if (resp === null) {\n return undefined;\n }\n\n return Buffer.from(resp);\n }\n\n public override async set(\n key: string,\n value: Buffer | string,\n options?: RedisSetOptions,\n ): Promise<Buffer> {\n const buf = Buffer.isBuffer(value) ? value : Buffer.from(value, \"utf-8\");\n\n // Convert RedisSetOptions to @redis/client SetOptions\n const setOptions: SetOptions = {};\n\n // Handle expiration object format (from alepha/cache-redis, alepha/lock-redis)\n if (options?.expiration) {\n if (options.expiration.type === \"KEEPTTL\") {\n setOptions.KEEPTTL = true;\n } else {\n setOptions[options.expiration.type] = options.expiration.value;\n }\n }\n\n // Handle direct expiration properties\n if (options?.EX !== undefined) {\n setOptions.EX = options.EX;\n }\n if (options?.PX !== undefined) {\n setOptions.PX = options.PX;\n }\n if (options?.EXAT !== undefined) {\n setOptions.EXAT = options.EXAT;\n }\n if (options?.PXAT !== undefined) {\n setOptions.PXAT = options.PXAT;\n }\n if (options?.KEEPTTL) {\n setOptions.KEEPTTL = true;\n }\n\n // Handle condition object format\n if (options?.condition === \"NX\") {\n setOptions.NX = true;\n } else if (options?.condition === \"XX\") {\n setOptions.XX = true;\n }\n\n // Handle direct condition properties\n if (options?.NX) {\n setOptions.NX = true;\n }\n if (options?.XX) {\n setOptions.XX = true;\n }\n if (options?.GET) {\n setOptions.GET = true;\n }\n\n const resp = await this.publisher.set(\n key,\n buf,\n Object.keys(setOptions).length > 0 ? setOptions : undefined,\n );\n\n if (resp === \"OK\" || !resp) {\n return buf;\n }\n\n return Buffer.from(resp);\n }\n\n public override async has(key: string): Promise<boolean> {\n const resp = await this.publisher.exists(key);\n return resp > 0;\n }\n\n public override async keys(pattern: string): Promise<string[]> {\n const keys = await this.publisher.keys(pattern);\n return keys.map((key) => key.toString());\n }\n\n public override async del(keys: string[]): Promise<void> {\n if (keys.length === 0) {\n return;\n }\n\n await this.publisher.del(keys);\n }\n\n // ---------------------------------------------------------\n // Queue operations\n // ---------------------------------------------------------\n\n public override async lpush(key: string, value: string): Promise<void> {\n await this.publisher.LPUSH(key, value);\n }\n\n public override async rpop(key: string): Promise<string | undefined> {\n const value = await this.publisher.RPOP(key);\n if (value == null) {\n return undefined;\n }\n return String(value);\n }\n\n // ---------------------------------------------------------\n // Pub/Sub operations\n // ---------------------------------------------------------\n\n public override async publish(\n channel: string,\n message: string,\n ): Promise<void> {\n await this.publisher.publish(channel, message);\n }\n\n // ---------------------------------------------------------\n // Counter operations\n // ---------------------------------------------------------\n\n public override async incr(key: string, amount: number): Promise<number> {\n return this.publisher.INCRBY(key, amount);\n }\n\n /**\n * Get the Redis connection URL.\n */\n protected getUrl(): string {\n return this.env.REDIS_URL;\n }\n\n /**\n * Redis client factory method.\n */\n protected createClient(): NodeRedisClient {\n const client = createClient({\n url: this.getUrl(),\n RESP: 3,\n }).withTypeMapping({\n [RESP_TYPES.BLOB_STRING]: Buffer,\n });\n\n client.on(\"error\", (error) => {\n if (this.alepha.isStarted()) {\n this.log.error(error);\n }\n });\n\n return client;\n }\n}\n","import { $hook, $inject, Alepha, AlephaError } from \"alepha\";\nimport { $logger } from \"alepha/logger\";\nimport {\n type NodeRedisClient,\n NodeRedisProvider,\n} from \"./NodeRedisProvider.ts\";\nimport {\n RedisSubscriberProvider,\n type SubscribeCallback,\n} from \"./RedisSubscriberProvider.ts\";\n\n/**\n * Node.js Redis subscriber provider using `@redis/client`.\n *\n * This provider creates a dedicated Redis connection for subscriptions,\n * as Redis requires separate connections for pub/sub operations.\n *\n * @example\n * ```ts\n * const subscriber = alepha.inject(RedisSubscriberProvider);\n * await subscriber.subscribe(\"channel\", (message, channel) => {\n * console.log(`Received: ${message} on ${channel}`);\n * });\n * ```\n */\nexport class NodeRedisSubscriberProvider extends RedisSubscriberProvider {\n protected readonly log = $logger();\n protected readonly alepha = $inject(Alepha);\n protected readonly redisProvider = $inject(NodeRedisProvider);\n protected readonly client: NodeRedisClient = this.createClient();\n\n public get subscriber(): NodeRedisClient {\n if (!this.client.isReady) {\n throw new AlephaError(\"Redis subscriber client is not ready\");\n }\n\n return this.client;\n }\n\n public override get isReady(): boolean {\n return this.client.isReady;\n }\n\n protected readonly start = $hook({\n on: \"start\",\n handler: () => this.connect(),\n });\n\n protected readonly stop = $hook({\n on: \"stop\",\n handler: () => this.close(),\n });\n\n public override async connect(): Promise<void> {\n this.log.debug(\"Connecting subscriber...\");\n await this.client.connect();\n this.log.info(\"Subscriber connection OK\");\n }\n\n public override async close(): Promise<void> {\n if (!this.client.isReady) {\n this.log.debug(\"Subscriber client not ready, skipping close\");\n return;\n }\n this.log.debug(\"Closing subscriber connection...\");\n await this.client.close();\n this.log.info(\"Subscriber connection closed\");\n }\n\n public override async subscribe(\n channel: string,\n callback: SubscribeCallback,\n ): Promise<void> {\n await this.subscriber.subscribe(channel, callback);\n }\n\n public override async unsubscribe(\n channel: string,\n callback?: SubscribeCallback,\n ): Promise<void> {\n await this.subscriber.unsubscribe(channel, callback);\n }\n\n /**\n * Redis subscriber client factory method.\n */\n protected createClient(): NodeRedisClient {\n const client = this.redisProvider.duplicate();\n\n client.on(\"error\", (error) => {\n if (this.alepha.isStarted()) {\n this.log.error(error);\n }\n });\n\n return client;\n }\n}\n","import { $module, type Alepha } from \"alepha\";\nimport { BunRedisProvider } from \"./providers/BunRedisProvider.ts\";\nimport { BunRedisSubscriberProvider } from \"./providers/BunRedisSubscriberProvider.ts\";\nimport { NodeRedisProvider } from \"./providers/NodeRedisProvider.ts\";\nimport { NodeRedisSubscriberProvider } from \"./providers/NodeRedisSubscriberProvider.ts\";\nimport { RedisProvider } from \"./providers/RedisProvider.ts\";\nimport { RedisSubscriberProvider } from \"./providers/RedisSubscriberProvider.ts\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport * from \"./providers/BunRedisProvider.ts\";\nexport * from \"./providers/BunRedisSubscriberProvider.ts\";\nexport * from \"./providers/NodeRedisProvider.ts\";\nexport * from \"./providers/NodeRedisSubscriberProvider.ts\";\nexport * from \"./providers/RedisProvider.ts\";\nexport * from \"./providers/RedisSubscriberProvider.ts\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\n/**\n * Redis client wrapper.\n *\n * **Features:**\n * - Connection pooling\n * - Automatic reconnection\n * - Command pipelining\n * - Pub/sub support\n *\n * @module alepha.redis\n */\nexport const AlephaRedis = $module({\n name: \"alepha.redis\",\n services: [RedisProvider, RedisSubscriberProvider],\n variants: [\n NodeRedisProvider,\n NodeRedisSubscriberProvider,\n BunRedisProvider,\n BunRedisSubscriberProvider,\n ],\n register: (alepha: Alepha) => {\n if (alepha.isBun()) {\n alepha\n .with({\n provide: RedisProvider,\n use: BunRedisProvider,\n })\n .with({\n provide: RedisSubscriberProvider,\n use: BunRedisSubscriberProvider,\n });\n } else {\n alepha\n .with({\n provide: RedisProvider,\n use: NodeRedisProvider,\n })\n .with({\n provide: RedisSubscriberProvider,\n use: NodeRedisSubscriberProvider,\n });\n }\n },\n});\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","names":["envSchema"],"sources":["../../src/redis/providers/RedisProvider.ts","../../src/redis/providers/BunRedisProvider.ts","../../src/redis/providers/RedisSubscriberProvider.ts","../../src/redis/providers/BunRedisSubscriberProvider.ts","../../src/redis/providers/NodeRedisProvider.ts","../../src/redis/providers/NodeRedisSubscriberProvider.ts","../../src/redis/index.ts"],"sourcesContent":["/**\n * Abstract Redis provider interface.\n *\n * This abstract class defines the common interface for Redis operations.\n * Implementations include:\n * - {@link NodeRedisProvider} - Uses `@redis/client` for Node.js runtime\n * - {@link BunRedisProvider} - Uses Bun's native `RedisClient` for Bun runtime\n *\n * @example\n * ```ts\n * // Inject the abstract provider - runtime selects the implementation\n * const redis = alepha.inject(RedisProvider);\n *\n * // Use common operations\n * await redis.set(\"key\", \"value\");\n * const value = await redis.get(\"key\");\n * ```\n */\nexport abstract class RedisProvider {\n /**\n * Whether the Redis client is ready to accept commands.\n */\n public abstract readonly isReady: boolean;\n\n /**\n * Connect to the Redis server.\n */\n public abstract connect(): Promise<void>;\n\n /**\n * Close the connection to the Redis server.\n */\n public abstract close(): Promise<void>;\n\n /**\n * Get the value of a key.\n *\n * @param key The key to get.\n * @returns The value as a Buffer, or undefined if the key does not exist.\n */\n public abstract get(key: string): Promise<Buffer | undefined>;\n\n /**\n * Set the value of a key.\n *\n * @param key The key to set.\n * @param value The value to set (Buffer or string).\n * @param options Optional set options (EX, PX, NX, XX, etc.).\n * @returns The value as a Buffer.\n */\n public abstract set(\n key: string,\n value: Buffer | string,\n options?: RedisSetOptions,\n ): Promise<Buffer>;\n\n /**\n * Check if a key exists.\n *\n * @param key The key to check.\n * @returns True if the key exists.\n */\n public abstract has(key: string): Promise<boolean>;\n\n /**\n * Get all keys matching a pattern.\n *\n * @param pattern The glob-style pattern to match.\n * @returns Array of matching key names.\n */\n public abstract keys(pattern: string): Promise<string[]>;\n\n /**\n * Delete one or more keys.\n *\n * @param keys The keys to delete.\n */\n public abstract del(keys: string[]): Promise<void>;\n\n // ---------------------------------------------------------\n // Queue operations (for alepha/queue-redis)\n // ---------------------------------------------------------\n\n /**\n * Push a value to the left (head) of a list.\n *\n * @param key The list key.\n * @param value The value to push.\n */\n public abstract lpush(key: string, value: string): Promise<void>;\n\n /**\n * Pop a value from the right (tail) of a list.\n *\n * @param key The list key.\n * @returns The value, or undefined if the list is empty.\n */\n public abstract rpop(key: string): Promise<string | undefined>;\n\n // ---------------------------------------------------------\n // Pub/Sub operations (for alepha/topic-redis)\n // ---------------------------------------------------------\n\n /**\n * Publish a message to a channel.\n *\n * @param channel The channel name.\n * @param message The message to publish.\n */\n public abstract publish(channel: string, message: string): Promise<void>;\n\n // ---------------------------------------------------------\n // Counter operations\n // ---------------------------------------------------------\n\n /**\n * Increment the integer value of a key by the given amount.\n *\n * If the key does not exist, it is set to 0 before performing the operation.\n * This operation is atomic.\n *\n * @param key The key to increment.\n * @param amount The amount to increment by.\n * @returns The new value after incrementing.\n */\n public abstract incr(key: string, amount: number): Promise<number>;\n}\n\n/**\n * Common Redis SET command options.\n * Compatible with @redis/client SetOptions format.\n */\nexport interface RedisSetOptions {\n /**\n * Set the specified expire time, in seconds.\n */\n EX?: number;\n /**\n * Set the specified expire time, in milliseconds.\n */\n PX?: number;\n /**\n * Set the specified Unix time at which the key will expire, in seconds.\n */\n EXAT?: number;\n /**\n * Set the specified Unix time at which the key will expire, in milliseconds.\n */\n PXAT?: number;\n /**\n * Only set the key if it does not already exist.\n */\n NX?: boolean;\n /**\n * Only set the key if it already exists.\n */\n XX?: boolean;\n /**\n * Retain the time to live associated with the key.\n */\n KEEPTTL?: boolean;\n /**\n * Return the old string stored at key, or nil if key did not exist.\n */\n GET?: boolean;\n /**\n * Alternative expiration format (compatible with @redis/client).\n */\n expiration?: {\n type: \"EX\" | \"PX\" | \"EXAT\" | \"PXAT\" | \"KEEPTTL\";\n value: number;\n };\n /**\n * Alternative condition format (compatible with @redis/client).\n */\n condition?: \"NX\" | \"XX\";\n}\n","import {\n $env,\n $hook,\n $inject,\n Alepha,\n AlephaError,\n type Static,\n t,\n} from \"alepha\";\nimport { $logger } from \"alepha/logger\";\nimport { RedisProvider, type RedisSetOptions } from \"./RedisProvider.ts\";\n\nconst envSchema = t.object({\n REDIS_URL: t.text({\n default: \"redis://localhost:6379\",\n description: \"Redis connection URL\",\n }),\n});\n\ndeclare module \"alepha\" {\n interface Env extends Partial<Static<typeof envSchema>> {}\n}\n\n/**\n * Bun Redis client provider using Bun's native Redis client.\n *\n * This provider uses Bun's built-in `RedisClient` class for Redis connections,\n * which provides excellent performance (7.9x faster than ioredis) on the Bun runtime.\n *\n * @example\n * ```ts\n * // Set REDIS_URL environment variable (default: redis://localhost:6379)\n * // REDIS_URL=redis://:password@myredis.example.com:6379\n *\n * // Or configure programmatically\n * alepha.with({\n * provide: RedisProvider,\n * use: BunRedisProvider,\n * });\n * ```\n */\nexport class BunRedisProvider extends RedisProvider {\n protected readonly log = $logger();\n protected readonly alepha = $inject(Alepha);\n protected readonly env = $env(envSchema);\n protected client?: Bun.RedisClient;\n\n public get publisher(): Bun.RedisClient {\n if (!this.client?.connected) {\n throw new AlephaError(\"Redis client is not ready\");\n }\n\n return this.client;\n }\n\n public override get isReady(): boolean {\n return this.client?.connected ?? false;\n }\n\n protected readonly start = $hook({\n on: \"start\",\n handler: () => this.connect(),\n });\n\n protected readonly stop = $hook({\n on: \"stop\",\n handler: () => this.close(),\n });\n\n /**\n * Connect to the Redis server.\n */\n public override async connect(): Promise<void> {\n // Check if we're running in Bun\n if (!this.alepha.isBun()) {\n throw new AlephaError(\n \"BunRedisProvider requires the Bun runtime. Use NodeRedisProvider for Node.js.\",\n );\n }\n\n this.log.debug(\"Connecting...\");\n\n this.client = new Bun.RedisClient(this.getUrl(), {\n autoReconnect: true,\n enableAutoPipelining: true,\n });\n\n this.client.onconnect = () => {\n this.log.trace(\"Redis connected\");\n };\n\n this.client.onclose = (error) => {\n if (this.alepha.isStarted() && error) {\n this.log.error(\"Redis connection closed\", error);\n }\n };\n\n await this.client.connect();\n\n this.log.info(\"Connection OK\");\n }\n\n /**\n * Close the connection to the Redis server.\n */\n public override async close(): Promise<void> {\n if (this.client) {\n this.log.debug(\"Closing connection...\");\n this.client.close();\n this.client = undefined;\n this.log.info(\"Connection closed\");\n }\n }\n\n /**\n * Create a duplicate connection for pub/sub or other isolated operations.\n */\n public async duplicate(): Promise<Bun.RedisClient> {\n if (typeof Bun === \"undefined\") {\n throw new AlephaError(\"BunRedisProvider requires the Bun runtime.\");\n }\n\n const client = new Bun.RedisClient(this.getUrl(), {\n autoReconnect: true,\n enableAutoPipelining: true,\n });\n\n client.onclose = (error) => {\n if (this.alepha.isStarted() && error) {\n this.log.error(\"Redis duplicate connection closed\", error);\n }\n };\n\n await client.connect();\n\n return client;\n }\n\n public override async get(key: string): Promise<Buffer | undefined> {\n this.log.trace(`Getting key ${key}`);\n const resp = await this.publisher.getBuffer(key);\n\n if (resp === null) {\n return undefined;\n }\n\n return Buffer.from(resp);\n }\n\n public override async set(\n key: string,\n value: Buffer | string,\n options?: RedisSetOptions,\n ): Promise<Buffer> {\n const buf = Buffer.isBuffer(value) ? value : Buffer.from(value, \"utf-8\");\n\n // Build SET command arguments\n const args: string[] = [key, buf.toString(\"binary\")];\n\n // Handle expiration object format (from alepha/cache-redis, alepha/lock-redis)\n if (options?.expiration) {\n if (options.expiration.type === \"KEEPTTL\") {\n args.push(\"KEEPTTL\");\n } else {\n args.push(options.expiration.type, String(options.expiration.value));\n }\n }\n\n // Handle direct expiration properties\n if (options?.EX !== undefined) {\n args.push(\"EX\", String(options.EX));\n }\n if (options?.PX !== undefined) {\n args.push(\"PX\", String(options.PX));\n }\n if (options?.EXAT !== undefined) {\n args.push(\"EXAT\", String(options.EXAT));\n }\n if (options?.PXAT !== undefined) {\n args.push(\"PXAT\", String(options.PXAT));\n }\n if (options?.KEEPTTL) {\n args.push(\"KEEPTTL\");\n }\n\n // Handle condition object format\n if (options?.condition === \"NX\") {\n args.push(\"NX\");\n } else if (options?.condition === \"XX\") {\n args.push(\"XX\");\n }\n\n // Handle direct condition properties\n if (options?.NX) {\n args.push(\"NX\");\n }\n if (options?.XX) {\n args.push(\"XX\");\n }\n if (options?.GET) {\n args.push(\"GET\");\n }\n\n if (args.length === 2) {\n // Simple set without options\n await this.publisher.set(key, buf);\n } else {\n // Set with options via raw command\n await this.publisher.send(\"SET\", args);\n }\n\n return buf;\n }\n\n public override async has(key: string): Promise<boolean> {\n return this.publisher.exists(key);\n }\n\n public override async keys(pattern: string): Promise<string[]> {\n const keys = await this.publisher.send(\"KEYS\", [pattern]);\n if (!Array.isArray(keys)) {\n return [];\n }\n return keys.map((key) =>\n key instanceof Uint8Array ? Buffer.from(key).toString() : String(key),\n );\n }\n\n public override async del(keys: string[]): Promise<void> {\n if (keys.length === 0) {\n return;\n }\n\n await this.publisher.send(\"DEL\", keys);\n }\n\n // ---------------------------------------------------------\n // Queue operations\n // ---------------------------------------------------------\n\n public override async lpush(key: string, value: string): Promise<void> {\n await this.publisher.send(\"LPUSH\", [key, value]);\n }\n\n public override async rpop(key: string): Promise<string | undefined> {\n const value = await this.publisher.send(\"RPOP\", [key]);\n if (value == null) {\n return undefined;\n }\n if (value instanceof Uint8Array) {\n return Buffer.from(value).toString();\n }\n return String(value);\n }\n\n // ---------------------------------------------------------\n // Pub/Sub operations\n // ---------------------------------------------------------\n\n public override async publish(\n channel: string,\n message: string,\n ): Promise<void> {\n await this.publisher.publish(channel, message);\n }\n\n // ---------------------------------------------------------\n // Counter operations\n // ---------------------------------------------------------\n\n public override async incr(key: string, amount: number): Promise<number> {\n const result = await this.publisher.send(\"INCRBY\", [key, String(amount)]);\n return Number(result);\n }\n\n /**\n * Get the Redis connection URL.\n */\n protected getUrl(): string {\n return this.env.REDIS_URL;\n }\n}\n","/**\n * Abstract Redis subscriber provider interface.\n *\n * This abstract class defines the common interface for Redis pub/sub subscriptions.\n * Implementations include:\n * - {@link NodeRedisSubscriberProvider} - Uses `@redis/client` for Node.js runtime\n * - {@link BunRedisSubscriberProvider} - Uses Bun's native `RedisClient` for Bun runtime\n *\n * Redis requires separate connections for pub/sub operations, so this provider\n * creates a dedicated connection for subscriptions.\n *\n * @example\n * ```ts\n * // Inject the abstract provider - runtime selects the implementation\n * const subscriber = alepha.inject(RedisSubscriberProvider);\n *\n * // Subscribe to a channel\n * await subscriber.subscribe(\"my-channel\", (message, channel) => {\n * console.log(`Received: ${message} on ${channel}`);\n * });\n * ```\n */\nexport abstract class RedisSubscriberProvider {\n /**\n * Whether the Redis subscriber client is ready to accept commands.\n */\n public abstract readonly isReady: boolean;\n\n /**\n * Connect to the Redis server for subscriptions.\n */\n public abstract connect(): Promise<void>;\n\n /**\n * Close the subscriber connection.\n */\n public abstract close(): Promise<void>;\n\n /**\n * Subscribe to a channel.\n *\n * @param channel The channel name.\n * @param callback The callback to invoke when a message is received.\n */\n public abstract subscribe(\n channel: string,\n callback: SubscribeCallback,\n ): Promise<void>;\n\n /**\n * Unsubscribe from a channel.\n *\n * @param channel The channel name.\n * @param callback Optional specific callback to remove.\n */\n public abstract unsubscribe(\n channel: string,\n callback?: SubscribeCallback,\n ): Promise<void>;\n}\n\n/**\n * Callback for subscription messages.\n */\nexport type SubscribeCallback = (message: string, channel: string) => void;\n","import { $hook, $inject, Alepha, AlephaError } from \"alepha\";\nimport { $logger } from \"alepha/logger\";\nimport { BunRedisProvider } from \"./BunRedisProvider.ts\";\nimport {\n RedisSubscriberProvider,\n type SubscribeCallback,\n} from \"./RedisSubscriberProvider.ts\";\n\n/**\n * Bun Redis subscriber provider for pub/sub operations.\n *\n * This provider creates a dedicated Redis connection for subscriptions,\n * as Redis requires separate connections for pub/sub operations.\n *\n * @example\n * ```ts\n * const subscriber = alepha.inject(RedisSubscriberProvider);\n * await subscriber.subscribe(\"channel\", (message, channel) => {\n * console.log(`Received: ${message} on ${channel}`);\n * });\n * ```\n */\nexport class BunRedisSubscriberProvider extends RedisSubscriberProvider {\n protected readonly log = $logger();\n protected readonly alepha = $inject(Alepha);\n protected readonly redisProvider = $inject(BunRedisProvider);\n protected client?: Bun.RedisClient;\n\n public get subscriber(): Bun.RedisClient {\n if (!this.client?.connected) {\n throw new AlephaError(\"Redis subscriber client is not ready\");\n }\n\n return this.client;\n }\n\n public override get isReady(): boolean {\n return this.client?.connected ?? false;\n }\n\n protected readonly start = $hook({\n on: \"start\",\n handler: () => this.connect(),\n });\n\n protected readonly stop = $hook({\n on: \"stop\",\n handler: () => this.close(),\n });\n\n /**\n * Connect to the Redis server for subscriptions.\n */\n public override async connect(): Promise<void> {\n this.log.debug(\"Connecting subscriber...\");\n this.client = await this.redisProvider.duplicate();\n this.log.info(\"Subscriber connection OK\");\n }\n\n /**\n * Close the subscriber connection.\n */\n public override async close(): Promise<void> {\n if (this.client) {\n this.log.debug(\"Closing subscriber connection...\");\n this.client.close();\n this.client = undefined;\n this.log.info(\"Subscriber connection closed\");\n }\n }\n\n public override async subscribe(\n channel: string,\n callback: SubscribeCallback,\n ): Promise<void> {\n await this.subscriber.subscribe(channel, (message, ch) => {\n // Bun's callback provides Buffer or string, normalize to string\n const msg =\n typeof message === \"object\" && message !== null\n ? Buffer.from(message as Uint8Array).toString()\n : String(message);\n callback(msg, ch);\n });\n }\n\n public override async unsubscribe(\n channel: string,\n _callback?: SubscribeCallback,\n ): Promise<void> {\n // Bun's unsubscribe doesn't support callback filtering\n await this.subscriber.unsubscribe(channel);\n }\n}\n","import {\n createClient,\n RESP_TYPES,\n type RedisClientType,\n type SetOptions,\n} from \"@redis/client\";\nimport {\n $env,\n $hook,\n $inject,\n Alepha,\n AlephaError,\n type Static,\n t,\n} from \"alepha\";\nimport { $logger } from \"alepha/logger\";\nimport { RedisProvider, type RedisSetOptions } from \"./RedisProvider.ts\";\n\nconst envSchema = t.object({\n REDIS_URL: t.text({\n default: \"redis://localhost:6379\",\n description: \"Redis connection URL\",\n }),\n});\n\ndeclare module \"alepha\" {\n interface Env extends Partial<Static<typeof envSchema>> {}\n}\n\nexport type NodeRedisClient = RedisClientType<\n {},\n {},\n {},\n 3,\n { 36: BufferConstructor }\n>;\nexport type NodeRedisClientOptions = Parameters<typeof createClient>[0];\n\n/**\n * Node.js Redis client provider using `@redis/client`.\n *\n * This provider uses the official Redis client for Node.js runtime.\n *\n * @example\n * ```ts\n * // Set REDIS_URL environment variable (default: redis://localhost:6379)\n * // REDIS_URL=redis://:password@myredis.example.com:6379\n *\n * // Or configure programmatically\n * alepha.with({\n * provide: RedisProvider,\n * use: NodeRedisProvider,\n * });\n * ```\n */\nexport class NodeRedisProvider extends RedisProvider {\n protected readonly log = $logger();\n protected readonly alepha = $inject(Alepha);\n protected readonly env = $env(envSchema);\n protected readonly client = this.createClient();\n\n public get publisher(): NodeRedisClient {\n if (!this.client.isReady) {\n throw new AlephaError(\"Redis client is not ready\");\n }\n\n return this.client;\n }\n\n public override get isReady(): boolean {\n return this.client.isReady;\n }\n\n protected readonly start = $hook({\n on: \"start\",\n handler: () => this.connect(),\n });\n\n protected readonly stop = $hook({\n on: \"stop\",\n handler: () => this.close(),\n });\n\n /**\n * Connect to the Redis server.\n */\n public override async connect(): Promise<void> {\n this.log.debug(\"Connecting...\");\n await this.client.connect();\n this.log.info(\"Connection OK\");\n }\n\n /**\n * Close the connection to the Redis server.\n */\n public override async close(): Promise<void> {\n this.log.debug(\"Closing connection...\");\n await this.client.close();\n this.log.info(\"Connection closed\");\n }\n\n public duplicate(options?: Partial<NodeRedisClientOptions>): NodeRedisClient {\n return this.client\n .duplicate({\n ...options,\n RESP: 3,\n })\n .withTypeMapping({\n [RESP_TYPES.BLOB_STRING]: Buffer,\n });\n }\n\n public override async get(key: string): Promise<Buffer | undefined> {\n this.log.trace(`Getting key ${key}`);\n const resp = await this.publisher.get(key);\n\n if (resp === null) {\n return undefined;\n }\n\n return Buffer.from(resp);\n }\n\n public override async set(\n key: string,\n value: Buffer | string,\n options?: RedisSetOptions,\n ): Promise<Buffer> {\n const buf = Buffer.isBuffer(value) ? value : Buffer.from(value, \"utf-8\");\n\n // Convert RedisSetOptions to @redis/client SetOptions\n const setOptions: SetOptions = {};\n\n // Handle expiration object format (from alepha/cache-redis, alepha/lock-redis)\n if (options?.expiration) {\n if (options.expiration.type === \"KEEPTTL\") {\n setOptions.KEEPTTL = true;\n } else {\n setOptions[options.expiration.type] = options.expiration.value;\n }\n }\n\n // Handle direct expiration properties\n if (options?.EX !== undefined) {\n setOptions.EX = options.EX;\n }\n if (options?.PX !== undefined) {\n setOptions.PX = options.PX;\n }\n if (options?.EXAT !== undefined) {\n setOptions.EXAT = options.EXAT;\n }\n if (options?.PXAT !== undefined) {\n setOptions.PXAT = options.PXAT;\n }\n if (options?.KEEPTTL) {\n setOptions.KEEPTTL = true;\n }\n\n // Handle condition object format\n if (options?.condition === \"NX\") {\n setOptions.NX = true;\n } else if (options?.condition === \"XX\") {\n setOptions.XX = true;\n }\n\n // Handle direct condition properties\n if (options?.NX) {\n setOptions.NX = true;\n }\n if (options?.XX) {\n setOptions.XX = true;\n }\n if (options?.GET) {\n setOptions.GET = true;\n }\n\n const resp = await this.publisher.set(\n key,\n buf,\n Object.keys(setOptions).length > 0 ? setOptions : undefined,\n );\n\n if (resp === \"OK\" || !resp) {\n return buf;\n }\n\n return Buffer.from(resp);\n }\n\n public override async has(key: string): Promise<boolean> {\n const resp = await this.publisher.exists(key);\n return resp > 0;\n }\n\n public override async keys(pattern: string): Promise<string[]> {\n const keys = await this.publisher.keys(pattern);\n return keys.map((key) => key.toString());\n }\n\n public override async del(keys: string[]): Promise<void> {\n if (keys.length === 0) {\n return;\n }\n\n await this.publisher.del(keys);\n }\n\n // ---------------------------------------------------------\n // Queue operations\n // ---------------------------------------------------------\n\n public override async lpush(key: string, value: string): Promise<void> {\n await this.publisher.LPUSH(key, value);\n }\n\n public override async rpop(key: string): Promise<string | undefined> {\n const value = await this.publisher.RPOP(key);\n if (value == null) {\n return undefined;\n }\n return String(value);\n }\n\n // ---------------------------------------------------------\n // Pub/Sub operations\n // ---------------------------------------------------------\n\n public override async publish(\n channel: string,\n message: string,\n ): Promise<void> {\n await this.publisher.publish(channel, message);\n }\n\n // ---------------------------------------------------------\n // Counter operations\n // ---------------------------------------------------------\n\n public override async incr(key: string, amount: number): Promise<number> {\n return this.publisher.INCRBY(key, amount);\n }\n\n /**\n * Get the Redis connection URL.\n */\n protected getUrl(): string {\n return this.env.REDIS_URL;\n }\n\n /**\n * Redis client factory method.\n */\n protected createClient(): NodeRedisClient {\n const client = createClient({\n url: this.getUrl(),\n RESP: 3,\n }).withTypeMapping({\n [RESP_TYPES.BLOB_STRING]: Buffer,\n });\n\n client.on(\"error\", (error) => {\n if (this.alepha.isStarted()) {\n this.log.error(error);\n }\n });\n\n return client;\n }\n}\n","import { $hook, $inject, Alepha, AlephaError } from \"alepha\";\nimport { $logger } from \"alepha/logger\";\nimport {\n type NodeRedisClient,\n NodeRedisProvider,\n} from \"./NodeRedisProvider.ts\";\nimport {\n RedisSubscriberProvider,\n type SubscribeCallback,\n} from \"./RedisSubscriberProvider.ts\";\n\n/**\n * Node.js Redis subscriber provider using `@redis/client`.\n *\n * This provider creates a dedicated Redis connection for subscriptions,\n * as Redis requires separate connections for pub/sub operations.\n *\n * @example\n * ```ts\n * const subscriber = alepha.inject(RedisSubscriberProvider);\n * await subscriber.subscribe(\"channel\", (message, channel) => {\n * console.log(`Received: ${message} on ${channel}`);\n * });\n * ```\n */\nexport class NodeRedisSubscriberProvider extends RedisSubscriberProvider {\n protected readonly log = $logger();\n protected readonly alepha = $inject(Alepha);\n protected readonly redisProvider = $inject(NodeRedisProvider);\n protected readonly client: NodeRedisClient = this.createClient();\n\n public get subscriber(): NodeRedisClient {\n if (!this.client.isReady) {\n throw new AlephaError(\"Redis subscriber client is not ready\");\n }\n\n return this.client;\n }\n\n public override get isReady(): boolean {\n return this.client.isReady;\n }\n\n protected readonly start = $hook({\n on: \"start\",\n handler: () => this.connect(),\n });\n\n protected readonly stop = $hook({\n on: \"stop\",\n handler: () => this.close(),\n });\n\n public override async connect(): Promise<void> {\n this.log.debug(\"Connecting subscriber...\");\n await this.client.connect();\n this.log.info(\"Subscriber connection OK\");\n }\n\n public override async close(): Promise<void> {\n if (!this.client.isReady) {\n this.log.debug(\"Subscriber client not ready, skipping close\");\n return;\n }\n this.log.debug(\"Closing subscriber connection...\");\n await this.client.close();\n this.log.info(\"Subscriber connection closed\");\n }\n\n public override async subscribe(\n channel: string,\n callback: SubscribeCallback,\n ): Promise<void> {\n await this.subscriber.subscribe(channel, callback);\n }\n\n public override async unsubscribe(\n channel: string,\n callback?: SubscribeCallback,\n ): Promise<void> {\n await this.subscriber.unsubscribe(channel, callback);\n }\n\n /**\n * Redis subscriber client factory method.\n */\n protected createClient(): NodeRedisClient {\n const client = this.redisProvider.duplicate();\n\n client.on(\"error\", (error) => {\n if (this.alepha.isStarted()) {\n this.log.error(error);\n }\n });\n\n return client;\n }\n}\n","import { $module, type Alepha } from \"alepha\";\nimport { BunRedisProvider } from \"./providers/BunRedisProvider.ts\";\nimport { BunRedisSubscriberProvider } from \"./providers/BunRedisSubscriberProvider.ts\";\nimport { NodeRedisProvider } from \"./providers/NodeRedisProvider.ts\";\nimport { NodeRedisSubscriberProvider } from \"./providers/NodeRedisSubscriberProvider.ts\";\nimport { RedisProvider } from \"./providers/RedisProvider.ts\";\nimport { RedisSubscriberProvider } from \"./providers/RedisSubscriberProvider.ts\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport * from \"./providers/BunRedisProvider.ts\";\nexport * from \"./providers/BunRedisSubscriberProvider.ts\";\nexport * from \"./providers/NodeRedisProvider.ts\";\nexport * from \"./providers/NodeRedisSubscriberProvider.ts\";\nexport * from \"./providers/RedisProvider.ts\";\nexport * from \"./providers/RedisSubscriberProvider.ts\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\n/**\n * Redis client wrapper.\n *\n * **Features:**\n * - Connection pooling\n * - Automatic reconnection\n * - Command pipelining\n * - Pub/sub support\n *\n * @module alepha.redis\n */\nexport const AlephaRedis = $module({\n name: \"alepha.redis\",\n services: [RedisProvider, RedisSubscriberProvider],\n variants: [\n NodeRedisProvider,\n NodeRedisSubscriberProvider,\n BunRedisProvider,\n BunRedisSubscriberProvider,\n ],\n register: (alepha: Alepha) => {\n if (alepha.isBun()) {\n alepha\n .with({\n provide: RedisProvider,\n use: BunRedisProvider,\n })\n .with({\n provide: RedisSubscriberProvider,\n use: BunRedisSubscriberProvider,\n });\n } else {\n alepha\n .with({\n provide: RedisProvider,\n use: NodeRedisProvider,\n })\n .with({\n provide: RedisSubscriberProvider,\n use: NodeRedisSubscriberProvider,\n });\n }\n },\n});\n"],"mappings":"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"}
|
package/dist/retry/index.d.ts
CHANGED
|
@@ -1,7 +1,5 @@
|
|
|
1
|
-
import * as _$alepha from "alepha";
|
|
2
1
|
import { AlephaError, Middleware, Primitive, PrimitiveArgs } from "alepha";
|
|
3
2
|
import { DateTimeProvider, DurationLike } from "alepha/datetime";
|
|
4
|
-
import * as _$alepha_logger0 from "alepha/logger";
|
|
5
3
|
|
|
6
4
|
//#region ../../src/retry/errors/RetryCancelError.d.ts
|
|
7
5
|
declare class RetryCancelError extends AlephaError {
|
|
@@ -88,7 +86,7 @@ interface RetryBackoffOptions {
|
|
|
88
86
|
* Supports exponential backoff, max duration, conditional retries, and cancellation.
|
|
89
87
|
*/
|
|
90
88
|
declare class RetryProvider {
|
|
91
|
-
protected readonly log:
|
|
89
|
+
protected readonly log: import("alepha/logger").Logger;
|
|
92
90
|
protected readonly dateTime: DateTimeProvider;
|
|
93
91
|
/**
|
|
94
92
|
* Execute a function with automatic retry logic.
|
|
@@ -212,7 +210,7 @@ interface RetryPrimitiveFn<T extends (...args: any[]) => any> extends RetryPrimi
|
|
|
212
210
|
*
|
|
213
211
|
* @module alepha.retry
|
|
214
212
|
*/
|
|
215
|
-
declare const AlephaRetry:
|
|
213
|
+
declare const AlephaRetry: import("alepha").Service<import("alepha").Module>;
|
|
216
214
|
//#endregion
|
|
217
215
|
export { $retry, AlephaRetry, RetryBackoffOptions, RetryCancelError, RetryMiddlewareOptions, RetryOptions, RetryPrimitive, RetryPrimitiveFn, RetryPrimitiveOptions, RetryProvider, RetryTimeoutError };
|
|
218
216
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","names":[],"sources":["../../src/retry/errors/RetryCancelError.ts","../../src/retry/errors/RetryTimeoutError.ts","../../src/retry/providers/RetryProvider.ts","../../src/retry/primitives/$retry.ts","../../src/retry/index.ts"],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.d.ts","names":[],"sources":["../../src/retry/errors/RetryCancelError.ts","../../src/retry/errors/RetryTimeoutError.ts","../../src/retry/providers/RetryProvider.ts","../../src/retry/primitives/$retry.ts","../../src/retry/index.ts"],"mappings":";;;;cAEa,gBAAA,SAAyB,WAAW;EAAX,WAAA;AAAA;;;cCAzB,iBAAA,SAA0B,WAAW;cACpC,QAAA;AAAA;;;UCGG,YAAA,eAA2B,IAAA;;;AFJ5C;EEQE,OAAA,EAAS,CAAA;;;;;;EAOT,GAAA;EDfW;;;;;;ECuBX,OAAA,YAAmB,mBAAA;EDtBS;AAAA;;;;EC6B5B,WAAA,GAAc,YAAA;EA1Ba;;;;;EAiC3B,IAAA,IAAQ,KAAA,EAAO,KAAA;EAMG;;;;EAAlB,OAAA,IAAW,KAAA,EAAO,KAAA,EAAO,OAAA,aAAoB,IAAA,EAAM,UAAA,CAAW,CAAA;EAWhC;;;EAN9B,MAAA,GAAS,WAAA;EAxCT;;;;EA8CA,gBAAA,GAAmB,WAAA;AAAA;AAAA,UAGJ,mBAAA;EApBf;;;;;EA0BA,OAAA;EApByB;;;;;EA2BzB,MAAA;EAhBA;;;EAqBA,GAAA;EAlBe;;;;;EAyBf,MAAA;AAAA;;;AAAM;AAOR;cAAa,aAAA;EAAA,mBACQ,GAAA,0BAAG,MAAA;EAAA,mBACH,QAAA,EAAQ,gBAAA;EAMH;;;EADlB,KAAA,eAAoB,IAAA,iBACxB,OAAA,EAAS,YAAA,CAAa,CAAA,MACnB,IAAA,EAAM,UAAA,CAAW,CAAA,IACnB,OAAA,CAAQ,UAAA,CAAW,CAAA;EAAA;;;EAAA,UA8GZ,gBAAA,CACR,OAAA,UACA,OAAA,YAAmB,mBAAA;AAAA;;;AFnNvB;;;;;;;;ACAA;;;;;ADAA,cGuBa,MAAA,GAAU,OAAA,GAAU,sBAAA,KAAyB,UA0BzD;AAAA,UAIgB,qBAAA,eAAoC,IAAA;EFpDvB;AAAA;;EEwD5B,OAAA,EAAS,CAAA;;ADrDX;;;;EC4DE,GAAA;EDlCc;;;;;;EC0Cd,OAAA,YAAmB,mBAAA;EDlBW;;;;;ECyB9B,WAAA,GAAc,YAAA;EDhEd;;;;;ECuEA,IAAA,IAAQ,KAAA,EAAO,KAAA;EDjDA;;;;ECuDf,OAAA,IAAW,KAAA,EAAO,KAAA,EAAO,OAAA,aAAoB,IAAA,EAAM,UAAA,CAAW,CAAA;EDjDrC;;;ECsDzB,MAAA,GAAS,WAAA;AAAA;;;;UAMM,sBAAA;EDjDe;AAGhC;;;;ECoDE,GAAA;EDvCA;;;;AAYM;ECkCN,OAAA,YAAmB,mBAAA;ED3BK;;;ECgCxB,WAAA,GAAc,YAAA;EDxBH;;;;;EC+BX,IAAA,IAAQ,KAAA,EAAO,KAAA;EDmFM;;;EC9ErB,OAAA,IAAW,KAAA,EAAO,KAAA,EAAO,OAAA;ED3CH;;;ECgDtB,MAAA,GAAS,WAAA;AAAA;AAAA,cAKE,cAAA,eACG,IAAA,yBACN,SAAA,CAAU,qBAAA,CAAsB,CAAA;EAAA,mBACrB,aAAA,EAAa,aAAA;EAAA,UACtB,kBAAA,GAAqB,eAAA;cAEnB,IAAA,EAAM,aAAA,CAAc,qBAAA,CAAsB,CAAA;EAQhD,GAAA,IAAO,IAAA,EAAM,UAAA,CAAW,CAAA,IAAK,OAAA,CAAQ,UAAA,CAAW,CAAA;AAAA;AAAA,UAcvC,gBAAA,eAA+B,IAAA,yBACtC,cAAA,CAAe,CAAA;EAAA,IACnB,IAAA,EAAM,UAAA,CAAW,CAAA,IAAK,OAAA,CAAQ,UAAA,CAAW,CAAA;AAAA;;;AH7K/C;;;;;;;;ACAA;;;ADAA,cIqBa,WAAA,mBAAW,OAAA,kBAAA,MAAA"}
|
package/dist/retry/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":[],"sources":["../../src/retry/errors/RetryCancelError.ts","../../src/retry/errors/RetryTimeoutError.ts","../../src/retry/providers/RetryProvider.ts","../../src/retry/primitives/$retry.ts","../../src/retry/index.ts"],"sourcesContent":["import { AlephaError } from \"alepha\";\n\nexport class RetryCancelError extends AlephaError {\n constructor() {\n super(\"Retry operation was cancelled.\");\n this.name = \"RetryCancelError\";\n }\n}\n","import { AlephaError } from \"alepha\";\n\nexport class RetryTimeoutError extends AlephaError {\n constructor(duration: number) {\n super(`Retry operation timed out after ${duration}ms.`);\n this.name = \"RetryTimeoutError\";\n }\n}\n","import { $inject } from \"alepha\";\nimport { DateTimeProvider, type DurationLike } from \"alepha/datetime\";\nimport { $logger } from \"alepha/logger\";\nimport { RetryCancelError } from \"../errors/RetryCancelError.ts\";\nimport { RetryTimeoutError } from \"../errors/RetryTimeoutError.ts\";\n\nexport interface RetryOptions<T extends (...args: any[]) => any> {\n /**\n * The function to retry.\n */\n handler: T;\n\n /**\n * The maximum number of attempts.\n *\n * @default 3\n */\n max?: number;\n\n /**\n * The backoff strategy for delays between retries.\n * Can be a fixed number (in ms) or a configuration object for exponential backoff.\n *\n * @default { initial: 200, factor: 2, jitter: true }\n */\n backoff?: number | RetryBackoffOptions;\n\n /**\n * An overall time limit for all retry attempts combined.\n *\n * e.g., `[5, 'seconds']`\n */\n maxDuration?: DurationLike;\n\n /**\n * A function that determines if a retry should be attempted based on the error.\n *\n * @default (error) => true (retries on any error)\n */\n when?: (error: Error) => boolean;\n\n /**\n * A custom callback for when a retry attempt fails.\n * This is called before the delay.\n */\n onError?: (error: Error, attempt: number, ...args: Parameters<T>) => void;\n\n /**\n * An AbortSignal to allow for external cancellation of the retry loop.\n */\n signal?: AbortSignal;\n\n /**\n * An additional AbortSignal to combine with the provided signal.\n * Used internally by $retry to handle app lifecycle.\n */\n additionalSignal?: AbortSignal;\n}\n\nexport interface RetryBackoffOptions {\n /**\n * Initial delay in milliseconds.\n *\n * @default 200\n */\n initial?: number;\n\n /**\n * Multiplier for each subsequent delay.\n *\n * @default 2\n */\n factor?: number;\n\n /**\n * Maximum delay in milliseconds.\n */\n max?: number;\n\n /**\n * If true, adds a random jitter to the delay to prevent thundering herd.\n *\n * @default true\n */\n jitter?: boolean;\n}\n\n/**\n * Service for executing functions with automatic retry logic.\n * Supports exponential backoff, max duration, conditional retries, and cancellation.\n */\nexport class RetryProvider {\n protected readonly log = $logger();\n protected readonly dateTime = $inject(DateTimeProvider);\n\n /**\n * Execute a function with automatic retry logic.\n */\n async retry<T extends (...args: any[]) => any>(\n options: RetryOptions<T>,\n ...args: Parameters<T>\n ): Promise<ReturnType<T>> {\n const maxAttempts = options.max ?? 3;\n const when = options.when ?? (() => true);\n const { handler, onError } = options;\n\n let lastError: Error | undefined;\n const startTime = this.dateTime.nowMillis();\n\n const maxDurationMs = options.maxDuration\n ? this.dateTime.duration(options.maxDuration).asMilliseconds()\n : Infinity;\n\n // Combine user-provided signal with additional signal (e.g., app lifecycle)\n const signals = [options.signal, options.additionalSignal].filter(Boolean);\n const onAbort = () => {\n // Always set RetryCancelError when aborted, even if another error exists\n // This ensures cancellation takes precedence over retry errors\n lastError = new RetryCancelError();\n };\n\n // Add abort listeners to all signals\n for (const signal of signals) {\n signal?.addEventListener(\"abort\", onAbort);\n }\n\n // FIX BUG #8: Create combined signal ONCE at the start instead of on each backoff\n // This prevents memory leak from creating multiple AbortSignal.any() instances\n const waitSignals = [options.signal, options.additionalSignal].filter(\n Boolean,\n ) as AbortSignal[];\n const combinedSignal =\n waitSignals.length > 0 ? AbortSignal.any(waitSignals) : undefined;\n\n try {\n for (let attempt = 1; attempt <= maxAttempts; attempt++) {\n // Check for cancellation\n if (signals.some((signal) => signal?.aborted)) {\n throw new RetryCancelError();\n }\n\n // Check for timeout before attempting\n if (this.dateTime.nowMillis() - startTime >= maxDurationMs) {\n throw new RetryTimeoutError(maxDurationMs);\n }\n\n try {\n const result = await handler(...args);\n\n // Check for timeout after handler execution\n if (this.dateTime.nowMillis() - startTime >= maxDurationMs) {\n throw new RetryTimeoutError(maxDurationMs);\n }\n\n return result;\n } catch (err) {\n lastError = err as Error;\n\n // Check for timeout after error\n if (this.dateTime.nowMillis() - startTime >= maxDurationMs) {\n throw new RetryTimeoutError(maxDurationMs);\n }\n\n // Log the error with warning level\n this.log.warn(\"Retry attempt failed\", {\n attempt,\n maxAttempts,\n remainingAttempts: maxAttempts - attempt,\n error: lastError.message,\n errorName: lastError.name,\n });\n\n if (!(err instanceof Error) || !when(err)) {\n throw err; // don't retry if it's not an Error or `when` returns false\n }\n\n // FIX BUG #7: Call onError BEFORE checking if this is the final attempt\n // This ensures onError is called for ALL failed attempts, including the last one\n if (onError) {\n onError(err, attempt, ...args);\n }\n\n if (attempt >= maxAttempts) {\n break; // will throw lastError after the loop\n }\n\n // Calculate and wait for backoff delay\n const delay = this.calculateBackoff(attempt, options.backoff);\n if (delay > 0) {\n await this.dateTime.wait(delay, { signal: combinedSignal });\n }\n\n // Check for timeout after backoff wait before next attempt\n if (this.dateTime.nowMillis() - startTime >= maxDurationMs) {\n throw new RetryTimeoutError(maxDurationMs);\n }\n }\n }\n } finally {\n // Clean up listeners to prevent memory leaks\n for (const signal of signals) {\n signal?.removeEventListener(\"abort\", onAbort);\n }\n }\n\n throw lastError;\n }\n\n /**\n * Calculate the backoff delay for a given attempt.\n */\n protected calculateBackoff(\n attempt: number,\n options?: number | RetryBackoffOptions,\n ): number {\n if (typeof options === \"number\") {\n return options;\n }\n\n const initial = options?.initial ?? 200;\n const factor = options?.factor ?? 2;\n const max = options?.max ?? 10000;\n const useJitter = options?.jitter !== false;\n\n const exponential = initial * factor ** (attempt - 1);\n let delay = Math.min(exponential, max);\n\n if (useJitter) {\n // Add a random amount of jitter (e.g., up to 50% of the delay)\n delay = delay * (1 + Math.random() * 0.5);\n }\n\n return Math.floor(delay);\n }\n}\n","import {\n $context,\n $inject,\n createMiddleware,\n type Middleware,\n Primitive,\n type PrimitiveArgs,\n} from \"alepha\";\nimport type { DurationLike } from \"alepha/datetime\";\nimport type { RetryBackoffOptions } from \"../providers/RetryProvider.ts\";\nimport { RetryProvider } from \"../providers/RetryProvider.ts\";\n\n/**\n * Retry middleware for `use` arrays in `$action`, `$job`, `$page`, `$pipeline`.\n *\n * Retries the handler on failure with configurable backoff, max attempts, and\n * conditional retry via `when`. Aborts on application shutdown.\n *\n * ```ts\n * processOrder = $action({\n * use: [$retry({ max: 3, backoff: { initial: 500 } })],\n * handler: async ({ body }) => { ... },\n * });\n * ```\n */\nexport const $retry = (options?: RetryMiddlewareOptions): Middleware => {\n const { alepha } = $context();\n const retryProvider = alepha.inject(RetryProvider);\n let appAbortController: AbortController | undefined;\n\n alepha.events.on(\"stop\", () => {\n appAbortController?.abort();\n });\n\n return createMiddleware({\n name: \"$retry\",\n options: options as unknown as Record<string, unknown>,\n handler: ({ next }) => {\n return async (...args) => {\n appAbortController ??= new AbortController();\n return retryProvider.retry(\n {\n ...options,\n handler: next,\n additionalSignal: appAbortController.signal,\n },\n ...args,\n );\n };\n },\n });\n};\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport interface RetryPrimitiveOptions<T extends (...args: any[]) => any> {\n /**\n * The function to retry.\n */\n handler: T;\n\n /**\n * The maximum number of attempts.\n *\n * @default 3\n */\n max?: number;\n\n /**\n * The backoff strategy for delays between retries.\n * Can be a fixed number (in ms) or a configuration object for exponential backoff.\n *\n * @default { initial: 200, factor: 2, jitter: true }\n */\n backoff?: number | RetryBackoffOptions;\n\n /**\n * An overall time limit for all retry attempts combined.\n *\n * e.g., `[5, 'seconds']`\n */\n maxDuration?: DurationLike;\n\n /**\n * A function that determines if a retry should be attempted based on the error.\n *\n * @default (error) => true (retries on any error)\n */\n when?: (error: Error) => boolean;\n\n /**\n * A custom callback for when a retry attempt fails.\n * This is called before the delay.\n */\n onError?: (error: Error, attempt: number, ...args: Parameters<T>) => void;\n\n /**\n * An AbortSignal to allow for external cancellation of the retry loop.\n */\n signal?: AbortSignal;\n}\n\n/**\n * Options for $retry in middleware mode (no handler).\n */\nexport interface RetryMiddlewareOptions {\n /**\n * The maximum number of attempts.\n *\n * @default 3\n */\n max?: number;\n\n /**\n * The backoff strategy for delays between retries.\n *\n * @default { initial: 200, factor: 2, jitter: true }\n */\n backoff?: number | RetryBackoffOptions;\n\n /**\n * An overall time limit for all retry attempts combined.\n */\n maxDuration?: DurationLike;\n\n /**\n * A function that determines if a retry should be attempted based on the error.\n *\n * @default (error) => true (retries on any error)\n */\n when?: (error: Error) => boolean;\n\n /**\n * A custom callback for when a retry attempt fails.\n */\n onError?: (error: Error, attempt: number) => void;\n\n /**\n * An AbortSignal to allow for external cancellation of the retry loop.\n */\n signal?: AbortSignal;\n}\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport class RetryPrimitive<\n T extends (...args: any[]) => any,\n> extends Primitive<RetryPrimitiveOptions<T>> {\n protected readonly retryProvider = $inject(RetryProvider);\n protected appAbortController?: AbortController;\n\n constructor(args: PrimitiveArgs<RetryPrimitiveOptions<T>>) {\n super(args);\n\n this.alepha.events.on(\"stop\", () => {\n this.appAbortController?.abort();\n });\n }\n\n async run(...args: Parameters<T>): Promise<ReturnType<T>> {\n // Nov 25: Cloudflare does not like 'new AbortController' outside main handler, we can't pre-create it in the constructor.\n this.appAbortController ??= new AbortController();\n\n return this.retryProvider.retry(\n {\n ...this.options,\n additionalSignal: this.appAbortController.signal,\n },\n ...args,\n );\n }\n}\n\nexport interface RetryPrimitiveFn<T extends (...args: any[]) => any>\n extends RetryPrimitive<T> {\n (...args: Parameters<T>): Promise<ReturnType<T>>;\n}\n","import { $module } from \"alepha\";\nimport { RetryProvider } from \"./providers/RetryProvider.ts\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport * from \"./errors/RetryCancelError.ts\";\nexport * from \"./errors/RetryTimeoutError.ts\";\nexport * from \"./primitives/$retry.ts\";\nexport * from \"./providers/RetryProvider.ts\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\n/**\n * Automatic retry with backoff.\n *\n * **Features:**\n * - Retry configuration\n * - Exponential backoff\n * - Max retry limits\n * - Custom retry predicates\n *\n * @module alepha.retry\n */\nexport const AlephaRetry = $module({\n name: \"alepha.retry\",\n services: [RetryProvider],\n});\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../../src/retry/errors/RetryCancelError.ts","../../src/retry/errors/RetryTimeoutError.ts","../../src/retry/providers/RetryProvider.ts","../../src/retry/primitives/$retry.ts","../../src/retry/index.ts"],"sourcesContent":["import { AlephaError } from \"alepha\";\n\nexport class RetryCancelError extends AlephaError {\n constructor() {\n super(\"Retry operation was cancelled.\");\n this.name = \"RetryCancelError\";\n }\n}\n","import { AlephaError } from \"alepha\";\n\nexport class RetryTimeoutError extends AlephaError {\n constructor(duration: number) {\n super(`Retry operation timed out after ${duration}ms.`);\n this.name = \"RetryTimeoutError\";\n }\n}\n","import { $inject } from \"alepha\";\nimport { DateTimeProvider, type DurationLike } from \"alepha/datetime\";\nimport { $logger } from \"alepha/logger\";\nimport { RetryCancelError } from \"../errors/RetryCancelError.ts\";\nimport { RetryTimeoutError } from \"../errors/RetryTimeoutError.ts\";\n\nexport interface RetryOptions<T extends (...args: any[]) => any> {\n /**\n * The function to retry.\n */\n handler: T;\n\n /**\n * The maximum number of attempts.\n *\n * @default 3\n */\n max?: number;\n\n /**\n * The backoff strategy for delays between retries.\n * Can be a fixed number (in ms) or a configuration object for exponential backoff.\n *\n * @default { initial: 200, factor: 2, jitter: true }\n */\n backoff?: number | RetryBackoffOptions;\n\n /**\n * An overall time limit for all retry attempts combined.\n *\n * e.g., `[5, 'seconds']`\n */\n maxDuration?: DurationLike;\n\n /**\n * A function that determines if a retry should be attempted based on the error.\n *\n * @default (error) => true (retries on any error)\n */\n when?: (error: Error) => boolean;\n\n /**\n * A custom callback for when a retry attempt fails.\n * This is called before the delay.\n */\n onError?: (error: Error, attempt: number, ...args: Parameters<T>) => void;\n\n /**\n * An AbortSignal to allow for external cancellation of the retry loop.\n */\n signal?: AbortSignal;\n\n /**\n * An additional AbortSignal to combine with the provided signal.\n * Used internally by $retry to handle app lifecycle.\n */\n additionalSignal?: AbortSignal;\n}\n\nexport interface RetryBackoffOptions {\n /**\n * Initial delay in milliseconds.\n *\n * @default 200\n */\n initial?: number;\n\n /**\n * Multiplier for each subsequent delay.\n *\n * @default 2\n */\n factor?: number;\n\n /**\n * Maximum delay in milliseconds.\n */\n max?: number;\n\n /**\n * If true, adds a random jitter to the delay to prevent thundering herd.\n *\n * @default true\n */\n jitter?: boolean;\n}\n\n/**\n * Service for executing functions with automatic retry logic.\n * Supports exponential backoff, max duration, conditional retries, and cancellation.\n */\nexport class RetryProvider {\n protected readonly log = $logger();\n protected readonly dateTime = $inject(DateTimeProvider);\n\n /**\n * Execute a function with automatic retry logic.\n */\n async retry<T extends (...args: any[]) => any>(\n options: RetryOptions<T>,\n ...args: Parameters<T>\n ): Promise<ReturnType<T>> {\n const maxAttempts = options.max ?? 3;\n const when = options.when ?? (() => true);\n const { handler, onError } = options;\n\n let lastError: Error | undefined;\n const startTime = this.dateTime.nowMillis();\n\n const maxDurationMs = options.maxDuration\n ? this.dateTime.duration(options.maxDuration).asMilliseconds()\n : Infinity;\n\n // Combine user-provided signal with additional signal (e.g., app lifecycle)\n const signals = [options.signal, options.additionalSignal].filter(Boolean);\n const onAbort = () => {\n // Always set RetryCancelError when aborted, even if another error exists\n // This ensures cancellation takes precedence over retry errors\n lastError = new RetryCancelError();\n };\n\n // Add abort listeners to all signals\n for (const signal of signals) {\n signal?.addEventListener(\"abort\", onAbort);\n }\n\n // FIX BUG #8: Create combined signal ONCE at the start instead of on each backoff\n // This prevents memory leak from creating multiple AbortSignal.any() instances\n const waitSignals = [options.signal, options.additionalSignal].filter(\n Boolean,\n ) as AbortSignal[];\n const combinedSignal =\n waitSignals.length > 0 ? AbortSignal.any(waitSignals) : undefined;\n\n try {\n for (let attempt = 1; attempt <= maxAttempts; attempt++) {\n // Check for cancellation\n if (signals.some((signal) => signal?.aborted)) {\n throw new RetryCancelError();\n }\n\n // Check for timeout before attempting\n if (this.dateTime.nowMillis() - startTime >= maxDurationMs) {\n throw new RetryTimeoutError(maxDurationMs);\n }\n\n try {\n const result = await handler(...args);\n\n // Check for timeout after handler execution\n if (this.dateTime.nowMillis() - startTime >= maxDurationMs) {\n throw new RetryTimeoutError(maxDurationMs);\n }\n\n return result;\n } catch (err) {\n lastError = err as Error;\n\n // Check for timeout after error\n if (this.dateTime.nowMillis() - startTime >= maxDurationMs) {\n throw new RetryTimeoutError(maxDurationMs);\n }\n\n // Log the error with warning level\n this.log.warn(\"Retry attempt failed\", {\n attempt,\n maxAttempts,\n remainingAttempts: maxAttempts - attempt,\n error: lastError.message,\n errorName: lastError.name,\n });\n\n if (!(err instanceof Error) || !when(err)) {\n throw err; // don't retry if it's not an Error or `when` returns false\n }\n\n // FIX BUG #7: Call onError BEFORE checking if this is the final attempt\n // This ensures onError is called for ALL failed attempts, including the last one\n if (onError) {\n onError(err, attempt, ...args);\n }\n\n if (attempt >= maxAttempts) {\n break; // will throw lastError after the loop\n }\n\n // Calculate and wait for backoff delay\n const delay = this.calculateBackoff(attempt, options.backoff);\n if (delay > 0) {\n await this.dateTime.wait(delay, { signal: combinedSignal });\n }\n\n // Check for timeout after backoff wait before next attempt\n if (this.dateTime.nowMillis() - startTime >= maxDurationMs) {\n throw new RetryTimeoutError(maxDurationMs);\n }\n }\n }\n } finally {\n // Clean up listeners to prevent memory leaks\n for (const signal of signals) {\n signal?.removeEventListener(\"abort\", onAbort);\n }\n }\n\n throw lastError;\n }\n\n /**\n * Calculate the backoff delay for a given attempt.\n */\n protected calculateBackoff(\n attempt: number,\n options?: number | RetryBackoffOptions,\n ): number {\n if (typeof options === \"number\") {\n return options;\n }\n\n const initial = options?.initial ?? 200;\n const factor = options?.factor ?? 2;\n const max = options?.max ?? 10000;\n const useJitter = options?.jitter !== false;\n\n const exponential = initial * factor ** (attempt - 1);\n let delay = Math.min(exponential, max);\n\n if (useJitter) {\n // Add a random amount of jitter (e.g., up to 50% of the delay)\n delay = delay * (1 + Math.random() * 0.5);\n }\n\n return Math.floor(delay);\n }\n}\n","import {\n $context,\n $inject,\n createMiddleware,\n type Middleware,\n Primitive,\n type PrimitiveArgs,\n} from \"alepha\";\nimport type { DurationLike } from \"alepha/datetime\";\nimport type { RetryBackoffOptions } from \"../providers/RetryProvider.ts\";\nimport { RetryProvider } from \"../providers/RetryProvider.ts\";\n\n/**\n * Retry middleware for `use` arrays in `$action`, `$job`, `$page`, `$pipeline`.\n *\n * Retries the handler on failure with configurable backoff, max attempts, and\n * conditional retry via `when`. Aborts on application shutdown.\n *\n * ```ts\n * processOrder = $action({\n * use: [$retry({ max: 3, backoff: { initial: 500 } })],\n * handler: async ({ body }) => { ... },\n * });\n * ```\n */\nexport const $retry = (options?: RetryMiddlewareOptions): Middleware => {\n const { alepha } = $context();\n const retryProvider = alepha.inject(RetryProvider);\n let appAbortController: AbortController | undefined;\n\n alepha.events.on(\"stop\", () => {\n appAbortController?.abort();\n });\n\n return createMiddleware({\n name: \"$retry\",\n options: options as unknown as Record<string, unknown>,\n handler: ({ next }) => {\n return async (...args) => {\n appAbortController ??= new AbortController();\n return retryProvider.retry(\n {\n ...options,\n handler: next,\n additionalSignal: appAbortController.signal,\n },\n ...args,\n );\n };\n },\n });\n};\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport interface RetryPrimitiveOptions<T extends (...args: any[]) => any> {\n /**\n * The function to retry.\n */\n handler: T;\n\n /**\n * The maximum number of attempts.\n *\n * @default 3\n */\n max?: number;\n\n /**\n * The backoff strategy for delays between retries.\n * Can be a fixed number (in ms) or a configuration object for exponential backoff.\n *\n * @default { initial: 200, factor: 2, jitter: true }\n */\n backoff?: number | RetryBackoffOptions;\n\n /**\n * An overall time limit for all retry attempts combined.\n *\n * e.g., `[5, 'seconds']`\n */\n maxDuration?: DurationLike;\n\n /**\n * A function that determines if a retry should be attempted based on the error.\n *\n * @default (error) => true (retries on any error)\n */\n when?: (error: Error) => boolean;\n\n /**\n * A custom callback for when a retry attempt fails.\n * This is called before the delay.\n */\n onError?: (error: Error, attempt: number, ...args: Parameters<T>) => void;\n\n /**\n * An AbortSignal to allow for external cancellation of the retry loop.\n */\n signal?: AbortSignal;\n}\n\n/**\n * Options for $retry in middleware mode (no handler).\n */\nexport interface RetryMiddlewareOptions {\n /**\n * The maximum number of attempts.\n *\n * @default 3\n */\n max?: number;\n\n /**\n * The backoff strategy for delays between retries.\n *\n * @default { initial: 200, factor: 2, jitter: true }\n */\n backoff?: number | RetryBackoffOptions;\n\n /**\n * An overall time limit for all retry attempts combined.\n */\n maxDuration?: DurationLike;\n\n /**\n * A function that determines if a retry should be attempted based on the error.\n *\n * @default (error) => true (retries on any error)\n */\n when?: (error: Error) => boolean;\n\n /**\n * A custom callback for when a retry attempt fails.\n */\n onError?: (error: Error, attempt: number) => void;\n\n /**\n * An AbortSignal to allow for external cancellation of the retry loop.\n */\n signal?: AbortSignal;\n}\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport class RetryPrimitive<\n T extends (...args: any[]) => any,\n> extends Primitive<RetryPrimitiveOptions<T>> {\n protected readonly retryProvider = $inject(RetryProvider);\n protected appAbortController?: AbortController;\n\n constructor(args: PrimitiveArgs<RetryPrimitiveOptions<T>>) {\n super(args);\n\n this.alepha.events.on(\"stop\", () => {\n this.appAbortController?.abort();\n });\n }\n\n async run(...args: Parameters<T>): Promise<ReturnType<T>> {\n // Nov 25: Cloudflare does not like 'new AbortController' outside main handler, we can't pre-create it in the constructor.\n this.appAbortController ??= new AbortController();\n\n return this.retryProvider.retry(\n {\n ...this.options,\n additionalSignal: this.appAbortController.signal,\n },\n ...args,\n );\n }\n}\n\nexport interface RetryPrimitiveFn<T extends (...args: any[]) => any>\n extends RetryPrimitive<T> {\n (...args: Parameters<T>): Promise<ReturnType<T>>;\n}\n","import { $module } from \"alepha\";\nimport { RetryProvider } from \"./providers/RetryProvider.ts\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport * from \"./errors/RetryCancelError.ts\";\nexport * from \"./errors/RetryTimeoutError.ts\";\nexport * from \"./primitives/$retry.ts\";\nexport * from \"./providers/RetryProvider.ts\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\n/**\n * Automatic retry with backoff.\n *\n * **Features:**\n * - Retry configuration\n * - Exponential backoff\n * - Max retry limits\n * - Custom retry predicates\n *\n * @module alepha.retry\n */\nexport const AlephaRetry = $module({\n name: \"alepha.retry\",\n services: [RetryProvider],\n});\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","names":[],"sources":["../../src/router/providers/RouterProvider.ts","../../src/router/TemplatedPathParser.ts"],"mappings":";uBAEsB,cAAA,WAAyB,KAAA,GAAQ,KAAA;EAAA,UAC3C,cAAA,EAAgB,MAAA;EAAA,UAEhB,IAAA,EAAM,IAAA,CAAK,CAAA;EAAA,UACX,KAAA,EAAK,GAAA,SAAA,UAAA,CAAA,CAAA;EAAA,UACL,YAAA;EAEH,KAAA,CAAM,IAAA,WAAe,UAAA,CAAW,CAAA;EAAA,UAY7B,IAAA,CAAK,IAAA;EAAA,UAML,IAAA,CAAK,KAAA,EAAO,CAAA;EAAA,UA+DZ,gBAAA,CAAiB,IAAA,WAAe,UAAA,CAAW,CAAA;EAAA,UA4C3C,SAAA,CAAU,KAAA,EAAO,UAAA,CAAW,CAAA,IAAK,UAAA,CAAW,CAAA;EAAA,UAa5C,WAAA,CAAY,IAAA;AAAA;AAAA,UAYP,UAAA,WAAqB,KAAA;EACpC,KAAA,GAAQ,CAAA;EACR,MAAA,GAAS,MAAA;AAAA;AAAA,UAGM,KAAA;EACf,IAAA;EA/B2B;;;;;;;EAwC3B,SAAA,GAAY,
|
|
1
|
+
{"version":3,"file":"index.d.ts","names":[],"sources":["../../src/router/providers/RouterProvider.ts","../../src/router/TemplatedPathParser.ts"],"mappings":";uBAEsB,cAAA,WAAyB,KAAA,GAAQ,KAAA;EAAA,UAC3C,cAAA,EAAgB,MAAA;EAAA,UAEhB,IAAA,EAAM,IAAA,CAAK,CAAA;EAAA,UACX,KAAA,EAAK,GAAA,SAAA,UAAA,CAAA,CAAA;EAAA,UACL,YAAA;EAEH,KAAA,CAAM,IAAA,WAAe,UAAA,CAAW,CAAA;EAAA,UAY7B,IAAA,CAAK,IAAA;EAAA,UAML,IAAA,CAAK,KAAA,EAAO,CAAA;EAAA,UA+DZ,gBAAA,CAAiB,IAAA,WAAe,UAAA,CAAW,CAAA;EAAA,UA4C3C,SAAA,CAAU,KAAA,EAAO,UAAA,CAAW,CAAA,IAAK,UAAA,CAAW,CAAA;EAAA,UAa5C,WAAA,CAAY,IAAA;AAAA;AAAA,UAYP,UAAA,WAAqB,KAAA;EACpC,KAAA,GAAQ,CAAA;EACR,MAAA,GAAS,MAAA;AAAA;AAAA,UAGM,KAAA;EACf,IAAA;EA/B2B;;;;;;;EAwC3B,SAAA,GAAY,MAAM;AAAA;AAAA,UAGH,IAAA,WAAe,KAAA;EAC9B,KAAA,GAAQ,CAAA;EACR,QAAA;IAAA,CACG,GAAA,WAAc,IAAA,CAAK,CAAA;EAAA;EAEtB,KAAA;IACE,KAAA,GAAQ,CAAA;IACR,IAAA;IACA,QAAA;MAAA,CACG,GAAA,WAAc,IAAA,CAAK,CAAA;IAAA;EAAA;EAGxB,QAAA;IACE,KAAA,EAAO,CAAA;EAAA;AAAA;;;;AA5LX;;;;;;;;;;;;;;;;;;;;;cCsBa,mBAAA;EAAA,0BACe,WAAA,EAAW,MAAA;EAAA,SAErB,QAAA;EAAA,SACA,SAAA;EAAA,SACA,UAAA;EAAA,SACA,SAAA;EAAA,mBACG,YAAA,EAAc,MAAA;cAErB,QAAA,UAAkB,SAAA;ED5BT;;;;EC+CrB,WAAA,CAAY,MAAA,EAAQ,MAAA;ED7CV;;;;;;;EC2DV,OAAA,CAAQ,IAAA,WAAe,MAAA;EDvCD;;;;EC4DtB,WAAA,CAAY,QAAA;EDGyC;;;;ECKrD,SAAA,CAAU,IAAA;EAAA,UAaA,iBAAA,IAAqB,MAAA;EAAA,UAerB,WAAA,CAAY,CAAA;AAAA"}
|
package/dist/router/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":[],"sources":["../../src/router/providers/RouterProvider.ts","../../src/router/TemplatedPathParser.ts"],"sourcesContent":["import { AlephaError } from \"alepha\";\n\nexport abstract class RouterProvider<T extends Route = Route> {\n protected routePathRegex: RegExp = /^\\/[A-Za-z0-9._~!$&%'()*+,;=:@{}?/-]*$/;\n\n protected tree: Tree<T> = { children: {} };\n protected cache = new Map<string, RouteMatch<T>>();\n protected maxCacheSize = 10_000;\n\n public match(path: string): RouteMatch<T> {\n const pathname = path.split(\"?\", 1)[0];\n const hit = this.cache.get(pathname);\n if (hit) {\n return { route: hit.route, params: { ...hit.params } };\n }\n const result = this.mapParams(this.createRouteMatch(pathname));\n if (this.cache.size >= this.maxCacheSize) this.cache.clear();\n this.cache.set(pathname, result);\n return { route: result.route, params: { ...result.params } };\n }\n\n protected test(path: string): void {\n if (!this.routePathRegex.test(path)) {\n throw new AlephaError(`Route '${path}' is not valid`);\n }\n }\n\n protected push(route: T): void {\n const path = route.path.replaceAll(\"//\", \"/\");\n\n this.test(path);\n this.cache.clear();\n\n const parts = this.createParts(path);\n\n let cursor = this.tree;\n for (let i = 0; i < parts.length; i++) {\n const isLast = i === parts.length - 1;\n let part = parts[i].toLowerCase(); // url is case-insensitive\n if (part === \"*\" && isLast) {\n cursor.wildcard = { route };\n break;\n }\n\n if (part.includes(\"*\")) {\n throw new AlephaError(`Route '${path}' has an invalid wildcard syntax`);\n }\n\n if (part.includes(\"{\") || part.includes(\"}\")) {\n if (part.startsWith(\"{\") && part.endsWith(\"}\")) {\n part = `:${part.slice(1, -1)}`; // convert {param} to :param\n } else {\n throw new AlephaError(`Route '${path}' has an invalid param syntax`);\n }\n }\n\n if (part.startsWith(\":\")) {\n const name = parts[i].slice(1).replaceAll(\"}\", \"\");\n if (!name) {\n throw new AlephaError(`Route '${path}' has an empty param name`);\n }\n if (!cursor.param) {\n cursor.param = { name, children: {} };\n } else if (cursor.param.name !== name) {\n // damn, 2 url params with different names\n // got this case with /customers/:id and /customers/:userId/payments\n route.mapParams ??= {};\n route.mapParams[cursor.param.name] = name;\n }\n\n if (isLast) {\n cursor.param.route = route;\n }\n\n cursor = cursor.param;\n continue;\n }\n\n if (!cursor.children[part]) {\n cursor.children[part] = { children: {} };\n }\n\n if (isLast) {\n cursor.children[part].route = route;\n }\n\n cursor = cursor.children[part];\n }\n }\n\n protected createRouteMatch(path: string): RouteMatch<T> {\n if (path[0] !== \"/\") {\n throw new AlephaError(`Path '${path}' must start with \"/\"`);\n }\n\n const parts = this.createParts(path);\n\n let cursor = this.tree;\n let wildcard: { route: T } | undefined;\n const params: Record<string, string> = {};\n\n for (let i = 0; i < parts.length; i++) {\n const part = parts[i].toLowerCase(); // url is case-insensitive\n if (cursor.children[part]) {\n if (cursor.wildcard) {\n wildcard = cursor.wildcard;\n }\n cursor = cursor.children[part];\n } else if (cursor.param) {\n if (cursor.wildcard) {\n wildcard = cursor.wildcard;\n }\n params[cursor.param.name] = parts[i];\n cursor = cursor.param;\n } else if (cursor.wildcard) {\n params[\"*\"] = parts.slice(i).join(\"/\");\n return { route: cursor.wildcard.route, params };\n } else {\n return { route: wildcard?.route, params };\n }\n }\n\n if (!cursor?.route) {\n // when \"/a/*\" - trigger if \"/a\"\n if (cursor.wildcard) {\n return { route: cursor.wildcard.route, params };\n }\n // return deep wildcard or nothing\n return { route: wildcard?.route, params };\n }\n\n return { route: cursor.route, params };\n }\n\n protected mapParams(match: RouteMatch<T>): RouteMatch<T> {\n if (match.route?.mapParams && match.params) {\n for (const [key, value] of Object.entries(match.route.mapParams)) {\n if (match.params[key]) {\n match.params[value] = match.params[key];\n delete match.params[key];\n }\n }\n }\n\n return match;\n }\n\n protected createParts(path: string): string[] {\n let pathname = path.split(\"?\")[0].replaceAll(\"//\", \"/\");\n\n // remove trailing slash\n if (pathname.endsWith(\"/\") && pathname.length > 1) {\n pathname = pathname.slice(0, -1);\n }\n\n return pathname.split(\"/\").slice(1);\n }\n}\n\nexport interface RouteMatch<T extends Route> {\n route?: T;\n params?: Record<string, string>;\n}\n\nexport interface Route {\n path: string;\n\n /**\n * Rename a param in the route.\n * This is automatically filled when you have scenarios like:\n * `/customers/:id` and `/customers/:userId/payments`\n *\n * In this case, `:id` will be renamed to `:userId` in the second route.\n */\n mapParams?: Record<string, string>;\n}\n\nexport interface Tree<T extends Route> {\n route?: T;\n children: {\n [key: string]: Tree<T>;\n };\n param?: {\n route?: T;\n name: string;\n children: {\n [key: string]: Tree<T>;\n };\n };\n wildcard?: {\n route: T;\n };\n}\n","import { AlephaError } from \"alepha\";\n\n/**\n * Parses and manipulates templated paths with `{param}` placeholders.\n *\n * Used by both RouterProvider (HTTP routes) and TopicProvider (pub/sub topics)\n * to handle parameterized path templates in a unified way.\n *\n * @example\n * ```ts\n * const parser = new TemplatedPathParser(\"/users/{userId}/posts/{postId}\");\n * parser.interpolate({ userId: \"7\", postId: \"42\" }); // \"/users/7/posts/42\"\n * parser.extract(\"/users/7/posts/42\"); // { userId: \"7\", postId: \"42\" }\n * parser.wildcardize(\"+\"); // \"/users/+/posts/+\"\n * ```\n *\n * @example\n * ```ts\n * // Redis-style colon-separated keys\n * const parser = new TemplatedPathParser(\"cache:{namespace}:{key}\", \":\");\n * parser.interpolate({ namespace: \"users\", key: \"42\" }); // \"cache:users:42\"\n * parser.wildcardize(\"*\"); // \"cache:*:*\"\n * ```\n */\nexport class TemplatedPathParser {\n protected static readonly PARAM_REGEX = /\\{([^}]+)\\}/g;\n\n public readonly template: string;\n public readonly separator: string;\n public readonly paramNames: readonly string[];\n public readonly hasParams: boolean;\n protected readonly extractRegex: RegExp | null;\n\n constructor(template: string, separator = \"/\") {\n if (separator.length !== 1) {\n throw new AlephaError(\n `TemplatedPathParser separator must be a single character, got '${separator}'`,\n );\n }\n this.template = template;\n this.separator = separator;\n this.paramNames = [\n ...template.matchAll(TemplatedPathParser.PARAM_REGEX),\n ].map((m) => m[1]);\n this.hasParams = this.paramNames.length > 0;\n this.extractRegex = this.hasParams ? this.buildExtractRegex() : null;\n }\n\n /**\n * Replaces each `{param}` in the template with the corresponding value\n * from the provided params record.\n */\n interpolate(params: Record<string, string>): string {\n return this.template.replace(\n TemplatedPathParser.PARAM_REGEX,\n (_, name: string) => params[name] ?? `{${name}}`,\n );\n }\n\n /**\n * Extracts parameter values from a concrete path by matching it against\n * the template structure.\n *\n * Returns `null` when the path does not match the template.\n * Returns `{}` when the template has no parameters and the path matches.\n */\n extract(path: string): Record<string, string> | null {\n if (!this.extractRegex) {\n return path === this.template ? {} : null;\n }\n\n const match = this.extractRegex.exec(path);\n if (!match) {\n return null;\n }\n\n const result: Record<string, string> = {};\n for (let i = 0; i < this.paramNames.length; i++) {\n result[this.paramNames[i]] = match[i + 1];\n }\n return result;\n }\n\n /**\n * Replaces each `{param}` placeholder in the template with the given\n * wildcard string. Defaults to `\"+\"` (MQTT-style).\n */\n wildcardize(wildcard = \"+\"): string {\n return this.template.replace(TemplatedPathParser.PARAM_REGEX, wildcard);\n }\n\n /**\n * Normalises a path by collapsing repeated separators and stripping a\n * trailing separator (unless the path is just the separator itself).\n */\n normalize(path: string): string {\n const sep = this.separator;\n const escapedSep = this.escapeRegex(sep);\n\n let result = path.replace(new RegExp(`${escapedSep}{2,}`, \"g\"), sep);\n\n if (result.endsWith(sep) && result.length > sep.length) {\n result = result.slice(0, -sep.length);\n }\n\n return result;\n }\n\n protected buildExtractRegex(): RegExp {\n const escapedSeparator = this.escapeRegex(this.separator);\n\n const regexSource = this.template\n .replace(/[.*+?^${}()|[\\]\\\\]/g, (char) => {\n if (char === \"{\" || char === \"}\") {\n return char;\n }\n return `\\\\${char}`;\n })\n .replace(/\\{[^}]+\\}/g, `([^${escapedSeparator}]+)`);\n\n return new RegExp(`^${regexSource}$`);\n }\n\n protected escapeRegex(s: string): string {\n return s.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\");\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../../src/router/providers/RouterProvider.ts","../../src/router/TemplatedPathParser.ts"],"sourcesContent":["import { AlephaError } from \"alepha\";\n\nexport abstract class RouterProvider<T extends Route = Route> {\n protected routePathRegex: RegExp = /^\\/[A-Za-z0-9._~!$&%'()*+,;=:@{}?/-]*$/;\n\n protected tree: Tree<T> = { children: {} };\n protected cache = new Map<string, RouteMatch<T>>();\n protected maxCacheSize = 10_000;\n\n public match(path: string): RouteMatch<T> {\n const pathname = path.split(\"?\", 1)[0];\n const hit = this.cache.get(pathname);\n if (hit) {\n return { route: hit.route, params: { ...hit.params } };\n }\n const result = this.mapParams(this.createRouteMatch(pathname));\n if (this.cache.size >= this.maxCacheSize) this.cache.clear();\n this.cache.set(pathname, result);\n return { route: result.route, params: { ...result.params } };\n }\n\n protected test(path: string): void {\n if (!this.routePathRegex.test(path)) {\n throw new AlephaError(`Route '${path}' is not valid`);\n }\n }\n\n protected push(route: T): void {\n const path = route.path.replaceAll(\"//\", \"/\");\n\n this.test(path);\n this.cache.clear();\n\n const parts = this.createParts(path);\n\n let cursor = this.tree;\n for (let i = 0; i < parts.length; i++) {\n const isLast = i === parts.length - 1;\n let part = parts[i].toLowerCase(); // url is case-insensitive\n if (part === \"*\" && isLast) {\n cursor.wildcard = { route };\n break;\n }\n\n if (part.includes(\"*\")) {\n throw new AlephaError(`Route '${path}' has an invalid wildcard syntax`);\n }\n\n if (part.includes(\"{\") || part.includes(\"}\")) {\n if (part.startsWith(\"{\") && part.endsWith(\"}\")) {\n part = `:${part.slice(1, -1)}`; // convert {param} to :param\n } else {\n throw new AlephaError(`Route '${path}' has an invalid param syntax`);\n }\n }\n\n if (part.startsWith(\":\")) {\n const name = parts[i].slice(1).replaceAll(\"}\", \"\");\n if (!name) {\n throw new AlephaError(`Route '${path}' has an empty param name`);\n }\n if (!cursor.param) {\n cursor.param = { name, children: {} };\n } else if (cursor.param.name !== name) {\n // damn, 2 url params with different names\n // got this case with /customers/:id and /customers/:userId/payments\n route.mapParams ??= {};\n route.mapParams[cursor.param.name] = name;\n }\n\n if (isLast) {\n cursor.param.route = route;\n }\n\n cursor = cursor.param;\n continue;\n }\n\n if (!cursor.children[part]) {\n cursor.children[part] = { children: {} };\n }\n\n if (isLast) {\n cursor.children[part].route = route;\n }\n\n cursor = cursor.children[part];\n }\n }\n\n protected createRouteMatch(path: string): RouteMatch<T> {\n if (path[0] !== \"/\") {\n throw new AlephaError(`Path '${path}' must start with \"/\"`);\n }\n\n const parts = this.createParts(path);\n\n let cursor = this.tree;\n let wildcard: { route: T } | undefined;\n const params: Record<string, string> = {};\n\n for (let i = 0; i < parts.length; i++) {\n const part = parts[i].toLowerCase(); // url is case-insensitive\n if (cursor.children[part]) {\n if (cursor.wildcard) {\n wildcard = cursor.wildcard;\n }\n cursor = cursor.children[part];\n } else if (cursor.param) {\n if (cursor.wildcard) {\n wildcard = cursor.wildcard;\n }\n params[cursor.param.name] = parts[i];\n cursor = cursor.param;\n } else if (cursor.wildcard) {\n params[\"*\"] = parts.slice(i).join(\"/\");\n return { route: cursor.wildcard.route, params };\n } else {\n return { route: wildcard?.route, params };\n }\n }\n\n if (!cursor?.route) {\n // when \"/a/*\" - trigger if \"/a\"\n if (cursor.wildcard) {\n return { route: cursor.wildcard.route, params };\n }\n // return deep wildcard or nothing\n return { route: wildcard?.route, params };\n }\n\n return { route: cursor.route, params };\n }\n\n protected mapParams(match: RouteMatch<T>): RouteMatch<T> {\n if (match.route?.mapParams && match.params) {\n for (const [key, value] of Object.entries(match.route.mapParams)) {\n if (match.params[key]) {\n match.params[value] = match.params[key];\n delete match.params[key];\n }\n }\n }\n\n return match;\n }\n\n protected createParts(path: string): string[] {\n let pathname = path.split(\"?\")[0].replaceAll(\"//\", \"/\");\n\n // remove trailing slash\n if (pathname.endsWith(\"/\") && pathname.length > 1) {\n pathname = pathname.slice(0, -1);\n }\n\n return pathname.split(\"/\").slice(1);\n }\n}\n\nexport interface RouteMatch<T extends Route> {\n route?: T;\n params?: Record<string, string>;\n}\n\nexport interface Route {\n path: string;\n\n /**\n * Rename a param in the route.\n * This is automatically filled when you have scenarios like:\n * `/customers/:id` and `/customers/:userId/payments`\n *\n * In this case, `:id` will be renamed to `:userId` in the second route.\n */\n mapParams?: Record<string, string>;\n}\n\nexport interface Tree<T extends Route> {\n route?: T;\n children: {\n [key: string]: Tree<T>;\n };\n param?: {\n route?: T;\n name: string;\n children: {\n [key: string]: Tree<T>;\n };\n };\n wildcard?: {\n route: T;\n };\n}\n","import { AlephaError } from \"alepha\";\n\n/**\n * Parses and manipulates templated paths with `{param}` placeholders.\n *\n * Used by both RouterProvider (HTTP routes) and TopicProvider (pub/sub topics)\n * to handle parameterized path templates in a unified way.\n *\n * @example\n * ```ts\n * const parser = new TemplatedPathParser(\"/users/{userId}/posts/{postId}\");\n * parser.interpolate({ userId: \"7\", postId: \"42\" }); // \"/users/7/posts/42\"\n * parser.extract(\"/users/7/posts/42\"); // { userId: \"7\", postId: \"42\" }\n * parser.wildcardize(\"+\"); // \"/users/+/posts/+\"\n * ```\n *\n * @example\n * ```ts\n * // Redis-style colon-separated keys\n * const parser = new TemplatedPathParser(\"cache:{namespace}:{key}\", \":\");\n * parser.interpolate({ namespace: \"users\", key: \"42\" }); // \"cache:users:42\"\n * parser.wildcardize(\"*\"); // \"cache:*:*\"\n * ```\n */\nexport class TemplatedPathParser {\n protected static readonly PARAM_REGEX = /\\{([^}]+)\\}/g;\n\n public readonly template: string;\n public readonly separator: string;\n public readonly paramNames: readonly string[];\n public readonly hasParams: boolean;\n protected readonly extractRegex: RegExp | null;\n\n constructor(template: string, separator = \"/\") {\n if (separator.length !== 1) {\n throw new AlephaError(\n `TemplatedPathParser separator must be a single character, got '${separator}'`,\n );\n }\n this.template = template;\n this.separator = separator;\n this.paramNames = [\n ...template.matchAll(TemplatedPathParser.PARAM_REGEX),\n ].map((m) => m[1]);\n this.hasParams = this.paramNames.length > 0;\n this.extractRegex = this.hasParams ? this.buildExtractRegex() : null;\n }\n\n /**\n * Replaces each `{param}` in the template with the corresponding value\n * from the provided params record.\n */\n interpolate(params: Record<string, string>): string {\n return this.template.replace(\n TemplatedPathParser.PARAM_REGEX,\n (_, name: string) => params[name] ?? `{${name}}`,\n );\n }\n\n /**\n * Extracts parameter values from a concrete path by matching it against\n * the template structure.\n *\n * Returns `null` when the path does not match the template.\n * Returns `{}` when the template has no parameters and the path matches.\n */\n extract(path: string): Record<string, string> | null {\n if (!this.extractRegex) {\n return path === this.template ? {} : null;\n }\n\n const match = this.extractRegex.exec(path);\n if (!match) {\n return null;\n }\n\n const result: Record<string, string> = {};\n for (let i = 0; i < this.paramNames.length; i++) {\n result[this.paramNames[i]] = match[i + 1];\n }\n return result;\n }\n\n /**\n * Replaces each `{param}` placeholder in the template with the given\n * wildcard string. Defaults to `\"+\"` (MQTT-style).\n */\n wildcardize(wildcard = \"+\"): string {\n return this.template.replace(TemplatedPathParser.PARAM_REGEX, wildcard);\n }\n\n /**\n * Normalises a path by collapsing repeated separators and stripping a\n * trailing separator (unless the path is just the separator itself).\n */\n normalize(path: string): string {\n const sep = this.separator;\n const escapedSep = this.escapeRegex(sep);\n\n let result = path.replace(new RegExp(`${escapedSep}{2,}`, \"g\"), sep);\n\n if (result.endsWith(sep) && result.length > sep.length) {\n result = result.slice(0, -sep.length);\n }\n\n return result;\n }\n\n protected buildExtractRegex(): RegExp {\n const escapedSeparator = this.escapeRegex(this.separator);\n\n const regexSource = this.template\n .replace(/[.*+?^${}()|[\\]\\\\]/g, (char) => {\n if (char === \"{\" || char === \"}\") {\n return char;\n }\n return `\\\\${char}`;\n })\n .replace(/\\{[^}]+\\}/g, `([^${escapedSeparator}]+)`);\n\n return new RegExp(`^${regexSource}$`);\n }\n\n protected escapeRegex(s: string): string {\n return s.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\");\n }\n}\n"],"mappings":";;AAEA,IAAsB,iBAAtB,MAA8D;CAC5D,iBAAmC;CAEnC,OAA0B,EAAE,UAAU,CAAC,EAAE;CACzC,wBAAkB,IAAI,IAA2B;CACjD,eAAyB;CAEzB,MAAa,MAA6B;EACxC,MAAM,WAAW,KAAK,MAAM,KAAK,CAAC,EAAE;EACpC,MAAM,MAAM,KAAK,MAAM,IAAI,QAAQ;EACnC,IAAI,KACF,OAAO;GAAE,OAAO,IAAI;GAAO,QAAQ,EAAE,GAAG,IAAI,OAAO;EAAE;EAEvD,MAAM,SAAS,KAAK,UAAU,KAAK,iBAAiB,QAAQ,CAAC;EAC7D,IAAI,KAAK,MAAM,QAAQ,KAAK,cAAc,KAAK,MAAM,MAAM;EAC3D,KAAK,MAAM,IAAI,UAAU,MAAM;EAC/B,OAAO;GAAE,OAAO,OAAO;GAAO,QAAQ,EAAE,GAAG,OAAO,OAAO;EAAE;CAC7D;CAEA,KAAe,MAAoB;EACjC,IAAI,CAAC,KAAK,eAAe,KAAK,IAAI,GAChC,MAAM,IAAI,YAAY,UAAU,KAAK,eAAe;CAExD;CAEA,KAAe,OAAgB;EAC7B,MAAM,OAAO,MAAM,KAAK,WAAW,MAAM,GAAG;EAE5C,KAAK,KAAK,IAAI;EACd,KAAK,MAAM,MAAM;EAEjB,MAAM,QAAQ,KAAK,YAAY,IAAI;EAEnC,IAAI,SAAS,KAAK;EAClB,KAAK,IAAI,IAAI,GAAG,IAAI,MAAM,QAAQ,KAAK;GACrC,MAAM,SAAS,MAAM,MAAM,SAAS;GACpC,IAAI,OAAO,MAAM,GAAG,YAAY;GAChC,IAAI,SAAS,OAAO,QAAQ;IAC1B,OAAO,WAAW,EAAE,MAAM;IAC1B;GACF;GAEA,IAAI,KAAK,SAAS,GAAG,GACnB,MAAM,IAAI,YAAY,UAAU,KAAK,iCAAiC;GAGxE,IAAI,KAAK,SAAS,GAAG,KAAK,KAAK,SAAS,GAAG,GACzC,IAAI,KAAK,WAAW,GAAG,KAAK,KAAK,SAAS,GAAG,GAC3C,OAAO,IAAI,KAAK,MAAM,GAAG,EAAE;QAE3B,MAAM,IAAI,YAAY,UAAU,KAAK,8BAA8B;GAIvE,IAAI,KAAK,WAAW,GAAG,GAAG;IACxB,MAAM,OAAO,MAAM,GAAG,MAAM,CAAC,EAAE,WAAW,KAAK,EAAE;IACjD,IAAI,CAAC,MACH,MAAM,IAAI,YAAY,UAAU,KAAK,0BAA0B;IAEjE,IAAI,CAAC,OAAO,OACV,OAAO,QAAQ;KAAE;KAAM,UAAU,CAAC;IAAE;SAC/B,IAAI,OAAO,MAAM,SAAS,MAAM;KAGrC,MAAM,cAAc,CAAC;KACrB,MAAM,UAAU,OAAO,MAAM,QAAQ;IACvC;IAEA,IAAI,QACF,OAAO,MAAM,QAAQ;IAGvB,SAAS,OAAO;IAChB;GACF;GAEA,IAAI,CAAC,OAAO,SAAS,OACnB,OAAO,SAAS,QAAQ,EAAE,UAAU,CAAC,EAAE;GAGzC,IAAI,QACF,OAAO,SAAS,MAAM,QAAQ;GAGhC,SAAS,OAAO,SAAS;EAC3B;CACF;CAEA,iBAA2B,MAA6B;EACtD,IAAI,KAAK,OAAO,KACd,MAAM,IAAI,YAAY,SAAS,KAAK,sBAAsB;EAG5D,MAAM,QAAQ,KAAK,YAAY,IAAI;EAEnC,IAAI,SAAS,KAAK;EAClB,IAAI;EACJ,MAAM,SAAiC,CAAC;EAExC,KAAK,IAAI,IAAI,GAAG,IAAI,MAAM,QAAQ,KAAK;GACrC,MAAM,OAAO,MAAM,GAAG,YAAY;GAClC,IAAI,OAAO,SAAS,OAAO;IACzB,IAAI,OAAO,UACT,WAAW,OAAO;IAEpB,SAAS,OAAO,SAAS;GAC3B,OAAO,IAAI,OAAO,OAAO;IACvB,IAAI,OAAO,UACT,WAAW,OAAO;IAEpB,OAAO,OAAO,MAAM,QAAQ,MAAM;IAClC,SAAS,OAAO;GAClB,OAAO,IAAI,OAAO,UAAU;IAC1B,OAAO,OAAO,MAAM,MAAM,CAAC,EAAE,KAAK,GAAG;IACrC,OAAO;KAAE,OAAO,OAAO,SAAS;KAAO;IAAO;GAChD,OACE,OAAO;IAAE,OAAO,UAAU;IAAO;GAAO;EAE5C;EAEA,IAAI,CAAC,QAAQ,OAAO;GAElB,IAAI,OAAO,UACT,OAAO;IAAE,OAAO,OAAO,SAAS;IAAO;GAAO;GAGhD,OAAO;IAAE,OAAO,UAAU;IAAO;GAAO;EAC1C;EAEA,OAAO;GAAE,OAAO,OAAO;GAAO;EAAO;CACvC;CAEA,UAAoB,OAAqC;EACvD,IAAI,MAAM,OAAO,aAAa,MAAM;QAC7B,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,MAAM,MAAM,SAAS,GAC7D,IAAI,MAAM,OAAO,MAAM;IACrB,MAAM,OAAO,SAAS,MAAM,OAAO;IACnC,OAAO,MAAM,OAAO;GACtB;;EAIJ,OAAO;CACT;CAEA,YAAsB,MAAwB;EAC5C,IAAI,WAAW,KAAK,MAAM,GAAG,EAAE,GAAG,WAAW,MAAM,GAAG;EAGtD,IAAI,SAAS,SAAS,GAAG,KAAK,SAAS,SAAS,GAC9C,WAAW,SAAS,MAAM,GAAG,EAAE;EAGjC,OAAO,SAAS,MAAM,GAAG,EAAE,MAAM,CAAC;CACpC;AACF;;;;;;;;;;;;;;;;;;;;;;;;;ACrIA,IAAa,sBAAb,MAAa,oBAAoB;CAC/B,OAA0B,cAAc;CAExC;CACA;CACA;CACA;CACA;CAEA,YAAY,UAAkB,YAAY,KAAK;EAC7C,IAAI,UAAU,WAAW,GACvB,MAAM,IAAI,YACR,kEAAkE,UAAU,EAC9E;EAEF,KAAK,WAAW;EAChB,KAAK,YAAY;EACjB,KAAK,aAAa,CAChB,GAAG,SAAS,SAAS,oBAAoB,WAAW,CACtD,EAAE,KAAK,MAAM,EAAE,EAAE;EACjB,KAAK,YAAY,KAAK,WAAW,SAAS;EAC1C,KAAK,eAAe,KAAK,YAAY,KAAK,kBAAkB,IAAI;CAClE;;;;;CAMA,YAAY,QAAwC;EAClD,OAAO,KAAK,SAAS,QACnB,oBAAoB,cACnB,GAAG,SAAiB,OAAO,SAAS,IAAI,KAAK,EAChD;CACF;;;;;;;;CASA,QAAQ,MAA6C;EACnD,IAAI,CAAC,KAAK,cACR,OAAO,SAAS,KAAK,WAAW,CAAC,IAAI;EAGvC,MAAM,QAAQ,KAAK,aAAa,KAAK,IAAI;EACzC,IAAI,CAAC,OACH,OAAO;EAGT,MAAM,SAAiC,CAAC;EACxC,KAAK,IAAI,IAAI,GAAG,IAAI,KAAK,WAAW,QAAQ,KAC1C,OAAO,KAAK,WAAW,MAAM,MAAM,IAAI;EAEzC,OAAO;CACT;;;;;CAMA,YAAY,WAAW,KAAa;EAClC,OAAO,KAAK,SAAS,QAAQ,oBAAoB,aAAa,QAAQ;CACxE;;;;;CAMA,UAAU,MAAsB;EAC9B,MAAM,MAAM,KAAK;EACjB,MAAM,aAAa,KAAK,YAAY,GAAG;EAEvC,IAAI,SAAS,KAAK,QAAQ,IAAI,OAAO,GAAG,WAAW,OAAO,GAAG,GAAG,GAAG;EAEnE,IAAI,OAAO,SAAS,GAAG,KAAK,OAAO,SAAS,IAAI,QAC9C,SAAS,OAAO,MAAM,GAAG,CAAC,IAAI,MAAM;EAGtC,OAAO;CACT;CAEA,oBAAsC;EACpC,MAAM,mBAAmB,KAAK,YAAY,KAAK,SAAS;EAExD,MAAM,cAAc,KAAK,SACtB,QAAQ,wBAAwB,SAAS;GACxC,IAAI,SAAS,OAAO,SAAS,KAC3B,OAAO;GAET,OAAO,KAAK;EACd,CAAC,EACA,QAAQ,cAAc,MAAM,iBAAiB,IAAI;EAEpD,OAAO,IAAI,OAAO,IAAI,YAAY,EAAE;CACtC;CAEA,YAAsB,GAAmB;EACvC,OAAO,EAAE,QAAQ,uBAAuB,MAAM;CAChD;AACF"}
|
|
@@ -1,8 +1,5 @@
|
|
|
1
|
-
import * as _$alepha from "alepha";
|
|
2
1
|
import { Alepha, Async, KIND, Primitive, Static } from "alepha";
|
|
3
2
|
import { DateTime, DateTimeProvider, DurationLike } from "alepha/datetime";
|
|
4
|
-
import * as _$alepha_logger0 from "alepha/logger";
|
|
5
|
-
import * as _$typebox from "typebox";
|
|
6
3
|
|
|
7
4
|
//#region ../../src/scheduler/constants/CRON.d.ts
|
|
8
5
|
declare const CRON: {
|
|
@@ -101,18 +98,18 @@ declare class Cron {
|
|
|
101
98
|
declare class CronProvider {
|
|
102
99
|
protected readonly dt: DateTimeProvider;
|
|
103
100
|
protected readonly alepha: Alepha;
|
|
104
|
-
protected readonly log:
|
|
101
|
+
protected readonly log: import("alepha/logger").Logger;
|
|
105
102
|
protected readonly cronJobs: Array<CronJob>;
|
|
106
103
|
getCronJobs(): Array<CronJob>;
|
|
107
|
-
protected readonly start:
|
|
108
|
-
protected readonly stop:
|
|
104
|
+
protected readonly start: import("alepha").HookPrimitive<"start">;
|
|
105
|
+
protected readonly stop: import("alepha").HookPrimitive<"stop">;
|
|
109
106
|
/**
|
|
110
107
|
* Generic serverless cron trigger. Vercel's platform-emitted entry
|
|
111
108
|
* point fires `serverless:cron` with the job name; we run the matching
|
|
112
109
|
* job in-process. On long-running runtimes this listener is harmless
|
|
113
110
|
* (no one fires the event).
|
|
114
111
|
*/
|
|
115
|
-
protected readonly onServerlessCron:
|
|
112
|
+
protected readonly onServerlessCron: import("alepha").HookPrimitive<"serverless:cron">;
|
|
116
113
|
protected boot(name: string | CronJob): void;
|
|
117
114
|
abort(name: string | CronJob): void;
|
|
118
115
|
/**
|
|
@@ -191,8 +188,8 @@ type SchedulerPrimitiveOptions = {
|
|
|
191
188
|
/**
|
|
192
189
|
* Scheduler configuration atom.
|
|
193
190
|
*/
|
|
194
|
-
declare const schedulerOptions:
|
|
195
|
-
prefix:
|
|
191
|
+
declare const schedulerOptions: import("alepha").Atom<import("typebox").TObject<{
|
|
192
|
+
prefix: import("typebox").TOptional<import("typebox").TString>;
|
|
196
193
|
}>, "alepha.scheduler.options">;
|
|
197
194
|
type SchedulerAtomOptions = Static<typeof schedulerOptions.schema>;
|
|
198
195
|
declare module "alepha" {
|
|
@@ -201,7 +198,7 @@ declare module "alepha" {
|
|
|
201
198
|
}
|
|
202
199
|
}
|
|
203
200
|
declare class SchedulerPrimitive extends Primitive<SchedulerPrimitiveOptions> {
|
|
204
|
-
protected readonly log:
|
|
201
|
+
protected readonly log: import("alepha/logger").Logger;
|
|
205
202
|
protected readonly settings: Readonly<{
|
|
206
203
|
prefix?: string | undefined;
|
|
207
204
|
}>;
|
|
@@ -211,7 +208,7 @@ declare class SchedulerPrimitive extends Primitive<SchedulerPrimitiveOptions> {
|
|
|
211
208
|
get name(): string;
|
|
212
209
|
protected onInit(): void;
|
|
213
210
|
trigger(): Promise<void>;
|
|
214
|
-
protected schedulerLock:
|
|
211
|
+
protected schedulerLock: import("alepha").PipelinePrimitiveFn<(args: SchedulerHandlerArguments) => Promise<void>>;
|
|
215
212
|
}
|
|
216
213
|
interface SchedulerHandlerArguments {
|
|
217
214
|
now: DateTime;
|
|
@@ -262,7 +259,7 @@ declare class WorkerdCronProvider extends CronProvider {
|
|
|
262
259
|
/**
|
|
263
260
|
* Handle a scheduled event from Cloudflare Workers.
|
|
264
261
|
*/
|
|
265
|
-
protected readonly onScheduledEvent:
|
|
262
|
+
protected readonly onScheduledEvent: import("alepha").HookPrimitive<"cloudflare:scheduled">;
|
|
266
263
|
}
|
|
267
264
|
//#endregion
|
|
268
265
|
//#region ../../src/scheduler/index.d.ts
|
|
@@ -314,7 +311,7 @@ declare module "alepha" {
|
|
|
314
311
|
*
|
|
315
312
|
* @module alepha.scheduler
|
|
316
313
|
*/
|
|
317
|
-
declare const AlephaScheduler:
|
|
314
|
+
declare const AlephaScheduler: import("alepha").Service<import("alepha").Module>;
|
|
318
315
|
//#endregion
|
|
319
316
|
export { $scheduler, AlephaScheduler, CRON, CronJob, CronProvider, SchedulerAtomOptions, SchedulerHandlerArguments, SchedulerPrimitive, SchedulerPrimitiveOptions, WorkerdCronProvider, schedulerOptions };
|
|
320
317
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","names":["
|
|
1
|
+
{"version":3,"file":"index.d.ts","names":["seconds","Set","minutes","hours","days","months","weekdays","ReadonlyArray","reversed","constructor","ICronDefinition","findAllowedHour","findAllowedMinute","findAllowedSecond","findAllowedTime","findAllowedDayInMonth","getNextDate","Date","startDate","getNextDates","amount","getNextDatesIterator","endDate","Generator","getPrevDate","getPrevDates","getPrevDatesIterator","matchDate","date"],"sources":["../../src/scheduler/constants/CRON.ts","../../../../node_modules/cron-schedule/dist/cron.d.ts","../../src/scheduler/providers/CronProvider.ts","../../src/scheduler/primitives/$scheduler.ts","../../src/scheduler/providers/WorkerdCronProvider.ts","../../src/scheduler/index.ts"],"x_google_ignoreList":[1],"mappings":"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"}
|