@nexusts/redis 0.9.0 → 0.9.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Bun runtime adapter for `nexusjs/redis`.
2
+ * Bun runtime adapter for `@nexusts/redis`.
3
3
  *
4
4
  * Uses the built-in `Bun.redis` client. No extra package needed.
5
5
  * The client is lazily opened on first use.
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Cloudflare Workers KV adapter for `nexusjs/redis`.
2
+ * Cloudflare Workers KV adapter for `@nexusts/redis`.
3
3
  *
4
4
  * Workers KV is **not** Redis, but the surface is close enough
5
5
  * that the same `RedisClient` API can sit on top of it. The
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Runtime detection and factory for `nexusjs/redis` adapters.
2
+ * Runtime detection and factory for `@nexusts/redis` adapters.
3
3
  *
4
4
  * - `Bun` → `BunRedisAdapter` (built-in `Bun.redis`, no extra package).
5
5
  * - `node` → `NodeRedisAdapter` (uses `ioredis` — install separately).
@@ -1,5 +1,5 @@
1
1
  /**
2
- * In-memory adapter for `nexusjs/redis`.
2
+ * In-memory adapter for `@nexusts/redis`.
3
3
  *
4
4
  * Used for tests and single-process dev. Not cluster-safe; values
5
5
  * don't survive process restart.
@@ -1,8 +1,8 @@
1
1
  /**
2
- * Node.js runtime adapter for `nexusjs/redis`.
2
+ * Node.js runtime adapter for `@nexusts/redis`.
3
3
  *
4
4
  * Uses the `ioredis` package. The package is an **optional**
5
- * peer dependency of `nexusjs` — install it only when targeting
5
+ * peer dependency of `@nexusts/redis` — install it only when targeting
6
6
  * Node:
7
7
  *
8
8
  * bun add ioredis
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  /**
2
- * `nexusjs/redis` — runtime-aware Redis-compatible key/value client.
2
+ * `@nexusts/redis` — runtime-aware Redis-compatible key/value client.
3
3
  *
4
4
  * Public API:
5
5
  * - `createRedisClient(config)` — factory; auto-detects the runtime
@@ -20,8 +20,8 @@
20
20
  * | Cloudflare Workers | `cloudflare` | none (Workers KV) |
21
21
  * | (other / no signal) | `memory` | none |
22
22
  *
23
- * Same API across runtimes — `nexusjs/session`, `nexusjs/cache`, and
24
- * `nexusjs/queue` (where applicable) all use the `RedisClient`
23
+ * Same API across runtimes — `@nexusts/session`, `@nexusts/cache`, and
24
+ * `@nexusts/queue` (where applicable) all use the `RedisClient`
25
25
  * interface so a single config switch chooses the backend.
26
26
  *
27
27
  * bun add ioredis # only for Node runtime
package/dist/index.js.map CHANGED
@@ -2,11 +2,11 @@
2
2
  "version": 3,
3
3
  "sources": ["../src/adapters/bun.ts", "../src/adapters/cloudflare.ts", "../src/adapters/memory.ts", "../src/adapters/node.ts", "../src/adapters/index.ts", "../src/module.ts"],
4
4
  "sourcesContent": [
5
- "/**\n * Bun runtime adapter for `nexusjs/redis`.\n *\n * Uses the built-in `Bun.redis` client. No extra package needed.\n * The client is lazily opened on first use.\n */\n\nimport type {\n\tRedisClient,\n\tRedisConfig,\n\tRedisScanOptions,\n\tRedisScanResult,\n\tRedisSetOptions,\n\tRedisValue,\n} from \"../types.js\";\n\n/** The shape we need from `Bun.redis()`. */\ninterface BunRedisClient {\n\tget(key: string): Promise<string | null>;\n\tset(\n\t\tkey: string,\n\t\tvalue: string,\n\t\toptions?: { EX?: number; PX?: number; NX?: boolean; XX?: boolean },\n\t): Promise<\"OK\" | null>;\n\tdel(key: string): Promise<number>;\n\texists(key: string): Promise<number>;\n\tincr(key: string): Promise<number>;\n\tscan(cursor: number, options?: { MATCH?: string; COUNT?: number }): Promise<{\n\t\tcursor: number;\n\t\tkeys: string[];\n\t}>;\n\tclose(): void | Promise<void>;\n}\n\nexport class BunRedisAdapter implements RedisClient {\n\treadonly adapter = \"bun\" as const;\n\tprivate client: BunRedisClient | null = null;\n\tprivate readonly url: string;\n\tprivate readonly keyPrefix: string;\n\tprivate readonly defaultTtlSeconds: number;\n\n\tconstructor(config: RedisConfig = {}) {\n\t\tthis.url = config.url ?? process.env[\"REDIS_URL\"] ?? \"redis://localhost:6379\";\n\t\tthis.keyPrefix = config.keyPrefix ?? \"\";\n\t\tthis.defaultTtlSeconds = config.defaultTtlSeconds ?? 0;\n\t}\n\n\tprivate getClient(): BunRedisClient {\n\t\tif (this.client) return this.client;\n\t\t// Bun.redis is a global; cast through unknown so we can compile\n\t\t// in environments that don't have Bun's lib.d.ts loaded.\n\t\tconst bun = (globalThis as unknown as { Bun?: { redis: (url: string) => BunRedisClient } }).Bun;\n\t\tif (!bun || typeof bun.redis !== \"function\") {\n\t\t\tthrow new Error(\n\t\t\t\t\"BunRedisAdapter can only be used in a Bun runtime. \" +\n\t\t\t\t\t\"On Node, use NodeRedisAdapter (install ioredis).\",\n\t\t\t);\n\t\t}\n\t\tthis.client = bun.redis(this.url);\n\t\treturn this.client!;\n\t}\n\n\tprivate k(key: string): string {\n\t\treturn this.keyPrefix + key;\n\t}\n\n\tasync get(key: string): Promise<RedisValue> {\n\t\treturn this.getClient().get(this.k(key));\n\t}\n\n\tasync set(key: string, value: string, options?: RedisSetOptions): Promise<void> {\n\t\tconst ex = options?.ex ?? this.defaultTtlSeconds ?? undefined;\n\t\tconst px = options?.px ?? undefined;\n\t\tconst bunOpts: { EX?: number; PX?: number; NX?: boolean; XX?: boolean } = {};\n\t\tif (ex) bunOpts.EX = ex;\n\t\tif (px) bunOpts.PX = px;\n\t\tif (options?.nx) bunOpts.NX = true;\n\t\tif (options?.xx) bunOpts.XX = true;\n\t\tawait this.getClient().set(this.k(key), value, bunOpts);\n\t}\n\n\tasync del(key: string): Promise<number> {\n\t\treturn this.getClient().del(this.k(key));\n\t}\n\n\tasync exists(key: string): Promise<boolean> {\n\t\treturn (await this.getClient().exists(this.k(key))) > 0;\n\t}\n\n\tasync incr(key: string, by = 1, options?: { ex?: number }): Promise<number> {\n\t\tconst fullKey = this.k(key);\n\t\tconst client = this.getClient();\n\t\tconst value = await client.incr(fullKey);\n\t\t// On the first increment (value === 1), apply the TTL if requested.\n\t\tif (options?.ex && value === by) {\n\t\t\tawait client.set(fullKey, String(value), { EX: options.ex });\n\t\t}\n\t\treturn value;\n\t}\n\n\tasync scan(options: RedisScanOptions = {}): Promise<RedisScanResult> {\n\t\tconst cursor = typeof options.cursor === \"number\" ? options.cursor : 0;\n\t\tconst res = await this.getClient().scan(cursor, {\n\t\t\tMATCH: options.match ?? \"*\",\n\t\t\tCOUNT: options.count ?? 100,\n\t\t});\n\t\treturn {\n\t\t\tcursor: res.cursor,\n\t\t\tkeys: (res.keys ?? []).map((k) =>\n\t\t\t\tthis.keyPrefix && k.startsWith(this.keyPrefix)\n\t\t\t\t\t? k.slice(this.keyPrefix.length)\n\t\t\t\t\t: k,\n\t\t\t),\n\t\t};\n\t}\n\n\tasync close(): Promise<void> {\n\t\tif (this.client) {\n\t\t\tawait this.client.close();\n\t\t\tthis.client = null;\n\t\t}\n\t}\n}\n",
6
- "/**\n * Cloudflare Workers KV adapter for `nexusjs/redis`.\n *\n * Workers KV is **not** Redis, but the surface is close enough\n * that the same `RedisClient` API can sit on top of it. The\n * adapter:\n *\n * - Maps `set(key, value, { ex })` to `KVNamespace.put()` with\n * `expirationTtl` (seconds).\n * - Maps `scan({ match })` to `KVNamespace.list({ prefix })`,\n * which is the closest thing KV offers to Redis `SCAN MATCH`.\n * The `cursor` field is a Cloudflare opaque string.\n * - `del` uses `KVNamespace.delete()`.\n * - `incr` falls back to a small read-modify-write: get the\n * value, parse as int, add, put back. (KV doesn't have\n * atomic INCR.) Use with care — concurrent writes can lose\n * updates. For high-contention counters use a real Redis.\n *\n * Cloudflare imposes a 25 MB value limit per key and a global\n * 100k reads / 1000 writes per second per KV namespace. Don't\n * store large objects in KV.\n *\n * To use:\n *\n * // In a Worker request handler:\n * const client = createRedisClient({\n * adapter: \"cloudflare\",\n * kv: c.env.SESSIONS, // KVNamespace binding from wrangler.toml\n * });\n */\n\nimport type {\n\tKVNamespaceLike,\n\tRedisClient,\n\tRedisConfig,\n\tRedisScanOptions,\n\tRedisScanResult,\n\tRedisSetOptions,\n\tRedisValue,\n} from \"../types.js\";\n\nexport class CloudflareKVAdapter implements RedisClient {\n\treadonly adapter = \"cloudflare\" as const;\n\tprivate kv: KVNamespaceLike | null = null;\n\tprivate readonly keyPrefix: string;\n\tprivate readonly defaultTtlSeconds: number;\n\n\tconstructor(config: RedisConfig = {}) {\n\t\tthis.keyPrefix = config.keyPrefix ?? \"\";\n\t\tthis.defaultTtlSeconds = config.defaultTtlSeconds ?? 0;\n\t\tif (config.kv) this.kv = config.kv;\n\t}\n\n\tprivate getKV(): KVNamespaceLike {\n\t\tif (this.kv) return this.kv;\n\t\t// Auto-detect from `globalThis.env` (Workers context).\n\t\tconst env = (globalThis as unknown as { env?: { KV?: KVNamespaceLike } }).env;\n\t\tif (env?.KV) {\n\t\t\tthis.kv = env.KV;\n\t\t\treturn this.kv;\n\t\t}\n\t\tthrow new Error(\n\t\t\t\"CloudflareKVAdapter could not find a KV binding. \" +\n\t\t\t\t\"Pass it explicitly via RedisConfig({ kv }) or run inside a \" +\n\t\t\t\t\"Workers request handler where `c.env.KV` is available.\",\n\t\t);\n\t}\n\n\tprivate k(key: string): string {\n\t\treturn this.keyPrefix + key;\n\t}\n\n\tprivate stripPrefix(key: string): string {\n\t\treturn this.keyPrefix && key.startsWith(this.keyPrefix)\n\t\t\t? key.slice(this.keyPrefix.length)\n\t\t\t: key;\n\t}\n\n\tasync get(key: string): Promise<RedisValue> {\n\t\treturn this.getKV().get(this.k(key));\n\t}\n\n\tasync set(key: string, value: string, options?: RedisSetOptions): Promise<void> {\n\t\tconst ex = options?.ex ?? this.defaultTtlSeconds ?? undefined;\n\t\tif (ex && ex > 0) {\n\t\t\tawait this.getKV().put(this.k(key), value, { expirationTtl: ex });\n\t\t} else {\n\t\t\tawait this.getKV().put(this.k(key), value);\n\t\t}\n\t}\n\n\tasync del(key: string): Promise<number> {\n\t\t// KV.delete returns void; we conservatively return 1 if a value\n\t\t// existed before, 0 otherwise.\n\t\tconst existed = (await this.getKV().get(this.k(key))) !== null;\n\t\tawait this.getKV().delete(this.k(key));\n\t\treturn existed ? 1 : 0;\n\t}\n\n\tasync exists(key: string): Promise<boolean> {\n\t\treturn (await this.getKV().get(this.k(key))) !== null;\n\t}\n\n\tasync incr(key: string, by = 1, options?: { ex?: number }): Promise<number> {\n\t\t// Naive read-modify-write. KV doesn't have atomic INCR. Don't\n\t\t// use this for high-contention counters.\n\t\tconst fullKey = this.k(key);\n\t\tconst raw = await this.getKV().get(fullKey);\n\t\tconst current = raw ? Number.parseInt(raw, 10) || 0 : 0;\n\t\tconst next = current + by;\n\t\tconst ex = options?.ex ?? this.defaultTtlSeconds ?? undefined;\n\t\tif (ex && ex > 0) {\n\t\t\tawait this.getKV().put(fullKey, String(next), { expirationTtl: ex });\n\t\t} else {\n\t\t\tawait this.getKV().put(fullKey, String(next));\n\t\t}\n\t\treturn next;\n\t}\n\n\tasync scan(options: RedisScanOptions = {}): Promise<RedisScanResult> {\n\t\t// KV.list() supports `prefix` only — convert a `match` glob\n\t\t// to a prefix. Full glob support would require client-side\n\t\t// filtering. The cursor is a Cloudflare opaque string.\n\t\tconst match = options.match ?? \"*\";\n\t\tconst prefix = globToPrefix(match, this.keyPrefix);\n\t\tconst res = await this.getKV().list({\n\t\t\tprefix,\n\t\t\tlimit: options.count ?? 100,\n\t\t\tcursor: typeof options.cursor === \"string\" ? options.cursor : undefined,\n\t\t});\n\t\treturn {\n\t\t\tcursor: res.cursor,\n\t\t\tkeys: (res.keys ?? []).map((k) => this.stripPrefix(k.name)),\n\t\t};\n\t}\n\n\tasync close(): Promise<void> {\n\t\t// KV has no client to close.\n\t}\n}\n\n/** Convert a Redis-style glob (`*`, `?`, `[abc]`) to a KV prefix. */\nfunction globToPrefix(glob: string, keyPrefix: string): string {\n\t// Everything up to the first glob meta-character.\n\tconst meta = /[*?\\[]/.exec(glob);\n\tconst base = meta ? glob.slice(0, meta.index) : glob;\n\treturn (keyPrefix ?? \"\") + base;\n}\n",
7
- "/**\n * In-memory adapter for `nexusjs/redis`.\n *\n * Used for tests and single-process dev. Not cluster-safe; values\n * don't survive process restart.\n */\n\nimport type {\n\tRedisClient,\n\tRedisConfig,\n\tRedisScanOptions,\n\tRedisScanResult,\n\tRedisSetOptions,\n\tRedisValue,\n} from \"../types.js\";\n\ninterface Entry {\n\tvalue: string;\n\texpiresAt: number | null; // ms epoch; null = no expiry\n}\n\nexport class MemoryRedisAdapter implements RedisClient {\n\treadonly adapter = \"memory\" as const;\n\tprivate data = new Map<string, Entry>();\n\tprivate readonly keyPrefix: string;\n\tprivate readonly defaultTtlSeconds: number;\n\n\tconstructor(config: RedisConfig = {}) {\n\t\tthis.keyPrefix = config.keyPrefix ?? \"\";\n\t\tthis.defaultTtlSeconds = config.defaultTtlSeconds ?? 0;\n\t}\n\n\tprivate k(key: string): string {\n\t\treturn this.keyPrefix + key;\n\t}\n\n\tprivate stripPrefix(key: string): string {\n\t\treturn this.keyPrefix && key.startsWith(this.keyPrefix)\n\t\t\t? key.slice(this.keyPrefix.length)\n\t\t\t: key;\n\t}\n\n\tprivate isExpired(e: Entry | undefined): e is undefined {\n\t\treturn !e || (e.expiresAt !== null && e.expiresAt < Date.now());\n\t}\n\n\tprivate purge(key: string): void {\n\t\tconst e = this.data.get(key);\n\t\tif (this.isExpired(e)) this.data.delete(key);\n\t}\n\n\tasync get(key: string): Promise<RedisValue> {\n\t\tconst k = this.k(key);\n\t\tthis.purge(k);\n\t\tconst e = this.data.get(k);\n\t\treturn e ? e.value : null;\n\t}\n\n\tasync set(key: string, value: string, options?: RedisSetOptions): Promise<void> {\n\t\tconst k = this.k(key);\n\t\tconst ttl =\n\t\t\toptions?.ex !== undefined\n\t\t\t\t? options.ex\n\t\t\t\t: options?.px !== undefined\n\t\t\t\t\t? options.px / 1000\n\t\t\t\t\t: this.defaultTtlSeconds;\n\t\tconst expiresAt = ttl && ttl > 0 ? Date.now() + ttl * 1000 : null;\n\t\tif (options?.nx && !this.isExpired(this.data.get(k))) return; // NX semantics\n\t\tif (options?.xx && this.isExpired(this.data.get(k))) return; // XX semantics\n\t\tthis.data.set(k, { value, expiresAt });\n\t}\n\n\tasync del(key: string): Promise<number> {\n\t\tconst k = this.k(key);\n\t\tconst existed = this.data.delete(k);\n\t\treturn existed ? 1 : 0;\n\t}\n\n\tasync exists(key: string): Promise<boolean> {\n\t\tconst k = this.k(key);\n\t\tthis.purge(k);\n\t\treturn this.data.has(k);\n\t}\n\n\tasync incr(key: string, by = 1, options?: { ex?: number }): Promise<number> {\n\t\tconst k = this.k(key);\n\t\tthis.purge(k);\n\t\tconst e = this.data.get(k);\n\t\tconst current = e ? Number.parseInt(e.value, 10) || 0 : 0;\n\t\tconst next = current + by;\n\t\tconst ttl = options?.ex ?? this.defaultTtlSeconds ?? undefined;\n\t\tconst expiresAt = ttl && ttl > 0 ? Date.now() + ttl * 1000 : null;\n\t\tthis.data.set(k, { value: String(next), expiresAt });\n\t\treturn next;\n\t}\n\n\tasync scan(options: RedisScanOptions = {}): Promise<RedisScanResult> {\n\t\tconst match = options.match ?? \"*\";\n\t\t// The match pattern is matched against the full key (with\n\t\t// prefix included). Callers should write patterns that\n\t\t// include the prefix.\n\t\tconst re = globToRegex(match);\n\t\tconst keys: string[] = [];\n\t\tfor (const k of this.data.keys()) {\n\t\t\tif (this.keyPrefix && !k.startsWith(this.keyPrefix)) continue;\n\t\t\tif (re.test(k)) keys.push(this.stripPrefix(k));\n\t\t}\n\t\treturn { cursor: \"0\", keys };\n\t}\n\n\tasync close(): Promise<void> {\n\t\tthis.data.clear();\n\t}\n}\n\nfunction globToRegex(glob: string): RegExp {\n\tconst escaped = glob\n\t\t.replace(/[.+^${}()|[\\]\\\\]/g, \"\\\\$&\")\n\t\t.replace(/\\*/g, \".*\")\n\t\t.replace(/\\?/g, \".\");\n\treturn new RegExp(\"^\" + escaped + \"$\");\n}\n",
8
- "/**\n * Node.js runtime adapter for `nexusjs/redis`.\n *\n * Uses the `ioredis` package. The package is an **optional**\n * peer dependency of `nexusjs` — install it only when targeting\n * Node:\n *\n * bun add ioredis\n *\n * The adapter opens a single shared client per `RedisConfig` and\n * reuses it across `get` / `set` / `del` / `scan` calls. Use\n * `await client.close()` to release the connection.\n */\n\nimport type {\n\tRedisClient,\n\tRedisConfig,\n\tRedisScanOptions,\n\tRedisScanResult,\n\tRedisSetOptions,\n\tRedisValue,\n} from \"../types.js\";\n\n/** The shape we need from `ioredis`. We don't depend on the\n * package at the type level so users who don't install it\n * still type-check. */\ninterface IORedisLike {\n\tget(key: string): Promise<string | null>;\n\tset(\n\t\tkey: string,\n\t\tvalue: string,\n\t\tmodeOrEx?: string | number,\n\t\tduration?: number,\n\t\tflag?: \"NX\" | \"XX\",\n\t): Promise<\"OK\" | null>;\n\tdel(key: string | string[]): Promise<number>;\n\texists(key: string): Promise<number>;\n\tincr(key: string): Promise<number>;\n\texpire(key: string, seconds: number): Promise<number>;\n\tscan(\n\t\tcursor: number | string,\n\t\tmatch: string,\n\t\tcount: number,\n\t): Promise<[string | number, string[]]>;\n\tquit(): Promise<\"OK\">;\n\tdisconnect(): void;\n}\n\nexport class NodeRedisAdapter implements RedisClient {\n\treadonly adapter = \"node\" as const;\n\tprivate client: IORedisLike | null = null;\n\tprivate readonly url: string;\n\tprivate readonly keyPrefix: string;\n\tprivate readonly defaultTtlSeconds: number;\n\tprivate readonly nodeOptions: Record<string, unknown>;\n\n\tconstructor(config: RedisConfig = {}) {\n\t\tthis.url = config.url ?? process.env[\"REDIS_URL\"] ?? \"redis://localhost:6379\";\n\t\tthis.keyPrefix = config.keyPrefix ?? \"\";\n\t\tthis.defaultTtlSeconds = config.defaultTtlSeconds ?? 0;\n\t\tthis.nodeOptions = config.nodeOptions ?? {};\n\t}\n\n\tprivate async getClient(): Promise<IORedisLike> {\n\t\tif (this.client) return this.client;\n\t\ttry {\n\t\t\t// @ts-ignore - optional peer dep\n\t\t\tconst mod = await import(\"ioredis\");\n\t\t\tconst Ctor = (mod as any).default ?? (mod as any);\n\t\t\tif (typeof Ctor !== \"function\") {\n\t\t\t\tthrow new Error(\"ioredis module did not export a constructor\");\n\t\t\t}\n\t\t\tthis.client = new Ctor(this.url, this.nodeOptions) as IORedisLike;\n\t\t} catch (err) {\n\t\t\tthrow new Error(\n\t\t\t\t\"NodeRedisAdapter requires the `ioredis` package. \" +\n\t\t\t\t\t\"Install with: bun add ioredis\",\n\t\t\t);\n\t\t}\n\t\treturn this.client!;\n\t}\n\n\tprivate k(key: string): string {\n\t\treturn this.keyPrefix + key;\n\t}\n\n\tprivate stripPrefix(key: string): string {\n\t\treturn this.keyPrefix && key.startsWith(this.keyPrefix)\n\t\t\t? key.slice(this.keyPrefix.length)\n\t\t\t: key;\n\t}\n\n\tasync get(key: string): Promise<RedisValue> {\n\t\treturn (await this.getClient()).get(this.k(key));\n\t}\n\n\tasync set(key: string, value: string, options?: RedisSetOptions): Promise<void> {\n\t\tconst ex = options?.ex ?? this.defaultTtlSeconds ?? undefined;\n\t\tconst c = await this.getClient();\n\t\tconst fullKey = this.k(key);\n\t\t// ioredis set signature: set(key, value, EX, seconds, NX|XX) or\n\t\t// set(key, value, NX|XX). We dispatch on the combination of\n\t\t// flags.\n\t\tif (options?.nx) {\n\t\t\tawait c.set(fullKey, value, \"EX\", ex ?? 0, \"NX\");\n\t\t} else if (options?.xx) {\n\t\t\tawait c.set(fullKey, value, \"EX\", ex ?? 0, \"XX\");\n\t\t} else if (ex) {\n\t\t\tawait c.set(fullKey, value, \"EX\", ex);\n\t\t} else {\n\t\t\tawait c.set(fullKey, value);\n\t\t}\n\t}\n\n\tasync del(key: string): Promise<number> {\n\t\treturn (await this.getClient()).del(this.k(key));\n\t}\n\n\tasync exists(key: string): Promise<boolean> {\n\t\tconst n = await (await this.getClient()).exists(this.k(key));\n\t\treturn n > 0;\n\t}\n\n\tasync incr(key: string, by = 1, options?: { ex?: number }): Promise<number> {\n\t\tconst fullKey = this.k(key);\n\t\tconst c = await this.getClient();\n\t\tlet value: number;\n\t\tif (by === 1) {\n\t\t\tvalue = await c.incr(fullKey);\n\t\t} else {\n\t\t\t// ioredis doesn't have incrby with delta? It does — use\n\t\t\t// the multi-step: `incrby` is the ioredis method. Patch\n\t\t\t// the IORedisLike to include it.\n\t\t\tvalue = await (c as unknown as { incrby: (k: string, n: number) => Promise<number> }).incrby(fullKey, by);\n\t\t}\n\t\tif (options?.ex && value === by) {\n\t\t\tawait c.expire(fullKey, options.ex);\n\t\t}\n\t\treturn value;\n\t}\n\n\tasync scan(options: RedisScanOptions = {}): Promise<RedisScanResult> {\n\t\tconst cursor = typeof options.cursor === \"number\" || typeof options.cursor === \"string\"\n\t\t\t? options.cursor\n\t\t\t: 0;\n\t\tconst [next, keys] = await (await this.getClient()).scan(\n\t\t\tcursor,\n\t\t\toptions.match ?? \"*\",\n\t\t\toptions.count ?? 100,\n\t\t);\n\t\treturn {\n\t\t\tcursor: next,\n\t\t\tkeys: (keys ?? []).map((k) => this.stripPrefix(k)),\n\t\t};\n\t}\n\n\tasync close(): Promise<void> {\n\t\tif (this.client) {\n\t\t\ttry {\n\t\t\t\tawait this.client.quit();\n\t\t\t} catch {\n\t\t\t\tthis.client.disconnect();\n\t\t\t}\n\t\t\tthis.client = null;\n\t\t}\n\t}\n}\n",
9
- "/**\n * Runtime detection and factory for `nexusjs/redis` adapters.\n *\n * - `Bun` → `BunRedisAdapter` (built-in `Bun.redis`, no extra package).\n * - `node` → `NodeRedisAdapter` (uses `ioredis` — install separately).\n * - `cloudflare` (Workers / Pages) → `CloudflareKVAdapter` (Workers KV).\n * - `memory` → `MemoryRedisAdapter` (always available, no external dep).\n *\n * The factory `createRedisClient(config)` auto-detects when\n * `config.adapter` is omitted.\n */\n\nimport type { RedisAdapterKind, RedisClient, RedisConfig } from \"../types.js\";\nimport { BunRedisAdapter } from \"./bun.js\";\nimport { CloudflareKVAdapter } from \"./cloudflare.js\";\nimport { MemoryRedisAdapter } from \"./memory.js\";\nimport { NodeRedisAdapter } from \"./node.js\";\n\n/** Detect the active runtime. */\nexport function detectRedisRuntime(): RedisAdapterKind {\n\t// Cloudflare Workers — most specific first.\n\tif (\n\t\ttypeof (globalThis as { caches?: unknown }).caches !== \"undefined\" &&\n\t\ttypeof (globalThis as { WebSocketPair?: unknown }).WebSocketPair !== \"undefined\"\n\t) {\n\t\treturn \"cloudflare\";\n\t}\n\tif (typeof (globalThis as { Bun?: unknown }).Bun !== \"undefined\") return \"bun\";\n\tif (typeof process !== \"undefined\" && process.versions?.node) return \"node\";\n\treturn \"memory\";\n}\n\n/**\n * Create a Redis client with the configured (or auto-detected)\n * adapter.\n *\n * const client = createRedisClient({ adapter: \"bun\", url: \"redis://localhost:6379\" });\n * await client.set(\"hello\", \"world\", { ex: 60 });\n * console.log(await client.get(\"hello\")); // → \"world\"\n */\nexport function createRedisClient(config: RedisConfig = {}): RedisClient {\n\tconst adapter = config.adapter ?? detectRedisRuntime();\n\tswitch (adapter) {\n\t\tcase \"bun\":\n\t\t\treturn new BunRedisAdapter(config);\n\t\tcase \"node\":\n\t\t\treturn new NodeRedisAdapter(config);\n\t\tcase \"cloudflare\":\n\t\t\treturn new CloudflareKVAdapter(config);\n\t\tcase \"memory\":\n\t\t\treturn new MemoryRedisAdapter(config);\n\t\tdefault: {\n\t\t\tconst _exhaustive: never = adapter;\n\t\t\tthrow new Error(`unknown redis adapter: ${_exhaustive as string}`);\n\t\t}\n\t}\n}\n\nexport {\n\tBunRedisAdapter,\n\tCloudflareKVAdapter,\n\tMemoryRedisAdapter,\n\tNodeRedisAdapter,\n};",
5
+ "/**\n * Bun runtime adapter for `@nexusts/redis`.\n *\n * Uses the built-in `Bun.redis` client. No extra package needed.\n * The client is lazily opened on first use.\n */\n\nimport type {\n\tRedisClient,\n\tRedisConfig,\n\tRedisScanOptions,\n\tRedisScanResult,\n\tRedisSetOptions,\n\tRedisValue,\n} from \"../types.js\";\n\n/** The shape we need from `Bun.redis()`. */\ninterface BunRedisClient {\n\tget(key: string): Promise<string | null>;\n\tset(\n\t\tkey: string,\n\t\tvalue: string,\n\t\toptions?: { EX?: number; PX?: number; NX?: boolean; XX?: boolean },\n\t): Promise<\"OK\" | null>;\n\tdel(key: string): Promise<number>;\n\texists(key: string): Promise<number>;\n\tincr(key: string): Promise<number>;\n\tscan(cursor: number, options?: { MATCH?: string; COUNT?: number }): Promise<{\n\t\tcursor: number;\n\t\tkeys: string[];\n\t}>;\n\tclose(): void | Promise<void>;\n}\n\nexport class BunRedisAdapter implements RedisClient {\n\treadonly adapter = \"bun\" as const;\n\tprivate client: BunRedisClient | null = null;\n\tprivate readonly url: string;\n\tprivate readonly keyPrefix: string;\n\tprivate readonly defaultTtlSeconds: number;\n\n\tconstructor(config: RedisConfig = {}) {\n\t\tthis.url = config.url ?? process.env[\"REDIS_URL\"] ?? \"redis://localhost:6379\";\n\t\tthis.keyPrefix = config.keyPrefix ?? \"\";\n\t\tthis.defaultTtlSeconds = config.defaultTtlSeconds ?? 0;\n\t}\n\n\tprivate getClient(): BunRedisClient {\n\t\tif (this.client) return this.client;\n\t\t// Bun.redis is a global; cast through unknown so we can compile\n\t\t// in environments that don't have Bun's lib.d.ts loaded.\n\t\tconst bun = (globalThis as unknown as { Bun?: { redis: (url: string) => BunRedisClient } }).Bun;\n\t\tif (!bun || typeof bun.redis !== \"function\") {\n\t\t\tthrow new Error(\n\t\t\t\t\"BunRedisAdapter can only be used in a Bun runtime. \" +\n\t\t\t\t\t\"On Node, use NodeRedisAdapter (install ioredis).\",\n\t\t\t);\n\t\t}\n\t\tthis.client = bun.redis(this.url);\n\t\treturn this.client!;\n\t}\n\n\tprivate k(key: string): string {\n\t\treturn this.keyPrefix + key;\n\t}\n\n\tasync get(key: string): Promise<RedisValue> {\n\t\treturn this.getClient().get(this.k(key));\n\t}\n\n\tasync set(key: string, value: string, options?: RedisSetOptions): Promise<void> {\n\t\tconst ex = options?.ex ?? this.defaultTtlSeconds ?? undefined;\n\t\tconst px = options?.px ?? undefined;\n\t\tconst bunOpts: { EX?: number; PX?: number; NX?: boolean; XX?: boolean } = {};\n\t\tif (ex) bunOpts.EX = ex;\n\t\tif (px) bunOpts.PX = px;\n\t\tif (options?.nx) bunOpts.NX = true;\n\t\tif (options?.xx) bunOpts.XX = true;\n\t\tawait this.getClient().set(this.k(key), value, bunOpts);\n\t}\n\n\tasync del(key: string): Promise<number> {\n\t\treturn this.getClient().del(this.k(key));\n\t}\n\n\tasync exists(key: string): Promise<boolean> {\n\t\treturn (await this.getClient().exists(this.k(key))) > 0;\n\t}\n\n\tasync incr(key: string, by = 1, options?: { ex?: number }): Promise<number> {\n\t\tconst fullKey = this.k(key);\n\t\tconst client = this.getClient();\n\t\tconst value = await client.incr(fullKey);\n\t\t// On the first increment (value === 1), apply the TTL if requested.\n\t\tif (options?.ex && value === by) {\n\t\t\tawait client.set(fullKey, String(value), { EX: options.ex });\n\t\t}\n\t\treturn value;\n\t}\n\n\tasync scan(options: RedisScanOptions = {}): Promise<RedisScanResult> {\n\t\tconst cursor = typeof options.cursor === \"number\" ? options.cursor : 0;\n\t\tconst res = await this.getClient().scan(cursor, {\n\t\t\tMATCH: options.match ?? \"*\",\n\t\t\tCOUNT: options.count ?? 100,\n\t\t});\n\t\treturn {\n\t\t\tcursor: res.cursor,\n\t\t\tkeys: (res.keys ?? []).map((k) =>\n\t\t\t\tthis.keyPrefix && k.startsWith(this.keyPrefix)\n\t\t\t\t\t? k.slice(this.keyPrefix.length)\n\t\t\t\t\t: k,\n\t\t\t),\n\t\t};\n\t}\n\n\tasync close(): Promise<void> {\n\t\tif (this.client) {\n\t\t\tawait this.client.close();\n\t\t\tthis.client = null;\n\t\t}\n\t}\n}\n",
6
+ "/**\n * Cloudflare Workers KV adapter for `@nexusts/redis`.\n *\n * Workers KV is **not** Redis, but the surface is close enough\n * that the same `RedisClient` API can sit on top of it. The\n * adapter:\n *\n * - Maps `set(key, value, { ex })` to `KVNamespace.put()` with\n * `expirationTtl` (seconds).\n * - Maps `scan({ match })` to `KVNamespace.list({ prefix })`,\n * which is the closest thing KV offers to Redis `SCAN MATCH`.\n * The `cursor` field is a Cloudflare opaque string.\n * - `del` uses `KVNamespace.delete()`.\n * - `incr` falls back to a small read-modify-write: get the\n * value, parse as int, add, put back. (KV doesn't have\n * atomic INCR.) Use with care — concurrent writes can lose\n * updates. For high-contention counters use a real Redis.\n *\n * Cloudflare imposes a 25 MB value limit per key and a global\n * 100k reads / 1000 writes per second per KV namespace. Don't\n * store large objects in KV.\n *\n * To use:\n *\n * // In a Worker request handler:\n * const client = createRedisClient({\n * adapter: \"cloudflare\",\n * kv: c.env.SESSIONS, // KVNamespace binding from wrangler.toml\n * });\n */\n\nimport type {\n\tKVNamespaceLike,\n\tRedisClient,\n\tRedisConfig,\n\tRedisScanOptions,\n\tRedisScanResult,\n\tRedisSetOptions,\n\tRedisValue,\n} from \"../types.js\";\n\nexport class CloudflareKVAdapter implements RedisClient {\n\treadonly adapter = \"cloudflare\" as const;\n\tprivate kv: KVNamespaceLike | null = null;\n\tprivate readonly keyPrefix: string;\n\tprivate readonly defaultTtlSeconds: number;\n\n\tconstructor(config: RedisConfig = {}) {\n\t\tthis.keyPrefix = config.keyPrefix ?? \"\";\n\t\tthis.defaultTtlSeconds = config.defaultTtlSeconds ?? 0;\n\t\tif (config.kv) this.kv = config.kv;\n\t}\n\n\tprivate getKV(): KVNamespaceLike {\n\t\tif (this.kv) return this.kv;\n\t\t// Auto-detect from `globalThis.env` (Workers context).\n\t\tconst env = (globalThis as unknown as { env?: { KV?: KVNamespaceLike } }).env;\n\t\tif (env?.KV) {\n\t\t\tthis.kv = env.KV;\n\t\t\treturn this.kv;\n\t\t}\n\t\tthrow new Error(\n\t\t\t\"CloudflareKVAdapter could not find a KV binding. \" +\n\t\t\t\t\"Pass it explicitly via RedisConfig({ kv }) or run inside a \" +\n\t\t\t\t\"Workers request handler where `c.env.KV` is available.\",\n\t\t);\n\t}\n\n\tprivate k(key: string): string {\n\t\treturn this.keyPrefix + key;\n\t}\n\n\tprivate stripPrefix(key: string): string {\n\t\treturn this.keyPrefix && key.startsWith(this.keyPrefix)\n\t\t\t? key.slice(this.keyPrefix.length)\n\t\t\t: key;\n\t}\n\n\tasync get(key: string): Promise<RedisValue> {\n\t\treturn this.getKV().get(this.k(key));\n\t}\n\n\tasync set(key: string, value: string, options?: RedisSetOptions): Promise<void> {\n\t\tconst ex = options?.ex ?? this.defaultTtlSeconds ?? undefined;\n\t\tif (ex && ex > 0) {\n\t\t\tawait this.getKV().put(this.k(key), value, { expirationTtl: ex });\n\t\t} else {\n\t\t\tawait this.getKV().put(this.k(key), value);\n\t\t}\n\t}\n\n\tasync del(key: string): Promise<number> {\n\t\t// KV.delete returns void; we conservatively return 1 if a value\n\t\t// existed before, 0 otherwise.\n\t\tconst existed = (await this.getKV().get(this.k(key))) !== null;\n\t\tawait this.getKV().delete(this.k(key));\n\t\treturn existed ? 1 : 0;\n\t}\n\n\tasync exists(key: string): Promise<boolean> {\n\t\treturn (await this.getKV().get(this.k(key))) !== null;\n\t}\n\n\tasync incr(key: string, by = 1, options?: { ex?: number }): Promise<number> {\n\t\t// Naive read-modify-write. KV doesn't have atomic INCR. Don't\n\t\t// use this for high-contention counters.\n\t\tconst fullKey = this.k(key);\n\t\tconst raw = await this.getKV().get(fullKey);\n\t\tconst current = raw ? Number.parseInt(raw, 10) || 0 : 0;\n\t\tconst next = current + by;\n\t\tconst ex = options?.ex ?? this.defaultTtlSeconds ?? undefined;\n\t\tif (ex && ex > 0) {\n\t\t\tawait this.getKV().put(fullKey, String(next), { expirationTtl: ex });\n\t\t} else {\n\t\t\tawait this.getKV().put(fullKey, String(next));\n\t\t}\n\t\treturn next;\n\t}\n\n\tasync scan(options: RedisScanOptions = {}): Promise<RedisScanResult> {\n\t\t// KV.list() supports `prefix` only — convert a `match` glob\n\t\t// to a prefix. Full glob support would require client-side\n\t\t// filtering. The cursor is a Cloudflare opaque string.\n\t\tconst match = options.match ?? \"*\";\n\t\tconst prefix = globToPrefix(match, this.keyPrefix);\n\t\tconst res = await this.getKV().list({\n\t\t\tprefix,\n\t\t\tlimit: options.count ?? 100,\n\t\t\tcursor: typeof options.cursor === \"string\" ? options.cursor : undefined,\n\t\t});\n\t\treturn {\n\t\t\tcursor: res.cursor,\n\t\t\tkeys: (res.keys ?? []).map((k) => this.stripPrefix(k.name)),\n\t\t};\n\t}\n\n\tasync close(): Promise<void> {\n\t\t// KV has no client to close.\n\t}\n}\n\n/** Convert a Redis-style glob (`*`, `?`, `[abc]`) to a KV prefix. */\nfunction globToPrefix(glob: string, keyPrefix: string): string {\n\t// Everything up to the first glob meta-character.\n\tconst meta = /[*?\\[]/.exec(glob);\n\tconst base = meta ? glob.slice(0, meta.index) : glob;\n\treturn (keyPrefix ?? \"\") + base;\n}\n",
7
+ "/**\n * In-memory adapter for `@nexusts/redis`.\n *\n * Used for tests and single-process dev. Not cluster-safe; values\n * don't survive process restart.\n */\n\nimport type {\n\tRedisClient,\n\tRedisConfig,\n\tRedisScanOptions,\n\tRedisScanResult,\n\tRedisSetOptions,\n\tRedisValue,\n} from \"../types.js\";\n\ninterface Entry {\n\tvalue: string;\n\texpiresAt: number | null; // ms epoch; null = no expiry\n}\n\nexport class MemoryRedisAdapter implements RedisClient {\n\treadonly adapter = \"memory\" as const;\n\tprivate data = new Map<string, Entry>();\n\tprivate readonly keyPrefix: string;\n\tprivate readonly defaultTtlSeconds: number;\n\n\tconstructor(config: RedisConfig = {}) {\n\t\tthis.keyPrefix = config.keyPrefix ?? \"\";\n\t\tthis.defaultTtlSeconds = config.defaultTtlSeconds ?? 0;\n\t}\n\n\tprivate k(key: string): string {\n\t\treturn this.keyPrefix + key;\n\t}\n\n\tprivate stripPrefix(key: string): string {\n\t\treturn this.keyPrefix && key.startsWith(this.keyPrefix)\n\t\t\t? key.slice(this.keyPrefix.length)\n\t\t\t: key;\n\t}\n\n\tprivate isExpired(e: Entry | undefined): e is undefined {\n\t\treturn !e || (e.expiresAt !== null && e.expiresAt < Date.now());\n\t}\n\n\tprivate purge(key: string): void {\n\t\tconst e = this.data.get(key);\n\t\tif (this.isExpired(e)) this.data.delete(key);\n\t}\n\n\tasync get(key: string): Promise<RedisValue> {\n\t\tconst k = this.k(key);\n\t\tthis.purge(k);\n\t\tconst e = this.data.get(k);\n\t\treturn e ? e.value : null;\n\t}\n\n\tasync set(key: string, value: string, options?: RedisSetOptions): Promise<void> {\n\t\tconst k = this.k(key);\n\t\tconst ttl =\n\t\t\toptions?.ex !== undefined\n\t\t\t\t? options.ex\n\t\t\t\t: options?.px !== undefined\n\t\t\t\t\t? options.px / 1000\n\t\t\t\t\t: this.defaultTtlSeconds;\n\t\tconst expiresAt = ttl && ttl > 0 ? Date.now() + ttl * 1000 : null;\n\t\tif (options?.nx && !this.isExpired(this.data.get(k))) return; // NX semantics\n\t\tif (options?.xx && this.isExpired(this.data.get(k))) return; // XX semantics\n\t\tthis.data.set(k, { value, expiresAt });\n\t}\n\n\tasync del(key: string): Promise<number> {\n\t\tconst k = this.k(key);\n\t\tconst existed = this.data.delete(k);\n\t\treturn existed ? 1 : 0;\n\t}\n\n\tasync exists(key: string): Promise<boolean> {\n\t\tconst k = this.k(key);\n\t\tthis.purge(k);\n\t\treturn this.data.has(k);\n\t}\n\n\tasync incr(key: string, by = 1, options?: { ex?: number }): Promise<number> {\n\t\tconst k = this.k(key);\n\t\tthis.purge(k);\n\t\tconst e = this.data.get(k);\n\t\tconst current = e ? Number.parseInt(e.value, 10) || 0 : 0;\n\t\tconst next = current + by;\n\t\tconst ttl = options?.ex ?? this.defaultTtlSeconds ?? undefined;\n\t\tconst expiresAt = ttl && ttl > 0 ? Date.now() + ttl * 1000 : null;\n\t\tthis.data.set(k, { value: String(next), expiresAt });\n\t\treturn next;\n\t}\n\n\tasync scan(options: RedisScanOptions = {}): Promise<RedisScanResult> {\n\t\tconst match = options.match ?? \"*\";\n\t\t// The match pattern is matched against the full key (with\n\t\t// prefix included). Callers should write patterns that\n\t\t// include the prefix.\n\t\tconst re = globToRegex(match);\n\t\tconst keys: string[] = [];\n\t\tfor (const k of this.data.keys()) {\n\t\t\tif (this.keyPrefix && !k.startsWith(this.keyPrefix)) continue;\n\t\t\tif (re.test(k)) keys.push(this.stripPrefix(k));\n\t\t}\n\t\treturn { cursor: \"0\", keys };\n\t}\n\n\tasync close(): Promise<void> {\n\t\tthis.data.clear();\n\t}\n}\n\nfunction globToRegex(glob: string): RegExp {\n\tconst escaped = glob\n\t\t.replace(/[.+^${}()|[\\]\\\\]/g, \"\\\\$&\")\n\t\t.replace(/\\*/g, \".*\")\n\t\t.replace(/\\?/g, \".\");\n\treturn new RegExp(\"^\" + escaped + \"$\");\n}\n",
8
+ "/**\n * Node.js runtime adapter for `@nexusts/redis`.\n *\n * Uses the `ioredis` package. The package is an **optional**\n * peer dependency of `@nexusts/redis` — install it only when targeting\n * Node:\n *\n * bun add ioredis\n *\n * The adapter opens a single shared client per `RedisConfig` and\n * reuses it across `get` / `set` / `del` / `scan` calls. Use\n * `await client.close()` to release the connection.\n */\n\nimport type {\n\tRedisClient,\n\tRedisConfig,\n\tRedisScanOptions,\n\tRedisScanResult,\n\tRedisSetOptions,\n\tRedisValue,\n} from \"../types.js\";\n\n/** The shape we need from `ioredis`. We don't depend on the\n * package at the type level so users who don't install it\n * still type-check. */\ninterface IORedisLike {\n\tget(key: string): Promise<string | null>;\n\tset(\n\t\tkey: string,\n\t\tvalue: string,\n\t\tmodeOrEx?: string | number,\n\t\tduration?: number,\n\t\tflag?: \"NX\" | \"XX\",\n\t): Promise<\"OK\" | null>;\n\tdel(key: string | string[]): Promise<number>;\n\texists(key: string): Promise<number>;\n\tincr(key: string): Promise<number>;\n\texpire(key: string, seconds: number): Promise<number>;\n\tscan(\n\t\tcursor: number | string,\n\t\tmatch: string,\n\t\tcount: number,\n\t): Promise<[string | number, string[]]>;\n\tquit(): Promise<\"OK\">;\n\tdisconnect(): void;\n}\n\nexport class NodeRedisAdapter implements RedisClient {\n\treadonly adapter = \"node\" as const;\n\tprivate client: IORedisLike | null = null;\n\tprivate readonly url: string;\n\tprivate readonly keyPrefix: string;\n\tprivate readonly defaultTtlSeconds: number;\n\tprivate readonly nodeOptions: Record<string, unknown>;\n\n\tconstructor(config: RedisConfig = {}) {\n\t\tthis.url = config.url ?? process.env[\"REDIS_URL\"] ?? \"redis://localhost:6379\";\n\t\tthis.keyPrefix = config.keyPrefix ?? \"\";\n\t\tthis.defaultTtlSeconds = config.defaultTtlSeconds ?? 0;\n\t\tthis.nodeOptions = config.nodeOptions ?? {};\n\t}\n\n\tprivate async getClient(): Promise<IORedisLike> {\n\t\tif (this.client) return this.client;\n\t\ttry {\n\t\t\t// @ts-ignore - optional peer dep\n\t\t\tconst mod = await import(\"ioredis\");\n\t\t\tconst Ctor = (mod as any).default ?? (mod as any);\n\t\t\tif (typeof Ctor !== \"function\") {\n\t\t\t\tthrow new Error(\"ioredis module did not export a constructor\");\n\t\t\t}\n\t\t\tthis.client = new Ctor(this.url, this.nodeOptions) as IORedisLike;\n\t\t} catch (err) {\n\t\t\tthrow new Error(\n\t\t\t\t\"NodeRedisAdapter requires the `ioredis` package. \" +\n\t\t\t\t\t\"Install with: bun add ioredis\",\n\t\t\t);\n\t\t}\n\t\treturn this.client!;\n\t}\n\n\tprivate k(key: string): string {\n\t\treturn this.keyPrefix + key;\n\t}\n\n\tprivate stripPrefix(key: string): string {\n\t\treturn this.keyPrefix && key.startsWith(this.keyPrefix)\n\t\t\t? key.slice(this.keyPrefix.length)\n\t\t\t: key;\n\t}\n\n\tasync get(key: string): Promise<RedisValue> {\n\t\treturn (await this.getClient()).get(this.k(key));\n\t}\n\n\tasync set(key: string, value: string, options?: RedisSetOptions): Promise<void> {\n\t\tconst ex = options?.ex ?? this.defaultTtlSeconds ?? undefined;\n\t\tconst c = await this.getClient();\n\t\tconst fullKey = this.k(key);\n\t\t// ioredis set signature: set(key, value, EX, seconds, NX|XX) or\n\t\t// set(key, value, NX|XX). We dispatch on the combination of\n\t\t// flags.\n\t\tif (options?.nx) {\n\t\t\tawait c.set(fullKey, value, \"EX\", ex ?? 0, \"NX\");\n\t\t} else if (options?.xx) {\n\t\t\tawait c.set(fullKey, value, \"EX\", ex ?? 0, \"XX\");\n\t\t} else if (ex) {\n\t\t\tawait c.set(fullKey, value, \"EX\", ex);\n\t\t} else {\n\t\t\tawait c.set(fullKey, value);\n\t\t}\n\t}\n\n\tasync del(key: string): Promise<number> {\n\t\treturn (await this.getClient()).del(this.k(key));\n\t}\n\n\tasync exists(key: string): Promise<boolean> {\n\t\tconst n = await (await this.getClient()).exists(this.k(key));\n\t\treturn n > 0;\n\t}\n\n\tasync incr(key: string, by = 1, options?: { ex?: number }): Promise<number> {\n\t\tconst fullKey = this.k(key);\n\t\tconst c = await this.getClient();\n\t\tlet value: number;\n\t\tif (by === 1) {\n\t\t\tvalue = await c.incr(fullKey);\n\t\t} else {\n\t\t\t// ioredis doesn't have incrby with delta? It does — use\n\t\t\t// the multi-step: `incrby` is the ioredis method. Patch\n\t\t\t// the IORedisLike to include it.\n\t\t\tvalue = await (c as unknown as { incrby: (k: string, n: number) => Promise<number> }).incrby(fullKey, by);\n\t\t}\n\t\tif (options?.ex && value === by) {\n\t\t\tawait c.expire(fullKey, options.ex);\n\t\t}\n\t\treturn value;\n\t}\n\n\tasync scan(options: RedisScanOptions = {}): Promise<RedisScanResult> {\n\t\tconst cursor = typeof options.cursor === \"number\" || typeof options.cursor === \"string\"\n\t\t\t? options.cursor\n\t\t\t: 0;\n\t\tconst [next, keys] = await (await this.getClient()).scan(\n\t\t\tcursor,\n\t\t\toptions.match ?? \"*\",\n\t\t\toptions.count ?? 100,\n\t\t);\n\t\treturn {\n\t\t\tcursor: next,\n\t\t\tkeys: (keys ?? []).map((k) => this.stripPrefix(k)),\n\t\t};\n\t}\n\n\tasync close(): Promise<void> {\n\t\tif (this.client) {\n\t\t\ttry {\n\t\t\t\tawait this.client.quit();\n\t\t\t} catch {\n\t\t\t\tthis.client.disconnect();\n\t\t\t}\n\t\t\tthis.client = null;\n\t\t}\n\t}\n}\n",
9
+ "/**\n * Runtime detection and factory for `@nexusts/redis` adapters.\n *\n * - `Bun` → `BunRedisAdapter` (built-in `Bun.redis`, no extra package).\n * - `node` → `NodeRedisAdapter` (uses `ioredis` — install separately).\n * - `cloudflare` (Workers / Pages) → `CloudflareKVAdapter` (Workers KV).\n * - `memory` → `MemoryRedisAdapter` (always available, no external dep).\n *\n * The factory `createRedisClient(config)` auto-detects when\n * `config.adapter` is omitted.\n */\n\nimport type { RedisAdapterKind, RedisClient, RedisConfig } from \"../types.js\";\nimport { BunRedisAdapter } from \"./bun.js\";\nimport { CloudflareKVAdapter } from \"./cloudflare.js\";\nimport { MemoryRedisAdapter } from \"./memory.js\";\nimport { NodeRedisAdapter } from \"./node.js\";\n\n/** Detect the active runtime. */\nexport function detectRedisRuntime(): RedisAdapterKind {\n\t// Cloudflare Workers — most specific first.\n\tif (\n\t\ttypeof (globalThis as { caches?: unknown }).caches !== \"undefined\" &&\n\t\ttypeof (globalThis as { WebSocketPair?: unknown }).WebSocketPair !== \"undefined\"\n\t) {\n\t\treturn \"cloudflare\";\n\t}\n\tif (typeof (globalThis as { Bun?: unknown }).Bun !== \"undefined\") return \"bun\";\n\tif (typeof process !== \"undefined\" && process.versions?.node) return \"node\";\n\treturn \"memory\";\n}\n\n/**\n * Create a Redis client with the configured (or auto-detected)\n * adapter.\n *\n * const client = createRedisClient({ adapter: \"bun\", url: \"redis://localhost:6379\" });\n * await client.set(\"hello\", \"world\", { ex: 60 });\n * console.log(await client.get(\"hello\")); // → \"world\"\n */\nexport function createRedisClient(config: RedisConfig = {}): RedisClient {\n\tconst adapter = config.adapter ?? detectRedisRuntime();\n\tswitch (adapter) {\n\t\tcase \"bun\":\n\t\t\treturn new BunRedisAdapter(config);\n\t\tcase \"node\":\n\t\t\treturn new NodeRedisAdapter(config);\n\t\tcase \"cloudflare\":\n\t\t\treturn new CloudflareKVAdapter(config);\n\t\tcase \"memory\":\n\t\t\treturn new MemoryRedisAdapter(config);\n\t\tdefault: {\n\t\t\tconst _exhaustive: never = adapter;\n\t\t\tthrow new Error(`unknown redis adapter: ${_exhaustive as string}`);\n\t\t}\n\t}\n}\n\nexport {\n\tBunRedisAdapter,\n\tCloudflareKVAdapter,\n\tMemoryRedisAdapter,\n\tNodeRedisAdapter,\n};",
10
10
  "/**\n * `RedisModule` — wires `RedisClient` into the DI container.\n *\n * @Module({\n * imports: [\n * RedisModule.forRoot({\n * adapter: \"bun\", // or \"node\" | \"cloudflare\" | \"memory\"\n * url: \"redis://localhost:6379\",\n * keyPrefix: \"myapp:\",\n * }),\n * ],\n * })\n * class AppModule {}\n *\n * @Injectable()\n * class UserService {\n * constructor(@Inject(REDIS_CLIENT_TOKEN) private redis: RedisClient) {}\n * async countVisits(userId: string) {\n * return this.redis.incr(`visits:${userId}`, 1, { ex: 60 * 60 });\n * }\n * }\n *\n * Most users won't import `RedisModule` directly — the\n * `SessionModule` and `CacheModule` use it under the hood when\n * the user configures a `redis` / `cloudflare-kv` backend.\n */\n\nimport { Module } from \"@nexusts/core\";\nimport { createRedisClient } from \"./adapters/index.js\";\nimport type { RedisClient, RedisConfig } from \"./types.js\";\n\nexport const REDIS_CLIENT_TOKEN = Symbol.for(\"nexus:RedisClient\");\n\n@Module({\n\tproviders: [\n\t\t{\n\t\t\tprovide: REDIS_CLIENT_TOKEN,\n\t\t\tuseFactory: () => createRedisClient(),\n\t\t},\n\t],\n\texports: [REDIS_CLIENT_TOKEN],\n})\nexport class RedisModule {\n\tstatic forRoot(config: RedisConfig = {}) {\n\t\t@Module({\n\t\t\tproviders: [\n\t\t\t\t{\n\t\t\t\t\tprovide: REDIS_CLIENT_TOKEN,\n\t\t\t\t\tuseFactory: () => createRedisClient(config),\n\t\t\t\t},\n\t\t\t\t{ provide: \"REDIS_CONFIG\", useValue: config },\n\t\t\t],\n\t\t\texports: [REDIS_CLIENT_TOKEN, \"REDIS_CONFIG\"],\n\t\t})\n\t\tclass ConfiguredRedisModule {}\n\t\tObject.defineProperty(ConfiguredRedisModule, \"name\", {\n\t\t\tvalue: \"ConfiguredRedisModule\",\n\t\t});\n\t\treturn ConfiguredRedisModule as unknown as typeof RedisModule;\n\t}\n}\n"
11
11
  ],
12
12
  "mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAkCO,MAAM,gBAAuC;AAAA,EAC1C,UAAU;AAAA,EACX,SAAgC;AAAA,EACvB;AAAA,EACA;AAAA,EACA;AAAA,EAEjB,WAAW,CAAC,SAAsB,CAAC,GAAG;AAAA,IACrC,KAAK,MAAM,OAAO,OAAO,QAAQ,IAAI,gBAAgB;AAAA,IACrD,KAAK,YAAY,OAAO,aAAa;AAAA,IACrC,KAAK,oBAAoB,OAAO,qBAAqB;AAAA;AAAA,EAG9C,SAAS,GAAmB;AAAA,IACnC,IAAI,KAAK;AAAA,MAAQ,OAAO,KAAK;AAAA,IAG7B,MAAM,MAAO,WAA+E;AAAA,IAC5F,IAAI,CAAC,OAAO,OAAO,IAAI,UAAU,YAAY;AAAA,MAC5C,MAAM,IAAI,MACT,wDACC,kDACF;AAAA,IACD;AAAA,IACA,KAAK,SAAS,IAAI,MAAM,KAAK,GAAG;AAAA,IAChC,OAAO,KAAK;AAAA;AAAA,EAGL,CAAC,CAAC,KAAqB;AAAA,IAC9B,OAAO,KAAK,YAAY;AAAA;AAAA,OAGnB,IAAG,CAAC,KAAkC;AAAA,IAC3C,OAAO,KAAK,UAAU,EAAE,IAAI,KAAK,EAAE,GAAG,CAAC;AAAA;AAAA,OAGlC,IAAG,CAAC,KAAa,OAAe,SAA0C;AAAA,IAC/E,MAAM,KAAK,SAAS,MAAM,KAAK,qBAAqB;AAAA,IACpD,MAAM,KAAK,SAAS,MAAM;AAAA,IAC1B,MAAM,UAAoE,CAAC;AAAA,IAC3E,IAAI;AAAA,MAAI,QAAQ,KAAK;AAAA,IACrB,IAAI;AAAA,MAAI,QAAQ,KAAK;AAAA,IACrB,IAAI,SAAS;AAAA,MAAI,QAAQ,KAAK;AAAA,IAC9B,IAAI,SAAS;AAAA,MAAI,QAAQ,KAAK;AAAA,IAC9B,MAAM,KAAK,UAAU,EAAE,IAAI,KAAK,EAAE,GAAG,GAAG,OAAO,OAAO;AAAA;AAAA,OAGjD,IAAG,CAAC,KAA8B;AAAA,IACvC,OAAO,KAAK,UAAU,EAAE,IAAI,KAAK,EAAE,GAAG,CAAC;AAAA;AAAA,OAGlC,OAAM,CAAC,KAA+B;AAAA,IAC3C,OAAQ,MAAM,KAAK,UAAU,EAAE,OAAO,KAAK,EAAE,GAAG,CAAC,IAAK;AAAA;AAAA,OAGjD,KAAI,CAAC,KAAa,KAAK,GAAG,SAA4C;AAAA,IAC3E,MAAM,UAAU,KAAK,EAAE,GAAG;AAAA,IAC1B,MAAM,SAAS,KAAK,UAAU;AAAA,IAC9B,MAAM,QAAQ,MAAM,OAAO,KAAK,OAAO;AAAA,IAEvC,IAAI,SAAS,MAAM,UAAU,IAAI;AAAA,MAChC,MAAM,OAAO,IAAI,SAAS,OAAO,KAAK,GAAG,EAAE,IAAI,QAAQ,GAAG,CAAC;AAAA,IAC5D;AAAA,IACA,OAAO;AAAA;AAAA,OAGF,KAAI,CAAC,UAA4B,CAAC,GAA6B;AAAA,IACpE,MAAM,SAAS,OAAO,QAAQ,WAAW,WAAW,QAAQ,SAAS;AAAA,IACrE,MAAM,MAAM,MAAM,KAAK,UAAU,EAAE,KAAK,QAAQ;AAAA,MAC/C,OAAO,QAAQ,SAAS;AAAA,MACxB,OAAO,QAAQ,SAAS;AAAA,IACzB,CAAC;AAAA,IACD,OAAO;AAAA,MACN,QAAQ,IAAI;AAAA,MACZ,OAAO,IAAI,QAAQ,CAAC,GAAG,IAAI,CAAC,MAC3B,KAAK,aAAa,EAAE,WAAW,KAAK,SAAS,IAC1C,EAAE,MAAM,KAAK,UAAU,MAAM,IAC7B,CACJ;AAAA,IACD;AAAA;AAAA,OAGK,MAAK,GAAkB;AAAA,IAC5B,IAAI,KAAK,QAAQ;AAAA,MAChB,MAAM,KAAK,OAAO,MAAM;AAAA,MACxB,KAAK,SAAS;AAAA,IACf;AAAA;AAEF;;;ACjFO,MAAM,oBAA2C;AAAA,EAC9C,UAAU;AAAA,EACX,KAA6B;AAAA,EACpB;AAAA,EACA;AAAA,EAEjB,WAAW,CAAC,SAAsB,CAAC,GAAG;AAAA,IACrC,KAAK,YAAY,OAAO,aAAa;AAAA,IACrC,KAAK,oBAAoB,OAAO,qBAAqB;AAAA,IACrD,IAAI,OAAO;AAAA,MAAI,KAAK,KAAK,OAAO;AAAA;AAAA,EAGzB,KAAK,GAAoB;AAAA,IAChC,IAAI,KAAK;AAAA,MAAI,OAAO,KAAK;AAAA,IAEzB,MAAM,MAAO,WAA6D;AAAA,IAC1E,IAAI,KAAK,IAAI;AAAA,MACZ,KAAK,KAAK,IAAI;AAAA,MACd,OAAO,KAAK;AAAA,IACb;AAAA,IACA,MAAM,IAAI,MACT,sDACC,gEACA,wDACF;AAAA;AAAA,EAGO,CAAC,CAAC,KAAqB;AAAA,IAC9B,OAAO,KAAK,YAAY;AAAA;AAAA,EAGjB,WAAW,CAAC,KAAqB;AAAA,IACxC,OAAO,KAAK,aAAa,IAAI,WAAW,KAAK,SAAS,IACnD,IAAI,MAAM,KAAK,UAAU,MAAM,IAC/B;AAAA;AAAA,OAGE,IAAG,CAAC,KAAkC;AAAA,IAC3C,OAAO,KAAK,MAAM,EAAE,IAAI,KAAK,EAAE,GAAG,CAAC;AAAA;AAAA,OAG9B,IAAG,CAAC,KAAa,OAAe,SAA0C;AAAA,IAC/E,MAAM,KAAK,SAAS,MAAM,KAAK,qBAAqB;AAAA,IACpD,IAAI,MAAM,KAAK,GAAG;AAAA,MACjB,MAAM,KAAK,MAAM,EAAE,IAAI,KAAK,EAAE,GAAG,GAAG,OAAO,EAAE,eAAe,GAAG,CAAC;AAAA,IACjE,EAAO;AAAA,MACN,MAAM,KAAK,MAAM,EAAE,IAAI,KAAK,EAAE,GAAG,GAAG,KAAK;AAAA;AAAA;AAAA,OAIrC,IAAG,CAAC,KAA8B;AAAA,IAGvC,MAAM,UAAW,MAAM,KAAK,MAAM,EAAE,IAAI,KAAK,EAAE,GAAG,CAAC,MAAO;AAAA,IAC1D,MAAM,KAAK,MAAM,EAAE,OAAO,KAAK,EAAE,GAAG,CAAC;AAAA,IACrC,OAAO,UAAU,IAAI;AAAA;AAAA,OAGhB,OAAM,CAAC,KAA+B;AAAA,IAC3C,OAAQ,MAAM,KAAK,MAAM,EAAE,IAAI,KAAK,EAAE,GAAG,CAAC,MAAO;AAAA;AAAA,OAG5C,KAAI,CAAC,KAAa,KAAK,GAAG,SAA4C;AAAA,IAG3E,MAAM,UAAU,KAAK,EAAE,GAAG;AAAA,IAC1B,MAAM,MAAM,MAAM,KAAK,MAAM,EAAE,IAAI,OAAO;AAAA,IAC1C,MAAM,UAAU,MAAM,OAAO,SAAS,KAAK,EAAE,KAAK,IAAI;AAAA,IACtD,MAAM,OAAO,UAAU;AAAA,IACvB,MAAM,KAAK,SAAS,MAAM,KAAK,qBAAqB;AAAA,IACpD,IAAI,MAAM,KAAK,GAAG;AAAA,MACjB,MAAM,KAAK,MAAM,EAAE,IAAI,SAAS,OAAO,IAAI,GAAG,EAAE,eAAe,GAAG,CAAC;AAAA,IACpE,EAAO;AAAA,MACN,MAAM,KAAK,MAAM,EAAE,IAAI,SAAS,OAAO,IAAI,CAAC;AAAA;AAAA,IAE7C,OAAO;AAAA;AAAA,OAGF,KAAI,CAAC,UAA4B,CAAC,GAA6B;AAAA,IAIpE,MAAM,QAAQ,QAAQ,SAAS;AAAA,IAC/B,MAAM,SAAS,aAAa,OAAO,KAAK,SAAS;AAAA,IACjD,MAAM,MAAM,MAAM,KAAK,MAAM,EAAE,KAAK;AAAA,MACnC;AAAA,MACA,OAAO,QAAQ,SAAS;AAAA,MACxB,QAAQ,OAAO,QAAQ,WAAW,WAAW,QAAQ,SAAS;AAAA,IAC/D,CAAC;AAAA,IACD,OAAO;AAAA,MACN,QAAQ,IAAI;AAAA,MACZ,OAAO,IAAI,QAAQ,CAAC,GAAG,IAAI,CAAC,MAAM,KAAK,YAAY,EAAE,IAAI,CAAC;AAAA,IAC3D;AAAA;AAAA,OAGK,MAAK,GAAkB;AAG9B;AAGA,SAAS,YAAY,CAAC,MAAc,WAA2B;AAAA,EAE9D,MAAM,OAAO,SAAS,KAAK,IAAI;AAAA,EAC/B,MAAM,OAAO,OAAO,KAAK,MAAM,GAAG,KAAK,KAAK,IAAI;AAAA,EAChD,QAAQ,aAAa,MAAM;AAAA;;;AC7HrB,MAAM,mBAA0C;AAAA,EAC7C,UAAU;AAAA,EACX,OAAO,IAAI;AAAA,EACF;AAAA,EACA;AAAA,EAEjB,WAAW,CAAC,SAAsB,CAAC,GAAG;AAAA,IACrC,KAAK,YAAY,OAAO,aAAa;AAAA,IACrC,KAAK,oBAAoB,OAAO,qBAAqB;AAAA;AAAA,EAG9C,CAAC,CAAC,KAAqB;AAAA,IAC9B,OAAO,KAAK,YAAY;AAAA;AAAA,EAGjB,WAAW,CAAC,KAAqB;AAAA,IACxC,OAAO,KAAK,aAAa,IAAI,WAAW,KAAK,SAAS,IACnD,IAAI,MAAM,KAAK,UAAU,MAAM,IAC/B;AAAA;AAAA,EAGI,SAAS,CAAC,GAAsC;AAAA,IACvD,OAAO,CAAC,KAAM,EAAE,cAAc,QAAQ,EAAE,YAAY,KAAK,IAAI;AAAA;AAAA,EAGtD,KAAK,CAAC,KAAmB;AAAA,IAChC,MAAM,IAAI,KAAK,KAAK,IAAI,GAAG;AAAA,IAC3B,IAAI,KAAK,UAAU,CAAC;AAAA,MAAG,KAAK,KAAK,OAAO,GAAG;AAAA;AAAA,OAGtC,IAAG,CAAC,KAAkC;AAAA,IAC3C,MAAM,IAAI,KAAK,EAAE,GAAG;AAAA,IACpB,KAAK,MAAM,CAAC;AAAA,IACZ,MAAM,IAAI,KAAK,KAAK,IAAI,CAAC;AAAA,IACzB,OAAO,IAAI,EAAE,QAAQ;AAAA;AAAA,OAGhB,IAAG,CAAC,KAAa,OAAe,SAA0C;AAAA,IAC/E,MAAM,IAAI,KAAK,EAAE,GAAG;AAAA,IACpB,MAAM,MACL,SAAS,OAAO,YACb,QAAQ,KACR,SAAS,OAAO,YACf,QAAQ,KAAK,OACb,KAAK;AAAA,IACV,MAAM,YAAY,OAAO,MAAM,IAAI,KAAK,IAAI,IAAI,MAAM,OAAO;AAAA,IAC7D,IAAI,SAAS,MAAM,CAAC,KAAK,UAAU,KAAK,KAAK,IAAI,CAAC,CAAC;AAAA,MAAG;AAAA,IACtD,IAAI,SAAS,MAAM,KAAK,UAAU,KAAK,KAAK,IAAI,CAAC,CAAC;AAAA,MAAG;AAAA,IACrD,KAAK,KAAK,IAAI,GAAG,EAAE,OAAO,UAAU,CAAC;AAAA;AAAA,OAGhC,IAAG,CAAC,KAA8B;AAAA,IACvC,MAAM,IAAI,KAAK,EAAE,GAAG;AAAA,IACpB,MAAM,UAAU,KAAK,KAAK,OAAO,CAAC;AAAA,IAClC,OAAO,UAAU,IAAI;AAAA;AAAA,OAGhB,OAAM,CAAC,KAA+B;AAAA,IAC3C,MAAM,IAAI,KAAK,EAAE,GAAG;AAAA,IACpB,KAAK,MAAM,CAAC;AAAA,IACZ,OAAO,KAAK,KAAK,IAAI,CAAC;AAAA;AAAA,OAGjB,KAAI,CAAC,KAAa,KAAK,GAAG,SAA4C;AAAA,IAC3E,MAAM,IAAI,KAAK,EAAE,GAAG;AAAA,IACpB,KAAK,MAAM,CAAC;AAAA,IACZ,MAAM,IAAI,KAAK,KAAK,IAAI,CAAC;AAAA,IACzB,MAAM,UAAU,IAAI,OAAO,SAAS,EAAE,OAAO,EAAE,KAAK,IAAI;AAAA,IACxD,MAAM,OAAO,UAAU;AAAA,IACvB,MAAM,MAAM,SAAS,MAAM,KAAK,qBAAqB;AAAA,IACrD,MAAM,YAAY,OAAO,MAAM,IAAI,KAAK,IAAI,IAAI,MAAM,OAAO;AAAA,IAC7D,KAAK,KAAK,IAAI,GAAG,EAAE,OAAO,OAAO,IAAI,GAAG,UAAU,CAAC;AAAA,IACnD,OAAO;AAAA;AAAA,OAGF,KAAI,CAAC,UAA4B,CAAC,GAA6B;AAAA,IACpE,MAAM,QAAQ,QAAQ,SAAS;AAAA,IAI/B,MAAM,KAAK,YAAY,KAAK;AAAA,IAC5B,MAAM,OAAiB,CAAC;AAAA,IACxB,WAAW,KAAK,KAAK,KAAK,KAAK,GAAG;AAAA,MACjC,IAAI,KAAK,aAAa,CAAC,EAAE,WAAW,KAAK,SAAS;AAAA,QAAG;AAAA,MACrD,IAAI,GAAG,KAAK,CAAC;AAAA,QAAG,KAAK,KAAK,KAAK,YAAY,CAAC,CAAC;AAAA,IAC9C;AAAA,IACA,OAAO,EAAE,QAAQ,KAAK,KAAK;AAAA;AAAA,OAGtB,MAAK,GAAkB;AAAA,IAC5B,KAAK,KAAK,MAAM;AAAA;AAElB;AAEA,SAAS,WAAW,CAAC,MAAsB;AAAA,EAC1C,MAAM,UAAU,KACd,QAAQ,qBAAqB,MAAM,EACnC,QAAQ,OAAO,IAAI,EACnB,QAAQ,OAAO,GAAG;AAAA,EACpB,OAAO,IAAI,OAAO,MAAM,UAAU,GAAG;AAAA;;;ACxE/B,MAAM,iBAAwC;AAAA,EAC3C,UAAU;AAAA,EACX,SAA6B;AAAA,EACpB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EAEjB,WAAW,CAAC,SAAsB,CAAC,GAAG;AAAA,IACrC,KAAK,MAAM,OAAO,OAAO,QAAQ,IAAI,gBAAgB;AAAA,IACrD,KAAK,YAAY,OAAO,aAAa;AAAA,IACrC,KAAK,oBAAoB,OAAO,qBAAqB;AAAA,IACrD,KAAK,cAAc,OAAO,eAAe,CAAC;AAAA;AAAA,OAG7B,UAAS,GAAyB;AAAA,IAC/C,IAAI,KAAK;AAAA,MAAQ,OAAO,KAAK;AAAA,IAC7B,IAAI;AAAA,MAEH,MAAM,MAAM,MAAa;AAAA,MACzB,MAAM,OAAQ,IAAY,WAAY;AAAA,MACtC,IAAI,OAAO,SAAS,YAAY;AAAA,QAC/B,MAAM,IAAI,MAAM,6CAA6C;AAAA,MAC9D;AAAA,MACA,KAAK,SAAS,IAAI,KAAK,KAAK,KAAK,KAAK,WAAW;AAAA,MAChD,OAAO,KAAK;AAAA,MACb,MAAM,IAAI,MACT,gFAED;AAAA;AAAA,IAED,OAAO,KAAK;AAAA;AAAA,EAGL,CAAC,CAAC,KAAqB;AAAA,IAC9B,OAAO,KAAK,YAAY;AAAA;AAAA,EAGjB,WAAW,CAAC,KAAqB;AAAA,IACxC,OAAO,KAAK,aAAa,IAAI,WAAW,KAAK,SAAS,IACnD,IAAI,MAAM,KAAK,UAAU,MAAM,IAC/B;AAAA;AAAA,OAGE,IAAG,CAAC,KAAkC;AAAA,IAC3C,QAAQ,MAAM,KAAK,UAAU,GAAG,IAAI,KAAK,EAAE,GAAG,CAAC;AAAA;AAAA,OAG1C,IAAG,CAAC,KAAa,OAAe,SAA0C;AAAA,IAC/E,MAAM,KAAK,SAAS,MAAM,KAAK,qBAAqB;AAAA,IACpD,MAAM,IAAI,MAAM,KAAK,UAAU;AAAA,IAC/B,MAAM,UAAU,KAAK,EAAE,GAAG;AAAA,IAI1B,IAAI,SAAS,IAAI;AAAA,MAChB,MAAM,EAAE,IAAI,SAAS,OAAO,MAAM,MAAM,GAAG,IAAI;AAAA,IAChD,EAAO,SAAI,SAAS,IAAI;AAAA,MACvB,MAAM,EAAE,IAAI,SAAS,OAAO,MAAM,MAAM,GAAG,IAAI;AAAA,IAChD,EAAO,SAAI,IAAI;AAAA,MACd,MAAM,EAAE,IAAI,SAAS,OAAO,MAAM,EAAE;AAAA,IACrC,EAAO;AAAA,MACN,MAAM,EAAE,IAAI,SAAS,KAAK;AAAA;AAAA;AAAA,OAItB,IAAG,CAAC,KAA8B;AAAA,IACvC,QAAQ,MAAM,KAAK,UAAU,GAAG,IAAI,KAAK,EAAE,GAAG,CAAC;AAAA;AAAA,OAG1C,OAAM,CAAC,KAA+B;AAAA,IAC3C,MAAM,IAAI,OAAO,MAAM,KAAK,UAAU,GAAG,OAAO,KAAK,EAAE,GAAG,CAAC;AAAA,IAC3D,OAAO,IAAI;AAAA;AAAA,OAGN,KAAI,CAAC,KAAa,KAAK,GAAG,SAA4C;AAAA,IAC3E,MAAM,UAAU,KAAK,EAAE,GAAG;AAAA,IAC1B,MAAM,IAAI,MAAM,KAAK,UAAU;AAAA,IAC/B,IAAI;AAAA,IACJ,IAAI,OAAO,GAAG;AAAA,MACb,QAAQ,MAAM,EAAE,KAAK,OAAO;AAAA,IAC7B,EAAO;AAAA,MAIN,QAAQ,MAAO,EAAuE,OAAO,SAAS,EAAE;AAAA;AAAA,IAEzG,IAAI,SAAS,MAAM,UAAU,IAAI;AAAA,MAChC,MAAM,EAAE,OAAO,SAAS,QAAQ,EAAE;AAAA,IACnC;AAAA,IACA,OAAO;AAAA;AAAA,OAGF,KAAI,CAAC,UAA4B,CAAC,GAA6B;AAAA,IACpE,MAAM,SAAS,OAAO,QAAQ,WAAW,YAAY,OAAO,QAAQ,WAAW,WAC5E,QAAQ,SACR;AAAA,IACH,OAAO,MAAM,QAAQ,OAAO,MAAM,KAAK,UAAU,GAAG,KACnD,QACA,QAAQ,SAAS,KACjB,QAAQ,SAAS,GAClB;AAAA,IACA,OAAO;AAAA,MACN,QAAQ;AAAA,MACR,OAAO,QAAQ,CAAC,GAAG,IAAI,CAAC,MAAM,KAAK,YAAY,CAAC,CAAC;AAAA,IAClD;AAAA;AAAA,OAGK,MAAK,GAAkB;AAAA,IAC5B,IAAI,KAAK,QAAQ;AAAA,MAChB,IAAI;AAAA,QACH,MAAM,KAAK,OAAO,KAAK;AAAA,QACtB,MAAM;AAAA,QACP,KAAK,OAAO,WAAW;AAAA;AAAA,MAExB,KAAK,SAAS;AAAA,IACf;AAAA;AAEF;;;ACnJO,SAAS,kBAAkB,GAAqB;AAAA,EAEtD,IACC,OAAQ,WAAoC,WAAW,eACvD,OAAQ,WAA2C,kBAAkB,aACpE;AAAA,IACD,OAAO;AAAA,EACR;AAAA,EACA,IAAI,OAAQ,WAAiC,QAAQ;AAAA,IAAa,OAAO;AAAA,EACzE,IAAI,OAAO,YAAY,eAAe,QAAQ,UAAU;AAAA,IAAM,OAAO;AAAA,EACrE,OAAO;AAAA;AAWD,SAAS,iBAAiB,CAAC,SAAsB,CAAC,GAAgB;AAAA,EACxE,MAAM,UAAU,OAAO,WAAW,mBAAmB;AAAA,EACrD,QAAQ;AAAA,SACF;AAAA,MACJ,OAAO,IAAI,gBAAgB,MAAM;AAAA,SAC7B;AAAA,MACJ,OAAO,IAAI,iBAAiB,MAAM;AAAA,SAC9B;AAAA,MACJ,OAAO,IAAI,oBAAoB,MAAM;AAAA,SACjC;AAAA,MACJ,OAAO,IAAI,mBAAmB,MAAM;AAAA,aAC5B;AAAA,MACR,MAAM,cAAqB;AAAA,MAC3B,MAAM,IAAI,MAAM,0BAA0B,aAAuB;AAAA,IAClE;AAAA;AAAA;;AC3BF;AAIO,IAAM,qBAAqB,OAAO,IAAI,mBAAmB;AAAA;AAWzD,MAAM,YAAY;AAAA,SACjB,OAAO,CAAC,SAAsB,CAAC,GAAG;AAAA;AAAA,IAWxC,MAAM,sBAAsB;AAAA,IAAC;AAAA,IAAvB,wBAAN;AAAA,MAVC,OAAO;AAAA,QACP,WAAW;AAAA,UACV;AAAA,YACC,SAAS;AAAA,YACT,YAAY,MAAM,kBAAkB,MAAM;AAAA,UAC3C;AAAA,UACA,EAAE,SAAS,gBAAgB,UAAU,OAAO;AAAA,QAC7C;AAAA,QACA,SAAS,CAAC,oBAAoB,cAAc;AAAA,MAC7C,CAAC;AAAA,OACK;AAAA,IACN,OAAO,eAAe,uBAAuB,QAAQ;AAAA,MACpD,OAAO;AAAA,IACR,CAAC;AAAA,IACD,OAAO;AAAA;AAET;AAlBa,cAAN;AAAA,EATN,OAAO;AAAA,IACP,WAAW;AAAA,MACV;AAAA,QACC,SAAS;AAAA,QACT,YAAY,MAAM,kBAAkB;AAAA,MACrC;AAAA,IACD;AAAA,IACA,SAAS,CAAC,kBAAkB;AAAA,EAC7B,CAAC;AAAA,GACY;",
package/dist/types.d.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  /**
2
- * Public types for `nexusjs/redis`.
2
+ * Public types for `@nexusts/redis`.
3
3
  *
4
- * `nexusjs/redis` is a thin, runtime-aware abstraction over a
4
+ * `@nexusts/redis` is a thin, runtime-aware abstraction over a
5
5
  * Redis-compatible key-value store. The same API works on:
6
6
  *
7
7
  * - **Bun** (primary): uses the built-in `Bun.redis` — no
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nexusts/redis",
3
- "version": "0.9.0",
3
+ "version": "0.9.2",
4
4
  "description": "Runtime-aware Redis client (Bun / Node / Workers)",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -34,7 +34,7 @@
34
34
  }
35
35
  },
36
36
  "dependencies": {
37
- "@nexusts/core": "^0.9.0"
37
+ "@nexusts/core": "^0.9.2"
38
38
  },
39
39
  "repository": {
40
40
  "type": "git",