UNPKG

bentocache

Version:

Multi-tier cache module for Node.js. Redis, Upstash, CloudfareKV, File, in-memory and others drivers

209 lines (206 loc) 6.21 kB
import { CacheBusMessageType } from "../../chunk-HVHH5I26.js"; import { BaseDriver } from "../../chunk-BO75WXSS.js"; // src/drivers/redis.ts import { Redis as IoRedis } from "ioredis"; import { RedisTransport } from "@boringnode/bus/transports/redis"; // src/bus/encoders/binary_encoder.ts var BinaryEncoder = class { #busIdLength; /** * We assume the bus ID is a string of length 24 by default. * Because this is the default length of a cuid */ constructor(busIdLength = 24) { this.#busIdLength = busIdLength; } busMessageTypeToNum(type) { if (type === CacheBusMessageType.Set) return 1; if (type === CacheBusMessageType.Clear) return 2; if (type === CacheBusMessageType.Delete) return 3; if (type === CacheBusMessageType.Expire) return 4; throw new Error(`Unknown message type: ${type}`); } numToBusMessageType(num) { if (num === 1) return CacheBusMessageType.Set; if (num === 2) return CacheBusMessageType.Clear; if (num === 3) return CacheBusMessageType.Delete; if (num === 4) return CacheBusMessageType.Expire; throw new Error(`Unknown message type: ${num}`); } /** * Encode the given message into a Buffer */ encode(message) { const payload = message.payload; const totalKeysLength = payload.keys.reduce( (sum, key) => sum + 4 + Buffer.byteLength(key, "utf8"), 0 ); const namespaceKeyLength = payload.namespace ? Buffer.byteLength(payload.namespace, "utf8") : 0; const totalLength = this.#busIdLength + 1 + 4 + namespaceKeyLength + totalKeysLength; const buffer = Buffer.alloc(totalLength); buffer.write(message.busId, 0, this.#busIdLength, "utf8"); buffer.writeUInt8(this.busMessageTypeToNum(payload.type), this.#busIdLength); let offset = this.#busIdLength + 1; buffer.writeUInt32BE(namespaceKeyLength, offset); offset += 4; if (payload.namespace) { buffer.write(payload.namespace, offset, namespaceKeyLength, "utf8"); offset += namespaceKeyLength; } for (const key of payload.keys) { const keyLength = Buffer.byteLength(key, "utf8"); buffer.writeUInt32BE(keyLength, offset); offset += 4; buffer.write(key, offset, keyLength, "utf8"); offset += keyLength; } return buffer; } /** * Decode the given Buffer into a CacheBusMessage */ decode(data) { let offset = 0; const buffer = Buffer.isBuffer(data) ? data : Buffer.from(data, "binary"); const busId = buffer.toString("utf8", offset, this.#busIdLength); offset += this.#busIdLength; const typeValue = buffer.readUInt8(offset++); const type = this.numToBusMessageType(typeValue); const namespaceKeyLength = buffer.readUInt32BE(offset); offset += 4; const namespace = namespaceKeyLength ? buffer.toString("utf8", offset, offset + namespaceKeyLength) : ""; offset += namespaceKeyLength; const keys = []; while (offset < buffer.length) { const keyLength = buffer.readUInt32BE(offset); offset += 4; const key = buffer.toString("utf8", offset, offset + keyLength); offset += keyLength; keys.push(key); } return { busId, payload: { keys, type, namespace } }; } }; // src/drivers/redis.ts function redisDriver(options) { return { options, factory: (config) => new RedisDriver(config) }; } function redisBusDriver(options) { return { options, factory: () => { return new RedisTransport( { ...options.connection, useMessageBuffer: true }, new BinaryEncoder() ); } }; } var RedisDriver = class _RedisDriver extends BaseDriver { type = "l2"; #connection; constructor(config) { super(config); if (config.connection instanceof IoRedis) { this.#connection = config.connection; return; } this.#connection = new IoRedis(config.connection); } getConnection() { return this.#connection; } /** * Returns a new instance of the driver namespaced */ namespace(namespace) { return new _RedisDriver({ ...this.config, connection: this.#connection, prefix: this.createNamespacePrefix(namespace) }); } /** * Get a value from the cache */ async get(key) { const result = await this.#connection.get(this.getItemKey(key)); return result ?? void 0; } /** * Get the value of a key and delete it * * Returns the value if the key exists, undefined otherwise */ async pull(key) { const value = await this.#connection.getdel(this.getItemKey(key)); return value ?? void 0; } /** * Put a value in the cache * Returns true if the value was set, false otherwise */ async set(key, value, ttl) { key = this.getItemKey(key); if (!ttl) { const result2 = await this.#connection.set(key, value); return result2 === "OK"; } const result = await this.#connection.set(key, value, "PX", ttl); return result === "OK"; } /** * Remove all items from the cache */ async clear() { let cursor = "0"; const COUNT = 1e3; const prefix = this.prefix && `${this.prefix}:`; const connectionKeyPrefix = this.#connection.options.keyPrefix; do { const [newCursor, keys] = await this.#connection.scan( cursor, "MATCH", `${connectionKeyPrefix}${prefix}*`, "COUNT", COUNT ); if (keys.length) this.#connection.unlink(keys.map((key) => key.slice(connectionKeyPrefix?.length))); cursor = newCursor; } while (cursor !== "0"); } /** * Delete a key from the cache * Returns true if the key was deleted, false otherwise */ async delete(key) { const deletedKeys = await this.#connection.unlink(this.getItemKey(key)); return deletedKeys > 0; } /** * Delete multiple keys from the cache */ async deleteMany(keys) { if (keys.length === 0) return true; await this.#connection.unlink(keys.map((key) => this.getItemKey(key))); return true; } /** * Closes the connection to the cache */ async disconnect() { this.#connection.disconnect(); } }; export { RedisDriver, redisBusDriver, redisDriver }; //# sourceMappingURL=redis.js.map