UNPKG

@nasriya/cachify

Version:

A lightweight, extensible in-memory caching library for storing anything, with built-in TTL and customizable cache types.

86 lines (85 loc) 4.21 kB
import cachify from "../../cachify.js"; import atomix from "@nasriya/atomix"; import constants from "../consts/consts.js"; class PersistenceProxy { #_manager; constructor(manager) { this.#_manager = manager; } #_helpers = { validate: { backupData: (data) => { if (!atomix.valueIs.record(data)) { throw new TypeError(`The "data" argument must be a record, but instead got ${typeof data}`); } if (atomix.dataTypes.record.hasOwnProperty(data, 'source')) { const source = data.source; if (!atomix.valueIs.string(source)) { throw new TypeError(`The "initiator" property of the "data" object must be a string, but instead got ${typeof source}`); } if (!constants.CACHE_FLAVORS.includes(source)) { throw new RangeError(`The "initiator" property of the "data" object must be one of the following values: ${constants.CACHE_FLAVORS.join(', ')}`); } if (!(source in cachify)) { throw new Error(`The cache flavor "${source}" is not implemented yet by cachify.`); } } else { throw new SyntaxError(`The "initiator" property of the "data" object is required and missing.`); } if (atomix.dataTypes.record.hasOwnProperty(data, 'content')) { const content = data.content; if (!(content instanceof Map)) { throw new TypeError(`The "content" property of the "data" object must be a Map, but instead got ${typeof content}`); } } else { throw new SyntaxError(`The "content" property of the "data" object is required and missing.`); } }, service: (service) => { if (!atomix.valueIs.string(service)) { throw new TypeError(`The "service" argument must be a string, but instead got ${typeof service}`); } if (!constants.BACKUP_STORAGE_SERVICES.includes(service)) { throw new RangeError(`The "service" argument must be one of the following values: ${constants.BACKUP_STORAGE_SERVICES.join(', ')}`); } } } }; async backup(...args) { const [data, to, ...rest] = args; this.#_helpers.validate.backupData(data); this.#_helpers.validate.service(to); const driver = this.#_manager.getDriver(to); if (!driver) { throw new Error(`The provided storage service (${to}) is not implemented.`); } const backupStream = this.#_manager.createBackupStream(); // Pass the stream to the backup() BEFORE writing data const backupPromise = driver.backup( // @ts-ignore - backup method signature is safe for the given storage type `S` data.source, backupStream, ...rest); const writePromise = async () => { for (const [scope, scopeMap] of data.content) { const scopeRecords = Array.from(scopeMap.values()); const promises = scopeRecords.map(record => record.export()); const records = await Promise.all(promises); for (const record of records.filter(i => atomix.valueIs.record(i))) { await backupStream.writeRecord(record); } } await backupStream.close(); }; await Promise.all([backupPromise, writePromise()]); } async restore(...args) { const [flavor, from, ...rest] = args; this.#_helpers.validate.service(from); const driver = this.#_manager.getDriver(from); if (!driver) { throw new Error(`The provided storage service (${from}) is not implemented.`); } // @ts-ignore - backup method signature is safe for the given storage type `S` await driver.restore(flavor, ...rest); } } export default PersistenceProxy;