{"version":3,"file":"api.cjs","sources":["../../src/lib/api/client.ts","../../src/lib/api/transformers/cart.ts","../../src/lib/tracking/normalize.ts","../../src/lib/tracking/context.ts","../../src/lib/tracking/consent.ts","../../src/lib/tracking/identity.ts","../../src/lib/tracking/attribution-store.ts","../../src/lib/tracking/beacon.ts","../../src/lib/api/orders.ts","../../src/lib/api/promo.ts","../../src/lib/api/transformers/performer.ts","../../src/lib/api/result.ts","../../src/lib/api/events.ts","../../src/lib/api/venues.ts","../../src/lib/api/transformers/giftCard.ts","../../src/lib/api/gift-cards.ts","../../src/lib/api/waitlist.ts","../../src/lib/api/cta.ts","../../src/lib/api/transformers/order.ts","../../src/lib/api/transformers/event.ts","../../src/lib/api/transformers/venue.ts"],"sourcesContent":["/**\r\n * API Client for MicDrop Public Checkout API\r\n *\r\n * Provides a centralized HTTP client with:\r\n * - Configurable base URL\r\n * - Per-request timeout via AbortController\r\n * - Exponential-backoff retry on 5xx and network errors (idempotent methods)\r\n * - Consistent error surfacing\r\n * - Request/response typing\r\n */\r\n\r\nimport { createLogger } from '@getmicdrop/svelte-components/utils/logger';\r\nconst logger = createLogger('VC');\r\nimport type { ApiConfig, ApiResponse } from './types.js';\r\n\r\n// Default API configuration\r\nconst DEFAULT_CONFIG: Required<ApiConfig> = {\r\n  baseUrl: 'https://get-micdrop.com',\r\n  timeout: 30000,\r\n  retries: 2,\r\n  retryDelay: 500,\r\n  onError: (error: Error) => logger.error('API Error:', error),\r\n};\r\n\r\nlet globalConfig: Required<ApiConfig> = { ...DEFAULT_CONFIG };\r\n\r\nexport function configureApi(config: Partial<ApiConfig>): void {\r\n  globalConfig = { ...globalConfig, ...config };\r\n}\r\n\r\nexport function getApiConfig(): Required<ApiConfig> {\r\n  return { ...globalConfig };\r\n}\r\n\r\nexport function getPublicBaseUrl(): string {\r\n  return `${globalConfig.baseUrl}/api/v2/public`;\r\n}\r\n\r\nexport function getLegacyPublicUrl(): string {\r\n  return `${globalConfig.baseUrl}/api/public`;\r\n}\r\n\r\nexport function getOrdersV2Url(): string {\r\n  return `${globalConfig.baseUrl}/api/orders/v2/public`;\r\n}\r\n\r\n/** @deprecated IP is now resolved server-side via request headers. */\r\nexport async function getClientIP(): Promise<string> {\r\n  return '';\r\n}\r\n\r\n/**\r\n * Sleep utility for retry backoff.\r\n */\r\nconst sleep = (ms: number): Promise<void> =>\r\n  new Promise(resolve => setTimeout(resolve, ms));\r\n\r\n/**\r\n * fetch with timeout, idempotent retry, and exponential backoff.\r\n *\r\n * - Retries on network errors and 5xx (transient) responses.\r\n * - Auto-retries GET/HEAD only. This is DELIBERATELY narrower than SC's\r\n *   canonical `IDEMPOTENT` set (GET/PUT/DELETE/HEAD/OPTIONS): although PUT and\r\n *   DELETE are HTTP-spec idempotent, \"spec-idempotent\" is not the same as\r\n *   \"safe to blind-retry a write on a public checkout flow\". A transient 5xx\r\n *   can arrive *after* the server already applied the write (e.g. a 504 on a\r\n *   lost response), so auto-retrying a PUT/DELETE risks a double-apply on the\r\n *   buyer-facing cart/order endpoints. VC leaves write-retries to caller-driven\r\n *   logic instead. See the blade b5-request-lifecycle-residual packet.\r\n * - The supplied AbortSignal (if any) is respected and short-circuits retries.\r\n */\r\nasync function fetchWithRetry(\r\n  url: string,\r\n  init: RequestInit,\r\n  cfg: Required<ApiConfig>\r\n): Promise<Response> {\r\n  const method = (init.method || 'GET').toUpperCase();\r\n  // @api-client-escape: retry set is intentionally GET/HEAD only, narrower than\r\n  // SC's IDEMPOTENT canon — see the fetchWithRetry docstring for why (checkout\r\n  // write-safety), a documented divergence, not undeclared drift.\r\n  const isIdempotent = method === 'GET' || method === 'HEAD';\r\n  const maxAttempts = isIdempotent ? cfg.retries + 1 : 1;\r\n\r\n  let lastError: unknown;\r\n  for (let attempt = 1; attempt <= maxAttempts; attempt++) {\r\n    const controller = new AbortController();\r\n    const timeoutId = setTimeout(() => controller.abort(), cfg.timeout);\r\n\r\n    // Chain the caller's signal if provided.\r\n    const callerSignal = init.signal;\r\n    let onCallerAbort: (() => void) | undefined;\r\n    if (callerSignal) {\r\n      if (callerSignal.aborted) controller.abort();\r\n      else {\r\n        onCallerAbort = () => controller.abort();\r\n        callerSignal.addEventListener('abort', onCallerAbort, { once: true });\r\n      }\r\n    }\r\n\r\n    try {\r\n      // @api-client-escape: internal canonical client implementation — not drift\r\n      const response = await fetch(url, { ...init, signal: controller.signal });\r\n\r\n      // Retry transient server errors.\r\n      if (response.status >= 500 && attempt < maxAttempts) {\r\n        lastError = new Error(`HTTP ${response.status}`);\r\n      } else {\r\n        return response;\r\n      }\r\n    } catch (err) {\r\n      lastError = err;\r\n      // Caller aborted — give up.\r\n      if (callerSignal?.aborted) throw err;\r\n    } finally {\r\n      clearTimeout(timeoutId);\r\n      if (callerSignal && onCallerAbort) {\r\n        callerSignal.removeEventListener('abort', onCallerAbort);\r\n      }\r\n    }\r\n\r\n    if (attempt < maxAttempts) {\r\n      await sleep(cfg.retryDelay * Math.pow(2, attempt - 1));\r\n    }\r\n  }\r\n\r\n  throw lastError;\r\n}\r\n\r\nexport async function apiGet<T>(\r\n  endpoint: string,\r\n  options?: RequestInit\r\n): Promise<ApiResponse<T>> {\r\n  return apiRequest<T>('GET', endpoint, undefined, options);\r\n}\r\n\r\nexport async function apiPost<T>(\r\n  endpoint: string,\r\n  body?: unknown,\r\n  options?: RequestInit\r\n): Promise<ApiResponse<T>> {\r\n  return apiRequest<T>('POST', endpoint, body, options);\r\n}\r\n\r\nexport async function apiPut<T>(\r\n  endpoint: string,\r\n  body?: unknown,\r\n  options?: RequestInit\r\n): Promise<ApiResponse<T>> {\r\n  return apiRequest<T>('PUT', endpoint, body, options);\r\n}\r\n\r\nexport async function apiDelete<T>(\r\n  endpoint: string,\r\n  options?: RequestInit\r\n): Promise<ApiResponse<T>> {\r\n  return apiRequest<T>('DELETE', endpoint, undefined, options);\r\n}\r\n\r\nasync function apiRequest<T>(\r\n  method: string,\r\n  endpoint: string,\r\n  body?: unknown,\r\n  options?: RequestInit\r\n): Promise<ApiResponse<T>> {\r\n  const url = endpoint.startsWith('http')\r\n    ? endpoint\r\n    : `${getPublicBaseUrl()}${endpoint}`;\r\n\r\n  try {\r\n    const response = await fetchWithRetry(\r\n      url,\r\n      {\r\n        method,\r\n        headers: {\r\n          'Content-Type': 'application/json',\r\n          ...options?.headers,\r\n        },\r\n        body: body !== undefined ? JSON.stringify(body) : undefined,\r\n        credentials: 'include',\r\n        ...options,\r\n      },\r\n      globalConfig\r\n    );\r\n\r\n    if (!response.ok) {\r\n      const errorData = await response.json().catch(() => ({}));\r\n      const message =\r\n        errorData.error || errorData.message || `HTTP ${response.status}`;\r\n      globalConfig.onError(new Error(message));\r\n      return { success: false, error: message, statusCode: response.status };\r\n    }\r\n\r\n    const data = await response.json();\r\n    return { success: true, data: data as T, statusCode: response.status };\r\n  } catch (error) {\r\n    const errorMessage =\r\n      error instanceof Error\r\n        ? error.name === 'AbortError'\r\n          ? 'Request timed out'\r\n          : error.message\r\n        : 'Unknown error';\r\n\r\n    globalConfig.onError(\r\n      error instanceof Error ? error : new Error(errorMessage)\r\n    );\r\n    return { success: false, error: errorMessage };\r\n  }\r\n}\r\n\r\n/**\r\n * Simple fetch wrapper that returns parsed JSON or null on error.\r\n * Inherits the global timeout + retry policy for GETs.\r\n */\r\nexport async function simpleFetch<T>(\r\n  url: string,\r\n  options?: RequestInit\r\n): Promise<T | null> {\r\n  try {\r\n    const response = await fetchWithRetry(\r\n      url,\r\n      { credentials: 'include', ...options },\r\n      globalConfig\r\n    );\r\n\r\n    if (!response.ok) {\r\n      const errorData = await response.json().catch(() => ({}));\r\n      logger.error(`API request failed: ${response.status}`, errorData);\r\n      return null;\r\n    }\r\n\r\n    return response.json();\r\n  } catch (error) {\r\n    logger.error('API request error:', error);\r\n    return null;\r\n  }\r\n}\r\n","/**\r\n * Cart Transformer\r\n *\r\n * Normalizes the orders-service `GET /v2Public/cart/{uuid}` response into the\r\n * domain CartView / CartReservationView shapes.\r\n *\r\n * Wire->domain boundary: one parseCart(raw: unknown) entry point plus a\r\n * parseCartReservation(raw: unknown) sub-parse. The wire field shapes\r\n * (RawCart / RawReservation) are named ONLY inside this module and never escape.\r\n *\r\n * IMPLEMENTATION NOTE -- PascalCase reconciliation:\r\n * The orders-service Cart model uses Go-style PascalCase JSON tags\r\n * (UUID, EventID, Status, ExpiresAt, Reservations) on some routes and\r\n * camelCase on the newer v2 routes. We tolerate both via `??` (NOT `||`):\r\n * `??` only falls through on null/undefined, so a legitimate `0` eventID or\r\n * empty-string status is preserved -- byte-identical to the inline mapper this\r\n * was lifted from (src/lib/api/orders.ts getCartByUUID). Numeric fields are\r\n * coerced with Number(), string fields with String(), exactly as before.\r\n *\r\n * Staleness/event-mismatch policy (status must be reserved|active, eventID must\r\n * match the expected event) is a CALLER decision and intentionally stays in\r\n * getCartByUUID -- it is not part of normalization.\r\n */\r\n\r\nimport type { CartView, CartReservationView } from '../orders.js';\r\n\r\n// -- Wire shapes (internal -- never exported) --------------------------------\r\ninterface RawCart {\r\n  UUID?: string;\r\n  uuid?: string;\r\n  EventID?: number;\r\n  eventID?: number;\r\n  Status?: string;\r\n  status?: string;\r\n  ExpiresAt?: string;\r\n  expiresAt?: string;\r\n  Reservations?: unknown[];\r\n  reservations?: unknown[];\r\n}\r\n\r\n/**\r\n * Parse a single raw reservation row into the normalized CartReservationView.\r\n *\r\n * Wire shape is contained here. Uses `??` (PascalCase first, camelCase\r\n * fallback) so a real `0` quantity / price is preserved. Numeric coercion via\r\n * Number(), status via String() -- byte-identical to the inline `.map` in\r\n * getCartByUUID.\r\n */\r\nexport function parseCartReservation(raw: unknown): CartReservationView {\r\n  const rec = (raw ?? {}) as Record<string, unknown>;\r\n  return {\r\n    ticketID: Number(rec.TicketID ?? rec.ticketID ?? 0),\r\n    quantity: Number(rec.Quantity ?? rec.quantity ?? 0),\r\n    priceAtReservation: Number(\r\n      rec.PriceAtReservation ?? rec.priceAtReservation ?? 0\r\n    ),\r\n    status: String(rec.Status ?? rec.status ?? ''),\r\n  };\r\n}\r\n\r\n/**\r\n * Parse a raw cart payload into the normalized CartView domain shape.\r\n *\r\n * Wire shape (RawCart) is contained inside this module and never escapes it.\r\n * Reconciles PascalCase and camelCase JSON tags via `??`. Reservations are\r\n * normalized through parseCartReservation. Byte-identical to the inline\r\n * normalization previously embedded in getCartByUUID.\r\n */\r\nexport function parseCart(raw: unknown): CartView {\r\n  const data = (raw ?? {}) as RawCart;\r\n  const uuid: string = data.UUID ?? data.uuid ?? '';\r\n  const eventID = Number(data.EventID ?? data.eventID ?? 0);\r\n  const status: string = data.Status ?? data.status ?? '';\r\n  const expiresAt: string = data.ExpiresAt ?? data.expiresAt ?? '';\r\n  const rawRes: unknown[] = data.Reservations ?? data.reservations ?? [];\r\n  const reservations: CartReservationView[] = rawRes.map(parseCartReservation);\r\n  return { uuid, eventID, status, expiresAt, reservations };\r\n}\r\n","/**\r\n * UTM normalization — the single write-time normalizer shared by every beacon\r\n * moment and the attribution store.\r\n *\r\n * Kills the historical fragmentation (`Facebook` vs `facebook`, `Direct` vs\r\n * `''` vs `direct`): every utm value is lowercased, trimmed, and has internal\r\n * whitespace collapsed to underscores at WRITE time, and an empty source\r\n * resolves to the ONE canonical default `direct`. Downstream marketing reports\r\n * then bucket page-views and order rows into the same key.\r\n *\r\n * No svelte-components canonical covers this (checked the MCP: `slugify` uses\r\n * hyphen/URL semantics and no `direct` default; `normalize*` transforms are\r\n * status/timezone/charge specific). This is the widget-local tracking\r\n * canonical for the utm write contract (MISSION-TRUTH C1).\r\n */\r\n\r\n/** The five utm dimensions, in canonical order. */\r\nexport const UTM_KEYS = [\r\n  'utmSource',\r\n  'utmMedium',\r\n  'utmCampaign',\r\n  'utmContent',\r\n  'utmTerm',\r\n] as const;\r\n\r\nexport type UtmKey = (typeof UTM_KEYS)[number];\r\n\r\n/** Query-param name for each utm dimension. */\r\nconst UTM_PARAM: Record<UtmKey, string> = {\r\n  utmSource: 'utm_source',\r\n  utmMedium: 'utm_medium',\r\n  utmCampaign: 'utm_campaign',\r\n  utmContent: 'utm_content',\r\n  utmTerm: 'utm_term',\r\n};\r\n\r\nexport type UtmSet = Record<UtmKey, string>;\r\n\r\n/** The one default source — used everywhere an empty source would otherwise appear. */\r\nexport const DIRECT_SOURCE = 'direct';\r\n\r\n/**\r\n * Normalize a single utm value: trim, lowercase, collapse internal whitespace\r\n * to underscores. Empty stays empty here (source-specific defaulting to\r\n * `direct` happens in {@link normalizeUtmSet} so medium/campaign/etc. can stay\r\n * genuinely empty).\r\n */\r\nexport function normalizeUtmValue(raw: string | null | undefined): string {\r\n  if (!raw) return '';\r\n  return raw.trim().toLowerCase().replace(/\\s+/g, '_');\r\n}\r\n\r\n/**\r\n * Normalize a full utm set. Every value is run through\r\n * {@link normalizeUtmValue}; an empty `utmSource` resolves to `direct` (the\r\n * single canonical default). The other four dimensions stay empty when absent.\r\n */\r\nexport function normalizeUtmSet(\r\n  raw: Partial<Record<UtmKey, string | null | undefined>>\r\n): UtmSet {\r\n  const out = {} as UtmSet;\r\n  for (const key of UTM_KEYS) {\r\n    out[key] = normalizeUtmValue(raw[key]);\r\n  }\r\n  if (!out.utmSource) out.utmSource = DIRECT_SOURCE;\r\n  return out;\r\n}\r\n\r\n/**\r\n * Read + normalize the utm set from URL search params (or any\r\n * `URLSearchParams`). Used at every host-context read so a beacon fired from a\r\n * `?utm_source=Instagram` visit records `instagram`.\r\n */\r\nexport function readUtmSet(params: URLSearchParams): UtmSet {\r\n  const raw = {} as Record<UtmKey, string | null>;\r\n  for (const key of UTM_KEYS) {\r\n    raw[key] = params.get(UTM_PARAM[key]);\r\n  }\r\n  return normalizeUtmSet(raw);\r\n}\r\n\r\n/**\r\n * True when a normalized utm set represents a real marketing touch (an\r\n * explicit non-direct source). Drives the attribution store's\r\n * last-non-direct-touch overwrite rule.\r\n */\r\nexport function isMarketingUtm(utm: UtmSet): boolean {\r\n  return !!utm.utmSource && utm.utmSource !== DIRECT_SOURCE;\r\n}\r\n","/**\r\n * Tracking host-context (MISSION-TRUTH C1).\r\n *\r\n * Every beacon and the attribution store read the visitor's marketing context\r\n * — utm params + referrer + page url — from HERE, never from `window.location`\r\n * at call time. Two reasons:\r\n *   1. The standalone checkout route drops query params, so a live read at\r\n *      purchase time would see nothing (that's why attribution is stored, C2).\r\n *   2. Iframe embed mode: the frame's own `window.location` is Micdrop's, not\r\n *      the host page's. `setForwardedHostContext` lets the embed layer push the\r\n *      host page's real utm + referrer into the frame (via its iframe src query\r\n *      string or a host→widget postMessage), and this module prefers it.\r\n *\r\n * `widgetId` / `venueId` are set once at widget init and threaded onto every\r\n * beacon so per-widget traffic is attributable (decision 14).\r\n *\r\n * No svelte-components canonical covers host-context resolution (checked the\r\n * MCP); this is widget-local tracking plumbing.\r\n */\r\nimport { readUtmSet, type UtmSet } from './normalize.js';\r\n\r\nexport type DeviceType = 'mobile' | 'desktop' | 'tablet';\r\n\r\nexport interface HostContext {\r\n  utm: UtmSet;\r\n  referrer: string;\r\n  pageUrl: string;\r\n}\r\n\r\ninterface ForwardedContext {\r\n  search: string | null;\r\n  referrer: string | null;\r\n  pageUrl: string | null;\r\n}\r\n\r\nlet widgetId: string | null = null;\r\nlet venueId: string | number | null = null;\r\nconst forwarded: ForwardedContext = {\r\n  search: null,\r\n  referrer: null,\r\n  pageUrl: null,\r\n};\r\n\r\n/** Set once at widget init from the embed attributes / init options. */\r\nexport function setTrackingContext(ctx: {\r\n  widgetId?: string | null;\r\n  venueId?: string | number | null;\r\n}): void {\r\n  if (ctx.widgetId !== undefined) widgetId = ctx.widgetId || null;\r\n  if (ctx.venueId !== undefined) venueId = ctx.venueId ?? null;\r\n}\r\n\r\n/**\r\n * Push the host page's real marketing context into the frame (iframe embed\r\n * mode). Accepts a raw query string / URLSearchParams for utm and the host\r\n * page's referrer + url. Any field left undefined keeps the current value, so a\r\n * partial forward (utm now, referrer later) composes.\r\n */\r\nexport function setForwardedHostContext(ctx: {\r\n  search?: string | URLSearchParams | null;\r\n  referrer?: string | null;\r\n  pageUrl?: string | null;\r\n}): void {\r\n  if (ctx.search !== undefined) {\r\n    forwarded.search =\r\n      ctx.search === null\r\n        ? null\r\n        : ctx.search instanceof URLSearchParams\r\n          ? ctx.search.toString()\r\n          : ctx.search;\r\n  }\r\n  if (ctx.referrer !== undefined) forwarded.referrer = ctx.referrer;\r\n  if (ctx.pageUrl !== undefined) forwarded.pageUrl = ctx.pageUrl;\r\n}\r\n\r\nexport function getWidgetId(): string | null {\r\n  return widgetId;\r\n}\r\n\r\nexport function getVenueId(): string | number | null {\r\n  return venueId;\r\n}\r\n\r\n/**\r\n * True when this document is the top-level window (light-DOM script embed).\r\n * False inside an iframe, where `window.location` is Micdrop's frame — NOT the\r\n * host page — so it must not be trusted as the host context. Cross-origin\r\n * access to `window.top` can throw; that itself means we're framed.\r\n */\r\nfunction isTopLevel(): boolean {\r\n  if (typeof window === 'undefined') return true;\r\n  try {\r\n    return window.self === window.top;\r\n  } catch {\r\n    return false;\r\n  }\r\n}\r\n\r\n/**\r\n * Resolve the current host context. Prefers forwarded values (iframe mode);\r\n * otherwise reads the live document — which is the host page itself in the\r\n * light-DOM script embed. `pageUrl` falls back to the live URL ONLY at the top\r\n * level: inside an unforwarded iframe the frame's own URL is Micdrop's, not the\r\n * host page's, so it's left empty rather than misrepresenting the host (which\r\n * would also mis-classify the referrer, see attribution-store).\r\n */\r\nexport function resolveHostContext(): HostContext {\r\n  const hasWindow = typeof window !== 'undefined';\r\n  const topLevel = isTopLevel();\r\n  const search = forwarded.search ?? (hasWindow ? window.location.search : '');\r\n  const referrer =\r\n    forwarded.referrer ??\r\n    (typeof document !== 'undefined' ? document.referrer : '') ??\r\n    '';\r\n  const pageUrl =\r\n    forwarded.pageUrl ?? (hasWindow && topLevel ? window.location.href : '');\r\n\r\n  return {\r\n    utm: readUtmSet(new URLSearchParams(search)),\r\n    referrer: referrer || '',\r\n    pageUrl: pageUrl || '',\r\n  };\r\n}\r\n\r\n/** Coarse device class from the user-agent — a marketing dimension on every beacon. */\r\nexport function getDeviceType(): DeviceType {\r\n  if (typeof navigator === 'undefined') return 'desktop';\r\n  const ua = navigator.userAgent.toLowerCase();\r\n  if (/ipad|tablet|playbook|silk/.test(ua)) return 'tablet';\r\n  if (/mobile|iphone|ipod|android.*mobile|windows phone|blackberry/.test(ua))\r\n    return 'mobile';\r\n  if (/android/.test(ua)) return 'tablet';\r\n  return 'desktop';\r\n}\r\n\r\n/** Test-only reset of the module singleton. */\r\nexport function _resetContextForTests(): void {\r\n  widgetId = null;\r\n  venueId = null;\r\n  forwarded.search = null;\r\n  forwarded.referrer = null;\r\n  forwarded.pageUrl = null;\r\n}\r\n","/**\r\n * Consent gating (MISSION-TRUTH C5).\r\n *\r\n * When a host consent signal exists — the IAB TCF API (`window.__tcfapi`) or an\r\n * explicit embed flag — the persistent `visitorId` (and, later, pixels) wait\r\n * for consent to be granted. Until then beacons still fire, but fall back to\r\n * `sessionId`-only (no cross-visit identity). When NO consent signal exists at\r\n * all, the widget is free to use the visitorId immediately (the default embed\r\n * has no consent layer).\r\n *\r\n * This module holds the resolved consent state. It is deliberately synchronous\r\n * for the fire-and-forget beacon path: TCF is best-effort — presence flips\r\n * \"required\", and an async TCF listener flips \"granted\" when the user acts.\r\n */\r\n\r\ninterface TcfData {\r\n  gdprApplies?: boolean;\r\n  eventStatus?: string;\r\n  // `1` = vendor/purpose consent present for the queried scope.\r\n  purpose?: { consents?: Record<string, boolean> };\r\n}\r\ntype TcfApi = (\r\n  _command: string,\r\n  _version: number,\r\n  _callback: (_data: TcfData, _success: boolean) => void,\r\n  ..._args: unknown[]\r\n) => void;\r\n\r\nlet explicitRequire: boolean | null = null;\r\nlet granted = false;\r\nlet tcfWired = false;\r\n\r\n/** Host explicitly declares whether embed-level consent is required. */\r\nexport function setConsentRequired(required: boolean): void {\r\n  explicitRequire = required;\r\n}\r\n\r\n/** Host (or its consent listener) declares consent granted/withdrawn. */\r\nexport function setConsentGranted(value: boolean): void {\r\n  granted = value;\r\n}\r\n\r\nfunction hasTcf(): boolean {\r\n  return (\r\n    typeof window !== 'undefined' &&\r\n    typeof (window as unknown as { __tcfapi?: TcfApi }).__tcfapi === 'function'\r\n  );\r\n}\r\n\r\n/**\r\n * A consent signal exists when the host set the explicit flag OR the TCF API is\r\n * present on the page. Explicit flag wins (a host may run TCF but opt the\r\n * widget out, or force consent without TCF).\r\n */\r\nexport function isConsentRequired(): boolean {\r\n  if (explicitRequire !== null) return explicitRequire;\r\n  return hasTcf();\r\n}\r\n\r\n/**\r\n * True when the persistent visitorId may be used: either no consent signal\r\n * exists, or one exists and consent has been granted.\r\n */\r\nexport function isConsentGranted(): boolean {\r\n  if (!isConsentRequired()) return true;\r\n  return granted;\r\n}\r\n\r\n/**\r\n * Best-effort TCF wiring. Registers a listener that flips `granted` once the CMP\r\n * reports a loaded/complete state with GDPR consent. Safe to call repeatedly\r\n * (wires once). No-op when TCF is absent.\r\n */\r\nexport function wireTcfConsent(): void {\r\n  if (tcfWired || !hasTcf()) return;\r\n  tcfWired = true;\r\n  const tcf = (window as unknown as { __tcfapi: TcfApi }).__tcfapi;\r\n  try {\r\n    tcf('addEventListener', 2, (data, success) => {\r\n      if (!success || !data) return;\r\n      // If GDPR doesn't apply the CMP is effectively pass-through → treat as\r\n      // granted. Otherwise wait for a settled CMP state (tcloaded /\r\n      // useractioncomplete) before honoring consent.\r\n      if (data.gdprApplies === false) {\r\n        granted = true;\r\n        return;\r\n      }\r\n      if (\r\n        data.eventStatus === 'tcloaded' ||\r\n        data.eventStatus === 'useractioncomplete'\r\n      ) {\r\n        // Presence of any purpose consent is enough for first-party analytics\r\n        // identity; the backend treats visitorId as opaque.\r\n        const consents = data.purpose?.consents;\r\n        granted = !!consents && Object.values(consents).some(Boolean);\r\n      }\r\n    });\r\n  } catch {\r\n    // A malformed CMP shim must never break tracking — stay ungranted.\r\n  }\r\n}\r\n\r\n/** Test-only reset of the module singleton. */\r\nexport function _resetConsentForTests(): void {\r\n  explicitRequire = null;\r\n  granted = false;\r\n  tcfWired = false;\r\n}\r\n","/**\r\n * Visitor + session identity (MISSION-TRUTH C1 + C5).\r\n *\r\n * - `sessionId`: per-tab, sessionStorage, created once and reused — the same\r\n *   `micdrop_session_id` key the legacy pageview beacon already used, so\r\n *   existing sessions carry over unchanged.\r\n * - `visitorId`: first-party persistent UUID v4 in localStorage\r\n *   (`micdrop_visitor_id`), created on first read and REUSED thereafter. Never\r\n *   regenerated on read (a literal refresh must not rotate identity). Replaced\r\n *   only when missing or not a valid UUID. Consent-gated: withheld while a host\r\n *   consent signal exists ungranted (beacons then fall back to sessionId-only).\r\n *\r\n * Storage goes through the svelte-components canonical helpers, which are\r\n * SSR-safe and no-op when storage is blocked (sandboxed iframe) — a blocked\r\n * store just means no persistent identity, never a thrown error.\r\n */\r\nimport {\r\n  getLocalStorage,\r\n  setLocalStorage,\r\n  getSessionStorage,\r\n  setSessionStorage,\r\n} from '@getmicdrop/svelte-components';\r\nimport { isConsentGranted } from './consent.js';\r\n\r\nconst SESSION_ID_KEY = 'micdrop_session_id';\r\nconst VISITOR_ID_KEY = 'micdrop_visitor_id';\r\n\r\n// RFC 4122 v4 shape — the validity gate for a stored id. A stored value that\r\n// isn't a well-formed v4 UUID is treated as corrupt and replaced.\r\nconst UUID_V4 =\r\n  /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;\r\n\r\nfunction isUuidV4(value: unknown): value is string {\r\n  return typeof value === 'string' && UUID_V4.test(value);\r\n}\r\n\r\n/**\r\n * Generate a UUID v4. Prefers the platform `crypto.randomUUID` (the same source\r\n * svelte-components uses); falls back to a `getRandomValues`-seeded v4 for the\r\n * rare embed host without it, and finally to a Math.random v4 so a beacon on a\r\n * legacy host still gets a well-formed id.\r\n */\r\nfunction generateUuidV4(): string {\r\n  const cryptoObj: Crypto | undefined =\r\n    typeof crypto !== 'undefined' ? crypto : undefined;\r\n  if (cryptoObj?.randomUUID) return cryptoObj.randomUUID();\r\n\r\n  const bytes = new Uint8Array(16);\r\n  if (cryptoObj?.getRandomValues) {\r\n    cryptoObj.getRandomValues(bytes);\r\n  } else {\r\n    for (let i = 0; i < 16; i++) bytes[i] = Math.floor(Math.random() * 256);\r\n  }\r\n  bytes[6] = (bytes[6] & 0x0f) | 0x40; // version 4\r\n  bytes[8] = (bytes[8] & 0x3f) | 0x80; // variant 10xx\r\n  const hex: string[] = [];\r\n  for (let i = 0; i < 16; i++) hex.push(bytes[i].toString(16).padStart(2, '0'));\r\n  return (\r\n    `${hex[0]}${hex[1]}${hex[2]}${hex[3]}-${hex[4]}${hex[5]}-${hex[6]}${hex[7]}-` +\r\n    `${hex[8]}${hex[9]}-${hex[10]}${hex[11]}${hex[12]}${hex[13]}${hex[14]}${hex[15]}`\r\n  );\r\n}\r\n\r\n/**\r\n * Per-tab session id. Created once and reused for the tab's lifetime. Reuses\r\n * the historical `micdrop_session_id` key so in-flight sessions are unbroken.\r\n */\r\nexport function getSessionId(): string {\r\n  let id = getSessionStorage<string>(SESSION_ID_KEY);\r\n  if (!id) {\r\n    id = generateUuidV4();\r\n    setSessionStorage(SESSION_ID_KEY, id);\r\n  }\r\n  return id;\r\n}\r\n\r\n/**\r\n * First-party persistent visitor id, or `null` when consent is required and not\r\n * yet granted (beacons then carry sessionId only). Created on first *allowed*\r\n * read and reused thereafter; a corrupt stored value is replaced.\r\n */\r\nexport function getVisitorId(): string | null {\r\n  if (!isConsentGranted()) return null;\r\n\r\n  const stored = getLocalStorage<string>(VISITOR_ID_KEY);\r\n  if (isUuidV4(stored)) return stored;\r\n\r\n  const id = generateUuidV4();\r\n  setLocalStorage(VISITOR_ID_KEY, id);\r\n  return id;\r\n}\r\n\r\n/** Exposed for tests + the storage keys other tracking modules assert against. */\r\nexport const _identityKeys = { SESSION_ID_KEY, VISITOR_ID_KEY } as const;\r\n","/**\r\n * First-party attribution store (MISSION-TRUTH C2).\r\n *\r\n * Every order-completing call stamps an `attribution` object read from HERE —\r\n * never from `window.location`, because the standalone checkout route drops the\r\n * query params. The store is updated on every beacon and follows the\r\n * LAST NON-DIRECT TOUCH rule:\r\n *\r\n *   - A beacon carrying an explicit non-direct utm (or a fresh external\r\n *     referrer) OVERWRITES the store.\r\n *   - A direct / internal touch NEVER overwrites a prior marketing touch; it\r\n *     only seeds the store when nothing has been recorded yet.\r\n *   - So `direct` appears in attribution only when the session never had a\r\n *     non-direct touch.\r\n *\r\n * Persisted in sessionStorage (per-tab, matches sessionId scope) via the SC\r\n * storage canonical — SSR-safe, no-op when storage is blocked (a blocked store\r\n * just means the live host-context fallback is used at read time).\r\n */\r\nimport {\r\n  getSessionStorage,\r\n  setSessionStorage,\r\n} from '@getmicdrop/svelte-components';\r\nimport { isMarketingUtm, type UtmSet } from './normalize.js';\r\nimport { resolveHostContext, type HostContext } from './context.js';\r\nimport { getSessionId, getVisitorId } from './identity.js';\r\n\r\nconst ATTRIBUTION_KEY = 'micdrop_attribution';\r\n\r\ninterface StoredTouch {\r\n  utm: UtmSet;\r\n  referrer: string;\r\n  /** true = a marketing touch that direct touches must not overwrite. */\r\n  marketing: boolean;\r\n}\r\n\r\n/** The additive object stamped onto every order-completing call. */\r\nexport interface AttributionPayload {\r\n  sessionId: string;\r\n  visitorId: string | null;\r\n  utmSource: string;\r\n  utmMedium: string;\r\n  utmCampaign: string;\r\n  utmContent: string;\r\n  utmTerm: string;\r\n  referrer: string;\r\n}\r\n\r\n/**\r\n * A referrer counts as \"external\" when it has a host that differs from the host\r\n * page's own host. In the light-DOM embed the page is the host page; in iframe\r\n * mode `pageUrl` is the forwarded host page. Same-host (internal) navigations\r\n * never qualify as a marketing touch.\r\n */\r\nfunction isExternalReferrer(referrer: string, pageUrl: string): boolean {\r\n  if (!referrer || !pageUrl) return false;\r\n  // `pageUrl` is the host page ONLY when known (top-level document, or the\r\n  // forwarded host url in iframe mode) — resolveHostContext leaves it empty for\r\n  // an unforwarded iframe, where we can't tell internal from external and so\r\n  // must not conjure a marketing touch (the early return above). Never fall\r\n  // back to window.location here: in a frame that's Micdrop's host, not the\r\n  // venue's.\r\n  try {\r\n    const refHost = new URL(referrer).host;\r\n    const ownHost = new URL(pageUrl).host;\r\n    return !!refHost && !!ownHost && refHost !== ownHost;\r\n  } catch {\r\n    return false;\r\n  }\r\n}\r\n\r\n/** A touch is \"marketing\" when it has a non-direct utm OR a fresh external referrer. */\r\nfunction isMarketingTouch(ctx: HostContext): boolean {\r\n  return (\r\n    isMarketingUtm(ctx.utm) || isExternalReferrer(ctx.referrer, ctx.pageUrl)\r\n  );\r\n}\r\n\r\nfunction readStore(): StoredTouch | null {\r\n  const raw = getSessionStorage<StoredTouch>(ATTRIBUTION_KEY);\r\n  if (!raw || !raw.utm || typeof raw.utm.utmSource !== 'string') return null;\r\n  return raw;\r\n}\r\n\r\n/**\r\n * Record the current host context as a touch, applying the last-non-direct-touch\r\n * rule. Called on every beacon. Idempotent for repeated direct touches.\r\n */\r\nexport function recordTouch(ctx: HostContext = resolveHostContext()): void {\r\n  const marketing = isMarketingTouch(ctx);\r\n  const existing = readStore();\r\n\r\n  // Direct/internal touch never overwrites a recorded touch — it only seeds an\r\n  // empty store.\r\n  if (!marketing && existing) return;\r\n\r\n  setSessionStorage(ATTRIBUTION_KEY, {\r\n    utm: ctx.utm,\r\n    referrer: ctx.referrer,\r\n    marketing,\r\n  } satisfies StoredTouch);\r\n}\r\n\r\n/**\r\n * Build the attribution object for an order-completing call. Reads the stored\r\n * touch (falling back to the live host context if nothing was recorded — e.g.\r\n * storage blocked) and attaches the current session + visitor identity.\r\n * `visitorId` is null while consent is withheld (C5).\r\n */\r\nexport function getAttribution(): AttributionPayload {\r\n  const stored = readStore();\r\n  const touch: Pick<StoredTouch, 'utm' | 'referrer'> =\r\n    stored ?? resolveHostContext();\r\n\r\n  return {\r\n    sessionId: getSessionId(),\r\n    visitorId: getVisitorId(),\r\n    utmSource: touch.utm.utmSource,\r\n    utmMedium: touch.utm.utmMedium,\r\n    utmCampaign: touch.utm.utmCampaign,\r\n    utmContent: touch.utm.utmContent,\r\n    utmTerm: touch.utm.utmTerm,\r\n    referrer: touch.referrer || '',\r\n  };\r\n}\r\n\r\n/** Test-only key accessor. */\r\nexport const _attributionKey = ATTRIBUTION_KEY;\r\n","// @fetch-escape: fire-and-forget funnel beacons — raw keepalive POST, response\r\n// ignored. apiPost would parse JSON on the empty 2xx body and log a false error,\r\n// and a beacon must survive the visitor navigating away (keepalive).\r\n/**\r\n * Funnel beacons (MISSION-TRUTH C1).\r\n *\r\n * One sender fires all six funnel moments — widget_load, list_view,\r\n * event_view, add_to_cart, checkout_started, purchase — to the extended\r\n * pageview endpoint. The payload is ADDITIVE: the old event_view shape keeps\r\n * working when the backend (MIC-1255) hasn't shipped the new fields yet.\r\n *\r\n * Every beacon also refreshes the first-party attribution store (last\r\n * non-direct touch, C2) so an order completing later can stamp the right\r\n * source even after the checkout route drops the query params.\r\n *\r\n * Fire-and-forget: a tracking failure must NEVER affect the embedded widget, so\r\n * errors are swallowed and the raw `keepalive` fetch is intentional (a beacon\r\n * must complete even if the visitor navigates away immediately).\r\n */\r\nimport {\r\n  createLogger,\r\n  getSessionStorage,\r\n  setSessionStorage,\r\n} from '@getmicdrop/svelte-components';\r\nimport { getPublicBaseUrl } from '../api/client.js';\r\nimport {\r\n  resolveHostContext,\r\n  getWidgetId,\r\n  getVenueId,\r\n  getDeviceType,\r\n} from './context.js';\r\nimport { getSessionId, getVisitorId } from './identity.js';\r\nimport { recordTouch } from './attribution-store.js';\r\n\r\nconst logger = createLogger('tracking');\r\n\r\nexport type BeaconEventType =\r\n  | 'widget_load'\r\n  | 'list_view'\r\n  | 'event_view'\r\n  | 'add_to_cart'\r\n  | 'checkout_started'\r\n  | 'purchase';\r\n\r\n/** Moments that are conversions — never deduped (each is a distinct funnel step). */\r\nconst NEVER_DEDUP: ReadonlySet<BeaconEventType> = new Set([\r\n  'add_to_cart',\r\n  'checkout_started',\r\n  'purchase',\r\n]);\r\n\r\nconst DEDUP_KEY = 'micdrop_tracked_beacons';\r\n// Cap the per-session dedup ledger so a long browsing session (many events /\r\n// list views, each a distinct key) can't grow the sessionStorage array without\r\n// bound. Oldest keys age out FIFO — re-firing a long-ago-seen view is a\r\n// harmless duplicate the backend already tolerates.\r\nconst DEDUP_LEDGER_MAX = 300;\r\n\r\ninterface BeaconOptions {\r\n  /** Null for widget_load / list_view (no specific entity yet). */\r\n  eventId?: string | number | null;\r\n  /**\r\n   * Per-instance widget id. Threaded as a prop from the mounting widget so two\r\n   * <micdrop-calendar> embeds on one page don't collapse into one view (the\r\n   * dedup key keys on it). Falls back to the init-time global context when the\r\n   * caller doesn't pass one (auto-mount single-widget / standalone entries).\r\n   */\r\n  widgetId?: string | null;\r\n  /** Per-instance venue id; same fallback semantics as widgetId. */\r\n  venueId?: string | number | null;\r\n}\r\n\r\n/**\r\n * Dedup key per MISSION-TRUTH C1:\r\n *   eventType:eventId:widgetId:normUtmSource:normUtmCampaign:sessionId\r\n * with pageUrl standing in for a null widgetId (so two widgets on one page\r\n * aren't collapsed into a single view).\r\n */\r\nfunction dedupKey(\r\n  eventType: BeaconEventType,\r\n  eventId: string | number | null,\r\n  widgetId: string | null,\r\n  utmSource: string,\r\n  utmCampaign: string,\r\n  sessionId: string,\r\n  pageUrl: string\r\n): string {\r\n  // `||` (not `??`): an empty-string widgetId must fall back to pageUrl too, so\r\n  // two id-less widgets on one page don't share a dedup bucket.\r\n  const widgetPart = widgetId || `url:${pageUrl}`;\r\n  return [\r\n    eventType,\r\n    eventId ?? '',\r\n    widgetPart,\r\n    utmSource,\r\n    utmCampaign,\r\n    sessionId,\r\n  ].join(':');\r\n}\r\n\r\n/**\r\n * Coerce an id to a finite number, or null. A non-numeric value (e.g. a venue\r\n * *slug* passed as `venueId` on a demo embed) becomes null rather than a `NaN`\r\n * that would serialize to a misleading value — the backend keys on the other\r\n * dimensions in that case.\r\n */\r\nfunction toNumericOrNull(\r\n  value: string | number | null | undefined\r\n): number | null {\r\n  if (value == null || value === '') return null;\r\n  const n = Number(value);\r\n  return Number.isFinite(n) ? n : null;\r\n}\r\n\r\nfunction alreadySent(key: string): boolean {\r\n  const sent = getSessionStorage<string[]>(DEDUP_KEY, []) ?? [];\r\n  if (sent.includes(key)) return true;\r\n  // Append, keeping only the most recent DEDUP_LEDGER_MAX keys (FIFO).\r\n  const next = [...sent, key];\r\n  setSessionStorage(\r\n    DEDUP_KEY,\r\n    next.length > DEDUP_LEDGER_MAX ? next.slice(-DEDUP_LEDGER_MAX) : next\r\n  );\r\n  return false;\r\n}\r\n\r\n/**\r\n * Fire one funnel beacon. Resolves host context once, refreshes the attribution\r\n * store, dedups (except conversions), then posts additively.\r\n */\r\nexport async function trackBeacon(\r\n  eventType: BeaconEventType,\r\n  {\r\n    eventId = null,\r\n    widgetId: widgetIdArg,\r\n    venueId: venueIdArg,\r\n  }: BeaconOptions = {}\r\n): Promise<void> {\r\n  if (typeof window === 'undefined') return;\r\n\r\n  const ctx = resolveHostContext();\r\n  // Keep the attribution store fresh on every touch — even a deduped view still\r\n  // represents the visitor being here now.\r\n  recordTouch(ctx);\r\n\r\n  const sessionId = getSessionId();\r\n  // Prefer the per-instance id passed by the mounting widget; fall back to the\r\n  // init-time global context.\r\n  const widgetId = widgetIdArg !== undefined ? widgetIdArg : getWidgetId();\r\n\r\n  if (!NEVER_DEDUP.has(eventType)) {\r\n    const key = dedupKey(\r\n      eventType,\r\n      eventId,\r\n      widgetId,\r\n      ctx.utm.utmSource,\r\n      ctx.utm.utmCampaign,\r\n      sessionId,\r\n      ctx.pageUrl\r\n    );\r\n    if (alreadySent(key)) return;\r\n  }\r\n\r\n  const venueId = venueIdArg !== undefined ? venueIdArg : getVenueId();\r\n  const body = {\r\n    eventType,\r\n    eventId: toNumericOrNull(eventId),\r\n    venueId: toNumericOrNull(venueId),\r\n    widgetId,\r\n    utmSource: ctx.utm.utmSource,\r\n    utmMedium: ctx.utm.utmMedium,\r\n    utmCampaign: ctx.utm.utmCampaign,\r\n    utmContent: ctx.utm.utmContent,\r\n    utmTerm: ctx.utm.utmTerm,\r\n    sessionId,\r\n    visitorId: getVisitorId(),\r\n    deviceType: getDeviceType(),\r\n    referrer: ctx.referrer,\r\n    pageUrl: ctx.pageUrl,\r\n  };\r\n\r\n  try {\r\n    await fetch(`${getPublicBaseUrl()}/track/pageview`, {\r\n      method: 'POST',\r\n      headers: { 'Content-Type': 'application/json' },\r\n      body: JSON.stringify(body),\r\n      keepalive: true,\r\n    });\r\n  } catch (err) {\r\n    logger.error(`Beacon '${eventType}' failed:`, err);\r\n  }\r\n}\r\n\r\n/** Widget mounted on the host page (no entity yet). */\r\nexport const trackWidgetLoad = (\r\n  widgetId?: string | null,\r\n  venueId?: string | number | null\r\n): Promise<void> => trackBeacon('widget_load', { widgetId, venueId });\r\n\r\n/** Visitor is looking at the calendar/list of events. */\r\nexport const trackListView = (\r\n  widgetId?: string | null,\r\n  venueId?: string | number | null\r\n): Promise<void> => trackBeacon('list_view', { widgetId, venueId });\r\n\r\n/** Visitor opened a specific event's detail page. */\r\nexport const trackEventView = (\r\n  eventId: string | number,\r\n  widgetId?: string | null,\r\n  venueId?: string | number | null\r\n): Promise<void> => trackBeacon('event_view', { eventId, widgetId, venueId });\r\n\r\n/** Visitor added tickets to the cart (cart created). */\r\nexport const trackAddToCart = (\r\n  eventId: string | number,\r\n  widgetId?: string | null,\r\n  venueId?: string | number | null\r\n): Promise<void> => trackBeacon('add_to_cart', { eventId, widgetId, venueId });\r\n\r\n/** Visitor opened the checkout UI. */\r\nexport const trackCheckoutStarted = (\r\n  eventId: string | number,\r\n  widgetId?: string | null,\r\n  venueId?: string | number | null\r\n): Promise<void> =>\r\n  trackBeacon('checkout_started', { eventId, widgetId, venueId });\r\n\r\n/** Order completed. */\r\nexport const trackPurchase = (\r\n  eventId: string | number,\r\n  widgetId?: string | null,\r\n  venueId?: string | number | null\r\n): Promise<void> => trackBeacon('purchase', { eventId, widgetId, venueId });\r\n\r\n/** Test-only reset of the dedup ledger key. */\r\nexport const _dedupKeyForTests = DEDUP_KEY;\r\n","import { AppError } from '@getmicdrop/svelte-components';\r\n/**\r\n * Orders API\r\n *\r\n * Functions for managing orders, payments, and reservations\r\n * in the public checkout flow.\r\n */\r\n\r\nimport { createLogger } from '@getmicdrop/svelte-components/utils/logger';\r\nconst logger = createLogger('VC');\r\nimport {\r\n  getPublicBaseUrl,\r\n  getOrdersV2Url,\r\n  simpleFetch,\r\n  apiPost,\r\n  apiGet,\r\n} from './client.js';\r\nimport { parseCart } from './transformers/cart.js';\r\nimport { getAttribution } from '../tracking/index.js';\r\nimport { normalizeUtmValue, DIRECT_SOURCE } from '../tracking/normalize.js';\r\nimport type {\r\n  PaymentIntentResponse,\r\n  CompleteReservationResponse,\r\n  CancelReservationResponse,\r\n  CreateOrderResponse,\r\n  ValidatePaymentRequest,\r\n  ValidatePaymentResponse,\r\n  ExtendSessionResponse,\r\n  SessionStatus,\r\n  Order,\r\n} from './types.js';\r\n\r\n/**\r\n * Create a payment intent for the cart\r\n *\r\n * This initiates the Stripe payment flow by creating a PaymentIntent\r\n * on the backend. The response includes the client_secret needed\r\n * for Stripe Elements.\r\n *\r\n * @param cartId - The cart/order UUID\r\n * @param quantities - Map of ticketId -> quantity\r\n * @param donationAmounts - Map of ticketId -> donation amount in dollars (for type=2 tickets)\r\n * @returns Payment intent data including client_secret, or null on error\r\n */\r\nexport async function createPaymentIntent(\r\n  cartId: string,\r\n  quantities: Record<string | number, number>,\r\n  donationAmounts?: Record<string | number, number>\r\n): Promise<PaymentIntentResponse | null> {\r\n  try {\r\n    const apiResult = await apiPost<PaymentIntentResponse>(\r\n      `${getOrdersV2Url()}/cart/${cartId}/payment-intent`,\r\n      {\r\n        productQuantities: quantities,\r\n        ...(donationAmounts && Object.keys(donationAmounts).length > 0\r\n          ? { donationAmounts }\r\n          : {}),\r\n        // Order attribution (MISSION-TRUTH C2) — additive; ignored by a backend\r\n        // that hasn't shipped MIC-1255 yet. Free orders finalize via this\r\n        // payment-intent pre-submit, so this covers the free-order path too.\r\n        attribution: getAttribution(),\r\n      }\r\n    );\r\n\r\n    if (!apiResult.success) {\r\n      logger.error('Payment intent creation failed:', apiResult.error);\r\n      throw new AppError(\r\n        apiResult.error || 'Failed to create payment intent',\r\n        'lib/api/orders/createPaymentIntent'\r\n      );\r\n    }\r\n\r\n    logger.debug('Payment intent created:', apiResult.data);\r\n    return apiResult.data!;\r\n  } catch (error) {\r\n    logger.error('createPaymentIntent error:', error);\r\n    return null;\r\n  }\r\n}\r\n\r\n/**\r\n * Fetch an existing cart by UUID (cross-device pre-fill — §1.3).\r\n *\r\n * Returns the server's authoritative view of a cart so a second device\r\n * visiting the tickets page can see the in-progress reservation it created\r\n * on another device. Returns `null` if:\r\n *   - the cart no longer exists (404)\r\n *   - the cart belongs to a different event than `expectedEventID` (when\r\n *     supplied) — we don't want to mix carts across events\r\n *   - the cart status is no longer `reserved` / `active` (e.g. expired,\r\n *     completed, abandoned) — caller should treat the local cookie as stale\r\n *   - any network/decode failure (caller falls back to localStorage only)\r\n *\r\n * Returned shape matches the orders-service `GET /v2Public/cart/{uuid}`\r\n * response: a Cart model with embedded Reservations.\r\n */\r\nexport interface CartReservationView {\r\n  ticketID: number;\r\n  quantity: number;\r\n  priceAtReservation: number;\r\n  status: string;\r\n}\r\nexport interface CartView {\r\n  uuid: string;\r\n  eventID: number;\r\n  status: string;\r\n  expiresAt: string;\r\n  reservations: CartReservationView[];\r\n}\r\n\r\nexport async function getCartByUUID(\r\n  cartUUID: string,\r\n  expectedEventID?: string | number\r\n): Promise<CartView | null> {\r\n  if (!cartUUID) return null;\r\n  try {\r\n    const apiResult = await apiGet<unknown>(\r\n      `${getOrdersV2Url()}/cart/${cartUUID}`\r\n    );\r\n    if (!apiResult.success) {\r\n      // 404 / 410 / 5xx — caller treats as stale\r\n      return null;\r\n    }\r\n    // Wire->domain normalization (PascalCase/camelCase reconciliation, numeric\r\n    // coercion, reservation sub-parse) lives in parseCart — byte-identical to\r\n    // the inline mapper this replaced. Staleness/event-mismatch policy below\r\n    // stays here because it is a caller decision, not normalization.\r\n    const { uuid, eventID, status, expiresAt, reservations } = parseCart(\r\n      apiResult.data!\r\n    );\r\n    if (status !== 'reserved' && status !== 'active') {\r\n      // stale cart row — caller clears the cookie\r\n      return null;\r\n    }\r\n    if (expectedEventID !== undefined && eventID !== Number(expectedEventID)) {\r\n      // cart belongs to a different event — don't mix\r\n      return null;\r\n    }\r\n    return { uuid, eventID, status, expiresAt, reservations };\r\n  } catch (err) {\r\n    logger.error('getCartByUUID error:', err);\r\n    return null;\r\n  }\r\n}\r\n\r\n/**\r\n * Update cart quantities after the cart already exists.\r\n *\r\n * Replaces all reservations on the cart with the new quantities.\r\n * The orders-service handler releases old reservations (returning\r\n * inventory) and creates new ones (decrementing inventory) atomically.\r\n *\r\n * Use this when the user changes ticket counts AFTER the initial\r\n * cart was created via initiateOrder. The cart UUID is preserved,\r\n * so the timer and extension state stay intact.\r\n *\r\n * @returns true on success, false on failure\r\n */\r\nexport async function updateCartQuantities(\r\n  cartId: string,\r\n  quantities: Record<string | number, number>,\r\n  donationAmounts?: Record<string | number, number>\r\n): Promise<boolean> {\r\n  try {\r\n    // Convert string keys to numbers (the orders-service handler\r\n    // unmarshals into map[uint]int).\r\n    const numericQuantities: Record<number, number> = {};\r\n    for (const [k, v] of Object.entries(quantities)) {\r\n      const n = typeof k === 'number' ? k : parseInt(k, 10);\r\n      if (!Number.isNaN(n) && v > 0) numericQuantities[n] = v;\r\n    }\r\n    const numericDonations: Record<number, number> = {};\r\n    if (donationAmounts) {\r\n      for (const [k, v] of Object.entries(donationAmounts)) {\r\n        const n = typeof k === 'number' ? k : parseInt(k, 10);\r\n        if (!Number.isNaN(n) && v > 0) numericDonations[n] = v;\r\n      }\r\n    }\r\n    // @fetch-escape: status-only endpoint — PUT /cart/:id returns an empty 2xx (no JSON body)\r\n    // on success; the canonical apiPut runs response.json() unconditionally on 2xx and would\r\n    // throw on the empty body, mis-reporting a successful cart update as a failure. Migrate once\r\n    // the client tolerates 204/empty bodies.\r\n    const response = await fetch(`${getOrdersV2Url()}/cart/${cartId}`, {\r\n      method: 'PUT',\r\n      headers: { 'Content-Type': 'application/json' },\r\n      credentials: 'include',\r\n      body: JSON.stringify({\r\n        quantities: numericQuantities,\r\n        ...(Object.keys(numericDonations).length > 0\r\n          ? { donationAmounts: numericDonations }\r\n          : {}),\r\n      }),\r\n    });\r\n    if (!response.ok) {\r\n      const errorData = await response.json().catch(() => ({}));\r\n      logger.error('Cart update failed:', errorData);\r\n      return false;\r\n    }\r\n    return true;\r\n  } catch (err) {\r\n    logger.error('updateCartQuantities error:', err);\r\n    return false;\r\n  }\r\n}\r\n\r\n/**\r\n * Complete reservation after successful payment\r\n *\r\n * Called after Stripe confirms the payment to finalize the order\r\n * and generate tickets.\r\n *\r\n * @param orderUuid - The order UUID\r\n * @returns Success status and message\r\n */\r\nexport async function completeReservation(\r\n  orderUuid: string\r\n): Promise<CompleteReservationResponse> {\r\n  try {\r\n    const apiResult = await apiPost<{ message?: string }>(\r\n      `${getPublicBaseUrl()}/orders/complete/${orderUuid}`\r\n    );\r\n\r\n    if (!apiResult.success) {\r\n      return {\r\n        success: false,\r\n        error: apiResult.error || 'Failed to complete reservation',\r\n      };\r\n    }\r\n\r\n    return {\r\n      success: true,\r\n      message: apiResult.data?.message,\r\n    };\r\n  } catch (error) {\r\n    logger.error('Error completing reservation:', error);\r\n    return { success: false, error: 'Network error completing reservation' };\r\n  }\r\n}\r\n\r\n/**\r\n * Cancel reservation and release tickets back to inventory\r\n *\r\n * Called when user abandons checkout or session expires.\r\n *\r\n * @param orderUuid - The order UUID\r\n * @returns Success status and message\r\n */\r\nexport async function cancelReservation(\r\n  orderUuid: string\r\n): Promise<CancelReservationResponse> {\r\n  try {\r\n    const apiResult = await apiPost<{ message?: string }>(\r\n      `${getPublicBaseUrl()}/orders/cancel/${orderUuid}`\r\n    );\r\n\r\n    if (!apiResult.success) {\r\n      return {\r\n        success: false,\r\n        error: apiResult.error || 'Failed to cancel reservation',\r\n      };\r\n    }\r\n\r\n    return {\r\n      success: true,\r\n      message: apiResult.data?.message,\r\n    };\r\n  } catch (error) {\r\n    logger.error('Error cancelling reservation:', error);\r\n    return { success: false, error: 'Network error cancelling reservation' };\r\n  }\r\n}\r\n\r\n/**\r\n * Create a new order/cart\r\n *\r\n * This creates an empty order that can be used for checkout.\r\n * The order UUID is used for all subsequent operations.\r\n *\r\n * @param eventId - The event ID\r\n * @param promoCode - Optional promo code to apply\r\n * @returns The order UUID, or null on error\r\n */\r\nexport async function createOrder(\r\n  eventId: string | number,\r\n  promoCode?: string\r\n): Promise<CreateOrderResponse | null> {\r\n  try {\r\n    const apiResult = await apiPost<CreateOrderResponse>(\r\n      `${getPublicBaseUrl()}/orders/create`,\r\n      // attribution (MISSION-TRUTH C2) rides on cart-create additively.\r\n      { eventID: eventId, promoCode, attribution: getAttribution() }\r\n    );\r\n\r\n    if (!apiResult.success) {\r\n      logger.error('Create order failed:', apiResult.error);\r\n      return null;\r\n    }\r\n\r\n    return apiResult.data!;\r\n  } catch (error) {\r\n    logger.error('createOrder error:', error);\r\n    return null;\r\n  }\r\n}\r\n\r\n/**\r\n * Get order details (public, no auth required)\r\n *\r\n * Fetches the order details for displaying on the success page.\r\n *\r\n * @param orderId - The order UUID or ID\r\n * @returns The order details, or null on error\r\n */\r\nexport async function getOrder(orderId: string): Promise<Order | null> {\r\n  return simpleFetch<Order>(`${getPublicBaseUrl()}/orders/${orderId}`);\r\n}\r\n\r\n/**\r\n * Validate payment intent and complete the order\r\n *\r\n * This is an alternative to completeReservation that accepts\r\n * additional payment details. Used by micdrop-frontend.\r\n *\r\n * @param cartId - The cart/order UUID\r\n * @param payload - Payment validation payload\r\n * @returns Validation result\r\n */\r\nexport async function validatePaymentIntent(\r\n  cartId: string,\r\n  payload: ValidatePaymentRequest\r\n): Promise<ValidatePaymentResponse | null> {\r\n  try {\r\n    const apiResult = await apiPost<{\r\n      success?: boolean;\r\n      status?: string;\r\n      orderUUID?: string;\r\n      uuid?: string;\r\n      // Fail-closed FLOOR flags — the backend gate sets one (with success:false)\r\n      // when the event was canceled/postponed mid-purchase and the charge was\r\n      // NOT captured. They ride in the 200 response body, so they must be read\r\n      // off `data` here and FORWARDED — Checkout.svelte keys the\r\n      // EventUnavailableModal off response.eventCanceled / response.eventPostponed.\r\n      eventCanceled?: boolean;\r\n      eventPostponed?: boolean;\r\n      // Order attribution (MISSION-TRUTH C2) — additive; finalize wins over the\r\n      // cart-create stamp on conflict, backend-side.\r\n    }>(`${getOrdersV2Url()}/validatePaymentIntent/${cartId}`, {\r\n      ...payload,\r\n      attribution: getAttribution(),\r\n    });\r\n\r\n    if (!apiResult.success) {\r\n      logger.error('Payment validation failed:', apiResult.error);\r\n      return {\r\n        success: false,\r\n        status: 'failed',\r\n        error: apiResult.error || 'Payment validation failed',\r\n      };\r\n    }\r\n\r\n    const result = apiResult.data!;\r\n\r\n    // FAIL-CLOSED FLOOR: a 200 body carrying eventCanceled/eventPostponed means\r\n    // the finalize was rejected and no charge was captured. Forward the flags\r\n    // (do NOT coerce to \"Payment succeeded\") so the checkout raises the\r\n    // charge-aware modal instead of navigating to the success page.\r\n    if (result.eventCanceled || result.eventPostponed) {\r\n      return {\r\n        success: false,\r\n        status: 'failed',\r\n        eventCanceled: result.eventCanceled,\r\n        eventPostponed: result.eventPostponed,\r\n      };\r\n    }\r\n\r\n    return {\r\n      success: true,\r\n      status: result.status || 'Payment succeeded',\r\n      orderUUID: result.orderUUID || result.uuid,\r\n    };\r\n  } catch (error) {\r\n    logger.error('validatePaymentIntent error:', error);\r\n    return null;\r\n  }\r\n}\r\n\r\n/**\r\n * Extend the checkout session by 15 minutes\r\n *\r\n * Users get a limited number of extensions (typically 2-3).\r\n *\r\n * @param orderUuid - The order UUID\r\n * @returns Extension result with new expiry time\r\n */\r\nexport async function extendCheckoutSession(\r\n  orderUuid: string\r\n): Promise<ExtendSessionResponse> {\r\n  try {\r\n    const apiResult = await apiPost<{\r\n      newExpiryTime?: string;\r\n      remainingExtensions?: number;\r\n    }>(`${getPublicBaseUrl()}/orders/extend-session`, { orderUuid });\r\n\r\n    if (!apiResult.success) {\r\n      return {\r\n        success: false,\r\n        error: apiResult.error || 'Failed to extend session',\r\n        statusCode: apiResult.statusCode,\r\n      };\r\n    }\r\n\r\n    const result = apiResult.data!;\r\n    return {\r\n      success: true,\r\n      newExpiryTime: result.newExpiryTime,\r\n      remainingExtensions: result.remainingExtensions,\r\n    };\r\n  } catch (error) {\r\n    logger.error('Error extending checkout session:', error);\r\n    return { success: false, error: 'Network error extending session' };\r\n  }\r\n}\r\n\r\n/**\r\n * Get current session status including expiry time\r\n *\r\n * Used to display countdown timer and check if extensions are available.\r\n *\r\n * @param orderUuid - The order UUID\r\n * @returns Session status including expiry time\r\n */\r\nexport async function getSessionStatus(\r\n  orderUuid: string\r\n): Promise<SessionStatus> {\r\n  try {\r\n    const apiResult = await apiGet<SessionStatus>(\r\n      `${getPublicBaseUrl()}/orders/session/${orderUuid}`\r\n    );\r\n\r\n    if (!apiResult.success) {\r\n      return {\r\n        error: apiResult.error || 'No active session found',\r\n        notFound: apiResult.statusCode === 404,\r\n      };\r\n    }\r\n\r\n    const result = apiResult.data!;\r\n    return {\r\n      expiresAt: result.expiresAt,\r\n      extensionCount: result.extensionCount,\r\n      remainingExtensions: result.remainingExtensions,\r\n      canExtend: result.canExtend,\r\n      reservationCount: result.reservationCount,\r\n      eventStatus: result.eventStatus,\r\n    };\r\n  } catch (error) {\r\n    logger.error('Error getting session status:', error);\r\n    return { error: 'Network error getting session status' };\r\n  }\r\n}\r\n\r\n/**\r\n * Initiate a new order (alias for createOrder)\r\n *\r\n * This function provides backwards compatibility with micdrop-frontend.\r\n * It accepts the same parameters as the legacy initiateOrder function.\r\n *\r\n * @param cartData - Object containing eventID and optional quantities/promoCode\r\n * @returns The order UUID, or null on error\r\n */\r\nexport async function initiateOrder(\r\n  cartData: {\r\n    eventID: string | number;\r\n    promoCode?: string;\r\n    quantities?: Record<string | number, number>;\r\n  } = { eventID: '' }\r\n): Promise<string | null> {\r\n  try {\r\n    const apiResult = await apiPost<{ uuid?: string }>(\r\n      `${getPublicBaseUrl()}/orders/create`,\r\n      // attribution (MISSION-TRUTH C2) rides on cart-create additively.\r\n      { ...cartData, attribution: getAttribution() }\r\n    );\r\n\r\n    if (!apiResult.success) {\r\n      logger.error('Order initiation failed:', apiResult.error);\r\n      return null;\r\n    }\r\n\r\n    return apiResult.data?.uuid ?? null;\r\n  } catch (error) {\r\n    logger.error('initiateOrder error:', error);\r\n    return null;\r\n  }\r\n}\r\n\r\n/**\r\n * Track UTM source for analytics\r\n *\r\n * Records the traffic source (utm_source parameter) for marketing analytics.\r\n *\r\n * @param venueId - The venue ID\r\n * @returns Promise that resolves when tracking is complete\r\n */\r\nexport async function trackUTMSource(venueId: string | number): Promise<void> {\r\n  if (typeof window === 'undefined') return;\r\n\r\n  const urlParams = new URLSearchParams(window.location.search);\r\n  // Normalized at write time; empty resolves to the ONE canonical default\r\n  // `direct` (kills the historical `Direct`/`''`/`direct` split).\r\n  const utmSource =\r\n    normalizeUtmValue(urlParams.get('utm_source')) || DIRECT_SOURCE;\r\n\r\n  try {\r\n    // @fetch-escape: fire-and-forget UTM beacon — GET with no response body consumed; simpleFetch would parse JSON on empty 200 and log false-positive errors\r\n    await fetch(\r\n      `${getPublicBaseUrl()}/utm/${venueId}/${encodeURIComponent(utmSource)}`\r\n    );\r\n  } catch (error) {\r\n    logger.error('UTM tracking failed:', error);\r\n  }\r\n}\r\n","/**\r\n * Promo Codes API\r\n *\r\n * Functions for validating and checking promo codes\r\n * in the public checkout flow.\r\n */\r\n\r\nimport { createLogger } from '@getmicdrop/svelte-components/utils/logger';\r\nconst logger = createLogger('VC');\r\nimport { apiGet, apiPost, getOrdersV2Url } from './client.js';\r\nimport type { PromoValidationResponse, HasPromoCodesResponse } from './types.js';\r\n\r\nconst ORDERS_V2_PUBLIC = () => getOrdersV2Url();\r\n\r\n/**\r\n * Validate a promo code for an event\r\n *\r\n * Checks if a promo code is valid and returns its effects:\r\n * - Discount amount and type\r\n * - Hidden ticket reveal\r\n *\r\n * @param eventId - The event ID\r\n * @param code - The promo code to validate\r\n * @returns Validation result with discount info\r\n */\r\nexport async function validatePromoCode(\r\n  eventId: string | number,\r\n  code: string\r\n): Promise<PromoValidationResponse> {\r\n  try {\r\n    if (!code || !code.trim()) {\r\n      return { valid: false, error: 'Promo code is required' };\r\n    }\r\n\r\n    // API uses GET with code in URL path\r\n    const encodedCode = encodeURIComponent(code.trim());\r\n    const apiResult = await apiGet<PromoValidationResponse>(\r\n      `/promo-codes/validate/${eventId}/${encodedCode}`\r\n    );\r\n\r\n    if (!apiResult.success) {\r\n      // 404 = invalid code, other errors are server issues\r\n      if (apiResult.statusCode === 404) {\r\n        return { valid: false, error: 'Invalid promo code' };\r\n      }\r\n      return { valid: false, error: 'Failed to validate code' };\r\n    }\r\n\r\n    const result = apiResult.data!;\r\n    return {\r\n      valid: result.valid ?? true,\r\n      revealHiddenTickets: result.revealHiddenTickets,\r\n      revealTicketIds: result.revealTicketIds,\r\n      provideDiscount: result.provideDiscount,\r\n      discountType: result.discountType,\r\n      amount: result.amount,\r\n      code: result.code || code,\r\n    };\r\n  } catch (error) {\r\n    logger.error('Error validating promo code:', error);\r\n    return { valid: false, error: 'Network error validating code' };\r\n  }\r\n}\r\n\r\n/**\r\n * Check if promo codes are available for an event\r\n *\r\n * Used to conditionally show/hide the promo code input field.\r\n *\r\n * @param eventId - The event ID\r\n * @returns Whether promo codes exist for this event\r\n */\r\nexport async function hasPromoCodes(eventId: string | number): Promise<boolean> {\r\n  try {\r\n    const apiResult = await apiGet<HasPromoCodesResponse>(\r\n      `/promo-codes/check/${eventId}`\r\n    );\r\n\r\n    if (!apiResult.success) {\r\n      // If endpoint doesn't exist or errors, default to showing the input\r\n      return true;\r\n    }\r\n\r\n    return apiResult.data!.hasPromoCodes === true;\r\n  } catch (error) {\r\n    logger.error('Error checking promo codes availability:', error);\r\n    // Default to showing promo input if we can't check\r\n    return true;\r\n  }\r\n}\r\n\r\n/**\r\n * Apply a promo code to a cart\r\n *\r\n * This updates the cart with the promo code discount.\r\n *\r\n * @param cartId - The cart UUID\r\n * @param code - The promo code to apply\r\n * @returns Success status\r\n */\r\nexport async function applyPromoCode(\r\n  cartId: string,\r\n  code: string\r\n): Promise<{ success: boolean; error?: string }> {\r\n  try {\r\n    // Backend route: POST /api/orders/v2/public/cart/{uuid}/apply-promo with a\r\n    // `promoCode` body (mirrors apply-gift-card). The old path/body\r\n    // (/orders/{id}/apply-promo + {code}) matched no route, so the cart's\r\n    // DiscountAmount was never set and the buyer was charged full price despite\r\n    // the UI showing a discount.\r\n    const apiResult = await apiPost(\r\n      `${ORDERS_V2_PUBLIC()}/cart/${cartId}/apply-promo`,\r\n      { promoCode: code }\r\n    );\r\n\r\n    if (!apiResult.success) {\r\n      return {\r\n        success: false,\r\n        error: apiResult.error || 'Failed to apply promo code',\r\n      };\r\n    }\r\n\r\n    return { success: true };\r\n  } catch (error) {\r\n    logger.error('Error applying promo code:', error);\r\n    return { success: false, error: 'Network error applying code' };\r\n  }\r\n}\r\n\r\n/**\r\n * Remove a promo code from a cart\r\n *\r\n * @param cartId - The cart UUID\r\n * @returns Success status\r\n */\r\nexport async function removePromoCode(\r\n  cartId: string\r\n): Promise<{ success: boolean; error?: string }> {\r\n  try {\r\n    const apiResult = await apiPost(`/orders/${cartId}/remove-promo`);\r\n\r\n    if (!apiResult.success) {\r\n      return {\r\n        success: false,\r\n        error: apiResult.error || 'Failed to remove promo code',\r\n      };\r\n    }\r\n\r\n    return { success: true };\r\n  } catch (error) {\r\n    logger.error('Error removing promo code:', error);\r\n    return { success: false, error: 'Network error removing code' };\r\n  }\r\n}\r\n","/**\r\n * Event Performers Transformer\r\n *\r\n * Normalizes the public `GET /events/{id}/performers` response into the\r\n * EventPerformersResponse domain shape consumed by the lineup UI.\r\n *\r\n * Wire->domain boundary: one parseEventPerformers(raw: unknown) entry point.\r\n * The normalization guarantees the two invariants the UI relies on:\r\n *   - `performers` is ALWAYS an array (non-array / missing -> [])\r\n *   - `showPerformers` is ALWAYS a strict boolean (only `=== true` is true)\r\n *\r\n * Byte-identical to the inline mapping previously embedded in\r\n * fetchEventPerformers (src/lib/api/events.ts). The network-failure guard\r\n * (`!data` -> empty response) is a CALLER concern and stays in\r\n * fetchEventPerformers; nullish input here is treated as an empty payload so\r\n * the parser never throws.\r\n */\r\n\r\nimport type { EventPerformersResponse } from '../types.js';\r\n\r\n/**\r\n * Parse a raw performers payload into the normalized EventPerformersResponse.\r\n *\r\n * `performers` is coerced to an array (Array.isArray gate), `showPerformers`\r\n * to a strict boolean (`=== true`) -- matching fetchEventPerformers exactly.\r\n */\r\nexport function parseEventPerformers(raw: unknown): EventPerformersResponse {\r\n  const data = (raw ?? {}) as Partial<EventPerformersResponse>;\r\n  return {\r\n    performers: Array.isArray(data.performers) ? data.performers : [],\r\n    showPerformers: data.showPerformers === true,\r\n  };\r\n}\r\n","/**\r\n * Result — the canonical error channel for API loaders.\r\n *\r\n * The public loaders in this package historically collapsed a failed fetch\r\n * to an empty value (`[]`, `{ performers: [], showPerformers: false }`, or\r\n * `null`), which is indistinguishable from a genuinely empty result. A buyer\r\n * hitting a live event during a transient endpoint blip then sees a dead\r\n * \"No tickets available\" button with no retry; a failed calendar month\r\n * renders dark, so the venue looks like it has nothing on.\r\n *\r\n * `Result<T>` gives loaders a typed error channel so callers can tell\r\n * \"the request failed\" apart from \"there is genuinely nothing here\", and\r\n * offer a retry instead of silently degrading. Loaders expose a `*Result`\r\n * variant returning this; the array/object-returning public functions stay\r\n * as thin fail-open adapters over that variant (single source of truth for\r\n * the fetch + validation logic).\r\n *\r\n * Not to be confused with `ApiResponse<T>` (types.ts) — that is the write-path\r\n * HTTP envelope returned by `apiPost`/`apiGet`/etc. (`{ success, data?, error?,\r\n * statusCode? }`, discriminated on `.success`). `Result<T>` is the read-path\r\n * loader channel, discriminated on `.ok`. They are deliberately separate:\r\n * `ApiResponse` carries transport metadata, `Result` carries only \"loaded vs\r\n * failed\". Check `.ok` on a Result, `.success` on an ApiResponse — never mix.\r\n */\r\n\r\nexport type Result<T> =\r\n  | { ok: true; data: T }\r\n  | { ok: false; error: string; notFound?: boolean };\r\n\r\n/** Wrap a successful value. */\r\nexport function ok<T>(data: T): Result<T> {\r\n  return { ok: true, data };\r\n}\r\n\r\n/**\r\n * Wrap a failure with a short, stable error code (used for i18n/render).\r\n *\r\n * This is a TRANSIENT failure by default (`notFound` is unset) — the request\r\n * couldn't complete (network, timeout, 5xx). Loaders that gate an SSR 404 on a\r\n * Result must treat a plain `err(...)` as retryable (503), not \"gone\" (404), so\r\n * a backend blip never renders a permanent-looking 404 to a buyer or crawler.\r\n */\r\nexport function err(error: string): Result<never> {\r\n  return { ok: false, error };\r\n}\r\n\r\n/**\r\n * Wrap a genuine \"this resource does not exist\" failure (upstream 404).\r\n *\r\n * Distinct from `err(...)`: only this warrants an SSR 404. A transient upstream\r\n * failure must NOT collapse to `errNotFound` — that is exactly the bug this\r\n * channel exists to prevent (a fetch blip becoming a permanent 404).\r\n */\r\nexport function errNotFound(error: string): Result<never> {\r\n  return { ok: false, error, notFound: true };\r\n}\r\n\r\n/** Narrowing guard — true when the result carries data. */\r\nexport function isOk<T>(result: Result<T>): result is { ok: true; data: T } {\r\n  return result.ok;\r\n}\r\n\r\n/**\r\n * Narrowing guard — true when the result is a failure (transient OR not-found).\r\n *\r\n * This package typechecks with `strictNullChecks` off, where the ELSE flow of a\r\n * discriminated union does NOT narrow (`if (r.ok) {…}` narrows the positive\r\n * branch, but the fall-through keeps the full union). Consumers that need the\r\n * failure branch's `error`/`notFound` must narrow POSITIVELY through this guard\r\n * rather than relying on `!r.ok` control flow.\r\n */\r\nexport function isErr<T>(\r\n  result: Result<T>\r\n): result is { ok: false; error: string; notFound?: boolean } {\r\n  return !result.ok;\r\n}\r\n\r\n/**\r\n * Collapse a Result to its data, substituting `fallback` on failure.\r\n *\r\n * This is the fail-open bridge the legacy array/object-returning loaders use\r\n * so existing callers keep their current \"degrade to empty\" behaviour until\r\n * they adopt the Result variant and render an error/retry state.\r\n */\r\nexport function unwrapOr<T>(result: Result<T>, fallback: T): T {\r\n  return result.ok ? result.data : fallback;\r\n}\r\n","import { AppError } from '@getmicdrop/svelte-components';\r\n/**\r\n * Events API\r\n *\r\n * Functions for fetching event data, tickets, and performers\r\n * in the public checkout flow.\r\n */\r\n\r\nimport { createLogger } from '@getmicdrop/svelte-components/utils/logger';\r\nconst logger = createLogger('VC');\r\nimport { getPublicBaseUrl, simpleFetch } from './client.js';\r\nimport { parseEventPerformers } from './transformers/performer.js';\r\nimport { ok, err, errNotFound, unwrapOr, type Result } from './result.js';\r\nimport type {\r\n  Event,\r\n  AvailableTicket,\r\n  EventPerformersResponse,\r\n  SeriesOccurrencesResponse,\r\n  SeriesPageData,\r\n  PublicCollectionData,\r\n  ResolvedEntity,\r\n} from './types.js';\r\n\r\n/**\r\n * In-flight `fetchEventDetails` requests keyed by event ID. Lets\r\n * concurrent callers (Checkout, CartView, EventDetailsView, +page.js\r\n * server load — all of which may run during a single navigation) share\r\n * one HTTP request rather than triggering three.\r\n *\r\n * The entry deletes as soon as the underlying promise settles, so this\r\n * is dedup not cache. For real cache-with-TTL we'd want SWR; that's a\r\n * future milestone.\r\n */\r\nconst _eventDetailsInflight = new Map<string, Promise<Result<Event>>>();\r\n\r\n/**\r\n * Fetch event details, discriminating a real 404 from a transient failure.\r\n *\r\n * The SSR loader (`[id]/[slug]/+page.js`) gates its HTTP status on this: only an\r\n * upstream 404 (`errNotFound`) becomes a page 404. A 5xx, a network error, or a\r\n * timeout is `err(...)` (transient) so the loader can answer 503/retry instead\r\n * of rendering a permanent-looking 404 to a buyer or crawler on a backend blip.\r\n *\r\n * @param eventId - The event ID\r\n * @param customFetch - Optional custom fetch function (for SSR)\r\n * @param password - Optional password for gated events\r\n * @returns `ok(event)`, `errNotFound(...)` for a real 404, or `err(...)` transient\r\n */\r\nexport async function fetchEventDetailsResult(\r\n  eventId: string | number,\r\n  customFetch: typeof fetch = fetch,\r\n  password?: string\r\n): Promise<Result<Event>> {\r\n  // Password-protected events are gated server-side: the detail endpoint returns\r\n  // a stripped teaser (+ passwordRequired:true) until the correct ?password= is\r\n  // supplied. The password varies the dedup key so an unlock re-fetch is never\r\n  // served the cached locked teaser.\r\n  const key = password ? `${eventId}:pw` : String(eventId);\r\n  // Only dedup when using the default fetch — custom-fetch callers\r\n  // (e.g. SvelteKit server loads) bring their own request context.\r\n  if (customFetch === fetch) {\r\n    const inflight = _eventDetailsInflight.get(key);\r\n    if (inflight) return inflight;\r\n  }\r\n\r\n  const run = (async (): Promise<Result<Event>> => {\r\n    try {\r\n      const url = password\r\n        ? `${getPublicBaseUrl()}/events/${eventId}?password=${encodeURIComponent(password)}`\r\n        : `${getPublicBaseUrl()}/events/${eventId}`;\r\n      // @fetch-escape: customFetch injection for SSR context — caller-supplied fetch instance, cannot use simpleFetch\r\n      const response = await customFetch(url);\r\n      if (!response.ok) {\r\n        // 404 = the event genuinely does not exist (removed / bad URL). Anything\r\n        // else (5xx, 429, 408, …) is a transient upstream failure the client\r\n        // already retried in fetchWithRetry — surface it as retryable, not gone.\r\n        if (response.status === 404) {\r\n          return errNotFound('event_not_found');\r\n        }\r\n        logger.error(`Failed to fetch event details: ${response.status}`);\r\n        return err('event_fetch_failed');\r\n      }\r\n      const data = await response.json();\r\n      // A 200 with a falsy/null body means the event is absent (an event is\r\n      // always a non-null object) — treat it as a real 404, matching the old\r\n      // `if (!event) throw error(404)` guard. Without this the loader would\r\n      // deref `null.title` and crash to a generic 500.\r\n      if (!data) {\r\n        return errNotFound('event_not_found');\r\n      }\r\n      return ok(data);\r\n    } catch (error) {\r\n      // Network error / timeout — transient by definition, never a 404.\r\n      logger.error('Error fetching event details:', error);\r\n      return err('event_fetch_failed');\r\n    }\r\n  })();\r\n\r\n  if (customFetch === fetch) {\r\n    _eventDetailsInflight.set(key, run);\r\n    run.finally(() => {\r\n      // Only clear if this same promise is still the inflight one — a\r\n      // later call may have set up a fresh request after the user\r\n      // triggered an explicit refresh.\r\n      if (_eventDetailsInflight.get(key) === run) {\r\n        _eventDetailsInflight.delete(key);\r\n      }\r\n    });\r\n  }\r\n  return run;\r\n}\r\n\r\n/**\r\n * Fetch event details\r\n *\r\n * Gets full event information including venue and ticket data.\r\n *\r\n * Fail-open adapter over {@link fetchEventDetailsResult}: existing in-widget\r\n * callers (Checkout, CartView, EventPage) keep degrading to `null` on any\r\n * failure. SSR loaders that need to answer 404-vs-503 use the Result variant.\r\n *\r\n * @param eventId - The event ID\r\n * @param customFetch - Optional custom fetch function (for SSR)\r\n * @returns Event details or null on error\r\n */\r\nexport async function fetchEventDetails(\r\n  eventId: string | number,\r\n  customFetch: typeof fetch = fetch,\r\n  password?: string\r\n): Promise<Event | null> {\r\n  const result = await fetchEventDetailsResult(eventId, customFetch, password);\r\n  return result.ok ? result.data : null;\r\n}\r\n\r\n/**\r\n * Shared loader skeleton for the public GET read endpoints.\r\n *\r\n * simpleFetch returns `null` on a network/server failure — indistinguishable\r\n * from a genuinely empty result once collapsed to `[]`. This centralises the\r\n * fetch + null-guard + catch so the failure contract lives in ONE place\r\n * instead of eight near-copies: a falsy body becomes `err(errorCode)` (the\r\n * request failed — offer retry); a real payload is mapped and returned as\r\n * `ok(...)` (there is genuinely this much, even if it's an empty list).\r\n *\r\n * `map` normalises the raw payload (e.g. unwrap a `{ events }` wrapper); it\r\n * defaults to identity for endpoints that return the value directly.\r\n */\r\ntype PayloadMap<T> = (_data: unknown) => T;\r\n\r\nasync function loadResult<T>(\r\n  url: string,\r\n  errorCode: string,\r\n  map: PayloadMap<T> = data => data as T\r\n): Promise<Result<T>> {\r\n  try {\r\n    const data = await simpleFetch<unknown>(url);\r\n    if (!data) {\r\n      logger.error(`Failed to load (${errorCode}): network or server error`);\r\n      return err(errorCode);\r\n    }\r\n    return ok(map(data));\r\n  } catch (error) {\r\n    logger.error(`Error loading (${errorCode}):`, error);\r\n    return err(errorCode);\r\n  }\r\n}\r\n\r\n/** Coerce a payload to an array, tolerating a non-array (malformed) body. */\r\nfunction asArray<T>(data: unknown): T[] {\r\n  return Array.isArray(data) ? (data as T[]) : [];\r\n}\r\n\r\n/**\r\n * Fetch available tickets for an event\r\n *\r\n * Returns all ticket types that are currently available for sale.\r\n *\r\n * @param eventId - The event ID\r\n * @returns Array of available tickets\r\n */\r\nexport async function fetchEventTicketsResult(\r\n  eventId: string | number\r\n): Promise<Result<AvailableTicket[]>> {\r\n  return loadResult(\r\n    `${getPublicBaseUrl()}/tickets/event/${eventId}`,\r\n    'tickets_fetch_failed',\r\n    asArray<AvailableTicket>\r\n  );\r\n}\r\n\r\nexport async function fetchEventTickets(\r\n  eventId: string | number\r\n): Promise<AvailableTicket[]> {\r\n  // Fail-open adapter — existing callers keep degrading to empty until they\r\n  // adopt fetchEventTicketsResult and render an error/retry state.\r\n  return unwrapOr(await fetchEventTicketsResult(eventId), []);\r\n}\r\n\r\n/**\r\n * Fetch performers for an event\r\n *\r\n * Returns the lineup with pre-resolved avatar URLs.\r\n *\r\n * @param eventId - The event ID\r\n * @returns Performers list and visibility flag\r\n */\r\nexport async function fetchEventPerformersResult(\r\n  eventId: string | number\r\n): Promise<Result<EventPerformersResponse>> {\r\n  if (!eventId) {\r\n    logger.warn('fetchEventPerformers called without eventId');\r\n    return err('performers_missing_event_id');\r\n  }\r\n  // Normalization (performers->array, showPerformers->strict boolean) lives in\r\n  // parseEventPerformers — byte-identical to the inline mapping it replaced.\r\n  return loadResult(\r\n    `${getPublicBaseUrl()}/events/${eventId}/performers`,\r\n    'performers_fetch_failed',\r\n    data => parseEventPerformers(data as EventPerformersResponse)\r\n  );\r\n}\r\n\r\nexport async function fetchEventPerformers(\r\n  eventId: string | number\r\n): Promise<EventPerformersResponse> {\r\n  return unwrapOr(await fetchEventPerformersResult(eventId), {\r\n    performers: [],\r\n    showPerformers: false,\r\n  });\r\n}\r\n\r\n/**\r\n * Fetch all venues for an organization\r\n *\r\n * @param orgId - The organization ID\r\n * @returns Array of venues\r\n */\r\nexport async function fetchAllVenuesResult(\r\n  orgId: string | number\r\n): Promise<Result<any[]>> {\r\n  if (!orgId) {\r\n    logger.warn('fetchAllVenues called without orgId');\r\n    return err('venues_missing_org_id');\r\n  }\r\n  return loadResult(\r\n    `${getPublicBaseUrl()}/venues/organization/${orgId}`,\r\n    'venues_fetch_failed',\r\n    asArray\r\n  );\r\n}\r\n\r\nexport async function fetchAllVenues(orgId: string | number): Promise<any[]> {\r\n  return unwrapOr(await fetchAllVenuesResult(orgId), []);\r\n}\r\n\r\n/**\r\n * Fetch events for a venue\r\n *\r\n * @param venueId - The venue ID\r\n * @returns Array of events\r\n */\r\nexport async function fetchVenueEventsResult(\r\n  venueId: string | number\r\n): Promise<Result<Event[]>> {\r\n  if (!venueId) {\r\n    logger.warn('fetchVenueEvents called without venueId');\r\n    return err('venue_events_missing_venue_id');\r\n  }\r\n  return loadResult(\r\n    `${getPublicBaseUrl()}/events/venue/${venueId}`,\r\n    'venue_events_fetch_failed',\r\n    asArray<Event>\r\n  );\r\n}\r\n\r\nexport async function fetchVenueEvents(\r\n  venueId: string | number\r\n): Promise<Event[]> {\r\n  return unwrapOr(await fetchVenueEventsResult(venueId), []);\r\n}\r\n\r\n/**\r\n * Get events for a specific month\r\n *\r\n * Used for calendar views to efficiently load events.\r\n *\r\n * @param venueId - The venue ID\r\n * @param year - The year (e.g., 2024)\r\n * @param month - The month (1-12)\r\n * @returns Array of events for the month\r\n */\r\n/**\r\n * Unwrap a month payload (bare `Event[]` or `{ events }` wrapper) to an array.\r\n * Shared by the venue and org month loaders so the two stay byte-identical.\r\n */\r\nfunction unwrapMonthEvents(data: { events?: Event[] } | Event[]): Event[] {\r\n  if (Array.isArray(data)) return data;\r\n  return Array.isArray((data as { events?: Event[] }).events)\r\n    ? (data as { events: Event[] }).events\r\n    : [];\r\n}\r\n\r\nexport async function getMonthEventsResult(\r\n  venueId: string | number,\r\n  year: number,\r\n  month: number\r\n): Promise<Result<Event[]>> {\r\n  return loadResult(\r\n    `${getPublicBaseUrl()}/events/venue/${venueId}/month/${year}/${month}`,\r\n    'month_events_fetch_failed',\r\n    data => unwrapMonthEvents(data as { events?: Event[] } | Event[])\r\n  );\r\n}\r\n\r\nexport async function getMonthEvents(\r\n  venueId: string | number,\r\n  year: number,\r\n  month: number\r\n): Promise<Event[]> {\r\n  return unwrapOr(await getMonthEventsResult(venueId, year, month), []);\r\n}\r\n\r\n/**\r\n * Get events for an organization for a specific month\r\n *\r\n * @param orgId - The organization ID\r\n * @param year - The year (e.g., 2024)\r\n * @param month - The month (1-12)\r\n * @returns Array of events for the month\r\n */\r\nexport async function getOrgMonthEventsResult(\r\n  orgId: string | number,\r\n  year: number,\r\n  month: number\r\n): Promise<Result<Event[]>> {\r\n  return loadResult(\r\n    `${getPublicBaseUrl()}/events/organization/${orgId}/month/${year}/${month}`,\r\n    'org_month_events_fetch_failed',\r\n    data => unwrapMonthEvents(data as { events?: Event[] } | Event[])\r\n  );\r\n}\r\n\r\nexport async function getOrgMonthEvents(\r\n  orgId: string | number,\r\n  year: number,\r\n  month: number\r\n): Promise<Event[]> {\r\n  return unwrapOr(await getOrgMonthEventsResult(orgId, year, month), []);\r\n}\r\n\r\n/**\r\n * Get series occurrences for date selector\r\n *\r\n * Returns all instances of a recurring event series.\r\n *\r\n * @param eventSeriesId - The series ID\r\n * @returns Series occurrences\r\n */\r\nexport async function getSeriesOccurrencesResult(\r\n  eventSeriesId: number\r\n): Promise<Result<SeriesOccurrencesResponse>> {\r\n  return loadResult(\r\n    `${getPublicBaseUrl()}/series/${eventSeriesId}/occurrences`,\r\n    'series_occurrences_fetch_failed'\r\n  );\r\n}\r\n\r\nexport async function getSeriesOccurrences(\r\n  eventSeriesId: number\r\n): Promise<SeriesOccurrencesResponse | null> {\r\n  const result = await getSeriesOccurrencesResult(eventSeriesId);\r\n  return result.ok ? result.data : null;\r\n}\r\n\r\n/**\r\n * Fetch series occurrences with CTA state\r\n *\r\n * Returns full series data including availability status.\r\n *\r\n * @param eventSeriesId - The series ID\r\n * @param venueId - The venue ID\r\n * @returns Series occurrences with CTA state\r\n */\r\nexport async function fetchSeriesOccurrencesResult(\r\n  eventSeriesId: number,\r\n  venueId: string | number\r\n): Promise<Result<SeriesOccurrencesResponse>> {\r\n  // Distinct code from getSeriesOccurrencesResult: this is the CTA-state\r\n  // variant (venue-scoped), so telemetry/retry can tell the two apart.\r\n  return loadResult(\r\n    `${getPublicBaseUrl()}/series/${eventSeriesId}/occurrences?venueId=${venueId}`,\r\n    'series_cta_fetch_failed'\r\n  );\r\n}\r\n\r\nexport async function fetchSeriesOccurrences(\r\n  eventSeriesId: number,\r\n  venueId: string | number\r\n): Promise<SeriesOccurrencesResponse | null> {\r\n  const result = await fetchSeriesOccurrencesResult(eventSeriesId, venueId);\r\n  return result.ok ? result.data : null;\r\n}\r\n\r\n/**\r\n * Resolve an entity-agnostic public id to its concrete type + data.\r\n *\r\n * The embed deep-link (`#{id}-{slug}`) carries no type prefix, so a cold\r\n * deep link (the show isn't in the loaded calendar month) needs the backend\r\n * to say whether the id is an event, series, or collection before the widget\r\n * can render the right view. This is the same `/api/v2/public/resolve/{id}`\r\n * endpoint get-micdrop.com's `/e/{slugId}` page resolves server-side; here we\r\n * call it cross-origin from the venue's page (the public API already serves\r\n * the calendar cross-origin, so CORS is in place).\r\n *\r\n * @param id - The entity id (numeric, type-agnostic)\r\n * @param slug - Optional slug hint (lets the backend canonicalize)\r\n * @returns `{ type, id, title, data }` or null on failure\r\n */\r\nexport async function resolvePublicEntity(\r\n  id: string | number,\r\n  slug?: string\r\n): Promise<ResolvedEntity | null> {\r\n  try {\r\n    const qs = slug ? `?slug=${encodeURIComponent(slug)}` : '';\r\n    const data = await simpleFetch<ResolvedEntity>(\r\n      `${getPublicBaseUrl()}/resolve/${encodeURIComponent(String(id))}${qs}`\r\n    );\r\n\r\n    if (!data || !data.type) {\r\n      throw new AppError(\r\n        'Failed to resolve entity',\r\n        'lib/api/events/resolvePublicEntity'\r\n      );\r\n    }\r\n\r\n    return data;\r\n  } catch (error) {\r\n    logger.error('Error resolving public entity:', error);\r\n    return null;\r\n  }\r\n}\r\n\r\n/**\r\n * Fetch series page data\r\n *\r\n * Returns full series information including occurrences, venue, and performers.\r\n *\r\n * @param seriesId - The series ID\r\n * @returns Series page data or null on error\r\n */\r\nexport async function fetchSeriesPage(\r\n  seriesId: string | number\r\n): Promise<SeriesPageData | null> {\r\n  try {\r\n    const data = await simpleFetch<SeriesPageData>(\r\n      `${getPublicBaseUrl()}/series/${seriesId}/page`\r\n    );\r\n\r\n    if (!data) {\r\n      throw new AppError(\r\n        'Failed to fetch series page',\r\n        'lib/api/events/fetchSeriesPage'\r\n      );\r\n    }\r\n\r\n    return data;\r\n  } catch (error) {\r\n    logger.error('Error fetching series page:', error);\r\n    return null;\r\n  }\r\n}\r\n\r\n/**\r\n * Fetch public collection data\r\n *\r\n * Returns collection info including events list.\r\n * For password-protected collections, pass the password to authenticate.\r\n *\r\n * @param collectionId - The collection ID\r\n * @param password - Optional password for protected collections\r\n * @returns Collection data or null on error\r\n */\r\nexport async function fetchPublicCollection(\r\n  collectionId: string | number,\r\n  password?: string\r\n): Promise<PublicCollectionData | null> {\r\n  try {\r\n    // Backend route is GET /api/v2/public/collections/:id (password via ?password=\r\n    // query, read by GetPublicCollection). The previous \"/public\" suffix matched\r\n    // no route and 404'd every public collection fetch.\r\n    const url = password\r\n      ? `${getPublicBaseUrl()}/collections/${collectionId}?password=${encodeURIComponent(password)}`\r\n      : `${getPublicBaseUrl()}/collections/${collectionId}`;\r\n\r\n    const data = await simpleFetch<PublicCollectionData>(url);\r\n\r\n    if (!data) {\r\n      throw new AppError(\r\n        'Failed to fetch collection',\r\n        'lib/api/events/fetchPublicCollection'\r\n      );\r\n    }\r\n\r\n    return data;\r\n  } catch (error) {\r\n    logger.error('Error fetching collection:', error);\r\n    return null;\r\n  }\r\n}\r\n\r\n/**\r\n * Check collection password\r\n *\r\n * Validates password for password-protected collections.\r\n *\r\n * @param collectionId - The collection ID\r\n * @param password - The password to check\r\n * @returns Whether the password is valid\r\n */\r\nexport async function checkCollectionPassword(\r\n  collectionId: string | number,\r\n  password: string\r\n): Promise<boolean> {\r\n  try {\r\n    // Password in the POST body, never the URL (AC-EV-5 — see checkEventPassword).\r\n    // Backend contract: POST /public/collections/:id/check-password { password } -> { valid }.\r\n    const result = await simpleFetch<{ valid: boolean }>(\r\n      `${getPublicBaseUrl()}/collections/${collectionId}/check-password`,\r\n      {\r\n        method: 'POST',\r\n        headers: { 'Content-Type': 'application/json' },\r\n        body: JSON.stringify({ password }),\r\n      }\r\n    );\r\n\r\n    return result?.valid === true;\r\n  } catch (error) {\r\n    logger.error('Error checking collection password:', error);\r\n    return false;\r\n  }\r\n}\r\n\r\n/**\r\n * Check event password\r\n *\r\n * Validates password for password-protected events.\r\n *\r\n * @param eventId - The event ID\r\n * @param password - The password to check\r\n * @returns Whether the password is valid\r\n */\r\nexport async function checkEventPassword(\r\n  eventId: string | number,\r\n  password: string\r\n): Promise<{ valid: boolean }> {\r\n  try {\r\n    // The password rides in the POST body, never the URL. A path/query secret\r\n    // lands in access logs, browser history, and Referer headers (AC-EV-5).\r\n    // Backend contract: POST /public/events/:id/check-password { password } -> { valid }.\r\n    // Guarded by check-password-contract.test.ts — flip both together, deliberately.\r\n    const result = await simpleFetch<{ valid: boolean } | boolean>(\r\n      `${getPublicBaseUrl()}/events/${eventId}/check-password`,\r\n      {\r\n        method: 'POST',\r\n        headers: { 'Content-Type': 'application/json' },\r\n        body: JSON.stringify({ password }),\r\n      }\r\n    );\r\n\r\n    if (typeof result === 'boolean') return { valid: result };\r\n    return { valid: result?.valid === true };\r\n  } catch (error) {\r\n    logger.error('Error checking event password:', error);\r\n    return { valid: false };\r\n  }\r\n}\r\n\r\n/**\r\n * Test network connection\r\n *\r\n * Health check endpoint for connectivity testing.\r\n *\r\n * @param orgId - Optional org ID\r\n * @param venueId - Optional venue ID\r\n * @returns Whether the connection is working\r\n */\r\nexport async function testNetworkConnection(\r\n  orgId?: string | number,\r\n  venueId?: string | number\r\n): Promise<boolean> {\r\n  try {\r\n    // Use a simple endpoint to test connectivity\r\n    const url = venueId\r\n      ? `${getPublicBaseUrl()}/events/venue/${venueId}`\r\n      : orgId\r\n        ? `${getPublicBaseUrl()}/venues/organization/${orgId}`\r\n        : `${getPublicBaseUrl()}/health`;\r\n\r\n    // @fetch-escape: HEAD request for connectivity probe — simpleFetch always parses JSON body; HEAD has no body\r\n    const response = await fetch(url, {\r\n      method: 'HEAD',\r\n    });\r\n\r\n    return response.ok;\r\n  } catch (error) {\r\n    logger.error('Network connection test failed:', error);\r\n    return false;\r\n  }\r\n}\r\n","/**\r\n * Venues API\r\n *\r\n * Functions for fetching venue data including\r\n * service fees and tax configuration.\r\n */\r\n\r\nimport { createLogger } from '@getmicdrop/svelte-components/utils/logger';\r\nconst logger = createLogger('VC');\r\nimport { apiGet } from './client.js';\r\nimport type { Venue } from './types.js';\r\n\r\n/**\r\n * Get venue details (public, no auth required)\r\n *\r\n * Fetches venue information including service fees and taxes\r\n * needed for checkout calculations.\r\n *\r\n * @param venueId - The venue ID\r\n * @returns Venue details or null on error\r\n */\r\nexport async function getVenue(venueId: string | number): Promise<Venue | null> {\r\n  try {\r\n    if (!venueId) {\r\n      logger.warn('getVenue called without venueId');\r\n      return null;\r\n    }\r\n\r\n    const result = await apiGet<Venue>(`/venues/${venueId}`);\r\n    if (!result.success) {\r\n      logger.error(`Failed to fetch venue: ${result.statusCode}`);\r\n      return null;\r\n    }\r\n    return result.data ?? null;\r\n  } catch (error) {\r\n    logger.error('Error fetching venue:', error);\r\n    return null;\r\n  }\r\n}\r\n\r\n/**\r\n * Get venue service fee configuration\r\n *\r\n * Returns just the fee-related fields for checkout calculations.\r\n *\r\n * @param venueId - The venue ID\r\n * @returns Fee configuration or null on error\r\n */\r\nexport async function getVenueFees(\r\n  venueId: string | number\r\n): Promise<{\r\n  serviceFeePercentage: number;\r\n  serviceFeeCents: number;\r\n  taxPercentage: number;\r\n} | null> {\r\n  const venue = await getVenue(venueId);\r\n\r\n  if (!venue) {\r\n    return null;\r\n  }\r\n\r\n  return {\r\n    serviceFeePercentage: venue.serviceFeePercentage ?? 0,\r\n    serviceFeeCents: venue.serviceFeeCents ?? 0,\r\n    taxPercentage: venue.taxPercentage ?? 0,\r\n  };\r\n}\r\n\r\n/**\r\n * Get venue by slug\r\n *\r\n * Fetches venue using its URL-friendly slug.\r\n *\r\n * @param slug - The venue slug\r\n * @returns Venue details or null on error\r\n */\r\nexport async function getVenueBySlug(slug: string): Promise<Venue | null> {\r\n  try {\r\n    if (!slug) {\r\n      logger.warn('getVenueBySlug called without slug');\r\n      return null;\r\n    }\r\n\r\n    const encodedSlug = encodeURIComponent(slug);\r\n    const result = await apiGet<Venue>(`/venues/slug/${encodedSlug}`);\r\n    if (!result.success) {\r\n      logger.error(`Failed to fetch venue by slug: ${result.statusCode}`);\r\n      return null;\r\n    }\r\n    return result.data ?? null;\r\n  } catch (error) {\r\n    logger.error('Error fetching venue by slug:', error);\r\n    return null;\r\n  }\r\n}\r\n","/**\r\n * Gift Card Application Transformer\r\n *\r\n * Normalizes the orders-service `POST /cart/{id}/apply-gift-card` SUCCESS\r\n * payload into the ApplyGiftCardResult shape the Checkout component /\r\n * GiftCardInput consume.\r\n *\r\n * Wire->domain boundary: one parseGiftCardApplication(raw: unknown) entry\r\n * point. The wire field shape (ApplyGiftCardApiResponse) is named ONLY inside\r\n * this module and never escapes. Error paths (429 rate-limit, !success,\r\n * network catch) are CALLER concerns and stay in applyGiftCard -- this parser\r\n * only maps the success body.\r\n *\r\n * UNIT CONTRACT (do NOT \"fix\" this -- VC public endpoints speak cents):\r\n *   Backend ApplyGiftCardResponse (orders-service controllers/cart.go):\r\n *     - appliedAmount   : integer CENTS  (int64)\r\n *     - giftCardBalance : integer CENTS  (int64)\r\n *     - remainingTotal  : DOLLARS        (float64)\r\n *   Domain ApplyGiftCardResult (formatCurrency-ready, DOLLARS):\r\n *     - giftCardAmount  : appliedAmount / 100   (cents -> dollars)\r\n *     - stripeAmount    : remainingTotal        (already dollars, no /100)\r\n *     - giftCardBalance : passed through as-is  (raw CENTS, not displayed via\r\n *                         formatCurrency)\r\n * The cents->dollars conversion lives HERE, exactly where/how the inline\r\n * mapper in applyGiftCard had it -- byte-identical.\r\n */\r\n\r\nimport type { ApplyGiftCardResult } from '../gift-cards.js';\r\n\r\n// -- Wire shape (internal -- never exported) ---------------------------------\r\n// Source of truth: orders-service controllers/cart.go ApplyGiftCardResponse.\r\ninterface ApplyGiftCardApiResponse {\r\n  valid: boolean;\r\n  message?: string;\r\n  giftCardCode?: string;\r\n  giftCardBalance?: number; // cents\r\n  appliedAmount?: number; // cents\r\n  remainingTotal?: number; // dollars\r\n  fullyCoveredByCard?: boolean;\r\n}\r\n\r\n/**\r\n * Parse a successful apply-gift-card backend payload into ApplyGiftCardResult.\r\n *\r\n * Maps the real backend contract to what the Checkout component expects:\r\n *   - appliedAmount (cents) -> giftCardAmount (dollars)\r\n *   - remainingTotal (dollars) -> stripeAmount (dollars, residual to charge)\r\n *   - fullyCoveredByCard -> paymentType 'gift_card_only' | 'split' + requiresStripe\r\n * Byte-identical to the inline success-path mapping previously in applyGiftCard.\r\n */\r\nexport function parseGiftCardApplication(raw: unknown): ApplyGiftCardResult {\r\n  const data = (raw ?? {}) as ApplyGiftCardApiResponse;\r\n\r\n  const giftCardAmountDollars = (data.appliedAmount ?? 0) / 100;\r\n  const residualDollars = data.remainingTotal ?? 0;\r\n  // Full coverage when the card covers everything (no residual to charge).\r\n  const fullyCovered = data.fullyCoveredByCard ?? residualDollars <= 0;\r\n  const requiresStripe = !fullyCovered;\r\n\r\n  return {\r\n    valid: true,\r\n    giftCardCode: data.giftCardCode,\r\n    giftCardAmount: giftCardAmountDollars,\r\n    giftCardBalance: data.giftCardBalance,\r\n    paymentType: fullyCovered ? 'gift_card_only' : 'split',\r\n    stripeAmount: residualDollars,\r\n    orderTotal: giftCardAmountDollars + residualDollars,\r\n    requiresStripe,\r\n  };\r\n}\r\n","import { AppError, asVenueId } from '@getmicdrop/svelte-components';\r\nimport type { VenueId } from '@getmicdrop/svelte-components';\r\n/**\r\n * Gift Card Purchase API Client\r\n * Handles gift card purchase creation, cart application/removal,\r\n * and gift-card-only checkout completion.\r\n */\r\n\r\nimport { createLogger } from '@getmicdrop/svelte-components/utils/logger';\r\nimport { getOrdersV2Url, apiPost, apiDelete } from './client.js';\r\nimport { ok, err, errNotFound, type Result } from './result.js';\r\nimport { parseGiftCardApplication } from './transformers/giftCard.js';\r\nimport { getAttribution } from '../tracking/index.js';\r\nconst logger = createLogger('VC');\r\n\r\nconst API_BASE_URL = 'https://get-micdrop.com';\r\nconst ORDERS_V2_URL = `${API_BASE_URL}/api/orders/v2`;\r\nconst ORDERS_V2_PUBLIC = () => getOrdersV2Url();\r\n\r\n/**\r\n * Request payload for creating a gift card purchase\r\n */\r\nexport interface GiftCardPurchaseRequest {\r\n  // Public boundary — accept plain number too so external TypeScript callers\r\n  // that pass `venueId: 1` keep compiling. Runtime serializes a number either\r\n  // way. Internal call sites pass branded VenueId via asVenueId().\r\n  venueId: number | VenueId;\r\n  amount: number; // In cents (e.g., 5000 = $50.00)\r\n  recipientEmail: string;\r\n  recipientName: string;\r\n  personalMessage?: string;\r\n  purchaserEmail: string;\r\n  purchaserName: string;\r\n  scheduledDeliveryAt?: string | null; // ISO date string or null for immediate\r\n}\r\n\r\n/**\r\n * Response from gift card purchase creation\r\n */\r\nexport interface GiftCardPurchaseResponse {\r\n  giftCardUUID: string;\r\n  clientSecret: string;\r\n  amount: number;\r\n  stripePublishableKey: string;\r\n}\r\n\r\n/**\r\n * Venue information needed for gift card purchase\r\n */\r\nexport interface VenueInfo {\r\n  id: VenueId;\r\n  name: string;\r\n  slug: string;\r\n  stripePublishableKey?: string;\r\n}\r\n\r\n/**\r\n * Create a gift card purchase and get Stripe client secret\r\n * @param req - Gift card purchase request details\r\n * @returns Gift card UUID and Stripe client secret, or null on failure\r\n */\r\nexport async function createGiftCardPurchase(\r\n  req: GiftCardPurchaseRequest\r\n): Promise<GiftCardPurchaseResponse | null> {\r\n  try {\r\n    const apiResult = await apiPost<GiftCardPurchaseResponse>(\r\n      `${ORDERS_V2_URL}/gift-cards`,\r\n      req\r\n    );\r\n\r\n    if (!apiResult.success) {\r\n      logger.error('Gift card purchase creation failed:', apiResult.error);\r\n      throw new AppError(\r\n        apiResult.error || 'Failed to create gift card purchase',\r\n        'lib/api/gift-cards/createGiftCardPurchase'\r\n      );\r\n    }\r\n\r\n    logger.debug('Gift card purchase created:', apiResult.data);\r\n    return apiResult.data!;\r\n  } catch (error) {\r\n    logger.error('Gift card purchase creation error:', error);\r\n    return null;\r\n  }\r\n}\r\n\r\n/**\r\n * Result shape consumed by the Checkout component / GiftCardInput.\r\n * Monetary fields here are in DOLLARS (formatCurrency-ready).\r\n */\r\nexport interface ApplyGiftCardResult {\r\n  valid: boolean;\r\n  giftCardCode?: string;\r\n  giftCardAmount?: number; // dollars applied from the gift card\r\n  giftCardBalance?: number; // cents (raw balance, not displayed via formatCurrency)\r\n  paymentType?: string; // 'gift_card_only' (full) | 'split' (partial)\r\n  stripeAmount?: number; // dollars the card must still cover (residual)\r\n  orderTotal?: number; // dollars (gift card + residual)\r\n  requiresStripe?: boolean;\r\n  error?: string;\r\n}\r\n\r\n/**\r\n * Apply a gift card to a cart.\r\n */\r\nexport async function applyGiftCard(\r\n  cartId: string,\r\n  code: string\r\n): Promise<ApplyGiftCardResult> {\r\n  try {\r\n    const apiResult = await apiPost<unknown>(\r\n      `${ORDERS_V2_PUBLIC()}/cart/${cartId}/apply-gift-card`,\r\n      { giftCardCode: code }\r\n    );\r\n\r\n    if (!apiResult.success) {\r\n      if (apiResult.statusCode === 429) {\r\n        return {\r\n          valid: false,\r\n          error: 'Too many attempts. Please wait a moment and try again.',\r\n        };\r\n      }\r\n      logger.error('Apply gift card failed:', apiResult.error);\r\n      return {\r\n        valid: false,\r\n        error: apiResult.error || 'Invalid gift card code',\r\n      };\r\n    }\r\n\r\n    // Success-path mapping (cents->dollars, fullyCovered/requiresStripe) lives\r\n    // in parseGiftCardApplication — byte-identical to the inline mapper this\r\n    // replaced. The cents->dollars conversion stays in the parser (unit\r\n    // contract documented there). Error/429/network branches stay here.\r\n    return parseGiftCardApplication(apiResult.data);\r\n  } catch (err) {\r\n    logger.error('applyGiftCard error:', err);\r\n    return {\r\n      valid: false,\r\n      error: 'Network error. Please check your connection and try again.',\r\n    };\r\n  }\r\n}\r\n\r\nexport interface RemoveGiftCardResult {\r\n  success: boolean;\r\n  error?: string;\r\n}\r\n\r\n/**\r\n * Remove a gift card from a cart. Pass a `code` to remove a single stacked card;\r\n * omit it to remove all applied gift cards.\r\n */\r\nexport async function removeGiftCard(\r\n  cartId: string,\r\n  code?: string\r\n): Promise<RemoveGiftCardResult> {\r\n  try {\r\n    const apiResult = await apiDelete(\r\n      `${ORDERS_V2_PUBLIC()}/cart/${cartId}/gift-card`,\r\n      code ? { body: JSON.stringify({ giftCardCode: code }) } : undefined\r\n    );\r\n\r\n    if (!apiResult.success) {\r\n      logger.error('Remove gift card failed:', apiResult.error);\r\n      return {\r\n        success: false,\r\n        error: apiResult.error || 'Failed to remove gift card',\r\n      };\r\n    }\r\n\r\n    return { success: true };\r\n  } catch (err) {\r\n    logger.error('removeGiftCard error:', err);\r\n    return {\r\n      success: false,\r\n      error: 'Network error. Please check your connection and try again.',\r\n    };\r\n  }\r\n}\r\n\r\nexport interface CompleteGiftCardPaymentInput {\r\n  firstName: string;\r\n  lastName: string;\r\n  email: string;\r\n  phoneNumber?: string | null;\r\n  mailingList?: boolean;\r\n}\r\n\r\nexport interface CompleteGiftCardPaymentResult {\r\n  success: boolean;\r\n  orderId?: string;\r\n  confirmationNumber?: string;\r\n  error?: string;\r\n}\r\n\r\n/**\r\n * Complete a gift-card-only payment (no Stripe required when balance >= total).\r\n */\r\nexport async function completeGiftCardPayment(\r\n  cartId: string,\r\n  customerDetails: CompleteGiftCardPaymentInput\r\n): Promise<CompleteGiftCardPaymentResult> {\r\n  try {\r\n    const apiResult = await apiPost<{\r\n      orderId?: string;\r\n      confirmationNumber?: string;\r\n    }>(`${ORDERS_V2_PUBLIC()}/cart/${cartId}/complete-gift-card-payment`, {\r\n      firstName: customerDetails.firstName,\r\n      lastName: customerDetails.lastName,\r\n      email: customerDetails.email,\r\n      phoneNumber: customerDetails.phoneNumber || null,\r\n      mailingList: customerDetails.mailingList || false,\r\n      // Order attribution (MISSION-TRUTH C2) — gift-card-only orders take this\r\n      // finalize endpoint, so attribution must ride here too; additive.\r\n      attribution: getAttribution(),\r\n    });\r\n\r\n    if (!apiResult.success) {\r\n      logger.error('Complete gift card payment failed:', apiResult.error);\r\n      return {\r\n        success: false,\r\n        error: apiResult.error || 'Failed to complete payment',\r\n      };\r\n    }\r\n\r\n    return {\r\n      success: true,\r\n      orderId: apiResult.data?.orderId,\r\n      confirmationNumber: apiResult.data?.confirmationNumber,\r\n    };\r\n  } catch (err) {\r\n    logger.error('completeGiftCardPayment error:', err);\r\n    return {\r\n      success: false,\r\n      error: 'Network error. Please check your connection and try again.',\r\n    };\r\n  }\r\n}\r\n\r\n/**\r\n * Fetch venue information by slug, discriminating a real 404 from a transient\r\n * failure.\r\n *\r\n * The gift-card SSR loader (`v/[venueSlug]/gift-cards/purchase/+page.ts`) gates\r\n * its HTTP status on this: only an upstream 404 (`errNotFound`) becomes a page\r\n * 404. A 5xx, network error, or timeout is `err(...)` (transient) so the loader\r\n * answers 503/retry rather than a permanent-looking 404 on a backend blip.\r\n *\r\n * @param slug - Venue slug\r\n * @param customFetch - Optional custom fetch function (for SSR)\r\n * @returns `ok(venue)`, `errNotFound(...)` for a real 404, or `err(...)` transient\r\n */\r\nexport async function getVenueBySlugResult(\r\n  slug: string,\r\n  customFetch: typeof fetch = fetch\r\n): Promise<Result<VenueInfo>> {\r\n  try {\r\n    // @fetch-escape: customFetch injection for SSR context — cannot use apiGet (caller-supplied fetch instance)\r\n    const response = await customFetch(\r\n      `${API_BASE_URL}/api/v2/public/venues/slug/${slug}`\r\n    );\r\n\r\n    if (!response.ok) {\r\n      // 404 = this venue slug genuinely does not exist. Anything else is a\r\n      // transient upstream failure — surface as retryable, not gone.\r\n      if (response.status === 404) {\r\n        return errNotFound('venue_not_found');\r\n      }\r\n      logger.error(`Failed to fetch venue by slug: ${response.status}`);\r\n      return err('venue_fetch_failed');\r\n    }\r\n\r\n    const venue = await response.json();\r\n    // A 200 with a falsy/null body means the venue is absent — treat it as a\r\n    // real 404 (matches the old behaviour where `venue.id` on null threw and\r\n    // collapsed to a not-found), rather than deref-crashing below.\r\n    if (!venue) {\r\n      return errNotFound('venue_not_found');\r\n    }\r\n    return ok({\r\n      id: asVenueId(venue.id),\r\n      name: venue.name,\r\n      slug: venue.slug,\r\n      stripePublishableKey: venue.stripePublishableKey,\r\n    });\r\n  } catch (error) {\r\n    // Network error / timeout — transient by definition, never a 404.\r\n    logger.error('Error fetching venue by slug:', error);\r\n    return err('venue_fetch_failed');\r\n  }\r\n}\r\n\r\n/**\r\n * Fetch venue information by slug\r\n *\r\n * Fail-open adapter over {@link getVenueBySlugResult}: returns `null` on any\r\n * failure. SSR loaders that need 404-vs-503 use the Result variant.\r\n *\r\n * @param slug - Venue slug\r\n * @returns Venue info or null on failure\r\n */\r\nexport async function getVenueBySlug(\r\n  slug: string,\r\n  customFetch: typeof fetch = fetch\r\n): Promise<VenueInfo | null> {\r\n  const result = await getVenueBySlugResult(slug, customFetch);\r\n  return result.ok ? result.data : null;\r\n}\r\n","/**\r\n * Waitlist API\r\n *\r\n * Endpoints for the sold-out-event waitlist flow.\r\n */\r\n\r\nimport { createLogger } from '@getmicdrop/svelte-components/utils/logger';\r\nimport { apiPost, apiGet } from './client.js';\r\n\r\nconst logger = createLogger('VC');\r\n\r\nexport interface JoinWaitlistResult {\r\n  success: boolean;\r\n  // No position — opaque to the customer (decision #2/#20).\r\n  updated?: boolean;\r\n  error?: string;\r\n}\r\n\r\nconst WAITLIST_ERROR_MESSAGES: Record<string, string> = {\r\n  waitlist_not_enabled: 'Waitlist is not available for this event',\r\n  event_not_found: 'Event not found',\r\n  invalid_phone: 'Please enter a valid phone number',\r\n  already_purchased: 'You have already purchased tickets for this event',\r\n  phone_already_registered: 'This phone number is already on the waitlist',\r\n};\r\n\r\n/**\r\n * Join the waitlist for a sold-out event.\r\n */\r\nexport async function joinWaitlist(\r\n  eventId: string | number,\r\n  email: string,\r\n  phone: string | null = null,\r\n  smsOptIn = false\r\n): Promise<JoinWaitlistResult> {\r\n  try {\r\n    const body: Record<string, unknown> = { email };\r\n    if (phone && smsOptIn) {\r\n      body.phone = phone;\r\n      body.smsOptIn = true;\r\n    }\r\n\r\n    const apiResult = await apiPost<{ updated?: boolean }>(\r\n      `/events/${eventId}/waitlist`,\r\n      body\r\n    );\r\n\r\n    if (!apiResult.success) {\r\n      logger.error('Join waitlist failed:', apiResult.error);\r\n      return {\r\n        success: false,\r\n        error:\r\n          WAITLIST_ERROR_MESSAGES[apiResult.error ?? ''] ||\r\n          'Failed to join waitlist. Please try again.',\r\n      };\r\n    }\r\n\r\n    return {\r\n      success: true,\r\n      updated: apiResult.data?.updated,\r\n    };\r\n  } catch (err) {\r\n    logger.error('joinWaitlist error:', err);\r\n    return {\r\n      success: false,\r\n      error: 'Network error. Please check your connection and try again.',\r\n    };\r\n  }\r\n}\r\n\r\nexport interface ValidateWaitlistTokenResult {\r\n  valid: boolean;\r\n  eventId: number;\r\n  /**\r\n   * Present only when `valid` is false. One of the server's failure enums:\r\n   * invalid_token | token_expired | token_used | email_mismatch |\r\n   * inventory_exhausted. Callers map it to friendly copy.\r\n   */\r\n  errorCode?: string;\r\n}\r\n\r\n/**\r\n * Validate a tokenized waitlist unlock link (decisions #3/#22/#36).\r\n *\r\n * A waitlister receives an email link carrying an opaque token bound to their\r\n * email. Presenting a valid (token, email) pair unlocks the normal Buy Tickets\r\n * flow on an otherwise-gated sold-out event, routing freed inventory to the\r\n * notified buyer. Failures are surfaced (not thrown) so the page can show a\r\n * non-blocking notice while keeping the event in its gated waitlist state.\r\n */\r\nexport async function validateWaitlistToken(\r\n  token: string,\r\n  email: string\r\n): Promise<ValidateWaitlistTokenResult> {\r\n  try {\r\n    const apiResult = await apiPost<{ eventId?: number }>(\r\n      `/checkout/validate-token/${token}`,\r\n      { email }\r\n    );\r\n\r\n    if (!apiResult.success) {\r\n      return {\r\n        valid: false,\r\n        eventId: 0,\r\n        errorCode: apiResult.error,\r\n      };\r\n    }\r\n\r\n    return {\r\n      valid: true,\r\n      eventId: apiResult.data?.eventId ?? 0,\r\n    };\r\n  } catch (err) {\r\n    logger.error('validateWaitlistToken error:', err);\r\n    return { valid: false, eventId: 0, errorCode: 'invalid_token' };\r\n  }\r\n}\r\n\r\nexport interface WaitlistStatusResult {\r\n  waitlistEnabled: boolean;\r\n  soldOut: boolean;\r\n  /**\r\n   * When gated, the public event page should present \"Sold Out — join the\r\n   * waitlist\" to non-members even if tickets are technically available, and only\r\n   * a valid tokenized link unlocks checkout (reconciled decision #3).\r\n   */\r\n  gated: boolean;\r\n}\r\n\r\n/**\r\n * Fetch an event's waitlist gating status. Returns a non-gated default if the\r\n * request fails so the page degrades to normal behavior.\r\n */\r\nexport async function getWaitlistStatus(\r\n  eventId: string | number\r\n): Promise<WaitlistStatusResult> {\r\n  const fallback: WaitlistStatusResult = {\r\n    waitlistEnabled: false,\r\n    soldOut: false,\r\n    gated: false,\r\n  };\r\n  try {\r\n    const apiResult = await apiGet<{ waitlistEnabled: boolean; soldOut: boolean; gated: boolean }>(\r\n      `/events/${eventId}/waitlist/status`\r\n    );\r\n    if (!apiResult.success) return fallback;\r\n    return {\r\n      waitlistEnabled: !!apiResult.data?.waitlistEnabled,\r\n      soldOut: !!apiResult.data?.soldOut,\r\n      gated: !!apiResult.data?.gated,\r\n    };\r\n  } catch (err) {\r\n    logger.error('getWaitlistStatus error:', err);\r\n    return fallback;\r\n  }\r\n}\r\n","/**\r\n * CTA state computation.\r\n *\r\n * Pure function (no I/O) — derives the buy-button state from an event and\r\n * its ticket list. Lives in `api/` because it is the canonical, single\r\n * source of truth that replaces the duplicate implementations previously\r\n * scattered across utils/api.js, core/api-client.js, and a few components.\r\n */\r\n\r\n// Import from the specific subpath (not the bare entry) so the api/\r\n// bundle doesn't pull in the entire SC component graph (Accordion etc.)\r\n// and break `npm run build:api`. `formatLocal` is the sanctioned browser-local\r\n// (no-timezone) Intl label home — these \"On sale …\" countdown labels render in\r\n// the viewer's own clock, so no venue timezone applies. The datetime subpath is\r\n// leaf/component-free; only `formatLocal` (→ Intl) is pulled, no date-fns.\r\nimport { formatLocal } from '@getmicdrop/svelte-components/datetime';\r\n// Hidden-ticket predicate from the `/transforms` leaf subpath (no component\r\n// graph, so `npm run build:api` stays clean). SC's copy was pinned FROM this\r\n// body (dedup #392) and additionally honours the backend-resolved\r\n// `effectiveSales*` window for relative-offset tickets (#388). Imported (not\r\n// just re-exported) so it stays in local scope for `computeCtaState` below.\r\nimport { isHiddenTicketPurchasable } from '@getmicdrop/svelte-components/transforms';\r\n\r\nexport interface CtaStateOptions {\r\n  cancelled?: boolean;\r\n  isRegistration?: boolean;\r\n  waitlistEnabled?: boolean;\r\n}\r\n\r\nexport interface CtaState {\r\n  text: string;\r\n  disabled: boolean;\r\n  reason:\r\n    | 'cancelled'\r\n    | 'event_past'\r\n    | 'no_tickets'\r\n    | 'available'\r\n    | 'coming_soon'\r\n    | 'sold_out_waitlist'\r\n    | 'sold_out'\r\n    | 'sales_ended'\r\n    | 'hidden_only';\r\n}\r\n\r\ninterface CtaEvent {\r\n  startDateTime?: string | null;\r\n  endDateTime?: string | null;\r\n  hasPurchasableHiddenTickets?: boolean;\r\n}\r\n\r\ninterface CtaTicket {\r\n  salesChannel?: number;\r\n  salesBegin?: string | null;\r\n  salesStart?: string | null;\r\n  saleBegin?: string | null;\r\n  onSaleStart?: string | null;\r\n  salesEnd?: string | null;\r\n  saleEnd?: string | null;\r\n  onSaleEnd?: string | null;\r\n  remainingCapacity?: number | null;\r\n  quantityRemaining?: number | null;\r\n  quantity?: number | null;\r\n  soldOut?: boolean;\r\n  isHidden?: boolean;\r\n  visibility?: number;\r\n}\r\n\r\n// A hidden ticket (visibility>=2 or isHidden) that's still on sale drives the\r\n// \"Sales ended\" / \"Sold out\" / \"No tickets\" → \"Get tickets\" override. The user\r\n// just needs the promo code to see it. Re-exported from the SC canonical so\r\n// existing consumers (cta.test.ts, computeCtaState) keep their import path while\r\n// the single-source body lives in SC (dedup #392).\r\nexport { isHiddenTicketPurchasable };\r\n\r\nexport function computeCtaState(\r\n  event: CtaEvent,\r\n  tickets: CtaTicket[] | undefined,\r\n  options: CtaStateOptions = {}\r\n): CtaState {\r\n  const {\r\n    cancelled = false,\r\n    isRegistration = false,\r\n    waitlistEnabled = false,\r\n  } = options;\r\n  const now = new Date();  // @datetime-escape: absolute-instant compare of a UTC-Z field vs now — tz-independent\r\n\r\n  if (cancelled)\r\n    return { text: 'Cancelled', disabled: true, reason: 'cancelled' };\r\n\r\n  const eventEnd = event.endDateTime || event.startDateTime;\r\n  // @datetime-escape: absolute-instant compare of a UTC-Z field vs now — tz-independent\r\n  if (eventEnd && new Date(eventEnd) < now) {\r\n    return { text: 'Sales ended', disabled: true, reason: 'event_past' };\r\n  }\r\n\r\n  const hiddenAvailable =\r\n    event?.hasPurchasableHiddenTickets === true ||\r\n    (Array.isArray(tickets) && tickets.some(isHiddenTicketPurchasable));\r\n\r\n  const publicTickets = (tickets || []).filter(\r\n    t =>\r\n      t.salesChannel !== 2 &&\r\n      !t.isHidden &&\r\n      !(typeof t.visibility === 'number' && t.visibility >= 2)\r\n  );\r\n  if (publicTickets.length === 0) {\r\n    if (hiddenAvailable) {\r\n      return {\r\n        text: isRegistration ? 'Reserve a spot' : 'Get tickets',\r\n        disabled: false,\r\n        reason: 'hidden_only',\r\n      };\r\n    }\r\n    return {\r\n      text: 'No tickets available',\r\n      disabled: true,\r\n      reason: 'no_tickets',\r\n    };\r\n  }\r\n\r\n  let hasPurchasable = false;\r\n  let allSoldOut = true;\r\n  let allComingSoon = true;\r\n  let earliestSalesStart: Date | null = null;\r\n\r\n  for (const ticket of publicTickets) {\r\n    const salesBegin =\r\n      ticket.salesBegin ||\r\n      ticket.salesStart ||\r\n      ticket.saleBegin ||\r\n      ticket.onSaleStart;\r\n    const salesEnd = ticket.salesEnd || ticket.saleEnd || ticket.onSaleEnd;\r\n    const remaining =\r\n      ticket.remainingCapacity ?? ticket.quantityRemaining ?? ticket.quantity;\r\n    const isSoldOut =\r\n      ticket.soldOut ||\r\n      (remaining !== null && remaining !== undefined && remaining <= 0);\r\n\r\n    const isScheduled = salesBegin ? new Date(salesBegin) > now : false;  // @datetime-escape: absolute-instant compare of a UTC-Z field vs now — tz-independent\r\n    const hasSalesEnded = salesEnd ? new Date(salesEnd) < now : false;  // @datetime-escape: absolute-instant compare of a UTC-Z field vs now — tz-independent\r\n    const isPurchasable = !isSoldOut && !isScheduled && !hasSalesEnded;\r\n\r\n    if (isPurchasable) hasPurchasable = true;\r\n    if (!isSoldOut) allSoldOut = false;\r\n    if (!isScheduled) allComingSoon = false;\r\n\r\n    if (isScheduled && salesBegin && !isSoldOut && !hasSalesEnded) {\r\n      const startDate = new Date(salesBegin);  // @datetime-escape: absolute-instant compare of a UTC-Z field vs now — tz-independent\r\n      if (!earliestSalesStart || startDate < earliestSalesStart) {\r\n        earliestSalesStart = startDate;\r\n      }\r\n    }\r\n  }\r\n\r\n  if (hasPurchasable) {\r\n    return {\r\n      text: isRegistration ? 'Reserve a spot' : 'Get tickets',\r\n      disabled: false,\r\n      reason: 'available',\r\n    };\r\n  }\r\n\r\n  if (allComingSoon && earliestSalesStart) {\r\n    if (hiddenAvailable) {\r\n      return {\r\n        text: isRegistration ? 'Reserve a spot' : 'Get tickets',\r\n        disabled: false,\r\n        reason: 'hidden_only',\r\n      };\r\n    }\r\n    const diffMs = earliestSalesStart.getTime() - now.getTime();\r\n    const diffHours = diffMs / (1000 * 60 * 60);\r\n    const diffDays = diffMs / (1000 * 60 * 60 * 24);\r\n    // Viewer-local time label (\"8:00 PM\") — the shape shared by the <24h branch\r\n    // and the >7d composite. Matches the deprecated `formatTime(d,{hour12:true})`\r\n    // defaults-merge exactly.\r\n    const timeLabel = (d: Date) =>\r\n      formatLocal(\r\n        d,\r\n        { hour: 'numeric', minute: '2-digit', hour12: true },\r\n        'en-US'\r\n      );\r\n    let dateText: string;\r\n    if (diffHours < 24) {\r\n      dateText = timeLabel(earliestSalesStart);\r\n    } else if (diffDays <= 7) {\r\n      dateText = formatLocal(\r\n        earliestSalesStart,\r\n        { weekday: 'short', hour: 'numeric', minute: '2-digit', hour12: true },\r\n        'en-US'\r\n      );\r\n    } else {\r\n      const d = formatLocal(\r\n        earliestSalesStart,\r\n        { month: 'short', day: 'numeric' },\r\n        'en-US'\r\n      );\r\n      dateText = `${d} ${timeLabel(earliestSalesStart)}`;\r\n    }\r\n    return {\r\n      text: `On sale ${dateText}`,\r\n      disabled: true,\r\n      reason: 'coming_soon',\r\n    };\r\n  }\r\n\r\n  if (allSoldOut) {\r\n    if (hiddenAvailable) {\r\n      return {\r\n        text: isRegistration ? 'Reserve a spot' : 'Get tickets',\r\n        disabled: false,\r\n        reason: 'hidden_only',\r\n      };\r\n    }\r\n    if (waitlistEnabled) {\r\n      return {\r\n        text: 'Join Waitlist',\r\n        disabled: false,\r\n        reason: 'sold_out_waitlist',\r\n      };\r\n    }\r\n    return { text: 'Sold out', disabled: true, reason: 'sold_out' };\r\n  }\r\n\r\n  if (hiddenAvailable) {\r\n    return {\r\n      text: isRegistration ? 'Reserve a spot' : 'Get tickets',\r\n      disabled: false,\r\n      reason: 'hidden_only',\r\n    };\r\n  }\r\n\r\n  return { text: 'Sales ended', disabled: true, reason: 'sales_ended' };\r\n}\r\n","// @currency-escape: widget-intentional.\r\nimport { asUSD, formatCurrency } from '@getmicdrop/svelte-components';\r\n/**\r\n * Order Transformer\r\n *\r\n * Normalizes API order responses to a consistent format.\r\n * Handles variations in field names between different API versions.\r\n *\r\n * Wire->domain boundary: one parseOrder(raw: unknown) entry point.\r\n * The wire field shapes (ApiOrder / ApiTicket) are named only inside this\r\n * module and never escape it.\r\n *\r\n * IMPLEMENTATION NOTE -- why no Zod schema here:\r\n * The old mapper used `||` for id and ALL money fields (totalAmount, serviceFeesAmount,\r\n * taxAmount, discount -- 0 falls through to next variant). The old mapper did NOT use\r\n * `??` for money; that was a bug in the first Zod-based rewrite. Replicated verbatim\r\n * for byte-identical behavior. Throw behavior is identical to the old mapper: null/\r\n * null/undefined input or a non-array (the latter throws on .map) tickets field throws exactly as the old code did.\r\n * Callers always pass real wire objects.\r\n */\r\n\r\nimport type { Order, PurchasedTicket } from '../types.js';\r\n\r\n// ── Wire shapes (internal -- never exported) ─────────────────────────────\r\ninterface ApiTicket {\r\n  uuid?: string;\r\n  id?: number;\r\n  ID?: number;\r\n  ticketNumber?: string;\r\n  ticket_number?: string;\r\n  orderId?: string | number;\r\n  order_id?: string | number;\r\n  attendeeFirstName?: string;\r\n  attendee_first_name?: string;\r\n  firstName?: string;\r\n  attendeeLastName?: string;\r\n  attendee_last_name?: string;\r\n  lastName?: string;\r\n  attendeeEmail?: string;\r\n  attendee_email?: string;\r\n  email?: string;\r\n  ticketName?: string;\r\n  ticket_name?: string;\r\n  name?: string;\r\n  ticketTypeId?: number;\r\n  ticket_type_id?: number;\r\n  purchasePrice?: number;\r\n  purchase_price?: number;\r\n  price?: number;\r\n  status?: string;\r\n  checkedIn?: boolean;\r\n  checked_in?: boolean;\r\n  checkedInAt?: string;\r\n  checked_in_at?: string;\r\n}\r\n\r\ninterface ApiOrder {\r\n  uuid?: string;\r\n  id?: number;\r\n  ID?: number;\r\n  customerEmail?: string;\r\n  email?: string;\r\n  customer_email?: string;\r\n  customerFirstName?: string;\r\n  firstName?: string;\r\n  first_name?: string;\r\n  customerLastName?: string;\r\n  lastName?: string;\r\n  last_name?: string;\r\n  status?: string;\r\n  totalAmount?: number;\r\n  total?: number;\r\n  total_amount?: number;\r\n  subtotal?: number;\r\n  serviceFeesAmount?: number;\r\n  serviceFee?: number;\r\n  service_fee?: number;\r\n  service_fees_amount?: number;\r\n  taxAmount?: number;\r\n  tax?: number;\r\n  tax_amount?: number;\r\n  discount?: number;\r\n  paymentIntentId?: string;\r\n  payment_intent_id?: string;\r\n  paymentMethod?: string;\r\n  payment_method?: string;\r\n  purchasedTickets?: any[];\r\n  tickets?: any[];\r\n  purchased_tickets?: any[];\r\n  createdAt?: string;\r\n  created_at?: string;\r\n  updatedAt?: string;\r\n  updated_at?: string;\r\n}\r\n\r\n/**\r\n * Parse a raw purchased-ticket payload into the normalized PurchasedTicket domain shape.\r\n *\r\n * Wire shape (ApiTicket) is contained inside this module. Uses `||` chains matching\r\n * the old transformer exactly -- byte-identical to previous behavior. Throw behavior\r\n * preserved from old mapper: null/undefined input (property access on a nullish value) throws as the old code did.\r\n */\r\nexport function parsePurchasedTicket(raw: unknown): PurchasedTicket {\r\n  const apiTicket = raw as ApiTicket;\r\n  return {\r\n    uuid: apiTicket.uuid || String(apiTicket.id || apiTicket.ID || ''),\r\n    id: apiTicket.id || apiTicket.ID,\r\n    ticketNumber: apiTicket.ticketNumber || apiTicket.ticket_number,\r\n    orderId: apiTicket.orderId || apiTicket.order_id,\r\n    attendeeFirstName:\r\n      apiTicket.attendeeFirstName ||\r\n      apiTicket.attendee_first_name ||\r\n      apiTicket.firstName,\r\n    attendeeLastName:\r\n      apiTicket.attendeeLastName ||\r\n      apiTicket.attendee_last_name ||\r\n      apiTicket.lastName,\r\n    attendeeEmail:\r\n      apiTicket.attendeeEmail || apiTicket.attendee_email || apiTicket.email,\r\n    ticketName:\r\n      apiTicket.ticketName || apiTicket.ticket_name || apiTicket.name || '',\r\n    ticketTypeId: apiTicket.ticketTypeId || apiTicket.ticket_type_id,\r\n    purchasePrice:\r\n      apiTicket.purchasePrice ||\r\n      apiTicket.purchase_price ||\r\n      apiTicket.price ||\r\n      0,\r\n    status: apiTicket.status,\r\n    checkedIn: apiTicket.checkedIn || apiTicket.checked_in,\r\n    checkedInAt: apiTicket.checkedInAt || apiTicket.checked_in_at,\r\n  };\r\n}\r\n\r\n/**\r\n * Parse a raw order payload into the normalized Order domain shape.\r\n *\r\n * Wire shape (ApiOrder) is contained inside this module and never escapes it.\r\n * Uses `||` chains matching the old transformer exactly (all money fields use `||`,\r\n * not `??` -- 0-valued amounts fall through to next variant, same as old) --\r\n * byte-identical to previous behavior. Throw behavior preserved from old mapper:\r\n * null/undefined input (property access on a nullish value) or a non-array tickets field throws as the old code did.\r\n */\r\nexport function parseOrder(raw: unknown): Order {\r\n  const apiOrder = raw as ApiOrder;\r\n\r\n  // Extract tickets array from various possible field names\r\n  const rawTickets =\r\n    apiOrder.purchasedTickets ||\r\n    apiOrder.tickets ||\r\n    apiOrder.purchased_tickets ||\r\n    [];\r\n\r\n  return {\r\n    uuid: apiOrder.uuid || String(apiOrder.id || apiOrder.ID || ''),\r\n    id: apiOrder.id || apiOrder.ID,\r\n    customerEmail:\r\n      apiOrder.customerEmail || apiOrder.email || apiOrder.customer_email || '',\r\n    customerFirstName:\r\n      apiOrder.customerFirstName || apiOrder.firstName || apiOrder.first_name,\r\n    customerLastName:\r\n      apiOrder.customerLastName || apiOrder.lastName || apiOrder.last_name,\r\n    status: apiOrder.status || 'unknown',\r\n    totalAmount:\r\n      apiOrder.totalAmount || apiOrder.total || apiOrder.total_amount || 0,\r\n    subtotal: apiOrder.subtotal,\r\n    serviceFeesAmount:\r\n      apiOrder.serviceFeesAmount ||\r\n      apiOrder.serviceFee ||\r\n      apiOrder.service_fee ||\r\n      apiOrder.service_fees_amount ||\r\n      0,\r\n    taxAmount: apiOrder.taxAmount || apiOrder.tax || apiOrder.tax_amount || 0,\r\n    discount: apiOrder.discount || 0,\r\n    paymentIntentId: apiOrder.paymentIntentId || apiOrder.payment_intent_id,\r\n    paymentMethod: apiOrder.paymentMethod || apiOrder.payment_method,\r\n    purchasedTickets: rawTickets.map(parsePurchasedTicket),\r\n    createdAt: apiOrder.createdAt || apiOrder.created_at,\r\n    updatedAt: apiOrder.updatedAt || apiOrder.updated_at,\r\n  };\r\n}\r\n\r\n/**\r\n * Back-compat alias -- existing call sites using transformTicket continue\r\n * to work unchanged.\r\n */\r\nexport const transformTicket = parsePurchasedTicket;\r\n\r\n/**\r\n * Back-compat alias -- existing call sites using transformOrder(apiOrder)\r\n * continue to work unchanged.\r\n */\r\nexport const transformOrder = parseOrder;\r\n\r\n/**\r\n * Transform order for display on success page.\r\n *\r\n * Returns a simplified object with display-ready values.\r\n */\r\nexport function transformOrderForDisplay(raw: unknown): {\r\n  orderId: string;\r\n  displayOrderId: string;\r\n  email: string;\r\n  customerName: string;\r\n  total: number;\r\n  formattedTotal: string;\r\n  ticketCount: number;\r\n  status: string;\r\n} {\r\n  const order = parseOrder(raw);\r\n  const displayId = order.uuid.split('-')[0] || order.uuid;\r\n  // Coerce before the finite check -- totalAmount is wire data typed as\r\n  // `number` but not schema-validated (see the no-Zod note above), and the\r\n  // old bare `as USD` cast tolerated a numeric-string wire value via the\r\n  // `/` operator's implicit coercion. `Number()` preserves that tolerance;\r\n  // a plain `Number.isFinite(order.totalAmount)` would not.\r\n  const rawTotal = Number(order.totalAmount);\r\n\r\n  return {\r\n    orderId: order.uuid,\r\n    displayOrderId: displayId,\r\n    email: order.customerEmail,\r\n    customerName: [order.customerFirstName, order.customerLastName]\r\n      .filter(Boolean)\r\n      .join(' '),\r\n    total: order.totalAmount,\r\n    formattedTotal: `${formatCurrency(asUSD(Number.isFinite(rawTotal) ? rawTotal / 100 : 0))}`,\r\n    ticketCount: order.purchasedTickets.length,\r\n    status: order.status,\r\n  };\r\n}\r\n","/**\r\n * Event Transformer\r\n *\r\n * Normalizes API event responses to a consistent format.\r\n * Handles image URL resolution and CTA state calculation.\r\n *\r\n * Wire->domain boundary: one parse<Entity>(raw: unknown) per entity.\r\n * The wire field shape (ApiEvent / ApiTicket interface) is named only inside\r\n * this module and never escapes it. Above the boundary only Event / AvailableTicket.\r\n *\r\n * IMPLEMENTATION NOTE — why no Zod schema here:\r\n * The old mappers used `||` (falsy fallthrough) for every primitive field, so\r\n * {venueId:0, venue_id:7} -> 7, {isHidden:false, is_hidden:true} -> true, etc.\r\n * Zod z.number()/z.boolean() would throw on wrong primitive types; firstDefined\r\n * (??) would silently mis-select 0/false values. This implementation replicates\r\n * the old `||` chains verbatim, wrapped in a typed parse<Entity>(raw: unknown)\r\n * boundary. Throw behavior is identical to the old mapper: null/undefined input (property access on a nullish value)\r\n * or a non-array where .map is called throws exactly as the old code did. Callers\r\n * always pass real wire objects.\r\n */\r\n\r\nimport { resolveImageUrl } from '@getmicdrop/svelte-components';\r\nimport type { Event, AvailableTicket } from '../types.js';\r\n\r\n// ── Wire shape (internal — never exported) ───────────────────────────────\r\ninterface ApiTicket {\r\n  id?: number;\r\n  ID?: number;\r\n  name?: string;\r\n  ticketName?: string;\r\n  description?: string;\r\n  price?: number;\r\n  quantity?: number;\r\n  totalQuantity?: number;\r\n  quantitySold?: number;\r\n  quantity_sold?: number;\r\n  quantityAvailable?: number;\r\n  quantity_available?: number;\r\n  minPerOrder?: number;\r\n  min_per_order?: number;\r\n  maxPerOrder?: number;\r\n  max_per_order?: number;\r\n  saleStartDate?: string;\r\n  sale_start_date?: string;\r\n  saleEndDate?: string;\r\n  sale_end_date?: string;\r\n  isHidden?: boolean;\r\n  is_hidden?: boolean;\r\n  revealWithPromoCode?: boolean;\r\n  reveal_with_promo_code?: boolean;\r\n  ticketType?: number;\r\n  ticket_type?: number;\r\n  sectionId?: number;\r\n  section_id?: number;\r\n  sortOrder?: number;\r\n  sort_order?: number;\r\n}\r\n\r\ninterface ApiEvent {\r\n  eventID?: number;\r\n  id?: number;\r\n  ID?: number;\r\n  name?: string;\r\n  title?: string;\r\n  slug?: string;\r\n  description?: string;\r\n  date?: string;\r\n  startDateTime?: string;\r\n  start_date_time?: string;\r\n  endDateTime?: string;\r\n  end_date_time?: string;\r\n  doorsOpenTime?: string;\r\n  doors_open_time?: string;\r\n  timezone?: string;\r\n  time_zone?: string;\r\n  venueId?: number;\r\n  venue_id?: number;\r\n  venueName?: string;\r\n  venue_name?: string;\r\n  venueAddress?: string;\r\n  venue_address?: string;\r\n  location?: string;\r\n  imageUrl?: string;\r\n  imageURL?: string;\r\n  image_url?: string;\r\n  image?: string;\r\n  status?: string;\r\n  isPublished?: boolean;\r\n  is_published?: boolean;\r\n  isCancelled?: boolean;\r\n  is_cancelled?: boolean;\r\n  availableTickets?: any[];\r\n  available_tickets?: any[];\r\n  tickets?: any[];\r\n  ticketsAvailable?: number;\r\n  tickets_available?: number;\r\n  ticketsSold?: number;\r\n  tickets_sold?: number;\r\n  minPrice?: number;\r\n  min_price?: number;\r\n  maxPrice?: number;\r\n  max_price?: number;\r\n  ctaText?: string;\r\n  cta_text?: string;\r\n  ctaState?: string;\r\n  cta_state?: string;\r\n  showPerformers?: boolean;\r\n  show_performers?: boolean;\r\n  eventSeriesId?: number;\r\n  event_series_id?: number;\r\n  seriesInstanceNumber?: number;\r\n  series_instance_number?: number;\r\n  // ── Passthrough seam fields (events convergence) ──────────────────────\r\n  // Carried verbatim onto the domain Event so the view-shape projections\r\n  // (utils/event-transform.js DETAIL/BROWSE, pcf/transform.ts EventData) can\r\n  // compose parseEvent instead of reading the wire directly. See the\r\n  // composition note above parseEvent.\r\n  timeZone?: string; // PascalCase variant some admin payloads carry\r\n  eventSummary?: string;\r\n  password?: string;\r\n  hasPassword?: boolean;\r\n  disclaimer?: string;\r\n  ticketType?: number; // event-level ticketing type (1 = registration)\r\n  eventTicketingType?: number;\r\n  ageRestriction?: number;\r\n  displayAgeRestriction?: boolean;\r\n  displayStartTime?: boolean;\r\n  displayEndTime?: boolean;\r\n  displayDoorsTime?: boolean;\r\n  collectionId?: number;\r\n  collection_id?: number;\r\n  ticketsRemaining?: number; // backend-supplied scarcity (org-month endpoint)\r\n  ticketsTotal?: number;\r\n  hasHiddenTickets?: boolean;\r\n  stage?: { name?: string } | null;\r\n  stageName?: string;\r\n  // Widget-filter dimensions carried by the public month feeds (venue +\r\n  // organization). publicPerformers is the backend's redacted summary\r\n  // (id/displayName/image) — ids are roster-performer ids, the same id-space\r\n  // the widget builder's performer filter saves.\r\n  stageId?: number;\r\n  eventCategoryTypes?: number[] | null;\r\n  publicPerformers?: Array<{\r\n    id?: number;\r\n    displayName?: string;\r\n    image?: string;\r\n  }> | null;\r\n  collectionIds?: number[] | null;\r\n  venue?: Record<string, any>;\r\n  performers?: Record<string, any>[];\r\n  faqs?: Record<string, any>[];\r\n  showtimes?: Record<string, any>[];\r\n  purchasedTickets?: Record<string, any>[];\r\n}\r\n\r\n/**\r\n * Get CDN image URL.\r\n *\r\n * Delegates the host-join to SC's canonical `resolveImageUrl` (one path-join,\r\n * one host constant). `{ cdn: true }` picks the Cloudflare-fronted CDN base\r\n * (`moxy.sfo3.cdn`), the correct, edge-cached host for public buyer surfaces.\r\n *\r\n * This replaced VC's local `micdrop-images.sfo3` bucket, which 404s for real\r\n * event/venue keys (CDN census 2026-07-05, ledger vc-getcdnimageurl-wrong-\r\n * bucket-404). SC additionally passes `data:`/`blob:` URLs through untouched\r\n * and normalizes a missing leading slash — a strict superset of the old body.\r\n */\r\nexport function getCDNImageUrl(path: string | undefined): string {\r\n  return resolveImageUrl(path, { cdn: true });\r\n}\r\n\r\n/**\r\n * Extract image URL from event with fallbacks.\r\n *\r\n * DIVERGENCE (no-shadowed-helper rename-keep): the HOST-JOIN is now the SC\r\n * canonical (via `getCDNImageUrl` above); only the FIELD PRECEDENCE stays local\r\n * and deliberately differs -- this transformer reads\r\n * `imageUrl -> imageURL -> image_url -> image` (incl. the camelCase `imageURL`,\r\n * asserted by event.test.ts), whereas SC reads\r\n * `image -> coverImage -> imageUrl -> image_url`. The VC chain is what this\r\n * wire boundary trusts; changing it would repoint which field wins for the same\r\n * event, so it is preserved. Declared under a non-shadowing name and\r\n * re-exported below as `getEventImageUrl` so call sites keep their existing\r\n * import -- satisfies `micdrop/no-shadowed-helper` without suppression.\r\n */\r\nfunction getEventImageUrlByFieldPrecedence(apiEvent: ApiEvent): string {\r\n  const rawUrl =\r\n    apiEvent.imageUrl ||\r\n    apiEvent.imageURL ||\r\n    apiEvent.image_url ||\r\n    apiEvent.image ||\r\n    '';\r\n\r\n  return getCDNImageUrl(rawUrl);\r\n}\r\n\r\nexport { getEventImageUrlByFieldPrecedence as getEventImageUrl };\r\n\r\n/**\r\n * Calculate CTA state based on ticket availability\r\n */\r\n/**\r\n * VC's 4-state CTA derivation (available/sold_out/coming_soon/ended) for parsed events.\r\n * Distinct from SC's `calculateCtaState` (richer wire-level canonical) — renamed to\r\n * avoid shadowing the SC export; this variant reads the parsed/raw event shape parseEvent consumes.\r\n */\r\ntype CtaState = 'available' | 'sold_out' | 'coming_soon' | 'ended';\r\n\r\n/**\r\n * Narrow a raw wire string to a known CTA state. The `ctaState`/`cta_state`\r\n * wire fields are loosely typed (`string`), so an unrecognised value (a typo or\r\n * a backend state we don't model) must NOT be trusted verbatim — it falls\r\n * through to the availability-based derivation instead of masquerading as a\r\n * valid state. The switch narrows each literal, so no cast is needed.\r\n */\r\nfunction toCtaState(raw: string): CtaState | null {\r\n  switch (raw) {\r\n    case 'available':\r\n    case 'sold_out':\r\n    case 'coming_soon':\r\n    case 'ended':\r\n      return raw;\r\n    default:\r\n      return null;\r\n  }\r\n}\r\n\r\nexport function deriveEventCtaState(apiEvent: ApiEvent): CtaState {\r\n  // Check explicit CTA state first — but only trust a value we recognise.\r\n  const explicitState = apiEvent.ctaState || apiEvent.cta_state;\r\n  if (explicitState) {\r\n    const known = toCtaState(explicitState);\r\n    if (known) {\r\n      return known;\r\n    }\r\n  }\r\n\r\n  // Check if event is cancelled or ended\r\n  if (apiEvent.isCancelled || apiEvent.is_cancelled) {\r\n    return 'ended';\r\n  }\r\n\r\n  // Check event date\r\n  const eventDate =\r\n    apiEvent.startDateTime || apiEvent.start_date_time || apiEvent.date;\r\n  if (eventDate) {\r\n    const eventTime = new Date(eventDate).getTime(); // @datetime-escape: event-past check — startDateTime/start_date_time are UTC-Z instants (tz-independent); the apiEvent.date fallback is date-only → UTC-midnight parse (near-midnight venue-tz edge) — follow-up dt-vc-eventdate-fallback\r\n    const now = Date.now();\r\n\r\n    if (eventTime < now) {\r\n      return 'ended';\r\n    }\r\n  }\r\n\r\n  // Check ticket availability\r\n  const availableCount =\r\n    apiEvent.ticketsAvailable ?? apiEvent.tickets_available;\r\n\r\n  if (availableCount !== undefined) {\r\n    if (availableCount <= 0) {\r\n      return 'sold_out';\r\n    }\r\n    return 'available';\r\n  }\r\n\r\n  // Check tickets array\r\n  const tickets =\r\n    apiEvent.availableTickets ||\r\n    apiEvent.available_tickets ||\r\n    apiEvent.tickets ||\r\n    [];\r\n\r\n  if (tickets.length === 0) {\r\n    return 'coming_soon';\r\n  }\r\n\r\n  const totalAvailable = tickets.reduce((sum: number, t: any) => {\r\n    const qty = t.quantityAvailable ?? t.quantity_available ?? t.quantity ?? 0;\r\n    const sold = t.quantitySold ?? t.quantity_sold ?? 0;\r\n    return sum + Math.max(0, qty - sold);\r\n  }, 0);\r\n\r\n  if (totalAvailable <= 0) {\r\n    return 'sold_out';\r\n  }\r\n\r\n  return 'available';\r\n}\r\n\r\n/**\r\n * Parse a raw ticket payload into the normalized AvailableTicket domain shape.\r\n *\r\n * Wire shape is contained inside this module. Uses `||` chains matching the\r\n * old transformer exactly -- 0/false values fall through to the next variant\r\n * or default, byte-identical to previous behavior.\r\n */\r\nexport function parseTicket(raw: unknown): AvailableTicket {\r\n  const apiTicket = raw as ApiTicket;\r\n  return {\r\n    id: apiTicket.id || apiTicket.ID,\r\n    name: apiTicket.name || apiTicket.ticketName || '',\r\n    description: apiTicket.description,\r\n    price: apiTicket.price || 0,\r\n    quantity: apiTicket.quantity || apiTicket.totalQuantity || 0,\r\n    quantitySold: apiTicket.quantitySold || apiTicket.quantity_sold || 0,\r\n    quantityAvailable:\r\n      apiTicket.quantityAvailable ||\r\n      apiTicket.quantity_available ||\r\n      (apiTicket.quantity || 0) - (apiTicket.quantitySold || 0),\r\n    minPerOrder: apiTicket.minPerOrder || apiTicket.min_per_order || 1,\r\n    maxPerOrder: apiTicket.maxPerOrder || apiTicket.max_per_order || 10,\r\n    saleStartDate: apiTicket.saleStartDate || apiTicket.sale_start_date,\r\n    saleEndDate: apiTicket.saleEndDate || apiTicket.sale_end_date,\r\n    isHidden: apiTicket.isHidden || apiTicket.is_hidden || false,\r\n    revealWithPromoCode:\r\n      apiTicket.revealWithPromoCode || apiTicket.reveal_with_promo_code,\r\n    ticketType: apiTicket.ticketType || apiTicket.ticket_type || 0,\r\n    sectionId: apiTicket.sectionId || apiTicket.section_id,\r\n    sortOrder: apiTicket.sortOrder || apiTicket.sort_order || 0,\r\n  };\r\n}\r\n\r\n/**\r\n * Parse a raw event payload into the normalized Event domain shape.\r\n *\r\n * Wire shape (ApiEvent) is contained inside this module and never escapes it.\r\n * Uses `||` chains matching the old transformer exactly -- 0/false values fall\r\n * through to the next variant, byte-identical to previous behavior. Throw\r\n * behavior preserved from old mapper: null/undefined input (property access on a nullish value) or a non-array\r\n * tickets field throws exactly as the old code did. Callers always pass real\r\n * wire objects.\r\n *\r\n * COMPOSITION NOTE — passthrough seams (events convergence):\r\n * The trailing block of fields below is carried VERBATIM from the wire (no\r\n * fallback chains, no normalization) so that the view-shape projections —\r\n * `utils/event-transform.js` (DETAIL/BROWSE) and\r\n * `public-calendar-flow/transform.ts` (EventData) — can compose parseEvent\r\n * for every event-level scalar instead of reading the wire themselves.\r\n * `venue`/`performers`/`faqs`/`showtimes`/`purchasedTickets` stay raw objects\r\n * by contract: their consumers (and #260's performer canonical) own that\r\n * variance; normalizing them here would change rendered output.\r\n */\r\nexport function parseEvent(raw: unknown): Event {\r\n  const apiEvent = raw as ApiEvent;\r\n\r\n  // Extract tickets array from various possible field names\r\n  const rawTickets =\r\n    apiEvent.availableTickets ||\r\n    apiEvent.available_tickets ||\r\n    apiEvent.tickets ||\r\n    [];\r\n\r\n  const tickets = rawTickets.map(parseTicket);\r\n\r\n  // Calculate min/max prices from tickets\r\n  const prices = tickets.map(t => t.price).filter(p => p > 0);\r\n  const minPrice = prices.length > 0 ? Math.min(...prices) : undefined;\r\n  const maxPrice = prices.length > 0 ? Math.max(...prices) : undefined;\r\n\r\n  return {\r\n    eventID: apiEvent.eventID || apiEvent.id || apiEvent.ID || 0,\r\n    id: apiEvent.id || apiEvent.ID,\r\n    name: apiEvent.name || apiEvent.title || '',\r\n    title: apiEvent.title || apiEvent.name,\r\n    slug: apiEvent.slug,\r\n    description: apiEvent.description,\r\n    date:\r\n      apiEvent.date || apiEvent.startDateTime || apiEvent.start_date_time || '',\r\n    startDateTime: apiEvent.startDateTime || apiEvent.start_date_time,\r\n    endDateTime: apiEvent.endDateTime || apiEvent.end_date_time,\r\n    doorsOpenTime: apiEvent.doorsOpenTime || apiEvent.doors_open_time,\r\n    timezone: apiEvent.timezone || apiEvent.time_zone,\r\n    venueId: apiEvent.venueId || apiEvent.venue_id,\r\n    venueName: apiEvent.venueName || apiEvent.venue_name,\r\n    venueAddress: apiEvent.venueAddress || apiEvent.venue_address,\r\n    location:\r\n      apiEvent.location || apiEvent.venueAddress || apiEvent.venue_address,\r\n    imageUrl: getEventImageUrlByFieldPrecedence(apiEvent),\r\n    imageURL: getEventImageUrlByFieldPrecedence(apiEvent),\r\n    status: apiEvent.status,\r\n    isPublished: apiEvent.isPublished ?? apiEvent.is_published,\r\n    isCancelled: apiEvent.isCancelled ?? apiEvent.is_cancelled,\r\n    availableTickets: tickets,\r\n    ticketsAvailable:\r\n      apiEvent.ticketsAvailable ??\r\n      apiEvent.tickets_available ??\r\n      tickets.reduce((sum, t) => sum + (t.quantityAvailable || 0), 0),\r\n    ticketsSold: apiEvent.ticketsSold ?? apiEvent.tickets_sold,\r\n    minPrice: apiEvent.minPrice ?? apiEvent.min_price ?? minPrice,\r\n    maxPrice: apiEvent.maxPrice ?? apiEvent.max_price ?? maxPrice,\r\n    ctaText: apiEvent.ctaText || apiEvent.cta_text,\r\n    ctaState: deriveEventCtaState(apiEvent),\r\n    showPerformers: apiEvent.showPerformers ?? apiEvent.show_performers,\r\n    eventSeriesId: apiEvent.eventSeriesId || apiEvent.event_series_id,\r\n    seriesInstanceNumber:\r\n      apiEvent.seriesInstanceNumber || apiEvent.series_instance_number,\r\n    // ── Passthrough seams (see composition note above) — verbatim wire ──\r\n    timeZone: apiEvent.timeZone,\r\n    eventSummary: apiEvent.eventSummary,\r\n    password: apiEvent.password,\r\n    hasPassword: apiEvent.hasPassword,\r\n    disclaimer: apiEvent.disclaimer,\r\n    ticketType: apiEvent.ticketType,\r\n    eventTicketingType: apiEvent.eventTicketingType,\r\n    ageRestriction: apiEvent.ageRestriction,\r\n    displayAgeRestriction: apiEvent.displayAgeRestriction,\r\n    displayStartTime: apiEvent.displayStartTime,\r\n    displayEndTime: apiEvent.displayEndTime,\r\n    displayDoorsTime: apiEvent.displayDoorsTime,\r\n    collectionId: apiEvent.collectionId || apiEvent.collection_id,\r\n    ticketsRemaining: apiEvent.ticketsRemaining,\r\n    ticketsTotal: apiEvent.ticketsTotal,\r\n    hasHiddenTickets: apiEvent.hasHiddenTickets,\r\n    stage: apiEvent.stage,\r\n    stageName: apiEvent.stageName,\r\n    stageId: apiEvent.stageId,\r\n    eventCategoryTypes: apiEvent.eventCategoryTypes,\r\n    publicPerformers: apiEvent.publicPerformers,\r\n    collectionIds: apiEvent.collectionIds,\r\n    venue: apiEvent.venue,\r\n    performers: apiEvent.performers,\r\n    faqs: apiEvent.faqs,\r\n    showtimes: apiEvent.showtimes,\r\n    purchasedTickets: apiEvent.purchasedTickets,\r\n  };\r\n}\r\n\r\n/**\r\n * Back-compat alias -- existing call sites using transformTicket continue\r\n * to work unchanged.\r\n */\r\nexport const transformTicket = parseTicket;\r\n\r\n/**\r\n * Back-compat alias -- existing call sites using transformEvent(apiEvent)\r\n * continue to work unchanged.\r\n */\r\nexport const transformEvent = parseEvent;\r\n\r\n/**\r\n * Legacy transformer name for backwards compatibility.\r\n */\r\nexport const transformEventData = parseEvent;\r\n\r\nexport function parseEventList(raws: unknown[]): Event[] {\r\n  return raws.map(parseEvent);\r\n}\r\n","/**\r\n * Venue Transformer\r\n *\r\n * Normalizes API venue responses to a consistent format.\r\n *\r\n * Wire->domain boundary: one parseVenue(raw: unknown) entry point.\r\n * The wire field shape (ApiVenue) is named only inside this module and never\r\n * escapes it. Above the boundary only the Venue domain type.\r\n *\r\n * IMPLEMENTATION NOTE -- why no Zod schema here:\r\n * The old mapper used `||` for id/name (0 falls through) and `??` for fee fields\r\n * (0 fee is a real value, must NOT fall through). Both operators replicated verbatim.\r\n * Throw behavior is identical to the old mapper: null/undefined input (property access on a nullish value) throws exactly\r\n * as the old code did. Callers always pass real wire objects.\r\n */\r\n\r\nimport { resolveImageUrl } from '@getmicdrop/svelte-components';\r\nimport type { Venue } from '../types.js';\r\n\r\n// ── Wire shape (internal -- never exported) ──────────────────────────────\r\ninterface ApiVenue {\r\n  id?: number;\r\n  ID?: number;\r\n  name?: string;\r\n  slug?: string;\r\n  address?: string;\r\n  googleLocationNameCache?: string;\r\n  google_location_name_cache?: string;\r\n  city?: string;\r\n  state?: string;\r\n  zipCode?: string;\r\n  zip_code?: string;\r\n  country?: string;\r\n  timezone?: string;\r\n  time_zone?: string;\r\n  logoUrl?: string;\r\n  logo_url?: string;\r\n  logo?: string;\r\n  serviceFeePercentage?: number;\r\n  service_fee_percentage?: number;\r\n  serviceFeeCents?: number;\r\n  service_fee_cents?: number;\r\n  taxPercentage?: number;\r\n  tax_percentage?: number;\r\n  organizationId?: number;\r\n  organization_id?: number;\r\n}\r\n\r\n/**\r\n * Get the CDN image URL for a venue logo.\r\n *\r\n * Delegates the host-join to SC's canonical `resolveImageUrl` with the\r\n * Cloudflare-fronted CDN base (`moxy.sfo3.cdn`) — the same correct, edge-cached\r\n * host every other VC public surface uses. Replaced VC's local `micdrop-images`\r\n * bucket, which 404s for real keys (CDN census 2026-07-05).\r\n */\r\nfunction getLogoUrl(path: string | undefined): string {\r\n  return resolveImageUrl(path, { cdn: true });\r\n}\r\n\r\n/**\r\n * Parse a raw venue payload into the normalized Venue domain shape.\r\n *\r\n * Wire shape (ApiVenue) is contained inside this module and never escapes it.\r\n * Uses `||` / `??` chains matching the old transformer exactly -- byte-identical\r\n * to previous behavior. Throw behavior preserved from old mapper: null/undefined\r\n * input throws exactly as the old code did. Callers always pass real wire objects.\r\n */\r\n// Compare-first vs SC canonical parseVenue(raw): unknown => ParsedVenue:\r\n// GENUINELY DIVERGED — different return shape. SC returns a `ParsedVenue` whose\r\n// `logoPath` is the raw path (and accepts extra fallback keys: image,\r\n// squareImageUrl, postalCode), leaving URL resolution to the caller; VC returns\r\n// the `Venue` domain type with an already-resolved `logoUrl: getLogoUrl(path)`\r\n// and `timezone` (not SC's `timeZone`). Kept local under a non-shadowing name\r\n// and re-exported under the public name; no eslint-disable.\r\nfunction vcParseVenue(raw: unknown): Venue {\r\n  const apiVenue = raw as ApiVenue;\r\n\r\n  const logoPath = apiVenue.logoUrl || apiVenue.logo_url || apiVenue.logo;\r\n\r\n  return {\r\n    id: apiVenue.id || apiVenue.ID || 0,\r\n    name: apiVenue.name || '',\r\n    slug: apiVenue.slug,\r\n    address: apiVenue.address,\r\n    googleLocationNameCache:\r\n      apiVenue.googleLocationNameCache || apiVenue.google_location_name_cache,\r\n    city: apiVenue.city,\r\n    state: apiVenue.state,\r\n    zipCode: apiVenue.zipCode || apiVenue.zip_code,\r\n    country: apiVenue.country,\r\n    timezone: apiVenue.timezone || apiVenue.time_zone,\r\n    logoUrl: getLogoUrl(logoPath),\r\n    serviceFeePercentage:\r\n      apiVenue.serviceFeePercentage ?? apiVenue.service_fee_percentage ?? 0,\r\n    serviceFeeCents:\r\n      apiVenue.serviceFeeCents ?? apiVenue.service_fee_cents ?? 0,\r\n    taxPercentage: apiVenue.taxPercentage ?? apiVenue.tax_percentage ?? 0,\r\n    organizationId: apiVenue.organizationId || apiVenue.organization_id,\r\n  };\r\n}\r\n\r\n/**\r\n * Public re-export under the canonical name + back-compat alias. Existing call\r\n * sites using either parseVenue(apiVenue) or transformVenue(apiVenue) continue\r\n * to work unchanged; re-exports don't redeclare the identifier so the\r\n * no-shadowed-helper rule is satisfied without a suppression.\r\n */\r\nexport { vcParseVenue as parseVenue };\r\nexport const transformVenue = vcParseVenue;\r\n\r\n/**\r\n * Extract fee configuration from a raw venue payload.\r\n *\r\n * Uses `??` (not `||`) so 0-valued fees are preserved correctly.\r\n */\r\n// Compare-first vs SC canonical extractVenueFees(raw): unknown => VenueFees:\r\n// GENUINELY DIVERGED — VC returns an inline object literal type derived from its\r\n// own ApiVenue wire shape (with VC's snake/camel casing fallbacks), not SC's\r\n// branded `VenueFees`. Kept local under a non-shadowing name and re-exported\r\n// below; no eslint-disable.\r\nfunction vcExtractVenueFees(raw: unknown): {\r\n  serviceFeePercentage: number;\r\n  serviceFeeCents: number;\r\n  taxPercentage: number;\r\n} {\r\n  const apiVenue = raw as ApiVenue;\r\n  return {\r\n    serviceFeePercentage:\r\n      apiVenue.serviceFeePercentage ?? apiVenue.service_fee_percentage ?? 0,\r\n    serviceFeeCents:\r\n      apiVenue.serviceFeeCents ?? apiVenue.service_fee_cents ?? 0,\r\n    taxPercentage: apiVenue.taxPercentage ?? apiVenue.tax_percentage ?? 0,\r\n  };\r\n}\r\n\r\n/**\r\n * Get formatted venue address.\r\n *\r\n * Compare-first vs SC canonical formatVenueAddress(venue: Pick<ParsedVenue,\r\n * 'address'|'city'|'state'|...>): GENUINELY DIVERGED — VC takes its own `Venue`\r\n * domain type (the resolved shape `vcParseVenue` emits), not SC's `ParsedVenue`\r\n * pick. Kept local under a non-shadowing name and re-exported below; no\r\n * eslint-disable.\r\n */\r\nfunction vcFormatVenueAddress(venue: Venue): string {\r\n  const parts = [venue.address, venue.city, venue.state, venue.zipCode].filter(\r\n    Boolean\r\n  );\r\n\r\n  return parts.join(', ');\r\n}\r\n\r\nexport {\r\n  vcExtractVenueFees as extractVenueFees,\r\n  vcFormatVenueAddress as formatVenueAddress,\r\n};\r\n"],"names":["logger","createLogger","DEFAULT_CONFIG","error","globalConfig","configureApi","config","getApiConfig","getPublicBaseUrl","getLegacyPublicUrl","getOrdersV2Url","getClientIP","sleep","ms","resolve","fetchWithRetry","url","init","cfg","method","maxAttempts","lastError","attempt","controller","timeoutId","callerSignal","onCallerAbort","response","err","apiGet","endpoint","options","apiRequest","apiPost","body","apiPut","apiDelete","errorData","message","errorMessage","simpleFetch","parseCartReservation","raw","rec","parseCart","data","uuid","eventID","status","expiresAt","reservations","UTM_KEYS","UTM_PARAM","DIRECT_SOURCE","normalizeUtmValue","normalizeUtmSet","out","key","readUtmSet","params","isTopLevel","resolveHostContext","hasWindow","topLevel","search","referrer","pageUrl","granted","hasTcf","isConsentRequired","isConsentGranted","SESSION_ID_KEY","VISITOR_ID_KEY","UUID_V4","isUuidV4","value","generateUuidV4","cryptoObj","bytes","i","hex","getSessionId","id","getSessionStorage","setSessionStorage","getVisitorId","stored","getLocalStorage","setLocalStorage","ATTRIBUTION_KEY","readStore","getAttribution","touch","createPaymentIntent","cartId","quantities","donationAmounts","apiResult","AppError","getCartByUUID","cartUUID","expectedEventID","updateCartQuantities","numericQuantities","k","v","n","numericDonations","completeReservation","orderUuid","cancelReservation","createOrder","eventId","promoCode","getOrder","orderId","validatePaymentIntent","payload","result","extendCheckoutSession","getSessionStatus","initiateOrder","cartData","trackUTMSource","venueId","urlParams","utmSource","ORDERS_V2_PUBLIC","validatePromoCode","code","encodedCode","hasPromoCodes","applyPromoCode","removePromoCode","parseEventPerformers","ok","errNotFound","isOk","isErr","unwrapOr","fallback","_eventDetailsInflight","fetchEventDetailsResult","customFetch","password","inflight","run","fetchEventDetails","loadResult","errorCode","map","asArray","fetchEventTicketsResult","fetchEventTickets","fetchEventPerformersResult","fetchEventPerformers","fetchAllVenuesResult","orgId","fetchAllVenues","fetchVenueEventsResult","fetchVenueEvents","unwrapMonthEvents","getMonthEventsResult","year","month","getMonthEvents","getOrgMonthEventsResult","getOrgMonthEvents","getSeriesOccurrencesResult","eventSeriesId","getSeriesOccurrences","fetchSeriesOccurrencesResult","fetchSeriesOccurrences","resolvePublicEntity","slug","qs","fetchSeriesPage","seriesId","fetchPublicCollection","collectionId","checkCollectionPassword","checkEventPassword","testNetworkConnection","getVenue","getVenueFees","venue","getVenueBySlug","encodedSlug","parseGiftCardApplication","giftCardAmountDollars","residualDollars","fullyCovered","requiresStripe","API_BASE_URL","ORDERS_V2_URL","createGiftCardPurchase","req","applyGiftCard","removeGiftCard","completeGiftCardPayment","customerDetails","WAITLIST_ERROR_MESSAGES","joinWaitlist","email","phone","smsOptIn","validateWaitlistToken","token","getWaitlistStatus","computeCtaState","event","tickets","cancelled","isRegistration","waitlistEnabled","now","eventEnd","hiddenAvailable","isHiddenTicketPurchasable","publicTickets","t","hasPurchasable","allSoldOut","allComingSoon","earliestSalesStart","ticket","salesBegin","salesEnd","remaining","isSoldOut","isScheduled","hasSalesEnded","startDate","diffMs","diffHours","diffDays","timeLabel","d","formatLocal","dateText","parsePurchasedTicket","apiTicket","parseOrder","apiOrder","rawTickets","transformTicket","transformOrder","transformOrderForDisplay","order","displayId","rawTotal","formatCurrency","asUSD","getCDNImageUrl","path","resolveImageUrl","getEventImageUrlByFieldPrecedence","apiEvent","rawUrl","toCtaState","deriveEventCtaState","explicitState","known","eventDate","eventTime","availableCount","sum","qty","sold","parseTicket","parseEvent","prices","p","minPrice","maxPrice","transformEvent","transformEventData","getLogoUrl","vcParseVenue","apiVenue","logoPath","transformVenue","vcExtractVenueFees","vcFormatVenueAddress"],"mappings":"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"}