UNPKG

@nasriya/cachify

Version:

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

134 lines (133 loc) 5.5 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); const stream_1 = require("stream"); const events_1 = require("events"); const EncryptStream_1 = __importDefault(require("./streams/EncryptStream")); const helpers_1 = __importDefault(require("./helpers")); class BackupStream { #_stream; #_encryptStream; #_errorHandler = (err) => this.#_userErrorHandler?.(err); #_userErrorHandler; #_closed = false; constructor() { // Create a Readable in "object mode" to allow pushing strings easily this.#_stream = new stream_1.Readable({ read() { }, encoding: 'utf-8', objectMode: false }); this.#_stream.on('error', this.#_errorHandler); const encryptionKey = helpers_1.default.getEncryptionKey(); if (encryptionKey) { this.#_encryptStream = new EncryptStream_1.default(encryptionKey); this.#_encryptStream.on('error', this.#_errorHandler); } // Push initial header lines this.#_controller.push(`CACHE_BACKUP v1\n`); this.#_controller.push(`CREATED_AT ${new Date().toISOString()}\n`); } #_controller = { push: (chunk) => { if (this.#_closed) { throw new Error("Cannot push after stream is closed"); } // Push data to the readable stream // Return false if the internal buffer is full (backpressure) const ok = this.#_stream.push(chunk); return ok; }, writeRecordSync: (record) => { const json = JSON.stringify(record); return this.#_controller.push(`RECORD ${json}\n`); } }; /** * Writes a record to the persistence stream asynchronously. * * This method is asynchronous to allow for backpressure handling. * If the internal buffer is full (i.e. the writable stream is not consuming data as fast as `writeRecord` is being called) * then this method will await the 'drain' event on the readable stream before returning. * * If the stream is already closed, this method does nothing and returns false. * @param record - The record to write to the persistence. * @returns A promise that resolves with a boolean indicating whether the write was successful. * @since v1.0.0 */ async writeRecord(record) { const ok = this.#_controller.writeRecordSync(record); if (!ok) { await (0, events_1.once)(this.#_stream, 'drain'); } return ok; } /** * Closes the persistence stream. * * This method is called when the persistence stream is finished writing records. * It will write a footer line to the stream and then signal the end of the stream. * If the internal buffer is full (i.e. the writable stream is not consuming data as fast as `writeRecord` is being called) * then this method will await the 'drain' event on the readable stream before returning. * @since v1.0.0 */ async close() { if (this.#_closed) { return; } if (!this.#_controller.push(`END_BACKUP\n`)) { await (0, events_1.once)(this.#_stream, 'drain'); } this.#_closed = true; this.#_stream.push(null); // Signal end of stream } /** * Sets the error handler for the persistence stream. * * If an error occurs on the readable or writable stream (i.e. when writing to the stream or when the stream is piped to a destination), * then the handler will be called with the error as an argument. * If the handler is not set, then errors will be thrown. * @param handler - The error handler to call when an error occurs. * @throws {TypeError} If the provided handler is not a function. * @since v1.0.0 */ #onError(handler) { if (typeof handler !== 'function') { throw new TypeError(`The provided handler (${handler}) is not a function.`); } this.#_userErrorHandler = handler; } /** * Pipes the internal readable stream to the provided writable stream. * * This method returns a promise that resolves when the writable stream is finished consuming data. * If an error occurs on either the readable or writable stream, then the promise will reject with the error. * The writable stream is destroyed when an error occurs. * * @param dest - The writable stream to pipe the internal readable stream to. * @returns A promise that resolves when the writable stream is finished consuming data. * @since v1.0.0 */ async streamTo(dest) { return new Promise((resolve, reject) => { const handleError = (err) => { dest.destroy(); this.#_encryptStream?.destroy(); this.#_stream.destroy(); reject(err); }; this.#onError(handleError); dest.on('finish', resolve); dest.on('error', handleError); let current = this.#_stream; if (this.#_encryptStream) { current.pipe(this.#_encryptStream); current = this.#_encryptStream; } current.pipe(dest); }); } } exports.default = BackupStream;