UNPKG

@kurai-io/redis

Version:

Schema-based abstraction layer for redis database interactions

165 lines (164 loc) 4.97 kB
import { seal as r } from "@sigiljs/seal"; import { createHash as o } from "node:crypto"; import l from "./seal-json-parser.mjs"; import * as c from "crypto"; class h { /** Original redis client */ client; template; options; randomKeyBytesCount = 16; /** * @param client redis client instance * @param template seal schema template * @param options schema options * @param config */ constructor(t, i, e, n) { const s = e?.namespace ?? o("shake256", { outputLength: n?.randomNamespaceLength || 8 }).update(JSON.stringify([r.exportMetadataOf(i), e || {}])).digest("base64url"); this.client = t, this.options = { ttl: e?.ttl, readOnce: e?.readOnce ?? !1, namespace: s }, this.template = i, n?.randomKeyBytesCount && (this.randomKeyBytesCount = n.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 = JSON.stringify(this.serialize(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 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 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, this.template); } /** * Deletes the specified key from Redis. * * @param key key to delete (without 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 Redis key (without namespace). */ set(i) { const e = c.randomBytes(t.randomKeyBytesCount).toString("base64url"); return t.set(e, i); } }; } fullKey(t) { return this.options.namespace + "+" + t; } serialize(t) { if (typeof t != "object") return t; const i = Object.keys(r.exportMetadataOf(this.template).shape || {}); if (i.length) { const e = []; for (const n of i) { const s = t[n]; e.push(s && typeof s == "object" ? this.serialize(s) : s); } return e; } return Object.values(t).map((e) => e && typeof e == "object" ? this.serialize(e) : e); } 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 p { client; #t; constructor(t, i) { this.client = t, this.#t = i || {}; } /** * Creates a new object schema template compatible with Seal. * * @param template schema definition. * @returns an ObjectSchema based on the provided template. */ template(t) { return r.object(t); } /** * Defines a new data schema with the given template and options. * * @param template redis schema template. * @param options schema options. * @returns a RedisSchema instance for data operations. */ model(t, i) { return new h(this.client, t, i, this.#t); } /** * Disconnects and cleans up the Redis client. */ destroy() { this.client.destroy(); } async connect() { return await this.client.connect(), this.client; } } export { p as default };