@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) • 6.11 kB
JavaScript
;
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const atomix_1 = __importDefault(require("@nasriya/atomix"));
class ExtEngines {
#_id = `${Date.now()}_${Math.floor(Math.random() * 100)}`;
#_engines;
constructor(engines) { this.#_engines = engines; }
#_helpers = {
isRedisClient(client) {
return (typeof client === 'object' &&
client !== null &&
typeof client.connect === 'function' &&
typeof client.isOpen === 'boolean');
}
};
/**
* Defines a new external storage engine with the given name and handlers.
*
* @param name - The name of the engine to define.
* @param handlers - An object with the methods "onSet", "onRead", and "onRemove" implemented.
* @returns The name of the newly defined engine.
* @throws TypeError If the arguments are invalid.
* @throws SyntaxError If the handlers argument is missing one of the required methods.
* @throws Error If an engine with the given name already exists.
* @since v1.0.0
*/
defineEngine(name, handlers) {
return this.#_engines.defineEngine(name, handlers);
}
/**
* Configures a Redis-based storage engine with the specified name and client.
*
* @param name - The name of the engine to define.
* @param client - The Redis client instance to be used for the engine.
* @param options - Optional configuration settings for the engine.
* @param options.prefix - An optional prefix to be applied to all keys used by this engine.
* @since v1.0.0
*
* The Redis client is automatically connected if it is not already open.
* If a prefix is provided, it is prepended to record keys when performing operations.
*/
useRedis(name, client, options) {
try {
const configs = {
prefix: undefined,
};
{
// validate the name argument
if (!atomix_1.default.valueIs.string(name)) {
throw new TypeError(`The "name" argument must be a string, but instead got ${typeof name}`);
}
if (name.length === 0) {
throw new RangeError(`The "name" argument must not be an empty string`);
}
if (this.#_engines.preservedNames.includes(name)) {
throw new Error(`The engine name "${name}" is reserved and cannot be used.`);
}
if (this.#_engines.hasEngine(name)) {
throw new Error(`An engine with the name "${name}" is already defined.`);
}
// validate the client argument
if (!this.#_helpers.isRedisClient(client)) {
throw new TypeError(`The "client" argument must be a Redis client instance, but instead got ${typeof client}`);
}
// validate the options argument
if (atomix_1.default.valueIs.record(options)) {
if (atomix_1.default.dataTypes.record.hasOwnProperty(options, 'prefix')) {
if (!atomix_1.default.valueIs.string(options.prefix)) {
throw new TypeError(`The "options.prefix" argument (when provided) must be a string, but instead got ${typeof options.prefix}`);
}
if (options.prefix.length === 0) {
throw new RangeError(`The "options.prefix" argument (when provided) must not be an empty string`);
}
configs.prefix = options.prefix.replace(/:$/, ''); // Ensure prefix does not end with a colon
}
}
}
const connect = async () => {
if (client.isOpen) {
return;
}
await client.connect();
};
const getKey = (record) => {
const base = `${record.flavor}/${record.scope}/${record.key}`;
return `${configs.prefix ?? 'cachify'}:client_${this.#_id}:${base}`;
};
return this.#_engines.defineEngine(name, {
onSet: async (record, value) => {
await connect();
const key = getKey(record);
if (record.flavor === 'files' && Buffer.isBuffer(value)) {
await client.set(key, value);
}
else {
const buffer = atomix_1.default.http.bodyCodec.encode(value);
await client.set(key, buffer);
}
},
onRead: async (record) => {
await connect();
const key = getKey(record);
if (record.flavor === 'files') {
const buffer = await client.sendCommand(['GET', key]);
return buffer === null ? undefined : buffer;
}
else {
const stringifiedBuffer = await client.get(key);
if (stringifiedBuffer === null) {
return undefined;
}
const buffer = Buffer.from(stringifiedBuffer, 'utf-8');
return atomix_1.default.http.bodyCodec.decode(buffer);
}
},
onRemove: async (record) => {
await connect();
await client.del(getKey(record));
}
});
}
catch (error) {
if (error instanceof Error) {
error.message = `Failed to define Redis engine "${name}": ${error.message}`;
}
throw error;
}
}
}
exports.default = ExtEngines;