UNPKG

@kurai-io/redis

Version:

Schema-based abstraction layer for redis database interactions

139 lines (138 loc) 4.11 kB
import { createHash as r } from "node:crypto"; import * as a from "crypto"; import { pack as o, unpack as l } from "jsonpack"; class c { /** Original redis client */ client; options; randomKeyBytesCount = 16; /** * @param client redis client instance * @param options schema options * @param config */ constructor(t, i, e) { const s = i?.namespace ?? r("shake256", { outputLength: e?.randomNamespaceLength || 8 }).update(JSON.stringify([i])).digest("base64url"); this.client = t, this.options = { ttl: i?.ttl, readOnce: i?.readOnce ?? !1, namespace: s }, e?.randomKeyBytesCount && (this.randomKeyBytesCount = e.randomKeyBytesCount); } /** * Compresses and writes the specified value to Redis. * * @param key key under which the value will be stored (without namespace). * @param value value to store. * @returns key used in Redis (without namespace). */ async set(t, i) { await this.waitWhenReady(); const e = o(i); return typeof this.options.ttl == "number" ? this.client.setEx(this.fullKey(t), this.options.ttl, e) : this.client.set(this.fullKey(t), e), t; } /** * Expire key after a specific ttl or date */ async expire(t, i, e) { typeof i == "number" ? this.client.expire(t, i, e) : this.client.expireAt(t, i, e); } /** * Retrieves data from Redis and validates it against the template. * * @param key key in Redis (without a namespace). * @param force if true, throws an error instead of returning null when * data is missing or fails validation. * @returns parsed data matching the template, or null if not in force mode. */ async get(t, i) { await this.waitWhenReady(); const e = await this.client.get(this.fullKey(t)); if (!e) { if (i) throw new Error(`Key ${t} not found`); return null; } return this.options.readOnce && this.delete(t), l(Buffer.isBuffer(e) ? e.toString("utf8") : e); } /** * Deletes the specified key from Redis. * * @param key key to delete (without a namespace). * @returns result of the deletion operation. */ async delete(t) { return await this.waitWhenReady(), this.client.del(this.fullKey(t)); } /** * Retrieves data from Redis and executes a callback if the data exists * and matches the template. * * @param key key to retrieve. * @param callback function to execute with the retrieved payload. * @returns callback result or null if no data. */ async with(t, i) { const e = await this.get(t); return e ? i(e) : null; } /** * Provides a method to set a value with an auto-generated random key. */ get randomKey() { const t = this; return { /** * Compresses and writes the value under a random key. * * @param value value to store. * @returns generated a Redis key (without a namespace). */ set(i) { const e = a.randomBytes(t.randomKeyBytesCount).toString("base64url"); return t.set(e, i); } }; } fullKey(t) { return this.options.namespace + "+" + t; } async waitWhenReady() { if (this.client.isReady) return; let t = 0; return new Promise((i) => { const e = setInterval(() => { if (t += 1, t > 300) throw clearInterval(e), new Error("Redis initialization timeout, if this is only error you encounter, double-check that Sigil app starts listening BEFORE any redis interactions"); this.client.isReady && (clearInterval(e), i()); }, 100); }); } } class y { client; #t; constructor(t, i) { this.client = t, this.#t = i || {}; } /** * Defines a new data schema with the given template and options. * * @param options schema options. * @returns a RedisSchema instance for data operations. */ model(t) { return new c(this.client, t, this.#t); } /** * Disconnects and cleans up the Redis client. */ destroy() { this.client.destroy(); } async connect() { return await this.client.connect(), this.client; } } export { y as default };