@getmicdrop/venue-calendar 4.0.95 → 4.0.96

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