alepha 0.20.3 → 0.20.5

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.
Files changed (218) hide show
  1. package/dist/api/audits/index.d.ts.map +1 -1
  2. package/dist/api/files/index.d.ts.map +1 -1
  3. package/dist/api/jobs/index.d.ts +14 -14
  4. package/dist/api/jobs/index.d.ts.map +1 -1
  5. package/dist/api/organizations/index.d.ts.map +1 -1
  6. package/dist/api/parameters/index.d.ts +6 -1
  7. package/dist/api/parameters/index.d.ts.map +1 -1
  8. package/dist/api/parameters/index.js +20 -4
  9. package/dist/api/parameters/index.js.map +1 -1
  10. package/dist/api/payments/index.d.ts.map +1 -1
  11. package/dist/api/users/index.browser.js +6 -0
  12. package/dist/api/users/index.browser.js.map +1 -1
  13. package/dist/api/users/index.d.ts +5032 -134
  14. package/dist/api/users/index.d.ts.map +1 -1
  15. package/dist/api/users/index.js +58 -10
  16. package/dist/api/users/index.js.map +1 -1
  17. package/dist/bin/index.js +0 -0
  18. package/dist/bucket/index.d.ts +77 -107
  19. package/dist/bucket/index.d.ts.map +1 -1
  20. package/dist/bucket/index.js +148 -4
  21. package/dist/bucket/index.js.map +1 -1
  22. package/dist/bucket/index.workerd.js +7 -1
  23. package/dist/bucket/index.workerd.js.map +1 -1
  24. package/dist/cache/core/index.d.ts +26 -0
  25. package/dist/cache/core/index.d.ts.map +1 -1
  26. package/dist/cache/core/index.js +11 -1
  27. package/dist/cache/core/index.js.map +1 -1
  28. package/dist/cache/core/index.workerd.js +11 -1
  29. package/dist/cache/core/index.workerd.js.map +1 -1
  30. package/dist/cli/config/index.d.ts +7 -5
  31. package/dist/cli/config/index.d.ts.map +1 -1
  32. package/dist/cli/config/index.js +2 -3
  33. package/dist/cli/config/index.js.map +1 -1
  34. package/dist/cli/core/index.d.ts +419 -12
  35. package/dist/cli/core/index.d.ts.map +1 -1
  36. package/dist/cli/core/index.js +22 -511
  37. package/dist/cli/core/index.js.map +1 -1
  38. package/dist/cli/devtools/index.d.ts +4 -8
  39. package/dist/cli/devtools/index.d.ts.map +1 -1
  40. package/dist/cli/devtools/index.js +13 -15
  41. package/dist/cli/devtools/index.js.map +1 -1
  42. package/dist/cli/platform/index.d.ts +10 -13
  43. package/dist/cli/platform/index.d.ts.map +1 -1
  44. package/dist/cli/platform/index.js +18 -15
  45. package/dist/cli/platform/index.js.map +1 -1
  46. package/dist/cli/vendor/index.d.ts +10 -13
  47. package/dist/cli/vendor/index.d.ts.map +1 -1
  48. package/dist/cli/vendor/index.js +16 -13
  49. package/dist/cli/vendor/index.js.map +1 -1
  50. package/dist/command/index.d.ts +1 -1
  51. package/dist/core/index.browser.js +27 -3
  52. package/dist/core/index.browser.js.map +1 -1
  53. package/dist/core/index.d.ts +6 -3
  54. package/dist/core/index.d.ts.map +1 -1
  55. package/dist/core/index.js +27 -3
  56. package/dist/core/index.js.map +1 -1
  57. package/dist/core/index.native.js +27 -3
  58. package/dist/core/index.native.js.map +1 -1
  59. package/dist/core/index.workerd.js +27 -3
  60. package/dist/core/index.workerd.js.map +1 -1
  61. package/dist/datetime/index.d.ts +69 -10
  62. package/dist/datetime/index.d.ts.map +1 -1
  63. package/dist/datetime/index.js +135 -13
  64. package/dist/datetime/index.js.map +1 -1
  65. package/dist/email/smtp/index.js +10636 -2
  66. package/dist/email/smtp/index.js.map +1 -1
  67. package/dist/fake/index.d.ts +8085 -4
  68. package/dist/fake/index.d.ts.map +1 -1
  69. package/dist/fake/index.js +33554 -3
  70. package/dist/fake/index.js.map +1 -1
  71. package/dist/lock/core/index.d.ts +30 -2
  72. package/dist/lock/core/index.d.ts.map +1 -1
  73. package/dist/lock/core/index.js +35 -12
  74. package/dist/lock/core/index.js.map +1 -1
  75. package/dist/mcp/index.d.ts +238 -31
  76. package/dist/mcp/index.d.ts.map +1 -1
  77. package/dist/mcp/index.js +198 -71
  78. package/dist/mcp/index.js.map +1 -1
  79. package/dist/orm/core/index.browser.js +1 -1
  80. package/dist/orm/core/index.browser.js.map +1 -1
  81. package/dist/orm/core/index.bun.js +4 -3
  82. package/dist/orm/core/index.bun.js.map +1 -1
  83. package/dist/orm/core/index.d.ts +4877 -9
  84. package/dist/orm/core/index.d.ts.map +1 -1
  85. package/dist/orm/core/index.js +4 -3
  86. package/dist/orm/core/index.js.map +1 -1
  87. package/dist/orm/postgres/index.d.ts +608 -1
  88. package/dist/orm/postgres/index.d.ts.map +1 -1
  89. package/dist/react/core/index.d.ts +102 -1
  90. package/dist/react/core/index.d.ts.map +1 -1
  91. package/dist/react/core/index.js +65 -1
  92. package/dist/react/core/index.js.map +1 -1
  93. package/dist/react/form/index.d.ts +6 -0
  94. package/dist/react/form/index.d.ts.map +1 -1
  95. package/dist/react/form/index.js +7 -7
  96. package/dist/react/form/index.js.map +1 -1
  97. package/dist/react/i18n/index.d.ts +7 -1
  98. package/dist/react/i18n/index.d.ts.map +1 -1
  99. package/dist/react/i18n/index.js +6 -0
  100. package/dist/react/i18n/index.js.map +1 -1
  101. package/dist/react/router/index.browser.js +20 -2
  102. package/dist/react/router/index.browser.js.map +1 -1
  103. package/dist/react/router/index.d.ts +36 -4
  104. package/dist/react/router/index.d.ts.map +1 -1
  105. package/dist/react/router/index.js +20 -2
  106. package/dist/react/router/index.js.map +1 -1
  107. package/dist/react/testing/chunk-6Ep1yQYe.js +16 -0
  108. package/dist/react/testing/index.d.ts +411 -1
  109. package/dist/react/testing/index.d.ts.map +1 -1
  110. package/dist/react/testing/index.js +12293 -13
  111. package/dist/react/testing/index.js.map +1 -1
  112. package/dist/react/ui/index.d.ts +195 -1
  113. package/dist/react/ui/index.d.ts.map +1 -1
  114. package/dist/react/ui/index.js +61 -1
  115. package/dist/react/ui/index.js.map +1 -1
  116. package/dist/scheduler/index.d.ts +84 -3
  117. package/dist/scheduler/index.d.ts.map +1 -1
  118. package/dist/scheduler/index.js +390 -1
  119. package/dist/scheduler/index.js.map +1 -1
  120. package/dist/scheduler/index.workerd.js +390 -1
  121. package/dist/scheduler/index.workerd.js.map +1 -1
  122. package/dist/security/index.d.ts +325 -2
  123. package/dist/security/index.d.ts.map +1 -1
  124. package/dist/security/index.js +1361 -2
  125. package/dist/security/index.js.map +1 -1
  126. package/dist/server/auth/index.d.ts +1054 -1
  127. package/dist/server/auth/index.d.ts.map +1 -1
  128. package/dist/server/auth/index.js +1223 -1
  129. package/dist/server/auth/index.js.map +1 -1
  130. package/dist/server/core/index.browser.js +10 -3
  131. package/dist/server/core/index.browser.js.map +1 -1
  132. package/dist/server/core/index.d.ts.map +1 -1
  133. package/dist/server/core/index.js +28 -5
  134. package/dist/server/core/index.js.map +1 -1
  135. package/dist/server/metrics/index.d.ts +514 -1
  136. package/dist/server/metrics/index.d.ts.map +1 -1
  137. package/dist/server/metrics/index.js +4374 -4
  138. package/dist/server/metrics/index.js.map +1 -1
  139. package/dist/server/swagger/index.d.ts.map +1 -1
  140. package/dist/server/swagger/index.js +3 -4
  141. package/dist/server/swagger/index.js.map +1 -1
  142. package/dist/websocket/index.browser.js +11 -5
  143. package/dist/websocket/index.browser.js.map +1 -1
  144. package/dist/websocket/index.d.ts +3 -1
  145. package/dist/websocket/index.d.ts.map +1 -1
  146. package/dist/websocket/index.js +21 -6
  147. package/dist/websocket/index.js.map +1 -1
  148. package/package.json +416 -8
  149. package/src/api/parameters/services/ParameterProvider.ts +21 -4
  150. package/src/api/users/__tests__/SessionService.spec.ts +99 -0
  151. package/src/api/users/__tests__/UserJobs.spec.ts +67 -0
  152. package/src/api/users/atoms/realmAuthSettingsAtom.ts +15 -0
  153. package/src/api/users/entities/sessions.ts +6 -0
  154. package/src/api/users/jobs/UserJobs.ts +44 -17
  155. package/src/api/users/providers/RealmProvider.ts +4 -0
  156. package/src/api/users/services/SessionService.ts +27 -0
  157. package/src/bucket/__tests__/NodeS3BucketProvider.spec.ts +74 -0
  158. package/src/bucket/index.ts +19 -2
  159. package/src/bucket/primitives/$bucket.ts +9 -1
  160. package/src/bucket/providers/CloudflareR2Provider.ts +2 -137
  161. package/src/bucket/providers/NodeS3BucketProvider.ts +218 -0
  162. package/src/cache/core/index.ts +29 -0
  163. package/src/cache/core/primitives/$cache.ts +14 -1
  164. package/src/cli/config/defineConfig.ts +13 -15
  165. package/src/cli/core/__tests__/init.spec.ts +6 -7
  166. package/src/cli/core/services/ProjectScaffolder.ts +18 -14
  167. package/src/cli/core/tasks/BuildCloudflareTask.ts +5 -0
  168. package/src/cli/core/templates/agentMd.ts +2 -10
  169. package/src/cli/core/templates/saasAdminLayoutTsx.ts +3 -3
  170. package/src/cli/devtools/index.ts +12 -26
  171. package/src/cli/platform/index.ts +15 -24
  172. package/src/cli/vendor/atoms/vendorOptions.ts +1 -1
  173. package/src/cli/vendor/index.ts +14 -23
  174. package/src/core/Alepha.ts +11 -1
  175. package/src/core/helpers/ref.ts +18 -0
  176. package/src/core/index.shared.ts +1 -0
  177. package/src/core/providers/SchemaValidator.ts +9 -1
  178. package/src/core/providers/TypeProvider.ts +1 -2
  179. package/src/datetime/REFACTORING.md +118 -0
  180. package/src/datetime/providers/DateTimeProvider.ts +203 -24
  181. package/src/lock/core/index.ts +31 -0
  182. package/src/lock/core/primitives/$lock.ts +14 -1
  183. package/src/mcp/__tests__/jsonrpc.spec.ts +1 -1
  184. package/src/mcp/helpers/jsonrpc.ts +26 -1
  185. package/src/mcp/index.ts +10 -5
  186. package/src/mcp/interfaces/McpTypes.ts +83 -6
  187. package/src/mcp/primitives/$prompt.ts +18 -1
  188. package/src/mcp/primitives/$resource.ts +18 -1
  189. package/src/mcp/primitives/$tool.ts +83 -7
  190. package/src/mcp/providers/McpServerProvider.ts +74 -16
  191. package/src/mcp/transports/StreamableHttpMcpTransport.ts +226 -0
  192. package/src/orm/REFACTORING.md +330 -0
  193. package/src/orm/core/primitives/$transactional.ts +11 -0
  194. package/src/orm/core/schemas/updateSchema.ts +1 -1
  195. package/src/orm/core/services/PgRelationManager.ts +4 -2
  196. package/src/react/core/__tests__/useQuery.browser.spec.tsx +86 -0
  197. package/src/react/core/hooks/useQuery.ts +153 -0
  198. package/src/react/core/index.ts +1 -0
  199. package/src/react/form/services/FormModel.ts +15 -6
  200. package/src/react/form/services/parseField.ts +8 -0
  201. package/src/react/i18n/providers/I18nProvider.ts +8 -2
  202. package/src/react/router/__tests__/$page.spec.tsx +0 -16
  203. package/src/react/router/__tests__/ssr.spec.tsx +339 -0
  204. package/src/react/router/primitives/$page.ts +28 -4
  205. package/src/react/router/providers/ReactPageProvider.ts +27 -9
  206. package/src/react/ui/atoms/uiThemeListAtom.ts +36 -0
  207. package/src/react/ui/index.ts +6 -0
  208. package/src/react/ui/services/SchemaControl.ts +209 -0
  209. package/src/security/primitives/$issuer.ts +6 -3
  210. package/src/server/core/__tests__/ServerRouterProvider-serializationError.spec.ts +75 -0
  211. package/src/server/core/__tests__/ServerRouterProvider-validationError.spec.ts +306 -0
  212. package/src/server/core/errors/ValidationError.ts +13 -1
  213. package/src/server/core/primitives/$action.ts +16 -5
  214. package/src/server/core/providers/ServerRouterProvider.ts +26 -4
  215. package/src/server/swagger/providers/ServerSwaggerProvider.ts +5 -7
  216. package/src/websocket/providers/NodeWebSocketServerProvider.ts +10 -4
  217. package/src/websocket/services/WebSocketClient.ts +11 -5
  218. package/src/mcp/transports/SseMcpTransport.ts +0 -182
@@ -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 type InstantiableClass,\n KIND,\n type MiddlewareMetadata,\n OPTIONS,\n Primitive,\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 cached = await instance.get(key);\n if (cached !== undefined) return cached;\n\n const result = await handler(...args);\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\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 * 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// ---------------------------------------------------------------------------------------------------------------------\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\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 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 cached = await this.get(key);\n if (cached !== undefined) {\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 incr(key: string, amount = 1): Promise<number> {\n return this.provider.incr(this.container, key, amount);\n }\n\n public async invalidate(...keys: string[]): Promise<void> {\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 px = this.dateTimeProvider\n .duration(\n ttl ?? this.options.ttl ?? [this.settings.defaultTtl, \"seconds\"],\n )\n .as(\"milliseconds\");\n\n await this.provider.setTyped(this.container, key, value, {\n ttl: px > 0 ? px : undefined,\n compress: this.options.compress,\n });\n }\n\n public async get(key: string): Promise<TReturn | undefined> {\n if (\n !this.alepha.isStarted() ||\n this.options.disabled ||\n !this.settings.enabled\n ) {\n return undefined;\n }\n\n return this.provider.getTyped<TReturn>(this.container, key);\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/**\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\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 type InstantiableClass,\n KIND,\n type MiddlewareMetadata,\n OPTIONS,\n Primitive,\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 cached = await instance.get(key);\n if (cached !== undefined) return cached;\n\n const result = await handler(...args);\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\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 * 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// ---------------------------------------------------------------------------------------------------------------------\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\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 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 cached = await this.get(key);\n if (cached !== undefined) {\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 incr(key: string, amount = 1): Promise<number> {\n return this.provider.incr(this.container, key, amount);\n }\n\n public async invalidate(...keys: string[]): Promise<void> {\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 px = this.dateTimeProvider\n .duration(\n ttl ?? this.options.ttl ?? [this.settings.defaultTtl, \"seconds\"],\n )\n .as(\"milliseconds\");\n\n await this.provider.setTyped(this.container, key, value, {\n ttl: px > 0 ? px : undefined,\n compress: this.options.compress,\n });\n\n await this.alepha.events.emit(\"cache:set\", {\n container: this.container,\n key,\n ttlMs: 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.settings.enabled\n ) {\n return undefined;\n }\n\n const value = await this.provider.getTyped<TReturn>(this.container, key);\n\n await this.alepha.events.emit(\n value === undefined ? \"cache:miss\" : \"cache:hit\",\n { container: this.container, key },\n );\n\n return value;\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/**\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}\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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"}
@@ -448,10 +448,20 @@ var CachePrimitive = class extends Primitive {
448
448
  ttl: px > 0 ? px : void 0,
449
449
  compress: this.options.compress
450
450
  });
451
+ await this.alepha.events.emit("cache:set", {
452
+ container: this.container,
453
+ key,
454
+ ttlMs: px > 0 ? px : void 0
455
+ });
451
456
  }
452
457
  async get(key) {
453
458
  if (!this.alepha.isStarted() || this.options.disabled || !this.settings.enabled) return;
454
- return this.provider.getTyped(this.container, key);
459
+ const value = await this.provider.getTyped(this.container, key);
460
+ await this.alepha.events.emit(value === void 0 ? "cache:miss" : "cache:hit", {
461
+ container: this.container,
462
+ key
463
+ });
464
+ return value;
455
465
  }
456
466
  $provider() {
457
467
  if (!this.options.provider) return this.alepha.inject(CacheProvider);
@@ -1 +1 @@
1
- {"version":3,"file":"index.workerd.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.workerd.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 type InstantiableClass,\n KIND,\n type MiddlewareMetadata,\n OPTIONS,\n Primitive,\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 cached = await instance.get(key);\n if (cached !== undefined) return cached;\n\n const result = await handler(...args);\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\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 * 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// ---------------------------------------------------------------------------------------------------------------------\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\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 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 cached = await this.get(key);\n if (cached !== undefined) {\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 incr(key: string, amount = 1): Promise<number> {\n return this.provider.incr(this.container, key, amount);\n }\n\n public async invalidate(...keys: string[]): Promise<void> {\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 px = this.dateTimeProvider\n .duration(\n ttl ?? this.options.ttl ?? [this.settings.defaultTtl, \"seconds\"],\n )\n .as(\"milliseconds\");\n\n await this.provider.setTyped(this.container, key, value, {\n ttl: px > 0 ? px : undefined,\n compress: this.options.compress,\n });\n }\n\n public async get(key: string): Promise<TReturn | undefined> {\n if (\n !this.alepha.isStarted() ||\n this.options.disabled ||\n !this.settings.enabled\n ) {\n return undefined;\n }\n\n return this.provider.getTyped<TReturn>(this.container, key);\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/**\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 { CloudflareKVProvider } from \"./providers/CloudflareKVProvider.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\nexport const AlephaCache = $module({\n name: \"alepha.cache\",\n primitives: [$cache],\n services: [CacheProvider],\n variants: [MemoryCacheProvider, CloudflareKVProvider],\n register: (alepha) => {\n alepha.with({\n optional: true,\n provide: CacheProvider,\n use: CloudflareKVProvider,\n });\n },\n});\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.workerd.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.workerd.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 type InstantiableClass,\n KIND,\n type MiddlewareMetadata,\n OPTIONS,\n Primitive,\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 cached = await instance.get(key);\n if (cached !== undefined) return cached;\n\n const result = await handler(...args);\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\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 * 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// ---------------------------------------------------------------------------------------------------------------------\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\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 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 cached = await this.get(key);\n if (cached !== undefined) {\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 incr(key: string, amount = 1): Promise<number> {\n return this.provider.incr(this.container, key, amount);\n }\n\n public async invalidate(...keys: string[]): Promise<void> {\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 px = this.dateTimeProvider\n .duration(\n ttl ?? this.options.ttl ?? [this.settings.defaultTtl, \"seconds\"],\n )\n .as(\"milliseconds\");\n\n await this.provider.setTyped(this.container, key, value, {\n ttl: px > 0 ? px : undefined,\n compress: this.options.compress,\n });\n\n await this.alepha.events.emit(\"cache:set\", {\n container: this.container,\n key,\n ttlMs: 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.settings.enabled\n ) {\n return undefined;\n }\n\n const value = await this.provider.getTyped<TReturn>(this.container, key);\n\n await this.alepha.events.emit(\n value === undefined ? \"cache:miss\" : \"cache:hit\",\n { container: this.container, key },\n );\n\n return value;\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/**\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 { CloudflareKVProvider } from \"./providers/CloudflareKVProvider.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\nexport const AlephaCache = $module({\n name: \"alepha.cache\",\n primitives: [$cache],\n services: [CacheProvider],\n variants: [MemoryCacheProvider, CloudflareKVProvider],\n register: (alepha) => {\n alepha.with({\n optional: true,\n provide: CacheProvider,\n use: CloudflareKVProvider,\n });\n },\n});\n"],"mappings":"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"}
@@ -1,9 +1,7 @@
1
1
  import { AppEntryOptions, BuildOptions, DevOptions } from "alepha/cli";
2
- import { Alepha } from "alepha";
2
+ import { Alepha, Service } from "alepha";
3
3
 
4
4
  //#region ../../src/cli/config/defineConfig.d.ts
5
- type AlephaCliConfigPlugin = (config: AlephaCliConfig, alepha: Alepha) => void;
6
- declare const cliConfigPlugins: AlephaCliConfigPlugin[];
7
5
  interface AlephaCliConfig {
8
6
  /**
9
7
  * Override entry paths.
@@ -12,7 +10,11 @@ interface AlephaCliConfig {
12
10
  /**
13
11
  * Register more services to the Alepha CLI (enhancements, commands, etc.).
14
12
  */
15
- services?: Array<any>;
13
+ services?: Array<Service>;
14
+ /**
15
+ * @alias services Register more services to the Alepha CLI (enhancements, commands, etc.).
16
+ */
17
+ plugins?: Array<Service>;
16
18
  /**
17
19
  * Configure Alepha build command.
18
20
  */
@@ -30,5 +32,5 @@ interface AlephaCliConfig {
30
32
  }
31
33
  declare const defineConfig: (config: AlephaCliConfig) => (alepha: Alepha) => {};
32
34
  //#endregion
33
- export { AlephaCliConfig, AlephaCliConfigPlugin, cliConfigPlugins, defineConfig };
35
+ export { AlephaCliConfig, defineConfig };
34
36
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","names":[],"sources":["../../../src/cli/config/defineConfig.ts"],"mappings":";;;;KAYY,qBAAA,IACV,MAAA,EAAQ,eAAA,EACR,MAAA,EAAQ,MAAA;AAAA,cAGG,gBAAA,EAAkB,qBAAA;AAAA,UAId,eAAA;EATgB;;;EAa/B,KAAA,GAAQ,eAAA;EAZR;;;EAiBA,QAAA,GAAW,KAAA;EAhBG;AAGhB;;EAkBE,KAAA,GAAQ,YAAA;EAlBqB;;AAI/B;EAmBE,GAAA,GAAM,UAAA;;;;;;EAON,GAAA,GAAM,MAAA;AAAA;AAAA,cAKK,YAAA,GAAgB,MAAA,EAAQ,eAAA,MAC3B,MAAA,EAAQ,MAAA"}
1
+ {"version":3,"file":"index.d.ts","names":[],"sources":["../../../src/cli/config/defineConfig.ts"],"mappings":";;;;UAYiB,eAAA;;AAAjB;;EAIE,KAAA,GAAQ,eAAA;EAAA;;;EAKR,QAAA,GAAW,KAAA,CAAM,OAAA;EAKP;;;EAAV,OAAA,GAAU,KAAA,CAAM,OAAA;EAiBJ;;;EAZZ,KAAA,GAAQ,YAAA;EAVR;;;EAeA,GAAA,GAAM,UAAA;EAVI;;;;;EAiBV,GAAA,GAAM,MAAA;AAAA;AAAA,cAKK,YAAA,GAAgB,MAAA,EAAQ,eAAA,MAC3B,MAAA,EAAQ,MAAA"}
@@ -1,18 +1,17 @@
1
1
  import { appEntryOptions, buildOptions, devOptions } from "alepha/cli";
2
2
  //#region ../../src/cli/config/defineConfig.ts
3
- const cliConfigPlugins = [];
4
3
  const defineConfig = (config) => {
5
4
  return (alepha) => {
6
5
  if (config.services) for (const it of config.services) alepha.with(it);
6
+ if (config.plugins) for (const it of config.plugins) alepha.with(it);
7
7
  if (config.env) for (const [key, value] of Object.entries(config.env)) process.env[key] = String(value);
8
8
  if (config.build) alepha.set(buildOptions, config.build);
9
9
  if (config.dev) alepha.set(devOptions, config.dev);
10
10
  if (config.entry) alepha.set(appEntryOptions, config.entry);
11
- for (const plugin of cliConfigPlugins) plugin(config, alepha);
12
11
  return {};
13
12
  };
14
13
  };
15
14
  //#endregion
16
- export { cliConfigPlugins, defineConfig };
15
+ export { defineConfig };
17
16
 
18
17
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../../../src/cli/config/defineConfig.ts"],"sourcesContent":["import type { Alepha } from \"alepha\";\nimport {\n type AppEntryOptions,\n appEntryOptions,\n type BuildOptions,\n buildOptions,\n type DevOptions,\n devOptions,\n} from \"alepha/cli\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport type AlephaCliConfigPlugin = (\n config: AlephaCliConfig,\n alepha: Alepha,\n) => void;\n\nexport const cliConfigPlugins: AlephaCliConfigPlugin[] = [];\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport interface AlephaCliConfig {\n /**\n * Override entry paths.\n */\n entry?: AppEntryOptions;\n\n /**\n * Register more services to the Alepha CLI (enhancements, commands, etc.).\n */\n services?: Array<any>;\n\n /**\n * Configure Alepha build command.\n */\n build?: BuildOptions;\n\n /**\n * Configure Alepha dev command.\n */\n dev?: DevOptions;\n\n /**\n * Environment variables to set before running commands.\n *\n * Always use .env files by default, this is only for dynamic values.\n */\n env?: Record<string, unknown>;\n}\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport const defineConfig = (config: AlephaCliConfig) => {\n return (alepha: Alepha) => {\n if (config.services) {\n for (const it of config.services) {\n alepha.with(it);\n }\n }\n\n if (config.env) {\n for (const [key, value] of Object.entries(config.env)) {\n process.env[key] = String(value);\n }\n }\n\n if (config.build) {\n alepha.set(buildOptions, config.build);\n }\n\n if (config.dev) {\n alepha.set(devOptions, config.dev);\n }\n\n if (config.entry) {\n alepha.set(appEntryOptions, config.entry);\n }\n\n for (const plugin of cliConfigPlugins) {\n plugin(config, alepha);\n }\n\n return {};\n };\n};\n"],"mappings":";;AAiBA,MAAa,mBAA4C,EAAE;AAmC3D,MAAa,gBAAgB,WAA4B;AACvD,SAAQ,WAAmB;AACzB,MAAI,OAAO,SACT,MAAK,MAAM,MAAM,OAAO,SACtB,QAAO,KAAK,GAAG;AAInB,MAAI,OAAO,IACT,MAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,OAAO,IAAI,CACnD,SAAQ,IAAI,OAAO,OAAO,MAAM;AAIpC,MAAI,OAAO,MACT,QAAO,IAAI,cAAc,OAAO,MAAM;AAGxC,MAAI,OAAO,IACT,QAAO,IAAI,YAAY,OAAO,IAAI;AAGpC,MAAI,OAAO,MACT,QAAO,IAAI,iBAAiB,OAAO,MAAM;AAG3C,OAAK,MAAM,UAAU,iBACnB,QAAO,QAAQ,OAAO;AAGxB,SAAO,EAAE"}
1
+ {"version":3,"file":"index.js","names":[],"sources":["../../../src/cli/config/defineConfig.ts"],"sourcesContent":["import type { Alepha, Service } from \"alepha\";\nimport {\n type AppEntryOptions,\n appEntryOptions,\n type BuildOptions,\n buildOptions,\n type DevOptions,\n devOptions,\n} from \"alepha/cli\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport interface AlephaCliConfig {\n /**\n * Override entry paths.\n */\n entry?: AppEntryOptions;\n\n /**\n * Register more services to the Alepha CLI (enhancements, commands, etc.).\n */\n services?: Array<Service>;\n\n /**\n * @alias services Register more services to the Alepha CLI (enhancements, commands, etc.).\n */\n plugins?: Array<Service>;\n\n /**\n * Configure Alepha build command.\n */\n build?: BuildOptions;\n\n /**\n * Configure Alepha dev command.\n */\n dev?: DevOptions;\n\n /**\n * Environment variables to set before running commands.\n *\n * Always use .env files by default, this is only for dynamic values.\n */\n env?: Record<string, unknown>;\n}\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport const defineConfig = (config: AlephaCliConfig) => {\n return (alepha: Alepha) => {\n if (config.services) {\n for (const it of config.services) {\n alepha.with(it);\n }\n }\n\n if (config.plugins) {\n for (const it of config.plugins) {\n alepha.with(it);\n }\n }\n\n if (config.env) {\n for (const [key, value] of Object.entries(config.env)) {\n process.env[key] = String(value);\n }\n }\n\n if (config.build) {\n alepha.set(buildOptions, config.build);\n }\n\n if (config.dev) {\n alepha.set(devOptions, config.dev);\n }\n\n if (config.entry) {\n alepha.set(appEntryOptions, config.entry);\n }\n\n return {};\n };\n};\n"],"mappings":";;AAgDA,MAAa,gBAAgB,WAA4B;AACvD,SAAQ,WAAmB;AACzB,MAAI,OAAO,SACT,MAAK,MAAM,MAAM,OAAO,SACtB,QAAO,KAAK,GAAG;AAInB,MAAI,OAAO,QACT,MAAK,MAAM,MAAM,OAAO,QACtB,QAAO,KAAK,GAAG;AAInB,MAAI,OAAO,IACT,MAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,OAAO,IAAI,CACnD,SAAQ,IAAI,OAAO,OAAO,MAAM;AAIpC,MAAI,OAAO,MACT,QAAO,IAAI,cAAc,OAAO,MAAM;AAGxC,MAAI,OAAO,IACT,QAAO,IAAI,YAAY,OAAO,IAAI;AAGpC,MAAI,OAAO,MACT,QAAO,IAAI,iBAAiB,OAAO,MAAM;AAG3C,SAAO,EAAE"}