/** * ETag generation utilities */ import type * as plugins from '../plugins.js'; /** * Generate ETag from file stats * Uses weak ETag format: W/"size-mtime" */ export function generateETag(stat: { size: number; mtime: Date }): string { const mtime = stat.mtime.getTime().toString(16); const size = stat.size.toString(16); return `W/"${size}-${mtime}"`; } /** * Generate strong ETag from content * Uses hash of content */ export async function generateStrongETag(content: Uint8Array): Promise { const hashBuffer = await crypto.subtle.digest('SHA-256', content as unknown as ArrayBuffer); const hashArray = Array.from(new Uint8Array(hashBuffer)); const hashHex = hashArray.map(b => b.toString(16).padStart(2, '0')).join(''); return `"${hashHex.slice(0, 32)}"`; } /** * Check if ETag matches */ export function matchesETag(etag: string, ifNoneMatch: string | null): boolean { if (!ifNoneMatch) return false; // Handle multiple ETags const etags = ifNoneMatch.split(',').map(e => e.trim()); // Wildcard match if (etags.includes('*')) return true; // Weak comparison (ignore W/ prefix) const normalizeETag = (e: string) => e.replace(/^W\//, ''); const normalizedETag = normalizeETag(etag); return etags.some(e => normalizeETag(e) === normalizedETag); }