Index: frontend/node_modules/keyv/src/index.d.ts
===================================================================
--- frontend/node_modules/keyv/src/index.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/keyv/src/index.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,112 @@
+import {EventEmitter} from 'events';
+
+type WithRequiredProperties<T, K extends keyof T> = T & Required<Pick<T, K>>;
+
+declare class Keyv<Value = any, Options extends Record<string, any> = Record<string, unknown>> extends EventEmitter {
+	/**
+     * `this.opts` is an object containing at least the properties listed
+     * below. However, `Keyv.Options` allows arbitrary properties as well.
+     * These properties can be specified as the second type parameter to `Keyv`.
+     */
+	opts: WithRequiredProperties<
+	Keyv.Options<Value>,
+	'deserialize' | 'namespace' | 'serialize' | 'store' | 'uri'
+	> &
+	Options;
+
+	/**
+     * @param opts The options object is also passed through to the storage adapter. Check your storage adapter docs for any extra options.
+     */
+	constructor(options?: Keyv.Options<Value> & Options);
+	/**
+     * @param uri The connection string URI.
+     *
+     * Merged into the options object as options.uri.
+     * @param opts The options object is also passed through to the storage adapter. Check your storage adapter docs for any extra options.
+     */
+	constructor(uri?: string, options?: Keyv.Options<Value> & Options);
+
+	/** Returns the value. */
+	get(key: string, options?: {raw?: false}): Promise<Value | undefined>;
+	/** Returns the raw value. */
+	get(key: string, options: {raw: true}): Promise<Keyv.DeserializedData<Value> | undefined>;
+
+	/** Returns an array of values. Uses `store.getMany` if it exists, otherwise uses parallel calls to `store.get`. */
+	get(key: string[], options?: {raw?: false}): Promise<Array<Value | undefined>>;
+	/** Returns an array of raw values. Uses `store.getMany` if it exists, otherwise uses parallel calls to `store.get`. */
+	get(key: string[], options: {raw: true}): Promise<Array<Keyv.DeserializedData<Value> | undefined>>;
+
+	/**
+     * Set a value.
+     *
+     * By default keys are persistent. You can set an expiry TTL in milliseconds.
+     */
+	set(key: string, value: Value, ttl?: number): Promise<true>;
+	/**
+     * Deletes an entry.
+     *
+     * Returns `true` if the key existed, `false` if not.
+     */
+	delete(key: string | string[]): Promise<boolean>;
+	/** Delete all entries in the current namespace. */
+	clear(): Promise<void>;
+	/** Check if key exists in current namespace. */
+	has(key: string): Promise<boolean>;
+	/** Iterator */
+	iterator(namespace?: string): AsyncGenerator<any, void, any>;
+	/**
+	 * Closes the connection.
+	 *
+	 * Returns `undefined` when the connection closes.
+	 */
+	disconnect(): Promise<void>;
+}
+
+declare namespace Keyv {
+	interface Options<Value> {
+		[key: string]: any;
+
+		/** Namespace for the current instance. */
+		namespace?: string | undefined;
+		/** A custom serialization function. */
+		serialize?: ((data: DeserializedData<Value>) => string) | undefined;
+		/** A custom deserialization function. */
+		deserialize?: ((data: string) => DeserializedData<Value> | undefined) | undefined;
+		/** The connection string URI. */
+		uri?: string | undefined;
+		/** The storage adapter instance to be used by Keyv. */
+		store?: Store<string | undefined> | undefined;
+		/** Default TTL. Can be overridden by specififying a TTL on `.set()`. */
+		ttl?: number | undefined;
+		/** Specify an adapter to use. e.g `'redis'` or `'mongodb'`. */
+		adapter?: 'redis' | 'mongodb' | 'mongo' | 'sqlite' | 'postgresql' | 'postgres' | 'mysql' | undefined;
+		/** Enable compression option **/
+		compression?: CompressionAdapter | undefined;
+	}
+
+	interface CompressionAdapter {
+		compress(value: any, options?: any): Promise<any>;
+		decompress(value: any, options?: any): Promise<any>;
+		serialize(value: any): Promise<any>;
+		deserialize(value: any): Promise<any>;
+	}
+
+	interface DeserializedData<Value> {
+		value: Value; expires: number | undefined;
+	}
+
+	type StoredData<Value> = DeserializedData<Value> | string | undefined;
+
+	interface Store<Value> {
+		get(key: string): Value | Promise<Value | undefined> | undefined;
+		set(key: string, value: Value, ttl?: number): any;
+		delete(key: string): boolean | Promise<boolean>;
+		clear(): void | Promise<void>;
+		has?(key: string): boolean | Promise<boolean>;
+		getMany?(
+			keys: string[]
+		): Array<StoredData<Value>> | Promise<Array<StoredData<Value>>> | undefined;
+	}
+}
+
+export = Keyv;
Index: frontend/node_modules/keyv/src/index.js
===================================================================
--- frontend/node_modules/keyv/src/index.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/keyv/src/index.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,259 @@
+'use strict';
+
+const EventEmitter = require('events');
+const JSONB = require('json-buffer');
+
+const loadStore = options => {
+	const adapters = {
+		redis: '@keyv/redis',
+		rediss: '@keyv/redis',
+		mongodb: '@keyv/mongo',
+		mongo: '@keyv/mongo',
+		sqlite: '@keyv/sqlite',
+		postgresql: '@keyv/postgres',
+		postgres: '@keyv/postgres',
+		mysql: '@keyv/mysql',
+		etcd: '@keyv/etcd',
+		offline: '@keyv/offline',
+		tiered: '@keyv/tiered',
+	};
+	if (options.adapter || options.uri) {
+		const adapter = options.adapter || /^[^:+]*/.exec(options.uri)[0];
+		return new (require(adapters[adapter]))(options);
+	}
+
+	return new Map();
+};
+
+const iterableAdapters = [
+	'sqlite',
+	'postgres',
+	'mysql',
+	'mongo',
+	'redis',
+	'tiered',
+];
+
+class Keyv extends EventEmitter {
+	constructor(uri, {emitErrors = true, ...options} = {}) {
+		super();
+		this.opts = {
+			namespace: 'keyv',
+			serialize: JSONB.stringify,
+			deserialize: JSONB.parse,
+			...((typeof uri === 'string') ? {uri} : uri),
+			...options,
+		};
+
+		if (!this.opts.store) {
+			const adapterOptions = {...this.opts};
+			this.opts.store = loadStore(adapterOptions);
+		}
+
+		if (this.opts.compression) {
+			const compression = this.opts.compression;
+			this.opts.serialize = compression.serialize.bind(compression);
+			this.opts.deserialize = compression.deserialize.bind(compression);
+		}
+
+		if (typeof this.opts.store.on === 'function' && emitErrors) {
+			this.opts.store.on('error', error => this.emit('error', error));
+		}
+
+		this.opts.store.namespace = this.opts.namespace;
+
+		const generateIterator = iterator => async function * () {
+			for await (const [key, raw] of typeof iterator === 'function'
+				? iterator(this.opts.store.namespace)
+				: iterator) {
+				const data = await this.opts.deserialize(raw);
+				if (this.opts.store.namespace && !key.includes(this.opts.store.namespace)) {
+					continue;
+				}
+
+				if (typeof data.expires === 'number' && Date.now() > data.expires) {
+					this.delete(key);
+					continue;
+				}
+
+				yield [this._getKeyUnprefix(key), data.value];
+			}
+		};
+
+		// Attach iterators
+		if (typeof this.opts.store[Symbol.iterator] === 'function' && this.opts.store instanceof Map) {
+			this.iterator = generateIterator(this.opts.store);
+		} else if (typeof this.opts.store.iterator === 'function' && this.opts.store.opts
+			&& this._checkIterableAdaptar()) {
+			this.iterator = generateIterator(this.opts.store.iterator.bind(this.opts.store));
+		}
+	}
+
+	_checkIterableAdaptar() {
+		return iterableAdapters.includes(this.opts.store.opts.dialect)
+			|| iterableAdapters.findIndex(element => this.opts.store.opts.url.includes(element)) >= 0;
+	}
+
+	_getKeyPrefix(key) {
+		return `${this.opts.namespace}:${key}`;
+	}
+
+	_getKeyPrefixArray(keys) {
+		return keys.map(key => `${this.opts.namespace}:${key}`);
+	}
+
+	_getKeyUnprefix(key) {
+		return key
+			.split(':')
+			.splice(1)
+			.join(':');
+	}
+
+	get(key, options) {
+		const {store} = this.opts;
+		const isArray = Array.isArray(key);
+		const keyPrefixed = isArray ? this._getKeyPrefixArray(key) : this._getKeyPrefix(key);
+		if (isArray && store.getMany === undefined) {
+			const promises = [];
+			for (const key of keyPrefixed) {
+				promises.push(Promise.resolve()
+					.then(() => store.get(key))
+					.then(data => (typeof data === 'string') ? this.opts.deserialize(data) : (this.opts.compression ? this.opts.deserialize(data) : data))
+					.then(data => {
+						if (data === undefined || data === null) {
+							return undefined;
+						}
+
+						if (typeof data.expires === 'number' && Date.now() > data.expires) {
+							return this.delete(key).then(() => undefined);
+						}
+
+						return (options && options.raw) ? data : data.value;
+					}),
+				);
+			}
+
+			return Promise.allSettled(promises)
+				.then(values => {
+					const data = [];
+					for (const value of values) {
+						data.push(value.value);
+					}
+
+					return data;
+				});
+		}
+
+		return Promise.resolve()
+			.then(() => isArray ? store.getMany(keyPrefixed) : store.get(keyPrefixed))
+			.then(data => (typeof data === 'string') ? this.opts.deserialize(data) : (this.opts.compression ? this.opts.deserialize(data) : data))
+			.then(data => {
+				if (data === undefined || data === null) {
+					return undefined;
+				}
+
+				if (isArray) {
+					return data.map((row, index) => {
+						if ((typeof row === 'string')) {
+							row = this.opts.deserialize(row);
+						}
+
+						if (row === undefined || row === null) {
+							return undefined;
+						}
+
+						if (typeof row.expires === 'number' && Date.now() > row.expires) {
+							this.delete(key[index]).then(() => undefined);
+							return undefined;
+						}
+
+						return (options && options.raw) ? row : row.value;
+					});
+				}
+
+				if (typeof data.expires === 'number' && Date.now() > data.expires) {
+					return this.delete(key).then(() => undefined);
+				}
+
+				return (options && options.raw) ? data : data.value;
+			});
+	}
+
+	set(key, value, ttl) {
+		const keyPrefixed = this._getKeyPrefix(key);
+		if (typeof ttl === 'undefined') {
+			ttl = this.opts.ttl;
+		}
+
+		if (ttl === 0) {
+			ttl = undefined;
+		}
+
+		const {store} = this.opts;
+
+		return Promise.resolve()
+			.then(() => {
+				const expires = (typeof ttl === 'number') ? (Date.now() + ttl) : null;
+				if (typeof value === 'symbol') {
+					this.emit('error', 'symbol cannot be serialized');
+				}
+
+				value = {value, expires};
+				return this.opts.serialize(value);
+			})
+			.then(value => store.set(keyPrefixed, value, ttl))
+			.then(() => true);
+	}
+
+	delete(key) {
+		const {store} = this.opts;
+		if (Array.isArray(key)) {
+			const keyPrefixed = this._getKeyPrefixArray(key);
+			if (store.deleteMany === undefined) {
+				const promises = [];
+				for (const key of keyPrefixed) {
+					promises.push(store.delete(key));
+				}
+
+				return Promise.allSettled(promises)
+					.then(values => values.every(x => x.value === true));
+			}
+
+			return Promise.resolve()
+				.then(() => store.deleteMany(keyPrefixed));
+		}
+
+		const keyPrefixed = this._getKeyPrefix(key);
+		return Promise.resolve()
+			.then(() => store.delete(keyPrefixed));
+	}
+
+	clear() {
+		const {store} = this.opts;
+		return Promise.resolve()
+			.then(() => store.clear());
+	}
+
+	has(key) {
+		const keyPrefixed = this._getKeyPrefix(key);
+		const {store} = this.opts;
+		return Promise.resolve()
+			.then(async () => {
+				if (typeof store.has === 'function') {
+					return store.has(keyPrefixed);
+				}
+
+				const value = await store.get(keyPrefixed);
+				return value !== undefined;
+			});
+	}
+
+	disconnect() {
+		const {store} = this.opts;
+		if (typeof store.disconnect === 'function') {
+			return store.disconnect();
+		}
+	}
+}
+
+module.exports = Keyv;
