alepha 0.21.2 → 0.23.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +0 -1
- package/dist/api/audits/index.browser.js.map +1 -1
- package/dist/api/audits/index.d.ts +393 -403
- package/dist/api/audits/index.d.ts.map +1 -1
- package/dist/api/audits/index.js +25 -56
- package/dist/api/audits/index.js.map +1 -1
- package/dist/api/files/index.browser.js +31 -1
- package/dist/api/files/index.browser.js.map +1 -1
- package/dist/api/files/index.d.ts +313 -208
- package/dist/api/files/index.d.ts.map +1 -1
- package/dist/api/files/index.js +152 -42
- package/dist/api/files/index.js.map +1 -1
- package/dist/api/jobs/index.browser.js +2 -2
- package/dist/api/jobs/index.browser.js.map +1 -1
- package/dist/api/jobs/index.d.ts +282 -285
- package/dist/api/jobs/index.d.ts.map +1 -1
- package/dist/api/jobs/index.js +39 -33
- package/dist/api/jobs/index.js.map +1 -1
- package/dist/api/keys/index.d.ts +217 -222
- package/dist/api/keys/index.d.ts.map +1 -1
- package/dist/api/keys/index.js.map +1 -1
- package/dist/api/notifications/index.browser.js.map +1 -1
- package/dist/api/notifications/index.d.ts +188 -195
- package/dist/api/notifications/index.d.ts.map +1 -1
- package/dist/api/notifications/index.js.map +1 -1
- package/dist/api/oauth/index.d.ts +71 -76
- package/dist/api/oauth/index.d.ts.map +1 -1
- package/dist/api/oauth/index.js.map +1 -1
- package/dist/api/organizations/index.browser.js.map +1 -1
- package/dist/api/organizations/index.d.ts +104 -109
- package/dist/api/organizations/index.d.ts.map +1 -1
- package/dist/api/organizations/index.js.map +1 -1
- package/dist/api/parameters/index.browser.js +43 -16
- package/dist/api/parameters/index.browser.js.map +1 -1
- package/dist/api/parameters/index.d.ts +488 -344
- package/dist/api/parameters/index.d.ts.map +1 -1
- package/dist/api/parameters/index.js +175 -35
- package/dist/api/parameters/index.js.map +1 -1
- package/dist/api/payments/index.d.ts +396 -402
- package/dist/api/payments/index.d.ts.map +1 -1
- package/dist/api/payments/index.js.map +1 -1
- package/dist/api/subscriptions/index.d.ts +644 -652
- package/dist/api/subscriptions/index.d.ts.map +1 -1
- package/dist/api/subscriptions/index.js +1 -1
- package/dist/api/subscriptions/index.js.map +1 -1
- package/dist/api/users/index.browser.js +7 -0
- package/dist/api/users/index.browser.js.map +1 -1
- package/dist/api/users/index.d.ts +1106 -1005
- package/dist/api/users/index.d.ts.map +1 -1
- package/dist/api/users/index.js +307 -64
- package/dist/api/users/index.js.map +1 -1
- package/dist/api/verifications/index.browser.js.map +1 -1
- package/dist/api/verifications/index.d.ts +137 -143
- package/dist/api/verifications/index.d.ts.map +1 -1
- package/dist/api/verifications/index.js.map +1 -1
- package/dist/background/index.d.ts +95 -0
- package/dist/background/index.d.ts.map +1 -0
- package/dist/background/index.js +121 -0
- package/dist/background/index.js.map +1 -0
- package/dist/background/index.workerd.js +110 -0
- package/dist/background/index.workerd.js.map +1 -0
- package/dist/batch/index.d.ts +5 -7
- package/dist/batch/index.d.ts.map +1 -1
- package/dist/batch/index.js.map +1 -1
- package/dist/bin/index.js.map +1 -1
- package/dist/bucket/index.d.ts +76 -54
- package/dist/bucket/index.d.ts.map +1 -1
- package/dist/bucket/index.js +58 -11
- package/dist/bucket/index.js.map +1 -1
- package/dist/bucket/index.workerd.js +200 -5
- package/dist/bucket/index.workerd.js.map +1 -1
- package/dist/cache/core/index.d.ts +7 -10
- package/dist/cache/core/index.d.ts.map +1 -1
- package/dist/cache/core/index.js.map +1 -1
- package/dist/cache/core/index.workerd.js.map +1 -1
- package/dist/cache/database/index.d.ts +22 -26
- package/dist/cache/database/index.d.ts.map +1 -1
- package/dist/cache/database/index.js.map +1 -1
- package/dist/cache/redis/index.d.ts +4 -7
- package/dist/cache/redis/index.d.ts.map +1 -1
- package/dist/cache/redis/index.js.map +1 -1
- package/dist/captcha/index.d.ts +3 -6
- package/dist/captcha/index.d.ts.map +1 -1
- package/dist/captcha/index.js.map +1 -1
- package/dist/cli/config/index.d.ts.map +1 -1
- package/dist/cli/config/index.js.map +1 -1
- package/dist/cli/core/index.d.ts +458 -249
- package/dist/cli/core/index.d.ts.map +1 -1
- package/dist/cli/core/index.js +372 -660
- package/dist/cli/core/index.js.map +1 -1
- package/dist/cli/devtools/index.d.ts +3 -5
- package/dist/cli/devtools/index.d.ts.map +1 -1
- package/dist/cli/devtools/index.js.map +1 -1
- package/dist/cli/i18n/index.d.ts +20 -17
- package/dist/cli/i18n/index.d.ts.map +1 -1
- package/dist/cli/i18n/index.js +45 -11
- package/dist/cli/i18n/index.js.map +1 -1
- package/dist/cli/platform/index.d.ts +126 -1342
- package/dist/cli/platform/index.d.ts.map +1 -1
- package/dist/cli/platform/index.js +136 -2374
- package/dist/cli/platform/index.js.map +1 -1
- package/dist/cli/platform-lib/index.d.ts +1472 -0
- package/dist/cli/platform-lib/index.d.ts.map +1 -0
- package/dist/cli/platform-lib/index.js +2660 -0
- package/dist/cli/platform-lib/index.js.map +1 -0
- package/dist/cli/vendor/index.d.ts +17 -21
- package/dist/cli/vendor/index.d.ts.map +1 -1
- package/dist/cli/vendor/index.js.map +1 -1
- package/dist/command/index.d.ts +20 -19
- package/dist/command/index.d.ts.map +1 -1
- package/dist/command/index.js +39 -10
- package/dist/command/index.js.map +1 -1
- package/dist/{containers → container}/core/index.d.ts +13 -15
- package/dist/container/core/index.d.ts.map +1 -0
- package/dist/{containers → container}/core/index.js +23 -14
- package/dist/container/core/index.js.map +1 -0
- package/dist/{containers → container}/core/index.workerd.js +37 -22
- package/dist/container/core/index.workerd.js.map +1 -0
- package/dist/core/index.browser.js +27 -1
- package/dist/core/index.browser.js.map +1 -1
- package/dist/core/index.d.ts +48 -24
- package/dist/core/index.d.ts.map +1 -1
- package/dist/core/index.js +27 -1
- package/dist/core/index.js.map +1 -1
- package/dist/core/index.native.js +27 -1
- package/dist/core/index.native.js.map +1 -1
- package/dist/core/index.workerd.js +27 -1
- package/dist/core/index.workerd.js.map +1 -1
- package/dist/crypto/index.browser.js.map +1 -1
- package/dist/crypto/index.d.ts +5 -8
- package/dist/crypto/index.d.ts.map +1 -1
- package/dist/crypto/index.js.map +1 -1
- package/dist/datetime/index.d.ts +3 -4
- package/dist/datetime/index.d.ts.map +1 -1
- package/dist/datetime/index.js.map +1 -1
- package/dist/email/brevo/index.d.ts +2 -4
- package/dist/email/brevo/index.d.ts.map +1 -1
- package/dist/email/brevo/index.js.map +1 -1
- package/dist/email/cloudflare/index.d.ts +20 -7
- package/dist/email/cloudflare/index.d.ts.map +1 -1
- package/dist/email/cloudflare/index.js +46 -9
- package/dist/email/cloudflare/index.js.map +1 -1
- package/dist/email/core/index.d.ts +6 -9
- package/dist/email/core/index.d.ts.map +1 -1
- package/dist/email/core/index.js.map +1 -1
- package/dist/email/core/index.workerd.js.map +1 -1
- package/dist/email/smtp/index.d.ts +10 -13
- package/dist/email/smtp/index.d.ts.map +1 -1
- package/dist/email/smtp/index.js +107 -32
- package/dist/email/smtp/index.js.map +1 -1
- package/dist/fake/index.d.ts +1 -2
- package/dist/fake/index.d.ts.map +1 -1
- package/dist/fake/index.js.map +1 -1
- package/dist/lock/core/index.d.ts +9 -14
- package/dist/lock/core/index.d.ts.map +1 -1
- package/dist/lock/core/index.js.map +1 -1
- package/dist/lock/redis/index.d.ts +2 -4
- package/dist/lock/redis/index.d.ts.map +1 -1
- package/dist/lock/redis/index.js.map +1 -1
- package/dist/logger/index.d.ts +105 -76
- package/dist/logger/index.d.ts.map +1 -1
- package/dist/logger/index.js +196 -174
- package/dist/logger/index.js.map +1 -1
- package/dist/mcp/index.d.ts +25 -20
- package/dist/mcp/index.d.ts.map +1 -1
- package/dist/mcp/index.js +23 -0
- package/dist/mcp/index.js.map +1 -1
- package/dist/orm/core/index.browser.js.map +1 -1
- package/dist/orm/core/index.bun.js +19 -1
- package/dist/orm/core/index.bun.js.map +1 -1
- package/dist/orm/core/index.d.ts +76 -62
- package/dist/orm/core/index.d.ts.map +1 -1
- package/dist/orm/core/index.js +20 -2
- package/dist/orm/core/index.js.map +1 -1
- package/dist/orm/postgres/index.bun.js.map +1 -1
- package/dist/orm/postgres/index.d.ts +28 -20
- package/dist/orm/postgres/index.d.ts.map +1 -1
- package/dist/orm/postgres/index.js.map +1 -1
- package/dist/queue/core/index.d.ts +12 -15
- package/dist/queue/core/index.d.ts.map +1 -1
- package/dist/queue/core/index.js.map +1 -1
- package/dist/queue/core/index.workerd.js.map +1 -1
- package/dist/queue/redis/index.d.ts +3 -5
- package/dist/queue/redis/index.d.ts.map +1 -1
- package/dist/queue/redis/index.js.map +1 -1
- package/dist/react/auth/index.browser.js +9 -2
- package/dist/react/auth/index.browser.js.map +1 -1
- package/dist/react/auth/index.d.ts +14 -9
- package/dist/react/auth/index.d.ts.map +1 -1
- package/dist/react/auth/index.js +9 -2
- package/dist/react/auth/index.js.map +1 -1
- package/dist/react/core/index.d.ts +7 -8
- package/dist/react/core/index.d.ts.map +1 -1
- package/dist/react/core/index.js +6 -3
- package/dist/react/core/index.js.map +1 -1
- package/dist/react/form/index.d.ts +2 -5
- package/dist/react/form/index.d.ts.map +1 -1
- package/dist/react/form/index.js +16 -15
- package/dist/react/form/index.js.map +1 -1
- package/dist/react/head/index.browser.js.map +1 -1
- package/dist/react/head/index.d.ts +2 -4
- package/dist/react/head/index.d.ts.map +1 -1
- package/dist/react/head/index.js.map +1 -1
- package/dist/react/i18n/index.d.ts +90 -11
- package/dist/react/i18n/index.d.ts.map +1 -1
- package/dist/react/i18n/index.js +147 -11
- package/dist/react/i18n/index.js.map +1 -1
- package/dist/react/intro/index.d.ts +1 -2
- package/dist/react/intro/index.d.ts.map +1 -1
- package/dist/react/intro/index.js +2 -2
- package/dist/react/intro/index.js.map +1 -1
- package/dist/react/router/index.browser.js +193 -24
- package/dist/react/router/index.browser.js.map +1 -1
- package/dist/react/router/index.d.ts +434 -222
- package/dist/react/router/index.d.ts.map +1 -1
- package/dist/react/router/index.js +249 -35
- package/dist/react/router/index.js.map +1 -1
- package/dist/react/sitemap/index.browser.js +35 -0
- package/dist/react/sitemap/index.browser.js.map +1 -0
- package/dist/react/sitemap/index.d.ts +92 -0
- package/dist/react/sitemap/index.d.ts.map +1 -0
- package/dist/react/sitemap/index.js +131 -0
- package/dist/react/sitemap/index.js.map +1 -0
- package/dist/react/testing/index.d.ts +1 -2
- package/dist/react/testing/index.d.ts.map +1 -1
- package/dist/react/testing/index.js +16 -17
- package/dist/react/testing/index.js.map +1 -1
- package/dist/react/ui/index.d.ts +20 -25
- package/dist/react/ui/index.d.ts.map +1 -1
- package/dist/react/ui/index.js.map +1 -1
- package/dist/redis/index.bun.js.map +1 -1
- package/dist/redis/index.d.ts +17 -19
- package/dist/redis/index.d.ts.map +1 -1
- package/dist/redis/index.js.map +1 -1
- package/dist/retry/index.d.ts +2 -4
- package/dist/retry/index.d.ts.map +1 -1
- package/dist/retry/index.js.map +1 -1
- package/dist/router/index.d.ts.map +1 -1
- package/dist/router/index.js.map +1 -1
- package/dist/scheduler/index.d.ts +10 -13
- package/dist/scheduler/index.d.ts.map +1 -1
- package/dist/scheduler/index.js.map +1 -1
- package/dist/scheduler/index.workerd.js.map +1 -1
- package/dist/security/index.browser.js.map +1 -1
- package/dist/security/index.d.ts +45 -48
- package/dist/security/index.d.ts.map +1 -1
- package/dist/security/index.js.map +1 -1
- package/dist/server/auth/index.browser.js.map +1 -1
- package/dist/server/auth/index.d.ts +272 -173
- package/dist/server/auth/index.d.ts.map +1 -1
- package/dist/server/auth/index.js +1608 -15
- package/dist/server/auth/index.js.map +1 -1
- package/dist/server/cookies/index.browser.js.map +1 -1
- package/dist/server/cookies/index.d.ts +20 -7
- package/dist/server/cookies/index.d.ts.map +1 -1
- package/dist/server/cookies/index.js +22 -3
- package/dist/server/cookies/index.js.map +1 -1
- package/dist/server/core/index.browser.js.map +1 -1
- package/dist/server/core/index.d.ts +106 -73
- package/dist/server/core/index.d.ts.map +1 -1
- package/dist/server/core/index.js +44 -0
- package/dist/server/core/index.js.map +1 -1
- package/dist/server/cors/index.d.ts +11 -14
- package/dist/server/cors/index.d.ts.map +1 -1
- package/dist/server/cors/index.js.map +1 -1
- package/dist/server/etag/index.d.ts +6 -9
- package/dist/server/etag/index.d.ts.map +1 -1
- package/dist/server/etag/index.js.map +1 -1
- package/dist/server/health/index.d.ts +18 -21
- package/dist/server/health/index.d.ts.map +1 -1
- package/dist/server/health/index.js.map +1 -1
- package/dist/server/links/index.browser.js +2 -0
- package/dist/server/links/index.browser.js.map +1 -1
- package/dist/server/links/index.d.ts +63 -67
- package/dist/server/links/index.d.ts.map +1 -1
- package/dist/server/links/index.js +2 -0
- package/dist/server/links/index.js.map +1 -1
- package/dist/server/metrics/index.d.ts +5 -7
- package/dist/server/metrics/index.d.ts.map +1 -1
- package/dist/server/metrics/index.js.map +1 -1
- package/dist/server/proxy/index.d.ts +3 -5
- package/dist/server/proxy/index.d.ts.map +1 -1
- package/dist/server/proxy/index.js.map +1 -1
- package/dist/server/rate-limit/index.d.ts +10 -13
- package/dist/server/rate-limit/index.d.ts.map +1 -1
- package/dist/server/rate-limit/index.js.map +1 -1
- package/dist/server/static/index.d.ts +3 -5
- package/dist/server/static/index.d.ts.map +1 -1
- package/dist/server/static/index.js.map +1 -1
- package/dist/server/swagger/index.d.ts +5 -8
- package/dist/server/swagger/index.d.ts.map +1 -1
- package/dist/server/swagger/index.js.map +1 -1
- package/dist/sms/index.d.ts +3 -5
- package/dist/sms/index.d.ts.map +1 -1
- package/dist/sms/index.js.map +1 -1
- package/dist/system/index.browser.js.map +1 -1
- package/dist/system/index.d.ts +2 -4
- package/dist/system/index.d.ts.map +1 -1
- package/dist/system/index.js.map +1 -1
- package/dist/system/index.workerd.js.map +1 -1
- package/dist/topic/core/index.d.ts +4 -6
- package/dist/topic/core/index.d.ts.map +1 -1
- package/dist/topic/core/index.js.map +1 -1
- package/dist/topic/redis/index.d.ts +5 -8
- package/dist/topic/redis/index.d.ts.map +1 -1
- package/dist/topic/redis/index.js.map +1 -1
- package/package.json +59 -23
- package/src/api/audits/__tests__/AuditService.spec.ts +18 -110
- package/src/api/audits/controllers/AdminAuditController.ts +14 -0
- package/src/api/audits/services/AuditService.ts +21 -88
- package/src/api/files/__tests__/FileService.spec.ts +207 -2
- package/src/api/files/index.ts +3 -0
- package/src/api/files/schemas/fileCreatorSummarySchema.ts +22 -0
- package/src/api/files/schemas/fileResourceSchema.ts +10 -1
- package/src/api/files/services/FileService.ts +170 -72
- package/src/api/jobs/__tests__/$job.spec.ts +24 -1
- package/src/api/jobs/index.ts +4 -3
- package/src/api/jobs/primitives/$job.ts +7 -3
- package/src/api/jobs/providers/DirectJobDispatcher.ts +17 -36
- package/src/api/jobs/providers/JobProvider.ts +53 -24
- package/src/api/jobs/schemas/jobConfigAtom.ts +1 -1
- package/src/api/jobs/schemas/jobExecutionResourceSchema.ts +4 -1
- package/src/api/keys/schemas/adminApiKeyResourceSchema.ts +3 -1
- package/src/api/parameters/__tests__/$parameter.spec.ts +19 -2
- package/src/api/parameters/audits/ParameterAudits.ts +17 -0
- package/src/api/parameters/controllers/AdminParameterController.ts +95 -19
- package/src/api/parameters/index.ts +3 -0
- package/src/api/parameters/schemas/activateParameterBodySchema.ts +3 -3
- package/src/api/parameters/schemas/createParameterVersionBodySchema.ts +3 -2
- package/src/api/parameters/schemas/parameterCreatorSummarySchema.ts +25 -0
- package/src/api/parameters/schemas/parameterResponseSchema.ts +5 -0
- package/src/api/parameters/schemas/rollbackParameterBodySchema.ts +4 -2
- package/src/api/parameters/services/ParameterProvider.ts +69 -6
- package/src/api/subscriptions/jobs/SubscriptionJobs.ts +1 -1
- package/src/api/users/__tests__/AdminSessionController.spec.ts +37 -0
- package/src/api/users/audits/SessionAudits.ts +33 -0
- package/src/api/users/audits/UserAudits.ts +19 -43
- package/src/api/users/controllers/AdminUserController.ts +66 -1
- package/src/api/users/controllers/RealmController.ts +1 -0
- package/src/api/users/entities/sessions.ts +6 -0
- package/src/api/users/entities/users.ts +2 -0
- package/src/api/users/index.ts +9 -1
- package/src/api/users/primitives/$realm.ts +29 -0
- package/src/api/users/providers/RealmProvider.ts +15 -0
- package/src/api/users/schemas/realmConfigSchema.ts +14 -0
- package/src/api/users/schemas/sessionResourceSchema.ts +16 -0
- package/src/api/users/schemas/updateUserSchema.ts +1 -8
- package/src/api/users/schemas/userQuerySchema.ts +7 -0
- package/src/api/users/services/CredentialService.ts +15 -6
- package/src/api/users/services/IdentityService.ts +2 -1
- package/src/api/users/services/RegistrationService.ts +2 -1
- package/src/api/users/services/SessionCrudService.ts +19 -2
- package/src/api/users/services/SessionService.ts +39 -19
- package/src/api/users/services/UserService.ts +106 -8
- package/src/background/__tests__/BackgroundTaskProvider.spec.ts +96 -0
- package/src/background/index.ts +37 -0
- package/src/background/index.workerd.ts +28 -0
- package/src/background/providers/BackgroundTaskProvider.ts +70 -0
- package/src/background/providers/WorkerdBackgroundTaskProvider.ts +43 -0
- package/src/bucket/__tests__/$bucket.spec.ts +18 -0
- package/src/bucket/__tests__/LocalFileStorageProvider.spec.ts +5 -0
- package/src/bucket/__tests__/MemoryFileStorageProvider.spec.ts +5 -0
- package/src/bucket/__tests__/NodeS3BucketProvider.spec.ts +23 -4
- package/src/bucket/__tests__/shared.ts +30 -0
- package/src/bucket/index.ts +5 -5
- package/src/bucket/index.workerd.ts +11 -4
- package/src/bucket/primitives/$bucket.ts +27 -0
- package/src/bucket/providers/FileStorageProvider.ts +13 -0
- package/src/bucket/providers/LocalFileStorageProvider.ts +17 -1
- package/src/bucket/providers/MemoryFileStorageProvider.ts +7 -0
- package/src/bucket/providers/{CloudflareR2Provider.ts → R2FileStorageProvider.ts} +10 -1
- package/src/bucket/providers/{NodeS3BucketProvider.ts → S3FileStorageProvider.ts} +27 -5
- package/src/cli/core/__tests__/BuildDockerTask.spec.ts +25 -1
- package/src/cli/core/__tests__/init.spec.ts +0 -219
- package/src/cli/core/atoms/buildOptions.ts +0 -12
- package/src/cli/core/commands/__tests__/BuildCommand.spec.ts +43 -0
- package/src/cli/core/commands/build.ts +105 -37
- package/src/cli/core/commands/init.ts +0 -12
- package/src/cli/core/commands/pack.ts +133 -0
- package/src/cli/core/index.ts +3 -3
- package/src/cli/core/providers/ViteDevServerProvider.ts +40 -16
- package/src/cli/core/services/PackageManagerUtils.ts +0 -16
- package/src/cli/core/services/ProjectScaffolder.ts +29 -291
- package/src/cli/core/tasks/BuildCloudflareTask.ts +382 -56
- package/src/cli/core/tasks/BuildDockerTask.ts +33 -3
- package/src/cli/core/tasks/BuildPrerenderTask.ts +44 -7
- package/src/cli/core/tasks/BuildTask.ts +34 -0
- package/src/cli/core/templates/apiIndexTs.ts +1 -22
- package/src/cli/core/templates/mainCss.ts +0 -1
- package/src/cli/core/templates/webAppRouterTs.ts +0 -99
- package/src/cli/core/templates/webIndexTs.ts +1 -22
- package/src/cli/i18n/__tests__/I18nCheckService.spec.ts +48 -0
- package/src/cli/i18n/services/I18nCheckService.ts +65 -11
- package/src/cli/platform/__tests__/SecretsCommand.spec.ts +5 -3
- package/src/cli/platform/commands/SecretsCommand.ts +8 -6
- package/src/cli/platform/commands/platform.ts +192 -46
- package/src/cli/platform/index.ts +12 -52
- package/src/cli/{platform → platform-lib}/__tests__/CloudflareAdapter.spec.ts +426 -169
- package/src/cli/{platform → platform-lib}/__tests__/NamingService.spec.ts +91 -4
- package/src/cli/{platform → platform-lib}/__tests__/VercelAdapter.spec.ts +56 -85
- package/src/cli/{platform → platform-lib}/adapters/CloudflareAdapter.ts +519 -190
- package/src/cli/{platform → platform-lib}/adapters/PlatformAdapter.ts +62 -35
- package/src/cli/{platform → platform-lib}/adapters/VercelAdapter.ts +6 -10
- package/src/cli/{platform → platform-lib}/atoms/platformOptions.ts +34 -1
- package/src/cli/platform-lib/index.ts +67 -0
- package/src/cli/platform-lib/services/NamingService.ts +136 -0
- package/src/cli/{platform → platform-lib}/services/PlatformInspector.ts +60 -13
- package/src/cli/{platform → platform-lib}/services/PlatformOrchestrator.ts +54 -43
- package/src/cli/{platform → platform-lib}/services/WranglerApi.ts +4 -2
- package/src/command/__tests__/Runner.spec.ts +20 -0
- package/src/command/helpers/EnvUtils.ts +19 -3
- package/src/command/helpers/Runner.ts +12 -2
- package/src/command/providers/CliProvider.ts +34 -1
- package/src/{containers → container}/core/__tests__/$container.spec.ts +5 -5
- package/src/{containers → container}/core/index.ts +4 -4
- package/src/{containers → container}/core/index.workerd.ts +19 -3
- package/src/{containers → container}/core/primitives/$container.ts +1 -1
- package/src/{containers → container}/core/providers/CloudflareContainerProvider.ts +17 -19
- package/src/{containers → container}/core/providers/ContainerProvider.ts +16 -2
- package/src/{containers → container}/core/providers/MockContainerProvider.ts +1 -1
- package/src/core/Alepha.ts +49 -1
- package/src/core/__tests__/$env.spec.ts +42 -0
- package/src/core/__tests__/dump.spec.ts +47 -0
- package/src/email/cloudflare/__tests__/CloudflareEmailProvider.spec.ts +42 -10
- package/src/email/cloudflare/index.ts +14 -5
- package/src/email/cloudflare/providers/CloudflareEmailProvider.ts +54 -9
- package/src/logger/__tests__/Logger.spec.ts +55 -0
- package/src/logger/index.ts +13 -0
- package/src/logger/services/Logger.ts +31 -1
- package/src/mcp/__tests__/McpServerProvider.spec.ts +71 -0
- package/src/mcp/providers/McpServerProvider.ts +55 -0
- package/src/orm/__tests__/orm-showcase-tests.ts +27 -0
- package/src/orm/__tests__/orm-showcase.spec.ts +12 -0
- package/src/orm/core/interfaces/PgQuery.ts +4 -1
- package/src/orm/core/services/Repository.ts +27 -11
- package/src/react/auth/hooks/useAuth.ts +10 -5
- package/src/react/core/__tests__/useQuery.browser.spec.tsx +25 -0
- package/src/react/core/hooks/useAction.ts +14 -3
- package/src/react/core/hooks/useQuery.ts +24 -4
- package/src/react/form/__tests__/FormModel-submit-loading.spec.ts +71 -0
- package/src/react/form/__tests__/form-submitting-reactive.browser.spec.tsx +96 -0
- package/src/react/form/services/FormModel.ts +57 -39
- package/src/react/i18n/__tests__/I18nProvider.spec.ts +89 -0
- package/src/react/i18n/__tests__/locale-routing.spec.ts +107 -0
- package/src/react/i18n/components/Translate.tsx +47 -0
- package/src/react/i18n/index.ts +2 -0
- package/src/react/i18n/providers/I18nProvider.ts +171 -12
- package/src/react/intro/components/GettingStartedAdminSlide.tsx +2 -2
- package/src/react/router/__tests__/$page.spec.tsx +3 -2
- package/src/react/router/__tests__/RouterLocaleProvider.spec.ts +127 -0
- package/src/react/router/__tests__/page-can.spec.ts +18 -13
- package/src/react/router/hooks/useQueryParams.ts +114 -14
- package/src/react/router/index.browser.ts +4 -0
- package/src/react/router/index.shared.ts +1 -0
- package/src/react/router/index.ts +9 -0
- package/src/react/router/primitives/$page.ts +85 -4
- package/src/react/router/providers/ReactBrowserRouterProvider.ts +18 -8
- package/src/react/router/providers/ReactPageProvider.ts +12 -1
- package/src/react/router/providers/ReactServerProvider.ts +96 -14
- package/src/react/router/providers/RootComponentsProvider.ts +13 -0
- package/src/react/router/providers/RouterLocaleProvider.ts +125 -0
- package/src/react/router/providers/__tests__/RootComponentsProvider.spec.ts +15 -0
- package/src/react/router/providers/__tests__/rootComponents.ssr.browser.spec.tsx +67 -0
- package/src/react/sitemap/__tests__/$sitemap.spec.ts +131 -0
- package/src/react/sitemap/index.browser.ts +21 -0
- package/src/react/sitemap/index.ts +25 -0
- package/src/react/sitemap/primitives/$sitemap.browser.ts +26 -0
- package/src/react/sitemap/primitives/$sitemap.ts +196 -0
- package/src/react/ui/services/SchemaControl.ts +3 -4
- package/src/server/auth/__tests__/appleClientSecret.spec.ts +34 -0
- package/src/server/auth/__tests__/authFederationClient.spec.ts +40 -0
- package/src/server/auth/__tests__/federationAssertion.spec.ts +146 -0
- package/src/server/auth/__tests__/federationRedirectReplay.spec.ts +44 -0
- package/src/server/auth/helpers/appleClientSecret.ts +24 -0
- package/src/server/auth/helpers/federationAssertion.ts +74 -0
- package/src/server/auth/helpers/jtiReplayGuard.ts +41 -0
- package/src/server/auth/helpers/safeRedirectPath.ts +19 -0
- package/src/server/auth/index.ts +4 -0
- package/src/server/auth/primitives/$authFederationBroker.ts +273 -0
- package/src/server/auth/primitives/$authFederationClient.ts +89 -0
- package/src/server/auth/providers/ServerAuthProvider.ts +18 -4
- package/src/server/cookies/__tests__/ServerCookiesProvider.spec.ts +70 -0
- package/src/server/cookies/providers/ServerCookiesProvider.ts +23 -3
- package/src/server/core/interfaces/ServerRequest.ts +8 -0
- package/src/server/core/primitives/$route.ts +27 -0
- package/src/server/core/providers/ServerMultipartProvider.ts +19 -0
- package/src/server/links/providers/LinkProvider.ts +10 -0
- package/dist/containers/core/index.d.ts.map +0 -1
- package/dist/containers/core/index.js.map +0 -1
- package/dist/containers/core/index.workerd.js.map +0 -1
- package/src/cli/core/tasks/BuildSitemapTask.ts +0 -130
- package/src/cli/core/templates/componentsJsonTs.ts +0 -39
- package/src/cli/core/templates/saasAdminLayoutTsx.ts +0 -77
- package/src/cli/core/templates/saasAdminPagesTsx.ts +0 -26
- package/src/cli/core/templates/saasAuthLayoutTsx.ts +0 -22
- package/src/cli/core/templates/saasAuthPagesTsx.ts +0 -62
- package/src/cli/core/templates/saasRealmProviderTs.ts +0 -52
- package/src/cli/platform/services/NamingService.ts +0 -54
- /package/dist/orm/core/{chunk-o8xxKEmq.js → chunk-B4FMCO8f.js} +0 -0
- /package/dist/react/testing/{chunk-6Ep1yQYe.js → chunk-BpyX8vjI.js} +0 -0
- /package/src/cli/{platform → platform-lib}/__tests__/GitHubSecretStore.spec.ts +0 -0
- /package/src/cli/{platform → platform-lib}/__tests__/PlatformCacheProvider.spec.ts +0 -0
- /package/src/cli/{platform → platform-lib}/__tests__/PlatformInspector.spec.ts +0 -0
- /package/src/cli/{platform → platform-lib}/__tests__/PlatformOrchestrator.spec.ts +0 -0
- /package/src/cli/{platform → platform-lib}/__tests__/SecretFilterService.spec.ts +0 -0
- /package/src/cli/{platform → platform-lib}/__tests__/detectResources.spec.ts +0 -0
- /package/src/cli/{platform → platform-lib}/providers/GitHubSecretStore.ts +0 -0
- /package/src/cli/{platform → platform-lib}/providers/MemorySecretStore.ts +0 -0
- /package/src/cli/{platform → platform-lib}/providers/PlatformCacheProvider.ts +0 -0
- /package/src/cli/{platform → platform-lib}/providers/SecretStoreProvider.ts +0 -0
- /package/src/cli/{platform → platform-lib}/schemas/cloudflare.ts +0 -0
- /package/src/cli/{platform → platform-lib}/schemas/platform.ts +0 -0
- /package/src/cli/{platform → platform-lib}/schemas/vercel.ts +0 -0
- /package/src/cli/{platform → platform-lib}/services/CloudflareApi.ts +0 -0
- /package/src/cli/{platform → platform-lib}/services/SecretFilterService.ts +0 -0
- /package/src/cli/{platform → platform-lib}/services/VercelApi.ts +0 -0
- /package/src/cli/{platform → platform-lib}/services/VercelCli.ts +0 -0
- /package/src/{containers → container}/core/interfaces/ContainerOptions.ts +0 -0
- /package/src/{containers → container}/core/providers/NodeContainerProvider.ts +0 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.workerd.js","names":[],"sources":["../../src/bucket/errors/InvalidFileError.ts","../../src/bucket/providers/FileStorageProvider.ts","../../src/bucket/errors/FileNotFoundError.ts","../../src/bucket/providers/MemoryFileStorageProvider.ts","../../src/bucket/primitives/$bucket.ts","../../src/bucket/providers/CloudflareR2Provider.ts","../../src/bucket/index.workerd.ts"],"sourcesContent":["import { AlephaError } from \"alepha\";\n\nexport class InvalidFileError extends AlephaError {\n public readonly status = 400;\n}\n","import type { FileLike } from \"alepha\";\n\nexport abstract class FileStorageProvider {\n /**\n * Uploads a file to the storage.\n *\n * @param bucketName - Container name\n * @param file - File to upload\n * @param fileId - Optional file identifier. If not provided, a unique ID will be generated.\n * @return The identifier of the uploaded file.\n */\n abstract upload(\n bucketName: string,\n file: FileLike,\n fileId?: string,\n ): Promise<string>;\n\n /**\n * Downloads a file from the storage.\n *\n * @param bucketName - Container name\n * @param fileId - Identifier of the file to download\n * @return The downloaded file as a FileLike object.\n */\n abstract download(bucketName: string, fileId: string): Promise<FileLike>;\n\n /**\n * Check if fileId exists in the storage bucket.\n *\n * @param bucketName - Container name\n * @param fileId - Identifier of the file to stream\n * @return True is the file exists, false otherwise.\n */\n abstract exists(bucketName: string, fileId: string): Promise<boolean>;\n\n /**\n * Delete permanently a file from the storage.\n *\n * @param bucketName - Container name\n * @param fileId - Identifier of the file to delete\n */\n abstract delete(bucketName: string, fileId: string): Promise<void>;\n\n /**\n * Delete multiple files in one round-trip where the provider supports\n * batch (R2/S3, up to 1000 per call). Memory/Local fall back to a loop.\n *\n * @param bucketName - Container name\n * @param fileIds - Identifiers of the files to delete\n */\n abstract deleteMany(bucketName: string, fileIds: string[]): Promise<void>;\n}\n","import { AlephaError } from \"alepha\";\n\nexport class FileNotFoundError extends AlephaError {\n public readonly status = 404;\n}\n","import { $inject, type FileLike } from \"alepha\";\nimport { CryptoProvider } from \"alepha/crypto\";\nimport { FileDetector, FileSystemProvider } from \"alepha/system\";\nimport { FileNotFoundError } from \"../errors/FileNotFoundError.ts\";\nimport type { FileStorageProvider } from \"./FileStorageProvider.ts\";\n\ninterface StoredFile {\n buffer: Buffer;\n name: string;\n type: string;\n size: number;\n}\n\nexport class MemoryFileStorageProvider implements FileStorageProvider {\n public readonly files: Record<string, StoredFile> = {};\n protected readonly fileSystem = $inject(FileSystemProvider);\n protected readonly fileDetector = $inject(FileDetector);\n protected readonly crypto = $inject(CryptoProvider);\n\n public async upload(\n bucketName: string,\n file: FileLike,\n fileId?: string,\n ): Promise<string> {\n fileId ??= this.createId();\n\n // Consume the stream and store as buffer so downloads are repeatable\n const chunks: Uint8Array[] = [];\n for await (const chunk of file.stream() as AsyncIterable<Uint8Array>) {\n chunks.push(chunk);\n }\n const buffer = Buffer.concat(chunks);\n\n this.files[`${bucketName}/${fileId}`] = {\n buffer,\n name: file.name,\n type: file.type,\n size: file.size,\n };\n\n return fileId;\n }\n\n public async download(bucketName: string, fileId: string): Promise<FileLike> {\n const fileKey = `${bucketName}/${fileId}`;\n const stored = this.files[fileKey];\n\n if (!stored) {\n throw new FileNotFoundError(`File with ID ${fileId} not found.`);\n }\n\n // Create a fresh FileLike with a new stream from the stored buffer\n return this.fileSystem.createFile({\n stream: new Blob([new Uint8Array(stored.buffer)]).stream(),\n name: stored.name,\n type: stored.type,\n size: stored.size,\n });\n }\n\n public async exists(bucketName: string, fileId: string): Promise<boolean> {\n return `${bucketName}/${fileId}` in this.files;\n }\n\n public async delete(bucketName: string, fileId: string): Promise<void> {\n const fileKey = `${bucketName}/${fileId}`;\n if (!(fileKey in this.files)) {\n throw new FileNotFoundError(`File with ID ${fileId} not found.`);\n }\n\n delete this.files[fileKey];\n }\n\n public async deleteMany(\n bucketName: string,\n fileIds: string[],\n ): Promise<void> {\n for (const id of fileIds) {\n delete this.files[`${bucketName}/${id}`];\n }\n }\n\n protected createId(): string {\n return this.crypto.randomUUID();\n }\n}\n","import {\n $inject,\n createPrimitive,\n type FileLike,\n KIND,\n Primitive,\n type Service,\n} from \"alepha\";\nimport { FileSystemProvider } from \"alepha/system\";\nimport { InvalidFileError } from \"../errors/InvalidFileError.ts\";\nimport { FileStorageProvider } from \"../providers/FileStorageProvider.ts\";\nimport { MemoryFileStorageProvider } from \"../providers/MemoryFileStorageProvider.ts\";\n\n/**\n * Creates a bucket primitive for file storage and management with configurable validation.\n *\n * Provides a comprehensive file storage system that handles uploads, downloads, validation,\n * and management across multiple storage backends with MIME type and size limit controls.\n *\n * **Key Features**\n * - Multi-provider support (filesystem, cloud storage, in-memory)\n * - Automatic MIME type and file size validation\n * - Event integration for file operations monitoring\n * - Flexible per-bucket and per-operation configuration\n * - Smart file type and size detection\n *\n * **Common Use Cases**\n * - User profile pictures and document uploads\n * - Product images and media management\n * - Document storage and retrieval systems\n *\n * @example\n * ```ts\n * class MediaService {\n * images = $bucket({\n * name: \"user-images\",\n * mimeTypes: [\"image/jpeg\", \"image/png\", \"image/gif\"],\n * maxSize: 5 // 5MB limit\n * });\n *\n * documents = $bucket({\n * name: \"documents\",\n * mimeTypes: [\"application/pdf\", \"text/plain\"],\n * maxSize: 50 // 50MB limit\n * });\n *\n * async uploadProfileImage(file: FileLike, userId: string): Promise<string> {\n * const fileId = await this.images.upload(file);\n * await this.userService.updateProfileImage(userId, fileId);\n * return fileId;\n * }\n *\n * async downloadDocument(documentId: string): Promise<FileLike> {\n * return await this.documents.download(documentId);\n * }\n *\n * async deleteDocument(documentId: string): Promise<void> {\n * await this.documents.delete(documentId);\n * }\n * }\n * ```\n */\nexport const $bucket = (options: BucketPrimitiveOptions) =>\n createPrimitive(BucketPrimitive, options);\n\nexport interface BucketPrimitiveOptions extends BucketFileOptions {\n /**\n * File storage provider configuration for the bucket.\n *\n * Options:\n * - **\"memory\"**: In-memory storage (default for development, lost on restart)\n * - **Service<FileStorageProvider>**: Custom provider class (e.g., S3FileStorageProvider, AzureBlobProvider)\n * - **undefined**: Uses the default file storage provider from dependency injection\n *\n * **Provider Selection Guidelines**:\n * - **Development**: Use \"memory\" for fast, simple testing without external dependencies\n * - **Production**: Use cloud providers (S3, Azure Blob, Google Cloud Storage) for scalability\n * - **Local deployment**: Use filesystem providers for on-premise installations\n * - **Hybrid**: Use different providers for different bucket types (temp files vs permanent storage)\n *\n * **Provider Capabilities**:\n * - File persistence and durability guarantees\n * - Scalability and performance characteristics\n * - Geographic distribution and CDN integration\n * - Cost implications for storage and bandwidth\n * - Backup and disaster recovery features\n *\n * @default Uses injected FileStorageProvider\n * @example \"memory\"\n * @example S3FileStorageProvider\n * @example AzureBlobStorageProvider\n */\n provider?: Service<FileStorageProvider> | \"memory\";\n\n /**\n * Unique name identifier for the bucket.\n *\n * This name is used for:\n * - Storage backend organization and partitioning\n * - File path generation and URL construction\n * - Logging, monitoring, and debugging\n * - Access control and permissions management\n * - Backup and replication configuration\n *\n * **Naming Conventions**:\n * - Use lowercase with hyphens for consistency\n * - Include purpose or content type in the name\n * - Avoid spaces and special characters\n * - Consider environment prefixes for deployment isolation\n *\n * If not provided, defaults to the property key where the bucket is declared.\n *\n * @example \"user-avatars\"\n * @example \"product-images\"\n * @example \"legal-documents\"\n * @example \"temp-processing-files\"\n */\n name?: string;\n}\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport interface BucketFileOptions {\n /**\n * Human-readable description of the bucket's purpose and contents.\n *\n * Used for:\n * - Documentation generation and API references\n * - Developer onboarding and system understanding\n * - Monitoring dashboards and admin interfaces\n * - Compliance and audit documentation\n *\n * **Description Best Practices**:\n * - Explain what types of files this bucket stores\n * - Mention any special handling or processing requirements\n * - Include information about retention policies if applicable\n * - Note any compliance or security considerations\n *\n * @example \"User profile pictures and avatar images\"\n * @example \"Product catalog images with automated thumbnail generation\"\n * @example \"Legal documents requiring long-term retention\"\n * @example \"Temporary files for data processing workflows\"\n */\n description?: string;\n\n /**\n * Array of allowed MIME types for files uploaded to this bucket.\n *\n * When specified, only files with these exact MIME types will be accepted.\n * Files with disallowed MIME types will be rejected with an InvalidFileError.\n *\n * **MIME Type Categories**:\n * - Images: \"image/jpeg\", \"image/png\", \"image/gif\", \"image/webp\", \"image/svg+xml\"\n * - Documents: \"application/pdf\", \"text/plain\", \"text/csv\"\n * - Office: \"application/msword\", \"application/vnd.openxmlformats-officedocument.wordprocessingml.document\"\n * - Archives: \"application/zip\", \"application/x-tar\", \"application/gzip\"\n * - Media: \"video/mp4\", \"audio/mpeg\", \"audio/wav\"\n *\n * **Security Considerations**:\n * - Always validate MIME types for user uploads\n * - Be cautious with executable file types\n * - Consider using allow-lists rather than deny-lists\n * - Remember that MIME types can be spoofed by malicious users\n *\n * If not specified, all MIME types are allowed (not recommended for user uploads).\n *\n * @example [\"image/jpeg\", \"image/png\"] // Only JPEG and PNG images\n * @example [\"application/pdf\", \"text/plain\"] // Documents only\n * @example [\"video/mp4\", \"video/webm\"] // Video files\n */\n mimeTypes?: string[];\n\n /**\n * Maximum file size allowed in megabytes (MB).\n *\n * Files larger than this limit will be rejected with an InvalidFileError.\n * This helps prevent:\n * - Storage quota exhaustion\n * - Memory issues during file processing\n * - Long upload times and timeouts\n * - Abuse of storage resources\n *\n * **Size Guidelines by File Type**:\n * - Profile images: 1-5 MB\n * - Product photos: 5-10 MB\n * - Documents: 10-50 MB\n * - Video files: 50-500 MB\n * - Data files: 100-1000 MB\n *\n * **Considerations**:\n * - Consider your storage costs and limits\n * - Factor in network upload speeds for users\n * - Account for processing requirements (thumbnails, compression)\n * - Set reasonable limits based on actual use cases\n *\n * @default 10 MB\n *\n * @example 1 // 1MB for small images\n * @example 25 // 25MB for documents\n * @example 100 // 100MB for media files\n */\n maxSize?: number;\n}\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport class BucketPrimitive extends Primitive<BucketPrimitiveOptions> {\n public readonly provider = this.$provider();\n protected readonly fileSystem = $inject(FileSystemProvider);\n\n public get name() {\n return this.options.name ?? `${this.config.propertyKey}`;\n }\n\n /**\n * Uploads a file to the bucket.\n */\n public async upload(\n file: FileLike,\n options?: BucketFileOptions,\n ): Promise<string> {\n if (file instanceof File) {\n // our createFile is smarter than the browser's File constructor\n // by doing this, we can guess the MIME type and size!\n file = this.fileSystem.createFile({ file });\n }\n\n options = {\n ...this.options,\n ...options,\n };\n\n const mimeTypes = options.mimeTypes ?? undefined;\n const maxSize = options.maxSize ?? 10; // Default to 10 MB if not specified\n\n if (mimeTypes) {\n const mimeType = file.type || \"application/octet-stream\";\n if (!mimeTypes.includes(mimeType)) {\n throw new InvalidFileError(\n `MIME type ${mimeType} is not allowed in bucket ${this.name}`,\n );\n }\n }\n\n // check size in bytes, convert MB to bytes\n if (file.size > maxSize * 1024 * 1024) {\n throw new InvalidFileError(\n `File size ${file.size} exceeds the maximum size of ${maxSize} MB in bucket ${this.name}`,\n );\n }\n\n const id = await this.provider.upload(this.name, file);\n\n await this.alepha.events.emit(\"bucket:file:uploaded\", {\n id,\n bucket: this,\n file,\n options,\n });\n\n return id;\n }\n\n /**\n * Delete permanently a file from the bucket.\n */\n public async delete(fileId: string, skipHook = false): Promise<void> {\n await this.provider.delete(this.name, fileId);\n\n if (skipHook) {\n return;\n }\n\n await this.alepha.events.emit(\"bucket:file:deleted\", {\n id: fileId,\n bucket: this,\n });\n }\n\n /**\n * Delete many files in one round-trip when the underlying provider supports\n * batch (R2/S3 up to 1000 keys per call). Emits one `bucket:file:deleted`\n * event per id unless `skipHook` is set.\n */\n public async deleteMany(fileIds: string[], skipHook = false): Promise<void> {\n if (fileIds.length === 0) return;\n await this.provider.deleteMany(this.name, fileIds);\n\n if (skipHook) {\n return;\n }\n\n for (const id of fileIds) {\n await this.alepha.events.emit(\"bucket:file:deleted\", {\n id,\n bucket: this,\n });\n }\n }\n\n /**\n * Checks if a file exists in the bucket.\n */\n public async exists(fileId: string): Promise<boolean> {\n return this.provider.exists(this.name, fileId);\n }\n\n /**\n * Downloads a file from the bucket.\n */\n public async download(fileId: string): Promise<FileLike> {\n const file = await this.provider.download(this.name, fileId);\n\n await this.alepha.events.emit(\"bucket:file:downloaded\", {\n id: fileId,\n bucket: this,\n file,\n });\n\n return file;\n }\n\n protected $provider() {\n if (!this.options.provider) {\n return this.alepha.inject(FileStorageProvider);\n }\n if (this.options.provider === \"memory\") {\n return this.alepha.inject(MemoryFileStorageProvider);\n }\n return this.alepha.inject(this.options.provider);\n }\n}\n\n$bucket[KIND] = BucketPrimitive;\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport interface BucketFileOptions {\n /**\n * Optional description of the bucket.\n */\n description?: string;\n\n /**\n * Allowed MIME types.\n */\n mimeTypes?: string[];\n\n /**\n * Maximum size of the files in the bucket.\n *\n * @default 10\n */\n maxSize?: number;\n}\n","import type { R2Bucket } from \"@cloudflare/workers-types\";\nimport {\n $env,\n $hook,\n $inject,\n Alepha,\n AlephaError,\n type FileLike,\n t,\n} from \"alepha\";\nimport { CryptoProvider } from \"alepha/crypto\";\nimport { $logger } from \"alepha/logger\";\nimport { FileNotFoundError } from \"../errors/FileNotFoundError.ts\";\nimport { $bucket } from \"../primitives/$bucket.ts\";\nimport type { FileStorageProvider } from \"./FileStorageProvider.ts\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\n/**\n * Cloudflare R2 storage provider.\n *\n * Uses a single R2 bucket binding for all $bucket primitives.\n * Files are organized as: {APP_NAME}/{bucketName}/{fileId}\n *\n * **Required environment variables:**\n * - `R2_BUCKET_NAME` - The actual R2 bucket name in Cloudflare\n *\n * **Optional (uses core Alepha env):**\n * - `APP_NAME` - Prefix for all files (for multi-app setups sharing one R2 bucket)\n *\n * @example\n * ```bash\n * # .env\n * APP_NAME=myapp # optional, used as prefix\n * R2_BUCKET_NAME=storage\n * ```\n *\n * @example\n * ```toml\n * # wrangler.toml - automatically generated by alepha build\n * [[r2_buckets]]\n * binding = \"R2\"\n * bucket_name = \"storage\"\n * ```\n *\n * @example\n * ```ts\n * // Define buckets with validation rules\n * const avatars = $bucket({\n * name: \"avatars\",\n * maxFileSize: 5 * 1024 * 1024,\n * allowedMimeTypes: [\"image/*\"],\n * });\n *\n * const documents = $bucket({\n * name: \"documents\",\n * maxFileSize: 50 * 1024 * 1024,\n * allowedMimeTypes: [\"application/pdf\"],\n * });\n *\n * // Files stored at: myapp/avatars/uuid.png, myapp/documents/uuid.pdf\n * ```\n */\nexport class CloudflareR2Provider implements FileStorageProvider {\n protected readonly alepha = $inject(Alepha);\n protected readonly log = $logger();\n protected readonly crypto = $inject(CryptoProvider);\n protected readonly env = $env(\n t.object({\n /**\n * The actual R2 bucket name in Cloudflare.\n */\n R2_BUCKET_NAME: t.string({\n description: \"R2 bucket name in Cloudflare\",\n }),\n }),\n );\n\n protected r2?: R2Bucket;\n\n /**\n * Get the R2 bucket name from environment.\n */\n public get bucketName(): string {\n return this.env.R2_BUCKET_NAME;\n }\n\n /**\n * Get the optional prefix from APP_NAME environment variable.\n * Used for multi-app setups sharing the same R2 bucket.\n */\n public get prefix(): string | undefined {\n return this.alepha.env.APP_NAME;\n }\n\n protected readonly onStart = $hook({\n on: \"start\",\n handler: async () => {\n const cloudflareEnv = this.alepha.get(\"cloudflare.env\") as\n | Record<string, unknown>\n | undefined;\n if (!cloudflareEnv) {\n throw new AlephaError(\n \"Cloudflare Workers environment not found in Alepha store under 'cloudflare.env'.\",\n );\n }\n\n const binding = cloudflareEnv[this.bucketName] as R2Bucket | undefined;\n if (!binding) {\n throw new AlephaError(\n `R2 binding '${this.bucketName}' not found in Cloudflare Workers environment.`,\n );\n }\n\n this.r2 = binding;\n\n const prefixStr = this.prefix ? `${this.prefix}/` : \"\";\n this.log.info(\n `R2 storage ready (bucket: ${this.bucketName}, prefix: ${prefixStr || \"(none)\"})`,\n );\n\n for (const bucket of this.alepha.primitives($bucket)) {\n if (bucket.provider !== this) {\n continue;\n }\n this.log.debug(\n `Bucket '${bucket.name}' -> ${prefixStr}${bucket.name}/`,\n );\n }\n },\n });\n\n public async upload(\n bucketName: string,\n file: FileLike,\n fileId?: string,\n ): Promise<string> {\n const r2 = this.getR2();\n fileId ??= this.createId(file.name);\n const key = this.key(bucketName, fileId);\n\n this.log.trace(`Uploading '${key}'`);\n\n const arrayBuffer = await file.arrayBuffer();\n\n await r2.put(key, arrayBuffer, {\n httpMetadata: {\n contentType: file.type,\n },\n customMetadata: {\n originalName: file.name,\n bucket: bucketName,\n },\n });\n\n return fileId;\n }\n\n public async download(bucketName: string, fileId: string): Promise<FileLike> {\n const r2 = this.getR2();\n const key = this.key(bucketName, fileId);\n\n this.log.trace(`Downloading '${key}'`);\n\n const object = await r2.get(key);\n if (!object) {\n throw new FileNotFoundError(\n `File '${fileId}' not found in bucket '${bucketName}'.`,\n );\n }\n\n const originalName = object.customMetadata?.originalName ?? fileId;\n const contentType =\n object.httpMetadata?.contentType ?? \"application/octet-stream\";\n\n return {\n name: originalName,\n type: contentType,\n size: object.size,\n lastModified: object.uploaded.getTime(),\n stream: () => object.body as unknown as ReadableStream,\n arrayBuffer: () => object.arrayBuffer(),\n text: () => object.text(),\n };\n }\n\n public async exists(bucketName: string, fileId: string): Promise<boolean> {\n const r2 = this.getR2();\n const key = this.key(bucketName, fileId);\n\n this.log.trace(`Checking '${key}'`);\n\n const object = await r2.head(key);\n return object !== null;\n }\n\n public async delete(bucketName: string, fileId: string): Promise<void> {\n const r2 = this.getR2();\n const key = this.key(bucketName, fileId);\n\n this.log.trace(`Deleting '${key}'`);\n\n // R2's delete doesn't throw if the file doesn't exist,\n // so we check existence first for consistency with other providers\n const exists = await this.exists(bucketName, fileId);\n if (!exists) {\n throw new FileNotFoundError(\n `File '${fileId}' not found in bucket '${bucketName}'.`,\n );\n }\n\n await r2.delete(key);\n }\n\n public async deleteMany(\n bucketName: string,\n fileIds: string[],\n ): Promise<void> {\n if (fileIds.length === 0) return;\n const r2 = this.getR2();\n const keys = fileIds.map((id) => this.key(bucketName, id));\n this.log.trace(`Deleting ${keys.length} keys from '${bucketName}'`);\n // R2 binding accepts a string[] and silently ignores missing keys.\n // Chunk at 1000 to stay within the documented batch limit.\n for (let i = 0; i < keys.length; i += 1000) {\n await r2.delete(keys.slice(i, i + 1000));\n }\n }\n\n /**\n * Build the full R2 key: {prefix}/{bucketName}/{fileId}\n */\n protected key(bucketName: string, fileId: string): string {\n const parts = [bucketName, fileId];\n if (this.prefix) {\n parts.unshift(this.prefix);\n }\n return parts.join(\"/\");\n }\n\n protected getR2(): R2Bucket {\n if (!this.r2) {\n throw new AlephaError(\"R2 storage not initialized. Call start() first.\");\n }\n return this.r2;\n }\n\n protected createId(filename: string): string {\n const ext = filename.includes(\".\") ? filename.split(\".\").pop() : \"\";\n const id = this.crypto.randomUUID();\n return ext ? `${id}.${ext}` : id;\n }\n}\n","import { $module } from \"alepha\";\nimport { $bucket } from \"./primitives/$bucket.ts\";\nimport { CloudflareR2Provider } from \"./providers/CloudflareR2Provider.ts\";\nimport { FileStorageProvider } from \"./providers/FileStorageProvider.ts\";\nimport { MemoryFileStorageProvider } from \"./providers/MemoryFileStorageProvider.ts\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport * from \"./errors/FileNotFoundError.ts\";\nexport * from \"./primitives/$bucket.ts\";\nexport * from \"./providers/CloudflareR2Provider.ts\";\nexport * from \"./providers/FileStorageProvider.ts\";\nexport * from \"./providers/MemoryFileStorageProvider.ts\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport const AlephaBucket = $module({\n name: \"alepha.bucket\",\n primitives: [$bucket],\n services: [\n FileStorageProvider,\n MemoryFileStorageProvider,\n CloudflareR2Provider,\n ],\n register: (alepha) => {\n alepha.with({\n optional: true,\n provide: FileStorageProvider,\n use: alepha.isTest() ? MemoryFileStorageProvider : CloudflareR2Provider,\n });\n },\n});\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.workerd.js","names":["Buffer"],"sources":["../../src/bucket/errors/InvalidFileError.ts","../../src/bucket/providers/FileStorageProvider.ts","../../src/bucket/errors/FileNotFoundError.ts","../../src/bucket/providers/MemoryFileStorageProvider.ts","../../src/bucket/primitives/$bucket.ts","../../src/bucket/providers/R2FileStorageProvider.ts","../../src/bucket/providers/S3FileStorageProvider.ts","../../src/bucket/index.workerd.ts"],"sourcesContent":["import { AlephaError } from \"alepha\";\n\nexport class InvalidFileError extends AlephaError {\n public readonly status = 400;\n}\n","import type { FileLike } from \"alepha\";\n\nexport abstract class FileStorageProvider {\n /**\n * Uploads a file to the storage.\n *\n * @param bucketName - Container name\n * @param file - File to upload\n * @param fileId - Optional file identifier. If not provided, a unique ID will be generated.\n * @return The identifier of the uploaded file.\n */\n abstract upload(\n bucketName: string,\n file: FileLike,\n fileId?: string,\n ): Promise<string>;\n\n /**\n * Downloads a file from the storage.\n *\n * @param bucketName - Container name\n * @param fileId - Identifier of the file to download\n * @return The downloaded file as a FileLike object.\n */\n abstract download(bucketName: string, fileId: string): Promise<FileLike>;\n\n /**\n * Check if fileId exists in the storage bucket.\n *\n * @param bucketName - Container name\n * @param fileId - Identifier of the file to stream\n * @return True is the file exists, false otherwise.\n */\n abstract exists(bucketName: string, fileId: string): Promise<boolean>;\n\n /**\n * Delete permanently a file from the storage.\n *\n * @param bucketName - Container name\n * @param fileId - Identifier of the file to delete\n */\n abstract delete(bucketName: string, fileId: string): Promise<void>;\n\n /**\n * Delete multiple files in one round-trip where the provider supports\n * batch (R2/S3, up to 1000 per call). Memory/Local fall back to a loop.\n *\n * @param bucketName - Container name\n * @param fileIds - Identifiers of the files to delete\n */\n abstract deleteMany(bucketName: string, fileIds: string[]): Promise<void>;\n\n /**\n * Lists the file identifiers stored in a bucket, like `ls` on a directory.\n *\n * This is a flat, unpaginated listing intended for small buckets. Providers\n * cap the result at their natural page size (~1000 for S3/R2); anything beyond\n * that is silently dropped. It is NOT a search API — use `alepha/api/files`\n * when you need querying or pagination.\n *\n * @param bucketName - Container name\n * @return The identifiers of the files in the bucket (empty if none).\n */\n abstract list(bucketName: string): Promise<string[]>;\n}\n","import { AlephaError } from \"alepha\";\n\nexport class FileNotFoundError extends AlephaError {\n public readonly status = 404;\n}\n","import { $inject, type FileLike } from \"alepha\";\nimport { CryptoProvider } from \"alepha/crypto\";\nimport { FileDetector, FileSystemProvider } from \"alepha/system\";\nimport { FileNotFoundError } from \"../errors/FileNotFoundError.ts\";\nimport type { FileStorageProvider } from \"./FileStorageProvider.ts\";\n\ninterface StoredFile {\n buffer: Buffer;\n name: string;\n type: string;\n size: number;\n}\n\nexport class MemoryFileStorageProvider implements FileStorageProvider {\n public readonly files: Record<string, StoredFile> = {};\n protected readonly fileSystem = $inject(FileSystemProvider);\n protected readonly fileDetector = $inject(FileDetector);\n protected readonly crypto = $inject(CryptoProvider);\n\n public async upload(\n bucketName: string,\n file: FileLike,\n fileId?: string,\n ): Promise<string> {\n fileId ??= this.createId();\n\n // Consume the stream and store as buffer so downloads are repeatable\n const chunks: Uint8Array[] = [];\n for await (const chunk of file.stream() as AsyncIterable<Uint8Array>) {\n chunks.push(chunk);\n }\n const buffer = Buffer.concat(chunks);\n\n this.files[`${bucketName}/${fileId}`] = {\n buffer,\n name: file.name,\n type: file.type,\n size: file.size,\n };\n\n return fileId;\n }\n\n public async download(bucketName: string, fileId: string): Promise<FileLike> {\n const fileKey = `${bucketName}/${fileId}`;\n const stored = this.files[fileKey];\n\n if (!stored) {\n throw new FileNotFoundError(`File with ID ${fileId} not found.`);\n }\n\n // Create a fresh FileLike with a new stream from the stored buffer\n return this.fileSystem.createFile({\n stream: new Blob([new Uint8Array(stored.buffer)]).stream(),\n name: stored.name,\n type: stored.type,\n size: stored.size,\n });\n }\n\n public async exists(bucketName: string, fileId: string): Promise<boolean> {\n return `${bucketName}/${fileId}` in this.files;\n }\n\n public async delete(bucketName: string, fileId: string): Promise<void> {\n const fileKey = `${bucketName}/${fileId}`;\n if (!(fileKey in this.files)) {\n throw new FileNotFoundError(`File with ID ${fileId} not found.`);\n }\n\n delete this.files[fileKey];\n }\n\n public async deleteMany(\n bucketName: string,\n fileIds: string[],\n ): Promise<void> {\n for (const id of fileIds) {\n delete this.files[`${bucketName}/${id}`];\n }\n }\n\n public async list(bucketName: string): Promise<string[]> {\n const prefix = `${bucketName}/`;\n return Object.keys(this.files)\n .filter((key) => key.startsWith(prefix))\n .map((key) => key.slice(prefix.length));\n }\n\n protected createId(): string {\n return this.crypto.randomUUID();\n }\n}\n","import {\n $inject,\n createPrimitive,\n type FileLike,\n KIND,\n Primitive,\n type Service,\n} from \"alepha\";\nimport { FileSystemProvider } from \"alepha/system\";\nimport { InvalidFileError } from \"../errors/InvalidFileError.ts\";\nimport { FileStorageProvider } from \"../providers/FileStorageProvider.ts\";\nimport { MemoryFileStorageProvider } from \"../providers/MemoryFileStorageProvider.ts\";\n\n/**\n * Creates a bucket primitive for file storage and management with configurable validation.\n *\n * Provides a comprehensive file storage system that handles uploads, downloads, validation,\n * and management across multiple storage backends with MIME type and size limit controls.\n *\n * **Key Features**\n * - Multi-provider support (filesystem, cloud storage, in-memory)\n * - Automatic MIME type and file size validation\n * - Event integration for file operations monitoring\n * - Flexible per-bucket and per-operation configuration\n * - Smart file type and size detection\n *\n * **Common Use Cases**\n * - User profile pictures and document uploads\n * - Product images and media management\n * - Document storage and retrieval systems\n *\n * @example\n * ```ts\n * class MediaService {\n * images = $bucket({\n * name: \"user-images\",\n * mimeTypes: [\"image/jpeg\", \"image/png\", \"image/gif\"],\n * maxSize: 5 // 5MB limit\n * });\n *\n * documents = $bucket({\n * name: \"documents\",\n * mimeTypes: [\"application/pdf\", \"text/plain\"],\n * maxSize: 50 // 50MB limit\n * });\n *\n * async uploadProfileImage(file: FileLike, userId: string): Promise<string> {\n * const fileId = await this.images.upload(file);\n * await this.userService.updateProfileImage(userId, fileId);\n * return fileId;\n * }\n *\n * async downloadDocument(documentId: string): Promise<FileLike> {\n * return await this.documents.download(documentId);\n * }\n *\n * async deleteDocument(documentId: string): Promise<void> {\n * await this.documents.delete(documentId);\n * }\n * }\n * ```\n */\nexport const $bucket = (options: BucketPrimitiveOptions) =>\n createPrimitive(BucketPrimitive, options);\n\nexport interface BucketPrimitiveOptions extends BucketFileOptions {\n /**\n * File storage provider configuration for the bucket.\n *\n * Options:\n * - **\"memory\"**: In-memory storage (default for development, lost on restart)\n * - **Service<FileStorageProvider>**: Custom provider class (e.g., S3FileStorageProvider, AzureBlobProvider)\n * - **undefined**: Uses the default file storage provider from dependency injection\n *\n * **Provider Selection Guidelines**:\n * - **Development**: Use \"memory\" for fast, simple testing without external dependencies\n * - **Production**: Use cloud providers (S3, Azure Blob, Google Cloud Storage) for scalability\n * - **Local deployment**: Use filesystem providers for on-premise installations\n * - **Hybrid**: Use different providers for different bucket types (temp files vs permanent storage)\n *\n * **Provider Capabilities**:\n * - File persistence and durability guarantees\n * - Scalability and performance characteristics\n * - Geographic distribution and CDN integration\n * - Cost implications for storage and bandwidth\n * - Backup and disaster recovery features\n *\n * @default Uses injected FileStorageProvider\n * @example \"memory\"\n * @example S3FileStorageProvider\n * @example AzureBlobStorageProvider\n */\n provider?: Service<FileStorageProvider> | \"memory\";\n\n /**\n * Unique name identifier for the bucket.\n *\n * This name is used for:\n * - Storage backend organization and partitioning\n * - File path generation and URL construction\n * - Logging, monitoring, and debugging\n * - Access control and permissions management\n * - Backup and replication configuration\n *\n * **Naming Conventions**:\n * - Use lowercase with hyphens for consistency\n * - Include purpose or content type in the name\n * - Avoid spaces and special characters\n * - Consider environment prefixes for deployment isolation\n *\n * If not provided, defaults to the property key where the bucket is declared.\n *\n * @example \"user-avatars\"\n * @example \"product-images\"\n * @example \"legal-documents\"\n * @example \"temp-processing-files\"\n */\n name?: string;\n}\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport interface BucketFileOptions {\n /**\n * Human-readable description of the bucket's purpose and contents.\n *\n * Used for:\n * - Documentation generation and API references\n * - Developer onboarding and system understanding\n * - Monitoring dashboards and admin interfaces\n * - Compliance and audit documentation\n *\n * **Description Best Practices**:\n * - Explain what types of files this bucket stores\n * - Mention any special handling or processing requirements\n * - Include information about retention policies if applicable\n * - Note any compliance or security considerations\n *\n * @example \"User profile pictures and avatar images\"\n * @example \"Product catalog images with automated thumbnail generation\"\n * @example \"Legal documents requiring long-term retention\"\n * @example \"Temporary files for data processing workflows\"\n */\n description?: string;\n\n /**\n * Array of allowed MIME types for files uploaded to this bucket.\n *\n * When specified, only files with these exact MIME types will be accepted.\n * Files with disallowed MIME types will be rejected with an InvalidFileError.\n *\n * **MIME Type Categories**:\n * - Images: \"image/jpeg\", \"image/png\", \"image/gif\", \"image/webp\", \"image/svg+xml\"\n * - Documents: \"application/pdf\", \"text/plain\", \"text/csv\"\n * - Office: \"application/msword\", \"application/vnd.openxmlformats-officedocument.wordprocessingml.document\"\n * - Archives: \"application/zip\", \"application/x-tar\", \"application/gzip\"\n * - Media: \"video/mp4\", \"audio/mpeg\", \"audio/wav\"\n *\n * **Security Considerations**:\n * - Always validate MIME types for user uploads\n * - Be cautious with executable file types\n * - Consider using allow-lists rather than deny-lists\n * - Remember that MIME types can be spoofed by malicious users\n *\n * If not specified, all MIME types are allowed (not recommended for user uploads).\n *\n * @example [\"image/jpeg\", \"image/png\"] // Only JPEG and PNG images\n * @example [\"application/pdf\", \"text/plain\"] // Documents only\n * @example [\"video/mp4\", \"video/webm\"] // Video files\n */\n mimeTypes?: string[];\n\n /**\n * Maximum file size allowed in megabytes (MB).\n *\n * Files larger than this limit will be rejected with an InvalidFileError.\n * This helps prevent:\n * - Storage quota exhaustion\n * - Memory issues during file processing\n * - Long upload times and timeouts\n * - Abuse of storage resources\n *\n * **Size Guidelines by File Type**:\n * - Profile images: 1-5 MB\n * - Product photos: 5-10 MB\n * - Documents: 10-50 MB\n * - Video files: 50-500 MB\n * - Data files: 100-1000 MB\n *\n * **Considerations**:\n * - Consider your storage costs and limits\n * - Factor in network upload speeds for users\n * - Account for processing requirements (thumbnails, compression)\n * - Set reasonable limits based on actual use cases\n *\n * @default 10 MB\n *\n * @example 1 // 1MB for small images\n * @example 25 // 25MB for documents\n * @example 100 // 100MB for media files\n */\n maxSize?: number;\n}\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport class BucketPrimitive extends Primitive<BucketPrimitiveOptions> {\n public readonly provider = this.$provider();\n protected readonly fileSystem = $inject(FileSystemProvider);\n\n public get name() {\n return this.options.name ?? `${this.config.propertyKey}`;\n }\n\n /**\n * Uploads a file to the bucket.\n */\n public async upload(\n file: FileLike,\n options?: BucketFileOptions,\n ): Promise<string> {\n if (file instanceof File) {\n // our createFile is smarter than the browser's File constructor\n // by doing this, we can guess the MIME type and size!\n file = this.fileSystem.createFile({ file });\n }\n\n options = {\n ...this.options,\n ...options,\n };\n\n const mimeTypes = options.mimeTypes ?? undefined;\n const maxSize = options.maxSize ?? 10; // Default to 10 MB if not specified\n\n if (mimeTypes) {\n const mimeType = file.type || \"application/octet-stream\";\n if (!mimeTypes.includes(mimeType)) {\n throw new InvalidFileError(\n `MIME type ${mimeType} is not allowed in bucket ${this.name}`,\n );\n }\n }\n\n // A reported size of 0 means the body is a one-shot stream — real streams\n // report 0 until fully read (see FileLike.size). Materialize it once so\n // that (a) the maxSize check below is accurate instead of being silently\n // bypassed, and (b) the provider AND any `bucket:file:uploaded` hook can\n // read the same bytes without draining the stream out from under each\n // other. Files with a known size are already random-access (Blob/Buffer\n // backed) and need no copy. Uploads are size-capped, so this is bounded.\n if (file.size === 0) {\n file = this.fileSystem.createFile({\n arrayBuffer: await file.arrayBuffer(),\n name: file.name,\n type: file.type,\n });\n }\n\n // check size in bytes, convert MB to bytes\n if (file.size > maxSize * 1024 * 1024) {\n throw new InvalidFileError(\n `File size ${file.size} exceeds the maximum size of ${maxSize} MB in bucket ${this.name}`,\n );\n }\n\n const id = await this.provider.upload(this.name, file);\n\n await this.alepha.events.emit(\"bucket:file:uploaded\", {\n id,\n bucket: this,\n file,\n options,\n });\n\n return id;\n }\n\n /**\n * Delete permanently a file from the bucket.\n */\n public async delete(fileId: string, skipHook = false): Promise<void> {\n await this.provider.delete(this.name, fileId);\n\n if (skipHook) {\n return;\n }\n\n await this.alepha.events.emit(\"bucket:file:deleted\", {\n id: fileId,\n bucket: this,\n });\n }\n\n /**\n * Delete many files in one round-trip when the underlying provider supports\n * batch (R2/S3 up to 1000 keys per call). Emits one `bucket:file:deleted`\n * event per id unless `skipHook` is set.\n */\n public async deleteMany(fileIds: string[], skipHook = false): Promise<void> {\n if (fileIds.length === 0) return;\n await this.provider.deleteMany(this.name, fileIds);\n\n if (skipHook) {\n return;\n }\n\n for (const id of fileIds) {\n await this.alepha.events.emit(\"bucket:file:deleted\", {\n id,\n bucket: this,\n });\n }\n }\n\n /**\n * Checks if a file exists in the bucket.\n */\n public async exists(fileId: string): Promise<boolean> {\n return this.provider.exists(this.name, fileId);\n }\n\n /**\n * Lists the file identifiers stored in the bucket, like `ls` on a directory.\n *\n * This is a flat, unpaginated listing meant for small buckets — the result is\n * capped at the provider's natural page size (~1000 for S3/R2) and anything\n * beyond that is silently dropped. It is NOT a search API; reach for\n * `alepha/api/files` when you need querying or pagination.\n */\n public async list(): Promise<string[]> {\n return this.provider.list(this.name);\n }\n\n /**\n * Downloads a file from the bucket.\n */\n public async download(fileId: string): Promise<FileLike> {\n const file = await this.provider.download(this.name, fileId);\n\n await this.alepha.events.emit(\"bucket:file:downloaded\", {\n id: fileId,\n bucket: this,\n file,\n });\n\n return file;\n }\n\n protected $provider() {\n if (!this.options.provider) {\n return this.alepha.inject(FileStorageProvider);\n }\n if (this.options.provider === \"memory\") {\n return this.alepha.inject(MemoryFileStorageProvider);\n }\n return this.alepha.inject(this.options.provider);\n }\n}\n\n$bucket[KIND] = BucketPrimitive;\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport interface BucketFileOptions {\n /**\n * Optional description of the bucket.\n */\n description?: string;\n\n /**\n * Allowed MIME types.\n */\n mimeTypes?: string[];\n\n /**\n * Maximum size of the files in the bucket.\n *\n * @default 10\n */\n maxSize?: number;\n}\n","import type { R2Bucket } from \"@cloudflare/workers-types\";\nimport {\n $env,\n $hook,\n $inject,\n Alepha,\n AlephaError,\n type FileLike,\n t,\n} from \"alepha\";\nimport { CryptoProvider } from \"alepha/crypto\";\nimport { $logger } from \"alepha/logger\";\nimport { FileNotFoundError } from \"../errors/FileNotFoundError.ts\";\nimport { $bucket } from \"../primitives/$bucket.ts\";\nimport type { FileStorageProvider } from \"./FileStorageProvider.ts\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\n/**\n * Cloudflare R2 storage provider.\n *\n * Uses a single R2 bucket binding for all $bucket primitives.\n * Files are organized as: {APP_NAME}/{bucketName}/{fileId}\n *\n * **Required environment variables:**\n * - `R2_BUCKET_NAME` - The actual R2 bucket name in Cloudflare\n *\n * **Optional (uses core Alepha env):**\n * - `APP_NAME` - Prefix for all files (for multi-app setups sharing one R2 bucket)\n *\n * @example\n * ```bash\n * # .env\n * APP_NAME=myapp # optional, used as prefix\n * R2_BUCKET_NAME=storage\n * ```\n *\n * @example\n * ```toml\n * # wrangler.toml - automatically generated by alepha build\n * [[r2_buckets]]\n * binding = \"R2\"\n * bucket_name = \"storage\"\n * ```\n *\n * @example\n * ```ts\n * // Define buckets with validation rules\n * const avatars = $bucket({\n * name: \"avatars\",\n * maxFileSize: 5 * 1024 * 1024,\n * allowedMimeTypes: [\"image/*\"],\n * });\n *\n * const documents = $bucket({\n * name: \"documents\",\n * maxFileSize: 50 * 1024 * 1024,\n * allowedMimeTypes: [\"application/pdf\"],\n * });\n *\n * // Files stored at: myapp/avatars/uuid.png, myapp/documents/uuid.pdf\n * ```\n */\nexport class R2FileStorageProvider implements FileStorageProvider {\n protected readonly alepha = $inject(Alepha);\n protected readonly log = $logger();\n protected readonly crypto = $inject(CryptoProvider);\n protected readonly env = $env(\n t.object({\n /**\n * The actual R2 bucket name in Cloudflare.\n */\n R2_BUCKET_NAME: t.string({\n description: \"R2 bucket name in Cloudflare\",\n }),\n }),\n );\n\n protected r2?: R2Bucket;\n\n /**\n * Get the R2 bucket name from environment.\n */\n public get bucketName(): string {\n return this.env.R2_BUCKET_NAME;\n }\n\n /**\n * Get the optional prefix from APP_NAME environment variable.\n * Used for multi-app setups sharing the same R2 bucket.\n */\n public get prefix(): string | undefined {\n return this.alepha.env.APP_NAME;\n }\n\n protected readonly onStart = $hook({\n on: \"start\",\n handler: async () => {\n const cloudflareEnv = this.alepha.get(\"cloudflare.env\") as\n | Record<string, unknown>\n | undefined;\n if (!cloudflareEnv) {\n throw new AlephaError(\n \"Cloudflare Workers environment not found in Alepha store under 'cloudflare.env'.\",\n );\n }\n\n const binding = cloudflareEnv[this.bucketName] as R2Bucket | undefined;\n if (!binding) {\n throw new AlephaError(\n `R2 binding '${this.bucketName}' not found in Cloudflare Workers environment.`,\n );\n }\n\n this.r2 = binding;\n\n const prefixStr = this.prefix ? `${this.prefix}/` : \"\";\n this.log.info(\n `R2 storage ready (bucket: ${this.bucketName}, prefix: ${prefixStr || \"(none)\"})`,\n );\n\n for (const bucket of this.alepha.primitives($bucket)) {\n if (bucket.provider !== this) {\n continue;\n }\n this.log.debug(\n `Bucket '${bucket.name}' -> ${prefixStr}${bucket.name}/`,\n );\n }\n },\n });\n\n public async upload(\n bucketName: string,\n file: FileLike,\n fileId?: string,\n ): Promise<string> {\n const r2 = this.getR2();\n fileId ??= this.createId(file.name);\n const key = this.key(bucketName, fileId);\n\n this.log.trace(`Uploading '${key}'`);\n\n const arrayBuffer = await file.arrayBuffer();\n\n await r2.put(key, arrayBuffer, {\n httpMetadata: {\n contentType: file.type,\n },\n customMetadata: {\n originalName: file.name,\n bucket: bucketName,\n },\n });\n\n return fileId;\n }\n\n public async download(bucketName: string, fileId: string): Promise<FileLike> {\n const r2 = this.getR2();\n const key = this.key(bucketName, fileId);\n\n this.log.trace(`Downloading '${key}'`);\n\n const object = await r2.get(key);\n if (!object) {\n throw new FileNotFoundError(\n `File '${fileId}' not found in bucket '${bucketName}'.`,\n );\n }\n\n const originalName = object.customMetadata?.originalName ?? fileId;\n const contentType =\n object.httpMetadata?.contentType ?? \"application/octet-stream\";\n\n return {\n name: originalName,\n type: contentType,\n size: object.size,\n lastModified: object.uploaded.getTime(),\n stream: () => object.body as unknown as ReadableStream,\n arrayBuffer: () => object.arrayBuffer(),\n text: () => object.text(),\n };\n }\n\n public async exists(bucketName: string, fileId: string): Promise<boolean> {\n const r2 = this.getR2();\n const key = this.key(bucketName, fileId);\n\n this.log.trace(`Checking '${key}'`);\n\n const object = await r2.head(key);\n return object !== null;\n }\n\n public async delete(bucketName: string, fileId: string): Promise<void> {\n const r2 = this.getR2();\n const key = this.key(bucketName, fileId);\n\n this.log.trace(`Deleting '${key}'`);\n\n // R2's delete doesn't throw if the file doesn't exist,\n // so we check existence first for consistency with other providers\n const exists = await this.exists(bucketName, fileId);\n if (!exists) {\n throw new FileNotFoundError(\n `File '${fileId}' not found in bucket '${bucketName}'.`,\n );\n }\n\n await r2.delete(key);\n }\n\n public async deleteMany(\n bucketName: string,\n fileIds: string[],\n ): Promise<void> {\n if (fileIds.length === 0) return;\n const r2 = this.getR2();\n const keys = fileIds.map((id) => this.key(bucketName, id));\n this.log.trace(`Deleting ${keys.length} keys from '${bucketName}'`);\n // R2 binding accepts a string[] and silently ignores missing keys.\n // Chunk at 1000 to stay within the documented batch limit.\n for (let i = 0; i < keys.length; i += 1000) {\n await r2.delete(keys.slice(i, i + 1000));\n }\n }\n\n public async list(bucketName: string): Promise<string[]> {\n const r2 = this.getR2();\n const keyPrefix = this.key(bucketName, \"\");\n this.log.trace(`Listing files in '${keyPrefix}'`);\n // Flat, single-page listing (~1000 keys). Not a search API.\n const listed = await r2.list({ prefix: keyPrefix });\n return listed.objects.map((object) => object.key.slice(keyPrefix.length));\n }\n\n /**\n * Build the full R2 key: {prefix}/{bucketName}/{fileId}\n */\n protected key(bucketName: string, fileId: string): string {\n const parts = [bucketName, fileId];\n if (this.prefix) {\n parts.unshift(this.prefix);\n }\n return parts.join(\"/\");\n }\n\n protected getR2(): R2Bucket {\n if (!this.r2) {\n throw new AlephaError(\"R2 storage not initialized. Call start() first.\");\n }\n return this.r2;\n }\n\n protected createId(filename: string): string {\n const ext = filename.includes(\".\") ? filename.split(\".\").pop() : \"\";\n const id = this.crypto.randomUUID();\n return ext ? `${id}.${ext}` : id;\n }\n}\n","import { Buffer } from \"node:buffer\";\nimport { Readable } from \"node:stream\";\nimport type { ReadableStream as NodeWebStream } from \"node:stream/web\";\nimport {\n $env,\n $hook,\n $inject,\n Alepha,\n AlephaError,\n type FileLike,\n type Static,\n t,\n} from \"alepha\";\nimport { CryptoProvider } from \"alepha/crypto\";\nimport { $logger } from \"alepha/logger\";\nimport { FileDetector, FileSystemProvider } from \"alepha/system\";\nimport { S3mini } from \"s3mini\";\nimport { FileNotFoundError } from \"../errors/FileNotFoundError.ts\";\nimport { $bucket } from \"../primitives/$bucket.ts\";\nimport type { FileStorageProvider } from \"./FileStorageProvider.ts\";\n\nconst envSchema = t.object({\n /**\n * S3 endpoint URL. The bucket name is appended (path-style) per request.\n *\n * Examples:\n * - AWS S3: `https://s3.us-east-1.amazonaws.com`\n * - Cloudflare R2: `https://<account-id>.r2.cloudflarestorage.com`\n * - MinIO: `http://localhost:9000`\n * - DigitalOcean Spaces: `https://<region>.digitaloceanspaces.com`\n */\n S3_ENDPOINT: t.string(),\n\n /**\n * AWS region or \"auto\" for R2.\n *\n * @default \"auto\"\n */\n S3_REGION: t.optional(t.string()),\n\n /**\n * Access key ID for S3 authentication.\n */\n S3_ACCESS_KEY_ID: t.string(),\n\n /**\n * Secret access key for S3 authentication.\n */\n S3_SECRET_ACCESS_KEY: t.string(),\n});\n\ndeclare module \"alepha\" {\n interface Env extends Partial<Static<typeof envSchema>> {}\n}\n\n/**\n * S3-compatible file storage provider for Node.js.\n *\n * Backed by `s3mini` (zero-dep, ~20 KB). Works with AWS S3, Cloudflare R2,\n * MinIO, DigitalOcean Spaces, Backblaze B2, and any other S3-compatible service.\n *\n * Uses path-style addressing (`<endpoint>/<bucket>`).\n */\nexport class S3FileStorageProvider implements FileStorageProvider {\n protected readonly log = $logger();\n protected readonly env = $env(envSchema);\n protected readonly alepha = $inject(Alepha);\n protected readonly fileSystem = $inject(FileSystemProvider);\n protected readonly fileDetector = $inject(FileDetector);\n protected readonly crypto = $inject(CryptoProvider);\n protected readonly clients: Map<string, S3mini> = new Map();\n\n /**\n * Convert bucket name to S3-compatible format.\n * S3 bucket names must be lowercase, 3-63 characters, no underscores.\n */\n public convertName(name: string): string {\n return name.replaceAll(\"/\", \"-\").replaceAll(\"_\", \"-\").toLowerCase();\n }\n\n protected getClient(bucketName: string): S3mini {\n const name = this.convertName(bucketName);\n let client = this.clients.get(name);\n if (!client) {\n const endpoint = this.env.S3_ENDPOINT.replace(/\\/+$/, \"\");\n client = new S3mini({\n accessKeyId: this.env.S3_ACCESS_KEY_ID,\n secretAccessKey: this.env.S3_SECRET_ACCESS_KEY,\n region: this.env.S3_REGION || \"auto\",\n endpoint: `${endpoint}/${name}`,\n });\n this.clients.set(name, client);\n }\n return client;\n }\n\n protected readonly onStart = $hook({\n on: \"start\",\n handler: async () => {\n for (const bucket of this.alepha.primitives($bucket)) {\n if (bucket.provider !== this) {\n continue;\n }\n\n const name = this.convertName(bucket.name);\n const client = this.getClient(bucket.name);\n\n this.log.debug(`Preparing S3 bucket '${name}'...`);\n\n const exists = await client.bucketExists();\n if (!exists) {\n this.log.debug(`Creating S3 bucket '${name}'...`);\n const created = await client.createBucket();\n if (!created) {\n throw new AlephaError(`Failed to create S3 bucket '${name}'`);\n }\n }\n\n this.log.info(`S3 bucket '${bucket.name}' OK`);\n }\n },\n });\n\n protected createId(mimeType: string): string {\n const ext = this.fileDetector.getExtensionFromMimeType(mimeType);\n return `${this.crypto.randomUUID()}.${ext}`;\n }\n\n public async upload(\n bucketName: string,\n file: FileLike,\n fileId?: string,\n ): Promise<string> {\n fileId ??= this.createId(file.type);\n\n this.log.trace(\n `Uploading file '${file.name}' to bucket '${bucketName}' with id '${fileId}'...`,\n );\n\n const client = this.getClient(bucketName);\n\n try {\n const buffer = new Uint8Array(await file.arrayBuffer());\n await client.putObject(\n fileId,\n buffer,\n file.type || \"application/octet-stream\",\n undefined,\n { \"x-amz-meta-name\": encodeURIComponent(file.name) },\n file.size,\n );\n\n this.log.trace(`File uploaded successfully: ${fileId}`);\n return fileId;\n } catch (error) {\n this.log.error(`Failed to upload file: ${error}`);\n if (error instanceof Error) {\n throw new AlephaError(`Upload failed: ${error.message}`, {\n cause: error,\n });\n }\n throw error;\n }\n }\n\n public async download(bucketName: string, fileId: string): Promise<FileLike> {\n this.log.trace(\n `Downloading file '${fileId}' from bucket '${bucketName}'...`,\n );\n\n const client = this.getClient(bucketName);\n const response = await client.getObjectResponse(fileId);\n\n if (!response) {\n throw new FileNotFoundError(\n `File '${fileId}' not found in bucket '${bucketName}'`,\n );\n }\n\n const mimeType =\n response.headers.get(\"content-type\") ||\n this.fileDetector.getContentType(fileId);\n\n const metaName = response.headers.get(\"x-amz-meta-name\");\n const name = metaName ? decodeURIComponent(metaName) : fileId;\n\n const contentLength = response.headers.get(\"content-length\");\n const size = contentLength ? Number.parseInt(contentLength, 10) : 0;\n\n // Stream the body straight through instead of buffering the whole object\n // into memory. `response.body` is null only for a zero-byte object.\n if (!response.body) {\n return this.fileSystem.createFile({\n buffer: Buffer.alloc(0),\n name,\n type: mimeType,\n });\n }\n\n return this.fileSystem.createFile({\n stream: Readable.fromWeb(response.body as unknown as NodeWebStream),\n name,\n type: mimeType,\n size,\n });\n }\n\n public async exists(bucketName: string, fileId: string): Promise<boolean> {\n this.log.trace(\n `Checking existence of file '${fileId}' in bucket '${bucketName}'...`,\n );\n\n const client = this.getClient(bucketName);\n const result = await client.objectExists(fileId);\n return result === true;\n }\n\n public async delete(bucketName: string, fileId: string): Promise<void> {\n this.log.trace(`Deleting file '${fileId}' from bucket '${bucketName}'...`);\n\n const client = this.getClient(bucketName);\n\n try {\n await client.deleteObject(fileId);\n } catch (error) {\n this.log.error(`Failed to delete file: ${error}`);\n if (error instanceof Error) {\n throw new FileNotFoundError(\"Error deleting file\", { cause: error });\n }\n throw error;\n }\n }\n\n public async list(bucketName: string): Promise<string[]> {\n this.log.trace(`Listing files in bucket '${bucketName}'...`);\n const client = this.getClient(bucketName);\n // Flat, single-page listing (~1000 keys). Not a search API.\n const objects = await client.listObjects();\n if (!objects) return [];\n return objects.map((object) => object.Key);\n }\n\n public async deleteMany(\n bucketName: string,\n fileIds: string[],\n ): Promise<void> {\n if (fileIds.length === 0) return;\n this.log.trace(\n `Deleting ${fileIds.length} files from bucket '${bucketName}'...`,\n );\n const client = this.getClient(bucketName);\n // S3 DeleteObjects caps at 1000 keys per request.\n for (let i = 0; i < fileIds.length; i += 1000) {\n const chunk = fileIds.slice(i, i + 1000);\n try {\n // bun:s3 client exposes a per-key deleteObject; some SDKs also expose\n // deleteObjects(keys: string[]). Prefer batch when available.\n const batch = (\n client as unknown as {\n deleteObjects?: (keys: string[]) => Promise<unknown>;\n }\n ).deleteObjects;\n if (typeof batch === \"function\") {\n await batch.call(client, chunk);\n } else {\n await Promise.all(chunk.map((id) => client.deleteObject(id)));\n }\n } catch (error) {\n this.log.error(`Failed to delete files: ${error}`);\n if (error instanceof Error) {\n throw new FileNotFoundError(\"Error deleting files\", { cause: error });\n }\n throw error;\n }\n }\n }\n}\n","import { $module } from \"alepha\";\nimport { $bucket } from \"./primitives/$bucket.ts\";\nimport { FileStorageProvider } from \"./providers/FileStorageProvider.ts\";\nimport { MemoryFileStorageProvider } from \"./providers/MemoryFileStorageProvider.ts\";\nimport { R2FileStorageProvider } from \"./providers/R2FileStorageProvider.ts\";\nimport { S3FileStorageProvider } from \"./providers/S3FileStorageProvider.ts\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport * from \"./errors/FileNotFoundError.ts\";\nexport * from \"./primitives/$bucket.ts\";\nexport * from \"./providers/FileStorageProvider.ts\";\nexport * from \"./providers/MemoryFileStorageProvider.ts\";\nexport * from \"./providers/R2FileStorageProvider.ts\";\nexport * from \"./providers/S3FileStorageProvider.ts\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport const AlephaBucket = $module({\n name: \"alepha.bucket\",\n primitives: [$bucket],\n services: [\n FileStorageProvider,\n MemoryFileStorageProvider,\n R2FileStorageProvider,\n ],\n variants: [\n MemoryFileStorageProvider,\n R2FileStorageProvider,\n S3FileStorageProvider, // S3 is allowed, it's ok inside workers (s3mini = fetch)\n ],\n register: (alepha) => {\n alepha.with({\n optional: true,\n provide: FileStorageProvider,\n use: alepha.isTest() ? MemoryFileStorageProvider : R2FileStorageProvider,\n });\n },\n});\n"],"mappings":";;;;;;;;AAEA,IAAa,mBAAb,cAAsC,YAAY;CAChD,SAAyB;AAC3B;;;ACFA,IAAsB,sBAAtB,MAA0C,CA8D1C;;;AC9DA,IAAa,oBAAb,cAAuC,YAAY;CACjD,SAAyB;AAC3B;;;ACSA,IAAa,4BAAb,MAAsE;CACpE,QAAoD,CAAC;CACrD,aAAgC,QAAQ,kBAAkB;CAC1D,eAAkC,QAAQ,YAAY;CACtD,SAA4B,QAAQ,cAAc;CAElD,MAAa,OACX,YACA,MACA,QACiB;EACjB,WAAW,KAAK,SAAS;EAGzB,MAAM,SAAuB,CAAC;EAC9B,WAAW,MAAM,SAAS,KAAK,OAAO,GACpC,OAAO,KAAK,KAAK;EAEnB,MAAM,SAAS,OAAO,OAAO,MAAM;EAEnC,KAAK,MAAM,GAAG,WAAW,GAAG,YAAY;GACtC;GACA,MAAM,KAAK;GACX,MAAM,KAAK;GACX,MAAM,KAAK;EACb;EAEA,OAAO;CACT;CAEA,MAAa,SAAS,YAAoB,QAAmC;EAC3E,MAAM,UAAU,GAAG,WAAW,GAAG;EACjC,MAAM,SAAS,KAAK,MAAM;EAE1B,IAAI,CAAC,QACH,MAAM,IAAI,kBAAkB,gBAAgB,OAAO,YAAY;EAIjE,OAAO,KAAK,WAAW,WAAW;GAChC,QAAQ,IAAI,KAAK,CAAC,IAAI,WAAW,OAAO,MAAM,CAAC,CAAC,EAAE,OAAO;GACzD,MAAM,OAAO;GACb,MAAM,OAAO;GACb,MAAM,OAAO;EACf,CAAC;CACH;CAEA,MAAa,OAAO,YAAoB,QAAkC;EACxE,OAAO,GAAG,WAAW,GAAG,YAAY,KAAK;CAC3C;CAEA,MAAa,OAAO,YAAoB,QAA+B;EACrE,MAAM,UAAU,GAAG,WAAW,GAAG;EACjC,IAAI,EAAE,WAAW,KAAK,QACpB,MAAM,IAAI,kBAAkB,gBAAgB,OAAO,YAAY;EAGjE,OAAO,KAAK,MAAM;CACpB;CAEA,MAAa,WACX,YACA,SACe;EACf,KAAK,MAAM,MAAM,SACf,OAAO,KAAK,MAAM,GAAG,WAAW,GAAG;CAEvC;CAEA,MAAa,KAAK,YAAuC;EACvD,MAAM,SAAS,GAAG,WAAW;EAC7B,OAAO,OAAO,KAAK,KAAK,KAAK,EAC1B,QAAQ,QAAQ,IAAI,WAAW,MAAM,CAAC,EACtC,KAAK,QAAQ,IAAI,MAAM,OAAO,MAAM,CAAC;CAC1C;CAEA,WAA6B;EAC3B,OAAO,KAAK,OAAO,WAAW;CAChC;AACF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AC9BA,MAAa,WAAW,YACtB,gBAAgB,iBAAiB,OAAO;AA+I1C,IAAa,kBAAb,cAAqC,UAAkC;CACrE,WAA2B,KAAK,UAAU;CAC1C,aAAgC,QAAQ,kBAAkB;CAE1D,IAAW,OAAO;EAChB,OAAO,KAAK,QAAQ,QAAQ,GAAG,KAAK,OAAO;CAC7C;;;;CAKA,MAAa,OACX,MACA,SACiB;EACjB,IAAI,gBAAgB,MAGlB,OAAO,KAAK,WAAW,WAAW,EAAE,KAAK,CAAC;EAG5C,UAAU;GACR,GAAG,KAAK;GACR,GAAG;EACL;EAEA,MAAM,YAAY,QAAQ,aAAa,KAAA;EACvC,MAAM,UAAU,QAAQ,WAAW;EAEnC,IAAI,WAAW;GACb,MAAM,WAAW,KAAK,QAAQ;GAC9B,IAAI,CAAC,UAAU,SAAS,QAAQ,GAC9B,MAAM,IAAI,iBACR,aAAa,SAAS,4BAA4B,KAAK,MACzD;EAEJ;EASA,IAAI,KAAK,SAAS,GAChB,OAAO,KAAK,WAAW,WAAW;GAChC,aAAa,MAAM,KAAK,YAAY;GACpC,MAAM,KAAK;GACX,MAAM,KAAK;EACb,CAAC;EAIH,IAAI,KAAK,OAAO,UAAU,OAAO,MAC/B,MAAM,IAAI,iBACR,aAAa,KAAK,KAAK,+BAA+B,QAAQ,gBAAgB,KAAK,MACrF;EAGF,MAAM,KAAK,MAAM,KAAK,SAAS,OAAO,KAAK,MAAM,IAAI;EAErD,MAAM,KAAK,OAAO,OAAO,KAAK,wBAAwB;GACpD;GACA,QAAQ;GACR;GACA;EACF,CAAC;EAED,OAAO;CACT;;;;CAKA,MAAa,OAAO,QAAgB,WAAW,OAAsB;EACnE,MAAM,KAAK,SAAS,OAAO,KAAK,MAAM,MAAM;EAE5C,IAAI,UACF;EAGF,MAAM,KAAK,OAAO,OAAO,KAAK,uBAAuB;GACnD,IAAI;GACJ,QAAQ;EACV,CAAC;CACH;;;;;;CAOA,MAAa,WAAW,SAAmB,WAAW,OAAsB;EAC1E,IAAI,QAAQ,WAAW,GAAG;EAC1B,MAAM,KAAK,SAAS,WAAW,KAAK,MAAM,OAAO;EAEjD,IAAI,UACF;EAGF,KAAK,MAAM,MAAM,SACf,MAAM,KAAK,OAAO,OAAO,KAAK,uBAAuB;GACnD;GACA,QAAQ;EACV,CAAC;CAEL;;;;CAKA,MAAa,OAAO,QAAkC;EACpD,OAAO,KAAK,SAAS,OAAO,KAAK,MAAM,MAAM;CAC/C;;;;;;;;;CAUA,MAAa,OAA0B;EACrC,OAAO,KAAK,SAAS,KAAK,KAAK,IAAI;CACrC;;;;CAKA,MAAa,SAAS,QAAmC;EACvD,MAAM,OAAO,MAAM,KAAK,SAAS,SAAS,KAAK,MAAM,MAAM;EAE3D,MAAM,KAAK,OAAO,OAAO,KAAK,0BAA0B;GACtD,IAAI;GACJ,QAAQ;GACR;EACF,CAAC;EAED,OAAO;CACT;CAEA,YAAsB;EACpB,IAAI,CAAC,KAAK,QAAQ,UAChB,OAAO,KAAK,OAAO,OAAO,mBAAmB;EAE/C,IAAI,KAAK,QAAQ,aAAa,UAC5B,OAAO,KAAK,OAAO,OAAO,yBAAyB;EAErD,OAAO,KAAK,OAAO,OAAO,KAAK,QAAQ,QAAQ;CACjD;AACF;AAEA,QAAQ,QAAQ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;ACzShB,IAAa,wBAAb,MAAkE;CAChE,SAA4B,QAAQ,MAAM;CAC1C,MAAyB,QAAQ;CACjC,SAA4B,QAAQ,cAAc;CAClD,MAAyB,KACvB,EAAE,OAAO;;;;AAIP,gBAAgB,EAAE,OAAO,EACvB,aAAa,+BACf,CAAC,EACH,CAAC,CACH;CAEA;;;;CAKA,IAAW,aAAqB;EAC9B,OAAO,KAAK,IAAI;CAClB;;;;;CAMA,IAAW,SAA6B;EACtC,OAAO,KAAK,OAAO,IAAI;CACzB;CAEA,UAA6B,MAAM;EACjC,IAAI;EACJ,SAAS,YAAY;GACnB,MAAM,gBAAgB,KAAK,OAAO,IAAI,gBAAgB;GAGtD,IAAI,CAAC,eACH,MAAM,IAAI,YACR,kFACF;GAGF,MAAM,UAAU,cAAc,KAAK;GACnC,IAAI,CAAC,SACH,MAAM,IAAI,YACR,eAAe,KAAK,WAAW,+CACjC;GAGF,KAAK,KAAK;GAEV,MAAM,YAAY,KAAK,SAAS,GAAG,KAAK,OAAO,KAAK;GACpD,KAAK,IAAI,KACP,6BAA6B,KAAK,WAAW,YAAY,aAAa,SAAS,EACjF;GAEA,KAAK,MAAM,UAAU,KAAK,OAAO,WAAW,OAAO,GAAG;IACpD,IAAI,OAAO,aAAa,MACtB;IAEF,KAAK,IAAI,MACP,WAAW,OAAO,KAAK,OAAO,YAAY,OAAO,KAAK,EACxD;GACF;EACF;CACF,CAAC;CAED,MAAa,OACX,YACA,MACA,QACiB;EACjB,MAAM,KAAK,KAAK,MAAM;EACtB,WAAW,KAAK,SAAS,KAAK,IAAI;EAClC,MAAM,MAAM,KAAK,IAAI,YAAY,MAAM;EAEvC,KAAK,IAAI,MAAM,cAAc,IAAI,EAAE;EAEnC,MAAM,cAAc,MAAM,KAAK,YAAY;EAE3C,MAAM,GAAG,IAAI,KAAK,aAAa;GAC7B,cAAc,EACZ,aAAa,KAAK,KACpB;GACA,gBAAgB;IACd,cAAc,KAAK;IACnB,QAAQ;GACV;EACF,CAAC;EAED,OAAO;CACT;CAEA,MAAa,SAAS,YAAoB,QAAmC;EAC3E,MAAM,KAAK,KAAK,MAAM;EACtB,MAAM,MAAM,KAAK,IAAI,YAAY,MAAM;EAEvC,KAAK,IAAI,MAAM,gBAAgB,IAAI,EAAE;EAErC,MAAM,SAAS,MAAM,GAAG,IAAI,GAAG;EAC/B,IAAI,CAAC,QACH,MAAM,IAAI,kBACR,SAAS,OAAO,yBAAyB,WAAW,GACtD;EAOF,OAAO;GACL,MALmB,OAAO,gBAAgB,gBAAgB;GAM1D,MAJA,OAAO,cAAc,eAAe;GAKpC,MAAM,OAAO;GACb,cAAc,OAAO,SAAS,QAAQ;GACtC,cAAc,OAAO;GACrB,mBAAmB,OAAO,YAAY;GACtC,YAAY,OAAO,KAAK;EAC1B;CACF;CAEA,MAAa,OAAO,YAAoB,QAAkC;EACxE,MAAM,KAAK,KAAK,MAAM;EACtB,MAAM,MAAM,KAAK,IAAI,YAAY,MAAM;EAEvC,KAAK,IAAI,MAAM,aAAa,IAAI,EAAE;EAGlC,OAAO,MADc,GAAG,KAAK,GAAG,MACd;CACpB;CAEA,MAAa,OAAO,YAAoB,QAA+B;EACrE,MAAM,KAAK,KAAK,MAAM;EACtB,MAAM,MAAM,KAAK,IAAI,YAAY,MAAM;EAEvC,KAAK,IAAI,MAAM,aAAa,IAAI,EAAE;EAKlC,IAAI,CAAC,MADgB,KAAK,OAAO,YAAY,MAAM,GAEjD,MAAM,IAAI,kBACR,SAAS,OAAO,yBAAyB,WAAW,GACtD;EAGF,MAAM,GAAG,OAAO,GAAG;CACrB;CAEA,MAAa,WACX,YACA,SACe;EACf,IAAI,QAAQ,WAAW,GAAG;EAC1B,MAAM,KAAK,KAAK,MAAM;EACtB,MAAM,OAAO,QAAQ,KAAK,OAAO,KAAK,IAAI,YAAY,EAAE,CAAC;EACzD,KAAK,IAAI,MAAM,YAAY,KAAK,OAAO,cAAc,WAAW,EAAE;EAGlE,KAAK,IAAI,IAAI,GAAG,IAAI,KAAK,QAAQ,KAAK,KACpC,MAAM,GAAG,OAAO,KAAK,MAAM,GAAG,IAAI,GAAI,CAAC;CAE3C;CAEA,MAAa,KAAK,YAAuC;EACvD,MAAM,KAAK,KAAK,MAAM;EACtB,MAAM,YAAY,KAAK,IAAI,YAAY,EAAE;EACzC,KAAK,IAAI,MAAM,qBAAqB,UAAU,EAAE;EAGhD,QAAO,MADc,GAAG,KAAK,EAAE,QAAQ,UAAU,CAAC,GACpC,QAAQ,KAAK,WAAW,OAAO,IAAI,MAAM,UAAU,MAAM,CAAC;CAC1E;;;;CAKA,IAAc,YAAoB,QAAwB;EACxD,MAAM,QAAQ,CAAC,YAAY,MAAM;EACjC,IAAI,KAAK,QACP,MAAM,QAAQ,KAAK,MAAM;EAE3B,OAAO,MAAM,KAAK,GAAG;CACvB;CAEA,QAA4B;EAC1B,IAAI,CAAC,KAAK,IACR,MAAM,IAAI,YAAY,iDAAiD;EAEzE,OAAO,KAAK;CACd;CAEA,SAAmB,UAA0B;EAC3C,MAAM,MAAM,SAAS,SAAS,GAAG,IAAI,SAAS,MAAM,GAAG,EAAE,IAAI,IAAI;EACjE,MAAM,KAAK,KAAK,OAAO,WAAW;EAClC,OAAO,MAAM,GAAG,GAAG,GAAG,QAAQ;CAChC;AACF;;;AChPA,MAAM,YAAY,EAAE,OAAO;;;;;;;;;;CAUzB,aAAa,EAAE,OAAO;;;;;;CAOtB,WAAW,EAAE,SAAS,EAAE,OAAO,CAAC;;;;CAKhC,kBAAkB,EAAE,OAAO;;;;CAK3B,sBAAsB,EAAE,OAAO;AACjC,CAAC;;;;;;;;;AAcD,IAAa,wBAAb,MAAkE;CAChE,MAAyB,QAAQ;CACjC,MAAyB,KAAK,SAAS;CACvC,SAA4B,QAAQ,MAAM;CAC1C,aAAgC,QAAQ,kBAAkB;CAC1D,eAAkC,QAAQ,YAAY;CACtD,SAA4B,QAAQ,cAAc;CAClD,0BAAkD,IAAI,IAAI;;;;;CAM1D,YAAmB,MAAsB;EACvC,OAAO,KAAK,WAAW,KAAK,GAAG,EAAE,WAAW,KAAK,GAAG,EAAE,YAAY;CACpE;CAEA,UAAoB,YAA4B;EAC9C,MAAM,OAAO,KAAK,YAAY,UAAU;EACxC,IAAI,SAAS,KAAK,QAAQ,IAAI,IAAI;EAClC,IAAI,CAAC,QAAQ;GACX,MAAM,WAAW,KAAK,IAAI,YAAY,QAAQ,QAAQ,EAAE;GACxD,SAAS,IAAI,OAAO;IAClB,aAAa,KAAK,IAAI;IACtB,iBAAiB,KAAK,IAAI;IAC1B,QAAQ,KAAK,IAAI,aAAa;IAC9B,UAAU,GAAG,SAAS,GAAG;GAC3B,CAAC;GACD,KAAK,QAAQ,IAAI,MAAM,MAAM;EAC/B;EACA,OAAO;CACT;CAEA,UAA6B,MAAM;EACjC,IAAI;EACJ,SAAS,YAAY;GACnB,KAAK,MAAM,UAAU,KAAK,OAAO,WAAW,OAAO,GAAG;IACpD,IAAI,OAAO,aAAa,MACtB;IAGF,MAAM,OAAO,KAAK,YAAY,OAAO,IAAI;IACzC,MAAM,SAAS,KAAK,UAAU,OAAO,IAAI;IAEzC,KAAK,IAAI,MAAM,wBAAwB,KAAK,KAAK;IAGjD,IAAI,CAAC,MADgB,OAAO,aAAa,GAC5B;KACX,KAAK,IAAI,MAAM,uBAAuB,KAAK,KAAK;KAEhD,IAAI,CAAC,MADiB,OAAO,aAAa,GAExC,MAAM,IAAI,YAAY,+BAA+B,KAAK,EAAE;IAEhE;IAEA,KAAK,IAAI,KAAK,cAAc,OAAO,KAAK,KAAK;GAC/C;EACF;CACF,CAAC;CAED,SAAmB,UAA0B;EAC3C,MAAM,MAAM,KAAK,aAAa,yBAAyB,QAAQ;EAC/D,OAAO,GAAG,KAAK,OAAO,WAAW,EAAE,GAAG;CACxC;CAEA,MAAa,OACX,YACA,MACA,QACiB;EACjB,WAAW,KAAK,SAAS,KAAK,IAAI;EAElC,KAAK,IAAI,MACP,mBAAmB,KAAK,KAAK,eAAe,WAAW,aAAa,OAAO,KAC7E;EAEA,MAAM,SAAS,KAAK,UAAU,UAAU;EAExC,IAAI;GACF,MAAM,SAAS,IAAI,WAAW,MAAM,KAAK,YAAY,CAAC;GACtD,MAAM,OAAO,UACX,QACA,QACA,KAAK,QAAQ,4BACb,KAAA,GACA,EAAE,mBAAmB,mBAAmB,KAAK,IAAI,EAAE,GACnD,KAAK,IACP;GAEA,KAAK,IAAI,MAAM,+BAA+B,QAAQ;GACtD,OAAO;EACT,SAAS,OAAO;GACd,KAAK,IAAI,MAAM,0BAA0B,OAAO;GAChD,IAAI,iBAAiB,OACnB,MAAM,IAAI,YAAY,kBAAkB,MAAM,WAAW,EACvD,OAAO,MACT,CAAC;GAEH,MAAM;EACR;CACF;CAEA,MAAa,SAAS,YAAoB,QAAmC;EAC3E,KAAK,IAAI,MACP,qBAAqB,OAAO,iBAAiB,WAAW,KAC1D;EAGA,MAAM,WAAW,MADF,KAAK,UAAU,UACF,EAAE,kBAAkB,MAAM;EAEtD,IAAI,CAAC,UACH,MAAM,IAAI,kBACR,SAAS,OAAO,yBAAyB,WAAW,EACtD;EAGF,MAAM,WACJ,SAAS,QAAQ,IAAI,cAAc,KACnC,KAAK,aAAa,eAAe,MAAM;EAEzC,MAAM,WAAW,SAAS,QAAQ,IAAI,iBAAiB;EACvD,MAAM,OAAO,WAAW,mBAAmB,QAAQ,IAAI;EAEvD,MAAM,gBAAgB,SAAS,QAAQ,IAAI,gBAAgB;EAC3D,MAAM,OAAO,gBAAgB,OAAO,SAAS,eAAe,EAAE,IAAI;EAIlE,IAAI,CAAC,SAAS,MACZ,OAAO,KAAK,WAAW,WAAW;GAChC,QAAQA,SAAO,MAAM,CAAC;GACtB;GACA,MAAM;EACR,CAAC;EAGH,OAAO,KAAK,WAAW,WAAW;GAChC,QAAQ,SAAS,QAAQ,SAAS,IAAgC;GAClE;GACA,MAAM;GACN;EACF,CAAC;CACH;CAEA,MAAa,OAAO,YAAoB,QAAkC;EACxE,KAAK,IAAI,MACP,+BAA+B,OAAO,eAAe,WAAW,KAClE;EAIA,OAAO,MAFQ,KAAK,UAAU,UACJ,EAAE,aAAa,MAAM,MAC7B;CACpB;CAEA,MAAa,OAAO,YAAoB,QAA+B;EACrE,KAAK,IAAI,MAAM,kBAAkB,OAAO,iBAAiB,WAAW,KAAK;EAEzE,MAAM,SAAS,KAAK,UAAU,UAAU;EAExC,IAAI;GACF,MAAM,OAAO,aAAa,MAAM;EAClC,SAAS,OAAO;GACd,KAAK,IAAI,MAAM,0BAA0B,OAAO;GAChD,IAAI,iBAAiB,OACnB,MAAM,IAAI,kBAAkB,uBAAuB,EAAE,OAAO,MAAM,CAAC;GAErE,MAAM;EACR;CACF;CAEA,MAAa,KAAK,YAAuC;EACvD,KAAK,IAAI,MAAM,4BAA4B,WAAW,KAAK;EAG3D,MAAM,UAAU,MAFD,KAAK,UAAU,UAEH,EAAE,YAAY;EACzC,IAAI,CAAC,SAAS,OAAO,CAAC;EACtB,OAAO,QAAQ,KAAK,WAAW,OAAO,GAAG;CAC3C;CAEA,MAAa,WACX,YACA,SACe;EACf,IAAI,QAAQ,WAAW,GAAG;EAC1B,KAAK,IAAI,MACP,YAAY,QAAQ,OAAO,sBAAsB,WAAW,KAC9D;EACA,MAAM,SAAS,KAAK,UAAU,UAAU;EAExC,KAAK,IAAI,IAAI,GAAG,IAAI,QAAQ,QAAQ,KAAK,KAAM;GAC7C,MAAM,QAAQ,QAAQ,MAAM,GAAG,IAAI,GAAI;GACvC,IAAI;IAGF,MAAM,QACJ,OAGA;IACF,IAAI,OAAO,UAAU,YACnB,MAAM,MAAM,KAAK,QAAQ,KAAK;SAE9B,MAAM,QAAQ,IAAI,MAAM,KAAK,OAAO,OAAO,aAAa,EAAE,CAAC,CAAC;GAEhE,SAAS,OAAO;IACd,KAAK,IAAI,MAAM,2BAA2B,OAAO;IACjD,IAAI,iBAAiB,OACnB,MAAM,IAAI,kBAAkB,wBAAwB,EAAE,OAAO,MAAM,CAAC;IAEtE,MAAM;GACR;EACF;CACF;AACF;;;AClQA,MAAa,eAAe,QAAQ;CAClC,MAAM;CACN,YAAY,CAAC,OAAO;CACpB,UAAU;EACR;EACA;EACA;CACF;CACA,UAAU;EACR;EACA;EACA;CACF;CACA,WAAW,WAAW;EACpB,OAAO,KAAK;GACV,UAAU;GACV,SAAS;GACT,KAAK,OAAO,OAAO,IAAI,4BAA4B;EACrD,CAAC;CACH;AACF,CAAC"}
|
|
@@ -1,8 +1,5 @@
|
|
|
1
|
-
import * as _$alepha from "alepha";
|
|
2
1
|
import { Alepha, MiddlewareMetadata, OPTIONS, Primitive, Service, Static } from "alepha";
|
|
3
2
|
import { DateTimeProvider, DurationLike, Timeout } from "alepha/datetime";
|
|
4
|
-
import * as _$alepha_logger0 from "alepha/logger";
|
|
5
|
-
import * as _$typebox from "typebox";
|
|
6
3
|
|
|
7
4
|
//#region ../../src/cache/core/providers/CacheProvider.d.ts
|
|
8
5
|
/**
|
|
@@ -249,9 +246,9 @@ interface CachePrimitiveOptions<TReturn = any, TParameter extends any[] = any[]>
|
|
|
249
246
|
/**
|
|
250
247
|
* Cache configuration atom.
|
|
251
248
|
*/
|
|
252
|
-
declare const cacheOptions:
|
|
253
|
-
enabled:
|
|
254
|
-
defaultTtl:
|
|
249
|
+
declare const cacheOptions: import("alepha").Atom<import("typebox").TObject<{
|
|
250
|
+
enabled: import("typebox").TBoolean;
|
|
251
|
+
defaultTtl: import("typebox").TNumber;
|
|
255
252
|
}>, "alepha.cache.options">;
|
|
256
253
|
type CacheAtomOptions = Static<typeof cacheOptions.schema>;
|
|
257
254
|
declare module "alepha" {
|
|
@@ -386,9 +383,9 @@ declare const KV_DEFAULT_BINDING = "KV_CACHE";
|
|
|
386
383
|
*/
|
|
387
384
|
declare class CloudflareKVProvider extends CacheProvider {
|
|
388
385
|
protected readonly alepha: Alepha;
|
|
389
|
-
protected readonly log:
|
|
386
|
+
protected readonly log: import("alepha/logger").Logger;
|
|
390
387
|
protected kv?: KVNamespace;
|
|
391
|
-
protected readonly onStart:
|
|
388
|
+
protected readonly onStart: import("alepha").HookPrimitive<"start">;
|
|
392
389
|
get(name: string, key: string): Promise<Uint8Array | undefined>;
|
|
393
390
|
set(name: string, key: string, value: Uint8Array, ttl?: number): Promise<Uint8Array>;
|
|
394
391
|
del(name: string, ...keys: string[]): Promise<void>;
|
|
@@ -475,7 +472,7 @@ interface MemoryCacheProviderOptions {
|
|
|
475
472
|
*/
|
|
476
473
|
declare class MemoryCacheProvider extends CacheProvider {
|
|
477
474
|
protected readonly dateTimeProvider: DateTimeProvider;
|
|
478
|
-
protected readonly log:
|
|
475
|
+
protected readonly log: import("alepha/logger").Logger;
|
|
479
476
|
protected store: Record<CacheName, Record<CacheKey, CacheValue>>;
|
|
480
477
|
/**
|
|
481
478
|
* All recorded get calls.
|
|
@@ -636,7 +633,7 @@ declare module "alepha" {
|
|
|
636
633
|
*
|
|
637
634
|
* @module alepha.cache
|
|
638
635
|
*/
|
|
639
|
-
declare const AlephaCache:
|
|
636
|
+
declare const AlephaCache: import("alepha").Service<import("alepha").Module>;
|
|
640
637
|
//#endregion
|
|
641
638
|
export { $cache, AlephaCache, CacheAtomOptions, CacheMemoryTierOptions, CacheMiddlewareFn, CachePrimitive, CachePrimitiveFn, CachePrimitiveOptions, CacheProvider, CloudflareKVProvider, KVListKey, KVListOptions, KVListResult, KVNamespace, KVPutOptions, KV_DEFAULT_BINDING, MemoryCacheCall, MemoryCacheDelCall, MemoryCacheProvider, MemoryCacheProviderOptions, MemoryCacheSetCall, MemoryCacheStats, cacheOptions };
|
|
642
639
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","names":[],"sources":["../../../src/cache/core/providers/CacheProvider.ts","../../../src/cache/core/primitives/$cache.ts","../../../src/cache/core/providers/CloudflareKVProvider.ts","../../../src/cache/core/providers/MemoryCacheProvider.ts","../../../src/cache/core/index.ts"],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.d.ts","names":[],"sources":["../../../src/cache/core/providers/CacheProvider.ts","../../../src/cache/core/primitives/$cache.ts","../../../src/cache/core/providers/CloudflareKVProvider.ts","../../../src/cache/core/providers/MemoryCacheProvider.ts","../../../src/cache/core/index.ts"],"mappings":";;;;;;;;AAQA;;uBAAsB,aAAA;EAAA,UACV,OAAA,EAAS,WAAA;EAAA,UACT,OAAA,EAAS,WAAA;EAAA,UACT,KAAA;;;;;;EA6CsC;;;;;;;;EAAA,SA9BhC,GAAA,CACd,IAAA,UACA,GAAA,WACC,OAAA,CAAQ,UAAA;EAsI0B;;;;;;;;;;EAAA,SA1HrB,GAAA,CACd,IAAA,UACA,GAAA,UACA,KAAA,EAAO,UAAA,EACP,GAAA,YACC,OAAA,CAAQ,UAAA;EArCQ;;;;;;EAAA,SA6CH,GAAA,CAAI,IAAA,aAAiB,IAAA,aAAiB,OAAA;EAAA,SAEtC,GAAA,CAAI,IAAA,UAAc,GAAA,WAAc,OAAA;EAAA,SAEhC,IAAA,CAAK,IAAA,UAAc,MAAA,YAAkB,OAAA;EA/BnD;;;EAAA,SAoCc,KAAA,IAAS,OAAA;EAtBT;;;;;;;;;;;EAAA,SAmCA,IAAA,CACd,IAAA,UACA,GAAA,UACA,MAAA,WACC,OAAA;EAxBa;;;EAiCH,QAAA,CACX,IAAA,UACA,GAAA,UACA,KAAA,WACA,OAAA;IAAY,GAAA;IAAc,QAAA;EAAA,IACzB,OAAA;EApCkD;;;EA+CxC,QAAA,IAAY,IAAA,UAAc,GAAA,WAAc,OAAA,CAAQ,CAAA;EA5B3D;;;;;;EA8CW,cAAA,CAAe,IAAA,UAAc,IAAA,aAAiB,OAAA;EA/BzD;;;EAAA,UAiDQ,SAAA,CAAU,KAAA,YAAiB,UAAA;EA/ClC;;;EAAA,UAqEO,WAAA,IAAe,UAAA,EAAY,UAAA,GAAa,CAAA;EA1DX;;;EAAA,UA8EvB,QAAA,CAAS,IAAA,EAAM,UAAA,GAAa,OAAA,CAAQ,UAAA;EA5DxB;;;EAAA,UA+EZ,UAAA,CAAW,IAAA,EAAM,UAAA,GAAa,OAAA,CAAQ,UAAA;AAAA;;;;AAlMxD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBCwCgB,MAAA,qDACd,OAAA,EAAS,qBAAA,CAAsB,OAAA,EAAS,UAAA;EACtC,OAAA,MAAa,IAAA,EAAM,UAAA,KAAe,OAAA;AAAA,IAEnC,gBAAA,CAAiB,OAAA,EAAS,UAAA;AAAA,iBACb,MAAA,kDACd,OAAA,GAAU,qBAAA,CAAsB,OAAA,EAAS,UAAA,IACxC,iBAAA,CAAkB,OAAA;AAAA,kBAFL,MAAA;;;;UA8CC,sBAAA;;;;;;;EAOf,GAAA,GAAM,YAAA;EDhEJ;;;;;ECuEF,GAAA;EDnEW;;;;;;EC2EX,QAAA,GAAW,YAAY;AAAA;AAAA,UAGR,qBAAA;EDlEC;;;;;;;EC6EhB,IAAA;EDzDE;;;EC8DF,OAAA,OAAc,IAAA,EAAM,UAAA,KAAe,OAAA;EDlDjC;;;;ECwDF,GAAA,OAAU,IAAA,EAAM,UAAA;EDrDd;;;;;;;;;;;;;;;;;;;;;EC4EF,QAAA,GAAW,OAAA,CAAQ,aAAA;EDcM;;;;;;ECNzB,GAAA,GAAM,YAAA;EDyBgD;;AAAU;ECpBhE,QAAA;;;AAtIF;;EA4IE,QAAA;EA3I+B;;;;;;;;;;;;;;EA2J/B,MAAA,UAAgB,sBAAA;EA1Jd;;;;;;;;;AAEmC;AACvC;EAoKE,KAAA,GAAQ,YAAA;AAAA;;;;cAQG,YAAA,mBAAY,IAAA,mBAAA,OAAA;;;;KAkBb,gBAAA,GAAmB,MAAM,QAAQ,YAAA,CAAa,MAAA;AAAA;EAAA,UAG9C,KAAA;IAAA,CACP,YAAA,CAAa,GAAG,GAAG,gBAAA;EAAA;AAAA;AAAA,cAQlB,UAAA;AAAA,KAED,WAAA;EAAA,CACF,UAAU;EACX,CAAA;EACA,CAAA;AAAA;AAAA,KAGG,OAAA;EACH,KAAA,EAAO,CAAC;EACR,SAAA;EACA,QAAA;AAAA;AAAA,KAGG,UAAA;EACH,KAAA,EAAO,CAAC;EACR,KAAA;AAAA;AAAA,cAGW,cAAA,0DAGH,SAAA,CAAU,qBAAA,CAAsB,OAAA,EAAS,UAAA;EAAA,mBAC9B,QAAA,EAAQ,QAAA;;;;qBACR,gBAAA,EAAgB,gBAAA;EAAA,SACnB,QAAA,EAAQ,aAAA;EAAA,mBAEL,WAAA,GAAc,GAAA,SAAY,OAAA,CAAQ,OAAA;EAAA,mBAClC,SAAA;EAAA,mBACA,WAAA;EAAA,mBACA,aAAA;EAAA,mBAEA,iBAAA,EAAiB,GAAA,SAAA,OAAA,CAAA,OAAA;cAGlC,IAAA,EAAM,qBAAA,QACG,SAAA,CAAU,qBAAA,CAAsB,OAAA,EAAS,UAAA;EAAA,IA+BzC,SAAA;EAOE,GAAA,IAAO,IAAA,EAAM,UAAA,GAAa,OAAA,CAAQ,OAAA;EAsBxC,GAAA,IAAO,IAAA,EAAM,UAAA;EAIP,IAAA,CAAK,GAAA,UAAa,MAAA,YAAa,OAAA;EAO/B,UAAA,IAAc,IAAA,aAAiB,OAAA;EAoB/B,GAAA,CACX,GAAA,UACA,KAAA,EAAO,OAAA,EACP,GAAA,GAAM,YAAA,GACL,OAAA;EAqDU,GAAA,CAAI,GAAA,WAAc,OAAA,CAAQ,OAAA;EAhTvC;;;;;EA0Ta,IAAA,CAAK,GAAA,WAAc,OAAA,CAAQ,UAAA,CAAW,OAAA;EA/SzC;;;;EAoYG,eAAA,CACX,GAAA,UACA,OAAA,QAAe,OAAA,CAAQ,OAAA,IAAW,OAAA,GACjC,OAAA,CAAQ,OAAA;EAxWX;;;;;EA6XO,eAAA,CACL,GAAA,UACA,OAAA,QAAe,OAAA,CAAQ,OAAA,IAAW,OAAA;EAAA,UAwB1B,SAAA,IAAa,aAAA;EAAA,UAYb,aAAA,CAAc,KAAA,YAAiB,KAAA,IAAS,WAAA;EAAA,UAUxC,KAAA,CAAM,GAAA,UAAa,KAAA,EAAO,OAAA,CAAQ,OAAA;EAAA,UAalC,KAAA,CAAM,GAAA;AAAA;AAAA,UAKD,gBAAA,0DAGP,cAAA,CAAe,OAAA,EAAS,UAAA;;;;MAI5B,IAAA,EAAM,UAAA,GAAa,OAAA,CAAQ,OAAA;AAAA;;;;;UAOhB,iBAAA,wBACP,cAAA,CAAe,OAAA;EAAA,eACR,IAAA,iBAAqB,OAAA,EAAS,CAAA,GAAI,CAAA;EAAA,CAChD,OAAA,IAAW,kBAAA;AAAA;;;;;ADznBd;UEEiB,WAAA;EACf,GAAA,CAAI,GAAA,UAAa,IAAA,kBAAsB,OAAA,CAAQ,WAAA;EAC/C,GAAA,CAAI,GAAA,UAAa,IAAA,WAAe,OAAA;EAChC,GAAA,CAAI,GAAA,UAAa,IAAA,YAAgB,OAAA;EACjC,GAAA,CACE,GAAA,UACA,KAAA,WAAgB,WAAA,GAAc,cAAA,EAC9B,OAAA,GAAU,YAAA,GACT,OAAA;EACH,MAAA,CAAO,GAAA,WAAc,OAAA;EACrB,IAAA,CAAK,OAAA,GAAU,aAAA,GAAgB,OAAA,CAAQ,YAAA;AAAA;AAAA,UAGxB,YAAA;EACf,UAAA;EACA,aAAA;EACA,QAAA;AAAA;AAAA,UAGe,aAAA;EACf,MAAA;EACA,KAAA;EACA,MAAA;AAAA;AAAA,UAGe,YAAA;EACf,IAAA,EAAM,SAAS;EACf,aAAA;EACA,MAAA;AAAA;AAAA,UAGe,SAAA;EACf,IAAA;EACA,UAAA;EACA,QAAA;AAAA;;;;cAQW,kBAAA;;;;;;;;;;;;;;;;;;cAqBA,oBAAA,SAA6B,aAAA;EAAA,mBACrB,MAAA,EAAM,MAAA;EAAA,mBACN,GAAA,0BAAG,MAAA;EAAA,UAEZ,EAAA,GAAK,WAAA;EAAA,mBAEI,OAAA,mBAAO,aAAA;EAsCb,GAAA,CAAI,IAAA,UAAc,GAAA,WAAc,OAAA,CAAQ,UAAA;EAmBxC,GAAA,CACX,IAAA,UACA,GAAA,UACA,KAAA,EAAO,UAAA,EACP,GAAA,YACC,OAAA,CAAQ,UAAA;EAwBE,GAAA,CAAI,IAAA,aAAiB,IAAA,aAAiB,OAAA;EAoBtC,GAAA,CAAI,IAAA,UAAc,GAAA,WAAc,OAAA;EAMhC,IAAA,CAAK,IAAA,UAAc,MAAA,YAAkB,OAAA;EAQrC,KAAA,IAAS,OAAA;EAST,IAAA,CACX,IAAA,UACA,GAAA,UACA,MAAA,WACC,OAAA;EF1JgC;;;EAAA,UE6KzB,MAAA,IAAU,IAAA;EAAA,UAIV,KAAA,IAAS,WAAA;EF9JjB;;;EAAA,UE0Kc,WAAA,CAAY,MAAA,WAAiB,OAAA;AAAA;;;KClP1C,SAAA;AAAA,KACA,QAAA;AAAA,KACA,UAAA;EACH,IAAA,GAAO,UAAA;EACP,OAAA,GAAU,OAAO;AAAA;AAAA,UAKF,eAAA;EACf,IAAA;EACA,GAAA;EACA,SAAA;AAAA;AAAA,UAGe,kBAAA,SAA2B,eAAe;EACzD,KAAA,EAAO,UAAA;EACP,GAAA;AAAA;AAAA,UAGe,kBAAA;EACf,IAAA;EACA,IAAA;EACA,SAAA;AAAA;AAAA,UAGe,gBAAA;EACf,IAAA;EACA,MAAA;EACA,IAAA;EACA,OAAA;AAAA;AAAA,UAGe,0BAAA;EHoKkB;;;EGhKjC,QAAA,GAAW,KAAA;EHgK0C;;;EG5JrD,QAAA,GAAW,KAAA;EHpCQ;;;EGwCnB,QAAA,GAAW,KAAA;AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;cA8BA,mBAAA,SAA4B,aAAA;EAAA,mBACpB,gBAAA,EAAgB,gBAAA;EAAA,mBAChB,GAAA,0BAAG,MAAA;EAAA,UAEZ,KAAA,EAAO,MAAA,CAAO,SAAA,EAAW,MAAA,CAAO,QAAA,EAAU,UAAA;EHPlD;;;EGgBK,QAAA,EAAU,eAAA;EHJJ;;;EGSN,QAAA,EAAU,kBAAA;EHLH;;;EGUP,QAAA,EAAU,kBAAA;EHEJ;;;EAAA,UGGH,MAAA,EAAQ,gBAAA;EHHmC;;;EGa9C,QAAA,EAAU,KAAA;EHKyB;;;EGAnC,QAAA,EAAU,KAAA;EHkBoB;;;EGb9B,QAAA,EAAU,KAAA;cAEL,OAAA,GAAS,0BAAA;EAWR,GAAA,CAAI,IAAA,UAAc,GAAA,WAAc,OAAA,CAAQ,UAAA;EAsBxC,GAAA,CACX,IAAA,UACA,GAAA,UACA,KAAA,EAAO,UAAA,EACP,GAAA,YACC,OAAA,CAAQ,UAAA;EAuCE,GAAA,CAAI,IAAA,aAAiB,IAAA,aAAiB,OAAA;EAgDtC,GAAA,CAAI,IAAA,UAAc,GAAA,WAAc,OAAA;EAIhC,IAAA,CAAK,IAAA,UAAc,MAAA,YAAkB,OAAA;EASrC,KAAA,IAAS,OAAA;EAgBT,IAAA,CACX,IAAA,UACA,GAAA,UACA,MAAA,WACC,OAAA;EHtF8B;;;;;AAA+B;;;;EG4HzD,KAAA,IAAS,gBAAA;EFtRI;;;;;;;;EEkSb,MAAA,CAAO,IAAA,UAAc,GAAA;EF9R3B;;;;;;;;EE6SM,MAAA,CAAO,IAAA,UAAc,GAAA;EF/Sb;;;;;;;;EE8TR,UAAA,CAAW,IAAA,UAAc,GAAA;EF3TlB;;;;;;;;EE4UP,IAAA,CAAK,IAAA;EF1UM;;;;;;;;EE4VX,KAAA;EF5VmB;AAAA;;;;;AAFN;AA8CtB;;;EE8TS,KAAA;AAAA;;;;YClZG,KAAA;IJPuB;;;IIW/B,WAAA;MACE,SAAA;MACA,GAAA;IAAA;IJyBD;;;IIpBD,YAAA;MACE,SAAA;MACA,GAAA;IAAA;IJ6EyD;;;IIxE3D,WAAA;MACE,SAAA;MACA,GAAA;MACA,KAAA;IAAA;IJmJwC;;;;II7I1C,aAAA;MACE,SAAA;MACA,GAAA;IAAA;IJlCM;;;;IIwCR,kBAAA;MACE,SAAA;MACA,GAAA;IAAA;EAAA;AAAA;;;;;;;;;;;;;cAmBO,WAAA,mBAAW,OAAA,kBAAA,MAAA"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":[],"sources":["../../../src/cache/core/errors/CacheError.ts","../../../src/cache/core/providers/CacheProvider.ts","../../../src/cache/core/providers/MemoryCacheProvider.ts","../../../src/cache/core/primitives/$cache.ts","../../../src/cache/core/providers/CloudflareKVProvider.ts","../../../src/cache/core/index.ts"],"sourcesContent":["import { AlephaError } from \"alepha\";\n\nexport class CacheError extends AlephaError {}\n","import { CacheError } from \"../errors/CacheError.ts\";\n\n/**\n * Cache provider interface.\n *\n * All methods are asynchronous and return promises.\n * Values are stored as Uint8Array.\n */\nexport abstract class CacheProvider {\n protected encoder: TextEncoder = new TextEncoder();\n protected decoder: TextDecoder = new TextDecoder();\n protected codes = {\n BINARY: 0x01,\n JSON: 0x02,\n STRING: 0x03,\n COMPRESSED: 0x04,\n };\n\n /**\n * Get the value of a key.\n *\n * @param name Cache name, used to group keys. Should be Redis-like \"some:group:name\" format.\n * @param key The key of the value to get.\n *\n * @return The value of the key, or undefined if the key does not exist.\n */\n public abstract get(\n name: string,\n key: string,\n ): Promise<Uint8Array | undefined>;\n\n /**\n * Set the string value of a key.\n *\n * @param name Cache name, used to group keys. Should be Redis-like \"some:group:name\" format.\n * @param key The key of the value to set.\n * @param value The value to set.\n * @param ttl The time-to-live of the key, in milliseconds.\n *\n * @return The value of the key.\n */\n public abstract set(\n name: string,\n key: string,\n value: Uint8Array,\n ttl?: number,\n ): Promise<Uint8Array>;\n\n /**\n * Remove the specified keys.\n *\n * @param name Cache name, used to group keys. Should be Redis-like \"some:group:name\" format.\n * @param keys The keys to delete.\n */\n public abstract del(name: string, ...keys: string[]): Promise<void>;\n\n public abstract has(name: string, key: string): Promise<boolean>;\n\n public abstract keys(name: string, filter?: string): Promise<string[]>;\n\n /**\n * Remove all keys from all cache names.\n */\n public abstract clear(): Promise<void>;\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 when using Redis.\n *\n * @param name Cache name, used to group keys.\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(\n name: string,\n key: string,\n amount: number,\n ): Promise<number>;\n\n // ---------------------------------------------------------------------------\n // High-level methods — serialize/compress layer used by CachePrimitive\n // ---------------------------------------------------------------------------\n\n /**\n * Set a typed value with automatic serialization and optional compression.\n */\n public async setTyped(\n name: string,\n key: string,\n value: unknown,\n options?: { ttl?: number; compress?: boolean },\n ): Promise<void> {\n let data = this.serialize(value);\n if (options?.compress) {\n data = await this.compress(data);\n }\n await this.set(name, key, data, options?.ttl);\n }\n\n /**\n * Get a typed value with automatic deserialization and optional decompression.\n */\n public async getTyped<T>(name: string, key: string): Promise<T | undefined> {\n const data = await this.get(name, key);\n if (data) {\n if (data[0] === this.codes.COMPRESSED) {\n const decompressed = await this.decompress(data.subarray(1));\n return this.deserialize<T>(decompressed);\n }\n return this.deserialize<T>(data);\n }\n return undefined;\n }\n\n /**\n * Invalidate cache keys with wildcard support.\n *\n * Keys ending in `*` are expanded via `this.keys()`.\n * Called with no keys, delegates to `this.del(name)` which clears the entire container.\n */\n public async invalidateKeys(name: string, keys: string[]): Promise<void> {\n const keysToDelete: string[] = [];\n\n for (const key of keys) {\n if (key.endsWith(\"*\")) {\n const result = await this.keys(name, key.slice(0, -1));\n keysToDelete.push(...result);\n } else {\n keysToDelete.push(key);\n }\n }\n\n await this.del(name, ...keysToDelete);\n }\n\n /**\n * Serialize a value to a typed Uint8Array with a leading type marker byte.\n */\n protected serialize(value: unknown): Uint8Array {\n if (value instanceof Uint8Array) {\n const result = new Uint8Array(1 + value.length);\n result[0] = this.codes.BINARY;\n result.set(value, 1);\n return result;\n }\n\n const encoded = this.encoder.encode(\n typeof value === \"string\" ? value : JSON.stringify(value),\n );\n const code =\n typeof value === \"string\" ? this.codes.STRING : this.codes.JSON;\n const result = new Uint8Array(1 + encoded.length);\n result[0] = code;\n result.set(encoded, 1);\n return result;\n }\n\n /**\n * Deserialize a typed Uint8Array back to the original value.\n */\n protected deserialize<T>(uint8Array: Uint8Array): T {\n const type = uint8Array[0];\n const payload = uint8Array.slice(1);\n\n if (type === this.codes.BINARY) {\n return payload as T;\n }\n if (type === this.codes.JSON) {\n return JSON.parse(this.decoder.decode(payload)) as T;\n }\n if (type === this.codes.STRING) {\n return this.decoder.decode(payload) as T;\n }\n\n throw new CacheError(`Unknown serialization type: ${type}`);\n }\n\n /**\n * Compress data with gzip, prepending a COMPRESSED marker byte.\n */\n protected async compress(data: Uint8Array): Promise<Uint8Array> {\n const buf = (data.buffer as ArrayBuffer).slice(\n data.byteOffset,\n data.byteOffset + data.byteLength,\n );\n const compressed = new Uint8Array(\n await new Response(\n new Blob([buf]).stream().pipeThrough(new CompressionStream(\"gzip\")),\n ).arrayBuffer(),\n );\n const result = new Uint8Array(1 + compressed.length);\n result[0] = this.codes.COMPRESSED;\n result.set(compressed, 1);\n return result;\n }\n\n /**\n * Decompress gzipped data.\n */\n protected async decompress(data: Uint8Array): Promise<Uint8Array> {\n const buf = (data.buffer as ArrayBuffer).slice(\n data.byteOffset,\n data.byteOffset + data.byteLength,\n );\n return new Uint8Array(\n await new Response(\n new Blob([buf]).stream().pipeThrough(new DecompressionStream(\"gzip\")),\n ).arrayBuffer(),\n );\n }\n}\n","import { $inject } from \"alepha\";\nimport { DateTimeProvider, type Timeout } from \"alepha/datetime\";\nimport { $logger } from \"alepha/logger\";\nimport { CacheProvider } from \"./CacheProvider.ts\";\n\ntype CacheName = string;\ntype CacheKey = string;\ntype CacheValue = {\n data?: Uint8Array;\n timeout?: Timeout;\n};\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport interface MemoryCacheCall {\n name: string;\n key: string;\n timestamp: number;\n}\n\nexport interface MemoryCacheSetCall extends MemoryCacheCall {\n value: Uint8Array;\n ttl?: number;\n}\n\nexport interface MemoryCacheDelCall {\n name: string;\n keys: string[];\n timestamp: number;\n}\n\nexport interface MemoryCacheStats {\n hits: number;\n misses: number;\n sets: number;\n deletes: number;\n}\n\nexport interface MemoryCacheProviderOptions {\n /**\n * Error to throw on get operations (for testing error handling)\n */\n getError?: Error | null;\n /**\n * Error to throw on set operations (for testing error handling)\n */\n setError?: Error | null;\n /**\n * Error to throw on del operations (for testing error handling)\n */\n delError?: Error | null;\n}\n\n// ---------------------------------------------------------------------------------------------------------------------\n\n/**\n * In-memory implementation of CacheProvider for testing.\n *\n * This provider stores all cache entries in memory, making it ideal for\n * unit tests that need to verify cache operations without touching Redis or other backends.\n *\n * @example\n * ```typescript\n * // In tests, substitute the real CacheProvider with MemoryCacheProvider\n * const alepha = Alepha.create().with({\n * provide: CacheProvider,\n * use: MemoryCacheProvider,\n * });\n *\n * // Run code that uses caching\n * const service = alepha.inject(MyService);\n * await service.fetchWithCache(\"key\");\n *\n * // Verify cache behavior\n * const cache = alepha.inject(MemoryCacheProvider);\n * expect(cache.stats().misses).toBe(1);\n * await service.fetchWithCache(\"key\");\n * expect(cache.stats().hits).toBe(1);\n * ```\n */\nexport class MemoryCacheProvider extends CacheProvider {\n protected readonly dateTimeProvider = $inject(DateTimeProvider);\n protected readonly log = $logger();\n\n protected store: Record<CacheName, Record<CacheKey, CacheValue>> = {};\n\n // ─────────────────────────────────────────────────────────────────────────────\n // Test tracking\n // ─────────────────────────────────────────────────────────────────────────────\n\n /**\n * All recorded get calls.\n */\n public getCalls: MemoryCacheCall[] = [];\n\n /**\n * All recorded set calls.\n */\n public setCalls: MemoryCacheSetCall[] = [];\n\n /**\n * All recorded del calls.\n */\n public delCalls: MemoryCacheDelCall[] = [];\n\n /**\n * Cache statistics.\n */\n protected _stats: MemoryCacheStats = {\n hits: 0,\n misses: 0,\n sets: 0,\n deletes: 0,\n };\n\n /**\n * Error to throw on get (for testing error handling)\n */\n public getError: Error | null = null;\n\n /**\n * Error to throw on set (for testing error handling)\n */\n public setError: Error | null = null;\n\n /**\n * Error to throw on del (for testing error handling)\n */\n public delError: Error | null = null;\n\n constructor(options: MemoryCacheProviderOptions = {}) {\n super();\n this.getError = options.getError ?? null;\n this.setError = options.setError ?? null;\n this.delError = options.delError ?? null;\n }\n\n // ─────────────────────────────────────────────────────────────────────────────\n // CacheProvider implementation\n // ─────────────────────────────────────────────────────────────────────────────\n\n public async get(name: string, key: string): Promise<Uint8Array | undefined> {\n this.getCalls.push({\n name,\n key,\n timestamp: this.dateTimeProvider.nowMillis(),\n });\n\n if (this.getError) {\n throw this.getError;\n }\n\n const data = this.store[name]?.[key]?.data;\n\n if (data !== undefined) {\n this._stats.hits++;\n } else {\n this._stats.misses++;\n }\n\n return data;\n }\n\n public async set(\n name: string,\n key: string,\n value: Uint8Array,\n ttl?: number,\n ): Promise<Uint8Array> {\n this.setCalls.push({\n name,\n key,\n value,\n ttl,\n timestamp: this.dateTimeProvider.nowMillis(),\n });\n this._stats.sets++;\n\n if (this.setError) {\n throw this.setError;\n }\n\n if (this.store[name] == null) {\n this.store[name] = {};\n }\n\n this.store[name][key] ??= {};\n this.store[name][key].data = value;\n\n this.log.debug(`Setting cache for name`, { name, key, ttl });\n\n // clear previous timeout if exists\n if (this.store[name][key].timeout) {\n this.dateTimeProvider.clearTimeout(this.store[name][key].timeout);\n this.store[name][key].timeout = undefined;\n }\n\n if (ttl) {\n this.store[name][key].timeout = this.dateTimeProvider.createTimeout(\n () => this.del(name, key),\n ttl,\n );\n }\n\n return this.store[name][key].data;\n }\n\n public async del(name: string, ...keys: string[]): Promise<void> {\n this.delCalls.push({\n name,\n keys,\n timestamp: this.dateTimeProvider.nowMillis(),\n });\n this._stats.deletes++;\n\n if (this.delError) {\n throw this.delError;\n }\n\n // delete all keys in name\n if (keys.length === 0) {\n this.log.debug(`Deleting all cache for name`, { name });\n\n if (this.store[name]) {\n for (const key of Object.keys(this.store[name])) {\n const timeout = this.store[name][key]?.timeout;\n if (timeout) {\n this.dateTimeProvider.clearTimeout(timeout);\n }\n }\n }\n delete this.store[name];\n return;\n }\n\n this.log.debug(`Deleting cache for name`, { name, keys });\n\n // delete specific keys in name\n for (const key of keys) {\n if (this.store[name] == null) break;\n\n const timeout = this.store[name][key]?.timeout;\n if (timeout) {\n this.dateTimeProvider.clearTimeout(timeout);\n }\n\n delete this.store[name][key];\n }\n\n if (Object.keys(this.store[name] ?? {}).length === 0) {\n // if name is empty, delete it\n delete this.store[name];\n }\n }\n\n public async has(name: string, key: string): Promise<boolean> {\n return this.store[name]?.[key]?.data != null;\n }\n\n public async keys(name: string, filter?: string): Promise<string[]> {\n const store = this.store[name] ?? {};\n const keys = Object.keys(store);\n if (filter) {\n return keys.filter((key) => key.startsWith(filter));\n }\n return keys;\n }\n\n public async clear(): Promise<void> {\n this.log.debug(\"Clearing all cache\");\n\n // Clear all timeouts before clearing the store\n for (const name of Object.keys(this.store)) {\n for (const key of Object.keys(this.store[name])) {\n const timeout = this.store[name][key]?.timeout;\n if (timeout) {\n this.dateTimeProvider.clearTimeout(timeout);\n }\n }\n }\n\n this.store = {};\n }\n\n public async incr(\n name: string,\n key: string,\n amount: number,\n ): Promise<number> {\n if (this.store[name] == null) {\n this.store[name] = {};\n }\n\n const existing = this.store[name][key]?.data;\n let current = 0;\n\n if (existing) {\n try {\n current = this.deserialize<number>(existing);\n } catch {\n // Fallback for raw bytes without type marker\n const str = new TextDecoder().decode(existing);\n current = Number.parseInt(str, 10) || 0;\n }\n }\n\n const newValue = current + amount;\n this.store[name][key] ??= {};\n this.store[name][key].data = this.serialize(newValue);\n\n return newValue;\n }\n\n // ─────────────────────────────────────────────────────────────────────────────\n // Test utilities\n // ─────────────────────────────────────────────────────────────────────────────\n\n /**\n * Get cache statistics (hits, misses, sets, deletes).\n *\n * @example\n * ```typescript\n * expect(cache.stats().hits).toBe(1);\n * expect(cache.stats().misses).toBe(0);\n * ```\n */\n public stats(): MemoryCacheStats {\n return { ...this._stats };\n }\n\n /**\n * Check if a key was set during the test.\n *\n * @example\n * ```typescript\n * expect(cache.wasSet(\"my-cache\", \"user:123\")).toBe(true);\n * ```\n */\n public wasSet(name: string, key?: string): boolean {\n if (key === undefined) {\n return this.setCalls.some((call) => call.name === name);\n }\n return this.setCalls.some((call) => call.name === name && call.key === key);\n }\n\n /**\n * Check if a key was retrieved during the test.\n *\n * @example\n * ```typescript\n * expect(cache.wasGet(\"my-cache\", \"user:123\")).toBe(true);\n * ```\n */\n public wasGet(name: string, key?: string): boolean {\n if (key === undefined) {\n return this.getCalls.some((call) => call.name === name);\n }\n return this.getCalls.some((call) => call.name === name && call.key === key);\n }\n\n /**\n * Check if a key was deleted during the test.\n *\n * @example\n * ```typescript\n * expect(cache.wasDeleted(\"my-cache\", \"user:123\")).toBe(true);\n * ```\n */\n public wasDeleted(name: string, key?: string): boolean {\n if (key === undefined) {\n return this.delCalls.some((call) => call.name === name);\n }\n return this.delCalls.some(\n (call) => call.name === name && call.keys.includes(key),\n );\n }\n\n /**\n * Get the number of cached entries for a specific cache name.\n *\n * @example\n * ```typescript\n * expect(cache.size(\"my-cache\")).toBe(5);\n * ```\n */\n public size(name?: string): number {\n if (name === undefined) {\n return Object.values(this.store).reduce(\n (total, entries) => total + Object.keys(entries).length,\n 0,\n );\n }\n return Object.keys(this.store[name] ?? {}).length;\n }\n\n /**\n * Get all cache names.\n *\n * @example\n * ```typescript\n * expect(cache.names()).toContain(\"my-cache\");\n * ```\n */\n public names(): string[] {\n return Object.keys(this.store);\n }\n\n /**\n * Reset all in-memory state (useful between tests).\n *\n * @example\n * ```typescript\n * beforeEach(() => {\n * cache.reset();\n * });\n * ```\n */\n public reset(): void {\n // Clear all timeouts\n for (const name of Object.keys(this.store)) {\n for (const key of Object.keys(this.store[name])) {\n const timeout = this.store[name][key]?.timeout;\n if (timeout) {\n this.dateTimeProvider.clearTimeout(timeout);\n }\n }\n }\n\n this.store = {};\n this.getCalls = [];\n this.setCalls = [];\n this.delCalls = [];\n this._stats = { hits: 0, misses: 0, sets: 0, deletes: 0 };\n this.getError = null;\n this.setError = null;\n this.delError = null;\n }\n}\n","import {\n $atom,\n $inject,\n $state,\n AlephaError,\n createPrimitive,\n KIND,\n type MiddlewareMetadata,\n OPTIONS,\n Primitive,\n type Service,\n type Static,\n t,\n} from \"alepha\";\nimport { DateTimeProvider, type DurationLike } from \"alepha/datetime\";\nimport { CacheProvider } from \"../providers/CacheProvider.ts\";\nimport { MemoryCacheProvider } from \"../providers/MemoryCacheProvider.ts\";\n\n/**\n * Creates a cache primitive for caching with automatic management.\n *\n * **Middleware mode** (no `handler`) — usable in `use` arrays AND as a store:\n * ```ts\n * class UserService {\n * userCache = $cache({ name: \"users\", ttl: [10, \"minutes\"] });\n *\n * fetchUser = $pipeline({\n * use: [this.userCache],\n * handler: async (userId: string) => this.repo.getById(userId),\n * });\n *\n * async invalidateUser(userId: string) {\n * await this.userCache.invalidate(userId);\n * }\n * }\n * ```\n *\n * **Primitive mode** (with `handler`) — standalone callable:\n * ```ts\n * getUserData = $cache({\n * name: \"user-data\",\n * ttl: [10, \"minutes\"],\n * handler: async (userId: string) => {\n * return await database.users.findById(userId);\n * }\n * });\n * ```\n */\nexport function $cache<TReturn = string, TParameter extends any[] = any[]>(\n options: CachePrimitiveOptions<TReturn, TParameter> & {\n handler: (...args: TParameter) => TReturn;\n },\n): CachePrimitiveFn<TReturn, TParameter>;\nexport function $cache<TReturn = any, TParameter extends any[] = any[]>(\n options?: CachePrimitiveOptions<TReturn, TParameter>,\n): CacheMiddlewareFn<TReturn>;\nexport function $cache(options: any = {}): any {\n const instance = createPrimitive(CachePrimitive, options);\n\n if (options.handler) {\n const fn = (...args: any[]): Promise<any> => instance.run(...args);\n return Object.setPrototypeOf(fn, instance);\n }\n\n // Middleware mode: callable as (handler) => wrappedHandler, with store methods\n const mw: any = <T extends (...args: any[]) => any>(handler: T): T => {\n return (async (...args: any[]) => {\n const key = instance.key(...args);\n const read = await instance.read(key);\n\n if (read.value !== undefined) {\n if (read.stale) {\n instance.scheduleRefresh(key, () => handler(...args));\n }\n return read.value;\n }\n\n const result = await instance.runSingleFlight(key, () =>\n handler(...args),\n );\n // Fire-and-forget — cache write failures must not break the handler result\n instance.set(key, result).catch(() => {});\n return result;\n }) as any as T;\n };\n\n mw[OPTIONS] = {\n name: \"$cache\",\n options: options as unknown as Record<string, unknown>,\n } satisfies MiddlewareMetadata;\n\n return Object.setPrototypeOf(mw, instance);\n}\n\n// ---------------------------------------------------------------------------------------------------------------------\n\n/**\n * Options for the in-memory L1 tier.\n */\nexport interface CacheMemoryTierOptions {\n /**\n * TTL for the in-memory tier. Should be ≤ the remote `ttl`.\n * Bounds the cross-isolate staleness window after invalidation.\n *\n * @default min(ttl, 30s)\n */\n ttl?: DurationLike;\n\n /**\n * LRU bound — max entries kept in memory before eviction.\n *\n * @default 500\n */\n max?: number;\n\n /**\n * Also cache provider misses (`undefined`) in memory for this duration.\n * Prevents hammering the remote tier on cold/unknown keys.\n *\n * @default off\n */\n negative?: DurationLike;\n}\n\nexport interface CachePrimitiveOptions<\n TReturn = any,\n TParameter extends any[] = any[],\n> {\n /**\n * The cache name. This is useful for invalidating multiple caches at once.\n *\n * Store key as `cache:$name:$key`.\n *\n * @default Name of the key of the class.\n */\n name?: string;\n\n /**\n * Function which returns cached data.\n */\n handler?: (...args: TParameter) => TReturn;\n\n /**\n * The key generator for the cache.\n * If not provided, the arguments will be json.stringify().\n */\n key?: (...args: TParameter) => string;\n\n /**\n * The store provider for the cache.\n *\n * Accepts:\n * - `\"memory\"` — short-circuits to {@link MemoryCacheProvider} regardless\n * of the default `CacheProvider` binding. Useful for caches that must\n * stay process-local (e.g. ETag, HTTP client).\n * - A {@link CacheProvider} class (concrete OR abstract) — resolved via\n * `alepha.inject(...)` at primitive construction. Use this to opt a\n * specific cache into a non-default backend (e.g.\n * `provider: DatabaseCacheProvider` to keep one cache in SQL while the\n * rest of the app uses Cloudflare KV).\n * - `undefined` — falls back to whatever is bound to `CacheProvider` in\n * the container. On Cloudflare workers this is\n * {@link CloudflareKVProvider} by default; on Node it's\n * {@link MemoryCacheProvider}.\n *\n * Note: passing an *abstract* class works because Alepha's DI resolves\n * through substitutions, e.g. `alepha.with({ provide: CacheProvider, use:\n * MyCacheProvider })`.\n */\n provider?: Service<CacheProvider> | \"memory\";\n\n /**\n * The time-to-live for the cache in seconds.\n * Set 0 to skip expiration.\n *\n * @default 300 (5 minutes).\n */\n ttl?: DurationLike;\n\n /**\n * If the cache is disabled.\n */\n disabled?: boolean;\n\n /**\n * Enable gzip compression for cached values.\n * Reduces storage size by 60-80% for JSON payloads at the cost of CPU.\n */\n compress?: boolean;\n\n /**\n * Add an in-process L1 memory tier in front of `provider`.\n *\n * Reads check memory first, fall back to the provider on miss. Writes go\n * to both tiers (write-through), so own-writes are immediately visible.\n *\n * Caveats:\n * - Per-process only. Each Worker isolate / Node process has its own L1.\n * `invalidate()` clears the local L1 + the remote provider; other\n * processes keep their L1 until its TTL expires.\n * - Use a short L1 TTL to bound the cross-isolate staleness window.\n *\n * @default off\n */\n memory?: true | CacheMemoryTierOptions;\n\n /**\n * Stale-while-revalidate window. After `ttl` expires, the cached value\n * remains servable for `stale` longer; reads in this window return the\n * stale value immediately and trigger ONE background refresh\n * (single-flight per key).\n *\n * Requires a `handler` (primitive mode) OR middleware mode wrapping a\n * handler — the cache needs to know how to recompute.\n *\n * @default off\n */\n stale?: DurationLike;\n}\n\n// ---------------------------------------------------------------------------------------------------------------------\n\n/**\n * Cache configuration atom.\n */\nexport const cacheOptions = $atom({\n name: \"alepha.cache.options\",\n schema: t.object({\n enabled: t.boolean({\n default: true,\n description: \"Whether caching is enabled.\",\n }),\n defaultTtl: t.number({\n default: 300,\n description: \"Default time to live for cache entries in seconds.\",\n }),\n }),\n default: {\n enabled: true,\n defaultTtl: 300,\n },\n});\n\nexport type CacheAtomOptions = Static<typeof cacheOptions.schema>;\n\ndeclare module \"alepha\" {\n interface State {\n [cacheOptions.key]: CacheAtomOptions;\n }\n}\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nconst DEFAULT_MEMORY_MAX = 500;\nconst DEFAULT_MEMORY_TTL_MS = 30_000;\nconst SWR_MARKER = \"__swr\" as const;\n\ntype SwrEnvelope = {\n [SWR_MARKER]: 1;\n v: unknown;\n f: number;\n};\n\ntype L1Entry<T> = {\n value: T | undefined;\n expiresAt: number;\n negative: boolean;\n};\n\ntype ReadResult<T> = {\n value: T | undefined;\n stale: boolean;\n};\n\nexport class CachePrimitive<\n TReturn = any,\n TParameter extends any[] = any[],\n> extends Primitive<CachePrimitiveOptions<TReturn, TParameter>> {\n protected readonly settings = $state(cacheOptions);\n protected readonly dateTimeProvider = $inject(DateTimeProvider);\n public readonly provider = this.$provider();\n\n protected readonly memoryStore?: Map<string, L1Entry<TReturn>>;\n protected readonly memoryMax: number = DEFAULT_MEMORY_MAX;\n protected readonly memoryTtlMs: number = 0;\n protected readonly negativeTtlMs: number = 0;\n\n protected readonly inflightRefreshes = new Map<string, Promise<TReturn>>();\n\n constructor(\n args: ConstructorParameters<\n typeof Primitive<CachePrimitiveOptions<TReturn, TParameter>>\n >[0],\n ) {\n super(args);\n const mem = this.options.memory;\n if (mem) {\n this.memoryStore = new Map();\n const memOpts: CacheMemoryTierOptions = mem === true ? {} : mem;\n this.memoryMax = memOpts.max ?? DEFAULT_MEMORY_MAX;\n // Default L1 TTL: min(remote ttl, 30s). If remote ttl is 0/infinite, use 30s.\n if (memOpts.ttl !== undefined) {\n this.memoryTtlMs = this.dateTimeProvider\n .duration(memOpts.ttl)\n .as(\"milliseconds\");\n } else {\n const remoteTtlMs = this.options.ttl\n ? this.dateTimeProvider.duration(this.options.ttl).as(\"milliseconds\")\n : 0;\n this.memoryTtlMs =\n remoteTtlMs > 0\n ? Math.min(remoteTtlMs, DEFAULT_MEMORY_TTL_MS)\n : DEFAULT_MEMORY_TTL_MS;\n }\n if (memOpts.negative !== undefined) {\n this.negativeTtlMs = this.dateTimeProvider\n .duration(memOpts.negative)\n .as(\"milliseconds\");\n }\n }\n }\n\n public get container(): string {\n return (\n this.options.name ??\n `${this.config.service.name}:${this.config.propertyKey}`\n );\n }\n\n public async run(...args: TParameter): Promise<TReturn> {\n const handler = this.options.handler;\n if (!handler) {\n throw new AlephaError(\"Cache handler is not defined.\");\n }\n\n const key = this.key(...args);\n const read = await this.read(key);\n\n if (read.value !== undefined) {\n if (read.stale) {\n this.scheduleRefresh(key, () => handler(...args));\n }\n return read.value;\n }\n\n const result = await this.runSingleFlight(key, () => handler(...args));\n // note: when exception occurs, don't cache the result\n await this.set(key, result);\n return result;\n }\n\n public key(...args: TParameter): string {\n return this.options.key ? this.options.key(...args) : JSON.stringify(args);\n }\n\n public async incr(key: string, amount = 1): Promise<number> {\n const result = await this.provider.incr(this.container, key, amount);\n // L1 is no longer authoritative after atomic incr on remote.\n this.delL1(key);\n return result;\n }\n\n public async invalidate(...keys: string[]): Promise<void> {\n if (this.memoryStore) {\n if (keys.length === 0) {\n this.memoryStore.clear();\n } else {\n for (const key of keys) {\n if (key.endsWith(\"*\")) {\n const prefix = key.slice(0, -1);\n for (const k of this.memoryStore.keys()) {\n if (k.startsWith(prefix)) this.memoryStore.delete(k);\n }\n } else {\n this.memoryStore.delete(key);\n }\n }\n }\n }\n await this.provider.invalidateKeys(this.container, keys);\n }\n\n public async set(\n key: string,\n value: TReturn,\n ttl?: DurationLike,\n ): Promise<void> {\n if (\n !this.alepha.isStarted() ||\n this.options.disabled ||\n !this.settings.enabled\n ) {\n return;\n }\n\n const freshTtlMs = this.dateTimeProvider\n .duration(\n ttl ?? this.options.ttl ?? [this.settings.defaultTtl, \"seconds\"],\n )\n .as(\"milliseconds\");\n\n const staleMs = this.options.stale\n ? this.dateTimeProvider.duration(this.options.stale).as(\"milliseconds\")\n : 0;\n\n const providerTtlMs = freshTtlMs > 0 ? freshTtlMs + staleMs : 0;\n const now = this.dateTimeProvider.nowMillis();\n const freshUntil = freshTtlMs > 0 ? now + freshTtlMs : 0;\n\n const payload =\n this.options.stale && freshTtlMs > 0\n ? ({ [SWR_MARKER]: 1, v: value, f: freshUntil } satisfies SwrEnvelope)\n : value;\n\n await this.provider.setTyped(this.container, key, payload, {\n ttl: providerTtlMs > 0 ? providerTtlMs : undefined,\n compress: this.options.compress,\n });\n\n // Write-through to L1 (raw value, not wrapped).\n this.setL1(key, {\n value,\n expiresAt:\n this.memoryTtlMs > 0\n ? now + this.memoryTtlMs\n : Number.POSITIVE_INFINITY,\n negative: false,\n });\n\n // A fresh write supersedes any pending refresh for this key.\n this.inflightRefreshes.delete(key);\n\n await this.alepha.events.emit(\"cache:set\", {\n container: this.container,\n key,\n ttlMs: providerTtlMs > 0 ? providerTtlMs : undefined,\n });\n }\n\n public async get(key: string): Promise<TReturn | undefined> {\n const read = await this.read(key);\n return read.value;\n }\n\n /**\n * Internal read that also reports whether the value is stale (SWR\n * grace window). Middleware and `run()` use this to decide whether to\n * schedule a background refresh.\n */\n public async read(key: string): Promise<ReadResult<TReturn>> {\n if (\n !this.alepha.isStarted() ||\n this.options.disabled ||\n !this.settings.enabled\n ) {\n return { value: undefined, stale: false };\n }\n\n const now = this.dateTimeProvider.nowMillis();\n\n // L1 check\n if (this.memoryStore) {\n const entry = this.memoryStore.get(key);\n if (entry !== undefined) {\n if (entry.expiresAt > now) {\n // LRU touch\n this.memoryStore.delete(key);\n this.memoryStore.set(key, entry);\n await this.alepha.events.emit(\"cache:hit\", {\n container: this.container,\n key,\n });\n return {\n value: entry.negative ? undefined : entry.value,\n stale: false,\n };\n }\n this.memoryStore.delete(key);\n }\n }\n\n // L2 check\n const raw = await this.provider.getTyped<TReturn | SwrEnvelope>(\n this.container,\n key,\n );\n\n if (raw === undefined) {\n // Negative caching\n if (this.memoryStore && this.negativeTtlMs > 0) {\n this.setL1(key, {\n value: undefined,\n expiresAt: now + this.negativeTtlMs,\n negative: true,\n });\n }\n await this.alepha.events.emit(\"cache:miss\", {\n container: this.container,\n key,\n });\n return { value: undefined, stale: false };\n }\n\n let value: TReturn;\n let stale = false;\n\n if (this.isSwrEnvelope(raw)) {\n value = raw.v as TReturn;\n stale = raw.f > 0 && raw.f <= now;\n } else {\n value = raw as TReturn;\n }\n\n // Populate L1 (write-back from L2 read)\n if (this.memoryStore && this.memoryTtlMs > 0) {\n this.setL1(key, {\n value,\n expiresAt: now + this.memoryTtlMs,\n negative: false,\n });\n }\n\n await this.alepha.events.emit(stale ? \"cache:stale\" : \"cache:hit\", {\n container: this.container,\n key,\n });\n\n return { value, stale };\n }\n\n /**\n * Run a handler under single-flight: concurrent callers for the same\n * key share one in-flight promise.\n */\n public async runSingleFlight(\n key: string,\n handler: () => Promise<TReturn> | TReturn,\n ): Promise<TReturn> {\n const existing = this.inflightRefreshes.get(key);\n if (existing) {\n return existing;\n }\n const promise = (async () => {\n try {\n return await handler();\n } finally {\n this.inflightRefreshes.delete(key);\n }\n })();\n this.inflightRefreshes.set(key, promise);\n return promise;\n }\n\n /**\n * Schedule a background refresh for a stale key. At most one refresh\n * per key is in-flight at any time; failures are swallowed (the stale\n * value keeps being served until expiry).\n */\n public scheduleRefresh(\n key: string,\n handler: () => Promise<TReturn> | TReturn,\n ): void {\n if (this.inflightRefreshes.has(key)) {\n return;\n }\n const promise = (async () => {\n try {\n const result = await handler();\n await this.set(key, result);\n await this.alepha.events.emit(\"cache:revalidate\", {\n container: this.container,\n key,\n });\n return result;\n } finally {\n this.inflightRefreshes.delete(key);\n }\n })();\n promise.catch(() => {\n // swallow: stale value keeps serving until expiry\n });\n this.inflightRefreshes.set(key, promise);\n }\n\n protected $provider(): CacheProvider {\n if (!this.options.provider) {\n return this.alepha.inject(CacheProvider);\n }\n\n if (this.options.provider === \"memory\") {\n return this.alepha.inject(MemoryCacheProvider);\n }\n\n return this.alepha.inject(this.options.provider);\n }\n\n protected isSwrEnvelope(value: unknown): value is SwrEnvelope {\n return (\n value !== null &&\n typeof value === \"object\" &&\n (value as Record<string, unknown>)[SWR_MARKER] === 1 &&\n \"f\" in (value as Record<string, unknown>) &&\n \"v\" in (value as Record<string, unknown>)\n );\n }\n\n protected setL1(key: string, entry: L1Entry<TReturn>): void {\n if (!this.memoryStore) return;\n if (this.memoryStore.has(key)) {\n this.memoryStore.delete(key);\n }\n this.memoryStore.set(key, entry);\n while (this.memoryStore.size > this.memoryMax) {\n const first = this.memoryStore.keys().next().value;\n if (first === undefined) break;\n this.memoryStore.delete(first);\n }\n }\n\n protected delL1(key: string): void {\n this.memoryStore?.delete(key);\n }\n}\n\nexport interface CachePrimitiveFn<\n TReturn = any,\n TParameter extends any[] = any[],\n> extends CachePrimitive<TReturn, TParameter> {\n /**\n * Run the cache primitive with the provided arguments.\n */\n (...args: TParameter): Promise<TReturn>;\n}\n\n/**\n * Cache middleware + store. Callable as middleware `(handler) => wrappedHandler`\n * AND exposes store methods (`.get()`, `.set()`, `.invalidate()`, `.incr()`).\n */\nexport interface CacheMiddlewareFn<TReturn = any>\n extends CachePrimitive<TReturn, any[]> {\n <T extends (...args: any[]) => any>(handler: T): T;\n [OPTIONS]?: MiddlewareMetadata;\n}\n\n$cache[KIND] = CachePrimitive;\n","import { $hook, $inject, Alepha, AlephaError } from \"alepha\";\nimport { $logger } from \"alepha/logger\";\nimport type { CachePrimitive } from \"../primitives/$cache.ts\";\nimport { CacheProvider } from \"./CacheProvider.ts\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\n/**\n * KVNamespace interface matching Cloudflare's KV API.\n */\nexport interface KVNamespace {\n get(key: string, type: \"arrayBuffer\"): Promise<ArrayBuffer | null>;\n get(key: string, type: \"text\"): Promise<string | null>;\n get(key: string, type?: \"text\"): Promise<string | null>;\n put(\n key: string,\n value: string | ArrayBuffer | ReadableStream,\n options?: KVPutOptions,\n ): Promise<void>;\n delete(key: string): Promise<void>;\n list(options?: KVListOptions): Promise<KVListResult>;\n}\n\nexport interface KVPutOptions {\n expiration?: number;\n expirationTtl?: number;\n metadata?: unknown;\n}\n\nexport interface KVListOptions {\n prefix?: string;\n limit?: number;\n cursor?: string;\n}\n\nexport interface KVListResult {\n keys: KVListKey[];\n list_complete: boolean;\n cursor?: string;\n}\n\nexport interface KVListKey {\n name: string;\n expiration?: number;\n metadata?: unknown;\n}\n\n// ---------------------------------------------------------------------------------------------------------------------\n\n/**\n * Default KV binding name used in wrangler configuration.\n */\nexport const KV_DEFAULT_BINDING = \"KV_CACHE\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\n/**\n * Cloudflare KV cache provider.\n *\n * Uses a KV namespace binding for all cache operations.\n * Keys are stored as: `cache:{name}:{key}`\n *\n * **Required Cloudflare binding:**\n * - `KV_CACHE` - A KV namespace binding in wrangler configuration\n *\n * @example\n * ```toml\n * # wrangler.toml - automatically generated by alepha build\n * [[kv_namespaces]]\n * binding = \"KV_CACHE\"\n * id = \"abc123\"\n * ```\n */\nexport class CloudflareKVProvider extends CacheProvider {\n protected readonly alepha = $inject(Alepha);\n protected readonly log = $logger();\n\n protected kv?: KVNamespace;\n\n protected readonly onStart = $hook({\n on: \"start\",\n handler: async () => {\n const caches = this.alepha\n .primitives<CachePrimitive>(\"cache\")\n .filter((it) => it.provider === this);\n\n if (caches.length === 0) {\n this.log.info(\n \"CloudflareKVProvider is registered but no cache primitives are using it. Skipping KV initialization.\",\n );\n return;\n }\n\n const cloudflareEnv = this.alepha.get(\"cloudflare.env\") as\n | Record<string, unknown>\n | undefined;\n if (!cloudflareEnv) {\n throw new AlephaError(\n \"Cloudflare Workers environment not found in Alepha store under 'cloudflare.env'.\",\n );\n }\n\n const binding = cloudflareEnv[KV_DEFAULT_BINDING] as\n | KVNamespace\n | undefined;\n if (!binding) {\n throw new AlephaError(\n `KV binding '${KV_DEFAULT_BINDING}' not found in Cloudflare Workers environment.`,\n );\n }\n\n this.kv = binding;\n\n this.log.info(\"Cloudflare KV cache OK\");\n },\n });\n\n public async get(name: string, key: string): Promise<Uint8Array | undefined> {\n if (!this.alepha.isStarted()) {\n return;\n }\n\n const kvKey = this.prefix(name, key);\n const buffer = await this.getKV().get(kvKey, \"arrayBuffer\");\n if (!buffer) {\n return;\n }\n\n this.log.debug(\"Cache hit\", {\n size: buffer.byteLength,\n key: kvKey,\n });\n\n return new Uint8Array(buffer);\n }\n\n public async set(\n name: string,\n key: string,\n value: Uint8Array,\n ttl?: number,\n ): Promise<Uint8Array> {\n if (!this.alepha.isReady()) {\n return new Uint8Array(value);\n }\n\n const kvKey = this.prefix(name, key);\n const options: KVPutOptions = {};\n\n if (ttl) {\n // KV expects TTL in seconds, we receive milliseconds\n options.expirationTtl = Math.max(60, Math.ceil(ttl / 1000));\n }\n\n await this.getKV().put(\n kvKey,\n value.buffer.slice(\n value.byteOffset,\n value.byteOffset + value.byteLength,\n ) as ArrayBuffer,\n options,\n );\n return value;\n }\n\n public async del(name: string, ...keys: string[]): Promise<void> {\n const kv = this.getKV();\n\n if (keys.length === 0) {\n // Delete all keys under this cache name\n const prefix = this.prefix(name);\n const allKeys = await this.listAllKeys(`${prefix}:`);\n for (const k of allKeys) {\n await kv.delete(k);\n }\n return;\n }\n\n const nameKey = this.prefix(name);\n for (const key of keys) {\n const fullKey = key.startsWith(nameKey) ? key : this.prefix(name, key);\n await kv.delete(fullKey);\n }\n }\n\n public async has(name: string, key: string): Promise<boolean> {\n const kvKey = this.prefix(name, key);\n const value = await this.getKV().get(kvKey, \"text\");\n return value !== null;\n }\n\n public async keys(name: string, filter?: string): Promise<string[]> {\n const prefix = filter\n ? `${this.prefix(name)}:${filter}`\n : `${this.prefix(name)}:`;\n\n return this.listAllKeys(prefix);\n }\n\n public async clear(): Promise<void> {\n this.log.debug(\"Clearing all cache\");\n const kv = this.getKV();\n const allKeys = await this.listAllKeys(\"cache:\");\n for (const key of allKeys) {\n await kv.delete(key);\n }\n }\n\n public async incr(\n name: string,\n key: string,\n amount: number,\n ): Promise<number> {\n const kvKey = this.prefix(name, key);\n const kv = this.getKV();\n\n const existing = await kv.get(kvKey, \"text\");\n let current = 0;\n\n if (existing !== null) {\n current = Number.parseInt(existing, 10) || 0;\n }\n\n const newValue = current + amount;\n await kv.put(kvKey, String(newValue));\n return newValue;\n }\n\n /**\n * Build the full KV key: `cache:{name}:{key}`\n */\n protected prefix(...path: string[]): string {\n return [\"cache\", ...path].join(\":\");\n }\n\n protected getKV(): KVNamespace {\n if (!this.kv) {\n throw new AlephaError(\n \"KV namespace not initialized. Call start() first.\",\n );\n }\n return this.kv;\n }\n\n /**\n * List all keys matching a prefix, handling pagination.\n */\n protected async listAllKeys(prefix: string): Promise<string[]> {\n const kv = this.getKV();\n const allKeys: string[] = [];\n let cursor: string | undefined;\n\n do {\n const result = await kv.list({\n prefix,\n cursor,\n });\n\n for (const key of result.keys) {\n allKeys.push(key.name);\n }\n\n cursor = result.list_complete ? undefined : result.cursor;\n } while (cursor);\n\n return allKeys;\n }\n}\n","import { $module } from \"alepha\";\nimport { $cache } from \"./primitives/$cache.ts\";\nimport { CacheProvider } from \"./providers/CacheProvider.ts\";\nimport { MemoryCacheProvider } from \"./providers/MemoryCacheProvider.ts\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport * from \"./primitives/$cache.ts\";\nexport * from \"./providers/CacheProvider.ts\";\nexport * from \"./providers/CloudflareKVProvider.ts\";\nexport * from \"./providers/MemoryCacheProvider.ts\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\ndeclare module \"alepha\" {\n interface Hooks {\n /**\n * Fires when a cache lookup finds a value.\n */\n \"cache:hit\": {\n container: string;\n key: string;\n };\n /**\n * Fires when a cache lookup does not find a value.\n */\n \"cache:miss\": {\n container: string;\n key: string;\n };\n /**\n * Fires when a value is written to the cache.\n */\n \"cache:set\": {\n container: string;\n key: string;\n ttlMs?: number;\n };\n /**\n * Fires when a stale value (SWR grace window) is served and a\n * background refresh is scheduled.\n */\n \"cache:stale\": {\n container: string;\n key: string;\n };\n /**\n * Fires when a background SWR refresh completes successfully and\n * the value has been written back to the cache.\n */\n \"cache:revalidate\": {\n container: string;\n key: string;\n };\n }\n}\n\n// ---------------------------------------------------------------------------------------------------------------------\n\n/**\n * Type-safe caching with TTL support.\n *\n * **Features:**\n * - Cached computations with type-safe keys and values\n * - Configurable TTL\n * - Cache invalidation\n * - Automatic cache population\n * - Providers: Memory (default), Redis\n *\n * @module alepha.cache\n */\nexport const AlephaCache = $module({\n name: \"alepha.cache\",\n primitives: [$cache],\n services: [CacheProvider],\n variants: [MemoryCacheProvider],\n register: (alepha) =>\n alepha.with({\n optional: true,\n provide: CacheProvider,\n use: MemoryCacheProvider,\n }),\n});\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../../../src/cache/core/errors/CacheError.ts","../../../src/cache/core/providers/CacheProvider.ts","../../../src/cache/core/providers/MemoryCacheProvider.ts","../../../src/cache/core/primitives/$cache.ts","../../../src/cache/core/providers/CloudflareKVProvider.ts","../../../src/cache/core/index.ts"],"sourcesContent":["import { AlephaError } from \"alepha\";\n\nexport class CacheError extends AlephaError {}\n","import { CacheError } from \"../errors/CacheError.ts\";\n\n/**\n * Cache provider interface.\n *\n * All methods are asynchronous and return promises.\n * Values are stored as Uint8Array.\n */\nexport abstract class CacheProvider {\n protected encoder: TextEncoder = new TextEncoder();\n protected decoder: TextDecoder = new TextDecoder();\n protected codes = {\n BINARY: 0x01,\n JSON: 0x02,\n STRING: 0x03,\n COMPRESSED: 0x04,\n };\n\n /**\n * Get the value of a key.\n *\n * @param name Cache name, used to group keys. Should be Redis-like \"some:group:name\" format.\n * @param key The key of the value to get.\n *\n * @return The value of the key, or undefined if the key does not exist.\n */\n public abstract get(\n name: string,\n key: string,\n ): Promise<Uint8Array | undefined>;\n\n /**\n * Set the string value of a key.\n *\n * @param name Cache name, used to group keys. Should be Redis-like \"some:group:name\" format.\n * @param key The key of the value to set.\n * @param value The value to set.\n * @param ttl The time-to-live of the key, in milliseconds.\n *\n * @return The value of the key.\n */\n public abstract set(\n name: string,\n key: string,\n value: Uint8Array,\n ttl?: number,\n ): Promise<Uint8Array>;\n\n /**\n * Remove the specified keys.\n *\n * @param name Cache name, used to group keys. Should be Redis-like \"some:group:name\" format.\n * @param keys The keys to delete.\n */\n public abstract del(name: string, ...keys: string[]): Promise<void>;\n\n public abstract has(name: string, key: string): Promise<boolean>;\n\n public abstract keys(name: string, filter?: string): Promise<string[]>;\n\n /**\n * Remove all keys from all cache names.\n */\n public abstract clear(): Promise<void>;\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 when using Redis.\n *\n * @param name Cache name, used to group keys.\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(\n name: string,\n key: string,\n amount: number,\n ): Promise<number>;\n\n // ---------------------------------------------------------------------------\n // High-level methods — serialize/compress layer used by CachePrimitive\n // ---------------------------------------------------------------------------\n\n /**\n * Set a typed value with automatic serialization and optional compression.\n */\n public async setTyped(\n name: string,\n key: string,\n value: unknown,\n options?: { ttl?: number; compress?: boolean },\n ): Promise<void> {\n let data = this.serialize(value);\n if (options?.compress) {\n data = await this.compress(data);\n }\n await this.set(name, key, data, options?.ttl);\n }\n\n /**\n * Get a typed value with automatic deserialization and optional decompression.\n */\n public async getTyped<T>(name: string, key: string): Promise<T | undefined> {\n const data = await this.get(name, key);\n if (data) {\n if (data[0] === this.codes.COMPRESSED) {\n const decompressed = await this.decompress(data.subarray(1));\n return this.deserialize<T>(decompressed);\n }\n return this.deserialize<T>(data);\n }\n return undefined;\n }\n\n /**\n * Invalidate cache keys with wildcard support.\n *\n * Keys ending in `*` are expanded via `this.keys()`.\n * Called with no keys, delegates to `this.del(name)` which clears the entire container.\n */\n public async invalidateKeys(name: string, keys: string[]): Promise<void> {\n const keysToDelete: string[] = [];\n\n for (const key of keys) {\n if (key.endsWith(\"*\")) {\n const result = await this.keys(name, key.slice(0, -1));\n keysToDelete.push(...result);\n } else {\n keysToDelete.push(key);\n }\n }\n\n await this.del(name, ...keysToDelete);\n }\n\n /**\n * Serialize a value to a typed Uint8Array with a leading type marker byte.\n */\n protected serialize(value: unknown): Uint8Array {\n if (value instanceof Uint8Array) {\n const result = new Uint8Array(1 + value.length);\n result[0] = this.codes.BINARY;\n result.set(value, 1);\n return result;\n }\n\n const encoded = this.encoder.encode(\n typeof value === \"string\" ? value : JSON.stringify(value),\n );\n const code =\n typeof value === \"string\" ? this.codes.STRING : this.codes.JSON;\n const result = new Uint8Array(1 + encoded.length);\n result[0] = code;\n result.set(encoded, 1);\n return result;\n }\n\n /**\n * Deserialize a typed Uint8Array back to the original value.\n */\n protected deserialize<T>(uint8Array: Uint8Array): T {\n const type = uint8Array[0];\n const payload = uint8Array.slice(1);\n\n if (type === this.codes.BINARY) {\n return payload as T;\n }\n if (type === this.codes.JSON) {\n return JSON.parse(this.decoder.decode(payload)) as T;\n }\n if (type === this.codes.STRING) {\n return this.decoder.decode(payload) as T;\n }\n\n throw new CacheError(`Unknown serialization type: ${type}`);\n }\n\n /**\n * Compress data with gzip, prepending a COMPRESSED marker byte.\n */\n protected async compress(data: Uint8Array): Promise<Uint8Array> {\n const buf = (data.buffer as ArrayBuffer).slice(\n data.byteOffset,\n data.byteOffset + data.byteLength,\n );\n const compressed = new Uint8Array(\n await new Response(\n new Blob([buf]).stream().pipeThrough(new CompressionStream(\"gzip\")),\n ).arrayBuffer(),\n );\n const result = new Uint8Array(1 + compressed.length);\n result[0] = this.codes.COMPRESSED;\n result.set(compressed, 1);\n return result;\n }\n\n /**\n * Decompress gzipped data.\n */\n protected async decompress(data: Uint8Array): Promise<Uint8Array> {\n const buf = (data.buffer as ArrayBuffer).slice(\n data.byteOffset,\n data.byteOffset + data.byteLength,\n );\n return new Uint8Array(\n await new Response(\n new Blob([buf]).stream().pipeThrough(new DecompressionStream(\"gzip\")),\n ).arrayBuffer(),\n );\n }\n}\n","import { $inject } from \"alepha\";\nimport { DateTimeProvider, type Timeout } from \"alepha/datetime\";\nimport { $logger } from \"alepha/logger\";\nimport { CacheProvider } from \"./CacheProvider.ts\";\n\ntype CacheName = string;\ntype CacheKey = string;\ntype CacheValue = {\n data?: Uint8Array;\n timeout?: Timeout;\n};\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport interface MemoryCacheCall {\n name: string;\n key: string;\n timestamp: number;\n}\n\nexport interface MemoryCacheSetCall extends MemoryCacheCall {\n value: Uint8Array;\n ttl?: number;\n}\n\nexport interface MemoryCacheDelCall {\n name: string;\n keys: string[];\n timestamp: number;\n}\n\nexport interface MemoryCacheStats {\n hits: number;\n misses: number;\n sets: number;\n deletes: number;\n}\n\nexport interface MemoryCacheProviderOptions {\n /**\n * Error to throw on get operations (for testing error handling)\n */\n getError?: Error | null;\n /**\n * Error to throw on set operations (for testing error handling)\n */\n setError?: Error | null;\n /**\n * Error to throw on del operations (for testing error handling)\n */\n delError?: Error | null;\n}\n\n// ---------------------------------------------------------------------------------------------------------------------\n\n/**\n * In-memory implementation of CacheProvider for testing.\n *\n * This provider stores all cache entries in memory, making it ideal for\n * unit tests that need to verify cache operations without touching Redis or other backends.\n *\n * @example\n * ```typescript\n * // In tests, substitute the real CacheProvider with MemoryCacheProvider\n * const alepha = Alepha.create().with({\n * provide: CacheProvider,\n * use: MemoryCacheProvider,\n * });\n *\n * // Run code that uses caching\n * const service = alepha.inject(MyService);\n * await service.fetchWithCache(\"key\");\n *\n * // Verify cache behavior\n * const cache = alepha.inject(MemoryCacheProvider);\n * expect(cache.stats().misses).toBe(1);\n * await service.fetchWithCache(\"key\");\n * expect(cache.stats().hits).toBe(1);\n * ```\n */\nexport class MemoryCacheProvider extends CacheProvider {\n protected readonly dateTimeProvider = $inject(DateTimeProvider);\n protected readonly log = $logger();\n\n protected store: Record<CacheName, Record<CacheKey, CacheValue>> = {};\n\n // ─────────────────────────────────────────────────────────────────────────────\n // Test tracking\n // ─────────────────────────────────────────────────────────────────────────────\n\n /**\n * All recorded get calls.\n */\n public getCalls: MemoryCacheCall[] = [];\n\n /**\n * All recorded set calls.\n */\n public setCalls: MemoryCacheSetCall[] = [];\n\n /**\n * All recorded del calls.\n */\n public delCalls: MemoryCacheDelCall[] = [];\n\n /**\n * Cache statistics.\n */\n protected _stats: MemoryCacheStats = {\n hits: 0,\n misses: 0,\n sets: 0,\n deletes: 0,\n };\n\n /**\n * Error to throw on get (for testing error handling)\n */\n public getError: Error | null = null;\n\n /**\n * Error to throw on set (for testing error handling)\n */\n public setError: Error | null = null;\n\n /**\n * Error to throw on del (for testing error handling)\n */\n public delError: Error | null = null;\n\n constructor(options: MemoryCacheProviderOptions = {}) {\n super();\n this.getError = options.getError ?? null;\n this.setError = options.setError ?? null;\n this.delError = options.delError ?? null;\n }\n\n // ─────────────────────────────────────────────────────────────────────────────\n // CacheProvider implementation\n // ─────────────────────────────────────────────────────────────────────────────\n\n public async get(name: string, key: string): Promise<Uint8Array | undefined> {\n this.getCalls.push({\n name,\n key,\n timestamp: this.dateTimeProvider.nowMillis(),\n });\n\n if (this.getError) {\n throw this.getError;\n }\n\n const data = this.store[name]?.[key]?.data;\n\n if (data !== undefined) {\n this._stats.hits++;\n } else {\n this._stats.misses++;\n }\n\n return data;\n }\n\n public async set(\n name: string,\n key: string,\n value: Uint8Array,\n ttl?: number,\n ): Promise<Uint8Array> {\n this.setCalls.push({\n name,\n key,\n value,\n ttl,\n timestamp: this.dateTimeProvider.nowMillis(),\n });\n this._stats.sets++;\n\n if (this.setError) {\n throw this.setError;\n }\n\n if (this.store[name] == null) {\n this.store[name] = {};\n }\n\n this.store[name][key] ??= {};\n this.store[name][key].data = value;\n\n this.log.debug(`Setting cache for name`, { name, key, ttl });\n\n // clear previous timeout if exists\n if (this.store[name][key].timeout) {\n this.dateTimeProvider.clearTimeout(this.store[name][key].timeout);\n this.store[name][key].timeout = undefined;\n }\n\n if (ttl) {\n this.store[name][key].timeout = this.dateTimeProvider.createTimeout(\n () => this.del(name, key),\n ttl,\n );\n }\n\n return this.store[name][key].data;\n }\n\n public async del(name: string, ...keys: string[]): Promise<void> {\n this.delCalls.push({\n name,\n keys,\n timestamp: this.dateTimeProvider.nowMillis(),\n });\n this._stats.deletes++;\n\n if (this.delError) {\n throw this.delError;\n }\n\n // delete all keys in name\n if (keys.length === 0) {\n this.log.debug(`Deleting all cache for name`, { name });\n\n if (this.store[name]) {\n for (const key of Object.keys(this.store[name])) {\n const timeout = this.store[name][key]?.timeout;\n if (timeout) {\n this.dateTimeProvider.clearTimeout(timeout);\n }\n }\n }\n delete this.store[name];\n return;\n }\n\n this.log.debug(`Deleting cache for name`, { name, keys });\n\n // delete specific keys in name\n for (const key of keys) {\n if (this.store[name] == null) break;\n\n const timeout = this.store[name][key]?.timeout;\n if (timeout) {\n this.dateTimeProvider.clearTimeout(timeout);\n }\n\n delete this.store[name][key];\n }\n\n if (Object.keys(this.store[name] ?? {}).length === 0) {\n // if name is empty, delete it\n delete this.store[name];\n }\n }\n\n public async has(name: string, key: string): Promise<boolean> {\n return this.store[name]?.[key]?.data != null;\n }\n\n public async keys(name: string, filter?: string): Promise<string[]> {\n const store = this.store[name] ?? {};\n const keys = Object.keys(store);\n if (filter) {\n return keys.filter((key) => key.startsWith(filter));\n }\n return keys;\n }\n\n public async clear(): Promise<void> {\n this.log.debug(\"Clearing all cache\");\n\n // Clear all timeouts before clearing the store\n for (const name of Object.keys(this.store)) {\n for (const key of Object.keys(this.store[name])) {\n const timeout = this.store[name][key]?.timeout;\n if (timeout) {\n this.dateTimeProvider.clearTimeout(timeout);\n }\n }\n }\n\n this.store = {};\n }\n\n public async incr(\n name: string,\n key: string,\n amount: number,\n ): Promise<number> {\n if (this.store[name] == null) {\n this.store[name] = {};\n }\n\n const existing = this.store[name][key]?.data;\n let current = 0;\n\n if (existing) {\n try {\n current = this.deserialize<number>(existing);\n } catch {\n // Fallback for raw bytes without type marker\n const str = new TextDecoder().decode(existing);\n current = Number.parseInt(str, 10) || 0;\n }\n }\n\n const newValue = current + amount;\n this.store[name][key] ??= {};\n this.store[name][key].data = this.serialize(newValue);\n\n return newValue;\n }\n\n // ─────────────────────────────────────────────────────────────────────────────\n // Test utilities\n // ─────────────────────────────────────────────────────────────────────────────\n\n /**\n * Get cache statistics (hits, misses, sets, deletes).\n *\n * @example\n * ```typescript\n * expect(cache.stats().hits).toBe(1);\n * expect(cache.stats().misses).toBe(0);\n * ```\n */\n public stats(): MemoryCacheStats {\n return { ...this._stats };\n }\n\n /**\n * Check if a key was set during the test.\n *\n * @example\n * ```typescript\n * expect(cache.wasSet(\"my-cache\", \"user:123\")).toBe(true);\n * ```\n */\n public wasSet(name: string, key?: string): boolean {\n if (key === undefined) {\n return this.setCalls.some((call) => call.name === name);\n }\n return this.setCalls.some((call) => call.name === name && call.key === key);\n }\n\n /**\n * Check if a key was retrieved during the test.\n *\n * @example\n * ```typescript\n * expect(cache.wasGet(\"my-cache\", \"user:123\")).toBe(true);\n * ```\n */\n public wasGet(name: string, key?: string): boolean {\n if (key === undefined) {\n return this.getCalls.some((call) => call.name === name);\n }\n return this.getCalls.some((call) => call.name === name && call.key === key);\n }\n\n /**\n * Check if a key was deleted during the test.\n *\n * @example\n * ```typescript\n * expect(cache.wasDeleted(\"my-cache\", \"user:123\")).toBe(true);\n * ```\n */\n public wasDeleted(name: string, key?: string): boolean {\n if (key === undefined) {\n return this.delCalls.some((call) => call.name === name);\n }\n return this.delCalls.some(\n (call) => call.name === name && call.keys.includes(key),\n );\n }\n\n /**\n * Get the number of cached entries for a specific cache name.\n *\n * @example\n * ```typescript\n * expect(cache.size(\"my-cache\")).toBe(5);\n * ```\n */\n public size(name?: string): number {\n if (name === undefined) {\n return Object.values(this.store).reduce(\n (total, entries) => total + Object.keys(entries).length,\n 0,\n );\n }\n return Object.keys(this.store[name] ?? {}).length;\n }\n\n /**\n * Get all cache names.\n *\n * @example\n * ```typescript\n * expect(cache.names()).toContain(\"my-cache\");\n * ```\n */\n public names(): string[] {\n return Object.keys(this.store);\n }\n\n /**\n * Reset all in-memory state (useful between tests).\n *\n * @example\n * ```typescript\n * beforeEach(() => {\n * cache.reset();\n * });\n * ```\n */\n public reset(): void {\n // Clear all timeouts\n for (const name of Object.keys(this.store)) {\n for (const key of Object.keys(this.store[name])) {\n const timeout = this.store[name][key]?.timeout;\n if (timeout) {\n this.dateTimeProvider.clearTimeout(timeout);\n }\n }\n }\n\n this.store = {};\n this.getCalls = [];\n this.setCalls = [];\n this.delCalls = [];\n this._stats = { hits: 0, misses: 0, sets: 0, deletes: 0 };\n this.getError = null;\n this.setError = null;\n this.delError = null;\n }\n}\n","import {\n $atom,\n $inject,\n $state,\n AlephaError,\n createPrimitive,\n KIND,\n type MiddlewareMetadata,\n OPTIONS,\n Primitive,\n type Service,\n type Static,\n t,\n} from \"alepha\";\nimport { DateTimeProvider, type DurationLike } from \"alepha/datetime\";\nimport { CacheProvider } from \"../providers/CacheProvider.ts\";\nimport { MemoryCacheProvider } from \"../providers/MemoryCacheProvider.ts\";\n\n/**\n * Creates a cache primitive for caching with automatic management.\n *\n * **Middleware mode** (no `handler`) — usable in `use` arrays AND as a store:\n * ```ts\n * class UserService {\n * userCache = $cache({ name: \"users\", ttl: [10, \"minutes\"] });\n *\n * fetchUser = $pipeline({\n * use: [this.userCache],\n * handler: async (userId: string) => this.repo.getById(userId),\n * });\n *\n * async invalidateUser(userId: string) {\n * await this.userCache.invalidate(userId);\n * }\n * }\n * ```\n *\n * **Primitive mode** (with `handler`) — standalone callable:\n * ```ts\n * getUserData = $cache({\n * name: \"user-data\",\n * ttl: [10, \"minutes\"],\n * handler: async (userId: string) => {\n * return await database.users.findById(userId);\n * }\n * });\n * ```\n */\nexport function $cache<TReturn = string, TParameter extends any[] = any[]>(\n options: CachePrimitiveOptions<TReturn, TParameter> & {\n handler: (...args: TParameter) => TReturn;\n },\n): CachePrimitiveFn<TReturn, TParameter>;\nexport function $cache<TReturn = any, TParameter extends any[] = any[]>(\n options?: CachePrimitiveOptions<TReturn, TParameter>,\n): CacheMiddlewareFn<TReturn>;\nexport function $cache(options: any = {}): any {\n const instance = createPrimitive(CachePrimitive, options);\n\n if (options.handler) {\n const fn = (...args: any[]): Promise<any> => instance.run(...args);\n return Object.setPrototypeOf(fn, instance);\n }\n\n // Middleware mode: callable as (handler) => wrappedHandler, with store methods\n const mw: any = <T extends (...args: any[]) => any>(handler: T): T => {\n return (async (...args: any[]) => {\n const key = instance.key(...args);\n const read = await instance.read(key);\n\n if (read.value !== undefined) {\n if (read.stale) {\n instance.scheduleRefresh(key, () => handler(...args));\n }\n return read.value;\n }\n\n const result = await instance.runSingleFlight(key, () =>\n handler(...args),\n );\n // Fire-and-forget — cache write failures must not break the handler result\n instance.set(key, result).catch(() => {});\n return result;\n }) as any as T;\n };\n\n mw[OPTIONS] = {\n name: \"$cache\",\n options: options as unknown as Record<string, unknown>,\n } satisfies MiddlewareMetadata;\n\n return Object.setPrototypeOf(mw, instance);\n}\n\n// ---------------------------------------------------------------------------------------------------------------------\n\n/**\n * Options for the in-memory L1 tier.\n */\nexport interface CacheMemoryTierOptions {\n /**\n * TTL for the in-memory tier. Should be ≤ the remote `ttl`.\n * Bounds the cross-isolate staleness window after invalidation.\n *\n * @default min(ttl, 30s)\n */\n ttl?: DurationLike;\n\n /**\n * LRU bound — max entries kept in memory before eviction.\n *\n * @default 500\n */\n max?: number;\n\n /**\n * Also cache provider misses (`undefined`) in memory for this duration.\n * Prevents hammering the remote tier on cold/unknown keys.\n *\n * @default off\n */\n negative?: DurationLike;\n}\n\nexport interface CachePrimitiveOptions<\n TReturn = any,\n TParameter extends any[] = any[],\n> {\n /**\n * The cache name. This is useful for invalidating multiple caches at once.\n *\n * Store key as `cache:$name:$key`.\n *\n * @default Name of the key of the class.\n */\n name?: string;\n\n /**\n * Function which returns cached data.\n */\n handler?: (...args: TParameter) => TReturn;\n\n /**\n * The key generator for the cache.\n * If not provided, the arguments will be json.stringify().\n */\n key?: (...args: TParameter) => string;\n\n /**\n * The store provider for the cache.\n *\n * Accepts:\n * - `\"memory\"` — short-circuits to {@link MemoryCacheProvider} regardless\n * of the default `CacheProvider` binding. Useful for caches that must\n * stay process-local (e.g. ETag, HTTP client).\n * - A {@link CacheProvider} class (concrete OR abstract) — resolved via\n * `alepha.inject(...)` at primitive construction. Use this to opt a\n * specific cache into a non-default backend (e.g.\n * `provider: DatabaseCacheProvider` to keep one cache in SQL while the\n * rest of the app uses Cloudflare KV).\n * - `undefined` — falls back to whatever is bound to `CacheProvider` in\n * the container. On Cloudflare workers this is\n * {@link CloudflareKVProvider} by default; on Node it's\n * {@link MemoryCacheProvider}.\n *\n * Note: passing an *abstract* class works because Alepha's DI resolves\n * through substitutions, e.g. `alepha.with({ provide: CacheProvider, use:\n * MyCacheProvider })`.\n */\n provider?: Service<CacheProvider> | \"memory\";\n\n /**\n * The time-to-live for the cache in seconds.\n * Set 0 to skip expiration.\n *\n * @default 300 (5 minutes).\n */\n ttl?: DurationLike;\n\n /**\n * If the cache is disabled.\n */\n disabled?: boolean;\n\n /**\n * Enable gzip compression for cached values.\n * Reduces storage size by 60-80% for JSON payloads at the cost of CPU.\n */\n compress?: boolean;\n\n /**\n * Add an in-process L1 memory tier in front of `provider`.\n *\n * Reads check memory first, fall back to the provider on miss. Writes go\n * to both tiers (write-through), so own-writes are immediately visible.\n *\n * Caveats:\n * - Per-process only. Each Worker isolate / Node process has its own L1.\n * `invalidate()` clears the local L1 + the remote provider; other\n * processes keep their L1 until its TTL expires.\n * - Use a short L1 TTL to bound the cross-isolate staleness window.\n *\n * @default off\n */\n memory?: true | CacheMemoryTierOptions;\n\n /**\n * Stale-while-revalidate window. After `ttl` expires, the cached value\n * remains servable for `stale` longer; reads in this window return the\n * stale value immediately and trigger ONE background refresh\n * (single-flight per key).\n *\n * Requires a `handler` (primitive mode) OR middleware mode wrapping a\n * handler — the cache needs to know how to recompute.\n *\n * @default off\n */\n stale?: DurationLike;\n}\n\n// ---------------------------------------------------------------------------------------------------------------------\n\n/**\n * Cache configuration atom.\n */\nexport const cacheOptions = $atom({\n name: \"alepha.cache.options\",\n schema: t.object({\n enabled: t.boolean({\n default: true,\n description: \"Whether caching is enabled.\",\n }),\n defaultTtl: t.number({\n default: 300,\n description: \"Default time to live for cache entries in seconds.\",\n }),\n }),\n default: {\n enabled: true,\n defaultTtl: 300,\n },\n});\n\nexport type CacheAtomOptions = Static<typeof cacheOptions.schema>;\n\ndeclare module \"alepha\" {\n interface State {\n [cacheOptions.key]: CacheAtomOptions;\n }\n}\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nconst DEFAULT_MEMORY_MAX = 500;\nconst DEFAULT_MEMORY_TTL_MS = 30_000;\nconst SWR_MARKER = \"__swr\" as const;\n\ntype SwrEnvelope = {\n [SWR_MARKER]: 1;\n v: unknown;\n f: number;\n};\n\ntype L1Entry<T> = {\n value: T | undefined;\n expiresAt: number;\n negative: boolean;\n};\n\ntype ReadResult<T> = {\n value: T | undefined;\n stale: boolean;\n};\n\nexport class CachePrimitive<\n TReturn = any,\n TParameter extends any[] = any[],\n> extends Primitive<CachePrimitiveOptions<TReturn, TParameter>> {\n protected readonly settings = $state(cacheOptions);\n protected readonly dateTimeProvider = $inject(DateTimeProvider);\n public readonly provider = this.$provider();\n\n protected readonly memoryStore?: Map<string, L1Entry<TReturn>>;\n protected readonly memoryMax: number = DEFAULT_MEMORY_MAX;\n protected readonly memoryTtlMs: number = 0;\n protected readonly negativeTtlMs: number = 0;\n\n protected readonly inflightRefreshes = new Map<string, Promise<TReturn>>();\n\n constructor(\n args: ConstructorParameters<\n typeof Primitive<CachePrimitiveOptions<TReturn, TParameter>>\n >[0],\n ) {\n super(args);\n const mem = this.options.memory;\n if (mem) {\n this.memoryStore = new Map();\n const memOpts: CacheMemoryTierOptions = mem === true ? {} : mem;\n this.memoryMax = memOpts.max ?? DEFAULT_MEMORY_MAX;\n // Default L1 TTL: min(remote ttl, 30s). If remote ttl is 0/infinite, use 30s.\n if (memOpts.ttl !== undefined) {\n this.memoryTtlMs = this.dateTimeProvider\n .duration(memOpts.ttl)\n .as(\"milliseconds\");\n } else {\n const remoteTtlMs = this.options.ttl\n ? this.dateTimeProvider.duration(this.options.ttl).as(\"milliseconds\")\n : 0;\n this.memoryTtlMs =\n remoteTtlMs > 0\n ? Math.min(remoteTtlMs, DEFAULT_MEMORY_TTL_MS)\n : DEFAULT_MEMORY_TTL_MS;\n }\n if (memOpts.negative !== undefined) {\n this.negativeTtlMs = this.dateTimeProvider\n .duration(memOpts.negative)\n .as(\"milliseconds\");\n }\n }\n }\n\n public get container(): string {\n return (\n this.options.name ??\n `${this.config.service.name}:${this.config.propertyKey}`\n );\n }\n\n public async run(...args: TParameter): Promise<TReturn> {\n const handler = this.options.handler;\n if (!handler) {\n throw new AlephaError(\"Cache handler is not defined.\");\n }\n\n const key = this.key(...args);\n const read = await this.read(key);\n\n if (read.value !== undefined) {\n if (read.stale) {\n this.scheduleRefresh(key, () => handler(...args));\n }\n return read.value;\n }\n\n const result = await this.runSingleFlight(key, () => handler(...args));\n // note: when exception occurs, don't cache the result\n await this.set(key, result);\n return result;\n }\n\n public key(...args: TParameter): string {\n return this.options.key ? this.options.key(...args) : JSON.stringify(args);\n }\n\n public async incr(key: string, amount = 1): Promise<number> {\n const result = await this.provider.incr(this.container, key, amount);\n // L1 is no longer authoritative after atomic incr on remote.\n this.delL1(key);\n return result;\n }\n\n public async invalidate(...keys: string[]): Promise<void> {\n if (this.memoryStore) {\n if (keys.length === 0) {\n this.memoryStore.clear();\n } else {\n for (const key of keys) {\n if (key.endsWith(\"*\")) {\n const prefix = key.slice(0, -1);\n for (const k of this.memoryStore.keys()) {\n if (k.startsWith(prefix)) this.memoryStore.delete(k);\n }\n } else {\n this.memoryStore.delete(key);\n }\n }\n }\n }\n await this.provider.invalidateKeys(this.container, keys);\n }\n\n public async set(\n key: string,\n value: TReturn,\n ttl?: DurationLike,\n ): Promise<void> {\n if (\n !this.alepha.isStarted() ||\n this.options.disabled ||\n !this.settings.enabled\n ) {\n return;\n }\n\n const freshTtlMs = this.dateTimeProvider\n .duration(\n ttl ?? this.options.ttl ?? [this.settings.defaultTtl, \"seconds\"],\n )\n .as(\"milliseconds\");\n\n const staleMs = this.options.stale\n ? this.dateTimeProvider.duration(this.options.stale).as(\"milliseconds\")\n : 0;\n\n const providerTtlMs = freshTtlMs > 0 ? freshTtlMs + staleMs : 0;\n const now = this.dateTimeProvider.nowMillis();\n const freshUntil = freshTtlMs > 0 ? now + freshTtlMs : 0;\n\n const payload =\n this.options.stale && freshTtlMs > 0\n ? ({ [SWR_MARKER]: 1, v: value, f: freshUntil } satisfies SwrEnvelope)\n : value;\n\n await this.provider.setTyped(this.container, key, payload, {\n ttl: providerTtlMs > 0 ? providerTtlMs : undefined,\n compress: this.options.compress,\n });\n\n // Write-through to L1 (raw value, not wrapped).\n this.setL1(key, {\n value,\n expiresAt:\n this.memoryTtlMs > 0\n ? now + this.memoryTtlMs\n : Number.POSITIVE_INFINITY,\n negative: false,\n });\n\n // A fresh write supersedes any pending refresh for this key.\n this.inflightRefreshes.delete(key);\n\n await this.alepha.events.emit(\"cache:set\", {\n container: this.container,\n key,\n ttlMs: providerTtlMs > 0 ? providerTtlMs : undefined,\n });\n }\n\n public async get(key: string): Promise<TReturn | undefined> {\n const read = await this.read(key);\n return read.value;\n }\n\n /**\n * Internal read that also reports whether the value is stale (SWR\n * grace window). Middleware and `run()` use this to decide whether to\n * schedule a background refresh.\n */\n public async read(key: string): Promise<ReadResult<TReturn>> {\n if (\n !this.alepha.isStarted() ||\n this.options.disabled ||\n !this.settings.enabled\n ) {\n return { value: undefined, stale: false };\n }\n\n const now = this.dateTimeProvider.nowMillis();\n\n // L1 check\n if (this.memoryStore) {\n const entry = this.memoryStore.get(key);\n if (entry !== undefined) {\n if (entry.expiresAt > now) {\n // LRU touch\n this.memoryStore.delete(key);\n this.memoryStore.set(key, entry);\n await this.alepha.events.emit(\"cache:hit\", {\n container: this.container,\n key,\n });\n return {\n value: entry.negative ? undefined : entry.value,\n stale: false,\n };\n }\n this.memoryStore.delete(key);\n }\n }\n\n // L2 check\n const raw = await this.provider.getTyped<TReturn | SwrEnvelope>(\n this.container,\n key,\n );\n\n if (raw === undefined) {\n // Negative caching\n if (this.memoryStore && this.negativeTtlMs > 0) {\n this.setL1(key, {\n value: undefined,\n expiresAt: now + this.negativeTtlMs,\n negative: true,\n });\n }\n await this.alepha.events.emit(\"cache:miss\", {\n container: this.container,\n key,\n });\n return { value: undefined, stale: false };\n }\n\n let value: TReturn;\n let stale = false;\n\n if (this.isSwrEnvelope(raw)) {\n value = raw.v as TReturn;\n stale = raw.f > 0 && raw.f <= now;\n } else {\n value = raw as TReturn;\n }\n\n // Populate L1 (write-back from L2 read)\n if (this.memoryStore && this.memoryTtlMs > 0) {\n this.setL1(key, {\n value,\n expiresAt: now + this.memoryTtlMs,\n negative: false,\n });\n }\n\n await this.alepha.events.emit(stale ? \"cache:stale\" : \"cache:hit\", {\n container: this.container,\n key,\n });\n\n return { value, stale };\n }\n\n /**\n * Run a handler under single-flight: concurrent callers for the same\n * key share one in-flight promise.\n */\n public async runSingleFlight(\n key: string,\n handler: () => Promise<TReturn> | TReturn,\n ): Promise<TReturn> {\n const existing = this.inflightRefreshes.get(key);\n if (existing) {\n return existing;\n }\n const promise = (async () => {\n try {\n return await handler();\n } finally {\n this.inflightRefreshes.delete(key);\n }\n })();\n this.inflightRefreshes.set(key, promise);\n return promise;\n }\n\n /**\n * Schedule a background refresh for a stale key. At most one refresh\n * per key is in-flight at any time; failures are swallowed (the stale\n * value keeps being served until expiry).\n */\n public scheduleRefresh(\n key: string,\n handler: () => Promise<TReturn> | TReturn,\n ): void {\n if (this.inflightRefreshes.has(key)) {\n return;\n }\n const promise = (async () => {\n try {\n const result = await handler();\n await this.set(key, result);\n await this.alepha.events.emit(\"cache:revalidate\", {\n container: this.container,\n key,\n });\n return result;\n } finally {\n this.inflightRefreshes.delete(key);\n }\n })();\n promise.catch(() => {\n // swallow: stale value keeps serving until expiry\n });\n this.inflightRefreshes.set(key, promise);\n }\n\n protected $provider(): CacheProvider {\n if (!this.options.provider) {\n return this.alepha.inject(CacheProvider);\n }\n\n if (this.options.provider === \"memory\") {\n return this.alepha.inject(MemoryCacheProvider);\n }\n\n return this.alepha.inject(this.options.provider);\n }\n\n protected isSwrEnvelope(value: unknown): value is SwrEnvelope {\n return (\n value !== null &&\n typeof value === \"object\" &&\n (value as Record<string, unknown>)[SWR_MARKER] === 1 &&\n \"f\" in (value as Record<string, unknown>) &&\n \"v\" in (value as Record<string, unknown>)\n );\n }\n\n protected setL1(key: string, entry: L1Entry<TReturn>): void {\n if (!this.memoryStore) return;\n if (this.memoryStore.has(key)) {\n this.memoryStore.delete(key);\n }\n this.memoryStore.set(key, entry);\n while (this.memoryStore.size > this.memoryMax) {\n const first = this.memoryStore.keys().next().value;\n if (first === undefined) break;\n this.memoryStore.delete(first);\n }\n }\n\n protected delL1(key: string): void {\n this.memoryStore?.delete(key);\n }\n}\n\nexport interface CachePrimitiveFn<\n TReturn = any,\n TParameter extends any[] = any[],\n> extends CachePrimitive<TReturn, TParameter> {\n /**\n * Run the cache primitive with the provided arguments.\n */\n (...args: TParameter): Promise<TReturn>;\n}\n\n/**\n * Cache middleware + store. Callable as middleware `(handler) => wrappedHandler`\n * AND exposes store methods (`.get()`, `.set()`, `.invalidate()`, `.incr()`).\n */\nexport interface CacheMiddlewareFn<TReturn = any>\n extends CachePrimitive<TReturn, any[]> {\n <T extends (...args: any[]) => any>(handler: T): T;\n [OPTIONS]?: MiddlewareMetadata;\n}\n\n$cache[KIND] = CachePrimitive;\n","import { $hook, $inject, Alepha, AlephaError } from \"alepha\";\nimport { $logger } from \"alepha/logger\";\nimport type { CachePrimitive } from \"../primitives/$cache.ts\";\nimport { CacheProvider } from \"./CacheProvider.ts\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\n/**\n * KVNamespace interface matching Cloudflare's KV API.\n */\nexport interface KVNamespace {\n get(key: string, type: \"arrayBuffer\"): Promise<ArrayBuffer | null>;\n get(key: string, type: \"text\"): Promise<string | null>;\n get(key: string, type?: \"text\"): Promise<string | null>;\n put(\n key: string,\n value: string | ArrayBuffer | ReadableStream,\n options?: KVPutOptions,\n ): Promise<void>;\n delete(key: string): Promise<void>;\n list(options?: KVListOptions): Promise<KVListResult>;\n}\n\nexport interface KVPutOptions {\n expiration?: number;\n expirationTtl?: number;\n metadata?: unknown;\n}\n\nexport interface KVListOptions {\n prefix?: string;\n limit?: number;\n cursor?: string;\n}\n\nexport interface KVListResult {\n keys: KVListKey[];\n list_complete: boolean;\n cursor?: string;\n}\n\nexport interface KVListKey {\n name: string;\n expiration?: number;\n metadata?: unknown;\n}\n\n// ---------------------------------------------------------------------------------------------------------------------\n\n/**\n * Default KV binding name used in wrangler configuration.\n */\nexport const KV_DEFAULT_BINDING = \"KV_CACHE\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\n/**\n * Cloudflare KV cache provider.\n *\n * Uses a KV namespace binding for all cache operations.\n * Keys are stored as: `cache:{name}:{key}`\n *\n * **Required Cloudflare binding:**\n * - `KV_CACHE` - A KV namespace binding in wrangler configuration\n *\n * @example\n * ```toml\n * # wrangler.toml - automatically generated by alepha build\n * [[kv_namespaces]]\n * binding = \"KV_CACHE\"\n * id = \"abc123\"\n * ```\n */\nexport class CloudflareKVProvider extends CacheProvider {\n protected readonly alepha = $inject(Alepha);\n protected readonly log = $logger();\n\n protected kv?: KVNamespace;\n\n protected readonly onStart = $hook({\n on: \"start\",\n handler: async () => {\n const caches = this.alepha\n .primitives<CachePrimitive>(\"cache\")\n .filter((it) => it.provider === this);\n\n if (caches.length === 0) {\n this.log.info(\n \"CloudflareKVProvider is registered but no cache primitives are using it. Skipping KV initialization.\",\n );\n return;\n }\n\n const cloudflareEnv = this.alepha.get(\"cloudflare.env\") as\n | Record<string, unknown>\n | undefined;\n if (!cloudflareEnv) {\n throw new AlephaError(\n \"Cloudflare Workers environment not found in Alepha store under 'cloudflare.env'.\",\n );\n }\n\n const binding = cloudflareEnv[KV_DEFAULT_BINDING] as\n | KVNamespace\n | undefined;\n if (!binding) {\n throw new AlephaError(\n `KV binding '${KV_DEFAULT_BINDING}' not found in Cloudflare Workers environment.`,\n );\n }\n\n this.kv = binding;\n\n this.log.info(\"Cloudflare KV cache OK\");\n },\n });\n\n public async get(name: string, key: string): Promise<Uint8Array | undefined> {\n if (!this.alepha.isStarted()) {\n return;\n }\n\n const kvKey = this.prefix(name, key);\n const buffer = await this.getKV().get(kvKey, \"arrayBuffer\");\n if (!buffer) {\n return;\n }\n\n this.log.debug(\"Cache hit\", {\n size: buffer.byteLength,\n key: kvKey,\n });\n\n return new Uint8Array(buffer);\n }\n\n public async set(\n name: string,\n key: string,\n value: Uint8Array,\n ttl?: number,\n ): Promise<Uint8Array> {\n if (!this.alepha.isReady()) {\n return new Uint8Array(value);\n }\n\n const kvKey = this.prefix(name, key);\n const options: KVPutOptions = {};\n\n if (ttl) {\n // KV expects TTL in seconds, we receive milliseconds\n options.expirationTtl = Math.max(60, Math.ceil(ttl / 1000));\n }\n\n await this.getKV().put(\n kvKey,\n value.buffer.slice(\n value.byteOffset,\n value.byteOffset + value.byteLength,\n ) as ArrayBuffer,\n options,\n );\n return value;\n }\n\n public async del(name: string, ...keys: string[]): Promise<void> {\n const kv = this.getKV();\n\n if (keys.length === 0) {\n // Delete all keys under this cache name\n const prefix = this.prefix(name);\n const allKeys = await this.listAllKeys(`${prefix}:`);\n for (const k of allKeys) {\n await kv.delete(k);\n }\n return;\n }\n\n const nameKey = this.prefix(name);\n for (const key of keys) {\n const fullKey = key.startsWith(nameKey) ? key : this.prefix(name, key);\n await kv.delete(fullKey);\n }\n }\n\n public async has(name: string, key: string): Promise<boolean> {\n const kvKey = this.prefix(name, key);\n const value = await this.getKV().get(kvKey, \"text\");\n return value !== null;\n }\n\n public async keys(name: string, filter?: string): Promise<string[]> {\n const prefix = filter\n ? `${this.prefix(name)}:${filter}`\n : `${this.prefix(name)}:`;\n\n return this.listAllKeys(prefix);\n }\n\n public async clear(): Promise<void> {\n this.log.debug(\"Clearing all cache\");\n const kv = this.getKV();\n const allKeys = await this.listAllKeys(\"cache:\");\n for (const key of allKeys) {\n await kv.delete(key);\n }\n }\n\n public async incr(\n name: string,\n key: string,\n amount: number,\n ): Promise<number> {\n const kvKey = this.prefix(name, key);\n const kv = this.getKV();\n\n const existing = await kv.get(kvKey, \"text\");\n let current = 0;\n\n if (existing !== null) {\n current = Number.parseInt(existing, 10) || 0;\n }\n\n const newValue = current + amount;\n await kv.put(kvKey, String(newValue));\n return newValue;\n }\n\n /**\n * Build the full KV key: `cache:{name}:{key}`\n */\n protected prefix(...path: string[]): string {\n return [\"cache\", ...path].join(\":\");\n }\n\n protected getKV(): KVNamespace {\n if (!this.kv) {\n throw new AlephaError(\n \"KV namespace not initialized. Call start() first.\",\n );\n }\n return this.kv;\n }\n\n /**\n * List all keys matching a prefix, handling pagination.\n */\n protected async listAllKeys(prefix: string): Promise<string[]> {\n const kv = this.getKV();\n const allKeys: string[] = [];\n let cursor: string | undefined;\n\n do {\n const result = await kv.list({\n prefix,\n cursor,\n });\n\n for (const key of result.keys) {\n allKeys.push(key.name);\n }\n\n cursor = result.list_complete ? undefined : result.cursor;\n } while (cursor);\n\n return allKeys;\n }\n}\n","import { $module } from \"alepha\";\nimport { $cache } from \"./primitives/$cache.ts\";\nimport { CacheProvider } from \"./providers/CacheProvider.ts\";\nimport { MemoryCacheProvider } from \"./providers/MemoryCacheProvider.ts\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport * from \"./primitives/$cache.ts\";\nexport * from \"./providers/CacheProvider.ts\";\nexport * from \"./providers/CloudflareKVProvider.ts\";\nexport * from \"./providers/MemoryCacheProvider.ts\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\ndeclare module \"alepha\" {\n interface Hooks {\n /**\n * Fires when a cache lookup finds a value.\n */\n \"cache:hit\": {\n container: string;\n key: string;\n };\n /**\n * Fires when a cache lookup does not find a value.\n */\n \"cache:miss\": {\n container: string;\n key: string;\n };\n /**\n * Fires when a value is written to the cache.\n */\n \"cache:set\": {\n container: string;\n key: string;\n ttlMs?: number;\n };\n /**\n * Fires when a stale value (SWR grace window) is served and a\n * background refresh is scheduled.\n */\n \"cache:stale\": {\n container: string;\n key: string;\n };\n /**\n * Fires when a background SWR refresh completes successfully and\n * the value has been written back to the cache.\n */\n \"cache:revalidate\": {\n container: string;\n key: string;\n };\n }\n}\n\n// ---------------------------------------------------------------------------------------------------------------------\n\n/**\n * Type-safe caching with TTL support.\n *\n * **Features:**\n * - Cached computations with type-safe keys and values\n * - Configurable TTL\n * - Cache invalidation\n * - Automatic cache population\n * - Providers: Memory (default), Redis\n *\n * @module alepha.cache\n */\nexport const AlephaCache = $module({\n name: \"alepha.cache\",\n primitives: [$cache],\n services: [CacheProvider],\n variants: [MemoryCacheProvider],\n register: (alepha) =>\n alepha.with({\n optional: true,\n provide: CacheProvider,\n use: MemoryCacheProvider,\n }),\n});\n"],"mappings":"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"}
|