{"version":3,"sources":["../config.ts","../node_modules/.pnpm/@vercel+stega@0.1.2/node_modules/@vercel/stega/dist/index.mjs","../src/stega.ts","../src/core.ts","../src/cache.ts","../src/enhanced.ts"],"sourcesContent":["/**\n * @file config.ts\n * @description Centralized configuration for Sanity Edge Fetcher\n * @author Invisible Cities Agency\n * @license MIT\n */\n\nimport { createEdgeSanityFetcher } from './src/core';\nimport { createCachedFetcher } from './src/cache';\nimport { edgeSanityFetchWithRetry } from './src/enhanced';\n\n/**\n * Sanity configuration from environment variables\n */\nexport const sanityConfig = {\n  projectId: process.env.NEXT_PUBLIC_SANITY_PROJECT_ID!,\n  apiVersion: process.env.NEXT_PUBLIC_SANITY_API_VERSION || '2025-02-10',\n  dataset: process.env.NEXT_PUBLIC_SANITY_DATASET || 'production',\n  token: process.env.SANITY_VIEWER_TOKEN,\n  useCdn: process.env.NODE_ENV === 'production',\n} as const;\n\n/**\n * Cache configuration\n */\nexport const cacheConfig = {\n  // Default TTLs for different content types (in seconds)\n  ttl: {\n    default: 60,           // 1 minute\n    static: 3600,         // 1 hour for static content\n    dynamic: 30,          // 30 seconds for frequently changing content\n    long: 86400,          // 24 hours for rarely changing content\n  },\n  \n  // Cache prefixes for organization\n  prefixes: {\n    page: 'page:',\n    post: 'post:',\n    author: 'author:',\n    category: 'cat:',\n    section: 'section:',\n    global: 'global:',\n  },\n  \n  // Layer configuration\n  layers: {\n    memory: {\n      enabled: true,\n      maxSize: 100,\n    },\n    redis: {\n      enabled: !!(process.env.KV_REST_API_URL || process.env.UPSTASH_REDIS_REST_URL),\n      url: process.env.KV_REST_API_URL || process.env.UPSTASH_REDIS_REST_URL,\n      token: process.env.KV_REST_API_TOKEN || process.env.UPSTASH_REDIS_REST_TOKEN,\n      readOnlyToken: process.env.KV_REST_API_READ_ONLY_TOKEN,\n    },\n    nextCache: {\n      enabled: typeof window === 'undefined',\n    },\n  },\n} as const;\n\n/**\n * Rate limiting configuration\n */\nexport const rateLimitConfig = {\n  minInterval: 100,       // Minimum 100ms between requests\n  maxRequestsPerSecond: 10,\n} as const;\n\n/**\n * Retry configuration (if p-retry is available)\n */\nexport const retryConfig = {\n  retries: 3,\n  minTimeout: 100,\n  maxTimeout: 2000,\n  factor: 2,\n} as const;\n\n/**\n * Real-time configuration\n */\nexport const realtimeConfig = {\n  sse: {\n    endpoint: '/api/sanity-updates',\n    pollInterval: 5000,    // 5 seconds\n    heartbeatInterval: 30000, // 30 seconds\n  },\n  websocket: {\n    endpoint: process.env.NEXT_PUBLIC_WEBSOCKET_URL || 'wss://your-worker.workers.dev/ws',\n    reconnectDelay: 1000,\n    maxReconnectAttempts: 5,\n  },\n} as const;\n\n/**\n * Pre-configured fetchers for common use cases\n * NOTE: For Next.js apps, use the smart fetchers from the main export instead:\n * - sanityFetch() - Auto-detects draft mode\n * - sanityFetchWithFallback() - Smart fallback to drafts\n * - sanityFetchStatic() - Always CDN\n * - sanityFetchAuthenticated() - Always authenticated\n * \n * These are lower-level fetchers for advanced use cases:\n */\nexport const fetchers = {\n  /**\n   * Basic fetcher - no caching, no retry\n   * Use for: One-off queries, testing\n   */\n  basic: createEdgeSanityFetcher(sanityConfig.dataset, false),\n  \n  /**\n   * Authenticated fetcher - includes token for draft preview\n   * Use for: Preview mode, draft content\n   */\n  authenticated: createEdgeSanityFetcher(sanityConfig.dataset, true),\n  \n  /**\n   * Cached fetcher - multi-layer caching with default TTL\n   * Use for: Most production queries\n   */\n  cached: createCachedFetcher(sanityConfig.dataset, {\n    ttl: cacheConfig.ttl.default,\n    useRedis: cacheConfig.layers.redis.enabled,\n    useNextCache: cacheConfig.layers.nextCache.enabled,\n  }),\n  \n  /**\n   * Static content fetcher - long cache TTL\n   * Use for: Global settings, rarely changing content\n   */\n  static: createCachedFetcher(sanityConfig.dataset, {\n    ttl: cacheConfig.ttl.long,\n    prefix: cacheConfig.prefixes.global,\n    useRedis: cacheConfig.layers.redis.enabled,\n    useNextCache: true,\n  }),\n  \n  /**\n   * Dynamic content fetcher - short cache TTL with retry\n   * Use for: Frequently updated content, user-specific data\n   */\n  dynamic: createCachedFetcher(sanityConfig.dataset, {\n    ttl: cacheConfig.ttl.dynamic,\n    useRedis: cacheConfig.layers.redis.enabled,\n    useNextCache: false, // Skip Next.js cache for dynamic content\n  }),\n  \n  /**\n   * Page fetcher - optimized for page data\n   * Use for: Full page queries\n   */\n  page: createCachedFetcher(sanityConfig.dataset, {\n    ttl: cacheConfig.ttl.static,\n    prefix: cacheConfig.prefixes.page,\n    useRedis: cacheConfig.layers.redis.enabled,\n    useNextCache: true,\n  }),\n  \n  /**\n   * Section fetcher - for page sections/components\n   * Use for: Individual page sections\n   */\n  section: createCachedFetcher(sanityConfig.dataset, {\n    ttl: cacheConfig.ttl.default,\n    prefix: cacheConfig.prefixes.section,\n    useRedis: cacheConfig.layers.redis.enabled,\n    useNextCache: true,\n  }),\n} as const;\n\n/**\n * Helper to create a custom fetcher with merged config\n */\nexport function createCustomFetcher(options: {\n  dataset?: string;\n  ttl?: number;\n  prefix?: string;\n  useAuth?: boolean;\n  useCache?: boolean;\n  useRetry?: boolean;\n}) {\n  const {\n    dataset = sanityConfig.dataset,\n    ttl = cacheConfig.ttl.default,\n    prefix = '',\n    useAuth = false,\n    useCache = true,\n    useRetry = false,\n  } = options;\n  \n  if (!useCache && !useRetry) {\n    return createEdgeSanityFetcher(dataset, useAuth);\n  }\n  \n  if (useCache && !useRetry) {\n    return createCachedFetcher(dataset, {\n      ttl,\n      prefix,\n      useRedis: cacheConfig.layers.redis.enabled,\n      useNextCache: cacheConfig.layers.nextCache.enabled,\n    });\n  }\n  \n  // For retry, we'd need to wrap the fetcher\n  // This is a simplified version\n  return async <T>(query: string, params?: any) => {\n    const fetchFn = useCache \n      ? createCachedFetcher(dataset, { ttl, prefix })\n      : createEdgeSanityFetcher(dataset, useAuth);\n    \n    if (useRetry) {\n      return edgeSanityFetchWithRetry<T>(\n        { dataset, query, params, useAuth },\n        retryConfig\n      );\n    }\n    \n    return fetchFn<T>(query, params);\n  };\n}\n\n/**\n * Queries that should be warmed on startup\n */\nexport const warmupQueries = [\n  // Global settings\n  {\n    query: '*[_type == \"siteSettings\"][0]',\n    ttl: cacheConfig.ttl.long,\n    fetcher: 'static',\n  },\n  // Navigation\n  {\n    query: '*[_type == \"navigation\"][0]',\n    ttl: cacheConfig.ttl.long,\n    fetcher: 'static',\n  },\n  // Recent posts\n  {\n    query: '*[_type == \"post\"] | order(_createdAt desc)[0..10]',\n    ttl: cacheConfig.ttl.default,\n    fetcher: 'cached',\n  },\n  // Homepage\n  {\n    query: '*[_type == \"page\" && slug.current == \"home\"][0]',\n    ttl: cacheConfig.ttl.static,\n    fetcher: 'page',\n  },\n] as const;\n\n/**\n * Export all configurations for easy access\n */\nexport const config = {\n  sanity: sanityConfig,\n  cache: cacheConfig,\n  rateLimit: rateLimitConfig,\n  retry: retryConfig,\n  realtime: realtimeConfig,\n  fetchers,\n  warmupQueries,\n} as const;\n\n// Default export for convenience\nexport default config;","var s={0:8203,1:8204,2:8205,3:8290,4:8291,5:8288,6:65279,7:8289,8:119155,9:119156,a:119157,b:119158,c:119159,d:119160,e:119161,f:119162},c={0:8203,1:8204,2:8205,3:65279},u=new Array(4).fill(String.fromCodePoint(c[0])).join(\"\"),m=String.fromCharCode(0);function E(t){let e=JSON.stringify(t);return`${u}${Array.from(e).map(r=>{let n=r.charCodeAt(0);if(n>255)throw new Error(`Only ASCII edit info can be encoded. Error attempting to encode ${e} on character ${r} (${n})`);return Array.from(n.toString(4).padStart(4,\"0\")).map(o=>String.fromCodePoint(c[o])).join(\"\")}).join(\"\")}`}function y(t){let e=JSON.stringify(t);return Array.from(e).map(r=>{let n=r.charCodeAt(0);if(n>255)throw new Error(`Only ASCII edit info can be encoded. Error attempting to encode ${e} on character ${r} (${n})`);return Array.from(n.toString(16).padStart(2,\"0\")).map(o=>String.fromCodePoint(s[o])).join(\"\")}).join(\"\")}function I(t){return!Number.isNaN(Number(t))||/[a-z]/i.test(t)&&!/\\d+(?:[-:\\/]\\d+){2}(?:T\\d+(?:[-:\\/]\\d+){1,2}(\\.\\d+)?Z?)?/.test(t)?!1:Boolean(Date.parse(t))}function T(t){try{new URL(t,t.startsWith(\"/\")?\"https://acme.com\":void 0)}catch{return!1}return!0}function C(t,e,r=\"auto\"){return r===!0||r===\"auto\"&&(I(t)||T(t))?t:`${t}${E(e)}`}var x=Object.fromEntries(Object.entries(c).map(t=>t.reverse())),g=Object.fromEntries(Object.entries(s).map(t=>t.reverse())),S=`${Object.values(s).map(t=>`\\\\u{${t.toString(16)}}`).join(\"\")}`,f=new RegExp(`[${S}]{4,}`,\"gu\");function G(t){let e=t.match(f);if(!!e)return h(e[0],!0)[0]}function $(t){let e=t.match(f);if(!!e)return e.map(r=>h(r)).flat()}function h(t,e=!1){let r=Array.from(t);if(r.length%2===0){if(r.length%4||!t.startsWith(u))return A(r,e)}else throw new Error(\"Encoded data has invalid length\");let n=[];for(let o=r.length*.25;o--;){let p=r.slice(o*4,o*4+4).map(d=>x[d.codePointAt(0)]).join(\"\");n.unshift(String.fromCharCode(parseInt(p,4)))}if(e){n.shift();let o=n.indexOf(m);return o===-1&&(o=n.length),[JSON.parse(n.slice(0,o).join(\"\"))]}return n.join(\"\").split(m).filter(Boolean).map(o=>JSON.parse(o))}function A(t,e){var d;let r=[];for(let i=t.length*.5;i--;){let a=`${g[t[i*2].codePointAt(0)]}${g[t[i*2+1].codePointAt(0)]}`;r.unshift(String.fromCharCode(parseInt(a,16)))}let n=[],o=[r.join(\"\")],p=10;for(;o.length;){let i=o.shift();try{if(n.push(JSON.parse(i)),e)return n}catch(a){if(!p--)throw a;let l=+((d=a.message.match(/\\sposition\\s(\\d+)$/))==null?void 0:d[1]);if(!l)throw a;o.unshift(i.substring(0,l),i.substring(l))}}return n}function _(t){var e;return{cleaned:t.replace(f,\"\"),encoded:((e=t.match(f))==null?void 0:e[0])||\"\"}}function O(t){return t&&JSON.parse(_(JSON.stringify(t)).cleaned)}export{f as VERCEL_STEGA_REGEX,y as legacyStegaEncode,O as vercelStegaClean,C as vercelStegaCombine,G as vercelStegaDecode,$ as vercelStegaDecodeAll,E as vercelStegaEncode,_ as vercelStegaSplit};\n","/**\n * @file stega.ts\n * @description Stega encoding support for visual editing with optional @vercel/stega dependency\n * @author Invisible Cities Agency\n * @license MIT\n */\n\n// Edge-safe ESM import for @vercel/stega (tiny, browser/edge-friendly)\n// This package has no Node dependencies and is safe in Edge bundles.\nimport { vercelStegaEncode as _vercelStegaEncode, vercelStegaCombine as _vercelStegaCombine, vercelStegaClean as _vercelStegaClean } from '@vercel/stega';\n// NOTE: Library API: vercelStegaEncode(json) -> invisible suffix, vercelStegaCombine(text, json, skip?) -> combined string\nconst vercelStegaEncode: ((metadata: any) => string) | undefined = _vercelStegaEncode as any;\nconst vercelStegaCombine: ((value: string, metadata: any, skip?: 'auto' | boolean) => string) | undefined = _vercelStegaCombine as any;\nconst vercelStegaClean: (<T = any>(value: T) => T) | undefined = _vercelStegaClean as any;\n\n/**\n * Stega configuration options\n */\nexport interface StegaConfig {\n  enabled: boolean;\n  studioUrl?: string;\n  basePath?: string;\n  filter?: (path: string) => boolean;\n  projectId?: string;\n  dataset?: string;\n}\n\n/**\n * Inline stega implementation for when @vercel/stega is not available\n * Uses Unicode code points to encode invisible metadata\n */\nconst STEGA_CODES = {\n  // Tuple ensures index access returns number (not possibly undefined)\n  base4: [8203, 8204, 8205, 65279] as const,\n  hex: {\n    0: 8203, 1: 8204, 2: 8205, 3: 8290, 4: 8291, 5: 8288,\n    6: 65279, 7: 8289, 8: 119155, 9: 119156,\n    a: 119157, b: 119158, c: 119159, d: 119160, e: 119161, f: 119162\n  } as Record<string | number, number>\n};\n\nconst STEGA_PREFIX = new Array(4).fill(String.fromCodePoint(STEGA_CODES.base4[0])).join('');\n\n/**\n * Encode data as invisible Unicode characters (base4 encoding)\n */\nfunction encodeInvisibleBase4(data: any): string {\n  const jsonStr = JSON.stringify(data);\n  const encoded = Array.from(jsonStr).map(char => {\n    const charCode = char.charCodeAt(0);\n    if (charCode > 255) {\n      throw new Error(`Only ASCII can be encoded. Error on character ${char} (${charCode})`);\n    }\n    // Convert to base-4 and encode as invisible characters\n    return Array.from(charCode.toString(4).padStart(4, '0'))\n      .map(digit => {\n        // digit is one of '0' | '1' | '2' | '3'\n        const idx = parseInt(digit, 10) as 0 | 1 | 2 | 3;\n        return String.fromCodePoint(STEGA_CODES.base4[idx]);\n      })\n      .join('');\n  }).join('');\n  \n  return `${STEGA_PREFIX}${encoded}`;\n}\n\n/**\n * Check if a value should skip stega encoding\n */\nfunction shouldSkipEncoding(value: string): boolean {\n  // Skip dates and URLs as they shouldn't be encoded\n  const isDate = /\\d+(?:[-:\\/]\\d+){2}(?:T\\d+(?:[-:\\/]\\d+){1,2}(\\.\\d+)?Z?)?/.test(value);\n  const isUrl = (() => {\n    try {\n      new URL(value, value.startsWith('/') ? 'https://example.com' : undefined);\n      return true;\n    } catch {\n      return false;\n    }\n  })();\n  // Skip Sanity asset/file id strings like image-<hash>-<WxH>-<ext>\n  const isSanityAssetId = /^(image|file)-[A-Za-z0-9]+-\\d+x\\d+-[A-Za-z0-9]+$/.test(value);\n  \n  return isDate || isUrl || isSanityAssetId;\n}\n\n/**\n * Determine if the current path points to structured fields that must not be stega-encoded\n */\nfunction isStructuredPath(path: Array<string | number>): boolean {\n  if (!path.length) return false;\n  const last = String(path[path.length - 1]);\n  // Any Sanity meta or reference/slug/asset/urlish fields\n  const disallowed = new Set([\n    '_id', '_ref', '_key', '_type',\n    'slug', 'current',\n    'asset', 'path',\n    'href', 'url', 'src'\n  ]);\n  if (disallowed.has(last)) return true;\n  // If parent key is 'asset', skip child values as well\n  if (path.length >= 2 && String(path[path.length - 2]) === 'asset') return true;\n  return false;\n}\n\n/**\n * Encode stega metadata into a string value\n */\nfunction encodeStegaString(value: string, metadata: any, config: StegaConfig): string {\n  if (!config.enabled || !metadata) {\n    return value;\n  }\n  \n  // Skip encoding for dates and URLs\n  if (shouldSkipEncoding(value)) {\n    return value;\n  }\n  \n  // Skip encoding when there is no visible content to preserve\n  if (typeof value !== 'string' || value.trim().length === 0) {\n    return value;\n  }\n\n  // Skip if already stega-encoded to avoid double payloads\n  try {\n    if (vercelStegaClean && vercelStegaClean(value) !== value) {\n      return value;\n    }\n  } catch {\n    // ignore and continue\n  }\n  \n  // Use @vercel/stega if available, otherwise use inline implementation\n  if (vercelStegaEncode) {\n    // Prefer combine when available; fallback to appending encoded invisible suffix\n    if (vercelStegaCombine) return vercelStegaCombine(value, metadata, 'auto');\n    return `${value}${vercelStegaEncode(metadata)}`;\n  }\n  \n  // Inline implementation\n  return `${value}${encodeInvisibleBase4(metadata)}`;\n}\n\n/**\n * Recursively encode stega metadata into result data\n */\nfunction encodeStegaInResult(\n  data: any,\n  sourceMap: any,\n  config: StegaConfig,\n  path: Array<string | number> = []\n): any {\n  if (!config.enabled || !sourceMap) {\n    return data;\n  }\n  \n  // Handle null/undefined\n  if (data == null) {\n    return data;\n  }\n  \n  // Handle strings\n  if (typeof data === 'string') {\n    // Never encode structured/system fields\n    if (isStructuredPath(path)) return data;\n    const metadata = resolveSourceMapForPath(sourceMap, path, config);\n    // Only encode mapped leaf values\n    if (metadata && (metadata.type === undefined || metadata.type === 'value')) {\n      return encodeStegaString(data, metadata, config);\n    }\n    return data;\n  }\n  \n  // Handle arrays\n  if (Array.isArray(data)) {\n    return data.map((item, index) => \n      encodeStegaInResult(item, sourceMap, config, [...path, index])\n    );\n  }\n  \n  // Handle objects\n  if (typeof data === 'object') {\n    const result: any = {};\n    for (const key in data) {\n      if (data.hasOwnProperty(key)) {\n        result[key] = encodeStegaInResult(\n          data[key],\n          sourceMap,\n          config,\n          [...path, key]\n        );\n      }\n    }\n    return result;\n  }\n  \n  // Return primitives as-is\n  return data;\n}\n\n/**\n * Resolve source map metadata for a given path\n */\nfunction resolveSourceMapForPath(sourceMap: any, path: Array<string | number>, config?: StegaConfig): any {\n  if (!sourceMap?.mappings) {\n    return null;\n  }\n  \n  // Convert path to JSONPath format for lookup\n  const jsonPath = `$${path.map(segment => \n    typeof segment === 'number' ? `[${segment}]` : `['${segment}']`\n  ).join('')}`;\n  \n  // Look for exact match or closest parent\n  if (sourceMap.mappings[jsonPath]) {\n    const mapping = sourceMap.mappings[jsonPath];\n    const studioUrl = sourceMap.studioUrl || config?.studioUrl;\n\n    // Attempt to build a Studio Edit Intent URL: /intent/edit/id=<docId>;path=<fieldPath>\n    let href: string | undefined;\n    try {\n      const src = mapping?.source;\n      const docId = typeof src?.document === 'number' ? sourceMap.documents?.[src.document]?._id : undefined;\n      const fieldPath = typeof src?.path === 'number' ? sourceMap.paths?.[src.path] : undefined;\n\n      if (studioUrl && docId) {\n        // Normalize studio base to exclude trailing /presentation if present\n        const studioBase = String(studioUrl).replace(/\\/?presentation\\/?$/, '').replace(/\\/$/, '');\n        const pathParam = fieldPath ? `;path=${encodeURIComponent(fieldPath)}` : '';\n        href = `${studioBase}/intent/edit/id=${encodeURIComponent(docId)}${pathParam}`;\n      }\n    } catch {\n      // ignore href build errors\n    }\n\n    return {\n      // Canonical hints for overlay decoders\n      _origin: 'sanity',\n      projectId: config?.projectId,\n      dataset: config?.dataset,\n      studioUrl,\n      path: jsonPath,\n      source: sourceMap.source,\n      href,\n      ...mapping,\n    };\n  }\n  \n  // Try to find parent paths\n  let currentPath = jsonPath;\n  while (currentPath.includes('[') || currentPath.includes('.')) {\n    const lastIndex = Math.max(\n      currentPath.lastIndexOf('['),\n      currentPath.lastIndexOf('.')\n    );\n    if (lastIndex === -1) break;\n    \n    currentPath = currentPath.substring(0, lastIndex);\n    if (sourceMap.mappings[currentPath]) {\n      return {\n        _origin: 'sanity',\n        projectId: config?.projectId,\n        dataset: config?.dataset,\n        studioUrl: sourceMap.studioUrl,\n        source: sourceMap.source,\n        path: currentPath,\n        ...sourceMap.mappings[currentPath]\n      };\n    }\n  }\n  \n  return null;\n}\n\n/**\n * Get the studio URL from environment or config\n */\nfunction getStudioUrl(config?: Partial<StegaConfig>): string | undefined {\n  if (config?.studioUrl) {\n    return config.studioUrl;\n  }\n  \n  // Try environment variables\n  const envStudioUrl = process.env.NEXT_PUBLIC_SANITY_STUDIO_URL || \n                       process.env.SANITY_STUDIO_URL;\n  \n  if (envStudioUrl) {\n    return envStudioUrl;\n  }\n  \n  // Default based on environment\n  if (process.env.NODE_ENV === 'development') {\n    // Prefer HTTPS proxy for cookie/iframe parity with Presentation\n    return 'https://localhost:3334/presentation';\n  }\n  \n  return undefined;\n}\n\n/**\n * Check if stega should be enabled\n */\nexport function shouldEnableStega(isDraftMode: boolean, config?: Partial<StegaConfig>): boolean {\n  // Explicit config takes precedence\n  if (config?.enabled !== undefined) {\n    return config.enabled;\n  }\n  \n  // Default: enable in draft mode or development\n  return isDraftMode || process.env.NODE_ENV === 'development';\n}\n\n/**\n * Process Sanity response to handle stega encoding\n */\nexport function processStegaResponse(\n  response: any,\n  isDraftMode: boolean,\n  config?: Partial<StegaConfig> | StegaConfig\n): any {\n  // If stega is not enabled, return just the result\n  if (!shouldEnableStega(isDraftMode, config)) {\n    return response.result;\n  }\n  \n  // When stega is enabled, we need to encode the source map into the result\n  const result = response.result;\n  const sourceMap = response.resultSourceMap;\n  \n  if (sourceMap && config?.enabled) {\n    // Add studio URL to source map for visual editing\n    const studioUrl = getStudioUrl(config);\n    if (studioUrl && sourceMap) {\n      sourceMap.studioUrl = studioUrl;\n    }\n    \n    // Encode stega metadata into the result\n    const fullConfig = buildStegaConfig(isDraftMode, config);\n    return encodeStegaInResult(result, sourceMap, fullConfig);\n  }\n  \n  return result;\n}\n\n/**\n * Build stega configuration from environment and options\n */\nexport function buildStegaConfig(\n  isDraftMode: boolean,\n  options?: Partial<StegaConfig>\n): StegaConfig {\n  const config: Partial<StegaConfig> = options || {};\n  const enabled = shouldEnableStega(isDraftMode, config);\n  \n  const studioUrl = getStudioUrl(config);\n  const projectId = config.projectId || process.env.NEXT_PUBLIC_SANITY_PROJECT_ID;\n  const dataset = config.dataset || process.env.NEXT_PUBLIC_SANITY_DATASET;\n\n  return {\n    enabled,\n    ...(studioUrl !== undefined ? { studioUrl } : {}),\n    ...(config.basePath !== undefined ? { basePath: config.basePath } : {}),\n    ...(config.filter !== undefined ? { filter: config.filter } : {}),\n    ...(projectId !== undefined ? { projectId } : {}),\n    ...(dataset !== undefined ? { dataset } : {}),\n  } as StegaConfig;\n}\n\n/**\n * Clean stega-encoded strings (remove invisible characters)\n * Useful for comparing values or using in business logic\n */\nexport function stegaClean<T = any>(value: T): T {\n  if (vercelStegaClean) {\n    return vercelStegaClean(value);\n  }\n  \n  // Inline implementation - remove all stega Unicode characters\n  const stegaChars = Object.values(STEGA_CODES.hex)\n    .map(code => `\\\\u{${code.toString(16)}}`)\n    .join('');\n  const stegaRegex = new RegExp(`[${stegaChars}]{4,}`, 'gu');\n  \n  const cleanString = (str: string) => str.replace(stegaRegex, '');\n  \n  // Recursively clean all strings in the value\n  if (typeof value === 'string') {\n    return cleanString(value) as T;\n  }\n  \n  if (Array.isArray(value)) {\n    return value.map(item => stegaClean(item)) as T;\n  }\n  \n  if (value && typeof value === 'object') {\n    const cleaned: any = {};\n    for (const key in value) {\n      if ((value as any).hasOwnProperty(key)) {\n        cleaned[key] = stegaClean((value as any)[key]);\n      }\n    }\n    return cleaned;\n  }\n  \n  return value;\n}","/**\n * @file core.ts\n * @description Next.js-native, edge-compatible Sanity data fetcher with stega support\n * @author Invisible Cities Agency\n * @license MIT\n */\n\nimport { buildStegaConfig, processStegaResponse, type StegaConfig } from './stega';\n\n// Helper to check if draft mode is enabled in Next.js\nasync function isDraftModeEnabled(): Promise<boolean> {\n  try {\n    // Dynamic import to avoid build issues in non-Next.js environments\n    const { draftMode } = await import('next/headers');\n    const draft = await draftMode();\n    return draft.isEnabled;\n  } catch {\n    // Not in Next.js or draft mode not available\n    return false;\n  }\n}\n\n// Detect if current request should enable stega/visual editing overlays\n// Uses Next.js headers/cookies if available, but degrades gracefully when not running in Next\nexport async function detectStegaRequest(options?: {\n  /** Feature flag cookie name set by Studio or a toggle endpoint (default: 'ic_stega') */\n  cookieName?: string;\n  /** Optional custom header to allow one-shot enablement (default checks common names) */\n  headerName?: string;\n  /** Studio URL or origin to validate Referer against; defaults to NEXT_PUBLIC_SANITY_STUDIO_URL */\n  studioUrl?: string;\n  /** Force enable regardless of environment signals */\n  forceEnable?: boolean;\n  /** Force disable regardless of environment signals */\n  forceDisable?: boolean;\n}): Promise<boolean> {\n  if (options?.forceEnable) return true;\n  if (options?.forceDisable) return false;\n\n  // Defaults\n  const cookieName = options?.cookieName ?? 'ic_stega';\n  const studioEnv = options?.studioUrl || process.env.NEXT_PUBLIC_SANITY_STUDIO_URL || process.env.SANITY_STUDIO_URL;\n\n  try {\n    // Dynamic import to avoid hard Next.js dependency\n    const mod = await import('next/headers');\n    const cookies = (mod as any).cookies?.bind(mod);\n    const headers = (mod as any).headers?.bind(mod);\n\n    const draft = (await ((mod as any).draftMode?.() ?? { isEnabled: false }));\n    if (draft?.isEnabled) return true;\n\n    // Check cookie flag\n    const hasCookie = typeof cookies === 'function' ? Boolean(cookies().get(cookieName)?.value) : false;\n    if (hasCookie) return true;\n\n    // Check custom or common headers\n    const hdrs = typeof headers === 'function' ? headers() : undefined;\n    const headerName = options?.headerName;\n    const headerCandidates = [headerName, 'x-ic-stega', 'x-sanity-present', 'x-sanity-preview'].filter(Boolean) as string[];\n    if (hdrs) {\n      for (const name of headerCandidates) {\n        const v = hdrs.get(name);\n        if (v && v !== '0' && v.toLowerCase() !== 'false') return true;\n      }\n    }\n\n    // Referer origin check against Studio origin\n    if (hdrs && studioEnv) {\n      const ref = hdrs.get('referer');\n      if (ref) {\n        try {\n          const refererOrigin = new URL(ref).origin;\n          const studioOrigin = new URL(studioEnv).origin;\n          if (refererOrigin === studioOrigin) return true;\n        } catch {\n          // ignore URL parse errors\n        }\n      }\n    }\n  } catch {\n    // Not in Next.js environment; fall through\n  }\n\n  // Fallback: enable in development if explicitly configured via env\n  if (process.env.NEXT_PUBLIC_ENABLE_STEGA === '1') return true;\n  return false;\n}\n\n// Get config from environment variables\nconst getProjectId = () => {\n  const id = process.env.NEXT_PUBLIC_SANITY_PROJECT_ID;\n  if (!id) {\n    throw new Error('NEXT_PUBLIC_SANITY_PROJECT_ID environment variable is required');\n  }\n  return id;\n};\n\nconst apiVersion = process.env.NEXT_PUBLIC_SANITY_API_VERSION || '2025-02-10';\n\n// Get the viewer token - check multiple possible env vars\nconst getViewerToken = () => {\n  return process.env.SANITY_VIEWER_TOKEN || \n         process.env.SANITY_API_READ_TOKEN ||\n         process.env.NEXT_PUBLIC_SANITY_VIEWER_TOKEN;\n};\n\nexport type QueryParams = Record<string, string | number | boolean | null | undefined | Array<string | number | boolean>>;\n\nexport interface EdgeSanityFetchOptions {\n  /** Sanity dataset to query (e.g., 'production', 'staging') */\n  dataset: string;\n  /** GROQ query string */\n  query: string;\n  /** Optional query parameters for GROQ placeholders */\n  params?: QueryParams;\n  /** Whether to use Sanity's CDN (faster but no auth) */\n  useCdn?: boolean;\n  /** Whether to include auth token for draft preview access */\n  useAuth?: boolean;\n  /** Stega configuration for visual editing */\n  stega?: Partial<StegaConfig>;\n}\n\n/**\n * Simple rate limiter to prevent 429 errors\n * @internal\n */\nclass EdgeRateLimiter {\n  private lastRequest = 0;\n  private readonly minInterval = 100; // 10 req/sec max\n\n  async throttle(): Promise<void> {\n    const now = Date.now();\n    const timeSinceLastRequest = now - this.lastRequest;\n\n    if (timeSinceLastRequest < this.minInterval) {\n      const delay = this.minInterval - timeSinceLastRequest;\n      await new Promise(resolve => setTimeout(resolve, delay));\n    }\n\n    this.lastRequest = Date.now();\n  }\n}\n\nconst rateLimiter = new EdgeRateLimiter();\n\n/**\n * Fetches data from Sanity using native fetch API\n * Compatible with Edge Runtime and static generation\n */\nexport async function edgeSanityFetch<T>({\n  dataset,\n  query,\n  params = {},\n  useCdn = false,\n  useAuth = false,\n  stega\n}: EdgeSanityFetchOptions): Promise<T> {\n  // Apply rate limiting\n  await rateLimiter.throttle();\n\n  // Build the query URL\n  const projectId = getProjectId();\n\n  // Determine if we need source maps for stega (before choosing base URL)\n  const isDraftMode = useAuth;\n  const stegaConfig = buildStegaConfig(isDraftMode, stega);\n\n  // When stega is enabled, force non-CDN API to ensure resultSourceMap is returned\n  const useCdnEffective = stegaConfig.enabled ? false : useCdn;\n  const baseUrl = useCdnEffective\n    ? `https://${projectId}.apicdn.sanity.io`\n    : `https://${projectId}.api.sanity.io`;\n\n  const url = new URL(`${baseUrl}/v${apiVersion}/data/query/${dataset}`);\n  url.searchParams.set('query', query);\n  \n  if (useAuth) {\n    // Use 'previewDrafts' perspective to see draft documents merged with published\n    url.searchParams.set('perspective', 'previewDrafts');\n  }\n  \n  // Request source maps when stega is enabled\n  if (stegaConfig.enabled) {\n    url.searchParams.set('resultSourceMap', 'true');\n  }\n\n  // Add parameters\n  Object.entries(params).forEach(([key, value]) => {\n    url.searchParams.set(`$${key}`, JSON.stringify(value));\n  });\n\n  // Build headers\n  const headers: Record<string, string> = {\n    'Accept': 'application/json',\n  };\n\n  // Use env var for auth to maintain static generation compatibility\n  if (useAuth) {\n    const envToken = getViewerToken();\n    if (envToken) {\n      headers['Authorization'] = `Bearer ${envToken}`;\n    }\n  }\n  \n  const response = await fetch(url.toString(), {\n    method: 'GET',\n    headers,\n  });\n\n  if (!response.ok) {\n    await response.text(); // Consume the body to prevent memory leak\n    throw new Error(`Sanity fetch failed: ${response.status} ${response.statusText}`);\n  }\n\n  const data = await response.json();\n  \n  // Process response with stega support (reuse stegaConfig from above)\n  return processStegaResponse(data, isDraftMode, stegaConfig);\n}\n\n/**\n * Factory function to create a typed Sanity fetcher for a given dataset\n */\nexport function createEdgeSanityFetcher(dataset: string, useAuth = false, stega?: Partial<StegaConfig>) {\n  return <T>(query: string, params?: QueryParams) => {\n    const options: EdgeSanityFetchOptions = {\n      dataset,\n      query,\n      useAuth,\n      ...(stega !== undefined ? { stega } : {}),\n      ...(params !== undefined ? { params } : {}),\n    };\n    return edgeSanityFetch<T>(options);\n  };\n}\n\n/**\n * Next.js-aware Sanity fetcher that automatically handles draft mode\n * This is the primary fetcher for Next.js applications\n * \n * @example\n * const data = await sanityFetch('*[_type == \"post\"][0]');\n */\nexport async function sanityFetch<T = unknown>(\n  query: string,\n  params?: QueryParams,\n  options?: {\n    dataset?: string;\n    /** Override automatic draft mode detection */\n    forceAuth?: boolean;\n    /** Stega configuration for visual editing */\n    stega?: Partial<StegaConfig>;\n  }\n): Promise<T> {\n  const dataset = options?.dataset || process.env.NEXT_PUBLIC_SANITY_DATASET || 'production';\n  const useAuth = options?.forceAuth ?? await isDraftModeEnabled();\n  \n  return edgeSanityFetch<T>({\n    dataset,\n    query,\n    ...(params !== undefined ? { params } : {}),\n    useCdn: !useAuth, // Use CDN when not authenticated\n    useAuth,\n    stega: options?.stega || { enabled: useAuth }, // Enable stega in draft mode by default\n  });\n}\n\n/**\n * Presentation-aware hybrid fetcher\n * Automatically enables authenticated fetch + stega overlays when a Studio/Presentation\n * signal is detected (draftMode cookie, feature flag cookie, referer from Studio, or header).\n * Otherwise uses fast CDN fetch with no stega.\n */\nexport async function sanityFetchHybrid<T = unknown>(\n  query: string,\n  params?: QueryParams,\n  options?: {\n    dataset?: string;\n    /** Optional stega options to merge with defaults */\n    stega?: Partial<StegaConfig>;\n    /** Detection overrides */\n    cookieName?: string;\n    headerName?: string;\n    studioUrl?: string;\n    forceEnableStega?: boolean;\n    forceDisableStega?: boolean;\n  }\n): Promise<T> {\n  const dataset = options?.dataset || process.env.NEXT_PUBLIC_SANITY_DATASET || 'production';\n  const enableStega = await detectStegaRequest({\n    ...(options?.cookieName !== undefined ? { cookieName: options.cookieName } : {}),\n    ...(options?.headerName !== undefined ? { headerName: options.headerName } : {}),\n    ...(options?.studioUrl !== undefined ? { studioUrl: options.studioUrl } : {}),\n    ...(options?.forceEnableStega !== undefined ? { forceEnable: options.forceEnableStega } : {}),\n    ...(options?.forceDisableStega !== undefined ? { forceDisable: options.forceDisableStega } : {}),\n  });\n\n  return edgeSanityFetch<T>({\n    dataset,\n    query,\n    ...(params !== undefined ? { params } : {}),\n    useCdn: !enableStega,\n    useAuth: enableStega,\n    stega: { enabled: enableStega, ...(options?.stega || {}) },\n  });\n}\n\n/**\n * Sanity fetcher with automatic draft fallback\n * Tries to fetch published content first, falls back to drafts if empty\n * Perfect for singleton documents that might only exist as drafts\n * \n * @example\n * const page = await sanityFetchWithFallback('*[_type == \"page\" && slug.current == $slug][0]', { slug });\n */\nexport async function sanityFetchWithFallback<T = unknown>(\n  query: string,\n  params?: QueryParams,\n  options?: {\n    dataset?: string;\n    /** Log when falling back to drafts */\n    logFallback?: boolean;\n    /** Stega configuration for visual editing */\n    stega?: Partial<StegaConfig>;\n  }\n): Promise<T> {\n  const dataset = options?.dataset || process.env.NEXT_PUBLIC_SANITY_DATASET || 'production';\n  const isNextDraftMode = await isDraftModeEnabled();\n  \n  // If already in draft mode, just use authenticated fetch\n  if (isNextDraftMode) {\n    return edgeSanityFetch<T>({\n      dataset,\n      query,\n      ...(params !== undefined ? { params } : {}),\n      useCdn: false,\n      useAuth: true,\n      stega: options?.stega || { enabled: true },\n    });\n  }\n  \n  // Try published content first\n  const publishedResult = await edgeSanityFetch<T>({\n    dataset,\n    query,\n    ...(params !== undefined ? { params } : {}),\n    useCdn: true,\n    useAuth: false,\n  });\n  \n  // If we got content, return it\n  if (publishedResult) {\n    return publishedResult;\n  }\n  \n  // No published content, try drafts\n  if (options?.logFallback !== false && process.env.NODE_ENV !== 'production') {\n    console.warn('[sanityFetchWithFallback] No published content found, checking for drafts...');\n  }\n  \n  const draftResult = await edgeSanityFetch<T>({\n    dataset,\n    query,\n    ...(params !== undefined ? { params } : {}),\n    useCdn: false,\n    useAuth: true,\n    stega: options?.stega || { enabled: true },\n  });\n  \n  if (draftResult && options?.logFallback !== false && process.env.NODE_ENV !== 'production') {\n    console.warn('[sanityFetchWithFallback] Draft content found and returned');\n  }\n  \n  return draftResult;\n}\n\n/**\n * Static content fetcher - always uses CDN, never authenticates\n * Use for global settings and content that rarely changes\n * \n * @example\n * const settings = await sanityFetchStatic('*[_type == \"siteSettings\"][0]');\n */\nexport async function sanityFetchStatic<T = unknown>(\n  query: string,\n  params?: QueryParams,\n  dataset?: string\n): Promise<T> {\n  return edgeSanityFetch<T>({\n    dataset: dataset || process.env.NEXT_PUBLIC_SANITY_DATASET || 'production',\n    query,\n    ...(params !== undefined ? { params } : {}),\n    useCdn: true,\n    useAuth: false,\n  });\n}\n\n/**\n * Authenticated fetcher - always uses authentication\n * Use when you need to ensure draft content is visible\n * \n * @example\n * const drafts = await sanityFetchAuthenticated('*[_type == \"post\" && _id in path(\"drafts.**\")]');\n */\nexport async function sanityFetchAuthenticated<T = unknown>(\n  query: string,\n  params?: QueryParams,\n  dataset?: string\n): Promise<T> {\n  return edgeSanityFetch<T>({\n    dataset: dataset || process.env.NEXT_PUBLIC_SANITY_DATASET || 'production',\n    query,\n    ...(params !== undefined ? { params } : {}),\n    useCdn: false,\n    useAuth: true,\n  });\n}","/**\n * @file cache.ts\n * @description Multi-layer caching for Sanity Edge Fetcher\n * @author Invisible Cities Agency\n * @license MIT\n */\n\nimport { edgeSanityFetch, type EdgeSanityFetchOptions, type QueryParams } from './core';\nimport { Redis } from '@upstash/redis';\n\n// Check if Upstash Redis is configured\nconst REDIS_URL = process.env.KV_REST_API_URL || process.env.UPSTASH_REDIS_REST_URL;\nconst REDIS_TOKEN = process.env.KV_REST_API_TOKEN || process.env.UPSTASH_REDIS_REST_TOKEN;\nconst REDIS_READ_ONLY_TOKEN = process.env.KV_REST_API_READ_ONLY_TOKEN;\n\nconst isRedisConfigured = !!(REDIS_URL && (REDIS_TOKEN || REDIS_READ_ONLY_TOKEN));\n\n// Initialize Redis client if configured\nlet redis: Redis | null = null;\nlet redisWriter: Redis | null = null;\n\nif (isRedisConfigured && REDIS_URL && (REDIS_TOKEN || REDIS_READ_ONLY_TOKEN)) {\n  try {\n    redis = new Redis({\n      url: REDIS_URL,\n      token: (REDIS_READ_ONLY_TOKEN || REDIS_TOKEN) as string,\n      automaticDeserialization: true,\n    });\n    \n    // Separate writer client if write token available\n    if (REDIS_TOKEN) {\n      redisWriter = new Redis({\n        url: REDIS_URL,\n        token: REDIS_TOKEN,\n        automaticDeserialization: true,\n      });\n    } else {\n      redisWriter = redis;\n    }\n  } catch {\n    // Failed to initialize Redis client\n    redis = null;\n    redisWriter = null;\n  }\n}\n\ninterface CacheEntry<T> {\n  value: T;\n  timestamp: number;\n  validUntil: number;\n}\n\ninterface CachedFetchOptions extends EdgeSanityFetchOptions {\n  /** Cache configuration */\n  cache?: {\n    /** Time to live in seconds */\n    ttl?: number;\n    /** Cache key prefix */\n    prefix?: string;\n    /** Force cache refresh */\n    force?: boolean;\n    /** Enable Redis caching if available */\n    useRedis?: boolean;\n    /** Enable Next.js cache */\n    useNextCache?: boolean;\n  };\n}\n\n/**\n * Generate cache key from query and params\n */\nfunction generateCacheKey(\n  dataset: string,\n  query: string,\n  params?: QueryParams\n): string {\n  const baseKey = `sanity:${dataset}:${query}`;\n  if (!params || Object.keys(params).length === 0) {\n    return baseKey;\n  }\n  \n  // Sort params for consistent key generation\n  const sortedParams = Object.keys(params)\n    .sort()\n    .map(key => `${key}=${JSON.stringify(params[key])}`)\n    .join('&');\n  \n  return `${baseKey}:${sortedParams}`;\n}\n\n/**\n * In-memory LRU cache for edge runtime\n */\nclass MemoryCache {\n  private cache = new Map<string, CacheEntry<unknown>>();\n  private maxSize = 100;\n  \n  get<T>(key: string): T | null {\n    const entry = this.cache.get(key);\n    if (!entry) return null;\n    \n    if (Date.now() > entry.validUntil) {\n      this.cache.delete(key);\n      return null;\n    }\n    \n    // Move to end (LRU)\n    this.cache.delete(key);\n    this.cache.set(key, entry);\n    \n    return entry.value as T;\n  }\n  \n  set<T>(key: string, value: T, ttl: number): void {\n    // Evict oldest if at capacity\n    if (this.cache.size >= this.maxSize && !this.cache.has(key)) {\n      const firstKey = this.cache.keys().next().value;\n      if (firstKey !== undefined) {\n        this.cache.delete(firstKey);\n      }\n    }\n    \n    this.cache.set(key, {\n      value,\n      timestamp: Date.now(),\n      validUntil: Date.now() + (ttl * 1000)\n    });\n  }\n  \n  delete(key: string): void {\n    this.cache.delete(key);\n  }\n  \n  clear(): void {\n    this.cache.clear();\n  }\n  \n  size(): number {\n    return this.cache.size;\n  }\n}\n\n// Global memory cache instance\nconst memoryCache = new MemoryCache();\n\n/**\n * Fetches data from Sanity with multi-layer caching\n * \n * Cache layers (in order):\n * 1. In-memory LRU cache (fastest, ~1ms)\n * 2. Upstash Redis (if configured, ~10-30ms)\n * 3. Origin fetch with Next.js cache\n */\nexport async function cachedSanityFetch<T>(\n  options: CachedFetchOptions\n): Promise<T> {\n  const {\n    dataset,\n    query,\n    params,\n    cache = {}\n  } = options;\n  \n  const {\n    ttl = 60, // 1 minute default\n    prefix = '',\n    force = false,\n    useRedis = true\n  } = cache;\n  \n  const cacheKey = prefix + generateCacheKey(dataset, query, params);\n  \n  // Layer 1: Memory cache (unless force refresh)\n  if (!force) {\n    const memoryResult = memoryCache.get<T>(cacheKey);\n    if (memoryResult !== null) {\n      // Cache hit from memory\n      return memoryResult;\n    }\n  }\n  \n  // Layer 2: Redis cache (if configured and enabled)\n  if (!force && useRedis && redis) {\n    try {\n      const redisEntry = await redis.get<CacheEntry<T>>(cacheKey);\n      if (redisEntry && Date.now() <= redisEntry.validUntil) {\n        // Cache hit from Redis\n        \n        // Populate memory cache\n        memoryCache.set(cacheKey, redisEntry.value, ttl);\n        \n        return redisEntry.value;\n      }\n    } catch {\n      // Redis cache read error, continue to fetch from origin\n      // Continue to fetch from origin\n    }\n  }\n  \n  // Layer 3: Fetch from origin\n  // Cache miss, fetching from origin\n  \n  // Build safe options for edge fetch (remove cache, avoid undefined params)\n  const { cache: _cache, params: _params, ...rest } = options;\n  const edgeOptions = (params !== undefined)\n    ? { ...rest, params }\n    : { ...rest };\n  \n  // Fetch from origin (Next.js cache removed for edge compatibility)\n  const result = await edgeSanityFetch<T>(edgeOptions as EdgeSanityFetchOptions);\n  \n  // Populate caches\n  memoryCache.set(cacheKey, result, ttl);\n  \n  if (useRedis && redisWriter) {\n    try {\n      const entry: CacheEntry<T> = {\n        value: result,\n        timestamp: Date.now(),\n        validUntil: Date.now() + (ttl * 1000)\n      };\n      await redisWriter.set(cacheKey, entry, { ex: ttl });\n    } catch {\n      // Redis cache write error, continue without caching\n      // Continue without caching\n    }\n  }\n  \n  return result;\n}\n\n/**\n * Create a cached fetcher with default options\n */\nexport function createCachedFetcher(\n  dataset: string,\n  defaultCacheOptions?: CachedFetchOptions['cache']\n) {\n  return <T>(\n    query: string,\n    params?: QueryParams,\n    cacheOverrides?: CachedFetchOptions['cache']\n  ) => {\n    return cachedSanityFetch<T>({\n      dataset,\n      query,\n      ...(params !== undefined ? { params } : {}),\n      cache: { ...(defaultCacheOptions || {}), ...(cacheOverrides || {}) }\n    });\n  };\n}\n\n/**\n * Clear caches for a specific dataset or pattern\n */\nexport async function clearSanityCache(options?: {\n  dataset?: string;\n  pattern?: string;\n  clearMemory?: boolean;\n  clearRedis?: boolean;\n}): Promise<void> {\n  const {\n    dataset,\n    pattern,\n    clearMemory = true,\n    clearRedis = true\n  } = options || {};\n  \n  // Clear memory cache\n  if (clearMemory) {\n    if (!dataset && !pattern) {\n      memoryCache.clear();\n    } else {\n      // Note: Memory cache doesn't support pattern matching\n      // Would need to iterate all keys for pattern support\n      // Pattern-based memory cache clearing not implemented\n    }\n  }\n  \n  // Clear Redis cache\n  if (clearRedis && redisWriter && redis) {\n    try {\n      const keyPattern = pattern || (dataset ? `sanity:${dataset}:*` : 'sanity:*');\n      const keys = await redis.keys(keyPattern);\n      if (keys.length > 0) {\n        await redisWriter.del(...keys);\n      }\n    } catch {\n      // Failed to clear Redis cache\n    }\n  }\n}\n\n/**\n * Warm cache by pre-fetching common queries\n */\nexport async function warmSanityCache(\n  queries: Array<{\n    dataset: string;\n    query: string;\n    params?: QueryParams;\n    ttl?: number;\n  }>\n): Promise<void> {\n  await Promise.all(\n    queries.map(({ dataset, query, params, ttl }) =>\n      cachedSanityFetch({\n        dataset,\n        query,\n        ...(params !== undefined ? { params } : {}),\n        cache: { ...(ttl !== undefined ? { ttl } : {}) }\n      }).catch(() => {\n        // Failed to warm cache for query\n      })\n    )\n  );\n}\n\n// Export cache status utility\nexport function getCacheStatus() {\n  return {\n    memory: {\n      available: true,\n      size: memoryCache.size()\n    },\n    redis: {\n      available: isRedisConfigured && redis !== null,\n      configured: isRedisConfigured,\n      url: REDIS_URL ? new URL(REDIS_URL).hostname : null\n    },\n    nextCache: {\n      available: typeof window === 'undefined'\n    }\n  };\n}","/**\n * @file enhanced.ts\n * @description Enhanced Sanity fetcher with retry and real-time capabilities\n * @author Invisible Cities Agency\n * @license MIT\n */\n\nimport { edgeSanityFetch, type EdgeSanityFetchOptions, type QueryParams } from './core';\n\n// Type for p-retry module\ninterface PRetryModule {\n  default: <T>(\n    input: () => Promise<T>,\n    options?: {\n      retries?: number;\n      minTimeout?: number;\n      maxTimeout?: number;\n      onFailedAttempt?: (error: { attemptNumber: number; retriesLeft: number }) => void;\n    }\n  ) => Promise<T>;\n}\n\n// Dynamic imports for optional dependencies\nlet pRetryModule: PRetryModule | null = null;\n\n// Lazy load optional dependencies\nconst loadPRetry = async (): Promise<PRetryModule | null> => {\n  if (pRetryModule) return pRetryModule;\n  try {\n    pRetryModule = await import('p-retry');\n    return pRetryModule;\n  } catch {\n    return null;\n  }\n};\n\n/**\n * Fetches data from Sanity with automatic retry support\n * Falls back to single attempt if p-retry not installed\n */\nexport async function edgeSanityFetchWithRetry<T>(\n  options: EdgeSanityFetchOptions,\n  retryOptions?: {\n    retries?: number;\n    minTimeout?: number;\n    maxTimeout?: number;\n  }\n): Promise<T> {\n  // Try to load p-retry if available\n  const retry = await loadPRetry();\n  \n  // If p-retry not available, fall back to basic fetch\n  if (!retry) {\n    return edgeSanityFetch<T>(options);\n  }\n\n  const defaultRetryOptions = {\n    retries: 3,\n    minTimeout: 100,\n    maxTimeout: 2000,\n    onFailedAttempt: (error: { attemptNumber: number; retriesLeft: number }) => {\n      // Sanity fetch attempt failed, will retry\n      void error.attemptNumber;\n      void error.retriesLeft;\n    }\n  };\n\n  return retry.default(\n    () => edgeSanityFetch<T>(options),\n    { ...defaultRetryOptions, ...retryOptions }\n  );\n}\n\n/**\n * Creates a cached Sanity fetcher\n * Note: Use cachedSanityFetch from cache.ts for full caching support\n */\nexport function createCachedSanityFetcher(\n  dataset: string, \n  _revalidate = 60,\n  _tags?: string[]\n) {\n  // Return basic fetcher - for caching use cachedSanityFetch from cache.ts\n  return <T>(query: string, params?: QueryParams) => \n    edgeSanityFetch<T>({\n      dataset,\n      query,\n      ...(params !== undefined ? { params } : {}),\n      useCdn: true\n    });\n}\n\n/**\n * Creates an EventSource connection for real-time Sanity updates\n * Requires a server endpoint to handle SSE (see examples/vercel-sse.ts)\n */\nexport function createSanityEventSource(\n  query: string,\n  dataset = 'production',\n  options?: {\n    endpoint?: string;\n    onMessage?: (data: unknown) => void;\n    onError?: (error: Event) => void;\n  }\n) {\n  const endpoint = options?.endpoint || '/api/sanity-updates';\n  const url = new URL(endpoint, window.location.origin);\n  url.searchParams.set('query', query);\n  url.searchParams.set('dataset', dataset);\n  \n  const eventSource = new EventSource(url.toString());\n  \n  if (options?.onMessage) {\n    eventSource.onmessage = (event) => {\n      try {\n        const data = JSON.parse(event.data);\n        if (options.onMessage) {\n          options.onMessage(data);\n        }\n      } catch {\n        // Failed to parse SSE data\n      }\n    };\n  }\n  \n  if (options?.onError) {\n    eventSource.onerror = options.onError;\n  }\n  \n  return eventSource;\n}\n\n/**\n * Result type for batch fetching\n */\nexport type BatchResult<T extends Record<string, unknown>> = {\n  [K in keyof T]: T[K] | null;\n};\n\n/**\n * Batch fetcher for multiple queries in a single request\n * Reduces API calls and improves performance\n */\nexport async function batchSanityFetch<T extends Record<string, unknown>>(\n  queries: Record<string, { query: string; params?: QueryParams }>,\n  dataset: string,\n  options?: { useAuth?: boolean; useCdn?: boolean }\n): Promise<BatchResult<T>> {\n  const results: Record<string, unknown> = {};\n  \n  // Use Promise.all for parallel fetching\n  await Promise.all(\n    Object.entries(queries).map(async ([key, { query, params }]) => {\n      try {\n        results[key] = await edgeSanityFetch({\n          dataset,\n          query,\n          ...(params !== undefined ? { params } : {}),\n          ...options\n        });\n      } catch {\n        // Failed to fetch this query\n        results[key] = null;\n      }\n    })\n  );\n  \n  return results as BatchResult<T>;\n}"],"mappings":"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","names":["config_exports","__export","cacheConfig","config","createCustomFetcher","config_default","fetchers","rateLimitConfig","realtimeConfig","retryConfig","sanityConfig","warmupQueries","__toCommonJS","s","c","u","E","t","e","u","r","n","o","c","I","t","T","C","e","r","E","x","c","g","s","S","f","_","t","e","f","O","vercelStegaEncode","E","vercelStegaCombine","C","vercelStegaClean","O","STEGA_CODES","STEGA_PREFIX","encodeInvisibleBase4","data","jsonStr","encoded","char","charCode","digit","idx","shouldSkipEncoding","value","isDate","isUrl","isSanityAssetId","isStructuredPath","path","last","encodeStegaString","metadata","config","encodeStegaInResult","sourceMap","resolveSourceMapForPath","item","index","result","key","jsonPath","segment","mapping","studioUrl","href","src","docId","fieldPath","studioBase","pathParam","currentPath","lastIndex","getStudioUrl","envStudioUrl","shouldEnableStega","isDraftMode","processStegaResponse","response","fullConfig","buildStegaConfig","options","enabled","projectId","dataset","getProjectId","id","apiVersion","getViewerToken","EdgeRateLimiter","timeSinceLastRequest","delay","resolve","rateLimiter","edgeSanityFetch","dataset","query","params","useCdn","useAuth","stega","projectId","isDraftMode","stegaConfig","buildStegaConfig","baseUrl","url","key","value","headers","envToken","response","data","processStegaResponse","createEdgeSanityFetcher","options","import_redis","REDIS_URL","REDIS_TOKEN","REDIS_READ_ONLY_TOKEN","isRedisConfigured","redis","redisWriter","generateCacheKey","dataset","query","params","baseKey","sortedParams","key","MemoryCache","entry","value","ttl","firstKey","memoryCache","cachedSanityFetch","options","cache","prefix","force","useRedis","cacheKey","memoryResult","redisEntry","_cache","_params","rest","edgeOptions","result","edgeSanityFetch","createCachedFetcher","defaultCacheOptions","cacheOverrides","pRetryModule","loadPRetry","edgeSanityFetchWithRetry","options","retryOptions","retry","edgeSanityFetch","defaultRetryOptions","error","sanityConfig","cacheConfig","rateLimitConfig","retryConfig","realtimeConfig","fetchers","createEdgeSanityFetcher","createCachedFetcher","createCustomFetcher","options","dataset","ttl","prefix","useAuth","useCache","useRetry","query","params","fetchFn","edgeSanityFetchWithRetry","warmupQueries","config","config_default"]}