UNPKG

@fedify/redis

Version:

Redis drivers for Fedify

117 lines (113 loc) 3.78 kB
const { Temporal } = require("@js-temporal/polyfill"); const require_rolldown_runtime = require('./_virtual/rolldown_runtime.cjs'); const require_codec = require('./codec.cjs'); const node_buffer = require_rolldown_runtime.__toESM(require("node:buffer")); //#region src/kv.ts /** * A key–value store that uses Redis as the underlying storage. * * @example * ```ts ignore * import { createFederation } from "@fedify/fedify"; * import { RedisKvStore } from "@fedify/redis"; * import { Redis, Cluster } from "ioredis"; * * // Using a standalone Redis instance: * const federation = createFederation({ * // ... * kv: new RedisKvStore(new Redis()), * }); * * // Using a Redis Cluster: * const cluster = new Cluster([ * { host: "127.0.0.1", port: 7000 }, * { host: "127.0.0.1", port: 7001 }, * { host: "127.0.0.1", port: 7002 }, * ]); * const federation = createFederation({ * // ... * kv: new RedisKvStore(cluster), * }); * ``` */ var RedisKvStore = class { #redis; #keyPrefix; #keyPrefixStr; #codec; #textEncoder = new TextEncoder(); /** * Creates a new Redis key–value store. * @param redis The Redis client (standalone or cluster) to use. * @param options The options for the key–value store. */ constructor(redis, options = {}) { this.#redis = redis; this.#keyPrefix = options.keyPrefix ?? "fedify::"; this.#keyPrefixStr = typeof this.#keyPrefix === "string" ? this.#keyPrefix : new TextDecoder().decode(new Uint8Array(this.#keyPrefix)); this.#codec = options.codec ?? new require_codec.JsonCodec(); } #serializeKey(key) { const suffix = key.map((part) => part.replaceAll(":", "_:")).join("::"); if (typeof this.#keyPrefix === "string") return `${this.#keyPrefix}${suffix}`; const suffixBytes = this.#textEncoder.encode(suffix); return node_buffer.Buffer.concat([new Uint8Array(this.#keyPrefix), suffixBytes]); } async get(key) { const serializedKey = this.#serializeKey(key); const encodedValue = await this.#redis.getBuffer(serializedKey); if (encodedValue == null) return void 0; return this.#codec.decode(encodedValue); } async set(key, value, options) { const serializedKey = this.#serializeKey(key); const encodedValue = this.#codec.encode(value); if (options?.ttl != null) await this.#redis.setex(serializedKey, options.ttl.total("second"), encodedValue); else await this.#redis.set(serializedKey, encodedValue); } async delete(key) { const serializedKey = this.#serializeKey(key); await this.#redis.del(serializedKey); } #deserializeKey(redisKey) { const suffix = redisKey.slice(this.#keyPrefixStr.length); return suffix.split("::").map((p) => p.replaceAll("_:", ":")); } /** * {@inheritDoc KvStore.list} * @since 1.10.0 */ async *list(prefix) { let pattern; let exactKey = null; if (prefix == null || prefix.length === 0) pattern = `${this.#keyPrefixStr}*`; else { const prefixKey = this.#serializeKey(prefix); const prefixKeyFullStr = typeof prefixKey === "string" ? prefixKey : new TextDecoder().decode(new Uint8Array(prefixKey)); exactKey = prefixKey; pattern = `${prefixKeyFullStr}::*`; } if (exactKey != null) { const exactValue = await this.#redis.getBuffer(exactKey); if (exactValue != null) yield { key: prefix, value: this.#codec.decode(exactValue) }; } let cursor = "0"; do { const [nextCursor, keys] = await this.#redis.scan(cursor, "MATCH", pattern, "COUNT", 100); cursor = nextCursor; for (const key of keys) { const encodedValue = await this.#redis.getBuffer(key); if (encodedValue == null) continue; yield { key: this.#deserializeKey(key), value: this.#codec.decode(encodedValue) }; } } while (cursor !== "0"); } }; //#endregion exports.RedisKvStore = RedisKvStore;