@revealui/cache 0.1.0 → 0.1.1

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/README.md ADDED
@@ -0,0 +1,88 @@
1
+ # @revealui/cache
2
+
3
+ Caching infrastructure for RevealUI applications. Provides CDN cache configuration, edge cache helpers, ISR presets, tag-based revalidation, and rate limiting at the edge.
4
+
5
+ ## When to Use This
6
+
7
+ - You need Cache-Control headers for CDN responses (Vercel, Cloudflare)
8
+ - You want ISR presets for Next.js pages (static, dynamic, real-time)
9
+ - You need tag-based cache invalidation when content changes
10
+ - You want edge-level rate limiting or A/B test variant assignment
11
+ - You need cache warming for static paths
12
+
13
+ If you're caching in-memory data within a single request, use standard `Map` or LRU — this package is for HTTP-layer and CDN caching.
14
+
15
+ ## Installation
16
+
17
+ ```bash
18
+ pnpm add @revealui/cache
19
+ ```
20
+
21
+ Optional peer dependency: `next` (>=14.0.0) — required for ISR helpers.
22
+
23
+ ## API Reference
24
+
25
+ ### CDN Configuration
26
+
27
+ | Export | Type | Purpose |
28
+ |--------|------|---------|
29
+ | `generateCacheControl` | Function | Build Cache-Control header string from config |
30
+ | `getCacheTTL` | Function | Get TTL for a content type |
31
+ | `CDN_CACHE_PRESETS` | Object | Pre-built configs (static, api, dynamic, immutable) |
32
+ | `DEFAULT_CDN_CONFIG` | Object | Default CDN configuration |
33
+ | `generateCacheTags` | Function | Generate cache tags for content-based invalidation |
34
+ | `generateVercelCacheConfig` | Function | Vercel-specific cache headers |
35
+ | `generateCloudflareConfig` | Function | Cloudflare-specific cache config |
36
+ | `shouldCacheResponse` | Function | Determine if a response should be cached |
37
+
38
+ ### CDN Purge
39
+
40
+ | Export | Type | Purpose |
41
+ |--------|------|---------|
42
+ | `purgeCDNCache` | Function | Purge CDN cache by URL patterns |
43
+ | `purgeCacheByTag` | Function | Purge by cache tag (content-type based) |
44
+ | `purgeAllCache` | Function | Full CDN cache purge |
45
+ | `warmCDNCache` | Function | Pre-warm cache for a list of URLs |
46
+
47
+ ### Edge Cache & ISR
48
+
49
+ | Export | Type | Purpose |
50
+ |--------|------|---------|
51
+ | `ISR_PRESETS` | Object | Next.js ISR configs (static: 1h, dynamic: 60s, realtime: 10s, immutable: 1y) |
52
+ | `revalidatePath` | Function | Revalidate a single Next.js path |
53
+ | `revalidatePaths` | Function | Batch path revalidation |
54
+ | `revalidateTag` | Function | Revalidate by cache tag |
55
+ | `revalidateTags` | Function | Batch tag revalidation |
56
+ | `generateStaticParams` | Function | Helper for Next.js static generation |
57
+ | `setEdgeCacheHeaders` | Function | Set edge-specific cache headers on response |
58
+ | `createEdgeCachedFetch` | Function | Fetch wrapper with edge caching |
59
+ | `createCachedFunction` | Function | Memoize an async function with TTL |
60
+ | `warmISRCache` | Function | Pre-warm ISR cache for static paths |
61
+ | `addPreloadLinks` | Function | Add `Link: <url>; rel=preload` headers |
62
+
63
+ ### Edge Utilities
64
+
65
+ | Export | Type | Purpose |
66
+ |--------|------|---------|
67
+ | `EdgeRateLimiter` | Class | Token bucket rate limiter for edge functions |
68
+ | `getGeoLocation` | Function | Extract geo data from edge request headers |
69
+ | `getABTestVariant` | Function | Deterministic A/B test variant assignment |
70
+ | `getPersonalizationConfig` | Function | Edge personalization based on geo/device |
71
+
72
+ ### Configuration
73
+
74
+ | Export | Type | Purpose |
75
+ |--------|------|---------|
76
+ | `configureCacheLogger` | Function | Set custom logger (defaults to console) |
77
+
78
+ ## JOSHUA Alignment
79
+
80
+ - **Adaptive**: ISR presets scale from real-time (10s) to immutable (1y) based on content volatility
81
+ - **Unified**: Cache tags follow the same taxonomy as CMS collections — invalidation is automatic
82
+ - **Orthogonal**: Caching is a separate concern from content serving — swap CDN providers without changing business logic
83
+
84
+ ## Related Packages
85
+
86
+ - `apps/api` — Applies cache headers to REST responses
87
+ - `apps/marketing` — Uses ISR presets for marketing pages
88
+ - `@revealui/core` — Triggers cache invalidation on content changes
package/dist/index.js CHANGED
@@ -343,13 +343,21 @@ function shouldCacheResponse(status, headers) {
343
343
  }
344
344
  function getCacheTTL(headers) {
345
345
  const cacheControl = headers.get("cache-control") || "";
346
- const sMaxAgeMatch = cacheControl.match(/s-maxage=(\d+)/);
347
- if (sMaxAgeMatch?.[1]) {
348
- return parseInt(sMaxAgeMatch[1], 10);
346
+ for (const directive of cacheControl.split(",")) {
347
+ const trimmed = directive.trim();
348
+ if (trimmed.startsWith("s-maxage=")) {
349
+ const val = trimmed.slice("s-maxage=".length);
350
+ const num = Number.parseInt(val, 10);
351
+ if (!Number.isNaN(num)) return num;
352
+ }
349
353
  }
350
- const maxAgeMatch = cacheControl.match(/max-age=(\d+)/);
351
- if (maxAgeMatch?.[1]) {
352
- return parseInt(maxAgeMatch[1], 10);
354
+ for (const directive of cacheControl.split(",")) {
355
+ const trimmed = directive.trim();
356
+ if (trimmed.startsWith("max-age=")) {
357
+ const val = trimmed.slice("max-age=".length);
358
+ const num = Number.parseInt(val, 10);
359
+ if (!Number.isNaN(num)) return num;
360
+ }
353
361
  }
354
362
  const expires = headers.get("expires");
355
363
  if (expires) {
@@ -651,12 +659,10 @@ function getPersonalizationConfig(request) {
651
659
  };
652
660
  }
653
661
  function getDeviceType(userAgent) {
654
- if (/mobile/i.test(userAgent) && !/tablet|ipad/i.test(userAgent)) {
655
- return "mobile";
656
- }
657
- if (/tablet|ipad/i.test(userAgent)) {
658
- return "tablet";
659
- }
662
+ const ua = userAgent.toLowerCase();
663
+ const isTablet = ua.includes("tablet") || ua.includes("ipad");
664
+ if (isTablet) return "tablet";
665
+ if (ua.includes("mobile")) return "mobile";
660
666
  return "desktop";
661
667
  }
662
668
  function setEdgeCacheHeaders(response, config) {
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/cdn-config.ts","../src/logger.ts","../src/edge-cache.ts"],"sourcesContent":["/**\n * CDN Configuration and Cache Management\n *\n * Utilities for CDN caching, edge caching, and cache invalidation\n */\n\n/**\n * CDN Cache Configuration\n */\nexport interface CDNCacheConfig {\n provider?: 'cloudflare' | 'vercel' | 'fastly' | 'custom';\n zones?: string[];\n ttl?: number;\n staleWhileRevalidate?: number;\n staleIfError?: number;\n bypassCache?: boolean;\n cacheKey?: string[];\n varyHeaders?: string[];\n}\n\nexport const DEFAULT_CDN_CONFIG: CDNCacheConfig = {\n provider: 'vercel',\n ttl: 31536000, // 1 year for static assets\n staleWhileRevalidate: 86400, // 1 day\n staleIfError: 604800, // 1 week\n bypassCache: false,\n cacheKey: ['url', 'headers.accept', 'headers.accept-encoding'],\n varyHeaders: ['Accept', 'Accept-Encoding'],\n};\n\n/**\n * Generate Cache-Control header\n */\nexport function generateCacheControl(config: {\n maxAge?: number;\n sMaxAge?: number;\n staleWhileRevalidate?: number;\n staleIfError?: number;\n public?: boolean;\n private?: boolean;\n immutable?: boolean;\n noCache?: boolean;\n noStore?: boolean;\n}): string {\n const directives: string[] = [];\n\n // Visibility\n if (config.noStore) {\n directives.push('no-store');\n return directives.join(', ');\n }\n\n if (config.noCache) {\n directives.push('no-cache');\n return directives.join(', ');\n }\n\n if (config.public) {\n directives.push('public');\n } else if (config.private) {\n directives.push('private');\n }\n\n // Max age\n if (config.maxAge !== undefined) {\n directives.push(`max-age=${config.maxAge}`);\n }\n\n // Shared max age (CDN)\n if (config.sMaxAge !== undefined) {\n directives.push(`s-maxage=${config.sMaxAge}`);\n }\n\n // Stale-while-revalidate\n if (config.staleWhileRevalidate !== undefined) {\n directives.push(`stale-while-revalidate=${config.staleWhileRevalidate}`);\n }\n\n // Stale-if-error\n if (config.staleIfError !== undefined) {\n directives.push(`stale-if-error=${config.staleIfError}`);\n }\n\n // Immutable\n if (config.immutable) {\n directives.push('immutable');\n }\n\n return directives.join(', ');\n}\n\n/**\n * Cache presets for different asset types\n */\nexport const CDN_CACHE_PRESETS = {\n // Static assets with hashed filenames (immutable)\n immutable: {\n maxAge: 31536000, // 1 year\n sMaxAge: 31536000,\n public: true,\n immutable: true,\n },\n\n // Static assets (images, fonts)\n static: {\n maxAge: 2592000, // 30 days\n sMaxAge: 31536000, // 1 year on CDN\n staleWhileRevalidate: 86400, // 1 day\n public: true,\n },\n\n // API responses (short-lived)\n api: {\n maxAge: 0,\n sMaxAge: 60, // 1 minute on CDN\n staleWhileRevalidate: 30,\n public: true,\n },\n\n // HTML pages (dynamic)\n page: {\n maxAge: 0,\n sMaxAge: 300, // 5 minutes on CDN\n staleWhileRevalidate: 60,\n public: true,\n },\n\n // User-specific data\n private: {\n maxAge: 300, // 5 minutes\n private: true,\n staleWhileRevalidate: 60,\n },\n\n // No caching\n noCache: {\n noStore: true,\n },\n\n // Revalidate every request\n revalidate: {\n maxAge: 0,\n sMaxAge: 0,\n noCache: true,\n },\n} as const;\n\n/**\n * CDN Purge Configuration\n */\nexport interface CDNPurgeConfig {\n provider: 'cloudflare' | 'vercel' | 'fastly';\n apiKey?: string;\n apiSecret?: string;\n zoneId?: string;\n distributionId?: string;\n}\n\n/**\n * Purge CDN cache\n */\nexport async function purgeCDNCache(\n urls: string[],\n config: CDNPurgeConfig,\n): Promise<{ success: boolean; purged: number; errors?: string[] }> {\n const { provider } = config;\n\n switch (provider) {\n case 'cloudflare':\n return purgeCloudflare(urls, config);\n case 'vercel':\n return purgeVercel(urls, config);\n case 'fastly':\n return purgeFastly(urls, config);\n default:\n throw new Error(`Unsupported CDN provider: ${provider}`);\n }\n}\n\n/**\n * Purge Cloudflare cache\n */\nasync function purgeCloudflare(\n urls: string[],\n config: CDNPurgeConfig,\n): Promise<{ success: boolean; purged: number; errors?: string[] }> {\n const { apiKey, zoneId } = config;\n\n if (!(apiKey && zoneId)) {\n throw new Error('Cloudflare API key and zone ID required');\n }\n\n try {\n const response = await fetch(\n `https://api.cloudflare.com/client/v4/zones/${zoneId}/purge_cache`,\n {\n method: 'POST',\n headers: {\n Authorization: `Bearer ${apiKey}`,\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({ files: urls }),\n },\n );\n\n const data = await response.json();\n\n return {\n success: data.success,\n purged: urls.length,\n errors: data.errors,\n };\n } catch (error) {\n return {\n success: false,\n purged: 0,\n errors: [error instanceof Error ? error.message : 'Unknown error'],\n };\n }\n}\n\n/**\n * Purge Vercel cache\n */\nasync function purgeVercel(\n urls: string[],\n config: CDNPurgeConfig,\n): Promise<{ success: boolean; purged: number; errors?: string[] }> {\n const { apiKey } = config;\n\n if (!apiKey) {\n throw new Error('Vercel API token required');\n }\n\n try {\n const response = await fetch('https://api.vercel.com/v1/purge', {\n method: 'POST',\n headers: {\n Authorization: `Bearer ${apiKey}`,\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({ urls }),\n });\n\n const data = await response.json();\n\n return {\n success: response.ok,\n purged: urls.length,\n errors: data.error ? [data.error.message] : undefined,\n };\n } catch (error) {\n return {\n success: false,\n purged: 0,\n errors: [error instanceof Error ? error.message : 'Unknown error'],\n };\n }\n}\n\n/**\n * Purge Fastly cache\n */\nasync function purgeFastly(\n urls: string[],\n config: CDNPurgeConfig,\n): Promise<{ success: boolean; purged: number; errors?: string[] }> {\n const { apiKey } = config;\n\n if (!apiKey) {\n throw new Error('Fastly API key required');\n }\n\n try {\n const results = await Promise.all(\n urls.map(async (url) => {\n const response = await fetch(url, {\n method: 'PURGE',\n headers: {\n 'Fastly-Key': apiKey,\n },\n });\n\n return response.ok;\n }),\n );\n\n const purged = results.filter(Boolean).length;\n\n return {\n success: purged === urls.length,\n purged,\n };\n } catch (error) {\n return {\n success: false,\n purged: 0,\n errors: [error instanceof Error ? error.message : 'Unknown error'],\n };\n }\n}\n\n/**\n * Purge by cache tag\n */\nexport async function purgeCacheByTag(\n tags: string[],\n config: CDNPurgeConfig,\n): Promise<{ success: boolean; purged: number; errors?: string[] }> {\n const { provider, apiKey, zoneId } = config;\n\n if (provider === 'cloudflare') {\n if (!(apiKey && zoneId)) {\n throw new Error('Cloudflare API key and zone ID required');\n }\n\n try {\n const response = await fetch(\n `https://api.cloudflare.com/client/v4/zones/${zoneId}/purge_cache`,\n {\n method: 'POST',\n headers: {\n Authorization: `Bearer ${apiKey}`,\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({ tags }),\n },\n );\n\n const data = await response.json();\n\n return {\n success: data.success,\n purged: tags.length,\n errors: data.errors,\n };\n } catch (error) {\n return {\n success: false,\n purged: 0,\n errors: [error instanceof Error ? error.message : 'Unknown error'],\n };\n }\n }\n\n throw new Error(`Cache tag purging not supported for ${provider}`);\n}\n\n/**\n * Purge everything\n */\nexport async function purgeAllCache(\n config: CDNPurgeConfig,\n): Promise<{ success: boolean; errors?: string[] }> {\n const { provider, apiKey, zoneId } = config;\n\n if (provider === 'cloudflare') {\n if (!(apiKey && zoneId)) {\n throw new Error('Cloudflare API key and zone ID required');\n }\n\n try {\n const response = await fetch(\n `https://api.cloudflare.com/client/v4/zones/${zoneId}/purge_cache`,\n {\n method: 'POST',\n headers: {\n Authorization: `Bearer ${apiKey}`,\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({ purge_everything: true }),\n },\n );\n\n const data = await response.json();\n\n return {\n success: data.success,\n errors: data.errors,\n };\n } catch (error) {\n return {\n success: false,\n errors: [error instanceof Error ? error.message : 'Unknown error'],\n };\n }\n }\n\n throw new Error(`Purge all not supported for ${provider}`);\n}\n\n/**\n * CDN cache warming\n */\nexport async function warmCDNCache(\n urls: string[],\n options: {\n concurrency?: number;\n headers?: Record<string, string>;\n } = {},\n): Promise<{ warmed: number; failed: number; errors: string[] }> {\n const { concurrency = 5, headers = {} } = options;\n\n const results: { success: boolean; error?: string }[] = [];\n const chunks: string[][] = [];\n\n // Split into chunks\n for (let i = 0; i < urls.length; i += concurrency) {\n chunks.push(urls.slice(i, i + concurrency));\n }\n\n // Warm cache in chunks\n for (const chunk of chunks) {\n const chunkResults = await Promise.all(\n chunk.map(async (url) => {\n try {\n const response = await fetch(url, { headers });\n return {\n success: response.ok,\n error: response.ok ? undefined : `${response.status} ${response.statusText}`,\n };\n } catch (error) {\n return {\n success: false,\n error: error instanceof Error ? error.message : 'Unknown error',\n };\n }\n }),\n );\n\n results.push(...chunkResults);\n }\n\n const warmed = results.filter((r) => r.success).length;\n const failed = results.filter((r) => !r.success).length;\n const errors = results.flatMap((r) => (r.error ? [r.error] : []));\n\n return { warmed, failed, errors };\n}\n\n/**\n * Generate cache tags\n */\nexport function generateCacheTags(resource: {\n type: string;\n id?: string | number;\n related?: string[];\n}): string[] {\n const tags: string[] = [];\n\n // Type tag\n tags.push(resource.type);\n\n // ID tag\n if (resource.id) {\n tags.push(`${resource.type}:${resource.id}`);\n }\n\n // Related tags\n if (resource.related) {\n tags.push(...resource.related);\n }\n\n return tags;\n}\n\n/**\n * Edge cache configuration for Vercel\n */\nexport function generateVercelCacheConfig(preset: keyof typeof CDN_CACHE_PRESETS) {\n const config = CDN_CACHE_PRESETS[preset];\n const cacheControl = generateCacheControl(config);\n\n return {\n headers: {\n 'Cache-Control': cacheControl,\n 'CDN-Cache-Control': cacheControl,\n 'Vercel-CDN-Cache-Control': cacheControl,\n },\n };\n}\n\n/**\n * Edge cache configuration for Cloudflare\n */\nexport function generateCloudflareConfig(\n preset: keyof typeof CDN_CACHE_PRESETS,\n options: {\n cacheTags?: string[];\n bypassOnCookie?: string;\n } = {},\n) {\n const config = CDN_CACHE_PRESETS[preset];\n const cacheControl = generateCacheControl(config);\n\n const headers: Record<string, string> = {\n 'Cache-Control': cacheControl,\n };\n\n // Cache tags\n if (options.cacheTags && options.cacheTags.length > 0) {\n headers['Cache-Tag'] = options.cacheTags.join(',');\n }\n\n // Bypass on cookie\n if (options.bypassOnCookie) {\n headers['Cache-Control'] = `${cacheControl}, bypass=${options.bypassOnCookie}`;\n }\n\n return { headers };\n}\n\n/**\n * Check if response should be cached\n */\nexport function shouldCacheResponse(status: number, headers: Headers): boolean {\n // Don't cache errors\n if (status >= 400) {\n return false;\n }\n\n // Check Cache-Control header\n const cacheControl = headers.get('cache-control') || '';\n if (\n cacheControl.includes('no-store') ||\n cacheControl.includes('no-cache') ||\n cacheControl.includes('private')\n ) {\n return false;\n }\n\n return true;\n}\n\n/**\n * Calculate cache TTL from headers\n */\nexport function getCacheTTL(headers: Headers): number {\n const cacheControl = headers.get('cache-control') || '';\n\n // Check s-maxage first (CDN)\n const sMaxAgeMatch = cacheControl.match(/s-maxage=(\\d+)/);\n if (sMaxAgeMatch?.[1]) {\n return parseInt(sMaxAgeMatch[1], 10);\n }\n\n // Check max-age\n const maxAgeMatch = cacheControl.match(/max-age=(\\d+)/);\n if (maxAgeMatch?.[1]) {\n return parseInt(maxAgeMatch[1], 10);\n }\n\n // Check Expires header\n const expires = headers.get('expires');\n if (expires) {\n const expiresDate = new Date(expires);\n const now = new Date();\n return Math.max(0, Math.floor((expiresDate.getTime() - now.getTime()) / 1000));\n }\n\n return 0;\n}\n","/**\n * Internal logger for @revealui/cache.\n *\n * Defaults to `console`. Consumers should call `configureCacheLogger()`\n * to supply a structured logger (e.g. from `@revealui/utils/logger`).\n */\n\nexport interface CacheLogger {\n warn(message: string, ...args: unknown[]): void;\n error(message: string, ...args: unknown[]): void;\n info(message: string, ...args: unknown[]): void;\n debug(message: string, ...args: unknown[]): void;\n}\n\nlet cacheLogger: CacheLogger = console;\n\n/**\n * Replace the default console logger with a structured logger.\n */\nexport function configureCacheLogger(logger: CacheLogger): void {\n cacheLogger = logger;\n}\n\n/**\n * Get the current cache logger instance.\n */\nexport function getCacheLogger(): CacheLogger {\n return cacheLogger;\n}\n","/**\n * Edge Caching and ISR (Incremental Static Regeneration)\n *\n * Utilities for Next.js edge caching, ISR, and on-demand revalidation\n */\n\nimport type { NextRequest, NextResponse } from 'next/server';\nimport { getCacheLogger } from './logger.js';\n\n/**\n * Next.js extends the standard RequestInit with a `next` property\n * for ISR revalidation and cache tags.\n */\ninterface NextFetchRequestInit extends RequestInit {\n next?: {\n revalidate?: number | false;\n tags?: string[];\n };\n}\n\n/**\n * ISR Configuration\n */\nexport interface ISRConfig {\n revalidate?: number | false;\n tags?: string[];\n dynamicParams?: boolean;\n}\n\nexport const ISR_PRESETS = {\n // Revalidate every request\n always: {\n revalidate: 0,\n },\n\n // Revalidate every minute\n minute: {\n revalidate: 60,\n },\n\n // Revalidate every 5 minutes\n fiveMinutes: {\n revalidate: 300,\n },\n\n // Revalidate every hour\n hourly: {\n revalidate: 3600,\n },\n\n // Revalidate daily\n daily: {\n revalidate: 86400,\n },\n\n // Never revalidate (static)\n never: {\n revalidate: false,\n },\n} as const;\n\n/**\n * Generate static params for ISR\n */\nexport async function generateStaticParams<T>(\n fetchFn: () => Promise<T[]>,\n mapFn: (item: T) => Record<string, string>,\n): Promise<Array<Record<string, string>>> {\n try {\n const items = await fetchFn();\n return items.map(mapFn);\n } catch (error) {\n getCacheLogger().error(\n 'Failed to generate static params',\n error instanceof Error ? error : new Error(String(error)),\n );\n return [];\n }\n}\n\n/**\n * Revalidate tag\n */\nexport async function revalidateTag(\n tag: string,\n secret?: string,\n): Promise<{ revalidated: boolean; error?: string }> {\n const baseUrl = process.env.NEXT_PUBLIC_URL;\n if (!baseUrl) {\n getCacheLogger().warn('revalidateTag skipped: NEXT_PUBLIC_URL is not configured', { tag });\n return { revalidated: false, error: 'NEXT_PUBLIC_URL is not configured' };\n }\n\n try {\n const url = new URL('/api/revalidate', baseUrl);\n\n const headers: HeadersInit = { 'Content-Type': 'application/json' };\n if (secret) {\n headers['x-revalidate-secret'] = secret;\n }\n\n const response = await fetch(url.toString(), {\n method: 'POST',\n headers,\n body: JSON.stringify({ tag }),\n });\n\n const data = await response.json();\n\n if (!response.ok) {\n getCacheLogger().warn('revalidateTag failed', {\n tag,\n status: response.status,\n error: data.error,\n });\n }\n\n return {\n revalidated: response.ok,\n error: data.error,\n };\n } catch (error) {\n const message = error instanceof Error ? error.message : 'Unknown error';\n getCacheLogger().warn('revalidateTag error', { tag, error: message });\n return {\n revalidated: false,\n error: message,\n };\n }\n}\n\n/**\n * Revalidate path\n */\nexport async function revalidatePath(\n path: string,\n secret?: string,\n): Promise<{ revalidated: boolean; error?: string }> {\n const baseUrl = process.env.NEXT_PUBLIC_URL;\n if (!baseUrl) {\n getCacheLogger().warn('revalidatePath skipped: NEXT_PUBLIC_URL is not configured', { path });\n return { revalidated: false, error: 'NEXT_PUBLIC_URL is not configured' };\n }\n\n try {\n const url = new URL('/api/revalidate', baseUrl);\n\n const headers: HeadersInit = { 'Content-Type': 'application/json' };\n if (secret) {\n headers['x-revalidate-secret'] = secret;\n }\n\n const response = await fetch(url.toString(), {\n method: 'POST',\n headers,\n body: JSON.stringify({ path }),\n });\n\n const data = await response.json();\n\n return {\n revalidated: response.ok,\n error: data.error,\n };\n } catch (error) {\n return {\n revalidated: false,\n error: error instanceof Error ? error.message : 'Unknown error',\n };\n }\n}\n\n/**\n * Revalidate multiple paths\n */\nexport async function revalidatePaths(\n paths: string[],\n secret?: string,\n): Promise<{\n revalidated: number;\n failed: number;\n errors: Array<{ path: string; error: string }>;\n}> {\n const results = await Promise.allSettled(paths.map((path) => revalidatePath(path, secret)));\n\n let revalidated = 0;\n let failed = 0;\n const errors: Array<{ path: string; error: string }> = [];\n\n for (let i = 0; i < results.length; i++) {\n const result = results[i];\n const path = paths[i];\n\n if (!(result && path)) {\n continue;\n }\n\n if (result.status === 'fulfilled' && result.value.revalidated) {\n revalidated++;\n } else {\n failed++;\n const error =\n result.status === 'fulfilled'\n ? result.value.error || 'Unknown error'\n : String(result.reason) || 'Unknown error';\n\n errors.push({ path, error });\n }\n }\n\n return { revalidated, failed, errors };\n}\n\n/**\n * Revalidate multiple tags\n */\nexport async function revalidateTags(\n tags: string[],\n secret?: string,\n): Promise<{\n revalidated: number;\n failed: number;\n errors: Array<{ tag: string; error: string }>;\n}> {\n const results = await Promise.allSettled(tags.map((tag) => revalidateTag(tag, secret)));\n\n let revalidated = 0;\n let failed = 0;\n const errors: Array<{ tag: string; error: string }> = [];\n\n for (let i = 0; i < results.length; i++) {\n const result = results[i];\n const tag = tags[i];\n\n if (!(result && tag)) {\n continue;\n }\n\n if (result.status === 'fulfilled' && result.value.revalidated) {\n revalidated++;\n } else {\n failed++;\n const error =\n result.status === 'fulfilled'\n ? result.value.error || 'Unknown error'\n : String(result.reason) || 'Unknown error';\n\n errors.push({ tag, error });\n }\n }\n\n return { revalidated, failed, errors };\n}\n\n/**\n * Edge middleware cache configuration\n */\nexport interface EdgeCacheConfig {\n cache?: 'force-cache' | 'no-cache' | 'no-store' | 'only-if-cached';\n next?: {\n revalidate?: number | false;\n tags?: string[];\n };\n}\n\n/**\n * Create edge cached fetch\n */\nexport function createEdgeCachedFetch(config: EdgeCacheConfig = {}) {\n return async <T>(url: string, options?: NextFetchRequestInit): Promise<T> => {\n const fetchOptions: NextFetchRequestInit = {\n ...options,\n ...config,\n next: {\n ...options?.next,\n ...config.next,\n },\n };\n\n const response = await fetch(url, fetchOptions);\n\n if (!response.ok) {\n throw new Error(`Fetch failed: ${response.statusText}`);\n }\n\n return response.json();\n };\n}\n\n/**\n * Unstable cache wrapper (Next.js 14+)\n */\nexport function createCachedFunction<TArgs extends unknown[], TReturn>(\n fn: (...args: TArgs) => Promise<TReturn>,\n options: {\n tags?: string[];\n revalidate?: number | false;\n } = {},\n): (...args: TArgs) => Promise<TReturn> {\n // If revalidation is disabled, bypass cache entirely\n if (options.revalidate === false) {\n return fn;\n }\n\n const ttlMs = (options.revalidate ?? 60) * 1000;\n const cache = new Map<string, { value: TReturn; expiresAt: number }>();\n\n return async (...args: TArgs): Promise<TReturn> => {\n const key = JSON.stringify(args);\n const now = Date.now();\n const cached = cache.get(key);\n\n if (cached && now < cached.expiresAt) {\n return cached.value;\n }\n\n const value = await fn(...args);\n cache.set(key, { value, expiresAt: now + ttlMs });\n return value;\n };\n}\n\n/**\n * Edge rate limiting with cache\n */\nexport interface EdgeRateLimitConfig {\n limit: number;\n window: number;\n key?: (request: NextRequest) => string;\n}\n\nexport class EdgeRateLimiter {\n private cache: Map<string, { count: number; resetTime: number }> = new Map();\n\n constructor(private config: EdgeRateLimitConfig) {}\n\n /**\n * Check rate limit\n */\n check(request: NextRequest): {\n allowed: boolean;\n limit: number;\n remaining: number;\n reset: number;\n } {\n const key = this.config.key\n ? this.config.key(request)\n : request.headers.get('x-forwarded-for') || 'unknown';\n\n const now = Date.now();\n let entry = this.cache.get(key);\n\n // Reset if window expired\n if (!entry || now > entry.resetTime) {\n entry = {\n count: 0,\n resetTime: now + this.config.window,\n };\n this.cache.set(key, entry);\n }\n\n // Increment count\n entry.count++;\n\n const allowed = entry.count <= this.config.limit;\n const remaining = Math.max(0, this.config.limit - entry.count);\n\n return {\n allowed,\n limit: this.config.limit,\n remaining,\n reset: entry.resetTime,\n };\n }\n\n /**\n * Clean up expired entries\n */\n cleanup(): void {\n const now = Date.now();\n for (const [key, entry] of this.cache.entries()) {\n if (now > entry.resetTime) {\n this.cache.delete(key);\n }\n }\n }\n}\n\n/**\n * Edge geolocation caching\n */\nexport interface GeoLocation {\n country?: string;\n region?: string;\n city?: string;\n latitude?: number;\n longitude?: number;\n}\n\nexport function getGeoLocation(request: NextRequest): GeoLocation | null {\n // Vercel edge headers\n const country = request.headers.get('x-vercel-ip-country');\n const region = request.headers.get('x-vercel-ip-country-region');\n const city = request.headers.get('x-vercel-ip-city');\n const latitude = request.headers.get('x-vercel-ip-latitude');\n const longitude = request.headers.get('x-vercel-ip-longitude');\n\n if (!country) {\n // Cloudflare headers\n const cfCountry = request.headers.get('cf-ipcountry');\n if (cfCountry) {\n return {\n country: cfCountry,\n };\n }\n\n return null;\n }\n\n return {\n country: country || undefined,\n region: region || undefined,\n city: city ? decodeURIComponent(city) : undefined,\n latitude: latitude ? parseFloat(latitude) : undefined,\n longitude: longitude ? parseFloat(longitude) : undefined,\n };\n}\n\n/**\n * Edge A/B testing with cache\n */\nexport function getABTestVariant(\n request: NextRequest,\n testName: string,\n variants: string[],\n): string {\n // Check cookie first\n const cookieName = `ab-test-${testName}`;\n const cookieVariant = request.cookies.get(cookieName)?.value;\n\n if (cookieVariant && variants.includes(cookieVariant)) {\n return cookieVariant;\n }\n\n // Assign variant based on IP hash\n const ip = request.headers.get('x-forwarded-for') || 'unknown';\n const hash = simpleHash(ip + testName);\n const variantIndex = hash % variants.length;\n const variant = variants[variantIndex];\n\n if (!variant) {\n throw new Error('No variant found for A/B test');\n }\n\n return variant;\n}\n\n/**\n * Simple hash function\n */\nfunction simpleHash(str: string): number {\n let hash = 0;\n for (let i = 0; i < str.length; i++) {\n const char = str.charCodeAt(i);\n hash = (hash << 5) - hash + char;\n hash = hash & hash;\n }\n return Math.abs(hash);\n}\n\n/**\n * Edge personalization cache\n */\nexport interface PersonalizationConfig {\n userId?: string;\n preferences?: Record<string, unknown>;\n location?: GeoLocation;\n device?: 'mobile' | 'tablet' | 'desktop';\n variant?: string;\n}\n\nexport function getPersonalizationConfig(request: NextRequest): PersonalizationConfig {\n const userAgent = request.headers.get('user-agent') || '';\n const device = getDeviceType(userAgent);\n const location = getGeoLocation(request);\n\n return {\n userId: request.cookies.get('user-id')?.value,\n location: location || undefined,\n device,\n };\n}\n\n/**\n * Detect device type\n */\nfunction getDeviceType(userAgent: string): 'mobile' | 'tablet' | 'desktop' {\n if (/mobile/i.test(userAgent) && !/tablet|ipad/i.test(userAgent)) {\n return 'mobile';\n }\n if (/tablet|ipad/i.test(userAgent)) {\n return 'tablet';\n }\n return 'desktop';\n}\n\n/**\n * Edge cache headers helper\n */\nexport function setEdgeCacheHeaders(\n response: NextResponse,\n config: {\n maxAge?: number;\n sMaxAge?: number;\n staleWhileRevalidate?: number;\n tags?: string[];\n },\n): NextResponse {\n const cacheControl: string[] = [];\n\n if (config.maxAge !== undefined) {\n cacheControl.push(`max-age=${config.maxAge}`);\n }\n\n if (config.sMaxAge !== undefined) {\n cacheControl.push(`s-maxage=${config.sMaxAge}`);\n }\n\n if (config.staleWhileRevalidate !== undefined) {\n cacheControl.push(`stale-while-revalidate=${config.staleWhileRevalidate}`);\n }\n\n if (cacheControl.length > 0) {\n response.headers.set('Cache-Control', cacheControl.join(', '));\n }\n\n if (config.tags && config.tags.length > 0) {\n response.headers.set('Cache-Tag', config.tags.join(','));\n }\n\n return response;\n}\n\n/**\n * Preload links for critical resources\n */\nexport function addPreloadLinks(\n response: NextResponse,\n resources: Array<{\n href: string;\n as: string;\n type?: string;\n crossorigin?: boolean;\n }>,\n): NextResponse {\n const links = resources.map((resource) => {\n const attrs = [`<${resource.href}>`, `rel=\"preload\"`, `as=\"${resource.as}\"`];\n\n if (resource.type) {\n attrs.push(`type=\"${resource.type}\"`);\n }\n\n if (resource.crossorigin) {\n attrs.push('crossorigin');\n }\n\n return attrs.join('; ');\n });\n\n if (links.length > 0) {\n response.headers.set('Link', links.join(', '));\n }\n\n return response;\n}\n\n/**\n * Cache warming for ISR pages\n */\nexport async function warmISRCache(\n paths: string[],\n baseURL: string = process.env.NEXT_PUBLIC_URL || 'http://localhost:3000',\n): Promise<{\n warmed: number;\n failed: number;\n errors: Array<{ path: string; error: string }>;\n}> {\n const results = await Promise.allSettled(\n paths.map(async (path) => {\n const url = new URL(path, baseURL);\n const response = await fetch(url.toString());\n\n if (!response.ok) {\n throw new Error(`${response.status} ${response.statusText}`);\n }\n\n return true;\n }),\n );\n\n let warmed = 0;\n let failed = 0;\n const errors: Array<{ path: string; error: string }> = [];\n\n for (let i = 0; i < results.length; i++) {\n const result = results[i];\n const path = paths[i];\n\n if (!(result && path)) {\n continue;\n }\n\n if (result.status === 'fulfilled') {\n warmed++;\n } else {\n failed++;\n errors.push({\n path,\n error:\n result.reason instanceof Error\n ? result.reason.message\n : String(result.reason) || 'Unknown error',\n });\n }\n }\n\n return { warmed, failed, errors };\n}\n"],"mappings":";AAoBO,IAAM,qBAAqC;AAAA,EAChD,UAAU;AAAA,EACV,KAAK;AAAA;AAAA,EACL,sBAAsB;AAAA;AAAA,EACtB,cAAc;AAAA;AAAA,EACd,aAAa;AAAA,EACb,UAAU,CAAC,OAAO,kBAAkB,yBAAyB;AAAA,EAC7D,aAAa,CAAC,UAAU,iBAAiB;AAC3C;AAKO,SAAS,qBAAqB,QAU1B;AACT,QAAM,aAAuB,CAAC;AAG9B,MAAI,OAAO,SAAS;AAClB,eAAW,KAAK,UAAU;AAC1B,WAAO,WAAW,KAAK,IAAI;AAAA,EAC7B;AAEA,MAAI,OAAO,SAAS;AAClB,eAAW,KAAK,UAAU;AAC1B,WAAO,WAAW,KAAK,IAAI;AAAA,EAC7B;AAEA,MAAI,OAAO,QAAQ;AACjB,eAAW,KAAK,QAAQ;AAAA,EAC1B,WAAW,OAAO,SAAS;AACzB,eAAW,KAAK,SAAS;AAAA,EAC3B;AAGA,MAAI,OAAO,WAAW,QAAW;AAC/B,eAAW,KAAK,WAAW,OAAO,MAAM,EAAE;AAAA,EAC5C;AAGA,MAAI,OAAO,YAAY,QAAW;AAChC,eAAW,KAAK,YAAY,OAAO,OAAO,EAAE;AAAA,EAC9C;AAGA,MAAI,OAAO,yBAAyB,QAAW;AAC7C,eAAW,KAAK,0BAA0B,OAAO,oBAAoB,EAAE;AAAA,EACzE;AAGA,MAAI,OAAO,iBAAiB,QAAW;AACrC,eAAW,KAAK,kBAAkB,OAAO,YAAY,EAAE;AAAA,EACzD;AAGA,MAAI,OAAO,WAAW;AACpB,eAAW,KAAK,WAAW;AAAA,EAC7B;AAEA,SAAO,WAAW,KAAK,IAAI;AAC7B;AAKO,IAAM,oBAAoB;AAAA;AAAA,EAE/B,WAAW;AAAA,IACT,QAAQ;AAAA;AAAA,IACR,SAAS;AAAA,IACT,QAAQ;AAAA,IACR,WAAW;AAAA,EACb;AAAA;AAAA,EAGA,QAAQ;AAAA,IACN,QAAQ;AAAA;AAAA,IACR,SAAS;AAAA;AAAA,IACT,sBAAsB;AAAA;AAAA,IACtB,QAAQ;AAAA,EACV;AAAA;AAAA,EAGA,KAAK;AAAA,IACH,QAAQ;AAAA,IACR,SAAS;AAAA;AAAA,IACT,sBAAsB;AAAA,IACtB,QAAQ;AAAA,EACV;AAAA;AAAA,EAGA,MAAM;AAAA,IACJ,QAAQ;AAAA,IACR,SAAS;AAAA;AAAA,IACT,sBAAsB;AAAA,IACtB,QAAQ;AAAA,EACV;AAAA;AAAA,EAGA,SAAS;AAAA,IACP,QAAQ;AAAA;AAAA,IACR,SAAS;AAAA,IACT,sBAAsB;AAAA,EACxB;AAAA;AAAA,EAGA,SAAS;AAAA,IACP,SAAS;AAAA,EACX;AAAA;AAAA,EAGA,YAAY;AAAA,IACV,QAAQ;AAAA,IACR,SAAS;AAAA,IACT,SAAS;AAAA,EACX;AACF;AAgBA,eAAsB,cACpB,MACA,QACkE;AAClE,QAAM,EAAE,SAAS,IAAI;AAErB,UAAQ,UAAU;AAAA,IAChB,KAAK;AACH,aAAO,gBAAgB,MAAM,MAAM;AAAA,IACrC,KAAK;AACH,aAAO,YAAY,MAAM,MAAM;AAAA,IACjC,KAAK;AACH,aAAO,YAAY,MAAM,MAAM;AAAA,IACjC;AACE,YAAM,IAAI,MAAM,6BAA6B,QAAQ,EAAE;AAAA,EAC3D;AACF;AAKA,eAAe,gBACb,MACA,QACkE;AAClE,QAAM,EAAE,QAAQ,OAAO,IAAI;AAE3B,MAAI,EAAE,UAAU,SAAS;AACvB,UAAM,IAAI,MAAM,yCAAyC;AAAA,EAC3D;AAEA,MAAI;AACF,UAAM,WAAW,MAAM;AAAA,MACrB,8CAA8C,MAAM;AAAA,MACpD;AAAA,QACE,QAAQ;AAAA,QACR,SAAS;AAAA,UACP,eAAe,UAAU,MAAM;AAAA,UAC/B,gBAAgB;AAAA,QAClB;AAAA,QACA,MAAM,KAAK,UAAU,EAAE,OAAO,KAAK,CAAC;AAAA,MACtC;AAAA,IACF;AAEA,UAAM,OAAO,MAAM,SAAS,KAAK;AAEjC,WAAO;AAAA,MACL,SAAS,KAAK;AAAA,MACd,QAAQ,KAAK;AAAA,MACb,QAAQ,KAAK;AAAA,IACf;AAAA,EACF,SAAS,OAAO;AACd,WAAO;AAAA,MACL,SAAS;AAAA,MACT,QAAQ;AAAA,MACR,QAAQ,CAAC,iBAAiB,QAAQ,MAAM,UAAU,eAAe;AAAA,IACnE;AAAA,EACF;AACF;AAKA,eAAe,YACb,MACA,QACkE;AAClE,QAAM,EAAE,OAAO,IAAI;AAEnB,MAAI,CAAC,QAAQ;AACX,UAAM,IAAI,MAAM,2BAA2B;AAAA,EAC7C;AAEA,MAAI;AACF,UAAM,WAAW,MAAM,MAAM,mCAAmC;AAAA,MAC9D,QAAQ;AAAA,MACR,SAAS;AAAA,QACP,eAAe,UAAU,MAAM;AAAA,QAC/B,gBAAgB;AAAA,MAClB;AAAA,MACA,MAAM,KAAK,UAAU,EAAE,KAAK,CAAC;AAAA,IAC/B,CAAC;AAED,UAAM,OAAO,MAAM,SAAS,KAAK;AAEjC,WAAO;AAAA,MACL,SAAS,SAAS;AAAA,MAClB,QAAQ,KAAK;AAAA,MACb,QAAQ,KAAK,QAAQ,CAAC,KAAK,MAAM,OAAO,IAAI;AAAA,IAC9C;AAAA,EACF,SAAS,OAAO;AACd,WAAO;AAAA,MACL,SAAS;AAAA,MACT,QAAQ;AAAA,MACR,QAAQ,CAAC,iBAAiB,QAAQ,MAAM,UAAU,eAAe;AAAA,IACnE;AAAA,EACF;AACF;AAKA,eAAe,YACb,MACA,QACkE;AAClE,QAAM,EAAE,OAAO,IAAI;AAEnB,MAAI,CAAC,QAAQ;AACX,UAAM,IAAI,MAAM,yBAAyB;AAAA,EAC3C;AAEA,MAAI;AACF,UAAM,UAAU,MAAM,QAAQ;AAAA,MAC5B,KAAK,IAAI,OAAO,QAAQ;AACtB,cAAM,WAAW,MAAM,MAAM,KAAK;AAAA,UAChC,QAAQ;AAAA,UACR,SAAS;AAAA,YACP,cAAc;AAAA,UAChB;AAAA,QACF,CAAC;AAED,eAAO,SAAS;AAAA,MAClB,CAAC;AAAA,IACH;AAEA,UAAM,SAAS,QAAQ,OAAO,OAAO,EAAE;AAEvC,WAAO;AAAA,MACL,SAAS,WAAW,KAAK;AAAA,MACzB;AAAA,IACF;AAAA,EACF,SAAS,OAAO;AACd,WAAO;AAAA,MACL,SAAS;AAAA,MACT,QAAQ;AAAA,MACR,QAAQ,CAAC,iBAAiB,QAAQ,MAAM,UAAU,eAAe;AAAA,IACnE;AAAA,EACF;AACF;AAKA,eAAsB,gBACpB,MACA,QACkE;AAClE,QAAM,EAAE,UAAU,QAAQ,OAAO,IAAI;AAErC,MAAI,aAAa,cAAc;AAC7B,QAAI,EAAE,UAAU,SAAS;AACvB,YAAM,IAAI,MAAM,yCAAyC;AAAA,IAC3D;AAEA,QAAI;AACF,YAAM,WAAW,MAAM;AAAA,QACrB,8CAA8C,MAAM;AAAA,QACpD;AAAA,UACE,QAAQ;AAAA,UACR,SAAS;AAAA,YACP,eAAe,UAAU,MAAM;AAAA,YAC/B,gBAAgB;AAAA,UAClB;AAAA,UACA,MAAM,KAAK,UAAU,EAAE,KAAK,CAAC;AAAA,QAC/B;AAAA,MACF;AAEA,YAAM,OAAO,MAAM,SAAS,KAAK;AAEjC,aAAO;AAAA,QACL,SAAS,KAAK;AAAA,QACd,QAAQ,KAAK;AAAA,QACb,QAAQ,KAAK;AAAA,MACf;AAAA,IACF,SAAS,OAAO;AACd,aAAO;AAAA,QACL,SAAS;AAAA,QACT,QAAQ;AAAA,QACR,QAAQ,CAAC,iBAAiB,QAAQ,MAAM,UAAU,eAAe;AAAA,MACnE;AAAA,IACF;AAAA,EACF;AAEA,QAAM,IAAI,MAAM,uCAAuC,QAAQ,EAAE;AACnE;AAKA,eAAsB,cACpB,QACkD;AAClD,QAAM,EAAE,UAAU,QAAQ,OAAO,IAAI;AAErC,MAAI,aAAa,cAAc;AAC7B,QAAI,EAAE,UAAU,SAAS;AACvB,YAAM,IAAI,MAAM,yCAAyC;AAAA,IAC3D;AAEA,QAAI;AACF,YAAM,WAAW,MAAM;AAAA,QACrB,8CAA8C,MAAM;AAAA,QACpD;AAAA,UACE,QAAQ;AAAA,UACR,SAAS;AAAA,YACP,eAAe,UAAU,MAAM;AAAA,YAC/B,gBAAgB;AAAA,UAClB;AAAA,UACA,MAAM,KAAK,UAAU,EAAE,kBAAkB,KAAK,CAAC;AAAA,QACjD;AAAA,MACF;AAEA,YAAM,OAAO,MAAM,SAAS,KAAK;AAEjC,aAAO;AAAA,QACL,SAAS,KAAK;AAAA,QACd,QAAQ,KAAK;AAAA,MACf;AAAA,IACF,SAAS,OAAO;AACd,aAAO;AAAA,QACL,SAAS;AAAA,QACT,QAAQ,CAAC,iBAAiB,QAAQ,MAAM,UAAU,eAAe;AAAA,MACnE;AAAA,IACF;AAAA,EACF;AAEA,QAAM,IAAI,MAAM,+BAA+B,QAAQ,EAAE;AAC3D;AAKA,eAAsB,aACpB,MACA,UAGI,CAAC,GAC0D;AAC/D,QAAM,EAAE,cAAc,GAAG,UAAU,CAAC,EAAE,IAAI;AAE1C,QAAM,UAAkD,CAAC;AACzD,QAAM,SAAqB,CAAC;AAG5B,WAAS,IAAI,GAAG,IAAI,KAAK,QAAQ,KAAK,aAAa;AACjD,WAAO,KAAK,KAAK,MAAM,GAAG,IAAI,WAAW,CAAC;AAAA,EAC5C;AAGA,aAAW,SAAS,QAAQ;AAC1B,UAAM,eAAe,MAAM,QAAQ;AAAA,MACjC,MAAM,IAAI,OAAO,QAAQ;AACvB,YAAI;AACF,gBAAM,WAAW,MAAM,MAAM,KAAK,EAAE,QAAQ,CAAC;AAC7C,iBAAO;AAAA,YACL,SAAS,SAAS;AAAA,YAClB,OAAO,SAAS,KAAK,SAAY,GAAG,SAAS,MAAM,IAAI,SAAS,UAAU;AAAA,UAC5E;AAAA,QACF,SAAS,OAAO;AACd,iBAAO;AAAA,YACL,SAAS;AAAA,YACT,OAAO,iBAAiB,QAAQ,MAAM,UAAU;AAAA,UAClD;AAAA,QACF;AAAA,MACF,CAAC;AAAA,IACH;AAEA,YAAQ,KAAK,GAAG,YAAY;AAAA,EAC9B;AAEA,QAAM,SAAS,QAAQ,OAAO,CAAC,MAAM,EAAE,OAAO,EAAE;AAChD,QAAM,SAAS,QAAQ,OAAO,CAAC,MAAM,CAAC,EAAE,OAAO,EAAE;AACjD,QAAM,SAAS,QAAQ,QAAQ,CAAC,MAAO,EAAE,QAAQ,CAAC,EAAE,KAAK,IAAI,CAAC,CAAE;AAEhE,SAAO,EAAE,QAAQ,QAAQ,OAAO;AAClC;AAKO,SAAS,kBAAkB,UAIrB;AACX,QAAM,OAAiB,CAAC;AAGxB,OAAK,KAAK,SAAS,IAAI;AAGvB,MAAI,SAAS,IAAI;AACf,SAAK,KAAK,GAAG,SAAS,IAAI,IAAI,SAAS,EAAE,EAAE;AAAA,EAC7C;AAGA,MAAI,SAAS,SAAS;AACpB,SAAK,KAAK,GAAG,SAAS,OAAO;AAAA,EAC/B;AAEA,SAAO;AACT;AAKO,SAAS,0BAA0B,QAAwC;AAChF,QAAM,SAAS,kBAAkB,MAAM;AACvC,QAAM,eAAe,qBAAqB,MAAM;AAEhD,SAAO;AAAA,IACL,SAAS;AAAA,MACP,iBAAiB;AAAA,MACjB,qBAAqB;AAAA,MACrB,4BAA4B;AAAA,IAC9B;AAAA,EACF;AACF;AAKO,SAAS,yBACd,QACA,UAGI,CAAC,GACL;AACA,QAAM,SAAS,kBAAkB,MAAM;AACvC,QAAM,eAAe,qBAAqB,MAAM;AAEhD,QAAM,UAAkC;AAAA,IACtC,iBAAiB;AAAA,EACnB;AAGA,MAAI,QAAQ,aAAa,QAAQ,UAAU,SAAS,GAAG;AACrD,YAAQ,WAAW,IAAI,QAAQ,UAAU,KAAK,GAAG;AAAA,EACnD;AAGA,MAAI,QAAQ,gBAAgB;AAC1B,YAAQ,eAAe,IAAI,GAAG,YAAY,YAAY,QAAQ,cAAc;AAAA,EAC9E;AAEA,SAAO,EAAE,QAAQ;AACnB;AAKO,SAAS,oBAAoB,QAAgB,SAA2B;AAE7E,MAAI,UAAU,KAAK;AACjB,WAAO;AAAA,EACT;AAGA,QAAM,eAAe,QAAQ,IAAI,eAAe,KAAK;AACrD,MACE,aAAa,SAAS,UAAU,KAChC,aAAa,SAAS,UAAU,KAChC,aAAa,SAAS,SAAS,GAC/B;AACA,WAAO;AAAA,EACT;AAEA,SAAO;AACT;AAKO,SAAS,YAAY,SAA0B;AACpD,QAAM,eAAe,QAAQ,IAAI,eAAe,KAAK;AAGrD,QAAM,eAAe,aAAa,MAAM,gBAAgB;AACxD,MAAI,eAAe,CAAC,GAAG;AACrB,WAAO,SAAS,aAAa,CAAC,GAAG,EAAE;AAAA,EACrC;AAGA,QAAM,cAAc,aAAa,MAAM,eAAe;AACtD,MAAI,cAAc,CAAC,GAAG;AACpB,WAAO,SAAS,YAAY,CAAC,GAAG,EAAE;AAAA,EACpC;AAGA,QAAM,UAAU,QAAQ,IAAI,SAAS;AACrC,MAAI,SAAS;AACX,UAAM,cAAc,IAAI,KAAK,OAAO;AACpC,UAAM,MAAM,oBAAI,KAAK;AACrB,WAAO,KAAK,IAAI,GAAG,KAAK,OAAO,YAAY,QAAQ,IAAI,IAAI,QAAQ,KAAK,GAAI,CAAC;AAAA,EAC/E;AAEA,SAAO;AACT;;;ACniBA,IAAI,cAA2B;AAKxB,SAAS,qBAAqB,QAA2B;AAC9D,gBAAc;AAChB;AAKO,SAAS,iBAA8B;AAC5C,SAAO;AACT;;;ACCO,IAAM,cAAc;AAAA;AAAA,EAEzB,QAAQ;AAAA,IACN,YAAY;AAAA,EACd;AAAA;AAAA,EAGA,QAAQ;AAAA,IACN,YAAY;AAAA,EACd;AAAA;AAAA,EAGA,aAAa;AAAA,IACX,YAAY;AAAA,EACd;AAAA;AAAA,EAGA,QAAQ;AAAA,IACN,YAAY;AAAA,EACd;AAAA;AAAA,EAGA,OAAO;AAAA,IACL,YAAY;AAAA,EACd;AAAA;AAAA,EAGA,OAAO;AAAA,IACL,YAAY;AAAA,EACd;AACF;AAKA,eAAsB,qBACpB,SACA,OACwC;AACxC,MAAI;AACF,UAAM,QAAQ,MAAM,QAAQ;AAC5B,WAAO,MAAM,IAAI,KAAK;AAAA,EACxB,SAAS,OAAO;AACd,mBAAe,EAAE;AAAA,MACf;AAAA,MACA,iBAAiB,QAAQ,QAAQ,IAAI,MAAM,OAAO,KAAK,CAAC;AAAA,IAC1D;AACA,WAAO,CAAC;AAAA,EACV;AACF;AAKA,eAAsB,cACpB,KACA,QACmD;AACnD,QAAM,UAAU,QAAQ,IAAI;AAC5B,MAAI,CAAC,SAAS;AACZ,mBAAe,EAAE,KAAK,4DAA4D,EAAE,IAAI,CAAC;AACzF,WAAO,EAAE,aAAa,OAAO,OAAO,oCAAoC;AAAA,EAC1E;AAEA,MAAI;AACF,UAAM,MAAM,IAAI,IAAI,mBAAmB,OAAO;AAE9C,UAAM,UAAuB,EAAE,gBAAgB,mBAAmB;AAClE,QAAI,QAAQ;AACV,cAAQ,qBAAqB,IAAI;AAAA,IACnC;AAEA,UAAM,WAAW,MAAM,MAAM,IAAI,SAAS,GAAG;AAAA,MAC3C,QAAQ;AAAA,MACR;AAAA,MACA,MAAM,KAAK,UAAU,EAAE,IAAI,CAAC;AAAA,IAC9B,CAAC;AAED,UAAM,OAAO,MAAM,SAAS,KAAK;AAEjC,QAAI,CAAC,SAAS,IAAI;AAChB,qBAAe,EAAE,KAAK,wBAAwB;AAAA,QAC5C;AAAA,QACA,QAAQ,SAAS;AAAA,QACjB,OAAO,KAAK;AAAA,MACd,CAAC;AAAA,IACH;AAEA,WAAO;AAAA,MACL,aAAa,SAAS;AAAA,MACtB,OAAO,KAAK;AAAA,IACd;AAAA,EACF,SAAS,OAAO;AACd,UAAM,UAAU,iBAAiB,QAAQ,MAAM,UAAU;AACzD,mBAAe,EAAE,KAAK,uBAAuB,EAAE,KAAK,OAAO,QAAQ,CAAC;AACpE,WAAO;AAAA,MACL,aAAa;AAAA,MACb,OAAO;AAAA,IACT;AAAA,EACF;AACF;AAKA,eAAsB,eACpB,MACA,QACmD;AACnD,QAAM,UAAU,QAAQ,IAAI;AAC5B,MAAI,CAAC,SAAS;AACZ,mBAAe,EAAE,KAAK,6DAA6D,EAAE,KAAK,CAAC;AAC3F,WAAO,EAAE,aAAa,OAAO,OAAO,oCAAoC;AAAA,EAC1E;AAEA,MAAI;AACF,UAAM,MAAM,IAAI,IAAI,mBAAmB,OAAO;AAE9C,UAAM,UAAuB,EAAE,gBAAgB,mBAAmB;AAClE,QAAI,QAAQ;AACV,cAAQ,qBAAqB,IAAI;AAAA,IACnC;AAEA,UAAM,WAAW,MAAM,MAAM,IAAI,SAAS,GAAG;AAAA,MAC3C,QAAQ;AAAA,MACR;AAAA,MACA,MAAM,KAAK,UAAU,EAAE,KAAK,CAAC;AAAA,IAC/B,CAAC;AAED,UAAM,OAAO,MAAM,SAAS,KAAK;AAEjC,WAAO;AAAA,MACL,aAAa,SAAS;AAAA,MACtB,OAAO,KAAK;AAAA,IACd;AAAA,EACF,SAAS,OAAO;AACd,WAAO;AAAA,MACL,aAAa;AAAA,MACb,OAAO,iBAAiB,QAAQ,MAAM,UAAU;AAAA,IAClD;AAAA,EACF;AACF;AAKA,eAAsB,gBACpB,OACA,QAKC;AACD,QAAM,UAAU,MAAM,QAAQ,WAAW,MAAM,IAAI,CAAC,SAAS,eAAe,MAAM,MAAM,CAAC,CAAC;AAE1F,MAAI,cAAc;AAClB,MAAI,SAAS;AACb,QAAM,SAAiD,CAAC;AAExD,WAAS,IAAI,GAAG,IAAI,QAAQ,QAAQ,KAAK;AACvC,UAAM,SAAS,QAAQ,CAAC;AACxB,UAAM,OAAO,MAAM,CAAC;AAEpB,QAAI,EAAE,UAAU,OAAO;AACrB;AAAA,IACF;AAEA,QAAI,OAAO,WAAW,eAAe,OAAO,MAAM,aAAa;AAC7D;AAAA,IACF,OAAO;AACL;AACA,YAAM,QACJ,OAAO,WAAW,cACd,OAAO,MAAM,SAAS,kBACtB,OAAO,OAAO,MAAM,KAAK;AAE/B,aAAO,KAAK,EAAE,MAAM,MAAM,CAAC;AAAA,IAC7B;AAAA,EACF;AAEA,SAAO,EAAE,aAAa,QAAQ,OAAO;AACvC;AAKA,eAAsB,eACpB,MACA,QAKC;AACD,QAAM,UAAU,MAAM,QAAQ,WAAW,KAAK,IAAI,CAAC,QAAQ,cAAc,KAAK,MAAM,CAAC,CAAC;AAEtF,MAAI,cAAc;AAClB,MAAI,SAAS;AACb,QAAM,SAAgD,CAAC;AAEvD,WAAS,IAAI,GAAG,IAAI,QAAQ,QAAQ,KAAK;AACvC,UAAM,SAAS,QAAQ,CAAC;AACxB,UAAM,MAAM,KAAK,CAAC;AAElB,QAAI,EAAE,UAAU,MAAM;AACpB;AAAA,IACF;AAEA,QAAI,OAAO,WAAW,eAAe,OAAO,MAAM,aAAa;AAC7D;AAAA,IACF,OAAO;AACL;AACA,YAAM,QACJ,OAAO,WAAW,cACd,OAAO,MAAM,SAAS,kBACtB,OAAO,OAAO,MAAM,KAAK;AAE/B,aAAO,KAAK,EAAE,KAAK,MAAM,CAAC;AAAA,IAC5B;AAAA,EACF;AAEA,SAAO,EAAE,aAAa,QAAQ,OAAO;AACvC;AAgBO,SAAS,sBAAsB,SAA0B,CAAC,GAAG;AAClE,SAAO,OAAU,KAAa,YAA+C;AAC3E,UAAM,eAAqC;AAAA,MACzC,GAAG;AAAA,MACH,GAAG;AAAA,MACH,MAAM;AAAA,QACJ,GAAG,SAAS;AAAA,QACZ,GAAG,OAAO;AAAA,MACZ;AAAA,IACF;AAEA,UAAM,WAAW,MAAM,MAAM,KAAK,YAAY;AAE9C,QAAI,CAAC,SAAS,IAAI;AAChB,YAAM,IAAI,MAAM,iBAAiB,SAAS,UAAU,EAAE;AAAA,IACxD;AAEA,WAAO,SAAS,KAAK;AAAA,EACvB;AACF;AAKO,SAAS,qBACd,IACA,UAGI,CAAC,GACiC;AAEtC,MAAI,QAAQ,eAAe,OAAO;AAChC,WAAO;AAAA,EACT;AAEA,QAAM,SAAS,QAAQ,cAAc,MAAM;AAC3C,QAAM,QAAQ,oBAAI,IAAmD;AAErE,SAAO,UAAU,SAAkC;AACjD,UAAM,MAAM,KAAK,UAAU,IAAI;AAC/B,UAAM,MAAM,KAAK,IAAI;AACrB,UAAM,SAAS,MAAM,IAAI,GAAG;AAE5B,QAAI,UAAU,MAAM,OAAO,WAAW;AACpC,aAAO,OAAO;AAAA,IAChB;AAEA,UAAM,QAAQ,MAAM,GAAG,GAAG,IAAI;AAC9B,UAAM,IAAI,KAAK,EAAE,OAAO,WAAW,MAAM,MAAM,CAAC;AAChD,WAAO;AAAA,EACT;AACF;AAWO,IAAM,kBAAN,MAAsB;AAAA,EAG3B,YAAoB,QAA6B;AAA7B;AAAA,EAA8B;AAAA,EAF1C,QAA2D,oBAAI,IAAI;AAAA;AAAA;AAAA;AAAA,EAO3E,MAAM,SAKJ;AACA,UAAM,MAAM,KAAK,OAAO,MACpB,KAAK,OAAO,IAAI,OAAO,IACvB,QAAQ,QAAQ,IAAI,iBAAiB,KAAK;AAE9C,UAAM,MAAM,KAAK,IAAI;AACrB,QAAI,QAAQ,KAAK,MAAM,IAAI,GAAG;AAG9B,QAAI,CAAC,SAAS,MAAM,MAAM,WAAW;AACnC,cAAQ;AAAA,QACN,OAAO;AAAA,QACP,WAAW,MAAM,KAAK,OAAO;AAAA,MAC/B;AACA,WAAK,MAAM,IAAI,KAAK,KAAK;AAAA,IAC3B;AAGA,UAAM;AAEN,UAAM,UAAU,MAAM,SAAS,KAAK,OAAO;AAC3C,UAAM,YAAY,KAAK,IAAI,GAAG,KAAK,OAAO,QAAQ,MAAM,KAAK;AAE7D,WAAO;AAAA,MACL;AAAA,MACA,OAAO,KAAK,OAAO;AAAA,MACnB;AAAA,MACA,OAAO,MAAM;AAAA,IACf;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,UAAgB;AACd,UAAM,MAAM,KAAK,IAAI;AACrB,eAAW,CAAC,KAAK,KAAK,KAAK,KAAK,MAAM,QAAQ,GAAG;AAC/C,UAAI,MAAM,MAAM,WAAW;AACzB,aAAK,MAAM,OAAO,GAAG;AAAA,MACvB;AAAA,IACF;AAAA,EACF;AACF;AAaO,SAAS,eAAe,SAA0C;AAEvE,QAAM,UAAU,QAAQ,QAAQ,IAAI,qBAAqB;AACzD,QAAM,SAAS,QAAQ,QAAQ,IAAI,4BAA4B;AAC/D,QAAM,OAAO,QAAQ,QAAQ,IAAI,kBAAkB;AACnD,QAAM,WAAW,QAAQ,QAAQ,IAAI,sBAAsB;AAC3D,QAAM,YAAY,QAAQ,QAAQ,IAAI,uBAAuB;AAE7D,MAAI,CAAC,SAAS;AAEZ,UAAM,YAAY,QAAQ,QAAQ,IAAI,cAAc;AACpD,QAAI,WAAW;AACb,aAAO;AAAA,QACL,SAAS;AAAA,MACX;AAAA,IACF;AAEA,WAAO;AAAA,EACT;AAEA,SAAO;AAAA,IACL,SAAS,WAAW;AAAA,IACpB,QAAQ,UAAU;AAAA,IAClB,MAAM,OAAO,mBAAmB,IAAI,IAAI;AAAA,IACxC,UAAU,WAAW,WAAW,QAAQ,IAAI;AAAA,IAC5C,WAAW,YAAY,WAAW,SAAS,IAAI;AAAA,EACjD;AACF;AAKO,SAAS,iBACd,SACA,UACA,UACQ;AAER,QAAM,aAAa,WAAW,QAAQ;AACtC,QAAM,gBAAgB,QAAQ,QAAQ,IAAI,UAAU,GAAG;AAEvD,MAAI,iBAAiB,SAAS,SAAS,aAAa,GAAG;AACrD,WAAO;AAAA,EACT;AAGA,QAAM,KAAK,QAAQ,QAAQ,IAAI,iBAAiB,KAAK;AACrD,QAAM,OAAO,WAAW,KAAK,QAAQ;AACrC,QAAM,eAAe,OAAO,SAAS;AACrC,QAAM,UAAU,SAAS,YAAY;AAErC,MAAI,CAAC,SAAS;AACZ,UAAM,IAAI,MAAM,+BAA+B;AAAA,EACjD;AAEA,SAAO;AACT;AAKA,SAAS,WAAW,KAAqB;AACvC,MAAI,OAAO;AACX,WAAS,IAAI,GAAG,IAAI,IAAI,QAAQ,KAAK;AACnC,UAAM,OAAO,IAAI,WAAW,CAAC;AAC7B,YAAQ,QAAQ,KAAK,OAAO;AAC5B,WAAO,OAAO;AAAA,EAChB;AACA,SAAO,KAAK,IAAI,IAAI;AACtB;AAaO,SAAS,yBAAyB,SAA6C;AACpF,QAAM,YAAY,QAAQ,QAAQ,IAAI,YAAY,KAAK;AACvD,QAAM,SAAS,cAAc,SAAS;AACtC,QAAM,WAAW,eAAe,OAAO;AAEvC,SAAO;AAAA,IACL,QAAQ,QAAQ,QAAQ,IAAI,SAAS,GAAG;AAAA,IACxC,UAAU,YAAY;AAAA,IACtB;AAAA,EACF;AACF;AAKA,SAAS,cAAc,WAAoD;AACzE,MAAI,UAAU,KAAK,SAAS,KAAK,CAAC,eAAe,KAAK,SAAS,GAAG;AAChE,WAAO;AAAA,EACT;AACA,MAAI,eAAe,KAAK,SAAS,GAAG;AAClC,WAAO;AAAA,EACT;AACA,SAAO;AACT;AAKO,SAAS,oBACd,UACA,QAMc;AACd,QAAM,eAAyB,CAAC;AAEhC,MAAI,OAAO,WAAW,QAAW;AAC/B,iBAAa,KAAK,WAAW,OAAO,MAAM,EAAE;AAAA,EAC9C;AAEA,MAAI,OAAO,YAAY,QAAW;AAChC,iBAAa,KAAK,YAAY,OAAO,OAAO,EAAE;AAAA,EAChD;AAEA,MAAI,OAAO,yBAAyB,QAAW;AAC7C,iBAAa,KAAK,0BAA0B,OAAO,oBAAoB,EAAE;AAAA,EAC3E;AAEA,MAAI,aAAa,SAAS,GAAG;AAC3B,aAAS,QAAQ,IAAI,iBAAiB,aAAa,KAAK,IAAI,CAAC;AAAA,EAC/D;AAEA,MAAI,OAAO,QAAQ,OAAO,KAAK,SAAS,GAAG;AACzC,aAAS,QAAQ,IAAI,aAAa,OAAO,KAAK,KAAK,GAAG,CAAC;AAAA,EACzD;AAEA,SAAO;AACT;AAKO,SAAS,gBACd,UACA,WAMc;AACd,QAAM,QAAQ,UAAU,IAAI,CAAC,aAAa;AACxC,UAAM,QAAQ,CAAC,IAAI,SAAS,IAAI,KAAK,iBAAiB,OAAO,SAAS,EAAE,GAAG;AAE3E,QAAI,SAAS,MAAM;AACjB,YAAM,KAAK,SAAS,SAAS,IAAI,GAAG;AAAA,IACtC;AAEA,QAAI,SAAS,aAAa;AACxB,YAAM,KAAK,aAAa;AAAA,IAC1B;AAEA,WAAO,MAAM,KAAK,IAAI;AAAA,EACxB,CAAC;AAED,MAAI,MAAM,SAAS,GAAG;AACpB,aAAS,QAAQ,IAAI,QAAQ,MAAM,KAAK,IAAI,CAAC;AAAA,EAC/C;AAEA,SAAO;AACT;AAKA,eAAsB,aACpB,OACA,UAAkB,QAAQ,IAAI,mBAAmB,yBAKhD;AACD,QAAM,UAAU,MAAM,QAAQ;AAAA,IAC5B,MAAM,IAAI,OAAO,SAAS;AACxB,YAAM,MAAM,IAAI,IAAI,MAAM,OAAO;AACjC,YAAM,WAAW,MAAM,MAAM,IAAI,SAAS,CAAC;AAE3C,UAAI,CAAC,SAAS,IAAI;AAChB,cAAM,IAAI,MAAM,GAAG,SAAS,MAAM,IAAI,SAAS,UAAU,EAAE;AAAA,MAC7D;AAEA,aAAO;AAAA,IACT,CAAC;AAAA,EACH;AAEA,MAAI,SAAS;AACb,MAAI,SAAS;AACb,QAAM,SAAiD,CAAC;AAExD,WAAS,IAAI,GAAG,IAAI,QAAQ,QAAQ,KAAK;AACvC,UAAM,SAAS,QAAQ,CAAC;AACxB,UAAM,OAAO,MAAM,CAAC;AAEpB,QAAI,EAAE,UAAU,OAAO;AACrB;AAAA,IACF;AAEA,QAAI,OAAO,WAAW,aAAa;AACjC;AAAA,IACF,OAAO;AACL;AACA,aAAO,KAAK;AAAA,QACV;AAAA,QACA,OACE,OAAO,kBAAkB,QACrB,OAAO,OAAO,UACd,OAAO,OAAO,MAAM,KAAK;AAAA,MACjC,CAAC;AAAA,IACH;AAAA,EACF;AAEA,SAAO,EAAE,QAAQ,QAAQ,OAAO;AAClC;","names":[]}
1
+ {"version":3,"sources":["../src/cdn-config.ts","../src/logger.ts","../src/edge-cache.ts"],"sourcesContent":["/**\n * CDN Configuration and Cache Management\n *\n * Utilities for CDN caching, edge caching, and cache invalidation\n */\n\n/**\n * CDN Cache Configuration\n */\nexport interface CDNCacheConfig {\n provider?: 'cloudflare' | 'vercel' | 'fastly' | 'custom';\n zones?: string[];\n ttl?: number;\n staleWhileRevalidate?: number;\n staleIfError?: number;\n bypassCache?: boolean;\n cacheKey?: string[];\n varyHeaders?: string[];\n}\n\nexport const DEFAULT_CDN_CONFIG: CDNCacheConfig = {\n provider: 'vercel',\n ttl: 31536000, // 1 year for static assets\n staleWhileRevalidate: 86400, // 1 day\n staleIfError: 604800, // 1 week\n bypassCache: false,\n cacheKey: ['url', 'headers.accept', 'headers.accept-encoding'],\n varyHeaders: ['Accept', 'Accept-Encoding'],\n};\n\n/**\n * Generate Cache-Control header\n */\nexport function generateCacheControl(config: {\n maxAge?: number;\n sMaxAge?: number;\n staleWhileRevalidate?: number;\n staleIfError?: number;\n public?: boolean;\n private?: boolean;\n immutable?: boolean;\n noCache?: boolean;\n noStore?: boolean;\n}): string {\n const directives: string[] = [];\n\n // Visibility\n if (config.noStore) {\n directives.push('no-store');\n return directives.join(', ');\n }\n\n if (config.noCache) {\n directives.push('no-cache');\n return directives.join(', ');\n }\n\n if (config.public) {\n directives.push('public');\n } else if (config.private) {\n directives.push('private');\n }\n\n // Max age\n if (config.maxAge !== undefined) {\n directives.push(`max-age=${config.maxAge}`);\n }\n\n // Shared max age (CDN)\n if (config.sMaxAge !== undefined) {\n directives.push(`s-maxage=${config.sMaxAge}`);\n }\n\n // Stale-while-revalidate\n if (config.staleWhileRevalidate !== undefined) {\n directives.push(`stale-while-revalidate=${config.staleWhileRevalidate}`);\n }\n\n // Stale-if-error\n if (config.staleIfError !== undefined) {\n directives.push(`stale-if-error=${config.staleIfError}`);\n }\n\n // Immutable\n if (config.immutable) {\n directives.push('immutable');\n }\n\n return directives.join(', ');\n}\n\n/**\n * Cache presets for different asset types\n */\nexport const CDN_CACHE_PRESETS = {\n // Static assets with hashed filenames (immutable)\n immutable: {\n maxAge: 31536000, // 1 year\n sMaxAge: 31536000,\n public: true,\n immutable: true,\n },\n\n // Static assets (images, fonts)\n static: {\n maxAge: 2592000, // 30 days\n sMaxAge: 31536000, // 1 year on CDN\n staleWhileRevalidate: 86400, // 1 day\n public: true,\n },\n\n // API responses (short-lived)\n api: {\n maxAge: 0,\n sMaxAge: 60, // 1 minute on CDN\n staleWhileRevalidate: 30,\n public: true,\n },\n\n // HTML pages (dynamic)\n page: {\n maxAge: 0,\n sMaxAge: 300, // 5 minutes on CDN\n staleWhileRevalidate: 60,\n public: true,\n },\n\n // User-specific data\n private: {\n maxAge: 300, // 5 minutes\n private: true,\n staleWhileRevalidate: 60,\n },\n\n // No caching\n noCache: {\n noStore: true,\n },\n\n // Revalidate every request\n revalidate: {\n maxAge: 0,\n sMaxAge: 0,\n noCache: true,\n },\n} as const;\n\n/**\n * CDN Purge Configuration\n */\nexport interface CDNPurgeConfig {\n provider: 'cloudflare' | 'vercel' | 'fastly';\n apiKey?: string;\n apiSecret?: string;\n zoneId?: string;\n distributionId?: string;\n}\n\n/**\n * Purge CDN cache\n */\nexport async function purgeCDNCache(\n urls: string[],\n config: CDNPurgeConfig,\n): Promise<{ success: boolean; purged: number; errors?: string[] }> {\n const { provider } = config;\n\n switch (provider) {\n case 'cloudflare':\n return purgeCloudflare(urls, config);\n case 'vercel':\n return purgeVercel(urls, config);\n case 'fastly':\n return purgeFastly(urls, config);\n default:\n throw new Error(`Unsupported CDN provider: ${provider}`);\n }\n}\n\n/**\n * Purge Cloudflare cache\n */\nasync function purgeCloudflare(\n urls: string[],\n config: CDNPurgeConfig,\n): Promise<{ success: boolean; purged: number; errors?: string[] }> {\n const { apiKey, zoneId } = config;\n\n if (!(apiKey && zoneId)) {\n throw new Error('Cloudflare API key and zone ID required');\n }\n\n try {\n const response = await fetch(\n `https://api.cloudflare.com/client/v4/zones/${zoneId}/purge_cache`,\n {\n method: 'POST',\n headers: {\n Authorization: `Bearer ${apiKey}`,\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({ files: urls }),\n },\n );\n\n const data = await response.json();\n\n return {\n success: data.success,\n purged: urls.length,\n errors: data.errors,\n };\n } catch (error) {\n return {\n success: false,\n purged: 0,\n errors: [error instanceof Error ? error.message : 'Unknown error'],\n };\n }\n}\n\n/**\n * Purge Vercel cache\n */\nasync function purgeVercel(\n urls: string[],\n config: CDNPurgeConfig,\n): Promise<{ success: boolean; purged: number; errors?: string[] }> {\n const { apiKey } = config;\n\n if (!apiKey) {\n throw new Error('Vercel API token required');\n }\n\n try {\n const response = await fetch('https://api.vercel.com/v1/purge', {\n method: 'POST',\n headers: {\n Authorization: `Bearer ${apiKey}`,\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({ urls }),\n });\n\n const data = await response.json();\n\n return {\n success: response.ok,\n purged: urls.length,\n errors: data.error ? [data.error.message] : undefined,\n };\n } catch (error) {\n return {\n success: false,\n purged: 0,\n errors: [error instanceof Error ? error.message : 'Unknown error'],\n };\n }\n}\n\n/**\n * Purge Fastly cache\n */\nasync function purgeFastly(\n urls: string[],\n config: CDNPurgeConfig,\n): Promise<{ success: boolean; purged: number; errors?: string[] }> {\n const { apiKey } = config;\n\n if (!apiKey) {\n throw new Error('Fastly API key required');\n }\n\n try {\n const results = await Promise.all(\n urls.map(async (url) => {\n const response = await fetch(url, {\n method: 'PURGE',\n headers: {\n 'Fastly-Key': apiKey,\n },\n });\n\n return response.ok;\n }),\n );\n\n const purged = results.filter(Boolean).length;\n\n return {\n success: purged === urls.length,\n purged,\n };\n } catch (error) {\n return {\n success: false,\n purged: 0,\n errors: [error instanceof Error ? error.message : 'Unknown error'],\n };\n }\n}\n\n/**\n * Purge by cache tag\n */\nexport async function purgeCacheByTag(\n tags: string[],\n config: CDNPurgeConfig,\n): Promise<{ success: boolean; purged: number; errors?: string[] }> {\n const { provider, apiKey, zoneId } = config;\n\n if (provider === 'cloudflare') {\n if (!(apiKey && zoneId)) {\n throw new Error('Cloudflare API key and zone ID required');\n }\n\n try {\n const response = await fetch(\n `https://api.cloudflare.com/client/v4/zones/${zoneId}/purge_cache`,\n {\n method: 'POST',\n headers: {\n Authorization: `Bearer ${apiKey}`,\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({ tags }),\n },\n );\n\n const data = await response.json();\n\n return {\n success: data.success,\n purged: tags.length,\n errors: data.errors,\n };\n } catch (error) {\n return {\n success: false,\n purged: 0,\n errors: [error instanceof Error ? error.message : 'Unknown error'],\n };\n }\n }\n\n throw new Error(`Cache tag purging not supported for ${provider}`);\n}\n\n/**\n * Purge everything\n */\nexport async function purgeAllCache(\n config: CDNPurgeConfig,\n): Promise<{ success: boolean; errors?: string[] }> {\n const { provider, apiKey, zoneId } = config;\n\n if (provider === 'cloudflare') {\n if (!(apiKey && zoneId)) {\n throw new Error('Cloudflare API key and zone ID required');\n }\n\n try {\n const response = await fetch(\n `https://api.cloudflare.com/client/v4/zones/${zoneId}/purge_cache`,\n {\n method: 'POST',\n headers: {\n Authorization: `Bearer ${apiKey}`,\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({ purge_everything: true }),\n },\n );\n\n const data = await response.json();\n\n return {\n success: data.success,\n errors: data.errors,\n };\n } catch (error) {\n return {\n success: false,\n errors: [error instanceof Error ? error.message : 'Unknown error'],\n };\n }\n }\n\n throw new Error(`Purge all not supported for ${provider}`);\n}\n\n/**\n * CDN cache warming\n */\nexport async function warmCDNCache(\n urls: string[],\n options: {\n concurrency?: number;\n headers?: Record<string, string>;\n } = {},\n): Promise<{ warmed: number; failed: number; errors: string[] }> {\n const { concurrency = 5, headers = {} } = options;\n\n const results: { success: boolean; error?: string }[] = [];\n const chunks: string[][] = [];\n\n // Split into chunks\n for (let i = 0; i < urls.length; i += concurrency) {\n chunks.push(urls.slice(i, i + concurrency));\n }\n\n // Warm cache in chunks\n for (const chunk of chunks) {\n const chunkResults = await Promise.all(\n chunk.map(async (url) => {\n try {\n const response = await fetch(url, { headers });\n return {\n success: response.ok,\n error: response.ok ? undefined : `${response.status} ${response.statusText}`,\n };\n } catch (error) {\n return {\n success: false,\n error: error instanceof Error ? error.message : 'Unknown error',\n };\n }\n }),\n );\n\n results.push(...chunkResults);\n }\n\n const warmed = results.filter((r) => r.success).length;\n const failed = results.filter((r) => !r.success).length;\n const errors = results.flatMap((r) => (r.error ? [r.error] : []));\n\n return { warmed, failed, errors };\n}\n\n/**\n * Generate cache tags\n */\nexport function generateCacheTags(resource: {\n type: string;\n id?: string | number;\n related?: string[];\n}): string[] {\n const tags: string[] = [];\n\n // Type tag\n tags.push(resource.type);\n\n // ID tag\n if (resource.id) {\n tags.push(`${resource.type}:${resource.id}`);\n }\n\n // Related tags\n if (resource.related) {\n tags.push(...resource.related);\n }\n\n return tags;\n}\n\n/**\n * Edge cache configuration for Vercel\n */\nexport function generateVercelCacheConfig(preset: keyof typeof CDN_CACHE_PRESETS) {\n const config = CDN_CACHE_PRESETS[preset];\n const cacheControl = generateCacheControl(config);\n\n return {\n headers: {\n 'Cache-Control': cacheControl,\n 'CDN-Cache-Control': cacheControl,\n 'Vercel-CDN-Cache-Control': cacheControl,\n },\n };\n}\n\n/**\n * Edge cache configuration for Cloudflare\n */\nexport function generateCloudflareConfig(\n preset: keyof typeof CDN_CACHE_PRESETS,\n options: {\n cacheTags?: string[];\n bypassOnCookie?: string;\n } = {},\n) {\n const config = CDN_CACHE_PRESETS[preset];\n const cacheControl = generateCacheControl(config);\n\n const headers: Record<string, string> = {\n 'Cache-Control': cacheControl,\n };\n\n // Cache tags\n if (options.cacheTags && options.cacheTags.length > 0) {\n headers['Cache-Tag'] = options.cacheTags.join(',');\n }\n\n // Bypass on cookie\n if (options.bypassOnCookie) {\n headers['Cache-Control'] = `${cacheControl}, bypass=${options.bypassOnCookie}`;\n }\n\n return { headers };\n}\n\n/**\n * Check if response should be cached\n */\nexport function shouldCacheResponse(status: number, headers: Headers): boolean {\n // Don't cache errors\n if (status >= 400) {\n return false;\n }\n\n // Check Cache-Control header\n const cacheControl = headers.get('cache-control') || '';\n if (\n cacheControl.includes('no-store') ||\n cacheControl.includes('no-cache') ||\n cacheControl.includes('private')\n ) {\n return false;\n }\n\n return true;\n}\n\n/**\n * Calculate cache TTL from headers\n */\nexport function getCacheTTL(headers: Headers): number {\n const cacheControl = headers.get('cache-control') || '';\n\n // Check s-maxage first (CDN), then max-age\n for (const directive of cacheControl.split(',')) {\n const trimmed = directive.trim();\n if (trimmed.startsWith('s-maxage=')) {\n const val = trimmed.slice('s-maxage='.length);\n const num = Number.parseInt(val, 10);\n if (!Number.isNaN(num)) return num;\n }\n }\n for (const directive of cacheControl.split(',')) {\n const trimmed = directive.trim();\n if (trimmed.startsWith('max-age=')) {\n const val = trimmed.slice('max-age='.length);\n const num = Number.parseInt(val, 10);\n if (!Number.isNaN(num)) return num;\n }\n }\n\n // Check Expires header\n const expires = headers.get('expires');\n if (expires) {\n const expiresDate = new Date(expires);\n const now = new Date();\n return Math.max(0, Math.floor((expiresDate.getTime() - now.getTime()) / 1000));\n }\n\n return 0;\n}\n","/**\n * Internal logger for @revealui/cache.\n *\n * Defaults to `console`. Consumers should call `configureCacheLogger()`\n * to supply a structured logger (e.g. from `@revealui/utils/logger`).\n */\n\nexport interface CacheLogger {\n warn(message: string, ...args: unknown[]): void;\n error(message: string, ...args: unknown[]): void;\n info(message: string, ...args: unknown[]): void;\n debug(message: string, ...args: unknown[]): void;\n}\n\nlet cacheLogger: CacheLogger = console;\n\n/**\n * Replace the default console logger with a structured logger.\n */\nexport function configureCacheLogger(logger: CacheLogger): void {\n cacheLogger = logger;\n}\n\n/**\n * Get the current cache logger instance.\n */\nexport function getCacheLogger(): CacheLogger {\n return cacheLogger;\n}\n","/**\n * Edge Caching and ISR (Incremental Static Regeneration)\n *\n * Utilities for Next.js edge caching, ISR, and on-demand revalidation\n */\n\nimport type { NextRequest, NextResponse } from 'next/server';\nimport { getCacheLogger } from './logger.js';\n\n/**\n * Next.js extends the standard RequestInit with a `next` property\n * for ISR revalidation and cache tags.\n */\ninterface NextFetchRequestInit extends RequestInit {\n next?: {\n revalidate?: number | false;\n tags?: string[];\n };\n}\n\n/**\n * ISR Configuration\n */\nexport interface ISRConfig {\n revalidate?: number | false;\n tags?: string[];\n dynamicParams?: boolean;\n}\n\nexport const ISR_PRESETS = {\n // Revalidate every request\n always: {\n revalidate: 0,\n },\n\n // Revalidate every minute\n minute: {\n revalidate: 60,\n },\n\n // Revalidate every 5 minutes\n fiveMinutes: {\n revalidate: 300,\n },\n\n // Revalidate every hour\n hourly: {\n revalidate: 3600,\n },\n\n // Revalidate daily\n daily: {\n revalidate: 86400,\n },\n\n // Never revalidate (static)\n never: {\n revalidate: false,\n },\n} as const;\n\n/**\n * Generate static params for ISR\n */\nexport async function generateStaticParams<T>(\n fetchFn: () => Promise<T[]>,\n mapFn: (item: T) => Record<string, string>,\n): Promise<Array<Record<string, string>>> {\n try {\n const items = await fetchFn();\n return items.map(mapFn);\n } catch (error) {\n getCacheLogger().error(\n 'Failed to generate static params',\n error instanceof Error ? error : new Error(String(error)),\n );\n return [];\n }\n}\n\n/**\n * Revalidate tag\n */\nexport async function revalidateTag(\n tag: string,\n secret?: string,\n): Promise<{ revalidated: boolean; error?: string }> {\n const baseUrl = process.env.NEXT_PUBLIC_URL;\n if (!baseUrl) {\n getCacheLogger().warn('revalidateTag skipped: NEXT_PUBLIC_URL is not configured', { tag });\n return { revalidated: false, error: 'NEXT_PUBLIC_URL is not configured' };\n }\n\n try {\n const url = new URL('/api/revalidate', baseUrl);\n\n const headers: HeadersInit = { 'Content-Type': 'application/json' };\n if (secret) {\n headers['x-revalidate-secret'] = secret;\n }\n\n const response = await fetch(url.toString(), {\n method: 'POST',\n headers,\n body: JSON.stringify({ tag }),\n });\n\n const data = await response.json();\n\n if (!response.ok) {\n getCacheLogger().warn('revalidateTag failed', {\n tag,\n status: response.status,\n error: data.error,\n });\n }\n\n return {\n revalidated: response.ok,\n error: data.error,\n };\n } catch (error) {\n const message = error instanceof Error ? error.message : 'Unknown error';\n getCacheLogger().warn('revalidateTag error', { tag, error: message });\n return {\n revalidated: false,\n error: message,\n };\n }\n}\n\n/**\n * Revalidate path\n */\nexport async function revalidatePath(\n path: string,\n secret?: string,\n): Promise<{ revalidated: boolean; error?: string }> {\n const baseUrl = process.env.NEXT_PUBLIC_URL;\n if (!baseUrl) {\n getCacheLogger().warn('revalidatePath skipped: NEXT_PUBLIC_URL is not configured', { path });\n return { revalidated: false, error: 'NEXT_PUBLIC_URL is not configured' };\n }\n\n try {\n const url = new URL('/api/revalidate', baseUrl);\n\n const headers: HeadersInit = { 'Content-Type': 'application/json' };\n if (secret) {\n headers['x-revalidate-secret'] = secret;\n }\n\n const response = await fetch(url.toString(), {\n method: 'POST',\n headers,\n body: JSON.stringify({ path }),\n });\n\n const data = await response.json();\n\n return {\n revalidated: response.ok,\n error: data.error,\n };\n } catch (error) {\n return {\n revalidated: false,\n error: error instanceof Error ? error.message : 'Unknown error',\n };\n }\n}\n\n/**\n * Revalidate multiple paths\n */\nexport async function revalidatePaths(\n paths: string[],\n secret?: string,\n): Promise<{\n revalidated: number;\n failed: number;\n errors: Array<{ path: string; error: string }>;\n}> {\n const results = await Promise.allSettled(paths.map((path) => revalidatePath(path, secret)));\n\n let revalidated = 0;\n let failed = 0;\n const errors: Array<{ path: string; error: string }> = [];\n\n for (let i = 0; i < results.length; i++) {\n const result = results[i];\n const path = paths[i];\n\n if (!(result && path)) {\n continue;\n }\n\n if (result.status === 'fulfilled' && result.value.revalidated) {\n revalidated++;\n } else {\n failed++;\n const error =\n result.status === 'fulfilled'\n ? result.value.error || 'Unknown error'\n : String(result.reason) || 'Unknown error';\n\n errors.push({ path, error });\n }\n }\n\n return { revalidated, failed, errors };\n}\n\n/**\n * Revalidate multiple tags\n */\nexport async function revalidateTags(\n tags: string[],\n secret?: string,\n): Promise<{\n revalidated: number;\n failed: number;\n errors: Array<{ tag: string; error: string }>;\n}> {\n const results = await Promise.allSettled(tags.map((tag) => revalidateTag(tag, secret)));\n\n let revalidated = 0;\n let failed = 0;\n const errors: Array<{ tag: string; error: string }> = [];\n\n for (let i = 0; i < results.length; i++) {\n const result = results[i];\n const tag = tags[i];\n\n if (!(result && tag)) {\n continue;\n }\n\n if (result.status === 'fulfilled' && result.value.revalidated) {\n revalidated++;\n } else {\n failed++;\n const error =\n result.status === 'fulfilled'\n ? result.value.error || 'Unknown error'\n : String(result.reason) || 'Unknown error';\n\n errors.push({ tag, error });\n }\n }\n\n return { revalidated, failed, errors };\n}\n\n/**\n * Edge middleware cache configuration\n */\nexport interface EdgeCacheConfig {\n cache?: 'force-cache' | 'no-cache' | 'no-store' | 'only-if-cached';\n next?: {\n revalidate?: number | false;\n tags?: string[];\n };\n}\n\n/**\n * Create edge cached fetch\n */\nexport function createEdgeCachedFetch(config: EdgeCacheConfig = {}) {\n return async <T>(url: string, options?: NextFetchRequestInit): Promise<T> => {\n const fetchOptions: NextFetchRequestInit = {\n ...options,\n ...config,\n next: {\n ...options?.next,\n ...config.next,\n },\n };\n\n const response = await fetch(url, fetchOptions);\n\n if (!response.ok) {\n throw new Error(`Fetch failed: ${response.statusText}`);\n }\n\n return response.json();\n };\n}\n\n/**\n * Unstable cache wrapper (Next.js 14+)\n */\nexport function createCachedFunction<TArgs extends unknown[], TReturn>(\n fn: (...args: TArgs) => Promise<TReturn>,\n options: {\n tags?: string[];\n revalidate?: number | false;\n } = {},\n): (...args: TArgs) => Promise<TReturn> {\n // If revalidation is disabled, bypass cache entirely\n if (options.revalidate === false) {\n return fn;\n }\n\n const ttlMs = (options.revalidate ?? 60) * 1000;\n const cache = new Map<string, { value: TReturn; expiresAt: number }>();\n\n return async (...args: TArgs): Promise<TReturn> => {\n const key = JSON.stringify(args);\n const now = Date.now();\n const cached = cache.get(key);\n\n if (cached && now < cached.expiresAt) {\n return cached.value;\n }\n\n const value = await fn(...args);\n cache.set(key, { value, expiresAt: now + ttlMs });\n return value;\n };\n}\n\n/**\n * Edge rate limiting with cache\n */\nexport interface EdgeRateLimitConfig {\n limit: number;\n window: number;\n key?: (request: NextRequest) => string;\n}\n\nexport class EdgeRateLimiter {\n private cache: Map<string, { count: number; resetTime: number }> = new Map();\n\n constructor(private config: EdgeRateLimitConfig) {}\n\n /**\n * Check rate limit\n */\n check(request: NextRequest): {\n allowed: boolean;\n limit: number;\n remaining: number;\n reset: number;\n } {\n const key = this.config.key\n ? this.config.key(request)\n : request.headers.get('x-forwarded-for') || 'unknown';\n\n const now = Date.now();\n let entry = this.cache.get(key);\n\n // Reset if window expired\n if (!entry || now > entry.resetTime) {\n entry = {\n count: 0,\n resetTime: now + this.config.window,\n };\n this.cache.set(key, entry);\n }\n\n // Increment count\n entry.count++;\n\n const allowed = entry.count <= this.config.limit;\n const remaining = Math.max(0, this.config.limit - entry.count);\n\n return {\n allowed,\n limit: this.config.limit,\n remaining,\n reset: entry.resetTime,\n };\n }\n\n /**\n * Clean up expired entries\n */\n cleanup(): void {\n const now = Date.now();\n for (const [key, entry] of this.cache.entries()) {\n if (now > entry.resetTime) {\n this.cache.delete(key);\n }\n }\n }\n}\n\n/**\n * Edge geolocation caching\n */\nexport interface GeoLocation {\n country?: string;\n region?: string;\n city?: string;\n latitude?: number;\n longitude?: number;\n}\n\nexport function getGeoLocation(request: NextRequest): GeoLocation | null {\n // Vercel edge headers\n const country = request.headers.get('x-vercel-ip-country');\n const region = request.headers.get('x-vercel-ip-country-region');\n const city = request.headers.get('x-vercel-ip-city');\n const latitude = request.headers.get('x-vercel-ip-latitude');\n const longitude = request.headers.get('x-vercel-ip-longitude');\n\n if (!country) {\n // Cloudflare headers\n const cfCountry = request.headers.get('cf-ipcountry');\n if (cfCountry) {\n return {\n country: cfCountry,\n };\n }\n\n return null;\n }\n\n return {\n country: country || undefined,\n region: region || undefined,\n city: city ? decodeURIComponent(city) : undefined,\n latitude: latitude ? parseFloat(latitude) : undefined,\n longitude: longitude ? parseFloat(longitude) : undefined,\n };\n}\n\n/**\n * Edge A/B testing with cache\n */\nexport function getABTestVariant(\n request: NextRequest,\n testName: string,\n variants: string[],\n): string {\n // Check cookie first\n const cookieName = `ab-test-${testName}`;\n const cookieVariant = request.cookies.get(cookieName)?.value;\n\n if (cookieVariant && variants.includes(cookieVariant)) {\n return cookieVariant;\n }\n\n // Assign variant based on IP hash\n const ip = request.headers.get('x-forwarded-for') || 'unknown';\n const hash = simpleHash(ip + testName);\n const variantIndex = hash % variants.length;\n const variant = variants[variantIndex];\n\n if (!variant) {\n throw new Error('No variant found for A/B test');\n }\n\n return variant;\n}\n\n/**\n * Simple hash function\n */\nfunction simpleHash(str: string): number {\n let hash = 0;\n for (let i = 0; i < str.length; i++) {\n const char = str.charCodeAt(i);\n hash = (hash << 5) - hash + char;\n hash = hash & hash;\n }\n return Math.abs(hash);\n}\n\n/**\n * Edge personalization cache\n */\nexport interface PersonalizationConfig {\n userId?: string;\n preferences?: Record<string, unknown>;\n location?: GeoLocation;\n device?: 'mobile' | 'tablet' | 'desktop';\n variant?: string;\n}\n\nexport function getPersonalizationConfig(request: NextRequest): PersonalizationConfig {\n const userAgent = request.headers.get('user-agent') || '';\n const device = getDeviceType(userAgent);\n const location = getGeoLocation(request);\n\n return {\n userId: request.cookies.get('user-id')?.value,\n location: location || undefined,\n device,\n };\n}\n\n/**\n * Detect device type\n */\nfunction getDeviceType(userAgent: string): 'mobile' | 'tablet' | 'desktop' {\n const ua = userAgent.toLowerCase();\n const isTablet = ua.includes('tablet') || ua.includes('ipad');\n if (isTablet) return 'tablet';\n if (ua.includes('mobile')) return 'mobile';\n return 'desktop';\n}\n\n/**\n * Edge cache headers helper\n */\nexport function setEdgeCacheHeaders(\n response: NextResponse,\n config: {\n maxAge?: number;\n sMaxAge?: number;\n staleWhileRevalidate?: number;\n tags?: string[];\n },\n): NextResponse {\n const cacheControl: string[] = [];\n\n if (config.maxAge !== undefined) {\n cacheControl.push(`max-age=${config.maxAge}`);\n }\n\n if (config.sMaxAge !== undefined) {\n cacheControl.push(`s-maxage=${config.sMaxAge}`);\n }\n\n if (config.staleWhileRevalidate !== undefined) {\n cacheControl.push(`stale-while-revalidate=${config.staleWhileRevalidate}`);\n }\n\n if (cacheControl.length > 0) {\n response.headers.set('Cache-Control', cacheControl.join(', '));\n }\n\n if (config.tags && config.tags.length > 0) {\n response.headers.set('Cache-Tag', config.tags.join(','));\n }\n\n return response;\n}\n\n/**\n * Preload links for critical resources\n */\nexport function addPreloadLinks(\n response: NextResponse,\n resources: Array<{\n href: string;\n as: string;\n type?: string;\n crossorigin?: boolean;\n }>,\n): NextResponse {\n const links = resources.map((resource) => {\n const attrs = [`<${resource.href}>`, `rel=\"preload\"`, `as=\"${resource.as}\"`];\n\n if (resource.type) {\n attrs.push(`type=\"${resource.type}\"`);\n }\n\n if (resource.crossorigin) {\n attrs.push('crossorigin');\n }\n\n return attrs.join('; ');\n });\n\n if (links.length > 0) {\n response.headers.set('Link', links.join(', '));\n }\n\n return response;\n}\n\n/**\n * Cache warming for ISR pages\n */\nexport async function warmISRCache(\n paths: string[],\n baseURL: string = process.env.NEXT_PUBLIC_URL || 'http://localhost:3000',\n): Promise<{\n warmed: number;\n failed: number;\n errors: Array<{ path: string; error: string }>;\n}> {\n const results = await Promise.allSettled(\n paths.map(async (path) => {\n const url = new URL(path, baseURL);\n const response = await fetch(url.toString());\n\n if (!response.ok) {\n throw new Error(`${response.status} ${response.statusText}`);\n }\n\n return true;\n }),\n );\n\n let warmed = 0;\n let failed = 0;\n const errors: Array<{ path: string; error: string }> = [];\n\n for (let i = 0; i < results.length; i++) {\n const result = results[i];\n const path = paths[i];\n\n if (!(result && path)) {\n continue;\n }\n\n if (result.status === 'fulfilled') {\n warmed++;\n } else {\n failed++;\n errors.push({\n path,\n error:\n result.reason instanceof Error\n ? result.reason.message\n : String(result.reason) || 'Unknown error',\n });\n }\n }\n\n return { warmed, failed, errors };\n}\n"],"mappings":";AAoBO,IAAM,qBAAqC;AAAA,EAChD,UAAU;AAAA,EACV,KAAK;AAAA;AAAA,EACL,sBAAsB;AAAA;AAAA,EACtB,cAAc;AAAA;AAAA,EACd,aAAa;AAAA,EACb,UAAU,CAAC,OAAO,kBAAkB,yBAAyB;AAAA,EAC7D,aAAa,CAAC,UAAU,iBAAiB;AAC3C;AAKO,SAAS,qBAAqB,QAU1B;AACT,QAAM,aAAuB,CAAC;AAG9B,MAAI,OAAO,SAAS;AAClB,eAAW,KAAK,UAAU;AAC1B,WAAO,WAAW,KAAK,IAAI;AAAA,EAC7B;AAEA,MAAI,OAAO,SAAS;AAClB,eAAW,KAAK,UAAU;AAC1B,WAAO,WAAW,KAAK,IAAI;AAAA,EAC7B;AAEA,MAAI,OAAO,QAAQ;AACjB,eAAW,KAAK,QAAQ;AAAA,EAC1B,WAAW,OAAO,SAAS;AACzB,eAAW,KAAK,SAAS;AAAA,EAC3B;AAGA,MAAI,OAAO,WAAW,QAAW;AAC/B,eAAW,KAAK,WAAW,OAAO,MAAM,EAAE;AAAA,EAC5C;AAGA,MAAI,OAAO,YAAY,QAAW;AAChC,eAAW,KAAK,YAAY,OAAO,OAAO,EAAE;AAAA,EAC9C;AAGA,MAAI,OAAO,yBAAyB,QAAW;AAC7C,eAAW,KAAK,0BAA0B,OAAO,oBAAoB,EAAE;AAAA,EACzE;AAGA,MAAI,OAAO,iBAAiB,QAAW;AACrC,eAAW,KAAK,kBAAkB,OAAO,YAAY,EAAE;AAAA,EACzD;AAGA,MAAI,OAAO,WAAW;AACpB,eAAW,KAAK,WAAW;AAAA,EAC7B;AAEA,SAAO,WAAW,KAAK,IAAI;AAC7B;AAKO,IAAM,oBAAoB;AAAA;AAAA,EAE/B,WAAW;AAAA,IACT,QAAQ;AAAA;AAAA,IACR,SAAS;AAAA,IACT,QAAQ;AAAA,IACR,WAAW;AAAA,EACb;AAAA;AAAA,EAGA,QAAQ;AAAA,IACN,QAAQ;AAAA;AAAA,IACR,SAAS;AAAA;AAAA,IACT,sBAAsB;AAAA;AAAA,IACtB,QAAQ;AAAA,EACV;AAAA;AAAA,EAGA,KAAK;AAAA,IACH,QAAQ;AAAA,IACR,SAAS;AAAA;AAAA,IACT,sBAAsB;AAAA,IACtB,QAAQ;AAAA,EACV;AAAA;AAAA,EAGA,MAAM;AAAA,IACJ,QAAQ;AAAA,IACR,SAAS;AAAA;AAAA,IACT,sBAAsB;AAAA,IACtB,QAAQ;AAAA,EACV;AAAA;AAAA,EAGA,SAAS;AAAA,IACP,QAAQ;AAAA;AAAA,IACR,SAAS;AAAA,IACT,sBAAsB;AAAA,EACxB;AAAA;AAAA,EAGA,SAAS;AAAA,IACP,SAAS;AAAA,EACX;AAAA;AAAA,EAGA,YAAY;AAAA,IACV,QAAQ;AAAA,IACR,SAAS;AAAA,IACT,SAAS;AAAA,EACX;AACF;AAgBA,eAAsB,cACpB,MACA,QACkE;AAClE,QAAM,EAAE,SAAS,IAAI;AAErB,UAAQ,UAAU;AAAA,IAChB,KAAK;AACH,aAAO,gBAAgB,MAAM,MAAM;AAAA,IACrC,KAAK;AACH,aAAO,YAAY,MAAM,MAAM;AAAA,IACjC,KAAK;AACH,aAAO,YAAY,MAAM,MAAM;AAAA,IACjC;AACE,YAAM,IAAI,MAAM,6BAA6B,QAAQ,EAAE;AAAA,EAC3D;AACF;AAKA,eAAe,gBACb,MACA,QACkE;AAClE,QAAM,EAAE,QAAQ,OAAO,IAAI;AAE3B,MAAI,EAAE,UAAU,SAAS;AACvB,UAAM,IAAI,MAAM,yCAAyC;AAAA,EAC3D;AAEA,MAAI;AACF,UAAM,WAAW,MAAM;AAAA,MACrB,8CAA8C,MAAM;AAAA,MACpD;AAAA,QACE,QAAQ;AAAA,QACR,SAAS;AAAA,UACP,eAAe,UAAU,MAAM;AAAA,UAC/B,gBAAgB;AAAA,QAClB;AAAA,QACA,MAAM,KAAK,UAAU,EAAE,OAAO,KAAK,CAAC;AAAA,MACtC;AAAA,IACF;AAEA,UAAM,OAAO,MAAM,SAAS,KAAK;AAEjC,WAAO;AAAA,MACL,SAAS,KAAK;AAAA,MACd,QAAQ,KAAK;AAAA,MACb,QAAQ,KAAK;AAAA,IACf;AAAA,EACF,SAAS,OAAO;AACd,WAAO;AAAA,MACL,SAAS;AAAA,MACT,QAAQ;AAAA,MACR,QAAQ,CAAC,iBAAiB,QAAQ,MAAM,UAAU,eAAe;AAAA,IACnE;AAAA,EACF;AACF;AAKA,eAAe,YACb,MACA,QACkE;AAClE,QAAM,EAAE,OAAO,IAAI;AAEnB,MAAI,CAAC,QAAQ;AACX,UAAM,IAAI,MAAM,2BAA2B;AAAA,EAC7C;AAEA,MAAI;AACF,UAAM,WAAW,MAAM,MAAM,mCAAmC;AAAA,MAC9D,QAAQ;AAAA,MACR,SAAS;AAAA,QACP,eAAe,UAAU,MAAM;AAAA,QAC/B,gBAAgB;AAAA,MAClB;AAAA,MACA,MAAM,KAAK,UAAU,EAAE,KAAK,CAAC;AAAA,IAC/B,CAAC;AAED,UAAM,OAAO,MAAM,SAAS,KAAK;AAEjC,WAAO;AAAA,MACL,SAAS,SAAS;AAAA,MAClB,QAAQ,KAAK;AAAA,MACb,QAAQ,KAAK,QAAQ,CAAC,KAAK,MAAM,OAAO,IAAI;AAAA,IAC9C;AAAA,EACF,SAAS,OAAO;AACd,WAAO;AAAA,MACL,SAAS;AAAA,MACT,QAAQ;AAAA,MACR,QAAQ,CAAC,iBAAiB,QAAQ,MAAM,UAAU,eAAe;AAAA,IACnE;AAAA,EACF;AACF;AAKA,eAAe,YACb,MACA,QACkE;AAClE,QAAM,EAAE,OAAO,IAAI;AAEnB,MAAI,CAAC,QAAQ;AACX,UAAM,IAAI,MAAM,yBAAyB;AAAA,EAC3C;AAEA,MAAI;AACF,UAAM,UAAU,MAAM,QAAQ;AAAA,MAC5B,KAAK,IAAI,OAAO,QAAQ;AACtB,cAAM,WAAW,MAAM,MAAM,KAAK;AAAA,UAChC,QAAQ;AAAA,UACR,SAAS;AAAA,YACP,cAAc;AAAA,UAChB;AAAA,QACF,CAAC;AAED,eAAO,SAAS;AAAA,MAClB,CAAC;AAAA,IACH;AAEA,UAAM,SAAS,QAAQ,OAAO,OAAO,EAAE;AAEvC,WAAO;AAAA,MACL,SAAS,WAAW,KAAK;AAAA,MACzB;AAAA,IACF;AAAA,EACF,SAAS,OAAO;AACd,WAAO;AAAA,MACL,SAAS;AAAA,MACT,QAAQ;AAAA,MACR,QAAQ,CAAC,iBAAiB,QAAQ,MAAM,UAAU,eAAe;AAAA,IACnE;AAAA,EACF;AACF;AAKA,eAAsB,gBACpB,MACA,QACkE;AAClE,QAAM,EAAE,UAAU,QAAQ,OAAO,IAAI;AAErC,MAAI,aAAa,cAAc;AAC7B,QAAI,EAAE,UAAU,SAAS;AACvB,YAAM,IAAI,MAAM,yCAAyC;AAAA,IAC3D;AAEA,QAAI;AACF,YAAM,WAAW,MAAM;AAAA,QACrB,8CAA8C,MAAM;AAAA,QACpD;AAAA,UACE,QAAQ;AAAA,UACR,SAAS;AAAA,YACP,eAAe,UAAU,MAAM;AAAA,YAC/B,gBAAgB;AAAA,UAClB;AAAA,UACA,MAAM,KAAK,UAAU,EAAE,KAAK,CAAC;AAAA,QAC/B;AAAA,MACF;AAEA,YAAM,OAAO,MAAM,SAAS,KAAK;AAEjC,aAAO;AAAA,QACL,SAAS,KAAK;AAAA,QACd,QAAQ,KAAK;AAAA,QACb,QAAQ,KAAK;AAAA,MACf;AAAA,IACF,SAAS,OAAO;AACd,aAAO;AAAA,QACL,SAAS;AAAA,QACT,QAAQ;AAAA,QACR,QAAQ,CAAC,iBAAiB,QAAQ,MAAM,UAAU,eAAe;AAAA,MACnE;AAAA,IACF;AAAA,EACF;AAEA,QAAM,IAAI,MAAM,uCAAuC,QAAQ,EAAE;AACnE;AAKA,eAAsB,cACpB,QACkD;AAClD,QAAM,EAAE,UAAU,QAAQ,OAAO,IAAI;AAErC,MAAI,aAAa,cAAc;AAC7B,QAAI,EAAE,UAAU,SAAS;AACvB,YAAM,IAAI,MAAM,yCAAyC;AAAA,IAC3D;AAEA,QAAI;AACF,YAAM,WAAW,MAAM;AAAA,QACrB,8CAA8C,MAAM;AAAA,QACpD;AAAA,UACE,QAAQ;AAAA,UACR,SAAS;AAAA,YACP,eAAe,UAAU,MAAM;AAAA,YAC/B,gBAAgB;AAAA,UAClB;AAAA,UACA,MAAM,KAAK,UAAU,EAAE,kBAAkB,KAAK,CAAC;AAAA,QACjD;AAAA,MACF;AAEA,YAAM,OAAO,MAAM,SAAS,KAAK;AAEjC,aAAO;AAAA,QACL,SAAS,KAAK;AAAA,QACd,QAAQ,KAAK;AAAA,MACf;AAAA,IACF,SAAS,OAAO;AACd,aAAO;AAAA,QACL,SAAS;AAAA,QACT,QAAQ,CAAC,iBAAiB,QAAQ,MAAM,UAAU,eAAe;AAAA,MACnE;AAAA,IACF;AAAA,EACF;AAEA,QAAM,IAAI,MAAM,+BAA+B,QAAQ,EAAE;AAC3D;AAKA,eAAsB,aACpB,MACA,UAGI,CAAC,GAC0D;AAC/D,QAAM,EAAE,cAAc,GAAG,UAAU,CAAC,EAAE,IAAI;AAE1C,QAAM,UAAkD,CAAC;AACzD,QAAM,SAAqB,CAAC;AAG5B,WAAS,IAAI,GAAG,IAAI,KAAK,QAAQ,KAAK,aAAa;AACjD,WAAO,KAAK,KAAK,MAAM,GAAG,IAAI,WAAW,CAAC;AAAA,EAC5C;AAGA,aAAW,SAAS,QAAQ;AAC1B,UAAM,eAAe,MAAM,QAAQ;AAAA,MACjC,MAAM,IAAI,OAAO,QAAQ;AACvB,YAAI;AACF,gBAAM,WAAW,MAAM,MAAM,KAAK,EAAE,QAAQ,CAAC;AAC7C,iBAAO;AAAA,YACL,SAAS,SAAS;AAAA,YAClB,OAAO,SAAS,KAAK,SAAY,GAAG,SAAS,MAAM,IAAI,SAAS,UAAU;AAAA,UAC5E;AAAA,QACF,SAAS,OAAO;AACd,iBAAO;AAAA,YACL,SAAS;AAAA,YACT,OAAO,iBAAiB,QAAQ,MAAM,UAAU;AAAA,UAClD;AAAA,QACF;AAAA,MACF,CAAC;AAAA,IACH;AAEA,YAAQ,KAAK,GAAG,YAAY;AAAA,EAC9B;AAEA,QAAM,SAAS,QAAQ,OAAO,CAAC,MAAM,EAAE,OAAO,EAAE;AAChD,QAAM,SAAS,QAAQ,OAAO,CAAC,MAAM,CAAC,EAAE,OAAO,EAAE;AACjD,QAAM,SAAS,QAAQ,QAAQ,CAAC,MAAO,EAAE,QAAQ,CAAC,EAAE,KAAK,IAAI,CAAC,CAAE;AAEhE,SAAO,EAAE,QAAQ,QAAQ,OAAO;AAClC;AAKO,SAAS,kBAAkB,UAIrB;AACX,QAAM,OAAiB,CAAC;AAGxB,OAAK,KAAK,SAAS,IAAI;AAGvB,MAAI,SAAS,IAAI;AACf,SAAK,KAAK,GAAG,SAAS,IAAI,IAAI,SAAS,EAAE,EAAE;AAAA,EAC7C;AAGA,MAAI,SAAS,SAAS;AACpB,SAAK,KAAK,GAAG,SAAS,OAAO;AAAA,EAC/B;AAEA,SAAO;AACT;AAKO,SAAS,0BAA0B,QAAwC;AAChF,QAAM,SAAS,kBAAkB,MAAM;AACvC,QAAM,eAAe,qBAAqB,MAAM;AAEhD,SAAO;AAAA,IACL,SAAS;AAAA,MACP,iBAAiB;AAAA,MACjB,qBAAqB;AAAA,MACrB,4BAA4B;AAAA,IAC9B;AAAA,EACF;AACF;AAKO,SAAS,yBACd,QACA,UAGI,CAAC,GACL;AACA,QAAM,SAAS,kBAAkB,MAAM;AACvC,QAAM,eAAe,qBAAqB,MAAM;AAEhD,QAAM,UAAkC;AAAA,IACtC,iBAAiB;AAAA,EACnB;AAGA,MAAI,QAAQ,aAAa,QAAQ,UAAU,SAAS,GAAG;AACrD,YAAQ,WAAW,IAAI,QAAQ,UAAU,KAAK,GAAG;AAAA,EACnD;AAGA,MAAI,QAAQ,gBAAgB;AAC1B,YAAQ,eAAe,IAAI,GAAG,YAAY,YAAY,QAAQ,cAAc;AAAA,EAC9E;AAEA,SAAO,EAAE,QAAQ;AACnB;AAKO,SAAS,oBAAoB,QAAgB,SAA2B;AAE7E,MAAI,UAAU,KAAK;AACjB,WAAO;AAAA,EACT;AAGA,QAAM,eAAe,QAAQ,IAAI,eAAe,KAAK;AACrD,MACE,aAAa,SAAS,UAAU,KAChC,aAAa,SAAS,UAAU,KAChC,aAAa,SAAS,SAAS,GAC/B;AACA,WAAO;AAAA,EACT;AAEA,SAAO;AACT;AAKO,SAAS,YAAY,SAA0B;AACpD,QAAM,eAAe,QAAQ,IAAI,eAAe,KAAK;AAGrD,aAAW,aAAa,aAAa,MAAM,GAAG,GAAG;AAC/C,UAAM,UAAU,UAAU,KAAK;AAC/B,QAAI,QAAQ,WAAW,WAAW,GAAG;AACnC,YAAM,MAAM,QAAQ,MAAM,YAAY,MAAM;AAC5C,YAAM,MAAM,OAAO,SAAS,KAAK,EAAE;AACnC,UAAI,CAAC,OAAO,MAAM,GAAG,EAAG,QAAO;AAAA,IACjC;AAAA,EACF;AACA,aAAW,aAAa,aAAa,MAAM,GAAG,GAAG;AAC/C,UAAM,UAAU,UAAU,KAAK;AAC/B,QAAI,QAAQ,WAAW,UAAU,GAAG;AAClC,YAAM,MAAM,QAAQ,MAAM,WAAW,MAAM;AAC3C,YAAM,MAAM,OAAO,SAAS,KAAK,EAAE;AACnC,UAAI,CAAC,OAAO,MAAM,GAAG,EAAG,QAAO;AAAA,IACjC;AAAA,EACF;AAGA,QAAM,UAAU,QAAQ,IAAI,SAAS;AACrC,MAAI,SAAS;AACX,UAAM,cAAc,IAAI,KAAK,OAAO;AACpC,UAAM,MAAM,oBAAI,KAAK;AACrB,WAAO,KAAK,IAAI,GAAG,KAAK,OAAO,YAAY,QAAQ,IAAI,IAAI,QAAQ,KAAK,GAAI,CAAC;AAAA,EAC/E;AAEA,SAAO;AACT;;;ACziBA,IAAI,cAA2B;AAKxB,SAAS,qBAAqB,QAA2B;AAC9D,gBAAc;AAChB;AAKO,SAAS,iBAA8B;AAC5C,SAAO;AACT;;;ACCO,IAAM,cAAc;AAAA;AAAA,EAEzB,QAAQ;AAAA,IACN,YAAY;AAAA,EACd;AAAA;AAAA,EAGA,QAAQ;AAAA,IACN,YAAY;AAAA,EACd;AAAA;AAAA,EAGA,aAAa;AAAA,IACX,YAAY;AAAA,EACd;AAAA;AAAA,EAGA,QAAQ;AAAA,IACN,YAAY;AAAA,EACd;AAAA;AAAA,EAGA,OAAO;AAAA,IACL,YAAY;AAAA,EACd;AAAA;AAAA,EAGA,OAAO;AAAA,IACL,YAAY;AAAA,EACd;AACF;AAKA,eAAsB,qBACpB,SACA,OACwC;AACxC,MAAI;AACF,UAAM,QAAQ,MAAM,QAAQ;AAC5B,WAAO,MAAM,IAAI,KAAK;AAAA,EACxB,SAAS,OAAO;AACd,mBAAe,EAAE;AAAA,MACf;AAAA,MACA,iBAAiB,QAAQ,QAAQ,IAAI,MAAM,OAAO,KAAK,CAAC;AAAA,IAC1D;AACA,WAAO,CAAC;AAAA,EACV;AACF;AAKA,eAAsB,cACpB,KACA,QACmD;AACnD,QAAM,UAAU,QAAQ,IAAI;AAC5B,MAAI,CAAC,SAAS;AACZ,mBAAe,EAAE,KAAK,4DAA4D,EAAE,IAAI,CAAC;AACzF,WAAO,EAAE,aAAa,OAAO,OAAO,oCAAoC;AAAA,EAC1E;AAEA,MAAI;AACF,UAAM,MAAM,IAAI,IAAI,mBAAmB,OAAO;AAE9C,UAAM,UAAuB,EAAE,gBAAgB,mBAAmB;AAClE,QAAI,QAAQ;AACV,cAAQ,qBAAqB,IAAI;AAAA,IACnC;AAEA,UAAM,WAAW,MAAM,MAAM,IAAI,SAAS,GAAG;AAAA,MAC3C,QAAQ;AAAA,MACR;AAAA,MACA,MAAM,KAAK,UAAU,EAAE,IAAI,CAAC;AAAA,IAC9B,CAAC;AAED,UAAM,OAAO,MAAM,SAAS,KAAK;AAEjC,QAAI,CAAC,SAAS,IAAI;AAChB,qBAAe,EAAE,KAAK,wBAAwB;AAAA,QAC5C;AAAA,QACA,QAAQ,SAAS;AAAA,QACjB,OAAO,KAAK;AAAA,MACd,CAAC;AAAA,IACH;AAEA,WAAO;AAAA,MACL,aAAa,SAAS;AAAA,MACtB,OAAO,KAAK;AAAA,IACd;AAAA,EACF,SAAS,OAAO;AACd,UAAM,UAAU,iBAAiB,QAAQ,MAAM,UAAU;AACzD,mBAAe,EAAE,KAAK,uBAAuB,EAAE,KAAK,OAAO,QAAQ,CAAC;AACpE,WAAO;AAAA,MACL,aAAa;AAAA,MACb,OAAO;AAAA,IACT;AAAA,EACF;AACF;AAKA,eAAsB,eACpB,MACA,QACmD;AACnD,QAAM,UAAU,QAAQ,IAAI;AAC5B,MAAI,CAAC,SAAS;AACZ,mBAAe,EAAE,KAAK,6DAA6D,EAAE,KAAK,CAAC;AAC3F,WAAO,EAAE,aAAa,OAAO,OAAO,oCAAoC;AAAA,EAC1E;AAEA,MAAI;AACF,UAAM,MAAM,IAAI,IAAI,mBAAmB,OAAO;AAE9C,UAAM,UAAuB,EAAE,gBAAgB,mBAAmB;AAClE,QAAI,QAAQ;AACV,cAAQ,qBAAqB,IAAI;AAAA,IACnC;AAEA,UAAM,WAAW,MAAM,MAAM,IAAI,SAAS,GAAG;AAAA,MAC3C,QAAQ;AAAA,MACR;AAAA,MACA,MAAM,KAAK,UAAU,EAAE,KAAK,CAAC;AAAA,IAC/B,CAAC;AAED,UAAM,OAAO,MAAM,SAAS,KAAK;AAEjC,WAAO;AAAA,MACL,aAAa,SAAS;AAAA,MACtB,OAAO,KAAK;AAAA,IACd;AAAA,EACF,SAAS,OAAO;AACd,WAAO;AAAA,MACL,aAAa;AAAA,MACb,OAAO,iBAAiB,QAAQ,MAAM,UAAU;AAAA,IAClD;AAAA,EACF;AACF;AAKA,eAAsB,gBACpB,OACA,QAKC;AACD,QAAM,UAAU,MAAM,QAAQ,WAAW,MAAM,IAAI,CAAC,SAAS,eAAe,MAAM,MAAM,CAAC,CAAC;AAE1F,MAAI,cAAc;AAClB,MAAI,SAAS;AACb,QAAM,SAAiD,CAAC;AAExD,WAAS,IAAI,GAAG,IAAI,QAAQ,QAAQ,KAAK;AACvC,UAAM,SAAS,QAAQ,CAAC;AACxB,UAAM,OAAO,MAAM,CAAC;AAEpB,QAAI,EAAE,UAAU,OAAO;AACrB;AAAA,IACF;AAEA,QAAI,OAAO,WAAW,eAAe,OAAO,MAAM,aAAa;AAC7D;AAAA,IACF,OAAO;AACL;AACA,YAAM,QACJ,OAAO,WAAW,cACd,OAAO,MAAM,SAAS,kBACtB,OAAO,OAAO,MAAM,KAAK;AAE/B,aAAO,KAAK,EAAE,MAAM,MAAM,CAAC;AAAA,IAC7B;AAAA,EACF;AAEA,SAAO,EAAE,aAAa,QAAQ,OAAO;AACvC;AAKA,eAAsB,eACpB,MACA,QAKC;AACD,QAAM,UAAU,MAAM,QAAQ,WAAW,KAAK,IAAI,CAAC,QAAQ,cAAc,KAAK,MAAM,CAAC,CAAC;AAEtF,MAAI,cAAc;AAClB,MAAI,SAAS;AACb,QAAM,SAAgD,CAAC;AAEvD,WAAS,IAAI,GAAG,IAAI,QAAQ,QAAQ,KAAK;AACvC,UAAM,SAAS,QAAQ,CAAC;AACxB,UAAM,MAAM,KAAK,CAAC;AAElB,QAAI,EAAE,UAAU,MAAM;AACpB;AAAA,IACF;AAEA,QAAI,OAAO,WAAW,eAAe,OAAO,MAAM,aAAa;AAC7D;AAAA,IACF,OAAO;AACL;AACA,YAAM,QACJ,OAAO,WAAW,cACd,OAAO,MAAM,SAAS,kBACtB,OAAO,OAAO,MAAM,KAAK;AAE/B,aAAO,KAAK,EAAE,KAAK,MAAM,CAAC;AAAA,IAC5B;AAAA,EACF;AAEA,SAAO,EAAE,aAAa,QAAQ,OAAO;AACvC;AAgBO,SAAS,sBAAsB,SAA0B,CAAC,GAAG;AAClE,SAAO,OAAU,KAAa,YAA+C;AAC3E,UAAM,eAAqC;AAAA,MACzC,GAAG;AAAA,MACH,GAAG;AAAA,MACH,MAAM;AAAA,QACJ,GAAG,SAAS;AAAA,QACZ,GAAG,OAAO;AAAA,MACZ;AAAA,IACF;AAEA,UAAM,WAAW,MAAM,MAAM,KAAK,YAAY;AAE9C,QAAI,CAAC,SAAS,IAAI;AAChB,YAAM,IAAI,MAAM,iBAAiB,SAAS,UAAU,EAAE;AAAA,IACxD;AAEA,WAAO,SAAS,KAAK;AAAA,EACvB;AACF;AAKO,SAAS,qBACd,IACA,UAGI,CAAC,GACiC;AAEtC,MAAI,QAAQ,eAAe,OAAO;AAChC,WAAO;AAAA,EACT;AAEA,QAAM,SAAS,QAAQ,cAAc,MAAM;AAC3C,QAAM,QAAQ,oBAAI,IAAmD;AAErE,SAAO,UAAU,SAAkC;AACjD,UAAM,MAAM,KAAK,UAAU,IAAI;AAC/B,UAAM,MAAM,KAAK,IAAI;AACrB,UAAM,SAAS,MAAM,IAAI,GAAG;AAE5B,QAAI,UAAU,MAAM,OAAO,WAAW;AACpC,aAAO,OAAO;AAAA,IAChB;AAEA,UAAM,QAAQ,MAAM,GAAG,GAAG,IAAI;AAC9B,UAAM,IAAI,KAAK,EAAE,OAAO,WAAW,MAAM,MAAM,CAAC;AAChD,WAAO;AAAA,EACT;AACF;AAWO,IAAM,kBAAN,MAAsB;AAAA,EAG3B,YAAoB,QAA6B;AAA7B;AAAA,EAA8B;AAAA,EAF1C,QAA2D,oBAAI,IAAI;AAAA;AAAA;AAAA;AAAA,EAO3E,MAAM,SAKJ;AACA,UAAM,MAAM,KAAK,OAAO,MACpB,KAAK,OAAO,IAAI,OAAO,IACvB,QAAQ,QAAQ,IAAI,iBAAiB,KAAK;AAE9C,UAAM,MAAM,KAAK,IAAI;AACrB,QAAI,QAAQ,KAAK,MAAM,IAAI,GAAG;AAG9B,QAAI,CAAC,SAAS,MAAM,MAAM,WAAW;AACnC,cAAQ;AAAA,QACN,OAAO;AAAA,QACP,WAAW,MAAM,KAAK,OAAO;AAAA,MAC/B;AACA,WAAK,MAAM,IAAI,KAAK,KAAK;AAAA,IAC3B;AAGA,UAAM;AAEN,UAAM,UAAU,MAAM,SAAS,KAAK,OAAO;AAC3C,UAAM,YAAY,KAAK,IAAI,GAAG,KAAK,OAAO,QAAQ,MAAM,KAAK;AAE7D,WAAO;AAAA,MACL;AAAA,MACA,OAAO,KAAK,OAAO;AAAA,MACnB;AAAA,MACA,OAAO,MAAM;AAAA,IACf;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,UAAgB;AACd,UAAM,MAAM,KAAK,IAAI;AACrB,eAAW,CAAC,KAAK,KAAK,KAAK,KAAK,MAAM,QAAQ,GAAG;AAC/C,UAAI,MAAM,MAAM,WAAW;AACzB,aAAK,MAAM,OAAO,GAAG;AAAA,MACvB;AAAA,IACF;AAAA,EACF;AACF;AAaO,SAAS,eAAe,SAA0C;AAEvE,QAAM,UAAU,QAAQ,QAAQ,IAAI,qBAAqB;AACzD,QAAM,SAAS,QAAQ,QAAQ,IAAI,4BAA4B;AAC/D,QAAM,OAAO,QAAQ,QAAQ,IAAI,kBAAkB;AACnD,QAAM,WAAW,QAAQ,QAAQ,IAAI,sBAAsB;AAC3D,QAAM,YAAY,QAAQ,QAAQ,IAAI,uBAAuB;AAE7D,MAAI,CAAC,SAAS;AAEZ,UAAM,YAAY,QAAQ,QAAQ,IAAI,cAAc;AACpD,QAAI,WAAW;AACb,aAAO;AAAA,QACL,SAAS;AAAA,MACX;AAAA,IACF;AAEA,WAAO;AAAA,EACT;AAEA,SAAO;AAAA,IACL,SAAS,WAAW;AAAA,IACpB,QAAQ,UAAU;AAAA,IAClB,MAAM,OAAO,mBAAmB,IAAI,IAAI;AAAA,IACxC,UAAU,WAAW,WAAW,QAAQ,IAAI;AAAA,IAC5C,WAAW,YAAY,WAAW,SAAS,IAAI;AAAA,EACjD;AACF;AAKO,SAAS,iBACd,SACA,UACA,UACQ;AAER,QAAM,aAAa,WAAW,QAAQ;AACtC,QAAM,gBAAgB,QAAQ,QAAQ,IAAI,UAAU,GAAG;AAEvD,MAAI,iBAAiB,SAAS,SAAS,aAAa,GAAG;AACrD,WAAO;AAAA,EACT;AAGA,QAAM,KAAK,QAAQ,QAAQ,IAAI,iBAAiB,KAAK;AACrD,QAAM,OAAO,WAAW,KAAK,QAAQ;AACrC,QAAM,eAAe,OAAO,SAAS;AACrC,QAAM,UAAU,SAAS,YAAY;AAErC,MAAI,CAAC,SAAS;AACZ,UAAM,IAAI,MAAM,+BAA+B;AAAA,EACjD;AAEA,SAAO;AACT;AAKA,SAAS,WAAW,KAAqB;AACvC,MAAI,OAAO;AACX,WAAS,IAAI,GAAG,IAAI,IAAI,QAAQ,KAAK;AACnC,UAAM,OAAO,IAAI,WAAW,CAAC;AAC7B,YAAQ,QAAQ,KAAK,OAAO;AAC5B,WAAO,OAAO;AAAA,EAChB;AACA,SAAO,KAAK,IAAI,IAAI;AACtB;AAaO,SAAS,yBAAyB,SAA6C;AACpF,QAAM,YAAY,QAAQ,QAAQ,IAAI,YAAY,KAAK;AACvD,QAAM,SAAS,cAAc,SAAS;AACtC,QAAM,WAAW,eAAe,OAAO;AAEvC,SAAO;AAAA,IACL,QAAQ,QAAQ,QAAQ,IAAI,SAAS,GAAG;AAAA,IACxC,UAAU,YAAY;AAAA,IACtB;AAAA,EACF;AACF;AAKA,SAAS,cAAc,WAAoD;AACzE,QAAM,KAAK,UAAU,YAAY;AACjC,QAAM,WAAW,GAAG,SAAS,QAAQ,KAAK,GAAG,SAAS,MAAM;AAC5D,MAAI,SAAU,QAAO;AACrB,MAAI,GAAG,SAAS,QAAQ,EAAG,QAAO;AAClC,SAAO;AACT;AAKO,SAAS,oBACd,UACA,QAMc;AACd,QAAM,eAAyB,CAAC;AAEhC,MAAI,OAAO,WAAW,QAAW;AAC/B,iBAAa,KAAK,WAAW,OAAO,MAAM,EAAE;AAAA,EAC9C;AAEA,MAAI,OAAO,YAAY,QAAW;AAChC,iBAAa,KAAK,YAAY,OAAO,OAAO,EAAE;AAAA,EAChD;AAEA,MAAI,OAAO,yBAAyB,QAAW;AAC7C,iBAAa,KAAK,0BAA0B,OAAO,oBAAoB,EAAE;AAAA,EAC3E;AAEA,MAAI,aAAa,SAAS,GAAG;AAC3B,aAAS,QAAQ,IAAI,iBAAiB,aAAa,KAAK,IAAI,CAAC;AAAA,EAC/D;AAEA,MAAI,OAAO,QAAQ,OAAO,KAAK,SAAS,GAAG;AACzC,aAAS,QAAQ,IAAI,aAAa,OAAO,KAAK,KAAK,GAAG,CAAC;AAAA,EACzD;AAEA,SAAO;AACT;AAKO,SAAS,gBACd,UACA,WAMc;AACd,QAAM,QAAQ,UAAU,IAAI,CAAC,aAAa;AACxC,UAAM,QAAQ,CAAC,IAAI,SAAS,IAAI,KAAK,iBAAiB,OAAO,SAAS,EAAE,GAAG;AAE3E,QAAI,SAAS,MAAM;AACjB,YAAM,KAAK,SAAS,SAAS,IAAI,GAAG;AAAA,IACtC;AAEA,QAAI,SAAS,aAAa;AACxB,YAAM,KAAK,aAAa;AAAA,IAC1B;AAEA,WAAO,MAAM,KAAK,IAAI;AAAA,EACxB,CAAC;AAED,MAAI,MAAM,SAAS,GAAG;AACpB,aAAS,QAAQ,IAAI,QAAQ,MAAM,KAAK,IAAI,CAAC;AAAA,EAC/C;AAEA,SAAO;AACT;AAKA,eAAsB,aACpB,OACA,UAAkB,QAAQ,IAAI,mBAAmB,yBAKhD;AACD,QAAM,UAAU,MAAM,QAAQ;AAAA,IAC5B,MAAM,IAAI,OAAO,SAAS;AACxB,YAAM,MAAM,IAAI,IAAI,MAAM,OAAO;AACjC,YAAM,WAAW,MAAM,MAAM,IAAI,SAAS,CAAC;AAE3C,UAAI,CAAC,SAAS,IAAI;AAChB,cAAM,IAAI,MAAM,GAAG,SAAS,MAAM,IAAI,SAAS,UAAU,EAAE;AAAA,MAC7D;AAEA,aAAO;AAAA,IACT,CAAC;AAAA,EACH;AAEA,MAAI,SAAS;AACb,MAAI,SAAS;AACb,QAAM,SAAiD,CAAC;AAExD,WAAS,IAAI,GAAG,IAAI,QAAQ,QAAQ,KAAK;AACvC,UAAM,SAAS,QAAQ,CAAC;AACxB,UAAM,OAAO,MAAM,CAAC;AAEpB,QAAI,EAAE,UAAU,OAAO;AACrB;AAAA,IACF;AAEA,QAAI,OAAO,WAAW,aAAa;AACjC;AAAA,IACF,OAAO;AACL;AACA,aAAO,KAAK;AAAA,QACV;AAAA,QACA,OACE,OAAO,kBAAkB,QACrB,OAAO,OAAO,UACd,OAAO,OAAO,MAAM,KAAK;AAAA,MACjC,CAAC;AAAA,IACH;AAAA,EACF;AAEA,SAAO,EAAE,QAAQ,QAAQ,OAAO;AAClC;","names":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@revealui/cache",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "description": "Caching infrastructure for RevealUI - CDN config, edge cache, ISR presets, revalidation",
5
5
  "license": "MIT",
6
6
  "dependencies": {},