|
1 | 1 | import { createHash } from 'node:crypto'; |
| 2 | +import { stringify } from 'safe-stable-stringify'; |
2 | 3 |
|
3 | 4 | interface CacheInterface<T> { |
4 | 5 | hasKey(key: string): boolean; |
@@ -81,17 +82,23 @@ export class Cache<T> implements CacheInterface<T> { |
81 | 82 | * @returns The corresponding entry to the provided key if it is still valid, returns `undefined` otherwise. |
82 | 83 | */ |
83 | 84 | get(key: string | undefined): T | undefined { |
84 | | - const entry = key ? this.cache.get(key) : undefined; |
85 | | - if (entry) { |
86 | | - if (isExpired(entry)) { |
87 | | - this.cache.delete(key!); |
88 | | - return undefined; |
89 | | - } |
90 | | - // LRU cache: Move accessed entry to the end of the Map to mark it as recently used |
91 | | - if (this.maxSize !== Infinity) { |
92 | | - this.cache.delete(key!); |
93 | | - this.cache.set(key!, entry); |
94 | | - } |
| 85 | + if (!key) { |
| 86 | + return undefined; |
| 87 | + } |
| 88 | + const entry = this.cache.get(key); |
| 89 | + if (!entry) { |
| 90 | + return undefined; |
| 91 | + } |
| 92 | + |
| 93 | + if (isExpired(entry)) { |
| 94 | + this.cache.delete(key!); |
| 95 | + return undefined; |
| 96 | + } |
| 97 | + |
| 98 | + // LRU cache: Move accessed entry to the end of the Map to mark it as recently used |
| 99 | + if (this.maxSize !== Infinity) { |
| 100 | + this.cache.delete(key!); |
| 101 | + this.cache.set(key!, entry); |
95 | 102 | } |
96 | 103 | return entry?.entry; |
97 | 104 | } |
@@ -149,54 +156,14 @@ export class Cache<T> implements CacheInterface<T> { |
149 | 156 | } |
150 | 157 | } |
151 | 158 |
|
152 | | -/** |
153 | | - * Stringifies a given object in a deterministic way, so that the same objects always yield the same string representation. |
154 | | - * @param toSerialize - The object to stringify. |
155 | | - * @returns A string representation of the given object. |
156 | | - */ |
157 | | -function stringifyJsonSafe(toSerialize: Record<string, unknown>): string { |
158 | | - return JSON.stringify(toSerialize, (_key, value) => { |
159 | | - if (value instanceof Map) { |
160 | | - return [ |
161 | | - ['dataType', 'Map'], |
162 | | - ['value', Array.from(value.entries()).sort()] |
163 | | - ]; |
164 | | - } |
165 | | - if (value instanceof Set) { |
166 | | - return [ |
167 | | - ['dataType', 'Set'], |
168 | | - ['value', Array.from(value.values()).sort()] |
169 | | - ]; |
170 | | - } |
171 | | - if (typeof value === 'function') { |
172 | | - return [ |
173 | | - ['dataType', 'Function'], |
174 | | - ['value', value.toString()] |
175 | | - ]; |
176 | | - } |
177 | | - if (value instanceof Object && !Array.isArray(value)) { |
178 | | - return [ |
179 | | - ['dataType', 'Object'], |
180 | | - [ |
181 | | - 'value', |
182 | | - Object.entries(value).sort(([keyA], [keyB]) => |
183 | | - keyA.localeCompare(keyB) |
184 | | - ) |
185 | | - ] |
186 | | - ]; |
187 | | - } |
188 | | - return value; |
189 | | - }); |
190 | | -} |
191 | | - |
192 | 159 | /** |
193 | 160 | * Hashes the given value to create a cache key. |
194 | 161 | * @internal |
195 | 162 | * @param value - The value to hash. |
196 | 163 | * @returns A hash of the given value using a cryptographic hash function. |
197 | 164 | */ |
198 | 165 | export function hashCacheKey(value: Record<string, unknown>): string { |
199 | | - const serialized = stringifyJsonSafe(value); |
| 166 | + const serialized = stringify(value); |
200 | 167 | return createHash('blake2s256').update(serialized).digest('hex'); |
201 | 168 | } |
202 | 169 |
|
|
0 commit comments