@akinon/next 1.93.0-rc.52 → 1.93.0-snapshot-ZERO-3586-20250826205340
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 +81 -0
- package/api/cache.ts +16 -3
- package/data/server/category.ts +4 -2
- package/data/server/list.ts +2 -1
- package/data/server/menu.ts +4 -1
- package/data/server/special-page.ts +2 -1
- package/lib/cache.ts +221 -10
- package/package.json +2 -2
- package/types/index.ts +1 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,86 @@
|
|
|
1
1
|
# @akinon/next
|
|
2
2
|
|
|
3
|
+
## 1.93.0-snapshot-ZERO-3586-20250826205340
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- 5dfeea04: ZERO-2801: Revert ZERO-2801
|
|
8
|
+
- 823d82f9: ZERO-3393: Enhance error handling in checkout middleware to ensure errors are checked for existence before processing
|
|
9
|
+
- 412f0e2: ZERO-3586: Enhance caching functionality by adding support for compressed data storage and retrieval, along with a new method for setting multiple key-value pairs.
|
|
10
|
+
- 28c7ea79: ZERO-3427: Refactor redirect utility to handle undefined URL and improve locale handling
|
|
11
|
+
- e1aa030d: ZERO-3473: Refactor locale handling to prioritize cookie value for matched locale
|
|
12
|
+
- 6e6b0a9e: ZERO-3422: Add pz-flow-payment package
|
|
13
|
+
- 63774a6a: ZERO-3351: Add commerce redirection ignore list functionality and related utility
|
|
14
|
+
- 2d9b2b2c9: ZERO-2816: Add segment to headers
|
|
15
|
+
- 5e1feca6: Revert "ZERO-3286: Add notFound handling for chunk URLs starting with \_next"
|
|
16
|
+
- 40a46853: ZERO-3182: Optimize basket update mutation with optimistic update
|
|
17
|
+
- 5f7edd6: ZERO-3571: Enhance Jest configuration by adding base directory resolution and module name mapping
|
|
18
|
+
- 68bbcb27: ZERO-3393: Fix error handling in checkout middleware to check for errors array length
|
|
19
|
+
- d8be48fb: ZERO-3422: Update fetch method to use dynamic request method in wallet complete redirection middleware
|
|
20
|
+
- b55acb76: ZERO-2577: Fix pagination bug and update usePagination hook and ensure pagination controls rendering correctly
|
|
21
|
+
- f49bb74f: ZERO-3097: Add setCookie to logging in payment redirection middlewares
|
|
22
|
+
- 0ad91bb: ZERO-3489: Improve error handling in data fetching across multiple pages and server functions
|
|
23
|
+
- 143be2b9: ZERO-3457: Crop styles are customizable and logic improved for rendering similar products modal
|
|
24
|
+
- e9541a13d: ZERO-2816: Add headers to url
|
|
25
|
+
- 9b7d0de6: ZERO-3393: Improve error handling in checkout middleware to support both object and array error formats
|
|
26
|
+
- 72fd4d67: ZERO-3084: Fix URL search parameters encoding in default middleware
|
|
27
|
+
- c53ef7b95: ZERO-2668: The Link component has been updated to improve the logic for handling href values. Previously, if the href was not a string or started with 'http', it would return the href as is. Now, if the href is not provided, it will default to '#' to prevent any potential errors. Additionally, if the href is a string and does not start with 'http', it will be formatted with the locale and pathname, based on the localeUrlStrategy and defaultLocaleValue. This ensures that the correct href is generated based on the localization settings.
|
|
28
|
+
- a8539c8c: ZERO-3439: Enhance locale handling in middleware and redirect utility
|
|
29
|
+
- 16aff543: ZERO-3431: Add test script for redirect utility in package.json
|
|
30
|
+
- 64699d3ff: ZERO-2761: Fix invalid import for plugin module
|
|
31
|
+
- 9f8cd3bc: ZERO-3449: AI Search Active Filters & Crop Style changes have been implemented
|
|
32
|
+
- e974d8e8: ZERO-3406: Fix rc build
|
|
33
|
+
- 89ce46f: ZERO-3493: return 404 status code for pz-not-found pages
|
|
34
|
+
- 8645d90: ZERO-3574:Refactor redirect tests: streamline mock setup, enhance locale handling, and improve URL path resolution logic
|
|
35
|
+
- 7eb51ca9: ZERO-3424 :Update package versions
|
|
36
|
+
- c806fad7: ZERO-3422: Add Flow Payment plugin to the defined plugins list
|
|
37
|
+
- 7727ae55: ZERO-3073: Refactor basket page to use server-side data fetching and simplify component structure
|
|
38
|
+
- 8b1d24eb: ZERO-3422: Update fetch method to use dynamic request method in wallet complete redirection middleware
|
|
39
|
+
- d552629f: ZERO-3182: Refactor basketApi to use invalidatesTags and comment out onQueryStarted logic
|
|
40
|
+
- 17f87524e: ZERO-2816: Make the incoming currency lowercase
|
|
41
|
+
- 65d3b862: ZERO-3054: Update headers in appFetch
|
|
42
|
+
- 0abde6bb: ZERO-3422: Update fetch method to use dynamic request method in wallet complete redirection middleware
|
|
43
|
+
- 72ad7bb1: ZERO-3422: Add Flow Payment to the list of available plugins
|
|
44
|
+
- c39c7000: ZERO-3420: Refactor Modal component
|
|
45
|
+
- e7cd3a5e: ZERO-3435: Add Accept-Language to requestHeaders
|
|
46
|
+
- bbe18b9ff: ZERO-2575: Fix build error
|
|
47
|
+
- 17bfadc4: ZERO-3275: Disable OpenTelemetry monitoring in production environment
|
|
48
|
+
- 35dfb8f8: ZERO-3363: Refactor URL handling in checkout and redirection middlewares to use url.origin instead of process.env.NEXT_PUBLIC_URL
|
|
49
|
+
- 4920742c: Disable getCachedTranslations
|
|
50
|
+
- b6e5b624: ZERO-3257: Enhance locale middleware to redirect using existing or default locale and support 303 status for POST requests
|
|
51
|
+
- 0de55738: ZERO-3418: Update remotePatterns hostname to allow all subdomains
|
|
52
|
+
- 7e56d6b6: ZERO-2841: Update api tagTypes
|
|
53
|
+
- dfaceffd: ZERO-3356: Add useLoyaltyAvailability hook and update checkout state management
|
|
54
|
+
- 86642cf: ZERO-3531: Add saveSampleProducts endpoint and update URLs in checkout
|
|
55
|
+
- d99a6a7d: ZERO-3457: Fixed the settings prop and made sure everything is customizable.
|
|
56
|
+
- 9dc7298a: ZERO-3416: Refactor Accordion component to enhance props and improve styling flexibility
|
|
57
|
+
- 33377cfd: ZERO-3267: Refactor import statement for ROUTES in error-page component
|
|
58
|
+
- 43c182ee: ZERO-3054: Update Redis variable checks to conditionally include CACHE_SECRET
|
|
59
|
+
- c480272: ZERO-3531: Refactor checkoutApi: Remove unnecessary invalidatesTags property from POST request from sample products
|
|
60
|
+
- b00a90b1: ZERO-3436: Preserve query params on redirect
|
|
61
|
+
- facf1ada: ZERO-3445: Add SameSite and Secure attributes
|
|
62
|
+
- 485e8ef8: ZERO-3422: Refactor parameter handling in wallet complete redirection middleware to use forEach
|
|
63
|
+
- 26b2d0b: ZERO-3571: Remove test script execution from prebuild and simplify Jest module name mapping
|
|
64
|
+
- eeb20bea: Revert "ZERO-3054: Refactor cache handler to use custom Redis handler and implement key hashing"
|
|
65
|
+
- 99b6e7b9: ZERO-3421: Enhance Sentry error handling by adding network error detection logic and refining initialization options
|
|
66
|
+
- 3bf63c8a: ZERO-3286: Add notFound handling for chunk URLs starting with \_next
|
|
67
|
+
- 9be2c081: ZERO-3243: Improve basket update query handling with optimistic updates
|
|
68
|
+
- f7fd459b: ZERO-3445: Refactor setCookie function to include domain handling and improve cookie string construction
|
|
69
|
+
- 4de5303c: ZERO-2504: add cookie filter to api client request
|
|
70
|
+
- dc678c3: ZERO-3523: Enhance redirect tests with dynamic locale handling and settings integration
|
|
71
|
+
- f2c92d5c7: ZERO-2816: Update cookie name
|
|
72
|
+
- a420947: ZERO-3517: Fix optional chaining for rawData in error logging for category data handlers
|
|
73
|
+
- 7bd3d9928: ZERO-2801: Refactor locale middleware to handle single locale configuration
|
|
74
|
+
- acd2afd: ZERO-3431: Fix import statement for findBaseDir in next-config test
|
|
75
|
+
- 2d3f1788: ZERO-3417: Enhance FileInput component with additional props for customization
|
|
76
|
+
- fdd255ee: ZERO-3054: Refactor cache handler to use custom Redis handler and implement key hashing
|
|
77
|
+
- b434ac8: ZERO-3545: Update fetchCheckout API URL to include page parameter
|
|
78
|
+
- 49eeebfa: ZERO-2909: Add deleteCollectionItem query to wishlistApi
|
|
79
|
+
- 3f9b8d7e7: ZERO-2761: Update plugins.js for akinon-next
|
|
80
|
+
- fee608dd: ZERO-3422: Refactor body handling in wallet complete redirection middleware
|
|
81
|
+
- cbdb5c14: ZERO-3448: fix set cookie domain handling for subdomain locale strategy
|
|
82
|
+
- 0e82301: ZERO-3531: Add saveSampleProducts endpoint
|
|
83
|
+
|
|
3
84
|
## 1.93.0-rc.52
|
|
4
85
|
|
|
5
86
|
## 1.93.0-rc.51
|
package/api/cache.ts
CHANGED
|
@@ -21,20 +21,33 @@ async function handleRequest(...args) {
|
|
|
21
21
|
}
|
|
22
22
|
|
|
23
23
|
const formData = await req.formData();
|
|
24
|
-
const body = {} as { key: string; value?: string; expire?: number };
|
|
24
|
+
const body = {} as { key: string; value?: string; expire?: number; keyValuePairs?: string };
|
|
25
25
|
|
|
26
26
|
formData.forEach((value, key) => {
|
|
27
27
|
body[key] = value;
|
|
28
28
|
});
|
|
29
29
|
|
|
30
|
-
const { key, value, expire } = body;
|
|
30
|
+
const { key, value, expire, keyValuePairs } = body;
|
|
31
31
|
let response: string | boolean;
|
|
32
32
|
|
|
33
33
|
try {
|
|
34
34
|
if (req.method === 'POST') {
|
|
35
35
|
response = await Cache.get(key);
|
|
36
36
|
} else if (req.method === 'PUT') {
|
|
37
|
-
|
|
37
|
+
if (keyValuePairs) {
|
|
38
|
+
try {
|
|
39
|
+
const parsedKeyValuePairs = JSON.parse(keyValuePairs);
|
|
40
|
+
if (typeof parsedKeyValuePairs !== 'object' || parsedKeyValuePairs === null || Array.isArray(parsedKeyValuePairs)) {
|
|
41
|
+
throw new Error('Invalid keyValuePairs format - must be an object');
|
|
42
|
+
}
|
|
43
|
+
response = await Cache.mset(parsedKeyValuePairs, expire);
|
|
44
|
+
} catch (error) {
|
|
45
|
+
logger.error('Invalid keyValuePairs in mset request', { error });
|
|
46
|
+
return NextResponse.json({ error: 'Invalid keyValuePairs format' }, { status: 400 });
|
|
47
|
+
}
|
|
48
|
+
} else {
|
|
49
|
+
response = await Cache.set(key, value, expire);
|
|
50
|
+
}
|
|
38
51
|
}
|
|
39
52
|
} catch (error) {
|
|
40
53
|
logger.error(error);
|
package/data/server/category.ts
CHANGED
|
@@ -118,7 +118,8 @@ export const getCategoryData = ({
|
|
|
118
118
|
locale,
|
|
119
119
|
getCategoryDataHandler(pk, locale, currency, searchParams, headers),
|
|
120
120
|
{
|
|
121
|
-
expire: 300
|
|
121
|
+
expire: 300,
|
|
122
|
+
compressed: true
|
|
122
123
|
}
|
|
123
124
|
);
|
|
124
125
|
};
|
|
@@ -178,7 +179,8 @@ export const getCategoryBySlugData = async ({
|
|
|
178
179
|
locale,
|
|
179
180
|
getCategoryBySlugDataHandler(slug, locale, currency),
|
|
180
181
|
{
|
|
181
|
-
expire: 300
|
|
182
|
+
expire: 300,
|
|
183
|
+
compressed: true // Compress category data for memory savings
|
|
182
184
|
}
|
|
183
185
|
);
|
|
184
186
|
};
|
package/data/server/list.ts
CHANGED
package/data/server/menu.ts
CHANGED
|
@@ -48,6 +48,9 @@ export const getMenu = async (params?: MenuHandlerParams) => {
|
|
|
48
48
|
return Cache.wrap(
|
|
49
49
|
CacheKey.Menu(params?.depth ?? DEFAULT_DEPTH, params?.parent),
|
|
50
50
|
params?.locale ?? ServerVariables.locale,
|
|
51
|
-
getMenuHandler(params)
|
|
51
|
+
getMenuHandler(params),
|
|
52
|
+
{
|
|
53
|
+
compressed: true
|
|
54
|
+
}
|
|
52
55
|
);
|
|
53
56
|
};
|
package/lib/cache.ts
CHANGED
|
@@ -3,6 +3,11 @@ import { RedisClientType } from 'redis';
|
|
|
3
3
|
import Settings from 'settings';
|
|
4
4
|
import { CacheOptions } from '../types';
|
|
5
5
|
import logger from '../utils/log';
|
|
6
|
+
import { gzip, gunzip } from 'zlib';
|
|
7
|
+
import { promisify } from 'util';
|
|
8
|
+
|
|
9
|
+
const gzipAsync = promisify(gzip);
|
|
10
|
+
const gunzipAsync = promisify(gunzip);
|
|
6
11
|
|
|
7
12
|
const hashCacheKey = (object?: Record<string, string>) => {
|
|
8
13
|
if (!object) {
|
|
@@ -60,6 +65,28 @@ export const CacheKey = {
|
|
|
60
65
|
export class Cache {
|
|
61
66
|
static PROXY_URL = `${process.env.NEXT_PUBLIC_URL}/api/cache`;
|
|
62
67
|
|
|
68
|
+
private static serializeValue(value: any): string {
|
|
69
|
+
return typeof value === 'object' ? JSON.stringify(value) : String(value);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
private static validateKey(key: string): boolean {
|
|
73
|
+
return !(!key || key.trim() === '');
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
private static validateKeyValuePairs(keyValuePairs: Record<string, any>): {
|
|
77
|
+
isValid: boolean;
|
|
78
|
+
invalidKeys: string[];
|
|
79
|
+
} {
|
|
80
|
+
if (!keyValuePairs || Object.keys(keyValuePairs).length === 0) {
|
|
81
|
+
return { isValid: false, invalidKeys: [] };
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
const invalidKeys = Object.keys(keyValuePairs).filter(
|
|
85
|
+
(key) => !this.validateKey(key)
|
|
86
|
+
);
|
|
87
|
+
return { isValid: invalidKeys.length === 0, invalidKeys };
|
|
88
|
+
}
|
|
89
|
+
|
|
63
90
|
static formatKey(key: string, locale: string) {
|
|
64
91
|
return encodeURIComponent(`${Settings.commerceUrl}_${locale}_${key}`);
|
|
65
92
|
}
|
|
@@ -98,9 +125,9 @@ export class Cache {
|
|
|
98
125
|
return await Cache.clientPool.acquire();
|
|
99
126
|
}
|
|
100
127
|
|
|
101
|
-
static async get(key: string) {
|
|
102
|
-
let value;
|
|
103
|
-
let client;
|
|
128
|
+
static async get(key: string): Promise<any> {
|
|
129
|
+
let value: any;
|
|
130
|
+
let client: RedisClientType | undefined;
|
|
104
131
|
|
|
105
132
|
try {
|
|
106
133
|
client = await Cache.getClient();
|
|
@@ -123,14 +150,13 @@ export class Cache {
|
|
|
123
150
|
return value;
|
|
124
151
|
}
|
|
125
152
|
|
|
126
|
-
static async set(key: string, value: any, expire?: number) {
|
|
153
|
+
static async set(key: string, value: any, expire?: number): Promise<boolean> {
|
|
127
154
|
let success = false;
|
|
128
|
-
let client;
|
|
155
|
+
let client: RedisClientType | undefined;
|
|
129
156
|
|
|
130
157
|
try {
|
|
131
158
|
client = await Cache.getClient();
|
|
132
|
-
const serializedValue =
|
|
133
|
-
typeof value === 'object' ? JSON.stringify(value) : value;
|
|
159
|
+
const serializedValue = Cache.serializeValue(value);
|
|
134
160
|
|
|
135
161
|
if (expire) {
|
|
136
162
|
await client.set(key, serializedValue, { EX: expire });
|
|
@@ -183,7 +209,7 @@ export class Cache {
|
|
|
183
209
|
logger.debug('Cache wrap', { key, formattedKey, _options });
|
|
184
210
|
|
|
185
211
|
if (_options.cache) {
|
|
186
|
-
let cachedValue;
|
|
212
|
+
let cachedValue: any;
|
|
187
213
|
|
|
188
214
|
if (_options.useProxy) {
|
|
189
215
|
const body = new URLSearchParams();
|
|
@@ -194,7 +220,9 @@ export class Cache {
|
|
|
194
220
|
logger.debug('Cache proxy request success', { key });
|
|
195
221
|
logger.trace('Cache proxy request', { key, cachedValue });
|
|
196
222
|
} else {
|
|
197
|
-
cachedValue =
|
|
223
|
+
cachedValue = _options.compressed
|
|
224
|
+
? await Cache.getCompressed(formattedKey)
|
|
225
|
+
: await Cache.get(formattedKey);
|
|
198
226
|
}
|
|
199
227
|
|
|
200
228
|
if (cachedValue) {
|
|
@@ -224,7 +252,11 @@ export class Cache {
|
|
|
224
252
|
logger.error('Cache proxy error', error);
|
|
225
253
|
}
|
|
226
254
|
} else {
|
|
227
|
-
|
|
255
|
+
if (_options.compressed) {
|
|
256
|
+
await Cache.setCompressed(formattedKey, data, _options?.expire);
|
|
257
|
+
} else {
|
|
258
|
+
await Cache.set(formattedKey, JSON.stringify(data), _options?.expire);
|
|
259
|
+
}
|
|
228
260
|
}
|
|
229
261
|
}
|
|
230
262
|
|
|
@@ -244,4 +276,183 @@ export class Cache {
|
|
|
244
276
|
|
|
245
277
|
return response;
|
|
246
278
|
}
|
|
279
|
+
|
|
280
|
+
static async mset(
|
|
281
|
+
keyValuePairs: Record<string, any>,
|
|
282
|
+
expire?: number
|
|
283
|
+
): Promise<boolean> {
|
|
284
|
+
const validation = Cache.validateKeyValuePairs(keyValuePairs);
|
|
285
|
+
if (!validation.isValid) {
|
|
286
|
+
if (validation.invalidKeys.length > 0) {
|
|
287
|
+
logger.error('Invalid keys in mset', {
|
|
288
|
+
invalidKeys: validation.invalidKeys
|
|
289
|
+
});
|
|
290
|
+
} else {
|
|
291
|
+
logger.warn('mset called with empty keyValuePairs');
|
|
292
|
+
}
|
|
293
|
+
return false;
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
let success = false;
|
|
297
|
+
let client: RedisClientType | undefined;
|
|
298
|
+
|
|
299
|
+
try {
|
|
300
|
+
client = await Cache.getClient();
|
|
301
|
+
const pipeline = client.multi();
|
|
302
|
+
|
|
303
|
+
Object.entries(keyValuePairs).forEach(([key, value]) => {
|
|
304
|
+
const serializedValue = Cache.serializeValue(value);
|
|
305
|
+
if (expire) {
|
|
306
|
+
pipeline.set(key, serializedValue, { EX: expire });
|
|
307
|
+
} else {
|
|
308
|
+
pipeline.set(key, serializedValue);
|
|
309
|
+
}
|
|
310
|
+
});
|
|
311
|
+
|
|
312
|
+
const results = await pipeline.exec();
|
|
313
|
+
|
|
314
|
+
const failures =
|
|
315
|
+
results?.filter((result) => result instanceof Error) || [];
|
|
316
|
+
|
|
317
|
+
if (failures.length > 0) {
|
|
318
|
+
logger.warn('Some mset operations failed', {
|
|
319
|
+
totalOperations: results?.length || 0,
|
|
320
|
+
failures: failures.length,
|
|
321
|
+
keys: Object.keys(keyValuePairs)
|
|
322
|
+
});
|
|
323
|
+
success = false;
|
|
324
|
+
} else {
|
|
325
|
+
success = true;
|
|
326
|
+
logger.debug('Redis mset success', {
|
|
327
|
+
keys: Object.keys(keyValuePairs),
|
|
328
|
+
expire,
|
|
329
|
+
operationsCount: Object.keys(keyValuePairs).length
|
|
330
|
+
});
|
|
331
|
+
}
|
|
332
|
+
} catch (error) {
|
|
333
|
+
logger.error('Redis mset error', {
|
|
334
|
+
keys: Object.keys(keyValuePairs),
|
|
335
|
+
error
|
|
336
|
+
});
|
|
337
|
+
success = false;
|
|
338
|
+
} finally {
|
|
339
|
+
if (client) {
|
|
340
|
+
await Cache.clientPool.release(client);
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
return success;
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
static async setCompressed(
|
|
348
|
+
key: string,
|
|
349
|
+
value: any,
|
|
350
|
+
expire?: number
|
|
351
|
+
): Promise<boolean> {
|
|
352
|
+
if (!Cache.validateKey(key)) {
|
|
353
|
+
logger.error('Invalid key in setCompressed', { key });
|
|
354
|
+
return false;
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
let success = false;
|
|
358
|
+
let client: RedisClientType | undefined;
|
|
359
|
+
|
|
360
|
+
try {
|
|
361
|
+
client = await Cache.getClient();
|
|
362
|
+
const serializedValue = Cache.serializeValue(value);
|
|
363
|
+
|
|
364
|
+
if (serializedValue.length > 1024 * 1024) {
|
|
365
|
+
logger.warn('Large data being compressed', {
|
|
366
|
+
key,
|
|
367
|
+
size: serializedValue.length
|
|
368
|
+
});
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
const compressed = await gzipAsync(Buffer.from(serializedValue, 'utf8'));
|
|
372
|
+
|
|
373
|
+
if (expire) {
|
|
374
|
+
await client.set(key, compressed, { EX: expire });
|
|
375
|
+
} else {
|
|
376
|
+
await client.set(key, compressed);
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
success = true;
|
|
380
|
+
const compressionRatio = (
|
|
381
|
+
(1 - compressed.length / serializedValue.length) *
|
|
382
|
+
100
|
|
383
|
+
).toFixed(2);
|
|
384
|
+
logger.debug('Redis setCompressed success', {
|
|
385
|
+
key,
|
|
386
|
+
originalSize: serializedValue.length,
|
|
387
|
+
compressedSize: compressed.length,
|
|
388
|
+
compressionRatio: `${compressionRatio}%`
|
|
389
|
+
});
|
|
390
|
+
} catch (error) {
|
|
391
|
+
logger.error('Redis setCompressed error', { key, error });
|
|
392
|
+
success = false;
|
|
393
|
+
} finally {
|
|
394
|
+
if (client) {
|
|
395
|
+
await Cache.clientPool.release(client);
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
return success;
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
static async getCompressed(
|
|
403
|
+
key: string,
|
|
404
|
+
maxDecompressedSize: number = 10 * 1024 * 1024
|
|
405
|
+
): Promise<any> {
|
|
406
|
+
if (!Cache.validateKey(key)) {
|
|
407
|
+
logger.error('Invalid key in getCompressed', { key });
|
|
408
|
+
return null;
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
let value: any;
|
|
412
|
+
let client: RedisClientType | undefined;
|
|
413
|
+
|
|
414
|
+
try {
|
|
415
|
+
client = await Cache.getClient();
|
|
416
|
+
const compressed = await client.get(key);
|
|
417
|
+
|
|
418
|
+
if (compressed) {
|
|
419
|
+
const compressedBuffer = Buffer.isBuffer(compressed)
|
|
420
|
+
? compressed
|
|
421
|
+
: Buffer.from(compressed, 'binary');
|
|
422
|
+
|
|
423
|
+
const decompressed = await gunzipAsync(compressedBuffer as any);
|
|
424
|
+
|
|
425
|
+
if (decompressed.length > maxDecompressedSize) {
|
|
426
|
+
logger.error('Decompressed data exceeds size limit', {
|
|
427
|
+
key,
|
|
428
|
+
size: decompressed.length,
|
|
429
|
+
limit: maxDecompressedSize
|
|
430
|
+
});
|
|
431
|
+
throw new Error(
|
|
432
|
+
`Decompressed data too large: ${decompressed.length} bytes`
|
|
433
|
+
);
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
value = JSON.parse(decompressed.toString('utf8'));
|
|
437
|
+
|
|
438
|
+
logger.debug('Redis getCompressed success', {
|
|
439
|
+
key,
|
|
440
|
+
compressedSize: compressedBuffer.length,
|
|
441
|
+
decompressedSize: decompressed.length
|
|
442
|
+
});
|
|
443
|
+
} else {
|
|
444
|
+
value = null;
|
|
445
|
+
logger.debug('Redis getCompressed: key not found', { key });
|
|
446
|
+
}
|
|
447
|
+
} catch (error) {
|
|
448
|
+
logger.error('Redis getCompressed error', { key, error });
|
|
449
|
+
value = null;
|
|
450
|
+
} finally {
|
|
451
|
+
if (client) {
|
|
452
|
+
await Cache.clientPool.release(client);
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
return value;
|
|
457
|
+
}
|
|
247
458
|
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@akinon/next",
|
|
3
3
|
"description": "Core package for Project Zero Next",
|
|
4
|
-
"version": "1.93.0-
|
|
4
|
+
"version": "1.93.0-snapshot-ZERO-3586-20250826205340",
|
|
5
5
|
"private": false,
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"bin": {
|
|
@@ -34,7 +34,7 @@
|
|
|
34
34
|
"set-cookie-parser": "2.6.0"
|
|
35
35
|
},
|
|
36
36
|
"devDependencies": {
|
|
37
|
-
"@akinon/eslint-plugin-projectzero": "1.93.0-
|
|
37
|
+
"@akinon/eslint-plugin-projectzero": "1.93.0-snapshot-ZERO-3586-20250826205340",
|
|
38
38
|
"@babel/core": "7.26.10",
|
|
39
39
|
"@babel/preset-env": "7.26.9",
|
|
40
40
|
"@babel/preset-typescript": "7.27.0",
|