cached-middleware-fetch-next
Version:
A Next.js fetch wrapper for edge middleware that uses Vercel Runtime Cache as its caching backend
1 lines • 25.8 kB
Source Map (JSON)
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["// @ts-ignore - getCache and waitUntil are available at runtime on Vercel\nimport { getCache, waitUntil } from '@vercel/functions';\nimport type { CachedFetchOptions, CacheEntry } from './types';\n\n// Re-export types for convenience\nexport type { CachedFetchOptions, CacheEntry } from './types';\n\n/**\n * Process body for cache key generation, matching Next.js behavior\n * Note: The body is consumed here for cache key generation only.\n * The original body is preserved for the actual fetch request.\n */\nasync function processBodyForCacheKey(body: BodyInit | null | undefined): Promise<{ chunks: any[], ogBody?: BodyInit }> {\n if (!body) return { chunks: [] };\n \n // Handle Uint8Array\n if (body instanceof Uint8Array) {\n const decoder = new TextDecoder();\n const decoded = decoder.decode(body);\n return { chunks: [decoded], ogBody: body };\n }\n \n // Handle ReadableStream\n if (body instanceof ReadableStream) {\n const reader = body.getReader();\n const chunks: Uint8Array[] = [];\n \n try {\n while (true) {\n const { done, value } = await reader.read();\n if (done) break;\n if (value) chunks.push(value);\n }\n } finally {\n reader.releaseLock();\n }\n \n // Reconstruct as single Uint8Array\n const totalLength = chunks.reduce((sum, chunk) => sum + chunk.length, 0);\n const combined = new Uint8Array(totalLength);\n let offset = 0;\n for (const chunk of chunks) {\n combined.set(chunk, offset);\n offset += chunk.length;\n }\n \n const decoder = new TextDecoder();\n const decoded = decoder.decode(combined);\n return { chunks: [decoded], ogBody: combined };\n }\n \n // Handle FormData\n if (typeof FormData !== 'undefined' && body instanceof FormData) {\n const serialized: string[] = [];\n body.forEach((value, key) => {\n if (typeof value === 'string') {\n serialized.push(`${key}=${value}`);\n }\n });\n return { chunks: [serialized.join(',')] };\n }\n \n // Handle URLSearchParams\n if (typeof URLSearchParams !== 'undefined' && body instanceof URLSearchParams) {\n const serialized: string[] = [];\n body.forEach((value, key) => {\n serialized.push(`${key}=${value}`);\n });\n return { chunks: [serialized.join(',')] };\n }\n \n // Handle Blob\n if (typeof Blob !== 'undefined' && body instanceof Blob) {\n const text = await body.text();\n const newBlob = new Blob([text], { type: body.type });\n return { chunks: [text], ogBody: newBlob };\n }\n \n // Handle string\n if (typeof body === 'string') {\n return { chunks: [body] };\n }\n \n // Fallback\n return { chunks: [JSON.stringify(body)] };\n}\n\n/**\n * Convert headers to plain object and remove trace context headers\n * CRITICAL: Removes 'traceparent' and 'tracestate' headers from cache key\n * to prevent cache fragmentation in distributed tracing scenarios\n */\nfunction processHeadersForCacheKey(headers: HeadersInit | undefined): Record<string, string> {\n const headerObj: Record<string, string> = {};\n \n if (!headers) return headerObj;\n \n if (headers instanceof Headers) {\n headers.forEach((value, key) => {\n const lowerKey = key.toLowerCase();\n // Skip trace context headers\n if (lowerKey !== 'traceparent' && lowerKey !== 'tracestate') {\n headerObj[lowerKey] = value;\n }\n });\n } else if (Array.isArray(headers)) {\n headers.forEach(([key, value]) => {\n const lowerKey = key.toLowerCase();\n // Skip trace context headers\n if (lowerKey !== 'traceparent' && lowerKey !== 'tracestate') {\n headerObj[lowerKey] = value;\n }\n });\n } else {\n // Plain object\n Object.entries(headers).forEach(([key, value]) => {\n const lowerKey = key.toLowerCase();\n // Skip trace context headers\n if (lowerKey !== 'traceparent' && lowerKey !== 'tracestate') {\n headerObj[lowerKey] = value as string;\n }\n });\n }\n \n return headerObj;\n}\n\n/**\n * Generate SHA-256 hash of a string\n */\nasync function sha256(message: string): Promise<string> {\n // Check if we're in Edge runtime (crypto.subtle is available)\n if (typeof crypto !== 'undefined' && crypto.subtle && crypto.subtle.digest) {\n const encoder = new TextEncoder();\n const data = encoder.encode(message);\n const hashBuffer = await crypto.subtle.digest('SHA-256', data);\n const hashArray = Array.from(new Uint8Array(hashBuffer));\n return hashArray.map(b => b.toString(16).padStart(2, '0')).join('');\n }\n \n // Node.js runtime\n // @ts-ignore - crypto module is available in Node.js\n const { createHash } = await import('crypto');\n return createHash('sha256').update(message).digest('hex');\n}\n\n/**\n * Generate a cache key matching Next.js fetch cache behavior\n */\nasync function generateCacheKey(\n input: RequestInfo | URL,\n init?: RequestInit,\n fetchCacheKeyPrefix?: string,\n preprocessedBodyChunks?: any[]\n): Promise<string> {\n // Extract URL and create Request object for consistent processing\n const url = typeof input === 'string' ? input : input instanceof URL ? input.toString() : input.url;\n const request = new Request(url, init);\n \n // Process body\n const bodyChunks = preprocessedBodyChunks ?? (await processBodyForCacheKey(init?.body)).chunks;\n \n // Process headers (removing trace context headers)\n const headers = processHeadersForCacheKey(init?.headers);\n \n // Build cache key components in exact order\n const keyComponents = [\n 'v1', // Version prefix\n fetchCacheKeyPrefix || '',\n url,\n request.method,\n headers,\n request.mode || '',\n request.redirect || '',\n request.credentials || '',\n request.referrer || '',\n request.referrerPolicy || '',\n request.integrity || '',\n request.cache || '',\n bodyChunks\n ];\n \n // Generate hash\n const jsonString = JSON.stringify(keyComponents);\n return sha256(jsonString);\n}\n\n/**\n * Check if a cache entry has expired (not just stale)\n */\nfunction isCacheEntryExpired(entry: CacheEntry): boolean {\n if (!entry.expiresAt) {\n // No expiry set, consider valid\n return false;\n }\n \n return Date.now() > entry.expiresAt;\n}\n\n/**\n * Check if a cache entry needs revalidation\n */\nfunction needsRevalidation(entry: CacheEntry): boolean {\n if (!entry.revalidateAfter) {\n // No revalidation time set\n return false;\n }\n \n return Date.now() > entry.revalidateAfter;\n}\n\n/**\n * Convert a Response object to a serializable cache entry\n */\nasync function responseToCache(response: Response, options?: CachedFetchOptions): Promise<CacheEntry> {\n const headers: Record<string, string> = {};\n response.headers.forEach((value, key) => {\n headers[key.toLowerCase()] = value;\n });\n \n const contentType = response.headers.get('content-type') || '';\n const shouldTreatAsText = /^(text\\/|application\\/(json|javascript|xml|x-www-form-urlencoded)|image\\/svg\\+xml)/i.test(contentType);\n let data: string;\n let isBinary = false;\n if (shouldTreatAsText) {\n data = await response.text();\n } else {\n const buffer = await response.arrayBuffer();\n const bytes = new Uint8Array(buffer);\n data = toBase64(bytes);\n isBinary = true;\n }\n const now = Date.now();\n \n // Calculate revalidation and expiry times\n let revalidateAfter: number | undefined;\n let expiresAt: number | undefined;\n \n const revalidate = options?.next?.revalidate;\n const expires = options?.next?.expires;\n \n if (revalidate === false) {\n // Never revalidate, but set a default expiry of 365 days\n expiresAt = now + (365 * 24 * 60 * 60 * 1000);\n } else if (typeof revalidate === 'number' && revalidate > 0) {\n revalidateAfter = now + (revalidate * 1000);\n \n // Set expiry time\n if (expires && expires > revalidate) {\n expiresAt = now + (expires * 1000);\n } else {\n // Default expiry: 24 hours or 10x revalidate time, whichever is larger\n const defaultExpiry = Math.max(86400, revalidate * 10);\n expiresAt = now + (defaultExpiry * 1000);\n }\n }\n \n return {\n data,\n headers,\n status: response.status,\n statusText: response.statusText,\n timestamp: now,\n revalidateAfter,\n expiresAt,\n tags: options?.next?.tags,\n isBinary,\n contentType: contentType || undefined,\n };\n}\n\n/**\n * Convert a cache entry back to a Response object with cache status headers\n */\nfunction cacheToResponse(entry: CacheEntry, cacheStatus: 'HIT' | 'STALE' = 'HIT'): Response {\n const headers = new Headers(entry.headers);\n headers.delete('content-length');\n if (entry.contentType && !headers.get('content-type')) {\n headers.set('content-type', entry.contentType);\n }\n \n // Add cache status headers\n const now = Date.now();\n const cacheAge = Math.floor((now - entry.timestamp) / 1000);\n headers.set('X-Cache-Status', cacheStatus);\n headers.set('X-Cache-Age', cacheAge.toString());\n \n if (entry.expiresAt) {\n const expiresIn = Math.max(0, Math.floor((entry.expiresAt - now) / 1000));\n headers.set('X-Cache-Expires-In', expiresIn.toString());\n }\n \n let body: BodyInit | null = null;\n if ((entry as any).isBinary) {\n const bytes = fromBase64(entry.data as string);\n const ab = bytes.buffer.slice(bytes.byteOffset, bytes.byteOffset + bytes.byteLength) as unknown as ArrayBuffer;\n body = ab;\n } else {\n body = entry.data as string;\n }\n return new Response(body, {\n status: entry.status,\n statusText: entry.statusText,\n headers,\n });\n}\n\n/**\n * Clean fetch options by removing custom properties\n */\nfunction cleanFetchOptions(options?: CachedFetchOptions): RequestInit | undefined {\n if (!options) return undefined;\n \n // Create a copy without our custom properties\n const { cache, next, ...rest } = options;\n const cleanOptions: RequestInit = { ...rest };\n \n // Map our custom cache values to standard RequestCache if needed\n if (cache === 'no-store') {\n cleanOptions.cache = 'no-store';\n } else if (cache === 'force-cache') {\n cleanOptions.cache = 'force-cache';\n }\n // 'auto no cache' doesn't have a direct mapping, so we omit it\n \n return cleanOptions;\n}\n\nfunction toBase64(bytes: Uint8Array): string {\n // @ts-ignore - Buffer may be available in Node runtime\n if (typeof Buffer !== 'undefined') {\n // @ts-ignore\n return Buffer.from(bytes).toString('base64');\n }\n let binary = '';\n const chunkSize = 0x8000;\n for (let i = 0; i < bytes.length; i += chunkSize) {\n const chunk = bytes.subarray(i, i + chunkSize);\n binary += String.fromCharCode(...chunk);\n }\n // @ts-ignore - btoa available in Edge/Web runtimes\n return btoa(binary);\n}\n\nfunction fromBase64(b64: string): Uint8Array {\n // @ts-ignore - Buffer may be available in Node runtime\n if (typeof Buffer !== 'undefined') {\n // @ts-ignore\n return new Uint8Array(Buffer.from(b64, 'base64'));\n }\n // @ts-ignore - atob available in Edge/Web runtimes\n const binary = atob(b64);\n const len = binary.length;\n const bytes = new Uint8Array(len);\n for (let i = 0; i < len; i++) bytes[i] = binary.charCodeAt(i);\n return bytes;\n}\n\nfunction computeTTL(expiresAt?: number): number {\n if (!expiresAt) return 86400;\n const ttl = Math.floor((expiresAt - Date.now()) / 1000);\n return Math.max(60, ttl);\n}\n\n/**\n * A fetch wrapper that uses Vercel Runtime Cache for caching\n * Mimics Next.js Data Cache API for use in edge middleware\n */\nexport async function cachedFetch(\n input: RequestInfo | URL,\n init?: CachedFetchOptions\n): Promise<Response> {\n const url = typeof input === 'string' ? input : input instanceof URL ? input.toString() : input.url;\n const cleanOptions = cleanFetchOptions(init) || {};\n const method = (cleanOptions.method ? String(cleanOptions.method) : 'GET').toUpperCase();\n cleanOptions.method = method;\n \n // Determine cache behavior\n const cacheOption = init?.cache || 'auto no cache';\n const revalidate = init?.next?.revalidate;\n \n // Skip cache for no-store or revalidate: 0\n if (cacheOption === 'no-store' || revalidate === 0) {\n const response = await fetch(input, cleanOptions);\n \n // Clone the response to avoid body consumption issues\n const responseClone = response.clone();\n \n // Add cache status headers to indicate cache was bypassed\n const responseWithCacheHeaders = new Response(responseClone.body, {\n status: response.status,\n statusText: response.statusText,\n headers: new Headers(response.headers)\n });\n responseWithCacheHeaders.headers.set('X-Cache-Status', 'MISS');\n responseWithCacheHeaders.headers.set('X-Cache-Age', '0');\n \n return responseWithCacheHeaders;\n }\n \n // Generate cache key\n const { chunks: bodyChunks, ogBody } = await processBodyForCacheKey(init?.body);\n if (ogBody !== undefined) {\n cleanOptions.body = ogBody;\n }\n const cacheKey = await generateCacheKey(input, cleanOptions, init?.next?.fetchCacheKeyPrefix, bodyChunks);\n \n // Get Vercel Runtime Cache instance\n const cache = getCache();\n \n try {\n // Try to get from cache first\n if (cacheOption === 'force-cache' || cacheOption === 'auto no cache') {\n const cachedEntry = await cache.get(cacheKey) as CacheEntry | undefined;\n \n if (\n cachedEntry &&\n typeof cachedEntry.status === 'number' &&\n cachedEntry.data !== undefined &&\n cachedEntry.headers &&\n !isCacheEntryExpired(cachedEntry)\n ) {\n // Check if we need to revalidate in the background\n const isStale = needsRevalidation(cachedEntry);\n if (isStale) {\n // Return stale data immediately and refresh in background (SWR)\n const backgroundRefresh = async () => {\n try {\n const freshResponse = await fetch(input, cleanOptions);\n \n if (freshResponse.ok && (method === 'GET' || method === 'POST' || method === 'PUT')) {\n const freshCacheEntry = await responseToCache(freshResponse.clone(), init);\n const cacheTTL = computeTTL(freshCacheEntry.expiresAt);\n await cache.set(cacheKey, freshCacheEntry, { ttl: cacheTTL });\n }\n } catch (error) {\n console.error('[cached-middleware-fetch] Background refresh failed:', error);\n }\n };\n \n // Use waitUntil to extend the lifetime of the request for background refresh\n if (typeof waitUntil === 'function') {\n waitUntil(backgroundRefresh());\n } else {\n // Fallback if waitUntil is not available (non-Vercel environment)\n backgroundRefresh().catch(() => {});\n }\n }\n \n // Return cached response with appropriate cache status\n return cacheToResponse(cachedEntry, isStale ? 'STALE' : 'HIT');\n }\n }\n \n // Fetch from origin (cache miss or expired)\n const response = await fetch(input, cleanOptions);\n \n // Clone the response first to avoid body consumption issues\n const responseForCaching = response.clone();\n const responseForReturn = response.clone();\n \n // Add cache status headers to indicate this was a miss\n const responseWithCacheHeaders = new Response(responseForReturn.body, {\n status: response.status,\n statusText: response.statusText,\n headers: new Headers(response.headers)\n });\n responseWithCacheHeaders.headers.set('X-Cache-Status', 'MISS');\n responseWithCacheHeaders.headers.set('X-Cache-Age', '0');\n \n // Only cache successful responses (2xx) and GET/POST/PUT requests\n if (response.ok && (method === 'GET' || method === 'POST' || method === 'PUT')) {\n const cacheEntry = await responseToCache(responseForCaching, init);\n \n // Store in cache with appropriate TTL\n const cacheTTL = computeTTL(cacheEntry.expiresAt);\n \n cache.set(cacheKey, cacheEntry, { ttl: cacheTTL }).catch((error: unknown) => {\n console.error('[cached-middleware-fetch] Failed to cache response:', error);\n });\n }\n \n return responseWithCacheHeaders;\n } catch (error) {\n // If cache operations fail, fallback to regular fetch\n console.error('[cached-middleware-fetch] Cache operation failed:', error);\n const fallbackResponse = await fetch(input, cleanOptions);\n \n // Clone the response to avoid body consumption issues\n const fallbackResponseClone = fallbackResponse.clone();\n \n // Add cache status headers to indicate this was a miss due to error\n const responseWithCacheHeaders = new Response(fallbackResponseClone.body, {\n status: fallbackResponse.status,\n statusText: fallbackResponse.statusText,\n headers: new Headers(fallbackResponse.headers)\n });\n responseWithCacheHeaders.headers.set('X-Cache-Status', 'MISS');\n responseWithCacheHeaders.headers.set('X-Cache-Age', '0');\n \n return responseWithCacheHeaders;\n }\n}\n\n// Export as default for easier drop-in replacement\nexport default cachedFetch;\n\n// Named exports for specific use cases\nexport { cachedFetch as fetch };\n"],"mappings":"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","names":[]}