@graphql-mesh/cache-redis 0.104.14-alpha-20250922151901-2100ea6ff399fc0053cc422e3cc6ab17fe79be1c → 0.105.0-alpha-20251010093057-4eeae9412ec39cfbab6e001ecaf527cf94f4ea31

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/cjs/index.js CHANGED
@@ -6,128 +6,150 @@ const ioredis_mock_1 = tslib_1.__importDefault(require("ioredis-mock"));
6
6
  const cross_helpers_1 = require("@graphql-mesh/cross-helpers");
7
7
  const string_interpolation_1 = require("@graphql-mesh/string-interpolation");
8
8
  const types_1 = require("@graphql-mesh/types");
9
+ const api_1 = require("@opentelemetry/api");
9
10
  const disposablestack_1 = require("@whatwg-node/disposablestack");
10
11
  function interpolateStrWithEnv(str) {
11
12
  return string_interpolation_1.stringInterpolator.parse(str, { env: cross_helpers_1.process.env });
12
13
  }
13
14
  class RedisCache {
14
15
  constructor(options) {
15
- const lazyConnect = options.lazyConnect !== false;
16
- if ('startupNodes' in options) {
17
- const parsedUsername = interpolateStrWithEnv(options.username?.toString()) || cross_helpers_1.process.env.REDIS_USERNAME;
18
- const parsedPassword = interpolateStrWithEnv(options.password?.toString()) || cross_helpers_1.process.env.REDIS_PASSWORD;
19
- const parsedDb = interpolateStrWithEnv(options.db?.toString()) || cross_helpers_1.process.env.REDIS_DB;
20
- const numDb = parseInt(parsedDb);
21
- this.client = new ioredis_1.default.Cluster(options.startupNodes.map(s => ({
22
- host: s.host && interpolateStrWithEnv(s.host),
23
- port: s.port && parseInt(interpolateStrWithEnv(s.port)),
24
- family: s.family && parseInt(interpolateStrWithEnv(s.family)),
25
- })), {
26
- dnsLookup: options.dnsLookupAsIs
27
- ? (address, callback) => callback(null, address)
28
- : undefined,
29
- redisOptions: {
30
- username: parsedUsername,
31
- password: parsedPassword,
32
- db: isNaN(numDb) ? undefined : numDb,
33
- enableAutoPipelining: true,
34
- ...(lazyConnect ? { lazyConnect: true } : {}),
35
- tls: options.tls ? {} : undefined,
36
- },
37
- enableAutoPipelining: true,
38
- enableOfflineQueue: true,
39
- ...(lazyConnect ? { lazyConnect: true } : {}),
40
- });
41
- }
42
- else if ('sentinels' in options) {
43
- this.client = new ioredis_1.default({
44
- name: options.name,
45
- sentinelPassword: options.sentinelPassword && interpolateStrWithEnv(options.sentinelPassword),
46
- sentinels: options.sentinels.map(s => ({
47
- host: s.host && interpolateStrWithEnv(s.host),
48
- port: s.port && parseInt(interpolateStrWithEnv(s.port)),
49
- family: s.family && parseInt(interpolateStrWithEnv(s.family)),
50
- })),
51
- role: options.role,
52
- enableTLSForSentinelMode: options.enableTLSForSentinelMode,
53
- enableAutoPipelining: true,
54
- enableOfflineQueue: true,
55
- lazyConnect,
56
- });
57
- }
58
- else if (options.url) {
59
- const redisUrl = new URL(interpolateStrWithEnv(options.url));
60
- if (!['redis:', 'rediss:'].includes(redisUrl.protocol)) {
61
- throw new Error('Redis URL must use either redis:// or rediss://');
62
- }
63
- if (lazyConnect) {
64
- redisUrl.searchParams.set('lazyConnect', 'true');
65
- }
66
- redisUrl.searchParams.set('enableAutoPipelining', 'true');
67
- redisUrl.searchParams.set('enableOfflineQueue', 'true');
68
- const IPV6_REGEX = /^(?:(?:[a-fA-F\d]{1,4}:){7}(?:[a-fA-F\d]{1,4}|:)|(?:[a-fA-F\d]{1,4}:){6}(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)(?:\\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)){3}|:[a-fA-F\d]{1,4}|:)|(?:[a-fA-F\d]{1,4}:){5}(?::(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)(?:\\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)){3}|(?::[a-fA-F\d]{1,4}){1,2}|:)|(?:[a-fA-F\d]{1,4}:){4}(?:(?::[a-fA-F\d]{1,4}){0,1}:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)(?:\\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)){3}|(?::[a-fA-F\d]{1,4}){1,3}|:)|(?:[a-fA-F\d]{1,4}:){3}(?:(?::[a-fA-F\d]{1,4}){0,2}:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)(?:\\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)){3}|(?::[a-fA-F\d]{1,4}){1,4}|:)|(?:[a-fA-F\d]{1,4}:){2}(?:(?::[a-fA-F\d]{1,4}){0,3}:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)(?:\\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)){3}|(?::[a-fA-F\d]{1,4}){1,5}|:)|(?:[a-fA-F\d]{1,4}:){1}(?:(?::[a-fA-F\d]{1,4}){0,4}:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)(?:\\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)){3}|(?::[a-fA-F\d]{1,4}){1,6}|:)|(?::(?:(?::[a-fA-F\d]{1,4}){0,5}:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)(?:\\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)){3}|(?::[a-fA-F\d]{1,4}){1,7}|:)))(?:%[0-9a-zA-Z]{1,})?$/gm;
69
- if (IPV6_REGEX.test(redisUrl.hostname)) {
70
- redisUrl.searchParams.set('family', '6');
71
- }
72
- const urlStr = redisUrl.toString();
73
- options.logger.debug(`Connecting to Redis at ${urlStr}`);
74
- this.client = new ioredis_1.default(urlStr);
75
- }
76
- else {
77
- const parsedHost = interpolateStrWithEnv(options.host?.toString()) || cross_helpers_1.process.env.REDIS_HOST;
78
- const parsedPort = interpolateStrWithEnv(options.port?.toString()) || cross_helpers_1.process.env.REDIS_PORT;
79
- const parsedUsername = interpolateStrWithEnv(options.username?.toString()) || cross_helpers_1.process.env.REDIS_USERNAME;
80
- const parsedPassword = interpolateStrWithEnv(options.password?.toString()) || cross_helpers_1.process.env.REDIS_PASSWORD;
81
- const parsedDb = interpolateStrWithEnv(options.db?.toString()) || cross_helpers_1.process.env.REDIS_DB;
82
- const parsedFamily = interpolateStrWithEnv(options.family?.toString()) || cross_helpers_1.process.env.REDIS_FAMILY;
83
- const numPort = parseInt(parsedPort);
84
- const numDb = parseInt(parsedDb);
85
- if (parsedHost) {
86
- options.logger.debug(`Connecting to Redis at ${parsedHost}:${parsedPort}`);
87
- this.client = new ioredis_1.default({
88
- host: parsedHost,
89
- port: isNaN(numPort) ? undefined : numPort,
90
- username: parsedUsername,
91
- password: parsedPassword,
92
- db: isNaN(numDb) ? undefined : numDb,
93
- family: parsedFamily === '6' ? 6 : undefined,
94
- ...(lazyConnect ? { lazyConnect: true } : {}),
95
- enableAutoPipelining: true,
96
- enableOfflineQueue: true,
16
+ this.tracer = api_1.trace.getTracer('hive.cache.redis');
17
+ this.tracer.startActiveSpan('hive.cache.redis.init', span => {
18
+ try {
19
+ const lazyConnect = options.lazyConnect !== false;
20
+ if ('startupNodes' in options) {
21
+ const parsedUsername = interpolateStrWithEnv(options.username?.toString()) || cross_helpers_1.process.env.REDIS_USERNAME;
22
+ const parsedPassword = interpolateStrWithEnv(options.password?.toString()) || cross_helpers_1.process.env.REDIS_PASSWORD;
23
+ const parsedDb = interpolateStrWithEnv(options.db?.toString()) || cross_helpers_1.process.env.REDIS_DB;
24
+ const numDb = parseInt(parsedDb);
25
+ this.client = new ioredis_1.default.Cluster(options.startupNodes.map(s => ({
26
+ host: s.host && interpolateStrWithEnv(s.host),
27
+ port: s.port && parseInt(interpolateStrWithEnv(s.port)),
28
+ family: s.family && parseInt(interpolateStrWithEnv(s.family)),
29
+ })), {
30
+ dnsLookup: options.dnsLookupAsIs
31
+ ? (address, callback) => callback(null, address)
32
+ : undefined,
33
+ redisOptions: {
34
+ username: parsedUsername,
35
+ password: parsedPassword,
36
+ db: isNaN(numDb) ? undefined : numDb,
37
+ enableAutoPipelining: true,
38
+ ...(lazyConnect ? { lazyConnect: true } : {}),
39
+ tls: options.tls ? {} : undefined,
40
+ },
41
+ enableAutoPipelining: true,
42
+ enableOfflineQueue: true,
43
+ ...(lazyConnect ? { lazyConnect: true } : {}),
44
+ });
45
+ }
46
+ else if ('sentinels' in options) {
47
+ this.client = new ioredis_1.default({
48
+ name: options.name,
49
+ sentinelPassword: options.sentinelPassword && interpolateStrWithEnv(options.sentinelPassword),
50
+ sentinels: options.sentinels.map(s => ({
51
+ host: s.host && interpolateStrWithEnv(s.host),
52
+ port: s.port && parseInt(interpolateStrWithEnv(s.port)),
53
+ family: s.family && parseInt(interpolateStrWithEnv(s.family)),
54
+ })),
55
+ role: options.role,
56
+ enableTLSForSentinelMode: options.enableTLSForSentinelMode,
57
+ enableAutoPipelining: true,
58
+ enableOfflineQueue: true,
59
+ lazyConnect,
60
+ });
61
+ }
62
+ else if (options.url) {
63
+ const redisUrl = new URL(interpolateStrWithEnv(options.url));
64
+ if (!['redis:', 'rediss:'].includes(redisUrl.protocol)) {
65
+ throw new Error('Redis URL must use either redis:// or rediss://');
66
+ }
67
+ if (lazyConnect) {
68
+ redisUrl.searchParams.set('lazyConnect', 'true');
69
+ }
70
+ redisUrl.searchParams.set('enableAutoPipelining', 'true');
71
+ redisUrl.searchParams.set('enableOfflineQueue', 'true');
72
+ const IPV6_REGEX = /^(?:(?:[a-fA-F\d]{1,4}:){7}(?:[a-fA-F\d]{1,4}|:)|(?:[a-fA-F\d]{1,4}:){6}(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)(?:\\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)){3}|:[a-fA-F\d]{1,4}|:)|(?:[a-fA-F\d]{1,4}:){5}(?::(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)(?:\\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)){3}|(?::[a-fA-F\d]{1,4}){1,2}|:)|(?:[a-fA-F\d]{1,4}:){4}(?:(?::[a-fA-F\d]{1,4}){0,1}:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)(?:\\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)){3}|(?::[a-fA-F\d]{1,4}){1,3}|:)|(?:[a-fA-F\d]{1,4}:){3}(?:(?::[a-fA-F\d]{1,4}){0,2}:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)(?:\\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)){3}|(?::[a-fA-F\d]{1,4}){1,4}|:)|(?:[a-fA-F\d]{1,4}:){2}(?:(?::[a-fA-F\d]{1,4}){0,3}:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)(?:\\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)){3}|(?::[a-fA-F\d]{1,4}){1,5}|:)|(?:[a-fA-F\d]{1,4}:){1}(?:(?::[a-fA-F\d]{1,4}){0,4}:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)(?:\\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)){3}|(?::[a-fA-F\d]{1,4}){1,6}|:)|(?::(?:(?::[a-fA-F\d]{1,4}){0,5}:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)(?:\\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)){3}|(?::[a-fA-F\d]{1,4}){1,7}|:)))(?:%[0-9a-zA-Z]{1,})?$/gm;
73
+ if (IPV6_REGEX.test(redisUrl.hostname)) {
74
+ redisUrl.searchParams.set('family', '6');
75
+ }
76
+ const urlStr = redisUrl.toString();
77
+ options.logger.debug(`Connecting to Redis at ${urlStr}`);
78
+ this.client = new ioredis_1.default(urlStr);
79
+ }
80
+ else {
81
+ const parsedHost = interpolateStrWithEnv(options.host?.toString()) || cross_helpers_1.process.env.REDIS_HOST;
82
+ const parsedPort = interpolateStrWithEnv(options.port?.toString()) || cross_helpers_1.process.env.REDIS_PORT;
83
+ const parsedUsername = interpolateStrWithEnv(options.username?.toString()) || cross_helpers_1.process.env.REDIS_USERNAME;
84
+ const parsedPassword = interpolateStrWithEnv(options.password?.toString()) || cross_helpers_1.process.env.REDIS_PASSWORD;
85
+ const parsedDb = interpolateStrWithEnv(options.db?.toString()) || cross_helpers_1.process.env.REDIS_DB;
86
+ const parsedFamily = interpolateStrWithEnv(options.family?.toString()) || cross_helpers_1.process.env.REDIS_FAMILY;
87
+ const numPort = parseInt(parsedPort);
88
+ const numDb = parseInt(parsedDb);
89
+ if (parsedHost) {
90
+ options.logger.debug(`Connecting to Redis at ${parsedHost}:${parsedPort}`);
91
+ this.client = new ioredis_1.default({
92
+ host: parsedHost,
93
+ port: isNaN(numPort) ? undefined : numPort,
94
+ username: parsedUsername,
95
+ password: parsedPassword,
96
+ db: isNaN(numDb) ? undefined : numDb,
97
+ family: parsedFamily === '6' ? 6 : undefined,
98
+ ...(lazyConnect ? { lazyConnect: true } : {}),
99
+ enableAutoPipelining: true,
100
+ enableOfflineQueue: true,
101
+ });
102
+ }
103
+ else {
104
+ options.logger.debug(`Connecting to Redis mock`);
105
+ this.client = new ioredis_mock_1.default();
106
+ }
107
+ }
108
+ const pubsub = (0, types_1.toMeshPubSub)(options.pubsub);
109
+ // TODO: PubSub.destroy will no longer be needed after v0
110
+ const id = pubsub?.subscribe('destroy', () => {
111
+ this.client.disconnect(false);
112
+ pubsub.unsubscribe(id);
97
113
  });
98
114
  }
99
- else {
100
- options.logger.debug(`Connecting to Redis mock`);
101
- this.client = new ioredis_mock_1.default();
115
+ finally {
116
+ span.end();
102
117
  }
103
- }
104
- const pubsub = (0, types_1.toMeshPubSub)(options.pubsub);
105
- // TODO: PubSub.destroy will no longer be needed after v0
106
- const id = pubsub?.subscribe('destroy', () => {
107
- this.client.disconnect(false);
108
- pubsub.unsubscribe(id);
109
118
  });
110
119
  }
111
120
  [disposablestack_1.DisposableSymbols.dispose]() {
112
121
  this.client.disconnect(false);
113
122
  }
114
123
  set(key, value, options) {
115
- const stringifiedValue = JSON.stringify(value);
116
- if (options?.ttl && options.ttl > 0) {
117
- return this.client.set(key, stringifiedValue, 'PX', options.ttl * 1000);
118
- }
119
- else {
120
- return this.client.set(key, stringifiedValue);
121
- }
124
+ return this.tracer.startActiveSpan('hive.cache.set', async (span) => {
125
+ try {
126
+ const stringifiedValue = JSON.stringify(value);
127
+ if (options?.ttl && options.ttl > 0) {
128
+ return await this.client.set(key, stringifiedValue, 'PX', options.ttl * 1000);
129
+ }
130
+ else {
131
+ return await this.client.set(key, stringifiedValue);
132
+ }
133
+ }
134
+ finally {
135
+ span.end();
136
+ }
137
+ });
122
138
  }
123
139
  get(key) {
124
- return this.client.get(key).then(value => (value != null ? JSON.parse(value) : undefined));
140
+ return this.tracer.startActiveSpan('hive.cache.get', span => this.client
141
+ .get(key)
142
+ .then(value => (value != null ? JSON.parse(value) : undefined))
143
+ .finally(() => span.end()));
125
144
  }
126
145
  getKeysByPrefix(prefix) {
127
146
  return scanPatterns(this.client, `${prefix}*`);
128
147
  }
129
148
  delete(key) {
130
- return this.client.del(key).then(value => value > 0, () => false);
149
+ return this.tracer.startActiveSpan('hive.cache.delete', span => this.client
150
+ .del(key)
151
+ .then(value => value > 0, () => false)
152
+ .finally(() => span.end()));
131
153
  }
132
154
  }
133
155
  exports.default = RedisCache;
package/esm/index.js CHANGED
@@ -3,128 +3,150 @@ import RedisMock from 'ioredis-mock';
3
3
  import { process } from '@graphql-mesh/cross-helpers';
4
4
  import { stringInterpolator } from '@graphql-mesh/string-interpolation';
5
5
  import { toMeshPubSub, } from '@graphql-mesh/types';
6
+ import { trace } from '@opentelemetry/api';
6
7
  import { DisposableSymbols } from '@whatwg-node/disposablestack';
7
8
  function interpolateStrWithEnv(str) {
8
9
  return stringInterpolator.parse(str, { env: process.env });
9
10
  }
10
11
  export default class RedisCache {
11
12
  constructor(options) {
12
- const lazyConnect = options.lazyConnect !== false;
13
- if ('startupNodes' in options) {
14
- const parsedUsername = interpolateStrWithEnv(options.username?.toString()) || process.env.REDIS_USERNAME;
15
- const parsedPassword = interpolateStrWithEnv(options.password?.toString()) || process.env.REDIS_PASSWORD;
16
- const parsedDb = interpolateStrWithEnv(options.db?.toString()) || process.env.REDIS_DB;
17
- const numDb = parseInt(parsedDb);
18
- this.client = new Redis.Cluster(options.startupNodes.map(s => ({
19
- host: s.host && interpolateStrWithEnv(s.host),
20
- port: s.port && parseInt(interpolateStrWithEnv(s.port)),
21
- family: s.family && parseInt(interpolateStrWithEnv(s.family)),
22
- })), {
23
- dnsLookup: options.dnsLookupAsIs
24
- ? (address, callback) => callback(null, address)
25
- : undefined,
26
- redisOptions: {
27
- username: parsedUsername,
28
- password: parsedPassword,
29
- db: isNaN(numDb) ? undefined : numDb,
30
- enableAutoPipelining: true,
31
- ...(lazyConnect ? { lazyConnect: true } : {}),
32
- tls: options.tls ? {} : undefined,
33
- },
34
- enableAutoPipelining: true,
35
- enableOfflineQueue: true,
36
- ...(lazyConnect ? { lazyConnect: true } : {}),
37
- });
38
- }
39
- else if ('sentinels' in options) {
40
- this.client = new Redis({
41
- name: options.name,
42
- sentinelPassword: options.sentinelPassword && interpolateStrWithEnv(options.sentinelPassword),
43
- sentinels: options.sentinels.map(s => ({
44
- host: s.host && interpolateStrWithEnv(s.host),
45
- port: s.port && parseInt(interpolateStrWithEnv(s.port)),
46
- family: s.family && parseInt(interpolateStrWithEnv(s.family)),
47
- })),
48
- role: options.role,
49
- enableTLSForSentinelMode: options.enableTLSForSentinelMode,
50
- enableAutoPipelining: true,
51
- enableOfflineQueue: true,
52
- lazyConnect,
53
- });
54
- }
55
- else if (options.url) {
56
- const redisUrl = new URL(interpolateStrWithEnv(options.url));
57
- if (!['redis:', 'rediss:'].includes(redisUrl.protocol)) {
58
- throw new Error('Redis URL must use either redis:// or rediss://');
59
- }
60
- if (lazyConnect) {
61
- redisUrl.searchParams.set('lazyConnect', 'true');
62
- }
63
- redisUrl.searchParams.set('enableAutoPipelining', 'true');
64
- redisUrl.searchParams.set('enableOfflineQueue', 'true');
65
- const IPV6_REGEX = /^(?:(?:[a-fA-F\d]{1,4}:){7}(?:[a-fA-F\d]{1,4}|:)|(?:[a-fA-F\d]{1,4}:){6}(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)(?:\\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)){3}|:[a-fA-F\d]{1,4}|:)|(?:[a-fA-F\d]{1,4}:){5}(?::(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)(?:\\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)){3}|(?::[a-fA-F\d]{1,4}){1,2}|:)|(?:[a-fA-F\d]{1,4}:){4}(?:(?::[a-fA-F\d]{1,4}){0,1}:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)(?:\\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)){3}|(?::[a-fA-F\d]{1,4}){1,3}|:)|(?:[a-fA-F\d]{1,4}:){3}(?:(?::[a-fA-F\d]{1,4}){0,2}:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)(?:\\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)){3}|(?::[a-fA-F\d]{1,4}){1,4}|:)|(?:[a-fA-F\d]{1,4}:){2}(?:(?::[a-fA-F\d]{1,4}){0,3}:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)(?:\\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)){3}|(?::[a-fA-F\d]{1,4}){1,5}|:)|(?:[a-fA-F\d]{1,4}:){1}(?:(?::[a-fA-F\d]{1,4}){0,4}:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)(?:\\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)){3}|(?::[a-fA-F\d]{1,4}){1,6}|:)|(?::(?:(?::[a-fA-F\d]{1,4}){0,5}:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)(?:\\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)){3}|(?::[a-fA-F\d]{1,4}){1,7}|:)))(?:%[0-9a-zA-Z]{1,})?$/gm;
66
- if (IPV6_REGEX.test(redisUrl.hostname)) {
67
- redisUrl.searchParams.set('family', '6');
68
- }
69
- const urlStr = redisUrl.toString();
70
- options.logger.debug(`Connecting to Redis at ${urlStr}`);
71
- this.client = new Redis(urlStr);
72
- }
73
- else {
74
- const parsedHost = interpolateStrWithEnv(options.host?.toString()) || process.env.REDIS_HOST;
75
- const parsedPort = interpolateStrWithEnv(options.port?.toString()) || process.env.REDIS_PORT;
76
- const parsedUsername = interpolateStrWithEnv(options.username?.toString()) || process.env.REDIS_USERNAME;
77
- const parsedPassword = interpolateStrWithEnv(options.password?.toString()) || process.env.REDIS_PASSWORD;
78
- const parsedDb = interpolateStrWithEnv(options.db?.toString()) || process.env.REDIS_DB;
79
- const parsedFamily = interpolateStrWithEnv(options.family?.toString()) || process.env.REDIS_FAMILY;
80
- const numPort = parseInt(parsedPort);
81
- const numDb = parseInt(parsedDb);
82
- if (parsedHost) {
83
- options.logger.debug(`Connecting to Redis at ${parsedHost}:${parsedPort}`);
84
- this.client = new Redis({
85
- host: parsedHost,
86
- port: isNaN(numPort) ? undefined : numPort,
87
- username: parsedUsername,
88
- password: parsedPassword,
89
- db: isNaN(numDb) ? undefined : numDb,
90
- family: parsedFamily === '6' ? 6 : undefined,
91
- ...(lazyConnect ? { lazyConnect: true } : {}),
92
- enableAutoPipelining: true,
93
- enableOfflineQueue: true,
13
+ this.tracer = trace.getTracer('hive.cache.redis');
14
+ this.tracer.startActiveSpan('hive.cache.redis.init', span => {
15
+ try {
16
+ const lazyConnect = options.lazyConnect !== false;
17
+ if ('startupNodes' in options) {
18
+ const parsedUsername = interpolateStrWithEnv(options.username?.toString()) || process.env.REDIS_USERNAME;
19
+ const parsedPassword = interpolateStrWithEnv(options.password?.toString()) || process.env.REDIS_PASSWORD;
20
+ const parsedDb = interpolateStrWithEnv(options.db?.toString()) || process.env.REDIS_DB;
21
+ const numDb = parseInt(parsedDb);
22
+ this.client = new Redis.Cluster(options.startupNodes.map(s => ({
23
+ host: s.host && interpolateStrWithEnv(s.host),
24
+ port: s.port && parseInt(interpolateStrWithEnv(s.port)),
25
+ family: s.family && parseInt(interpolateStrWithEnv(s.family)),
26
+ })), {
27
+ dnsLookup: options.dnsLookupAsIs
28
+ ? (address, callback) => callback(null, address)
29
+ : undefined,
30
+ redisOptions: {
31
+ username: parsedUsername,
32
+ password: parsedPassword,
33
+ db: isNaN(numDb) ? undefined : numDb,
34
+ enableAutoPipelining: true,
35
+ ...(lazyConnect ? { lazyConnect: true } : {}),
36
+ tls: options.tls ? {} : undefined,
37
+ },
38
+ enableAutoPipelining: true,
39
+ enableOfflineQueue: true,
40
+ ...(lazyConnect ? { lazyConnect: true } : {}),
41
+ });
42
+ }
43
+ else if ('sentinels' in options) {
44
+ this.client = new Redis({
45
+ name: options.name,
46
+ sentinelPassword: options.sentinelPassword && interpolateStrWithEnv(options.sentinelPassword),
47
+ sentinels: options.sentinels.map(s => ({
48
+ host: s.host && interpolateStrWithEnv(s.host),
49
+ port: s.port && parseInt(interpolateStrWithEnv(s.port)),
50
+ family: s.family && parseInt(interpolateStrWithEnv(s.family)),
51
+ })),
52
+ role: options.role,
53
+ enableTLSForSentinelMode: options.enableTLSForSentinelMode,
54
+ enableAutoPipelining: true,
55
+ enableOfflineQueue: true,
56
+ lazyConnect,
57
+ });
58
+ }
59
+ else if (options.url) {
60
+ const redisUrl = new URL(interpolateStrWithEnv(options.url));
61
+ if (!['redis:', 'rediss:'].includes(redisUrl.protocol)) {
62
+ throw new Error('Redis URL must use either redis:// or rediss://');
63
+ }
64
+ if (lazyConnect) {
65
+ redisUrl.searchParams.set('lazyConnect', 'true');
66
+ }
67
+ redisUrl.searchParams.set('enableAutoPipelining', 'true');
68
+ redisUrl.searchParams.set('enableOfflineQueue', 'true');
69
+ const IPV6_REGEX = /^(?:(?:[a-fA-F\d]{1,4}:){7}(?:[a-fA-F\d]{1,4}|:)|(?:[a-fA-F\d]{1,4}:){6}(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)(?:\\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)){3}|:[a-fA-F\d]{1,4}|:)|(?:[a-fA-F\d]{1,4}:){5}(?::(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)(?:\\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)){3}|(?::[a-fA-F\d]{1,4}){1,2}|:)|(?:[a-fA-F\d]{1,4}:){4}(?:(?::[a-fA-F\d]{1,4}){0,1}:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)(?:\\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)){3}|(?::[a-fA-F\d]{1,4}){1,3}|:)|(?:[a-fA-F\d]{1,4}:){3}(?:(?::[a-fA-F\d]{1,4}){0,2}:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)(?:\\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)){3}|(?::[a-fA-F\d]{1,4}){1,4}|:)|(?:[a-fA-F\d]{1,4}:){2}(?:(?::[a-fA-F\d]{1,4}){0,3}:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)(?:\\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)){3}|(?::[a-fA-F\d]{1,4}){1,5}|:)|(?:[a-fA-F\d]{1,4}:){1}(?:(?::[a-fA-F\d]{1,4}){0,4}:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)(?:\\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)){3}|(?::[a-fA-F\d]{1,4}){1,6}|:)|(?::(?:(?::[a-fA-F\d]{1,4}){0,5}:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)(?:\\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)){3}|(?::[a-fA-F\d]{1,4}){1,7}|:)))(?:%[0-9a-zA-Z]{1,})?$/gm;
70
+ if (IPV6_REGEX.test(redisUrl.hostname)) {
71
+ redisUrl.searchParams.set('family', '6');
72
+ }
73
+ const urlStr = redisUrl.toString();
74
+ options.logger.debug(`Connecting to Redis at ${urlStr}`);
75
+ this.client = new Redis(urlStr);
76
+ }
77
+ else {
78
+ const parsedHost = interpolateStrWithEnv(options.host?.toString()) || process.env.REDIS_HOST;
79
+ const parsedPort = interpolateStrWithEnv(options.port?.toString()) || process.env.REDIS_PORT;
80
+ const parsedUsername = interpolateStrWithEnv(options.username?.toString()) || process.env.REDIS_USERNAME;
81
+ const parsedPassword = interpolateStrWithEnv(options.password?.toString()) || process.env.REDIS_PASSWORD;
82
+ const parsedDb = interpolateStrWithEnv(options.db?.toString()) || process.env.REDIS_DB;
83
+ const parsedFamily = interpolateStrWithEnv(options.family?.toString()) || process.env.REDIS_FAMILY;
84
+ const numPort = parseInt(parsedPort);
85
+ const numDb = parseInt(parsedDb);
86
+ if (parsedHost) {
87
+ options.logger.debug(`Connecting to Redis at ${parsedHost}:${parsedPort}`);
88
+ this.client = new Redis({
89
+ host: parsedHost,
90
+ port: isNaN(numPort) ? undefined : numPort,
91
+ username: parsedUsername,
92
+ password: parsedPassword,
93
+ db: isNaN(numDb) ? undefined : numDb,
94
+ family: parsedFamily === '6' ? 6 : undefined,
95
+ ...(lazyConnect ? { lazyConnect: true } : {}),
96
+ enableAutoPipelining: true,
97
+ enableOfflineQueue: true,
98
+ });
99
+ }
100
+ else {
101
+ options.logger.debug(`Connecting to Redis mock`);
102
+ this.client = new RedisMock();
103
+ }
104
+ }
105
+ const pubsub = toMeshPubSub(options.pubsub);
106
+ // TODO: PubSub.destroy will no longer be needed after v0
107
+ const id = pubsub?.subscribe('destroy', () => {
108
+ this.client.disconnect(false);
109
+ pubsub.unsubscribe(id);
94
110
  });
95
111
  }
96
- else {
97
- options.logger.debug(`Connecting to Redis mock`);
98
- this.client = new RedisMock();
112
+ finally {
113
+ span.end();
99
114
  }
100
- }
101
- const pubsub = toMeshPubSub(options.pubsub);
102
- // TODO: PubSub.destroy will no longer be needed after v0
103
- const id = pubsub?.subscribe('destroy', () => {
104
- this.client.disconnect(false);
105
- pubsub.unsubscribe(id);
106
115
  });
107
116
  }
108
117
  [DisposableSymbols.dispose]() {
109
118
  this.client.disconnect(false);
110
119
  }
111
120
  set(key, value, options) {
112
- const stringifiedValue = JSON.stringify(value);
113
- if (options?.ttl && options.ttl > 0) {
114
- return this.client.set(key, stringifiedValue, 'PX', options.ttl * 1000);
115
- }
116
- else {
117
- return this.client.set(key, stringifiedValue);
118
- }
121
+ return this.tracer.startActiveSpan('hive.cache.set', async (span) => {
122
+ try {
123
+ const stringifiedValue = JSON.stringify(value);
124
+ if (options?.ttl && options.ttl > 0) {
125
+ return await this.client.set(key, stringifiedValue, 'PX', options.ttl * 1000);
126
+ }
127
+ else {
128
+ return await this.client.set(key, stringifiedValue);
129
+ }
130
+ }
131
+ finally {
132
+ span.end();
133
+ }
134
+ });
119
135
  }
120
136
  get(key) {
121
- return this.client.get(key).then(value => (value != null ? JSON.parse(value) : undefined));
137
+ return this.tracer.startActiveSpan('hive.cache.get', span => this.client
138
+ .get(key)
139
+ .then(value => (value != null ? JSON.parse(value) : undefined))
140
+ .finally(() => span.end()));
122
141
  }
123
142
  getKeysByPrefix(prefix) {
124
143
  return scanPatterns(this.client, `${prefix}*`);
125
144
  }
126
145
  delete(key) {
127
- return this.client.del(key).then(value => value > 0, () => false);
146
+ return this.tracer.startActiveSpan('hive.cache.delete', span => this.client
147
+ .del(key)
148
+ .then(value => value > 0, () => false)
149
+ .finally(() => span.end()));
128
150
  }
129
151
  }
130
152
  function scanPatterns(redis, pattern, cursor = '0', keys = []) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@graphql-mesh/cache-redis",
3
- "version": "0.104.14-alpha-20250922151901-2100ea6ff399fc0053cc422e3cc6ab17fe79be1c",
3
+ "version": "0.105.0-alpha-20251010093057-4eeae9412ec39cfbab6e001ecaf527cf94f4ea31",
4
4
  "sideEffects": false,
5
5
  "peerDependencies": {
6
6
  "graphql": "*"
@@ -8,7 +8,8 @@
8
8
  "dependencies": {
9
9
  "@graphql-mesh/cross-helpers": "^0.4.10",
10
10
  "@graphql-mesh/string-interpolation": "0.5.9",
11
- "@graphql-mesh/types": "0.104.14-alpha-20250922151901-2100ea6ff399fc0053cc422e3cc6ab17fe79be1c",
11
+ "@graphql-mesh/types": "^0.104.13",
12
+ "@opentelemetry/api": "^1.9.0",
12
13
  "@whatwg-node/disposablestack": "^0.0.6",
13
14
  "ioredis": "^5.3.2",
14
15
  "ioredis-mock": "^8.8.3",
@@ -2,6 +2,7 @@ import { type HivePubSub, type KeyValueCache, type KeyValueCacheSetOptions, type
2
2
  import { DisposableSymbols } from '@whatwg-node/disposablestack';
3
3
  export default class RedisCache<V = string> implements KeyValueCache<V>, Disposable {
4
4
  private client;
5
+ private tracer;
5
6
  constructor(options: YamlConfig.Cache['redis'] & {
6
7
  pubsub?: MeshPubSub | HivePubSub;
7
8
  logger: Logger;
@@ -2,6 +2,7 @@ import { type HivePubSub, type KeyValueCache, type KeyValueCacheSetOptions, type
2
2
  import { DisposableSymbols } from '@whatwg-node/disposablestack';
3
3
  export default class RedisCache<V = string> implements KeyValueCache<V>, Disposable {
4
4
  private client;
5
+ private tracer;
5
6
  constructor(options: YamlConfig.Cache['redis'] & {
6
7
  pubsub?: MeshPubSub | HivePubSub;
7
8
  logger: Logger;