UNPKG

@solid/community-server

Version:

Community Solid Server: an open and modular implementation of the Solid specifications

107 lines 3.92 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.WrappedExpiringStorage = void 0; const global_logger_factory_1 = require("global-logger-factory"); const InternalServerError_1 = require("../../util/errors/InternalServerError"); const TimerUtil_1 = require("../../util/TimerUtil"); /** * A storage that wraps around another storage and expires resources based on the given (optional) expiry date. * Will delete expired entries when trying to get their value. * Has a timer that will delete all expired data every hour (default value). */ class WrappedExpiringStorage { logger = (0, global_logger_factory_1.getLoggerFor)(this); source; timer; /** * @param source - KeyValueStorage to actually store the data. * @param timeout - How often the expired data needs to be checked in minutes. */ constructor(source, timeout = 60) { this.source = source; this.timer = (0, TimerUtil_1.setSafeInterval)(this.logger, 'Failed to remove expired entries', this.removeExpiredEntries.bind(this), timeout * 60 * 1000); this.timer.unref(); } async get(key) { return this.getUnexpired(key); } async has(key) { return Boolean(await this.getUnexpired(key)); } async set(key, value, expireValue) { const expires = typeof expireValue === 'number' ? new Date(Date.now() + expireValue) : expireValue; if (this.isExpired(expires)) { throw new InternalServerError_1.InternalServerError('Value is already expired'); } await this.source.set(key, this.toExpires(value, expires)); return this; } async delete(key) { return this.source.delete(key); } async *entries() { // Not deleting expired entries here to prevent iterator issues for await (const [key, value] of this.source.entries()) { const { expires, payload } = this.toData(value); if (!this.isExpired(expires)) { yield [key, payload]; } } } /** * Deletes all entries that have expired. */ async removeExpiredEntries() { this.logger.debug('Removing expired entries'); const expired = []; for await (const [key, value] of this.source.entries()) { const { expires } = this.toData(value); if (this.isExpired(expires)) { expired.push(key); } } await Promise.all(expired.map(async (key) => this.source.delete(key))); this.logger.debug('Finished removing expired entries'); } /** * Tries to get the data for the given key. * In case the data exists but has expired, * it will be deleted and `undefined` will be returned instead. */ async getUnexpired(key) { const data = await this.source.get(key); if (!data) { return; } const { expires, payload } = this.toData(data); if (this.isExpired(expires)) { await this.source.delete(key); return; } return payload; } /** * Checks if the given data entry has expired. */ isExpired(expires) { return typeof expires !== 'undefined' && expires < new Date(); } /** * Creates a new object where the `expires` field is a string instead of a Date. */ toExpires(data, expires) { return { expires: expires?.toISOString(), payload: data }; } /** * Creates a new object where the `expires` field is a Date instead of a string. */ toData(expireData) { const result = { payload: expireData.payload }; if (expireData.expires) { result.expires = new Date(expireData.expires); } return result; } } exports.WrappedExpiringStorage = WrappedExpiringStorage; //# sourceMappingURL=WrappedExpiringStorage.js.map