Source identity
| Field | Recorded value |
|---|---|
| Source type | GitHub source |
| Original record | Janady13/llms-txt-hub / packages/caching/upstash.ts |
| Date | Not specified in source |
| Repository | Janady13/llms-txt-hub |
| Revision | e0f4bf978d4df168369c7601d9c5f5ea89e84377 |
| Branch | main |
| Path or archive member | packages/caching/upstash.ts |
| Original SHA-256 | c1fa0812517f3ba799c0081bb8598c5079f265780a1f1c358ea9e8408ec99f4c |
| Git blob SHA | 1771e1644af568eaac44d1bc1c435c7944f07a72 |
| Reading-copy SHA-256 | c1fa0812517f3ba799c0081bb8598c5079f265780a1f1c358ea9e8408ec99f4c |
Recovered source transcript
Download complete source text ↓102 lines · 2.9 KB
import { Redis } from '@upstash/redis'
import type { CacheInterface, CacheOptions } from './interfaces'
import { keys } from './keys'
/**
* Upstash Redis cache implementation with namespace support
*/
export class UpstashCache implements CacheInterface {
private client: Redis | null = null
private defaultNamespace?: string
constructor(defaultNamespace?: string) {
this.defaultNamespace = defaultNamespace
}
private getClient(): Redis {
if (!this.client) {
const config = keys()
if (!config.KV_REST_API_URL || !config.KV_REST_API_TOKEN) {
throw new Error(
'Upstash Redis configuration is missing. Please set KV_REST_API_URL and KV_REST_API_TOKEN environment variables.'
)
}
this.client = new Redis({
url: config.KV_REST_API_URL,
token: config.KV_REST_API_TOKEN
})
}
return this.client
}
private getNamespacedKey(key: string, namespace?: string): string {
const ns = namespace || this.defaultNamespace
return ns ? `${ns}:${key}` : key
}
async get<T>(key: string): Promise<T | null> {
const client = this.getClient()
const value = await client.get<T>(this.getNamespacedKey(key))
return value
}
async set<T>(key: string, value: T, options?: CacheOptions): Promise<void> {
const client = this.getClient()
const namespacedKey = this.getNamespacedKey(key, options?.namespace)
if (options?.ttl) {
await client.set(namespacedKey, value, { ex: options.ttl })
} else {
await client.set(namespacedKey, value)
}
}
async delete(key: string): Promise<void> {
const client = this.getClient()
await client.del(this.getNamespacedKey(key))
}
async has(key: string): Promise<boolean> {
const client = this.getClient()
const exists = await client.exists(this.getNamespacedKey(key))
return exists === 1
}
async clear(): Promise<void> {
const client = this.getClient()
if (this.defaultNamespace) {
const keys = await client.keys(`${this.defaultNamespace}:*`)
if (keys.length > 0) {
await client.del(...keys)
}
} else {
await client.flushall()
}
}
async mget<T>(keys: string[]): Promise<(T | null)[]> {
const client = this.getClient()
const namespacedKeys = keys.map(key => this.getNamespacedKey(key))
const values = await client.mget<T[]>(...namespacedKeys)
return values
}
async mset<T>(entries: [string, T][], options?: CacheOptions): Promise<void> {
const client = this.getClient()
const namespacedEntries = entries.map(([key, value]) => [
this.getNamespacedKey(key, options?.namespace),
value
])
// Convert array of tuples to Record<string, unknown>
const record = Object.fromEntries(namespacedEntries)
await client.mset(record)
if (options?.ttl) {
await Promise.all(
namespacedEntries.map(([key]) => client.expire(key as string, options.ttl as number))
)
}
}
}