@getmicdrop/venue-calendar 4.0.79 → 4.0.81

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 (125) hide show
  1. package/README.md +173 -128
  2. package/dist/{CarouselView.legacy-CsMGxVbb.js → CarouselView.legacy-Bn76lnuH.js} +3 -3
  3. package/dist/{CarouselView.legacy-CsMGxVbb.js.map → CarouselView.legacy-Bn76lnuH.js.map} +1 -1
  4. package/dist/{CartView-BOaZufWZ.js → CartView-lR1zLxmN.js} +277 -280
  5. package/dist/CartView-lR1zLxmN.js.map +1 -0
  6. package/dist/{Checkout-bkXNpxIB.js → Checkout-D5mXj5zN.js} +69 -73
  7. package/dist/Checkout-D5mXj5zN.js.map +1 -0
  8. package/dist/Checkout-KyLZlwLk.js +1673 -0
  9. package/dist/Checkout-KyLZlwLk.js.map +1 -0
  10. package/dist/{Checkout.legacy-BOPwZMyO.js → Checkout.legacy-Dl8Oh7y3.js} +95 -99
  11. package/dist/Checkout.legacy-Dl8Oh7y3.js.map +1 -0
  12. package/dist/CheckoutTimer-Deo1mLnO.js +141 -0
  13. package/dist/CheckoutTimer-Deo1mLnO.js.map +1 -0
  14. package/dist/CollectionView-C1O9xfVC.js +338 -0
  15. package/dist/CollectionView-C1O9xfVC.js.map +1 -0
  16. package/dist/{CollectionView.legacy-Dw-J6Ycd.js → CollectionView.legacy-DK_mCA_g.js} +21 -24
  17. package/dist/{CollectionView.legacy-Dw-J6Ycd.js.map → CollectionView.legacy-DK_mCA_g.js.map} +1 -1
  18. package/dist/Event-B5WRygEf.js +2240 -0
  19. package/dist/Event-B5WRygEf.js.map +1 -0
  20. package/dist/EventPage-f1AOk4Jb.js +577 -0
  21. package/dist/EventPage-f1AOk4Jb.js.map +1 -0
  22. package/dist/{EventPage.legacy-Br2M8N9-.js → EventPage.legacy-Bk3PJyJs.js} +408 -411
  23. package/dist/EventPage.legacy-Bk3PJyJs.js.map +1 -0
  24. package/dist/{FeaturedView.legacy-B86kMzvH.js → FeaturedView.legacy-BzCt1X1W.js} +18 -18
  25. package/dist/{FeaturedView.legacy-B86kMzvH.js.map → FeaturedView.legacy-BzCt1X1W.js.map} +1 -1
  26. package/dist/{GalleryCard-CTsYUZu6.js → GalleryCard-C1EKGErR.js} +14 -14
  27. package/dist/{GalleryCard-CTsYUZu6.js.map → GalleryCard-C1EKGErR.js.map} +1 -1
  28. package/dist/{GalleryView.legacy-uIexOsl5.js → GalleryView.legacy-BmQtZKl7.js} +3 -3
  29. package/dist/{GalleryView.legacy-uIexOsl5.js.map → GalleryView.legacy-BmQtZKl7.js.map} +1 -1
  30. package/dist/{GroupedListView.legacy-DMAwHQ9T.js → GroupedListView.legacy-D72fbNfA.js} +21 -21
  31. package/dist/{GroupedListView.legacy-DMAwHQ9T.js.map → GroupedListView.legacy-D72fbNfA.js.map} +1 -1
  32. package/dist/Heading-Bwevh2c4.js +81 -0
  33. package/dist/Heading-Bwevh2c4.js.map +1 -0
  34. package/dist/ModalHeader-DKwE5ZYZ.js +22 -0
  35. package/dist/ModalHeader-DKwE5ZYZ.js.map +1 -0
  36. package/dist/OrderSummarySkeleton-DKWKZNLL.js +632 -0
  37. package/dist/OrderSummarySkeleton-DKWKZNLL.js.map +1 -0
  38. package/dist/{ScarcityBadge-D9RzN3Tz.js → ScarcityBadge-3cFxTOCh.js} +25 -29
  39. package/dist/{ScarcityBadge-D9RzN3Tz.js.map → ScarcityBadge-3cFxTOCh.js.map} +1 -1
  40. package/dist/{SeriesPage-D0pf6VuS.js → SeriesPage-Cq4Q8Vah.js} +17 -21
  41. package/dist/{SeriesPage-D0pf6VuS.js.map → SeriesPage-Cq4Q8Vah.js.map} +1 -1
  42. package/dist/{SeriesPage.legacy-VYpfyT7B.js → SeriesPage.legacy-zvgW0S_y.js} +67 -71
  43. package/dist/SeriesPage.legacy-zvgW0S_y.js.map +1 -0
  44. package/dist/{Success-Dxgbo_Ct.js → Success-kyiEO6_Z.js} +186 -175
  45. package/dist/{Success-Dxgbo_Ct.js.map → Success-kyiEO6_Z.js.map} +1 -1
  46. package/dist/{Success.legacy-umzUtow7.js → Success.legacy-BxKYmXgz.js} +53 -57
  47. package/dist/{Success.legacy-umzUtow7.js.map → Success.legacy-BxKYmXgz.js.map} +1 -1
  48. package/dist/Text-_bLxSPv-.js +158 -0
  49. package/dist/Text-_bLxSPv-.js.map +1 -0
  50. package/dist/{VenueCalendar-D9TCV_XB.js → VenueCalendar-d8u6MKEu.js} +8131 -22680
  51. package/dist/VenueCalendar-d8u6MKEu.js.map +1 -0
  52. package/dist/ViewTicketsEmbed-DquJJVIK.js +2081 -0
  53. package/dist/ViewTicketsEmbed-DquJJVIK.js.map +1 -0
  54. package/dist/__SKIP_NAVIGATION__-CmipjatL.js +18 -0
  55. package/dist/__SKIP_NAVIGATION__-CmipjatL.js.map +1 -0
  56. package/dist/api/api.cjs +1 -1
  57. package/dist/api/api.cjs.map +1 -1
  58. package/dist/api/api.mjs +482 -374
  59. package/dist/api/api.mjs.map +1 -1
  60. package/dist/api/cta.d.ts +2 -10
  61. package/dist/api/transformers/address.d.ts +18 -0
  62. package/dist/api/transformers/cart.d.ts +19 -0
  63. package/dist/api/transformers/collection.d.ts +12 -0
  64. package/dist/api/transformers/event.d.ts +50 -1
  65. package/dist/api/transformers/giftCard.d.ts +11 -0
  66. package/dist/api/transformers/index.d.ts +10 -3
  67. package/dist/api/transformers/performer.d.ts +8 -0
  68. package/dist/api/transformers/series.d.ts +12 -0
  69. package/dist/api/types.d.ts +423 -383
  70. package/dist/api-BzICORqy.js +6 -0
  71. package/dist/{api-DFMsiBOR.js.map → api-BzICORqy.js.map} +1 -1
  72. package/dist/colors-CmP-sSZD.js.map +1 -1
  73. package/dist/{data-toggle-store.svelte-DtDqN-QD.js → data-toggle-store.svelte-BGbzblUJ.js} +10 -15
  74. package/dist/data-toggle-store.svelte-BGbzblUJ.js.map +1 -0
  75. package/dist/index-BsWecoW1.js +63 -0
  76. package/dist/index-BsWecoW1.js.map +1 -0
  77. package/dist/labels-Bj_cocb1.js +966 -0
  78. package/dist/labels-Bj_cocb1.js.map +1 -0
  79. package/dist/seo/seo.cjs +1 -1
  80. package/dist/seo/seo.cjs.map +1 -1
  81. package/dist/seo/seo.mjs +131 -122
  82. package/dist/seo/seo.mjs.map +1 -1
  83. package/dist/transform-D7Oe8jUp.js +276 -0
  84. package/dist/transform-D7Oe8jUp.js.map +1 -0
  85. package/dist/types/index.d.ts +45 -0
  86. package/dist/venue-calendar.css +1 -1
  87. package/dist/venue-calendar.es.js +31 -37
  88. package/dist/venue-calendar.es.js.map +1 -1
  89. package/dist/venue-calendar.iife.js +40 -107
  90. package/dist/venue-calendar.iife.js.map +1 -1
  91. package/dist/venue-calendar.umd.js +38 -125
  92. package/dist/venue-calendar.umd.js.map +1 -1
  93. package/package.json +170 -169
  94. package/dist/CartView-BOaZufWZ.js.map +0 -1
  95. package/dist/Checkout-DD6ZTUh-.js +0 -1531
  96. package/dist/Checkout-DD6ZTUh-.js.map +0 -1
  97. package/dist/Checkout-bkXNpxIB.js.map +0 -1
  98. package/dist/Checkout.legacy-BOPwZMyO.js.map +0 -1
  99. package/dist/CheckoutTimer-CHoKjZyH.js +0 -35
  100. package/dist/CheckoutTimer-CHoKjZyH.js.map +0 -1
  101. package/dist/CollectionView-Ce-OAEC4.js +0 -148
  102. package/dist/CollectionView-Ce-OAEC4.js.map +0 -1
  103. package/dist/Event-rdstzev0.js +0 -1910
  104. package/dist/Event-rdstzev0.js.map +0 -1
  105. package/dist/EventPage-B8VRC83d.js +0 -336
  106. package/dist/EventPage-B8VRC83d.js.map +0 -1
  107. package/dist/EventPage.legacy-Br2M8N9-.js.map +0 -1
  108. package/dist/Heading-AFd3o0xt.js +0 -44
  109. package/dist/Heading-AFd3o0xt.js.map +0 -1
  110. package/dist/OrderSummarySkeleton-dDKUH741.js +0 -16
  111. package/dist/OrderSummarySkeleton-dDKUH741.js.map +0 -1
  112. package/dist/SeriesPage.legacy-VYpfyT7B.js.map +0 -1
  113. package/dist/Text-CXR2fhx6.js +0 -54
  114. package/dist/Text-CXR2fhx6.js.map +0 -1
  115. package/dist/VenueCalendar-D9TCV_XB.js.map +0 -1
  116. package/dist/ViewTicketsEmbed-Bt05E5Ex.js +0 -2039
  117. package/dist/ViewTicketsEmbed-Bt05E5Ex.js.map +0 -1
  118. package/dist/__SKIP_NAVIGATION__-CJ96TTPE.js +0 -7
  119. package/dist/__SKIP_NAVIGATION__-CJ96TTPE.js.map +0 -1
  120. package/dist/api-DFMsiBOR.js +0 -12
  121. package/dist/data-toggle-store.svelte-DtDqN-QD.js.map +0 -1
  122. package/dist/labels-CClq3Rb9.js +0 -659
  123. package/dist/labels-CClq3Rb9.js.map +0 -1
  124. package/dist/transform-CWf-YI_e.js +0 -275
  125. package/dist/transform-CWf-YI_e.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"api.cjs","sources":["../../src/lib/api/client.ts","../../src/lib/api/orders.ts","../../src/lib/api/promo.ts","../../src/lib/api/events.ts","../../src/lib/api/venues.ts","../../src/lib/api/gift-cards.ts","../../src/lib/api/waitlist.ts","../../src/lib/api/cta.ts","../../src/lib/api/transformers/order.ts","../../src/lib/api/transformers/event.ts","../../src/lib/api/transformers/venue.ts"],"sourcesContent":["/**\r\n * API Client for MicDrop Public Checkout API\r\n *\r\n * Provides a centralized HTTP client with:\r\n * - Configurable base URL\r\n * - Per-request timeout via AbortController\r\n * - Exponential-backoff retry on 5xx and network errors (idempotent methods)\r\n * - Consistent error surfacing\r\n * - Request/response typing\r\n */\r\n\r\nimport { createLogger } from '@getmicdrop/svelte-components/utils/logger';\r\nconst logger = createLogger('VC');\r\nimport type { ApiConfig, ApiResponse } from './types.js';\r\n\r\n// Default API configuration\r\nconst DEFAULT_CONFIG: Required<ApiConfig> = {\r\n baseUrl: 'https://get-micdrop.com',\r\n timeout: 30000,\r\n retries: 2,\r\n retryDelay: 500,\r\n onError: (error: Error) => logger.error('API Error:', error),\r\n};\r\n\r\nlet globalConfig: Required<ApiConfig> = { ...DEFAULT_CONFIG };\r\n\r\nexport function configureApi(config: Partial<ApiConfig>): void {\r\n globalConfig = { ...globalConfig, ...config };\r\n}\r\n\r\nexport function getApiConfig(): Required<ApiConfig> {\r\n return { ...globalConfig };\r\n}\r\n\r\nexport function getPublicBaseUrl(): string {\r\n return `${globalConfig.baseUrl}/api/v2/public`;\r\n}\r\n\r\nexport function getLegacyPublicUrl(): string {\r\n return `${globalConfig.baseUrl}/api/public`;\r\n}\r\n\r\nexport function getOrdersV2Url(): string {\r\n return `${globalConfig.baseUrl}/api/orders/v2/public`;\r\n}\r\n\r\n/** @deprecated IP is now resolved server-side via request headers. */\r\nexport async function getClientIP(): Promise<string> {\r\n return '';\r\n}\r\n\r\n/**\r\n * Sleep utility for retry backoff.\r\n */\r\nconst sleep = (ms: number): Promise<void> =>\r\n new Promise((resolve) => setTimeout(resolve, ms));\r\n\r\n/**\r\n * fetch with timeout, idempotent retry, and exponential backoff.\r\n *\r\n * - Retries on network errors and 5xx (transient) responses.\r\n * - Never retries non-idempotent methods (POST/PUT/PATCH/DELETE) — those are\r\n * left to caller-driven retries to avoid duplicate writes.\r\n * - The supplied AbortSignal (if any) is respected and short-circuits retries.\r\n */\r\nasync function fetchWithRetry(\r\n url: string,\r\n init: RequestInit,\r\n cfg: Required<ApiConfig>\r\n): Promise<Response> {\r\n const method = (init.method || 'GET').toUpperCase();\r\n const isIdempotent = method === 'GET' || method === 'HEAD';\r\n const maxAttempts = isIdempotent ? cfg.retries + 1 : 1;\r\n\r\n let lastError: unknown;\r\n for (let attempt = 1; attempt <= maxAttempts; attempt++) {\r\n const controller = new AbortController();\r\n const timeoutId = setTimeout(() => controller.abort(), cfg.timeout);\r\n\r\n // Chain the caller's signal if provided.\r\n const callerSignal = init.signal;\r\n let onCallerAbort: (() => void) | undefined;\r\n if (callerSignal) {\r\n if (callerSignal.aborted) controller.abort();\r\n else {\r\n onCallerAbort = () => controller.abort();\r\n callerSignal.addEventListener('abort', onCallerAbort, { once: true });\r\n }\r\n }\r\n\r\n try {\r\n // @api-client-escape: internal canonical client implementation — not drift\r\n const response = await fetch(url, { ...init, signal: controller.signal });\r\n\r\n // Retry transient server errors.\r\n if (response.status >= 500 && attempt < maxAttempts) {\r\n lastError = new Error(`HTTP ${response.status}`);\r\n } else {\r\n return response;\r\n }\r\n } catch (err) {\r\n lastError = err;\r\n // Caller aborted — give up.\r\n if (callerSignal?.aborted) throw err;\r\n } finally {\r\n clearTimeout(timeoutId);\r\n if (callerSignal && onCallerAbort) {\r\n callerSignal.removeEventListener('abort', onCallerAbort);\r\n }\r\n }\r\n\r\n if (attempt < maxAttempts) {\r\n await sleep(cfg.retryDelay * Math.pow(2, attempt - 1));\r\n }\r\n }\r\n\r\n throw lastError;\r\n}\r\n\r\nexport async function apiGet<T>(\r\n endpoint: string,\r\n options?: RequestInit\r\n): Promise<ApiResponse<T>> {\r\n return apiRequest<T>('GET', endpoint, undefined, options);\r\n}\r\n\r\nexport async function apiPost<T>(\r\n endpoint: string,\r\n body?: unknown,\r\n options?: RequestInit\r\n): Promise<ApiResponse<T>> {\r\n return apiRequest<T>('POST', endpoint, body, options);\r\n}\r\n\r\nexport async function apiPut<T>(\r\n endpoint: string,\r\n body?: unknown,\r\n options?: RequestInit\r\n): Promise<ApiResponse<T>> {\r\n return apiRequest<T>('PUT', endpoint, body, options);\r\n}\r\n\r\nexport async function apiDelete<T>(\r\n endpoint: string,\r\n options?: RequestInit\r\n): Promise<ApiResponse<T>> {\r\n return apiRequest<T>('DELETE', endpoint, undefined, options);\r\n}\r\n\r\nasync function apiRequest<T>(\r\n method: string,\r\n endpoint: string,\r\n body?: unknown,\r\n options?: RequestInit\r\n): Promise<ApiResponse<T>> {\r\n const url = endpoint.startsWith('http')\r\n ? endpoint\r\n : `${getPublicBaseUrl()}${endpoint}`;\r\n\r\n try {\r\n const response = await fetchWithRetry(\r\n url,\r\n {\r\n method,\r\n headers: {\r\n 'Content-Type': 'application/json',\r\n ...options?.headers,\r\n },\r\n body: body !== undefined ? JSON.stringify(body) : undefined,\r\n credentials: 'include',\r\n ...options,\r\n },\r\n globalConfig\r\n );\r\n\r\n if (!response.ok) {\r\n const errorData = await response.json().catch(() => ({}));\r\n const message =\r\n errorData.error || errorData.message || `HTTP ${response.status}`;\r\n globalConfig.onError(new Error(message));\r\n return { success: false, error: message, statusCode: response.status };\r\n }\r\n\r\n const data = await response.json();\r\n return { success: true, data: data as T, statusCode: response.status };\r\n } catch (error) {\r\n const errorMessage =\r\n error instanceof Error\r\n ? error.name === 'AbortError'\r\n ? 'Request timed out'\r\n : error.message\r\n : 'Unknown error';\r\n\r\n globalConfig.onError(\r\n error instanceof Error ? error : new Error(errorMessage)\r\n );\r\n return { success: false, error: errorMessage };\r\n }\r\n}\r\n\r\n/**\r\n * Simple fetch wrapper that returns parsed JSON or null on error.\r\n * Inherits the global timeout + retry policy for GETs.\r\n */\r\nexport async function simpleFetch<T>(\r\n url: string,\r\n options?: RequestInit\r\n): Promise<T | null> {\r\n try {\r\n const response = await fetchWithRetry(\r\n url,\r\n { credentials: 'include', ...options },\r\n globalConfig\r\n );\r\n\r\n if (!response.ok) {\r\n const errorData = await response.json().catch(() => ({}));\r\n logger.error(`API request failed: ${response.status}`, errorData);\r\n return null;\r\n }\r\n\r\n return response.json();\r\n } catch (error) {\r\n logger.error('API request error:', error);\r\n return null;\r\n }\r\n}\r\n","import { AppError } from '@getmicdrop/svelte-components';\r\n/**\r\n * Orders API\r\n *\r\n * Functions for managing orders, payments, and reservations\r\n * in the public checkout flow.\r\n */\r\n\r\nimport { createLogger } from '@getmicdrop/svelte-components/utils/logger';\r\nconst logger = createLogger('VC');\r\nimport { getPublicBaseUrl, getOrdersV2Url, simpleFetch, apiPost, apiGet } from './client.js';\r\nimport type {\r\n PaymentIntentResponse,\r\n CompleteReservationResponse,\r\n CancelReservationResponse,\r\n CreateOrderResponse,\r\n ValidatePaymentRequest,\r\n ValidatePaymentResponse,\r\n ExtendSessionResponse,\r\n SessionStatus,\r\n Order,\r\n} from './types.js';\r\n\r\n/**\r\n * Create a payment intent for the cart\r\n *\r\n * This initiates the Stripe payment flow by creating a PaymentIntent\r\n * on the backend. The response includes the client_secret needed\r\n * for Stripe Elements.\r\n *\r\n * @param cartId - The cart/order UUID\r\n * @param quantities - Map of ticketId -> quantity\r\n * @param donationAmounts - Map of ticketId -> donation amount in dollars (for type=2 tickets)\r\n * @returns Payment intent data including client_secret, or null on error\r\n */\r\nexport async function createPaymentIntent(\r\n cartId: string,\r\n quantities: Record<string | number, number>,\r\n donationAmounts?: Record<string | number, number>\r\n): Promise<PaymentIntentResponse | null> {\r\n try {\r\n const apiResult = await apiPost<PaymentIntentResponse>(\r\n `${getOrdersV2Url()}/cart/${cartId}/payment-intent`,\r\n {\r\n productQuantities: quantities,\r\n ...(donationAmounts && Object.keys(donationAmounts).length > 0\r\n ? { donationAmounts }\r\n : {}),\r\n }\r\n );\r\n\r\n if (!apiResult.success) {\r\n logger.error('Payment intent creation failed:', apiResult.error);\r\n throw new AppError(apiResult.error || 'Failed to create payment intent', 'lib/api/orders/createPaymentIntent');\r\n }\r\n\r\n logger.debug('Payment intent created:', apiResult.data);\r\n return apiResult.data!;\r\n } catch (error) {\r\n logger.error('createPaymentIntent error:', error);\r\n return null;\r\n }\r\n}\r\n\r\n/**\r\n * Fetch an existing cart by UUID (cross-device pre-fill — §1.3).\r\n *\r\n * Returns the server's authoritative view of a cart so a second device\r\n * visiting the tickets page can see the in-progress reservation it created\r\n * on another device. Returns `null` if:\r\n * - the cart no longer exists (404)\r\n * - the cart belongs to a different event than `expectedEventID` (when\r\n * supplied) — we don't want to mix carts across events\r\n * - the cart status is no longer `reserved` / `active` (e.g. expired,\r\n * completed, abandoned) — caller should treat the local cookie as stale\r\n * - any network/decode failure (caller falls back to localStorage only)\r\n *\r\n * Returned shape matches the orders-service `GET /v2Public/cart/{uuid}`\r\n * response: a Cart model with embedded Reservations.\r\n */\r\nexport interface CartReservationView {\r\n ticketID: number;\r\n quantity: number;\r\n priceAtReservation: number;\r\n status: string;\r\n}\r\nexport interface CartView {\r\n uuid: string;\r\n eventID: number;\r\n status: string;\r\n expiresAt: string;\r\n reservations: CartReservationView[];\r\n}\r\n\r\nexport async function getCartByUUID(\r\n cartUUID: string,\r\n expectedEventID?: string | number\r\n): Promise<CartView | null> {\r\n if (!cartUUID) return null;\r\n try {\r\n type RawCart = {\r\n UUID?: string; uuid?: string;\r\n EventID?: number; eventID?: number;\r\n Status?: string; status?: string;\r\n ExpiresAt?: string; expiresAt?: string;\r\n Reservations?: unknown[]; reservations?: unknown[];\r\n };\r\n const apiResult = await apiGet<RawCart>(\r\n `${getOrdersV2Url()}/cart/${cartUUID}`\r\n );\r\n if (!apiResult.success) {\r\n // 404 / 410 / 5xx — caller treats as stale\r\n return null;\r\n }\r\n const raw = apiResult.data!;\r\n // The orders-service Cart model uses Go-style PascalCase JSON tags\r\n // (UUID, EventID, Status, ExpiresAt, Reservations) on some routes and\r\n // camelCase on the newer v2 routes. Tolerate both.\r\n const uuid: string = raw.UUID ?? raw.uuid ?? '';\r\n const eventID = Number(raw.EventID ?? raw.eventID ?? 0);\r\n const status: string = raw.Status ?? raw.status ?? '';\r\n const expiresAt: string = raw.ExpiresAt ?? raw.expiresAt ?? '';\r\n const rawRes: unknown[] = raw.Reservations ?? raw.reservations ?? [];\r\n const reservations: CartReservationView[] = rawRes.map(r => {\r\n const rec = r as Record<string, unknown>;\r\n return {\r\n ticketID: Number(rec.TicketID ?? rec.ticketID ?? 0),\r\n quantity: Number(rec.Quantity ?? rec.quantity ?? 0),\r\n priceAtReservation: Number(\r\n rec.PriceAtReservation ?? rec.priceAtReservation ?? 0\r\n ),\r\n status: String(rec.Status ?? rec.status ?? ''),\r\n };\r\n });\r\n if (status !== 'reserved' && status !== 'active') {\r\n // stale cart row — caller clears the cookie\r\n return null;\r\n }\r\n if (expectedEventID !== undefined && eventID !== Number(expectedEventID)) {\r\n // cart belongs to a different event — don't mix\r\n return null;\r\n }\r\n return { uuid, eventID, status, expiresAt, reservations };\r\n } catch (err) {\r\n logger.error('getCartByUUID error:', err);\r\n return null;\r\n }\r\n}\r\n\r\n/**\r\n * Update cart quantities after the cart already exists.\r\n *\r\n * Replaces all reservations on the cart with the new quantities.\r\n * The orders-service handler releases old reservations (returning\r\n * inventory) and creates new ones (decrementing inventory) atomically.\r\n *\r\n * Use this when the user changes ticket counts AFTER the initial\r\n * cart was created via initiateOrder. The cart UUID is preserved,\r\n * so the timer and extension state stay intact.\r\n *\r\n * @returns true on success, false on failure\r\n */\r\nexport async function updateCartQuantities(\r\n cartId: string,\r\n quantities: Record<string | number, number>,\r\n donationAmounts?: Record<string | number, number>\r\n): Promise<boolean> {\r\n try {\r\n // Convert string keys to numbers (the orders-service handler\r\n // unmarshals into map[uint]int).\r\n const numericQuantities: Record<number, number> = {};\r\n for (const [k, v] of Object.entries(quantities)) {\r\n const n = typeof k === 'number' ? k : parseInt(k, 10);\r\n if (!Number.isNaN(n) && v > 0) numericQuantities[n] = v;\r\n }\r\n const numericDonations: Record<number, number> = {};\r\n if (donationAmounts) {\r\n for (const [k, v] of Object.entries(donationAmounts)) {\r\n const n = typeof k === 'number' ? k : parseInt(k, 10);\r\n if (!Number.isNaN(n) && v > 0) numericDonations[n] = v;\r\n }\r\n }\r\n // @fetch-escape: status-only endpoint — PUT /cart/:id returns an empty 2xx (no JSON body)\r\n // on success; the canonical apiPut runs response.json() unconditionally on 2xx and would\r\n // throw on the empty body, mis-reporting a successful cart update as a failure. Migrate once\r\n // the client tolerates 204/empty bodies.\r\n const response = await fetch(`${getOrdersV2Url()}/cart/${cartId}`, {\r\n method: 'PUT',\r\n headers: { 'Content-Type': 'application/json' },\r\n credentials: 'include',\r\n body: JSON.stringify({\r\n quantities: numericQuantities,\r\n ...(Object.keys(numericDonations).length > 0\r\n ? { donationAmounts: numericDonations }\r\n : {}),\r\n }),\r\n });\r\n if (!response.ok) {\r\n const errorData = await response.json().catch(() => ({}));\r\n logger.error('Cart update failed:', errorData);\r\n return false;\r\n }\r\n return true;\r\n } catch (err) {\r\n logger.error('updateCartQuantities error:', err);\r\n return false;\r\n }\r\n}\r\n\r\n/**\r\n * Complete reservation after successful payment\r\n *\r\n * Called after Stripe confirms the payment to finalize the order\r\n * and generate tickets.\r\n *\r\n * @param orderUuid - The order UUID\r\n * @returns Success status and message\r\n */\r\nexport async function completeReservation(\r\n orderUuid: string\r\n): Promise<CompleteReservationResponse> {\r\n try {\r\n const apiResult = await apiPost<{ message?: string }>(\r\n `${getPublicBaseUrl()}/orders/complete/${orderUuid}`\r\n );\r\n\r\n if (!apiResult.success) {\r\n return {\r\n success: false,\r\n error: apiResult.error || 'Failed to complete reservation',\r\n };\r\n }\r\n\r\n return {\r\n success: true,\r\n message: apiResult.data?.message,\r\n };\r\n } catch (error) {\r\n logger.error('Error completing reservation:', error);\r\n return { success: false, error: 'Network error completing reservation' };\r\n }\r\n}\r\n\r\n/**\r\n * Cancel reservation and release tickets back to inventory\r\n *\r\n * Called when user abandons checkout or session expires.\r\n *\r\n * @param orderUuid - The order UUID\r\n * @returns Success status and message\r\n */\r\nexport async function cancelReservation(\r\n orderUuid: string\r\n): Promise<CancelReservationResponse> {\r\n try {\r\n const apiResult = await apiPost<{ message?: string }>(\r\n `${getPublicBaseUrl()}/orders/cancel/${orderUuid}`\r\n );\r\n\r\n if (!apiResult.success) {\r\n return {\r\n success: false,\r\n error: apiResult.error || 'Failed to cancel reservation',\r\n };\r\n }\r\n\r\n return {\r\n success: true,\r\n message: apiResult.data?.message,\r\n };\r\n } catch (error) {\r\n logger.error('Error cancelling reservation:', error);\r\n return { success: false, error: 'Network error cancelling reservation' };\r\n }\r\n}\r\n\r\n/**\r\n * Create a new order/cart\r\n *\r\n * This creates an empty order that can be used for checkout.\r\n * The order UUID is used for all subsequent operations.\r\n *\r\n * @param eventId - The event ID\r\n * @param promoCode - Optional promo code to apply\r\n * @returns The order UUID, or null on error\r\n */\r\nexport async function createOrder(\r\n eventId: string | number,\r\n promoCode?: string\r\n): Promise<CreateOrderResponse | null> {\r\n try {\r\n const apiResult = await apiPost<CreateOrderResponse>(\r\n `${getPublicBaseUrl()}/orders/create`,\r\n { eventID: eventId, promoCode }\r\n );\r\n\r\n if (!apiResult.success) {\r\n logger.error('Create order failed:', apiResult.error);\r\n return null;\r\n }\r\n\r\n return apiResult.data!;\r\n } catch (error) {\r\n logger.error('createOrder error:', error);\r\n return null;\r\n }\r\n}\r\n\r\n/**\r\n * Get order details (public, no auth required)\r\n *\r\n * Fetches the order details for displaying on the success page.\r\n *\r\n * @param orderId - The order UUID or ID\r\n * @returns The order details, or null on error\r\n */\r\nexport async function getOrder(orderId: string): Promise<Order | null> {\r\n return simpleFetch<Order>(`${getPublicBaseUrl()}/orders/${orderId}`);\r\n}\r\n\r\n/**\r\n * Validate payment intent and complete the order\r\n *\r\n * This is an alternative to completeReservation that accepts\r\n * additional payment details. Used by micdrop-frontend.\r\n *\r\n * @param cartId - The cart/order UUID\r\n * @param payload - Payment validation payload\r\n * @returns Validation result\r\n */\r\nexport async function validatePaymentIntent(\r\n cartId: string,\r\n payload: ValidatePaymentRequest\r\n): Promise<ValidatePaymentResponse | null> {\r\n try {\r\n const apiResult = await apiPost<{ status?: string; orderUUID?: string; uuid?: string }>(\r\n `${getOrdersV2Url()}/validatePaymentIntent/${cartId}`,\r\n payload\r\n );\r\n\r\n if (!apiResult.success) {\r\n logger.error('Payment validation failed:', apiResult.error);\r\n return {\r\n success: false,\r\n status: 'failed',\r\n error: apiResult.error || 'Payment validation failed',\r\n };\r\n }\r\n\r\n const result = apiResult.data!;\r\n return {\r\n success: true,\r\n status: result.status || 'Payment succeeded',\r\n orderUUID: result.orderUUID || result.uuid,\r\n };\r\n } catch (error) {\r\n logger.error('validatePaymentIntent error:', error);\r\n return null;\r\n }\r\n}\r\n\r\n/**\r\n * Extend the checkout session by 15 minutes\r\n *\r\n * Users get a limited number of extensions (typically 2-3).\r\n *\r\n * @param orderUuid - The order UUID\r\n * @returns Extension result with new expiry time\r\n */\r\nexport async function extendCheckoutSession(\r\n orderUuid: string\r\n): Promise<ExtendSessionResponse> {\r\n try {\r\n const apiResult = await apiPost<{ newExpiryTime?: string; remainingExtensions?: number }>(\r\n `${getPublicBaseUrl()}/orders/extend-session`,\r\n { orderUuid }\r\n );\r\n\r\n if (!apiResult.success) {\r\n return {\r\n success: false,\r\n error: apiResult.error || 'Failed to extend session',\r\n statusCode: apiResult.statusCode,\r\n };\r\n }\r\n\r\n const result = apiResult.data!;\r\n return {\r\n success: true,\r\n newExpiryTime: result.newExpiryTime,\r\n remainingExtensions: result.remainingExtensions,\r\n };\r\n } catch (error) {\r\n logger.error('Error extending checkout session:', error);\r\n return { success: false, error: 'Network error extending session' };\r\n }\r\n}\r\n\r\n/**\r\n * Get current session status including expiry time\r\n *\r\n * Used to display countdown timer and check if extensions are available.\r\n *\r\n * @param orderUuid - The order UUID\r\n * @returns Session status including expiry time\r\n */\r\nexport async function getSessionStatus(\r\n orderUuid: string\r\n): Promise<SessionStatus> {\r\n try {\r\n const apiResult = await apiGet<SessionStatus>(\r\n `${getPublicBaseUrl()}/orders/session/${orderUuid}`\r\n );\r\n\r\n if (!apiResult.success) {\r\n return {\r\n error: apiResult.error || 'No active session found',\r\n notFound: apiResult.statusCode === 404,\r\n };\r\n }\r\n\r\n const result = apiResult.data!;\r\n return {\r\n expiresAt: result.expiresAt,\r\n extensionCount: result.extensionCount,\r\n remainingExtensions: result.remainingExtensions,\r\n canExtend: result.canExtend,\r\n reservationCount: result.reservationCount,\r\n };\r\n } catch (error) {\r\n logger.error('Error getting session status:', error);\r\n return { error: 'Network error getting session status' };\r\n }\r\n}\r\n\r\n/**\r\n * Initiate a new order (alias for createOrder)\r\n *\r\n * This function provides backwards compatibility with micdrop-frontend.\r\n * It accepts the same parameters as the legacy initiateOrder function.\r\n *\r\n * @param cartData - Object containing eventID and optional quantities/promoCode\r\n * @returns The order UUID, or null on error\r\n */\r\nexport async function initiateOrder(\r\n cartData: {\r\n eventID: string | number;\r\n promoCode?: string;\r\n quantities?: Record<string | number, number>;\r\n } = {} as any\r\n): Promise<string | null> {\r\n try {\r\n const apiResult = await apiPost<{ uuid?: string }>(\r\n `${getPublicBaseUrl()}/orders/create`,\r\n cartData\r\n );\r\n\r\n if (!apiResult.success) {\r\n logger.error('Order initiation failed:', apiResult.error);\r\n return null;\r\n }\r\n\r\n return apiResult.data?.uuid ?? null;\r\n } catch (error) {\r\n logger.error('initiateOrder error:', error);\r\n return null;\r\n }\r\n}\r\n\r\n/**\r\n * Track UTM source for analytics\r\n *\r\n * Records the traffic source (utm_source parameter) for marketing analytics.\r\n *\r\n * @param venueId - The venue ID\r\n * @returns Promise that resolves when tracking is complete\r\n */\r\nexport async function trackUTMSource(venueId: string | number): Promise<void> {\r\n if (typeof window === 'undefined') return;\r\n\r\n const urlParams = new URLSearchParams(window.location.search);\r\n const utmSource = urlParams.get('utm_source') || 'Direct';\r\n\r\n try {\r\n // @fetch-escape: fire-and-forget UTM beacon — GET with no response body consumed; simpleFetch would parse JSON on empty 200 and log false-positive errors\r\n await fetch(\r\n `${getPublicBaseUrl()}/utm/${venueId}/${encodeURIComponent(utmSource)}`\r\n );\r\n } catch (error) {\r\n logger.error('UTM tracking failed:', error);\r\n }\r\n}\r\n","/**\r\n * Promo Codes API\r\n *\r\n * Functions for validating and checking promo codes\r\n * in the public checkout flow.\r\n */\r\n\r\nimport { createLogger } from '@getmicdrop/svelte-components/utils/logger';\r\nconst logger = createLogger('VC');\r\nimport { apiGet, apiPost, getOrdersV2Url } from './client.js';\r\nimport type { PromoValidationResponse, HasPromoCodesResponse } from './types.js';\r\n\r\nconst ORDERS_V2_PUBLIC = () => getOrdersV2Url();\r\n\r\n/**\r\n * Validate a promo code for an event\r\n *\r\n * Checks if a promo code is valid and returns its effects:\r\n * - Discount amount and type\r\n * - Hidden ticket reveal\r\n *\r\n * @param eventId - The event ID\r\n * @param code - The promo code to validate\r\n * @returns Validation result with discount info\r\n */\r\nexport async function validatePromoCode(\r\n eventId: string | number,\r\n code: string\r\n): Promise<PromoValidationResponse> {\r\n try {\r\n if (!code || !code.trim()) {\r\n return { valid: false, error: 'Promo code is required' };\r\n }\r\n\r\n // API uses GET with code in URL path\r\n const encodedCode = encodeURIComponent(code.trim());\r\n const apiResult = await apiGet<PromoValidationResponse>(\r\n `/promo-codes/validate/${eventId}/${encodedCode}`\r\n );\r\n\r\n if (!apiResult.success) {\r\n // 404 = invalid code, other errors are server issues\r\n if (apiResult.statusCode === 404) {\r\n return { valid: false, error: 'Invalid promo code' };\r\n }\r\n return { valid: false, error: 'Failed to validate code' };\r\n }\r\n\r\n const result = apiResult.data!;\r\n return {\r\n valid: result.valid ?? true,\r\n revealHiddenTickets: result.revealHiddenTickets,\r\n revealTicketIds: result.revealTicketIds,\r\n provideDiscount: result.provideDiscount,\r\n discountType: result.discountType,\r\n amount: result.amount,\r\n code: result.code || code,\r\n };\r\n } catch (error) {\r\n logger.error('Error validating promo code:', error);\r\n return { valid: false, error: 'Network error validating code' };\r\n }\r\n}\r\n\r\n/**\r\n * Check if promo codes are available for an event\r\n *\r\n * Used to conditionally show/hide the promo code input field.\r\n *\r\n * @param eventId - The event ID\r\n * @returns Whether promo codes exist for this event\r\n */\r\nexport async function hasPromoCodes(eventId: string | number): Promise<boolean> {\r\n try {\r\n const apiResult = await apiGet<HasPromoCodesResponse>(\r\n `/promo-codes/check/${eventId}`\r\n );\r\n\r\n if (!apiResult.success) {\r\n // If endpoint doesn't exist or errors, default to showing the input\r\n return true;\r\n }\r\n\r\n return apiResult.data!.hasPromoCodes === true;\r\n } catch (error) {\r\n logger.error('Error checking promo codes availability:', error);\r\n // Default to showing promo input if we can't check\r\n return true;\r\n }\r\n}\r\n\r\n/**\r\n * Apply a promo code to a cart\r\n *\r\n * This updates the cart with the promo code discount.\r\n *\r\n * @param cartId - The cart UUID\r\n * @param code - The promo code to apply\r\n * @returns Success status\r\n */\r\nexport async function applyPromoCode(\r\n cartId: string,\r\n code: string\r\n): Promise<{ success: boolean; error?: string }> {\r\n try {\r\n // Backend route: POST /api/orders/v2/public/cart/{uuid}/apply-promo with a\r\n // `promoCode` body (mirrors apply-gift-card). The old path/body\r\n // (/orders/{id}/apply-promo + {code}) matched no route, so the cart's\r\n // DiscountAmount was never set and the buyer was charged full price despite\r\n // the UI showing a discount.\r\n const apiResult = await apiPost(\r\n `${ORDERS_V2_PUBLIC()}/cart/${cartId}/apply-promo`,\r\n { promoCode: code }\r\n );\r\n\r\n if (!apiResult.success) {\r\n return {\r\n success: false,\r\n error: apiResult.error || 'Failed to apply promo code',\r\n };\r\n }\r\n\r\n return { success: true };\r\n } catch (error) {\r\n logger.error('Error applying promo code:', error);\r\n return { success: false, error: 'Network error applying code' };\r\n }\r\n}\r\n\r\n/**\r\n * Remove a promo code from a cart\r\n *\r\n * @param cartId - The cart UUID\r\n * @returns Success status\r\n */\r\nexport async function removePromoCode(\r\n cartId: string\r\n): Promise<{ success: boolean; error?: string }> {\r\n try {\r\n const apiResult = await apiPost(`/orders/${cartId}/remove-promo`);\r\n\r\n if (!apiResult.success) {\r\n return {\r\n success: false,\r\n error: apiResult.error || 'Failed to remove promo code',\r\n };\r\n }\r\n\r\n return { success: true };\r\n } catch (error) {\r\n logger.error('Error removing promo code:', error);\r\n return { success: false, error: 'Network error removing code' };\r\n }\r\n}\r\n","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 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<Event | null>>();\n\n/**\n * Fetch event details\n *\n * Gets full event information including venue and ticket data.\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 // 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 () => {\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 throw new AppError(\n `Failed to fetch event details: ${response.status}`,\n 'lib/api/events/fetchEventDetails'\n );\n }\n return await response.json();\n } catch (error) {\n logger.error('Error fetching event details:', error);\n return null;\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 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 fetchEventTickets(\n eventId: string | number\n): Promise<AvailableTicket[]> {\n try {\n const tickets = await simpleFetch<AvailableTicket[]>(\n `${getPublicBaseUrl()}/tickets/event/${eventId}`\n );\n return Array.isArray(tickets) ? tickets : [];\n } catch (error) {\n logger.error('Error fetching tickets:', error);\n return [];\n }\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 fetchEventPerformers(\n eventId: string | number\n): Promise<EventPerformersResponse> {\n try {\n if (!eventId) {\n logger.warn('fetchEventPerformers called without eventId');\n return { performers: [], showPerformers: false };\n }\n\n const data = await simpleFetch<EventPerformersResponse>(\n `${getPublicBaseUrl()}/events/${eventId}/performers`\n );\n\n if (!data) {\n logger.error('Failed to fetch performers: network or server error');\n return { performers: [], showPerformers: false };\n }\n\n return {\n performers: Array.isArray(data.performers) ? data.performers : [],\n showPerformers: data.showPerformers === true,\n };\n } catch (error) {\n logger.error('Error fetching event performers:', error);\n return { performers: [], 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 fetchAllVenues(orgId: string | number): Promise<any[]> {\n try {\n if (!orgId) {\n logger.warn('fetchAllVenues called without orgId');\n return [];\n }\n\n const venues = await simpleFetch<any[]>(\n `${getPublicBaseUrl()}/venues/organization/${orgId}`\n );\n\n if (!venues) {\n logger.error('Failed to fetch venues: network or server error');\n return [];\n }\n\n return Array.isArray(venues) ? venues : [];\n } catch (error) {\n logger.error('Error fetching venues:', error);\n return [];\n }\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 fetchVenueEvents(\n venueId: string | number\n): Promise<Event[]> {\n try {\n if (!venueId) {\n logger.warn('fetchVenueEvents called without venueId');\n return [];\n }\n\n const events = await simpleFetch<Event[]>(\n `${getPublicBaseUrl()}/events/venue/${venueId}`\n );\n\n if (!events) {\n logger.error('Failed to fetch venue events: network or server error');\n return [];\n }\n\n return Array.isArray(events) ? events : [];\n } catch (error) {\n logger.error('Error fetching venue events:', error);\n return [];\n }\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 */\nexport async function getMonthEvents(\n venueId: string | number,\n year: number,\n month: number\n): Promise<Event[]> {\n try {\n const data = await simpleFetch<{ events?: Event[] } | Event[]>(\n `${getPublicBaseUrl()}/events/venue/${venueId}/month/${year}/${month}`\n );\n\n if (!data) {\n logger.error('Failed to fetch month events: network or server error');\n return [];\n }\n\n if (Array.isArray(data)) return data;\n return Array.isArray((data as { events?: Event[] }).events)\n ? (data as { events: Event[] }).events\n : [];\n } catch (error) {\n logger.error('Error fetching month events:', error);\n return [];\n }\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 getOrgMonthEvents(\n orgId: string | number,\n year: number,\n month: number\n): Promise<Event[]> {\n try {\n const data = await simpleFetch<{ events?: Event[] } | Event[]>(\n `${getPublicBaseUrl()}/events/organization/${orgId}/month/${year}/${month}`\n );\n\n if (!data) {\n logger.error('Failed to fetch org month events: network or server error');\n return [];\n }\n\n if (Array.isArray(data)) return data;\n return Array.isArray((data as { events?: Event[] }).events)\n ? (data as { events: Event[] }).events\n : [];\n } catch (error) {\n logger.error('Error fetching org month events:', error);\n return [];\n }\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 getSeriesOccurrences(\n eventSeriesId: number\n): Promise<SeriesOccurrencesResponse | null> {\n try {\n const data = await simpleFetch<SeriesOccurrencesResponse>(\n `${getPublicBaseUrl()}/series/${eventSeriesId}/occurrences`\n );\n\n if (!data) {\n logger.error(\n 'Failed to fetch series occurrences: network or server error'\n );\n return null;\n }\n\n return data;\n } catch (error) {\n logger.error('Error fetching series occurrences:', error);\n return null;\n }\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 fetchSeriesOccurrences(\n eventSeriesId: number,\n venueId: string | number\n): Promise<SeriesOccurrencesResponse | null> {\n try {\n const data = await simpleFetch<SeriesOccurrencesResponse>(\n `${getPublicBaseUrl()}/series/${eventSeriesId}/occurrences?venueId=${venueId}`\n );\n\n if (!data) {\n logger.error('Failed to fetch series with CTA: network or server error');\n return null;\n }\n\n return data;\n } catch (error) {\n logger.error('Error fetching series occurrences:', error);\n return null;\n }\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","/**\r\n * Venues API\r\n *\r\n * Functions for fetching venue data including\r\n * service fees and tax configuration.\r\n */\r\n\r\nimport { createLogger } from '@getmicdrop/svelte-components/utils/logger';\r\nconst logger = createLogger('VC');\r\nimport { apiGet } from './client.js';\r\nimport type { Venue } from './types.js';\r\n\r\n/**\r\n * Get venue details (public, no auth required)\r\n *\r\n * Fetches venue information including service fees and taxes\r\n * needed for checkout calculations.\r\n *\r\n * @param venueId - The venue ID\r\n * @returns Venue details or null on error\r\n */\r\nexport async function getVenue(venueId: string | number): Promise<Venue | null> {\r\n try {\r\n if (!venueId) {\r\n logger.warn('getVenue called without venueId');\r\n return null;\r\n }\r\n\r\n const result = await apiGet<Venue>(`/venues/${venueId}`);\r\n if (!result.success) {\r\n logger.error(`Failed to fetch venue: ${result.statusCode}`);\r\n return null;\r\n }\r\n return result.data ?? null;\r\n } catch (error) {\r\n logger.error('Error fetching venue:', error);\r\n return null;\r\n }\r\n}\r\n\r\n/**\r\n * Get venue service fee configuration\r\n *\r\n * Returns just the fee-related fields for checkout calculations.\r\n *\r\n * @param venueId - The venue ID\r\n * @returns Fee configuration or null on error\r\n */\r\nexport async function getVenueFees(\r\n venueId: string | number\r\n): Promise<{\r\n serviceFeePercentage: number;\r\n serviceFeeCents: number;\r\n taxPercentage: number;\r\n} | null> {\r\n const venue = await getVenue(venueId);\r\n\r\n if (!venue) {\r\n return null;\r\n }\r\n\r\n return {\r\n serviceFeePercentage: venue.serviceFeePercentage ?? 0,\r\n serviceFeeCents: venue.serviceFeeCents ?? 0,\r\n taxPercentage: venue.taxPercentage ?? 0,\r\n };\r\n}\r\n\r\n/**\r\n * Get venue by slug\r\n *\r\n * Fetches venue using its URL-friendly slug.\r\n *\r\n * @param slug - The venue slug\r\n * @returns Venue details or null on error\r\n */\r\nexport async function getVenueBySlug(slug: string): Promise<Venue | null> {\r\n try {\r\n if (!slug) {\r\n logger.warn('getVenueBySlug called without slug');\r\n return null;\r\n }\r\n\r\n const encodedSlug = encodeURIComponent(slug);\r\n const result = await apiGet<Venue>(`/venues/slug/${encodedSlug}`);\r\n if (!result.success) {\r\n logger.error(`Failed to fetch venue by slug: ${result.statusCode}`);\r\n return null;\r\n }\r\n return result.data ?? null;\r\n } catch (error) {\r\n logger.error('Error fetching venue by slug:', error);\r\n return null;\r\n }\r\n}\r\n","import { AppError, asVenueId } from '@getmicdrop/svelte-components';\r\nimport type { VenueId } from '@getmicdrop/svelte-components';\r\n/**\r\n * Gift Card Purchase API Client\r\n * Handles gift card purchase creation, cart application/removal,\r\n * and gift-card-only checkout completion.\r\n */\r\n\r\nimport { createLogger } from '@getmicdrop/svelte-components/utils/logger';\r\nimport { getOrdersV2Url, apiPost, apiDelete } from './client.js';\r\nconst logger = createLogger('VC');\r\n\r\nconst API_BASE_URL = 'https://get-micdrop.com';\r\nconst ORDERS_V2_URL = `${API_BASE_URL}/api/orders/v2`;\r\nconst ORDERS_V2_PUBLIC = () => getOrdersV2Url();\r\n\r\n/**\r\n * Request payload for creating a gift card purchase\r\n */\r\nexport interface GiftCardPurchaseRequest {\r\n // Public boundary — accept plain number too so external TypeScript callers\r\n // that pass `venueId: 1` keep compiling. Runtime serializes a number either\r\n // way. Internal call sites pass branded VenueId via asVenueId().\r\n venueId: number | VenueId;\r\n amount: number; // In cents (e.g., 5000 = $50.00)\r\n recipientEmail: string;\r\n recipientName: string;\r\n personalMessage?: string;\r\n purchaserEmail: string;\r\n purchaserName: string;\r\n scheduledDeliveryAt?: string | null; // ISO date string or null for immediate\r\n}\r\n\r\n/**\r\n * Response from gift card purchase creation\r\n */\r\nexport interface GiftCardPurchaseResponse {\r\n giftCardUUID: string;\r\n clientSecret: string;\r\n amount: number;\r\n stripePublishableKey: string;\r\n}\r\n\r\n/**\r\n * Venue information needed for gift card purchase\r\n */\r\nexport interface VenueInfo {\r\n id: VenueId;\r\n name: string;\r\n slug: string;\r\n stripePublishableKey?: string;\r\n}\r\n\r\n/**\r\n * Create a gift card purchase and get Stripe client secret\r\n * @param req - Gift card purchase request details\r\n * @returns Gift card UUID and Stripe client secret, or null on failure\r\n */\r\nexport async function createGiftCardPurchase(\r\n req: GiftCardPurchaseRequest\r\n): Promise<GiftCardPurchaseResponse | null> {\r\n try {\r\n const apiResult = await apiPost<GiftCardPurchaseResponse>(\r\n `${ORDERS_V2_URL}/gift-cards`,\r\n req\r\n );\r\n\r\n if (!apiResult.success) {\r\n logger.error('Gift card purchase creation failed:', apiResult.error);\r\n throw new AppError(apiResult.error || 'Failed to create gift card purchase', 'lib/api/gift-cards/createGiftCardPurchase');\r\n }\r\n\r\n logger.debug('Gift card purchase created:', apiResult.data);\r\n return apiResult.data!;\r\n } catch (error) {\r\n logger.error('Gift card purchase creation error:', error);\r\n return null;\r\n }\r\n}\r\n\r\n/**\r\n * Raw backend response from the apply-gift-card endpoint.\r\n * Source of truth: orders-service controllers/cart.go ApplyGiftCardResponse.\r\n * NOTE on units:\r\n * - giftCardBalance / appliedAmount are integer CENTS (int64 backend)\r\n * - remainingTotal is DOLLARS (float64 backend)\r\n */\r\ninterface ApplyGiftCardApiResponse {\r\n valid: boolean;\r\n message?: string;\r\n giftCardCode?: string;\r\n giftCardBalance?: number; // cents\r\n appliedAmount?: number; // cents\r\n remainingTotal?: number; // dollars\r\n fullyCoveredByCard?: boolean;\r\n}\r\n\r\n/**\r\n * Result shape consumed by the Checkout component / GiftCardInput.\r\n * Monetary fields here are in DOLLARS (formatCurrency-ready).\r\n */\r\nexport interface ApplyGiftCardResult {\r\n valid: boolean;\r\n giftCardCode?: string;\r\n giftCardAmount?: number; // dollars applied from the gift card\r\n giftCardBalance?: number; // cents (raw balance, not displayed via formatCurrency)\r\n paymentType?: string; // 'gift_card_only' (full) | 'split' (partial)\r\n stripeAmount?: number; // dollars the card must still cover (residual)\r\n orderTotal?: number; // dollars (gift card + residual)\r\n requiresStripe?: boolean;\r\n error?: string;\r\n}\r\n\r\n/**\r\n * Apply a gift card to a cart.\r\n */\r\nexport async function applyGiftCard(\r\n cartId: string,\r\n code: string\r\n): Promise<ApplyGiftCardResult> {\r\n try {\r\n const apiResult = await apiPost<ApplyGiftCardApiResponse>(\r\n `${ORDERS_V2_PUBLIC()}/cart/${cartId}/apply-gift-card`,\r\n { giftCardCode: code }\r\n );\r\n\r\n if (!apiResult.success) {\r\n if (apiResult.statusCode === 429) {\r\n return {\r\n valid: false,\r\n error: 'Too many attempts. Please wait a moment and try again.',\r\n };\r\n }\r\n logger.error('Apply gift card failed:', apiResult.error);\r\n return { valid: false, error: apiResult.error || 'Invalid gift card code' };\r\n }\r\n\r\n const data = apiResult.data!;\r\n\r\n // Map the real backend contract to what the Checkout component expects.\r\n // Backend keys: appliedAmount (cents), remainingTotal (dollars),\r\n // fullyCoveredByCard (bool), giftCardBalance (cents).\r\n const giftCardAmountDollars = (data.appliedAmount ?? 0) / 100;\r\n const residualDollars = data.remainingTotal ?? 0;\r\n // Full coverage when the card covers everything (no residual to charge).\r\n const fullyCovered = data.fullyCoveredByCard ?? residualDollars <= 0;\r\n const requiresStripe = !fullyCovered;\r\n\r\n return {\r\n valid: true,\r\n giftCardCode: data.giftCardCode,\r\n giftCardAmount: giftCardAmountDollars,\r\n giftCardBalance: data.giftCardBalance,\r\n paymentType: fullyCovered ? 'gift_card_only' : 'split',\r\n stripeAmount: residualDollars,\r\n orderTotal: giftCardAmountDollars + residualDollars,\r\n requiresStripe,\r\n };\r\n } catch (err) {\r\n logger.error('applyGiftCard error:', err);\r\n return {\r\n valid: false,\r\n error: 'Network error. Please check your connection and try again.',\r\n };\r\n }\r\n}\r\n\r\nexport interface RemoveGiftCardResult {\r\n success: boolean;\r\n error?: string;\r\n}\r\n\r\n/**\r\n * Remove a gift card from a cart.\r\n */\r\nexport async function removeGiftCard(cartId: string): Promise<RemoveGiftCardResult> {\r\n try {\r\n const apiResult = await apiDelete(`${ORDERS_V2_PUBLIC()}/cart/${cartId}/gift-card`);\r\n\r\n if (!apiResult.success) {\r\n logger.error('Remove gift card failed:', apiResult.error);\r\n return { success: false, error: apiResult.error || 'Failed to remove gift card' };\r\n }\r\n\r\n return { success: true };\r\n } catch (err) {\r\n logger.error('removeGiftCard error:', err);\r\n return {\r\n success: false,\r\n error: 'Network error. Please check your connection and try again.',\r\n };\r\n }\r\n}\r\n\r\nexport interface CompleteGiftCardPaymentInput {\r\n firstName: string;\r\n lastName: string;\r\n email: string;\r\n phoneNumber?: string | null;\r\n mailingList?: boolean;\r\n}\r\n\r\nexport interface CompleteGiftCardPaymentResult {\r\n success: boolean;\r\n orderId?: string;\r\n confirmationNumber?: string;\r\n error?: string;\r\n}\r\n\r\n/**\r\n * Complete a gift-card-only payment (no Stripe required when balance >= total).\r\n */\r\nexport async function completeGiftCardPayment(\r\n cartId: string,\r\n customerDetails: CompleteGiftCardPaymentInput\r\n): Promise<CompleteGiftCardPaymentResult> {\r\n try {\r\n const apiResult = await apiPost<{ orderId?: string; confirmationNumber?: string }>(\r\n `${ORDERS_V2_PUBLIC()}/cart/${cartId}/complete-gift-card-payment`,\r\n {\r\n firstName: customerDetails.firstName,\r\n lastName: customerDetails.lastName,\r\n email: customerDetails.email,\r\n phoneNumber: customerDetails.phoneNumber || null,\r\n mailingList: customerDetails.mailingList || false,\r\n }\r\n );\r\n\r\n if (!apiResult.success) {\r\n logger.error('Complete gift card payment failed:', apiResult.error);\r\n return { success: false, error: apiResult.error || 'Failed to complete payment' };\r\n }\r\n\r\n return {\r\n success: true,\r\n orderId: apiResult.data?.orderId,\r\n confirmationNumber: apiResult.data?.confirmationNumber,\r\n };\r\n } catch (err) {\r\n logger.error('completeGiftCardPayment error:', err);\r\n return {\r\n success: false,\r\n error: 'Network error. Please check your connection and try again.',\r\n };\r\n }\r\n}\r\n\r\n/**\r\n * Fetch venue information by slug\r\n * @param slug - Venue slug\r\n * @returns Venue info or null on failure\r\n */\r\nexport async function getVenueBySlug(\r\n slug: string,\r\n customFetch: typeof fetch = fetch\r\n): Promise<VenueInfo | null> {\r\n try {\r\n // @fetch-escape: customFetch injection for SSR context — cannot use apiGet (caller-supplied fetch instance)\r\n const response = await customFetch(\r\n `${API_BASE_URL}/api/v2/public/venues/slug/${slug}`\r\n );\r\n\r\n if (!response.ok) {\r\n logger.error(`Failed to fetch venue by slug: ${response.status}`);\r\n return null;\r\n }\r\n\r\n const venue = await response.json();\r\n return {\r\n id: asVenueId(venue.id),\r\n name: venue.name,\r\n slug: venue.slug,\r\n stripePublishableKey: venue.stripePublishableKey,\r\n };\r\n } catch (error) {\r\n logger.error('Error fetching venue by slug:', error);\r\n return null;\r\n }\r\n}\r\n","/**\r\n * Waitlist API\r\n *\r\n * Endpoints for the sold-out-event waitlist flow.\r\n */\r\n\r\nimport { createLogger } from '@getmicdrop/svelte-components/utils/logger';\r\nimport { apiPost, apiGet } from './client.js';\r\n\r\nconst logger = createLogger('VC');\r\n\r\nexport interface JoinWaitlistResult {\r\n success: boolean;\r\n // No position — opaque to the customer (decision #2/#20).\r\n updated?: boolean;\r\n error?: string;\r\n}\r\n\r\nconst WAITLIST_ERROR_MESSAGES: Record<string, string> = {\r\n waitlist_not_enabled: 'Waitlist is not available for this event',\r\n event_not_found: 'Event not found',\r\n invalid_phone: 'Please enter a valid phone number',\r\n already_purchased: 'You have already purchased tickets for this event',\r\n phone_already_registered: 'This phone number is already on the waitlist',\r\n};\r\n\r\n/**\r\n * Join the waitlist for a sold-out event.\r\n */\r\nexport async function joinWaitlist(\r\n eventId: string | number,\r\n email: string,\r\n phone: string | null = null,\r\n smsOptIn = false\r\n): Promise<JoinWaitlistResult> {\r\n try {\r\n const body: Record<string, unknown> = { email };\r\n if (phone && smsOptIn) {\r\n body.phone = phone;\r\n body.smsOptIn = true;\r\n }\r\n\r\n const apiResult = await apiPost<{ updated?: boolean }>(\r\n `/events/${eventId}/waitlist`,\r\n body\r\n );\r\n\r\n if (!apiResult.success) {\r\n logger.error('Join waitlist failed:', apiResult.error);\r\n return {\r\n success: false,\r\n error:\r\n WAITLIST_ERROR_MESSAGES[apiResult.error ?? ''] ||\r\n 'Failed to join waitlist. Please try again.',\r\n };\r\n }\r\n\r\n return {\r\n success: true,\r\n updated: apiResult.data?.updated,\r\n };\r\n } catch (err) {\r\n logger.error('joinWaitlist error:', err);\r\n return {\r\n success: false,\r\n error: 'Network error. Please check your connection and try again.',\r\n };\r\n }\r\n}\r\n\r\nexport interface WaitlistStatusResult {\r\n waitlistEnabled: boolean;\r\n soldOut: boolean;\r\n /**\r\n * When gated, the public event page should present \"Sold Out — join the\r\n * waitlist\" to non-members even if tickets are technically available, and only\r\n * a valid tokenized link unlocks checkout (reconciled decision #3).\r\n */\r\n gated: boolean;\r\n}\r\n\r\n/**\r\n * Fetch an event's waitlist gating status. Returns a non-gated default if the\r\n * request fails so the page degrades to normal behavior.\r\n */\r\nexport async function getWaitlistStatus(\r\n eventId: string | number\r\n): Promise<WaitlistStatusResult> {\r\n const fallback: WaitlistStatusResult = {\r\n waitlistEnabled: false,\r\n soldOut: false,\r\n gated: false,\r\n };\r\n try {\r\n const apiResult = await apiGet<{ waitlistEnabled: boolean; soldOut: boolean; gated: boolean }>(\r\n `/events/${eventId}/waitlist/status`\r\n );\r\n if (!apiResult.success) return fallback;\r\n return {\r\n waitlistEnabled: !!apiResult.data?.waitlistEnabled,\r\n soldOut: !!apiResult.data?.soldOut,\r\n gated: !!apiResult.data?.gated,\r\n };\r\n } catch (err) {\r\n logger.error('getWaitlistStatus error:', err);\r\n return fallback;\r\n }\r\n}\r\n","/**\r\n * CTA state computation.\r\n *\r\n * Pure function (no I/O) — derives the buy-button state from an event and\r\n * its ticket list. Lives in `api/` because it is the canonical, single\r\n * source of truth that replaces the duplicate implementations previously\r\n * scattered across utils/api.js, core/api-client.js, and a few components.\r\n */\r\n\r\n// Import from the specific subpath (not the bare entry) so the api/\r\n// bundle doesn't pull in the entire SC component graph (Accordion etc.)\r\n// and break `npm run build:api`.\r\nimport {\r\n formatDateRaw,\r\n formatTime,\r\n} from '@getmicdrop/svelte-components/utils/formatters';\r\n\r\nexport interface CtaStateOptions {\r\n cancelled?: boolean;\r\n isRegistration?: boolean;\r\n waitlistEnabled?: boolean;\r\n}\r\n\r\nexport interface CtaState {\r\n text: string;\r\n disabled: boolean;\r\n reason:\r\n | 'cancelled'\r\n | 'event_past'\r\n | 'no_tickets'\r\n | 'available'\r\n | 'coming_soon'\r\n | 'sold_out_waitlist'\r\n | 'sold_out'\r\n | 'sales_ended'\r\n | 'hidden_only';\r\n}\r\n\r\ninterface CtaEvent {\r\n startDateTime?: string | null;\r\n endDateTime?: string | null;\r\n hasPurchasableHiddenTickets?: boolean;\r\n}\r\n\r\ninterface CtaTicket {\r\n salesChannel?: number;\r\n salesBegin?: string | null;\r\n salesStart?: string | null;\r\n saleBegin?: string | null;\r\n onSaleStart?: string | null;\r\n salesEnd?: string | null;\r\n saleEnd?: string | null;\r\n onSaleEnd?: string | null;\r\n remainingCapacity?: number | null;\r\n quantityRemaining?: number | null;\r\n quantity?: number | null;\r\n soldOut?: boolean;\r\n isHidden?: boolean;\r\n visibility?: number;\r\n}\r\n\r\n// A hidden ticket (visibility>=2 or isHidden) that's still on sale drives the\r\n// \"Sales ended\" / \"Sold out\" / \"No tickets\" → \"Get tickets\" override. The user\r\n// just needs the promo code to see it.\r\nexport function isHiddenTicketPurchasable(ticket: CtaTicket): boolean {\r\n if (!ticket) return false;\r\n const isHidden =\r\n ticket.isHidden === true ||\r\n (typeof ticket.visibility === 'number' && ticket.visibility >= 2);\r\n if (!isHidden) return false;\r\n if (ticket.salesChannel === 2) return false;\r\n\r\n const now = new Date();\r\n const salesBegin =\r\n ticket.salesBegin ||\r\n ticket.salesStart ||\r\n ticket.saleBegin ||\r\n ticket.onSaleStart;\r\n const salesEnd = ticket.salesEnd || ticket.saleEnd || ticket.onSaleEnd;\r\n if (salesBegin && new Date(salesBegin) > now) return false;\r\n if (salesEnd && new Date(salesEnd) < now) return false;\r\n\r\n const remaining =\r\n ticket.remainingCapacity ?? ticket.quantityRemaining ?? ticket.quantity;\r\n const isSoldOut =\r\n ticket.soldOut ||\r\n (remaining !== null && remaining !== undefined && remaining <= 0);\r\n if (isSoldOut) return false;\r\n\r\n return true;\r\n}\r\n\r\nexport function computeCtaState(\r\n event: CtaEvent,\r\n tickets: CtaTicket[] | undefined,\r\n options: CtaStateOptions = {}\r\n): CtaState {\r\n const {\r\n cancelled = false,\r\n isRegistration = false,\r\n waitlistEnabled = false,\r\n } = options;\r\n const now = new Date();\r\n\r\n if (cancelled)\r\n return { text: 'Cancelled', disabled: true, reason: 'cancelled' };\r\n\r\n const eventEnd = event.endDateTime || event.startDateTime;\r\n if (eventEnd && new Date(eventEnd) < now) {\r\n return { text: 'Sales ended', disabled: true, reason: 'event_past' };\r\n }\r\n\r\n const hiddenAvailable =\r\n event?.hasPurchasableHiddenTickets === true ||\r\n (Array.isArray(tickets) && tickets.some(isHiddenTicketPurchasable));\r\n\r\n const publicTickets = (tickets || []).filter(\r\n t =>\r\n t.salesChannel !== 2 &&\r\n !t.isHidden &&\r\n !(typeof t.visibility === 'number' && t.visibility >= 2)\r\n );\r\n if (publicTickets.length === 0) {\r\n if (hiddenAvailable) {\r\n return {\r\n text: isRegistration ? 'Reserve a spot' : 'Get tickets',\r\n disabled: false,\r\n reason: 'hidden_only',\r\n };\r\n }\r\n return {\r\n text: 'No tickets available',\r\n disabled: true,\r\n reason: 'no_tickets',\r\n };\r\n }\r\n\r\n let hasPurchasable = false;\r\n let allSoldOut = true;\r\n let allComingSoon = true;\r\n let earliestSalesStart: Date | null = null;\r\n\r\n for (const ticket of publicTickets) {\r\n const salesBegin =\r\n ticket.salesBegin ||\r\n ticket.salesStart ||\r\n ticket.saleBegin ||\r\n ticket.onSaleStart;\r\n const salesEnd = ticket.salesEnd || ticket.saleEnd || ticket.onSaleEnd;\r\n const remaining =\r\n ticket.remainingCapacity ?? ticket.quantityRemaining ?? ticket.quantity;\r\n const isSoldOut =\r\n ticket.soldOut ||\r\n (remaining !== null && remaining !== undefined && remaining <= 0);\r\n\r\n const isScheduled = salesBegin ? new Date(salesBegin) > now : false;\r\n const hasSalesEnded = salesEnd ? new Date(salesEnd) < now : false;\r\n const isPurchasable = !isSoldOut && !isScheduled && !hasSalesEnded;\r\n\r\n if (isPurchasable) hasPurchasable = true;\r\n if (!isSoldOut) allSoldOut = false;\r\n if (!isScheduled) allComingSoon = false;\r\n\r\n if (isScheduled && salesBegin && !isSoldOut && !hasSalesEnded) {\r\n const startDate = new Date(salesBegin);\r\n if (!earliestSalesStart || startDate < earliestSalesStart) {\r\n earliestSalesStart = startDate;\r\n }\r\n }\r\n }\r\n\r\n if (hasPurchasable) {\r\n return {\r\n text: isRegistration ? 'Reserve a spot' : 'Get tickets',\r\n disabled: false,\r\n reason: 'available',\r\n };\r\n }\r\n\r\n if (allComingSoon && earliestSalesStart) {\r\n if (hiddenAvailable) {\r\n return {\r\n text: isRegistration ? 'Reserve a spot' : 'Get tickets',\r\n disabled: false,\r\n reason: 'hidden_only',\r\n };\r\n }\r\n const diffMs = earliestSalesStart.getTime() - now.getTime();\r\n const diffHours = diffMs / (1000 * 60 * 60);\r\n const diffDays = diffMs / (1000 * 60 * 60 * 24);\r\n let dateText: string;\r\n if (diffHours < 24) {\r\n dateText = formatTime(earliestSalesStart, { hour12: true }, 'en-US');\r\n } else if (diffDays <= 7) {\r\n dateText = formatDateRaw(\r\n earliestSalesStart,\r\n { weekday: 'short', hour: 'numeric', minute: '2-digit', hour12: true },\r\n 'en-US'\r\n );\r\n } else {\r\n const d = formatDateRaw(\r\n earliestSalesStart,\r\n { month: 'short', day: 'numeric' },\r\n 'en-US'\r\n );\r\n const t = formatTime(earliestSalesStart, { hour12: true }, 'en-US');\r\n dateText = `${d} ${t}`;\r\n }\r\n return {\r\n text: `On sale ${dateText}`,\r\n disabled: true,\r\n reason: 'coming_soon',\r\n };\r\n }\r\n\r\n if (allSoldOut) {\r\n if (hiddenAvailable) {\r\n return {\r\n text: isRegistration ? 'Reserve a spot' : 'Get tickets',\r\n disabled: false,\r\n reason: 'hidden_only',\r\n };\r\n }\r\n if (waitlistEnabled) {\r\n return {\r\n text: 'Join Waitlist',\r\n disabled: false,\r\n reason: 'sold_out_waitlist',\r\n };\r\n }\r\n return { text: 'Sold out', disabled: true, reason: 'sold_out' };\r\n }\r\n\r\n if (hiddenAvailable) {\r\n return {\r\n text: isRegistration ? 'Reserve a spot' : 'Get tickets',\r\n disabled: false,\r\n reason: 'hidden_only',\r\n };\r\n }\r\n\r\n return { text: 'Sales ended', disabled: true, reason: 'sales_ended' };\r\n}\r\n","// @currency-escape: widget-intentional. \r\nimport type { USD } from '@getmicdrop/svelte-components';\r\nimport { formatCurrency } from '@getmicdrop/svelte-components';\r\n/**\r\n * Order Transformer\r\n *\r\n * Normalizes API order responses to a consistent format.\r\n * Handles variations in field names between different API versions.\r\n *\r\n * Wire->domain boundary: one parseOrder(raw: unknown) entry point.\r\n * The wire field shapes (ApiOrder / ApiTicket) are named only inside this\r\n * module and never escape it.\r\n *\r\n * IMPLEMENTATION NOTE -- why no Zod schema here:\r\n * The old mapper used `||` for id and ALL money fields (totalAmount, serviceFeesAmount,\r\n * taxAmount, discount -- 0 falls through to next variant). The old mapper did NOT use\r\n * `??` for money; that was a bug in the first Zod-based rewrite. Replicated verbatim\r\n * for byte-identical behavior. Throw behavior is identical to the old mapper: null/\r\n * null/undefined input or a non-array (the latter throws on .map) tickets field throws exactly as the old code did.\r\n * Callers always pass real wire objects.\r\n */\r\n\r\nimport type { Order, PurchasedTicket } from '../types.js';\r\n\r\n// ── Wire shapes (internal -- never exported) ─────────────────────────────\r\ninterface ApiTicket {\r\n uuid?: string;\r\n id?: number;\r\n ID?: number;\r\n ticketNumber?: string;\r\n ticket_number?: string;\r\n orderId?: string | number;\r\n order_id?: string | number;\r\n attendeeFirstName?: string;\r\n attendee_first_name?: string;\r\n firstName?: string;\r\n attendeeLastName?: string;\r\n attendee_last_name?: string;\r\n lastName?: string;\r\n attendeeEmail?: string;\r\n attendee_email?: string;\r\n email?: string;\r\n ticketName?: string;\r\n ticket_name?: string;\r\n name?: string;\r\n ticketTypeId?: number;\r\n ticket_type_id?: number;\r\n purchasePrice?: number;\r\n purchase_price?: number;\r\n price?: number;\r\n status?: string;\r\n checkedIn?: boolean;\r\n checked_in?: boolean;\r\n checkedInAt?: string;\r\n checked_in_at?: string;\r\n}\r\n\r\ninterface ApiOrder {\r\n uuid?: string;\r\n id?: number;\r\n ID?: number;\r\n customerEmail?: string;\r\n email?: string;\r\n customer_email?: string;\r\n customerFirstName?: string;\r\n firstName?: string;\r\n first_name?: string;\r\n customerLastName?: string;\r\n lastName?: string;\r\n last_name?: string;\r\n status?: string;\r\n totalAmount?: number;\r\n total?: number;\r\n total_amount?: number;\r\n subtotal?: number;\r\n serviceFeesAmount?: number;\r\n serviceFee?: number;\r\n service_fee?: number;\r\n service_fees_amount?: number;\r\n taxAmount?: number;\r\n tax?: number;\r\n tax_amount?: number;\r\n discount?: number;\r\n paymentIntentId?: string;\r\n payment_intent_id?: string;\r\n paymentMethod?: string;\r\n payment_method?: string;\r\n purchasedTickets?: any[];\r\n tickets?: any[];\r\n purchased_tickets?: any[];\r\n createdAt?: string;\r\n created_at?: string;\r\n updatedAt?: string;\r\n updated_at?: string;\r\n}\r\n\r\n/**\r\n * Parse a raw purchased-ticket payload into the normalized PurchasedTicket domain shape.\r\n *\r\n * Wire shape (ApiTicket) is contained inside this module. Uses `||` chains matching\r\n * the old transformer exactly -- byte-identical to previous behavior. Throw behavior\r\n * preserved from old mapper: null/undefined input (property access on a nullish value) throws as the old code did.\r\n */\r\nexport function parsePurchasedTicket(raw: unknown): PurchasedTicket {\r\n const apiTicket = raw as ApiTicket;\r\n return {\r\n uuid: apiTicket.uuid || String(apiTicket.id || apiTicket.ID || ''),\r\n id: apiTicket.id || apiTicket.ID,\r\n ticketNumber: apiTicket.ticketNumber || apiTicket.ticket_number,\r\n orderId: apiTicket.orderId || apiTicket.order_id,\r\n attendeeFirstName:\r\n apiTicket.attendeeFirstName ||\r\n apiTicket.attendee_first_name ||\r\n apiTicket.firstName,\r\n attendeeLastName:\r\n apiTicket.attendeeLastName ||\r\n apiTicket.attendee_last_name ||\r\n apiTicket.lastName,\r\n attendeeEmail:\r\n apiTicket.attendeeEmail || apiTicket.attendee_email || apiTicket.email,\r\n ticketName:\r\n apiTicket.ticketName || apiTicket.ticket_name || apiTicket.name || '',\r\n ticketTypeId: apiTicket.ticketTypeId || apiTicket.ticket_type_id,\r\n purchasePrice:\r\n apiTicket.purchasePrice ||\r\n apiTicket.purchase_price ||\r\n apiTicket.price ||\r\n 0,\r\n status: apiTicket.status,\r\n checkedIn: apiTicket.checkedIn || apiTicket.checked_in,\r\n checkedInAt: apiTicket.checkedInAt || apiTicket.checked_in_at,\r\n };\r\n}\r\n\r\n/**\r\n * Parse a raw order payload into the normalized Order domain shape.\r\n *\r\n * Wire shape (ApiOrder) is contained inside this module and never escapes it.\r\n * Uses `||` chains matching the old transformer exactly (all money fields use `||`,\r\n * not `??` -- 0-valued amounts fall through to next variant, same as old) --\r\n * byte-identical to previous behavior. Throw behavior preserved from old mapper:\r\n * null/undefined input (property access on a nullish value) or a non-array tickets field throws as the old code did.\r\n */\r\nexport function parseOrder(raw: unknown): Order {\r\n const apiOrder = raw as ApiOrder;\r\n\r\n // Extract tickets array from various possible field names\r\n const rawTickets =\r\n apiOrder.purchasedTickets ||\r\n apiOrder.tickets ||\r\n apiOrder.purchased_tickets ||\r\n [];\r\n\r\n return {\r\n uuid: apiOrder.uuid || String(apiOrder.id || apiOrder.ID || ''),\r\n id: apiOrder.id || apiOrder.ID,\r\n customerEmail:\r\n apiOrder.customerEmail ||\r\n apiOrder.email ||\r\n apiOrder.customer_email ||\r\n '',\r\n customerFirstName:\r\n apiOrder.customerFirstName ||\r\n apiOrder.firstName ||\r\n apiOrder.first_name,\r\n customerLastName:\r\n apiOrder.customerLastName ||\r\n apiOrder.lastName ||\r\n apiOrder.last_name,\r\n status: apiOrder.status || 'unknown',\r\n totalAmount:\r\n apiOrder.totalAmount ||\r\n apiOrder.total ||\r\n apiOrder.total_amount ||\r\n 0,\r\n subtotal: apiOrder.subtotal,\r\n serviceFeesAmount:\r\n apiOrder.serviceFeesAmount ||\r\n apiOrder.serviceFee ||\r\n apiOrder.service_fee ||\r\n apiOrder.service_fees_amount ||\r\n 0,\r\n taxAmount:\r\n apiOrder.taxAmount || apiOrder.tax || apiOrder.tax_amount || 0,\r\n discount: apiOrder.discount || 0,\r\n paymentIntentId:\r\n apiOrder.paymentIntentId || apiOrder.payment_intent_id,\r\n paymentMethod: apiOrder.paymentMethod || apiOrder.payment_method,\r\n purchasedTickets: rawTickets.map(parsePurchasedTicket),\r\n createdAt: apiOrder.createdAt || apiOrder.created_at,\r\n updatedAt: apiOrder.updatedAt || apiOrder.updated_at,\r\n };\r\n}\r\n\r\n/**\r\n * Back-compat alias -- existing call sites using transformTicket continue\r\n * to work unchanged.\r\n */\r\nexport const transformTicket = parsePurchasedTicket;\r\n\r\n/**\r\n * Back-compat alias -- existing call sites using transformOrder(apiOrder)\r\n * continue to work unchanged.\r\n */\r\nexport const transformOrder = parseOrder;\r\n\r\n/**\r\n * Transform order for display on success page.\r\n *\r\n * Returns a simplified object with display-ready values.\r\n */\r\nexport function transformOrderForDisplay(raw: unknown): {\r\n orderId: string;\r\n displayOrderId: string;\r\n email: string;\r\n customerName: string;\r\n total: number;\r\n formattedTotal: string;\r\n ticketCount: number;\r\n status: string;\r\n} {\r\n const order = parseOrder(raw);\r\n const displayId = order.uuid.split('-')[0] || order.uuid;\r\n\r\n return {\r\n orderId: order.uuid,\r\n displayOrderId: displayId,\r\n email: order.customerEmail,\r\n customerName: [order.customerFirstName, order.customerLastName]\r\n .filter(Boolean)\r\n .join(' '),\r\n total: order.totalAmount,\r\n formattedTotal: `${formatCurrency((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. */)}`,\r\n ticketCount: order.purchasedTickets.length,\r\n status: order.status,\r\n };\r\n}\r\n","/**\r\n * Event Transformer\r\n *\r\n * Normalizes API event responses to a consistent format.\r\n * Handles image URL resolution and CTA state calculation.\r\n *\r\n * Wire->domain boundary: one parse<Entity>(raw: unknown) per entity.\r\n * The wire field shape (ApiEvent / ApiTicket interface) is named only inside\r\n * this module and never escapes it. Above the boundary only Event / AvailableTicket.\r\n *\r\n * IMPLEMENTATION NOTE — why no Zod schema here:\r\n * The old mappers used `||` (falsy fallthrough) for every primitive field, so\r\n * {venueId:0, venue_id:7} -> 7, {isHidden:false, is_hidden:true} -> true, etc.\r\n * Zod z.number()/z.boolean() would throw on wrong primitive types; firstDefined\r\n * (??) would silently mis-select 0/false values. This implementation replicates\r\n * the old `||` chains verbatim, wrapped in a typed parse<Entity>(raw: unknown)\r\n * boundary. Throw behavior is identical to the old mapper: null/undefined input (property access on a nullish value)\r\n * or a non-array where .map is called throws exactly as the old code did. Callers\r\n * always pass real wire objects.\r\n */\r\n\r\nimport type { Event, AvailableTicket } from '../types.js';\r\n\r\n// CDN base URL for images\r\nconst CDN_BASE_URL = 'https://micdrop-images.sfo3.digitaloceanspaces.com';\r\n\r\n// ── Wire shape (internal — never exported) ───────────────────────────────\r\ninterface ApiTicket {\r\n id?: number;\r\n ID?: number;\r\n name?: string;\r\n ticketName?: string;\r\n description?: string;\r\n price?: number;\r\n quantity?: number;\r\n totalQuantity?: number;\r\n quantitySold?: number;\r\n quantity_sold?: number;\r\n quantityAvailable?: number;\r\n quantity_available?: number;\r\n minPerOrder?: number;\r\n min_per_order?: number;\r\n maxPerOrder?: number;\r\n max_per_order?: number;\r\n saleStartDate?: string;\r\n sale_start_date?: string;\r\n saleEndDate?: string;\r\n sale_end_date?: string;\r\n isHidden?: boolean;\r\n is_hidden?: boolean;\r\n revealWithPromoCode?: boolean;\r\n reveal_with_promo_code?: boolean;\r\n ticketType?: number;\r\n ticket_type?: number;\r\n sectionId?: number;\r\n section_id?: number;\r\n sortOrder?: number;\r\n sort_order?: number;\r\n}\r\n\r\ninterface ApiEvent {\r\n eventID?: number;\r\n id?: number;\r\n ID?: number;\r\n name?: string;\r\n title?: string;\r\n slug?: string;\r\n description?: string;\r\n date?: string;\r\n startDateTime?: string;\r\n start_date_time?: string;\r\n endDateTime?: string;\r\n end_date_time?: string;\r\n doorsOpenTime?: string;\r\n doors_open_time?: string;\r\n timezone?: string;\r\n time_zone?: string;\r\n venueId?: number;\r\n venue_id?: number;\r\n venueName?: string;\r\n venue_name?: string;\r\n venueAddress?: string;\r\n venue_address?: string;\r\n location?: string;\r\n imageUrl?: string;\r\n imageURL?: string;\r\n image_url?: string;\r\n image?: string;\r\n status?: string;\r\n isPublished?: boolean;\r\n is_published?: boolean;\r\n isCancelled?: boolean;\r\n is_cancelled?: boolean;\r\n availableTickets?: any[];\r\n available_tickets?: any[];\r\n tickets?: any[];\r\n ticketsAvailable?: number;\r\n tickets_available?: number;\r\n ticketsSold?: number;\r\n tickets_sold?: number;\r\n minPrice?: number;\r\n min_price?: number;\r\n maxPrice?: number;\r\n max_price?: number;\r\n ctaText?: string;\r\n cta_text?: string;\r\n ctaState?: string;\r\n cta_state?: string;\r\n showPerformers?: boolean;\r\n show_performers?: boolean;\r\n eventSeriesId?: number;\r\n event_series_id?: number;\r\n seriesInstanceNumber?: number;\r\n series_instance_number?: number;\r\n}\r\n\r\n/**\r\n * Get CDN image URL\r\n *\r\n * Prepends the CDN base URL if the path is relative.\r\n */\r\nexport function getCDNImageUrl(path: string | undefined): string {\r\n if (!path) return '';\r\n if (path.startsWith('http')) return path;\r\n return `${CDN_BASE_URL}/${path.replace(/^\\//, '')}`;\r\n}\r\n\r\n/**\r\n * Extract image URL from event with fallbacks.\r\n *\r\n * NOTE: shadows SC's hoisted `getEventImageUrl` (surfaced once VC pinned SC\r\n * 6.24.78 for EventCard). NOT swapped to SC's yet because the field coverage\r\n * differs -- this transformer reads `imageUrl -> imageURL -> image_url -> image`\r\n * (incl. the camelCase `imageURL`, asserted by event.test.ts), whereas SC reads\r\n * `image -> coverImage -> imageUrl -> image_url` (no `imageURL`, different\r\n * precedence). Reconciling those is consolidation audit #4/#5's job; deferring\r\n * here keeps the API-transformer behavior unchanged.\r\n */\r\n// eslint-disable-next-line micdrop/no-shadowed-helper -- see note above: SC's getEventImageUrl has different field coverage; consolidation deferred to audit #4/#5\r\nexport function getEventImageUrl(apiEvent: ApiEvent): string {\r\n const rawUrl =\r\n apiEvent.imageUrl ||\r\n apiEvent.imageURL ||\r\n apiEvent.image_url ||\r\n apiEvent.image ||\r\n '';\r\n\r\n return getCDNImageUrl(rawUrl);\r\n}\r\n\r\n/**\r\n * Calculate CTA state based on ticket availability\r\n */\r\nexport function calculateCtaState(\r\n apiEvent: ApiEvent\r\n): 'available' | 'sold_out' | 'coming_soon' | 'ended' {\r\n // Check explicit CTA state first\r\n const explicitState = apiEvent.ctaState || apiEvent.cta_state;\r\n if (explicitState) {\r\n return explicitState as any;\r\n }\r\n\r\n // Check if event is cancelled or ended\r\n if (apiEvent.isCancelled || apiEvent.is_cancelled) {\r\n return 'ended';\r\n }\r\n\r\n // Check event date\r\n const eventDate = apiEvent.startDateTime || apiEvent.start_date_time || apiEvent.date;\r\n if (eventDate) {\r\n const eventTime = new Date(eventDate).getTime();\r\n const now = Date.now();\r\n\r\n if (eventTime < now) {\r\n return 'ended';\r\n }\r\n }\r\n\r\n // Check ticket availability\r\n const availableCount =\r\n apiEvent.ticketsAvailable ?? apiEvent.tickets_available;\r\n\r\n if (availableCount !== undefined) {\r\n if (availableCount <= 0) {\r\n return 'sold_out';\r\n }\r\n return 'available';\r\n }\r\n\r\n // Check tickets array\r\n const tickets =\r\n apiEvent.availableTickets ||\r\n apiEvent.available_tickets ||\r\n apiEvent.tickets ||\r\n [];\r\n\r\n if (tickets.length === 0) {\r\n return 'coming_soon';\r\n }\r\n\r\n const totalAvailable = tickets.reduce((sum: number, t: any) => {\r\n const qty =\r\n t.quantityAvailable ?? t.quantity_available ?? t.quantity ?? 0;\r\n const sold = t.quantitySold ?? t.quantity_sold ?? 0;\r\n return sum + Math.max(0, qty - sold);\r\n }, 0);\r\n\r\n if (totalAvailable <= 0) {\r\n return 'sold_out';\r\n }\r\n\r\n return 'available';\r\n}\r\n\r\n/**\r\n * Parse a raw ticket payload into the normalized AvailableTicket domain shape.\r\n *\r\n * Wire shape is contained inside this module. Uses `||` chains matching the\r\n * old transformer exactly -- 0/false values fall through to the next variant\r\n * or default, byte-identical to previous behavior.\r\n */\r\nexport function parseTicket(raw: unknown): AvailableTicket {\r\n const apiTicket = raw as ApiTicket;\r\n return {\r\n id: apiTicket.id || apiTicket.ID,\r\n name: apiTicket.name || apiTicket.ticketName || '',\r\n description: apiTicket.description,\r\n price: apiTicket.price || 0,\r\n quantity: apiTicket.quantity || apiTicket.totalQuantity || 0,\r\n quantitySold: apiTicket.quantitySold || apiTicket.quantity_sold || 0,\r\n quantityAvailable:\r\n apiTicket.quantityAvailable ||\r\n apiTicket.quantity_available ||\r\n (apiTicket.quantity || 0) - (apiTicket.quantitySold || 0),\r\n minPerOrder: apiTicket.minPerOrder || apiTicket.min_per_order || 1,\r\n maxPerOrder: apiTicket.maxPerOrder || apiTicket.max_per_order || 10,\r\n saleStartDate: apiTicket.saleStartDate || apiTicket.sale_start_date,\r\n saleEndDate: apiTicket.saleEndDate || apiTicket.sale_end_date,\r\n isHidden: apiTicket.isHidden || apiTicket.is_hidden || false,\r\n revealWithPromoCode:\r\n apiTicket.revealWithPromoCode || apiTicket.reveal_with_promo_code,\r\n ticketType: apiTicket.ticketType || apiTicket.ticket_type || 0,\r\n sectionId: apiTicket.sectionId || apiTicket.section_id,\r\n sortOrder: apiTicket.sortOrder || apiTicket.sort_order || 0,\r\n };\r\n}\r\n\r\n/**\r\n * Parse a raw event payload into the normalized Event domain shape.\r\n *\r\n * Wire shape (ApiEvent) is contained inside this module and never escapes it.\r\n * Uses `||` chains matching the old transformer exactly -- 0/false values fall\r\n * through to the next variant, byte-identical to previous behavior. Throw\r\n * behavior preserved from old mapper: null/undefined input (property access on a nullish value) or a non-array\r\n * tickets field throws exactly as the old code did. Callers always pass real\r\n * wire objects.\r\n */\r\nexport function parseEvent(raw: unknown): Event {\r\n const apiEvent = raw as ApiEvent;\r\n\r\n // Extract tickets array from various possible field names\r\n const rawTickets =\r\n apiEvent.availableTickets ||\r\n apiEvent.available_tickets ||\r\n apiEvent.tickets ||\r\n [];\r\n\r\n const tickets = rawTickets.map(parseTicket);\r\n\r\n // Calculate min/max prices from tickets\r\n const prices = tickets.map((t) => t.price).filter((p) => p > 0);\r\n const minPrice = prices.length > 0 ? Math.min(...prices) : undefined;\r\n const maxPrice = prices.length > 0 ? Math.max(...prices) : undefined;\r\n\r\n return {\r\n eventID: apiEvent.eventID || apiEvent.id || apiEvent.ID || 0,\r\n id: apiEvent.id || apiEvent.ID,\r\n name: apiEvent.name || apiEvent.title || '',\r\n title: apiEvent.title || apiEvent.name,\r\n slug: apiEvent.slug,\r\n description: apiEvent.description,\r\n date: apiEvent.date || apiEvent.startDateTime || apiEvent.start_date_time || '',\r\n startDateTime: apiEvent.startDateTime || apiEvent.start_date_time,\r\n endDateTime: apiEvent.endDateTime || apiEvent.end_date_time,\r\n doorsOpenTime: apiEvent.doorsOpenTime || apiEvent.doors_open_time,\r\n timezone: apiEvent.timezone || apiEvent.time_zone,\r\n venueId: apiEvent.venueId || apiEvent.venue_id,\r\n venueName: apiEvent.venueName || apiEvent.venue_name,\r\n venueAddress: apiEvent.venueAddress || apiEvent.venue_address,\r\n location: apiEvent.location || apiEvent.venueAddress || apiEvent.venue_address,\r\n imageUrl: getEventImageUrl(apiEvent),\r\n imageURL: getEventImageUrl(apiEvent),\r\n status: apiEvent.status,\r\n isPublished: apiEvent.isPublished ?? apiEvent.is_published,\r\n isCancelled: apiEvent.isCancelled ?? apiEvent.is_cancelled,\r\n availableTickets: tickets,\r\n ticketsAvailable:\r\n apiEvent.ticketsAvailable ??\r\n apiEvent.tickets_available ??\r\n tickets.reduce((sum, t) => sum + (t.quantityAvailable || 0), 0),\r\n ticketsSold: apiEvent.ticketsSold ?? apiEvent.tickets_sold,\r\n minPrice: apiEvent.minPrice ?? apiEvent.min_price ?? minPrice,\r\n maxPrice: apiEvent.maxPrice ?? apiEvent.max_price ?? maxPrice,\r\n ctaText: apiEvent.ctaText || apiEvent.cta_text,\r\n ctaState: calculateCtaState(apiEvent),\r\n showPerformers: apiEvent.showPerformers ?? apiEvent.show_performers,\r\n eventSeriesId: apiEvent.eventSeriesId || apiEvent.event_series_id,\r\n seriesInstanceNumber:\r\n apiEvent.seriesInstanceNumber || apiEvent.series_instance_number,\r\n };\r\n}\r\n\r\n/**\r\n * Back-compat alias -- existing call sites using transformTicket continue\r\n * to work unchanged.\r\n */\r\nexport const transformTicket = parseTicket;\r\n\r\n/**\r\n * Back-compat alias -- existing call sites using transformEvent(apiEvent)\r\n * continue to work unchanged.\r\n */\r\nexport const transformEvent = parseEvent;\r\n\r\n/**\r\n * Legacy transformer name for backwards compatibility.\r\n */\r\nexport const transformEventData = parseEvent;\r\n\r\nexport function parseEventList(raws: unknown[]): Event[] {\r\n return raws.map(parseEvent);\r\n}\r\n","/**\r\n * Venue Transformer\r\n *\r\n * Normalizes API venue responses to a consistent format.\r\n *\r\n * Wire->domain boundary: one parseVenue(raw: unknown) entry point.\r\n * The wire field shape (ApiVenue) is named only inside this module and never\r\n * escapes it. Above the boundary only the Venue domain type.\r\n *\r\n * IMPLEMENTATION NOTE -- why no Zod schema here:\r\n * The old mapper used `||` for id/name (0 falls through) and `??` for fee fields\r\n * (0 fee is a real value, must NOT fall through). Both operators replicated verbatim.\r\n * Throw behavior is identical to the old mapper: null/undefined input (property access on a nullish value) throws exactly\r\n * as the old code did. Callers always pass real wire objects.\r\n */\r\n\r\nimport type { Venue } from '../types.js';\r\n\r\n// CDN base URL for images\r\nconst CDN_BASE_URL = 'https://micdrop-images.sfo3.digitaloceanspaces.com';\r\n\r\n// ── Wire shape (internal -- never exported) ──────────────────────────────\r\ninterface ApiVenue {\r\n id?: number;\r\n ID?: number;\r\n name?: string;\r\n slug?: string;\r\n address?: string;\r\n googleLocationNameCache?: string;\r\n google_location_name_cache?: string;\r\n city?: string;\r\n state?: string;\r\n zipCode?: string;\r\n zip_code?: string;\r\n country?: string;\r\n timezone?: string;\r\n time_zone?: string;\r\n logoUrl?: string;\r\n logo_url?: string;\r\n logo?: string;\r\n serviceFeePercentage?: number;\r\n service_fee_percentage?: number;\r\n serviceFeeCents?: number;\r\n service_fee_cents?: number;\r\n taxPercentage?: number;\r\n tax_percentage?: number;\r\n organizationId?: number;\r\n organization_id?: number;\r\n}\r\n\r\n/**\r\n * Get CDN image URL for venue logo\r\n */\r\nfunction getLogoUrl(path: string | undefined): string {\r\n if (!path) return '';\r\n if (path.startsWith('http')) return path;\r\n return `${CDN_BASE_URL}/${path.replace(/^\\//, '')}`;\r\n}\r\n\r\n/**\r\n * Parse a raw venue payload into the normalized Venue domain shape.\r\n *\r\n * Wire shape (ApiVenue) is contained inside this module and never escapes it.\r\n * Uses `||` / `??` chains matching the old transformer exactly -- byte-identical\r\n * to previous behavior. Throw behavior preserved from old mapper: null/undefined\r\n * input throws exactly as the old code did. Callers always pass real wire objects.\r\n */\r\nexport function parseVenue(raw: unknown): Venue {\r\n const apiVenue = raw as ApiVenue;\r\n\r\n const logoPath =\r\n apiVenue.logoUrl || apiVenue.logo_url || apiVenue.logo;\r\n\r\n return {\r\n id: apiVenue.id || apiVenue.ID || 0,\r\n name: apiVenue.name || '',\r\n slug: apiVenue.slug,\r\n address: apiVenue.address,\r\n googleLocationNameCache:\r\n apiVenue.googleLocationNameCache ||\r\n apiVenue.google_location_name_cache,\r\n city: apiVenue.city,\r\n state: apiVenue.state,\r\n zipCode: apiVenue.zipCode || apiVenue.zip_code,\r\n country: apiVenue.country,\r\n timezone: apiVenue.timezone || apiVenue.time_zone,\r\n logoUrl: getLogoUrl(logoPath),\r\n serviceFeePercentage:\r\n apiVenue.serviceFeePercentage ??\r\n apiVenue.service_fee_percentage ??\r\n 0,\r\n serviceFeeCents:\r\n apiVenue.serviceFeeCents ?? apiVenue.service_fee_cents ?? 0,\r\n taxPercentage:\r\n apiVenue.taxPercentage ?? apiVenue.tax_percentage ?? 0,\r\n organizationId:\r\n apiVenue.organizationId || apiVenue.organization_id,\r\n };\r\n}\r\n\r\n/**\r\n * Back-compat alias -- existing call sites using transformVenue(apiVenue)\r\n * continue to work unchanged.\r\n */\r\nexport const transformVenue = parseVenue;\r\n\r\n/**\r\n * Extract fee configuration from a raw venue payload.\r\n *\r\n * Uses `??` (not `||`) so 0-valued fees are preserved correctly.\r\n */\r\nexport function extractVenueFees(raw: unknown): {\r\n serviceFeePercentage: number;\r\n serviceFeeCents: number;\r\n taxPercentage: number;\r\n} {\r\n const apiVenue = raw as ApiVenue;\r\n return {\r\n serviceFeePercentage:\r\n apiVenue.serviceFeePercentage ??\r\n apiVenue.service_fee_percentage ??\r\n 0,\r\n serviceFeeCents:\r\n apiVenue.serviceFeeCents ?? apiVenue.service_fee_cents ?? 0,\r\n taxPercentage:\r\n apiVenue.taxPercentage ?? apiVenue.tax_percentage ?? 0,\r\n };\r\n}\r\n\r\n/**\r\n * Get formatted venue address\r\n */\r\nexport function formatVenueAddress(venue: Venue): string {\r\n const parts = [\r\n venue.address,\r\n venue.city,\r\n venue.state,\r\n venue.zipCode,\r\n ].filter(Boolean);\r\n\r\n return parts.join(', ');\r\n}\r\n"],"names":["logger","createLogger","DEFAULT_CONFIG","error","globalConfig","configureApi","config","getApiConfig","getPublicBaseUrl","getLegacyPublicUrl","getOrdersV2Url","getClientIP","sleep","ms","resolve","fetchWithRetry","url","init","cfg","method","maxAttempts","lastError","attempt","controller","timeoutId","callerSignal","onCallerAbort","response","err","apiGet","endpoint","options","apiRequest","apiPost","body","apiPut","apiDelete","errorData","message","errorMessage","simpleFetch","createPaymentIntent","cartId","quantities","donationAmounts","apiResult","AppError","getCartByUUID","cartUUID","expectedEventID","raw","uuid","eventID","status","expiresAt","reservations","r","rec","updateCartQuantities","numericQuantities","k","v","n","numericDonations","completeReservation","orderUuid","cancelReservation","createOrder","eventId","promoCode","getOrder","orderId","validatePaymentIntent","payload","result","extendCheckoutSession","getSessionStatus","initiateOrder","cartData","trackUTMSource","venueId","utmSource","ORDERS_V2_PUBLIC","validatePromoCode","code","encodedCode","hasPromoCodes","applyPromoCode","removePromoCode","_eventDetailsInflight","fetchEventDetails","customFetch","password","key","inflight","run","fetchEventTickets","tickets","fetchEventPerformers","data","fetchAllVenues","orgId","venues","fetchVenueEvents","events","getMonthEvents","year","month","getOrgMonthEvents","getSeriesOccurrences","eventSeriesId","fetchSeriesOccurrences","resolvePublicEntity","id","slug","qs","fetchSeriesPage","seriesId","fetchPublicCollection","collectionId","checkCollectionPassword","encodedPassword","checkEventPassword","testNetworkConnection","getVenue","getVenueFees","venue","getVenueBySlug","encodedSlug","API_BASE_URL","ORDERS_V2_URL","createGiftCardPurchase","req","applyGiftCard","giftCardAmountDollars","residualDollars","fullyCovered","requiresStripe","removeGiftCard","completeGiftCardPayment","customerDetails","WAITLIST_ERROR_MESSAGES","joinWaitlist","email","phone","smsOptIn","getWaitlistStatus","fallback","isHiddenTicketPurchasable","ticket","now","salesBegin","salesEnd","remaining","computeCtaState","event","cancelled","isRegistration","waitlistEnabled","eventEnd","hiddenAvailable","publicTickets","t","hasPurchasable","allSoldOut","allComingSoon","earliestSalesStart","isSoldOut","isScheduled","hasSalesEnded","startDate","diffMs","diffHours","diffDays","dateText","formatTime","formatDateRaw","d","parsePurchasedTicket","apiTicket","parseOrder","apiOrder","rawTickets","transformTicket","transformOrder","transformOrderForDisplay","order","displayId","formatCurrency","CDN_BASE_URL","getCDNImageUrl","path","getEventImageUrl","apiEvent","rawUrl","calculateCtaState","explicitState","eventDate","eventTime","availableCount","sum","qty","sold","parseTicket","parseEvent","prices","p","minPrice","maxPrice","transformEvent","transformEventData","getLogoUrl","parseVenue","apiVenue","logoPath","transformVenue","extractVenueFees","formatVenueAddress"],"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/api/orders.ts","../../src/lib/api/promo.ts","../../src/lib/api/transformers/performer.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","../../node_modules/js-cookie/dist/js.cookie.mjs","../../src/lib/utils/checkoutState.ts","../../src/lib/utils/orderApi.ts","../../src/lib/utils/utm.ts"],"sourcesContent":["/**\r\n * API Client for MicDrop Public Checkout API\r\n *\r\n * Provides a centralized HTTP client with:\r\n * - Configurable base URL\r\n * - Per-request timeout via AbortController\r\n * - Exponential-backoff retry on 5xx and network errors (idempotent methods)\r\n * - Consistent error surfacing\r\n * - Request/response typing\r\n */\r\n\r\nimport { createLogger } from '@getmicdrop/svelte-components/utils/logger';\r\nconst logger = createLogger('VC');\r\nimport type { ApiConfig, ApiResponse } from './types.js';\r\n\r\n// Default API configuration\r\nconst DEFAULT_CONFIG: Required<ApiConfig> = {\r\n baseUrl: 'https://get-micdrop.com',\r\n timeout: 30000,\r\n retries: 2,\r\n retryDelay: 500,\r\n onError: (error: Error) => logger.error('API Error:', error),\r\n};\r\n\r\nlet globalConfig: Required<ApiConfig> = { ...DEFAULT_CONFIG };\r\n\r\nexport function configureApi(config: Partial<ApiConfig>): void {\r\n globalConfig = { ...globalConfig, ...config };\r\n}\r\n\r\nexport function getApiConfig(): Required<ApiConfig> {\r\n return { ...globalConfig };\r\n}\r\n\r\nexport function getPublicBaseUrl(): string {\r\n return `${globalConfig.baseUrl}/api/v2/public`;\r\n}\r\n\r\nexport function getLegacyPublicUrl(): string {\r\n return `${globalConfig.baseUrl}/api/public`;\r\n}\r\n\r\nexport function getOrdersV2Url(): string {\r\n return `${globalConfig.baseUrl}/api/orders/v2/public`;\r\n}\r\n\r\n/** @deprecated IP is now resolved server-side via request headers. */\r\nexport async function getClientIP(): Promise<string> {\r\n return '';\r\n}\r\n\r\n/**\r\n * Sleep utility for retry backoff.\r\n */\r\nconst sleep = (ms: number): Promise<void> =>\r\n new Promise((resolve) => setTimeout(resolve, ms));\r\n\r\n/**\r\n * fetch with timeout, idempotent retry, and exponential backoff.\r\n *\r\n * - Retries on network errors and 5xx (transient) responses.\r\n * - Never retries non-idempotent methods (POST/PUT/PATCH/DELETE) — those are\r\n * left to caller-driven retries to avoid duplicate writes.\r\n * - The supplied AbortSignal (if any) is respected and short-circuits retries.\r\n */\r\nasync function fetchWithRetry(\r\n url: string,\r\n init: RequestInit,\r\n cfg: Required<ApiConfig>\r\n): Promise<Response> {\r\n const method = (init.method || 'GET').toUpperCase();\r\n const isIdempotent = method === 'GET' || method === 'HEAD';\r\n const maxAttempts = isIdempotent ? cfg.retries + 1 : 1;\r\n\r\n let lastError: unknown;\r\n for (let attempt = 1; attempt <= maxAttempts; attempt++) {\r\n const controller = new AbortController();\r\n const timeoutId = setTimeout(() => controller.abort(), cfg.timeout);\r\n\r\n // Chain the caller's signal if provided.\r\n const callerSignal = init.signal;\r\n let onCallerAbort: (() => void) | undefined;\r\n if (callerSignal) {\r\n if (callerSignal.aborted) controller.abort();\r\n else {\r\n onCallerAbort = () => controller.abort();\r\n callerSignal.addEventListener('abort', onCallerAbort, { once: true });\r\n }\r\n }\r\n\r\n try {\r\n // @api-client-escape: internal canonical client implementation — not drift\r\n const response = await fetch(url, { ...init, signal: controller.signal });\r\n\r\n // Retry transient server errors.\r\n if (response.status >= 500 && attempt < maxAttempts) {\r\n lastError = new Error(`HTTP ${response.status}`);\r\n } else {\r\n return response;\r\n }\r\n } catch (err) {\r\n lastError = err;\r\n // Caller aborted — give up.\r\n if (callerSignal?.aborted) throw err;\r\n } finally {\r\n clearTimeout(timeoutId);\r\n if (callerSignal && onCallerAbort) {\r\n callerSignal.removeEventListener('abort', onCallerAbort);\r\n }\r\n }\r\n\r\n if (attempt < maxAttempts) {\r\n await sleep(cfg.retryDelay * Math.pow(2, attempt - 1));\r\n }\r\n }\r\n\r\n throw lastError;\r\n}\r\n\r\nexport async function apiGet<T>(\r\n endpoint: string,\r\n options?: RequestInit\r\n): Promise<ApiResponse<T>> {\r\n return apiRequest<T>('GET', endpoint, undefined, options);\r\n}\r\n\r\nexport async function apiPost<T>(\r\n endpoint: string,\r\n body?: unknown,\r\n options?: RequestInit\r\n): Promise<ApiResponse<T>> {\r\n return apiRequest<T>('POST', endpoint, body, options);\r\n}\r\n\r\nexport async function apiPut<T>(\r\n endpoint: string,\r\n body?: unknown,\r\n options?: RequestInit\r\n): Promise<ApiResponse<T>> {\r\n return apiRequest<T>('PUT', endpoint, body, options);\r\n}\r\n\r\nexport async function apiDelete<T>(\r\n endpoint: string,\r\n options?: RequestInit\r\n): Promise<ApiResponse<T>> {\r\n return apiRequest<T>('DELETE', endpoint, undefined, options);\r\n}\r\n\r\nasync function apiRequest<T>(\r\n method: string,\r\n endpoint: string,\r\n body?: unknown,\r\n options?: RequestInit\r\n): Promise<ApiResponse<T>> {\r\n const url = endpoint.startsWith('http')\r\n ? endpoint\r\n : `${getPublicBaseUrl()}${endpoint}`;\r\n\r\n try {\r\n const response = await fetchWithRetry(\r\n url,\r\n {\r\n method,\r\n headers: {\r\n 'Content-Type': 'application/json',\r\n ...options?.headers,\r\n },\r\n body: body !== undefined ? JSON.stringify(body) : undefined,\r\n credentials: 'include',\r\n ...options,\r\n },\r\n globalConfig\r\n );\r\n\r\n if (!response.ok) {\r\n const errorData = await response.json().catch(() => ({}));\r\n const message =\r\n errorData.error || errorData.message || `HTTP ${response.status}`;\r\n globalConfig.onError(new Error(message));\r\n return { success: false, error: message, statusCode: response.status };\r\n }\r\n\r\n const data = await response.json();\r\n return { success: true, data: data as T, statusCode: response.status };\r\n } catch (error) {\r\n const errorMessage =\r\n error instanceof Error\r\n ? error.name === 'AbortError'\r\n ? 'Request timed out'\r\n : error.message\r\n : 'Unknown error';\r\n\r\n globalConfig.onError(\r\n error instanceof Error ? error : new Error(errorMessage)\r\n );\r\n return { success: false, error: errorMessage };\r\n }\r\n}\r\n\r\n/**\r\n * Simple fetch wrapper that returns parsed JSON or null on error.\r\n * Inherits the global timeout + retry policy for GETs.\r\n */\r\nexport async function simpleFetch<T>(\r\n url: string,\r\n options?: RequestInit\r\n): Promise<T | null> {\r\n try {\r\n const response = await fetchWithRetry(\r\n url,\r\n { credentials: 'include', ...options },\r\n globalConfig\r\n );\r\n\r\n if (!response.ok) {\r\n const errorData = await response.json().catch(() => ({}));\r\n logger.error(`API request failed: ${response.status}`, errorData);\r\n return null;\r\n }\r\n\r\n return response.json();\r\n } catch (error) {\r\n logger.error('API request error:', error);\r\n return null;\r\n }\r\n}\r\n","/**\r\n * Cart Transformer\r\n *\r\n * Normalizes the orders-service `GET /v2Public/cart/{uuid}` response into the\r\n * domain CartView / CartReservationView shapes.\r\n *\r\n * Wire->domain boundary: one parseCart(raw: unknown) entry point plus a\r\n * parseCartReservation(raw: unknown) sub-parse. The wire field shapes\r\n * (RawCart / RawReservation) are named ONLY inside this module and never escape.\r\n *\r\n * IMPLEMENTATION NOTE -- PascalCase reconciliation:\r\n * The orders-service Cart model uses Go-style PascalCase JSON tags\r\n * (UUID, EventID, Status, ExpiresAt, Reservations) on some routes and\r\n * camelCase on the newer v2 routes. We tolerate both via `??` (NOT `||`):\r\n * `??` only falls through on null/undefined, so a legitimate `0` eventID or\r\n * empty-string status is preserved -- byte-identical to the inline mapper this\r\n * was lifted from (src/lib/api/orders.ts getCartByUUID). Numeric fields are\r\n * coerced with Number(), string fields with String(), exactly as before.\r\n *\r\n * Staleness/event-mismatch policy (status must be reserved|active, eventID must\r\n * match the expected event) is a CALLER decision and intentionally stays in\r\n * getCartByUUID -- it is not part of normalization.\r\n */\r\n\r\nimport type { CartView, CartReservationView } from '../orders.js';\r\n\r\n// -- Wire shapes (internal -- never exported) --------------------------------\r\ninterface RawCart {\r\n UUID?: string;\r\n uuid?: string;\r\n EventID?: number;\r\n eventID?: number;\r\n Status?: string;\r\n status?: string;\r\n ExpiresAt?: string;\r\n expiresAt?: string;\r\n Reservations?: unknown[];\r\n reservations?: unknown[];\r\n}\r\n\r\n/**\r\n * Parse a single raw reservation row into the normalized CartReservationView.\r\n *\r\n * Wire shape is contained here. Uses `??` (PascalCase first, camelCase\r\n * fallback) so a real `0` quantity / price is preserved. Numeric coercion via\r\n * Number(), status via String() -- byte-identical to the inline `.map` in\r\n * getCartByUUID.\r\n */\r\nexport function parseCartReservation(raw: unknown): CartReservationView {\r\n const rec = (raw ?? {}) as Record<string, unknown>;\r\n return {\r\n ticketID: Number(rec.TicketID ?? rec.ticketID ?? 0),\r\n quantity: Number(rec.Quantity ?? rec.quantity ?? 0),\r\n priceAtReservation: Number(\r\n rec.PriceAtReservation ?? rec.priceAtReservation ?? 0\r\n ),\r\n status: String(rec.Status ?? rec.status ?? ''),\r\n };\r\n}\r\n\r\n/**\r\n * Parse a raw cart payload into the normalized CartView domain shape.\r\n *\r\n * Wire shape (RawCart) is contained inside this module and never escapes it.\r\n * Reconciles PascalCase and camelCase JSON tags via `??`. Reservations are\r\n * normalized through parseCartReservation. Byte-identical to the inline\r\n * normalization previously embedded in getCartByUUID.\r\n */\r\nexport function parseCart(raw: unknown): CartView {\r\n const data = (raw ?? {}) as RawCart;\r\n const uuid: string = data.UUID ?? data.uuid ?? '';\r\n const eventID = Number(data.EventID ?? data.eventID ?? 0);\r\n const status: string = data.Status ?? data.status ?? '';\r\n const expiresAt: string = data.ExpiresAt ?? data.expiresAt ?? '';\r\n const rawRes: unknown[] = data.Reservations ?? data.reservations ?? [];\r\n const reservations: CartReservationView[] = rawRes.map(parseCartReservation);\r\n return { uuid, eventID, status, expiresAt, reservations };\r\n}\r\n","import { AppError } from '@getmicdrop/svelte-components';\r\n/**\r\n * Orders API\r\n *\r\n * Functions for managing orders, payments, and reservations\r\n * in the public checkout flow.\r\n */\r\n\r\nimport { createLogger } from '@getmicdrop/svelte-components/utils/logger';\r\nconst logger = createLogger('VC');\r\nimport { getPublicBaseUrl, getOrdersV2Url, simpleFetch, apiPost, apiGet } from './client.js';\r\nimport { parseCart } from './transformers/cart.js';\r\nimport type {\r\n PaymentIntentResponse,\r\n CompleteReservationResponse,\r\n CancelReservationResponse,\r\n CreateOrderResponse,\r\n ValidatePaymentRequest,\r\n ValidatePaymentResponse,\r\n ExtendSessionResponse,\r\n SessionStatus,\r\n Order,\r\n} from './types.js';\r\n\r\n/**\r\n * Create a payment intent for the cart\r\n *\r\n * This initiates the Stripe payment flow by creating a PaymentIntent\r\n * on the backend. The response includes the client_secret needed\r\n * for Stripe Elements.\r\n *\r\n * @param cartId - The cart/order UUID\r\n * @param quantities - Map of ticketId -> quantity\r\n * @param donationAmounts - Map of ticketId -> donation amount in dollars (for type=2 tickets)\r\n * @returns Payment intent data including client_secret, or null on error\r\n */\r\nexport async function createPaymentIntent(\r\n cartId: string,\r\n quantities: Record<string | number, number>,\r\n donationAmounts?: Record<string | number, number>\r\n): Promise<PaymentIntentResponse | null> {\r\n try {\r\n const apiResult = await apiPost<PaymentIntentResponse>(\r\n `${getOrdersV2Url()}/cart/${cartId}/payment-intent`,\r\n {\r\n productQuantities: quantities,\r\n ...(donationAmounts && Object.keys(donationAmounts).length > 0\r\n ? { donationAmounts }\r\n : {}),\r\n }\r\n );\r\n\r\n if (!apiResult.success) {\r\n logger.error('Payment intent creation failed:', apiResult.error);\r\n throw new AppError(apiResult.error || 'Failed to create payment intent', 'lib/api/orders/createPaymentIntent');\r\n }\r\n\r\n logger.debug('Payment intent created:', apiResult.data);\r\n return apiResult.data!;\r\n } catch (error) {\r\n logger.error('createPaymentIntent error:', error);\r\n return null;\r\n }\r\n}\r\n\r\n/**\r\n * Fetch an existing cart by UUID (cross-device pre-fill — §1.3).\r\n *\r\n * Returns the server's authoritative view of a cart so a second device\r\n * visiting the tickets page can see the in-progress reservation it created\r\n * on another device. Returns `null` if:\r\n * - the cart no longer exists (404)\r\n * - the cart belongs to a different event than `expectedEventID` (when\r\n * supplied) — we don't want to mix carts across events\r\n * - the cart status is no longer `reserved` / `active` (e.g. expired,\r\n * completed, abandoned) — caller should treat the local cookie as stale\r\n * - any network/decode failure (caller falls back to localStorage only)\r\n *\r\n * Returned shape matches the orders-service `GET /v2Public/cart/{uuid}`\r\n * response: a Cart model with embedded Reservations.\r\n */\r\nexport interface CartReservationView {\r\n ticketID: number;\r\n quantity: number;\r\n priceAtReservation: number;\r\n status: string;\r\n}\r\nexport interface CartView {\r\n uuid: string;\r\n eventID: number;\r\n status: string;\r\n expiresAt: string;\r\n reservations: CartReservationView[];\r\n}\r\n\r\nexport async function getCartByUUID(\r\n cartUUID: string,\r\n expectedEventID?: string | number\r\n): Promise<CartView | null> {\r\n if (!cartUUID) return null;\r\n try {\r\n const apiResult = await apiGet<unknown>(\r\n `${getOrdersV2Url()}/cart/${cartUUID}`\r\n );\r\n if (!apiResult.success) {\r\n // 404 / 410 / 5xx — caller treats as stale\r\n return null;\r\n }\r\n // Wire->domain normalization (PascalCase/camelCase reconciliation, numeric\r\n // coercion, reservation sub-parse) lives in parseCart — byte-identical to\r\n // the inline mapper this replaced. Staleness/event-mismatch policy below\r\n // stays here because it is a caller decision, not normalization.\r\n const { uuid, eventID, status, expiresAt, reservations } = parseCart(\r\n apiResult.data!\r\n );\r\n if (status !== 'reserved' && status !== 'active') {\r\n // stale cart row — caller clears the cookie\r\n return null;\r\n }\r\n if (expectedEventID !== undefined && eventID !== Number(expectedEventID)) {\r\n // cart belongs to a different event — don't mix\r\n return null;\r\n }\r\n return { uuid, eventID, status, expiresAt, reservations };\r\n } catch (err) {\r\n logger.error('getCartByUUID error:', err);\r\n return null;\r\n }\r\n}\r\n\r\n/**\r\n * Update cart quantities after the cart already exists.\r\n *\r\n * Replaces all reservations on the cart with the new quantities.\r\n * The orders-service handler releases old reservations (returning\r\n * inventory) and creates new ones (decrementing inventory) atomically.\r\n *\r\n * Use this when the user changes ticket counts AFTER the initial\r\n * cart was created via initiateOrder. The cart UUID is preserved,\r\n * so the timer and extension state stay intact.\r\n *\r\n * @returns true on success, false on failure\r\n */\r\nexport async function updateCartQuantities(\r\n cartId: string,\r\n quantities: Record<string | number, number>,\r\n donationAmounts?: Record<string | number, number>\r\n): Promise<boolean> {\r\n try {\r\n // Convert string keys to numbers (the orders-service handler\r\n // unmarshals into map[uint]int).\r\n const numericQuantities: Record<number, number> = {};\r\n for (const [k, v] of Object.entries(quantities)) {\r\n const n = typeof k === 'number' ? k : parseInt(k, 10);\r\n if (!Number.isNaN(n) && v > 0) numericQuantities[n] = v;\r\n }\r\n const numericDonations: Record<number, number> = {};\r\n if (donationAmounts) {\r\n for (const [k, v] of Object.entries(donationAmounts)) {\r\n const n = typeof k === 'number' ? k : parseInt(k, 10);\r\n if (!Number.isNaN(n) && v > 0) numericDonations[n] = v;\r\n }\r\n }\r\n // @fetch-escape: status-only endpoint — PUT /cart/:id returns an empty 2xx (no JSON body)\r\n // on success; the canonical apiPut runs response.json() unconditionally on 2xx and would\r\n // throw on the empty body, mis-reporting a successful cart update as a failure. Migrate once\r\n // the client tolerates 204/empty bodies.\r\n const response = await fetch(`${getOrdersV2Url()}/cart/${cartId}`, {\r\n method: 'PUT',\r\n headers: { 'Content-Type': 'application/json' },\r\n credentials: 'include',\r\n body: JSON.stringify({\r\n quantities: numericQuantities,\r\n ...(Object.keys(numericDonations).length > 0\r\n ? { donationAmounts: numericDonations }\r\n : {}),\r\n }),\r\n });\r\n if (!response.ok) {\r\n const errorData = await response.json().catch(() => ({}));\r\n logger.error('Cart update failed:', errorData);\r\n return false;\r\n }\r\n return true;\r\n } catch (err) {\r\n logger.error('updateCartQuantities error:', err);\r\n return false;\r\n }\r\n}\r\n\r\n/**\r\n * Complete reservation after successful payment\r\n *\r\n * Called after Stripe confirms the payment to finalize the order\r\n * and generate tickets.\r\n *\r\n * @param orderUuid - The order UUID\r\n * @returns Success status and message\r\n */\r\nexport async function completeReservation(\r\n orderUuid: string\r\n): Promise<CompleteReservationResponse> {\r\n try {\r\n const apiResult = await apiPost<{ message?: string }>(\r\n `${getPublicBaseUrl()}/orders/complete/${orderUuid}`\r\n );\r\n\r\n if (!apiResult.success) {\r\n return {\r\n success: false,\r\n error: apiResult.error || 'Failed to complete reservation',\r\n };\r\n }\r\n\r\n return {\r\n success: true,\r\n message: apiResult.data?.message,\r\n };\r\n } catch (error) {\r\n logger.error('Error completing reservation:', error);\r\n return { success: false, error: 'Network error completing reservation' };\r\n }\r\n}\r\n\r\n/**\r\n * Cancel reservation and release tickets back to inventory\r\n *\r\n * Called when user abandons checkout or session expires.\r\n *\r\n * @param orderUuid - The order UUID\r\n * @returns Success status and message\r\n */\r\nexport async function cancelReservation(\r\n orderUuid: string\r\n): Promise<CancelReservationResponse> {\r\n try {\r\n const apiResult = await apiPost<{ message?: string }>(\r\n `${getPublicBaseUrl()}/orders/cancel/${orderUuid}`\r\n );\r\n\r\n if (!apiResult.success) {\r\n return {\r\n success: false,\r\n error: apiResult.error || 'Failed to cancel reservation',\r\n };\r\n }\r\n\r\n return {\r\n success: true,\r\n message: apiResult.data?.message,\r\n };\r\n } catch (error) {\r\n logger.error('Error cancelling reservation:', error);\r\n return { success: false, error: 'Network error cancelling reservation' };\r\n }\r\n}\r\n\r\n/**\r\n * Create a new order/cart\r\n *\r\n * This creates an empty order that can be used for checkout.\r\n * The order UUID is used for all subsequent operations.\r\n *\r\n * @param eventId - The event ID\r\n * @param promoCode - Optional promo code to apply\r\n * @returns The order UUID, or null on error\r\n */\r\nexport async function createOrder(\r\n eventId: string | number,\r\n promoCode?: string\r\n): Promise<CreateOrderResponse | null> {\r\n try {\r\n const apiResult = await apiPost<CreateOrderResponse>(\r\n `${getPublicBaseUrl()}/orders/create`,\r\n { eventID: eventId, promoCode }\r\n );\r\n\r\n if (!apiResult.success) {\r\n logger.error('Create order failed:', apiResult.error);\r\n return null;\r\n }\r\n\r\n return apiResult.data!;\r\n } catch (error) {\r\n logger.error('createOrder error:', error);\r\n return null;\r\n }\r\n}\r\n\r\n/**\r\n * Get order details (public, no auth required)\r\n *\r\n * Fetches the order details for displaying on the success page.\r\n *\r\n * @param orderId - The order UUID or ID\r\n * @returns The order details, or null on error\r\n */\r\nexport async function getOrder(orderId: string): Promise<Order | null> {\r\n return simpleFetch<Order>(`${getPublicBaseUrl()}/orders/${orderId}`);\r\n}\r\n\r\n/**\r\n * Validate payment intent and complete the order\r\n *\r\n * This is an alternative to completeReservation that accepts\r\n * additional payment details. Used by micdrop-frontend.\r\n *\r\n * @param cartId - The cart/order UUID\r\n * @param payload - Payment validation payload\r\n * @returns Validation result\r\n */\r\nexport async function validatePaymentIntent(\r\n cartId: string,\r\n payload: ValidatePaymentRequest\r\n): Promise<ValidatePaymentResponse | null> {\r\n try {\r\n const apiResult = await apiPost<{ status?: string; orderUUID?: string; uuid?: string }>(\r\n `${getOrdersV2Url()}/validatePaymentIntent/${cartId}`,\r\n payload\r\n );\r\n\r\n if (!apiResult.success) {\r\n logger.error('Payment validation failed:', apiResult.error);\r\n return {\r\n success: false,\r\n status: 'failed',\r\n error: apiResult.error || 'Payment validation failed',\r\n };\r\n }\r\n\r\n const result = apiResult.data!;\r\n return {\r\n success: true,\r\n status: result.status || 'Payment succeeded',\r\n orderUUID: result.orderUUID || result.uuid,\r\n };\r\n } catch (error) {\r\n logger.error('validatePaymentIntent error:', error);\r\n return null;\r\n }\r\n}\r\n\r\n/**\r\n * Extend the checkout session by 15 minutes\r\n *\r\n * Users get a limited number of extensions (typically 2-3).\r\n *\r\n * @param orderUuid - The order UUID\r\n * @returns Extension result with new expiry time\r\n */\r\nexport async function extendCheckoutSession(\r\n orderUuid: string\r\n): Promise<ExtendSessionResponse> {\r\n try {\r\n const apiResult = await apiPost<{ newExpiryTime?: string; remainingExtensions?: number }>(\r\n `${getPublicBaseUrl()}/orders/extend-session`,\r\n { orderUuid }\r\n );\r\n\r\n if (!apiResult.success) {\r\n return {\r\n success: false,\r\n error: apiResult.error || 'Failed to extend session',\r\n statusCode: apiResult.statusCode,\r\n };\r\n }\r\n\r\n const result = apiResult.data!;\r\n return {\r\n success: true,\r\n newExpiryTime: result.newExpiryTime,\r\n remainingExtensions: result.remainingExtensions,\r\n };\r\n } catch (error) {\r\n logger.error('Error extending checkout session:', error);\r\n return { success: false, error: 'Network error extending session' };\r\n }\r\n}\r\n\r\n/**\r\n * Get current session status including expiry time\r\n *\r\n * Used to display countdown timer and check if extensions are available.\r\n *\r\n * @param orderUuid - The order UUID\r\n * @returns Session status including expiry time\r\n */\r\nexport async function getSessionStatus(\r\n orderUuid: string\r\n): Promise<SessionStatus> {\r\n try {\r\n const apiResult = await apiGet<SessionStatus>(\r\n `${getPublicBaseUrl()}/orders/session/${orderUuid}`\r\n );\r\n\r\n if (!apiResult.success) {\r\n return {\r\n error: apiResult.error || 'No active session found',\r\n notFound: apiResult.statusCode === 404,\r\n };\r\n }\r\n\r\n const result = apiResult.data!;\r\n return {\r\n expiresAt: result.expiresAt,\r\n extensionCount: result.extensionCount,\r\n remainingExtensions: result.remainingExtensions,\r\n canExtend: result.canExtend,\r\n reservationCount: result.reservationCount,\r\n };\r\n } catch (error) {\r\n logger.error('Error getting session status:', error);\r\n return { error: 'Network error getting session status' };\r\n }\r\n}\r\n\r\n/**\r\n * Initiate a new order (alias for createOrder)\r\n *\r\n * This function provides backwards compatibility with micdrop-frontend.\r\n * It accepts the same parameters as the legacy initiateOrder function.\r\n *\r\n * @param cartData - Object containing eventID and optional quantities/promoCode\r\n * @returns The order UUID, or null on error\r\n */\r\nexport async function initiateOrder(\r\n cartData: {\r\n eventID: string | number;\r\n promoCode?: string;\r\n quantities?: Record<string | number, number>;\r\n } = {} as any\r\n): Promise<string | null> {\r\n try {\r\n const apiResult = await apiPost<{ uuid?: string }>(\r\n `${getPublicBaseUrl()}/orders/create`,\r\n cartData\r\n );\r\n\r\n if (!apiResult.success) {\r\n logger.error('Order initiation failed:', apiResult.error);\r\n return null;\r\n }\r\n\r\n return apiResult.data?.uuid ?? null;\r\n } catch (error) {\r\n logger.error('initiateOrder error:', error);\r\n return null;\r\n }\r\n}\r\n\r\n/**\r\n * Track UTM source for analytics\r\n *\r\n * Records the traffic source (utm_source parameter) for marketing analytics.\r\n *\r\n * @param venueId - The venue ID\r\n * @returns Promise that resolves when tracking is complete\r\n */\r\nexport async function trackUTMSource(venueId: string | number): Promise<void> {\r\n if (typeof window === 'undefined') return;\r\n\r\n const urlParams = new URLSearchParams(window.location.search);\r\n const utmSource = urlParams.get('utm_source') || 'Direct';\r\n\r\n try {\r\n // @fetch-escape: fire-and-forget UTM beacon — GET with no response body consumed; simpleFetch would parse JSON on empty 200 and log false-positive errors\r\n await fetch(\r\n `${getPublicBaseUrl()}/utm/${venueId}/${encodeURIComponent(utmSource)}`\r\n );\r\n } catch (error) {\r\n logger.error('UTM tracking failed:', error);\r\n }\r\n}\r\n","/**\r\n * Promo Codes API\r\n *\r\n * Functions for validating and checking promo codes\r\n * in the public checkout flow.\r\n */\r\n\r\nimport { createLogger } from '@getmicdrop/svelte-components/utils/logger';\r\nconst logger = createLogger('VC');\r\nimport { apiGet, apiPost, getOrdersV2Url } from './client.js';\r\nimport type { PromoValidationResponse, HasPromoCodesResponse } from './types.js';\r\n\r\nconst ORDERS_V2_PUBLIC = () => getOrdersV2Url();\r\n\r\n/**\r\n * Validate a promo code for an event\r\n *\r\n * Checks if a promo code is valid and returns its effects:\r\n * - Discount amount and type\r\n * - Hidden ticket reveal\r\n *\r\n * @param eventId - The event ID\r\n * @param code - The promo code to validate\r\n * @returns Validation result with discount info\r\n */\r\nexport async function validatePromoCode(\r\n eventId: string | number,\r\n code: string\r\n): Promise<PromoValidationResponse> {\r\n try {\r\n if (!code || !code.trim()) {\r\n return { valid: false, error: 'Promo code is required' };\r\n }\r\n\r\n // API uses GET with code in URL path\r\n const encodedCode = encodeURIComponent(code.trim());\r\n const apiResult = await apiGet<PromoValidationResponse>(\r\n `/promo-codes/validate/${eventId}/${encodedCode}`\r\n );\r\n\r\n if (!apiResult.success) {\r\n // 404 = invalid code, other errors are server issues\r\n if (apiResult.statusCode === 404) {\r\n return { valid: false, error: 'Invalid promo code' };\r\n }\r\n return { valid: false, error: 'Failed to validate code' };\r\n }\r\n\r\n const result = apiResult.data!;\r\n return {\r\n valid: result.valid ?? true,\r\n revealHiddenTickets: result.revealHiddenTickets,\r\n revealTicketIds: result.revealTicketIds,\r\n provideDiscount: result.provideDiscount,\r\n discountType: result.discountType,\r\n amount: result.amount,\r\n code: result.code || code,\r\n };\r\n } catch (error) {\r\n logger.error('Error validating promo code:', error);\r\n return { valid: false, error: 'Network error validating code' };\r\n }\r\n}\r\n\r\n/**\r\n * Check if promo codes are available for an event\r\n *\r\n * Used to conditionally show/hide the promo code input field.\r\n *\r\n * @param eventId - The event ID\r\n * @returns Whether promo codes exist for this event\r\n */\r\nexport async function hasPromoCodes(eventId: string | number): Promise<boolean> {\r\n try {\r\n const apiResult = await apiGet<HasPromoCodesResponse>(\r\n `/promo-codes/check/${eventId}`\r\n );\r\n\r\n if (!apiResult.success) {\r\n // If endpoint doesn't exist or errors, default to showing the input\r\n return true;\r\n }\r\n\r\n return apiResult.data!.hasPromoCodes === true;\r\n } catch (error) {\r\n logger.error('Error checking promo codes availability:', error);\r\n // Default to showing promo input if we can't check\r\n return true;\r\n }\r\n}\r\n\r\n/**\r\n * Apply a promo code to a cart\r\n *\r\n * This updates the cart with the promo code discount.\r\n *\r\n * @param cartId - The cart UUID\r\n * @param code - The promo code to apply\r\n * @returns Success status\r\n */\r\nexport async function applyPromoCode(\r\n cartId: string,\r\n code: string\r\n): Promise<{ success: boolean; error?: string }> {\r\n try {\r\n // Backend route: POST /api/orders/v2/public/cart/{uuid}/apply-promo with a\r\n // `promoCode` body (mirrors apply-gift-card). The old path/body\r\n // (/orders/{id}/apply-promo + {code}) matched no route, so the cart's\r\n // DiscountAmount was never set and the buyer was charged full price despite\r\n // the UI showing a discount.\r\n const apiResult = await apiPost(\r\n `${ORDERS_V2_PUBLIC()}/cart/${cartId}/apply-promo`,\r\n { promoCode: code }\r\n );\r\n\r\n if (!apiResult.success) {\r\n return {\r\n success: false,\r\n error: apiResult.error || 'Failed to apply promo code',\r\n };\r\n }\r\n\r\n return { success: true };\r\n } catch (error) {\r\n logger.error('Error applying promo code:', error);\r\n return { success: false, error: 'Network error applying code' };\r\n }\r\n}\r\n\r\n/**\r\n * Remove a promo code from a cart\r\n *\r\n * @param cartId - The cart UUID\r\n * @returns Success status\r\n */\r\nexport async function removePromoCode(\r\n cartId: string\r\n): Promise<{ success: boolean; error?: string }> {\r\n try {\r\n const apiResult = await apiPost(`/orders/${cartId}/remove-promo`);\r\n\r\n if (!apiResult.success) {\r\n return {\r\n success: false,\r\n error: apiResult.error || 'Failed to remove promo code',\r\n };\r\n }\r\n\r\n return { success: true };\r\n } catch (error) {\r\n logger.error('Error removing promo code:', error);\r\n return { success: false, error: 'Network error removing code' };\r\n }\r\n}\r\n","/**\r\n * Event Performers Transformer\r\n *\r\n * Normalizes the public `GET /events/{id}/performers` response into the\r\n * EventPerformersResponse domain shape consumed by the lineup UI.\r\n *\r\n * Wire->domain boundary: one parseEventPerformers(raw: unknown) entry point.\r\n * The normalization guarantees the two invariants the UI relies on:\r\n * - `performers` is ALWAYS an array (non-array / missing -> [])\r\n * - `showPerformers` is ALWAYS a strict boolean (only `=== true` is true)\r\n *\r\n * Byte-identical to the inline mapping previously embedded in\r\n * fetchEventPerformers (src/lib/api/events.ts). The network-failure guard\r\n * (`!data` -> empty response) is a CALLER concern and stays in\r\n * fetchEventPerformers; nullish input here is treated as an empty payload so\r\n * the parser never throws.\r\n */\r\n\r\nimport type { EventPerformersResponse } from '../types.js';\r\n\r\n/**\r\n * Parse a raw performers payload into the normalized EventPerformersResponse.\r\n *\r\n * `performers` is coerced to an array (Array.isArray gate), `showPerformers`\r\n * to a strict boolean (`=== true`) -- matching fetchEventPerformers exactly.\r\n */\r\nexport function parseEventPerformers(raw: unknown): EventPerformersResponse {\r\n const data = (raw ?? {}) as Partial<EventPerformersResponse>;\r\n return {\r\n performers: Array.isArray(data.performers) ? data.performers : [],\r\n showPerformers: data.showPerformers === true,\r\n };\r\n}\r\n","import { AppError } from '@getmicdrop/svelte-components';\r\n/**\r\n * Events API\r\n *\r\n * Functions for fetching event data, tickets, and performers\r\n * in the public checkout flow.\r\n */\r\n\r\nimport { createLogger } from '@getmicdrop/svelte-components/utils/logger';\r\nconst logger = createLogger('VC');\r\nimport { getPublicBaseUrl, simpleFetch, apiPost } from './client.js';\r\nimport { parseEventPerformers } from './transformers/performer.js';\r\nimport type {\r\n Event,\r\n AvailableTicket,\r\n EventPerformersResponse,\r\n SeriesOccurrencesResponse,\r\n SeriesPageData,\r\n PublicCollectionData,\r\n ResolvedEntity,\r\n} from './types.js';\r\n\r\n/**\r\n * In-flight `fetchEventDetails` requests keyed by event ID. Lets\r\n * concurrent callers (Checkout, CartView, EventDetailsView, +page.js\r\n * server load — all of which may run during a single navigation) share\r\n * one HTTP request rather than triggering three.\r\n *\r\n * The entry deletes as soon as the underlying promise settles, so this\r\n * is dedup not cache. For real cache-with-TTL we'd want SWR; that's a\r\n * future milestone.\r\n */\r\nconst _eventDetailsInflight = new Map<string, Promise<Event | null>>();\r\n\r\n/**\r\n * Fetch event details\r\n *\r\n * Gets full event information including venue and ticket data.\r\n *\r\n * @param eventId - The event ID\r\n * @param customFetch - Optional custom fetch function (for SSR)\r\n * @returns Event details or null on error\r\n */\r\nexport async function fetchEventDetails(\r\n eventId: string | number,\r\n customFetch: typeof fetch = fetch,\r\n password?: string\r\n): Promise<Event | null> {\r\n // Password-protected events are gated server-side: the detail endpoint returns\r\n // a stripped teaser (+ passwordRequired:true) until the correct ?password= is\r\n // supplied. The password varies the dedup key so an unlock re-fetch is never\r\n // served the cached locked teaser.\r\n const key = password ? `${eventId}:pw` : String(eventId);\r\n // Only dedup when using the default fetch — custom-fetch callers\r\n // (e.g. SvelteKit server loads) bring their own request context.\r\n if (customFetch === fetch) {\r\n const inflight = _eventDetailsInflight.get(key);\r\n if (inflight) return inflight;\r\n }\r\n\r\n const run = (async () => {\r\n try {\r\n const url = password\r\n ? `${getPublicBaseUrl()}/events/${eventId}?password=${encodeURIComponent(password)}`\r\n : `${getPublicBaseUrl()}/events/${eventId}`;\r\n // @fetch-escape: customFetch injection for SSR context — caller-supplied fetch instance, cannot use simpleFetch\r\n const response = await customFetch(url);\r\n if (!response.ok) {\r\n throw new AppError(\r\n `Failed to fetch event details: ${response.status}`,\r\n 'lib/api/events/fetchEventDetails'\r\n );\r\n }\r\n return await response.json();\r\n } catch (error) {\r\n logger.error('Error fetching event details:', error);\r\n return null;\r\n }\r\n })();\r\n\r\n if (customFetch === fetch) {\r\n _eventDetailsInflight.set(key, run);\r\n run.finally(() => {\r\n // Only clear if this same promise is still the inflight one — a\r\n // later call may have set up a fresh request after the user\r\n // triggered an explicit refresh.\r\n if (_eventDetailsInflight.get(key) === run) {\r\n _eventDetailsInflight.delete(key);\r\n }\r\n });\r\n }\r\n return run;\r\n}\r\n\r\n/**\r\n * Fetch available tickets for an event\r\n *\r\n * Returns all ticket types that are currently available for sale.\r\n *\r\n * @param eventId - The event ID\r\n * @returns Array of available tickets\r\n */\r\nexport async function fetchEventTickets(\r\n eventId: string | number\r\n): Promise<AvailableTicket[]> {\r\n try {\r\n const tickets = await simpleFetch<AvailableTicket[]>(\r\n `${getPublicBaseUrl()}/tickets/event/${eventId}`\r\n );\r\n return Array.isArray(tickets) ? tickets : [];\r\n } catch (error) {\r\n logger.error('Error fetching tickets:', error);\r\n return [];\r\n }\r\n}\r\n\r\n/**\r\n * Fetch performers for an event\r\n *\r\n * Returns the lineup with pre-resolved avatar URLs.\r\n *\r\n * @param eventId - The event ID\r\n * @returns Performers list and visibility flag\r\n */\r\nexport async function fetchEventPerformers(\r\n eventId: string | number\r\n): Promise<EventPerformersResponse> {\r\n try {\r\n if (!eventId) {\r\n logger.warn('fetchEventPerformers called without eventId');\r\n return { performers: [], showPerformers: false };\r\n }\r\n\r\n const data = await simpleFetch<EventPerformersResponse>(\r\n `${getPublicBaseUrl()}/events/${eventId}/performers`\r\n );\r\n\r\n if (!data) {\r\n logger.error('Failed to fetch performers: network or server error');\r\n return { performers: [], showPerformers: false };\r\n }\r\n\r\n // Normalization (performers->array, showPerformers->strict boolean) lives\r\n // in parseEventPerformers — byte-identical to the inline mapping it\r\n // replaced. The network-failure guard above stays here.\r\n return parseEventPerformers(data);\r\n } catch (error) {\r\n logger.error('Error fetching event performers:', error);\r\n return { performers: [], showPerformers: false };\r\n }\r\n}\r\n\r\n/**\r\n * Fetch all venues for an organization\r\n *\r\n * @param orgId - The organization ID\r\n * @returns Array of venues\r\n */\r\nexport async function fetchAllVenues(orgId: string | number): Promise<any[]> {\r\n try {\r\n if (!orgId) {\r\n logger.warn('fetchAllVenues called without orgId');\r\n return [];\r\n }\r\n\r\n const venues = await simpleFetch<any[]>(\r\n `${getPublicBaseUrl()}/venues/organization/${orgId}`\r\n );\r\n\r\n if (!venues) {\r\n logger.error('Failed to fetch venues: network or server error');\r\n return [];\r\n }\r\n\r\n return Array.isArray(venues) ? venues : [];\r\n } catch (error) {\r\n logger.error('Error fetching venues:', error);\r\n return [];\r\n }\r\n}\r\n\r\n/**\r\n * Fetch events for a venue\r\n *\r\n * @param venueId - The venue ID\r\n * @returns Array of events\r\n */\r\nexport async function fetchVenueEvents(\r\n venueId: string | number\r\n): Promise<Event[]> {\r\n try {\r\n if (!venueId) {\r\n logger.warn('fetchVenueEvents called without venueId');\r\n return [];\r\n }\r\n\r\n const events = await simpleFetch<Event[]>(\r\n `${getPublicBaseUrl()}/events/venue/${venueId}`\r\n );\r\n\r\n if (!events) {\r\n logger.error('Failed to fetch venue events: network or server error');\r\n return [];\r\n }\r\n\r\n return Array.isArray(events) ? events : [];\r\n } catch (error) {\r\n logger.error('Error fetching venue events:', error);\r\n return [];\r\n }\r\n}\r\n\r\n/**\r\n * Get events for a specific month\r\n *\r\n * Used for calendar views to efficiently load events.\r\n *\r\n * @param venueId - The venue ID\r\n * @param year - The year (e.g., 2024)\r\n * @param month - The month (1-12)\r\n * @returns Array of events for the month\r\n */\r\nexport async function getMonthEvents(\r\n venueId: string | number,\r\n year: number,\r\n month: number\r\n): Promise<Event[]> {\r\n try {\r\n const data = await simpleFetch<{ events?: Event[] } | Event[]>(\r\n `${getPublicBaseUrl()}/events/venue/${venueId}/month/${year}/${month}`\r\n );\r\n\r\n if (!data) {\r\n logger.error('Failed to fetch month events: network or server error');\r\n return [];\r\n }\r\n\r\n if (Array.isArray(data)) return data;\r\n return Array.isArray((data as { events?: Event[] }).events)\r\n ? (data as { events: Event[] }).events\r\n : [];\r\n } catch (error) {\r\n logger.error('Error fetching month events:', error);\r\n return [];\r\n }\r\n}\r\n\r\n/**\r\n * Get events for an organization for a specific month\r\n *\r\n * @param orgId - The organization ID\r\n * @param year - The year (e.g., 2024)\r\n * @param month - The month (1-12)\r\n * @returns Array of events for the month\r\n */\r\nexport async function getOrgMonthEvents(\r\n orgId: string | number,\r\n year: number,\r\n month: number\r\n): Promise<Event[]> {\r\n try {\r\n const data = await simpleFetch<{ events?: Event[] } | Event[]>(\r\n `${getPublicBaseUrl()}/events/organization/${orgId}/month/${year}/${month}`\r\n );\r\n\r\n if (!data) {\r\n logger.error('Failed to fetch org month events: network or server error');\r\n return [];\r\n }\r\n\r\n if (Array.isArray(data)) return data;\r\n return Array.isArray((data as { events?: Event[] }).events)\r\n ? (data as { events: Event[] }).events\r\n : [];\r\n } catch (error) {\r\n logger.error('Error fetching org month events:', error);\r\n return [];\r\n }\r\n}\r\n\r\n/**\r\n * Get series occurrences for date selector\r\n *\r\n * Returns all instances of a recurring event series.\r\n *\r\n * @param eventSeriesId - The series ID\r\n * @returns Series occurrences\r\n */\r\nexport async function getSeriesOccurrences(\r\n eventSeriesId: number\r\n): Promise<SeriesOccurrencesResponse | null> {\r\n try {\r\n const data = await simpleFetch<SeriesOccurrencesResponse>(\r\n `${getPublicBaseUrl()}/series/${eventSeriesId}/occurrences`\r\n );\r\n\r\n if (!data) {\r\n logger.error(\r\n 'Failed to fetch series occurrences: network or server error'\r\n );\r\n return null;\r\n }\r\n\r\n return data;\r\n } catch (error) {\r\n logger.error('Error fetching series occurrences:', error);\r\n return null;\r\n }\r\n}\r\n\r\n/**\r\n * Fetch series occurrences with CTA state\r\n *\r\n * Returns full series data including availability status.\r\n *\r\n * @param eventSeriesId - The series ID\r\n * @param venueId - The venue ID\r\n * @returns Series occurrences with CTA state\r\n */\r\nexport async function fetchSeriesOccurrences(\r\n eventSeriesId: number,\r\n venueId: string | number\r\n): Promise<SeriesOccurrencesResponse | null> {\r\n try {\r\n const data = await simpleFetch<SeriesOccurrencesResponse>(\r\n `${getPublicBaseUrl()}/series/${eventSeriesId}/occurrences?venueId=${venueId}`\r\n );\r\n\r\n if (!data) {\r\n logger.error('Failed to fetch series with CTA: network or server error');\r\n return null;\r\n }\r\n\r\n return data;\r\n } catch (error) {\r\n logger.error('Error fetching series occurrences:', error);\r\n return null;\r\n }\r\n}\r\n\r\n/**\r\n * Resolve an entity-agnostic public id to its concrete type + data.\r\n *\r\n * The embed deep-link (`#{id}-{slug}`) carries no type prefix, so a cold\r\n * deep link (the show isn't in the loaded calendar month) needs the backend\r\n * to say whether the id is an event, series, or collection before the widget\r\n * can render the right view. This is the same `/api/v2/public/resolve/{id}`\r\n * endpoint get-micdrop.com's `/e/{slugId}` page resolves server-side; here we\r\n * call it cross-origin from the venue's page (the public API already serves\r\n * the calendar cross-origin, so CORS is in place).\r\n *\r\n * @param id - The entity id (numeric, type-agnostic)\r\n * @param slug - Optional slug hint (lets the backend canonicalize)\r\n * @returns `{ type, id, title, data }` or null on failure\r\n */\r\nexport async function resolvePublicEntity(\r\n id: string | number,\r\n slug?: string\r\n): Promise<ResolvedEntity | null> {\r\n try {\r\n const qs = slug ? `?slug=${encodeURIComponent(slug)}` : '';\r\n const data = await simpleFetch<ResolvedEntity>(\r\n `${getPublicBaseUrl()}/resolve/${encodeURIComponent(String(id))}${qs}`\r\n );\r\n\r\n if (!data || !data.type) {\r\n throw new AppError(\r\n 'Failed to resolve entity',\r\n 'lib/api/events/resolvePublicEntity'\r\n );\r\n }\r\n\r\n return data;\r\n } catch (error) {\r\n logger.error('Error resolving public entity:', error);\r\n return null;\r\n }\r\n}\r\n\r\n/**\r\n * Fetch series page data\r\n *\r\n * Returns full series information including occurrences, venue, and performers.\r\n *\r\n * @param seriesId - The series ID\r\n * @returns Series page data or null on error\r\n */\r\nexport async function fetchSeriesPage(\r\n seriesId: string | number\r\n): Promise<SeriesPageData | null> {\r\n try {\r\n const data = await simpleFetch<SeriesPageData>(\r\n `${getPublicBaseUrl()}/series/${seriesId}/page`\r\n );\r\n\r\n if (!data) {\r\n throw new AppError(\r\n 'Failed to fetch series page',\r\n 'lib/api/events/fetchSeriesPage'\r\n );\r\n }\r\n\r\n return data;\r\n } catch (error) {\r\n logger.error('Error fetching series page:', error);\r\n return null;\r\n }\r\n}\r\n\r\n/**\r\n * Fetch public collection data\r\n *\r\n * Returns collection info including events list.\r\n * For password-protected collections, pass the password to authenticate.\r\n *\r\n * @param collectionId - The collection ID\r\n * @param password - Optional password for protected collections\r\n * @returns Collection data or null on error\r\n */\r\nexport async function fetchPublicCollection(\r\n collectionId: string | number,\r\n password?: string\r\n): Promise<PublicCollectionData | null> {\r\n try {\r\n // Backend route is GET /api/v2/public/collections/:id (password via ?password=\r\n // query, read by GetPublicCollection). The previous \"/public\" suffix matched\r\n // no route and 404'd every public collection fetch.\r\n const url = password\r\n ? `${getPublicBaseUrl()}/collections/${collectionId}?password=${encodeURIComponent(password)}`\r\n : `${getPublicBaseUrl()}/collections/${collectionId}`;\r\n\r\n const data = await simpleFetch<PublicCollectionData>(url);\r\n\r\n if (!data) {\r\n throw new AppError(\r\n 'Failed to fetch collection',\r\n 'lib/api/events/fetchPublicCollection'\r\n );\r\n }\r\n\r\n return data;\r\n } catch (error) {\r\n logger.error('Error fetching collection:', error);\r\n return null;\r\n }\r\n}\r\n\r\n/**\r\n * Check collection password\r\n *\r\n * Validates password for password-protected collections.\r\n *\r\n * @param collectionId - The collection ID\r\n * @param password - The password to check\r\n * @returns Whether the password is valid\r\n */\r\nexport async function checkCollectionPassword(\r\n collectionId: string | number,\r\n password: string\r\n): Promise<boolean> {\r\n try {\r\n const encodedPassword = encodeURIComponent(password);\r\n const result = await simpleFetch<{ valid: boolean }>(\r\n `${getPublicBaseUrl()}/collections/${collectionId}/check-password/${encodedPassword}`\r\n );\r\n\r\n return result?.valid === true;\r\n } catch (error) {\r\n logger.error('Error checking collection password:', error);\r\n return false;\r\n }\r\n}\r\n\r\n/**\r\n * Check event password\r\n *\r\n * Validates password for password-protected events.\r\n *\r\n * @param eventId - The event ID\r\n * @param password - The password to check\r\n * @returns Whether the password is valid\r\n */\r\nexport async function checkEventPassword(\r\n eventId: string | number,\r\n password: string\r\n): Promise<{ valid: boolean }> {\r\n try {\r\n // POST in the body — never in the URL path, which is captured by\r\n // access logs, browser history, and Referer headers.\r\n const apiResult = await apiPost<{ valid: boolean } | boolean>(\r\n `${getPublicBaseUrl()}/events/${eventId}/check-password`,\r\n { password }\r\n );\r\n\r\n if (!apiResult.success) return { valid: false };\r\n\r\n const result = apiResult.data;\r\n if (typeof result === 'boolean') return { valid: result };\r\n return { valid: (result as { valid: boolean }).valid === true };\r\n } catch (error) {\r\n logger.error('Error checking event password:', error);\r\n return { valid: false };\r\n }\r\n}\r\n\r\n/**\r\n * Test network connection\r\n *\r\n * Health check endpoint for connectivity testing.\r\n *\r\n * @param orgId - Optional org ID\r\n * @param venueId - Optional venue ID\r\n * @returns Whether the connection is working\r\n */\r\nexport async function testNetworkConnection(\r\n orgId?: string | number,\r\n venueId?: string | number\r\n): Promise<boolean> {\r\n try {\r\n // Use a simple endpoint to test connectivity\r\n const url = venueId\r\n ? `${getPublicBaseUrl()}/events/venue/${venueId}`\r\n : orgId\r\n ? `${getPublicBaseUrl()}/venues/organization/${orgId}`\r\n : `${getPublicBaseUrl()}/health`;\r\n\r\n // @fetch-escape: HEAD request for connectivity probe — simpleFetch always parses JSON body; HEAD has no body\r\n const response = await fetch(url, {\r\n method: 'HEAD',\r\n });\r\n\r\n return response.ok;\r\n } catch (error) {\r\n logger.error('Network connection test failed:', error);\r\n return false;\r\n }\r\n}\r\n","/**\r\n * Venues API\r\n *\r\n * Functions for fetching venue data including\r\n * service fees and tax configuration.\r\n */\r\n\r\nimport { createLogger } from '@getmicdrop/svelte-components/utils/logger';\r\nconst logger = createLogger('VC');\r\nimport { apiGet } from './client.js';\r\nimport type { Venue } from './types.js';\r\n\r\n/**\r\n * Get venue details (public, no auth required)\r\n *\r\n * Fetches venue information including service fees and taxes\r\n * needed for checkout calculations.\r\n *\r\n * @param venueId - The venue ID\r\n * @returns Venue details or null on error\r\n */\r\nexport async function getVenue(venueId: string | number): Promise<Venue | null> {\r\n try {\r\n if (!venueId) {\r\n logger.warn('getVenue called without venueId');\r\n return null;\r\n }\r\n\r\n const result = await apiGet<Venue>(`/venues/${venueId}`);\r\n if (!result.success) {\r\n logger.error(`Failed to fetch venue: ${result.statusCode}`);\r\n return null;\r\n }\r\n return result.data ?? null;\r\n } catch (error) {\r\n logger.error('Error fetching venue:', error);\r\n return null;\r\n }\r\n}\r\n\r\n/**\r\n * Get venue service fee configuration\r\n *\r\n * Returns just the fee-related fields for checkout calculations.\r\n *\r\n * @param venueId - The venue ID\r\n * @returns Fee configuration or null on error\r\n */\r\nexport async function getVenueFees(\r\n venueId: string | number\r\n): Promise<{\r\n serviceFeePercentage: number;\r\n serviceFeeCents: number;\r\n taxPercentage: number;\r\n} | null> {\r\n const venue = await getVenue(venueId);\r\n\r\n if (!venue) {\r\n return null;\r\n }\r\n\r\n return {\r\n serviceFeePercentage: venue.serviceFeePercentage ?? 0,\r\n serviceFeeCents: venue.serviceFeeCents ?? 0,\r\n taxPercentage: venue.taxPercentage ?? 0,\r\n };\r\n}\r\n\r\n/**\r\n * Get venue by slug\r\n *\r\n * Fetches venue using its URL-friendly slug.\r\n *\r\n * @param slug - The venue slug\r\n * @returns Venue details or null on error\r\n */\r\nexport async function getVenueBySlug(slug: string): Promise<Venue | null> {\r\n try {\r\n if (!slug) {\r\n logger.warn('getVenueBySlug called without slug');\r\n return null;\r\n }\r\n\r\n const encodedSlug = encodeURIComponent(slug);\r\n const result = await apiGet<Venue>(`/venues/slug/${encodedSlug}`);\r\n if (!result.success) {\r\n logger.error(`Failed to fetch venue by slug: ${result.statusCode}`);\r\n return null;\r\n }\r\n return result.data ?? null;\r\n } catch (error) {\r\n logger.error('Error fetching venue by slug:', error);\r\n return null;\r\n }\r\n}\r\n","/**\r\n * Gift Card Application Transformer\r\n *\r\n * Normalizes the orders-service `POST /cart/{id}/apply-gift-card` SUCCESS\r\n * payload into the ApplyGiftCardResult shape the Checkout component /\r\n * GiftCardInput consume.\r\n *\r\n * Wire->domain boundary: one parseGiftCardApplication(raw: unknown) entry\r\n * point. The wire field shape (ApplyGiftCardApiResponse) is named ONLY inside\r\n * this module and never escapes. Error paths (429 rate-limit, !success,\r\n * network catch) are CALLER concerns and stay in applyGiftCard -- this parser\r\n * only maps the success body.\r\n *\r\n * UNIT CONTRACT (do NOT \"fix\" this -- VC public endpoints speak cents):\r\n * Backend ApplyGiftCardResponse (orders-service controllers/cart.go):\r\n * - appliedAmount : integer CENTS (int64)\r\n * - giftCardBalance : integer CENTS (int64)\r\n * - remainingTotal : DOLLARS (float64)\r\n * Domain ApplyGiftCardResult (formatCurrency-ready, DOLLARS):\r\n * - giftCardAmount : appliedAmount / 100 (cents -> dollars)\r\n * - stripeAmount : remainingTotal (already dollars, no /100)\r\n * - giftCardBalance : passed through as-is (raw CENTS, not displayed via\r\n * formatCurrency)\r\n * The cents->dollars conversion lives HERE, exactly where/how the inline\r\n * mapper in applyGiftCard had it -- byte-identical.\r\n */\r\n\r\nimport type { ApplyGiftCardResult } from '../gift-cards.js';\r\n\r\n// -- Wire shape (internal -- never exported) ---------------------------------\r\n// Source of truth: orders-service controllers/cart.go ApplyGiftCardResponse.\r\ninterface ApplyGiftCardApiResponse {\r\n valid: boolean;\r\n message?: string;\r\n giftCardCode?: string;\r\n giftCardBalance?: number; // cents\r\n appliedAmount?: number; // cents\r\n remainingTotal?: number; // dollars\r\n fullyCoveredByCard?: boolean;\r\n}\r\n\r\n/**\r\n * Parse a successful apply-gift-card backend payload into ApplyGiftCardResult.\r\n *\r\n * Maps the real backend contract to what the Checkout component expects:\r\n * - appliedAmount (cents) -> giftCardAmount (dollars)\r\n * - remainingTotal (dollars) -> stripeAmount (dollars, residual to charge)\r\n * - fullyCoveredByCard -> paymentType 'gift_card_only' | 'split' + requiresStripe\r\n * Byte-identical to the inline success-path mapping previously in applyGiftCard.\r\n */\r\nexport function parseGiftCardApplication(raw: unknown): ApplyGiftCardResult {\r\n const data = (raw ?? {}) as ApplyGiftCardApiResponse;\r\n\r\n const giftCardAmountDollars = (data.appliedAmount ?? 0) / 100;\r\n const residualDollars = data.remainingTotal ?? 0;\r\n // Full coverage when the card covers everything (no residual to charge).\r\n const fullyCovered = data.fullyCoveredByCard ?? residualDollars <= 0;\r\n const requiresStripe = !fullyCovered;\r\n\r\n return {\r\n valid: true,\r\n giftCardCode: data.giftCardCode,\r\n giftCardAmount: giftCardAmountDollars,\r\n giftCardBalance: data.giftCardBalance,\r\n paymentType: fullyCovered ? 'gift_card_only' : 'split',\r\n stripeAmount: residualDollars,\r\n orderTotal: giftCardAmountDollars + residualDollars,\r\n requiresStripe,\r\n };\r\n}\r\n","import { AppError, asVenueId } from '@getmicdrop/svelte-components';\r\nimport type { VenueId } from '@getmicdrop/svelte-components';\r\n/**\r\n * Gift Card Purchase API Client\r\n * Handles gift card purchase creation, cart application/removal,\r\n * and gift-card-only checkout completion.\r\n */\r\n\r\nimport { createLogger } from '@getmicdrop/svelte-components/utils/logger';\r\nimport { getOrdersV2Url, apiPost, apiDelete } from './client.js';\r\nimport { parseGiftCardApplication } from './transformers/giftCard.js';\r\nconst logger = createLogger('VC');\r\n\r\nconst API_BASE_URL = 'https://get-micdrop.com';\r\nconst ORDERS_V2_URL = `${API_BASE_URL}/api/orders/v2`;\r\nconst ORDERS_V2_PUBLIC = () => getOrdersV2Url();\r\n\r\n/**\r\n * Request payload for creating a gift card purchase\r\n */\r\nexport interface GiftCardPurchaseRequest {\r\n // Public boundary — accept plain number too so external TypeScript callers\r\n // that pass `venueId: 1` keep compiling. Runtime serializes a number either\r\n // way. Internal call sites pass branded VenueId via asVenueId().\r\n venueId: number | VenueId;\r\n amount: number; // In cents (e.g., 5000 = $50.00)\r\n recipientEmail: string;\r\n recipientName: string;\r\n personalMessage?: string;\r\n purchaserEmail: string;\r\n purchaserName: string;\r\n scheduledDeliveryAt?: string | null; // ISO date string or null for immediate\r\n}\r\n\r\n/**\r\n * Response from gift card purchase creation\r\n */\r\nexport interface GiftCardPurchaseResponse {\r\n giftCardUUID: string;\r\n clientSecret: string;\r\n amount: number;\r\n stripePublishableKey: string;\r\n}\r\n\r\n/**\r\n * Venue information needed for gift card purchase\r\n */\r\nexport interface VenueInfo {\r\n id: VenueId;\r\n name: string;\r\n slug: string;\r\n stripePublishableKey?: string;\r\n}\r\n\r\n/**\r\n * Create a gift card purchase and get Stripe client secret\r\n * @param req - Gift card purchase request details\r\n * @returns Gift card UUID and Stripe client secret, or null on failure\r\n */\r\nexport async function createGiftCardPurchase(\r\n req: GiftCardPurchaseRequest\r\n): Promise<GiftCardPurchaseResponse | null> {\r\n try {\r\n const apiResult = await apiPost<GiftCardPurchaseResponse>(\r\n `${ORDERS_V2_URL}/gift-cards`,\r\n req\r\n );\r\n\r\n if (!apiResult.success) {\r\n logger.error('Gift card purchase creation failed:', apiResult.error);\r\n throw new AppError(apiResult.error || 'Failed to create gift card purchase', 'lib/api/gift-cards/createGiftCardPurchase');\r\n }\r\n\r\n logger.debug('Gift card purchase created:', apiResult.data);\r\n return apiResult.data!;\r\n } catch (error) {\r\n logger.error('Gift card purchase creation error:', error);\r\n return null;\r\n }\r\n}\r\n\r\n/**\r\n * Result shape consumed by the Checkout component / GiftCardInput.\r\n * Monetary fields here are in DOLLARS (formatCurrency-ready).\r\n */\r\nexport interface ApplyGiftCardResult {\r\n valid: boolean;\r\n giftCardCode?: string;\r\n giftCardAmount?: number; // dollars applied from the gift card\r\n giftCardBalance?: number; // cents (raw balance, not displayed via formatCurrency)\r\n paymentType?: string; // 'gift_card_only' (full) | 'split' (partial)\r\n stripeAmount?: number; // dollars the card must still cover (residual)\r\n orderTotal?: number; // dollars (gift card + residual)\r\n requiresStripe?: boolean;\r\n error?: string;\r\n}\r\n\r\n/**\r\n * Apply a gift card to a cart.\r\n */\r\nexport async function applyGiftCard(\r\n cartId: string,\r\n code: string\r\n): Promise<ApplyGiftCardResult> {\r\n try {\r\n const apiResult = await apiPost<unknown>(\r\n `${ORDERS_V2_PUBLIC()}/cart/${cartId}/apply-gift-card`,\r\n { giftCardCode: code }\r\n );\r\n\r\n if (!apiResult.success) {\r\n if (apiResult.statusCode === 429) {\r\n return {\r\n valid: false,\r\n error: 'Too many attempts. Please wait a moment and try again.',\r\n };\r\n }\r\n logger.error('Apply gift card failed:', apiResult.error);\r\n return { valid: false, error: apiResult.error || 'Invalid gift card code' };\r\n }\r\n\r\n // Success-path mapping (cents->dollars, fullyCovered/requiresStripe) lives\r\n // in parseGiftCardApplication — byte-identical to the inline mapper this\r\n // replaced. The cents->dollars conversion stays in the parser (unit\r\n // contract documented there). Error/429/network branches stay here.\r\n return parseGiftCardApplication(apiResult.data);\r\n } catch (err) {\r\n logger.error('applyGiftCard error:', err);\r\n return {\r\n valid: false,\r\n error: 'Network error. Please check your connection and try again.',\r\n };\r\n }\r\n}\r\n\r\nexport interface RemoveGiftCardResult {\r\n success: boolean;\r\n error?: string;\r\n}\r\n\r\n/**\r\n * Remove a gift card from a cart.\r\n */\r\nexport async function removeGiftCard(cartId: string): Promise<RemoveGiftCardResult> {\r\n try {\r\n const apiResult = await apiDelete(`${ORDERS_V2_PUBLIC()}/cart/${cartId}/gift-card`);\r\n\r\n if (!apiResult.success) {\r\n logger.error('Remove gift card failed:', apiResult.error);\r\n return { success: false, error: apiResult.error || 'Failed to remove gift card' };\r\n }\r\n\r\n return { success: true };\r\n } catch (err) {\r\n logger.error('removeGiftCard error:', err);\r\n return {\r\n success: false,\r\n error: 'Network error. Please check your connection and try again.',\r\n };\r\n }\r\n}\r\n\r\nexport interface CompleteGiftCardPaymentInput {\r\n firstName: string;\r\n lastName: string;\r\n email: string;\r\n phoneNumber?: string | null;\r\n mailingList?: boolean;\r\n}\r\n\r\nexport interface CompleteGiftCardPaymentResult {\r\n success: boolean;\r\n orderId?: string;\r\n confirmationNumber?: string;\r\n error?: string;\r\n}\r\n\r\n/**\r\n * Complete a gift-card-only payment (no Stripe required when balance >= total).\r\n */\r\nexport async function completeGiftCardPayment(\r\n cartId: string,\r\n customerDetails: CompleteGiftCardPaymentInput\r\n): Promise<CompleteGiftCardPaymentResult> {\r\n try {\r\n const apiResult = await apiPost<{ orderId?: string; confirmationNumber?: string }>(\r\n `${ORDERS_V2_PUBLIC()}/cart/${cartId}/complete-gift-card-payment`,\r\n {\r\n firstName: customerDetails.firstName,\r\n lastName: customerDetails.lastName,\r\n email: customerDetails.email,\r\n phoneNumber: customerDetails.phoneNumber || null,\r\n mailingList: customerDetails.mailingList || false,\r\n }\r\n );\r\n\r\n if (!apiResult.success) {\r\n logger.error('Complete gift card payment failed:', apiResult.error);\r\n return { success: false, error: apiResult.error || 'Failed to complete payment' };\r\n }\r\n\r\n return {\r\n success: true,\r\n orderId: apiResult.data?.orderId,\r\n confirmationNumber: apiResult.data?.confirmationNumber,\r\n };\r\n } catch (err) {\r\n logger.error('completeGiftCardPayment error:', err);\r\n return {\r\n success: false,\r\n error: 'Network error. Please check your connection and try again.',\r\n };\r\n }\r\n}\r\n\r\n/**\r\n * Fetch venue information by slug\r\n * @param slug - Venue slug\r\n * @returns Venue info or null on failure\r\n */\r\nexport async function getVenueBySlug(\r\n slug: string,\r\n customFetch: typeof fetch = fetch\r\n): Promise<VenueInfo | null> {\r\n try {\r\n // @fetch-escape: customFetch injection for SSR context — cannot use apiGet (caller-supplied fetch instance)\r\n const response = await customFetch(\r\n `${API_BASE_URL}/api/v2/public/venues/slug/${slug}`\r\n );\r\n\r\n if (!response.ok) {\r\n logger.error(`Failed to fetch venue by slug: ${response.status}`);\r\n return null;\r\n }\r\n\r\n const venue = await response.json();\r\n return {\r\n id: asVenueId(venue.id),\r\n name: venue.name,\r\n slug: venue.slug,\r\n stripePublishableKey: venue.stripePublishableKey,\r\n };\r\n } catch (error) {\r\n logger.error('Error fetching venue by slug:', error);\r\n return null;\r\n }\r\n}\r\n","/**\r\n * Waitlist API\r\n *\r\n * Endpoints for the sold-out-event waitlist flow.\r\n */\r\n\r\nimport { createLogger } from '@getmicdrop/svelte-components/utils/logger';\r\nimport { apiPost, apiGet } from './client.js';\r\n\r\nconst logger = createLogger('VC');\r\n\r\nexport interface JoinWaitlistResult {\r\n success: boolean;\r\n // No position — opaque to the customer (decision #2/#20).\r\n updated?: boolean;\r\n error?: string;\r\n}\r\n\r\nconst WAITLIST_ERROR_MESSAGES: Record<string, string> = {\r\n waitlist_not_enabled: 'Waitlist is not available for this event',\r\n event_not_found: 'Event not found',\r\n invalid_phone: 'Please enter a valid phone number',\r\n already_purchased: 'You have already purchased tickets for this event',\r\n phone_already_registered: 'This phone number is already on the waitlist',\r\n};\r\n\r\n/**\r\n * Join the waitlist for a sold-out event.\r\n */\r\nexport async function joinWaitlist(\r\n eventId: string | number,\r\n email: string,\r\n phone: string | null = null,\r\n smsOptIn = false\r\n): Promise<JoinWaitlistResult> {\r\n try {\r\n const body: Record<string, unknown> = { email };\r\n if (phone && smsOptIn) {\r\n body.phone = phone;\r\n body.smsOptIn = true;\r\n }\r\n\r\n const apiResult = await apiPost<{ updated?: boolean }>(\r\n `/events/${eventId}/waitlist`,\r\n body\r\n );\r\n\r\n if (!apiResult.success) {\r\n logger.error('Join waitlist failed:', apiResult.error);\r\n return {\r\n success: false,\r\n error:\r\n WAITLIST_ERROR_MESSAGES[apiResult.error ?? ''] ||\r\n 'Failed to join waitlist. Please try again.',\r\n };\r\n }\r\n\r\n return {\r\n success: true,\r\n updated: apiResult.data?.updated,\r\n };\r\n } catch (err) {\r\n logger.error('joinWaitlist error:', err);\r\n return {\r\n success: false,\r\n error: 'Network error. Please check your connection and try again.',\r\n };\r\n }\r\n}\r\n\r\nexport interface WaitlistStatusResult {\r\n waitlistEnabled: boolean;\r\n soldOut: boolean;\r\n /**\r\n * When gated, the public event page should present \"Sold Out — join the\r\n * waitlist\" to non-members even if tickets are technically available, and only\r\n * a valid tokenized link unlocks checkout (reconciled decision #3).\r\n */\r\n gated: boolean;\r\n}\r\n\r\n/**\r\n * Fetch an event's waitlist gating status. Returns a non-gated default if the\r\n * request fails so the page degrades to normal behavior.\r\n */\r\nexport async function getWaitlistStatus(\r\n eventId: string | number\r\n): Promise<WaitlistStatusResult> {\r\n const fallback: WaitlistStatusResult = {\r\n waitlistEnabled: false,\r\n soldOut: false,\r\n gated: false,\r\n };\r\n try {\r\n const apiResult = await apiGet<{ waitlistEnabled: boolean; soldOut: boolean; gated: boolean }>(\r\n `/events/${eventId}/waitlist/status`\r\n );\r\n if (!apiResult.success) return fallback;\r\n return {\r\n waitlistEnabled: !!apiResult.data?.waitlistEnabled,\r\n soldOut: !!apiResult.data?.soldOut,\r\n gated: !!apiResult.data?.gated,\r\n };\r\n } catch (err) {\r\n logger.error('getWaitlistStatus error:', err);\r\n return fallback;\r\n }\r\n}\r\n","/**\r\n * CTA state computation.\r\n *\r\n * Pure function (no I/O) — derives the buy-button state from an event and\r\n * its ticket list. Lives in `api/` because it is the canonical, single\r\n * source of truth that replaces the duplicate implementations previously\r\n * scattered across utils/api.js, core/api-client.js, and a few components.\r\n */\r\n\r\n// Import from the specific subpath (not the bare entry) so the api/\r\n// bundle doesn't pull in the entire SC component graph (Accordion etc.)\r\n// and break `npm run build:api`.\r\nimport {\r\n formatDateRaw,\r\n formatTime,\r\n} from '@getmicdrop/svelte-components/utils/formatters';\r\n// Hidden-ticket predicate from the `/transforms` leaf subpath (no component\r\n// graph, so `npm run build:api` stays clean). SC's copy was pinned FROM this\r\n// body (dedup #392) and additionally honours the backend-resolved\r\n// `effectiveSales*` window for relative-offset tickets (#388). Imported (not\r\n// just re-exported) so it stays in local scope for `computeCtaState` below.\r\nimport { isHiddenTicketPurchasable } from '@getmicdrop/svelte-components/transforms';\r\n\r\nexport interface CtaStateOptions {\r\n cancelled?: boolean;\r\n isRegistration?: boolean;\r\n waitlistEnabled?: boolean;\r\n}\r\n\r\nexport interface CtaState {\r\n text: string;\r\n disabled: boolean;\r\n reason:\r\n | 'cancelled'\r\n | 'event_past'\r\n | 'no_tickets'\r\n | 'available'\r\n | 'coming_soon'\r\n | 'sold_out_waitlist'\r\n | 'sold_out'\r\n | 'sales_ended'\r\n | 'hidden_only';\r\n}\r\n\r\ninterface CtaEvent {\r\n startDateTime?: string | null;\r\n endDateTime?: string | null;\r\n hasPurchasableHiddenTickets?: boolean;\r\n}\r\n\r\ninterface CtaTicket {\r\n salesChannel?: number;\r\n salesBegin?: string | null;\r\n salesStart?: string | null;\r\n saleBegin?: string | null;\r\n onSaleStart?: string | null;\r\n salesEnd?: string | null;\r\n saleEnd?: string | null;\r\n onSaleEnd?: string | null;\r\n remainingCapacity?: number | null;\r\n quantityRemaining?: number | null;\r\n quantity?: number | null;\r\n soldOut?: boolean;\r\n isHidden?: boolean;\r\n visibility?: number;\r\n}\r\n\r\n// A hidden ticket (visibility>=2 or isHidden) that's still on sale drives the\r\n// \"Sales ended\" / \"Sold out\" / \"No tickets\" → \"Get tickets\" override. The user\r\n// just needs the promo code to see it. Re-exported from the SC canonical so\r\n// existing consumers (cta.test.ts, computeCtaState) keep their import path while\r\n// the single-source body lives in SC (dedup #392).\r\nexport { isHiddenTicketPurchasable };\r\n\r\nexport function computeCtaState(\r\n event: CtaEvent,\r\n tickets: CtaTicket[] | undefined,\r\n options: CtaStateOptions = {}\r\n): CtaState {\r\n const {\r\n cancelled = false,\r\n isRegistration = false,\r\n waitlistEnabled = false,\r\n } = options;\r\n const now = new Date();\r\n\r\n if (cancelled)\r\n return { text: 'Cancelled', disabled: true, reason: 'cancelled' };\r\n\r\n const eventEnd = event.endDateTime || event.startDateTime;\r\n if (eventEnd && new Date(eventEnd) < now) {\r\n return { text: 'Sales ended', disabled: true, reason: 'event_past' };\r\n }\r\n\r\n const hiddenAvailable =\r\n event?.hasPurchasableHiddenTickets === true ||\r\n (Array.isArray(tickets) && tickets.some(isHiddenTicketPurchasable));\r\n\r\n const publicTickets = (tickets || []).filter(\r\n t =>\r\n t.salesChannel !== 2 &&\r\n !t.isHidden &&\r\n !(typeof t.visibility === 'number' && t.visibility >= 2)\r\n );\r\n if (publicTickets.length === 0) {\r\n if (hiddenAvailable) {\r\n return {\r\n text: isRegistration ? 'Reserve a spot' : 'Get tickets',\r\n disabled: false,\r\n reason: 'hidden_only',\r\n };\r\n }\r\n return {\r\n text: 'No tickets available',\r\n disabled: true,\r\n reason: 'no_tickets',\r\n };\r\n }\r\n\r\n let hasPurchasable = false;\r\n let allSoldOut = true;\r\n let allComingSoon = true;\r\n let earliestSalesStart: Date | null = null;\r\n\r\n for (const ticket of publicTickets) {\r\n const salesBegin =\r\n ticket.salesBegin ||\r\n ticket.salesStart ||\r\n ticket.saleBegin ||\r\n ticket.onSaleStart;\r\n const salesEnd = ticket.salesEnd || ticket.saleEnd || ticket.onSaleEnd;\r\n const remaining =\r\n ticket.remainingCapacity ?? ticket.quantityRemaining ?? ticket.quantity;\r\n const isSoldOut =\r\n ticket.soldOut ||\r\n (remaining !== null && remaining !== undefined && remaining <= 0);\r\n\r\n const isScheduled = salesBegin ? new Date(salesBegin) > now : false;\r\n const hasSalesEnded = salesEnd ? new Date(salesEnd) < now : false;\r\n const isPurchasable = !isSoldOut && !isScheduled && !hasSalesEnded;\r\n\r\n if (isPurchasable) hasPurchasable = true;\r\n if (!isSoldOut) allSoldOut = false;\r\n if (!isScheduled) allComingSoon = false;\r\n\r\n if (isScheduled && salesBegin && !isSoldOut && !hasSalesEnded) {\r\n const startDate = new Date(salesBegin);\r\n if (!earliestSalesStart || startDate < earliestSalesStart) {\r\n earliestSalesStart = startDate;\r\n }\r\n }\r\n }\r\n\r\n if (hasPurchasable) {\r\n return {\r\n text: isRegistration ? 'Reserve a spot' : 'Get tickets',\r\n disabled: false,\r\n reason: 'available',\r\n };\r\n }\r\n\r\n if (allComingSoon && earliestSalesStart) {\r\n if (hiddenAvailable) {\r\n return {\r\n text: isRegistration ? 'Reserve a spot' : 'Get tickets',\r\n disabled: false,\r\n reason: 'hidden_only',\r\n };\r\n }\r\n const diffMs = earliestSalesStart.getTime() - now.getTime();\r\n const diffHours = diffMs / (1000 * 60 * 60);\r\n const diffDays = diffMs / (1000 * 60 * 60 * 24);\r\n let dateText: string;\r\n if (diffHours < 24) {\r\n dateText = formatTime(earliestSalesStart, { hour12: true }, 'en-US');\r\n } else if (diffDays <= 7) {\r\n dateText = formatDateRaw(\r\n earliestSalesStart,\r\n { weekday: 'short', hour: 'numeric', minute: '2-digit', hour12: true },\r\n 'en-US'\r\n );\r\n } else {\r\n const d = formatDateRaw(\r\n earliestSalesStart,\r\n { month: 'short', day: 'numeric' },\r\n 'en-US'\r\n );\r\n const t = formatTime(earliestSalesStart, { hour12: true }, 'en-US');\r\n dateText = `${d} ${t}`;\r\n }\r\n return {\r\n text: `On sale ${dateText}`,\r\n disabled: true,\r\n reason: 'coming_soon',\r\n };\r\n }\r\n\r\n if (allSoldOut) {\r\n if (hiddenAvailable) {\r\n return {\r\n text: isRegistration ? 'Reserve a spot' : 'Get tickets',\r\n disabled: false,\r\n reason: 'hidden_only',\r\n };\r\n }\r\n if (waitlistEnabled) {\r\n return {\r\n text: 'Join Waitlist',\r\n disabled: false,\r\n reason: 'sold_out_waitlist',\r\n };\r\n }\r\n return { text: 'Sold out', disabled: true, reason: 'sold_out' };\r\n }\r\n\r\n if (hiddenAvailable) {\r\n return {\r\n text: isRegistration ? 'Reserve a spot' : 'Get tickets',\r\n disabled: false,\r\n reason: 'hidden_only',\r\n };\r\n }\r\n\r\n return { text: 'Sales ended', disabled: true, reason: 'sales_ended' };\r\n}\r\n","// @currency-escape: widget-intentional. \r\nimport type { USD } from '@getmicdrop/svelte-components';\r\nimport { formatCurrency } from '@getmicdrop/svelte-components';\r\n/**\r\n * Order Transformer\r\n *\r\n * Normalizes API order responses to a consistent format.\r\n * Handles variations in field names between different API versions.\r\n *\r\n * Wire->domain boundary: one parseOrder(raw: unknown) entry point.\r\n * The wire field shapes (ApiOrder / ApiTicket) are named only inside this\r\n * module and never escape it.\r\n *\r\n * IMPLEMENTATION NOTE -- why no Zod schema here:\r\n * The old mapper used `||` for id and ALL money fields (totalAmount, serviceFeesAmount,\r\n * taxAmount, discount -- 0 falls through to next variant). The old mapper did NOT use\r\n * `??` for money; that was a bug in the first Zod-based rewrite. Replicated verbatim\r\n * for byte-identical behavior. Throw behavior is identical to the old mapper: null/\r\n * null/undefined input or a non-array (the latter throws on .map) tickets field throws exactly as the old code did.\r\n * Callers always pass real wire objects.\r\n */\r\n\r\nimport type { Order, PurchasedTicket } from '../types.js';\r\n\r\n// ── Wire shapes (internal -- never exported) ─────────────────────────────\r\ninterface ApiTicket {\r\n uuid?: string;\r\n id?: number;\r\n ID?: number;\r\n ticketNumber?: string;\r\n ticket_number?: string;\r\n orderId?: string | number;\r\n order_id?: string | number;\r\n attendeeFirstName?: string;\r\n attendee_first_name?: string;\r\n firstName?: string;\r\n attendeeLastName?: string;\r\n attendee_last_name?: string;\r\n lastName?: string;\r\n attendeeEmail?: string;\r\n attendee_email?: string;\r\n email?: string;\r\n ticketName?: string;\r\n ticket_name?: string;\r\n name?: string;\r\n ticketTypeId?: number;\r\n ticket_type_id?: number;\r\n purchasePrice?: number;\r\n purchase_price?: number;\r\n price?: number;\r\n status?: string;\r\n checkedIn?: boolean;\r\n checked_in?: boolean;\r\n checkedInAt?: string;\r\n checked_in_at?: string;\r\n}\r\n\r\ninterface ApiOrder {\r\n uuid?: string;\r\n id?: number;\r\n ID?: number;\r\n customerEmail?: string;\r\n email?: string;\r\n customer_email?: string;\r\n customerFirstName?: string;\r\n firstName?: string;\r\n first_name?: string;\r\n customerLastName?: string;\r\n lastName?: string;\r\n last_name?: string;\r\n status?: string;\r\n totalAmount?: number;\r\n total?: number;\r\n total_amount?: number;\r\n subtotal?: number;\r\n serviceFeesAmount?: number;\r\n serviceFee?: number;\r\n service_fee?: number;\r\n service_fees_amount?: number;\r\n taxAmount?: number;\r\n tax?: number;\r\n tax_amount?: number;\r\n discount?: number;\r\n paymentIntentId?: string;\r\n payment_intent_id?: string;\r\n paymentMethod?: string;\r\n payment_method?: string;\r\n purchasedTickets?: any[];\r\n tickets?: any[];\r\n purchased_tickets?: any[];\r\n createdAt?: string;\r\n created_at?: string;\r\n updatedAt?: string;\r\n updated_at?: string;\r\n}\r\n\r\n/**\r\n * Parse a raw purchased-ticket payload into the normalized PurchasedTicket domain shape.\r\n *\r\n * Wire shape (ApiTicket) is contained inside this module. Uses `||` chains matching\r\n * the old transformer exactly -- byte-identical to previous behavior. Throw behavior\r\n * preserved from old mapper: null/undefined input (property access on a nullish value) throws as the old code did.\r\n */\r\nexport function parsePurchasedTicket(raw: unknown): PurchasedTicket {\r\n const apiTicket = raw as ApiTicket;\r\n return {\r\n uuid: apiTicket.uuid || String(apiTicket.id || apiTicket.ID || ''),\r\n id: apiTicket.id || apiTicket.ID,\r\n ticketNumber: apiTicket.ticketNumber || apiTicket.ticket_number,\r\n orderId: apiTicket.orderId || apiTicket.order_id,\r\n attendeeFirstName:\r\n apiTicket.attendeeFirstName ||\r\n apiTicket.attendee_first_name ||\r\n apiTicket.firstName,\r\n attendeeLastName:\r\n apiTicket.attendeeLastName ||\r\n apiTicket.attendee_last_name ||\r\n apiTicket.lastName,\r\n attendeeEmail:\r\n apiTicket.attendeeEmail || apiTicket.attendee_email || apiTicket.email,\r\n ticketName:\r\n apiTicket.ticketName || apiTicket.ticket_name || apiTicket.name || '',\r\n ticketTypeId: apiTicket.ticketTypeId || apiTicket.ticket_type_id,\r\n purchasePrice:\r\n apiTicket.purchasePrice ||\r\n apiTicket.purchase_price ||\r\n apiTicket.price ||\r\n 0,\r\n status: apiTicket.status,\r\n checkedIn: apiTicket.checkedIn || apiTicket.checked_in,\r\n checkedInAt: apiTicket.checkedInAt || apiTicket.checked_in_at,\r\n };\r\n}\r\n\r\n/**\r\n * Parse a raw order payload into the normalized Order domain shape.\r\n *\r\n * Wire shape (ApiOrder) is contained inside this module and never escapes it.\r\n * Uses `||` chains matching the old transformer exactly (all money fields use `||`,\r\n * not `??` -- 0-valued amounts fall through to next variant, same as old) --\r\n * byte-identical to previous behavior. Throw behavior preserved from old mapper:\r\n * null/undefined input (property access on a nullish value) or a non-array tickets field throws as the old code did.\r\n */\r\nexport function parseOrder(raw: unknown): Order {\r\n const apiOrder = raw as ApiOrder;\r\n\r\n // Extract tickets array from various possible field names\r\n const rawTickets =\r\n apiOrder.purchasedTickets ||\r\n apiOrder.tickets ||\r\n apiOrder.purchased_tickets ||\r\n [];\r\n\r\n return {\r\n uuid: apiOrder.uuid || String(apiOrder.id || apiOrder.ID || ''),\r\n id: apiOrder.id || apiOrder.ID,\r\n customerEmail:\r\n apiOrder.customerEmail ||\r\n apiOrder.email ||\r\n apiOrder.customer_email ||\r\n '',\r\n customerFirstName:\r\n apiOrder.customerFirstName ||\r\n apiOrder.firstName ||\r\n apiOrder.first_name,\r\n customerLastName:\r\n apiOrder.customerLastName ||\r\n apiOrder.lastName ||\r\n apiOrder.last_name,\r\n status: apiOrder.status || 'unknown',\r\n totalAmount:\r\n apiOrder.totalAmount ||\r\n apiOrder.total ||\r\n apiOrder.total_amount ||\r\n 0,\r\n subtotal: apiOrder.subtotal,\r\n serviceFeesAmount:\r\n apiOrder.serviceFeesAmount ||\r\n apiOrder.serviceFee ||\r\n apiOrder.service_fee ||\r\n apiOrder.service_fees_amount ||\r\n 0,\r\n taxAmount:\r\n apiOrder.taxAmount || apiOrder.tax || apiOrder.tax_amount || 0,\r\n discount: apiOrder.discount || 0,\r\n paymentIntentId:\r\n apiOrder.paymentIntentId || apiOrder.payment_intent_id,\r\n paymentMethod: apiOrder.paymentMethod || apiOrder.payment_method,\r\n purchasedTickets: rawTickets.map(parsePurchasedTicket),\r\n createdAt: apiOrder.createdAt || apiOrder.created_at,\r\n updatedAt: apiOrder.updatedAt || apiOrder.updated_at,\r\n };\r\n}\r\n\r\n/**\r\n * Back-compat alias -- existing call sites using transformTicket continue\r\n * to work unchanged.\r\n */\r\nexport const transformTicket = parsePurchasedTicket;\r\n\r\n/**\r\n * Back-compat alias -- existing call sites using transformOrder(apiOrder)\r\n * continue to work unchanged.\r\n */\r\nexport const transformOrder = parseOrder;\r\n\r\n/**\r\n * Transform order for display on success page.\r\n *\r\n * Returns a simplified object with display-ready values.\r\n */\r\nexport function transformOrderForDisplay(raw: unknown): {\r\n orderId: string;\r\n displayOrderId: string;\r\n email: string;\r\n customerName: string;\r\n total: number;\r\n formattedTotal: string;\r\n ticketCount: number;\r\n status: string;\r\n} {\r\n const order = parseOrder(raw);\r\n const displayId = order.uuid.split('-')[0] || order.uuid;\r\n\r\n return {\r\n orderId: order.uuid,\r\n displayOrderId: displayId,\r\n email: order.customerEmail,\r\n customerName: [order.customerFirstName, order.customerLastName]\r\n .filter(Boolean)\r\n .join(' '),\r\n total: order.totalAmount,\r\n formattedTotal: `${formatCurrency((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. */)}`,\r\n ticketCount: order.purchasedTickets.length,\r\n status: order.status,\r\n };\r\n}\r\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 type { Event, AvailableTicket } from '../types.js';\n\n// CDN base URL for images\nconst CDN_BASE_URL = 'https://micdrop-images.sfo3.digitaloceanspaces.com';\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 * Prepends the CDN base URL if the path is relative.\n */\nexport function getCDNImageUrl(path: string | undefined): string {\n if (!path) return '';\n if (path.startsWith('http')) return path;\n return `${CDN_BASE_URL}/${path.replace(/^\\//, '')}`;\n}\n\n/**\n * Extract image URL from event with fallbacks.\n *\n * NOTE: shadows SC's hoisted `getEventImageUrl` (surfaced once VC pinned SC\n * 6.24.78 for EventCard). NOT swapped to SC's yet because the field coverage\n * differs -- this transformer reads `imageUrl -> imageURL -> image_url -> image`\n * (incl. the camelCase `imageURL`, asserted by event.test.ts), whereas SC reads\n * `image -> coverImage -> imageUrl -> image_url` (no `imageURL`, different\n * precedence). Reconciling those is consolidation audit #4/#5's job; deferring\n * here keeps the API-transformer behavior unchanged.\n */\n// eslint-disable-next-line micdrop/no-shadowed-helper -- see note above: SC's getEventImageUrl has different field coverage; consolidation deferred to audit #4/#5\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 */\nexport function deriveEventCtaState(\n apiEvent: ApiEvent\n): 'available' | 'sold_out' | 'coming_soon' | 'ended' {\n // Check explicit CTA state first\n const explicitState = apiEvent.ctaState || apiEvent.cta_state;\n if (explicitState) {\n return explicitState as any;\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","/**\r\n * Venue Transformer\r\n *\r\n * Normalizes API venue responses to a consistent format.\r\n *\r\n * Wire->domain boundary: one parseVenue(raw: unknown) entry point.\r\n * The wire field shape (ApiVenue) is named only inside this module and never\r\n * escapes it. Above the boundary only the Venue domain type.\r\n *\r\n * IMPLEMENTATION NOTE -- why no Zod schema here:\r\n * The old mapper used `||` for id/name (0 falls through) and `??` for fee fields\r\n * (0 fee is a real value, must NOT fall through). Both operators replicated verbatim.\r\n * Throw behavior is identical to the old mapper: null/undefined input (property access on a nullish value) throws exactly\r\n * as the old code did. Callers always pass real wire objects.\r\n */\r\n\r\nimport type { Venue } from '../types.js';\r\n\r\n// CDN base URL for images\r\nconst CDN_BASE_URL = 'https://micdrop-images.sfo3.digitaloceanspaces.com';\r\n\r\n// ── Wire shape (internal -- never exported) ──────────────────────────────\r\ninterface ApiVenue {\r\n id?: number;\r\n ID?: number;\r\n name?: string;\r\n slug?: string;\r\n address?: string;\r\n googleLocationNameCache?: string;\r\n google_location_name_cache?: string;\r\n city?: string;\r\n state?: string;\r\n zipCode?: string;\r\n zip_code?: string;\r\n country?: string;\r\n timezone?: string;\r\n time_zone?: string;\r\n logoUrl?: string;\r\n logo_url?: string;\r\n logo?: string;\r\n serviceFeePercentage?: number;\r\n service_fee_percentage?: number;\r\n serviceFeeCents?: number;\r\n service_fee_cents?: number;\r\n taxPercentage?: number;\r\n tax_percentage?: number;\r\n organizationId?: number;\r\n organization_id?: number;\r\n}\r\n\r\n/**\r\n * Get CDN image URL for venue logo\r\n */\r\nfunction getLogoUrl(path: string | undefined): string {\r\n if (!path) return '';\r\n if (path.startsWith('http')) return path;\r\n return `${CDN_BASE_URL}/${path.replace(/^\\//, '')}`;\r\n}\r\n\r\n/**\r\n * Parse a raw venue payload into the normalized Venue domain shape.\r\n *\r\n * Wire shape (ApiVenue) is contained inside this module and never escapes it.\r\n * Uses `||` / `??` chains matching the old transformer exactly -- byte-identical\r\n * to previous behavior. Throw behavior preserved from old mapper: null/undefined\r\n * input throws exactly as the old code did. Callers always pass real wire objects.\r\n */\r\nexport function parseVenue(raw: unknown): Venue {\r\n const apiVenue = raw as ApiVenue;\r\n\r\n const logoPath =\r\n apiVenue.logoUrl || apiVenue.logo_url || apiVenue.logo;\r\n\r\n return {\r\n id: apiVenue.id || apiVenue.ID || 0,\r\n name: apiVenue.name || '',\r\n slug: apiVenue.slug,\r\n address: apiVenue.address,\r\n googleLocationNameCache:\r\n apiVenue.googleLocationNameCache ||\r\n apiVenue.google_location_name_cache,\r\n city: apiVenue.city,\r\n state: apiVenue.state,\r\n zipCode: apiVenue.zipCode || apiVenue.zip_code,\r\n country: apiVenue.country,\r\n timezone: apiVenue.timezone || apiVenue.time_zone,\r\n logoUrl: getLogoUrl(logoPath),\r\n serviceFeePercentage:\r\n apiVenue.serviceFeePercentage ??\r\n apiVenue.service_fee_percentage ??\r\n 0,\r\n serviceFeeCents:\r\n apiVenue.serviceFeeCents ?? apiVenue.service_fee_cents ?? 0,\r\n taxPercentage:\r\n apiVenue.taxPercentage ?? apiVenue.tax_percentage ?? 0,\r\n organizationId:\r\n apiVenue.organizationId || apiVenue.organization_id,\r\n };\r\n}\r\n\r\n/**\r\n * Back-compat alias -- existing call sites using transformVenue(apiVenue)\r\n * continue to work unchanged.\r\n */\r\nexport const transformVenue = parseVenue;\r\n\r\n/**\r\n * Extract fee configuration from a raw venue payload.\r\n *\r\n * Uses `??` (not `||`) so 0-valued fees are preserved correctly.\r\n */\r\nexport function extractVenueFees(raw: unknown): {\r\n serviceFeePercentage: number;\r\n serviceFeeCents: number;\r\n taxPercentage: number;\r\n} {\r\n const apiVenue = raw as ApiVenue;\r\n return {\r\n serviceFeePercentage:\r\n apiVenue.serviceFeePercentage ??\r\n apiVenue.service_fee_percentage ??\r\n 0,\r\n serviceFeeCents:\r\n apiVenue.serviceFeeCents ?? apiVenue.service_fee_cents ?? 0,\r\n taxPercentage:\r\n apiVenue.taxPercentage ?? apiVenue.tax_percentage ?? 0,\r\n };\r\n}\r\n\r\n/**\r\n * Get formatted venue address\r\n */\r\nexport function formatVenueAddress(venue: Venue): string {\r\n const parts = [\r\n venue.address,\r\n venue.city,\r\n venue.state,\r\n venue.zipCode,\r\n ].filter(Boolean);\r\n\r\n return parts.join(', ');\r\n}\r\n","/*! js-cookie v3.0.8 | MIT */\nfunction assign (target) {\n for (var i = 1; i < arguments.length; i++) {\n var source = arguments[i];\n for (var key in source) {\n if (key === '__proto__') continue\n target[key] = source[key];\n }\n }\n return target\n}\n\nvar defaultConverter = {\n read: function (value) {\n if (value[0] === '\"') {\n value = value.slice(1, -1);\n }\n return value.replace(/(%[\\dA-F]{2})+/gi, decodeURIComponent)\n },\n write: function (value) {\n return encodeURIComponent(value).replace(\n /%(2[346BF]|3[AC-F]|40|5[BDE]|60|7[BCD])/g,\n decodeURIComponent\n )\n }\n};\n\nfunction init(converter, defaultAttributes) {\n function set(name, value, attributes) {\n if (typeof document === 'undefined') {\n return\n }\n\n attributes = assign({}, defaultAttributes, attributes);\n\n if (typeof attributes.expires === 'number') {\n attributes.expires = new Date(Date.now() + attributes.expires * 864e5);\n }\n if (attributes.expires) {\n attributes.expires = attributes.expires.toUTCString();\n }\n\n name = encodeURIComponent(name)\n .replace(/%(2[346B]|5E|60|7C)/g, decodeURIComponent)\n .replace(/[()]/g, escape);\n\n var stringifiedAttributes = '';\n for (var attributeName in attributes) {\n if (!attributes[attributeName]) {\n continue\n }\n\n stringifiedAttributes += '; ' + attributeName;\n\n if (attributes[attributeName] === true) {\n continue\n }\n\n // Considers RFC 6265 section 5.2:\n // ...\n // 3. If the remaining unparsed-attributes contains a %x3B (\";\")\n // character:\n // Consume the characters of the unparsed-attributes up to,\n // not including, the first %x3B (\";\") character.\n // ...\n stringifiedAttributes += '=' + attributes[attributeName].split(';')[0];\n }\n\n return (document.cookie =\n name + '=' + converter.write(value, name) + stringifiedAttributes)\n }\n\n function get(name) {\n if (typeof document === 'undefined' || (arguments.length && !name)) {\n return\n }\n\n // To prevent the for loop in the first place assign an empty array\n // in case there are no cookies at all.\n var cookies = document.cookie ? document.cookie.split('; ') : [];\n var jar = {};\n for (var i = 0; i < cookies.length; i++) {\n var parts = cookies[i].split('=');\n var value = parts.slice(1).join('=');\n\n try {\n var found = decodeURIComponent(parts[0]);\n if (!(found in jar)) jar[found] = converter.read(value, found);\n if (name === found) {\n break\n }\n } catch (_e) {\n // Do nothing...\n }\n }\n\n return name ? jar[name] : jar\n }\n\n return Object.create(\n {\n set: set,\n get: get,\n remove: function (name, attributes) {\n set(\n name,\n '',\n assign({}, attributes, {\n expires: -1\n })\n );\n },\n withAttributes: function (attributes) {\n return init(this.converter, assign({}, this.attributes, attributes))\n },\n withConverter: function (converter) {\n return init(assign({}, this.converter, converter), this.attributes)\n }\n },\n {\n attributes: { value: Object.freeze(defaultAttributes) },\n converter: { value: Object.freeze(converter) }\n }\n )\n}\n\nvar api = init(defaultConverter, { path: '/' });\n\nexport { api as default };\n","// @storage-escape: Canonical checkout-state persistence wrapper — this\r\n// module IS the localStorage layer for checkout state (cart, promo,\r\n// totals) across page refreshes. utility-storage record documents\r\n// the persistence-wrapper shape as exempt.\r\n\r\nimport { createLogger } from '@getmicdrop/svelte-components';\r\n/**\r\n * Checkout state persistence: localStorage with cookie fallback for migration\r\n */\r\nimport { getCookie, setCookie, removeCookie } from './cookies';\r\n\r\nconst logger = createLogger('checkoutState');\r\n\r\ninterface CheckoutTicket {\r\n ID: number | string;\r\n name: string;\r\n price: number;\r\n type?: string;\r\n visibility?: string;\r\n salesChannel?: string;\r\n [key: string]: unknown;\r\n}\r\n\r\ninterface CheckoutState {\r\n quantities: Record<string, number>;\r\n donationAmounts: Record<string, number>;\r\n promocode: string;\r\n promoDiscountAmount: number;\r\n tickets: CheckoutTicket[];\r\n}\r\n\r\ninterface PersistCheckoutParams {\r\n eventID: string;\r\n quantities: Record<string, number>;\r\n donationAmounts?: Record<string, number>;\r\n promocode: string;\r\n promoDiscountAmount: number;\r\n tickets: CheckoutTicket[];\r\n}\r\n\r\n/**\r\n * Load checkout state from localStorage (preferred) with cookie fallback for migration\r\n */\r\nexport function loadCheckoutStateFromCookies(eventID: string): CheckoutState {\r\n let quantities: Record<string, number> = {};\r\n let donationAmounts: Record<string, number> = {};\r\n let tickets: CheckoutTicket[] = [];\r\n let promocode = '';\r\n let promoDiscountAmount = 0;\r\n\r\n if (typeof window === 'undefined') {\r\n return {\r\n quantities,\r\n donationAmounts,\r\n promocode,\r\n promoDiscountAmount,\r\n tickets,\r\n };\r\n }\r\n\r\n // Try localStorage first (new approach)\r\n try {\r\n const stored = localStorage.getItem(`checkout-state-${eventID}`); // @storage-escape: canonical checkout-state persistence wrapper — this module IS the localStorage layer (see file header)\r\n if (stored) {\r\n const parsed = JSON.parse(stored);\r\n return {\r\n quantities: parsed.quantities || {},\r\n donationAmounts: parsed.donationAmounts || {},\r\n promocode: parsed.promocode || '',\r\n promoDiscountAmount: parseFloat(parsed.promoDiscountAmount) || 0,\r\n tickets: parsed.tickets || [],\r\n };\r\n }\r\n } catch (e) {\r\n logger.warn('Failed to parse checkout state from localStorage:', e);\r\n }\r\n\r\n // Fallback to cookies for migration (old approach)\r\n try {\r\n quantities = JSON.parse(\r\n getCookie(`checkout-quantities-${eventID}`) || '{}'\r\n );\r\n } catch (e) {\r\n logger.warn('Failed to parse checkout quantities cookie:', e);\r\n quantities = {};\r\n }\r\n\r\n try {\r\n tickets = JSON.parse(getCookie(`checkout-tickets-${eventID}`) || '[]');\r\n } catch (e) {\r\n logger.warn('Failed to parse checkout tickets cookie:', e);\r\n tickets = [];\r\n }\r\n\r\n promocode = getCookie(`checkout-promocode-${eventID}`) || '';\r\n promoDiscountAmount = parseFloat(getCookie('checkout-promo-discount') || '0');\r\n\r\n // If we found data in cookies, migrate it to localStorage and clear cookies\r\n if (Object.keys(quantities).length > 0 || tickets.length > 0 || promocode) {\r\n persistCheckoutState({\r\n eventID,\r\n quantities,\r\n donationAmounts,\r\n promocode,\r\n promoDiscountAmount,\r\n tickets,\r\n });\r\n clearCheckoutCookies(eventID);\r\n }\r\n\r\n return {\r\n quantities,\r\n donationAmounts,\r\n promocode,\r\n promoDiscountAmount,\r\n tickets,\r\n };\r\n}\r\n\r\n/**\r\n * Clear checkout state from both localStorage and cookies\r\n */\r\nexport function clearCheckoutCookies(eventID: string): void {\r\n // Clear localStorage (new approach)\r\n if (typeof window !== 'undefined') {\r\n localStorage.removeItem(`checkout-state-${eventID}`); // @storage-escape: canonical checkout-state persistence wrapper — this module IS the localStorage layer (see file header)\r\n }\r\n\r\n // Clear cookies (for migration cleanup)\r\n removeCookie(`checkout-quantities-${eventID}`);\r\n removeCookie(`checkout-promocode-${eventID}`);\r\n removeCookie(`checkout-promo-discount`);\r\n removeCookie(`checkout-tickets-${eventID}`);\r\n}\r\n\r\n/**\r\n * Persist checkout state to localStorage\r\n * Uses localStorage instead of cookies to avoid size limits and reduce request overhead\r\n */\r\nexport function persistCheckoutState({\r\n eventID,\r\n quantities,\r\n donationAmounts,\r\n promocode,\r\n promoDiscountAmount,\r\n tickets,\r\n}: PersistCheckoutParams): void {\r\n if (typeof window === 'undefined') return;\r\n\r\n // Store minimal ticket data to avoid storage bloat\r\n const minimalTickets = (tickets || []).map(t => ({\r\n ID: t.ID,\r\n name: t.name,\r\n price: t.price,\r\n type: t.type, // Include type for donation ticket detection\r\n visibility: t.visibility,\r\n salesChannel: t.salesChannel,\r\n }));\r\n\r\n const state = {\r\n quantities: quantities || {},\r\n donationAmounts: donationAmounts || {},\r\n promocode: promocode || '',\r\n promoDiscountAmount: promoDiscountAmount || 0,\r\n tickets: minimalTickets,\r\n updatedAt: Date.now(),\r\n };\r\n\r\n try {\r\n localStorage.setItem(`checkout-state-${eventID}`, JSON.stringify(state)); // @storage-escape: canonical checkout-state persistence wrapper — this module IS the localStorage layer (see file header)\r\n } catch (e) {\r\n logger.warn('Failed to persist checkout state to localStorage:', e);\r\n }\r\n}\r\n\r\n/**\r\n * Ensure order ID exists, create if not\r\n */\r\nexport async function ensureOrderIdExists(eventId: string): Promise<string> {\r\n const existingOrderId = getCookie(`order-id-${eventId}`);\r\n\r\n if (existingOrderId) {\r\n return existingOrderId;\r\n }\r\n\r\n const newOrderId = `order-${eventId}-${Date.now()}`;\r\n // SameSite=None; Secure so the cookie survives iframe embeds on\r\n // third-party origins (Safari ITP / Chrome SameSite-Lax-by-default).\r\n setCookie(`order-id-${eventId}`, newOrderId, {\r\n secure: true,\r\n sameSite: 'none' as const,\r\n path: '/',\r\n });\r\n\r\n return newOrderId;\r\n}\r\n","import { AppError, createLogger } from '@getmicdrop/svelte-components';\r\n/**\r\n * Order-related API calls: getVenueDetails, initiateOrder, getOrder,\r\n * createPaymentIntent, validatePaymentIntent, getClientIP\r\n *\r\n * @legacy-retire-with=?legacy=1\r\n * LEGACY — slated for deletion. Do NOT add new code here and do NOT build\r\n * canonicals against the untyped `[key: string]: unknown` bag interfaces below\r\n * (VenueResponse / PaymentIntentResponse / ValidatePaymentResponse /\r\n * OrderResponse). They survive only via `as` casts and exist purely for\r\n * backward compat. The canonical, typed wire->domain home for all of this is\r\n * `src/lib/api/transformers/*` (parseOrder, parseCart, parseGiftCardApplication,\r\n * parseEventPerformers, parseVenue, parseEvent) composed by the `src/lib/api/*`\r\n * clients (orders.ts, gift-cards.ts, events.ts). Retire this module behind the\r\n * `?legacy=1` flag once its remaining consumer is migrated to `$lib/api/*`.\r\n *\r\n * Live consumer (as of this change): `src/lib/utils/utils.js` re-exports\r\n * getClientIP / getVenueDetails / initiateOrder / getOrder / createPaymentIntent\r\n * / validatePaymentIntent from here (the `$lib/utils/utils.js` back-compat\r\n * barrel). That barrel is the single retirement blocker to plan around.\r\n */\r\nimport { apiGet, apiPost } from '$lib/api/client';\r\nimport { getCookie, setCookie } from './cookies';\r\n\r\nconst logger = createLogger('orderApi');\r\n\r\ninterface OrderResult {\r\n success: boolean;\r\n uuid?: string;\r\n serviceFee?: number;\r\n subtotal?: number;\r\n error?: string;\r\n errorType?: 'inventory' | 'server' | 'network';\r\n}\r\n\r\ninterface CartData {\r\n eventID?: string | number;\r\n quantities?: Record<string, number>;\r\n donationAmounts?: Record<string, number>;\r\n [key: string]: unknown;\r\n}\r\n\r\ninterface PaymentIntentResponse {\r\n clientSecret?: string;\r\n stripe_publishable_key?: string;\r\n [key: string]: unknown;\r\n}\r\n\r\ninterface ValidatePaymentResponse {\r\n [key: string]: unknown;\r\n}\r\n\r\ninterface OrderResponse {\r\n [key: string]: unknown;\r\n}\r\n\r\ninterface VenueResponse {\r\n [key: string]: unknown;\r\n}\r\n\r\n/**\r\n * @deprecated IP is now resolved server-side via request headers\r\n */\r\nexport async function getClientIP(): Promise<string | null> {\r\n return null;\r\n}\r\n\r\nexport async function getVenueDetails(\r\n venueID: string | number\r\n): Promise<VenueResponse> {\r\n try {\r\n const result = await apiGet<VenueResponse>(`/venues/${venueID}`);\r\n\r\n if (!result.success) {\r\n throw new AppError(\r\n result.error ?? `API error: ${result.statusCode}`,\r\n 'lib/utils/orderApi/getVenueDetails'\r\n );\r\n }\r\n\r\n return result.data as VenueResponse;\r\n } catch (error) {\r\n logger.error('getVenueDetails error:', error);\r\n throw error;\r\n }\r\n}\r\n\r\nexport async function initiateOrder(\r\n cartData: CartData = {}\r\n): Promise<OrderResult> {\r\n try {\r\n const ip = await getClientIP();\r\n\r\n const { eventID, quantities, donationAmounts, ...restCartData } = cartData;\r\n\r\n // Convert quantities keys to numbers and eventID to number (API expects numeric types)\r\n const numericQuantities = quantities\r\n ? Object.fromEntries(\r\n Object.entries(quantities).map(([k, v]) => [parseInt(k, 10), v])\r\n )\r\n : {};\r\n\r\n // Convert donationAmounts keys to numbers and values to floats\r\n const numericDonationAmounts = donationAmounts\r\n ? Object.fromEntries(\r\n Object.entries(donationAmounts)\r\n .filter(([, v]) => parseFloat(String(v)) > 0)\r\n .map(([k, v]) => [parseInt(k, 10), parseFloat(String(v))])\r\n )\r\n : undefined;\r\n\r\n const orderPayload = {\r\n purchaserIP: ip,\r\n eventID: eventID ? parseInt(String(eventID), 10) : undefined,\r\n quantities: numericQuantities,\r\n ...(numericDonationAmounts &&\r\n Object.keys(numericDonationAmounts).length > 0\r\n ? { donationAmounts: numericDonationAmounts }\r\n : {}),\r\n ...restCartData,\r\n };\r\n\r\n const result = await apiPost<Record<string, unknown>>(\r\n `/orders/create`,\r\n orderPayload\r\n );\r\n\r\n const data = result.data ?? {};\r\n\r\n // Check for API errors (including 200-with-error-field pattern)\r\n if (!result.success || data['error']) {\r\n const errorMessage =\r\n (data['error'] as string) ||\r\n (data['message'] as string) ||\r\n result.error ||\r\n 'Order creation failed';\r\n // Check for inventory-related errors\r\n const isInventoryError =\r\n errorMessage.toLowerCase().includes('inventory') ||\r\n errorMessage.toLowerCase().includes('sold out') ||\r\n errorMessage.toLowerCase().includes('not available') ||\r\n errorMessage.toLowerCase().includes('insufficient') ||\r\n result.statusCode === 409; // Conflict status often used for inventory issues\r\n\r\n return {\r\n success: false,\r\n error: errorMessage,\r\n errorType: isInventoryError ? 'inventory' : 'server',\r\n };\r\n }\r\n\r\n // SameSite=None; Secure so the cookie survives iframe embeds on\r\n // third-party origins (Safari ITP / Chrome SameSite-Lax-by-default).\r\n // Without this, embedding the calendar in another venue's site\r\n // silently drops the cart on every navigation.\r\n setCookie(\r\n 'checkout-cartid',\r\n (data['uuid'] as string) || (data['cartUUID'] as string),\r\n {\r\n secure: true,\r\n sameSite: 'none' as const,\r\n path: '/',\r\n }\r\n );\r\n return {\r\n success: true,\r\n uuid: (data['uuid'] as string) || (data['cartUUID'] as string),\r\n serviceFee: (data['serviceFee'] as number) ?? 0,\r\n subtotal: (data['subtotal'] as number) ?? 0,\r\n };\r\n } catch (err: unknown) {\r\n const message = err instanceof Error ? err.message : 'Network error';\r\n logger.error('Order initiation failed:', err);\r\n return {\r\n success: false,\r\n error: message,\r\n errorType: 'network',\r\n };\r\n }\r\n}\r\n\r\nexport async function getOrder(orderId: string): Promise<OrderResponse> {\r\n try {\r\n const result = await apiGet<OrderResponse>(`/orders/${orderId}`);\r\n\r\n if (!result.success) {\r\n const errorMessage =\r\n result.error ?? `Failed to fetch order (${result.statusCode})`;\r\n logger.error('getOrder error response:', errorMessage);\r\n throw new AppError(errorMessage, 'lib/utils/orderApi/getOrder');\r\n }\r\n\r\n return result.data as OrderResponse;\r\n } catch (err) {\r\n logger.error('getOrder error:', err);\r\n throw err;\r\n }\r\n}\r\n\r\nexport async function createPaymentIntent(\r\n cartId: string,\r\n quantities: Record<string, number>,\r\n donationAmounts?: Record<string, number>\r\n): Promise<PaymentIntentResponse> {\r\n const token = getCookie('operator_token');\r\n\r\n try {\r\n const result = await apiPost<PaymentIntentResponse>(\r\n `/orders/${cartId}/payment-intent`,\r\n {\r\n productQuantities: quantities,\r\n ...(donationAmounts && Object.keys(donationAmounts).length > 0\r\n ? { donationAmounts }\r\n : {}),\r\n },\r\n {\r\n headers: {\r\n Authorization: token ? `Bearer ${token}` : '',\r\n },\r\n }\r\n );\r\n\r\n if (!result.success) {\r\n logger.error('Payment intent creation failed:', result.error);\r\n throw new AppError(\r\n result.error === 'Request timed out'\r\n ? 'Request timed out. Please check your connection and try again.'\r\n : result.error?.includes('Failed to fetch')\r\n ? 'Unable to connect to payment server. Please check your internet connection or try again later.'\r\n : (result.error ??\r\n `Failed to create payment intent: ${result.statusCode}`),\r\n 'lib/utils/orderApi/createPaymentIntent'\r\n );\r\n }\r\n\r\n return result.data as PaymentIntentResponse;\r\n } catch (err: unknown) {\r\n logger.error('createPaymentIntent error:', err);\r\n // Re-throw the error so the caller can handle it\r\n throw err;\r\n }\r\n}\r\n\r\nexport async function validatePaymentIntent(\r\n cartId: string,\r\n payload: Record<string, unknown>\r\n): Promise<ValidatePaymentResponse | null> {\r\n try {\r\n const token = getCookie('operator_token');\r\n\r\n const result = await apiPost<ValidatePaymentResponse>(\r\n `/orders/${cartId}/validate-payment`,\r\n payload,\r\n {\r\n headers: {\r\n Authorization: token ? `Bearer ${token}` : '',\r\n },\r\n }\r\n );\r\n\r\n return result.data ?? null;\r\n } catch (err) {\r\n logger.error('validatePaymentIntent error:', err);\r\n return null;\r\n }\r\n}\r\n","// @fetch-escape: widget-intentional. \r\n// @api-client-escape: fire-and-forget UTM beacon — keeps raw fetch (GET, no body, response ignored).\r\n// Migrating to apiPost broke the GET contract; apiGet would parse JSON on a no-body 200 and log a false-positive error.\r\nimport { createLogger } from '@getmicdrop/svelte-components';\r\nconst logger = createLogger('utm');\r\n\r\n/**\r\n * UTM source tracking\r\n */\r\n\r\n/**\r\n * Track UTM source for an event (parameter named venueID for backwards compatibility\r\n * but actually represents eventID)\r\n */\r\nexport async function trackUTMSource(venueID: string | number): Promise<void> {\r\n const urlParams = new URLSearchParams(window.location.search);\r\n const utmSource = urlParams.get(\"utm_source\") || \"Direct\";\r\n\r\n try {\r\n await fetch(\r\n `https://get-micdrop.com/api/v2/public/utm/${venueID}/${utmSource}`\r\n );\r\n } catch (err) {\r\n logger.error(\"UTM tracking failed:\", err);\r\n }\r\n}"],"names":["logger","createLogger","DEFAULT_CONFIG","error","globalConfig","configureApi","config","getApiConfig","getPublicBaseUrl","getLegacyPublicUrl","getOrdersV2Url","getClientIP","sleep","ms","resolve","fetchWithRetry","url","init","cfg","method","maxAttempts","lastError","attempt","controller","timeoutId","callerSignal","onCallerAbort","response","err","apiGet","endpoint","options","apiRequest","apiPost","body","apiPut","apiDelete","errorData","message","errorMessage","simpleFetch","parseCartReservation","raw","rec","parseCart","data","uuid","eventID","status","expiresAt","reservations","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","utmSource","ORDERS_V2_PUBLIC","validatePromoCode","code","encodedCode","hasPromoCodes","applyPromoCode","removePromoCode","parseEventPerformers","_eventDetailsInflight","fetchEventDetails","customFetch","password","key","inflight","run","fetchEventTickets","tickets","fetchEventPerformers","fetchAllVenues","orgId","venues","fetchVenueEvents","events","getMonthEvents","year","month","getOrgMonthEvents","getSeriesOccurrences","eventSeriesId","fetchSeriesOccurrences","resolvePublicEntity","id","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","fallback","computeCtaState","event","cancelled","isRegistration","waitlistEnabled","now","eventEnd","hiddenAvailable","isHiddenTicketPurchasable","publicTickets","t","hasPurchasable","allSoldOut","allComingSoon","earliestSalesStart","ticket","salesBegin","salesEnd","remaining","isSoldOut","isScheduled","hasSalesEnded","startDate","diffMs","diffHours","diffDays","dateText","formatTime","formatDateRaw","d","parsePurchasedTicket","apiTicket","parseOrder","apiOrder","rawTickets","transformTicket","transformOrder","transformOrderForDisplay","order","displayId","formatCurrency","CDN_BASE_URL","getCDNImageUrl","path","getEventImageUrl","apiEvent","rawUrl","deriveEventCtaState","explicitState","eventDate","eventTime","availableCount","sum","qty","sold","parseTicket","parseEvent","prices","p","minPrice","maxPrice","transformEvent","transformEventData","getLogoUrl","parseVenue","apiVenue","logoPath","transformVenue","extractVenueFees","formatVenueAddress","assign","target","i","source","defaultConverter","value","converter","defaultAttributes","set","name","attributes","stringifiedAttributes","attributeName","get","cookies","jar","parts","found"],"mappings":"oWAYA,MAAMA,EAASC,EAAAA,aAAa,IAAI,EAI1BC,GAAsC,CAC1C,QAAS,0BACT,QAAS,IACT,QAAS,EACT,WAAY,IACZ,QAAUC,GAAiBH,EAAO,MAAM,aAAcG,CAAK,CAC7D,EAEA,IAAIC,EAAoC,CAAE,GAAGF,EAAA,EAEtC,SAASG,GAAaC,EAAkC,CAC7DF,EAAe,CAAE,GAAGA,EAAc,GAAGE,CAAA,CACvC,CAEO,SAASC,IAAoC,CAClD,MAAO,CAAE,GAAGH,CAAA,CACd,CAEO,SAASI,GAA2B,CACzC,MAAO,GAAGJ,EAAa,OAAO,gBAChC,CAEO,SAASK,IAA6B,CAC3C,MAAO,GAAGL,EAAa,OAAO,aAChC,CAEO,SAASM,GAAyB,CACvC,MAAO,GAAGN,EAAa,OAAO,uBAChC,CAGA,eAAsBO,IAA+B,CACnD,MAAO,EACT,CAKA,MAAMC,GAASC,GACb,IAAI,QAASC,GAAY,WAAWA,EAASD,CAAE,CAAC,EAUlD,eAAeE,EACbC,EACAC,EACAC,EACmB,CACnB,MAAMC,GAAUF,EAAK,QAAU,OAAO,YAAA,EAEhCG,EADeD,IAAW,OAASA,IAAW,OACjBD,EAAI,QAAU,EAAI,EAErD,IAAIG,EACJ,QAASC,EAAU,EAAGA,GAAWF,EAAaE,IAAW,CACvD,MAAMC,EAAa,IAAI,gBACjBC,EAAY,WAAW,IAAMD,EAAW,MAAA,EAASL,EAAI,OAAO,EAG5DO,EAAeR,EAAK,OAC1B,IAAIS,EACAD,IACEA,EAAa,QAASF,EAAW,MAAA,GAEnCG,EAAgB,IAAMH,EAAW,MAAA,EACjCE,EAAa,iBAAiB,QAASC,EAAe,CAAE,KAAM,GAAM,IAIxE,GAAI,CAEF,MAAMC,EAAW,MAAM,MAAMX,EAAK,CAAE,GAAGC,EAAM,OAAQM,EAAW,OAAQ,EAGxE,GAAII,EAAS,QAAU,KAAOL,EAAUF,EACtCC,EAAY,IAAI,MAAM,QAAQM,EAAS,MAAM,EAAE,MAE/C,QAAOA,CAEX,OAASC,EAAK,CAGZ,GAFAP,EAAYO,EAERH,GAAc,QAAS,MAAMG,CACnC,QAAA,CACE,aAAaJ,CAAS,EAClBC,GAAgBC,GAClBD,EAAa,oBAAoB,QAASC,CAAa,CAE3D,CAEIJ,EAAUF,GACZ,MAAMR,GAAMM,EAAI,WAAa,KAAK,IAAI,EAAGI,EAAU,CAAC,CAAC,CAEzD,CAEA,MAAMD,CACR,CAEA,eAAsBQ,EACpBC,EACAC,EACyB,CACzB,OAAOC,EAAc,MAAOF,EAAU,OAAWC,CAAO,CAC1D,CAEA,eAAsBE,EACpBH,EACAI,EACAH,EACyB,CACzB,OAAOC,EAAc,OAAQF,EAAUI,EAAMH,CAAO,CACtD,CAEA,eAAsBI,GACpBL,EACAI,EACAH,EACyB,CACzB,OAAOC,EAAc,MAAOF,EAAUI,EAAMH,CAAO,CACrD,CAEA,eAAsBK,EACpBN,EACAC,EACyB,CACzB,OAAOC,EAAc,SAAUF,EAAU,OAAWC,CAAO,CAC7D,CAEA,eAAeC,EACbb,EACAW,EACAI,EACAH,EACyB,CACzB,MAAMf,EAAMc,EAAS,WAAW,MAAM,EAClCA,EACA,GAAGtB,EAAA,CAAkB,GAAGsB,CAAQ,GAEpC,GAAI,CACF,MAAMH,EAAW,MAAMZ,EACrBC,EACA,CACE,OAAAG,EACA,QAAS,CACP,eAAgB,mBAChB,GAAGY,GAAS,OAAA,EAEd,KAAMG,IAAS,OAAY,KAAK,UAAUA,CAAI,EAAI,OAClD,YAAa,UACb,GAAGH,CAAA,EAEL3B,CAAA,EAGF,GAAI,CAACuB,EAAS,GAAI,CAChB,MAAMU,EAAY,MAAMV,EAAS,KAAA,EAAO,MAAM,KAAO,CAAA,EAAG,EAClDW,EACJD,EAAU,OAASA,EAAU,SAAW,QAAQV,EAAS,MAAM,GACjE,OAAAvB,EAAa,QAAQ,IAAI,MAAMkC,CAAO,CAAC,EAChC,CAAE,QAAS,GAAO,MAAOA,EAAS,WAAYX,EAAS,MAAA,CAChE,CAGA,MAAO,CAAE,QAAS,GAAM,KADX,MAAMA,EAAS,KAAA,EACa,WAAYA,EAAS,MAAA,CAChE,OAASxB,EAAO,CACd,MAAMoC,EACJpC,aAAiB,MACbA,EAAM,OAAS,aACb,oBACAA,EAAM,QACR,gBAEN,OAAAC,EAAa,QACXD,aAAiB,MAAQA,EAAQ,IAAI,MAAMoC,CAAY,CAAA,EAElD,CAAE,QAAS,GAAO,MAAOA,CAAA,CAClC,CACF,CAMA,eAAsBC,EACpBxB,EACAe,EACmB,CACnB,GAAI,CACF,MAAMJ,EAAW,MAAMZ,EACrBC,EACA,CAAE,YAAa,UAAW,GAAGe,CAAA,EAC7B3B,CAAA,EAGF,GAAI,CAACuB,EAAS,GAAI,CAChB,MAAMU,EAAY,MAAMV,EAAS,KAAA,EAAO,MAAM,KAAO,CAAA,EAAG,EACxD3B,OAAAA,EAAO,MAAM,uBAAuB2B,EAAS,MAAM,GAAIU,CAAS,EACzD,IACT,CAEA,OAAOV,EAAS,KAAA,CAClB,OAASxB,EAAO,CACdH,OAAAA,EAAO,MAAM,qBAAsBG,CAAK,EACjC,IACT,CACF,CClLO,SAASsC,GAAqBC,EAAmC,CACtE,MAAMC,EAAOD,GAAO,CAAA,EACpB,MAAO,CACL,SAAU,OAAOC,EAAI,UAAYA,EAAI,UAAY,CAAC,EAClD,SAAU,OAAOA,EAAI,UAAYA,EAAI,UAAY,CAAC,EAClD,mBAAoB,OAClBA,EAAI,oBAAsBA,EAAI,oBAAsB,CAAA,EAEtD,OAAQ,OAAOA,EAAI,QAAUA,EAAI,QAAU,EAAE,CAAA,CAEjD,CAUO,SAASC,GAAUF,EAAwB,CAChD,MAAMG,EAAQH,GAAO,CAAA,EACfI,EAAeD,EAAK,MAAQA,EAAK,MAAQ,GACzCE,EAAU,OAAOF,EAAK,SAAWA,EAAK,SAAW,CAAC,EAClDG,EAAiBH,EAAK,QAAUA,EAAK,QAAU,GAC/CI,EAAoBJ,EAAK,WAAaA,EAAK,WAAa,GAExDK,GADoBL,EAAK,cAAgBA,EAAK,cAAgB,CAAA,GACjB,IAAIJ,EAAoB,EAC3E,MAAO,CAAE,KAAAK,EAAM,QAAAC,EAAS,OAAAC,EAAQ,UAAAC,EAAW,aAAAC,CAAA,CAC7C,CCpEA,MAAMlD,EAASC,EAAAA,aAAa,IAAI,EA2BhC,eAAsBkD,GACpBC,EACAC,EACAC,EACuC,CACvC,GAAI,CACF,MAAMC,EAAY,MAAMtB,EACtB,GAAGvB,EAAA,CAAgB,SAAS0C,CAAM,kBAClC,CACE,kBAAmBC,EACnB,GAAIC,GAAmB,OAAO,KAAKA,CAAe,EAAE,OAAS,EACzD,CAAE,gBAAAA,GACF,CAAA,CAAC,CACP,EAGF,GAAI,CAACC,EAAU,QACbvD,MAAAA,EAAO,MAAM,kCAAmCuD,EAAU,KAAK,EACzD,IAAIC,EAAAA,SAASD,EAAU,OAAS,kCAAmC,oCAAoC,EAG/GvD,OAAAA,EAAO,MAAM,0BAA2BuD,EAAU,IAAI,EAC/CA,EAAU,IACnB,OAASpD,EAAO,CACdH,OAAAA,EAAO,MAAM,6BAA8BG,CAAK,EACzC,IACT,CACF,CAgCA,eAAsBsD,GACpBC,EACAC,EAC0B,CAC1B,GAAI,CAACD,EAAU,OAAO,KACtB,GAAI,CACF,MAAMH,EAAY,MAAM1B,EACtB,GAAGnB,EAAA,CAAgB,SAASgD,CAAQ,EAAA,EAEtC,GAAI,CAACH,EAAU,QAEb,OAAO,KAMT,KAAM,CAAE,KAAAT,EAAM,QAAAC,EAAS,OAAAC,EAAQ,UAAAC,EAAW,aAAAC,GAAiBN,GACzDW,EAAU,IAAA,EAMZ,OAJIP,IAAW,YAAcA,IAAW,UAIpCW,IAAoB,QAAaZ,IAAY,OAAOY,CAAe,EAE9D,KAEF,CAAE,KAAAb,EAAM,QAAAC,EAAS,OAAAC,EAAQ,UAAAC,EAAW,aAAAC,CAAA,CAC7C,OAAStB,EAAK,CACZ5B,OAAAA,EAAO,MAAM,uBAAwB4B,CAAG,EACjC,IACT,CACF,CAeA,eAAsBgC,GACpBR,EACAC,EACAC,EACkB,CAClB,GAAI,CAGF,MAAMO,EAA4C,CAAA,EAClD,SAAW,CAACC,EAAGC,CAAC,IAAK,OAAO,QAAQV,CAAU,EAAG,CAC/C,MAAMW,EAAI,OAAOF,GAAM,SAAWA,EAAI,SAASA,EAAG,EAAE,EAChD,CAAC,OAAO,MAAME,CAAC,GAAKD,EAAI,IAAGF,EAAkBG,CAAC,EAAID,EACxD,CACA,MAAME,EAA2C,CAAA,EACjD,GAAIX,EACF,SAAW,CAACQ,EAAGC,CAAC,IAAK,OAAO,QAAQT,CAAe,EAAG,CACpD,MAAMU,EAAI,OAAOF,GAAM,SAAWA,EAAI,SAASA,EAAG,EAAE,EAChD,CAAC,OAAO,MAAME,CAAC,GAAKD,EAAI,IAAGE,EAAiBD,CAAC,EAAID,EACvD,CAMF,MAAMpC,EAAW,MAAM,MAAM,GAAGjB,GAAgB,SAAS0C,CAAM,GAAI,CACjE,OAAQ,MACR,QAAS,CAAE,eAAgB,kBAAA,EAC3B,YAAa,UACb,KAAM,KAAK,UAAU,CACnB,WAAYS,EACZ,GAAI,OAAO,KAAKI,CAAgB,EAAE,OAAS,EACvC,CAAE,gBAAiBA,GACnB,CAAA,CAAC,CACN,CAAA,CACF,EACD,GAAI,CAACtC,EAAS,GAAI,CAChB,MAAMU,EAAY,MAAMV,EAAS,KAAA,EAAO,MAAM,KAAO,CAAA,EAAG,EACxD3B,OAAAA,EAAO,MAAM,sBAAuBqC,CAAS,EACtC,EACT,CACA,MAAO,EACT,OAAST,EAAK,CACZ5B,OAAAA,EAAO,MAAM,8BAA+B4B,CAAG,EACxC,EACT,CACF,CAWA,eAAsBsC,GACpBC,EACsC,CACtC,GAAI,CACF,MAAMZ,EAAY,MAAMtB,EACtB,GAAGzB,EAAA,CAAkB,oBAAoB2D,CAAS,EAAA,EAGpD,OAAKZ,EAAU,QAOR,CACL,QAAS,GACT,QAASA,EAAU,MAAM,OAAA,EARlB,CACL,QAAS,GACT,MAAOA,EAAU,OAAS,gCAAA,CAQhC,OAASpD,EAAO,CACdH,OAAAA,EAAO,MAAM,gCAAiCG,CAAK,EAC5C,CAAE,QAAS,GAAO,MAAO,sCAAA,CAClC,CACF,CAUA,eAAsBiE,GACpBD,EACoC,CACpC,GAAI,CACF,MAAMZ,EAAY,MAAMtB,EACtB,GAAGzB,EAAA,CAAkB,kBAAkB2D,CAAS,EAAA,EAGlD,OAAKZ,EAAU,QAOR,CACL,QAAS,GACT,QAASA,EAAU,MAAM,OAAA,EARlB,CACL,QAAS,GACT,MAAOA,EAAU,OAAS,8BAAA,CAQhC,OAASpD,EAAO,CACdH,OAAAA,EAAO,MAAM,gCAAiCG,CAAK,EAC5C,CAAE,QAAS,GAAO,MAAO,sCAAA,CAClC,CACF,CAYA,eAAsBkE,GACpBC,EACAC,EACqC,CACrC,GAAI,CACF,MAAMhB,EAAY,MAAMtB,EACtB,GAAGzB,GAAkB,iBACrB,CAAE,QAAS8D,EAAS,UAAAC,CAAA,CAAU,EAGhC,OAAKhB,EAAU,QAKRA,EAAU,MAJfvD,EAAO,MAAM,uBAAwBuD,EAAU,KAAK,EAC7C,KAIX,OAASpD,EAAO,CACdH,OAAAA,EAAO,MAAM,qBAAsBG,CAAK,EACjC,IACT,CACF,CAUA,eAAsBqE,GAASC,EAAwC,CACrE,OAAOjC,EAAmB,GAAGhC,EAAA,CAAkB,WAAWiE,CAAO,EAAE,CACrE,CAYA,eAAsBC,GACpBtB,EACAuB,EACyC,CACzC,GAAI,CACF,MAAMpB,EAAY,MAAMtB,EACtB,GAAGvB,EAAA,CAAgB,0BAA0B0C,CAAM,GACnDuB,CAAA,EAGF,GAAI,CAACpB,EAAU,QACbvD,OAAAA,EAAO,MAAM,6BAA8BuD,EAAU,KAAK,EACnD,CACL,QAAS,GACT,OAAQ,SACR,MAAOA,EAAU,OAAS,2BAAA,EAI9B,MAAMqB,EAASrB,EAAU,KACzB,MAAO,CACL,QAAS,GACT,OAAQqB,EAAO,QAAU,oBACzB,UAAWA,EAAO,WAAaA,EAAO,IAAA,CAE1C,OAASzE,EAAO,CACdH,OAAAA,EAAO,MAAM,+BAAgCG,CAAK,EAC3C,IACT,CACF,CAUA,eAAsB0E,GACpBV,EACgC,CAChC,GAAI,CACF,MAAMZ,EAAY,MAAMtB,EACtB,GAAGzB,GAAkB,yBACrB,CAAE,UAAA2D,CAAA,CAAU,EAGd,GAAI,CAACZ,EAAU,QACb,MAAO,CACL,QAAS,GACT,MAAOA,EAAU,OAAS,2BAC1B,WAAYA,EAAU,UAAA,EAI1B,MAAMqB,EAASrB,EAAU,KACzB,MAAO,CACL,QAAS,GACT,cAAeqB,EAAO,cACtB,oBAAqBA,EAAO,mBAAA,CAEhC,OAASzE,EAAO,CACdH,OAAAA,EAAO,MAAM,oCAAqCG,CAAK,EAChD,CAAE,QAAS,GAAO,MAAO,iCAAA,CAClC,CACF,CAUA,eAAsB2E,GACpBX,EACwB,CACxB,GAAI,CACF,MAAMZ,EAAY,MAAM1B,EACtB,GAAGrB,EAAA,CAAkB,mBAAmB2D,CAAS,EAAA,EAGnD,GAAI,CAACZ,EAAU,QACb,MAAO,CACL,MAAOA,EAAU,OAAS,0BAC1B,SAAUA,EAAU,aAAe,GAAA,EAIvC,MAAMqB,EAASrB,EAAU,KACzB,MAAO,CACL,UAAWqB,EAAO,UAClB,eAAgBA,EAAO,eACvB,oBAAqBA,EAAO,oBAC5B,UAAWA,EAAO,UAClB,iBAAkBA,EAAO,gBAAA,CAE7B,OAASzE,EAAO,CACdH,OAAAA,EAAO,MAAM,gCAAiCG,CAAK,EAC5C,CAAE,MAAO,sCAAA,CAClB,CACF,CAWA,eAAsB4E,GACpBC,EAII,GACoB,CACxB,GAAI,CACF,MAAMzB,EAAY,MAAMtB,EACtB,GAAGzB,GAAkB,iBACrBwE,CAAA,EAGF,OAAKzB,EAAU,QAKRA,EAAU,MAAM,MAAQ,MAJ7BvD,EAAO,MAAM,2BAA4BuD,EAAU,KAAK,EACjD,KAIX,OAASpD,EAAO,CACdH,OAAAA,EAAO,MAAM,uBAAwBG,CAAK,EACnC,IACT,CACF,CAUA,eAAsB8E,GAAeC,EAAyC,CAC5E,GAAI,OAAO,OAAW,IAAa,OAGnC,MAAMC,EADY,IAAI,gBAAgB,OAAO,SAAS,MAAM,EAChC,IAAI,YAAY,GAAK,SAEjD,GAAI,CAEF,MAAM,MACJ,GAAG3E,GAAkB,QAAQ0E,CAAO,IAAI,mBAAmBC,CAAS,CAAC,EAAA,CAEzE,OAAShF,EAAO,CACdH,EAAO,MAAM,uBAAwBG,CAAK,CAC5C,CACF,CChdA,MAAMH,EAASC,EAAAA,aAAa,IAAI,EAI1BmF,GAAmB,IAAM1E,EAAA,EAa/B,eAAsB2E,GACpBf,EACAgB,EACkC,CAClC,GAAI,CACF,GAAI,CAACA,GAAQ,CAACA,EAAK,OACjB,MAAO,CAAE,MAAO,GAAO,MAAO,wBAAA,EAIhC,MAAMC,EAAc,mBAAmBD,EAAK,KAAA,CAAM,EAC5C/B,EAAY,MAAM1B,EACtB,yBAAyByC,CAAO,IAAIiB,CAAW,EAAA,EAGjD,GAAI,CAAChC,EAAU,QAEb,OAAIA,EAAU,aAAe,IACpB,CAAE,MAAO,GAAO,MAAO,oBAAA,EAEzB,CAAE,MAAO,GAAO,MAAO,yBAAA,EAGhC,MAAMqB,EAASrB,EAAU,KACzB,MAAO,CACL,MAAOqB,EAAO,OAAS,GACvB,oBAAqBA,EAAO,oBAC5B,gBAAiBA,EAAO,gBACxB,gBAAiBA,EAAO,gBACxB,aAAcA,EAAO,aACrB,OAAQA,EAAO,OACf,KAAMA,EAAO,MAAQU,CAAA,CAEzB,OAASnF,EAAO,CACdH,OAAAA,EAAO,MAAM,+BAAgCG,CAAK,EAC3C,CAAE,MAAO,GAAO,MAAO,+BAAA,CAChC,CACF,CAUA,eAAsBqF,GAAclB,EAA4C,CAC9E,GAAI,CACF,MAAMf,EAAY,MAAM1B,EACtB,sBAAsByC,CAAO,EAAA,EAG/B,OAAKf,EAAU,QAKRA,EAAU,KAAM,gBAAkB,GAHhC,EAIX,OAASpD,EAAO,CACdH,OAAAA,EAAO,MAAM,2CAA4CG,CAAK,EAEvD,EACT,CACF,CAWA,eAAsBsF,GACpBrC,EACAkC,EAC+C,CAC/C,GAAI,CAMF,MAAM/B,EAAY,MAAMtB,EACtB,GAAGmD,GAAA,CAAkB,SAAShC,CAAM,eACpC,CAAE,UAAWkC,CAAA,CAAK,EAGpB,OAAK/B,EAAU,QAOR,CAAE,QAAS,EAAA,EANT,CACL,QAAS,GACT,MAAOA,EAAU,OAAS,4BAAA,CAKhC,OAASpD,EAAO,CACdH,OAAAA,EAAO,MAAM,6BAA8BG,CAAK,EACzC,CAAE,QAAS,GAAO,MAAO,6BAAA,CAClC,CACF,CAQA,eAAsBuF,GACpBtC,EAC+C,CAC/C,GAAI,CACF,MAAMG,EAAY,MAAMtB,EAAQ,WAAWmB,CAAM,eAAe,EAEhE,OAAKG,EAAU,QAOR,CAAE,QAAS,EAAA,EANT,CACL,QAAS,GACT,MAAOA,EAAU,OAAS,6BAAA,CAKhC,OAASpD,EAAO,CACdH,OAAAA,EAAO,MAAM,6BAA8BG,CAAK,EACzC,CAAE,QAAS,GAAO,MAAO,6BAAA,CAClC,CACF,CC/HO,SAASwF,GAAqBjD,EAAuC,CAC1E,MAAMG,EAAQH,GAAO,CAAA,EACrB,MAAO,CACL,WAAY,MAAM,QAAQG,EAAK,UAAU,EAAIA,EAAK,WAAa,CAAA,EAC/D,eAAgBA,EAAK,iBAAmB,EAAA,CAE5C,CCvBA,MAAM7C,EAASC,EAAAA,aAAa,IAAI,EAuB1B2F,MAA4B,IAWlC,eAAsBC,GACpBvB,EACAwB,EAA4B,MAC5BC,EACuB,CAKvB,MAAMC,EAAMD,EAAW,GAAGzB,CAAO,MAAQ,OAAOA,CAAO,EAGvD,GAAIwB,IAAgB,MAAO,CACzB,MAAMG,EAAWL,EAAsB,IAAII,CAAG,EAC9C,GAAIC,EAAU,OAAOA,CACvB,CAEA,MAAMC,GAAO,SAAY,CACvB,GAAI,CACF,MAAMlF,EAAM+E,EACR,GAAGvF,EAAA,CAAkB,WAAW8D,CAAO,aAAa,mBAAmByB,CAAQ,CAAC,GAChF,GAAGvF,EAAA,CAAkB,WAAW8D,CAAO,GAErC3C,EAAW,MAAMmE,EAAY9E,CAAG,EACtC,GAAI,CAACW,EAAS,GACZ,MAAM,IAAI6B,EAAAA,SACR,kCAAkC7B,EAAS,MAAM,GACjD,kCAAA,EAGJ,OAAO,MAAMA,EAAS,KAAA,CACxB,OAASxB,EAAO,CACdH,OAAAA,EAAO,MAAM,gCAAiCG,CAAK,EAC5C,IACT,CACF,GAAA,EAEA,OAAI2F,IAAgB,QAClBF,EAAsB,IAAII,EAAKE,CAAG,EAClCA,EAAI,QAAQ,IAAM,CAIZN,EAAsB,IAAII,CAAG,IAAME,GACrCN,EAAsB,OAAOI,CAAG,CAEpC,CAAC,GAEIE,CACT,CAUA,eAAsBC,GACpB7B,EAC4B,CAC5B,GAAI,CACF,MAAM8B,EAAU,MAAM5D,EACpB,GAAGhC,EAAA,CAAkB,kBAAkB8D,CAAO,EAAA,EAEhD,OAAO,MAAM,QAAQ8B,CAAO,EAAIA,EAAU,CAAA,CAC5C,OAASjG,EAAO,CACdH,OAAAA,EAAO,MAAM,0BAA2BG,CAAK,EACtC,CAAA,CACT,CACF,CAUA,eAAsBkG,GACpB/B,EACkC,CAClC,GAAI,CACF,GAAI,CAACA,EACHtE,OAAAA,EAAO,KAAK,6CAA6C,EAClD,CAAE,WAAY,GAAI,eAAgB,EAAA,EAG3C,MAAM6C,EAAO,MAAML,EACjB,GAAGhC,EAAA,CAAkB,WAAW8D,CAAO,aAAA,EAGzC,OAAKzB,EAQE8C,GAAqB9C,CAAI,GAP9B7C,EAAO,MAAM,qDAAqD,EAC3D,CAAE,WAAY,GAAI,eAAgB,EAAA,EAO7C,OAASG,EAAO,CACdH,OAAAA,EAAO,MAAM,mCAAoCG,CAAK,EAC/C,CAAE,WAAY,GAAI,eAAgB,EAAA,CAC3C,CACF,CAQA,eAAsBmG,GAAeC,EAAwC,CAC3E,GAAI,CACF,GAAI,CAACA,EACHvG,OAAAA,EAAO,KAAK,qCAAqC,EAC1C,CAAA,EAGT,MAAMwG,EAAS,MAAMhE,EACnB,GAAGhC,EAAA,CAAkB,wBAAwB+F,CAAK,EAAA,EAGpD,OAAKC,EAKE,MAAM,QAAQA,CAAM,EAAIA,EAAS,CAAA,GAJtCxG,EAAO,MAAM,iDAAiD,EACvD,CAAA,EAIX,OAASG,EAAO,CACdH,OAAAA,EAAO,MAAM,yBAA0BG,CAAK,EACrC,CAAA,CACT,CACF,CAQA,eAAsBsG,GACpBvB,EACkB,CAClB,GAAI,CACF,GAAI,CAACA,EACHlF,OAAAA,EAAO,KAAK,yCAAyC,EAC9C,CAAA,EAGT,MAAM0G,EAAS,MAAMlE,EACnB,GAAGhC,EAAA,CAAkB,iBAAiB0E,CAAO,EAAA,EAG/C,OAAKwB,EAKE,MAAM,QAAQA,CAAM,EAAIA,EAAS,CAAA,GAJtC1G,EAAO,MAAM,uDAAuD,EAC7D,CAAA,EAIX,OAASG,EAAO,CACdH,OAAAA,EAAO,MAAM,+BAAgCG,CAAK,EAC3C,CAAA,CACT,CACF,CAYA,eAAsBwG,GACpBzB,EACA0B,EACAC,EACkB,CAClB,GAAI,CACF,MAAMhE,EAAO,MAAML,EACjB,GAAGhC,GAAkB,iBAAiB0E,CAAO,UAAU0B,CAAI,IAAIC,CAAK,EAAA,EAGtE,OAAKhE,EAKD,MAAM,QAAQA,CAAI,EAAUA,EACzB,MAAM,QAASA,EAA8B,MAAM,EACrDA,EAA6B,OAC9B,CAAA,GAPF7C,EAAO,MAAM,uDAAuD,EAC7D,CAAA,EAOX,OAASG,EAAO,CACdH,OAAAA,EAAO,MAAM,+BAAgCG,CAAK,EAC3C,CAAA,CACT,CACF,CAUA,eAAsB2G,GACpBP,EACAK,EACAC,EACkB,CAClB,GAAI,CACF,MAAMhE,EAAO,MAAML,EACjB,GAAGhC,GAAkB,wBAAwB+F,CAAK,UAAUK,CAAI,IAAIC,CAAK,EAAA,EAG3E,OAAKhE,EAKD,MAAM,QAAQA,CAAI,EAAUA,EACzB,MAAM,QAASA,EAA8B,MAAM,EACrDA,EAA6B,OAC9B,CAAA,GAPF7C,EAAO,MAAM,2DAA2D,EACjE,CAAA,EAOX,OAASG,EAAO,CACdH,OAAAA,EAAO,MAAM,mCAAoCG,CAAK,EAC/C,CAAA,CACT,CACF,CAUA,eAAsB4G,GACpBC,EAC2C,CAC3C,GAAI,CACF,MAAMnE,EAAO,MAAML,EACjB,GAAGhC,EAAA,CAAkB,WAAWwG,CAAa,cAAA,EAG/C,OAAKnE,IACH7C,EAAO,MACL,6DAAA,EAEK,KAIX,OAASG,EAAO,CACdH,OAAAA,EAAO,MAAM,qCAAsCG,CAAK,EACjD,IACT,CACF,CAWA,eAAsB8G,GACpBD,EACA9B,EAC2C,CAC3C,GAAI,CACF,MAAMrC,EAAO,MAAML,EACjB,GAAGhC,EAAA,CAAkB,WAAWwG,CAAa,wBAAwB9B,CAAO,EAAA,EAG9E,OAAKrC,IACH7C,EAAO,MAAM,0DAA0D,EAChE,KAIX,OAASG,EAAO,CACdH,OAAAA,EAAO,MAAM,qCAAsCG,CAAK,EACjD,IACT,CACF,CAiBA,eAAsB+G,GACpBC,EACAC,EACgC,CAChC,GAAI,CACF,MAAMC,EAAKD,EAAO,SAAS,mBAAmBA,CAAI,CAAC,GAAK,GAClDvE,EAAO,MAAML,EACjB,GAAGhC,EAAA,CAAkB,YAAY,mBAAmB,OAAO2G,CAAE,CAAC,CAAC,GAAGE,CAAE,EAAA,EAGtE,GAAI,CAACxE,GAAQ,CAACA,EAAK,KACjB,MAAM,IAAIW,EAAAA,SACR,2BACA,oCAAA,EAIJ,OAAOX,CACT,OAAS1C,EAAO,CACdH,OAAAA,EAAO,MAAM,iCAAkCG,CAAK,EAC7C,IACT,CACF,CAUA,eAAsBmH,GACpBC,EACgC,CAChC,GAAI,CACF,MAAM1E,EAAO,MAAML,EACjB,GAAGhC,EAAA,CAAkB,WAAW+G,CAAQ,OAAA,EAG1C,GAAI,CAAC1E,EACH,MAAM,IAAIW,EAAAA,SACR,8BACA,gCAAA,EAIJ,OAAOX,CACT,OAAS1C,EAAO,CACdH,OAAAA,EAAO,MAAM,8BAA+BG,CAAK,EAC1C,IACT,CACF,CAYA,eAAsBqH,GACpBC,EACA1B,EACsC,CACtC,GAAI,CAIF,MAAM/E,EAAM+E,EACR,GAAGvF,EAAA,CAAkB,gBAAgBiH,CAAY,aAAa,mBAAmB1B,CAAQ,CAAC,GAC1F,GAAGvF,EAAA,CAAkB,gBAAgBiH,CAAY,GAE/C5E,EAAO,MAAML,EAAkCxB,CAAG,EAExD,GAAI,CAAC6B,EACH,MAAM,IAAIW,EAAAA,SACR,6BACA,sCAAA,EAIJ,OAAOX,CACT,OAAS1C,EAAO,CACdH,OAAAA,EAAO,MAAM,6BAA8BG,CAAK,EACzC,IACT,CACF,CAWA,eAAsBuH,GACpBD,EACA1B,EACkB,CAClB,GAAI,CACF,MAAM4B,EAAkB,mBAAmB5B,CAAQ,EAKnD,OAJe,MAAMvD,EACnB,GAAGhC,EAAA,CAAkB,gBAAgBiH,CAAY,mBAAmBE,CAAe,EAAA,IAGtE,QAAU,EAC3B,OAASxH,EAAO,CACdH,OAAAA,EAAO,MAAM,sCAAuCG,CAAK,EAClD,EACT,CACF,CAWA,eAAsByH,GACpBtD,EACAyB,EAC6B,CAC7B,GAAI,CAGF,MAAMxC,EAAY,MAAMtB,EACtB,GAAGzB,EAAA,CAAkB,WAAW8D,CAAO,kBACvC,CAAE,SAAAyB,CAAA,CAAS,EAGb,GAAI,CAACxC,EAAU,QAAS,MAAO,CAAE,MAAO,EAAA,EAExC,MAAMqB,EAASrB,EAAU,KACzB,OAAI,OAAOqB,GAAW,UAAkB,CAAE,MAAOA,CAAA,EAC1C,CAAE,MAAQA,EAA8B,QAAU,EAAA,CAC3D,OAASzE,EAAO,CACdH,OAAAA,EAAO,MAAM,iCAAkCG,CAAK,EAC7C,CAAE,MAAO,EAAA,CAClB,CACF,CAWA,eAAsB0H,GACpBtB,EACArB,EACkB,CAClB,GAAI,CAEF,MAAMlE,EAAMkE,EACR,GAAG1E,EAAA,CAAkB,iBAAiB0E,CAAO,GAC7CqB,EACE,GAAG/F,GAAkB,wBAAwB+F,CAAK,GAClD,GAAG/F,GAAkB,UAO3B,OAJiB,MAAM,MAAMQ,EAAK,CAChC,OAAQ,MAAA,CACT,GAEe,EAClB,OAASb,EAAO,CACdH,OAAAA,EAAO,MAAM,kCAAmCG,CAAK,EAC9C,EACT,CACF,CChhBA,MAAMH,EAASC,EAAAA,aAAa,IAAI,EAahC,eAAsB6H,EAAS5C,EAAiD,CAC9E,GAAI,CACF,GAAI,CAACA,EACHlF,OAAAA,EAAO,KAAK,iCAAiC,EACtC,KAGT,MAAM4E,EAAS,MAAM/C,EAAc,WAAWqD,CAAO,EAAE,EACvD,OAAKN,EAAO,QAILA,EAAO,MAAQ,MAHpB5E,EAAO,MAAM,0BAA0B4E,EAAO,UAAU,EAAE,EACnD,KAGX,OAASzE,EAAO,CACdH,OAAAA,EAAO,MAAM,wBAAyBG,CAAK,EACpC,IACT,CACF,CAUA,eAAsB4H,GACpB7C,EAKQ,CACR,MAAM8C,EAAQ,MAAMF,EAAS5C,CAAO,EAEpC,OAAK8C,EAIE,CACL,qBAAsBA,EAAM,sBAAwB,EACpD,gBAAiBA,EAAM,iBAAmB,EAC1C,cAAeA,EAAM,eAAiB,CAAA,EAN/B,IAQX,CAUA,eAAsBC,GAAeb,EAAqC,CACxE,GAAI,CACF,GAAI,CAACA,EACHpH,OAAAA,EAAO,KAAK,oCAAoC,EACzC,KAGT,MAAMkI,EAAc,mBAAmBd,CAAI,EACrCxC,EAAS,MAAM/C,EAAc,gBAAgBqG,CAAW,EAAE,EAChE,OAAKtD,EAAO,QAILA,EAAO,MAAQ,MAHpB5E,EAAO,MAAM,kCAAkC4E,EAAO,UAAU,EAAE,EAC3D,KAGX,OAASzE,EAAO,CACdH,OAAAA,EAAO,MAAM,gCAAiCG,CAAK,EAC5C,IACT,CACF,CC5CO,SAASgI,GAAyBzF,EAAmC,CAC1E,MAAMG,EAAQH,GAAO,CAAA,EAEf0F,GAAyBvF,EAAK,eAAiB,GAAK,IACpDwF,EAAkBxF,EAAK,gBAAkB,EAEzCyF,EAAezF,EAAK,oBAAsBwF,GAAmB,EAC7DE,EAAiB,CAACD,EAExB,MAAO,CACL,MAAO,GACP,aAAczF,EAAK,aACnB,eAAgBuF,EAChB,gBAAiBvF,EAAK,gBACtB,YAAayF,EAAe,iBAAmB,QAC/C,aAAcD,EACd,WAAYD,EAAwBC,EACpC,eAAAE,CAAA,CAEJ,CC1DA,MAAMvI,EAASC,EAAAA,aAAa,IAAI,EAE1BuI,GAAe,0BACfC,GAAgB,GAAGD,EAAY,iBAC/BpD,EAAmB,IAAM1E,EAAA,EA4C/B,eAAsBgI,GACpBC,EAC0C,CAC1C,GAAI,CACF,MAAMpF,EAAY,MAAMtB,EACtB,GAAGwG,EAAa,cAChBE,CAAA,EAGF,GAAI,CAACpF,EAAU,QACbvD,MAAAA,EAAO,MAAM,sCAAuCuD,EAAU,KAAK,EAC7D,IAAIC,EAAAA,SAASD,EAAU,OAAS,sCAAuC,2CAA2C,EAG1HvD,OAAAA,EAAO,MAAM,8BAA+BuD,EAAU,IAAI,EACnDA,EAAU,IACnB,OAASpD,EAAO,CACdH,OAAAA,EAAO,MAAM,qCAAsCG,CAAK,EACjD,IACT,CACF,CAqBA,eAAsByI,GACpBxF,EACAkC,EAC8B,CAC9B,GAAI,CACF,MAAM/B,EAAY,MAAMtB,EACtB,GAAGmD,EAAA,CAAkB,SAAShC,CAAM,mBACpC,CAAE,aAAckC,CAAA,CAAK,EAGvB,OAAK/B,EAAU,QAeR4E,GAAyB5E,EAAU,IAAI,EAdxCA,EAAU,aAAe,IACpB,CACL,MAAO,GACP,MAAO,wDAAA,GAGXvD,EAAO,MAAM,0BAA2BuD,EAAU,KAAK,EAChD,CAAE,MAAO,GAAO,MAAOA,EAAU,OAAS,wBAAA,EAQrD,OAAS3B,EAAK,CACZ5B,OAAAA,EAAO,MAAM,uBAAwB4B,CAAG,EACjC,CACL,MAAO,GACP,MAAO,4DAAA,CAEX,CACF,CAUA,eAAsBiH,GAAezF,EAA+C,CAClF,GAAI,CACF,MAAMG,EAAY,MAAMnB,EAAU,GAAGgD,GAAkB,SAAShC,CAAM,YAAY,EAElF,OAAKG,EAAU,QAKR,CAAE,QAAS,EAAA,GAJhBvD,EAAO,MAAM,2BAA4BuD,EAAU,KAAK,EACjD,CAAE,QAAS,GAAO,MAAOA,EAAU,OAAS,4BAAA,EAIvD,OAAS3B,EAAK,CACZ5B,OAAAA,EAAO,MAAM,wBAAyB4B,CAAG,EAClC,CACL,QAAS,GACT,MAAO,4DAAA,CAEX,CACF,CAoBA,eAAsBkH,GACpB1F,EACA2F,EACwC,CACxC,GAAI,CACF,MAAMxF,EAAY,MAAMtB,EACtB,GAAGmD,EAAA,CAAkB,SAAShC,CAAM,8BACpC,CACE,UAAW2F,EAAgB,UAC3B,SAAUA,EAAgB,SAC1B,MAAOA,EAAgB,MACvB,YAAaA,EAAgB,aAAe,KAC5C,YAAaA,EAAgB,aAAe,EAAA,CAC9C,EAGF,OAAKxF,EAAU,QAKR,CACL,QAAS,GACT,QAASA,EAAU,MAAM,QACzB,mBAAoBA,EAAU,MAAM,kBAAA,GAPpCvD,EAAO,MAAM,qCAAsCuD,EAAU,KAAK,EAC3D,CAAE,QAAS,GAAO,MAAOA,EAAU,OAAS,4BAAA,EAQvD,OAAS3B,EAAK,CACZ5B,OAAAA,EAAO,MAAM,iCAAkC4B,CAAG,EAC3C,CACL,QAAS,GACT,MAAO,4DAAA,CAEX,CACF,CC5MA,MAAM5B,EAASC,EAAAA,aAAa,IAAI,EAS1B+I,GAAkD,CACtD,qBAAsB,2CACtB,gBAAiB,kBACjB,cAAe,oCACf,kBAAmB,oDACnB,yBAA0B,8CAC5B,EAKA,eAAsBC,GACpB3E,EACA4E,EACAC,EAAuB,KACvBC,EAAW,GACkB,CAC7B,GAAI,CACF,MAAMlH,EAAgC,CAAE,MAAAgH,CAAA,EACpCC,GAASC,IACXlH,EAAK,MAAQiH,EACbjH,EAAK,SAAW,IAGlB,MAAMqB,EAAY,MAAMtB,EACtB,WAAWqC,CAAO,YAClBpC,CAAA,EAGF,OAAKqB,EAAU,QAUR,CACL,QAAS,GACT,QAASA,EAAU,MAAM,OAAA,GAXzBvD,EAAO,MAAM,wBAAyBuD,EAAU,KAAK,EAC9C,CACL,QAAS,GACT,MACEyF,GAAwBzF,EAAU,OAAS,EAAE,GAC7C,4CAAA,EAQR,OAAS3B,EAAK,CACZ,OAAA5B,EAAO,MAAM,sBAAuB4B,CAAG,EAChC,CACL,QAAS,GACT,MAAO,4DAAA,CAEX,CACF,CAiBA,eAAsByH,GACpB/E,EAC+B,CAC/B,MAAMgF,EAAiC,CACrC,gBAAiB,GACjB,QAAS,GACT,MAAO,EAAA,EAET,GAAI,CACF,MAAM/F,EAAY,MAAM1B,EACtB,WAAWyC,CAAO,kBAAA,EAEpB,OAAKf,EAAU,QACR,CACL,gBAAiB,CAAC,CAACA,EAAU,MAAM,gBACnC,QAAS,CAAC,CAACA,EAAU,MAAM,QAC3B,MAAO,CAAC,CAACA,EAAU,MAAM,KAAA,EAJI+F,CAMjC,OAAS1H,EAAK,CACZ,OAAA5B,EAAO,MAAM,2BAA4B4B,CAAG,EACrC0H,CACT,CACF,CCjCO,SAASC,GACdC,EACApD,EACArE,EAA2B,CAAA,EACjB,CACV,KAAM,CACJ,UAAA0H,EAAY,GACZ,eAAAC,EAAiB,GACjB,gBAAAC,EAAkB,EAAA,EAChB5H,EACE6H,MAAU,KAEhB,GAAIH,EACF,MAAO,CAAE,KAAM,YAAa,SAAU,GAAM,OAAQ,WAAA,EAEtD,MAAMI,EAAWL,EAAM,aAAeA,EAAM,cAC5C,GAAIK,GAAY,IAAI,KAAKA,CAAQ,EAAID,EACnC,MAAO,CAAE,KAAM,cAAe,SAAU,GAAM,OAAQ,YAAA,EAGxD,MAAME,EACJN,GAAO,8BAAgC,IACtC,MAAM,QAAQpD,CAAO,GAAKA,EAAQ,KAAK2D,EAAAA,yBAAyB,EAE7DC,GAAiB5D,GAAW,CAAA,GAAI,OACpC6D,GACEA,EAAE,eAAiB,GACnB,CAACA,EAAE,UACH,EAAE,OAAOA,EAAE,YAAe,UAAYA,EAAE,YAAc,EAAA,EAE1D,GAAID,EAAc,SAAW,EAC3B,OAAIF,EACK,CACL,KAAMJ,EAAiB,iBAAmB,cAC1C,SAAU,GACV,OAAQ,aAAA,EAGL,CACL,KAAM,uBACN,SAAU,GACV,OAAQ,YAAA,EAIZ,IAAIQ,EAAiB,GACjBC,EAAa,GACbC,EAAgB,GAChBC,EAAkC,KAEtC,UAAWC,KAAUN,EAAe,CAClC,MAAMO,EACJD,EAAO,YACPA,EAAO,YACPA,EAAO,WACPA,EAAO,YACHE,EAAWF,EAAO,UAAYA,EAAO,SAAWA,EAAO,UACvDG,EACJH,EAAO,mBAAqBA,EAAO,mBAAqBA,EAAO,SAC3DI,EACJJ,EAAO,SACNG,GAAc,MAAmCA,GAAa,EAE3DE,EAAcJ,EAAa,IAAI,KAAKA,CAAU,EAAIX,EAAM,GACxDgB,EAAgBJ,EAAW,IAAI,KAAKA,CAAQ,EAAIZ,EAAM,GAO5D,GANsB,CAACc,GAAa,CAACC,GAAe,CAACC,IAElCV,EAAiB,IAC/BQ,IAAWP,EAAa,IACxBQ,IAAaP,EAAgB,IAE9BO,GAAeJ,GAAc,CAACG,GAAa,CAACE,EAAe,CAC7D,MAAMC,EAAY,IAAI,KAAKN,CAAU,GACjC,CAACF,GAAsBQ,EAAYR,KACrCA,EAAqBQ,EAEzB,CACF,CAEA,GAAIX,EACF,MAAO,CACL,KAAMR,EAAiB,iBAAmB,cAC1C,SAAU,GACV,OAAQ,WAAA,EAIZ,GAAIU,GAAiBC,EAAoB,CACvC,GAAIP,EACF,MAAO,CACL,KAAMJ,EAAiB,iBAAmB,cAC1C,SAAU,GACV,OAAQ,aAAA,EAGZ,MAAMoB,EAAST,EAAmB,QAAA,EAAYT,EAAI,QAAA,EAC5CmB,EAAYD,GAAU,IAAO,GAAK,IAClCE,EAAWF,GAAU,IAAO,GAAK,GAAK,IAC5C,IAAIG,EACJ,GAAIF,EAAY,GACdE,EAAWC,EAAAA,WAAWb,EAAoB,CAAE,OAAQ,EAAA,EAAQ,OAAO,UAC1DW,GAAY,EACrBC,EAAWE,EAAAA,cACTd,EACA,CAAE,QAAS,QAAS,KAAM,UAAW,OAAQ,UAAW,OAAQ,EAAA,EAChE,OAAA,MAEG,CACL,MAAMe,EAAID,EAAAA,cACRd,EACA,CAAE,MAAO,QAAS,IAAK,SAAA,EACvB,OAAA,EAEIJ,EAAIiB,EAAAA,WAAWb,EAAoB,CAAE,OAAQ,EAAA,EAAQ,OAAO,EAClEY,EAAW,GAAGG,CAAC,IAAInB,CAAC,EACtB,CACA,MAAO,CACL,KAAM,WAAWgB,CAAQ,GACzB,SAAU,GACV,OAAQ,aAAA,CAEZ,CAEA,OAAId,EACEL,EACK,CACL,KAAMJ,EAAiB,iBAAmB,cAC1C,SAAU,GACV,OAAQ,aAAA,EAGRC,EACK,CACL,KAAM,gBACN,SAAU,GACV,OAAQ,mBAAA,EAGL,CAAE,KAAM,WAAY,SAAU,GAAM,OAAQ,UAAA,EAGjDG,EACK,CACL,KAAMJ,EAAiB,iBAAmB,cAC1C,SAAU,GACV,OAAQ,aAAA,EAIL,CAAE,KAAM,cAAe,SAAU,GAAM,OAAQ,aAAA,CACxD,CCzHO,SAAS2B,EAAqB3I,EAA+B,CAClE,MAAM4I,EAAY5I,EAClB,MAAO,CACL,KAAM4I,EAAU,MAAQ,OAAOA,EAAU,IAAMA,EAAU,IAAM,EAAE,EACjE,GAAIA,EAAU,IAAMA,EAAU,GAC9B,aAAcA,EAAU,cAAgBA,EAAU,cAClD,QAASA,EAAU,SAAWA,EAAU,SACxC,kBACEA,EAAU,mBACVA,EAAU,qBACVA,EAAU,UACZ,iBACEA,EAAU,kBACVA,EAAU,oBACVA,EAAU,SACZ,cACEA,EAAU,eAAiBA,EAAU,gBAAkBA,EAAU,MACnE,WACEA,EAAU,YAAcA,EAAU,aAAeA,EAAU,MAAQ,GACrE,aAAcA,EAAU,cAAgBA,EAAU,eAClD,cACEA,EAAU,eACVA,EAAU,gBACVA,EAAU,OACV,EACF,OAAQA,EAAU,OAClB,UAAWA,EAAU,WAAaA,EAAU,WAC5C,YAAaA,EAAU,aAAeA,EAAU,aAAA,CAEpD,CAWO,SAASC,EAAW7I,EAAqB,CAC9C,MAAM8I,EAAW9I,EAGX+I,EACJD,EAAS,kBACTA,EAAS,SACTA,EAAS,mBACT,CAAA,EAEF,MAAO,CACL,KAAMA,EAAS,MAAQ,OAAOA,EAAS,IAAMA,EAAS,IAAM,EAAE,EAC9D,GAAIA,EAAS,IAAMA,EAAS,GAC5B,cACEA,EAAS,eACTA,EAAS,OACTA,EAAS,gBACT,GACF,kBACEA,EAAS,mBACTA,EAAS,WACTA,EAAS,WACX,iBACEA,EAAS,kBACTA,EAAS,UACTA,EAAS,UACX,OAAQA,EAAS,QAAU,UAC3B,YACEA,EAAS,aACTA,EAAS,OACTA,EAAS,cACT,EACF,SAAUA,EAAS,SACnB,kBACEA,EAAS,mBACTA,EAAS,YACTA,EAAS,aACTA,EAAS,qBACT,EACF,UACEA,EAAS,WAAaA,EAAS,KAAOA,EAAS,YAAc,EAC/D,SAAUA,EAAS,UAAY,EAC/B,gBACEA,EAAS,iBAAmBA,EAAS,kBACvC,cAAeA,EAAS,eAAiBA,EAAS,eAClD,iBAAkBC,EAAW,IAAIJ,CAAoB,EACrD,UAAWG,EAAS,WAAaA,EAAS,WAC1C,UAAWA,EAAS,WAAaA,EAAS,UAAA,CAE9C,CAMO,MAAME,GAAkBL,EAMlBM,GAAiBJ,EAOvB,SAASK,GAAyBlJ,EASvC,CACA,MAAMmJ,EAAQN,EAAW7I,CAAG,EACtBoJ,EAAYD,EAAM,KAAK,MAAM,GAAG,EAAE,CAAC,GAAKA,EAAM,KAEpD,MAAO,CACL,QAASA,EAAM,KACf,eAAgBC,EAChB,MAAOD,EAAM,cACb,aAAc,CAACA,EAAM,kBAAmBA,EAAM,gBAAgB,EAC3D,OAAO,OAAO,EACd,KAAK,GAAG,EACX,MAAOA,EAAM,YACb,eAAgB,GAAGE,EAAAA,eAAgBF,EAAM,YAAc,GAAA,CAA2J,GAClN,YAAaA,EAAM,iBAAiB,OACpC,OAAQA,EAAM,MAAA,CAElB,CCpNA,MAAMG,GAAe,qDA0Id,SAASC,EAAeC,EAAkC,CAC/D,OAAKA,EACDA,EAAK,WAAW,MAAM,EAAUA,EAC7B,GAAGF,EAAY,IAAIE,EAAK,QAAQ,MAAO,EAAE,CAAC,GAF/B,EAGpB,CAcO,SAASC,EAAiBC,EAA4B,CAC3D,MAAMC,EACJD,EAAS,UACTA,EAAS,UACTA,EAAS,WACTA,EAAS,OACT,GAEF,OAAOH,EAAeI,CAAM,CAC9B,CAUO,SAASC,EACdF,EACoD,CAEpD,MAAMG,EAAgBH,EAAS,UAAYA,EAAS,UACpD,GAAIG,EACF,OAAOA,EAIT,GAAIH,EAAS,aAAeA,EAAS,aACnC,MAAO,QAIT,MAAMI,EACJJ,EAAS,eAAiBA,EAAS,iBAAmBA,EAAS,KACjE,GAAII,EAAW,CACb,MAAMC,EAAY,IAAI,KAAKD,CAAS,EAAE,QAAA,EAChC5C,EAAM,KAAK,IAAA,EAEjB,GAAI6C,EAAY7C,EACd,MAAO,OAEX,CAGA,MAAM8C,EACJN,EAAS,kBAAoBA,EAAS,kBAExC,GAAIM,IAAmB,OACrB,OAAIA,GAAkB,EACb,WAEF,YAIT,MAAMtG,EACJgG,EAAS,kBACTA,EAAS,mBACTA,EAAS,SACT,CAAA,EAEF,OAAIhG,EAAQ,SAAW,EACd,cAGcA,EAAQ,OAAO,CAACuG,EAAa1C,IAAW,CAC7D,MAAM2C,EAAM3C,EAAE,mBAAqBA,EAAE,oBAAsBA,EAAE,UAAY,EACnE4C,EAAO5C,EAAE,cAAgBA,EAAE,eAAiB,EAClD,OAAO0C,EAAM,KAAK,IAAI,EAAGC,EAAMC,CAAI,CACrC,EAAG,CAAC,GAEkB,EACb,WAGF,WACT,CASO,SAASC,EAAYpK,EAA+B,CACzD,MAAM4I,EAAY5I,EAClB,MAAO,CACL,GAAI4I,EAAU,IAAMA,EAAU,GAC9B,KAAMA,EAAU,MAAQA,EAAU,YAAc,GAChD,YAAaA,EAAU,YACvB,MAAOA,EAAU,OAAS,EAC1B,SAAUA,EAAU,UAAYA,EAAU,eAAiB,EAC3D,aAAcA,EAAU,cAAgBA,EAAU,eAAiB,EACnE,kBACEA,EAAU,mBACVA,EAAU,qBACTA,EAAU,UAAY,IAAMA,EAAU,cAAgB,GACzD,YAAaA,EAAU,aAAeA,EAAU,eAAiB,EACjE,YAAaA,EAAU,aAAeA,EAAU,eAAiB,GACjE,cAAeA,EAAU,eAAiBA,EAAU,gBACpD,YAAaA,EAAU,aAAeA,EAAU,cAChD,SAAUA,EAAU,UAAYA,EAAU,WAAa,GACvD,oBACEA,EAAU,qBAAuBA,EAAU,uBAC7C,WAAYA,EAAU,YAAcA,EAAU,aAAe,EAC7D,UAAWA,EAAU,WAAaA,EAAU,WAC5C,UAAWA,EAAU,WAAaA,EAAU,YAAc,CAAA,CAE9D,CAsBO,SAASyB,EAAWrK,EAAqB,CAC9C,MAAM0J,EAAW1J,EASX0D,GALJgG,EAAS,kBACTA,EAAS,mBACTA,EAAS,SACT,CAAA,GAEyB,IAAIU,CAAW,EAGpCE,EAAS5G,EAAQ,IAAI6D,GAAKA,EAAE,KAAK,EAAE,OAAOgD,GAAKA,EAAI,CAAC,EACpDC,EAAWF,EAAO,OAAS,EAAI,KAAK,IAAI,GAAGA,CAAM,EAAI,OACrDG,EAAWH,EAAO,OAAS,EAAI,KAAK,IAAI,GAAGA,CAAM,EAAI,OAE3D,MAAO,CACL,QAASZ,EAAS,SAAWA,EAAS,IAAMA,EAAS,IAAM,EAC3D,GAAIA,EAAS,IAAMA,EAAS,GAC5B,KAAMA,EAAS,MAAQA,EAAS,OAAS,GACzC,MAAOA,EAAS,OAASA,EAAS,KAClC,KAAMA,EAAS,KACf,YAAaA,EAAS,YACtB,KACEA,EAAS,MAAQA,EAAS,eAAiBA,EAAS,iBAAmB,GACzE,cAAeA,EAAS,eAAiBA,EAAS,gBAClD,YAAaA,EAAS,aAAeA,EAAS,cAC9C,cAAeA,EAAS,eAAiBA,EAAS,gBAClD,SAAUA,EAAS,UAAYA,EAAS,UACxC,QAASA,EAAS,SAAWA,EAAS,SACtC,UAAWA,EAAS,WAAaA,EAAS,WAC1C,aAAcA,EAAS,cAAgBA,EAAS,cAChD,SACEA,EAAS,UAAYA,EAAS,cAAgBA,EAAS,cACzD,SAAUD,EAAiBC,CAAQ,EACnC,SAAUD,EAAiBC,CAAQ,EACnC,OAAQA,EAAS,OACjB,YAAaA,EAAS,aAAeA,EAAS,aAC9C,YAAaA,EAAS,aAAeA,EAAS,aAC9C,iBAAkBhG,EAClB,iBACEgG,EAAS,kBACTA,EAAS,mBACThG,EAAQ,OAAO,CAACuG,EAAK1C,IAAM0C,GAAO1C,EAAE,mBAAqB,GAAI,CAAC,EAChE,YAAamC,EAAS,aAAeA,EAAS,aAC9C,SAAUA,EAAS,UAAYA,EAAS,WAAac,EACrD,SAAUd,EAAS,UAAYA,EAAS,WAAae,EACrD,QAASf,EAAS,SAAWA,EAAS,SACtC,SAAUE,EAAoBF,CAAQ,EACtC,eAAgBA,EAAS,gBAAkBA,EAAS,gBACpD,cAAeA,EAAS,eAAiBA,EAAS,gBAClD,qBACEA,EAAS,sBAAwBA,EAAS,uBAE5C,SAAUA,EAAS,SACnB,aAAcA,EAAS,aACvB,SAAUA,EAAS,SACnB,YAAaA,EAAS,YACtB,WAAYA,EAAS,WACrB,WAAYA,EAAS,WACrB,mBAAoBA,EAAS,mBAC7B,eAAgBA,EAAS,eACzB,sBAAuBA,EAAS,sBAChC,iBAAkBA,EAAS,iBAC3B,eAAgBA,EAAS,eACzB,iBAAkBA,EAAS,iBAC3B,aAAcA,EAAS,cAAgBA,EAAS,cAChD,iBAAkBA,EAAS,iBAC3B,aAAcA,EAAS,aACvB,iBAAkBA,EAAS,iBAC3B,MAAOA,EAAS,MAChB,UAAWA,EAAS,UACpB,QAASA,EAAS,QAClB,mBAAoBA,EAAS,mBAC7B,iBAAkBA,EAAS,iBAC3B,cAAeA,EAAS,cACxB,MAAOA,EAAS,MAChB,WAAYA,EAAS,WACrB,KAAMA,EAAS,KACf,UAAWA,EAAS,UACpB,iBAAkBA,EAAS,gBAAA,CAE/B,CAMO,MAAMV,GAAkBoB,EAMlBM,GAAiBL,EAKjBM,GAAqBN,EC1Y5Bf,GAAe,qDAkCrB,SAASsB,GAAWpB,EAAkC,CACpD,OAAKA,EACDA,EAAK,WAAW,MAAM,EAAUA,EAC7B,GAAGF,EAAY,IAAIE,EAAK,QAAQ,MAAO,EAAE,CAAC,GAF/B,EAGpB,CAUO,SAASqB,GAAW7K,EAAqB,CAC9C,MAAM8K,EAAW9K,EAEX+K,EACJD,EAAS,SAAWA,EAAS,UAAYA,EAAS,KAEpD,MAAO,CACL,GAAIA,EAAS,IAAMA,EAAS,IAAM,EAClC,KAAMA,EAAS,MAAQ,GACvB,KAAMA,EAAS,KACf,QAASA,EAAS,QAClB,wBACEA,EAAS,yBACTA,EAAS,2BACX,KAAMA,EAAS,KACf,MAAOA,EAAS,MAChB,QAASA,EAAS,SAAWA,EAAS,SACtC,QAASA,EAAS,QAClB,SAAUA,EAAS,UAAYA,EAAS,UACxC,QAASF,GAAWG,CAAQ,EAC5B,qBACED,EAAS,sBACTA,EAAS,wBACT,EACF,gBACEA,EAAS,iBAAmBA,EAAS,mBAAqB,EAC5D,cACEA,EAAS,eAAiBA,EAAS,gBAAkB,EACvD,eACEA,EAAS,gBAAkBA,EAAS,eAAA,CAE1C,CAMO,MAAME,GAAiBH,GAOvB,SAASI,GAAiBjL,EAI/B,CACA,MAAM8K,EAAW9K,EACjB,MAAO,CACL,qBACE8K,EAAS,sBACTA,EAAS,wBACT,EACF,gBACEA,EAAS,iBAAmBA,EAAS,mBAAqB,EAC5D,cACEA,EAAS,eAAiBA,EAAS,gBAAkB,CAAA,CAE3D,CAKO,SAASI,GAAmB5F,EAAsB,CAQvD,MAPc,CACZA,EAAM,QACNA,EAAM,KACNA,EAAM,MACNA,EAAM,OAAA,EACN,OAAO,OAAO,EAEH,KAAK,IAAI,CACxB,CC5IA,SAAS6F,EAAQC,EAAQ,CACvB,QAASC,EAAI,EAAGA,EAAI,UAAU,OAAQA,IAAK,CACzC,IAAIC,EAAS,UAAUD,CAAC,EACxB,QAAS/H,KAAOgI,EACVhI,IAAQ,cACZ8H,EAAO9H,CAAG,EAAIgI,EAAOhI,CAAG,EAE5B,CACA,OAAO8H,CACT,CAEA,IAAIG,GAAmB,CACrB,KAAM,SAAUC,EAAO,CACrB,OAAIA,EAAM,CAAC,IAAM,MACfA,EAAQA,EAAM,MAAM,EAAG,EAAE,GAEpBA,EAAM,QAAQ,mBAAoB,kBAAkB,CAC7D,EACA,MAAO,SAAUA,EAAO,CACtB,OAAO,mBAAmBA,CAAK,EAAE,QAC/B,2CACA,kBACN,CACE,CACF,EAEA,SAASjN,EAAKkN,EAAWC,EAAmB,CAC1C,SAASC,EAAIC,EAAMJ,EAAOK,EAAY,CACpC,GAAI,SAAO,SAAa,KAIxB,CAAAA,EAAaV,EAAO,GAAIO,EAAmBG,CAAU,EAEjD,OAAOA,EAAW,SAAY,WAChCA,EAAW,QAAU,IAAI,KAAK,KAAK,MAAQA,EAAW,QAAU,KAAK,GAEnEA,EAAW,UACbA,EAAW,QAAUA,EAAW,QAAQ,YAAW,GAGrDD,EAAO,mBAAmBA,CAAI,EAC3B,QAAQ,uBAAwB,kBAAkB,EAClD,QAAQ,QAAS,MAAM,EAE1B,IAAIE,EAAwB,GAC5B,QAASC,KAAiBF,EACnBA,EAAWE,CAAa,IAI7BD,GAAyB,KAAOC,EAE5BF,EAAWE,CAAa,IAAM,KAWlCD,GAAyB,IAAMD,EAAWE,CAAa,EAAE,MAAM,GAAG,EAAE,CAAC,IAGvE,OAAQ,SAAS,OACfH,EAAO,IAAMH,EAAU,MAAMD,EAAOI,CAAI,EAAIE,EAChD,CAEA,SAASE,EAAIJ,EAAM,CACjB,GAAI,SAAO,SAAa,KAAgB,UAAU,QAAU,CAACA,GAQ7D,SAFIK,EAAU,SAAS,OAAS,SAAS,OAAO,MAAM,IAAI,EAAI,CAAA,EAC1DC,EAAM,CAAA,EACD,EAAI,EAAG,EAAID,EAAQ,OAAQ,IAAK,CACvC,IAAIE,EAAQF,EAAQ,CAAC,EAAE,MAAM,GAAG,EAC5BT,EAAQW,EAAM,MAAM,CAAC,EAAE,KAAK,GAAG,EAEnC,GAAI,CACF,IAAIC,EAAQ,mBAAmBD,EAAM,CAAC,CAAC,EAEvC,GADMC,KAASF,IAAMA,EAAIE,CAAK,EAAIX,EAAU,KAAKD,EAAOY,CAAK,GACzDR,IAASQ,EACX,KAEJ,MAAa,CAEb,CACF,CAEA,OAAOR,EAAOM,EAAIN,CAAI,EAAIM,EAC5B,CAEA,OAAO,OAAO,OACZ,CACE,IAAKP,EACL,IAAKK,EACL,OAAQ,SAAUJ,EAAMC,EAAY,CAClCF,EACEC,EACA,GACAT,EAAO,CAAA,EAAIU,EAAY,CACrB,QAAS,EACrB,CAAW,CACX,CACM,EACA,eAAgB,SAAUA,EAAY,CACpC,OAAOtN,EAAK,KAAK,UAAW4M,EAAO,CAAA,EAAI,KAAK,WAAYU,CAAU,CAAC,CACrE,EACA,cAAe,SAAUJ,EAAW,CAClC,OAAOlN,EAAK4M,EAAO,GAAI,KAAK,UAAWM,CAAS,EAAG,KAAK,UAAU,CACpE,CACN,EACI,CACE,WAAY,CAAE,MAAO,OAAO,OAAOC,CAAiB,CAAC,EACrD,UAAW,CAAE,MAAO,OAAO,OAAOD,CAAS,CAAC,CAClD,CACA,CACA,CAEUlN,EAAKgN,GAAkB,CAAE,KAAM,GAAG,CAAE,ECnH/BhO,EAAAA,aAAa,eAAe,ECa5BA,EAAAA,aAAa,UAAU,ECpBvBA,EAAAA,aAAa,KAAK","x_google_ignoreList":[14]}