{"version":3,"sources":["../src/normalization.ts","../src/detection.ts","../src/site-utils.ts"],"names":["safeIntlSupported"],"mappings":"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","file":"site.cjs","sourcesContent":["import { safeIntlSupported } from \"@multisystemsuite/timezone-engine-utils\";\n\nconst DISPLAY_SUFFIX_PATTERN = /\\s*\\(.*\\)\\s*$/;\n\n/**\n * Strip any display suffix from a timezone string and return a bare IANA id.\n *\n * @example\n * normalizeTimezone(\"America/New_York (GMT-5:00)\") // \"America/New_York\"\n * normalizeTimezone(\"Asia/Kolkata (GMT+5:30)\")     // \"Asia/Kolkata\"\n * normalizeTimezone(\"\")                            // \"UTC\"\n */\nexport function normalizeTimezone(timeZone: string | null | undefined): string {\n  if (!timeZone) return \"UTC\";\n  const stripped = timeZone.replace(DISPLAY_SUFFIX_PATTERN, \"\").trim();\n  return stripped || \"UTC\";\n}\n\n/** Validate a bare IANA timezone id using the runtime Intl implementation */\nexport function isValidIanaTimezone(timeZone: string): boolean {\n  if (!safeIntlSupported()) return true;\n  try {\n    Intl.DateTimeFormat(undefined, { timeZone });\n    return true;\n  } catch {\n    return false;\n  }\n}\n\n/**\n * Normalise and validate in one step. Returns the IANA id if valid,\n * otherwise returns the fallback and logs a warning.\n */\nexport function safeTimezone(timeZone: string | null | undefined, fallback = \"UTC\"): string {\n  const tz = normalizeTimezone(timeZone);\n  if (isValidIanaTimezone(tz)) return tz;\n  console.warn(\n    `[timezone-engine] Invalid timezone \"${String(timeZone)}\" (normalised: \"${tz}\"), falling back to ${fallback}`,\n  );\n  return fallback;\n}\n\n/**\n * Sanitise a timezone value before saving it to the database.\n * Strips any display suffix so only the bare IANA id is persisted.\n *\n * @example\n * site.TimeZone = sanitizeTimezoneForDB(req.body.TimeZone);\n */\nexport function sanitizeTimezoneForDB(timeZone: string | null | undefined): string {\n  return safeTimezone(timeZone);\n}\n","import type {\n  DetectTimezoneResult,\n  Environment,\n} from \"@multisystemsuite/timezone-engine-shared-types\";\nimport { isBrowser, isNode, safeIntlSupported } from \"@multisystemsuite/timezone-engine-utils\";\nimport { isValidIanaTimezone, normalizeTimezone } from \"./normalization.js\";\n\nconst DEFAULT_TIMEZONE = \"UTC\";\n\nfunction detectViaIntl(): string | undefined {\n  if (!safeIntlSupported()) return undefined;\n  try {\n    return Intl.DateTimeFormat().resolvedOptions().timeZone;\n  } catch {\n    return undefined;\n  }\n}\n\nfunction detectViaEnv(): string | undefined {\n  if (isNode()) {\n    return process.env[\"TZ\"] ?? process.env[\"DEFAULT_TIMEZONE\"];\n  }\n  return undefined;\n}\n\nfunction getEnvironment(): Environment {\n  if (isBrowser()) return \"browser\";\n  if (isNode()) return \"node\";\n  return \"ssr\";\n}\n\n/** Detect timezone in browser environment */\nexport function detectBrowserTimezone(): DetectTimezoneResult {\n  const timezone = detectViaIntl() ?? DEFAULT_TIMEZONE;\n  return { timezone, source: \"intl\", environment: \"browser\" };\n}\n\n/** Detect timezone in Node.js environment */\nexport function detectNodeTimezone(): DetectTimezoneResult {\n  const envTz = detectViaEnv();\n  const timezone = envTz ?? detectViaIntl() ?? DEFAULT_TIMEZONE;\n  return {\n    timezone,\n    source: envTz ? \"env\" : \"intl\",\n    environment: \"node\",\n  };\n}\n\n/** Detect server timezone */\nexport function detectServerTimezone(defaultTimezone = DEFAULT_TIMEZONE): DetectTimezoneResult {\n  const envTz = detectViaEnv();\n  const timezone = envTz ?? defaultTimezone;\n  return {\n    timezone,\n    source: envTz ? \"env\" : \"config\",\n    environment: isNode() ? \"node\" : \"ssr\",\n  };\n}\n\n/** Universal timezone detection — SSR-safe */\nexport function detectTimezone(fallback = DEFAULT_TIMEZONE): DetectTimezoneResult {\n  const intlTz = detectViaIntl();\n  const envTz = detectViaEnv();\n\n  if (intlTz) {\n    return { timezone: intlTz, source: \"intl\", environment: getEnvironment() };\n  }\n  if (envTz) {\n    return { timezone: envTz, source: \"env\", environment: getEnvironment() };\n  }\n  return { timezone: fallback, source: \"config\", environment: getEnvironment() };\n}\n\n/** Get user's current timezone */\nexport function getUserTimezone(fallback = DEFAULT_TIMEZONE): string {\n  return detectTimezone(fallback).timezone;\n}\n\n/** Resolve timezone from header, cookie, or query param value */\nexport function resolveTimezoneFromValue(\n  value: string | undefined,\n  fallback = DEFAULT_TIMEZONE,\n): DetectTimezoneResult {\n  const normalized = normalizeTimezone(value);\n  if (value && isValidTimezone(normalized)) {\n    return { timezone: normalized, source: \"header\", environment: getEnvironment() };\n  }\n  return detectTimezone(fallback);\n}\n\n/**\n * Validate that a timezone string (after normalisation) is a valid IANA id\n * recognised by the runtime Intl implementation.\n */\nexport function isValidTimezone(timeZone: string | null | undefined): boolean {\n  const tz = normalizeTimezone(timeZone);\n  return isValidIanaTimezone(tz);\n}\n\n/** List all supported IANA timezones via Intl */\nexport function listSupportedTimezones(): string[] {\n  if (typeof Intl.supportedValuesOf === \"function\") {\n    return Intl.supportedValuesOf(\"timeZone\");\n  }\n  return [];\n}\n","import { safeTimezone } from \"./normalization.js\";\n\nexport { normalizeTimezone, safeTimezone, sanitizeTimezoneForDB } from \"./normalization.js\";\nexport { isValidTimezone } from \"./detection.js\";\n\n/**\n * Convert a Date object to a wall-clock Date in the given timezone.\n * The returned Date's local methods (.getHours(), .getDate(), …) reflect\n * the time at that location.\n */\nexport function convertToTimezone(date: Date, timeZone: string): Date {\n  const tz = safeTimezone(timeZone);\n  try {\n    const formatter = new Intl.DateTimeFormat(\"en-US\", {\n      timeZone: tz,\n      year: \"numeric\",\n      month: \"2-digit\",\n      day: \"2-digit\",\n      hour: \"2-digit\",\n      minute: \"2-digit\",\n      second: \"2-digit\",\n      hour12: false,\n    });\n\n    const parts = formatter.formatToParts(date);\n    const get = (type: Intl.DateTimeFormatPartTypes) =>\n      parts.find((p) => p.type === type)?.value ?? \"0\";\n\n    return new Date(\n      parseInt(get(\"year\"), 10),\n      parseInt(get(\"month\"), 10) - 1,\n      parseInt(get(\"day\"), 10),\n      parseInt(get(\"hour\"), 10),\n      parseInt(get(\"minute\"), 10),\n      parseInt(get(\"second\"), 10),\n    );\n  } catch (error) {\n    console.error(`[timezone-engine] convertToTimezone failed for \"${timeZone}\", using UTC`, error);\n    return date;\n  }\n}\n\n/**\n * Format a Date using a simple token-based format string.\n *\n * Supported tokens: YYYY, YY, MM, DD, HH, mm, ss, SSS\n *\n * @example\n * formatDate(new Date(), \"DD/MM/YYYY HH:mm\")\n */\nexport function formatDate(date: Date, format: string): string {\n  const pad = (n: number, len = 2) => String(n).padStart(len, \"0\");\n  const tokens: Record<string, string> = {\n    YYYY: String(date.getFullYear()),\n    YY: String(date.getFullYear()).slice(-2),\n    MM: pad(date.getMonth() + 1),\n    DD: pad(date.getDate()),\n    HH: pad(date.getHours()),\n    mm: pad(date.getMinutes()),\n    ss: pad(date.getSeconds()),\n    SSS: pad(date.getMilliseconds(), 3),\n  };\n\n  let result = format;\n  for (const [token, value] of Object.entries(tokens)) {\n    result = result.replace(new RegExp(token, \"g\"), value);\n  }\n  return result;\n}\n\n/**\n * Convert a date to the site's timezone then format it.\n * Returns null if the date is invalid or not provided.\n */\nexport function formatDateForSite(\n  date: Date | string | null | undefined,\n  timeZone: string,\n  dateTimeFormat: string,\n): string | null {\n  if (!date) return null;\n  const dateObj = typeof date === \"string\" ? new Date(date) : date;\n  if (isNaN(dateObj.getTime())) return null;\n  const converted = convertToTimezone(dateObj, timeZone);\n  return formatDate(converted, dateTimeFormat);\n}\n\n/**\n * Return the current timestamp formatted for a given site timezone + format.\n */\nexport function getFormattedDateTime(timeZone: string, dateTimeFormat: string): string {\n  return formatDateForSite(new Date(), timeZone, dateTimeFormat) ?? new Date().toISOString();\n}\n\n/** Alias for token-based formatting — avoids clash with locale-aware formatDate */\nexport const formatDateWithPattern = formatDate;\n"]}