UNPKG

@remotion/gif

Version:

Embed GIFs in a Remotion video

283 lines (271 loc) β€’ 8.65 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.QuickLRU = void 0; class QuickLRU { /** The maximum number of milliseconds an item should remain in the cache. @default Infinity By default, `maxAge` will be `Infinity`, which means that items will never expire. Lazy expiration upon the next write or read call. Individual expiration of an item can be specified by the `set(key, value, maxAge)` method. */ maxAge; /** The maximum number of items before evicting the least recently used items. */ maxSize; /** Called right before an item is evicted from the cache. Useful for side effects or for items like object URLs that need explicit cleanup (`revokeObjectURL`). */ onEviction; _size; cache; oldCache; /** Simple ["Least Recently Used" (LRU) cache](https://en.m.wikipedia.org/wiki/Cache_replacement_policies#Least_Recently_Used_.28LRU.29). The instance is an [`Iterable`](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Iteration_protocols) of `[key, value]` pairs so you can use it directly in a [`for…of`](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Statements/for...of) loop. @example ``` import { QuickLRU } from 'quick-lru-ts'; const lru = new QuickLRU({maxSize: 1000}); lru.set('πŸ¦„', '🌈'); lru.has('πŸ¦„'); //=> true lru.get('πŸ¦„'); //=> '🌈' ``` */ constructor(options) { if (!(options.maxSize && options.maxSize > 0)) { throw new TypeError('`maxSize` must be a number greater than 0'); } if (typeof options.maxAge === 'number' && options.maxAge === 0) { throw new TypeError('`maxAge` must be a number greater than 0'); } this.maxSize = options.maxSize; this.maxAge = options.maxAge || Number.POSITIVE_INFINITY; this.onEviction = options.onEviction; this.cache = new Map(); this.oldCache = new Map(); this._size = 0; } _emitEvictions(cache) { if (typeof this.onEviction !== 'function') { return; } for (const [key, item] of cache) { this.onEviction(key, item.value); } } _deleteIfExpired(key, item) { if (item === undefined) return true; if (typeof item.expiry === 'number' && item.expiry <= Date.now()) { if (typeof this.onEviction === 'function') { this.onEviction(key, item.value); } return this.delete(key); } return false; } _getOrDeleteIfExpired(key, item) { const deleted = this._deleteIfExpired(key, item); if (deleted === false) { return item.value; } } _getItemValue(key, item) { if (item === undefined) return undefined; return item.expiry ? this._getOrDeleteIfExpired(key, item) : item.value; } _peek(key, cache) { const item = cache.get(key); return this._getItemValue(key, item); } _set(key, value) { this.cache.set(key, value); this._size++; if (this._size >= this.maxSize) { this._size = 0; this._emitEvictions(this.oldCache); this.oldCache = this.cache; this.cache = new Map(); } } _moveToRecent(key, item) { this.oldCache.delete(key); this._set(key, item); } *_entriesAscending() { for (const item of this.oldCache) { const [key, value] = item; if (!this.cache.has(key)) { const deleted = this._deleteIfExpired(key, value); if (deleted === false) { yield item; } } } for (const item of this.cache) { const [key, value] = item; const deleted = this._deleteIfExpired(key, value); if (deleted === false) { yield item; } } } /** Get an item. @returns The stored item or `undefined`. */ get(key) { if (this.cache.has(key)) { const item = this.cache.get(key); return this._getItemValue(key, item); } if (this.oldCache.has(key)) { const item = this.oldCache.get(key); if (this._deleteIfExpired(key, item) === false) { this._moveToRecent(key, item); return item.value; } } } set(key, value, { maxAge = this.maxAge } = {}) { const expiry = typeof maxAge === 'number' && maxAge !== Number.POSITIVE_INFINITY ? Date.now() + maxAge : undefined; if (this.cache.has(key)) { this.cache.set(key, { value, expiry, }); } else { this._set(key, { value, expiry }); } } has(key) { if (this.cache.has(key)) { return !this._deleteIfExpired(key, this.cache.get(key)); } if (this.oldCache.has(key)) { return !this._deleteIfExpired(key, this.oldCache.get(key)); } return false; } peek(key) { if (this.cache.has(key)) { return this._peek(key, this.cache); } if (this.oldCache.has(key)) { return this._peek(key, this.oldCache); } } delete(key) { const deleted = this.cache.delete(key); if (deleted) { this._size--; } return this.oldCache.delete(key) || deleted; } clear() { this.cache.clear(); this.oldCache.clear(); this._size = 0; } resize(maxSize) { if (!(maxSize && maxSize > 0)) { throw new TypeError('`maxSize` must be a number greater than 0'); } const items = [...this._entriesAscending()]; const removeCount = items.length - maxSize; if (removeCount < 0) { this.cache = new Map(items); this.oldCache = new Map(); this._size = items.length; } else { if (removeCount > 0) { this._emitEvictions(items.slice(0, removeCount)); } this.oldCache = new Map(items.slice(removeCount)); this.cache = new Map(); this._size = 0; } this.maxSize = maxSize; } *keys() { for (const [key] of this) { yield key; } } *values() { for (const [, value] of this) { yield value; } } *[Symbol.iterator]() { for (const item of this.cache) { const [key, value] = item; const deleted = this._deleteIfExpired(key, value); if (deleted === false) { yield [key, value.value]; } } for (const item of this.oldCache) { const [key, value] = item; if (!this.cache.has(key)) { const deleted = this._deleteIfExpired(key, value); if (deleted === false) { yield [key, value.value]; } } } } /** Iterable for all entries, starting with the newest (descending in recency). */ *entriesDescending() { let items = [...this.cache]; for (let i = items.length - 1; i >= 0; --i) { const item = items[i]; const [key, value] = item; const deleted = this._deleteIfExpired(key, value); if (deleted === false) { yield [key, value.value]; } } items = [...this.oldCache]; for (let i = items.length - 1; i >= 0; --i) { const item = items[i]; const [key, value] = item; if (!this.cache.has(key)) { const deleted = this._deleteIfExpired(key, value); if (deleted === false) { yield [key, value.value]; } } } } *entriesAscending() { for (const [key, value] of this._entriesAscending()) { yield [key, value.value]; } } get size() { if (!this._size) { return this.oldCache.size; } let oldCacheSize = 0; for (const key of this.oldCache.keys()) { if (!this.cache.has(key)) { oldCacheSize++; } } return Math.min(this._size + oldCacheSize, this.maxSize); } } exports.QuickLRU = QuickLRU;