{"version":3,"sources":["../../../src/lib/server/googleReviews.ts"],"names":["kv"],"mappings":"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","file":"googleReviews.cjs","sourcesContent":["import { kv } from '@vercel/kv';\n\nconst env = import.meta.env;\n\nif (!process.env.GOOGLE_PLACES_API_KEY && env.GOOGLE_PLACES_API_KEY) {\n\tprocess.env.GOOGLE_PLACES_API_KEY = env.GOOGLE_PLACES_API_KEY;\n}\nif (!process.env.GOOGLE_PLACES_DEFAULT_PLACE_ID && env.GOOGLE_PLACES_DEFAULT_PLACE_ID) {\n\tprocess.env.GOOGLE_PLACES_DEFAULT_PLACE_ID = env.GOOGLE_PLACES_DEFAULT_PLACE_ID;\n}\nif (!process.env.KV_REST_API_URL && env.KV_REST_API_URL) {\n\tprocess.env.KV_REST_API_URL = env.KV_REST_API_URL;\n}\nif (!process.env.KV_REST_API_TOKEN && env.KV_REST_API_TOKEN) {\n\tprocess.env.KV_REST_API_TOKEN = env.KV_REST_API_TOKEN;\n}\nif (!process.env.KV_REST_API_READ_ONLY_TOKEN && env.KV_REST_API_READ_ONLY_TOKEN) {\n\tprocess.env.KV_REST_API_READ_ONLY_TOKEN = env.KV_REST_API_READ_ONLY_TOKEN;\n}\nconst CACHE_PREFIX = 'google-reviews:';\nconst DEFAULT_TTL_SECONDS =\n\tNumber.parseInt(env.GOOGLE_REVIEWS_CACHE_TTL ?? process.env.GOOGLE_REVIEWS_CACHE_TTL ?? '', 10) ||\n\t60 * 60 * 24; // 24h\nconst CACHE_EXPIRY_BUFFER = DEFAULT_TTL_SECONDS * 2;\nconst GOOGLE_API_BASE = 'https://places.googleapis.com/v1';\nconst FIELD_MASK = 'rating,userRatingCount,displayName';\nconst isKvConfigured = Boolean(\n\t(env.KV_REST_API_URL || process.env.KV_REST_API_URL) &&\n\t\t(env.KV_REST_API_TOKEN || process.env.KV_REST_API_TOKEN) &&\n\t\t(env.KV_REST_API_READ_ONLY_TOKEN || process.env.KV_REST_API_READ_ONLY_TOKEN),\n);\n\ntype CachePayload = {\n\tplaceId: string;\n\tbusinessName?: string;\n\tlanguageCode: string;\n\trating: number;\n\treviewCount: number;\n\treviewsUrl: string;\n\tupdatedAt: string;\n};\n\nexport type GoogleReviewSnapshot = CachePayload & {\n\tsource: 'fresh' | 'cache' | 'fallback';\n};\n\ntype InMemoryCacheEntry = {\n\tpayload: CachePayload;\n\texpiresAt: number;\n};\n\ndeclare global {\n\t// eslint-disable-next-line no-var\n\tvar __GOOGLE_REVIEWS_CACHE__: Map<string, InMemoryCacheEntry> | undefined;\n}\n\nconst inMemoryCache =\n\tglobalThis.__GOOGLE_REVIEWS_CACHE__ ??\n\t(globalThis.__GOOGLE_REVIEWS_CACHE__ = new Map<string, InMemoryCacheEntry>());\n\nconst normalizeLanguageCode = (languageCode?: string) => {\n\tconst normalized = languageCode?.trim().toLowerCase();\n\treturn normalized?.length ? normalized : 'en';\n};\nconst getCacheKey = (placeId: string, languageCode?: string) =>\n\t`${CACHE_PREFIX}${placeId}:${normalizeLanguageCode(languageCode)}`;\n\nasync function readCache(placeId: string, languageCode?: string): Promise<CachePayload | null> {\n\tconst key = getCacheKey(placeId, languageCode);\n\n\tif (isKvConfigured) {\n\t\tconst value = await kv.get<CachePayload>(key);\n\t\treturn value ?? null;\n\t}\n\n\tconst entry = inMemoryCache.get(key);\n\tif (!entry) {\n\t\treturn null;\n\t}\n\n\tif (entry.expiresAt <= Date.now()) {\n\t\tinMemoryCache.delete(key);\n\t\treturn null;\n\t}\n\n\treturn entry.payload;\n}\n\nasync function writeCache(placeId: string, payload: CachePayload): Promise<void> {\n\tconst key = getCacheKey(placeId, payload.languageCode);\n\n\tif (isKvConfigured) {\n\t\tawait kv.set(key, payload, { ex: CACHE_EXPIRY_BUFFER });\n\t\treturn;\n\t}\n\n\tinMemoryCache.set(key, {\n\t\tpayload,\n\t\texpiresAt: Date.now() + CACHE_EXPIRY_BUFFER * 1000,\n\t});\n}\n\ntype FetchOptions = {\n\tplaceId: string;\n\tlanguageCode: string;\n\tbusinessName?: string;\n};\n\nasync function fetchFromGoogle({ placeId, languageCode, businessName }: FetchOptions): Promise<CachePayload> {\n\tconst apiKey = env.GOOGLE_PLACES_API_KEY ?? process.env.GOOGLE_PLACES_API_KEY;\n\n\tif (!apiKey) {\n\t\tthrow new Error('GOOGLE_PLACES_API_KEY is not set');\n\t}\n\n\tif (!placeId) {\n\t\tthrow new Error('A Google placeId is required');\n\t}\n\n\tconst url = new URL(`${GOOGLE_API_BASE}/places/${placeId}`);\n\turl.searchParams.set('languageCode', languageCode);\n\n\tconst response = await fetch(url, {\n\t\theaders: {\n\t\t\t'X-Goog-Api-Key': apiKey,\n\t\t\t'X-Goog-FieldMask': FIELD_MASK,\n\t\t},\n\t});\n\n\tconst responseText = await response.text();\n\tif (!response.ok) {\n\t\tlet errorDetail = response.statusText;\n\n\t\ttry {\n\t\t\tconst parsed = JSON.parse(responseText);\n\t\t\terrorDetail = parsed.error?.message ?? response.statusText;\n\t\t} catch {\n\t\t\t// Keep default message\n\t\t}\n\n\t\tthrow new Error(`Google Places API error (${response.status}): ${errorDetail}`);\n\t}\n\n\tconst data = JSON.parse(responseText);\n\tconst rating = typeof data.rating === 'number' ? data.rating : 0;\n\tconst reviewCount = typeof data.userRatingCount === 'number' ? data.userRatingCount : 0;\n\tconst displayName = data.displayName?.text as string | undefined;\n\tconst normalizedDisplayName = displayName?.trim().length ? displayName.trim() : undefined;\n\tconst normalizedInputName = businessName?.trim().length ? businessName.trim() : undefined;\n\tconst resolvedBusinessName = normalizedDisplayName ?? normalizedInputName;\n\n\treturn {\n\t\tplaceId,\n\t\tbusinessName: resolvedBusinessName,\n\t\tlanguageCode,\n\t\trating,\n\t\treviewCount,\n\t\treviewsUrl: buildReviewsUrl(placeId),\n\t\tupdatedAt: new Date().toISOString(),\n\t};\n}\n\nconst buildReviewsUrl = (placeId: string) =>\n\t`https://search.google.com/local/reviews?placeid=${encodeURIComponent(placeId)}`;\n\ntype SnapshotOptions = {\n\tlanguageCode?: string;\n\tbusinessName?: string;\n\tforceRefresh?: boolean;\n};\n\nexport async function getGoogleReviewSnapshot(\n\tplaceId: string,\n\toptions: SnapshotOptions = {},\n): Promise<{ data: GoogleReviewSnapshot; error?: string }> {\n\tconst languageCode = normalizeLanguageCode(options.languageCode);\n\tconst cached = await readCache(placeId, languageCode);\n\tconst now = Date.now();\n\tconst staleThreshold = DEFAULT_TTL_SECONDS * 1000;\n\tconst isCachedFresh = cached ? now - new Date(cached.updatedAt).getTime() <= staleThreshold : false;\n\n\tif (!options.forceRefresh && cached && isCachedFresh) {\n\t\treturn {\n\t\t\tdata: {\n\t\t\t\t...cached,\n\t\t\t\tsource: 'cache',\n\t\t\t},\n\t\t};\n\t}\n\n\ttry {\n\t\tconst fresh = await fetchFromGoogle({\n\t\t\tplaceId,\n\t\t\tlanguageCode,\n\t\t\tbusinessName: options.businessName ?? cached?.businessName,\n\t\t});\n\n\t\tawait writeCache(placeId, fresh);\n\n\t\treturn {\n\t\t\tdata: {\n\t\t\t\t...fresh,\n\t\t\t\tsource: 'fresh',\n\t\t\t},\n\t\t};\n\t} catch (error) {\n\t\tconst message = error instanceof Error ? error.message : 'Unknown error talking to Google Places API';\n\n\t\tif (cached) {\n\t\t\treturn {\n\t\t\t\tdata: {\n\t\t\t\t\t...cached,\n\t\t\t\t\tsource: 'fallback',\n\t\t\t\t},\n\t\t\t\terror: message,\n\t\t\t};\n\t\t}\n\n\t\tthrow error instanceof Error ? error : new Error(message);\n\t}\n}\n"]}