cds-caching 1.0.0 → 1.2.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.
@@ -15,9 +15,39 @@ class CapOperations {
15
15
  this.statistics = statistics;
16
16
  this.tagResolver = new TagResolver();
17
17
  this.runtimeConfigManager = runtimeConfigManager;
18
+ this.log = log || console;
18
19
  }
19
20
 
20
-
21
+ /**
22
+ * Safely execute cache operations with error handling
23
+ * @param {Function} operation - The cache operation to execute
24
+ * @param {string} operationName - Name of the operation for logging
25
+ * @param {object} context - Context information for logging
26
+ * @returns {Promise<object>} - The result with error information
27
+ */
28
+ async safeCacheOperation(operation, operationName, context = {}) {
29
+ try {
30
+ const result = await operation();
31
+ this.log.info('RESULT', { result, operationName, context });
32
+ return { success: true, result, error: null };
33
+ } catch (error) {
34
+ this.log.warn(`Cache ${operationName} failed:`, {
35
+ error: error.message,
36
+ stack: error.stack,
37
+ context: context
38
+ });
39
+
40
+ return {
41
+ success: false,
42
+ result: null,
43
+ error: {
44
+ message: error.message,
45
+ operation: operationName,
46
+ context: context
47
+ }
48
+ };
49
+ }
50
+ }
21
51
 
22
52
  /**
23
53
  * Send a request with caching with read-through capabilities.
@@ -34,7 +64,7 @@ class CapOperations {
34
64
  }
35
65
 
36
66
  if (['POST', 'PUT', 'DELETE', 'PATCH'].includes(request.method?.toUpperCase()) || !service.send) {
37
- return { result: null, cacheKey: null, metadata: { hit: false, latency: 0 } };
67
+ return { result: null, cacheKey: null, metadata: { hit: false, latency: 0 }, cacheErrors: [] };
38
68
  }
39
69
 
40
70
  const keyParts = {
@@ -79,31 +109,94 @@ class CapOperations {
79
109
  cacheOptions: JSON.stringify(requestOptions)
80
110
  };
81
111
 
82
- if (await this.cache.has(key)) {
112
+ // Safely check if key exists in cache
113
+ const hasKeyResult = await this.safeCacheOperation(
114
+ () => this.cache.has(key),
115
+ 'has',
116
+ { key, serviceName: service.name }
117
+ );
118
+
119
+ const hasKey = hasKeyResult.success && hasKeyResult.result;
120
+ const cacheErrors = [];
121
+
122
+ if (hasKey) {
83
123
  const latency = this.getElapsedMs(startTime);
84
124
 
85
- this.statistics.recordHit(latency, key, metadata);
125
+ // Safely record hit statistics
126
+ const hitStatsResult = await this.safeCacheOperation(
127
+ () => this.statistics.recordHit(latency, key, metadata),
128
+ 'recordHit',
129
+ { key, latency }
130
+ );
131
+ if (!hitStatsResult.success) {
132
+ cacheErrors.push(hitStatsResult.error);
133
+ }
134
+
135
+ // Safely get value from cache
136
+ const getResult = await this.safeCacheOperation(
137
+ () => this.cache.send("GET", { key }),
138
+ 'get',
139
+ { key }
140
+ );
141
+
142
+ if (getResult.success && getResult.result?.value !== undefined) {
143
+ return {
144
+ result: getResult.result.value,
145
+ cacheKey: key,
146
+ metadata: { hit: true, latency: latency },
147
+ cacheErrors: cacheErrors
148
+ };
149
+ }
150
+ }
86
151
 
87
- const wrappedValue = await this.cache.send("GET", { key });
88
- return { result: wrappedValue?.value, cacheKey: key, metadata: { hit: true, latency: latency } };
89
- } else {
152
+ // Cache miss or cache error - delegate to underlying service
153
+ try {
90
154
  const response = await service.send(request);
91
155
  const totalLatency = this.getElapsedMs(startTime);
92
- this.statistics.recordMiss(totalLatency, key, metadata);
156
+
157
+ // Safely record miss statistics
158
+ const missStatsResult = await this.safeCacheOperation(
159
+ () => this.statistics.recordMiss(totalLatency, key, metadata),
160
+ 'recordMiss',
161
+ { key, latency: totalLatency }
162
+ );
163
+ if (!missStatsResult.success) {
164
+ cacheErrors.push(missStatsResult.error);
165
+ }
93
166
 
167
+ // Safely store in cache
94
168
  const wrappedValue = {
95
169
  value: response,
96
170
  tags: this.tagResolver.resolveTags(requestOptions.tags, response, { ...request.params, user: request.user?.id, tenant: request.tenant, locale: request.locale, hash: this.keyManager.createContentHash(request) }),
97
171
  timestamp: Date.now()
98
172
  };
99
- await this.cache.send("SET", { key, value: wrappedValue, ttl: requestOptions.ttl || 0 });
173
+
174
+ const setResult = await this.safeCacheOperation(
175
+ () => this.cache.send("SET", { key, value: wrappedValue, ttl: requestOptions.ttl || 0 }),
176
+ 'set',
177
+ { key, ttl: requestOptions.ttl }
178
+ );
179
+ if (!setResult.success) {
180
+ cacheErrors.push(setResult.error);
181
+ }
100
182
 
101
- return { result: response, cacheKey: key, metadata: { hit: false, latency: totalLatency } };
183
+ return {
184
+ result: response,
185
+ cacheKey: key,
186
+ metadata: { hit: false, latency: totalLatency },
187
+ cacheErrors: cacheErrors
188
+ };
189
+ } catch (serviceError) {
190
+ // If the underlying service fails, throw the error
191
+ this.log.error('Service operation failed:', {
192
+ error: serviceError.message,
193
+ serviceName: service.name,
194
+ key: key
195
+ });
196
+ throw serviceError;
102
197
  }
103
198
  }
104
199
 
105
-
106
-
107
200
  /**
108
201
  * Run a cached operation with automatic key generation
109
202
  * @param {object} req - the request object
@@ -133,34 +226,87 @@ class CapOperations {
133
226
 
134
227
  // Track cache operation timing
135
228
  const startTime = process.hrtime();
136
- const wrappedValue = await this.cache.send("GET", { key: req.cacheKey });
137
- const cacheHit = wrappedValue?.value !== undefined;
229
+
230
+ // Safely get value from cache
231
+ const getResult = await this.safeCacheOperation(
232
+ () => this.cache.send("GET", { key: req.cacheKey }),
233
+ 'get',
234
+ { key: req.cacheKey, serviceName: req.target?.name }
235
+ );
236
+
237
+ const cacheHit = getResult.success && getResult.result?.value !== undefined;
138
238
  const cacheLatency = this.getElapsedMs(startTime);
139
239
  const metadata = this.extractMetadataFromRequest(req);
240
+ const cacheErrors = [];
140
241
 
141
242
  if (cacheHit) {
142
243
  // Cache hit
143
- this.statistics.recordHit(cacheLatency, req.cacheKey, metadata);
244
+ const hitStatsResult = await this.safeCacheOperation(
245
+ () => this.statistics.recordHit(cacheLatency, req.cacheKey, metadata),
246
+ 'recordHit',
247
+ { key: req.cacheKey, latency: cacheLatency }
248
+ );
249
+ if (!hitStatsResult.success) {
250
+ cacheErrors.push(hitStatsResult.error);
251
+ }
252
+
144
253
  req.res?.setHeader('x-sap-cap-cache', "hit");
145
-
146
- const wrappedValue = await this.cache.send("GET", { key: req.cacheKey });
147
- return { result: wrappedValue?.value, cacheKey: req.cacheKey, metadata: { hit: true, latency: cacheLatency } };
254
+ return {
255
+ result: getResult.result.value,
256
+ cacheKey: req.cacheKey,
257
+ metadata: { hit: true, latency: cacheLatency },
258
+ cacheErrors: cacheErrors
259
+ };
148
260
  } else {
149
261
  // Cache miss - track the backend operation
150
- const response = await next();
151
- const totalLatency = this.getElapsedMs(startTime);
152
-
153
- // Track the miss with total latency (cache lookup + backend operation)
154
- this.statistics.recordMiss(totalLatency, req.cacheKey, metadata);
155
- req.res?.setHeader('x-sap-cap-cache', "miss");
262
+ try {
263
+ const response = await next();
264
+ const totalLatency = this.getElapsedMs(startTime);
156
265
 
157
- const wrappedValue = {
158
- value: response,
159
- tags: this.tagResolver.resolveTags(req.cacheOptions.tags, response, { ...req.params, hash: this.keyManager.createContentHash(req) }),
160
- timestamp: Date.now()
161
- };
162
- await this.cache.send("SET", { key: req.cacheKey, value: wrappedValue, ttl: req.cacheOptions.ttl || 0 });
163
- return { result: response, cacheKey: req.cacheKey, metadata: { hit: false, latency: totalLatency } };
266
+ // Safely record miss statistics
267
+ const missStatsResult = await this.safeCacheOperation(
268
+ () => this.statistics.recordMiss(totalLatency, req.cacheKey, metadata),
269
+ 'recordMiss',
270
+ { key: req.cacheKey, latency: totalLatency }
271
+ );
272
+ if (!missStatsResult.success) {
273
+ cacheErrors.push(missStatsResult.error);
274
+ }
275
+
276
+ req.res?.setHeader('x-sap-cap-cache', "miss");
277
+
278
+ // Safely store in cache
279
+ const wrappedValue = {
280
+ value: response,
281
+ tags: this.tagResolver.resolveTags(req.cacheOptions.tags, response, { ...req.params, hash: this.keyManager.createContentHash(req) }),
282
+ timestamp: Date.now()
283
+ };
284
+
285
+ const setResult = await this.safeCacheOperation(
286
+ () => this.cache.send("SET", { key: req.cacheKey, value: wrappedValue, ttl: req.cacheOptions.ttl || 0 }),
287
+ 'set',
288
+ { key: req.cacheKey, ttl: req.cacheOptions.ttl }
289
+ );
290
+
291
+ if (!setResult.success) {
292
+ cacheErrors.push(setResult.error);
293
+ }
294
+
295
+ return {
296
+ result: response,
297
+ cacheKey: req.cacheKey,
298
+ metadata: { hit: false, latency: totalLatency },
299
+ cacheErrors: cacheErrors
300
+ };
301
+ } catch (serviceError) {
302
+ // If the underlying service fails, throw the error
303
+ this.log.error('Service operation failed:', {
304
+ error: serviceError.message,
305
+ serviceName: req.target?.name,
306
+ key: req.cacheKey
307
+ });
308
+ throw serviceError;
309
+ }
164
310
  }
165
311
  case "cds.ql":
166
312
  const query = arg1;
@@ -177,7 +323,15 @@ class CapOperations {
177
323
 
178
324
  // Track cache operation timing
179
325
  const startTime = process.hrtime();
180
- const hasCachedValue = await this.cache.has(query.cacheKey);
326
+
327
+ // Safely check if key exists in cache
328
+ const hasCachedValueResult = await this.safeCacheOperation(
329
+ () => this.cache.has(query.cacheKey),
330
+ 'has',
331
+ { key: query.cacheKey, serviceName: srv?.name }
332
+ );
333
+
334
+ const hasCachedValue = hasCachedValueResult.success && hasCachedValueResult.result;
181
335
  const cacheLatency = this.getElapsedMs(startTime);
182
336
  const metadata = {
183
337
  dataType: 'Query',
@@ -197,35 +351,89 @@ class CapOperations {
197
351
  }),
198
352
  cacheOptions: JSON.stringify(options)
199
353
  };
354
+ const cacheErrors = [];
200
355
 
201
356
  if (hasCachedValue) {
202
357
  // Cache hit
203
- this.statistics.recordHit(cacheLatency, query.cacheKey, metadata);
358
+ const hitStatsResult = await this.safeCacheOperation(
359
+ () => this.statistics.recordHit(cacheLatency, query.cacheKey, metadata),
360
+ 'recordHit',
361
+ { key: query.cacheKey, latency: cacheLatency }
362
+ );
363
+ if (!hitStatsResult.success) {
364
+ cacheErrors.push(hitStatsResult.error);
365
+ }
366
+
367
+ const getResult = await this.safeCacheOperation(
368
+ () => this.cache.send("GET", { key: query.cacheKey }),
369
+ 'get',
370
+ { key: query.cacheKey }
371
+ );
372
+
373
+ if (getResult.success && getResult.result?.value !== undefined) {
374
+ return {
375
+ result: getResult.result.value,
376
+ cacheKey: query.cacheKey,
377
+ metadata: { hit: true, latency: cacheLatency },
378
+ cacheErrors: cacheErrors
379
+ };
380
+ }
381
+ }
204
382
 
205
- const wrappedValue = await this.cache.send("GET", { key: query.cacheKey });
206
- return { result: wrappedValue?.value, cacheKey: query.cacheKey, metadata: { hit: true, latency: cacheLatency } };
207
- } else {
208
- // Cache miss
383
+ // Cache miss or cache error
384
+ try {
209
385
  const data = await srv.run(query);
210
386
  const totalLatency = this.getElapsedMs(startTime);
211
387
 
212
- // Track the miss with total latency (cache lookup + backend operation)
213
- this.statistics.recordMiss(totalLatency, query.cacheKey, metadata);
214
-
388
+ // Safely record miss statistics
389
+ const missStatsResult = await this.safeCacheOperation(
390
+ () => this.statistics.recordMiss(totalLatency, query.cacheKey, metadata),
391
+ 'recordMiss',
392
+ { key: query.cacheKey, latency: totalLatency }
393
+ );
394
+ if (!missStatsResult.success) {
395
+ cacheErrors.push(missStatsResult.error);
396
+ }
397
+
398
+ // Safely store in cache
215
399
  const wrappedValue = {
216
400
  value: data,
217
401
  tags: this.tagResolver.resolveTags(options.tags, data, { ...query.params, hash: this.keyManager.createKey(query, { serviceName: srv?.name, template: '{hash}' }) }),
218
402
  timestamp: Date.now()
219
403
  };
220
- await this.cache.send("SET", { key: query.cacheKey, value: wrappedValue, ttl: options.ttl || 0 });
221
- return { result: data, cacheKey: query.cacheKey, metadata: { hit: false, latency: totalLatency } };
404
+
405
+ const setResult = await this.safeCacheOperation(
406
+ () => this.cache.send("SET", { key: query.cacheKey, value: wrappedValue, ttl: options.ttl || 0 }),
407
+ 'set',
408
+ { key: query.cacheKey, ttl: options.ttl }
409
+ );
410
+ if (!setResult.success) {
411
+ cacheErrors.push(setResult.error);
412
+ }
413
+
414
+ this.log.info('REEEESULT', { setResult, cacheErrors, wrappedValue });
415
+
416
+ return {
417
+ result: data,
418
+ cacheKey: query.cacheKey,
419
+ metadata: { hit: false, latency: totalLatency },
420
+ cacheErrors: cacheErrors
421
+ };
422
+ } catch (serviceError) {
423
+ // If the underlying service fails, throw the error
424
+ this.log.error('Service operation failed:', {
425
+ error: serviceError.message,
426
+ serviceName: srv?.name,
427
+ key: query.cacheKey
428
+ });
429
+ throw serviceError;
222
430
  }
223
431
  } else {
224
432
  return srv.run(query);
225
433
  }
226
434
  }
227
435
  }
228
- return null;
436
+ return { result: null, cacheKey: null, metadata: { hit: false, latency: 0 }, cacheErrors: [] };
229
437
  }
230
438
 
231
439
  /**
@@ -292,8 +500,6 @@ class CapOperations {
292
500
  */
293
501
  extractMetadataFromRequest(req) {
294
502
 
295
- console.log(JSON.stringify(req.http?.req, null, 2));
296
-
297
503
  const metadata = {
298
504
  dataType: req.constructor.name,
299
505
  serviceName: req.target?.name || '',
@@ -1,8 +1,5 @@
1
1
  const { Keyv } = require('keyv');
2
- const { default: KeyvRedis } = require('@keyv/redis');
3
- const { default: KeyvSqlite } = require('@keyv/sqlite');
4
- const { default: KeyvLz4 } = require('@keyv/compress-lz4');
5
- const { default: KeyvGzip } = require('@keyv/compress-gzip');
2
+ const { requireOptional, requireAnyOptional } = require('./optionalRequire');
6
3
 
7
4
  /**
8
5
  * Manages cache store initialization and configuration
@@ -10,6 +7,7 @@ const { default: KeyvGzip } = require('@keyv/compress-gzip');
10
7
  class CacheStoreManager {
11
8
  constructor(options = {}) {
12
9
  this.options = options;
10
+ this.log = cds.log('cds-caching')
13
11
  }
14
12
 
15
13
  /**
@@ -20,12 +18,13 @@ class CacheStoreManager {
20
18
  */
21
19
  createStore(options, cacheName) {
22
20
  const store = this._createStoreInstance(options);
23
- const cacheOptions = this._createCacheOptions(options, cacheName);
21
+ const cacheOptions = this._createCacheOptions(options, cacheName, store);
24
22
  const cache = new Keyv(cacheOptions);
23
+ cache.throwOnErrors = options.throwOnErrors;
25
24
 
26
25
  // Set up error handling
27
26
  cache.on('error', err => {
28
- console.error(`Cache error for ${cacheName}:`, err);
27
+ this.log.error(`Cache error for ${cacheName}:`, err);
29
28
  });
30
29
 
31
30
  // Set up cleanup function
@@ -41,15 +40,46 @@ class CacheStoreManager {
41
40
  _createStoreInstance(options) {
42
41
  switch (options.store) {
43
42
  case "sqlite":
43
+ // Support both adapters:
44
+ // - @keyv/sqlite (default export)
45
+ // - @resolid/keyv-sqlite (named export KeyvSqlite) https://github.com/huijiewei/keyv-sqlite
46
+ const sqliteMod = requireAnyOptional(['@keyv/sqlite', '@resolid/keyv-sqlite'], { feature: 'store', value: 'sqlite' });
47
+ const KeyvSqlite = sqliteMod?.KeyvSqlite ?? sqliteMod?.default ?? sqliteMod;
44
48
  return new KeyvSqlite({
45
49
  url: options.credentials?.url,
46
50
  table: options.credentials?.table || 'cache',
47
51
  busyTimeout: options.credentials?.busyTimeout || 10000
48
52
  });
49
53
  case "redis":
50
- return new KeyvRedis({
54
+ const KeyvRedis = requireOptional('@keyv/redis', { feature: 'store', value: 'redis' });
55
+ const store = new KeyvRedis({
51
56
  ...options.credentials,
52
57
  ...(options.credentials?.uri ? { url: options.credentials?.uri } : {}),
58
+ ...{ throwOnConnectErrors: options.throwOnErrors, useKeyPrefix: false, throwOnErrors: options.throwOnErrors }
59
+ });
60
+
61
+ // see https://keyv.org/docs/storage-adapters/redis/#gracefully-handling-errors-and-timeouts for more details
62
+ if (options.throwOnErrors) {
63
+ store.throwOnConnectErrors = false; // We want to handle the errors ourselves
64
+ store.throwOnErrors = true; // Redis will throw errors for connection issues, etc.
65
+ const redisClient = store.client;
66
+ if (redisClient.options) {
67
+ redisClient.options.disableOfflineQueue = true;
68
+ if (redisClient.options.socket) {
69
+ redisClient.options.socket.reconnectStrategy = false; // Disable automatic reconnection
70
+ }
71
+ }
72
+ }
73
+
74
+ return store;
75
+ case "postgres":
76
+ const KeyvPostgres = requireOptional('@keyv/postgres', { feature: 'store', value: 'postgres' });
77
+ return new KeyvPostgres({
78
+ ...options.credentials,
79
+ ...(options.credentials?.url ? { uri: options.credentials?.url } : options.credentials?.uri ? { url: options.credentials?.uri } : {}),
80
+ ...(options.credentials?.schema ? { schema: options.credentials?.schema } : {}),
81
+ ...(options.credentials?.table ? { table: options.credentials?.table } : {}),
82
+ ...{ throwOnConnectErrors: options.throwOnErrors, useKeyPrefix: false, throwOnErrors: options.throwOnErrors }
53
83
  });
54
84
  default:
55
85
  return new Map();
@@ -60,11 +90,12 @@ class CacheStoreManager {
60
90
  * Create cache options object
61
91
  * @private
62
92
  */
63
- _createCacheOptions(options, cacheName) {
93
+ _createCacheOptions(options, cacheName, store) {
64
94
  return {
65
95
  namespace: options.namespace || cacheName,
66
- store: this._createStoreInstance(options),
67
- compression: this._createCompression(options.compression)
96
+ store,
97
+ compression: this._createCompression(options.compression),
98
+ useKeyPrefix: store.constructor.name === 'KeyvRedis' ? false : true // see https://github.com/mikezaschka/cds-caching/issues/11
68
99
  };
69
100
  }
70
101
 
@@ -75,8 +106,10 @@ class CacheStoreManager {
75
106
  _createCompression(compressionType) {
76
107
  switch (compressionType) {
77
108
  case "lz4":
109
+ const KeyvLz4 = requireOptional('@keyv/compress-lz4', { feature: 'compression', value: 'lz4' });
78
110
  return new KeyvLz4();
79
111
  case "gzip":
112
+ const KeyvGzip = requireOptional('@keyv/compress-gzip', { feature: 'compression', value: 'gzip' });
80
113
  return new KeyvGzip();
81
114
  default:
82
115
  return undefined;
@@ -93,7 +126,7 @@ class CacheStoreManager {
93
126
  try {
94
127
  await store.disconnect();
95
128
  } catch (err) {
96
- console.error(`Error disconnecting from store for ${cacheName}:`, err);
129
+ this.log.error(`Error disconnecting from store for ${cacheName}:`, err);
97
130
  }
98
131
  }
99
132
  };
@@ -103,7 +103,8 @@ class RuntimeConfigurationManager {
103
103
  return {
104
104
  metricsEnabled: cacheConfig?.metricsEnabled === true || cacheConfig?.metricsEnabled === 1 || false,
105
105
  keyMetricsEnabled: cacheConfig?.keyMetricsEnabled === true || cacheConfig?.keyMetricsEnabled === 1 || false,
106
- keyManagement
106
+ keyManagement,
107
+ throwOnErrors: this.options.throwOnErrors
107
108
  };
108
109
  } catch (error) {
109
110
  this.log.warn(`Failed to get runtime configuration for cache ${this.cacheName}:`, error);
@@ -114,7 +115,8 @@ class RuntimeConfigurationManager {
114
115
  isUserAware: false,
115
116
  isTenantAware: false,
116
117
  isLocaleAware: false
117
- }
118
+ },
119
+ throwOnErrors: this.options.throwOnErrors
118
120
  };
119
121
  }
120
122
  }
@@ -0,0 +1,45 @@
1
+ function isMissingModuleError(err, packageName) {
2
+ const msg = String(err?.message || '');
3
+ return (
4
+ (err?.code === 'MODULE_NOT_FOUND' || err?.code === 'ERR_MODULE_NOT_FOUND') &&
5
+ (msg.includes(`'${packageName}'`) || msg.includes(`"${packageName}"`) || msg.includes(packageName))
6
+ );
7
+ }
8
+
9
+ function requireOptional(packageName, { feature, value } = {}) {
10
+ try {
11
+ const mod = require(packageName);
12
+ return mod?.default ?? mod;
13
+ } catch (err) {
14
+ if (!isMissingModuleError(err, packageName)) throw err;
15
+
16
+ const featureText = feature ? `${feature}="${value}" ` : '';
17
+ throw new Error(`cds-caching: ${featureText}requires installing "${packageName}" (npm i ${packageName})`);
18
+ }
19
+ }
20
+
21
+ function requireAnyOptional(packageNames, { feature, value } = {}) {
22
+ const missing = [];
23
+ for (const packageName of packageNames) {
24
+ try {
25
+ return require(packageName);
26
+ } catch (err) {
27
+ if (isMissingModuleError(err, packageName)) {
28
+ missing.push(packageName);
29
+ continue;
30
+ }
31
+ throw err;
32
+ }
33
+ }
34
+
35
+ const featureText = feature ? `${feature}="${value}" ` : '';
36
+ const pkgsQuoted = missing.map((p) => `"${p}"`).join(' or ');
37
+ const pkgsInstall = missing.map((p) => `npm i ${p}`).join(' OR ');
38
+ throw new Error(`cds-caching: ${featureText}requires installing ${pkgsQuoted} (${pkgsInstall})`);
39
+ }
40
+
41
+ module.exports = {
42
+ requireOptional,
43
+ requireAnyOptional
44
+ };
45
+
package/lib/util.js CHANGED
@@ -93,7 +93,7 @@ const bindEntity = async (service, entity) => {
93
93
  const createCacheEntry = async (cacheName, serviceConfig = {}) => {
94
94
  try {
95
95
  const db = await cds.connect.to('db');
96
- const { Caches } = db.entities('plugin.cds_caching');
96
+ const { Caches } = cds.entities('plugin.cds_caching');
97
97
 
98
98
  // Check if cache entry already exists
99
99
  const existingCache = await db.read(Caches).where({ name: cacheName });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cds-caching",
3
- "version": "1.0.0",
3
+ "version": "1.2.0",
4
4
  "description": "A caching plugin for SAP CAP applications",
5
5
  "repository": {
6
6
  "type": "git",
@@ -34,7 +34,33 @@
34
34
  "release": "release-it"
35
35
  },
36
36
  "peerDependencies": {
37
- "@sap/cds": ">=8"
37
+ "@sap/cds": ">=8",
38
+ "@keyv/compress-gzip": "^2.0.3",
39
+ "@keyv/compress-lz4": "^1.0.1",
40
+ "@keyv/redis": "^5.0.0",
41
+ "@keyv/sqlite": "^4.0.5",
42
+ "@resolid/keyv-sqlite": "^4.0.5",
43
+ "@keyv/postgres": "^4.0.5"
44
+ },
45
+ "peerDependenciesMeta": {
46
+ "@keyv/redis": {
47
+ "optional": true
48
+ },
49
+ "@keyv/sqlite": {
50
+ "optional": true
51
+ },
52
+ "@keyv/compress-lz4": {
53
+ "optional": true
54
+ },
55
+ "@keyv/compress-gzip": {
56
+ "optional": true
57
+ },
58
+ "@resolid/keyv-sqlite": {
59
+ "optional": true
60
+ },
61
+ "@keyv/postgres": {
62
+ "optional": true
63
+ }
38
64
  },
39
65
  "workspaces": [
40
66
  ".",
@@ -43,20 +69,22 @@
43
69
  "examples/app"
44
70
  ],
45
71
  "dependencies": {
46
- "@keyv/compress-gzip": "^2.0.3",
47
- "@keyv/compress-lz4": "^1.0.0",
48
- "@keyv/redis": "^4.5.0",
49
- "@keyv/sqlite": "^4.0.5",
50
- "keyv": "^5.3.4"
72
+ "keyv": "^5.6.0"
51
73
  },
52
74
  "devDependencies": {
53
- "eslint": "^9.30.1",
75
+ "@cap-js/cds-test": "^0.4.1",
76
+ "@release-it/conventional-changelog": "^10.0.5",
77
+ "eslint": "^10.0.0",
54
78
  "husky": "^9.1.7",
55
- "jest": "^30.0.4",
56
- "release-it": "^19.0.3",
57
- "@cap-js/cds-test": "^0.4.0"
79
+ "jest": "^30.2.0",
80
+ "release-it": "^19.2.4",
81
+ "@resolid/keyv-sqlite": "~5.0.1",
82
+ "@keyv/sqlite": "~4.0.8",
83
+ "@keyv/compress-lz4": "~1.0.1",
84
+ "@keyv/compress-gzip": "~2.0.3",
85
+ "@keyv/postgres": "~2.2.3"
58
86
  },
59
87
  "engines": {
60
- "node": ">=20"
88
+ "node": ">=24"
61
89
  }
62
90
  }