@akinon/next 1.93.0-snapshot-ZERO-3586-20250828143733 → 1.93.0-snapshot-ZERO-3586-20250901110545

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  # @akinon/next
2
2
 
3
- ## 1.93.0-snapshot-ZERO-3586-20250828143733
3
+ ## 1.93.0-snapshot-ZERO-3586-20250901110545
4
4
 
5
5
  ### Minor Changes
6
6
 
@@ -24,7 +24,7 @@
24
24
  - e9541a13: ZERO-2816: Add headers to url
25
25
  - 9b7d0de6: ZERO-3393: Improve error handling in checkout middleware to support both object and array error formats
26
26
  - 72fd4d67: ZERO-3084: Fix URL search parameters encoding in default middleware
27
- - c53ef7b9: 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.
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
28
  - a8539c8c: ZERO-3439: Enhance locale handling in middleware and redirect utility
29
29
  - 16aff543: ZERO-3431: Add test script for redirect utility in package.json
30
30
  - 64699d3f: ZERO-2761: Fix invalid import for plugin module
@@ -43,7 +43,7 @@
43
43
  - 72ad7bb1: ZERO-3422: Add Flow Payment to the list of available plugins
44
44
  - c39c7000: ZERO-3420: Refactor Modal component
45
45
  - e7cd3a5e: ZERO-3435: Add Accept-Language to requestHeaders
46
- - bbe18b9f: ZERO-2575: Fix build error
46
+ - bbe18b9ff: ZERO-2575: Fix build error
47
47
  - 17bfadc4: ZERO-3275: Disable OpenTelemetry monitoring in production environment
48
48
  - 35dfb8f8: ZERO-3363: Refactor URL handling in checkout and redirection middlewares to use url.origin instead of process.env.NEXT_PUBLIC_URL
49
49
  - 4920742c: Disable getCachedTranslations
@@ -56,7 +56,7 @@
56
56
  - 9dc7298a: ZERO-3416: Refactor Accordion component to enhance props and improve styling flexibility
57
57
  - 33377cfd: ZERO-3267: Refactor import statement for ROUTES in error-page component
58
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
59
+ - c480272c: ZERO-3531: Refactor checkoutApi: Remove unnecessary invalidatesTags property from POST request from sample products
60
60
  - b00a90b1: ZERO-3436: Preserve query params on redirect
61
61
  - facf1ada: ZERO-3445: Add SameSite and Secure attributes
62
62
  - 485e8ef8: ZERO-3422: Refactor parameter handling in wallet complete redirection middleware to use forEach
@@ -4,7 +4,6 @@ import createRedisHandler from '@neshca/cache-handler/redis-strings';
4
4
  import { createClient } from 'redis';
5
5
  import * as zstdWasm from '@bokuweb/zstd-wasm';
6
6
 
7
- // Color utilities for logs
8
7
  const colors = {
9
8
  red: '\x1b[31m',
10
9
  green: '\x1b[32m',
@@ -18,15 +17,12 @@ const colors = {
18
17
  };
19
18
 
20
19
  const colorLog = (color, prefix, ...args) => {
21
- // Always show compression-related logs regardless of debug flag
22
20
  // eslint-disable-next-line no-console
23
21
  console.log(`${colors.bright}${color}${prefix}${colors.reset}`, ...args);
24
22
  };
25
23
 
26
- // Initialize ZSTD immediately with IIFE (to avoid top-level await)
27
24
  let zstd;
28
25
 
29
- // Initialize immediately using IIFE
30
26
  (async () => {
31
27
  try {
32
28
  await zstdWasm.init();
@@ -51,9 +47,6 @@ const getZstd = () => {
51
47
  return zstd;
52
48
  };
53
49
 
54
- // Only ZSTD compression is used now - gzip/deflate removed for simplicity
55
-
56
- // Compression/decompression functions for cache values
57
50
  const compressValue = async (value) => {
58
51
  try {
59
52
  console_log(
@@ -63,12 +56,9 @@ const compressValue = async (value) => {
63
56
  value && typeof value === 'object' ? Object.keys(value) : 'N/A'
64
57
  );
65
58
 
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
59
  if (value && typeof value === 'object' && value.value !== undefined) {
69
60
  console_log('📦 Next.js cache entry detected, preserving structure');
70
61
 
71
- // Debug original lifespan values
72
62
  console_log('🔍 Original lifespan values:', {
73
63
  expireAge: value.lifespan?.expireAge,
74
64
  expireAt: value.lifespan?.expireAt,
@@ -84,19 +74,17 @@ const compressValue = async (value) => {
84
74
  : JSON.stringify(nestedValue);
85
75
  const originalSize = Buffer.byteLength(serializedNestedValue, 'utf8');
86
76
 
87
- // Only compress the nested value if larger than 1KB
88
77
  if (originalSize < 1024) {
89
78
  colorLog(
90
79
  colors.blue,
91
80
  '[Cache Handler] ⚡ ',
92
81
  `Skipping compression for small value (${originalSize} bytes)`
93
82
  );
94
- // Ensure tags is always an array even for small values
95
83
  const result = {
96
84
  ...value,
97
85
  tags: Array.isArray(value.tags) ? value.tags : []
98
86
  };
99
- return result; // Return original structure with guaranteed array tags
87
+ return result;
100
88
  }
101
89
 
102
90
  const zstdLib = getZstd();
@@ -109,7 +97,6 @@ const compressValue = async (value) => {
109
97
  );
110
98
  colorLog(colors.cyan, '[Cache Handler] 🔵 ', `Using ZSTD compression`);
111
99
  } else {
112
- // No fallback compression - if ZSTD fails, don't compress
113
100
  colorLog(
114
101
  colors.yellow,
115
102
  '[Cache Handler] ⚠️ ',
@@ -133,17 +120,12 @@ const compressValue = async (value) => {
133
120
  } bytes (${compressionRatio.toFixed(1)}% reduction, method: zstd)`
134
121
  );
135
122
 
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
123
  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
124
+ ...value,
125
+ tags: Array.isArray(value.tags) ? value.tags : [],
142
126
  lifespan: {
143
127
  ...value.lifespan,
144
- // Use revalidate value for expireAge to match project configuration
145
128
  expireAge: value.lifespan?.revalidate || value.lifespan?.expireAge,
146
- // Recalculate expireAt based on lastModifiedAt + revalidate
147
129
  expireAt:
148
130
  value.lifespan?.lastModifiedAt && value.lifespan?.revalidate
149
131
  ? value.lifespan.lastModifiedAt + value.lifespan.revalidate
@@ -158,7 +140,6 @@ const compressValue = async (value) => {
158
140
  }
159
141
  };
160
142
 
161
- // Debug final lifespan values to see if they changed
162
143
  console_log('🔍 Final lifespan values after compression and fix:', {
163
144
  expireAge: result.lifespan?.expireAge,
164
145
  expireAt: result.lifespan?.expireAt,
@@ -170,7 +151,6 @@ const compressValue = async (value) => {
170
151
  changed: result.lifespan?.expireAge !== value.lifespan?.expireAge
171
152
  });
172
153
 
173
- // Additional validation for Redis handler requirements
174
154
  console_log('🔍 Compressed cache entry validation:', {
175
155
  hasLastModified: result.lastModified !== undefined,
176
156
  hasLifespan: result.lifespan !== undefined,
@@ -188,7 +168,6 @@ const compressValue = async (value) => {
188
168
  return result;
189
169
  }
190
170
 
191
- // For primitive values or non-cache-entry objects, compress directly
192
171
  const serializedValue =
193
172
  typeof value === 'string' ? value : JSON.stringify(value);
194
173
  const originalSize = Buffer.byteLength(serializedValue, 'utf8');
@@ -199,8 +178,7 @@ const compressValue = async (value) => {
199
178
  '[Cache Handler] ⚡ ',
200
179
  `Skipping compression for small value (${originalSize} bytes)`
201
180
  );
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)
181
+
204
182
  if (
205
183
  value &&
206
184
  typeof value === 'object' &&
@@ -208,17 +186,15 @@ const compressValue = async (value) => {
208
186
  value.lifespan === undefined &&
209
187
  value.value === undefined
210
188
  ) {
211
- // This is a primitive object, not a Next.js cache entry
212
189
  return {
213
190
  ...value,
214
- tags: value.tags || [], // Preserve existing tags or add empty array
191
+ tags: value.tags || [],
215
192
  lastModified: Date.now(),
216
193
  lifespan: {
217
194
  expireAt: Math.floor(Date.now() / 1000) + 3600
218
195
  }
219
196
  };
220
197
  }
221
- // Fix expireAge for Next.js cache entries to match project's revalidate value
222
198
  if (
223
199
  value &&
224
200
  typeof value === 'object' &&
@@ -229,9 +205,7 @@ const compressValue = async (value) => {
229
205
  ...value,
230
206
  lifespan: {
231
207
  ...value.lifespan,
232
- // Use revalidate value for expireAge to match project configuration
233
208
  expireAge: value.lifespan.revalidate,
234
- // Recalculate expireAt based on lastModifiedAt + revalidate
235
209
  expireAt:
236
210
  value.lifespan.lastModifiedAt && value.lifespan.revalidate
237
211
  ? value.lifespan.lastModifiedAt + value.lifespan.revalidate
@@ -239,7 +213,6 @@ const compressValue = async (value) => {
239
213
  }
240
214
  };
241
215
  }
242
- // Return other values unchanged
243
216
  return value;
244
217
  }
245
218
 
@@ -250,14 +223,11 @@ const compressValue = async (value) => {
250
223
  compressed = zstdLib.compress(Buffer.from(serializedValue, 'utf8'), 3);
251
224
  colorLog(colors.cyan, '[Cache Handler] 🔵 ', `Using ZSTD compression`);
252
225
  } else {
253
- // No fallback compression - if ZSTD fails, don't compress
254
226
  colorLog(
255
227
  colors.yellow,
256
228
  '[Cache Handler] ⚠️ ',
257
229
  `ZSTD not available, storing uncompressed`
258
230
  );
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
231
  if (
262
232
  value &&
263
233
  typeof value === 'object' &&
@@ -265,17 +235,15 @@ const compressValue = async (value) => {
265
235
  value.lifespan === undefined &&
266
236
  value.value === undefined
267
237
  ) {
268
- // This is a primitive object, not a Next.js cache entry
269
238
  return {
270
239
  ...value,
271
- tags: value.tags || [], // Preserve existing tags or add empty array
240
+ tags: value.tags || [],
272
241
  lastModified: Date.now(),
273
242
  lifespan: {
274
243
  expireAt: Math.floor(Date.now() / 1000) + 3600
275
244
  }
276
245
  };
277
246
  }
278
- // Return Next.js cache entries and other values unchanged
279
247
  return value;
280
248
  }
281
249
 
@@ -291,17 +259,15 @@ const compressValue = async (value) => {
291
259
  } bytes (${compressionRatio.toFixed(1)}% reduction, method: zstd)`
292
260
  );
293
261
 
294
- // For compressed non-Next.js entries, ensure Redis compatibility
295
262
  const compressedResult = {
296
263
  __compressed: true,
297
264
  __method: 'zstd',
298
265
  __originalSize: originalSize,
299
266
  __compressedSize: compressed.length,
300
267
  __data: compressedBase64,
301
- // Add required Redis fields only for truly primitive values (not cache entries)
302
- tags: [], // Required array for Redis handler
268
+ tags: [],
303
269
  lastModified: Date.now(),
304
- lifespan: { expireAt: Math.floor(Date.now() / 1000) + 3600 } // Default 1 hour for primitive values only
270
+ lifespan: { expireAt: Math.floor(Date.now() / 1000) + 3600 }
305
271
  };
306
272
 
307
273
  console_log('🔍 Non-Next.js compressed entry validation:', {
@@ -318,7 +284,7 @@ const compressValue = async (value) => {
318
284
  '[Cache Handler] Compression failed, storing uncompressed:',
319
285
  error.message
320
286
  );
321
- return value; // Return original value on error
287
+ return value;
322
288
  }
323
289
  };
324
290
 
@@ -333,7 +299,6 @@ const decompressValue = async (compressedData) => {
333
299
  : 'N/A'
334
300
  );
335
301
 
336
- // Check if this is a Next.js cache entry with compressed nested value
337
302
  if (
338
303
  compressedData &&
339
304
  typeof compressedData === 'object' &&
@@ -365,7 +330,6 @@ const decompressValue = async (compressedData) => {
365
330
  throw new Error('zstd not available for decompression');
366
331
  }
367
332
  } else {
368
- // Legacy support for gzip compressed data, but warn about it
369
333
  colorLog(
370
334
  colors.yellow,
371
335
  '[Cache Handler] ⚠️ ',
@@ -382,14 +346,12 @@ const decompressValue = async (compressedData) => {
382
346
  `Decompressed nested value ${compressedNestedValue.__compressedSize} → ${decompressed.length} bytes (method: zstd)`
383
347
  );
384
348
 
385
- // Restore the original cache entry structure with decompressed nested value
386
349
  return {
387
- ...compressedData, // Preserve all metadata (lastModified, expiresAt, tags, etc.)
388
- value: JSON.parse(decompressed) // Restore original nested value
350
+ ...compressedData,
351
+ value: JSON.parse(decompressed)
389
352
  };
390
353
  }
391
354
 
392
- // Check if this is a directly compressed value
393
355
  if (
394
356
  compressedData &&
395
357
  typeof compressedData === 'object' &&
@@ -413,7 +375,6 @@ const decompressValue = async (compressedData) => {
413
375
  throw new Error('zstd not available for decompression');
414
376
  }
415
377
  } else {
416
- // Legacy support for gzip compressed data, but warn about it
417
378
  colorLog(
418
379
  colors.yellow,
419
380
  '[Cache Handler] ⚠️ ',
@@ -433,16 +394,14 @@ const decompressValue = async (compressedData) => {
433
394
  return JSON.parse(decompressed);
434
395
  }
435
396
 
436
- // No compression detected, return as-is
437
397
  console_log('✨ No compression detected, returning value as-is');
438
398
  return compressedData;
439
399
  } catch (error) {
440
400
  console.warn('[Cache Handler] Decompression failed:', error.message);
441
- return compressedData; // Return original data on error
401
+ return compressedData;
442
402
  }
443
403
  };
444
404
 
445
- // Cache configuration
446
405
  const CACHE_CONFIG = {
447
406
  lru: {
448
407
  maxItemCount: 2000
@@ -461,7 +420,6 @@ const CACHE_CONFIG = {
461
420
  version: process.env.ACC_APP_VERSION || ''
462
421
  };
463
422
 
464
- // Use global to persist across module reloads in development
465
423
  const globalForRedis = global;
466
424
  if (!globalForRedis.redisClient) {
467
425
  globalForRedis.redisClient = null;
@@ -470,7 +428,6 @@ if (!globalForRedis.redisClient) {
470
428
  globalForRedis.connectionAttempts = 0;
471
429
  }
472
430
 
473
- // Logging configuration
474
431
  const debugValue = process.env.NEXT_PRIVATE_DEBUG_CACHE;
475
432
  const debug = debugValue === 'true' || debugValue === '1';
476
433
 
@@ -483,19 +440,16 @@ if (debug) {
483
440
  }
484
441
 
485
442
  async function getRedisClient() {
486
- // If client exists and is ready, return it
487
443
  if (globalForRedis.redisClient?.isReady) {
488
444
  console_log('Reusing existing Redis connection');
489
445
  return globalForRedis.redisClient;
490
446
  }
491
447
 
492
- // If we're already connecting, wait a bit and retry
493
448
  if (globalForRedis.isConnecting) {
494
449
  await new Promise((resolve) => setTimeout(resolve, 100));
495
450
  return getRedisClient();
496
451
  }
497
452
 
498
- // Start new connection
499
453
  globalForRedis.isConnecting = true;
500
454
  globalForRedis.connectionAttempts++;
501
455
 
@@ -526,7 +480,6 @@ async function getRedisClient() {
526
480
  });
527
481
 
528
482
  redisClient.on('error', (error) => {
529
- // Only log the first connection error to avoid spam
530
483
  if (!globalForRedis.hasLoggedConnectionError) {
531
484
  if (error.code === 'ECONNREFUSED') {
532
485
  console.warn(
@@ -581,7 +534,6 @@ CacheHandler.onCreation(async () => {
581
534
  'Starting cache handler initialization...'
582
535
  );
583
536
 
584
- // Force log to appear even if debug is off
585
537
  colorLog(
586
538
  colors.green,
587
539
  '[Cache Handler] 🚀 ',
@@ -616,7 +568,6 @@ CacheHandler.onCreation(async () => {
616
568
  'Redis client connected successfully'
617
569
  );
618
570
  } catch (error) {
619
- // Error already logged in getRedisClient, just return local handler
620
571
  colorLog(
621
572
  colors.red,
622
573
  '[Cache Handler] ❌ ',
@@ -630,7 +581,7 @@ CacheHandler.onCreation(async () => {
630
581
  const redisHandler = createRedisHandler({
631
582
  client,
632
583
  timeoutMs: CACHE_CONFIG.redis.timeoutMs,
633
- keyExpirationStrategy: 'EXPIREAT' // Use EXPIREAT for Redis 6.0.x compatibility
584
+ keyExpirationStrategy: 'EXPIREAT'
634
585
  });
635
586
 
636
587
  const localHandler = createLruHandler(CACHE_CONFIG.lru);
@@ -638,32 +589,27 @@ CacheHandler.onCreation(async () => {
638
589
  const CACHE_VERSION = 'v2';
639
590
  const versionPrefix = `${CACHE_VERSION}_`;
640
591
 
641
- // Create optimized functions for each scenario
642
592
  const versionKeyString = (key) => `${versionPrefix}${key}`;
643
593
  const versionKeyObject = (key) => ({
644
594
  ...key,
645
595
  key: `${versionPrefix}${key.key}`
646
596
  });
647
597
 
648
- // Main version key function that routes to optimized paths
649
598
  const versionKey = (key) => {
650
599
  return typeof key === 'string'
651
600
  ? versionKeyString(key)
652
601
  : versionKeyObject(key);
653
602
  };
654
603
 
655
- // Create a custom handler that checks local first, then Redis
656
604
  const customHandler = {
657
605
  name: 'custom-local-then-redis',
658
606
  get: async (key, context) => {
659
607
  const vKey = versionKey(key);
660
608
  const keyStr = typeof vKey === 'string' ? vKey : vKey?.key;
661
609
 
662
- // Force log GET calls to see if handler is being used
663
610
  colorLog(colors.white, '[Cache Handler] 🔍 ', `CACHE GET: ${keyStr}`);
664
611
  console_log('GET called for key:', keyStr);
665
612
 
666
- // Extract and log URL information for better debugging
667
613
  if (keyStr) {
668
614
  const urlMatch = keyStr.match(/https?%3A%2F%2F[^_]+|\/[^_]*$/);
669
615
  const extractedUrl = urlMatch
@@ -676,13 +622,11 @@ CacheHandler.onCreation(async () => {
676
622
  );
677
623
  }
678
624
 
679
- // Check local cache first
680
625
  console_log('Checking local cache...');
681
626
  const localResult = await localHandler.get(vKey, context);
682
627
 
683
628
  if (localResult) {
684
629
  console_log('Found in local cache');
685
- // Check if it's compressed data (new format or legacy format)
686
630
  if (
687
631
  localResult &&
688
632
  typeof localResult === 'object' &&
@@ -692,7 +636,6 @@ CacheHandler.onCreation(async () => {
692
636
  ) {
693
637
  try {
694
638
  const decompressed = await decompressValue(localResult);
695
- // If it's a string, parse it; otherwise return as-is (it's already parsed)
696
639
  return typeof decompressed === 'string'
697
640
  ? JSON.parse(decompressed)
698
641
  : decompressed;
@@ -705,7 +648,6 @@ CacheHandler.onCreation(async () => {
705
648
  }
706
649
  }
707
650
 
708
- // Debug info for non-compressed values
709
651
  console_log('🔍 Local result type:', typeof localResult);
710
652
  if (
711
653
  localResult &&
@@ -726,7 +668,6 @@ CacheHandler.onCreation(async () => {
726
668
 
727
669
  let finalResult = redisResult;
728
670
 
729
- // Parse JSON if it's a string (Redis stores as JSON)
730
671
  if (typeof redisResult === 'string') {
731
672
  try {
732
673
  finalResult = JSON.parse(redisResult);
@@ -740,7 +681,6 @@ CacheHandler.onCreation(async () => {
740
681
  }
741
682
  }
742
683
 
743
- // Check if it's compressed data and decompress (new format or legacy format)
744
684
  if (
745
685
  finalResult &&
746
686
  typeof finalResult === 'object' &&
@@ -750,7 +690,6 @@ CacheHandler.onCreation(async () => {
750
690
  ) {
751
691
  try {
752
692
  const decompressed = await decompressValue(finalResult);
753
- // If it's a string, parse it; otherwise return as-is (it's already parsed)
754
693
  finalResult =
755
694
  typeof decompressed === 'string'
756
695
  ? JSON.parse(decompressed)
@@ -760,11 +699,9 @@ CacheHandler.onCreation(async () => {
760
699
  '[Cache Handler] Failed to decompress Redis cache value:',
761
700
  error.message
762
701
  );
763
- // finalResult stays as is
764
702
  }
765
703
  }
766
704
 
767
- // Debug Redis result
768
705
  console_log('🔍 Redis result type:', typeof redisResult);
769
706
  console_log('🔍 Final result type:', typeof finalResult);
770
707
  if (
@@ -775,7 +712,6 @@ CacheHandler.onCreation(async () => {
775
712
  console_log('🔍 Final result keys:', Object.keys(finalResult));
776
713
  }
777
714
 
778
- // Sync back to local cache for faster future access
779
715
  try {
780
716
  await localHandler.set(vKey, finalResult, context);
781
717
  console_log('Synced to local cache');
@@ -795,11 +731,9 @@ CacheHandler.onCreation(async () => {
795
731
  const vKey = versionKey(key);
796
732
  const keyStr = typeof vKey === 'string' ? vKey : vKey?.key;
797
733
 
798
- // Force log SET calls to see if handler is being used
799
734
  colorLog(colors.white, '[Cache Handler] 💾 ', `CACHE SET: ${keyStr}`);
800
735
  console_log('SET called for key:', keyStr);
801
736
 
802
- // Extract and log URL information for better debugging
803
737
  if (keyStr) {
804
738
  const urlMatch = keyStr.match(/https?%3A%2F%2F[^_]+|\/[^_]*$/);
805
739
  const extractedUrl = urlMatch
@@ -812,7 +746,6 @@ CacheHandler.onCreation(async () => {
812
746
  );
813
747
  }
814
748
 
815
- // Log context and value information for debugging
816
749
  console_log('SET Debug Info:', {
817
750
  contextKeys: context ? Object.keys(context) : 'no context',
818
751
  valueKeys:
@@ -834,7 +767,6 @@ CacheHandler.onCreation(async () => {
834
767
  : 'unknown'
835
768
  });
836
769
 
837
- // Debug original value TTL before any processing
838
770
  colorLog(
839
771
  colors.yellow,
840
772
  '[Cache Handler] 🔍 ',
@@ -850,7 +782,6 @@ CacheHandler.onCreation(async () => {
850
782
  }
851
783
  );
852
784
 
853
- // Try to compress the value first
854
785
  let compressedValue;
855
786
  let shouldUseCompressed = false;
856
787
 
@@ -862,7 +793,6 @@ CacheHandler.onCreation(async () => {
862
793
  );
863
794
  compressedValue = await compressValue(value);
864
795
 
865
- // Debug compressed value TTL after compression
866
796
  colorLog(
867
797
  colors.yellow,
868
798
  '[Cache Handler] 🔍 ',
@@ -878,7 +808,6 @@ CacheHandler.onCreation(async () => {
878
808
  }
879
809
  );
880
810
 
881
- // Check if compression actually happened (compressValue returns original if no compression)
882
811
  shouldUseCompressed =
883
812
  compressedValue !== value &&
884
813
  (compressedValue?.__compressed ||
@@ -905,14 +834,9 @@ CacheHandler.onCreation(async () => {
905
834
  shouldUseCompressed = false;
906
835
  }
907
836
 
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
837
  let redisSetResult;
913
838
 
914
839
  if (shouldUseCompressed) {
915
- // Log before attempting Redis SET with compression
916
840
  colorLog(
917
841
  colors.blue,
918
842
  '[Cache Handler] 💾 ',
@@ -920,7 +844,6 @@ CacheHandler.onCreation(async () => {
920
844
  );
921
845
 
922
846
  try {
923
- // Debug what we're sending to Redis
924
847
  colorLog(
925
848
  colors.yellow,
926
849
  '[Cache Handler] 🔍 ',
@@ -950,9 +873,7 @@ CacheHandler.onCreation(async () => {
950
873
  compressionError.message
951
874
  );
952
875
 
953
- // Fallback to original uncompressed value
954
876
  try {
955
- // Debug what we're sending to Redis as fallback
956
877
  colorLog(
957
878
  colors.yellow,
958
879
  '[Cache Handler] 🔍 ',
@@ -985,7 +906,6 @@ CacheHandler.onCreation(async () => {
985
906
  }
986
907
  }
987
908
  } else {
988
- // No compression, use original value directly
989
909
  colorLog(
990
910
  colors.blue,
991
911
  '[Cache Handler] 💾 ',
@@ -993,7 +913,6 @@ CacheHandler.onCreation(async () => {
993
913
  );
994
914
 
995
915
  try {
996
- // Debug what we're sending to Redis (no compression)
997
916
  colorLog(
998
917
  colors.yellow,
999
918
  '[Cache Handler] 🔍 ',
@@ -1026,7 +945,6 @@ CacheHandler.onCreation(async () => {
1026
945
  }
1027
946
  }
1028
947
 
1029
- // Always set to local cache with original value
1030
948
  let localSetResult;
1031
949
  try {
1032
950
  await localHandler.set(vKey, value, context);
@@ -1037,7 +955,6 @@ CacheHandler.onCreation(async () => {
1037
955
 
1038
956
  const results = [localSetResult, redisSetResult];
1039
957
 
1040
- // Log results
1041
958
  console_log('SET Results:', {
1042
959
  local: results[0].status,
1043
960
  redis: results[1].status,
package/lib/cache.ts CHANGED
@@ -5,124 +5,62 @@ import { CacheOptions } from '../types';
5
5
  import logger from '../utils/log';
6
6
  const CACHE_VERSION = 'v2';
7
7
 
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
- };
8
+ const compressData = async (data: string): Promise<Uint8Array> => {
9
+ const stream = new CompressionStream('gzip');
10
+ const writer = stream.writable.getWriter();
11
+ const reader = stream.readable.getReader();
19
12
 
20
- const colorLog = (color: string, prefix: string, ...args: any[]) => {
21
- // eslint-disable-next-line no-console
22
- console.log(`${colors.bright}${color}${prefix}${colors.reset}`, ...args);
23
- };
13
+ writer.write(new TextEncoder().encode(data));
14
+ writer.close();
24
15
 
25
- colorLog(
26
- colors.green,
27
- '[Cache] 🚀 ',
28
- 'Web API compression initialized successfully at module load'
29
- );
30
- logger.debug('Web API compression initialized successfully at module load');
31
-
32
- const extractUrlFromKey = (key: string): string => {
33
- try {
34
- const decoded = decodeURIComponent(key);
35
- const urlMatch =
36
- decoded.match(/https?:\/\/[^\s]+/) || decoded.match(/\/[^\s]*/);
37
- return urlMatch ? urlMatch[0] : 'unknown';
38
- } catch {
39
- return 'unknown';
40
- }
41
- };
16
+ const chunks: Uint8Array[] = [];
17
+ let done = false;
42
18
 
43
- // Web API compression for edge runtime compatibility
44
- const compressData = async (data: string): Promise<Uint8Array> => {
45
- if (typeof CompressionStream !== 'undefined') {
46
- // Use Web API compression (edge runtime)
47
- const stream = new CompressionStream('gzip');
48
- const writer = stream.writable.getWriter();
49
- const reader = stream.readable.getReader();
50
-
51
- writer.write(new TextEncoder().encode(data));
52
- writer.close();
53
-
54
- const chunks: Uint8Array[] = [];
55
- let done = false;
56
-
57
- while (!done) {
58
- const { value, done: readerDone } = await reader.read();
59
- done = readerDone;
60
- if (value) chunks.push(value);
61
- }
19
+ while (!done) {
20
+ const { value, done: readerDone } = await reader.read();
21
+ done = readerDone;
22
+ if (value) chunks.push(value);
23
+ }
62
24
 
63
- const totalLength = chunks.reduce((sum, chunk) => sum + chunk.length, 0);
64
- const result = new Uint8Array(totalLength);
65
- let offset = 0;
25
+ const totalLength = chunks.reduce((sum, chunk) => sum + chunk.length, 0);
26
+ const result = new Uint8Array(totalLength);
27
+ let offset = 0;
66
28
 
67
- for (const chunk of chunks) {
68
- result.set(chunk, offset);
69
- offset += chunk.length;
70
- }
71
-
72
- return result;
73
- } else {
74
- // Node.js fallback (server runtime)
75
- const { deflate } = await import('zlib');
76
- const { promisify } = await import('util');
77
- const deflateAsync = promisify(deflate);
78
- const inputBuffer = Buffer.from(data, 'utf8');
79
- const compressed = await deflateAsync(inputBuffer);
80
- return new Uint8Array(
81
- compressed.buffer,
82
- compressed.byteOffset,
83
- compressed.byteLength
84
- );
29
+ for (const chunk of chunks) {
30
+ result.set(chunk, offset);
31
+ offset += chunk.length;
85
32
  }
33
+
34
+ return result;
86
35
  };
87
36
 
88
- // Web API decompression for edge runtime compatibility
89
37
  const decompressData = async (compressed: Uint8Array): Promise<string> => {
90
- if (typeof DecompressionStream !== 'undefined') {
91
- // Use Web API decompression (edge runtime)
92
- const stream = new DecompressionStream('gzip');
93
- const writer = stream.writable.getWriter();
94
- const reader = stream.readable.getReader();
95
-
96
- writer.write(compressed);
97
- writer.close();
98
-
99
- const chunks: Uint8Array[] = [];
100
- let done = false;
101
-
102
- while (!done) {
103
- const { value, done: readerDone } = await reader.read();
104
- done = readerDone;
105
- if (value) chunks.push(value);
106
- }
38
+ const stream = new DecompressionStream('gzip');
39
+ const writer = stream.writable.getWriter();
40
+ const reader = stream.readable.getReader();
107
41
 
108
- const totalLength = chunks.reduce((sum, chunk) => sum + chunk.length, 0);
109
- const result = new Uint8Array(totalLength);
110
- let offset = 0;
42
+ writer.write(compressed);
43
+ writer.close();
111
44
 
112
- for (const chunk of chunks) {
113
- result.set(chunk, offset);
114
- offset += chunk.length;
115
- }
45
+ const chunks: Uint8Array[] = [];
46
+ let done = false;
47
+
48
+ while (!done) {
49
+ const { value, done: readerDone } = await reader.read();
50
+ done = readerDone;
51
+ if (value) chunks.push(value);
52
+ }
116
53
 
117
- return new TextDecoder().decode(result);
118
- } else {
119
- // Node.js fallback (server runtime)
120
- const { inflate } = await import('zlib');
121
- const { promisify } = await import('util');
122
- const inflateAsync = promisify(inflate);
123
- const decompressed = await inflateAsync(Buffer.from(compressed));
124
- return decompressed.toString('utf8');
54
+ const totalLength = chunks.reduce((sum, chunk) => sum + chunk.length, 0);
55
+ const result = new Uint8Array(totalLength);
56
+ let offset = 0;
57
+
58
+ for (const chunk of chunks) {
59
+ result.set(chunk, offset);
60
+ offset += chunk.length;
125
61
  }
62
+
63
+ return new TextDecoder().decode(result);
126
64
  };
127
65
 
128
66
  const hashCacheKey = (object?: Record<string, string>) => {
@@ -255,9 +193,7 @@ export class Cache {
255
193
  } else {
256
194
  value = null;
257
195
  }
258
- logger.debug('Redis get success', { key, value });
259
196
  } catch (error) {
260
- logger.error('Redis get error', { key, error });
261
197
  value = null;
262
198
  } finally {
263
199
  if (client) {
@@ -283,9 +219,7 @@ export class Cache {
283
219
  }
284
220
 
285
221
  success = true;
286
- logger.debug('Redis set success', { key, value });
287
222
  } catch (error) {
288
- logger.error('Redis set error', { key, error });
289
223
  success = false;
290
224
  } finally {
291
225
  if (client) {
@@ -325,8 +259,6 @@ export class Cache {
325
259
  _options.expire = 120;
326
260
  }
327
261
 
328
- logger.debug('Cache wrap', { key, formattedKey, _options });
329
-
330
262
  if (_options.cache) {
331
263
  let cachedValue: any;
332
264
 
@@ -339,8 +271,6 @@ export class Cache {
339
271
  }
340
272
 
341
273
  cachedValue = await Cache.proxyRequest('POST', body);
342
- logger.debug('Cache proxy request success', { key });
343
- logger.trace('Cache proxy request', { key, cachedValue });
344
274
  } else {
345
275
  cachedValue = _options.compressed
346
276
  ? await Cache.getCompressed(formattedKey)
@@ -352,8 +282,6 @@ export class Cache {
352
282
  }
353
283
  }
354
284
 
355
- logger.debug('Redis cache miss. Setting new value...', { key });
356
-
357
285
  const data = await handler();
358
286
 
359
287
  if (data && _options.cache) {
@@ -371,8 +299,6 @@ export class Cache {
371
299
  body.append('compressed', 'true');
372
300
  }
373
301
  await Cache.proxyRequest('PUT', body);
374
-
375
- logger.debug('Cache proxy request', { key, body: body.toString() });
376
302
  } catch (error) {
377
303
  logger.error('Cache proxy error', error);
378
304
  }
@@ -440,26 +366,11 @@ export class Cache {
440
366
  results?.filter((result) => result instanceof Error) || [];
441
367
 
442
368
  if (failures.length > 0) {
443
- logger.warn('Some mset operations failed', {
444
- totalOperations: results?.length || 0,
445
- failures: failures.length,
446
- keys: Object.keys(keyValuePairs)
447
- });
448
369
  success = false;
449
370
  } else {
450
371
  success = true;
451
-
452
- logger.debug('Redis mset success', {
453
- keys: Object.keys(keyValuePairs),
454
- expire,
455
- operationsCount: Object.keys(keyValuePairs).length
456
- });
457
372
  }
458
373
  } catch (error) {
459
- logger.error('Redis mset error', {
460
- keys: Object.keys(keyValuePairs),
461
- error
462
- });
463
374
  success = false;
464
375
  } finally {
465
376
  if (client) {
@@ -476,7 +387,6 @@ export class Cache {
476
387
  expire?: number
477
388
  ): Promise<boolean> {
478
389
  if (!Cache.validateKey(key)) {
479
- logger.error('Invalid key in setCompressed', { key });
480
390
  return false;
481
391
  }
482
392
 
@@ -487,27 +397,7 @@ export class Cache {
487
397
  client = await Cache.getClient();
488
398
  const serializedValue = Cache.serializeValue(value);
489
399
 
490
- if (serializedValue.length > 1024 * 1024) {
491
- logger.warn('Large data being compressed', {
492
- key,
493
- size: serializedValue.length
494
- });
495
- }
496
-
497
- const url = extractUrlFromKey(key);
498
-
499
- logger.debug('setCompressed called', {
500
- key,
501
- url,
502
- dataSize: serializedValue.length
503
- });
504
-
505
400
  try {
506
- colorLog(
507
- colors.magenta,
508
- '[Cache] 🟣 ',
509
- `Using GZIP compression for: ${url}`
510
- );
511
401
  const compressed = await compressData(serializedValue);
512
402
  const compressedBase64 = Buffer.from(compressed).toString('base64');
513
403
 
@@ -518,35 +408,7 @@ export class Cache {
518
408
  }
519
409
 
520
410
  success = true;
521
- const compressionRatio = (
522
- (1 - compressed.length / serializedValue.length) *
523
- 100
524
- ).toFixed(1);
525
- colorLog(
526
- colors.magenta,
527
- '[Cache] 🟣 ',
528
- `Compressed ${serializedValue.length} → ${compressed.length} bytes (${compressionRatio}% reduction) for: ${url}`
529
- );
530
- logger.debug('Redis setCompressed success', {
531
- key,
532
- url,
533
- originalSize: serializedValue.length,
534
- compressedSize: compressed.length,
535
- compressionRatio: `${compressionRatio}%`,
536
- method: 'gzip'
537
- });
538
411
  } catch (compressionError) {
539
- colorLog(
540
- colors.yellow,
541
- '[Cache] ⚠️ ',
542
- `Compression failed, storing uncompressed for: ${url}`
543
- );
544
- logger.warn('Compression failed, storing uncompressed', {
545
- key,
546
- url,
547
- error: compressionError
548
- });
549
-
550
412
  if (expire) {
551
413
  await client.set(key, serializedValue, { EX: expire });
552
414
  } else {
@@ -554,14 +416,8 @@ export class Cache {
554
416
  }
555
417
 
556
418
  success = true;
557
- logger.debug('Redis set success (uncompressed)', {
558
- key,
559
- url,
560
- size: serializedValue.length
561
- });
562
419
  }
563
420
  } catch (error) {
564
- logger.error('Redis setCompressed error', { key, error });
565
421
  success = false;
566
422
  } finally {
567
423
  if (client) {
@@ -572,12 +428,8 @@ export class Cache {
572
428
  return success;
573
429
  }
574
430
 
575
- static async getCompressed(
576
- key: string,
577
- maxDecompressedSize: number = 10 * 1024 * 1024
578
- ): Promise<unknown> {
431
+ static async getCompressed(key: string): Promise<unknown> {
579
432
  if (!Cache.validateKey(key)) {
580
- logger.error('Invalid key in getCompressed', { key });
581
433
  return null;
582
434
  }
583
435
 
@@ -591,67 +443,25 @@ export class Cache {
591
443
  if (compressed) {
592
444
  const compressedBuffer = Buffer.from(compressed, 'base64');
593
445
 
594
- const url = extractUrlFromKey(key);
595
-
596
446
  try {
597
- colorLog(
598
- colors.magenta,
599
- '[Cache] 🟣 ',
600
- `Using GZIP decompression for: ${url}`
601
- );
602
447
  const decompressedString = await decompressData(
603
448
  new Uint8Array(compressedBuffer)
604
449
  );
605
450
  value = JSON.parse(decompressedString);
606
- colorLog(
607
- colors.magenta,
608
- '[Cache] 🟣 ',
609
- `Decompressed ${compressedBuffer.length} → ${decompressedString.length} bytes for: ${url}`
610
- );
611
- logger.debug('Redis getCompressed success', {
612
- key,
613
- url,
614
- compressedSize: compressedBuffer.length,
615
- decompressedSize: decompressedString.length,
616
- method: 'gzip'
617
- });
618
451
  return value;
619
452
  } catch (decompressionError) {
620
- colorLog(
621
- colors.yellow,
622
- '[Cache] ⚠️ ',
623
- `Decompression failed, trying uncompressed for: ${url}`
624
- );
625
- logger.debug('Decompression failed, trying direct JSON parse', {
626
- key,
627
- url,
628
- error: decompressionError
629
- });
630
-
631
453
  try {
632
454
  const rawString = compressed;
633
455
  const parsedData = JSON.parse(rawString);
634
- logger.debug('Data read as uncompressed', {
635
- key,
636
- url
637
- });
638
456
  return parsedData;
639
457
  } catch (jsonError) {
640
- logger.error('Failed to parse data in all formats', {
641
- key,
642
- url,
643
- decompressionError,
644
- jsonError
645
- });
646
458
  return null;
647
459
  }
648
460
  }
649
461
  } else {
650
462
  value = null;
651
- logger.debug('Redis getCompressed: key not found', { key });
652
463
  }
653
464
  } catch (error) {
654
- logger.error('Redis getCompressed error', { key, error });
655
465
  value = null;
656
466
  } finally {
657
467
  if (client) {
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-snapshot-ZERO-3586-20250828143733",
4
+ "version": "1.93.0-snapshot-ZERO-3586-20250901110545",
5
5
  "private": false,
6
6
  "license": "MIT",
7
7
  "bin": {
@@ -35,7 +35,7 @@
35
35
  "set-cookie-parser": "2.6.0"
36
36
  },
37
37
  "devDependencies": {
38
- "@akinon/eslint-plugin-projectzero": "1.93.0-snapshot-ZERO-3586-20250828143733",
38
+ "@akinon/eslint-plugin-projectzero": "1.93.0-snapshot-ZERO-3586-20250901110545",
39
39
  "@babel/core": "7.26.10",
40
40
  "@babel/preset-env": "7.26.9",
41
41
  "@babel/preset-typescript": "7.27.0",