alepha 0.15.1 → 0.15.3
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 +68 -80
- package/dist/api/audits/index.d.ts +10 -33
- package/dist/api/audits/index.d.ts.map +1 -1
- package/dist/api/audits/index.js +10 -33
- package/dist/api/audits/index.js.map +1 -1
- package/dist/api/files/index.d.ts +10 -3
- package/dist/api/files/index.d.ts.map +1 -1
- package/dist/api/files/index.js +10 -3
- package/dist/api/files/index.js.map +1 -1
- package/dist/api/jobs/index.d.ts +162 -155
- package/dist/api/jobs/index.d.ts.map +1 -1
- package/dist/api/jobs/index.js +10 -3
- package/dist/api/jobs/index.js.map +1 -1
- package/dist/api/keys/index.d.ts +413 -0
- package/dist/api/keys/index.d.ts.map +1 -0
- package/dist/api/keys/index.js +476 -0
- package/dist/api/keys/index.js.map +1 -0
- package/dist/api/notifications/index.d.ts +10 -4
- package/dist/api/notifications/index.d.ts.map +1 -1
- package/dist/api/notifications/index.js +10 -4
- package/dist/api/notifications/index.js.map +1 -1
- package/dist/api/parameters/index.d.ts +43 -50
- package/dist/api/parameters/index.d.ts.map +1 -1
- package/dist/api/parameters/index.js +30 -37
- package/dist/api/parameters/index.js.map +1 -1
- package/dist/api/users/index.d.ts +1081 -760
- package/dist/api/users/index.d.ts.map +1 -1
- package/dist/api/users/index.js +2539 -218
- package/dist/api/users/index.js.map +1 -1
- package/dist/api/verifications/index.d.ts +138 -132
- package/dist/api/verifications/index.d.ts.map +1 -1
- package/dist/api/verifications/index.js +12 -4
- package/dist/api/verifications/index.js.map +1 -1
- package/dist/batch/index.d.ts +20 -40
- package/dist/batch/index.d.ts.map +1 -1
- package/dist/batch/index.js +31 -44
- package/dist/batch/index.js.map +1 -1
- package/dist/bucket/index.d.ts +440 -8
- package/dist/bucket/index.d.ts.map +1 -1
- package/dist/bucket/index.js +1861 -12
- package/dist/bucket/index.js.map +1 -1
- package/dist/cache/core/index.d.ts +179 -7
- package/dist/cache/core/index.d.ts.map +1 -1
- package/dist/cache/core/index.js +213 -7
- package/dist/cache/core/index.js.map +1 -1
- package/dist/cache/redis/index.d.ts +1 -0
- package/dist/cache/redis/index.d.ts.map +1 -1
- package/dist/cache/redis/index.js +4 -0
- package/dist/cache/redis/index.js.map +1 -1
- package/dist/cli/index.d.ts +638 -5645
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +2550 -368
- package/dist/cli/index.js.map +1 -1
- package/dist/command/index.d.ts +203 -45
- package/dist/command/index.d.ts.map +1 -1
- package/dist/command/index.js +2060 -71
- package/dist/command/index.js.map +1 -1
- package/dist/core/index.browser.js +70 -40
- package/dist/core/index.browser.js.map +1 -1
- package/dist/core/index.d.ts +34 -13
- package/dist/core/index.d.ts.map +1 -1
- package/dist/core/index.js +90 -40
- package/dist/core/index.js.map +1 -1
- package/dist/core/index.native.js +70 -40
- package/dist/core/index.native.js.map +1 -1
- package/dist/datetime/index.d.ts +15 -0
- package/dist/datetime/index.d.ts.map +1 -1
- package/dist/datetime/index.js +15 -0
- package/dist/datetime/index.js.map +1 -1
- package/dist/email/index.d.ts +323 -20
- package/dist/email/index.d.ts.map +1 -1
- package/dist/email/index.js +1857 -7
- package/dist/email/index.js.map +1 -1
- package/dist/fake/index.d.ts +90 -8
- package/dist/fake/index.d.ts.map +1 -1
- package/dist/fake/index.js +91 -20
- package/dist/fake/index.js.map +1 -1
- package/dist/lock/core/index.d.ts +11 -4
- package/dist/lock/core/index.d.ts.map +1 -1
- package/dist/lock/core/index.js +11 -4
- package/dist/lock/core/index.js.map +1 -1
- package/dist/logger/index.d.ts +17 -66
- package/dist/logger/index.d.ts.map +1 -1
- package/dist/logger/index.js +14 -63
- package/dist/logger/index.js.map +1 -1
- package/dist/mcp/index.d.ts +10 -30
- package/dist/mcp/index.d.ts.map +1 -1
- package/dist/mcp/index.js +12 -35
- package/dist/mcp/index.js.map +1 -1
- package/dist/orm/index.browser.js +3 -3
- package/dist/orm/index.browser.js.map +1 -1
- package/dist/orm/index.bun.js +39 -20
- package/dist/orm/index.bun.js.map +1 -1
- package/dist/orm/index.d.ts +517 -540
- package/dist/orm/index.d.ts.map +1 -1
- package/dist/orm/index.js +58 -71
- package/dist/orm/index.js.map +1 -1
- package/dist/queue/core/index.d.ts +18 -10
- package/dist/queue/core/index.d.ts.map +1 -1
- package/dist/queue/core/index.js +14 -6
- package/dist/queue/core/index.js.map +1 -1
- package/dist/react/auth/index.browser.js +108 -0
- package/dist/react/auth/index.browser.js.map +1 -0
- package/dist/react/auth/index.d.ts +100 -0
- package/dist/react/auth/index.d.ts.map +1 -0
- package/dist/react/auth/index.js +145 -0
- package/dist/react/auth/index.js.map +1 -0
- package/dist/react/core/index.d.ts +469 -0
- package/dist/react/core/index.d.ts.map +1 -0
- package/dist/react/core/index.js +464 -0
- package/dist/react/core/index.js.map +1 -0
- package/dist/react/form/index.d.ts +232 -0
- package/dist/react/form/index.d.ts.map +1 -0
- package/dist/react/form/index.js +432 -0
- package/dist/react/form/index.js.map +1 -0
- package/dist/react/head/index.browser.js +423 -0
- package/dist/react/head/index.browser.js.map +1 -0
- package/dist/react/head/index.d.ts +288 -0
- package/dist/react/head/index.d.ts.map +1 -0
- package/dist/react/head/index.js +465 -0
- package/dist/react/head/index.js.map +1 -0
- package/dist/react/i18n/index.d.ts +175 -0
- package/dist/react/i18n/index.d.ts.map +1 -0
- package/dist/react/i18n/index.js +224 -0
- package/dist/react/i18n/index.js.map +1 -0
- package/dist/react/router/index.browser.js +1974 -0
- package/dist/react/router/index.browser.js.map +1 -0
- package/dist/react/router/index.d.ts +1956 -0
- package/dist/react/router/index.d.ts.map +1 -0
- package/dist/react/router/index.js +4722 -0
- package/dist/react/router/index.js.map +1 -0
- package/dist/react/websocket/index.d.ts +117 -0
- package/dist/react/websocket/index.d.ts.map +1 -0
- package/dist/react/websocket/index.js +107 -0
- package/dist/react/websocket/index.js.map +1 -0
- package/dist/redis/index.bun.js +4 -0
- package/dist/redis/index.bun.js.map +1 -1
- package/dist/redis/index.d.ts +41 -44
- package/dist/redis/index.d.ts.map +1 -1
- package/dist/redis/index.js +16 -25
- package/dist/redis/index.js.map +1 -1
- package/dist/retry/index.d.ts +11 -2
- package/dist/retry/index.d.ts.map +1 -1
- package/dist/retry/index.js +11 -2
- package/dist/retry/index.js.map +1 -1
- package/dist/scheduler/index.d.ts +11 -2
- package/dist/scheduler/index.d.ts.map +1 -1
- package/dist/scheduler/index.js +11 -2
- package/dist/scheduler/index.js.map +1 -1
- package/dist/security/index.d.ts +140 -49
- package/dist/security/index.d.ts.map +1 -1
- package/dist/security/index.js +164 -32
- package/dist/security/index.js.map +1 -1
- package/dist/server/auth/index.d.ts +12 -7
- package/dist/server/auth/index.d.ts.map +1 -1
- package/dist/server/auth/index.js +12 -7
- package/dist/server/auth/index.js.map +1 -1
- package/dist/server/cache/index.d.ts +7 -22
- package/dist/server/cache/index.d.ts.map +1 -1
- package/dist/server/cache/index.js +7 -22
- package/dist/server/cache/index.js.map +1 -1
- package/dist/server/compress/index.d.ts +10 -2
- package/dist/server/compress/index.d.ts.map +1 -1
- package/dist/server/compress/index.js +10 -2
- package/dist/server/compress/index.js.map +1 -1
- package/dist/server/cookies/index.d.ts +40 -16
- package/dist/server/cookies/index.d.ts.map +1 -1
- package/dist/server/cookies/index.js +7 -5
- package/dist/server/cookies/index.js.map +1 -1
- package/dist/server/core/index.d.ts +124 -23
- package/dist/server/core/index.d.ts.map +1 -1
- package/dist/server/core/index.js +231 -14
- package/dist/server/core/index.js.map +1 -1
- package/dist/server/cors/index.d.ts +13 -23
- package/dist/server/cors/index.d.ts.map +1 -1
- package/dist/server/cors/index.js +7 -21
- package/dist/server/cors/index.js.map +1 -1
- package/dist/server/health/index.d.ts +8 -2
- package/dist/server/health/index.d.ts.map +1 -1
- package/dist/server/health/index.js +8 -2
- package/dist/server/health/index.js.map +1 -1
- package/dist/server/helmet/index.d.ts +11 -3
- package/dist/server/helmet/index.d.ts.map +1 -1
- package/dist/server/helmet/index.js +11 -3
- package/dist/server/helmet/index.js.map +1 -1
- package/dist/server/links/index.d.ts +11 -6
- package/dist/server/links/index.d.ts.map +1 -1
- package/dist/server/links/index.js +11 -6
- package/dist/server/links/index.js.map +1 -1
- package/dist/server/metrics/index.d.ts +10 -3
- package/dist/server/metrics/index.d.ts.map +1 -1
- package/dist/server/metrics/index.js +10 -3
- package/dist/server/metrics/index.js.map +1 -1
- package/dist/server/multipart/index.d.ts +9 -3
- package/dist/server/multipart/index.d.ts.map +1 -1
- package/dist/server/multipart/index.js +9 -3
- package/dist/server/multipart/index.js.map +1 -1
- package/dist/server/proxy/index.d.ts +8 -2
- package/dist/server/proxy/index.d.ts.map +1 -1
- package/dist/server/proxy/index.js +8 -2
- package/dist/server/proxy/index.js.map +1 -1
- package/dist/server/rate-limit/index.d.ts +30 -35
- package/dist/server/rate-limit/index.d.ts.map +1 -1
- package/dist/server/rate-limit/index.js +18 -55
- package/dist/server/rate-limit/index.js.map +1 -1
- package/dist/server/static/index.d.ts +137 -4
- package/dist/server/static/index.d.ts.map +1 -1
- package/dist/server/static/index.js +1853 -5
- package/dist/server/static/index.js.map +1 -1
- package/dist/server/swagger/index.d.ts +309 -6
- package/dist/server/swagger/index.d.ts.map +1 -1
- package/dist/server/swagger/index.js +1854 -6
- package/dist/server/swagger/index.js.map +1 -1
- package/dist/sms/index.d.ts +309 -7
- package/dist/sms/index.d.ts.map +1 -1
- package/dist/sms/index.js +1856 -7
- package/dist/sms/index.js.map +1 -1
- package/dist/system/index.browser.js +1218 -0
- package/dist/system/index.browser.js.map +1 -0
- package/dist/{file → system}/index.d.ts +343 -16
- package/dist/system/index.d.ts.map +1 -0
- package/dist/{file → system}/index.js +419 -22
- package/dist/system/index.js.map +1 -0
- package/dist/thread/index.d.ts +11 -2
- package/dist/thread/index.d.ts.map +1 -1
- package/dist/thread/index.js +11 -2
- package/dist/thread/index.js.map +1 -1
- package/dist/topic/core/index.d.ts +12 -5
- package/dist/topic/core/index.d.ts.map +1 -1
- package/dist/topic/core/index.js +12 -5
- package/dist/topic/core/index.js.map +1 -1
- package/dist/vite/index.d.ts +5 -6272
- package/dist/vite/index.d.ts.map +1 -1
- package/dist/vite/index.js +23 -10
- package/dist/vite/index.js.map +1 -1
- package/dist/websocket/index.d.ts +12 -8
- package/dist/websocket/index.d.ts.map +1 -1
- package/dist/websocket/index.js +12 -8
- package/dist/websocket/index.js.map +1 -1
- package/package.json +82 -11
- package/src/api/audits/index.ts +10 -33
- package/src/api/files/__tests__/$bucket.spec.ts +1 -1
- package/src/api/files/controllers/AdminFileStatsController.spec.ts +1 -1
- package/src/api/files/controllers/FileController.spec.ts +1 -1
- package/src/api/files/index.ts +10 -3
- package/src/api/files/jobs/FileJobs.spec.ts +1 -1
- package/src/api/files/services/FileService.spec.ts +1 -1
- package/src/api/jobs/index.ts +10 -3
- package/src/api/keys/controllers/AdminApiKeyController.ts +75 -0
- package/src/api/keys/controllers/ApiKeyController.ts +103 -0
- package/src/api/keys/entities/apiKeyEntity.ts +41 -0
- package/src/api/keys/index.ts +49 -0
- package/src/api/keys/schemas/adminApiKeyQuerySchema.ts +7 -0
- package/src/api/keys/schemas/adminApiKeyResourceSchema.ts +17 -0
- package/src/api/keys/schemas/createApiKeyBodySchema.ts +7 -0
- package/src/api/keys/schemas/createApiKeyResponseSchema.ts +11 -0
- package/src/api/keys/schemas/listApiKeyResponseSchema.ts +15 -0
- package/src/api/keys/schemas/revokeApiKeyParamsSchema.ts +5 -0
- package/src/api/keys/schemas/revokeApiKeyResponseSchema.ts +5 -0
- package/src/api/keys/services/ApiKeyService.spec.ts +553 -0
- package/src/api/keys/services/ApiKeyService.ts +306 -0
- package/src/api/logs/TODO.md +55 -0
- package/src/api/notifications/index.ts +10 -4
- package/src/api/parameters/index.ts +9 -30
- package/src/api/parameters/primitives/$config.ts +12 -4
- package/src/api/parameters/services/ConfigStore.ts +9 -3
- package/src/api/users/__tests__/ApiKeys-integration.spec.ts +1035 -0
- package/src/api/users/__tests__/ApiKeys.spec.ts +401 -0
- package/src/api/users/index.ts +14 -3
- package/src/api/users/primitives/$realm.ts +33 -5
- package/src/api/users/providers/RealmProvider.ts +1 -12
- package/src/api/users/services/SessionService.ts +1 -1
- package/src/api/verifications/controllers/VerificationController.ts +2 -0
- package/src/api/verifications/index.ts +10 -4
- package/src/batch/index.ts +9 -36
- package/src/batch/primitives/$batch.ts +0 -8
- package/src/batch/providers/BatchProvider.ts +29 -2
- package/src/bucket/__tests__/shared.ts +1 -1
- package/src/bucket/index.ts +13 -6
- package/src/bucket/primitives/$bucket.ts +1 -1
- package/src/bucket/providers/LocalFileStorageProvider.ts +1 -1
- package/src/bucket/providers/MemoryFileStorageProvider.ts +1 -1
- package/src/cache/core/__tests__/shared.ts +30 -0
- package/src/cache/core/index.ts +11 -6
- package/src/cache/core/primitives/$cache.spec.ts +5 -0
- package/src/cache/core/providers/CacheProvider.ts +17 -0
- package/src/cache/core/providers/MemoryCacheProvider.ts +300 -1
- package/src/cache/redis/__tests__/cache-redis.spec.ts +5 -0
- package/src/cache/redis/providers/RedisCacheProvider.ts +9 -0
- package/src/cli/apps/AlephaCli.ts +1 -14
- package/src/cli/apps/AlephaPackageBuilderCli.ts +10 -1
- package/src/cli/atoms/buildOptions.ts +99 -9
- package/src/cli/commands/build.ts +150 -37
- package/src/cli/commands/db.ts +22 -18
- package/src/cli/commands/deploy.ts +1 -1
- package/src/cli/commands/dev.ts +1 -20
- package/src/cli/commands/gen/env.ts +5 -2
- package/src/cli/commands/gen/openapi.ts +5 -2
- package/src/cli/commands/init.spec.ts +588 -0
- package/src/cli/commands/init.ts +115 -58
- package/src/cli/commands/lint.ts +7 -1
- package/src/cli/commands/typecheck.ts +11 -0
- package/src/cli/providers/AppEntryProvider.ts +1 -1
- package/src/cli/providers/ViteBuildProvider.ts +8 -50
- package/src/cli/providers/ViteDevServerProvider.ts +35 -16
- package/src/cli/services/AlephaCliUtils.ts +52 -121
- package/src/cli/services/PackageManagerUtils.ts +129 -11
- package/src/cli/services/ProjectScaffolder.spec.ts +97 -0
- package/src/cli/services/ProjectScaffolder.ts +148 -81
- package/src/cli/services/ViteUtils.ts +82 -0
- package/src/cli/{assets/claudeMd.ts → templates/agentMd.ts} +37 -24
- package/src/cli/templates/apiAppSecurityTs.ts +11 -0
- package/src/cli/templates/apiIndexTs.ts +30 -0
- package/src/cli/templates/gitignore.ts +39 -0
- package/src/cli/{assets → templates}/mainCss.ts +11 -2
- package/src/cli/templates/mainServerTs.ts +33 -0
- package/src/cli/templates/webAppRouterTs.ts +74 -0
- package/src/cli/templates/webHelloComponentTsx.ts +30 -0
- package/src/command/helpers/Runner.spec.ts +139 -0
- package/src/command/helpers/Runner.ts +7 -22
- package/src/command/index.ts +12 -4
- package/src/command/providers/CliProvider.spec.ts +1392 -0
- package/src/command/providers/CliProvider.ts +320 -47
- package/src/core/Alepha.ts +34 -27
- package/src/core/__tests__/Alepha-start.spec.ts +4 -4
- package/src/core/helpers/jsonSchemaToTypeBox.spec.ts +771 -0
- package/src/core/helpers/jsonSchemaToTypeBox.ts +62 -10
- package/src/core/index.shared.ts +1 -0
- package/src/core/index.ts +20 -0
- package/src/core/providers/EventManager.spec.ts +0 -71
- package/src/core/providers/EventManager.ts +3 -15
- package/src/core/providers/Json.ts +2 -14
- package/src/datetime/index.ts +15 -0
- package/src/email/index.ts +10 -5
- package/src/email/providers/LocalEmailProvider.spec.ts +1 -1
- package/src/email/providers/LocalEmailProvider.ts +1 -1
- package/src/fake/__tests__/keyName.example.ts +1 -1
- package/src/fake/__tests__/keyName.spec.ts +5 -5
- package/src/fake/index.ts +9 -6
- package/src/fake/providers/FakeProvider.spec.ts +258 -40
- package/src/fake/providers/FakeProvider.ts +133 -19
- package/src/lock/core/index.ts +11 -4
- package/src/logger/index.ts +17 -66
- package/src/mcp/index.ts +10 -27
- package/src/mcp/transports/SseMcpTransport.ts +0 -11
- package/src/orm/__tests__/PostgresProvider.spec.ts +2 -2
- package/src/orm/index.browser.ts +2 -2
- package/src/orm/index.bun.ts +5 -3
- package/src/orm/index.ts +23 -53
- package/src/orm/providers/drivers/BunSqliteProvider.ts +5 -1
- package/src/orm/providers/drivers/CloudflareD1Provider.ts +57 -30
- package/src/orm/providers/drivers/DatabaseProvider.ts +9 -1
- package/src/orm/providers/drivers/NodeSqliteProvider.ts +4 -1
- package/src/orm/services/Repository.ts +7 -3
- package/src/queue/core/index.ts +14 -6
- package/src/react/auth/__tests__/$auth.spec.ts +202 -0
- package/src/react/auth/hooks/useAuth.ts +32 -0
- package/src/react/auth/index.browser.ts +13 -0
- package/src/react/auth/index.shared.ts +2 -0
- package/src/react/auth/index.ts +48 -0
- package/src/react/auth/providers/ReactAuthProvider.ts +16 -0
- package/src/react/auth/services/ReactAuth.ts +135 -0
- package/src/react/core/__tests__/Router.spec.tsx +169 -0
- package/src/react/core/components/ClientOnly.tsx +49 -0
- package/src/react/core/components/ErrorBoundary.tsx +73 -0
- package/src/react/core/contexts/AlephaContext.ts +7 -0
- package/src/react/core/contexts/AlephaProvider.tsx +42 -0
- package/src/react/core/hooks/useAction.browser.spec.tsx +569 -0
- package/src/react/core/hooks/useAction.ts +480 -0
- package/src/react/core/hooks/useAlepha.ts +26 -0
- package/src/react/core/hooks/useClient.ts +17 -0
- package/src/react/core/hooks/useEvents.ts +51 -0
- package/src/react/core/hooks/useInject.ts +12 -0
- package/src/react/core/hooks/useStore.ts +52 -0
- package/src/react/core/index.ts +90 -0
- package/src/react/form/components/FormState.tsx +17 -0
- package/src/react/form/errors/FormValidationError.ts +18 -0
- package/src/react/form/hooks/useForm.browser.spec.tsx +366 -0
- package/src/react/form/hooks/useForm.ts +47 -0
- package/src/react/form/hooks/useFormState.ts +130 -0
- package/src/react/form/index.ts +44 -0
- package/src/react/form/services/FormModel.ts +614 -0
- package/src/react/head/helpers/SeoExpander.spec.ts +203 -0
- package/src/react/head/helpers/SeoExpander.ts +142 -0
- package/src/react/head/hooks/useHead.spec.tsx +288 -0
- package/src/react/head/hooks/useHead.ts +62 -0
- package/src/react/head/index.browser.ts +26 -0
- package/src/react/head/index.ts +44 -0
- package/src/react/head/interfaces/Head.ts +105 -0
- package/src/react/head/primitives/$head.ts +25 -0
- package/src/react/head/providers/BrowserHeadProvider.browser.spec.ts +196 -0
- package/src/react/head/providers/BrowserHeadProvider.ts +212 -0
- package/src/react/head/providers/HeadProvider.ts +168 -0
- package/src/react/head/providers/ServerHeadProvider.ts +31 -0
- package/src/react/i18n/__tests__/integration.spec.tsx +239 -0
- package/src/react/i18n/components/Localize.spec.tsx +357 -0
- package/src/react/i18n/components/Localize.tsx +35 -0
- package/src/react/i18n/hooks/useI18n.browser.spec.tsx +438 -0
- package/src/react/i18n/hooks/useI18n.ts +18 -0
- package/src/react/i18n/index.ts +41 -0
- package/src/react/i18n/primitives/$dictionary.ts +69 -0
- package/src/react/i18n/providers/I18nProvider.spec.ts +389 -0
- package/src/react/i18n/providers/I18nProvider.ts +278 -0
- package/src/react/router/__tests__/page-head-browser.browser.spec.ts +95 -0
- package/src/react/router/__tests__/page-head.spec.ts +48 -0
- package/src/react/router/__tests__/seo-head.spec.ts +125 -0
- package/src/react/router/atoms/ssrManifestAtom.ts +58 -0
- package/src/react/router/components/ErrorViewer.tsx +872 -0
- package/src/react/router/components/Link.tsx +23 -0
- package/src/react/router/components/NestedView.tsx +223 -0
- package/src/react/router/components/NotFound.tsx +30 -0
- package/src/react/router/constants/PAGE_PRELOAD_KEY.ts +6 -0
- package/src/react/router/contexts/RouterLayerContext.ts +12 -0
- package/src/react/router/errors/Redirection.ts +28 -0
- package/src/react/router/hooks/useActive.ts +52 -0
- package/src/react/router/hooks/useQueryParams.ts +63 -0
- package/src/react/router/hooks/useRouter.ts +20 -0
- package/src/react/router/hooks/useRouterState.ts +11 -0
- package/src/react/router/index.browser.ts +45 -0
- package/src/react/router/index.shared.ts +19 -0
- package/src/react/router/index.ts +146 -0
- package/src/react/router/primitives/$page.browser.spec.tsx +851 -0
- package/src/react/router/primitives/$page.spec.tsx +676 -0
- package/src/react/router/primitives/$page.ts +489 -0
- package/src/react/router/providers/ReactBrowserProvider.ts +312 -0
- package/src/react/router/providers/ReactBrowserRendererProvider.ts +25 -0
- package/src/react/router/providers/ReactBrowserRouterProvider.ts +168 -0
- package/src/react/router/providers/ReactPageProvider.ts +726 -0
- package/src/react/router/providers/ReactPreloadProvider.spec.ts +142 -0
- package/src/react/router/providers/ReactPreloadProvider.ts +85 -0
- package/src/react/router/providers/ReactServerProvider.spec.tsx +316 -0
- package/src/react/router/providers/ReactServerProvider.ts +487 -0
- package/src/react/router/providers/ReactServerTemplateProvider.spec.ts +210 -0
- package/src/react/router/providers/ReactServerTemplateProvider.ts +542 -0
- package/src/react/router/providers/SSRManifestProvider.ts +334 -0
- package/src/react/router/services/ReactPageServerService.ts +48 -0
- package/src/react/router/services/ReactPageService.ts +27 -0
- package/src/react/router/services/ReactRouter.ts +262 -0
- package/src/react/websocket/hooks/useRoom.tsx +242 -0
- package/src/react/websocket/index.ts +7 -0
- package/src/redis/__tests__/redis.spec.ts +13 -0
- package/src/redis/index.ts +9 -25
- package/src/redis/providers/BunRedisProvider.ts +9 -0
- package/src/redis/providers/NodeRedisProvider.ts +8 -0
- package/src/redis/providers/RedisProvider.ts +16 -0
- package/src/retry/index.ts +11 -2
- package/src/router/index.ts +15 -0
- package/src/scheduler/index.ts +11 -2
- package/src/security/__tests__/BasicAuth.spec.ts +2 -0
- package/src/security/__tests__/ServerSecurityProvider.spec.ts +90 -5
- package/src/security/index.ts +15 -10
- package/src/security/interfaces/IssuerResolver.ts +27 -0
- package/src/security/primitives/$issuer.ts +55 -0
- package/src/security/providers/SecurityProvider.ts +179 -0
- package/src/security/providers/ServerBasicAuthProvider.ts +6 -2
- package/src/security/providers/ServerSecurityProvider.ts +63 -41
- package/src/server/auth/index.ts +12 -7
- package/src/server/cache/index.ts +7 -22
- package/src/server/compress/index.ts +10 -2
- package/src/server/cookies/index.ts +7 -5
- package/src/server/cookies/primitives/$cookie.ts +33 -11
- package/src/server/core/index.ts +16 -6
- package/src/server/core/interfaces/ServerRequest.ts +83 -1
- package/src/server/core/primitives/$action.spec.ts +1 -1
- package/src/server/core/primitives/$action.ts +8 -3
- package/src/server/core/providers/NodeHttpServerProvider.spec.ts +9 -3
- package/src/server/core/providers/NodeHttpServerProvider.ts +9 -3
- package/src/server/core/services/ServerRequestParser.spec.ts +520 -0
- package/src/server/core/services/ServerRequestParser.ts +306 -13
- package/src/server/cors/index.ts +7 -21
- package/src/server/cors/primitives/$cors.ts +6 -2
- package/src/server/health/index.ts +8 -2
- package/src/server/helmet/index.ts +11 -3
- package/src/server/links/index.ts +11 -6
- package/src/server/metrics/index.ts +10 -3
- package/src/server/multipart/index.ts +9 -3
- package/src/server/proxy/index.ts +8 -2
- package/src/server/rate-limit/index.ts +21 -25
- package/src/server/rate-limit/primitives/$rateLimit.ts +6 -2
- package/src/server/rate-limit/providers/ServerRateLimitProvider.spec.ts +38 -14
- package/src/server/rate-limit/providers/ServerRateLimitProvider.ts +22 -56
- package/src/server/static/index.ts +8 -2
- package/src/server/static/providers/ServerStaticProvider.ts +1 -1
- package/src/server/swagger/index.ts +9 -4
- package/src/server/swagger/providers/ServerSwaggerProvider.ts +1 -1
- package/src/sms/index.ts +9 -5
- package/src/sms/providers/LocalSmsProvider.spec.ts +1 -1
- package/src/sms/providers/LocalSmsProvider.ts +1 -1
- package/src/system/index.browser.ts +36 -0
- package/src/system/index.ts +62 -0
- package/src/system/index.workerd.ts +1 -0
- package/src/{file → system}/providers/FileSystemProvider.ts +24 -0
- package/src/{file → system}/providers/MemoryFileSystemProvider.ts +116 -3
- package/src/system/providers/MemoryShellProvider.ts +164 -0
- package/src/{file → system}/providers/NodeFileSystemProvider.spec.ts +2 -2
- package/src/{file → system}/providers/NodeFileSystemProvider.ts +47 -2
- package/src/system/providers/NodeShellProvider.ts +184 -0
- package/src/system/providers/ShellProvider.ts +74 -0
- package/src/{file → system}/services/FileDetector.spec.ts +2 -2
- package/src/thread/index.ts +11 -2
- package/src/topic/core/index.ts +12 -5
- package/src/vite/tasks/buildClient.ts +2 -7
- package/src/vite/tasks/buildServer.ts +19 -13
- package/src/vite/tasks/generateCloudflare.ts +10 -7
- package/src/vite/tasks/generateDocker.ts +4 -0
- package/src/websocket/index.ts +12 -8
- package/dist/file/index.d.ts.map +0 -1
- package/dist/file/index.js.map +0 -1
- package/src/cli/assets/apiIndexTs.ts +0 -16
- package/src/cli/assets/mainServerTs.ts +0 -24
- package/src/cli/assets/webAppRouterTs.ts +0 -16
- package/src/cli/assets/webHelloComponentTsx.ts +0 -20
- package/src/cli/providers/ViteTemplateProvider.ts +0 -27
- package/src/file/index.ts +0 -43
- /package/src/cli/{assets → templates}/apiHelloControllerTs.ts +0 -0
- /package/src/cli/{assets → templates}/biomeJson.ts +0 -0
- /package/src/cli/{assets → templates}/dummySpecTs.ts +0 -0
- /package/src/cli/{assets → templates}/editorconfig.ts +0 -0
- /package/src/cli/{assets → templates}/mainBrowserTs.ts +0 -0
- /package/src/cli/{assets → templates}/tsconfigJson.ts +0 -0
- /package/src/cli/{assets → templates}/webIndexTs.ts +0 -0
- /package/src/{file → system}/errors/FileError.ts +0 -0
- /package/src/{file → system}/services/FileDetector.ts +0 -0
package/dist/cache/core/index.js
CHANGED
|
@@ -17,14 +17,95 @@ var CacheProvider = class {};
|
|
|
17
17
|
|
|
18
18
|
//#endregion
|
|
19
19
|
//#region ../../src/cache/core/providers/MemoryCacheProvider.ts
|
|
20
|
+
/**
|
|
21
|
+
* In-memory implementation of CacheProvider for testing.
|
|
22
|
+
*
|
|
23
|
+
* This provider stores all cache entries in memory, making it ideal for
|
|
24
|
+
* unit tests that need to verify cache operations without touching Redis or other backends.
|
|
25
|
+
*
|
|
26
|
+
* @example
|
|
27
|
+
* ```typescript
|
|
28
|
+
* // In tests, substitute the real CacheProvider with MemoryCacheProvider
|
|
29
|
+
* const alepha = Alepha.create().with({
|
|
30
|
+
* provide: CacheProvider,
|
|
31
|
+
* use: MemoryCacheProvider,
|
|
32
|
+
* });
|
|
33
|
+
*
|
|
34
|
+
* // Run code that uses caching
|
|
35
|
+
* const service = alepha.inject(MyService);
|
|
36
|
+
* await service.fetchWithCache("key");
|
|
37
|
+
*
|
|
38
|
+
* // Verify cache behavior
|
|
39
|
+
* const cache = alepha.inject(MemoryCacheProvider);
|
|
40
|
+
* expect(cache.stats().misses).toBe(1);
|
|
41
|
+
* await service.fetchWithCache("key");
|
|
42
|
+
* expect(cache.stats().hits).toBe(1);
|
|
43
|
+
* ```
|
|
44
|
+
*/
|
|
20
45
|
var MemoryCacheProvider = class {
|
|
21
46
|
dateTimeProvider = $inject(DateTimeProvider);
|
|
22
47
|
log = $logger();
|
|
23
48
|
store = {};
|
|
49
|
+
/**
|
|
50
|
+
* All recorded get calls.
|
|
51
|
+
*/
|
|
52
|
+
getCalls = [];
|
|
53
|
+
/**
|
|
54
|
+
* All recorded set calls.
|
|
55
|
+
*/
|
|
56
|
+
setCalls = [];
|
|
57
|
+
/**
|
|
58
|
+
* All recorded del calls.
|
|
59
|
+
*/
|
|
60
|
+
delCalls = [];
|
|
61
|
+
/**
|
|
62
|
+
* Cache statistics.
|
|
63
|
+
*/
|
|
64
|
+
_stats = {
|
|
65
|
+
hits: 0,
|
|
66
|
+
misses: 0,
|
|
67
|
+
sets: 0,
|
|
68
|
+
deletes: 0
|
|
69
|
+
};
|
|
70
|
+
/**
|
|
71
|
+
* Error to throw on get (for testing error handling)
|
|
72
|
+
*/
|
|
73
|
+
getError = null;
|
|
74
|
+
/**
|
|
75
|
+
* Error to throw on set (for testing error handling)
|
|
76
|
+
*/
|
|
77
|
+
setError = null;
|
|
78
|
+
/**
|
|
79
|
+
* Error to throw on del (for testing error handling)
|
|
80
|
+
*/
|
|
81
|
+
delError = null;
|
|
82
|
+
constructor(options = {}) {
|
|
83
|
+
this.getError = options.getError ?? null;
|
|
84
|
+
this.setError = options.setError ?? null;
|
|
85
|
+
this.delError = options.delError ?? null;
|
|
86
|
+
}
|
|
24
87
|
async get(name, key) {
|
|
25
|
-
|
|
88
|
+
this.getCalls.push({
|
|
89
|
+
name,
|
|
90
|
+
key,
|
|
91
|
+
timestamp: Date.now()
|
|
92
|
+
});
|
|
93
|
+
if (this.getError) throw this.getError;
|
|
94
|
+
const data = this.store[name]?.[key]?.data;
|
|
95
|
+
if (data !== void 0) this._stats.hits++;
|
|
96
|
+
else this._stats.misses++;
|
|
97
|
+
return data;
|
|
26
98
|
}
|
|
27
99
|
async set(name, key, value, ttl) {
|
|
100
|
+
this.setCalls.push({
|
|
101
|
+
name,
|
|
102
|
+
key,
|
|
103
|
+
value,
|
|
104
|
+
ttl,
|
|
105
|
+
timestamp: Date.now()
|
|
106
|
+
});
|
|
107
|
+
this._stats.sets++;
|
|
108
|
+
if (this.setError) throw this.setError;
|
|
28
109
|
if (this.store[name] == null) this.store[name] = {};
|
|
29
110
|
this.store[name][key] ??= {};
|
|
30
111
|
this.store[name][key].data = value;
|
|
@@ -41,6 +122,13 @@ var MemoryCacheProvider = class {
|
|
|
41
122
|
return this.store[name][key].data;
|
|
42
123
|
}
|
|
43
124
|
async del(name, ...keys) {
|
|
125
|
+
this.delCalls.push({
|
|
126
|
+
name,
|
|
127
|
+
keys,
|
|
128
|
+
timestamp: Date.now()
|
|
129
|
+
});
|
|
130
|
+
this._stats.deletes++;
|
|
131
|
+
if (this.delError) throw this.delError;
|
|
44
132
|
if (keys.length === 0) {
|
|
45
133
|
this.log.debug(`Deleting all cache for name`, { name });
|
|
46
134
|
if (this.store[name]) for (const key of Object.keys(this.store[name])) {
|
|
@@ -79,6 +167,119 @@ var MemoryCacheProvider = class {
|
|
|
79
167
|
}
|
|
80
168
|
this.store = {};
|
|
81
169
|
}
|
|
170
|
+
async incr(name, key, amount) {
|
|
171
|
+
if (this.store[name] == null) this.store[name] = {};
|
|
172
|
+
const existing = this.store[name][key]?.data;
|
|
173
|
+
let current = 0;
|
|
174
|
+
if (existing) {
|
|
175
|
+
const str = new TextDecoder().decode(existing);
|
|
176
|
+
current = Number.parseInt(str, 10) || 0;
|
|
177
|
+
}
|
|
178
|
+
const newValue = current + amount;
|
|
179
|
+
this.store[name][key] ??= {};
|
|
180
|
+
this.store[name][key].data = new TextEncoder().encode(String(newValue));
|
|
181
|
+
return newValue;
|
|
182
|
+
}
|
|
183
|
+
/**
|
|
184
|
+
* Get cache statistics (hits, misses, sets, deletes).
|
|
185
|
+
*
|
|
186
|
+
* @example
|
|
187
|
+
* ```typescript
|
|
188
|
+
* expect(cache.stats().hits).toBe(1);
|
|
189
|
+
* expect(cache.stats().misses).toBe(0);
|
|
190
|
+
* ```
|
|
191
|
+
*/
|
|
192
|
+
stats() {
|
|
193
|
+
return { ...this._stats };
|
|
194
|
+
}
|
|
195
|
+
/**
|
|
196
|
+
* Check if a key was set during the test.
|
|
197
|
+
*
|
|
198
|
+
* @example
|
|
199
|
+
* ```typescript
|
|
200
|
+
* expect(cache.wasSet("my-cache", "user:123")).toBe(true);
|
|
201
|
+
* ```
|
|
202
|
+
*/
|
|
203
|
+
wasSet(name, key) {
|
|
204
|
+
if (key === void 0) return this.setCalls.some((call) => call.name === name);
|
|
205
|
+
return this.setCalls.some((call) => call.name === name && call.key === key);
|
|
206
|
+
}
|
|
207
|
+
/**
|
|
208
|
+
* Check if a key was retrieved during the test.
|
|
209
|
+
*
|
|
210
|
+
* @example
|
|
211
|
+
* ```typescript
|
|
212
|
+
* expect(cache.wasGet("my-cache", "user:123")).toBe(true);
|
|
213
|
+
* ```
|
|
214
|
+
*/
|
|
215
|
+
wasGet(name, key) {
|
|
216
|
+
if (key === void 0) return this.getCalls.some((call) => call.name === name);
|
|
217
|
+
return this.getCalls.some((call) => call.name === name && call.key === key);
|
|
218
|
+
}
|
|
219
|
+
/**
|
|
220
|
+
* Check if a key was deleted during the test.
|
|
221
|
+
*
|
|
222
|
+
* @example
|
|
223
|
+
* ```typescript
|
|
224
|
+
* expect(cache.wasDeleted("my-cache", "user:123")).toBe(true);
|
|
225
|
+
* ```
|
|
226
|
+
*/
|
|
227
|
+
wasDeleted(name, key) {
|
|
228
|
+
if (key === void 0) return this.delCalls.some((call) => call.name === name);
|
|
229
|
+
return this.delCalls.some((call) => call.name === name && call.keys.includes(key));
|
|
230
|
+
}
|
|
231
|
+
/**
|
|
232
|
+
* Get the number of cached entries for a specific cache name.
|
|
233
|
+
*
|
|
234
|
+
* @example
|
|
235
|
+
* ```typescript
|
|
236
|
+
* expect(cache.size("my-cache")).toBe(5);
|
|
237
|
+
* ```
|
|
238
|
+
*/
|
|
239
|
+
size(name) {
|
|
240
|
+
if (name === void 0) return Object.values(this.store).reduce((total, entries) => total + Object.keys(entries).length, 0);
|
|
241
|
+
return Object.keys(this.store[name] ?? {}).length;
|
|
242
|
+
}
|
|
243
|
+
/**
|
|
244
|
+
* Get all cache names.
|
|
245
|
+
*
|
|
246
|
+
* @example
|
|
247
|
+
* ```typescript
|
|
248
|
+
* expect(cache.names()).toContain("my-cache");
|
|
249
|
+
* ```
|
|
250
|
+
*/
|
|
251
|
+
names() {
|
|
252
|
+
return Object.keys(this.store);
|
|
253
|
+
}
|
|
254
|
+
/**
|
|
255
|
+
* Reset all in-memory state (useful between tests).
|
|
256
|
+
*
|
|
257
|
+
* @example
|
|
258
|
+
* ```typescript
|
|
259
|
+
* beforeEach(() => {
|
|
260
|
+
* cache.reset();
|
|
261
|
+
* });
|
|
262
|
+
* ```
|
|
263
|
+
*/
|
|
264
|
+
reset() {
|
|
265
|
+
for (const name of Object.keys(this.store)) for (const key of Object.keys(this.store[name])) {
|
|
266
|
+
const timeout = this.store[name][key]?.timeout;
|
|
267
|
+
if (timeout) this.dateTimeProvider.clearTimeout(timeout);
|
|
268
|
+
}
|
|
269
|
+
this.store = {};
|
|
270
|
+
this.getCalls = [];
|
|
271
|
+
this.setCalls = [];
|
|
272
|
+
this.delCalls = [];
|
|
273
|
+
this._stats = {
|
|
274
|
+
hits: 0,
|
|
275
|
+
misses: 0,
|
|
276
|
+
sets: 0,
|
|
277
|
+
deletes: 0
|
|
278
|
+
};
|
|
279
|
+
this.getError = null;
|
|
280
|
+
this.setError = null;
|
|
281
|
+
this.delError = null;
|
|
282
|
+
}
|
|
82
283
|
};
|
|
83
284
|
|
|
84
285
|
//#endregion
|
|
@@ -211,14 +412,19 @@ $cache[KIND] = CachePrimitive;
|
|
|
211
412
|
//#endregion
|
|
212
413
|
//#region ../../src/cache/core/index.ts
|
|
213
414
|
/**
|
|
214
|
-
*
|
|
415
|
+
* | type | quality | stability |
|
|
416
|
+
* |------|---------|-----------|
|
|
417
|
+
* | backend | rare | stable |
|
|
418
|
+
*
|
|
419
|
+
* Type-safe caching with TTL support.
|
|
215
420
|
*
|
|
216
|
-
*
|
|
217
|
-
*
|
|
218
|
-
*
|
|
421
|
+
* **Features:**
|
|
422
|
+
* - Cached computations with type-safe keys and values
|
|
423
|
+
* - Configurable TTL
|
|
424
|
+
* - Cache invalidation
|
|
425
|
+
* - Automatic cache population
|
|
426
|
+
* - Providers: Memory (default), Redis
|
|
219
427
|
*
|
|
220
|
-
* @see {@link $cache}
|
|
221
|
-
* @see {@link CacheProvider}
|
|
222
428
|
* @module alepha.cache
|
|
223
429
|
*/
|
|
224
430
|
const AlephaCache = $module({
|
|
@@ -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/index.ts"],"sourcesContent":["import { AlephaError } from \"alepha\";\n\nexport class CacheError extends AlephaError {}\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 /**\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","import { $inject } from \"alepha\";\nimport { DateTimeProvider, type Timeout } from \"alepha/datetime\";\nimport { $logger } from \"alepha/logger\";\nimport type { CacheProvider } from \"./CacheProvider.ts\";\n\ntype CacheName = string;\ntype CacheKey = string;\ntype CacheValue = {\n data?: Uint8Array;\n timeout?: Timeout;\n};\n\nexport class MemoryCacheProvider implements CacheProvider {\n protected readonly dateTimeProvider = $inject(DateTimeProvider);\n protected readonly log = $logger();\n\n protected store: Record<CacheName, Record<CacheKey, CacheValue>> = {};\n\n public async get(name: string, key: string): Promise<Uint8Array | undefined> {\n return this.store[name]?.[key]?.data;\n }\n\n public async set(\n name: string,\n key: string,\n value: Uint8Array,\n ttl?: number,\n ): Promise<Uint8Array> {\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 // 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","import {\n $env,\n $inject,\n createPrimitive,\n type InstantiableClass,\n KIND,\n Primitive,\n t,\n} from \"alepha\";\nimport { DateTimeProvider, type DurationLike } from \"alepha/datetime\";\nimport { CacheError } from \"../errors/CacheError.ts\";\nimport { CacheProvider } from \"../providers/CacheProvider.ts\";\nimport { MemoryCacheProvider } from \"../providers/MemoryCacheProvider.ts\";\n\n/**\n * Creates a cache primitive for high-performance data caching with automatic management.\n *\n * Provides a caching layer that improves application performance by storing frequently accessed\n * data in memory or external stores like Redis, with support for both function result caching\n * and manual cache operations.\n *\n * **Key Features**\n * - Automatic function result caching based on input parameters\n * - Multiple storage backends (in-memory, Redis, custom providers)\n * - Intelligent serialization for JSON, strings, and binary data\n * - Configurable TTL with automatic expiration\n * - Pattern-based cache invalidation with wildcard support\n * - Environment controls to enable/disable caching\n *\n * **Storage Backends**\n * - Memory: Fast in-memory cache (default for development)\n * - Redis: Distributed cache for production environments\n * - Custom providers: Implement your own storage backend\n *\n * @example\n * ```ts\n * class DataService {\n * // Function result caching\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 * // Manual cache operations\n * sessionCache = $cache<UserSession>({\n * name: \"sessions\",\n * ttl: [1, \"hour\"]\n * });\n *\n * async storeSession(id: string, session: UserSession) {\n * await this.sessionCache.set(id, session);\n * }\n *\n * async invalidateUserSessions(userId: string) {\n * await this.sessionCache.invalidate(`user:${userId}:*`);\n * }\n * }\n * ```\n */\nexport const $cache = <TReturn = string, TParameter extends any[] = any[]>(\n options: CachePrimitiveOptions<TReturn, TParameter> = {},\n): CachePrimitiveFn<TReturn, TParameter> => {\n const instance = createPrimitive(\n CachePrimitive<TReturn, TParameter>,\n options,\n );\n const fn = (...args: TParameter): Promise<TReturn> => instance.run(...args);\n return Object.setPrototypeOf(fn, instance) as CachePrimitiveFn<\n TReturn,\n TParameter\n >;\n};\n\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 * If not provided, the default store provider will be used.\n */\n provider?: InstantiableClass<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// ---------------------------------------------------------------------------------------------------------------------\n\nconst envSchema = t.object({\n CACHE_ENABLED: t.boolean({ default: true }),\n CACHE_DEFAULT_TTL: t.number({\n default: 300, // 5 minutes\n description: \"The default time to live for cache entries. In seconds.\",\n }),\n});\n\nexport class CachePrimitive<\n TReturn = any,\n TParameter extends any[] = any[],\n> extends Primitive<CachePrimitiveOptions<TReturn, TParameter>> {\n protected readonly env = $env(envSchema);\n protected readonly dateTimeProvider = $inject(DateTimeProvider);\n protected readonly provider = this.$provider();\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 };\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 Error(\"Cache handler is not defined.\");\n }\n\n const key = this.key(...args);\n const cached = await this.get(key);\n if (cached) {\n return cached;\n }\n\n const result = await handler(...args);\n // note: when exception occurs, don't cache the result\n\n await this.set(key, result);\n\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 invalidate(...keys: string[]): Promise<void> {\n const keysToDelete: string[] = [];\n\n for (const key of keys) {\n if (key.endsWith(\"*\")) {\n const result = await this.provider.keys(\n this.container,\n key.slice(0, -1),\n );\n keysToDelete.push(...result);\n } else {\n keysToDelete.push(key);\n }\n }\n\n await this.provider.del(this.container, ...keysToDelete);\n }\n\n public async set(\n key: string,\n value: TReturn,\n ttl?: DurationLike,\n ): Promise<void> {\n const px = this.dateTimeProvider\n .duration(\n ttl ?? this.options.ttl ?? [this.env.CACHE_DEFAULT_TTL, \"seconds\"],\n )\n .as(\"milliseconds\");\n\n await this.provider.set(\n this.container,\n key,\n this.serialize(value),\n px > 0 ? px : undefined,\n );\n }\n\n public async get(key: string): Promise<TReturn | undefined> {\n if (\n !this.alepha.isStarted() ||\n this.options.disabled ||\n !this.env.CACHE_ENABLED\n ) {\n return undefined;\n }\n\n const data = await this.provider.get(this.container, key);\n if (data) {\n return await this.deserialize<TReturn>(data);\n }\n\n return undefined;\n }\n\n protected serialize<TReturn>(value: TReturn): Uint8Array {\n if (value instanceof Uint8Array) {\n return new Uint8Array([this.codes.BINARY, ...value]); // TODO: check if copy is ok?\n }\n\n if (typeof value === \"string\") {\n return new Uint8Array([this.codes.STRING, ...this.encoder.encode(value)]);\n }\n\n return new Uint8Array([\n this.codes.JSON,\n ...this.encoder.encode(JSON.stringify(value)),\n ]);\n }\n\n protected async deserialize<TReturn>(\n uint8Array: Uint8Array,\n ): Promise<TReturn> {\n const type = uint8Array[0];\n const payload = uint8Array.slice(1);\n\n if (type === this.codes.BINARY) {\n return payload as TReturn;\n }\n if (type === this.codes.JSON) {\n return JSON.parse(this.decoder.decode(payload)) as TReturn;\n }\n if (type === this.codes.STRING) {\n return this.decoder.decode(payload) as TReturn;\n }\n\n throw new CacheError(`Unknown serialization type: ${type}`);\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\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$cache[KIND] = CachePrimitive;\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/MemoryCacheProvider.ts\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\n/**\n * Provides high-performance caching capabilities for Alepha applications with configurable TTL and multiple storage backends.\n *\n * The cache module enables declarative caching through the `$cache` primitive, allowing you to cache method results,\n * API responses, or computed values with automatic invalidation and type safety. It supports both in-memory and\n * persistent storage backends for different performance and durability requirements.\n *\n * @see {@link $cache}\n * @see {@link CacheProvider}\n * @module alepha.cache\n */\nexport const AlephaCache = $module({\n name: \"alepha.cache\",\n primitives: [$cache],\n services: [CacheProvider, 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/index.ts"],"sourcesContent":["import { AlephaError } from \"alepha\";\n\nexport class CacheError extends AlephaError {}\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 /**\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","import { $inject } from \"alepha\";\nimport { DateTimeProvider, type Timeout } from \"alepha/datetime\";\nimport { $logger } from \"alepha/logger\";\nimport type { 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 implements 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 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({ name, key, timestamp: Date.now() });\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({ name, key, value, ttl, timestamp: Date.now() });\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({ name, keys, timestamp: Date.now() });\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 const str = new TextDecoder().decode(existing);\n current = Number.parseInt(str, 10) || 0;\n }\n\n const newValue = current + amount;\n this.store[name][key] ??= {};\n this.store[name][key].data = new TextEncoder().encode(String(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 $env,\n $inject,\n createPrimitive,\n type InstantiableClass,\n KIND,\n Primitive,\n t,\n} from \"alepha\";\nimport { DateTimeProvider, type DurationLike } from \"alepha/datetime\";\nimport { CacheError } from \"../errors/CacheError.ts\";\nimport { CacheProvider } from \"../providers/CacheProvider.ts\";\nimport { MemoryCacheProvider } from \"../providers/MemoryCacheProvider.ts\";\n\n/**\n * Creates a cache primitive for high-performance data caching with automatic management.\n *\n * Provides a caching layer that improves application performance by storing frequently accessed\n * data in memory or external stores like Redis, with support for both function result caching\n * and manual cache operations.\n *\n * **Key Features**\n * - Automatic function result caching based on input parameters\n * - Multiple storage backends (in-memory, Redis, custom providers)\n * - Intelligent serialization for JSON, strings, and binary data\n * - Configurable TTL with automatic expiration\n * - Pattern-based cache invalidation with wildcard support\n * - Environment controls to enable/disable caching\n *\n * **Storage Backends**\n * - Memory: Fast in-memory cache (default for development)\n * - Redis: Distributed cache for production environments\n * - Custom providers: Implement your own storage backend\n *\n * @example\n * ```ts\n * class DataService {\n * // Function result caching\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 * // Manual cache operations\n * sessionCache = $cache<UserSession>({\n * name: \"sessions\",\n * ttl: [1, \"hour\"]\n * });\n *\n * async storeSession(id: string, session: UserSession) {\n * await this.sessionCache.set(id, session);\n * }\n *\n * async invalidateUserSessions(userId: string) {\n * await this.sessionCache.invalidate(`user:${userId}:*`);\n * }\n * }\n * ```\n */\nexport const $cache = <TReturn = string, TParameter extends any[] = any[]>(\n options: CachePrimitiveOptions<TReturn, TParameter> = {},\n): CachePrimitiveFn<TReturn, TParameter> => {\n const instance = createPrimitive(\n CachePrimitive<TReturn, TParameter>,\n options,\n );\n const fn = (...args: TParameter): Promise<TReturn> => instance.run(...args);\n return Object.setPrototypeOf(fn, instance) as CachePrimitiveFn<\n TReturn,\n TParameter\n >;\n};\n\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 * If not provided, the default store provider will be used.\n */\n provider?: InstantiableClass<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// ---------------------------------------------------------------------------------------------------------------------\n\nconst envSchema = t.object({\n CACHE_ENABLED: t.boolean({ default: true }),\n CACHE_DEFAULT_TTL: t.number({\n default: 300, // 5 minutes\n description: \"The default time to live for cache entries. In seconds.\",\n }),\n});\n\nexport class CachePrimitive<\n TReturn = any,\n TParameter extends any[] = any[],\n> extends Primitive<CachePrimitiveOptions<TReturn, TParameter>> {\n protected readonly env = $env(envSchema);\n protected readonly dateTimeProvider = $inject(DateTimeProvider);\n protected readonly provider = this.$provider();\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 };\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 Error(\"Cache handler is not defined.\");\n }\n\n const key = this.key(...args);\n const cached = await this.get(key);\n if (cached) {\n return cached;\n }\n\n const result = await handler(...args);\n // note: when exception occurs, don't cache the result\n\n await this.set(key, result);\n\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 invalidate(...keys: string[]): Promise<void> {\n const keysToDelete: string[] = [];\n\n for (const key of keys) {\n if (key.endsWith(\"*\")) {\n const result = await this.provider.keys(\n this.container,\n key.slice(0, -1),\n );\n keysToDelete.push(...result);\n } else {\n keysToDelete.push(key);\n }\n }\n\n await this.provider.del(this.container, ...keysToDelete);\n }\n\n public async set(\n key: string,\n value: TReturn,\n ttl?: DurationLike,\n ): Promise<void> {\n const px = this.dateTimeProvider\n .duration(\n ttl ?? this.options.ttl ?? [this.env.CACHE_DEFAULT_TTL, \"seconds\"],\n )\n .as(\"milliseconds\");\n\n await this.provider.set(\n this.container,\n key,\n this.serialize(value),\n px > 0 ? px : undefined,\n );\n }\n\n public async get(key: string): Promise<TReturn | undefined> {\n if (\n !this.alepha.isStarted() ||\n this.options.disabled ||\n !this.env.CACHE_ENABLED\n ) {\n return undefined;\n }\n\n const data = await this.provider.get(this.container, key);\n if (data) {\n return await this.deserialize<TReturn>(data);\n }\n\n return undefined;\n }\n\n protected serialize<TReturn>(value: TReturn): Uint8Array {\n if (value instanceof Uint8Array) {\n return new Uint8Array([this.codes.BINARY, ...value]); // TODO: check if copy is ok?\n }\n\n if (typeof value === \"string\") {\n return new Uint8Array([this.codes.STRING, ...this.encoder.encode(value)]);\n }\n\n return new Uint8Array([\n this.codes.JSON,\n ...this.encoder.encode(JSON.stringify(value)),\n ]);\n }\n\n protected async deserialize<TReturn>(\n uint8Array: Uint8Array,\n ): Promise<TReturn> {\n const type = uint8Array[0];\n const payload = uint8Array.slice(1);\n\n if (type === this.codes.BINARY) {\n return payload as TReturn;\n }\n if (type === this.codes.JSON) {\n return JSON.parse(this.decoder.decode(payload)) as TReturn;\n }\n if (type === this.codes.STRING) {\n return this.decoder.decode(payload) as TReturn;\n }\n\n throw new CacheError(`Unknown serialization type: ${type}`);\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\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$cache[KIND] = CachePrimitive;\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/MemoryCacheProvider.ts\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\n/**\n * | type | quality | stability |\n * |------|---------|-----------|\n * | backend | rare | stable |\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, MemoryCacheProvider],\n register: (alepha) =>\n alepha.with({\n optional: true,\n provide: CacheProvider,\n use: MemoryCacheProvider,\n }),\n});\n"],"mappings":"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"}
|
|
@@ -24,6 +24,7 @@ declare class RedisCacheProvider implements CacheProvider {
|
|
|
24
24
|
has(name: string, key: string): Promise<boolean>;
|
|
25
25
|
keys(name: string, filter?: string): Promise<string[]>;
|
|
26
26
|
clear(): Promise<void>;
|
|
27
|
+
incr(name: string, key: string, amount: number): Promise<number>;
|
|
27
28
|
protected prefix(...path: string[]): string;
|
|
28
29
|
}
|
|
29
30
|
//#endregion
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","names":[],"sources":["../../../src/cache/redis/providers/RedisCacheProvider.ts","../../../src/cache/redis/index.ts"],"mappings":";;;;;;;cAKM,SAAA,UAAS,OAAA;wCAOb,OAAA,CAAA,OAAA;AAAA;AAAA;EAAA,UAGU,GAAA,SAAY,OAAA,CAAQ,MAAA,QAAc,SAAA;AAAA;AAAA,cAGjC,kBAAA,YAA8B,aAAA;EAAA,mBACtB,GAAA,EADW,cAAA,CACR,MAAA;EAAA,mBACH,aAAA,EAAa,aAAA;EAAA,mBACb,GAAA;;;qBACA,MAAA,EAAM,MAAA;EAEZ,GAAA,CAAI,IAAA,UAAc,GAAA,WAAc,OAAA,CAAQ,UAAA;EAmBxC,GAAA,CACX,IAAA,UACA,GAAA,UACA,KAAA,EAAO,UAAA,WACP,GAAA,YACC,OAAA,CAAQ,UAAA;EAmBE,GAAA,CAAI,IAAA,aAAiB,IAAA,aAAiB,OAAA;EAgBtC,GAAA,CAAI,IAAA,UAAc,GAAA,WAAc,OAAA;EAIhC,IAAA,CAAK,IAAA,UAAc,MAAA,YAAkB,OAAA;EAOrC,KAAA,CAAA,GAAS,OAAA;EAAA,
|
|
1
|
+
{"version":3,"file":"index.d.ts","names":[],"sources":["../../../src/cache/redis/providers/RedisCacheProvider.ts","../../../src/cache/redis/index.ts"],"mappings":";;;;;;;cAKM,SAAA,UAAS,OAAA;wCAOb,OAAA,CAAA,OAAA;AAAA;AAAA;EAAA,UAGU,GAAA,SAAY,OAAA,CAAQ,MAAA,QAAc,SAAA;AAAA;AAAA,cAGjC,kBAAA,YAA8B,aAAA;EAAA,mBACtB,GAAA,EADW,cAAA,CACR,MAAA;EAAA,mBACH,aAAA,EAAa,aAAA;EAAA,mBACb,GAAA;;;qBACA,MAAA,EAAM,MAAA;EAEZ,GAAA,CAAI,IAAA,UAAc,GAAA,WAAc,OAAA,CAAQ,UAAA;EAmBxC,GAAA,CACX,IAAA,UACA,GAAA,UACA,KAAA,EAAO,UAAA,WACP,GAAA,YACC,OAAA,CAAQ,UAAA;EAmBE,GAAA,CAAI,IAAA,aAAiB,IAAA,aAAiB,OAAA;EAgBtC,GAAA,CAAI,IAAA,UAAc,GAAA,WAAc,OAAA;EAIhC,IAAA,CAAK,IAAA,UAAc,MAAA,YAAkB,OAAA;EAOrC,KAAA,CAAA,GAAS,OAAA;EAOT,IAAA,CACX,IAAA,UACA,GAAA,UACA,MAAA,WACC,OAAA;EAAA,UAKO,MAAA,CAAA,GAAU,IAAA;AAAA;;;;;;;;;cC9FT,gBAAA,EAAgB,OAAA,CAAA,OAAA,CAW3B,OAAA,CAX2B,MAAA"}
|
|
@@ -53,6 +53,10 @@ var RedisCacheProvider = class {
|
|
|
53
53
|
const keys = await this.redisProvider.keys(pattern);
|
|
54
54
|
await this.redisProvider.del(keys);
|
|
55
55
|
}
|
|
56
|
+
async incr(name, key, amount) {
|
|
57
|
+
const keyWithPrefix = this.prefix(name, key);
|
|
58
|
+
return this.redisProvider.incr(keyWithPrefix, amount);
|
|
59
|
+
}
|
|
56
60
|
prefix(...path) {
|
|
57
61
|
const parts = ["cache", ...path];
|
|
58
62
|
if (this.env.REDIS_CACHE_PREFIX) parts.unshift(this.env.REDIS_CACHE_PREFIX);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":[],"sources":["../../../src/cache/redis/providers/RedisCacheProvider.ts","../../../src/cache/redis/index.ts"],"sourcesContent":["import { $env, $inject, Alepha, type Static, t } from \"alepha\";\nimport type { CacheProvider } from \"alepha/cache\";\nimport { $logger } from \"alepha/logger\";\nimport { RedisProvider } from \"alepha/redis\";\n\nconst envSchema = t.object({\n REDIS_CACHE_PREFIX: t.optional(\n t.text({\n description:\n \"Force a prefix for all cache keys in Redis. Useful for testing or multi-tenant applications.\",\n }),\n ),\n});\n\ndeclare module \"alepha\" {\n interface Env extends Partial<Static<typeof envSchema>> {}\n}\n\nexport class RedisCacheProvider implements CacheProvider {\n protected readonly log = $logger();\n protected readonly redisProvider = $inject(RedisProvider);\n protected readonly env = $env(envSchema);\n protected readonly alepha = $inject(Alepha);\n\n public async get(name: string, key: string): Promise<Uint8Array | undefined> {\n if (!this.alepha.isStarted()) {\n return;\n }\n\n const keyWithPrefix = this.prefix(name, key);\n const buffer = await this.redisProvider.get(keyWithPrefix);\n if (!buffer) {\n return;\n }\n\n this.log.debug(`Cache hit`, {\n size: buffer.byteLength,\n key: keyWithPrefix,\n });\n\n return new Uint8Array(buffer);\n }\n\n public async set(\n name: string,\n key: string,\n value: Uint8Array | string,\n ttl?: number,\n ): Promise<Uint8Array> {\n if (!this.alepha.isReady()) {\n return new Uint8Array(Buffer.from(value));\n }\n\n const buffer = Buffer.from(value);\n const prefix = this.prefix(name, key);\n\n if (ttl) {\n return new Uint8Array(\n await this.redisProvider.set(prefix, buffer, {\n expiration: { type: \"PX\", value: ttl },\n }),\n );\n }\n\n return new Uint8Array(await this.redisProvider.set(prefix, buffer));\n }\n\n public async del(name: string, ...keys: string[]): Promise<void> {\n const nameKey = this.prefix(name);\n\n if (keys.length === 0) {\n const keys = await this.redisProvider.keys(`${nameKey}:*`);\n await this.redisProvider.del(keys);\n return;\n }\n\n await this.redisProvider.del(\n keys.map((key) =>\n key.startsWith(nameKey) ? key : this.prefix(name, key),\n ),\n );\n }\n\n public async has(name: string, key: string): Promise<boolean> {\n return this.get(name, key).then((value) => value != null);\n }\n\n public async keys(name: string, filter?: string): Promise<string[]> {\n if (filter) {\n return await this.redisProvider.keys(`${this.prefix(name)}:${filter}*`);\n }\n return this.redisProvider.keys(`${this.prefix(name)}:*`);\n }\n\n public async clear(): Promise<void> {\n this.log.debug(\"Clearing all cache\");\n const pattern = `${this.prefix()}:*`;\n const keys = await this.redisProvider.keys(pattern);\n await this.redisProvider.del(keys);\n }\n\n protected prefix(...path: string[]): string {\n const parts = [\"cache\", ...path];\n\n if (this.env.REDIS_CACHE_PREFIX) {\n parts.unshift(this.env.REDIS_CACHE_PREFIX);\n }\n\n return parts.join(\":\");\n }\n}\n","import { $module } from \"alepha\";\nimport { AlephaCache, CacheProvider } from \"alepha/cache\";\nimport { RedisCacheProvider } from \"./providers/RedisCacheProvider.ts\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport * from \"./providers/RedisCacheProvider.ts\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\n/**\n * Plugin for Alepha Cache that provides Redis caching capabilities.\n *\n * @see {@link RedisCacheProvider}\n * @module alepha.cache.redis\n */\nexport const AlephaCacheRedis = $module({\n name: \"alepha.cache.redis\",\n services: [RedisCacheProvider],\n register: (alepha) =>\n alepha\n .with({\n provide: CacheProvider,\n use: RedisCacheProvider,\n optional: true,\n })\n .with(AlephaCache),\n});\n"],"mappings":";;;;;;AAKA,MAAM,YAAY,EAAE,OAAO,EACzB,oBAAoB,EAAE,SACpB,EAAE,KAAK,EACL,aACE,gGACH,CAAC,CACH,EACF,CAAC;AAMF,IAAa,qBAAb,MAAyD;CACvD,AAAmB,MAAM,SAAS;CAClC,AAAmB,gBAAgB,QAAQ,cAAc;CACzD,AAAmB,MAAM,KAAK,UAAU;CACxC,AAAmB,SAAS,QAAQ,OAAO;CAE3C,MAAa,IAAI,MAAc,KAA8C;AAC3E,MAAI,CAAC,KAAK,OAAO,WAAW,CAC1B;EAGF,MAAM,gBAAgB,KAAK,OAAO,MAAM,IAAI;EAC5C,MAAM,SAAS,MAAM,KAAK,cAAc,IAAI,cAAc;AAC1D,MAAI,CAAC,OACH;AAGF,OAAK,IAAI,MAAM,aAAa;GAC1B,MAAM,OAAO;GACb,KAAK;GACN,CAAC;AAEF,SAAO,IAAI,WAAW,OAAO;;CAG/B,MAAa,IACX,MACA,KACA,OACA,KACqB;AACrB,MAAI,CAAC,KAAK,OAAO,SAAS,CACxB,QAAO,IAAI,WAAW,OAAO,KAAK,MAAM,CAAC;EAG3C,MAAM,SAAS,OAAO,KAAK,MAAM;EACjC,MAAM,SAAS,KAAK,OAAO,MAAM,IAAI;AAErC,MAAI,IACF,QAAO,IAAI,WACT,MAAM,KAAK,cAAc,IAAI,QAAQ,QAAQ,EAC3C,YAAY;GAAE,MAAM;GAAM,OAAO;GAAK,EACvC,CAAC,CACH;AAGH,SAAO,IAAI,WAAW,MAAM,KAAK,cAAc,IAAI,QAAQ,OAAO,CAAC;;CAGrE,MAAa,IAAI,MAAc,GAAG,MAA+B;EAC/D,MAAM,UAAU,KAAK,OAAO,KAAK;AAEjC,MAAI,KAAK,WAAW,GAAG;GACrB,MAAM,OAAO,MAAM,KAAK,cAAc,KAAK,GAAG,QAAQ,IAAI;AAC1D,SAAM,KAAK,cAAc,IAAI,KAAK;AAClC;;AAGF,QAAM,KAAK,cAAc,IACvB,KAAK,KAAK,QACR,IAAI,WAAW,QAAQ,GAAG,MAAM,KAAK,OAAO,MAAM,IAAI,CACvD,CACF;;CAGH,MAAa,IAAI,MAAc,KAA+B;AAC5D,SAAO,KAAK,IAAI,MAAM,IAAI,CAAC,MAAM,UAAU,SAAS,KAAK;;CAG3D,MAAa,KAAK,MAAc,QAAoC;AAClE,MAAI,OACF,QAAO,MAAM,KAAK,cAAc,KAAK,GAAG,KAAK,OAAO,KAAK,CAAC,GAAG,OAAO,GAAG;AAEzE,SAAO,KAAK,cAAc,KAAK,GAAG,KAAK,OAAO,KAAK,CAAC,IAAI;;CAG1D,MAAa,QAAuB;AAClC,OAAK,IAAI,MAAM,qBAAqB;EACpC,MAAM,UAAU,GAAG,KAAK,QAAQ,CAAC;EACjC,MAAM,OAAO,MAAM,KAAK,cAAc,KAAK,QAAQ;AACnD,QAAM,KAAK,cAAc,IAAI,KAAK;;CAGpC,AAAU,OAAO,GAAG,MAAwB;EAC1C,MAAM,QAAQ,CAAC,SAAS,GAAG,KAAK;AAEhC,MAAI,KAAK,IAAI,mBACX,OAAM,QAAQ,KAAK,IAAI,mBAAmB;AAG5C,SAAO,MAAM,KAAK,IAAI;;;;;;;;;;;;
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../../../src/cache/redis/providers/RedisCacheProvider.ts","../../../src/cache/redis/index.ts"],"sourcesContent":["import { $env, $inject, Alepha, type Static, t } from \"alepha\";\nimport type { CacheProvider } from \"alepha/cache\";\nimport { $logger } from \"alepha/logger\";\nimport { RedisProvider } from \"alepha/redis\";\n\nconst envSchema = t.object({\n REDIS_CACHE_PREFIX: t.optional(\n t.text({\n description:\n \"Force a prefix for all cache keys in Redis. Useful for testing or multi-tenant applications.\",\n }),\n ),\n});\n\ndeclare module \"alepha\" {\n interface Env extends Partial<Static<typeof envSchema>> {}\n}\n\nexport class RedisCacheProvider implements CacheProvider {\n protected readonly log = $logger();\n protected readonly redisProvider = $inject(RedisProvider);\n protected readonly env = $env(envSchema);\n protected readonly alepha = $inject(Alepha);\n\n public async get(name: string, key: string): Promise<Uint8Array | undefined> {\n if (!this.alepha.isStarted()) {\n return;\n }\n\n const keyWithPrefix = this.prefix(name, key);\n const buffer = await this.redisProvider.get(keyWithPrefix);\n if (!buffer) {\n return;\n }\n\n this.log.debug(`Cache hit`, {\n size: buffer.byteLength,\n key: keyWithPrefix,\n });\n\n return new Uint8Array(buffer);\n }\n\n public async set(\n name: string,\n key: string,\n value: Uint8Array | string,\n ttl?: number,\n ): Promise<Uint8Array> {\n if (!this.alepha.isReady()) {\n return new Uint8Array(Buffer.from(value));\n }\n\n const buffer = Buffer.from(value);\n const prefix = this.prefix(name, key);\n\n if (ttl) {\n return new Uint8Array(\n await this.redisProvider.set(prefix, buffer, {\n expiration: { type: \"PX\", value: ttl },\n }),\n );\n }\n\n return new Uint8Array(await this.redisProvider.set(prefix, buffer));\n }\n\n public async del(name: string, ...keys: string[]): Promise<void> {\n const nameKey = this.prefix(name);\n\n if (keys.length === 0) {\n const keys = await this.redisProvider.keys(`${nameKey}:*`);\n await this.redisProvider.del(keys);\n return;\n }\n\n await this.redisProvider.del(\n keys.map((key) =>\n key.startsWith(nameKey) ? key : this.prefix(name, key),\n ),\n );\n }\n\n public async has(name: string, key: string): Promise<boolean> {\n return this.get(name, key).then((value) => value != null);\n }\n\n public async keys(name: string, filter?: string): Promise<string[]> {\n if (filter) {\n return await this.redisProvider.keys(`${this.prefix(name)}:${filter}*`);\n }\n return this.redisProvider.keys(`${this.prefix(name)}:*`);\n }\n\n public async clear(): Promise<void> {\n this.log.debug(\"Clearing all cache\");\n const pattern = `${this.prefix()}:*`;\n const keys = await this.redisProvider.keys(pattern);\n await this.redisProvider.del(keys);\n }\n\n public async incr(\n name: string,\n key: string,\n amount: number,\n ): Promise<number> {\n const keyWithPrefix = this.prefix(name, key);\n return this.redisProvider.incr(keyWithPrefix, amount);\n }\n\n protected prefix(...path: string[]): string {\n const parts = [\"cache\", ...path];\n\n if (this.env.REDIS_CACHE_PREFIX) {\n parts.unshift(this.env.REDIS_CACHE_PREFIX);\n }\n\n return parts.join(\":\");\n }\n}\n","import { $module } from \"alepha\";\nimport { AlephaCache, CacheProvider } from \"alepha/cache\";\nimport { RedisCacheProvider } from \"./providers/RedisCacheProvider.ts\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport * from \"./providers/RedisCacheProvider.ts\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\n/**\n * Plugin for Alepha Cache that provides Redis caching capabilities.\n *\n * @see {@link RedisCacheProvider}\n * @module alepha.cache.redis\n */\nexport const AlephaCacheRedis = $module({\n name: \"alepha.cache.redis\",\n services: [RedisCacheProvider],\n register: (alepha) =>\n alepha\n .with({\n provide: CacheProvider,\n use: RedisCacheProvider,\n optional: true,\n })\n .with(AlephaCache),\n});\n"],"mappings":";;;;;;AAKA,MAAM,YAAY,EAAE,OAAO,EACzB,oBAAoB,EAAE,SACpB,EAAE,KAAK,EACL,aACE,gGACH,CAAC,CACH,EACF,CAAC;AAMF,IAAa,qBAAb,MAAyD;CACvD,AAAmB,MAAM,SAAS;CAClC,AAAmB,gBAAgB,QAAQ,cAAc;CACzD,AAAmB,MAAM,KAAK,UAAU;CACxC,AAAmB,SAAS,QAAQ,OAAO;CAE3C,MAAa,IAAI,MAAc,KAA8C;AAC3E,MAAI,CAAC,KAAK,OAAO,WAAW,CAC1B;EAGF,MAAM,gBAAgB,KAAK,OAAO,MAAM,IAAI;EAC5C,MAAM,SAAS,MAAM,KAAK,cAAc,IAAI,cAAc;AAC1D,MAAI,CAAC,OACH;AAGF,OAAK,IAAI,MAAM,aAAa;GAC1B,MAAM,OAAO;GACb,KAAK;GACN,CAAC;AAEF,SAAO,IAAI,WAAW,OAAO;;CAG/B,MAAa,IACX,MACA,KACA,OACA,KACqB;AACrB,MAAI,CAAC,KAAK,OAAO,SAAS,CACxB,QAAO,IAAI,WAAW,OAAO,KAAK,MAAM,CAAC;EAG3C,MAAM,SAAS,OAAO,KAAK,MAAM;EACjC,MAAM,SAAS,KAAK,OAAO,MAAM,IAAI;AAErC,MAAI,IACF,QAAO,IAAI,WACT,MAAM,KAAK,cAAc,IAAI,QAAQ,QAAQ,EAC3C,YAAY;GAAE,MAAM;GAAM,OAAO;GAAK,EACvC,CAAC,CACH;AAGH,SAAO,IAAI,WAAW,MAAM,KAAK,cAAc,IAAI,QAAQ,OAAO,CAAC;;CAGrE,MAAa,IAAI,MAAc,GAAG,MAA+B;EAC/D,MAAM,UAAU,KAAK,OAAO,KAAK;AAEjC,MAAI,KAAK,WAAW,GAAG;GACrB,MAAM,OAAO,MAAM,KAAK,cAAc,KAAK,GAAG,QAAQ,IAAI;AAC1D,SAAM,KAAK,cAAc,IAAI,KAAK;AAClC;;AAGF,QAAM,KAAK,cAAc,IACvB,KAAK,KAAK,QACR,IAAI,WAAW,QAAQ,GAAG,MAAM,KAAK,OAAO,MAAM,IAAI,CACvD,CACF;;CAGH,MAAa,IAAI,MAAc,KAA+B;AAC5D,SAAO,KAAK,IAAI,MAAM,IAAI,CAAC,MAAM,UAAU,SAAS,KAAK;;CAG3D,MAAa,KAAK,MAAc,QAAoC;AAClE,MAAI,OACF,QAAO,MAAM,KAAK,cAAc,KAAK,GAAG,KAAK,OAAO,KAAK,CAAC,GAAG,OAAO,GAAG;AAEzE,SAAO,KAAK,cAAc,KAAK,GAAG,KAAK,OAAO,KAAK,CAAC,IAAI;;CAG1D,MAAa,QAAuB;AAClC,OAAK,IAAI,MAAM,qBAAqB;EACpC,MAAM,UAAU,GAAG,KAAK,QAAQ,CAAC;EACjC,MAAM,OAAO,MAAM,KAAK,cAAc,KAAK,QAAQ;AACnD,QAAM,KAAK,cAAc,IAAI,KAAK;;CAGpC,MAAa,KACX,MACA,KACA,QACiB;EACjB,MAAM,gBAAgB,KAAK,OAAO,MAAM,IAAI;AAC5C,SAAO,KAAK,cAAc,KAAK,eAAe,OAAO;;CAGvD,AAAU,OAAO,GAAG,MAAwB;EAC1C,MAAM,QAAQ,CAAC,SAAS,GAAG,KAAK;AAEhC,MAAI,KAAK,IAAI,mBACX,OAAM,QAAQ,KAAK,IAAI,mBAAmB;AAG5C,SAAO,MAAM,KAAK,IAAI;;;;;;;;;;;;ACrG1B,MAAa,mBAAmB,QAAQ;CACtC,MAAM;CACN,UAAU,CAAC,mBAAmB;CAC9B,WAAW,WACT,OACG,KAAK;EACJ,SAAS;EACT,KAAK;EACL,UAAU;EACX,CAAC,CACD,KAAK,YAAY;CACvB,CAAC"}
|