Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 | 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 45x 45x 45x 45x 45x 1x 1x 1x 1x 9x 9x 9x 9x 9x 1x 1x 6x 7x 2x 2x 2x 9x 1x 1x 1x 1x 39x 39x 39x 10494x 10494x 10494x 10494x 39x 39x 1x 1x 1x 1x 1x 45x 45x 45x 45x 45x 45x 45x 45x 6x 6x 6x 6x 6x 39x 39x 39x 39x 39x 39x 39x 39x 39x 39x 39x 39x 39x 45x 45x 1x 1x 1x 1x 1x 10x 10x 10x 10x 10x 10x 9x 9x 10x 10x 1x 1x 1x 1x 5x 5x 5x 5x 5x 5x 5x 5x 1x 1x 1x 1x 1x 2x 2x 2x 2x 2x 2x 2x 1x 1x 1x 1x 1x 1x 1x 1x 2x 2x 2x 2x 1x 1x 1x 1x 40x 40x 40x 40x 40x 40x 40x 40x 40x 40x 1x 1x 1x 1x 1x 2x 2x 2x 2x 2x 2x 2x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x | import Cookies from 'js-cookie';
import { CartItem } from '@/redux/features/cartSlice';
import { logger } from '@/lib/logging';
// Cookie configuration
const CART_COOKIE_NAME = 'cart_items_v1';
const CART_VERSION_COOKIE = 'cart_version_hash';
const ANONYMOUS_SESSION_COOKIE = 'anonymous_session_id';
const CART_COOKIE_EXPIRY_DAYS = 7;
const SESSION_COOKIE_EXPIRY_DAYS = 30;
const MAX_COOKIE_SIZE = 4096; // 4KB limit
/**
* Serialize cart items to JSON with error handling
*/
function serializeCart(items: CartItem[]): string | null {
try {
return JSON.stringify(items);
} catch (error) {
logger.error('Failed to serialize cart', error instanceof Error ? error : new Error(String(error)), { category: 'CART' });
return null;
}
}
/**
* Deserialize cart items from JSON with error handling
*/
function deserializeCart(json: string): CartItem[] | null {
try {
const items = JSON.parse(json);
// Validate structure
if (!Array.isArray(items)) {
return null;
}
return items as CartItem[];
} catch (error) {
logger.error('Failed to deserialize cart', error instanceof Error ? error : new Error(String(error)), { category: 'CART' });
return null;
}
}
/**
* Generate a simple hash for version tracking
*/
function generateHash(str: string): string {
let hash = 0;
for (let i = 0; i < str.length; i++) {
const char = str.charCodeAt(i);
hash = ((hash << 5) - hash) + char;
hash = hash & hash;
}
return Math.abs(hash).toString(16);
}
/**
* Set cart items in cookie
* Returns true if successful, false if cart exceeds size limit
*/
export function setCartCookie(items: CartItem[]): boolean {
if (typeof window === 'undefined') return false;
const serialized = serializeCart(items);
if (!serialized) return false;
// Check size limit
if (serialized.length > MAX_COOKIE_SIZE) {
logger.warn('Cart exceeds 4KB cookie size limit. Truncating...', { category: 'CART' });
// Attempt to save a subset of items
const truncatedItems = items.slice(0, Math.floor(items.length / 2));
return setCartCookie(truncatedItems);
}
try {
// Set cart cookie
Cookies.set(CART_COOKIE_NAME, serialized, {
expires: CART_COOKIE_EXPIRY_DAYS,
sameSite: 'strict',
secure: process.env.NODE_ENV === 'production'});
// Set version hash for conflict detection
const hash = generateHash(serialized);
setCartVersion(hash);
return true;
} catch (error) {
logger.error(
'Failed to set cart cookie',
error instanceof Error ? error : new Error(String(error)),
{ category: 'CART' }
);
return false;
}
}
/**
* Get cart items from cookie
* Returns null if cookie doesn't exist or is invalid
*/
export function getCartCookie(): CartItem[] | null {
if (typeof window === 'undefined') return null;
try {
const cookieValue = Cookies.get(CART_COOKIE_NAME);
if (!cookieValue) return null;
return deserializeCart(cookieValue);
} catch (error) {
logger.error(
'Failed to get cart cookie',
error instanceof Error ? error : new Error(String(error)),
{ category: 'CART' }
);
return null;
}
}
/**
* Delete cart cookie
*/
export function deleteCartCookie(): void {
if (typeof window === 'undefined') return;
try {
Cookies.remove(CART_COOKIE_NAME);
Cookies.remove(CART_VERSION_COOKIE);
} catch (error) {
logger.error('Failed to delete cart cookie', error instanceof Error ? error : new Error(String(error)), { category: 'CART' });
}
}
/**
* Get or create anonymous session ID
* Used for analytics and tracking
*/
export function getAnonymousSessionId(): string {
if (typeof window === 'undefined') return 'server';
try {
let sessionId = Cookies.get(ANONYMOUS_SESSION_COOKIE);
if (!sessionId) {
// Generate unique session ID
sessionId = `anon_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
Cookies.set(ANONYMOUS_SESSION_COOKIE, sessionId, {
expires: SESSION_COOKIE_EXPIRY_DAYS,
sameSite: 'strict',
secure: process.env.NODE_ENV === 'production'});
}
return sessionId;
} catch (error) {
logger.error('Failed to get anonymous session ID', error instanceof Error ? error : new Error(String(error)), { category: 'CART' });
return 'unknown';
}
}
/**
* Set cart version hash for conflict detection
*/
export function setCartVersion(hash: string): void {
if (typeof window === 'undefined') return;
try {
Cookies.set(CART_VERSION_COOKIE, hash, {
expires: CART_COOKIE_EXPIRY_DAYS,
sameSite: 'strict',
secure: process.env.NODE_ENV === 'production'});
} catch (error) {
logger.error('Failed to set cart version', error instanceof Error ? error : new Error(String(error)), { category: 'CART' });
}
}
/**
* Get cart version hash
* Returns null if not set
*/
export function getCartVersion(): string | null {
if (typeof window === 'undefined') return null;
try {
return Cookies.get(CART_VERSION_COOKIE) || null;
} catch (error) {
logger.error('Failed to get cart version', error instanceof Error ? error : new Error(String(error)), { category: 'CART' });
return null;
}
}
/**
* Clear all cart-related cookies
*/
export function clearAllCartCookies(): void {
deleteCartCookie();
if (typeof window !== 'undefined') {
try {
Cookies.remove(ANONYMOUS_SESSION_COOKIE);
} catch (error) {
logger.error('Failed to clear anonymous session cookie', error instanceof Error ? error : new Error(String(error)), { category: 'CART' });
}
}
}
|