UNPKG

@enbox/dids

Version:
163 lines (143 loc) 5.2 kB
import type { AbstractLevel } from 'abstract-level'; import ms from 'ms'; import { Level } from 'level'; import type { DidResolutionResult } from '../types/did-core.js'; import type { DidResolverCache } from '../types/did-resolution.js'; /** * Configuration parameters for creating a LevelDB-based cache for DID resolution results. * * Allows customization of the underlying database instance, storage location, and cache * time-to-live (TTL) settings. */ export type DidResolverCacheLevelParams = { /** * Optional. An instance of `AbstractLevel` to use as the database. If not provided, a new * LevelDB instance will be created at the specified `location`. */ db?: AbstractLevel<string | Buffer | Uint8Array, string, string>; /** * Optional. The file system path or IndexedDB name where the LevelDB store will be created. * Defaults to 'DATA/DID_RESOLVERCACHE' if not specified. */ location?: string; /** * Optional. The time-to-live for cache entries, expressed as a string (e.g., '1h', '15m'). * Determines how long a cache entry should remain valid before being considered expired. Defaults * to '15m' if not specified. */ ttl?: string; } /** * Encapsulates a DID resolution result along with its expiration information for caching purposes. * * This type is used internally by the `DidResolverCacheLevel` to store DID resolution results * with an associated time-to-live (TTL) value. The TTL is represented in milliseconds and * determines when the cached entry is considered expired and eligible for removal. */ type CachedDidResolutionResult = { /** * The expiration time of the cache entry in milliseconds since the Unix epoch. * * This value is used to calculate whether the cached entry is still valid or has expired. */ ttlMillis: number; /** * The DID resolution result being cached. * * This object contains the resolved DID document and associated metadata. */ value: DidResolutionResult; } /** * A Level-based cache implementation for storing and retrieving DID resolution results. * * This cache uses LevelDB for storage, allowing data persistence across process restarts or * browser refreshes. It's suitable for both Node.js and browser environments. * * @remarks * The LevelDB cache keeps data in memory for fast access and also writes to the filesystem in * Node.js or indexedDB in browsers. Time-to-live (TTL) for cache entries is configurable. * * @example * ``` * const cache = new DidResolverCacheLevel({ ttl: '15m' }); * ``` */ export class DidResolverCacheLevel implements DidResolverCache { /** The underlying LevelDB store used for caching. */ protected cache; /** The time-to-live for cache entries in milliseconds. */ protected ttl: number; constructor({ db, location = 'DATA/DID_RESOLVERCACHE', ttl = '15m' }: DidResolverCacheLevelParams = {}) { this.cache = db ?? new Level<string, string>(location); this.ttl = ms(ttl); } /** * Retrieves a DID resolution result from the cache. * * If the cached item has exceeded its TTL, it's scheduled for deletion and undefined is returned. * * @param did - The DID string used as the key for retrieving the cached result. * @returns The cached DID resolution result or undefined if not found or expired. */ async get(did: string): Promise<DidResolutionResult | void> { try { const str = await this.cache.get(did); const cachedDidResolutionResult: CachedDidResolutionResult = JSON.parse(str); if (Date.now() >= cachedDidResolutionResult.ttlMillis) { // defer deletion to be called in the next tick of the js event loop this.cache.nextTick(() => this.cache.del(did)); return; } else { return cachedDidResolutionResult.value; } } catch(error: any) { // Don't throw when a key wasn't found. if (error.notFound) { return; } throw error; } } /** * Stores a DID resolution result in the cache with a TTL. * * @param did - The DID string used as the key for storing the result. * @param value - The DID resolution result to be cached. * @returns A promise that resolves when the operation is complete. */ set(did: string, value: DidResolutionResult): Promise<void> { const cachedDidResolutionResult: CachedDidResolutionResult = { ttlMillis: Date.now() + this.ttl, value }; const str = JSON.stringify(cachedDidResolutionResult); return this.cache.put(did, str); } /** * Deletes a DID resolution result from the cache. * * @param did - The DID string used as the key for deletion. * @returns A promise that resolves when the operation is complete. */ delete(did: string): Promise<void> { return this.cache.del(did); } /** * Clears all entries from the cache. * * @returns A promise that resolves when the operation is complete. */ clear(): Promise<void> { return this.cache.clear(); } /** * Closes the underlying LevelDB store. * * @returns A promise that resolves when the store is closed. */ close(): Promise<void> { return this.cache.close(); } }