@forklaunch/infrastructure-redis 0.0.1

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/lib/index.d.ts ADDED
@@ -0,0 +1,176 @@
1
+ import { TtlCache, TtlCacheRecord } from '@forklaunch/core/cache';
2
+ import { OpenTelemetryCollector, MetricsDefinition, TelemetryOptions } from '@forklaunch/core/http';
3
+ import { RedisClientOptions } from 'redis';
4
+
5
+ /**
6
+ * Class representing a Redis-based TTL (Time-To-Live) cache.
7
+ * Implements the TtlCache interface to provide caching functionality with automatic expiration.
8
+ */
9
+ declare class RedisTtlCache implements TtlCache {
10
+ private ttlMilliseconds;
11
+ private openTelemetryCollector;
12
+ private client;
13
+ private telemetryOptions;
14
+ /**
15
+ * Creates an instance of RedisTtlCache.
16
+ *
17
+ * @param {number} ttlMilliseconds - The default Time-To-Live in milliseconds for cache entries
18
+ * @param {OpenTelemetryCollector<MetricsDefinition>} openTelemetryCollector - Collector for OpenTelemetry metrics
19
+ * @param {RedisClientOptions} hostingOptions - Configuration options for the Redis client
20
+ * @param {TelemetryOptions} telemetryOptions - Configuration options for telemetry
21
+ */
22
+ constructor(ttlMilliseconds: number, openTelemetryCollector: OpenTelemetryCollector<MetricsDefinition>, options: RedisClientOptions, telemetryOptions: TelemetryOptions);
23
+ /**
24
+ * Parses a raw Redis reply into the expected type.
25
+ * Handles null values, arrays, buffers, and JSON strings.
26
+ *
27
+ * @template T - The expected type of the parsed value
28
+ * @param {RedisCommandRawReply} value - The raw value from Redis to parse
29
+ * @returns {T} The parsed value cast to type T
30
+ */
31
+ private parseValue;
32
+ /**
33
+ * Puts a record into the Redis cache.
34
+ *
35
+ * @template T - The type of value being cached
36
+ * @param {TtlCacheRecord<T>} param0 - The cache record containing key, value and optional TTL
37
+ * @param {string} param0.key - The key to store the value under
38
+ * @param {T} param0.value - The value to cache
39
+ * @param {number} [param0.ttlMilliseconds] - Optional TTL in milliseconds, defaults to constructor value
40
+ * @returns {Promise<void>} A promise that resolves when the value is cached
41
+ */
42
+ putRecord<T>({ key, value, ttlMilliseconds }: TtlCacheRecord<T>): Promise<void>;
43
+ /**
44
+ * Puts multiple records into the Redis cache in a single transaction.
45
+ *
46
+ * @template T - The type of values being cached
47
+ * @param {TtlCacheRecord<T>[]} cacheRecords - Array of cache records to store
48
+ * @returns {Promise<void>} A promise that resolves when all values are cached
49
+ */
50
+ putBatchRecords<T>(cacheRecords: TtlCacheRecord<T>[]): Promise<void>;
51
+ /**
52
+ * Adds a value to the left end of a Redis list.
53
+ *
54
+ * @template T - The type of value being enqueued
55
+ * @param {string} queueName - The name of the Redis list
56
+ * @param {T} value - The value to add to the list
57
+ * @returns {Promise<void>} A promise that resolves when the value is enqueued
58
+ */
59
+ enqueueRecord<T>(queueName: string, value: T): Promise<void>;
60
+ /**
61
+ * Adds multiple values to the left end of a Redis list in a single transaction.
62
+ *
63
+ * @template T - The type of values being enqueued
64
+ * @param {string} queueName - The name of the Redis list
65
+ * @param {T[]} values - Array of values to add to the list
66
+ * @returns {Promise<void>} A promise that resolves when all values are enqueued
67
+ */
68
+ enqueueBatchRecords<T>(queueName: string, values: T[]): Promise<void>;
69
+ /**
70
+ * Deletes a record from the Redis cache.
71
+ *
72
+ * @param {string} cacheRecordKey - The key of the record to delete
73
+ * @returns {Promise<void>} A promise that resolves when the record is deleted
74
+ */
75
+ deleteRecord(cacheRecordKey: string): Promise<void>;
76
+ /**
77
+ * Deletes multiple records from the Redis cache in a single transaction.
78
+ *
79
+ * @param {string[]} cacheRecordKeys - Array of keys to delete
80
+ * @returns {Promise<void>} A promise that resolves when all records are deleted
81
+ */
82
+ deleteBatchRecords(cacheRecordKeys: string[]): Promise<void>;
83
+ /**
84
+ * Removes and returns the rightmost element from a Redis list.
85
+ *
86
+ * @template T - The type of value being dequeued
87
+ * @param {string} queueName - The name of the Redis list
88
+ * @returns {Promise<T>} A promise that resolves with the dequeued value
89
+ * @throws {Error} If the queue is empty
90
+ */
91
+ dequeueRecord<T>(queueName: string): Promise<T>;
92
+ /**
93
+ * Removes and returns multiple elements from the right end of a Redis list.
94
+ *
95
+ * @template T - The type of values being dequeued
96
+ * @param {string} queueName - The name of the Redis list
97
+ * @param {number} pageSize - Maximum number of elements to dequeue
98
+ * @returns {Promise<T[]>} A promise that resolves with an array of dequeued values
99
+ */
100
+ dequeueBatchRecords<T>(queueName: string, pageSize: number): Promise<T[]>;
101
+ /**
102
+ * Reads a record from the Redis cache.
103
+ *
104
+ * @template T - The type of value being read
105
+ * @param {string} cacheRecordKey - The key of the record to read
106
+ * @returns {Promise<TtlCacheRecord<T>>} A promise that resolves with the cache record
107
+ * @throws {Error} If the record is not found
108
+ */
109
+ readRecord<T>(cacheRecordKey: string): Promise<TtlCacheRecord<T>>;
110
+ /**
111
+ * Reads multiple records from the Redis cache.
112
+ *
113
+ * @template T - The type of values being read
114
+ * @param {string[] | string} cacheRecordKeysOrPrefix - Array of keys to read, or a prefix pattern
115
+ * @returns {Promise<TtlCacheRecord<T>[]>} A promise that resolves with an array of cache records
116
+ */
117
+ readBatchRecords<T>(cacheRecordKeysOrPrefix: string[] | string): Promise<TtlCacheRecord<T>[]>;
118
+ /**
119
+ * Lists all keys in the Redis cache that match a pattern prefix.
120
+ *
121
+ * @param {string} pattern_prefix - The prefix pattern to match keys against
122
+ * @returns {Promise<string[]>} A promise that resolves with an array of matching keys
123
+ */
124
+ listKeys(pattern_prefix: string): Promise<string[]>;
125
+ /**
126
+ * Checks if a record exists in the Redis cache.
127
+ *
128
+ * @param {string} cacheRecordKey - The key to check
129
+ * @returns {Promise<boolean>} A promise that resolves with true if the record exists, false otherwise
130
+ */
131
+ peekRecord(cacheRecordKey: string): Promise<boolean>;
132
+ /**
133
+ * Checks if multiple records exist in the Redis cache.
134
+ *
135
+ * @param {string[] | string} cacheRecordKeysOrPrefix - Array of keys to check, or a prefix pattern
136
+ * @returns {Promise<boolean[]>} A promise that resolves with an array of existence booleans
137
+ */
138
+ peekBatchRecords(cacheRecordKeysOrPrefix: string[] | string): Promise<boolean[]>;
139
+ /**
140
+ * Peeks at a record in the Redis cache.
141
+ *
142
+ * @template T - The type of value being peeked at
143
+ * @param {string} queueName - The name of the Redis queue
144
+ * @returns {Promise<T>} A promise that resolves with the peeked value
145
+ */
146
+ peekQueueRecord<T>(queueName: string): Promise<T>;
147
+ /**
148
+ * Peeks at multiple records in the Redis cache.
149
+ *
150
+ * @template T - The type of values being peeked at
151
+ * @param {string} queueName - The name of the Redis queue
152
+ * @param {number} pageSize - The number of records to peek at
153
+ * @returns {Promise<T[]>} A promise that resolves with an array of peeked values
154
+ */
155
+ peekQueueRecords<T>(queueName: string, pageSize: number): Promise<T[]>;
156
+ /**
157
+ * Gracefully disconnects from the Redis server.
158
+ *
159
+ * @returns {Promise<void>} A promise that resolves when the connection is closed
160
+ */
161
+ disconnect(): Promise<void>;
162
+ /**
163
+ * Gets the default Time-To-Live value in milliseconds.
164
+ *
165
+ * @returns {number} The default TTL in milliseconds
166
+ */
167
+ getTtlMilliseconds(): number;
168
+ /**
169
+ * Gets the underlying Redis client instance.
170
+ *
171
+ * @returns {typeof this.client} The Redis client instance
172
+ */
173
+ getClient(): typeof this.client;
174
+ }
175
+
176
+ export { RedisTtlCache };
package/lib/index.js ADDED
@@ -0,0 +1,335 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // index.ts
21
+ var index_exports = {};
22
+ __export(index_exports, {
23
+ RedisTtlCache: () => RedisTtlCache
24
+ });
25
+ module.exports = __toCommonJS(index_exports);
26
+ var import_common = require("@forklaunch/common");
27
+ var import_http = require("@forklaunch/core/http");
28
+ var import_redis = require("redis");
29
+ var RedisTtlCache = class {
30
+ /**
31
+ * Creates an instance of RedisTtlCache.
32
+ *
33
+ * @param {number} ttlMilliseconds - The default Time-To-Live in milliseconds for cache entries
34
+ * @param {OpenTelemetryCollector<MetricsDefinition>} openTelemetryCollector - Collector for OpenTelemetry metrics
35
+ * @param {RedisClientOptions} hostingOptions - Configuration options for the Redis client
36
+ * @param {TelemetryOptions} telemetryOptions - Configuration options for telemetry
37
+ */
38
+ constructor(ttlMilliseconds, openTelemetryCollector, options, telemetryOptions) {
39
+ this.ttlMilliseconds = ttlMilliseconds;
40
+ this.openTelemetryCollector = openTelemetryCollector;
41
+ this.telemetryOptions = (0, import_http.evaluateTelemetryOptions)(telemetryOptions);
42
+ this.client = (0, import_redis.createClient)(options);
43
+ if (this.telemetryOptions.enabled.logging) {
44
+ this.client.on("error", (err) => this.openTelemetryCollector.error(err));
45
+ this.client.connect().catch(this.openTelemetryCollector.error);
46
+ }
47
+ }
48
+ client;
49
+ telemetryOptions;
50
+ /**
51
+ * Parses a raw Redis reply into the expected type.
52
+ * Handles null values, arrays, buffers, and JSON strings.
53
+ *
54
+ * @template T - The expected type of the parsed value
55
+ * @param {RedisCommandRawReply} value - The raw value from Redis to parse
56
+ * @returns {T} The parsed value cast to type T
57
+ */
58
+ parseValue(value) {
59
+ if (value == null) {
60
+ return null;
61
+ }
62
+ if (Array.isArray(value)) {
63
+ return value.map((v) => this.parseValue(v));
64
+ }
65
+ if (Buffer.isBuffer(value)) {
66
+ return value.toJSON();
67
+ }
68
+ switch (typeof value) {
69
+ case "object":
70
+ case "string":
71
+ return (0, import_common.safeParse)(value);
72
+ case "number":
73
+ return value;
74
+ }
75
+ }
76
+ /**
77
+ * Puts a record into the Redis cache.
78
+ *
79
+ * @template T - The type of value being cached
80
+ * @param {TtlCacheRecord<T>} param0 - The cache record containing key, value and optional TTL
81
+ * @param {string} param0.key - The key to store the value under
82
+ * @param {T} param0.value - The value to cache
83
+ * @param {number} [param0.ttlMilliseconds] - Optional TTL in milliseconds, defaults to constructor value
84
+ * @returns {Promise<void>} A promise that resolves when the value is cached
85
+ */
86
+ async putRecord({
87
+ key,
88
+ value,
89
+ ttlMilliseconds = this.ttlMilliseconds
90
+ }) {
91
+ if (this.telemetryOptions.enabled.logging) {
92
+ this.openTelemetryCollector.info(`Putting record into cache: ${key}`);
93
+ }
94
+ await this.client.set(key, (0, import_common.safeStringify)(value), {
95
+ PX: ttlMilliseconds
96
+ });
97
+ }
98
+ /**
99
+ * Puts multiple records into the Redis cache in a single transaction.
100
+ *
101
+ * @template T - The type of values being cached
102
+ * @param {TtlCacheRecord<T>[]} cacheRecords - Array of cache records to store
103
+ * @returns {Promise<void>} A promise that resolves when all values are cached
104
+ */
105
+ async putBatchRecords(cacheRecords) {
106
+ const multiCommand = this.client.multi();
107
+ for (const { key, value, ttlMilliseconds } of cacheRecords) {
108
+ multiCommand.set(key, (0, import_common.safeStringify)(value), {
109
+ PX: ttlMilliseconds || this.ttlMilliseconds
110
+ });
111
+ }
112
+ await multiCommand.exec();
113
+ }
114
+ /**
115
+ * Adds a value to the left end of a Redis list.
116
+ *
117
+ * @template T - The type of value being enqueued
118
+ * @param {string} queueName - The name of the Redis list
119
+ * @param {T} value - The value to add to the list
120
+ * @returns {Promise<void>} A promise that resolves when the value is enqueued
121
+ */
122
+ async enqueueRecord(queueName, value) {
123
+ await this.client.lPush(queueName, (0, import_common.safeStringify)(value));
124
+ }
125
+ /**
126
+ * Adds multiple values to the left end of a Redis list in a single transaction.
127
+ *
128
+ * @template T - The type of values being enqueued
129
+ * @param {string} queueName - The name of the Redis list
130
+ * @param {T[]} values - Array of values to add to the list
131
+ * @returns {Promise<void>} A promise that resolves when all values are enqueued
132
+ */
133
+ async enqueueBatchRecords(queueName, values) {
134
+ const multiCommand = this.client.multi();
135
+ for (const value of values) {
136
+ multiCommand.lPush(queueName, (0, import_common.safeStringify)(value));
137
+ }
138
+ await multiCommand.exec();
139
+ }
140
+ /**
141
+ * Deletes a record from the Redis cache.
142
+ *
143
+ * @param {string} cacheRecordKey - The key of the record to delete
144
+ * @returns {Promise<void>} A promise that resolves when the record is deleted
145
+ */
146
+ async deleteRecord(cacheRecordKey) {
147
+ await this.client.del(cacheRecordKey);
148
+ }
149
+ /**
150
+ * Deletes multiple records from the Redis cache in a single transaction.
151
+ *
152
+ * @param {string[]} cacheRecordKeys - Array of keys to delete
153
+ * @returns {Promise<void>} A promise that resolves when all records are deleted
154
+ */
155
+ async deleteBatchRecords(cacheRecordKeys) {
156
+ const multiCommand = this.client.multi();
157
+ for (const key of cacheRecordKeys) {
158
+ multiCommand.del(key);
159
+ }
160
+ await multiCommand.exec();
161
+ }
162
+ /**
163
+ * Removes and returns the rightmost element from a Redis list.
164
+ *
165
+ * @template T - The type of value being dequeued
166
+ * @param {string} queueName - The name of the Redis list
167
+ * @returns {Promise<T>} A promise that resolves with the dequeued value
168
+ * @throws {Error} If the queue is empty
169
+ */
170
+ async dequeueRecord(queueName) {
171
+ const value = await this.client.rPop(queueName);
172
+ if (value === null) {
173
+ throw new Error(`Queue is empty: ${queueName}`);
174
+ }
175
+ return (0, import_common.safeParse)(value);
176
+ }
177
+ /**
178
+ * Removes and returns multiple elements from the right end of a Redis list.
179
+ *
180
+ * @template T - The type of values being dequeued
181
+ * @param {string} queueName - The name of the Redis list
182
+ * @param {number} pageSize - Maximum number of elements to dequeue
183
+ * @returns {Promise<T[]>} A promise that resolves with an array of dequeued values
184
+ */
185
+ async dequeueBatchRecords(queueName, pageSize) {
186
+ const multiCommand = this.client.multi();
187
+ for (let i = 0; i < pageSize; i++) {
188
+ multiCommand.rPop(queueName);
189
+ }
190
+ const values = await multiCommand.exec();
191
+ return values.map(
192
+ (value) => this.parseValue(value)
193
+ ).filter(Boolean);
194
+ }
195
+ /**
196
+ * Reads a record from the Redis cache.
197
+ *
198
+ * @template T - The type of value being read
199
+ * @param {string} cacheRecordKey - The key of the record to read
200
+ * @returns {Promise<TtlCacheRecord<T>>} A promise that resolves with the cache record
201
+ * @throws {Error} If the record is not found
202
+ */
203
+ async readRecord(cacheRecordKey) {
204
+ const [value, ttl] = await this.client.multi().get(cacheRecordKey).ttl(cacheRecordKey).exec();
205
+ if (value === null) {
206
+ throw new Error(`Record not found for key: ${cacheRecordKey}`);
207
+ }
208
+ return {
209
+ key: cacheRecordKey,
210
+ value: this.parseValue(value),
211
+ ttlMilliseconds: this.parseValue(ttl) * 1e3
212
+ };
213
+ }
214
+ /**
215
+ * Reads multiple records from the Redis cache.
216
+ *
217
+ * @template T - The type of values being read
218
+ * @param {string[] | string} cacheRecordKeysOrPrefix - Array of keys to read, or a prefix pattern
219
+ * @returns {Promise<TtlCacheRecord<T>[]>} A promise that resolves with an array of cache records
220
+ */
221
+ async readBatchRecords(cacheRecordKeysOrPrefix) {
222
+ const keys = Array.isArray(cacheRecordKeysOrPrefix) ? cacheRecordKeysOrPrefix : await this.client.keys(cacheRecordKeysOrPrefix + "*");
223
+ const multiCommand = this.client.multi();
224
+ for (const key of keys) {
225
+ multiCommand.get(key);
226
+ multiCommand.ttl(key);
227
+ }
228
+ const values = await multiCommand.exec();
229
+ return values.reduce((acc, value, index) => {
230
+ if (index % 2 === 0) {
231
+ const maybeValue = this.parseValue(
232
+ value
233
+ );
234
+ const ttl = this.parseValue(
235
+ values[index + 1]
236
+ );
237
+ if (maybeValue && ttl) {
238
+ acc.push({
239
+ key: keys[index / 2],
240
+ value: maybeValue,
241
+ ttlMilliseconds: ttl * 1e3
242
+ });
243
+ }
244
+ }
245
+ return acc;
246
+ }, []);
247
+ }
248
+ /**
249
+ * Lists all keys in the Redis cache that match a pattern prefix.
250
+ *
251
+ * @param {string} pattern_prefix - The prefix pattern to match keys against
252
+ * @returns {Promise<string[]>} A promise that resolves with an array of matching keys
253
+ */
254
+ async listKeys(pattern_prefix) {
255
+ const keys = await this.client.keys(pattern_prefix + "*");
256
+ return keys;
257
+ }
258
+ /**
259
+ * Checks if a record exists in the Redis cache.
260
+ *
261
+ * @param {string} cacheRecordKey - The key to check
262
+ * @returns {Promise<boolean>} A promise that resolves with true if the record exists, false otherwise
263
+ */
264
+ async peekRecord(cacheRecordKey) {
265
+ const result = await this.client.exists(cacheRecordKey);
266
+ return result === 1;
267
+ }
268
+ /**
269
+ * Checks if multiple records exist in the Redis cache.
270
+ *
271
+ * @param {string[] | string} cacheRecordKeysOrPrefix - Array of keys to check, or a prefix pattern
272
+ * @returns {Promise<boolean[]>} A promise that resolves with an array of existence booleans
273
+ */
274
+ async peekBatchRecords(cacheRecordKeysOrPrefix) {
275
+ const keys = Array.isArray(cacheRecordKeysOrPrefix) ? cacheRecordKeysOrPrefix : await this.client.keys(cacheRecordKeysOrPrefix + "*");
276
+ const multiCommand = this.client.multi();
277
+ for (const key of keys) {
278
+ multiCommand.exists(key);
279
+ }
280
+ const results = await multiCommand.exec();
281
+ return results.map((result) => result === 1);
282
+ }
283
+ /**
284
+ * Peeks at a record in the Redis cache.
285
+ *
286
+ * @template T - The type of value being peeked at
287
+ * @param {string} queueName - The name of the Redis queue
288
+ * @returns {Promise<T>} A promise that resolves with the peeked value
289
+ */
290
+ async peekQueueRecord(queueName) {
291
+ const value = await this.client.lRange(queueName, 0, 0);
292
+ return this.parseValue(value[0]);
293
+ }
294
+ /**
295
+ * Peeks at multiple records in the Redis cache.
296
+ *
297
+ * @template T - The type of values being peeked at
298
+ * @param {string} queueName - The name of the Redis queue
299
+ * @param {number} pageSize - The number of records to peek at
300
+ * @returns {Promise<T[]>} A promise that resolves with an array of peeked values
301
+ */
302
+ async peekQueueRecords(queueName, pageSize) {
303
+ const values = await this.client.lRange(queueName, 0, pageSize - 1);
304
+ return values.map((value) => this.parseValue(value)).filter(Boolean);
305
+ }
306
+ /**
307
+ * Gracefully disconnects from the Redis server.
308
+ *
309
+ * @returns {Promise<void>} A promise that resolves when the connection is closed
310
+ */
311
+ async disconnect() {
312
+ await this.client.quit();
313
+ }
314
+ /**
315
+ * Gets the default Time-To-Live value in milliseconds.
316
+ *
317
+ * @returns {number} The default TTL in milliseconds
318
+ */
319
+ getTtlMilliseconds() {
320
+ return this.ttlMilliseconds;
321
+ }
322
+ /**
323
+ * Gets the underlying Redis client instance.
324
+ *
325
+ * @returns {typeof this.client} The Redis client instance
326
+ */
327
+ getClient() {
328
+ return this.client;
329
+ }
330
+ };
331
+ // Annotate the CommonJS export names for ESM import in node:
332
+ 0 && (module.exports = {
333
+ RedisTtlCache
334
+ });
335
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../index.ts"],"sourcesContent":["import { safeParse, safeStringify } from '@forklaunch/common';\nimport { TtlCache, TtlCacheRecord } from '@forklaunch/core/cache';\nimport {\n evaluateTelemetryOptions,\n MetricsDefinition,\n OpenTelemetryCollector,\n TelemetryOptions\n} from '@forklaunch/core/http';\nimport { createClient, RedisClientOptions } from 'redis';\n\n/**\n * Type representing a raw reply from Redis commands.\n * Can be a string, number, Buffer, null, undefined, or array of raw replies.\n */\ntype RedisCommandRawReply =\n | string\n | number\n | Buffer\n | null\n | undefined\n | Array<RedisCommandRawReply>;\n\n/**\n * Class representing a Redis-based TTL (Time-To-Live) cache.\n * Implements the TtlCache interface to provide caching functionality with automatic expiration.\n */\nexport class RedisTtlCache implements TtlCache {\n private client;\n private telemetryOptions;\n\n /**\n * Creates an instance of RedisTtlCache.\n *\n * @param {number} ttlMilliseconds - The default Time-To-Live in milliseconds for cache entries\n * @param {OpenTelemetryCollector<MetricsDefinition>} openTelemetryCollector - Collector for OpenTelemetry metrics\n * @param {RedisClientOptions} hostingOptions - Configuration options for the Redis client\n * @param {TelemetryOptions} telemetryOptions - Configuration options for telemetry\n */\n constructor(\n private ttlMilliseconds: number,\n private openTelemetryCollector: OpenTelemetryCollector<MetricsDefinition>,\n options: RedisClientOptions,\n telemetryOptions: TelemetryOptions\n ) {\n this.telemetryOptions = evaluateTelemetryOptions(telemetryOptions);\n this.client = createClient(options);\n if (this.telemetryOptions.enabled.logging) {\n this.client.on('error', (err) => this.openTelemetryCollector.error(err));\n this.client.connect().catch(this.openTelemetryCollector.error);\n }\n }\n\n /**\n * Parses a raw Redis reply into the expected type.\n * Handles null values, arrays, buffers, and JSON strings.\n *\n * @template T - The expected type of the parsed value\n * @param {RedisCommandRawReply} value - The raw value from Redis to parse\n * @returns {T} The parsed value cast to type T\n */\n private parseValue<T>(value: RedisCommandRawReply): T {\n if (value == null) {\n return null as T;\n }\n\n if (Array.isArray(value)) {\n return value.map((v) => this.parseValue<T>(v)) as T;\n }\n\n if (Buffer.isBuffer(value)) {\n return value.toJSON() as T;\n }\n\n switch (typeof value) {\n case 'object':\n case 'string':\n return safeParse(value) as T;\n case 'number':\n return value as T;\n }\n }\n\n /**\n * Puts a record into the Redis cache.\n *\n * @template T - The type of value being cached\n * @param {TtlCacheRecord<T>} param0 - The cache record containing key, value and optional TTL\n * @param {string} param0.key - The key to store the value under\n * @param {T} param0.value - The value to cache\n * @param {number} [param0.ttlMilliseconds] - Optional TTL in milliseconds, defaults to constructor value\n * @returns {Promise<void>} A promise that resolves when the value is cached\n */\n async putRecord<T>({\n key,\n value,\n ttlMilliseconds = this.ttlMilliseconds\n }: TtlCacheRecord<T>): Promise<void> {\n if (this.telemetryOptions.enabled.logging) {\n this.openTelemetryCollector.info(`Putting record into cache: ${key}`);\n }\n await this.client.set(key, safeStringify(value), {\n PX: ttlMilliseconds\n });\n }\n\n /**\n * Puts multiple records into the Redis cache in a single transaction.\n *\n * @template T - The type of values being cached\n * @param {TtlCacheRecord<T>[]} cacheRecords - Array of cache records to store\n * @returns {Promise<void>} A promise that resolves when all values are cached\n */\n async putBatchRecords<T>(cacheRecords: TtlCacheRecord<T>[]): Promise<void> {\n const multiCommand = this.client.multi();\n for (const { key, value, ttlMilliseconds } of cacheRecords) {\n multiCommand.set(key, safeStringify(value), {\n PX: ttlMilliseconds || this.ttlMilliseconds\n });\n }\n await multiCommand.exec();\n }\n\n /**\n * Adds a value to the left end of a Redis list.\n *\n * @template T - The type of value being enqueued\n * @param {string} queueName - The name of the Redis list\n * @param {T} value - The value to add to the list\n * @returns {Promise<void>} A promise that resolves when the value is enqueued\n */\n async enqueueRecord<T>(queueName: string, value: T): Promise<void> {\n await this.client.lPush(queueName, safeStringify(value));\n }\n\n /**\n * Adds multiple values to the left end of a Redis list in a single transaction.\n *\n * @template T - The type of values being enqueued\n * @param {string} queueName - The name of the Redis list\n * @param {T[]} values - Array of values to add to the list\n * @returns {Promise<void>} A promise that resolves when all values are enqueued\n */\n async enqueueBatchRecords<T>(queueName: string, values: T[]): Promise<void> {\n const multiCommand = this.client.multi();\n for (const value of values) {\n multiCommand.lPush(queueName, safeStringify(value));\n }\n await multiCommand.exec();\n }\n\n /**\n * Deletes a record from the Redis cache.\n *\n * @param {string} cacheRecordKey - The key of the record to delete\n * @returns {Promise<void>} A promise that resolves when the record is deleted\n */\n async deleteRecord(cacheRecordKey: string): Promise<void> {\n await this.client.del(cacheRecordKey);\n }\n\n /**\n * Deletes multiple records from the Redis cache in a single transaction.\n *\n * @param {string[]} cacheRecordKeys - Array of keys to delete\n * @returns {Promise<void>} A promise that resolves when all records are deleted\n */\n async deleteBatchRecords(cacheRecordKeys: string[]): Promise<void> {\n const multiCommand = this.client.multi();\n for (const key of cacheRecordKeys) {\n multiCommand.del(key);\n }\n await multiCommand.exec();\n }\n\n /**\n * Removes and returns the rightmost element from a Redis list.\n *\n * @template T - The type of value being dequeued\n * @param {string} queueName - The name of the Redis list\n * @returns {Promise<T>} A promise that resolves with the dequeued value\n * @throws {Error} If the queue is empty\n */\n async dequeueRecord<T>(queueName: string): Promise<T> {\n const value = await this.client.rPop(queueName);\n if (value === null) {\n throw new Error(`Queue is empty: ${queueName}`);\n }\n return safeParse(value) as T;\n }\n\n /**\n * Removes and returns multiple elements from the right end of a Redis list.\n *\n * @template T - The type of values being dequeued\n * @param {string} queueName - The name of the Redis list\n * @param {number} pageSize - Maximum number of elements to dequeue\n * @returns {Promise<T[]>} A promise that resolves with an array of dequeued values\n */\n async dequeueBatchRecords<T>(\n queueName: string,\n pageSize: number\n ): Promise<T[]> {\n const multiCommand = this.client.multi();\n for (let i = 0; i < pageSize; i++) {\n multiCommand.rPop(queueName);\n }\n const values = await multiCommand.exec();\n return values\n .map((value) =>\n this.parseValue<T>(value as unknown as RedisCommandRawReply)\n )\n .filter(Boolean);\n }\n\n /**\n * Reads a record from the Redis cache.\n *\n * @template T - The type of value being read\n * @param {string} cacheRecordKey - The key of the record to read\n * @returns {Promise<TtlCacheRecord<T>>} A promise that resolves with the cache record\n * @throws {Error} If the record is not found\n */\n async readRecord<T>(cacheRecordKey: string): Promise<TtlCacheRecord<T>> {\n const [value, ttl] = await this.client\n .multi()\n .get(cacheRecordKey)\n .ttl(cacheRecordKey)\n .exec();\n if (value === null) {\n throw new Error(`Record not found for key: ${cacheRecordKey}`);\n }\n\n return {\n key: cacheRecordKey,\n value: this.parseValue<T>(value as unknown as RedisCommandRawReply),\n ttlMilliseconds:\n this.parseValue<number>(ttl as unknown as RedisCommandRawReply) * 1000\n };\n }\n\n /**\n * Reads multiple records from the Redis cache.\n *\n * @template T - The type of values being read\n * @param {string[] | string} cacheRecordKeysOrPrefix - Array of keys to read, or a prefix pattern\n * @returns {Promise<TtlCacheRecord<T>[]>} A promise that resolves with an array of cache records\n */\n async readBatchRecords<T>(\n cacheRecordKeysOrPrefix: string[] | string\n ): Promise<TtlCacheRecord<T>[]> {\n const keys = Array.isArray(cacheRecordKeysOrPrefix)\n ? cacheRecordKeysOrPrefix\n : await this.client.keys(cacheRecordKeysOrPrefix + '*');\n const multiCommand = this.client.multi();\n for (const key of keys) {\n multiCommand.get(key);\n multiCommand.ttl(key);\n }\n const values = await multiCommand.exec();\n return values.reduce<TtlCacheRecord<T>[]>((acc, value, index) => {\n if (index % 2 === 0) {\n const maybeValue = this.parseValue<T>(\n value as unknown as RedisCommandRawReply\n );\n const ttl = this.parseValue<number>(\n values[index + 1] as unknown as RedisCommandRawReply\n );\n if (maybeValue && ttl) {\n acc.push({\n key: keys[index / 2],\n value: maybeValue,\n ttlMilliseconds: ttl * 1000\n });\n }\n }\n return acc;\n }, []);\n }\n\n /**\n * Lists all keys in the Redis cache that match a pattern prefix.\n *\n * @param {string} pattern_prefix - The prefix pattern to match keys against\n * @returns {Promise<string[]>} A promise that resolves with an array of matching keys\n */\n async listKeys(pattern_prefix: string): Promise<string[]> {\n const keys = await this.client.keys(pattern_prefix + '*');\n return keys;\n }\n\n /**\n * Checks if a record exists in the Redis cache.\n *\n * @param {string} cacheRecordKey - The key to check\n * @returns {Promise<boolean>} A promise that resolves with true if the record exists, false otherwise\n */\n async peekRecord(cacheRecordKey: string): Promise<boolean> {\n const result = await this.client.exists(cacheRecordKey);\n return result === 1;\n }\n\n /**\n * Checks if multiple records exist in the Redis cache.\n *\n * @param {string[] | string} cacheRecordKeysOrPrefix - Array of keys to check, or a prefix pattern\n * @returns {Promise<boolean[]>} A promise that resolves with an array of existence booleans\n */\n async peekBatchRecords(\n cacheRecordKeysOrPrefix: string[] | string\n ): Promise<boolean[]> {\n const keys = Array.isArray(cacheRecordKeysOrPrefix)\n ? cacheRecordKeysOrPrefix\n : await this.client.keys(cacheRecordKeysOrPrefix + '*');\n const multiCommand = this.client.multi();\n for (const key of keys) {\n multiCommand.exists(key);\n }\n const results = await multiCommand.exec();\n return results.map((result) => (result as unknown as number) === 1);\n }\n\n /**\n * Peeks at a record in the Redis cache.\n *\n * @template T - The type of value being peeked at\n * @param {string} queueName - The name of the Redis queue\n * @returns {Promise<T>} A promise that resolves with the peeked value\n */\n async peekQueueRecord<T>(queueName: string): Promise<T> {\n const value = await this.client.lRange(queueName, 0, 0);\n return this.parseValue<T>(value[0]);\n }\n\n /**\n * Peeks at multiple records in the Redis cache.\n *\n * @template T - The type of values being peeked at\n * @param {string} queueName - The name of the Redis queue\n * @param {number} pageSize - The number of records to peek at\n * @returns {Promise<T[]>} A promise that resolves with an array of peeked values\n */\n async peekQueueRecords<T>(queueName: string, pageSize: number): Promise<T[]> {\n const values = await this.client.lRange(queueName, 0, pageSize - 1);\n return values.map((value) => this.parseValue<T>(value)).filter(Boolean);\n }\n\n /**\n * Gracefully disconnects from the Redis server.\n *\n * @returns {Promise<void>} A promise that resolves when the connection is closed\n */\n async disconnect(): Promise<void> {\n await this.client.quit();\n }\n\n /**\n * Gets the default Time-To-Live value in milliseconds.\n *\n * @returns {number} The default TTL in milliseconds\n */\n getTtlMilliseconds(): number {\n return this.ttlMilliseconds;\n }\n\n /**\n * Gets the underlying Redis client instance.\n *\n * @returns {typeof this.client} The Redis client instance\n */\n getClient(): typeof this.client {\n return this.client;\n }\n}\n"],"mappings":"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","names":[]}