UNPKG

@httpx/lru

Version:

LruCache implementations with O(1) complexity

309 lines (308 loc) 8.08 kB
var DoublyLinkedNode = class { key; prev = null; next = null; constructor(key) { this.key = key; } }; var LruCache = class { #maxSize; #touchOnHas; #onEviction; #cache; #head = null; #tail = null; constructor(params) { const { maxSize, touchOnHas = false, onEviction } = params; if (!Number.isSafeInteger(maxSize) || maxSize < 1) throw new TypeError("Invalid maxSize"); this.#touchOnHas = touchOnHas; this.#onEviction = onEviction; this.#maxSize = maxSize; this.#cache = /* @__PURE__ */ new Map(); } get params() { return { maxSize: this.#maxSize }; } get size() { return this.#cache.size; } clear() { const size = this.#cache.size; this.#cache.clear(); this.#head = this.#tail = null; return size; } has(key, options) { const hasEntry = this.#cache.has(key); if (hasEntry && (options?.touch ?? this.#touchOnHas)) this.#moveToHead(this.#cache.get(key).node); return hasEntry; } set(key, value) { if (this.#cache.has(key)) { const data$1 = this.#cache.get(key); data$1.value = value; this.#moveToHead(data$1.node); return false; } const newNode = new DoublyLinkedNode(key); const data = { value, node: newNode }; this.#cache.set(key, data); this.#moveToHead(newNode); if (this.#cache.size > this.#maxSize) this.#removeTail(); return true; } get(key) { const data = this.#cache.get(key); if (data === void 0) return; this.#moveToHead(data.node); return data.value; } getOrSet(key, valueOrFn) { const val = this.get(key); if (val === void 0) { const value = typeof valueOrFn === "function" ? valueOrFn() : valueOrFn; this.set(key, value); return value; } return val; } peek(key) { return this.#cache.get(key)?.value; } delete(key) { const node = this.#cache.get(key)?.node; if (node === void 0) return false; this.#removeNode(node); return this.#cache.delete(key); } *[Symbol.iterator]() { let current = this.#tail; while (current) { const data = this.#cache.get(current.key); yield [current.key, data.value]; current = current.prev; } } #moveToHead(node) { if (node === this.#head) return; this.#removeNode(node); node.next = this.#head; node.prev = null; if (this.#head) this.#head.prev = node; this.#head = node; this.#tail ??= node; } #removeNode(node) { if (node.prev) node.prev.next = node.next; if (node.next) node.next.prev = node.prev; if (node === this.#head) this.#head = node.next; if (node === this.#tail) this.#tail = node.prev; } #removeTail() { if (this.#tail === null) return; const tailKey = this.#tail.key; this.#removeNode(this.#tail); if (this.#onEviction) this.#onEviction(tailKey, this.#cache.get(tailKey).value); this.#cache.delete(tailKey); } }; const getOrCreateLruCache = (name, lruCacheParams, options) => { globalThis.__httpx_lru_cache_instances ??= /* @__PURE__ */ new Map(); if (!globalThis.__httpx_lru_cache_instances.has(name)) { if (options?.onCreate instanceof Function) options.onCreate(name, lruCacheParams); globalThis.__httpx_lru_cache_instances.set(name, new LruCache(lruCacheParams)); } return globalThis.__httpx_lru_cache_instances.get(name); }; var TimeLruCache = class { #maxSize; #touchOnHas; #ttl; #onEviction; #cache; #head = null; #tail = null; constructor(params) { const { maxSize, touchOnHas = false, onEviction, defaultTTL } = params; if (!Number.isSafeInteger(maxSize) || maxSize < 1) throw new TypeError("Invalid maxSize"); if (!Number.isSafeInteger(defaultTTL) || defaultTTL < 1) throw new TypeError("Invalid ttl"); this.#touchOnHas = touchOnHas; this.#onEviction = onEviction; this.#maxSize = maxSize; this.#ttl = defaultTTL; this.#cache = /* @__PURE__ */ new Map(); } get params() { return { maxSize: this.#maxSize, defaultTTL: this.#ttl }; } get size() { return this.#cache.size; } clear() { const size = this.#cache.size; this.#cache.clear(); this.#head = this.#tail = null; return size; } has(key, options) { const value = this.#cache.get(key); const hasEntry = value !== void 0; if (hasEntry && value.expiry < Date.now()) { if (this.#onEviction) this.#onEviction(key, this.#cache.get(key).value); this.#removeNode(value.node); return false; } if (hasEntry && (options?.touch ?? this.#touchOnHas)) this.#moveToHead(this.#cache.get(key).node); return hasEntry; } set(key, value, ttl) { if (this.#cache.has(key)) { const data$1 = this.#cache.get(key); data$1.value = value; data$1.expiry = Date.now() + (ttl ?? this.#ttl); this.#moveToHead(data$1.node); return false; } const newNode = new DoublyLinkedNode(key); const data = { value, expiry: Date.now() + (ttl ?? this.#ttl), node: newNode }; this.#cache.set(key, data); this.#moveToHead(newNode); if (this.#cache.size > this.#maxSize) this.#removeTail(); return true; } get(key) { const data = this.#cache.get(key); if (data === void 0) return; if (data.expiry < Date.now()) { if (this.#onEviction) this.#onEviction(key, data.value); this.#removeNode(data.node); return; } this.#moveToHead(data.node); return data.value; } getOrSet(key, valueOrFn, ttl) { const val = this.get(key); if (val === void 0) { const value = typeof valueOrFn === "function" ? valueOrFn() : valueOrFn; this.set(key, value, ttl); return value; } return val; } peek(key) { const val = this.#cache.get(key); if (val === void 0) return; return val.expiry < Date.now() ? void 0 : val.value; } delete(key) { const node = this.#cache.get(key)?.node; if (node === void 0) return false; this.#removeNode(node); return this.#cache.delete(key); } *[Symbol.iterator]() { let current = this.#tail; while (current) { const data = this.#cache.get(current.key); yield [current.key, data.value]; current = current.prev; } } #moveToHead(node) { if (node === this.#head) return; this.#removeNode(node); node.next = this.#head; node.prev = null; if (this.#head) this.#head.prev = node; this.#head = node; this.#tail ??= node; } #removeNode(node) { if (node.prev) node.prev.next = node.next; if (node.next) node.next.prev = node.prev; if (node === this.#head) this.#head = node.next; if (node === this.#tail) this.#tail = node.prev; } #removeTail() { if (this.#tail === null) return; const tailKey = this.#tail.key; this.#removeNode(this.#tail); if (this.#onEviction) this.#onEviction(tailKey, this.#cache.get(tailKey).value); this.#cache.delete(tailKey); } }; const getOrCreateTimeLruCache = (name, lruCacheParams, options) => { globalThis.__httpx_time_lru_cache_instances ??= /* @__PURE__ */ new Map(); if (!globalThis.__httpx_time_lru_cache_instances.has(name)) { if (options?.onCreate instanceof Function) options.onCreate(name, lruCacheParams); globalThis.__httpx_time_lru_cache_instances.set(name, new TimeLruCache(lruCacheParams)); } return globalThis.__httpx_time_lru_cache_instances.get(name); }; var NullLruCache = class { constructor(_params) {} size = 0; params = { maxSize: 0 }; clear() { return 0; } has(_key, _options) { return false; } set(_key, _value) { return false; } get(_key) {} getOrSet(_key, valueOrFn) { return typeof valueOrFn === "function" ? valueOrFn() : valueOrFn; } peek(_key) {} delete(_key) { return false; } *[Symbol.iterator]() {} }; var NullTimeLruCache = class { constructor(_params) {} size = 0; params = { maxSize: 0, defaultTTL: 0 }; clear() { return 0; } has(_key, _options) { return false; } set(_key, _value, _ttl) { return false; } get(_key) {} getOrSet(_key, valueOrFn, _ttl) { return typeof valueOrFn === "function" ? valueOrFn() : valueOrFn; } peek(_key) {} delete(_key) { return false; } *[Symbol.iterator]() {} }; exports.LruCache = LruCache; exports.NullLruCache = NullLruCache; exports.NullTimeLruCache = NullTimeLruCache; exports.TimeLruCache = TimeLruCache; exports.getOrCreateLruCache = getOrCreateLruCache; exports.getOrCreateTimeLruCache = getOrCreateTimeLruCache;