@prismakit/redis 3.2.0 → 4.0.0
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.
- package/README.md +14 -0
- package/dist/index.cjs +0 -14
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +2 -4
- package/dist/index.d.ts +2 -4
- package/dist/index.js +0 -14
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
- package/src/index.ts +0 -9
- package/src/redis-json.ts +0 -5
package/README.md
CHANGED
|
@@ -4,6 +4,8 @@ Redis `CacheAdapter` for `@prismakit/core` repositories (ioredis).
|
|
|
4
4
|
|
|
5
5
|
Fail-open: cache errors do not break reads/writes.
|
|
6
6
|
|
|
7
|
+
**Status:** pre-stable (4.0).
|
|
8
|
+
|
|
7
9
|
[Documentation](https://github.com/fikiap23/prismakit/blob/master/docs/README.md) · [Cache guide](https://github.com/fikiap23/prismakit/blob/master/docs/guide/cache.md) · [GitHub](https://github.com/fikiap23/prismakit)
|
|
8
10
|
|
|
9
11
|
## Install
|
|
@@ -49,6 +51,18 @@ PrismaKitModule.forRoot({
|
|
|
49
51
|
});
|
|
50
52
|
```
|
|
51
53
|
|
|
54
|
+
### JSON codec
|
|
55
|
+
|
|
56
|
+
Tagged JSON for `Date`, `BigInt`, `Bytes`, and Prisma `Decimal`. Custom revive:
|
|
57
|
+
|
|
58
|
+
```typescript
|
|
59
|
+
import { createRedisJsonReviver } from '@prismakit/redis';
|
|
60
|
+
|
|
61
|
+
const reviver = createRedisJsonReviver({ /* DecimalFactory? */ });
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
Cache debug (`CACHE_DEBUG=true`, `cacheDebugStorage`, …) lives on `@prismakit/core`.
|
|
65
|
+
|
|
52
66
|
## Cache behavior (reminder)
|
|
53
67
|
|
|
54
68
|
- Reads cache only when `setCache: true`
|
package/dist/index.cjs
CHANGED
|
@@ -31,14 +31,10 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
31
31
|
var index_exports = {};
|
|
32
32
|
__export(index_exports, {
|
|
33
33
|
RedisCacheAdapter: () => RedisCacheAdapter,
|
|
34
|
-
cacheDebugStorage: () => import_core.cacheDebugStorage,
|
|
35
34
|
cloneWithCodec: () => cloneWithCodec,
|
|
36
35
|
createRedisJsonReviver: () => createRedisJsonReviver,
|
|
37
|
-
isCacheDebugEnabled: () => import_core.isCacheDebugEnabled,
|
|
38
|
-
recordCacheDebug: () => import_core.recordCacheDebug,
|
|
39
36
|
redisJsonParse: () => redisJsonParse,
|
|
40
37
|
redisJsonReplacer: () => redisJsonReplacer,
|
|
41
|
-
redisJsonReviver: () => redisJsonReviver,
|
|
42
38
|
redisJsonStringify: () => redisJsonStringify
|
|
43
39
|
});
|
|
44
40
|
module.exports = __toCommonJS(index_exports);
|
|
@@ -101,9 +97,6 @@ function createRedisJsonReviver(options = {}) {
|
|
|
101
97
|
return value;
|
|
102
98
|
};
|
|
103
99
|
}
|
|
104
|
-
function redisJsonReviver(_key, value) {
|
|
105
|
-
return createRedisJsonReviver()(_key, value);
|
|
106
|
-
}
|
|
107
100
|
function redisJsonStringify(value) {
|
|
108
101
|
return JSON.stringify(value, function(key, val) {
|
|
109
102
|
const holder = this;
|
|
@@ -322,20 +315,13 @@ var RedisCacheAdapter = class {
|
|
|
322
315
|
}
|
|
323
316
|
}
|
|
324
317
|
};
|
|
325
|
-
|
|
326
|
-
// src/index.ts
|
|
327
|
-
var import_core = require("@prismakit/core");
|
|
328
318
|
// Annotate the CommonJS export names for ESM import in node:
|
|
329
319
|
0 && (module.exports = {
|
|
330
320
|
RedisCacheAdapter,
|
|
331
|
-
cacheDebugStorage,
|
|
332
321
|
cloneWithCodec,
|
|
333
322
|
createRedisJsonReviver,
|
|
334
|
-
isCacheDebugEnabled,
|
|
335
|
-
recordCacheDebug,
|
|
336
323
|
redisJsonParse,
|
|
337
324
|
redisJsonReplacer,
|
|
338
|
-
redisJsonReviver,
|
|
339
325
|
redisJsonStringify
|
|
340
326
|
});
|
|
341
327
|
//# sourceMappingURL=index.cjs.map
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/redis-cache-adapter.ts","../src/redis-json.ts"],"sourcesContent":["export {\n RedisCacheAdapter,\n type RedisCacheAdapterOptions,\n type RedisCompression,\n} from './redis-cache-adapter';\n\nexport {\n redisJsonParse,\n redisJsonStringify,\n redisJsonReplacer,\n redisJsonReviver,\n createRedisJsonReviver,\n cloneWithCodec,\n type DecimalFactory,\n type RedisJsonOptions,\n} from './redis-json';\n\n/** Re-export cache debug helpers from core — do not duplicate. */\nexport {\n type CacheDebugStatus,\n cacheDebugStorage,\n isCacheDebugEnabled,\n recordCacheDebug,\n} from '@prismakit/core';\n\nexport type { CacheAdapter } from '@prismakit/core';\n","import Redis from 'ioredis';\nimport { gzipSync, gunzipSync } from 'zlib';\nimport type { CacheAdapter } from '@prismakit/core';\nimport {\n redisJsonParse,\n redisJsonStringify,\n type DecimalFactory,\n type RedisJsonOptions,\n} from './redis-json';\n\nconst INDEX_TTL_BUFFER = 60;\n\n/** Lua: atomically SMEMBERS index + DEL members + index. */\nconst INVALIDATE_BY_INDEX_LUA = `\nlocal members = redis.call('SMEMBERS', KEYS[1])\nfor i, member in ipairs(members) do\n redis.call('DEL', member)\nend\nredis.call('DEL', KEYS[1])\nreturn #members\n`;\n\nexport type RedisCompression = 'none' | 'gzip';\n\nexport type RedisCacheAdapterOptions = {\n url?: string;\n host?: string;\n port?: number;\n prefix?: string;\n /**\n * Compress payloads larger than `compressionThresholdBytes` (default 1024).\n * Uses gzip (widely available; zstd/lz4 can be added later).\n */\n compression?: RedisCompression;\n /** Minimum payload size (bytes) before compression (default 1024). */\n compressionThresholdBytes?: number;\n /**\n * Reconstruct Prisma Decimal from tagged cache payloads.\n * @example decimalFactory: (s) => new Prisma.Decimal(s)\n */\n decimalFactory?: DecimalFactory;\n /** Optional error hook for safe* wrappers (also used by telemetry). */\n onError?: (err: unknown, op?: string) => void;\n};\n\nconst COMPRESSED_PREFIX = 'gz:';\n\n/**\n * Redis-backed {@link CacheAdapter}. Framework-agnostic — no NestJS / ConfigService.\n */\nexport class RedisCacheAdapter implements CacheAdapter {\n private readonly client: Redis;\n private readonly prefix: string;\n private ready = false;\n private readonly compression: RedisCompression;\n private readonly compressionThreshold: number;\n private readonly jsonOptions: RedisJsonOptions;\n onError?: (err: unknown, op?: string) => void;\n\n constructor(options: RedisCacheAdapterOptions = {}) {\n const {\n url,\n host = 'localhost',\n port = 6379,\n prefix = 'prismakit',\n compression = 'none',\n compressionThresholdBytes = 1024,\n decimalFactory,\n onError,\n } = options;\n\n this.prefix = prefix;\n this.compression = compression;\n this.compressionThreshold = compressionThresholdBytes;\n this.jsonOptions = { decimalFactory };\n this.onError = onError;\n this.client = url\n ? new Redis(url, { lazyConnect: true })\n : new Redis({ host, port, lazyConnect: true });\n\n this.client.on('ready', () => {\n this.ready = true;\n });\n this.client.on('error', (err) => {\n this.ready = false;\n console.warn('[RedisCacheAdapter] connection error', err.message);\n });\n this.client.on('close', () => {\n this.ready = false;\n });\n\n void this.connect();\n }\n\n private report(op: string, err: unknown): void {\n console.warn(\n `[RedisCacheAdapter] ${op} failed`,\n (err as Error)?.message ?? err,\n );\n try {\n this.onError?.(err, op);\n } catch {\n /* ignore hook errors */\n }\n }\n\n async connect(): Promise<void> {\n if (this.client.status === 'ready' || this.client.status === 'connecting') {\n return;\n }\n try {\n await this.client.connect();\n } catch (err) {\n this.report('connect', err);\n }\n }\n\n async disconnect(): Promise<void> {\n await this.client?.quit();\n this.ready = false;\n }\n\n isReady(): boolean {\n return this.ready;\n }\n\n getPrefix(): string {\n return this.prefix;\n }\n\n private encode(value: unknown): string {\n const json = redisJsonStringify(value);\n if (\n this.compression === 'gzip' &&\n Buffer.byteLength(json, 'utf8') >= this.compressionThreshold\n ) {\n const compressed = gzipSync(Buffer.from(json, 'utf8')).toString('base64');\n return COMPRESSED_PREFIX + compressed;\n }\n return json;\n }\n\n private decode<T>(raw: string): T {\n if (raw.startsWith(COMPRESSED_PREFIX)) {\n const buf = Buffer.from(raw.slice(COMPRESSED_PREFIX.length), 'base64');\n const json = gunzipSync(buf).toString('utf8');\n return redisJsonParse<T>(json, this.jsonOptions);\n }\n return redisJsonParse<T>(raw, this.jsonOptions);\n }\n\n async get<T>(key: string): Promise<T | null> {\n const raw = await this.client.get(key);\n if (raw === null) return null;\n return this.decode<T>(raw);\n }\n\n async set(key: string, value: unknown, ttlSeconds: number): Promise<void> {\n await this.client.set(key, this.encode(value), 'EX', ttlSeconds);\n }\n\n async del(...keys: string[]): Promise<void> {\n if (keys.length === 0) return;\n await this.client.del(...keys);\n }\n\n async sadd(key: string, ...members: string[]): Promise<void> {\n if (members.length === 0) return;\n await this.client.sadd(key, ...members);\n }\n\n async smembers(key: string): Promise<string[]> {\n return this.client.smembers(key);\n }\n\n async setNx(key: string, ttlSeconds: number): Promise<boolean> {\n const result = await this.client.set(key, '1', 'EX', ttlSeconds, 'NX');\n return result === 'OK';\n }\n\n async setWithIndex(\n key: string,\n value: unknown,\n ttlSeconds: number,\n indexKey: string,\n ): Promise<void> {\n const pipeline = this.client.pipeline();\n pipeline.set(key, this.encode(value), 'EX', ttlSeconds);\n pipeline.sadd(indexKey, key);\n pipeline.expire(indexKey, ttlSeconds + INDEX_TTL_BUFFER);\n await pipeline.exec();\n }\n\n async invalidateByIndex(indexKey: string): Promise<void> {\n await this.client.eval(INVALIDATE_BY_INDEX_LUA, 1, indexKey);\n }\n\n async saddAndExpire(\n key: string,\n members: string[],\n ttlSeconds: number,\n ): Promise<void> {\n if (members.length === 0) return;\n const pipeline = this.client.pipeline();\n pipeline.sadd(key, ...members);\n pipeline.expire(key, ttlSeconds + INDEX_TTL_BUFFER);\n await pipeline.exec();\n }\n\n async safeGet<T>(key: string): Promise<T | null> {\n try {\n return await this.get<T>(key);\n } catch (err) {\n this.report(`safeGet key=${key}`, err);\n return null;\n }\n }\n\n async safeSet(\n key: string,\n value: unknown,\n ttlSeconds: number,\n ): Promise<void> {\n try {\n await this.set(key, value, ttlSeconds);\n } catch (err) {\n this.report(`safeSet key=${key}`, err);\n }\n }\n\n async safeDel(...keys: string[]): Promise<void> {\n try {\n await this.del(...keys);\n } catch (err) {\n this.report('safeDel', err);\n }\n }\n\n async safeSetNx(key: string, ttlSeconds: number): Promise<boolean> {\n try {\n return await this.setNx(key, ttlSeconds);\n } catch (err) {\n this.report(`safeSetNx key=${key}`, err);\n return false;\n }\n }\n\n async safeSetWithIndex(\n key: string,\n value: unknown,\n ttlSeconds: number,\n indexKey: string,\n ): Promise<void> {\n try {\n await this.setWithIndex(key, value, ttlSeconds, indexKey);\n } catch (err) {\n this.report(`safeSetWithIndex key=${key}`, err);\n }\n }\n\n async safeInvalidateByIndex(indexKey: string): Promise<void> {\n try {\n await this.invalidateByIndex(indexKey);\n } catch (err) {\n this.report(`safeInvalidateByIndex idx=${indexKey}`, err);\n }\n }\n\n async safeSaddAndExpire(\n key: string,\n members: string[],\n ttlSeconds: number,\n ): Promise<void> {\n try {\n await this.saddAndExpire(key, members, ttlSeconds);\n } catch (err) {\n this.report(`safeSaddAndExpire key=${key}`, err);\n }\n }\n\n async safeSmembers(key: string): Promise<string[]> {\n try {\n return await this.smembers(key);\n } catch (err) {\n this.report(`safeSmembers key=${key}`, err);\n return [];\n }\n }\n}\n","const BIGINT_TAG = '__bigint';\nconst DATE_TAG = '__date';\nconst BYTES_TAG = '__bytes';\nconst DECIMAL_TAG = '__decimal';\n\nexport type DecimalFactory = (value: string) => unknown;\n\nexport type RedisJsonOptions = {\n /**\n * Reconstruct Prisma `Decimal` (or equivalent) from the tagged string.\n * Default keeps the precision-preserving string so values are not silently\n * coerced to numbers.\n *\n * @example\n * decimalFactory: (s) => new Prisma.Decimal(s)\n */\n decimalFactory?: DecimalFactory;\n};\n\nfunction isPlainObject(value: unknown): value is Record<string, unknown> {\n return (\n value !== null &&\n typeof value === 'object' &&\n !Array.isArray(value) &&\n !(value instanceof Date) &&\n !Buffer.isBuffer(value)\n );\n}\n\nfunction isDecimalLike(value: unknown): value is { toFixed: () => string } {\n return (\n isPlainObject(value) &&\n typeof (value as { constructor?: { name?: string } }).constructor?.name ===\n 'string' &&\n ((value as { constructor: { name: string } }).constructor.name ===\n 'Decimal' ||\n (typeof (value as { toFixed?: unknown }).toFixed === 'function' &&\n typeof (value as { d?: unknown }).d === 'object'))\n );\n}\n\n/** JSON.stringify replacer — Prisma BigInt / Date / Bytes / Decimal. */\nexport function redisJsonReplacer(_key: string, value: unknown): unknown {\n if (typeof value === 'bigint') {\n return { [BIGINT_TAG]: value.toString() };\n }\n if (value instanceof Date) {\n return { [DATE_TAG]: value.toISOString() };\n }\n if (Buffer.isBuffer(value)) {\n return { [BYTES_TAG]: value.toString('base64') };\n }\n if (\n value &&\n typeof value === 'object' &&\n (value as { type?: string }).type === 'Buffer' &&\n Array.isArray((value as { data?: unknown }).data)\n ) {\n return {\n [BYTES_TAG]: Buffer.from(\n (value as { data: number[] }).data,\n ).toString('base64'),\n };\n }\n if (isDecimalLike(value)) {\n return { [DECIMAL_TAG]: value.toFixed() };\n }\n return value;\n}\n\n/** JSON.parse reviver — restore tagged payloads from {@link redisJsonReplacer}. */\nexport function createRedisJsonReviver(options: RedisJsonOptions = {}) {\n const decimalFactory = options.decimalFactory ?? ((s: string) => s);\n return (_key: string, value: unknown): unknown => {\n if (!isPlainObject(value)) return value;\n const keys = Object.keys(value);\n if (keys.length !== 1) return value;\n\n if (BIGINT_TAG in value && typeof value[BIGINT_TAG] === 'string') {\n return BigInt(value[BIGINT_TAG] as string);\n }\n if (DATE_TAG in value && typeof value[DATE_TAG] === 'string') {\n return new Date(value[DATE_TAG] as string);\n }\n if (BYTES_TAG in value && typeof value[BYTES_TAG] === 'string') {\n return Buffer.from(value[BYTES_TAG] as string, 'base64');\n }\n if (DECIMAL_TAG in value && typeof value[DECIMAL_TAG] === 'string') {\n return decimalFactory(value[DECIMAL_TAG] as string);\n }\n return value;\n };\n}\n\n/** @deprecated Prefer {@link createRedisJsonReviver} when a decimalFactory is needed. */\nexport function redisJsonReviver(_key: string, value: unknown): unknown {\n return createRedisJsonReviver()(_key, value);\n}\n\nexport function redisJsonStringify(value: unknown): string {\n return JSON.stringify(value, function (key, val) {\n const holder = this as Record<string, unknown>;\n const raw = key === '' ? value : holder[key];\n if (raw instanceof Date) {\n return redisJsonReplacer(key, raw);\n }\n return redisJsonReplacer(key, val);\n });\n}\n\nexport function redisJsonParse<T>(\n raw: string,\n options?: RedisJsonOptions,\n): T {\n return JSON.parse(raw, createRedisJsonReviver(options)) as T;\n}\n\n/**\n * Deep clone via structuredClone, falling back to the tagged JSON codec so\n * Date / BigInt / Buffer / Decimal survive (unlike plain JSON.stringify).\n */\nexport function cloneWithCodec<T>(value: T, options?: RedisJsonOptions): T {\n if (value === null || value === undefined) return value;\n if (typeof structuredClone === 'function') {\n try {\n return structuredClone(value);\n } catch {\n // Fall through for non-cloneable values (e.g. Decimal)\n }\n }\n return redisJsonParse(redisJsonStringify(value), options);\n}\n"],"mappings":"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","names":["Redis"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/redis-cache-adapter.ts","../src/redis-json.ts"],"sourcesContent":["export {\n RedisCacheAdapter,\n type RedisCacheAdapterOptions,\n type RedisCompression,\n} from './redis-cache-adapter';\n\nexport {\n redisJsonParse,\n redisJsonStringify,\n redisJsonReplacer,\n createRedisJsonReviver,\n cloneWithCodec,\n type DecimalFactory,\n type RedisJsonOptions,\n} from './redis-json';\n\nexport type { CacheAdapter } from '@prismakit/core';\n","import Redis from 'ioredis';\nimport { gzipSync, gunzipSync } from 'zlib';\nimport type { CacheAdapter } from '@prismakit/core';\nimport {\n redisJsonParse,\n redisJsonStringify,\n type DecimalFactory,\n type RedisJsonOptions,\n} from './redis-json';\n\nconst INDEX_TTL_BUFFER = 60;\n\n/** Lua: atomically SMEMBERS index + DEL members + index. */\nconst INVALIDATE_BY_INDEX_LUA = `\nlocal members = redis.call('SMEMBERS', KEYS[1])\nfor i, member in ipairs(members) do\n redis.call('DEL', member)\nend\nredis.call('DEL', KEYS[1])\nreturn #members\n`;\n\nexport type RedisCompression = 'none' | 'gzip';\n\nexport type RedisCacheAdapterOptions = {\n url?: string;\n host?: string;\n port?: number;\n prefix?: string;\n /**\n * Compress payloads larger than `compressionThresholdBytes` (default 1024).\n * Uses gzip (widely available; zstd/lz4 can be added later).\n */\n compression?: RedisCompression;\n /** Minimum payload size (bytes) before compression (default 1024). */\n compressionThresholdBytes?: number;\n /**\n * Reconstruct Prisma Decimal from tagged cache payloads.\n * @example decimalFactory: (s) => new Prisma.Decimal(s)\n */\n decimalFactory?: DecimalFactory;\n /** Optional error hook for safe* wrappers (also used by telemetry). */\n onError?: (err: unknown, op?: string) => void;\n};\n\nconst COMPRESSED_PREFIX = 'gz:';\n\n/**\n * Redis-backed {@link CacheAdapter}. Framework-agnostic — no NestJS / ConfigService.\n */\nexport class RedisCacheAdapter implements CacheAdapter {\n private readonly client: Redis;\n private readonly prefix: string;\n private ready = false;\n private readonly compression: RedisCompression;\n private readonly compressionThreshold: number;\n private readonly jsonOptions: RedisJsonOptions;\n onError?: (err: unknown, op?: string) => void;\n\n constructor(options: RedisCacheAdapterOptions = {}) {\n const {\n url,\n host = 'localhost',\n port = 6379,\n prefix = 'prismakit',\n compression = 'none',\n compressionThresholdBytes = 1024,\n decimalFactory,\n onError,\n } = options;\n\n this.prefix = prefix;\n this.compression = compression;\n this.compressionThreshold = compressionThresholdBytes;\n this.jsonOptions = { decimalFactory };\n this.onError = onError;\n this.client = url\n ? new Redis(url, { lazyConnect: true })\n : new Redis({ host, port, lazyConnect: true });\n\n this.client.on('ready', () => {\n this.ready = true;\n });\n this.client.on('error', (err) => {\n this.ready = false;\n console.warn('[RedisCacheAdapter] connection error', err.message);\n });\n this.client.on('close', () => {\n this.ready = false;\n });\n\n void this.connect();\n }\n\n private report(op: string, err: unknown): void {\n console.warn(\n `[RedisCacheAdapter] ${op} failed`,\n (err as Error)?.message ?? err,\n );\n try {\n this.onError?.(err, op);\n } catch {\n /* ignore hook errors */\n }\n }\n\n async connect(): Promise<void> {\n if (this.client.status === 'ready' || this.client.status === 'connecting') {\n return;\n }\n try {\n await this.client.connect();\n } catch (err) {\n this.report('connect', err);\n }\n }\n\n async disconnect(): Promise<void> {\n await this.client?.quit();\n this.ready = false;\n }\n\n isReady(): boolean {\n return this.ready;\n }\n\n getPrefix(): string {\n return this.prefix;\n }\n\n private encode(value: unknown): string {\n const json = redisJsonStringify(value);\n if (\n this.compression === 'gzip' &&\n Buffer.byteLength(json, 'utf8') >= this.compressionThreshold\n ) {\n const compressed = gzipSync(Buffer.from(json, 'utf8')).toString('base64');\n return COMPRESSED_PREFIX + compressed;\n }\n return json;\n }\n\n private decode<T>(raw: string): T {\n if (raw.startsWith(COMPRESSED_PREFIX)) {\n const buf = Buffer.from(raw.slice(COMPRESSED_PREFIX.length), 'base64');\n const json = gunzipSync(buf).toString('utf8');\n return redisJsonParse<T>(json, this.jsonOptions);\n }\n return redisJsonParse<T>(raw, this.jsonOptions);\n }\n\n async get<T>(key: string): Promise<T | null> {\n const raw = await this.client.get(key);\n if (raw === null) return null;\n return this.decode<T>(raw);\n }\n\n async set(key: string, value: unknown, ttlSeconds: number): Promise<void> {\n await this.client.set(key, this.encode(value), 'EX', ttlSeconds);\n }\n\n async del(...keys: string[]): Promise<void> {\n if (keys.length === 0) return;\n await this.client.del(...keys);\n }\n\n async sadd(key: string, ...members: string[]): Promise<void> {\n if (members.length === 0) return;\n await this.client.sadd(key, ...members);\n }\n\n async smembers(key: string): Promise<string[]> {\n return this.client.smembers(key);\n }\n\n async setNx(key: string, ttlSeconds: number): Promise<boolean> {\n const result = await this.client.set(key, '1', 'EX', ttlSeconds, 'NX');\n return result === 'OK';\n }\n\n async setWithIndex(\n key: string,\n value: unknown,\n ttlSeconds: number,\n indexKey: string,\n ): Promise<void> {\n const pipeline = this.client.pipeline();\n pipeline.set(key, this.encode(value), 'EX', ttlSeconds);\n pipeline.sadd(indexKey, key);\n pipeline.expire(indexKey, ttlSeconds + INDEX_TTL_BUFFER);\n await pipeline.exec();\n }\n\n async invalidateByIndex(indexKey: string): Promise<void> {\n await this.client.eval(INVALIDATE_BY_INDEX_LUA, 1, indexKey);\n }\n\n async saddAndExpire(\n key: string,\n members: string[],\n ttlSeconds: number,\n ): Promise<void> {\n if (members.length === 0) return;\n const pipeline = this.client.pipeline();\n pipeline.sadd(key, ...members);\n pipeline.expire(key, ttlSeconds + INDEX_TTL_BUFFER);\n await pipeline.exec();\n }\n\n async safeGet<T>(key: string): Promise<T | null> {\n try {\n return await this.get<T>(key);\n } catch (err) {\n this.report(`safeGet key=${key}`, err);\n return null;\n }\n }\n\n async safeSet(\n key: string,\n value: unknown,\n ttlSeconds: number,\n ): Promise<void> {\n try {\n await this.set(key, value, ttlSeconds);\n } catch (err) {\n this.report(`safeSet key=${key}`, err);\n }\n }\n\n async safeDel(...keys: string[]): Promise<void> {\n try {\n await this.del(...keys);\n } catch (err) {\n this.report('safeDel', err);\n }\n }\n\n async safeSetNx(key: string, ttlSeconds: number): Promise<boolean> {\n try {\n return await this.setNx(key, ttlSeconds);\n } catch (err) {\n this.report(`safeSetNx key=${key}`, err);\n return false;\n }\n }\n\n async safeSetWithIndex(\n key: string,\n value: unknown,\n ttlSeconds: number,\n indexKey: string,\n ): Promise<void> {\n try {\n await this.setWithIndex(key, value, ttlSeconds, indexKey);\n } catch (err) {\n this.report(`safeSetWithIndex key=${key}`, err);\n }\n }\n\n async safeInvalidateByIndex(indexKey: string): Promise<void> {\n try {\n await this.invalidateByIndex(indexKey);\n } catch (err) {\n this.report(`safeInvalidateByIndex idx=${indexKey}`, err);\n }\n }\n\n async safeSaddAndExpire(\n key: string,\n members: string[],\n ttlSeconds: number,\n ): Promise<void> {\n try {\n await this.saddAndExpire(key, members, ttlSeconds);\n } catch (err) {\n this.report(`safeSaddAndExpire key=${key}`, err);\n }\n }\n\n async safeSmembers(key: string): Promise<string[]> {\n try {\n return await this.smembers(key);\n } catch (err) {\n this.report(`safeSmembers key=${key}`, err);\n return [];\n }\n }\n}\n","const BIGINT_TAG = '__bigint';\nconst DATE_TAG = '__date';\nconst BYTES_TAG = '__bytes';\nconst DECIMAL_TAG = '__decimal';\n\nexport type DecimalFactory = (value: string) => unknown;\n\nexport type RedisJsonOptions = {\n /**\n * Reconstruct Prisma `Decimal` (or equivalent) from the tagged string.\n * Default keeps the precision-preserving string so values are not silently\n * coerced to numbers.\n *\n * @example\n * decimalFactory: (s) => new Prisma.Decimal(s)\n */\n decimalFactory?: DecimalFactory;\n};\n\nfunction isPlainObject(value: unknown): value is Record<string, unknown> {\n return (\n value !== null &&\n typeof value === 'object' &&\n !Array.isArray(value) &&\n !(value instanceof Date) &&\n !Buffer.isBuffer(value)\n );\n}\n\nfunction isDecimalLike(value: unknown): value is { toFixed: () => string } {\n return (\n isPlainObject(value) &&\n typeof (value as { constructor?: { name?: string } }).constructor?.name ===\n 'string' &&\n ((value as { constructor: { name: string } }).constructor.name ===\n 'Decimal' ||\n (typeof (value as { toFixed?: unknown }).toFixed === 'function' &&\n typeof (value as { d?: unknown }).d === 'object'))\n );\n}\n\n/** JSON.stringify replacer — Prisma BigInt / Date / Bytes / Decimal. */\nexport function redisJsonReplacer(_key: string, value: unknown): unknown {\n if (typeof value === 'bigint') {\n return { [BIGINT_TAG]: value.toString() };\n }\n if (value instanceof Date) {\n return { [DATE_TAG]: value.toISOString() };\n }\n if (Buffer.isBuffer(value)) {\n return { [BYTES_TAG]: value.toString('base64') };\n }\n if (\n value &&\n typeof value === 'object' &&\n (value as { type?: string }).type === 'Buffer' &&\n Array.isArray((value as { data?: unknown }).data)\n ) {\n return {\n [BYTES_TAG]: Buffer.from(\n (value as { data: number[] }).data,\n ).toString('base64'),\n };\n }\n if (isDecimalLike(value)) {\n return { [DECIMAL_TAG]: value.toFixed() };\n }\n return value;\n}\n\n/** JSON.parse reviver — restore tagged payloads from {@link redisJsonReplacer}. */\nexport function createRedisJsonReviver(options: RedisJsonOptions = {}) {\n const decimalFactory = options.decimalFactory ?? ((s: string) => s);\n return (_key: string, value: unknown): unknown => {\n if (!isPlainObject(value)) return value;\n const keys = Object.keys(value);\n if (keys.length !== 1) return value;\n\n if (BIGINT_TAG in value && typeof value[BIGINT_TAG] === 'string') {\n return BigInt(value[BIGINT_TAG] as string);\n }\n if (DATE_TAG in value && typeof value[DATE_TAG] === 'string') {\n return new Date(value[DATE_TAG] as string);\n }\n if (BYTES_TAG in value && typeof value[BYTES_TAG] === 'string') {\n return Buffer.from(value[BYTES_TAG] as string, 'base64');\n }\n if (DECIMAL_TAG in value && typeof value[DECIMAL_TAG] === 'string') {\n return decimalFactory(value[DECIMAL_TAG] as string);\n }\n return value;\n };\n}\n\nexport function redisJsonStringify(value: unknown): string {\n return JSON.stringify(value, function (key, val) {\n const holder = this as Record<string, unknown>;\n const raw = key === '' ? value : holder[key];\n if (raw instanceof Date) {\n return redisJsonReplacer(key, raw);\n }\n return redisJsonReplacer(key, val);\n });\n}\n\nexport function redisJsonParse<T>(\n raw: string,\n options?: RedisJsonOptions,\n): T {\n return JSON.parse(raw, createRedisJsonReviver(options)) as T;\n}\n\n/**\n * Deep clone via structuredClone, falling back to the tagged JSON codec so\n * Date / BigInt / Buffer / Decimal survive (unlike plain JSON.stringify).\n */\nexport function cloneWithCodec<T>(value: T, options?: RedisJsonOptions): T {\n if (value === null || value === undefined) return value;\n if (typeof structuredClone === 'function') {\n try {\n return structuredClone(value);\n } catch {\n // Fall through for non-cloneable values (e.g. Decimal)\n }\n }\n return redisJsonParse(redisJsonStringify(value), options);\n}\n"],"mappings":"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","names":["Redis"]}
|
package/dist/index.d.cts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { CacheAdapter } from '@prismakit/core';
|
|
2
|
-
export { CacheAdapter
|
|
2
|
+
export { CacheAdapter } from '@prismakit/core';
|
|
3
3
|
|
|
4
4
|
type DecimalFactory = (value: string) => unknown;
|
|
5
5
|
type RedisJsonOptions = {
|
|
@@ -17,8 +17,6 @@ type RedisJsonOptions = {
|
|
|
17
17
|
declare function redisJsonReplacer(_key: string, value: unknown): unknown;
|
|
18
18
|
/** JSON.parse reviver — restore tagged payloads from {@link redisJsonReplacer}. */
|
|
19
19
|
declare function createRedisJsonReviver(options?: RedisJsonOptions): (_key: string, value: unknown) => unknown;
|
|
20
|
-
/** @deprecated Prefer {@link createRedisJsonReviver} when a decimalFactory is needed. */
|
|
21
|
-
declare function redisJsonReviver(_key: string, value: unknown): unknown;
|
|
22
20
|
declare function redisJsonStringify(value: unknown): string;
|
|
23
21
|
declare function redisJsonParse<T>(raw: string, options?: RedisJsonOptions): T;
|
|
24
22
|
/**
|
|
@@ -86,4 +84,4 @@ declare class RedisCacheAdapter implements CacheAdapter {
|
|
|
86
84
|
safeSmembers(key: string): Promise<string[]>;
|
|
87
85
|
}
|
|
88
86
|
|
|
89
|
-
export { type DecimalFactory, RedisCacheAdapter, type RedisCacheAdapterOptions, type RedisCompression, type RedisJsonOptions, cloneWithCodec, createRedisJsonReviver, redisJsonParse, redisJsonReplacer,
|
|
87
|
+
export { type DecimalFactory, RedisCacheAdapter, type RedisCacheAdapterOptions, type RedisCompression, type RedisJsonOptions, cloneWithCodec, createRedisJsonReviver, redisJsonParse, redisJsonReplacer, redisJsonStringify };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { CacheAdapter } from '@prismakit/core';
|
|
2
|
-
export { CacheAdapter
|
|
2
|
+
export { CacheAdapter } from '@prismakit/core';
|
|
3
3
|
|
|
4
4
|
type DecimalFactory = (value: string) => unknown;
|
|
5
5
|
type RedisJsonOptions = {
|
|
@@ -17,8 +17,6 @@ type RedisJsonOptions = {
|
|
|
17
17
|
declare function redisJsonReplacer(_key: string, value: unknown): unknown;
|
|
18
18
|
/** JSON.parse reviver — restore tagged payloads from {@link redisJsonReplacer}. */
|
|
19
19
|
declare function createRedisJsonReviver(options?: RedisJsonOptions): (_key: string, value: unknown) => unknown;
|
|
20
|
-
/** @deprecated Prefer {@link createRedisJsonReviver} when a decimalFactory is needed. */
|
|
21
|
-
declare function redisJsonReviver(_key: string, value: unknown): unknown;
|
|
22
20
|
declare function redisJsonStringify(value: unknown): string;
|
|
23
21
|
declare function redisJsonParse<T>(raw: string, options?: RedisJsonOptions): T;
|
|
24
22
|
/**
|
|
@@ -86,4 +84,4 @@ declare class RedisCacheAdapter implements CacheAdapter {
|
|
|
86
84
|
safeSmembers(key: string): Promise<string[]>;
|
|
87
85
|
}
|
|
88
86
|
|
|
89
|
-
export { type DecimalFactory, RedisCacheAdapter, type RedisCacheAdapterOptions, type RedisCompression, type RedisJsonOptions, cloneWithCodec, createRedisJsonReviver, redisJsonParse, redisJsonReplacer,
|
|
87
|
+
export { type DecimalFactory, RedisCacheAdapter, type RedisCacheAdapterOptions, type RedisCompression, type RedisJsonOptions, cloneWithCodec, createRedisJsonReviver, redisJsonParse, redisJsonReplacer, redisJsonStringify };
|
package/dist/index.js
CHANGED
|
@@ -56,9 +56,6 @@ function createRedisJsonReviver(options = {}) {
|
|
|
56
56
|
return value;
|
|
57
57
|
};
|
|
58
58
|
}
|
|
59
|
-
function redisJsonReviver(_key, value) {
|
|
60
|
-
return createRedisJsonReviver()(_key, value);
|
|
61
|
-
}
|
|
62
59
|
function redisJsonStringify(value) {
|
|
63
60
|
return JSON.stringify(value, function(key, val) {
|
|
64
61
|
const holder = this;
|
|
@@ -277,23 +274,12 @@ var RedisCacheAdapter = class {
|
|
|
277
274
|
}
|
|
278
275
|
}
|
|
279
276
|
};
|
|
280
|
-
|
|
281
|
-
// src/index.ts
|
|
282
|
-
import {
|
|
283
|
-
cacheDebugStorage,
|
|
284
|
-
isCacheDebugEnabled,
|
|
285
|
-
recordCacheDebug
|
|
286
|
-
} from "@prismakit/core";
|
|
287
277
|
export {
|
|
288
278
|
RedisCacheAdapter,
|
|
289
|
-
cacheDebugStorage,
|
|
290
279
|
cloneWithCodec,
|
|
291
280
|
createRedisJsonReviver,
|
|
292
|
-
isCacheDebugEnabled,
|
|
293
|
-
recordCacheDebug,
|
|
294
281
|
redisJsonParse,
|
|
295
282
|
redisJsonReplacer,
|
|
296
|
-
redisJsonReviver,
|
|
297
283
|
redisJsonStringify
|
|
298
284
|
};
|
|
299
285
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/redis-cache-adapter.ts","../src/redis-json.ts","../src/index.ts"],"sourcesContent":["import Redis from 'ioredis';\nimport { gzipSync, gunzipSync } from 'zlib';\nimport type { CacheAdapter } from '@prismakit/core';\nimport {\n redisJsonParse,\n redisJsonStringify,\n type DecimalFactory,\n type RedisJsonOptions,\n} from './redis-json';\n\nconst INDEX_TTL_BUFFER = 60;\n\n/** Lua: atomically SMEMBERS index + DEL members + index. */\nconst INVALIDATE_BY_INDEX_LUA = `\nlocal members = redis.call('SMEMBERS', KEYS[1])\nfor i, member in ipairs(members) do\n redis.call('DEL', member)\nend\nredis.call('DEL', KEYS[1])\nreturn #members\n`;\n\nexport type RedisCompression = 'none' | 'gzip';\n\nexport type RedisCacheAdapterOptions = {\n url?: string;\n host?: string;\n port?: number;\n prefix?: string;\n /**\n * Compress payloads larger than `compressionThresholdBytes` (default 1024).\n * Uses gzip (widely available; zstd/lz4 can be added later).\n */\n compression?: RedisCompression;\n /** Minimum payload size (bytes) before compression (default 1024). */\n compressionThresholdBytes?: number;\n /**\n * Reconstruct Prisma Decimal from tagged cache payloads.\n * @example decimalFactory: (s) => new Prisma.Decimal(s)\n */\n decimalFactory?: DecimalFactory;\n /** Optional error hook for safe* wrappers (also used by telemetry). */\n onError?: (err: unknown, op?: string) => void;\n};\n\nconst COMPRESSED_PREFIX = 'gz:';\n\n/**\n * Redis-backed {@link CacheAdapter}. Framework-agnostic — no NestJS / ConfigService.\n */\nexport class RedisCacheAdapter implements CacheAdapter {\n private readonly client: Redis;\n private readonly prefix: string;\n private ready = false;\n private readonly compression: RedisCompression;\n private readonly compressionThreshold: number;\n private readonly jsonOptions: RedisJsonOptions;\n onError?: (err: unknown, op?: string) => void;\n\n constructor(options: RedisCacheAdapterOptions = {}) {\n const {\n url,\n host = 'localhost',\n port = 6379,\n prefix = 'prismakit',\n compression = 'none',\n compressionThresholdBytes = 1024,\n decimalFactory,\n onError,\n } = options;\n\n this.prefix = prefix;\n this.compression = compression;\n this.compressionThreshold = compressionThresholdBytes;\n this.jsonOptions = { decimalFactory };\n this.onError = onError;\n this.client = url\n ? new Redis(url, { lazyConnect: true })\n : new Redis({ host, port, lazyConnect: true });\n\n this.client.on('ready', () => {\n this.ready = true;\n });\n this.client.on('error', (err) => {\n this.ready = false;\n console.warn('[RedisCacheAdapter] connection error', err.message);\n });\n this.client.on('close', () => {\n this.ready = false;\n });\n\n void this.connect();\n }\n\n private report(op: string, err: unknown): void {\n console.warn(\n `[RedisCacheAdapter] ${op} failed`,\n (err as Error)?.message ?? err,\n );\n try {\n this.onError?.(err, op);\n } catch {\n /* ignore hook errors */\n }\n }\n\n async connect(): Promise<void> {\n if (this.client.status === 'ready' || this.client.status === 'connecting') {\n return;\n }\n try {\n await this.client.connect();\n } catch (err) {\n this.report('connect', err);\n }\n }\n\n async disconnect(): Promise<void> {\n await this.client?.quit();\n this.ready = false;\n }\n\n isReady(): boolean {\n return this.ready;\n }\n\n getPrefix(): string {\n return this.prefix;\n }\n\n private encode(value: unknown): string {\n const json = redisJsonStringify(value);\n if (\n this.compression === 'gzip' &&\n Buffer.byteLength(json, 'utf8') >= this.compressionThreshold\n ) {\n const compressed = gzipSync(Buffer.from(json, 'utf8')).toString('base64');\n return COMPRESSED_PREFIX + compressed;\n }\n return json;\n }\n\n private decode<T>(raw: string): T {\n if (raw.startsWith(COMPRESSED_PREFIX)) {\n const buf = Buffer.from(raw.slice(COMPRESSED_PREFIX.length), 'base64');\n const json = gunzipSync(buf).toString('utf8');\n return redisJsonParse<T>(json, this.jsonOptions);\n }\n return redisJsonParse<T>(raw, this.jsonOptions);\n }\n\n async get<T>(key: string): Promise<T | null> {\n const raw = await this.client.get(key);\n if (raw === null) return null;\n return this.decode<T>(raw);\n }\n\n async set(key: string, value: unknown, ttlSeconds: number): Promise<void> {\n await this.client.set(key, this.encode(value), 'EX', ttlSeconds);\n }\n\n async del(...keys: string[]): Promise<void> {\n if (keys.length === 0) return;\n await this.client.del(...keys);\n }\n\n async sadd(key: string, ...members: string[]): Promise<void> {\n if (members.length === 0) return;\n await this.client.sadd(key, ...members);\n }\n\n async smembers(key: string): Promise<string[]> {\n return this.client.smembers(key);\n }\n\n async setNx(key: string, ttlSeconds: number): Promise<boolean> {\n const result = await this.client.set(key, '1', 'EX', ttlSeconds, 'NX');\n return result === 'OK';\n }\n\n async setWithIndex(\n key: string,\n value: unknown,\n ttlSeconds: number,\n indexKey: string,\n ): Promise<void> {\n const pipeline = this.client.pipeline();\n pipeline.set(key, this.encode(value), 'EX', ttlSeconds);\n pipeline.sadd(indexKey, key);\n pipeline.expire(indexKey, ttlSeconds + INDEX_TTL_BUFFER);\n await pipeline.exec();\n }\n\n async invalidateByIndex(indexKey: string): Promise<void> {\n await this.client.eval(INVALIDATE_BY_INDEX_LUA, 1, indexKey);\n }\n\n async saddAndExpire(\n key: string,\n members: string[],\n ttlSeconds: number,\n ): Promise<void> {\n if (members.length === 0) return;\n const pipeline = this.client.pipeline();\n pipeline.sadd(key, ...members);\n pipeline.expire(key, ttlSeconds + INDEX_TTL_BUFFER);\n await pipeline.exec();\n }\n\n async safeGet<T>(key: string): Promise<T | null> {\n try {\n return await this.get<T>(key);\n } catch (err) {\n this.report(`safeGet key=${key}`, err);\n return null;\n }\n }\n\n async safeSet(\n key: string,\n value: unknown,\n ttlSeconds: number,\n ): Promise<void> {\n try {\n await this.set(key, value, ttlSeconds);\n } catch (err) {\n this.report(`safeSet key=${key}`, err);\n }\n }\n\n async safeDel(...keys: string[]): Promise<void> {\n try {\n await this.del(...keys);\n } catch (err) {\n this.report('safeDel', err);\n }\n }\n\n async safeSetNx(key: string, ttlSeconds: number): Promise<boolean> {\n try {\n return await this.setNx(key, ttlSeconds);\n } catch (err) {\n this.report(`safeSetNx key=${key}`, err);\n return false;\n }\n }\n\n async safeSetWithIndex(\n key: string,\n value: unknown,\n ttlSeconds: number,\n indexKey: string,\n ): Promise<void> {\n try {\n await this.setWithIndex(key, value, ttlSeconds, indexKey);\n } catch (err) {\n this.report(`safeSetWithIndex key=${key}`, err);\n }\n }\n\n async safeInvalidateByIndex(indexKey: string): Promise<void> {\n try {\n await this.invalidateByIndex(indexKey);\n } catch (err) {\n this.report(`safeInvalidateByIndex idx=${indexKey}`, err);\n }\n }\n\n async safeSaddAndExpire(\n key: string,\n members: string[],\n ttlSeconds: number,\n ): Promise<void> {\n try {\n await this.saddAndExpire(key, members, ttlSeconds);\n } catch (err) {\n this.report(`safeSaddAndExpire key=${key}`, err);\n }\n }\n\n async safeSmembers(key: string): Promise<string[]> {\n try {\n return await this.smembers(key);\n } catch (err) {\n this.report(`safeSmembers key=${key}`, err);\n return [];\n }\n }\n}\n","const BIGINT_TAG = '__bigint';\nconst DATE_TAG = '__date';\nconst BYTES_TAG = '__bytes';\nconst DECIMAL_TAG = '__decimal';\n\nexport type DecimalFactory = (value: string) => unknown;\n\nexport type RedisJsonOptions = {\n /**\n * Reconstruct Prisma `Decimal` (or equivalent) from the tagged string.\n * Default keeps the precision-preserving string so values are not silently\n * coerced to numbers.\n *\n * @example\n * decimalFactory: (s) => new Prisma.Decimal(s)\n */\n decimalFactory?: DecimalFactory;\n};\n\nfunction isPlainObject(value: unknown): value is Record<string, unknown> {\n return (\n value !== null &&\n typeof value === 'object' &&\n !Array.isArray(value) &&\n !(value instanceof Date) &&\n !Buffer.isBuffer(value)\n );\n}\n\nfunction isDecimalLike(value: unknown): value is { toFixed: () => string } {\n return (\n isPlainObject(value) &&\n typeof (value as { constructor?: { name?: string } }).constructor?.name ===\n 'string' &&\n ((value as { constructor: { name: string } }).constructor.name ===\n 'Decimal' ||\n (typeof (value as { toFixed?: unknown }).toFixed === 'function' &&\n typeof (value as { d?: unknown }).d === 'object'))\n );\n}\n\n/** JSON.stringify replacer — Prisma BigInt / Date / Bytes / Decimal. */\nexport function redisJsonReplacer(_key: string, value: unknown): unknown {\n if (typeof value === 'bigint') {\n return { [BIGINT_TAG]: value.toString() };\n }\n if (value instanceof Date) {\n return { [DATE_TAG]: value.toISOString() };\n }\n if (Buffer.isBuffer(value)) {\n return { [BYTES_TAG]: value.toString('base64') };\n }\n if (\n value &&\n typeof value === 'object' &&\n (value as { type?: string }).type === 'Buffer' &&\n Array.isArray((value as { data?: unknown }).data)\n ) {\n return {\n [BYTES_TAG]: Buffer.from(\n (value as { data: number[] }).data,\n ).toString('base64'),\n };\n }\n if (isDecimalLike(value)) {\n return { [DECIMAL_TAG]: value.toFixed() };\n }\n return value;\n}\n\n/** JSON.parse reviver — restore tagged payloads from {@link redisJsonReplacer}. */\nexport function createRedisJsonReviver(options: RedisJsonOptions = {}) {\n const decimalFactory = options.decimalFactory ?? ((s: string) => s);\n return (_key: string, value: unknown): unknown => {\n if (!isPlainObject(value)) return value;\n const keys = Object.keys(value);\n if (keys.length !== 1) return value;\n\n if (BIGINT_TAG in value && typeof value[BIGINT_TAG] === 'string') {\n return BigInt(value[BIGINT_TAG] as string);\n }\n if (DATE_TAG in value && typeof value[DATE_TAG] === 'string') {\n return new Date(value[DATE_TAG] as string);\n }\n if (BYTES_TAG in value && typeof value[BYTES_TAG] === 'string') {\n return Buffer.from(value[BYTES_TAG] as string, 'base64');\n }\n if (DECIMAL_TAG in value && typeof value[DECIMAL_TAG] === 'string') {\n return decimalFactory(value[DECIMAL_TAG] as string);\n }\n return value;\n };\n}\n\n/** @deprecated Prefer {@link createRedisJsonReviver} when a decimalFactory is needed. */\nexport function redisJsonReviver(_key: string, value: unknown): unknown {\n return createRedisJsonReviver()(_key, value);\n}\n\nexport function redisJsonStringify(value: unknown): string {\n return JSON.stringify(value, function (key, val) {\n const holder = this as Record<string, unknown>;\n const raw = key === '' ? value : holder[key];\n if (raw instanceof Date) {\n return redisJsonReplacer(key, raw);\n }\n return redisJsonReplacer(key, val);\n });\n}\n\nexport function redisJsonParse<T>(\n raw: string,\n options?: RedisJsonOptions,\n): T {\n return JSON.parse(raw, createRedisJsonReviver(options)) as T;\n}\n\n/**\n * Deep clone via structuredClone, falling back to the tagged JSON codec so\n * Date / BigInt / Buffer / Decimal survive (unlike plain JSON.stringify).\n */\nexport function cloneWithCodec<T>(value: T, options?: RedisJsonOptions): T {\n if (value === null || value === undefined) return value;\n if (typeof structuredClone === 'function') {\n try {\n return structuredClone(value);\n } catch {\n // Fall through for non-cloneable values (e.g. Decimal)\n }\n }\n return redisJsonParse(redisJsonStringify(value), options);\n}\n","export {\n RedisCacheAdapter,\n type RedisCacheAdapterOptions,\n type RedisCompression,\n} from './redis-cache-adapter';\n\nexport {\n redisJsonParse,\n redisJsonStringify,\n redisJsonReplacer,\n redisJsonReviver,\n createRedisJsonReviver,\n cloneWithCodec,\n type DecimalFactory,\n type RedisJsonOptions,\n} from './redis-json';\n\n/** Re-export cache debug helpers from core — do not duplicate. */\nexport {\n type CacheDebugStatus,\n cacheDebugStorage,\n isCacheDebugEnabled,\n recordCacheDebug,\n} from '@prismakit/core';\n\nexport type { CacheAdapter } from '@prismakit/core';\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/redis-cache-adapter.ts","../src/redis-json.ts"],"sourcesContent":["import Redis from 'ioredis';\nimport { gzipSync, gunzipSync } from 'zlib';\nimport type { CacheAdapter } from '@prismakit/core';\nimport {\n redisJsonParse,\n redisJsonStringify,\n type DecimalFactory,\n type RedisJsonOptions,\n} from './redis-json';\n\nconst INDEX_TTL_BUFFER = 60;\n\n/** Lua: atomically SMEMBERS index + DEL members + index. */\nconst INVALIDATE_BY_INDEX_LUA = `\nlocal members = redis.call('SMEMBERS', KEYS[1])\nfor i, member in ipairs(members) do\n redis.call('DEL', member)\nend\nredis.call('DEL', KEYS[1])\nreturn #members\n`;\n\nexport type RedisCompression = 'none' | 'gzip';\n\nexport type RedisCacheAdapterOptions = {\n url?: string;\n host?: string;\n port?: number;\n prefix?: string;\n /**\n * Compress payloads larger than `compressionThresholdBytes` (default 1024).\n * Uses gzip (widely available; zstd/lz4 can be added later).\n */\n compression?: RedisCompression;\n /** Minimum payload size (bytes) before compression (default 1024). */\n compressionThresholdBytes?: number;\n /**\n * Reconstruct Prisma Decimal from tagged cache payloads.\n * @example decimalFactory: (s) => new Prisma.Decimal(s)\n */\n decimalFactory?: DecimalFactory;\n /** Optional error hook for safe* wrappers (also used by telemetry). */\n onError?: (err: unknown, op?: string) => void;\n};\n\nconst COMPRESSED_PREFIX = 'gz:';\n\n/**\n * Redis-backed {@link CacheAdapter}. Framework-agnostic — no NestJS / ConfigService.\n */\nexport class RedisCacheAdapter implements CacheAdapter {\n private readonly client: Redis;\n private readonly prefix: string;\n private ready = false;\n private readonly compression: RedisCompression;\n private readonly compressionThreshold: number;\n private readonly jsonOptions: RedisJsonOptions;\n onError?: (err: unknown, op?: string) => void;\n\n constructor(options: RedisCacheAdapterOptions = {}) {\n const {\n url,\n host = 'localhost',\n port = 6379,\n prefix = 'prismakit',\n compression = 'none',\n compressionThresholdBytes = 1024,\n decimalFactory,\n onError,\n } = options;\n\n this.prefix = prefix;\n this.compression = compression;\n this.compressionThreshold = compressionThresholdBytes;\n this.jsonOptions = { decimalFactory };\n this.onError = onError;\n this.client = url\n ? new Redis(url, { lazyConnect: true })\n : new Redis({ host, port, lazyConnect: true });\n\n this.client.on('ready', () => {\n this.ready = true;\n });\n this.client.on('error', (err) => {\n this.ready = false;\n console.warn('[RedisCacheAdapter] connection error', err.message);\n });\n this.client.on('close', () => {\n this.ready = false;\n });\n\n void this.connect();\n }\n\n private report(op: string, err: unknown): void {\n console.warn(\n `[RedisCacheAdapter] ${op} failed`,\n (err as Error)?.message ?? err,\n );\n try {\n this.onError?.(err, op);\n } catch {\n /* ignore hook errors */\n }\n }\n\n async connect(): Promise<void> {\n if (this.client.status === 'ready' || this.client.status === 'connecting') {\n return;\n }\n try {\n await this.client.connect();\n } catch (err) {\n this.report('connect', err);\n }\n }\n\n async disconnect(): Promise<void> {\n await this.client?.quit();\n this.ready = false;\n }\n\n isReady(): boolean {\n return this.ready;\n }\n\n getPrefix(): string {\n return this.prefix;\n }\n\n private encode(value: unknown): string {\n const json = redisJsonStringify(value);\n if (\n this.compression === 'gzip' &&\n Buffer.byteLength(json, 'utf8') >= this.compressionThreshold\n ) {\n const compressed = gzipSync(Buffer.from(json, 'utf8')).toString('base64');\n return COMPRESSED_PREFIX + compressed;\n }\n return json;\n }\n\n private decode<T>(raw: string): T {\n if (raw.startsWith(COMPRESSED_PREFIX)) {\n const buf = Buffer.from(raw.slice(COMPRESSED_PREFIX.length), 'base64');\n const json = gunzipSync(buf).toString('utf8');\n return redisJsonParse<T>(json, this.jsonOptions);\n }\n return redisJsonParse<T>(raw, this.jsonOptions);\n }\n\n async get<T>(key: string): Promise<T | null> {\n const raw = await this.client.get(key);\n if (raw === null) return null;\n return this.decode<T>(raw);\n }\n\n async set(key: string, value: unknown, ttlSeconds: number): Promise<void> {\n await this.client.set(key, this.encode(value), 'EX', ttlSeconds);\n }\n\n async del(...keys: string[]): Promise<void> {\n if (keys.length === 0) return;\n await this.client.del(...keys);\n }\n\n async sadd(key: string, ...members: string[]): Promise<void> {\n if (members.length === 0) return;\n await this.client.sadd(key, ...members);\n }\n\n async smembers(key: string): Promise<string[]> {\n return this.client.smembers(key);\n }\n\n async setNx(key: string, ttlSeconds: number): Promise<boolean> {\n const result = await this.client.set(key, '1', 'EX', ttlSeconds, 'NX');\n return result === 'OK';\n }\n\n async setWithIndex(\n key: string,\n value: unknown,\n ttlSeconds: number,\n indexKey: string,\n ): Promise<void> {\n const pipeline = this.client.pipeline();\n pipeline.set(key, this.encode(value), 'EX', ttlSeconds);\n pipeline.sadd(indexKey, key);\n pipeline.expire(indexKey, ttlSeconds + INDEX_TTL_BUFFER);\n await pipeline.exec();\n }\n\n async invalidateByIndex(indexKey: string): Promise<void> {\n await this.client.eval(INVALIDATE_BY_INDEX_LUA, 1, indexKey);\n }\n\n async saddAndExpire(\n key: string,\n members: string[],\n ttlSeconds: number,\n ): Promise<void> {\n if (members.length === 0) return;\n const pipeline = this.client.pipeline();\n pipeline.sadd(key, ...members);\n pipeline.expire(key, ttlSeconds + INDEX_TTL_BUFFER);\n await pipeline.exec();\n }\n\n async safeGet<T>(key: string): Promise<T | null> {\n try {\n return await this.get<T>(key);\n } catch (err) {\n this.report(`safeGet key=${key}`, err);\n return null;\n }\n }\n\n async safeSet(\n key: string,\n value: unknown,\n ttlSeconds: number,\n ): Promise<void> {\n try {\n await this.set(key, value, ttlSeconds);\n } catch (err) {\n this.report(`safeSet key=${key}`, err);\n }\n }\n\n async safeDel(...keys: string[]): Promise<void> {\n try {\n await this.del(...keys);\n } catch (err) {\n this.report('safeDel', err);\n }\n }\n\n async safeSetNx(key: string, ttlSeconds: number): Promise<boolean> {\n try {\n return await this.setNx(key, ttlSeconds);\n } catch (err) {\n this.report(`safeSetNx key=${key}`, err);\n return false;\n }\n }\n\n async safeSetWithIndex(\n key: string,\n value: unknown,\n ttlSeconds: number,\n indexKey: string,\n ): Promise<void> {\n try {\n await this.setWithIndex(key, value, ttlSeconds, indexKey);\n } catch (err) {\n this.report(`safeSetWithIndex key=${key}`, err);\n }\n }\n\n async safeInvalidateByIndex(indexKey: string): Promise<void> {\n try {\n await this.invalidateByIndex(indexKey);\n } catch (err) {\n this.report(`safeInvalidateByIndex idx=${indexKey}`, err);\n }\n }\n\n async safeSaddAndExpire(\n key: string,\n members: string[],\n ttlSeconds: number,\n ): Promise<void> {\n try {\n await this.saddAndExpire(key, members, ttlSeconds);\n } catch (err) {\n this.report(`safeSaddAndExpire key=${key}`, err);\n }\n }\n\n async safeSmembers(key: string): Promise<string[]> {\n try {\n return await this.smembers(key);\n } catch (err) {\n this.report(`safeSmembers key=${key}`, err);\n return [];\n }\n }\n}\n","const BIGINT_TAG = '__bigint';\nconst DATE_TAG = '__date';\nconst BYTES_TAG = '__bytes';\nconst DECIMAL_TAG = '__decimal';\n\nexport type DecimalFactory = (value: string) => unknown;\n\nexport type RedisJsonOptions = {\n /**\n * Reconstruct Prisma `Decimal` (or equivalent) from the tagged string.\n * Default keeps the precision-preserving string so values are not silently\n * coerced to numbers.\n *\n * @example\n * decimalFactory: (s) => new Prisma.Decimal(s)\n */\n decimalFactory?: DecimalFactory;\n};\n\nfunction isPlainObject(value: unknown): value is Record<string, unknown> {\n return (\n value !== null &&\n typeof value === 'object' &&\n !Array.isArray(value) &&\n !(value instanceof Date) &&\n !Buffer.isBuffer(value)\n );\n}\n\nfunction isDecimalLike(value: unknown): value is { toFixed: () => string } {\n return (\n isPlainObject(value) &&\n typeof (value as { constructor?: { name?: string } }).constructor?.name ===\n 'string' &&\n ((value as { constructor: { name: string } }).constructor.name ===\n 'Decimal' ||\n (typeof (value as { toFixed?: unknown }).toFixed === 'function' &&\n typeof (value as { d?: unknown }).d === 'object'))\n );\n}\n\n/** JSON.stringify replacer — Prisma BigInt / Date / Bytes / Decimal. */\nexport function redisJsonReplacer(_key: string, value: unknown): unknown {\n if (typeof value === 'bigint') {\n return { [BIGINT_TAG]: value.toString() };\n }\n if (value instanceof Date) {\n return { [DATE_TAG]: value.toISOString() };\n }\n if (Buffer.isBuffer(value)) {\n return { [BYTES_TAG]: value.toString('base64') };\n }\n if (\n value &&\n typeof value === 'object' &&\n (value as { type?: string }).type === 'Buffer' &&\n Array.isArray((value as { data?: unknown }).data)\n ) {\n return {\n [BYTES_TAG]: Buffer.from(\n (value as { data: number[] }).data,\n ).toString('base64'),\n };\n }\n if (isDecimalLike(value)) {\n return { [DECIMAL_TAG]: value.toFixed() };\n }\n return value;\n}\n\n/** JSON.parse reviver — restore tagged payloads from {@link redisJsonReplacer}. */\nexport function createRedisJsonReviver(options: RedisJsonOptions = {}) {\n const decimalFactory = options.decimalFactory ?? ((s: string) => s);\n return (_key: string, value: unknown): unknown => {\n if (!isPlainObject(value)) return value;\n const keys = Object.keys(value);\n if (keys.length !== 1) return value;\n\n if (BIGINT_TAG in value && typeof value[BIGINT_TAG] === 'string') {\n return BigInt(value[BIGINT_TAG] as string);\n }\n if (DATE_TAG in value && typeof value[DATE_TAG] === 'string') {\n return new Date(value[DATE_TAG] as string);\n }\n if (BYTES_TAG in value && typeof value[BYTES_TAG] === 'string') {\n return Buffer.from(value[BYTES_TAG] as string, 'base64');\n }\n if (DECIMAL_TAG in value && typeof value[DECIMAL_TAG] === 'string') {\n return decimalFactory(value[DECIMAL_TAG] as string);\n }\n return value;\n };\n}\n\nexport function redisJsonStringify(value: unknown): string {\n return JSON.stringify(value, function (key, val) {\n const holder = this as Record<string, unknown>;\n const raw = key === '' ? value : holder[key];\n if (raw instanceof Date) {\n return redisJsonReplacer(key, raw);\n }\n return redisJsonReplacer(key, val);\n });\n}\n\nexport function redisJsonParse<T>(\n raw: string,\n options?: RedisJsonOptions,\n): T {\n return JSON.parse(raw, createRedisJsonReviver(options)) as T;\n}\n\n/**\n * Deep clone via structuredClone, falling back to the tagged JSON codec so\n * Date / BigInt / Buffer / Decimal survive (unlike plain JSON.stringify).\n */\nexport function cloneWithCodec<T>(value: T, options?: RedisJsonOptions): T {\n if (value === null || value === undefined) return value;\n if (typeof structuredClone === 'function') {\n try {\n return structuredClone(value);\n } catch {\n // Fall through for non-cloneable values (e.g. Decimal)\n }\n }\n return redisJsonParse(redisJsonStringify(value), options);\n}\n"],"mappings":"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","names":[]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@prismakit/redis",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "4.0.0",
|
|
4
4
|
"description": "Redis CacheAdapter for @prismakit/core",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"engines": {
|
|
@@ -24,10 +24,10 @@
|
|
|
24
24
|
"README.md"
|
|
25
25
|
],
|
|
26
26
|
"dependencies": {
|
|
27
|
-
"@prismakit/core": "
|
|
27
|
+
"@prismakit/core": "4.0.0"
|
|
28
28
|
},
|
|
29
29
|
"peerDependencies": {
|
|
30
|
-
"@prismakit/core": ">=
|
|
30
|
+
"@prismakit/core": ">=4.0.0 <5",
|
|
31
31
|
"ioredis": ">=5.0.0"
|
|
32
32
|
},
|
|
33
33
|
"devDependencies": {
|
package/src/index.ts
CHANGED
|
@@ -8,19 +8,10 @@ export {
|
|
|
8
8
|
redisJsonParse,
|
|
9
9
|
redisJsonStringify,
|
|
10
10
|
redisJsonReplacer,
|
|
11
|
-
redisJsonReviver,
|
|
12
11
|
createRedisJsonReviver,
|
|
13
12
|
cloneWithCodec,
|
|
14
13
|
type DecimalFactory,
|
|
15
14
|
type RedisJsonOptions,
|
|
16
15
|
} from './redis-json';
|
|
17
16
|
|
|
18
|
-
/** Re-export cache debug helpers from core — do not duplicate. */
|
|
19
|
-
export {
|
|
20
|
-
type CacheDebugStatus,
|
|
21
|
-
cacheDebugStorage,
|
|
22
|
-
isCacheDebugEnabled,
|
|
23
|
-
recordCacheDebug,
|
|
24
|
-
} from '@prismakit/core';
|
|
25
|
-
|
|
26
17
|
export type { CacheAdapter } from '@prismakit/core';
|
package/src/redis-json.ts
CHANGED
|
@@ -92,11 +92,6 @@ export function createRedisJsonReviver(options: RedisJsonOptions = {}) {
|
|
|
92
92
|
};
|
|
93
93
|
}
|
|
94
94
|
|
|
95
|
-
/** @deprecated Prefer {@link createRedisJsonReviver} when a decimalFactory is needed. */
|
|
96
|
-
export function redisJsonReviver(_key: string, value: unknown): unknown {
|
|
97
|
-
return createRedisJsonReviver()(_key, value);
|
|
98
|
-
}
|
|
99
|
-
|
|
100
95
|
export function redisJsonStringify(value: unknown): string {
|
|
101
96
|
return JSON.stringify(value, function (key, val) {
|
|
102
97
|
const holder = this as Record<string, unknown>;
|