@akinon/next 1.93.0-snapshot-ZERO-3586-20250828143733 → 1.94.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/CHANGELOG.md +39 -1337
- package/__tests__/next-config.test.ts +10 -1
- package/api/cache.ts +5 -41
- package/components/accordion.tsx +5 -20
- package/components/file-input.tsx +3 -65
- package/components/input.tsx +0 -2
- package/components/link.tsx +12 -16
- package/components/modal.tsx +16 -32
- package/components/plugin-module.tsx +3 -30
- package/data/client/checkout.ts +4 -5
- package/data/server/category.ts +30 -52
- package/data/server/flatpage.ts +13 -20
- package/data/server/form.ts +1 -4
- package/data/server/landingpage.ts +13 -20
- package/data/server/list.ts +14 -25
- package/data/server/menu.ts +1 -4
- package/data/server/product.ts +40 -68
- package/data/server/seo.ts +1 -4
- package/data/server/special-page.ts +13 -18
- package/data/server/widget.ts +1 -4
- package/data/urls.ts +1 -5
- package/hocs/server/with-segment-defaults.tsx +2 -5
- package/hooks/use-localization.ts +3 -2
- package/jest.config.js +1 -7
- package/lib/cache-handler.mjs +26 -850
- package/lib/cache.ts +13 -432
- package/middlewares/checkout-provider.ts +1 -1
- package/middlewares/complete-gpay.ts +1 -2
- package/middlewares/complete-masterpass.ts +1 -2
- package/middlewares/default.ts +13 -50
- package/middlewares/locale.ts +1 -9
- package/middlewares/pretty-url.ts +1 -2
- package/middlewares/redirection-payment.ts +1 -2
- package/middlewares/saved-card-redirection.ts +1 -2
- package/middlewares/three-d-redirection.ts +1 -2
- package/middlewares/url-redirection.ts +15 -9
- package/package.json +3 -4
- package/plugins.d.ts +0 -8
- package/plugins.js +1 -3
- package/redux/middlewares/checkout.ts +1 -5
- package/sentry/index.ts +17 -54
- package/types/commerce/order.ts +0 -1
- package/types/index.ts +1 -43
- package/utils/app-fetch.ts +2 -7
- package/utils/index.ts +10 -34
- package/utils/redirect.ts +6 -31
- package/with-pz-config.js +5 -2
- package/__tests__/redirect.test.ts +0 -319
- package/api/image-proxy.ts +0 -75
- package/api/similar-product-list.ts +0 -84
- package/api/similar-products.ts +0 -120
- package/data/server/basket.ts +0 -72
- package/utils/redirect-ignore.ts +0 -35
package/lib/cache-handler.mjs
CHANGED
|
@@ -2,445 +2,6 @@ import { CacheHandler } from '@neshca/cache-handler';
|
|
|
2
2
|
import createLruHandler from '@neshca/cache-handler/local-lru';
|
|
3
3
|
import createRedisHandler from '@neshca/cache-handler/redis-strings';
|
|
4
4
|
import { createClient } from 'redis';
|
|
5
|
-
import * as zstdWasm from '@bokuweb/zstd-wasm';
|
|
6
|
-
|
|
7
|
-
// Color utilities for logs
|
|
8
|
-
const colors = {
|
|
9
|
-
red: '\x1b[31m',
|
|
10
|
-
green: '\x1b[32m',
|
|
11
|
-
yellow: '\x1b[33m',
|
|
12
|
-
blue: '\x1b[34m',
|
|
13
|
-
magenta: '\x1b[35m',
|
|
14
|
-
cyan: '\x1b[36m',
|
|
15
|
-
white: '\x1b[37m',
|
|
16
|
-
reset: '\x1b[0m',
|
|
17
|
-
bright: '\x1b[1m'
|
|
18
|
-
};
|
|
19
|
-
|
|
20
|
-
const colorLog = (color, prefix, ...args) => {
|
|
21
|
-
// Always show compression-related logs regardless of debug flag
|
|
22
|
-
// eslint-disable-next-line no-console
|
|
23
|
-
console.log(`${colors.bright}${color}${prefix}${colors.reset}`, ...args);
|
|
24
|
-
};
|
|
25
|
-
|
|
26
|
-
// Initialize ZSTD immediately with IIFE (to avoid top-level await)
|
|
27
|
-
let zstd;
|
|
28
|
-
|
|
29
|
-
// Initialize immediately using IIFE
|
|
30
|
-
(async () => {
|
|
31
|
-
try {
|
|
32
|
-
await zstdWasm.init();
|
|
33
|
-
zstd = zstdWasm;
|
|
34
|
-
colorLog(
|
|
35
|
-
colors.green,
|
|
36
|
-
'[Cache Handler] 🚀 ',
|
|
37
|
-
'ZSTD compression initialized successfully at module load'
|
|
38
|
-
);
|
|
39
|
-
} catch (error) {
|
|
40
|
-
colorLog(
|
|
41
|
-
colors.yellow,
|
|
42
|
-
'[Cache Handler] ⚠️ ',
|
|
43
|
-
'ZSTD compression failed to initialize:',
|
|
44
|
-
error.message
|
|
45
|
-
);
|
|
46
|
-
zstd = false;
|
|
47
|
-
}
|
|
48
|
-
})();
|
|
49
|
-
|
|
50
|
-
const getZstd = () => {
|
|
51
|
-
return zstd;
|
|
52
|
-
};
|
|
53
|
-
|
|
54
|
-
// Only ZSTD compression is used now - gzip/deflate removed for simplicity
|
|
55
|
-
|
|
56
|
-
// Compression/decompression functions for cache values
|
|
57
|
-
const compressValue = async (value) => {
|
|
58
|
-
try {
|
|
59
|
-
console_log(
|
|
60
|
-
'🔍 Compressing value type:',
|
|
61
|
-
typeof value,
|
|
62
|
-
'keys:',
|
|
63
|
-
value && typeof value === 'object' ? Object.keys(value) : 'N/A'
|
|
64
|
-
);
|
|
65
|
-
|
|
66
|
-
// For Next.js cache entries, we need to preserve the entire structure
|
|
67
|
-
// and only compress the nested 'value' field if it exists
|
|
68
|
-
if (value && typeof value === 'object' && value.value !== undefined) {
|
|
69
|
-
console_log('📦 Next.js cache entry detected, preserving structure');
|
|
70
|
-
|
|
71
|
-
// Debug original lifespan values
|
|
72
|
-
console_log('🔍 Original lifespan values:', {
|
|
73
|
-
expireAge: value.lifespan?.expireAge,
|
|
74
|
-
expireAt: value.lifespan?.expireAt,
|
|
75
|
-
revalidate: value.lifespan?.revalidate,
|
|
76
|
-
staleAge: value.lifespan?.staleAge,
|
|
77
|
-
currentTime: Math.floor(Date.now() / 1000)
|
|
78
|
-
});
|
|
79
|
-
|
|
80
|
-
const nestedValue = value.value;
|
|
81
|
-
const serializedNestedValue =
|
|
82
|
-
typeof nestedValue === 'string'
|
|
83
|
-
? nestedValue
|
|
84
|
-
: JSON.stringify(nestedValue);
|
|
85
|
-
const originalSize = Buffer.byteLength(serializedNestedValue, 'utf8');
|
|
86
|
-
|
|
87
|
-
// Only compress the nested value if larger than 1KB
|
|
88
|
-
if (originalSize < 1024) {
|
|
89
|
-
colorLog(
|
|
90
|
-
colors.blue,
|
|
91
|
-
'[Cache Handler] ⚡ ',
|
|
92
|
-
`Skipping compression for small value (${originalSize} bytes)`
|
|
93
|
-
);
|
|
94
|
-
// Ensure tags is always an array even for small values
|
|
95
|
-
const result = {
|
|
96
|
-
...value,
|
|
97
|
-
tags: Array.isArray(value.tags) ? value.tags : []
|
|
98
|
-
};
|
|
99
|
-
return result; // Return original structure with guaranteed array tags
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
const zstdLib = getZstd();
|
|
103
|
-
let compressed;
|
|
104
|
-
|
|
105
|
-
if (zstdLib && zstdLib !== false) {
|
|
106
|
-
compressed = zstdLib.compress(
|
|
107
|
-
Buffer.from(serializedNestedValue, 'utf8'),
|
|
108
|
-
3
|
|
109
|
-
);
|
|
110
|
-
colorLog(colors.cyan, '[Cache Handler] 🔵 ', `Using ZSTD compression`);
|
|
111
|
-
} else {
|
|
112
|
-
// No fallback compression - if ZSTD fails, don't compress
|
|
113
|
-
colorLog(
|
|
114
|
-
colors.yellow,
|
|
115
|
-
'[Cache Handler] ⚠️ ',
|
|
116
|
-
`ZSTD not available, storing uncompressed`
|
|
117
|
-
);
|
|
118
|
-
return {
|
|
119
|
-
...value,
|
|
120
|
-
tags: Array.isArray(value.tags) ? value.tags : []
|
|
121
|
-
};
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
const compressedBase64 = Buffer.from(compressed).toString('base64');
|
|
125
|
-
const compressionRatio =
|
|
126
|
-
((originalSize - compressed.length) / originalSize) * 100;
|
|
127
|
-
|
|
128
|
-
colorLog(
|
|
129
|
-
colors.cyan,
|
|
130
|
-
'[Cache Handler] 🔵 ',
|
|
131
|
-
`Compressed nested value ${originalSize} → ${
|
|
132
|
-
compressed.length
|
|
133
|
-
} bytes (${compressionRatio.toFixed(1)}% reduction, method: zstd)`
|
|
134
|
-
);
|
|
135
|
-
|
|
136
|
-
// Return the cache entry with compressed nested value but preserved structure
|
|
137
|
-
// Ensure tags is always an array for Redis handler compatibility
|
|
138
|
-
// Fix expireAge to match project's revalidate value (300) instead of Next.js calculated value (450)
|
|
139
|
-
const result = {
|
|
140
|
-
...value, // Preserve all metadata (lastModified, expiresAt, tags, etc.)
|
|
141
|
-
tags: Array.isArray(value.tags) ? value.tags : [], // Ensure tags is always an array
|
|
142
|
-
lifespan: {
|
|
143
|
-
...value.lifespan,
|
|
144
|
-
// Use revalidate value for expireAge to match project configuration
|
|
145
|
-
expireAge: value.lifespan?.revalidate || value.lifespan?.expireAge,
|
|
146
|
-
// Recalculate expireAt based on lastModifiedAt + revalidate
|
|
147
|
-
expireAt:
|
|
148
|
-
value.lifespan?.lastModifiedAt && value.lifespan?.revalidate
|
|
149
|
-
? value.lifespan.lastModifiedAt + value.lifespan.revalidate
|
|
150
|
-
: value.lifespan?.expireAt
|
|
151
|
-
},
|
|
152
|
-
value: {
|
|
153
|
-
__compressed: true,
|
|
154
|
-
__method: 'zstd',
|
|
155
|
-
__originalSize: originalSize,
|
|
156
|
-
__compressedSize: compressed.length,
|
|
157
|
-
__data: compressedBase64
|
|
158
|
-
}
|
|
159
|
-
};
|
|
160
|
-
|
|
161
|
-
// Debug final lifespan values to see if they changed
|
|
162
|
-
console_log('🔍 Final lifespan values after compression and fix:', {
|
|
163
|
-
expireAge: result.lifespan?.expireAge,
|
|
164
|
-
expireAt: result.lifespan?.expireAt,
|
|
165
|
-
revalidate: result.lifespan?.revalidate,
|
|
166
|
-
staleAge: result.lifespan?.staleAge,
|
|
167
|
-
originalExpireAge: value.lifespan?.expireAge,
|
|
168
|
-
fixedToMatchRevalidate:
|
|
169
|
-
result.lifespan?.expireAge === result.lifespan?.revalidate,
|
|
170
|
-
changed: result.lifespan?.expireAge !== value.lifespan?.expireAge
|
|
171
|
-
});
|
|
172
|
-
|
|
173
|
-
// Additional validation for Redis handler requirements
|
|
174
|
-
console_log('🔍 Compressed cache entry validation:', {
|
|
175
|
-
hasLastModified: result.lastModified !== undefined,
|
|
176
|
-
hasLifespan: result.lifespan !== undefined,
|
|
177
|
-
lifespanExpireAtType: typeof result.lifespan?.expireAt,
|
|
178
|
-
lifespanExpireAtValue: result.lifespan?.expireAt,
|
|
179
|
-
tagsIsArray: Array.isArray(result.tags),
|
|
180
|
-
tagsLength: result.tags?.length,
|
|
181
|
-
isValidForRedis:
|
|
182
|
-
result.lastModified !== undefined &&
|
|
183
|
-
result.lifespan?.expireAt !== undefined &&
|
|
184
|
-
typeof result.lifespan.expireAt === 'number' &&
|
|
185
|
-
Array.isArray(result.tags)
|
|
186
|
-
});
|
|
187
|
-
|
|
188
|
-
return result;
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
// For primitive values or non-cache-entry objects, compress directly
|
|
192
|
-
const serializedValue =
|
|
193
|
-
typeof value === 'string' ? value : JSON.stringify(value);
|
|
194
|
-
const originalSize = Buffer.byteLength(serializedValue, 'utf8');
|
|
195
|
-
|
|
196
|
-
if (originalSize < 1024) {
|
|
197
|
-
colorLog(
|
|
198
|
-
colors.blue,
|
|
199
|
-
'[Cache Handler] ⚡ ',
|
|
200
|
-
`Skipping compression for small value (${originalSize} bytes)`
|
|
201
|
-
);
|
|
202
|
-
// For non-Next.js cache entries, ensure they have required Redis fields if they're objects
|
|
203
|
-
// But NEVER modify Next.js cache entries (they have lastModified, lifespan, tags, value)
|
|
204
|
-
if (
|
|
205
|
-
value &&
|
|
206
|
-
typeof value === 'object' &&
|
|
207
|
-
value.lastModified === undefined &&
|
|
208
|
-
value.lifespan === undefined &&
|
|
209
|
-
value.value === undefined
|
|
210
|
-
) {
|
|
211
|
-
// This is a primitive object, not a Next.js cache entry
|
|
212
|
-
return {
|
|
213
|
-
...value,
|
|
214
|
-
tags: value.tags || [], // Preserve existing tags or add empty array
|
|
215
|
-
lastModified: Date.now(),
|
|
216
|
-
lifespan: {
|
|
217
|
-
expireAt: Math.floor(Date.now() / 1000) + 3600
|
|
218
|
-
}
|
|
219
|
-
};
|
|
220
|
-
}
|
|
221
|
-
// Fix expireAge for Next.js cache entries to match project's revalidate value
|
|
222
|
-
if (
|
|
223
|
-
value &&
|
|
224
|
-
typeof value === 'object' &&
|
|
225
|
-
value.lifespan &&
|
|
226
|
-
value.lifespan.revalidate
|
|
227
|
-
) {
|
|
228
|
-
return {
|
|
229
|
-
...value,
|
|
230
|
-
lifespan: {
|
|
231
|
-
...value.lifespan,
|
|
232
|
-
// Use revalidate value for expireAge to match project configuration
|
|
233
|
-
expireAge: value.lifespan.revalidate,
|
|
234
|
-
// Recalculate expireAt based on lastModifiedAt + revalidate
|
|
235
|
-
expireAt:
|
|
236
|
-
value.lifespan.lastModifiedAt && value.lifespan.revalidate
|
|
237
|
-
? value.lifespan.lastModifiedAt + value.lifespan.revalidate
|
|
238
|
-
: value.lifespan.expireAt
|
|
239
|
-
}
|
|
240
|
-
};
|
|
241
|
-
}
|
|
242
|
-
// Return other values unchanged
|
|
243
|
-
return value;
|
|
244
|
-
}
|
|
245
|
-
|
|
246
|
-
const zstdLib = getZstd();
|
|
247
|
-
let compressed;
|
|
248
|
-
|
|
249
|
-
if (zstdLib && zstdLib !== false) {
|
|
250
|
-
compressed = zstdLib.compress(Buffer.from(serializedValue, 'utf8'), 3);
|
|
251
|
-
colorLog(colors.cyan, '[Cache Handler] 🔵 ', `Using ZSTD compression`);
|
|
252
|
-
} else {
|
|
253
|
-
// No fallback compression - if ZSTD fails, don't compress
|
|
254
|
-
colorLog(
|
|
255
|
-
colors.yellow,
|
|
256
|
-
'[Cache Handler] ⚠️ ',
|
|
257
|
-
`ZSTD not available, storing uncompressed`
|
|
258
|
-
);
|
|
259
|
-
// For non-Next.js cache entries, ensure they have required Redis fields if they're objects
|
|
260
|
-
// But NEVER modify Next.js cache entries (they have lastModified, lifespan, tags, value)
|
|
261
|
-
if (
|
|
262
|
-
value &&
|
|
263
|
-
typeof value === 'object' &&
|
|
264
|
-
value.lastModified === undefined &&
|
|
265
|
-
value.lifespan === undefined &&
|
|
266
|
-
value.value === undefined
|
|
267
|
-
) {
|
|
268
|
-
// This is a primitive object, not a Next.js cache entry
|
|
269
|
-
return {
|
|
270
|
-
...value,
|
|
271
|
-
tags: value.tags || [], // Preserve existing tags or add empty array
|
|
272
|
-
lastModified: Date.now(),
|
|
273
|
-
lifespan: {
|
|
274
|
-
expireAt: Math.floor(Date.now() / 1000) + 3600
|
|
275
|
-
}
|
|
276
|
-
};
|
|
277
|
-
}
|
|
278
|
-
// Return Next.js cache entries and other values unchanged
|
|
279
|
-
return value;
|
|
280
|
-
}
|
|
281
|
-
|
|
282
|
-
const compressedBase64 = Buffer.from(compressed).toString('base64');
|
|
283
|
-
const compressionRatio =
|
|
284
|
-
((originalSize - compressed.length) / originalSize) * 100;
|
|
285
|
-
|
|
286
|
-
colorLog(
|
|
287
|
-
colors.cyan,
|
|
288
|
-
'[Cache Handler] 🔵 ',
|
|
289
|
-
`Compressed ${originalSize} → ${
|
|
290
|
-
compressed.length
|
|
291
|
-
} bytes (${compressionRatio.toFixed(1)}% reduction, method: zstd)`
|
|
292
|
-
);
|
|
293
|
-
|
|
294
|
-
// For compressed non-Next.js entries, ensure Redis compatibility
|
|
295
|
-
const compressedResult = {
|
|
296
|
-
__compressed: true,
|
|
297
|
-
__method: 'zstd',
|
|
298
|
-
__originalSize: originalSize,
|
|
299
|
-
__compressedSize: compressed.length,
|
|
300
|
-
__data: compressedBase64,
|
|
301
|
-
// Add required Redis fields only for truly primitive values (not cache entries)
|
|
302
|
-
tags: [], // Required array for Redis handler
|
|
303
|
-
lastModified: Date.now(),
|
|
304
|
-
lifespan: { expireAt: Math.floor(Date.now() / 1000) + 3600 } // Default 1 hour for primitive values only
|
|
305
|
-
};
|
|
306
|
-
|
|
307
|
-
console_log('🔍 Non-Next.js compressed entry validation:', {
|
|
308
|
-
hasRequiredFields:
|
|
309
|
-
Array.isArray(compressedResult.tags) &&
|
|
310
|
-
compressedResult.lastModified !== undefined &&
|
|
311
|
-
compressedResult.lifespan?.expireAt !== undefined &&
|
|
312
|
-
typeof compressedResult.lifespan.expireAt === 'number'
|
|
313
|
-
});
|
|
314
|
-
|
|
315
|
-
return compressedResult;
|
|
316
|
-
} catch (error) {
|
|
317
|
-
console.warn(
|
|
318
|
-
'[Cache Handler] Compression failed, storing uncompressed:',
|
|
319
|
-
error.message
|
|
320
|
-
);
|
|
321
|
-
return value; // Return original value on error
|
|
322
|
-
}
|
|
323
|
-
};
|
|
324
|
-
|
|
325
|
-
const decompressValue = async (compressedData) => {
|
|
326
|
-
try {
|
|
327
|
-
console_log(
|
|
328
|
-
'🔍 Decompressing value type:',
|
|
329
|
-
typeof compressedData,
|
|
330
|
-
'keys:',
|
|
331
|
-
compressedData && typeof compressedData === 'object'
|
|
332
|
-
? Object.keys(compressedData)
|
|
333
|
-
: 'N/A'
|
|
334
|
-
);
|
|
335
|
-
|
|
336
|
-
// Check if this is a Next.js cache entry with compressed nested value
|
|
337
|
-
if (
|
|
338
|
-
compressedData &&
|
|
339
|
-
typeof compressedData === 'object' &&
|
|
340
|
-
compressedData.value &&
|
|
341
|
-
typeof compressedData.value === 'object' &&
|
|
342
|
-
compressedData.value.__compressed
|
|
343
|
-
) {
|
|
344
|
-
console_log(
|
|
345
|
-
'📦 Next.js cache entry with compressed nested value detected'
|
|
346
|
-
);
|
|
347
|
-
|
|
348
|
-
const compressedNestedValue = compressedData.value;
|
|
349
|
-
const compressedBuffer = Buffer.from(
|
|
350
|
-
compressedNestedValue.__data,
|
|
351
|
-
'base64'
|
|
352
|
-
);
|
|
353
|
-
let decompressed;
|
|
354
|
-
|
|
355
|
-
if (compressedNestedValue.__method === 'zstd') {
|
|
356
|
-
const zstdLib = getZstd();
|
|
357
|
-
if (zstdLib && zstdLib !== false) {
|
|
358
|
-
decompressed = zstdLib.decompress(compressedBuffer).toString('utf8');
|
|
359
|
-
colorLog(
|
|
360
|
-
colors.cyan,
|
|
361
|
-
'[Cache Handler] 🔵 ',
|
|
362
|
-
'Using ZSTD decompression for nested value'
|
|
363
|
-
);
|
|
364
|
-
} else {
|
|
365
|
-
throw new Error('zstd not available for decompression');
|
|
366
|
-
}
|
|
367
|
-
} else {
|
|
368
|
-
// Legacy support for gzip compressed data, but warn about it
|
|
369
|
-
colorLog(
|
|
370
|
-
colors.yellow,
|
|
371
|
-
'[Cache Handler] ⚠️ ',
|
|
372
|
-
'Found legacy gzip compressed data, consider cache invalidation'
|
|
373
|
-
);
|
|
374
|
-
throw new Error(
|
|
375
|
-
'gzip decompression no longer supported - please invalidate cache'
|
|
376
|
-
);
|
|
377
|
-
}
|
|
378
|
-
|
|
379
|
-
colorLog(
|
|
380
|
-
colors.cyan,
|
|
381
|
-
'[Cache Handler] 🔵 ',
|
|
382
|
-
`Decompressed nested value ${compressedNestedValue.__compressedSize} → ${decompressed.length} bytes (method: zstd)`
|
|
383
|
-
);
|
|
384
|
-
|
|
385
|
-
// Restore the original cache entry structure with decompressed nested value
|
|
386
|
-
return {
|
|
387
|
-
...compressedData, // Preserve all metadata (lastModified, expiresAt, tags, etc.)
|
|
388
|
-
value: JSON.parse(decompressed) // Restore original nested value
|
|
389
|
-
};
|
|
390
|
-
}
|
|
391
|
-
|
|
392
|
-
// Check if this is a directly compressed value
|
|
393
|
-
if (
|
|
394
|
-
compressedData &&
|
|
395
|
-
typeof compressedData === 'object' &&
|
|
396
|
-
compressedData.__compressed
|
|
397
|
-
) {
|
|
398
|
-
console_log('📄 Directly compressed value detected');
|
|
399
|
-
|
|
400
|
-
const compressedBuffer = Buffer.from(compressedData.__data, 'base64');
|
|
401
|
-
let decompressed;
|
|
402
|
-
|
|
403
|
-
if (compressedData.__method === 'zstd') {
|
|
404
|
-
const zstdLib = getZstd();
|
|
405
|
-
if (zstdLib && zstdLib !== false) {
|
|
406
|
-
decompressed = zstdLib.decompress(compressedBuffer).toString('utf8');
|
|
407
|
-
colorLog(
|
|
408
|
-
colors.cyan,
|
|
409
|
-
'[Cache Handler] 🔵 ',
|
|
410
|
-
'Using ZSTD decompression'
|
|
411
|
-
);
|
|
412
|
-
} else {
|
|
413
|
-
throw new Error('zstd not available for decompression');
|
|
414
|
-
}
|
|
415
|
-
} else {
|
|
416
|
-
// Legacy support for gzip compressed data, but warn about it
|
|
417
|
-
colorLog(
|
|
418
|
-
colors.yellow,
|
|
419
|
-
'[Cache Handler] ⚠️ ',
|
|
420
|
-
'Found legacy gzip compressed data, consider cache invalidation'
|
|
421
|
-
);
|
|
422
|
-
throw new Error(
|
|
423
|
-
'gzip decompression no longer supported - please invalidate cache'
|
|
424
|
-
);
|
|
425
|
-
}
|
|
426
|
-
|
|
427
|
-
colorLog(
|
|
428
|
-
colors.cyan,
|
|
429
|
-
'[Cache Handler] 🔵 ',
|
|
430
|
-
`Decompressed ${compressedData.__compressedSize} → ${decompressed.length} bytes (method: zstd)`
|
|
431
|
-
);
|
|
432
|
-
|
|
433
|
-
return JSON.parse(decompressed);
|
|
434
|
-
}
|
|
435
|
-
|
|
436
|
-
// No compression detected, return as-is
|
|
437
|
-
console_log('✨ No compression detected, returning value as-is');
|
|
438
|
-
return compressedData;
|
|
439
|
-
} catch (error) {
|
|
440
|
-
console.warn('[Cache Handler] Decompression failed:', error.message);
|
|
441
|
-
return compressedData; // Return original data on error
|
|
442
|
-
}
|
|
443
|
-
};
|
|
444
5
|
|
|
445
6
|
// Cache configuration
|
|
446
7
|
const CACHE_CONFIG = {
|
|
@@ -575,53 +136,12 @@ async function getRedisClient() {
|
|
|
575
136
|
|
|
576
137
|
CacheHandler.onCreation(async () => {
|
|
577
138
|
console_log('Initializing cache handlers...');
|
|
578
|
-
colorLog(
|
|
579
|
-
colors.blue,
|
|
580
|
-
'[Cache Handler] 🔧 ',
|
|
581
|
-
'Starting cache handler initialization...'
|
|
582
|
-
);
|
|
583
|
-
|
|
584
|
-
// Force log to appear even if debug is off
|
|
585
|
-
colorLog(
|
|
586
|
-
colors.green,
|
|
587
|
-
'[Cache Handler] 🚀 ',
|
|
588
|
-
'CACHE HANDLER INITIALIZATION STARTING'
|
|
589
|
-
);
|
|
590
|
-
colorLog(colors.blue, '[Cache Handler] 🔧 ', 'Environment check:');
|
|
591
|
-
colorLog(
|
|
592
|
-
colors.blue,
|
|
593
|
-
'[Cache Handler] 🔧 ',
|
|
594
|
-
'- CACHE_HOST:',
|
|
595
|
-
process.env.CACHE_HOST
|
|
596
|
-
);
|
|
597
|
-
colorLog(
|
|
598
|
-
colors.blue,
|
|
599
|
-
'[Cache Handler] 🔧 ',
|
|
600
|
-
'- CACHE_PORT:',
|
|
601
|
-
process.env.CACHE_PORT
|
|
602
|
-
);
|
|
603
|
-
colorLog(
|
|
604
|
-
colors.blue,
|
|
605
|
-
'[Cache Handler] 🔧 ',
|
|
606
|
-
'- NODE_ENV:',
|
|
607
|
-
process.env.NODE_ENV
|
|
608
|
-
);
|
|
609
139
|
|
|
610
140
|
let client;
|
|
611
141
|
try {
|
|
612
142
|
client = await getRedisClient();
|
|
613
|
-
colorLog(
|
|
614
|
-
colors.green,
|
|
615
|
-
'[Cache Handler] ✅ ',
|
|
616
|
-
'Redis client connected successfully'
|
|
617
|
-
);
|
|
618
143
|
} catch (error) {
|
|
619
144
|
// Error already logged in getRedisClient, just return local handler
|
|
620
|
-
colorLog(
|
|
621
|
-
colors.red,
|
|
622
|
-
'[Cache Handler] ❌ ',
|
|
623
|
-
'Redis connection failed, using local cache only'
|
|
624
|
-
);
|
|
625
145
|
return {
|
|
626
146
|
handlers: [createLruHandler(CACHE_CONFIG.lru)]
|
|
627
147
|
};
|
|
@@ -630,20 +150,22 @@ CacheHandler.onCreation(async () => {
|
|
|
630
150
|
const redisHandler = createRedisHandler({
|
|
631
151
|
client,
|
|
632
152
|
timeoutMs: CACHE_CONFIG.redis.timeoutMs,
|
|
633
|
-
keyExpirationStrategy: '
|
|
153
|
+
keyExpirationStrategy: 'EXAT'
|
|
634
154
|
});
|
|
635
155
|
|
|
636
156
|
const localHandler = createLruHandler(CACHE_CONFIG.lru);
|
|
637
157
|
|
|
638
|
-
|
|
639
|
-
const versionPrefix = `${
|
|
158
|
+
// Pre-compute version prefix if exists
|
|
159
|
+
const versionPrefix = CACHE_CONFIG.version ? `${CACHE_CONFIG.version}:` : '';
|
|
640
160
|
|
|
641
161
|
// Create optimized functions for each scenario
|
|
642
|
-
const versionKeyString =
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
162
|
+
const versionKeyString = versionPrefix
|
|
163
|
+
? (key) => `${versionPrefix}${key}`
|
|
164
|
+
: (key) => key;
|
|
165
|
+
|
|
166
|
+
const versionKeyObject = versionPrefix
|
|
167
|
+
? (key) => ({ ...key, key: `${versionPrefix}${key.key}` })
|
|
168
|
+
: (key) => key;
|
|
647
169
|
|
|
648
170
|
// Main version key function that routes to optimized paths
|
|
649
171
|
const versionKey = (key) => {
|
|
@@ -657,24 +179,10 @@ CacheHandler.onCreation(async () => {
|
|
|
657
179
|
name: 'custom-local-then-redis',
|
|
658
180
|
get: async (key, context) => {
|
|
659
181
|
const vKey = versionKey(key);
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
console_log('GET called for key:', keyStr);
|
|
665
|
-
|
|
666
|
-
// Extract and log URL information for better debugging
|
|
667
|
-
if (keyStr) {
|
|
668
|
-
const urlMatch = keyStr.match(/https?%3A%2F%2F[^_]+|\/[^_]*$/);
|
|
669
|
-
const extractedUrl = urlMatch
|
|
670
|
-
? decodeURIComponent(urlMatch[0])
|
|
671
|
-
: 'unknown';
|
|
672
|
-
colorLog(
|
|
673
|
-
colors.white,
|
|
674
|
-
'[Cache Handler] 🌐 ',
|
|
675
|
-
`Cache GET for URL: ${extractedUrl}`
|
|
676
|
-
);
|
|
677
|
-
}
|
|
182
|
+
console_log(
|
|
183
|
+
'GET called for key:',
|
|
184
|
+
typeof vKey === 'string' ? vKey : vKey?.key
|
|
185
|
+
);
|
|
678
186
|
|
|
679
187
|
// Check local cache first
|
|
680
188
|
console_log('Checking local cache...');
|
|
@@ -682,38 +190,6 @@ CacheHandler.onCreation(async () => {
|
|
|
682
190
|
|
|
683
191
|
if (localResult) {
|
|
684
192
|
console_log('Found in local cache');
|
|
685
|
-
// Check if it's compressed data (new format or legacy format)
|
|
686
|
-
if (
|
|
687
|
-
localResult &&
|
|
688
|
-
typeof localResult === 'object' &&
|
|
689
|
-
(localResult.__compressed ||
|
|
690
|
-
(localResult.value && localResult.value.__compressed) ||
|
|
691
|
-
localResult.compressed !== undefined)
|
|
692
|
-
) {
|
|
693
|
-
try {
|
|
694
|
-
const decompressed = await decompressValue(localResult);
|
|
695
|
-
// If it's a string, parse it; otherwise return as-is (it's already parsed)
|
|
696
|
-
return typeof decompressed === 'string'
|
|
697
|
-
? JSON.parse(decompressed)
|
|
698
|
-
: decompressed;
|
|
699
|
-
} catch (error) {
|
|
700
|
-
console.warn(
|
|
701
|
-
'[Cache Handler] Failed to decompress local cache value:',
|
|
702
|
-
error.message
|
|
703
|
-
);
|
|
704
|
-
return localResult;
|
|
705
|
-
}
|
|
706
|
-
}
|
|
707
|
-
|
|
708
|
-
// Debug info for non-compressed values
|
|
709
|
-
console_log('🔍 Local result type:', typeof localResult);
|
|
710
|
-
if (
|
|
711
|
-
localResult &&
|
|
712
|
-
typeof localResult === 'object' &&
|
|
713
|
-
localResult.constructor === Object
|
|
714
|
-
) {
|
|
715
|
-
console_log('🔍 Local result keys:', Object.keys(localResult));
|
|
716
|
-
}
|
|
717
193
|
return localResult;
|
|
718
194
|
}
|
|
719
195
|
|
|
@@ -723,66 +199,14 @@ CacheHandler.onCreation(async () => {
|
|
|
723
199
|
|
|
724
200
|
if (redisResult) {
|
|
725
201
|
console_log('Found in Redis');
|
|
726
|
-
|
|
727
|
-
let finalResult = redisResult;
|
|
728
|
-
|
|
729
|
-
// Parse JSON if it's a string (Redis stores as JSON)
|
|
730
|
-
if (typeof redisResult === 'string') {
|
|
731
|
-
try {
|
|
732
|
-
finalResult = JSON.parse(redisResult);
|
|
733
|
-
console_log('🔄 Parsed JSON from Redis');
|
|
734
|
-
} catch (parseError) {
|
|
735
|
-
console.warn(
|
|
736
|
-
'⚠️ Failed to parse Redis JSON, using raw string:',
|
|
737
|
-
parseError.message
|
|
738
|
-
);
|
|
739
|
-
finalResult = redisResult;
|
|
740
|
-
}
|
|
741
|
-
}
|
|
742
|
-
|
|
743
|
-
// Check if it's compressed data and decompress (new format or legacy format)
|
|
744
|
-
if (
|
|
745
|
-
finalResult &&
|
|
746
|
-
typeof finalResult === 'object' &&
|
|
747
|
-
(finalResult.__compressed ||
|
|
748
|
-
(finalResult.value && finalResult.value.__compressed) ||
|
|
749
|
-
finalResult.compressed !== undefined)
|
|
750
|
-
) {
|
|
751
|
-
try {
|
|
752
|
-
const decompressed = await decompressValue(finalResult);
|
|
753
|
-
// If it's a string, parse it; otherwise return as-is (it's already parsed)
|
|
754
|
-
finalResult =
|
|
755
|
-
typeof decompressed === 'string'
|
|
756
|
-
? JSON.parse(decompressed)
|
|
757
|
-
: decompressed;
|
|
758
|
-
} catch (error) {
|
|
759
|
-
console.warn(
|
|
760
|
-
'[Cache Handler] Failed to decompress Redis cache value:',
|
|
761
|
-
error.message
|
|
762
|
-
);
|
|
763
|
-
// finalResult stays as is
|
|
764
|
-
}
|
|
765
|
-
}
|
|
766
|
-
|
|
767
|
-
// Debug Redis result
|
|
768
|
-
console_log('🔍 Redis result type:', typeof redisResult);
|
|
769
|
-
console_log('🔍 Final result type:', typeof finalResult);
|
|
770
|
-
if (
|
|
771
|
-
finalResult &&
|
|
772
|
-
typeof finalResult === 'object' &&
|
|
773
|
-
finalResult.constructor === Object
|
|
774
|
-
) {
|
|
775
|
-
console_log('🔍 Final result keys:', Object.keys(finalResult));
|
|
776
|
-
}
|
|
777
|
-
|
|
778
202
|
// Sync back to local cache for faster future access
|
|
779
203
|
try {
|
|
780
|
-
await localHandler.set(vKey,
|
|
204
|
+
await localHandler.set(vKey, redisResult, context);
|
|
781
205
|
console_log('Synced to local cache');
|
|
782
206
|
} catch (error) {
|
|
783
207
|
console_log('Failed to sync to local:', error.message);
|
|
784
208
|
}
|
|
785
|
-
return
|
|
209
|
+
return redisResult;
|
|
786
210
|
}
|
|
787
211
|
} catch (error) {
|
|
788
212
|
console_log('Redis error:', error.message);
|
|
@@ -793,260 +217,17 @@ CacheHandler.onCreation(async () => {
|
|
|
793
217
|
},
|
|
794
218
|
set: async (key, value, context) => {
|
|
795
219
|
const vKey = versionKey(key);
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
colorLog(colors.white, '[Cache Handler] 💾 ', `CACHE SET: ${keyStr}`);
|
|
800
|
-
console_log('SET called for key:', keyStr);
|
|
801
|
-
|
|
802
|
-
// Extract and log URL information for better debugging
|
|
803
|
-
if (keyStr) {
|
|
804
|
-
const urlMatch = keyStr.match(/https?%3A%2F%2F[^_]+|\/[^_]*$/);
|
|
805
|
-
const extractedUrl = urlMatch
|
|
806
|
-
? decodeURIComponent(urlMatch[0])
|
|
807
|
-
: 'unknown';
|
|
808
|
-
colorLog(
|
|
809
|
-
colors.white,
|
|
810
|
-
'[Cache Handler] 🌐 ',
|
|
811
|
-
`Cache SET for URL: ${extractedUrl}`
|
|
812
|
-
);
|
|
813
|
-
}
|
|
814
|
-
|
|
815
|
-
// Log context and value information for debugging
|
|
816
|
-
console_log('SET Debug Info:', {
|
|
817
|
-
contextKeys: context ? Object.keys(context) : 'no context',
|
|
818
|
-
valueKeys:
|
|
819
|
-
value && typeof value === 'object'
|
|
820
|
-
? Object.keys(value)
|
|
821
|
-
: 'primitive value',
|
|
822
|
-
valueType: typeof value,
|
|
823
|
-
contextRevalidatedAt: context?.revalidatedAt,
|
|
824
|
-
contextExpiresAt: context?.expiresAt,
|
|
825
|
-
contextLifespan: context?.lifespan,
|
|
826
|
-
valueTags: value?.tags,
|
|
827
|
-
valueLastModified: value?.lastModified,
|
|
828
|
-
valueExpiresAt: value?.expiresAt,
|
|
829
|
-
valueLifespan: value?.lifespan,
|
|
830
|
-
lifespanExpireAt: value?.lifespan?.expireAt,
|
|
831
|
-
currentTime: Math.floor(Date.now() / 1000),
|
|
832
|
-
ttlRemaining: value?.lifespan?.expireAt
|
|
833
|
-
? value.lifespan.expireAt - Math.floor(Date.now() / 1000)
|
|
834
|
-
: 'unknown'
|
|
835
|
-
});
|
|
836
|
-
|
|
837
|
-
// Debug original value TTL before any processing
|
|
838
|
-
colorLog(
|
|
839
|
-
colors.yellow,
|
|
840
|
-
'[Cache Handler] 🔍 ',
|
|
841
|
-
'ORIGINAL VALUE TTL DEBUG:',
|
|
842
|
-
{
|
|
843
|
-
originalExpireAt: value?.lifespan?.expireAt,
|
|
844
|
-
originalExpireAge: value?.lifespan?.expireAge,
|
|
845
|
-
originalRevalidate: value?.lifespan?.revalidate,
|
|
846
|
-
currentTime: Math.floor(Date.now() / 1000),
|
|
847
|
-
ttlRemaining: value?.lifespan?.expireAt
|
|
848
|
-
? value.lifespan.expireAt - Math.floor(Date.now() / 1000)
|
|
849
|
-
: 'N/A'
|
|
850
|
-
}
|
|
220
|
+
console_log(
|
|
221
|
+
'SET called for key:',
|
|
222
|
+
typeof vKey === 'string' ? vKey : vKey?.key
|
|
851
223
|
);
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
colors.blue,
|
|
860
|
-
'[Cache Handler] 🧪 ',
|
|
861
|
-
`Attempting compression for ${keyStr}`
|
|
862
|
-
);
|
|
863
|
-
compressedValue = await compressValue(value);
|
|
864
|
-
|
|
865
|
-
// Debug compressed value TTL after compression
|
|
866
|
-
colorLog(
|
|
867
|
-
colors.yellow,
|
|
868
|
-
'[Cache Handler] 🔍 ',
|
|
869
|
-
'COMPRESSED VALUE TTL DEBUG:',
|
|
870
|
-
{
|
|
871
|
-
compressedExpireAt: compressedValue?.lifespan?.expireAt,
|
|
872
|
-
compressedExpireAge: compressedValue?.lifespan?.expireAge,
|
|
873
|
-
compressedRevalidate: compressedValue?.lifespan?.revalidate,
|
|
874
|
-
ttlChanged:
|
|
875
|
-
value?.lifespan?.expireAt !== compressedValue?.lifespan?.expireAt,
|
|
876
|
-
originalTTL: value?.lifespan?.expireAt,
|
|
877
|
-
compressedTTL: compressedValue?.lifespan?.expireAt
|
|
878
|
-
}
|
|
879
|
-
);
|
|
880
|
-
|
|
881
|
-
// Check if compression actually happened (compressValue returns original if no compression)
|
|
882
|
-
shouldUseCompressed =
|
|
883
|
-
compressedValue !== value &&
|
|
884
|
-
(compressedValue?.__compressed ||
|
|
885
|
-
compressedValue?.value?.__compressed);
|
|
886
|
-
colorLog(
|
|
887
|
-
colors.green,
|
|
888
|
-
'[Cache Handler] ✅ ',
|
|
889
|
-
`Compression ${
|
|
890
|
-
shouldUseCompressed ? 'SUCCESS' : 'SKIPPED'
|
|
891
|
-
} for ${keyStr}`
|
|
892
|
-
);
|
|
893
|
-
} catch (error) {
|
|
894
|
-
colorLog(
|
|
895
|
-
colors.red,
|
|
896
|
-
'[Cache Handler] ❌ ',
|
|
897
|
-
`Compression failed for ${keyStr}:`,
|
|
898
|
-
error.message
|
|
899
|
-
);
|
|
900
|
-
console.warn(
|
|
901
|
-
'[Cache Handler] Compression failed, using original value:',
|
|
902
|
-
error.message
|
|
903
|
-
);
|
|
904
|
-
compressedValue = value;
|
|
905
|
-
shouldUseCompressed = false;
|
|
906
|
-
}
|
|
907
|
-
|
|
908
|
-
// Set to both caches - local gets original, Redis gets compressed (if successful)
|
|
909
|
-
// IMPORTANT: Both calls must use the same context to preserve TTL
|
|
910
|
-
|
|
911
|
-
// Try Redis with compressed value first, fallback to original on error
|
|
912
|
-
let redisSetResult;
|
|
913
|
-
|
|
914
|
-
if (shouldUseCompressed) {
|
|
915
|
-
// Log before attempting Redis SET with compression
|
|
916
|
-
colorLog(
|
|
917
|
-
colors.blue,
|
|
918
|
-
'[Cache Handler] 💾 ',
|
|
919
|
-
`Attempting Redis SET with compression for: ${keyStr}`
|
|
920
|
-
);
|
|
921
|
-
|
|
922
|
-
try {
|
|
923
|
-
// Debug what we're sending to Redis
|
|
924
|
-
colorLog(
|
|
925
|
-
colors.yellow,
|
|
926
|
-
'[Cache Handler] 🔍 ',
|
|
927
|
-
'SENDING TO REDIS (COMPRESSED):',
|
|
928
|
-
{
|
|
929
|
-
keyStr,
|
|
930
|
-
hasLifespan: compressedValue?.lifespan !== undefined,
|
|
931
|
-
expireAt: compressedValue?.lifespan?.expireAt,
|
|
932
|
-
expireAge: compressedValue?.lifespan?.expireAge,
|
|
933
|
-
contextExpireAt: context?.expiresAt,
|
|
934
|
-
contextLifespan: context?.lifespan
|
|
935
|
-
}
|
|
936
|
-
);
|
|
937
|
-
|
|
938
|
-
await redisHandler.set(vKey, compressedValue, context);
|
|
939
|
-
colorLog(
|
|
940
|
-
colors.green,
|
|
941
|
-
'[Cache Handler] ✅ ',
|
|
942
|
-
`Redis SET with compression successful for: ${keyStr}`
|
|
943
|
-
);
|
|
944
|
-
redisSetResult = { status: 'fulfilled' };
|
|
945
|
-
} catch (compressionError) {
|
|
946
|
-
colorLog(
|
|
947
|
-
colors.yellow,
|
|
948
|
-
'[Cache Handler] ⚠️ ',
|
|
949
|
-
`Redis SET with compression failed for ${keyStr}, trying uncompressed fallback:`,
|
|
950
|
-
compressionError.message
|
|
951
|
-
);
|
|
952
|
-
|
|
953
|
-
// Fallback to original uncompressed value
|
|
954
|
-
try {
|
|
955
|
-
// Debug what we're sending to Redis as fallback
|
|
956
|
-
colorLog(
|
|
957
|
-
colors.yellow,
|
|
958
|
-
'[Cache Handler] 🔍 ',
|
|
959
|
-
'SENDING TO REDIS (FALLBACK UNCOMPRESSED):',
|
|
960
|
-
{
|
|
961
|
-
keyStr,
|
|
962
|
-
hasLifespan: value?.lifespan !== undefined,
|
|
963
|
-
expireAt: value?.lifespan?.expireAt,
|
|
964
|
-
expireAge: value?.lifespan?.expireAge,
|
|
965
|
-
contextExpireAt: context?.expiresAt,
|
|
966
|
-
contextLifespan: context?.lifespan
|
|
967
|
-
}
|
|
968
|
-
);
|
|
969
|
-
|
|
970
|
-
await redisHandler.set(vKey, value, context);
|
|
971
|
-
colorLog(
|
|
972
|
-
colors.green,
|
|
973
|
-
'[Cache Handler] ✅ ',
|
|
974
|
-
`Redis SET fallback (uncompressed) successful for: ${keyStr}`
|
|
975
|
-
);
|
|
976
|
-
redisSetResult = { status: 'fulfilled' };
|
|
977
|
-
} catch (fallbackError) {
|
|
978
|
-
colorLog(
|
|
979
|
-
colors.red,
|
|
980
|
-
'[Cache Handler] ❌ ',
|
|
981
|
-
`Redis SET fallback also failed for: ${keyStr}`,
|
|
982
|
-
fallbackError.message
|
|
983
|
-
);
|
|
984
|
-
redisSetResult = { status: 'rejected', reason: fallbackError };
|
|
985
|
-
}
|
|
986
|
-
}
|
|
987
|
-
} else {
|
|
988
|
-
// No compression, use original value directly
|
|
989
|
-
colorLog(
|
|
990
|
-
colors.blue,
|
|
991
|
-
'[Cache Handler] 💾 ',
|
|
992
|
-
`Attempting Redis SET (uncompressed) for: ${keyStr}`
|
|
993
|
-
);
|
|
994
|
-
|
|
995
|
-
try {
|
|
996
|
-
// Debug what we're sending to Redis (no compression)
|
|
997
|
-
colorLog(
|
|
998
|
-
colors.yellow,
|
|
999
|
-
'[Cache Handler] 🔍 ',
|
|
1000
|
-
'SENDING TO REDIS (NO COMPRESSION):',
|
|
1001
|
-
{
|
|
1002
|
-
keyStr,
|
|
1003
|
-
hasLifespan: value?.lifespan !== undefined,
|
|
1004
|
-
expireAt: value?.lifespan?.expireAt,
|
|
1005
|
-
expireAge: value?.lifespan?.expireAge,
|
|
1006
|
-
contextExpireAt: context?.expiresAt,
|
|
1007
|
-
contextLifespan: context?.lifespan
|
|
1008
|
-
}
|
|
1009
|
-
);
|
|
1010
|
-
|
|
1011
|
-
await redisHandler.set(vKey, value, context);
|
|
1012
|
-
colorLog(
|
|
1013
|
-
colors.green,
|
|
1014
|
-
'[Cache Handler] ✅ ',
|
|
1015
|
-
`Redis SET (uncompressed) successful for: ${keyStr}`
|
|
1016
|
-
);
|
|
1017
|
-
redisSetResult = { status: 'fulfilled' };
|
|
1018
|
-
} catch (error) {
|
|
1019
|
-
colorLog(
|
|
1020
|
-
colors.red,
|
|
1021
|
-
'[Cache Handler] ❌ ',
|
|
1022
|
-
`Redis SET (uncompressed) failed for: ${keyStr}`,
|
|
1023
|
-
error.message
|
|
1024
|
-
);
|
|
1025
|
-
redisSetResult = { status: 'rejected', reason: error };
|
|
1026
|
-
}
|
|
1027
|
-
}
|
|
1028
|
-
|
|
1029
|
-
// Always set to local cache with original value
|
|
1030
|
-
let localSetResult;
|
|
1031
|
-
try {
|
|
1032
|
-
await localHandler.set(vKey, value, context);
|
|
1033
|
-
localSetResult = { status: 'fulfilled' };
|
|
1034
|
-
} catch (error) {
|
|
1035
|
-
localSetResult = { status: 'rejected', reason: error };
|
|
1036
|
-
}
|
|
1037
|
-
|
|
1038
|
-
const results = [localSetResult, redisSetResult];
|
|
1039
|
-
|
|
1040
|
-
// Log results
|
|
1041
|
-
console_log('SET Results:', {
|
|
1042
|
-
local: results[0].status,
|
|
1043
|
-
redis: results[1].status,
|
|
1044
|
-
localError:
|
|
1045
|
-
results[0].status === 'rejected' ? results[0].reason?.message : null,
|
|
1046
|
-
redisError:
|
|
1047
|
-
results[1].status === 'rejected' ? results[1].reason?.message : null,
|
|
1048
|
-
compressionUsed: shouldUseCompressed
|
|
1049
|
-
});
|
|
224
|
+
// Set to both caches
|
|
225
|
+
await Promise.allSettled([
|
|
226
|
+
localHandler.set(vKey, value, context),
|
|
227
|
+
redisHandler
|
|
228
|
+
.set(vKey, value, context)
|
|
229
|
+
.catch((error) => console_log('Redis SET error:', error.message))
|
|
230
|
+
]);
|
|
1050
231
|
},
|
|
1051
232
|
delete: async (key, context) => {
|
|
1052
233
|
const vKey = versionKey(key);
|
|
@@ -1069,11 +250,6 @@ CacheHandler.onCreation(async () => {
|
|
|
1069
250
|
};
|
|
1070
251
|
|
|
1071
252
|
console_log('[Cache Handler] Handlers initialized successfully');
|
|
1072
|
-
colorLog(
|
|
1073
|
-
colors.green,
|
|
1074
|
-
'[Cache Handler] 🎉 ',
|
|
1075
|
-
'Cache handlers ready with Redis + Local LRU + Compression'
|
|
1076
|
-
);
|
|
1077
253
|
|
|
1078
254
|
return {
|
|
1079
255
|
handlers: [customHandler]
|