Vendored
+52
@@ -0,0 +1,52 @@
|
||||
import { CacheInterface } from './types.js';
|
||||
|
||||
export class CacheBase<Key, Value> implements CacheInterface<Key, Value> {
|
||||
private _cache: Map<Key, Value>;
|
||||
|
||||
constructor(cache: Map<Key, Value>) {
|
||||
this._cache = cache;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the cache has a given id.
|
||||
* @param key
|
||||
* @returns `true` if the id is in the cache, `false` otherwise.
|
||||
*/
|
||||
public has(key: Key): boolean {
|
||||
return this._cache.has(key);
|
||||
}
|
||||
|
||||
public entries(): MapIterator<[Key, Value]> {
|
||||
return this._cache.entries();
|
||||
}
|
||||
|
||||
public delete(key: Key): boolean {
|
||||
return this._cache.delete(key);
|
||||
}
|
||||
|
||||
public clear(): void {
|
||||
this._cache.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get resolved media information given an id.
|
||||
* @param key The id.
|
||||
* @returns The `ResolvedMedia` for this id.
|
||||
*/
|
||||
public get(key: Key): Value | null {
|
||||
return this._cache.get(key) ?? null;
|
||||
}
|
||||
|
||||
public getMatches(predicate: (arg: Value) => boolean): Value[] {
|
||||
return [...this._cache.values()].filter(predicate);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a given ResolvedMedia to the cache.
|
||||
* @param key The id for the object.
|
||||
* @param resolvedMedia The `ResolvedMedia` object.
|
||||
*/
|
||||
public set(key: Key, val: Value): void {
|
||||
this._cache.set(key, val);
|
||||
}
|
||||
}
|
||||
Vendored
+7
@@ -0,0 +1,7 @@
|
||||
import { CacheBase } from './base';
|
||||
|
||||
export class Cache<Key, Value> extends CacheBase<Key, Value> {
|
||||
constructor() {
|
||||
super(new Map());
|
||||
}
|
||||
}
|
||||
Vendored
+8
@@ -0,0 +1,8 @@
|
||||
import { CacheBase } from './base';
|
||||
import { EqualityMap } from './equality-map';
|
||||
|
||||
export class EqualityCache<Key, Value> extends CacheBase<Key, Value> {
|
||||
constructor() {
|
||||
super(new EqualityMap());
|
||||
}
|
||||
}
|
||||
Vendored
+80
@@ -0,0 +1,80 @@
|
||||
import { isEqual } from 'lodash-es';
|
||||
|
||||
interface EqualityMapItem<Key, Value> {
|
||||
key: Key;
|
||||
value: Value;
|
||||
}
|
||||
|
||||
/** A simple equality based map. This is not performant and should be used for
|
||||
* small datasets only.
|
||||
*/
|
||||
export class EqualityMap<Key, Value> implements Map<Key, Value> {
|
||||
private _data: EqualityMapItem<Key, Value>[] = [];
|
||||
|
||||
get [Symbol.toStringTag](): string {
|
||||
return 'EqualityMap';
|
||||
}
|
||||
|
||||
public has(key: Key): boolean {
|
||||
return !!this.get(key);
|
||||
}
|
||||
|
||||
public get(searchKey: Key): Value | undefined {
|
||||
for (const pair of this._data) {
|
||||
if (isEqual(pair.key, searchKey)) {
|
||||
return pair.value;
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
public set(key: Key, value: Value): this {
|
||||
this.delete(key);
|
||||
this._data.push({ key, value });
|
||||
return this;
|
||||
}
|
||||
|
||||
public delete(searchKey: Key): boolean {
|
||||
for (let i = 0; i < this._data.length; i++) {
|
||||
if (isEqual(this._data[i].key, searchKey)) {
|
||||
this._data.splice(i, 1);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public clear(): void {
|
||||
this._data = [];
|
||||
}
|
||||
|
||||
public *entries(): MapIterator<[Key, Value]> {
|
||||
for (const pair of this._data) {
|
||||
yield [pair.key, pair.value];
|
||||
}
|
||||
}
|
||||
|
||||
public forEach(
|
||||
callbackfn: (value: Value, key: Key, map: Map<Key, Value>) => void,
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
thisArg?: any,
|
||||
): void {
|
||||
for (const pair of this._data) {
|
||||
callbackfn.call(thisArg, pair.value, pair.key, this);
|
||||
}
|
||||
}
|
||||
|
||||
public get size(): number {
|
||||
return this._data.length;
|
||||
}
|
||||
|
||||
public [Symbol.iterator](): IterableIterator<[Key, Value]> {
|
||||
return this.entries();
|
||||
}
|
||||
public keys(): IterableIterator<Key> {
|
||||
return this._data.map((pair) => pair.key).values();
|
||||
}
|
||||
public values(): IterableIterator<Value> {
|
||||
return this._data.map((pair) => pair.value).values();
|
||||
}
|
||||
}
|
||||
Vendored
+70
@@ -0,0 +1,70 @@
|
||||
import { CacheInterface } from './types';
|
||||
import { EqualityCache } from './equality-cache';
|
||||
|
||||
interface ExpiringValue<Value> {
|
||||
value: Value;
|
||||
expires?: Date;
|
||||
}
|
||||
|
||||
export class ExpiringEqualityCache<Key, Value> implements CacheInterface<Key, Value> {
|
||||
protected _data: EqualityCache<Key, ExpiringValue<Value>> = new EqualityCache();
|
||||
|
||||
public get(key: Key): Value | null {
|
||||
const value = this._data.get(key);
|
||||
const now = new Date();
|
||||
return value && (!value.expires || now <= value.expires) ? value.value : null;
|
||||
}
|
||||
|
||||
public has(key: Key): boolean {
|
||||
return !!this.get(key);
|
||||
}
|
||||
|
||||
public set(key: Key, value: Value, expiry?: Date): void {
|
||||
this._data.set(key, {
|
||||
value: value,
|
||||
expires: expiry,
|
||||
});
|
||||
|
||||
// Clean up old requests on set.
|
||||
this._expireOldValues();
|
||||
}
|
||||
|
||||
public delete(key: Key): boolean {
|
||||
return this._data.delete(key);
|
||||
}
|
||||
|
||||
public clear(): void {
|
||||
this._data.clear();
|
||||
}
|
||||
|
||||
public *entries(): MapIterator<[Key, Value]> {
|
||||
const now = new Date();
|
||||
for (const [key, value] of this._data.entries()) {
|
||||
if (!value.expires || now <= value.expires) {
|
||||
yield [key, value.value];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public getMatches(predicate: (value: Value) => boolean): Value[] {
|
||||
const out: Value[] = [];
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
for (const [_key, value] of this.entries()) {
|
||||
if (predicate(value)) {
|
||||
out.push(value);
|
||||
}
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
protected _expireOldValues(): void {
|
||||
const now = new Date();
|
||||
|
||||
for (const [key, value] of this._data.entries()) {
|
||||
if (value.expires && now > value.expires) {
|
||||
this._data.delete(key);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Vendored
+8
@@ -0,0 +1,8 @@
|
||||
import QuickLRU from 'quick-lru';
|
||||
import { CacheBase } from './base';
|
||||
|
||||
export class LRUCache<Key, Value> extends CacheBase<Key, Value> {
|
||||
constructor(maxSize: number) {
|
||||
super(new QuickLRU({ maxSize }));
|
||||
}
|
||||
}
|
||||
Vendored
+9
@@ -0,0 +1,9 @@
|
||||
export interface CacheInterface<K, V> {
|
||||
has(k: K): boolean;
|
||||
get(k: K): V | null;
|
||||
set(k: K, v: V): void;
|
||||
delete(k: K): boolean;
|
||||
clear(): void;
|
||||
entries(): MapIterator<[K, V]>;
|
||||
getMatches(predicate: (arg: V) => boolean): V[];
|
||||
}
|
||||
Reference in New Issue
Block a user