@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.
- package/README.md +173 -128
- package/dist/{CarouselView.legacy-CsMGxVbb.js → CarouselView.legacy-Bn76lnuH.js} +3 -3
- package/dist/{CarouselView.legacy-CsMGxVbb.js.map → CarouselView.legacy-Bn76lnuH.js.map} +1 -1
- package/dist/{CartView-BOaZufWZ.js → CartView-lR1zLxmN.js} +277 -280
- package/dist/CartView-lR1zLxmN.js.map +1 -0
- package/dist/{Checkout-bkXNpxIB.js → Checkout-D5mXj5zN.js} +69 -73
- package/dist/Checkout-D5mXj5zN.js.map +1 -0
- package/dist/Checkout-KyLZlwLk.js +1673 -0
- package/dist/Checkout-KyLZlwLk.js.map +1 -0
- package/dist/{Checkout.legacy-BOPwZMyO.js → Checkout.legacy-Dl8Oh7y3.js} +95 -99
- package/dist/Checkout.legacy-Dl8Oh7y3.js.map +1 -0
- package/dist/CheckoutTimer-Deo1mLnO.js +141 -0
- package/dist/CheckoutTimer-Deo1mLnO.js.map +1 -0
- package/dist/CollectionView-C1O9xfVC.js +338 -0
- package/dist/CollectionView-C1O9xfVC.js.map +1 -0
- package/dist/{CollectionView.legacy-Dw-J6Ycd.js → CollectionView.legacy-DK_mCA_g.js} +21 -24
- package/dist/{CollectionView.legacy-Dw-J6Ycd.js.map → CollectionView.legacy-DK_mCA_g.js.map} +1 -1
- package/dist/Event-B5WRygEf.js +2240 -0
- package/dist/Event-B5WRygEf.js.map +1 -0
- package/dist/EventPage-f1AOk4Jb.js +577 -0
- package/dist/EventPage-f1AOk4Jb.js.map +1 -0
- package/dist/{EventPage.legacy-Br2M8N9-.js → EventPage.legacy-Bk3PJyJs.js} +408 -411
- package/dist/EventPage.legacy-Bk3PJyJs.js.map +1 -0
- package/dist/{FeaturedView.legacy-B86kMzvH.js → FeaturedView.legacy-BzCt1X1W.js} +18 -18
- package/dist/{FeaturedView.legacy-B86kMzvH.js.map → FeaturedView.legacy-BzCt1X1W.js.map} +1 -1
- package/dist/{GalleryCard-CTsYUZu6.js → GalleryCard-C1EKGErR.js} +14 -14
- package/dist/{GalleryCard-CTsYUZu6.js.map → GalleryCard-C1EKGErR.js.map} +1 -1
- package/dist/{GalleryView.legacy-uIexOsl5.js → GalleryView.legacy-BmQtZKl7.js} +3 -3
- package/dist/{GalleryView.legacy-uIexOsl5.js.map → GalleryView.legacy-BmQtZKl7.js.map} +1 -1
- package/dist/{GroupedListView.legacy-DMAwHQ9T.js → GroupedListView.legacy-D72fbNfA.js} +21 -21
- package/dist/{GroupedListView.legacy-DMAwHQ9T.js.map → GroupedListView.legacy-D72fbNfA.js.map} +1 -1
- package/dist/Heading-Bwevh2c4.js +81 -0
- package/dist/Heading-Bwevh2c4.js.map +1 -0
- package/dist/ModalHeader-DKwE5ZYZ.js +22 -0
- package/dist/ModalHeader-DKwE5ZYZ.js.map +1 -0
- package/dist/OrderSummarySkeleton-DKWKZNLL.js +632 -0
- package/dist/OrderSummarySkeleton-DKWKZNLL.js.map +1 -0
- package/dist/{ScarcityBadge-D9RzN3Tz.js → ScarcityBadge-3cFxTOCh.js} +25 -29
- package/dist/{ScarcityBadge-D9RzN3Tz.js.map → ScarcityBadge-3cFxTOCh.js.map} +1 -1
- package/dist/{SeriesPage-D0pf6VuS.js → SeriesPage-Cq4Q8Vah.js} +17 -21
- package/dist/{SeriesPage-D0pf6VuS.js.map → SeriesPage-Cq4Q8Vah.js.map} +1 -1
- package/dist/{SeriesPage.legacy-VYpfyT7B.js → SeriesPage.legacy-zvgW0S_y.js} +67 -71
- package/dist/SeriesPage.legacy-zvgW0S_y.js.map +1 -0
- package/dist/{Success-Dxgbo_Ct.js → Success-kyiEO6_Z.js} +186 -175
- package/dist/{Success-Dxgbo_Ct.js.map → Success-kyiEO6_Z.js.map} +1 -1
- package/dist/{Success.legacy-umzUtow7.js → Success.legacy-BxKYmXgz.js} +53 -57
- package/dist/{Success.legacy-umzUtow7.js.map → Success.legacy-BxKYmXgz.js.map} +1 -1
- package/dist/Text-_bLxSPv-.js +158 -0
- package/dist/Text-_bLxSPv-.js.map +1 -0
- package/dist/{VenueCalendar-D9TCV_XB.js → VenueCalendar-d8u6MKEu.js} +8131 -22680
- package/dist/VenueCalendar-d8u6MKEu.js.map +1 -0
- package/dist/ViewTicketsEmbed-DquJJVIK.js +2081 -0
- package/dist/ViewTicketsEmbed-DquJJVIK.js.map +1 -0
- package/dist/__SKIP_NAVIGATION__-CmipjatL.js +18 -0
- package/dist/__SKIP_NAVIGATION__-CmipjatL.js.map +1 -0
- package/dist/api/api.cjs +1 -1
- package/dist/api/api.cjs.map +1 -1
- package/dist/api/api.mjs +482 -374
- package/dist/api/api.mjs.map +1 -1
- package/dist/api/cta.d.ts +2 -10
- package/dist/api/transformers/address.d.ts +18 -0
- package/dist/api/transformers/cart.d.ts +19 -0
- package/dist/api/transformers/collection.d.ts +12 -0
- package/dist/api/transformers/event.d.ts +50 -1
- package/dist/api/transformers/giftCard.d.ts +11 -0
- package/dist/api/transformers/index.d.ts +10 -3
- package/dist/api/transformers/performer.d.ts +8 -0
- package/dist/api/transformers/series.d.ts +12 -0
- package/dist/api/types.d.ts +423 -383
- package/dist/api-BzICORqy.js +6 -0
- package/dist/{api-DFMsiBOR.js.map → api-BzICORqy.js.map} +1 -1
- package/dist/colors-CmP-sSZD.js.map +1 -1
- package/dist/{data-toggle-store.svelte-DtDqN-QD.js → data-toggle-store.svelte-BGbzblUJ.js} +10 -15
- package/dist/data-toggle-store.svelte-BGbzblUJ.js.map +1 -0
- package/dist/index-BsWecoW1.js +63 -0
- package/dist/index-BsWecoW1.js.map +1 -0
- package/dist/labels-Bj_cocb1.js +966 -0
- package/dist/labels-Bj_cocb1.js.map +1 -0
- package/dist/seo/seo.cjs +1 -1
- package/dist/seo/seo.cjs.map +1 -1
- package/dist/seo/seo.mjs +131 -122
- package/dist/seo/seo.mjs.map +1 -1
- package/dist/transform-D7Oe8jUp.js +276 -0
- package/dist/transform-D7Oe8jUp.js.map +1 -0
- package/dist/types/index.d.ts +45 -0
- package/dist/venue-calendar.css +1 -1
- package/dist/venue-calendar.es.js +31 -37
- package/dist/venue-calendar.es.js.map +1 -1
- package/dist/venue-calendar.iife.js +40 -107
- package/dist/venue-calendar.iife.js.map +1 -1
- package/dist/venue-calendar.umd.js +38 -125
- package/dist/venue-calendar.umd.js.map +1 -1
- package/package.json +170 -169
- package/dist/CartView-BOaZufWZ.js.map +0 -1
- package/dist/Checkout-DD6ZTUh-.js +0 -1531
- package/dist/Checkout-DD6ZTUh-.js.map +0 -1
- package/dist/Checkout-bkXNpxIB.js.map +0 -1
- package/dist/Checkout.legacy-BOPwZMyO.js.map +0 -1
- package/dist/CheckoutTimer-CHoKjZyH.js +0 -35
- package/dist/CheckoutTimer-CHoKjZyH.js.map +0 -1
- package/dist/CollectionView-Ce-OAEC4.js +0 -148
- package/dist/CollectionView-Ce-OAEC4.js.map +0 -1
- package/dist/Event-rdstzev0.js +0 -1910
- package/dist/Event-rdstzev0.js.map +0 -1
- package/dist/EventPage-B8VRC83d.js +0 -336
- package/dist/EventPage-B8VRC83d.js.map +0 -1
- package/dist/EventPage.legacy-Br2M8N9-.js.map +0 -1
- package/dist/Heading-AFd3o0xt.js +0 -44
- package/dist/Heading-AFd3o0xt.js.map +0 -1
- package/dist/OrderSummarySkeleton-dDKUH741.js +0 -16
- package/dist/OrderSummarySkeleton-dDKUH741.js.map +0 -1
- package/dist/SeriesPage.legacy-VYpfyT7B.js.map +0 -1
- package/dist/Text-CXR2fhx6.js +0 -54
- package/dist/Text-CXR2fhx6.js.map +0 -1
- package/dist/VenueCalendar-D9TCV_XB.js.map +0 -1
- package/dist/ViewTicketsEmbed-Bt05E5Ex.js +0 -2039
- package/dist/ViewTicketsEmbed-Bt05E5Ex.js.map +0 -1
- package/dist/__SKIP_NAVIGATION__-CJ96TTPE.js +0 -7
- package/dist/__SKIP_NAVIGATION__-CJ96TTPE.js.map +0 -1
- package/dist/api-DFMsiBOR.js +0 -12
- package/dist/data-toggle-store.svelte-DtDqN-QD.js.map +0 -1
- package/dist/labels-CClq3Rb9.js +0 -659
- package/dist/labels-CClq3Rb9.js.map +0 -1
- package/dist/transform-CWf-YI_e.js +0 -275
- package/dist/transform-CWf-YI_e.js.map +0 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"labels-Bj_cocb1.js","sources":["../node_modules/@getmicdrop/svelte-components/dist/components/Toast/toast.svelte.js","../src/lib/api/cta.ts","../src/lib/public-calendar-flow/defaults.ts","../src/lib/public-calendar-flow/OrderSummary.svelte","../src/lib/public-calendar-flow/i18n/labels.js"],"sourcesContent":["import { createLogger } from '../../utils/logger.js';\n/**\n * Micdrop Toast System\n * A modern, macOS-style toast notification system built with Svelte 5 runes.\n * Supports both simple toasts and rich notification-style toasts.\n *\n * Features:\n * - Opt-in batching for high-frequency notifications (merge toasts sharing a key)\n * - Desktop notification support (browser API)\n * - Swipe-to-dismiss on mobile\n * - Haptic feedback on toast display (QOL Bible)\n */\nimport { SvelteMap } from 'svelte/reactivity';\nimport { triggerHaptic } from '../../utils/haptic.js';\nconst logger = createLogger('toast.svelte');\n// =============================================================================\n// BATCHING CONFIGURATION\n// =============================================================================\nconst BATCH_WINDOW_MS = 20000; // 20 seconds - default window to aggregate batched toasts\nconst activeBatches = new SvelteMap();\n// Namespace for opt-in (generic) batch keys, kept distinct from any other\n// internal map usage.\nconst GENERIC_BATCH_PREFIX = 'opt-in:';\nfunction registerBatch(mapKey, toastId, amount, windowMs) {\n const timeout = setTimeout(() => {\n activeBatches.delete(mapKey);\n }, windowMs);\n activeBatches.set(mapKey, {\n toastId,\n count: 1,\n total: amount,\n timeout,\n lastUpdate: Date.now(),\n });\n}\nfunction bumpBatch(batch, amount) {\n batch.count++;\n batch.total += amount;\n batch.lastUpdate = Date.now();\n return { count: batch.count, total: batch.total };\n}\n// Apply a merge to the batched toast (MF parity: update-if-present — if the\n// toast was already dismissed, this is bookkeeping-only; no resurrection).\n// Resets the visual timer (createdAt) and reschedules auto-dismiss.\nfunction applyBatchMerge(batch, ctx, update, duration) {\n toasts = toasts.map(t => t.id === batch.toastId\n ? {\n ...t,\n title: update.title ?? t.title,\n description: update.description ?? t.description,\n onClick: update.onClick ?? t.onClick,\n batchCount: ctx.count,\n batchTotal: ctx.total,\n notification: t.notification\n ? {\n ...t.notification,\n batchCount: ctx.count,\n batchTotal: ctx.total,\n message: update.message ?? t.notification.message,\n }\n : t.notification,\n createdAt: Date.now(), // Reset timer visual (MF parity)\n }\n : t);\n if (duration !== Infinity) {\n scheduleRemoval(batch.toastId, duration);\n }\n}\n// =============================================================================\n// DESKTOP NOTIFICATIONS\n// =============================================================================\nlet desktopNotificationsEnabled = $state(false);\nlet desktopNotificationPermission = $state('default');\n// Check initial permission state\nif (typeof window !== 'undefined' && 'Notification' in window) {\n desktopNotificationPermission = Notification.permission;\n desktopNotificationsEnabled = Notification.permission === 'granted';\n}\nasync function requestDesktopPermission() {\n if (typeof window === 'undefined' || !('Notification' in window)) {\n return false;\n }\n const permission = await Notification.requestPermission();\n desktopNotificationPermission = permission;\n desktopNotificationsEnabled = permission === 'granted';\n return desktopNotificationsEnabled;\n}\nlet pushPreferenceChecker = null;\n// Status-to-preference mapping\nconst STATUS_TO_PREFERENCE = {\n // Admin notifications\n 'ticket sold': { category: 'admin', key: 'ticketSales' },\n 'order completed': { category: 'admin', key: 'ticketSales' },\n 'performer confirmed': { category: 'admin', key: 'performerResponses' },\n 'performer declined': { category: 'admin', key: 'performerResponses' },\n 'event sold out': { category: 'admin', key: 'eventSoldOut' },\n 'show sold out': { category: 'admin', key: 'eventSoldOut' },\n 'section sold out': { category: 'admin', key: 'eventSoldOut' },\n 'ticket type sold out': { category: 'admin', key: 'eventSoldOut' },\n 'vip tickets sold out': { category: 'admin', key: 'eventSoldOut' },\n 'event published': { category: 'admin', key: 'eventPublished' },\n // Performer notifications\n 'avails sent': { category: 'performer', key: 'availabilityRequests' },\n 'avails sent to all': { category: 'performer', key: 'availabilityRequests' },\n 'availability request': {\n category: 'performer',\n key: 'availabilityRequests',\n },\n 'performance invitation': {\n category: 'performer',\n key: 'performanceInvitations',\n },\n 'invited to perform': {\n category: 'performer',\n key: 'performanceInvitations',\n },\n};\nfunction setPushPreferenceChecker(checker) {\n pushPreferenceChecker = checker;\n}\n// Shared normalization — trim + lowercase — used by both the legacy\n// notification-toast path and the new opt-in desktopNotification path so the\n// push-preference checker always receives the same casing the STATUS_TO_PREFERENCE\n// keys were authored in. Without this, a display-cased status like 'Ticket Sold'\n// would miss the 'ticket sold' key and bypass a muted category.\nfunction normalizeStatus(status) {\n return status.trim().toLowerCase();\n}\nfunction isPushEnabledForStatus(status) {\n // If no preference checker is set, allow all (backwards compatibility)\n if (!pushPreferenceChecker)\n return true;\n return pushPreferenceChecker(status);\n}\nfunction showDesktopNotification(title, options) {\n if (!desktopNotificationsEnabled)\n return;\n // Normalize status before the preference check so both the legacy\n // notification-toast path and the opt-in simple-toast path see the same\n // lowercase form the push-preference checker's keys are authored in.\n if (options?.status && !isPushEnabledForStatus(normalizeStatus(options.status))) {\n return; // User has disabled push for this category\n }\n try {\n new Notification(title, {\n body: options?.body,\n icon: options?.icon || '/favicon.png',\n badge: '/favicon.png',\n silent: false,\n });\n }\n catch (e) {\n // Desktop notifications may fail in some contexts (e.g., service workers)\n logger.warn('Desktop notification failed:', e);\n }\n}\n// Fire the OPT-IN desktop notification for a simple toast (creation or batch\n// merge). No-op unless the caller passed `desktopNotification` — notification\n// toasts keep their automatic legacy behavior elsewhere.\nfunction fireDesktopNotificationOption(title, options, update) {\n const dn = options.desktopNotification;\n if (!dn)\n return;\n const cfg = dn === true ? {} : dn;\n showDesktopNotification(cfg.title ?? update?.title ?? title, {\n body: cfg.body ?? update?.description ?? options.description,\n icon: cfg.icon,\n status: cfg.status,\n });\n}\n// Default durations by type (ms).\n// Decided feel (premium brief 02-B): success/info ~4s with pause-on-hover;\n// warning ~5s; ERRORS PERSIST until dismissed (manual/click dismiss) so a\n// failure can't scroll away before it's read. loading is also persistent.\nconst DEFAULT_DURATIONS = {\n success: 4000,\n error: Infinity, // persist until dismissed (was 6000ms auto-dismiss)\n warning: 5000,\n info: 4000,\n loading: Infinity,\n notification: 6000,\n};\n// Maximum number of toasts before oldest are auto-dismissed\nconst MAX_TOASTS = 5;\n// Toast store using Svelte 5 runes\nlet toasts = $state([]);\nconst timers = new SvelteMap();\nfunction generateId() {\n return `toast-${Date.now()}-${Math.random().toString(36).slice(2, 9)}`;\n}\n// Map toast types to haptic feedback styles\nconst TOAST_HAPTIC_STYLES = {\n success: 'success',\n error: 'error',\n warning: 'warning',\n info: 'light',\n loading: null, // No haptic for loading states\n notification: 'light',\n};\n// ===========================================================================\n// ARIA-LIVE ANNOUNCEMENTS (brief 02-C)\n// ===========================================================================\n// Two SEPARATE, persistent live regions — one polite, one assertive — instead\n// of one shared region whose politeness is mutated per-announcement. A shared\n// region races: an error (assertive) at t=0 followed by a success (polite) at\n// t=50ms would flip the region to polite before the error text lands. Separate\n// regions let each politeness write immediately and independently.\nconst LIVE_REGION_IDS = {\n polite: 'sc-toast-live-polite',\n assertive: 'sc-toast-live-assertive',\n};\nfunction getToastLiveRegion(politeness) {\n const id = LIVE_REGION_IDS[politeness];\n let region = document.getElementById(id);\n if (region)\n return region;\n region = document.createElement('div');\n region.id = id;\n region.setAttribute('role', politeness === 'assertive' ? 'alert' : 'status');\n region.setAttribute('aria-live', politeness);\n region.setAttribute('aria-atomic', 'true');\n region.className = 'sr-only';\n Object.assign(region.style, {\n position: 'absolute',\n width: '1px',\n height: '1px',\n padding: '0',\n margin: '-1px',\n overflow: 'hidden',\n clip: 'rect(0, 0, 0, 0)',\n whiteSpace: 'nowrap',\n border: '0',\n });\n document.body.appendChild(region);\n return region;\n}\nfunction announceToMatchingRegion(politeness, message) {\n if (typeof document === 'undefined' || !message)\n return;\n const region = getToastLiveRegion(politeness);\n // Clear then set on the next frame so repeated identical messages still fire.\n region.textContent = '';\n setTimeout(() => {\n region.textContent = message;\n }, 100);\n}\n// Announce a toast to the politeness-matched live region (brief 02-C).\n// Errors interrupt (assertive); everything else is polite.\nfunction announceToast(type, title, description) {\n const message = description ? `${title}. ${description}` : title;\n announceToMatchingRegion(type === 'error' ? 'assertive' : 'polite', message);\n}\nfunction addToast(type, title, options = {}) {\n const duration = options.duration ?? DEFAULT_DURATIONS[type];\n // Opt-in generic batching: merge into an active batch instead of stacking.\n // MF-parity merge semantics: no new announcement, no haptic, timer reset.\n if (options.batch) {\n const mapKey = GENERIC_BATCH_PREFIX + options.batch.key;\n const existingBatch = activeBatches.get(mapKey);\n if (existingBatch) {\n const ctx = bumpBatch(existingBatch, options.batch.amount ?? 1);\n const update = options.batch.render?.(ctx) ?? {};\n applyBatchMerge(existingBatch, ctx, update, duration);\n fireDesktopNotificationOption(title, options, update);\n return existingBatch.toastId;\n }\n }\n const id = generateId();\n // A PERSISTENT ERROR (brief 02-B made errors duration:Infinity) MUST keep a\n // dismiss affordance, or a failure could never be closed. Force `dismissible`\n // on only for that case — never for loading / programmatic Infinity toasts,\n // which intentionally close from code (dismissible:false stays honored there).\n const isPersistentError = type === 'error' && duration === Infinity;\n const dismissible = isPersistentError ? true : (options.dismissible ?? true);\n const newToast = {\n id,\n type,\n title,\n description: options.description,\n duration,\n dismissible,\n onClick: options.onClick,\n action: options.action,\n createdAt: Date.now(),\n ...(options.batch\n ? { batchCount: 1, batchTotal: options.batch.amount ?? 1 }\n : {}),\n };\n // Add to beginning so newest appears at top\n toasts = [newToast, ...toasts];\n // Announce to screen readers via the politeness-matched live region\n // (brief 02-C): errors are assertive (interrupt), everything else is polite.\n announceToast(type, title, options.description);\n // Trigger haptic feedback based on toast type (QOL Bible)\n const hapticStyle = TOAST_HAPTIC_STYLES[type];\n if (hapticStyle) {\n triggerHaptic(hapticStyle);\n }\n // Set auto-dismiss timer if not infinite\n if (duration !== Infinity) {\n scheduleRemoval(id, duration);\n }\n // Opt-in batching: open the (non-sliding) batch window for this key.\n if (options.batch) {\n registerBatch(GENERIC_BATCH_PREFIX + options.batch.key, id, options.batch.amount ?? 1, options.batch.window ?? BATCH_WINDOW_MS);\n }\n // Opt-in desktop notification (simple toasts never fired these before).\n fireDesktopNotificationOption(title, options);\n // Trim overflow — keep only MAX_TOASTS\n if (toasts.length > MAX_TOASTS) {\n const overflow = toasts.slice(MAX_TOASTS);\n for (const t of overflow) {\n const timer = timers.get(t.id);\n if (timer) {\n clearTimeout(timer);\n timers.delete(t.id);\n }\n }\n toasts = toasts.slice(0, MAX_TOASTS);\n }\n return id;\n}\nfunction addNotificationToast(options) {\n const normalizedStatus = normalizeStatus(options.status || '');\n // Batching is purely opt-in: an explicit `batch` object merges toasts\n // sharing the caller's key; `batch: false`/undefined disables batching.\n const genericBatch = typeof options.batch === 'object' && options.batch !== null\n ? options.batch\n : undefined;\n // Generic opt-in batching: merge into an active batch under the caller's key.\n if (genericBatch) {\n const mapKey = GENERIC_BATCH_PREFIX + genericBatch.key;\n const existingBatch = activeBatches.get(mapKey);\n if (existingBatch) {\n const ctx = bumpBatch(existingBatch, genericBatch.amount ?? 1);\n const update = genericBatch.render?.(ctx) ?? {};\n applyBatchMerge(existingBatch, ctx, update, options.duration ?? DEFAULT_DURATIONS.notification);\n // Notification toasts fire desktop notifications automatically (legacy\n // behavior); on merge, prefer the batch-updated body.\n showDesktopNotification(options.event, {\n body: update.desktopBody ??\n update.message ??\n options.message ??\n options.status,\n icon: options.avatar,\n status: normalizedStatus,\n });\n return existingBatch.toastId;\n }\n }\n // Create new toast (either not batched, or first in a new batch)\n const id = generateId();\n const duration = options.duration ?? DEFAULT_DURATIONS.notification;\n const newToast = {\n id,\n type: 'notification',\n notification: {\n name: options.name,\n displayName: options.displayName,\n event: options.event,\n status: options.status,\n avatar: options.avatar,\n message: options.message,\n performerMessage: options.performerMessage,\n eventId: options.eventId,\n batchCount: genericBatch ? 1 : undefined,\n batchTotal: genericBatch ? (genericBatch.amount ?? 1) : undefined,\n },\n duration,\n dismissible: options.dismissible ?? true,\n onClick: options.onClick,\n createdAt: Date.now(),\n ...(genericBatch\n ? { batchCount: 1, batchTotal: genericBatch.amount ?? 1 }\n : {}),\n };\n toasts = [newToast, ...toasts];\n // Announce notification toasts politely (brief 02-C).\n announceToMatchingRegion('polite', [options.name || options.event, options.status, options.message]\n .filter(Boolean)\n .join('. '));\n // Trigger haptic feedback for notification toasts\n triggerHaptic('light');\n if (duration !== Infinity) {\n scheduleRemoval(id, duration);\n }\n // Opt-in batching: open the (non-sliding) batch window for this key.\n if (genericBatch) {\n registerBatch(GENERIC_BATCH_PREFIX + genericBatch.key, id, genericBatch.amount ?? 1, genericBatch.window ?? BATCH_WINDOW_MS);\n }\n // Show desktop notification\n const desktopTitle = options.name || options.event;\n const desktopBody = options.name\n ? `${options.status} · ${options.event}`\n : options.message || options.status;\n showDesktopNotification(desktopTitle, {\n body: desktopBody,\n icon: options.avatar,\n status: normalizedStatus,\n });\n // Trim overflow — keep only MAX_TOASTS\n if (toasts.length > MAX_TOASTS) {\n const overflow = toasts.slice(MAX_TOASTS);\n for (const t of overflow) {\n const timer = timers.get(t.id);\n if (timer) {\n clearTimeout(timer);\n timers.delete(t.id);\n }\n }\n toasts = toasts.slice(0, MAX_TOASTS);\n }\n return id;\n}\nfunction scheduleRemoval(id, delay) {\n const existingTimer = timers.get(id);\n if (existingTimer) {\n clearTimeout(existingTimer);\n }\n const timer = setTimeout(() => {\n dismiss(id);\n }, delay);\n timers.set(id, timer);\n}\nfunction dismiss(id) {\n const timer = timers.get(id);\n if (timer) {\n clearTimeout(timer);\n timers.delete(id);\n }\n toasts = toasts.filter(t => t.id !== id);\n}\nfunction dismissAll() {\n timers.forEach(timer => clearTimeout(timer));\n timers.clear();\n toasts = [];\n}\nfunction pause(id) {\n const toast = toasts.find(t => t.id === id);\n if (!toast || toast.duration === Infinity)\n return;\n const timer = timers.get(id);\n if (timer) {\n clearTimeout(timer);\n timers.delete(id);\n }\n const elapsed = Date.now() - toast.createdAt;\n const remaining = Math.max(0, toast.duration - elapsed);\n toasts = toasts.map(t => t.id === id ? { ...t, pausedAt: Date.now(), remainingTime: remaining } : t);\n}\nfunction resume(id) {\n const foundToast = toasts.find(t => t.id === id);\n if (!foundToast || !foundToast.pausedAt || foundToast.duration === Infinity)\n return;\n const remaining = foundToast.remainingTime ?? foundToast.duration;\n toasts = toasts.map(t => t.id === id\n ? {\n ...t,\n pausedAt: undefined,\n remainingTime: undefined,\n createdAt: Date.now() - (foundToast.duration - remaining),\n }\n : t);\n if (remaining > 0) {\n scheduleRemoval(id, remaining);\n }\n else {\n dismiss(id);\n }\n}\n// Main toast function and methods\nfunction toast(title, options) {\n return addToast('info', title, options);\n}\ntoast.success = (title, options) => addToast('success', title, options);\ntoast.error = (title, options) => addToast('error', title, options);\ntoast.warning = (title, options) => addToast('warning', title, options);\ntoast.info = (title, options) => addToast('info', title, options);\ntoast.loading = (title, options) => addToast('loading', title, { ...options, duration: Infinity });\ntoast.notification = (options) => addNotificationToast(options);\ntoast.dismiss = dismiss;\ntoast.dismissAll = dismissAll;\ntoast.pause = pause;\ntoast.resume = resume;\n// Desktop notification controls\ntoast.requestDesktopPermission = requestDesktopPermission;\ntoast.getDesktopPermission = () => desktopNotificationPermission;\ntoast.isDesktopEnabled = () => desktopNotificationsEnabled;\ntoast.setPushPreferenceChecker = setPushPreferenceChecker;\ntoast.STATUS_TO_PREFERENCE = STATUS_TO_PREFERENCE;\n// Composable: fire a desktop notification WITHOUT creating a toast (no-op\n// unless permission is granted; respects the push-preference checker when a\n// status is supplied). Lets app wrappers compose their own notify flows.\ntoast.desktopNotify = (title, options) => showDesktopNotification(title, options);\ntoast.promise = async (promise, options) => {\n const id = toast.loading(options.loading);\n try {\n const result = await promise;\n dismiss(id);\n toast.success(options.success);\n return result;\n }\n catch (err) {\n dismiss(id);\n toast.error(options.error);\n throw err;\n }\n};\n// Getter for reactive access to toasts\nexport function getToasts() {\n return toasts;\n}\nexport { toast };\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: feeCents / 100 in calculateServiceFee is arithmetic,\r\n// not display. Produces a dollar-magnitude number used in the fee formula\r\n// `price * (feePercentage / 100) + feeCents / 100`. Per Atlas\r\n// utility-currency convergence record; the no-handrolled-currency\r\n// ESLint rule's divideByHundred detector treats variable-name `Cents`\r\n// as drift but here it's real math.\r\n\r\n// Premium Ticket Experience — Constants & Defaults\r\n// Used by all design variants. DO NOT MODIFY per-variant.\r\n\r\n// @raw-intl-datetimeformat-escape: formatSmartSalesDate is a date-formatter\r\n// implementation (smart relative-date thresholds → \"on Friday at 8 PM\" / \"on Mar 15\").\r\n// The weekday-long (\"Friday\") and month-short+day (\"Mar 15\") fragments are interpolated\r\n// into smart-threshold strings; SC formatDate/DATE_FORMATS has no preset that emits a\r\n// bare weekday or a bare month+day, so it cannot reproduce these byte-identical fragments.\r\n// This file is the canonical formatter module for public-calendar-flow (it wraps + re-exports\r\n// scFormatDate/scFormatTime), not a call site.\r\n\r\nimport type {\r\n EventData,\r\n TicketTypeData,\r\n ScarcityLevel,\r\n CtaConfig,\r\n PromoCodeState,\r\n} from './types';\r\nimport { computeCtaState } from '$lib/api';\r\nimport type { Cents } from '@getmicdrop/svelte-components';\r\nimport {\r\n formatTime as scFormatTime,\r\n roundCurrency,\r\n} from '@getmicdrop/svelte-components';\r\nimport {\r\n formatFullDate,\r\n formatFullDateCompact,\r\n formatEventTime,\r\n formatTimeRange,\r\n formatDoorsTime,\r\n formatSalesDate,\r\n formatBrowseDate,\r\n formatEventDate,\r\n formatCleanTimeRange,\r\n getDateParts,\r\n} from '$lib/utils/datetime.js';\r\n\r\n// --- Re-export production date/time formatters (timezone-aware) ---\r\nexport {\r\n formatFullDate,\r\n formatFullDateCompact,\r\n formatEventTime,\r\n formatTimeRange,\r\n formatDoorsTime,\r\n formatSalesDate,\r\n formatBrowseDate,\r\n formatEventDate,\r\n formatCleanTimeRange,\r\n getDateParts,\r\n};\r\n\r\n// --- Scarcity Badge Rules ---\r\nexport const SCARCITY_THRESHOLDS = {\r\n SOLD_OUT: 0,\r\n URGENT: 0.1, // ≤10% remaining → urgent (\"Only X left\")\r\n LOW: 0.2, // ≤20% remaining (80%+ sold) → \"Selling fast\"\r\n MAX_EXACT_COUNT: 20, // Don't show exact count above 20\r\n} as const;\r\n\r\nexport function getScarcityLevel(\r\n remaining: number,\r\n total: number\r\n): ScarcityLevel {\r\n if (total === 0) return 'none';\r\n if (remaining <= 0) return 'sold_out';\r\n const pct = remaining / total;\r\n if (pct <= SCARCITY_THRESHOLDS.URGENT) return 'urgent';\r\n if (pct <= SCARCITY_THRESHOLDS.LOW) return 'low';\r\n return 'none';\r\n}\r\n\r\nexport function getScarcityText(\r\n level: ScarcityLevel,\r\n remaining: number,\r\n context: 'browse' | 'event' | 'ticket',\r\n options?: { isFree?: boolean }\r\n): string | null {\r\n if (level === 'none') return null;\r\n if (level === 'sold_out') return 'Sold out';\r\n // No scarcity badges on free events/tickets — nothing to lose = no urgency\r\n if (options?.isFree) return null;\r\n if (\r\n level === 'urgent' &&\r\n context !== 'browse' &&\r\n remaining <= SCARCITY_THRESHOLDS.MAX_EXACT_COUNT\r\n ) {\r\n return `Only ${remaining} left`;\r\n }\r\n if (level === 'low' || level === 'urgent') return 'Selling fast';\r\n return null;\r\n}\r\n\r\n// --- CTA Rules ---\r\n// Delegates to production computeCtaState() but adapts our EventData shape.\r\n// Components keep calling getCtaConfig(event) — internal logic is production-aligned.\r\nexport function getCtaConfig(event: EventData): CtaConfig {\r\n // Map our EventData tickets to the shape computeCtaState expects.\r\n // `isHidden` flows through so computeCtaState can apply the hidden-only\r\n // \"Sales ended\" → \"Get tickets\" override (MIC-1039).\r\n const apiTickets = event.tickets.map(t => ({\r\n salesBegin: t.salesStartDate,\r\n salesEnd: t.salesEndDate,\r\n remainingCapacity: t.quantityAvailable,\r\n totalCapacity: t.quantityTotal,\r\n soldOut: t.status === 'sold_out',\r\n salesChannel: 1, // All our tickets are online (not \"at the door only\")\r\n isHidden: t.isHidden,\r\n }));\r\n\r\n // Handle statuses that computeCtaState doesn't compute from tickets\r\n if (event.status === 'cancelled') {\r\n return {\r\n text: 'Cancelled',\r\n disabled: true,\r\n reason: 'This event has been cancelled',\r\n };\r\n }\r\n if (event.status === 'past') {\r\n return {\r\n text: 'Event ended',\r\n disabled: true,\r\n reason: 'This event has ended',\r\n };\r\n }\r\n\r\n const result = computeCtaState(\r\n { startDateTime: event.startDateTime, endDateTime: event.endDateTime },\r\n apiTickets,\r\n {\r\n cancelled: false,\r\n isRegistration: event.isRegistrationEvent,\r\n waitlistEnabled: false,\r\n }\r\n );\r\n\r\n return {\r\n text: result.text,\r\n disabled: result.disabled,\r\n reason: result.disabled ? toFriendlyReason(result.reason) : undefined,\r\n };\r\n}\r\n\r\n/**\r\n * computeCtaState() returns machine-enum reasons ('event_past', 'sold_out',\r\n * 'coming_soon', etc) intended for branching, not display. The BookingWidget\r\n * and MobileCta render `reason` directly as supplementary copy below the\r\n * button text — which leaks raw enum strings to users.\r\n *\r\n * Map each enum to a friendly sentence, OR return undefined when the button\r\n * `text` already says everything (\"Sales ended\" / \"Sold out\" — no need for\r\n * a second line repeating it).\r\n */\r\nfunction toFriendlyReason(reason: string | undefined): string | undefined {\r\n if (!reason) return undefined;\r\n switch (reason) {\r\n case 'cancelled':\r\n return 'This event has been cancelled.';\r\n case 'event_past':\r\n return 'This event has already happened.';\r\n case 'no_tickets':\r\n return 'There are no tickets configured for this event.';\r\n case 'coming_soon':\r\n return undefined; // text already shows \"On sale <date>\"\r\n case 'sold_out':\r\n return undefined; // text already shows \"Sold out\"\r\n case 'sold_out_waitlist':\r\n return 'All tickets are claimed. Join the waitlist to be notified.';\r\n case 'sales_ended':\r\n return undefined; // text already shows \"Sales ended\"\r\n default:\r\n return undefined; // unknown enum — never leak it\r\n }\r\n}\r\n\r\n// --- Price Display Rules (from DECISIONS.md #1) ---\r\nexport function getPriceDisplay(event: EventData): string | null {\r\n const visibleTickets = event.tickets.filter(\r\n t => t.status === 'on_sale' && !t.isHidden\r\n );\r\n if (visibleTickets.length === 0) return null;\r\n\r\n const hasDonation = visibleTickets.some(\r\n t => t.isDonation || t.ticketType === 2\r\n );\r\n const paidTickets = visibleTickets.filter(\r\n t => !t.isDonation && t.ticketType !== 2\r\n );\r\n const hasFree = paidTickets.some(t => t.price === 0);\r\n const pricedTickets = paidTickets.filter(t => t.price > 0);\r\n\r\n // Only donation tickets → \"Donation\"\r\n if (pricedTickets.length === 0 && !hasFree && hasDonation) return 'Donation';\r\n\r\n // Only free tickets (no paid, no donation) → null (BookingWidget shows \"Free\" via isFreeEvent)\r\n if (pricedTickets.length === 0 && hasFree) return null;\r\n\r\n const minPrice = Math.min(...pricedTickets.map(t => t.price));\r\n\r\n // Free + paid → \"Free\" (free dominates the display)\r\n if (hasFree) return null;\r\n\r\n // Donation + paid → \"From $X\" (donation is an alternative option)\r\n if (hasDonation) return `From $${minPrice}`;\r\n\r\n // Single paid ticket type → \"$X\"\r\n if (pricedTickets.length === 1) return `$${minPrice}`;\r\n\r\n // Multiple paid ticket types → \"From $X\"\r\n return `From $${minPrice}`;\r\n}\r\n\r\n// --- Fee Calculation ---\r\nexport function calculateFee(\r\n price: number,\r\n feePercentage: number,\r\n feeCents: Cents\r\n): number {\r\n return roundCurrency(price * (feePercentage / 100) + feeCents / 100);\r\n}\r\n\r\nexport function calculateTax(amount: number, taxPercentage: number): number {\r\n return roundCurrency(amount * (taxPercentage / 100));\r\n}\r\n\r\n// --- Date Formatting ---\r\n// PREFERRED: Use the timezone-aware production formatters re-exported above:\r\n// formatBrowseDate(iso, tz) → \"Fri, Feb 20\"\r\n// formatFullDateCompact(iso, tz) → \"Friday, February 20\"\r\n// formatEventTime(iso, tz) → \"8:00 PM\"\r\n// formatTimeRange(start, end, tz) → \"8:00 PM - 10:00 PM\"\r\n// formatDoorsTime(doors, tz) → \"7:00 PM\"\r\n\r\n// --- Animation Durations ---\r\nexport const DURATION = {\r\n instant: 100,\r\n micro: 150,\r\n fast: 200,\r\n normal: 300,\r\n slow: 400,\r\n elaborate: 600,\r\n} as const;\r\n\r\n// --- Smart Sales Date Formatting ---\r\n// Ported from production TicketBlock.svelte — smart thresholds for human-readable dates\r\nexport function formatSmartSalesDate(dateStr: string): string {\r\n if (!dateStr) return '';\r\n const date = new Date(dateStr);\r\n const now = new Date();\r\n const diffMs = date.getTime() - now.getTime();\r\n const diffMinutes = Math.round(diffMs / (1000 * 60));\r\n const diffHours = diffMs / (1000 * 60 * 60);\r\n\r\n // < 1 hour: \"in X minutes\"\r\n if (diffMinutes > 0 && diffMinutes < 60) {\r\n const mins = Math.max(1, diffMinutes);\r\n return `in ${mins} minute${mins !== 1 ? 's' : ''}`;\r\n }\r\n\r\n // 1-24 hours: \"in X hours\"\r\n if (diffHours > 0 && diffHours < 24) {\r\n const hours = Math.max(1, Math.round(diffHours));\r\n return `in ${hours} hour${hours !== 1 ? 's' : ''}`;\r\n }\r\n\r\n // Tomorrow: \"tomorrow at X PM\"\r\n const tomorrow = new Date(now);\r\n tomorrow.setDate(tomorrow.getDate() + 1);\r\n if (date.toDateString() === tomorrow.toDateString()) {\r\n const timeStr = scFormatTime(\r\n date,\r\n { hour: 'numeric', minute: '2-digit' },\r\n 'en-US'\r\n );\r\n return `tomorrow at ${timeStr}`;\r\n }\r\n\r\n // 2-7 days: \"on Friday at X PM\"\r\n const diffDays = diffMs / (1000 * 60 * 60 * 24);\r\n if (diffDays > 0 && diffDays <= 7) {\r\n // @datetime-escape: composite string \"on {dayname} at {time}\" in formatSmartSalesDate — no <Time> preset, function returns a JS string\r\n const dayName = new Intl.DateTimeFormat('en-US', {\r\n weekday: 'long',\r\n }).format(date);\r\n const timeStr = scFormatTime(\r\n date,\r\n { hour: 'numeric', minute: '2-digit' },\r\n 'en-US'\r\n );\r\n return `on ${dayName} at ${timeStr}`;\r\n }\r\n\r\n // > 7 days: \"on Mar 15\"\r\n // @datetime-escape: composite string \"on {date}\" in formatSmartSalesDate — no <Time> preset, function returns a JS string\r\n const dateFormatted = new Intl.DateTimeFormat('en-US', {\r\n month: 'short',\r\n day: 'numeric',\r\n }).format(date);\r\n return `on ${dateFormatted}`;\r\n}\r\n\r\n// --- Promo Code State Factory ---\r\nexport function createDefaultPromoState(): PromoCodeState {\r\n return {\r\n code: '',\r\n isApplied: false,\r\n isValidating: false,\r\n error: '',\r\n hint: '',\r\n discountAmount: 0,\r\n discountType: '',\r\n discountTicketIds: [],\r\n revealHiddenTickets: false,\r\n revealTicketIds: [],\r\n };\r\n}\r\n\r\n// --- Ticket helpers ---\r\nexport function getAvailableTickets(\r\n tickets: TicketTypeData[]\r\n): TicketTypeData[] {\r\n return tickets.filter(t => !t.isHidden && t.status === 'on_sale');\r\n}\r\n","<!-- @style-escape: widget-intentional. -->\r\n<script lang=\"ts\">\r\n\timport type { USD } from '@getmicdrop/svelte-components';\r\n\timport { Text, formatCurrency } from '@getmicdrop/svelte-components';\r\n\timport type { OrderTotals } from '$lib/public-calendar-flow/types';\r\n\timport { DURATION } from '$lib/public-calendar-flow/defaults';\r\n\r\n\tinterface Props {\r\n\t\torderTotals: OrderTotals;\r\n\t\tlabels?: Record<string, string>;\r\n\t\t/**\r\n\t\t * Which section to render.\r\n\t\t * - `full` (default): ticket lines + totals stacked together.\r\n\t\t * - `lines` : ticket-line items only. Use when the parent scrolls\r\n\t\t * this section in a flex column.\r\n\t\t * - `totals` : subtotal/fees/taxes/promo/total only. Use in a fixed\r\n\t\t * footer alongside the action button.\r\n\t\t *\r\n\t\t * Splitting via `lines` / `totals` lets the sticky sidebars scroll\r\n\t\t * just the variable-length ticket list while keeping the summary\r\n\t\t * block and CTA always visible.\r\n\t\t */\r\n\t\tmode?: 'full' | 'lines' | 'totals';\r\n\t}\r\n\r\n\tlet { orderTotals, labels = {}, mode = 'full' }: Props = $props();\r\n</script>\r\n\r\n<div\r\n\tclass=\"space-y-2\"\r\n\tstyle=\"transition-duration: {DURATION.normal}ms;\"\r\n>\r\n\t{#if mode !== 'totals'}\r\n\t\t<!-- Line items -->\r\n\t\t{#each orderTotals.lines as line (line.ticketId)}\r\n\t\t\t<div class=\"flex justify-between\">\r\n\t\t\t\t<Text size=\"sm\">{line.quantity}x {line.ticketName}</Text>\r\n\t\t\t\t<Text size=\"sm\">{formatCurrency(line.subtotal as USD)}</Text>\r\n\t\t\t</div>\r\n\t\t{/each}\r\n\t{/if}\r\n\r\n\t{#if mode !== 'lines'}\r\n\t\t<!-- Subtotal. In `totals` mode the parent supplies the separator above\r\n\t\t this block (BookingWidget wraps the totals block in border-t); a\r\n\t\t second internal border would render as a double line. Only show\r\n\t\t the internal border-t in `full` mode where lines + totals stack\r\n\t\t in one container with no external separator. -->\r\n\t\t{#if mode === 'full' && orderTotals.lines.length > 1}\r\n\t\t\t<div class=\"flex justify-between pt-1 border-t border-default\">\r\n\t\t\t\t<Text size=\"sm\" color=\"muted\">{labels.subtotal || 'Subtotal'}</Text>\r\n\t\t\t\t<Text size=\"sm\" color=\"muted\">{formatCurrency(orderTotals.subtotal as USD)}</Text>\r\n\t\t\t</div>\r\n\t\t{:else if orderTotals.lines.length > 1}\r\n\t\t\t<div class=\"flex justify-between\">\r\n\t\t\t\t<Text size=\"sm\" color=\"muted\">{labels.subtotal || 'Subtotal'}</Text>\r\n\t\t\t\t<Text size=\"sm\" color=\"muted\">{formatCurrency(orderTotals.subtotal as USD)}</Text>\r\n\t\t\t</div>\r\n\t\t{/if}\r\n\r\n\t\t<!-- Fees -->\r\n\t\t{#if orderTotals.fees > 0}\r\n\t\t\t<div class=\"flex justify-between\">\r\n\t\t\t\t<Text size=\"sm\" color=\"muted\">{labels.serviceFees || 'Service fees'}</Text>\r\n\t\t\t\t<Text size=\"sm\" color=\"muted\">{formatCurrency(orderTotals.fees as USD)}</Text>\r\n\t\t\t</div>\r\n\t\t{/if}\r\n\r\n\t\t<!-- Taxes -->\r\n\t\t{#if orderTotals.taxes > 0}\r\n\t\t\t<div class=\"flex justify-between\">\r\n\t\t\t\t<Text size=\"sm\" color=\"muted\">{labels.taxes || 'Taxes'}</Text>\r\n\t\t\t\t<Text size=\"sm\" color=\"muted\">{formatCurrency(orderTotals.taxes as USD)}</Text>\r\n\t\t\t</div>\r\n\t\t{/if}\r\n\r\n\t\t<!-- Promo discount -->\r\n\t\t{#if orderTotals.promoDiscount > 0}\r\n\t\t\t<div class=\"flex justify-between\">\r\n\t\t\t\t<Text size=\"sm\" color=\"success\">{labels.promoDiscount || 'Promo discount'}</Text>\r\n\t\t\t\t<Text size=\"sm\" color=\"success\">-{formatCurrency(orderTotals.promoDiscount as USD)}</Text>\r\n\t\t\t</div>\r\n\t\t{/if}\r\n\r\n\t\t<!-- Gift card — deduction from the amount due; `total` already reflects it -->\r\n\t\t{#if orderTotals.giftCardAmount > 0}\r\n\t\t\t<div class=\"flex justify-between\">\r\n\t\t\t\t<Text size=\"sm\" color=\"success\">{labels.giftCard || 'Gift card'}</Text>\r\n\t\t\t\t<Text size=\"sm\" color=\"success\">-{formatCurrency(orderTotals.giftCardAmount as USD)}</Text>\r\n\t\t\t</div>\r\n\t\t{/if}\r\n\r\n\t\t<!-- Total -->\r\n\t\t<div class=\"flex justify-between pt-2 border-t border-default\">\r\n\t\t\t<Text size=\"base\" class=\"font-semibold\">{labels.total || 'Total'}</Text>\r\n\t\t\t<Text size=\"base\" class=\"font-semibold\">{formatCurrency(orderTotals.total as USD)}</Text>\r\n\t\t</div>\r\n\t{/if}\r\n</div>","/**\r\n * Default English labels for public-calendar-flow components.\r\n * Consumers override via the `labels` prop on any *Main component.\r\n *\r\n * Label keys use flat camelCase. For composed strings (e.g. aria-labels),\r\n * components build them from atomic label parts.\r\n */\r\nexport const defaultLabels = {\r\n // ── Ticket quantity controls ──────────────────────────────────────\r\n decrease: 'Decrease',\r\n increase: 'Increase',\r\n quantity: 'quantity',\r\n\r\n // ── Ticket states ─────────────────────────────────────────────────\r\n soldOut: 'Sold out',\r\n free: 'Free',\r\n unavailable: 'Unavailable',\r\n eventSoldOut: 'This event is sold out',\r\n\r\n // ── Price display ─────────────────────────────────────────────────\r\n includesFees: 'Includes',\r\n fees: 'fees',\r\n fee: 'fee',\r\n\r\n // ── Checkout CTAs ─────────────────────────────────────────────────\r\n checkout: 'Checkout',\r\n getTickets: 'Get tickets',\r\n selectTickets: 'Select tickets',\r\n selectTicketsBelow: 'Select tickets below',\r\n selectTicketsToContinue: 'Select tickets to continue',\r\n reserveASpot: 'Reserve a spot',\r\n\r\n // ── Password modal / gate ─────────────────────────────────────────\r\n enterPassword: 'Enter Password',\r\n enterEventPassword: 'Enter event password',\r\n passwordRequired: 'Password Required',\r\n passwordRequiredShort: 'Password required',\r\n eventRequiresPassword: 'This event requires a password to purchase tickets.',\r\n eventRequiresPasswordAccess: 'This event requires a password to access tickets.',\r\n enterEventPasswordToView: 'Enter the event password to view tickets.',\r\n enterEventPasswordToViewAvailable: 'Enter the event password to view available tickets.',\r\n eventPassword: 'Event password',\r\n password: 'Password',\r\n pleaseEnterPassword: 'Please enter the event password',\r\n pleaseEnterAPassword: 'Please enter a password',\r\n eventRequiresPasswordShort: 'This event requires a password',\r\n unlockTickets: 'Unlock tickets',\r\n cancel: 'Cancel',\r\n continue: 'Continue',\r\n submit: 'Submit',\r\n\r\n // ── Order summary ─────────────────────────────────────────────────\r\n subtotal: 'Subtotal',\r\n serviceFees: 'Service fees',\r\n taxes: 'Taxes',\r\n tax: 'Tax',\r\n promoDiscount: 'Promo discount',\r\n giftCard: 'Gift card',\r\n total: 'Total',\r\n orderSummary: 'Order summary',\r\n yourOrder: 'Your Order',\r\n\r\n // ── Ticket counts (used in CTA buttons) ───────────────────────────\r\n ticket: 'ticket',\r\n tickets: 'tickets',\r\n\r\n // ── Checkout form (public-calendar-flow) ───────────────────────────────\r\n yourDetails: 'Your details',\r\n attendeeDetails: 'Attendee details',\r\n attendee: 'Attendee',\r\n sameAsPurchaser: 'Same as purchaser',\r\n payment: 'Payment',\r\n noPaymentRequired: 'No payment required',\r\n freeEventMessage: 'This is a free event. Complete the form above to reserve your spot.',\r\n firstName: 'First name',\r\n lastName: 'Last name',\r\n emailAddress: 'Email address',\r\n email: 'Email',\r\n emailPlaceholder: 'you@example.com',\r\n attendeeEmailPlaceholder: 'attendee@example.com',\r\n cardNumber: 'Card number',\r\n cardPlaceholder: '4242 4242 4242 4242',\r\n expiryDate: 'Expiry date',\r\n expiryPlaceholder: 'MM / YY',\r\n cvc: 'CVC',\r\n cvcPlaceholder: '123',\r\n keepMeUpdated: 'Keep me updated about this event and similar events',\r\n agreeToTerms: 'I agree to the terms and conditions',\r\n goBack: 'Go back',\r\n loadingPaymentForm: 'Loading secure payment form...',\r\n\r\n // ── Checkout form validation ──────────────────────────────────────\r\n firstNameRequired: 'First name is required',\r\n lastNameRequired: 'Last name is required',\r\n emailRequired: 'Email is required',\r\n emailInvalid: 'Enter a valid email address',\r\n emailInvalidShort: 'Enter a valid email',\r\n cardNumberRequired: 'Card number is required',\r\n expiryRequired: 'Expiry date is required',\r\n cvcRequired: 'CVC is required',\r\n mustAgreeTerms: 'You must agree to the terms',\r\n\r\n // ── Place order CTAs ──────────────────────────────────────────────\r\n placeOrder: 'Place order',\r\n completeOrder: 'Complete order',\r\n placingOrder: 'Placing order...',\r\n\r\n // ── Promo code ────────────────────────────────────────────────────\r\n havePromoCode: 'Have a promo code?',\r\n promoCode: 'Promo code',\r\n enterCode: 'Enter code',\r\n apply: 'Apply',\r\n remove: 'Remove',\r\n removePromoCode: 'Remove promo code',\r\n applyPromoCode: 'Apply promo code',\r\n checkingPromoCode: 'Checking promo code',\r\n checking: 'Checking...',\r\n promoApplied: 'Promo applied',\r\n\r\n // ── Navigation tabs (app-segments) ────────────────────────────────\r\n about: 'About',\r\n ticketsTab: 'Tickets',\r\n eventSections: 'Event sections',\r\n\r\n // ── Event details ─────────────────────────────────────────────────\r\n eventDetails: 'Event details',\r\n performers: 'Performers',\r\n faq: 'FAQ',\r\n frequentlyAskedQuestions: 'Frequently asked questions',\r\n doors: 'Doors',\r\n doorsOpenAt: 'Doors open at',\r\n\r\n // ── Series occurrences ────────────────────────────────────────────\r\n upcomingDates: 'Upcoming Dates',\r\n\r\n // ── Close / dismiss ───────────────────────────────────────────────\r\n closeTicketSelection: 'Close ticket selection',\r\n closeTicketSelector: 'Close ticket selector',\r\n\r\n // ── Accessibility ─────────────────────────────────────────────────\r\n ticketSelection: 'ticket selection',\r\n ticketsSelected: 'tickets selected',\r\n view: 'View',\r\n\r\n // ── Success page (public-calendar-flow) ────────────────────────────────\r\n youreIn: \"You're in!\",\r\n youreRegistered: \"You're registered!\",\r\n confirmed: 'confirmed',\r\n viewTickets: 'View tickets',\r\n order: 'Order',\r\n confirmationSentTo: 'Confirmation sent to',\r\n addToCalendar: 'Add to Calendar',\r\n googleCalendar: 'Google Calendar',\r\n appleCalendar: 'Apple Calendar (.ics)',\r\n share: 'Share',\r\n map: 'Map',\r\n backToEvent: 'Back to event',\r\n needHelp: 'Need help?',\r\n linkCopied: 'Link copied to clipboard!',\r\n copyLink: 'Copy this link:',\r\n addToAppleWallet: 'Add to Apple Wallet',\r\n saveToGoogleWallet: 'Save to Google Wallet',\r\n\r\n // ── Session timer / expiration modals ─────────────────────────────\r\n sessionExpiringSoon: 'Session expiring soon',\r\n sessionExpiringSoonMessage: 'Your checkout session will expire in {time}. Your selected tickets will be released back to inventory.',\r\n extensionsRemaining: '{count} extension{plural} remaining',\r\n continueCheckout: 'Continue checkout',\r\n okIllHurry: \"OK, I'll hurry\",\r\n maxExtensionsReached: 'Maximum extensions reached',\r\n wantToKeepTickets: 'Want to keep your tickets?',\r\n sessionExpiredMessage: 'Your checkout session has expired and your selected tickets have been released.',\r\n sessionExpired: 'Session expired',\r\n reservationTimedOut: 'Your reservation has timed out. Please select your tickets again to continue.',\r\n keepMyTickets: 'Keep my tickets',\r\n selectTicketsAgain: 'Select tickets again',\r\n extendingSession: 'Extending...',\r\n\r\n // ── MobileCta ─────────────────────────────────────────────────────\r\n selectTicketsLabel: 'Select Tickets',\r\n};\r\n\r\n/**\r\n * Merge user-provided label overrides with the defaults.\r\n * Shallow merge — any key provided by the consumer wins.\r\n *\r\n * @param {Record<string, string>} [userLabels] - Partial label overrides\r\n * @returns {Record<string, string>} Merged labels object\r\n */\r\nexport function mergeLabels(userLabels = {}) {\r\n return { ...defaultLabels, ...userLabels };\r\n}\r\n"],"names":["logger","createLogger","BATCH_WINDOW_MS","activeBatches","SvelteMap","GENERIC_BATCH_PREFIX","registerBatch","mapKey","toastId","amount","windowMs","timeout","bumpBatch","batch","applyBatchMerge","ctx","update","duration","toasts","t","scheduleRemoval","desktopNotificationsEnabled","$","desktopNotificationPermission","requestDesktopPermission","permission","pushPreferenceChecker","STATUS_TO_PREFERENCE","setPushPreferenceChecker","checker","normalizeStatus","status","isPushEnabledForStatus","showDesktopNotification","title","options","e","fireDesktopNotificationOption","dn","cfg","DEFAULT_DURATIONS","MAX_TOASTS","timers","generateId","TOAST_HAPTIC_STYLES","LIVE_REGION_IDS","getToastLiveRegion","politeness","id","region","announceToMatchingRegion","message","announceToast","type","description","addToast","existingBatch","dismissible","newToast","hapticStyle","triggerHaptic","overflow","timer","addNotificationToast","normalizedStatus","genericBatch","desktopTitle","desktopBody","delay","existingTimer","dismiss","dismissAll","pause","toast","elapsed","remaining","resume","foundToast","promise","result","err","computeCtaState","event","tickets","cancelled","isRegistration","waitlistEnabled","now","eventEnd","hiddenAvailable","isHiddenTicketPurchasable","publicTickets","hasPurchasable","allSoldOut","allComingSoon","earliestSalesStart","ticket","salesBegin","salesEnd","isSoldOut","isScheduled","hasSalesEnded","startDate","diffMs","diffHours","diffDays","dateText","formatTime","formatDateRaw","d","SCARCITY_THRESHOLDS","getScarcityLevel","total","pct","getScarcityText","level","context","getCtaConfig","apiTickets","toFriendlyReason","reason","getPriceDisplay","visibleTickets","hasDonation","paidTickets","hasFree","pricedTickets","minPrice","calculateTax","taxPercentage","roundCurrency","DURATION","formatSmartSalesDate","dateStr","date","diffMinutes","mins","hours","tomorrow","scFormatTime","dayName","timeStr","createDefaultPromoState","labels","$$props","mode","div","root_3","node_1","line","div_1","root","Text","node_2","text","node_3","formatCurrency","$$render","consequent","div_2","root_1","node_6","node_7","$0","text_3","div_3","node_8","node_9","text_5","consequent_1","consequent_2","div_4","node_11","node_12","text_7","consequent_3","div_5","node_14","node_15","text_9","consequent_4","div_6","node_17","node_18","text_11","consequent_5","div_7","node_20","node_21","text_13","consequent_6","div_8","node_19","node_22","node_23","text_15","consequent_7","defaultLabels","mergeLabels","userLabels"],"mappings":"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","x_google_ignoreList":[0]}
|
package/dist/seo/seo.cjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});function P(){return"America/Los_Angeles"}function U(i){return i.replace(/</g,"\\u003c").replace(/>/g,"\\u003e").replace(/&/g,"\\u0026")}function y(i,t){if(!i)return null;const e=new Date(i);if(isNaN(e.getTime()))return null;const s=new Intl.DateTimeFormat("en-US",{timeZone:t,year:"numeric",month:"2-digit",day:"2-digit",hour:"2-digit",minute:"2-digit",second:"2-digit",hour12:!1,timeZoneName:"shortOffset"}).formatToParts(e),r=u=>s.find(p=>p.type===u)?.value||"",o=r("year"),l=r("month"),c=r("day"),a=r("hour"),d=r("minute"),m=r("second"),h=r("timeZoneName");let g="+00:00";if(h){const u=h.match(/GMT([+-])(\d+)(?::(\d+))?/);if(u){const p=u[1],C=u[2].padStart(2,"0"),D=u[3]||"00";g=`${p}${C}:${D}`}}return`${o}-${l}-${c}T${a}:${d}:${m}${g}`}function E(i){if(!i)return null;const t=i.split(",").map(s=>s.trim());if(t.length<2)return{"@type":"PostalAddress",streetAddress:i,addressCountry:"US"};const n=t[t.length-1].match(/^([A-Z]{2})\s+(\d{5}(?:-\d{4})?)$/);return n&&t.length>=3?{"@type":"PostalAddress",streetAddress:t.slice(0,-2).join(", "),addressLocality:t[t.length-2],addressRegion:n[1],postalCode:n[2],addressCountry:"US"}:{"@type":"PostalAddress",streetAddress:t[0],addressLocality:t.slice(1).join(", "),addressCountry:"US"}}function S(i,t){if(i&&(i.city||i.state||i.postalCode)){const e={"@type":"PostalAddress"};return i.address&&(e.streetAddress=i.address),i.city&&(e.addressLocality=i.city),i.state&&(e.addressRegion=i.state),i.postalCode&&(e.postalCode=i.postalCode),e.addressCountry=i.country||"US",e}return E(i&&i.address||t||null)}function A(i){if(!i)return"https://schema.org/EventScheduled";const t=i.toLowerCase();return t==="cancelled"||t==="canceled"?"https://schema.org/EventCancelled":t==="postponed"?"https://schema.org/EventPostponed":t==="rescheduled"?"https://schema.org/EventRescheduled":"https://schema.org/EventScheduled"}function _(i){if((i.remainingCapacity??i.quantityRemaining??i.quantity)===0||i.soldOut)return"https://schema.org/SoldOut";const e=new Date,n=i.salesBegin||i.salesStart||i.saleBegin||i.onSaleStart,s=i.salesEnd||i.saleEnd||i.onSaleEnd;return n&&new Date(n)>e?"https://schema.org/PreOrder":s&&new Date(s)<e?"https://schema.org/SoldOut":"https://schema.org/InStock"}function N(i,t){if(!i||i.length===0)return null;const e=i.filter(l=>{const c=l.visibility??0,a=c===0||c===1,d=l.salesChannel!==2;return a&&d});if(e.length===0)return null;const n=e.map(l=>parseFloat(String(l.price??0))).filter(l=>!isNaN(l));if(n.length===0)return null;const s=Math.min(...n),r=Math.max(...n);if(s!==r)return{"@type":"AggregateOffer",lowPrice:s,highPrice:r,priceCurrency:"USD",availability:_(e[0]),url:t||void 0,validFrom:e[0]?.salesBegin||e[0]?.salesStart||void 0};const o=e[0];return{"@type":"Offer",price:s,priceCurrency:"USD",availability:_(o),url:t||void 0,validFrom:o?.salesBegin||o?.salesStart||void 0}}function L(i){if(!i||i.length===0)return null;const t=i.filter(e=>{const n=e.acceptedState===2,s=!e.shouldBeHidden,r=e.RosterPerformer?.User?.performerProfile,o=r?.firstName||r?.lastName||r?.displayName;return n&&s&&o});return t.length===0?null:t.map(e=>{const n=e.RosterPerformer?.User?.performerProfile;return{"@type":"PerformingGroup",name:n?.displayName||[n?.firstName,n?.lastName].filter(Boolean).join(" ")}})}function I(i){const{event:t,venue:e,venueTimeZone:n=P(),eventUrl:s,organizer:r}=i;if(!t)return null;const o=y(t.startDateTime,n);if(!t.name||!o||!e?.name)return null;const l=S(e,e?.googleLocationNameCache),c={"@type":"Place",name:e.name};l&&(c.address=l);const a={"@context":"https://schema.org","@type":"Event",name:t.name,startDate:o,eventStatus:A(t.status),eventAttendanceMode:"https://schema.org/OfflineEventAttendanceMode",location:c},d=y(t.endDateTime,n);d&&(a.endDate=d);const m=t.description||t.eventSummary;m&&(a.description=m);const h=t.image||t.coverImage||t.ShowImage;h&&(a.image=[h]);const g=N(t.availableTickets,s);g&&(a.offers=g);const u=L(t.performers);if(u&&u.length>0&&(a.performer=u),r?.name){const p={"@type":"Organization",name:r.name};r.url&&(p.url=r.url),a.organizer=p}return s&&(a.url=s),a}function T(i){switch(i){case"available":return"https://schema.org/InStock";case"sold_out":return"https://schema.org/SoldOut";case"coming_soon":return"https://schema.org/PreOrder";case"ended":return"https://schema.org/SoldOut";default:return"https://schema.org/InStock"}}function M(i,t){if(!i||i.length===0)return null;const e=i.filter(o=>o.status!=="sold_out"&&o.status!=="ended");if(e.length===0){const o=i.map(a=>a.price).filter(a=>!isNaN(a));if(o.length===0)return null;const l=Math.min(...o),c=Math.max(...o);return l!==c?{"@type":"AggregateOffer",lowPrice:l,highPrice:c,priceCurrency:"USD",availability:"https://schema.org/SoldOut"}:{"@type":"Offer",price:l,priceCurrency:"USD",availability:"https://schema.org/SoldOut"}}const n=e.map(o=>o.price).filter(o=>!isNaN(o));if(n.length===0)return null;const s=Math.min(...n),r=Math.max(...n);return s!==r?{"@type":"AggregateOffer",lowPrice:s,highPrice:r,priceCurrency:"USD",availability:T(e[0].status)}:{"@type":"Offer",price:s,priceCurrency:"USD",availability:T(e[0].status)}}function O(i){const{series:t,baseEventUrl:e}=i;if(!t.title||!t.occurrences||t.occurrences.length===0||!t.venue?.name)return null;const n=S(t.venue,t.venue.googleLocationNameCache),s={"@type":"Place",name:t.venue.name};n&&(s.address=n);const r={"@context":"https://schema.org","@type":"EventSeries",name:t.title,location:s},o=t.description||t.eventSummary;o&&(r.description=o),t.image&&(r.image=[t.image]);const l=t.performers?.filter(a=>a.displayName);l&&l.length>0&&(r.performer=l.map(a=>({"@type":"PerformingGroup",name:a.displayName})));const c=[];for(const a of t.occurrences){const d=y(a.startDateTime,t.timeZone);if(!d)continue;const m={"@type":"Event",name:a.title,startDate:d,eventStatus:A(a.status),location:s};e&&(m["@id"]=`${e}/${a.id}-${a.slug}`);const h=y(a.endDateTime,t.timeZone);h&&(m.endDate=h);const g=M(a.tickets,a.status);g&&(m.offers=g),c.push(m)}return c.length===0?null:(r.subEvent=c,r)}function $(i){const{collection:t,baseEventUrl:e}=i;if(!t.collectionTitle||!t.events||t.events.length===0)return null;const n=[];for(const o of t.events){if(!o.title)continue;const l={"@type":"ListItem",position:n.length+1,name:o.title};e&&(l.url=o.slug?`${e}/${o.id}-${o.slug}`:`${e}/${o.id}`),n.push(l)}if(n.length===0)return null;const s={"@context":"https://schema.org","@type":"CollectionPage",name:t.collectionTitle},r=t.description||t.summary;return r&&(s.description=r),t.coverImage&&(s.image=[t.coverImage]),s.mainEntity={"@type":"ItemList",numberOfItems:n.length,itemListElement:n},s}function v(i){return i?i.toLowerCase().trim().replace(/[^\w\s-]/g,"").replace(/\s+/g,"-").replace(/--+/g,"-").replace(/^-+|-+$/g,""):""}const f=class f{constructor(t){this._state="idle",this._originals=null,this._originalsSaved=!1,this._events=[],!(typeof document>"u")&&(f._activeInstance&&f._activeInstance!==this&&f._activeInstance.destroy(),this._venueName=t.venueName,this._venueAddress=t.venueAddress,this._venueTimeZone=t.venueTimeZone||P(),this._baseUrl=t.baseUrl||window.location.origin+window.location.pathname,this._saveOriginals(),f._activeInstance=this)}_saveOriginals(){if(this._originalsSaved)return;const t=document.querySelector('link[rel="canonical"]'),e=document.querySelector('meta[property="og:title"]'),n=document.querySelector('meta[property="og:description"]'),s=document.querySelector('meta[property="og:image"]'),r=document.querySelector('meta[property="og:url"]'),o=document.querySelector('meta[property="og:type"]'),l=document.querySelector('meta[name="description"]');this._originals={title:document.title,canonicalHref:t?.getAttribute("href")??null,ogTitle:e?.getAttribute("content")??null,ogDescription:n?.getAttribute("content")??null,ogImage:s?.getAttribute("content")??null,ogUrl:r?.getAttribute("content")??null,ogType:o?.getAttribute("content")??null,metaDescription:l?.getAttribute("content")??null},this._originalsSaved=!0}_injectJsonLd(t){if(typeof document>"u")return;document.querySelectorAll('script[data-micdrop="jsonld"]').forEach(n=>n.remove());const e=document.createElement("script");e.setAttribute("type","application/ld+json"),e.setAttribute("data-micdrop","jsonld"),e.textContent=JSON.stringify(t),document.head.appendChild(e)}_setMeta(t,e){if(!(typeof document>"u"))if(t.startsWith("og:")){let n=document.querySelector(`meta[property="${t}"]`);n?n.setAttribute("content",e):(n=document.createElement("meta"),n.setAttribute("property",t),n.setAttribute("content",e),n.setAttribute("data-micdrop","og"),document.head.appendChild(n))}else{let n=document.querySelector(`meta[name="${t}"]`);n?n.setAttribute("content",e):(n=document.createElement("meta"),n.setAttribute("name",t),n.setAttribute("content",e),n.setAttribute("data-micdrop","meta"),document.head.appendChild(n))}}_setCanonical(t){if(typeof document>"u")return;let e=document.querySelector('link[rel="canonical"]');e?e.setAttribute("href",t):(e=document.createElement("link"),e.setAttribute("rel","canonical"),e.setAttribute("href",t),e.setAttribute("data-micdrop","canonical"),document.head.appendChild(e))}_removeAllInjected(){typeof document>"u"||document.querySelectorAll("[data-micdrop]").forEach(t=>t.remove())}_restoreOriginals(){if(!(typeof document>"u"||!this._originals)){if(document.title=this._originals.title,this._originals.canonicalHref!==null){const t=document.querySelector('link[rel="canonical"]');t&&t.setAttribute("href",this._originals.canonicalHref)}this._restoreMetaProperty("og:title",this._originals.ogTitle),this._restoreMetaProperty("og:description",this._originals.ogDescription),this._restoreMetaProperty("og:image",this._originals.ogImage),this._restoreMetaProperty("og:url",this._originals.ogUrl),this._restoreMetaProperty("og:type",this._originals.ogType),this._restoreMetaName("description",this._originals.metaDescription),this._originalsSaved=!1}}_restoreMetaProperty(t,e){const n=document.querySelector(`meta[property="${t}"]`);e!==null&&n?n.setAttribute("content",e):e===null&&n?.hasAttribute("data-micdrop")&&n.remove()}_restoreMetaName(t,e){const n=document.querySelector(`meta[name="${t}"]`);e!==null&&n?n.setAttribute("content",e):e===null&&n?.hasAttribute("data-micdrop")&&n.remove()}_buildItemList(t){return{"@context":"https://schema.org","@type":"ItemList",itemListElement:t.map((e,n)=>{const s=e.slug||v(e.name);return{"@type":"ListItem",position:n+1,url:`${this._baseUrl}?e=${e.id}-${s}`}})}}_buildEntityUrl(t,e,n){const s=t==="event"?"e":t==="series"?"s":"c";return`${this._baseUrl}?${s}=${e}-${n}`}onEventsLoaded(t,e){if(typeof document>"u"||this._state==="destroyed")return;if(e){this._venueName=e.name||this._venueName,this._venueAddress=e.address||e.googleLocationNameCache||this._venueAddress;const s=e.timeZone||e.timezone;s&&(this._venueTimeZone=s)}this._events=t;const n=this._buildItemList(t);this._injectJsonLd(n),this._state="listing"}onEntitySelected(t,e,n){if(typeof document>"u"||this._state==="destroyed")return;if(this._saveOriginals(),n){this._venueName=n.name||this._venueName,this._venueAddress=n.address||n.googleLocationNameCache||this._venueAddress;const a=n.timeZone||n.timezone;a&&(this._venueTimeZone=a)}let s=null,r="",o="",l="",c="";if(t==="event"){const a=e.slug||v(e.name||"");c=this._buildEntityUrl("event",e.id||e.eventID||"",a),s=I({event:e,venue:{name:this._venueName,googleLocationNameCache:this._venueAddress},venueTimeZone:this._venueTimeZone,eventUrl:c}),r=e.name||"",o=e.description||e.eventSummary||"",l=e.image||e.coverImage||e.ShowImage||""}else if(t==="series"){const a=e.slug||v(e.title||"");c=this._buildEntityUrl("series",e.id||"",a),s=O({series:{...e,venue:{name:this._venueName,googleLocationNameCache:this._venueAddress},timeZone:this._venueTimeZone},baseEventUrl:c}),r=e.title||"",o=e.description||e.eventSummary||"",l=e.image||""}else if(t==="collection"){const a=e.slug||v(e.collectionTitle||"");c=this._buildEntityUrl("collection",e.id||"",a),s=$({collection:e,baseEventUrl:c,defaultTimeZone:this._venueTimeZone}),r=e.collectionTitle||"",o=e.description||e.summary||"",l=e.coverImage||""}s&&this._injectJsonLd(s),c&&this._setCanonical(c),r&&this._setMeta("og:title",r),o&&this._setMeta("og:description",o),l&&this._setMeta("og:image",l),c&&this._setMeta("og:url",c),this._setMeta("og:type","website"),this._setMeta("twitter:card","summary_large_image"),o&&this._setMeta("description",o),r&&(document.title=`${r} | ${this._venueName}`),this._state="entity"}onBackToListing(){if(!(typeof document>"u")&&this._state!=="destroyed"){if(this._restoreOriginals(),this._removeAllInjected(),this._events.length>0){const t=this._buildItemList(this._events);this._injectJsonLd(t)}this._saveOriginals(),this._state="listing"}}destroy(){typeof document>"u"||this._state!=="destroyed"&&(this._removeAllInjected(),this._restoreOriginals(),f._activeInstance===this&&(f._activeInstance=null),this._state="destroyed")}};f._activeInstance=null;let b=f;exports.HostSeoController=b;exports.buildCollectionJsonLd=$;exports.buildEventJsonLd=I;exports.buildOffers=N;exports.buildPerformers=L;exports.buildPostalAddress=S;exports.buildSeriesJsonLd=O;exports.escapeJsonLdForScript=U;exports.formatDateTimeWithOffset=y;exports.getEventStatus=A;exports.getTicketAvailability=_;exports.parseAddressToPostal=E;
|
|
1
|
+
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});function A(){return"America/Los_Angeles"}function M(i){return i.replace(/</g,"\\u003c").replace(/>/g,"\\u003e").replace(/&/g,"\\u0026")}function y(i,t){if(!i)return null;const e=new Date(i);if(isNaN(e.getTime()))return null;const n={year:"numeric",month:"2-digit",day:"2-digit",hour:"2-digit",minute:"2-digit",second:"2-digit",hour12:!1,timeZoneName:"shortOffset"};let s;try{s=new Intl.DateTimeFormat("en-US",{...n,timeZone:t})}catch{s=new Intl.DateTimeFormat("en-US",{...n,timeZone:A()})}const a=s.formatToParts(e),r=m=>a.find(_=>_.type===m)?.value||"",l=r("year"),c=r("month"),o=r("day"),u=r("hour"),d=r("minute"),g=r("second"),h=r("timeZoneName");let p="+00:00";if(h){const m=h.match(/GMT([+-])(\d+)(?::(\d+))?/);if(m){const _=m[1],$=m[2].padStart(2,"0"),C=m[3]||"00";p=`${_}${$}:${C}`}}return`${l}-${c}-${o}T${u}:${d}:${g}${p}`}function N(i){if(!i)return null;const t=i.split(",").map(s=>s.trim());if(t.length<2)return{"@type":"PostalAddress",streetAddress:i,addressCountry:"US"};const n=t[t.length-1].match(/^([A-Z]{2})\s+(\d{5}(?:-\d{4})?)$/);return n&&t.length>=3?{"@type":"PostalAddress",streetAddress:t.slice(0,-2).join(", "),addressLocality:t[t.length-2],addressRegion:n[1],postalCode:n[2],addressCountry:"US"}:{"@type":"PostalAddress",streetAddress:t[0],addressLocality:t.slice(1).join(", "),addressCountry:"US"}}function T(i,t){if(i&&(i.city||i.state||i.postalCode)){const e={"@type":"PostalAddress"};return i.address&&(e.streetAddress=i.address),i.city&&(e.addressLocality=i.city),i.state&&(e.addressRegion=i.state),i.postalCode&&(e.postalCode=i.postalCode),e.addressCountry=i.country||"US",e}return N(i&&i.address||t||null)}function P(i){if(!i)return"https://schema.org/EventScheduled";const t=i.toLowerCase();return t==="cancelled"||t==="canceled"?"https://schema.org/EventCancelled":t==="postponed"?"https://schema.org/EventPostponed":t==="rescheduled"?"https://schema.org/EventRescheduled":"https://schema.org/EventScheduled"}function b(i){if((i.remainingCapacity??i.quantityRemaining??i.quantity)===0||i.soldOut)return"https://schema.org/SoldOut";const e=new Date,n=i.salesBegin||i.salesStart||i.saleBegin||i.onSaleStart,s=i.salesEnd||i.saleEnd||i.onSaleEnd;return n&&new Date(n)>e?"https://schema.org/PreOrder":s&&new Date(s)<e?"https://schema.org/SoldOut":"https://schema.org/InStock"}function L(i,t){if(!i||i.length===0)return null;const e=i.filter(l=>{const c=l.visibility??0,o=c===0||c===1,u=l.salesChannel!==2;return o&&u});if(e.length===0)return null;const n=e.map(l=>parseFloat(String(l.price??0))).filter(l=>!isNaN(l));if(n.length===0)return null;const s=Math.min(...n),a=Math.max(...n);if(s!==a)return{"@type":"AggregateOffer",lowPrice:s,highPrice:a,priceCurrency:"USD",availability:b(e[0]),url:t||void 0,validFrom:e[0]?.salesBegin||e[0]?.salesStart||void 0};const r=e[0];return{"@type":"Offer",price:s,priceCurrency:"USD",availability:b(r),url:t||void 0,validFrom:r?.salesBegin||r?.salesStart||void 0}}function I(i){if(!i||i.length===0)return null;const t=i.filter(e=>{const n=e.acceptedState===2,s=!e.shouldBeHidden,a=e.RosterPerformer?.User?.performerProfile,r=a?.firstName||a?.lastName||a?.displayName;return n&&s&&r});return t.length===0?null:t.map(e=>{const n=e.RosterPerformer?.User?.performerProfile;return{"@type":"PerformingGroup",name:n?.displayName||[n?.firstName,n?.lastName].filter(Boolean).join(" ")}})}function O(i){const{event:t,venue:e,venueTimeZone:n=A(),eventUrl:s,organizer:a}=i;if(!t)return null;const r=y(t.startDateTime,n);if(!t.name||!r||!e?.name)return null;const l=T(e,e?.googleLocationNameCache),c={"@type":"Place",name:e.name};l&&(c.address=l);const o={"@context":"https://schema.org","@type":"Event",name:t.name,startDate:r,eventStatus:P(t.status),eventAttendanceMode:"https://schema.org/OfflineEventAttendanceMode",location:c},u=y(t.endDateTime,n);u&&(o.endDate=u);const d=t.description||t.eventSummary;d&&(o.description=d);const g=t.image||t.coverImage||t.ShowImage;g&&(o.image=[g]);const h=L(t.availableTickets,s);h&&(o.offers=h);const p=I(t.performers);if(p&&p.length>0&&(o.performer=p),a?.name){const m={"@type":"Organization",name:a.name};a.url&&(m.url=a.url),o.organizer=m}return s&&(o.url=s),o}function E(i){switch(i){case"available":return"https://schema.org/InStock";case"sold_out":return"https://schema.org/SoldOut";case"coming_soon":return"https://schema.org/PreOrder";case"ended":return"https://schema.org/SoldOut";default:return"https://schema.org/InStock"}}function w(i,t){if(!i||i.length===0)return null;const e=i.filter(r=>r.status!=="sold_out"&&r.status!=="ended");if(e.length===0){const r=i.map(o=>o.price).filter(o=>!isNaN(o));if(r.length===0)return null;const l=Math.min(...r),c=Math.max(...r);return l!==c?{"@type":"AggregateOffer",lowPrice:l,highPrice:c,priceCurrency:"USD",availability:"https://schema.org/SoldOut"}:{"@type":"Offer",price:l,priceCurrency:"USD",availability:"https://schema.org/SoldOut"}}const n=e.map(r=>r.price).filter(r=>!isNaN(r));if(n.length===0)return null;const s=Math.min(...n),a=Math.max(...n);return s!==a?{"@type":"AggregateOffer",lowPrice:s,highPrice:a,priceCurrency:"USD",availability:E(e[0].status)}:{"@type":"Offer",price:s,priceCurrency:"USD",availability:E(e[0].status)}}function D(i){const{series:t,baseEventUrl:e}=i;if(!t.title||!t.occurrences||t.occurrences.length===0||!t.venue?.name)return null;const n=T(t.venue,t.venue.googleLocationNameCache),s={"@type":"Place",name:t.venue.name};n&&(s.address=n);const a={"@context":"https://schema.org","@type":"EventSeries",name:t.title,location:s},r=t.description||t.eventSummary;r&&(a.description=r),t.image&&(a.image=[t.image]);const l=t.performers?.filter(o=>o.displayName);l&&l.length>0&&(a.performer=l.map(o=>({"@type":"PerformingGroup",name:o.displayName})));const c=[];for(const o of t.occurrences){const u=y(o.startDateTime,t.timeZone);if(!u)continue;const d={"@type":"Event",name:o.title,startDate:u,eventStatus:P(o.status),location:s};e&&(d["@id"]=`${e}/${o.id}-${o.slug}`);const g=y(o.endDateTime,t.timeZone);g&&(d.endDate=g);const h=w(o.tickets,o.status);h&&(d.offers=h),c.push(d)}return c.length===0?null:(a.subEvent=c,a)}function U(i){const{collection:t,baseEventUrl:e}=i;if(!t.collectionTitle||!t.events||t.events.length===0)return null;const n=[];for(const r of t.events){if(!r.title)continue;const l={"@type":"ListItem",position:n.length+1,name:r.title};e&&(l.url=r.slug?`${e}/${r.id}-${r.slug}`:`${e}/${r.id}`),n.push(l)}if(n.length===0)return null;const s={"@context":"https://schema.org","@type":"CollectionPage",name:t.collectionTitle},a=t.description||t.summary;return a&&(s.description=a),t.coverImage&&(s.image=[t.coverImage]),s.mainEntity={"@type":"ItemList",numberOfItems:n.length,itemListElement:n},s}function v(i){return i?i.toLowerCase().trim().replace(/[^\w\s-]/g,"").replace(/\s+/g,"-").replace(/--+/g,"-").replace(/^-+|-+$/g,""):""}const f=class f{constructor(t){this._state="idle",this._originals=null,this._originalsSaved=!1,this._events=[],!(typeof document>"u")&&(f._activeInstance&&f._activeInstance!==this&&f._activeInstance.destroy(),this._venueName=t.venueName,this._venueAddress=t.venueAddress,this._venueTimeZone=t.venueTimeZone||A(),this._baseUrl=t.baseUrl||window.location.origin+window.location.pathname,this._saveOriginals(),f._activeInstance=this)}_saveOriginals(){if(this._originalsSaved)return;const t=document.querySelector('link[rel="canonical"]'),e=document.querySelector('meta[property="og:title"]'),n=document.querySelector('meta[property="og:description"]'),s=document.querySelector('meta[property="og:image"]'),a=document.querySelector('meta[property="og:url"]'),r=document.querySelector('meta[property="og:type"]'),l=document.querySelector('meta[name="description"]');this._originals={title:document.title,canonicalHref:t?.getAttribute("href")??null,ogTitle:e?.getAttribute("content")??null,ogDescription:n?.getAttribute("content")??null,ogImage:s?.getAttribute("content")??null,ogUrl:a?.getAttribute("content")??null,ogType:r?.getAttribute("content")??null,metaDescription:l?.getAttribute("content")??null},this._originalsSaved=!0}_injectJsonLd(t){if(typeof document>"u")return;document.querySelectorAll('script[data-micdrop="jsonld"]').forEach(n=>n.remove());const e=document.createElement("script");e.setAttribute("type","application/ld+json"),e.setAttribute("data-micdrop","jsonld"),e.textContent=JSON.stringify(t),document.head.appendChild(e)}_setMeta(t,e){if(!(typeof document>"u"))if(t.startsWith("og:")){let n=document.querySelector(`meta[property="${t}"]`);n?n.setAttribute("content",e):(n=document.createElement("meta"),n.setAttribute("property",t),n.setAttribute("content",e),n.setAttribute("data-micdrop","og"),document.head.appendChild(n))}else{let n=document.querySelector(`meta[name="${t}"]`);n?n.setAttribute("content",e):(n=document.createElement("meta"),n.setAttribute("name",t),n.setAttribute("content",e),n.setAttribute("data-micdrop","meta"),document.head.appendChild(n))}}_setCanonical(t){if(typeof document>"u")return;let e=document.querySelector('link[rel="canonical"]');e?e.setAttribute("href",t):(e=document.createElement("link"),e.setAttribute("rel","canonical"),e.setAttribute("href",t),e.setAttribute("data-micdrop","canonical"),document.head.appendChild(e))}_removeAllInjected(){typeof document>"u"||document.querySelectorAll("[data-micdrop]").forEach(t=>t.remove())}_restoreOriginals(){if(!(typeof document>"u"||!this._originals)){if(document.title=this._originals.title,this._originals.canonicalHref!==null){const t=document.querySelector('link[rel="canonical"]');t&&t.setAttribute("href",this._originals.canonicalHref)}this._restoreMetaProperty("og:title",this._originals.ogTitle),this._restoreMetaProperty("og:description",this._originals.ogDescription),this._restoreMetaProperty("og:image",this._originals.ogImage),this._restoreMetaProperty("og:url",this._originals.ogUrl),this._restoreMetaProperty("og:type",this._originals.ogType),this._restoreMetaName("description",this._originals.metaDescription),this._originalsSaved=!1}}_restoreMetaProperty(t,e){const n=document.querySelector(`meta[property="${t}"]`);e!==null&&n?n.setAttribute("content",e):e===null&&n?.hasAttribute("data-micdrop")&&n.remove()}_restoreMetaName(t,e){const n=document.querySelector(`meta[name="${t}"]`);e!==null&&n?n.setAttribute("content",e):e===null&&n?.hasAttribute("data-micdrop")&&n.remove()}_buildItemList(t){return{"@context":"https://schema.org","@type":"ItemList",itemListElement:t.map((e,n)=>{const s=e.slug||v(e.name);return{"@type":"ListItem",position:n+1,url:`${this._baseUrl}?e=${e.id}-${s}`}})}}_buildEntityUrl(t,e,n){const s=t==="event"?"e":t==="series"?"s":"c";return`${this._baseUrl}?${s}=${e}-${n}`}onEventsLoaded(t,e){if(typeof document>"u"||this._state==="destroyed")return;if(e){this._venueName=e.name||this._venueName,this._venueAddress=e.address||e.googleLocationNameCache||this._venueAddress;const s=e.timeZone||e.timezone;s&&(this._venueTimeZone=s)}this._events=t;const n=this._buildItemList(t);this._injectJsonLd(n),this._state="listing"}onEntitySelected(t,e,n){if(typeof document>"u"||this._state==="destroyed")return;if(this._saveOriginals(),n){this._venueName=n.name||this._venueName,this._venueAddress=n.address||n.googleLocationNameCache||this._venueAddress;const o=n.timeZone||n.timezone;o&&(this._venueTimeZone=o)}let s=null,a="",r="",l="",c="";if(t==="event"){const o=e.slug||v(e.name||"");c=this._buildEntityUrl("event",e.id||e.eventID||"",o),s=O({event:e,venue:{name:this._venueName,googleLocationNameCache:this._venueAddress},venueTimeZone:this._venueTimeZone,eventUrl:c}),a=e.name||"",r=e.description||e.eventSummary||"",l=e.image||e.coverImage||e.ShowImage||""}else if(t==="series"){const o=e.slug||v(e.title||"");c=this._buildEntityUrl("series",e.id||"",o),s=D({series:{...e,venue:{name:this._venueName,googleLocationNameCache:this._venueAddress},timeZone:this._venueTimeZone},baseEventUrl:c}),a=e.title||"",r=e.description||e.eventSummary||"",l=e.image||""}else if(t==="collection"){const o=e.slug||v(e.collectionTitle||"");c=this._buildEntityUrl("collection",e.id||"",o),s=U({collection:e,baseEventUrl:c,defaultTimeZone:this._venueTimeZone}),a=e.collectionTitle||"",r=e.description||e.summary||"",l=e.coverImage||""}s&&this._injectJsonLd(s),c&&this._setCanonical(c),a&&this._setMeta("og:title",a),r&&this._setMeta("og:description",r),l&&this._setMeta("og:image",l),c&&this._setMeta("og:url",c),this._setMeta("og:type","website"),this._setMeta("twitter:card","summary_large_image"),r&&this._setMeta("description",r),a&&(document.title=`${a} | ${this._venueName}`),this._state="entity"}onBackToListing(){if(!(typeof document>"u")&&this._state!=="destroyed"){if(this._restoreOriginals(),this._removeAllInjected(),this._events.length>0){const t=this._buildItemList(this._events);this._injectJsonLd(t)}this._saveOriginals(),this._state="listing"}}destroy(){typeof document>"u"||this._state!=="destroyed"&&(this._removeAllInjected(),this._restoreOriginals(),f._activeInstance===this&&(f._activeInstance=null),this._state="destroyed")}};f._activeInstance=null;let S=f;exports.HostSeoController=S;exports.buildCollectionJsonLd=U;exports.buildEventJsonLd=O;exports.buildOffers=L;exports.buildPerformers=I;exports.buildPostalAddress=T;exports.buildSeriesJsonLd=D;exports.escapeJsonLdForScript=M;exports.formatDateTimeWithOffset=y;exports.getEventStatus=P;exports.getTicketAvailability=b;exports.parseAddressToPostal=N;
|
|
2
2
|
//# sourceMappingURL=seo.cjs.map
|
package/dist/seo/seo.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"seo.cjs","sources":["../../node_modules/@getmicdrop/svelte-components/dist/utils/timezones.js","../../src/lib/seo/helpers.ts","../../src/lib/seo/buildEventJsonLd.ts","../../src/lib/seo/buildSeriesJsonLd.ts","../../src/lib/seo/buildCollectionJsonLd.ts","../../src/lib/utils/textUtils.ts","../../src/lib/seo/HostSeoController.ts"],"sourcesContent":["/**\n * Timezone utility helpers.\n * Canonical source for timezone defaults and validation.\n */\n/**\n * Returns the default timezone for Micdrop, branded as `IANATimezone`.\n * Used when no explicit timezone is provided to date formatters. Bypass\n * call sites that pass `'America/Los_Angeles'` literally are red-squiggle\n * type errors at canonicals that take `IANATimezone` — the fix is to\n * thread `getDefaultTimezone()` through.\n *\n * @returns Branded IANA timezone (`'America/Los_Angeles'`)\n *\n * @example\n * formatDate(new Date(), {}, 'en-US', getDefaultTimezone())\n */\nexport function getDefaultTimezone() {\n return 'America/Los_Angeles';\n}\n/**\n * Validates an IANA timezone string.\n * Uses Intl.DateTimeFormat to test if the timezone is recognized.\n *\n * @param tz - Timezone string to validate\n * @returns true if valid IANA timezone, false otherwise\n *\n * @example\n * isValidTimezone('America/Los_Angeles') // true\n * isValidTimezone('Invalid/Zone') // false\n *\n * @deprecated Lenient: `Intl.DateTimeFormat` accepts non-canonical inputs.\n * Use `isValidTimezone` from `$lib/datetime` (stricter — rejects\n * abbreviations like `'PST'`), or `toIANATimezone` from `$lib/types/brands`\n * when you need a branded value.\n */\nexport function isValidTimezone(tz) {\n try {\n new Intl.DateTimeFormat('en-US', { timeZone: tz });\n return true;\n }\n catch {\n return false;\n }\n}\n","/**\n * Shared helper utilities for JSON-LD builders.\n *\n * All functions are pure (no DOM, no Svelte, no side effects).\n * Extracted verbatim from EventStructuredData.svelte.\n *\n * @raw-intl-datetimeformat-escape: SEO/JSON-LD implementation. formatDateTimeWithOffset\n * must emit Google-required ISO-8601-with-tz-offset (\"2025-07-21T19:00:00-05:00\"), which\n * requires Intl.DateTimeFormat.formatToParts(...) with timeZoneName:'shortOffset' to read\n * the per-timezone UTC offset. The SC formatDate/DATE_FORMATS canonical produces\n * human-readable display strings, not parts-based offset extraction — it cannot express this.\n */\n\nimport type { EventTicketInput, EventPerformerInput } from './types';\n\n/**\n * Escape a JSON string for safe embedding inside an inline\n * <script type=\"application/ld+json\"> element.\n *\n * JSON.stringify does NOT escape `<`, `>` or `&`, so a field value containing\n * `</script><script>...` would break out of the script element and execute\n * (stored XSS). Replacing those characters with their unicode escape sequences\n * keeps the output valid JSON (parsers decode `<` back to `<`) while making\n * it impossible to terminate the surrounding <script> tag.\n *\n * This is the standard JSON-LD-in-<script> hardening; the sibling\n * HostSeoController avoids the issue entirely by using element.textContent.\n */\nexport function escapeJsonLdForScript(json: string): string {\n return json\n .replace(/</g, '\\\\u003c')\n .replace(/>/g, '\\\\u003e')\n .replace(/&/g, '\\\\u0026');\n}\n\n/**\n * Format datetime to ISO-8601 with timezone offset.\n * Google requires: \"2025-07-21T19:00:00-05:00\"\n *\n * Extracted from EventStructuredData.svelte lines 25-68.\n */\nexport function formatDateTimeWithOffset(\n isoString: string | null | undefined,\n tz: string\n): string | null {\n if (!isoString) return null;\n\n const date = new Date(isoString);\n if (isNaN(date.getTime())) return null;\n\n // Get the offset in the target timezone\n const formatter = new Intl.DateTimeFormat('en-US', {\n timeZone: tz,\n year: 'numeric',\n month: '2-digit',\n day: '2-digit',\n hour: '2-digit',\n minute: '2-digit',\n second: '2-digit',\n hour12: false,\n timeZoneName: 'shortOffset',\n });\n\n const parts = formatter.formatToParts(date);\n const get = (type: string) => parts.find(p => p.type === type)?.value || '';\n\n const year = get('year');\n const month = get('month');\n const day = get('day');\n const hour = get('hour');\n const minute = get('minute');\n const second = get('second');\n const tzOffset = get('timeZoneName'); // e.g., \"GMT-5\" or \"GMT+2\"\n\n // Convert \"GMT-5\" to \"-05:00\" format\n let offset = '+00:00';\n if (tzOffset) {\n const match = tzOffset.match(/GMT([+-])(\\d+)(?::(\\d+))?/);\n if (match) {\n const sign = match[1];\n const hours = match[2].padStart(2, '0');\n const mins = match[3] || '00';\n offset = `${sign}${hours}:${mins}`;\n }\n }\n\n return `${year}-${month}-${day}T${hour}:${minute}:${second}${offset}`;\n}\n\n/**\n * Parse address string into PostalAddress components.\n * Input: \"7702 Santa Monica Blvd, West Hollywood, CA 90046\"\n * Output: { @type, streetAddress, addressLocality, addressRegion, postalCode, addressCountry }\n *\n * Extracted from EventStructuredData.svelte lines 75-111.\n */\nexport function parseAddressToPostal(\n addressStr: string | null | undefined\n): Record<string, string> | null {\n if (!addressStr) return null;\n\n const parts = addressStr.split(',').map(p => p.trim());\n\n if (parts.length < 2) {\n return {\n '@type': 'PostalAddress',\n streetAddress: addressStr,\n addressCountry: 'US',\n };\n }\n\n // Check if last part is \"State ZIP\" (e.g., \"CA 90046\")\n const lastPart = parts[parts.length - 1];\n const stateZipMatch = lastPart.match(/^([A-Z]{2})\\s+(\\d{5}(?:-\\d{4})?)$/);\n\n if (stateZipMatch && parts.length >= 3) {\n // Standard US address format\n return {\n '@type': 'PostalAddress',\n streetAddress: parts.slice(0, -2).join(', '),\n addressLocality: parts[parts.length - 2],\n addressRegion: stateZipMatch[1],\n postalCode: stateZipMatch[2],\n addressCountry: 'US',\n };\n }\n\n // Fallback for non-standard formats\n return {\n '@type': 'PostalAddress',\n streetAddress: parts[0],\n addressLocality: parts.slice(1).join(', '),\n addressCountry: 'US',\n };\n}\n\n/**\n * Structured postal-address parts, as the public resolve/series payloads now\n * expose them: `address` is the STREET line, with city/state/postalCode/country\n * as discrete fields (PublicVenueInfo / SeriesVenueDTO).\n */\nexport interface PostalAddressParts {\n address?: string | null; // street line\n city?: string | null;\n state?: string | null;\n postalCode?: string | null;\n country?: string | null;\n}\n\n/**\n * Build a schema.org PostalAddress, preferring discrete structured fields when\n * any locality signal (city / state / postalCode) is present, and otherwise\n * falling back to parsing a single comma-separated string (the street `address`\n * field if it carries a full address, else `googleLocationNameCache`).\n *\n * Discrete fields are more reliable than string-splitting — Google requires a\n * well-formed PostalAddress for Event rich-result eligibility (online-only\n * events lost eligibility June 2025).\n */\nexport function buildPostalAddress(\n parts: PostalAddressParts | null | undefined,\n fallback?: string | null\n): Record<string, string> | null {\n if (parts && (parts.city || parts.state || parts.postalCode)) {\n const out: Record<string, string> = { '@type': 'PostalAddress' };\n if (parts.address) out.streetAddress = parts.address;\n if (parts.city) out.addressLocality = parts.city;\n if (parts.state) out.addressRegion = parts.state;\n if (parts.postalCode) out.postalCode = parts.postalCode;\n out.addressCountry = parts.country || 'US';\n return out;\n }\n // No discrete locality fields — parse the best single string we have. The\n // street `address` may legacy-hold a full comma-separated address, so prefer\n // it over the location-name cache.\n return parseAddressToPostal((parts && parts.address) || fallback || null);\n}\n\n/**\n * Map event status to schema.org EventStatusType.\n *\n * Extracted from EventStructuredData.svelte lines 116-132.\n */\nexport function getEventStatus(rawStatus: string | null | undefined): string {\n if (!rawStatus) return 'https://schema.org/EventScheduled';\n\n const status = rawStatus.toLowerCase();\n\n if (status === 'cancelled' || status === 'canceled') {\n return 'https://schema.org/EventCancelled';\n }\n if (status === 'postponed') {\n return 'https://schema.org/EventPostponed';\n }\n if (status === 'rescheduled') {\n return 'https://schema.org/EventRescheduled';\n }\n\n return 'https://schema.org/EventScheduled';\n}\n\n/**\n * Map ticket availability to schema.org ItemAvailability.\n *\n * Extracted from EventStructuredData.svelte lines 137-157.\n */\nexport function getTicketAvailability(ticket: EventTicketInput): string {\n const remaining =\n ticket.remainingCapacity ?? ticket.quantityRemaining ?? ticket.quantity;\n\n if (remaining === 0 || ticket.soldOut) {\n return 'https://schema.org/SoldOut';\n }\n\n const now = new Date();\n const salesBegin =\n ticket.salesBegin ||\n ticket.salesStart ||\n ticket.saleBegin ||\n ticket.onSaleStart;\n const salesEnd = ticket.salesEnd || ticket.saleEnd || ticket.onSaleEnd;\n\n if (salesBegin && new Date(salesBegin) > now) {\n return 'https://schema.org/PreOrder';\n }\n\n if (salesEnd && new Date(salesEnd) < now) {\n return 'https://schema.org/SoldOut';\n }\n\n return 'https://schema.org/InStock';\n}\n\n/**\n * Build offers from available tickets.\n *\n * Extracted from EventStructuredData.svelte lines 162-208.\n */\nexport function buildOffers(\n tickets: EventTicketInput[] | null | undefined,\n url?: string\n): Record<string, unknown> | null {\n if (!tickets || tickets.length === 0) return null;\n\n // Filter to public tickets only (exclude door-only and hidden)\n const publicTickets = tickets.filter(t => {\n const visibility = t.visibility ?? 0;\n const isVisible = visibility === 0 || visibility === 1;\n const isPublic = t.salesChannel !== 2;\n return isVisible && isPublic;\n });\n\n if (publicTickets.length === 0) return null;\n\n // Find lowest and highest price\n const prices = publicTickets\n .map(t => parseFloat(String(t.price ?? 0)))\n .filter(p => !isNaN(p));\n\n if (prices.length === 0) return null;\n\n const lowPrice = Math.min(...prices);\n const highPrice = Math.max(...prices);\n\n // Use AggregateOffer when there are multiple price points\n if (lowPrice !== highPrice) {\n return {\n '@type': 'AggregateOffer',\n lowPrice: lowPrice,\n highPrice: highPrice,\n priceCurrency: 'USD',\n availability: getTicketAvailability(publicTickets[0]),\n url: url || undefined,\n validFrom:\n publicTickets[0]?.salesBegin ||\n publicTickets[0]?.salesStart ||\n undefined,\n };\n }\n\n // Single price point - use Offer\n const ticket = publicTickets[0];\n return {\n '@type': 'Offer',\n price: lowPrice,\n priceCurrency: 'USD',\n availability: getTicketAvailability(ticket),\n url: url || undefined,\n validFrom: ticket?.salesBegin || ticket?.salesStart || undefined,\n };\n}\n\n/**\n * Build performer array from event performers.\n *\n * Extracted from EventStructuredData.svelte lines 213-237.\n */\nexport function buildPerformers(\n performers: EventPerformerInput[] | null | undefined\n): Array<Record<string, string>> | null {\n if (!performers || performers.length === 0) return null;\n\n // Filter to confirmed, visible performers\n const confirmed = performers.filter(p => {\n const isConfirmed = p.acceptedState === 2;\n const isNotHidden = !p.shouldBeHidden;\n const profile = p.RosterPerformer?.User?.performerProfile;\n const hasName =\n profile?.firstName || profile?.lastName || profile?.displayName;\n return isConfirmed && isNotHidden && hasName;\n });\n\n if (confirmed.length === 0) return null;\n\n return confirmed.map(p => {\n const profile = p.RosterPerformer?.User?.performerProfile;\n const name =\n profile?.displayName ||\n [profile?.firstName, profile?.lastName].filter(Boolean).join(' ');\n\n return {\n '@type': 'PerformingGroup',\n name: name,\n };\n });\n}\n","import { getDefaultTimezone } from '@getmicdrop/svelte-components/utils/timezones';\n/**\n * Pure function that builds Event JSON-LD structured data for Google Events rich results.\n *\n * Extracted from EventStructuredData.svelte (lines 242-313).\n * Produces identical output to the original $derived.by block.\n */\n\nimport type { EventJsonLdInput, VenueInput, OrganizerInput } from './types';\nimport {\n formatDateTimeWithOffset,\n buildPostalAddress,\n getEventStatus,\n buildOffers,\n buildPerformers,\n} from './helpers';\n\nexport interface BuildEventJsonLdOptions {\n event: EventJsonLdInput;\n venue: VenueInput;\n venueTimeZone?: string;\n eventUrl?: string;\n organizer?: OrganizerInput;\n}\n\nexport function buildEventJsonLd(\n options: BuildEventJsonLdOptions\n): Record<string, unknown> | null {\n const {\n event,\n venue,\n venueTimeZone = getDefaultTimezone(),\n eventUrl,\n organizer,\n } = options;\n\n if (!event) return null;\n\n const startDate = formatDateTimeWithOffset(\n event.startDateTime,\n venueTimeZone\n );\n\n // Skip if we don't have required fields\n if (!event.name || !startDate || !venue?.name) {\n return null;\n }\n\n // Google requires a PostalAddress on a physical event's location (online-only\n // events lost rich-result eligibility June 2025). Prefer the venue's discrete\n // address fields (street/city/state/postalCode), falling back to parsing the\n // googleLocationNameCache string. Only attach `address` when we could build\n // one — never emit `address: null`. A missing address is a data-pipeline gap\n // (the resolve payload must carry the venue address).\n const postalAddress = buildPostalAddress(\n venue,\n venue?.googleLocationNameCache\n );\n const location: Record<string, unknown> = {\n '@type': 'Place',\n name: venue.name,\n };\n if (postalAddress) {\n location.address = postalAddress;\n }\n\n const data: Record<string, unknown> = {\n '@context': 'https://schema.org',\n '@type': 'Event',\n name: event.name,\n startDate: startDate,\n eventStatus: getEventStatus(event.status),\n eventAttendanceMode: 'https://schema.org/OfflineEventAttendanceMode',\n location,\n };\n\n // Add endDate if available\n const endDate = formatDateTimeWithOffset(event.endDateTime, venueTimeZone);\n if (endDate) {\n data.endDate = endDate;\n }\n\n // Add description\n const description = event.description || event.eventSummary;\n if (description) {\n data.description = description;\n }\n\n // Add image - Google recommends multiple aspect ratios\n const imageUrl = event.image || event.coverImage || event.ShowImage;\n if (imageUrl) {\n data.image = [imageUrl];\n }\n\n // Add offers from tickets\n const offers = buildOffers(event.availableTickets, eventUrl);\n if (offers) {\n data.offers = offers;\n }\n\n // Add performers\n const performers = buildPerformers(event.performers);\n if (performers && performers.length > 0) {\n data.performer = performers;\n }\n\n // Add organizer if provided\n if (organizer?.name) {\n const org: Record<string, string> = {\n '@type': 'Organization',\n name: organizer.name,\n };\n if (organizer.url) {\n org.url = organizer.url;\n }\n data.organizer = org;\n }\n\n // Add event URL if provided\n if (eventUrl) {\n data.url = eventUrl;\n }\n\n return data;\n}\n","/**\n * Pure function that builds EventSeries JSON-LD structured data.\n *\n * Series use schema.org EventSeries type with subEvent array containing\n * individual Event blocks for each occurrence. Each subEvent has its own\n * offers built from the simplified series ticket DTOs (different shape\n * from the event builder's tickets).\n *\n * Google Rich Results Test does NOT support EventSeries directly, but\n * the individual subEvent Event blocks can qualify for rich results.\n */\n\nimport type { SeriesJsonLdInput } from './types';\nimport {\n formatDateTimeWithOffset,\n buildPostalAddress,\n getEventStatus,\n} from './helpers';\n\nexport interface BuildSeriesJsonLdOptions {\n series: SeriesJsonLdInput;\n baseEventUrl?: string; // e.g., \"https://get-micdrop.com/e\" -- used to build subEvent @id URLs\n}\n\n/**\n * Map a series ticket status string to schema.org ItemAvailability.\n *\n * Series tickets use a simplified { name, price, status } shape from the\n * backend (SeriesTicketDTO), unlike event tickets which have visibility,\n * salesChannel, remainingCapacity, etc.\n */\nfunction mapSeriesTicketAvailability(status: string): string {\n switch (status) {\n case 'available':\n return 'https://schema.org/InStock';\n case 'sold_out':\n return 'https://schema.org/SoldOut';\n case 'coming_soon':\n return 'https://schema.org/PreOrder';\n case 'ended':\n return 'https://schema.org/SoldOut';\n default:\n return 'https://schema.org/InStock';\n }\n}\n\n/**\n * Build offers from series occurrence tickets.\n *\n * Series tickets have a simpler shape than event tickets: { name, price, status }.\n * Uses AggregateOffer for multiple price points, Offer for single price.\n */\nfunction buildSeriesOccurrenceOffers(\n tickets: Array<{ name: string; price: number; status: string }> | undefined,\n occurrenceStatus: string | undefined\n): Record<string, unknown> | null {\n if (!tickets || tickets.length === 0) return null;\n\n // Filter out sold_out and ended tickets for offer building\n const activeTickets = tickets.filter(\n t => t.status !== 'sold_out' && t.status !== 'ended'\n );\n\n // If all tickets are sold out / ended, report SoldOut with the price range\n if (activeTickets.length === 0) {\n const prices = tickets.map(t => t.price).filter(p => !isNaN(p));\n if (prices.length === 0) return null;\n\n const lowPrice = Math.min(...prices);\n const highPrice = Math.max(...prices);\n\n if (lowPrice !== highPrice) {\n return {\n '@type': 'AggregateOffer',\n lowPrice,\n highPrice,\n priceCurrency: 'USD',\n availability: 'https://schema.org/SoldOut',\n };\n }\n\n return {\n '@type': 'Offer',\n price: lowPrice,\n priceCurrency: 'USD',\n availability: 'https://schema.org/SoldOut',\n };\n }\n\n const prices = activeTickets.map(t => t.price).filter(p => !isNaN(p));\n if (prices.length === 0) return null;\n\n const lowPrice = Math.min(...prices);\n const highPrice = Math.max(...prices);\n\n if (lowPrice !== highPrice) {\n return {\n '@type': 'AggregateOffer',\n lowPrice,\n highPrice,\n priceCurrency: 'USD',\n availability: mapSeriesTicketAvailability(activeTickets[0].status),\n };\n }\n\n return {\n '@type': 'Offer',\n price: lowPrice,\n priceCurrency: 'USD',\n availability: mapSeriesTicketAvailability(activeTickets[0].status),\n };\n}\n\nexport function buildSeriesJsonLd(\n options: BuildSeriesJsonLdOptions\n): Record<string, unknown> | null {\n const { series, baseEventUrl } = options;\n\n // Required fields\n if (!series.title) return null;\n if (!series.occurrences || series.occurrences.length === 0) return null;\n if (!series.venue?.name) return null;\n\n // Build location — prefer the venue's discrete address fields, falling back to\n // parsing googleLocationNameCache (see buildEventJsonLd for the rationale).\n const address = buildPostalAddress(\n series.venue,\n series.venue.googleLocationNameCache\n );\n const location: Record<string, unknown> = {\n '@type': 'Place',\n name: series.venue.name,\n };\n if (address) {\n location.address = address;\n }\n\n // Build parent EventSeries\n const data: Record<string, unknown> = {\n '@context': 'https://schema.org',\n '@type': 'EventSeries',\n name: series.title,\n location,\n };\n\n // Description: prefer description, fall back to eventSummary\n const description = series.description || series.eventSummary;\n if (description) {\n data.description = description;\n }\n\n // Image\n if (series.image) {\n data.image = [series.image];\n }\n\n // Performers: flat displayName array (different from event builder's nested shape)\n const performers = series.performers?.filter(p => p.displayName);\n if (performers && performers.length > 0) {\n data.performer = performers.map(p => ({\n '@type': 'PerformingGroup',\n name: p.displayName,\n }));\n }\n\n // SubEvents: build Event blocks for each occurrence\n const subEvents: Record<string, unknown>[] = [];\n\n for (const occ of series.occurrences) {\n const startDate = formatDateTimeWithOffset(\n occ.startDateTime,\n series.timeZone\n );\n if (!startDate) continue; // Skip occurrences without valid startDate\n\n const subEvent: Record<string, unknown> = {\n '@type': 'Event',\n name: occ.title,\n startDate,\n eventStatus: getEventStatus(occ.status),\n location,\n };\n\n // @id from baseEventUrl\n if (baseEventUrl) {\n subEvent['@id'] = `${baseEventUrl}/${occ.id}-${occ.slug}`;\n }\n\n // endDate\n const endDate = formatDateTimeWithOffset(occ.endDateTime, series.timeZone);\n if (endDate) {\n subEvent.endDate = endDate;\n }\n\n // Offers from simplified ticket DTOs\n const offers = buildSeriesOccurrenceOffers(occ.tickets, occ.status);\n if (offers) {\n subEvent.offers = offers;\n }\n\n subEvents.push(subEvent);\n }\n\n // If all occurrences were skipped, return null\n if (subEvents.length === 0) return null;\n\n data.subEvent = subEvents;\n\n return data;\n}\n","/**\r\n * Pure function that builds CollectionPage JSON-LD for an event collection.\r\n *\r\n * A collection is a curated list of shows — NOT a single event, and NOT a\r\n * Festival (the product can't infer festival intent from a collection named\r\n * \"Detroit Comedy Festival\"; it's still just a collection). We emit a\r\n * schema.org `CollectionPage` whose `mainEntity` is a *summary-page* `ItemList`\r\n * — each `ListItem` is just `{ position, name, url }` pointing at the show's\r\n * canonical `/e/{id}-{slug}` leaf. The full `Event` rich-result markup lives on\r\n * each event's own page (one event = one leaf URL); duplicating full Event\r\n * objects here would create competing entities and trigger missing-field\r\n * (offers/address) warnings on the collection page.\r\n *\r\n * (Previously emitted `Festival` + `subEvent`, which Google's Rich Results\r\n * Test does not support.)\r\n */\r\n\r\nimport type { CollectionJsonLdInput } from './types';\r\n\r\nexport interface BuildCollectionJsonLdOptions {\r\n collection: CollectionJsonLdInput;\r\n baseEventUrl?: string; // e.g., \"https://get-micdrop.com/e\"\r\n /** Accepted for API compatibility; the summary ItemList carries no dates. */\r\n defaultTimeZone?: string;\r\n}\r\n\r\nexport function buildCollectionJsonLd(\r\n options: BuildCollectionJsonLdOptions,\r\n): Record<string, unknown> | null {\r\n const { collection, baseEventUrl } = options;\r\n\r\n // Required fields\r\n if (!collection.collectionTitle) return null;\r\n if (!collection.events || collection.events.length === 0) return null;\r\n\r\n // Build a summary ListItem per collection member: position + name + the\r\n // canonical leaf url. Prefer the slugged form, else bare `/e/{id}` (the app\r\n // self-heals bare ids to the slugged canonical).\r\n const itemListElement: Record<string, unknown>[] = [];\r\n\r\n for (const event of collection.events) {\r\n if (!event.title) continue; // Skip members with no name to list\r\n\r\n const listItem: Record<string, unknown> = {\r\n '@type': 'ListItem',\r\n position: itemListElement.length + 1,\r\n name: event.title,\r\n };\r\n\r\n if (baseEventUrl) {\r\n listItem.url = event.slug\r\n ? `${baseEventUrl}/${event.id}-${event.slug}`\r\n : `${baseEventUrl}/${event.id}`;\r\n }\r\n\r\n itemListElement.push(listItem);\r\n }\r\n\r\n // If every event was skipped, there's nothing to mark up.\r\n if (itemListElement.length === 0) return null;\r\n\r\n const data: Record<string, unknown> = {\r\n '@context': 'https://schema.org',\r\n '@type': 'CollectionPage',\r\n name: collection.collectionTitle,\r\n };\r\n\r\n // Description: prefer description, fall back to summary\r\n const description = collection.description || collection.summary;\r\n if (description) {\r\n data.description = description;\r\n }\r\n\r\n // Cover image\r\n if (collection.coverImage) {\r\n data.image = [collection.coverImage];\r\n }\r\n\r\n data.mainEntity = {\r\n '@type': 'ItemList',\r\n numberOfItems: itemListElement.length,\r\n itemListElement,\r\n };\r\n\r\n return data;\r\n}\r\n","/**\r\n * Text utility functions: generateSlug, validateNegativeNumber\r\n *\r\n * classNames and truncateTitle re-exported from @getmicdrop/svelte-components\r\n * via ./utils.js for backward compatibility with existing consumers.\r\n */\r\n\r\n/**\r\n * Generate a URL-friendly slug from a string\r\n */\r\nexport function generateSlug(text: string): string {\r\n if (!text) return '';\r\n\r\n return text\r\n .toLowerCase()\r\n .trim()\r\n .replace(/[^\\w\\s-]/g, '')\r\n .replace(/\\s+/g, '-')\r\n .replace(/--+/g, '-')\r\n .replace(/^-+|-+$/g, '');\r\n}\r\n\r\n/**\r\n * Validate that a value is a positive number\r\n * Returns an error message string, or empty string if valid\r\n */\r\nexport function validateNegativeNumber(\r\n value: string | number | null | undefined\r\n): string {\r\n if (value === null || value === undefined || value === '') {\r\n return 'Must be greater than zero.';\r\n }\r\n\r\n const stringValue = String(value).trim();\r\n\r\n if (\r\n stringValue.includes('--') ||\r\n stringValue.includes('++') ||\r\n stringValue.match(/^-+$/) ||\r\n stringValue.match(/^\\++$/)\r\n ) {\r\n return 'Please enter a valid number';\r\n }\r\n\r\n if (stringValue.match(/^-?\\d+-$/)) {\r\n return 'Please enter a valid number';\r\n }\r\n\r\n if ((stringValue.match(/\\./g) || []).length > 1) {\r\n return 'Please enter a valid number';\r\n }\r\n\r\n const numValue = Number(value);\r\n if (isNaN(numValue)) {\r\n return 'Please enter a valid number';\r\n }\r\n\r\n if (numValue < 0 || numValue === 0) {\r\n return 'Price must be greater than zero.';\r\n }\r\n\r\n return '';\r\n}\r\n","import { getDefaultTimezone } from '@getmicdrop/svelte-components/utils/timezones';\r\n/**\r\n * HostSeoController -- manages JSON-LD structured data, canonical URLs,\r\n * OG tags, meta descriptions, and page title injection into venue pages'\r\n * <head> when the Micdrop calendar widget is embedded.\r\n *\r\n * Follows a save-replace-restore pattern to be a \"good citizen\" on venue pages:\r\n * idle -> listing -> entity -> listing -> ... -> destroyed\r\n *\r\n * Uses textContent (never innerHTML) for JSON-LD script tags to prevent XSS.\r\n */\r\n\r\nimport type { HostSeoControllerOptions } from './types';\r\nimport { buildEventJsonLd } from './buildEventJsonLd';\r\nimport { buildSeriesJsonLd } from './buildSeriesJsonLd';\r\nimport { buildCollectionJsonLd } from './buildCollectionJsonLd';\r\nimport { generateSlug } from '$lib/utils/textUtils';\r\n\r\ntype ControllerState = 'idle' | 'listing' | 'entity' | 'destroyed';\r\n\r\ninterface SavedOriginals {\r\n title: string;\r\n canonicalHref: string | null;\r\n ogTitle: string | null;\r\n ogDescription: string | null;\r\n ogImage: string | null;\r\n ogUrl: string | null;\r\n ogType: string | null;\r\n metaDescription: string | null;\r\n}\r\n\r\n// Minimal event shape for listing mode\r\ninterface ListingEvent {\r\n id: number | string;\r\n name: string;\r\n slug?: string;\r\n startDateTime?: string;\r\n image?: string;\r\n}\r\n\r\n// Venue data passed at runtime\r\ninterface VenueData {\r\n name: string;\r\n address?: string;\r\n googleLocationNameCache?: string;\r\n // Transformed venues carry lowercase `timezone`; raw/host data may use\r\n // PascalCase `timeZone`. Accept both so the venue tz isn't silently dropped.\r\n timeZone?: string;\r\n timezone?: string;\r\n}\r\n\r\nexport class HostSeoController {\r\n /**\r\n * Single active instance. A venue page that mounts >1 calendar embed\r\n * (e.g. \"upcoming shows\" + \"this weekend\") will see the *later*\r\n * controller take over `<head>` — the earlier one is destroyed.\r\n *\r\n * KNOWN LIMITATION: multi-embed pages cannot run two SEO controllers\r\n * in parallel because they would both fight to own `<title>`/OG/etc.\r\n * Pass `data-inject-seo=\"false\"` on all but one embed to opt out.\r\n */\r\n static _activeInstance: HostSeoController | null = null;\r\n\r\n private _state: ControllerState = 'idle';\r\n private _venueName: string;\r\n private _venueAddress: string | undefined;\r\n private _venueTimeZone: string;\r\n private _baseUrl: string;\r\n private _originals: SavedOriginals | null = null;\r\n private _originalsSaved = false;\r\n private _events: ListingEvent[] = [];\r\n\r\n constructor(options: HostSeoControllerOptions) {\r\n // SSR guard\r\n if (typeof document === 'undefined') return;\r\n\r\n // If another instance exists, destroy it first.\r\n if (\r\n HostSeoController._activeInstance &&\r\n HostSeoController._activeInstance !== this\r\n ) {\r\n HostSeoController._activeInstance.destroy();\r\n }\r\n\r\n this._venueName = options.venueName;\r\n this._venueAddress = options.venueAddress;\r\n this._venueTimeZone = options.venueTimeZone || getDefaultTimezone();\r\n this._baseUrl =\r\n options.baseUrl || window.location.origin + window.location.pathname;\r\n\r\n this._saveOriginals();\r\n\r\n HostSeoController._activeInstance = this;\r\n }\r\n\r\n // ---------------------------------------------------------------------------\r\n // Private helpers\r\n // ---------------------------------------------------------------------------\r\n\r\n private _saveOriginals(): void {\r\n if (this._originalsSaved) return;\r\n\r\n const canonical = document.querySelector('link[rel=\"canonical\"]') as HTMLLinkElement | null;\r\n const ogTitle = document.querySelector('meta[property=\"og:title\"]') as HTMLMetaElement | null;\r\n const ogDescription = document.querySelector('meta[property=\"og:description\"]') as HTMLMetaElement | null;\r\n const ogImage = document.querySelector('meta[property=\"og:image\"]') as HTMLMetaElement | null;\r\n const ogUrl = document.querySelector('meta[property=\"og:url\"]') as HTMLMetaElement | null;\r\n const ogType = document.querySelector('meta[property=\"og:type\"]') as HTMLMetaElement | null;\r\n const metaDescription = document.querySelector('meta[name=\"description\"]') as HTMLMetaElement | null;\r\n\r\n this._originals = {\r\n title: document.title,\r\n canonicalHref: canonical?.getAttribute('href') ?? null,\r\n ogTitle: ogTitle?.getAttribute('content') ?? null,\r\n ogDescription: ogDescription?.getAttribute('content') ?? null,\r\n ogImage: ogImage?.getAttribute('content') ?? null,\r\n ogUrl: ogUrl?.getAttribute('content') ?? null,\r\n ogType: ogType?.getAttribute('content') ?? null,\r\n metaDescription: metaDescription?.getAttribute('content') ?? null,\r\n };\r\n\r\n this._originalsSaved = true;\r\n }\r\n\r\n private _injectJsonLd(data: Record<string, unknown>): void {\r\n if (typeof document === 'undefined') return;\r\n\r\n // Remove any existing micdrop JSON-LD\r\n document.querySelectorAll('script[data-micdrop=\"jsonld\"]').forEach((el) => el.remove());\r\n\r\n const script = document.createElement('script');\r\n script.setAttribute('type', 'application/ld+json');\r\n script.setAttribute('data-micdrop', 'jsonld');\r\n // Use textContent (NOT innerHTML) to prevent XSS\r\n script.textContent = JSON.stringify(data);\r\n document.head.appendChild(script);\r\n }\r\n\r\n private _setMeta(property: string, content: string): void {\r\n if (typeof document === 'undefined') return;\r\n\r\n if (property.startsWith('og:')) {\r\n // OG tags use property attribute\r\n let el = document.querySelector(`meta[property=\"${property}\"]`) as HTMLMetaElement | null;\r\n if (el) {\r\n el.setAttribute('content', content);\r\n } else {\r\n el = document.createElement('meta');\r\n el.setAttribute('property', property);\r\n el.setAttribute('content', content);\r\n el.setAttribute('data-micdrop', 'og');\r\n document.head.appendChild(el);\r\n }\r\n } else {\r\n // Standard meta tags use name attribute (e.g., description)\r\n let el = document.querySelector(`meta[name=\"${property}\"]`) as HTMLMetaElement | null;\r\n if (el) {\r\n el.setAttribute('content', content);\r\n } else {\r\n el = document.createElement('meta');\r\n el.setAttribute('name', property);\r\n el.setAttribute('content', content);\r\n el.setAttribute('data-micdrop', 'meta');\r\n document.head.appendChild(el);\r\n }\r\n }\r\n }\r\n\r\n private _setCanonical(url: string): void {\r\n if (typeof document === 'undefined') return;\r\n\r\n let el = document.querySelector('link[rel=\"canonical\"]') as HTMLLinkElement | null;\r\n if (el) {\r\n el.setAttribute('href', url);\r\n } else {\r\n el = document.createElement('link');\r\n el.setAttribute('rel', 'canonical');\r\n el.setAttribute('href', url);\r\n el.setAttribute('data-micdrop', 'canonical');\r\n document.head.appendChild(el);\r\n }\r\n }\r\n\r\n private _removeAllInjected(): void {\r\n if (typeof document === 'undefined') return;\r\n document.querySelectorAll('[data-micdrop]').forEach((el) => el.remove());\r\n }\r\n\r\n private _restoreOriginals(): void {\r\n if (typeof document === 'undefined' || !this._originals) return;\r\n\r\n // Restore title\r\n document.title = this._originals.title;\r\n\r\n // Restore canonical\r\n if (this._originals.canonicalHref !== null) {\r\n const canonical = document.querySelector('link[rel=\"canonical\"]') as HTMLLinkElement | null;\r\n if (canonical) {\r\n canonical.setAttribute('href', this._originals.canonicalHref);\r\n }\r\n }\r\n\r\n // Restore OG tags\r\n this._restoreMetaProperty('og:title', this._originals.ogTitle);\r\n this._restoreMetaProperty('og:description', this._originals.ogDescription);\r\n this._restoreMetaProperty('og:image', this._originals.ogImage);\r\n this._restoreMetaProperty('og:url', this._originals.ogUrl);\r\n this._restoreMetaProperty('og:type', this._originals.ogType);\r\n\r\n // Restore meta description\r\n this._restoreMetaName('description', this._originals.metaDescription);\r\n\r\n this._originalsSaved = false;\r\n }\r\n\r\n private _restoreMetaProperty(property: string, originalContent: string | null): void {\r\n const el = document.querySelector(`meta[property=\"${property}\"]`) as HTMLMetaElement | null;\r\n if (originalContent !== null && el) {\r\n // Had original value -- restore it\r\n el.setAttribute('content', originalContent);\r\n } else if (originalContent === null && el?.hasAttribute('data-micdrop')) {\r\n // We created it -- remove it\r\n el.remove();\r\n }\r\n }\r\n\r\n private _restoreMetaName(name: string, originalContent: string | null): void {\r\n const el = document.querySelector(`meta[name=\"${name}\"]`) as HTMLMetaElement | null;\r\n if (originalContent !== null && el) {\r\n el.setAttribute('content', originalContent);\r\n } else if (originalContent === null && el?.hasAttribute('data-micdrop')) {\r\n el.remove();\r\n }\r\n }\r\n\r\n private _buildItemList(events: ListingEvent[]): Record<string, unknown> {\r\n return {\r\n '@context': 'https://schema.org',\r\n '@type': 'ItemList',\r\n itemListElement: events.map((event, i) => {\r\n const slug = event.slug || generateSlug(event.name);\r\n return {\r\n '@type': 'ListItem',\r\n position: i + 1,\r\n url: `${this._baseUrl}?e=${event.id}-${slug}`,\r\n };\r\n }),\r\n };\r\n }\r\n\r\n private _buildEntityUrl(type: string, id: string | number, slug: string): string {\r\n const prefix = type === 'event' ? 'e' : type === 'series' ? 's' : 'c';\r\n return `${this._baseUrl}?${prefix}=${id}-${slug}`;\r\n }\r\n\r\n // ---------------------------------------------------------------------------\r\n // Public API\r\n // ---------------------------------------------------------------------------\r\n\r\n /**\r\n * Called when events finish loading from the API.\r\n * Injects an ItemList JSON-LD for the listing. Does not modify OG/canonical/title.\r\n */\r\n onEventsLoaded(\r\n events: ListingEvent[],\r\n venue?: VenueData,\r\n ): void {\r\n if (typeof document === 'undefined') return;\r\n if (this._state === 'destroyed') return;\r\n\r\n // Update venue info from parameter if provided\r\n if (venue) {\r\n this._venueName = venue.name || this._venueName;\r\n this._venueAddress = venue.address || venue.googleLocationNameCache || this._venueAddress;\r\n const venueTz = venue.timeZone || venue.timezone;\r\n if (venueTz) {\r\n this._venueTimeZone = venueTz;\r\n }\r\n }\r\n\r\n // Store events for later use (re-injection on back to listing)\r\n this._events = events;\r\n\r\n // Build and inject ItemList JSON-LD\r\n const itemList = this._buildItemList(events);\r\n this._injectJsonLd(itemList);\r\n\r\n this._state = 'listing';\r\n }\r\n\r\n /**\r\n * Called when the user selects an event, series, or collection.\r\n * Replaces ItemList JSON-LD with entity-specific JSON-LD and updates\r\n * all head tags (canonical, OG, meta description, title).\r\n */\r\n onEntitySelected(\r\n type: 'event' | 'series' | 'collection',\r\n data: any,\r\n venue?: VenueData,\r\n ): void {\r\n if (typeof document === 'undefined') return;\r\n if (this._state === 'destroyed') return;\r\n\r\n // Save originals if not already saved\r\n this._saveOriginals();\r\n\r\n // Update venue info from parameter if provided\r\n if (venue) {\r\n this._venueName = venue.name || this._venueName;\r\n this._venueAddress = venue.address || venue.googleLocationNameCache || this._venueAddress;\r\n const venueTz = venue.timeZone || venue.timezone;\r\n if (venueTz) {\r\n this._venueTimeZone = venueTz;\r\n }\r\n }\r\n\r\n let jsonLd: Record<string, unknown> | null = null;\r\n let entityName = '';\r\n let entityDescription = '';\r\n let entityImage = '';\r\n let entityUrl = '';\r\n\r\n if (type === 'event') {\r\n const slug = data.slug || generateSlug(data.name || '');\r\n entityUrl = this._buildEntityUrl('event', data.id || data.eventID || '', slug);\r\n\r\n jsonLd = buildEventJsonLd({\r\n event: data,\r\n venue: {\r\n name: this._venueName,\r\n googleLocationNameCache: this._venueAddress,\r\n },\r\n venueTimeZone: this._venueTimeZone,\r\n eventUrl: entityUrl,\r\n });\r\n\r\n entityName = data.name || '';\r\n entityDescription = data.description || data.eventSummary || '';\r\n entityImage = data.image || data.coverImage || data.ShowImage || '';\r\n } else if (type === 'series') {\r\n const slug = data.slug || generateSlug(data.title || '');\r\n entityUrl = this._buildEntityUrl('series', data.id || '', slug);\r\n\r\n jsonLd = buildSeriesJsonLd({\r\n series: {\r\n ...data,\r\n venue: {\r\n name: this._venueName,\r\n googleLocationNameCache: this._venueAddress,\r\n },\r\n timeZone: this._venueTimeZone,\r\n },\r\n baseEventUrl: entityUrl,\r\n });\r\n\r\n entityName = data.title || '';\r\n entityDescription = data.description || data.eventSummary || '';\r\n entityImage = data.image || '';\r\n } else if (type === 'collection') {\r\n const slug = data.slug || generateSlug(data.collectionTitle || '');\r\n entityUrl = this._buildEntityUrl('collection', data.id || '', slug);\r\n\r\n jsonLd = buildCollectionJsonLd({\r\n collection: data,\r\n baseEventUrl: entityUrl,\r\n defaultTimeZone: this._venueTimeZone,\r\n });\r\n\r\n entityName = data.collectionTitle || '';\r\n entityDescription = data.description || data.summary || '';\r\n entityImage = data.coverImage || '';\r\n }\r\n\r\n // Inject JSON-LD (replaces existing micdrop JSON-LD)\r\n if (jsonLd) {\r\n this._injectJsonLd(jsonLd);\r\n }\r\n\r\n // Update canonical URL\r\n if (entityUrl) {\r\n this._setCanonical(entityUrl);\r\n }\r\n\r\n // Update OG tags\r\n if (entityName) {\r\n this._setMeta('og:title', entityName);\r\n }\r\n if (entityDescription) {\r\n this._setMeta('og:description', entityDescription);\r\n }\r\n if (entityImage) {\r\n this._setMeta('og:image', entityImage);\r\n }\r\n if (entityUrl) {\r\n this._setMeta('og:url', entityUrl);\r\n }\r\n this._setMeta('og:type', 'website');\r\n\r\n // Twitter Card: large image preview\r\n this._setMeta('twitter:card', 'summary_large_image');\r\n\r\n // Update meta description\r\n if (entityDescription) {\r\n this._setMeta('description', entityDescription);\r\n }\r\n\r\n // Update page title\r\n if (entityName) {\r\n document.title = `${entityName} | ${this._venueName}`;\r\n }\r\n\r\n this._state = 'entity';\r\n }\r\n\r\n /**\r\n * Called when the user navigates back to the event listing.\r\n * Restores original head elements and re-injects ItemList JSON-LD.\r\n */\r\n onBackToListing(): void {\r\n if (typeof document === 'undefined') return;\r\n if (this._state === 'destroyed') return;\r\n\r\n // Restore all originals\r\n this._restoreOriginals();\r\n\r\n // Remove any remaining injected elements\r\n this._removeAllInjected();\r\n\r\n // Re-inject ItemList JSON-LD if events are available\r\n if (this._events.length > 0) {\r\n const itemList = this._buildItemList(this._events);\r\n this._injectJsonLd(itemList);\r\n }\r\n\r\n // Re-save originals since we just restored them\r\n this._saveOriginals();\r\n\r\n this._state = 'listing';\r\n }\r\n\r\n /**\r\n * Called when the widget is destroyed. Removes all injected elements\r\n * and restores originals. Clears the static active instance.\r\n */\r\n destroy(): void {\r\n if (typeof document === 'undefined') return;\r\n if (this._state === 'destroyed') return;\r\n\r\n this._removeAllInjected();\r\n this._restoreOriginals();\r\n\r\n if (HostSeoController._activeInstance === this) {\r\n HostSeoController._activeInstance = null;\r\n }\r\n\r\n this._state = 'destroyed';\r\n }\r\n}\r\n"],"names":["getDefaultTimezone","escapeJsonLdForScript","json","formatDateTimeWithOffset","isoString","tz","date","parts","get","type","year","month","day","hour","minute","second","tzOffset","offset","match","sign","hours","mins","parseAddressToPostal","addressStr","p","stateZipMatch","buildPostalAddress","fallback","out","getEventStatus","rawStatus","status","getTicketAvailability","ticket","now","salesBegin","salesEnd","buildOffers","tickets","url","publicTickets","t","visibility","isVisible","isPublic","prices","lowPrice","highPrice","buildPerformers","performers","confirmed","isConfirmed","isNotHidden","profile","hasName","buildEventJsonLd","options","event","venue","venueTimeZone","eventUrl","organizer","startDate","postalAddress","location","data","endDate","description","imageUrl","offers","org","mapSeriesTicketAvailability","buildSeriesOccurrenceOffers","occurrenceStatus","activeTickets","buildSeriesJsonLd","series","baseEventUrl","address","subEvents","occ","subEvent","buildCollectionJsonLd","collection","itemListElement","listItem","generateSlug","text","_HostSeoController","canonical","ogTitle","ogDescription","ogImage","ogUrl","ogType","metaDescription","el","script","property","content","originalContent","name","events","i","slug","id","prefix","venueTz","itemList","jsonLd","entityName","entityDescription","entityImage","entityUrl","HostSeoController"],"mappings":"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","x_google_ignoreList":[0]}
|
|
1
|
+
{"version":3,"file":"seo.cjs","sources":["../../node_modules/@getmicdrop/svelte-components/dist/utils/timezones.js","../../src/lib/seo/helpers.ts","../../src/lib/seo/buildEventJsonLd.ts","../../src/lib/seo/buildSeriesJsonLd.ts","../../src/lib/seo/buildCollectionJsonLd.ts","../../src/lib/utils/textUtils.ts","../../src/lib/seo/HostSeoController.ts"],"sourcesContent":["/**\n * Timezone utility helpers.\n * Canonical source for timezone defaults and validation.\n */\n/**\n * Returns the default timezone for Micdrop, branded as `IANATimezone`.\n * Used when no explicit timezone is provided to date formatters. Bypass\n * call sites that pass `'America/Los_Angeles'` literally are red-squiggle\n * type errors at canonicals that take `IANATimezone` — the fix is to\n * thread `getDefaultTimezone()` through.\n *\n * @returns Branded IANA timezone (`'America/Los_Angeles'`)\n *\n * @example\n * formatDate(new Date(), {}, 'en-US', getDefaultTimezone())\n *\n * @deprecated Duplicate of the canonical default. Use `getDefaultTimezone`\n * (or the `DEFAULT_TIMEZONE` constant) from `$lib/datetime`\n * (re-exported via `datetime/constants`) — the single source of truth.\n */\nexport function getDefaultTimezone() {\n return 'America/Los_Angeles';\n}\n/**\n * Validates an IANA timezone string.\n * Uses Intl.DateTimeFormat to test if the timezone is recognized.\n *\n * @param tz - Timezone string to validate\n * @returns true if valid IANA timezone, false otherwise\n *\n * @example\n * isValidTimezone('America/Los_Angeles') // true\n * isValidTimezone('Invalid/Zone') // false\n *\n * @deprecated Lenient: `Intl.DateTimeFormat` accepts non-canonical inputs.\n * Use `isValidTimezone` from `$lib/datetime` (stricter — rejects\n * abbreviations like `'PST'`), or `toIANATimezone` from `$lib/types/brands`\n * when you need a branded value.\n */\nexport function isValidTimezone(tz) {\n try {\n new Intl.DateTimeFormat('en-US', { timeZone: tz });\n return true;\n }\n catch {\n return false;\n }\n}\n","/**\r\n * Shared helper utilities for JSON-LD builders.\r\n *\r\n * All functions are pure (no DOM, no Svelte, no side effects).\r\n * Extracted verbatim from EventStructuredData.svelte.\r\n *\r\n * @raw-intl-datetimeformat-escape: SEO/JSON-LD implementation. formatDateTimeWithOffset\r\n * must emit Google-required ISO-8601-with-tz-offset (\"2025-07-21T19:00:00-05:00\"), which\r\n * requires Intl.DateTimeFormat.formatToParts(...) with timeZoneName:'shortOffset' to read\r\n * the per-timezone UTC offset. The SC formatDate/DATE_FORMATS canonical produces\r\n * human-readable display strings, not parts-based offset extraction — it cannot express this.\r\n */\r\n\r\nimport { getDefaultTimezone } from '@getmicdrop/svelte-components/utils/timezones';\r\nimport type { EventTicketInput, EventPerformerInput } from './types';\r\n\r\n/**\r\n * Escape a JSON string for safe embedding inside an inline\r\n * <script type=\"application/ld+json\"> element.\r\n *\r\n * JSON.stringify does NOT escape `<`, `>` or `&`, so a field value containing\r\n * `</script><script>...` would break out of the script element and execute\r\n * (stored XSS). Replacing those characters with their unicode escape sequences\r\n * keeps the output valid JSON (parsers decode `<` back to `<`) while making\r\n * it impossible to terminate the surrounding <script> tag.\r\n *\r\n * This is the standard JSON-LD-in-<script> hardening; the sibling\r\n * HostSeoController avoids the issue entirely by using element.textContent.\r\n */\r\nexport function escapeJsonLdForScript(json: string): string {\r\n return json\r\n .replace(/</g, '\\\\u003c')\r\n .replace(/>/g, '\\\\u003e')\r\n .replace(/&/g, '\\\\u0026');\r\n}\r\n\r\n/**\r\n * Format datetime to ISO-8601 with timezone offset.\r\n * Google requires: \"2025-07-21T19:00:00-05:00\"\r\n *\r\n * Extracted from EventStructuredData.svelte lines 25-68.\r\n */\r\nexport function formatDateTimeWithOffset(\r\n isoString: string | null | undefined,\r\n tz: string\r\n): string | null {\r\n if (!isoString) return null;\r\n\r\n const date = new Date(isoString);\r\n if (isNaN(date.getTime())) return null;\r\n\r\n // Get the offset in the target timezone\r\n const offsetFormatOptions: Intl.DateTimeFormatOptions = {\r\n year: 'numeric',\r\n month: '2-digit',\r\n day: '2-digit',\r\n hour: '2-digit',\r\n minute: '2-digit',\r\n second: '2-digit',\r\n hour12: false,\r\n timeZoneName: 'shortOffset',\r\n };\r\n\r\n let formatter: Intl.DateTimeFormat;\r\n try {\r\n formatter = new Intl.DateTimeFormat('en-US', { ...offsetFormatOptions, timeZone: tz });\r\n } catch {\r\n // An invalid IANA timezone makes Intl.DateTimeFormat throw a RangeError,\r\n // which would otherwise escape the JSON-LD builders unguarded. Fall back\r\n // to the canonical default timezone — the same invalid-tz fallback the\r\n // rest of the repo uses ($lib/utils/datetime.js formatInTz).\r\n formatter = new Intl.DateTimeFormat('en-US', {\r\n ...offsetFormatOptions,\r\n timeZone: getDefaultTimezone(),\r\n });\r\n }\r\n\r\n const parts = formatter.formatToParts(date);\r\n const get = (type: string) => parts.find(p => p.type === type)?.value || '';\r\n\r\n const year = get('year');\r\n const month = get('month');\r\n const day = get('day');\r\n const hour = get('hour');\r\n const minute = get('minute');\r\n const second = get('second');\r\n const tzOffset = get('timeZoneName'); // e.g., \"GMT-5\" or \"GMT+2\"\r\n\r\n // Convert \"GMT-5\" to \"-05:00\" format\r\n let offset = '+00:00';\r\n if (tzOffset) {\r\n const match = tzOffset.match(/GMT([+-])(\\d+)(?::(\\d+))?/);\r\n if (match) {\r\n const sign = match[1];\r\n const hours = match[2].padStart(2, '0');\r\n const mins = match[3] || '00';\r\n offset = `${sign}${hours}:${mins}`;\r\n }\r\n }\r\n\r\n return `${year}-${month}-${day}T${hour}:${minute}:${second}${offset}`;\r\n}\r\n\r\n/**\r\n * Parse address string into PostalAddress components.\r\n * Input: \"7702 Santa Monica Blvd, West Hollywood, CA 90046\"\r\n * Output: { @type, streetAddress, addressLocality, addressRegion, postalCode, addressCountry }\r\n *\r\n * Extracted from EventStructuredData.svelte lines 75-111.\r\n */\r\nexport function parseAddressToPostal(\r\n addressStr: string | null | undefined\r\n): Record<string, string> | null {\r\n if (!addressStr) return null;\r\n\r\n const parts = addressStr.split(',').map(p => p.trim());\r\n\r\n if (parts.length < 2) {\r\n return {\r\n '@type': 'PostalAddress',\r\n streetAddress: addressStr,\r\n addressCountry: 'US',\r\n };\r\n }\r\n\r\n // Check if last part is \"State ZIP\" (e.g., \"CA 90046\")\r\n const lastPart = parts[parts.length - 1];\r\n const stateZipMatch = lastPart.match(/^([A-Z]{2})\\s+(\\d{5}(?:-\\d{4})?)$/);\r\n\r\n if (stateZipMatch && parts.length >= 3) {\r\n // Standard US address format\r\n return {\r\n '@type': 'PostalAddress',\r\n streetAddress: parts.slice(0, -2).join(', '),\r\n addressLocality: parts[parts.length - 2],\r\n addressRegion: stateZipMatch[1],\r\n postalCode: stateZipMatch[2],\r\n addressCountry: 'US',\r\n };\r\n }\r\n\r\n // Fallback for non-standard formats\r\n return {\r\n '@type': 'PostalAddress',\r\n streetAddress: parts[0],\r\n addressLocality: parts.slice(1).join(', '),\r\n addressCountry: 'US',\r\n };\r\n}\r\n\r\n/**\r\n * Structured postal-address parts, as the public resolve/series payloads now\r\n * expose them: `address` is the STREET line, with city/state/postalCode/country\r\n * as discrete fields (PublicVenueInfo / SeriesVenueDTO).\r\n */\r\nexport interface PostalAddressParts {\r\n address?: string | null; // street line\r\n city?: string | null;\r\n state?: string | null;\r\n postalCode?: string | null;\r\n country?: string | null;\r\n}\r\n\r\n/**\r\n * Build a schema.org PostalAddress, preferring discrete structured fields when\r\n * any locality signal (city / state / postalCode) is present, and otherwise\r\n * falling back to parsing a single comma-separated string (the street `address`\r\n * field if it carries a full address, else `googleLocationNameCache`).\r\n *\r\n * Discrete fields are more reliable than string-splitting — Google requires a\r\n * well-formed PostalAddress for Event rich-result eligibility (online-only\r\n * events lost eligibility June 2025).\r\n */\r\nexport function buildPostalAddress(\r\n parts: PostalAddressParts | null | undefined,\r\n fallback?: string | null\r\n): Record<string, string> | null {\r\n if (parts && (parts.city || parts.state || parts.postalCode)) {\r\n const out: Record<string, string> = { '@type': 'PostalAddress' };\r\n if (parts.address) out.streetAddress = parts.address;\r\n if (parts.city) out.addressLocality = parts.city;\r\n if (parts.state) out.addressRegion = parts.state;\r\n if (parts.postalCode) out.postalCode = parts.postalCode;\r\n out.addressCountry = parts.country || 'US';\r\n return out;\r\n }\r\n // No discrete locality fields — parse the best single string we have. The\r\n // street `address` may legacy-hold a full comma-separated address, so prefer\r\n // it over the location-name cache.\r\n return parseAddressToPostal((parts && parts.address) || fallback || null);\r\n}\r\n\r\n/**\r\n * Map event status to schema.org EventStatusType.\r\n *\r\n * Extracted from EventStructuredData.svelte lines 116-132.\r\n */\r\nexport function getEventStatus(rawStatus: string | null | undefined): string {\r\n if (!rawStatus) return 'https://schema.org/EventScheduled';\r\n\r\n const status = rawStatus.toLowerCase();\r\n\r\n if (status === 'cancelled' || status === 'canceled') {\r\n return 'https://schema.org/EventCancelled';\r\n }\r\n if (status === 'postponed') {\r\n return 'https://schema.org/EventPostponed';\r\n }\r\n if (status === 'rescheduled') {\r\n return 'https://schema.org/EventRescheduled';\r\n }\r\n\r\n return 'https://schema.org/EventScheduled';\r\n}\r\n\r\n/**\r\n * Map ticket availability to schema.org ItemAvailability.\r\n *\r\n * Extracted from EventStructuredData.svelte lines 137-157.\r\n */\r\nexport function getTicketAvailability(ticket: EventTicketInput): string {\r\n const remaining =\r\n ticket.remainingCapacity ?? ticket.quantityRemaining ?? ticket.quantity;\r\n\r\n if (remaining === 0 || ticket.soldOut) {\r\n return 'https://schema.org/SoldOut';\r\n }\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\r\n if (salesBegin && new Date(salesBegin) > now) {\r\n return 'https://schema.org/PreOrder';\r\n }\r\n\r\n if (salesEnd && new Date(salesEnd) < now) {\r\n return 'https://schema.org/SoldOut';\r\n }\r\n\r\n return 'https://schema.org/InStock';\r\n}\r\n\r\n/**\r\n * Build offers from available tickets.\r\n *\r\n * Extracted from EventStructuredData.svelte lines 162-208.\r\n */\r\nexport function buildOffers(\r\n tickets: EventTicketInput[] | null | undefined,\r\n url?: string\r\n): Record<string, unknown> | null {\r\n if (!tickets || tickets.length === 0) return null;\r\n\r\n // Filter to public tickets only (exclude door-only and hidden)\r\n const publicTickets = tickets.filter(t => {\r\n const visibility = t.visibility ?? 0;\r\n const isVisible = visibility === 0 || visibility === 1;\r\n const isPublic = t.salesChannel !== 2;\r\n return isVisible && isPublic;\r\n });\r\n\r\n if (publicTickets.length === 0) return null;\r\n\r\n // Find lowest and highest price\r\n const prices = publicTickets\r\n .map(t => parseFloat(String(t.price ?? 0)))\r\n .filter(p => !isNaN(p));\r\n\r\n if (prices.length === 0) return null;\r\n\r\n const lowPrice = Math.min(...prices);\r\n const highPrice = Math.max(...prices);\r\n\r\n // Use AggregateOffer when there are multiple price points\r\n if (lowPrice !== highPrice) {\r\n return {\r\n '@type': 'AggregateOffer',\r\n lowPrice: lowPrice,\r\n highPrice: highPrice,\r\n priceCurrency: 'USD',\r\n availability: getTicketAvailability(publicTickets[0]),\r\n url: url || undefined,\r\n validFrom:\r\n publicTickets[0]?.salesBegin ||\r\n publicTickets[0]?.salesStart ||\r\n undefined,\r\n };\r\n }\r\n\r\n // Single price point - use Offer\r\n const ticket = publicTickets[0];\r\n return {\r\n '@type': 'Offer',\r\n price: lowPrice,\r\n priceCurrency: 'USD',\r\n availability: getTicketAvailability(ticket),\r\n url: url || undefined,\r\n validFrom: ticket?.salesBegin || ticket?.salesStart || undefined,\r\n };\r\n}\r\n\r\n/**\r\n * Build performer array from event performers.\r\n *\r\n * Extracted from EventStructuredData.svelte lines 213-237.\r\n */\r\nexport function buildPerformers(\r\n performers: EventPerformerInput[] | null | undefined\r\n): Array<Record<string, string>> | null {\r\n if (!performers || performers.length === 0) return null;\r\n\r\n // Filter to confirmed, visible performers\r\n const confirmed = performers.filter(p => {\r\n const isConfirmed = p.acceptedState === 2;\r\n const isNotHidden = !p.shouldBeHidden;\r\n const profile = p.RosterPerformer?.User?.performerProfile;\r\n const hasName =\r\n profile?.firstName || profile?.lastName || profile?.displayName;\r\n return isConfirmed && isNotHidden && hasName;\r\n });\r\n\r\n if (confirmed.length === 0) return null;\r\n\r\n return confirmed.map(p => {\r\n const profile = p.RosterPerformer?.User?.performerProfile;\r\n const name =\r\n profile?.displayName ||\r\n [profile?.firstName, profile?.lastName].filter(Boolean).join(' ');\r\n\r\n return {\r\n '@type': 'PerformingGroup',\r\n name: name,\r\n };\r\n });\r\n}\r\n","import { getDefaultTimezone } from '@getmicdrop/svelte-components/utils/timezones';\n/**\n * Pure function that builds Event JSON-LD structured data for Google Events rich results.\n *\n * Extracted from EventStructuredData.svelte (lines 242-313).\n * Produces identical output to the original $derived.by block.\n */\n\nimport type { EventJsonLdInput, VenueInput, OrganizerInput } from './types';\nimport {\n formatDateTimeWithOffset,\n buildPostalAddress,\n getEventStatus,\n buildOffers,\n buildPerformers,\n} from './helpers';\n\nexport interface BuildEventJsonLdOptions {\n event: EventJsonLdInput;\n venue: VenueInput;\n venueTimeZone?: string;\n eventUrl?: string;\n organizer?: OrganizerInput;\n}\n\nexport function buildEventJsonLd(\n options: BuildEventJsonLdOptions\n): Record<string, unknown> | null {\n const {\n event,\n venue,\n venueTimeZone = getDefaultTimezone(),\n eventUrl,\n organizer,\n } = options;\n\n if (!event) return null;\n\n const startDate = formatDateTimeWithOffset(\n event.startDateTime,\n venueTimeZone\n );\n\n // Skip if we don't have required fields\n if (!event.name || !startDate || !venue?.name) {\n return null;\n }\n\n // Google requires a PostalAddress on a physical event's location (online-only\n // events lost rich-result eligibility June 2025). Prefer the venue's discrete\n // address fields (street/city/state/postalCode), falling back to parsing the\n // googleLocationNameCache string. Only attach `address` when we could build\n // one — never emit `address: null`. A missing address is a data-pipeline gap\n // (the resolve payload must carry the venue address).\n const postalAddress = buildPostalAddress(\n venue,\n venue?.googleLocationNameCache\n );\n const location: Record<string, unknown> = {\n '@type': 'Place',\n name: venue.name,\n };\n if (postalAddress) {\n location.address = postalAddress;\n }\n\n const data: Record<string, unknown> = {\n '@context': 'https://schema.org',\n '@type': 'Event',\n name: event.name,\n startDate: startDate,\n eventStatus: getEventStatus(event.status),\n eventAttendanceMode: 'https://schema.org/OfflineEventAttendanceMode',\n location,\n };\n\n // Add endDate if available\n const endDate = formatDateTimeWithOffset(event.endDateTime, venueTimeZone);\n if (endDate) {\n data.endDate = endDate;\n }\n\n // Add description\n const description = event.description || event.eventSummary;\n if (description) {\n data.description = description;\n }\n\n // Add image - Google recommends multiple aspect ratios\n const imageUrl = event.image || event.coverImage || event.ShowImage;\n if (imageUrl) {\n data.image = [imageUrl];\n }\n\n // Add offers from tickets\n const offers = buildOffers(event.availableTickets, eventUrl);\n if (offers) {\n data.offers = offers;\n }\n\n // Add performers\n const performers = buildPerformers(event.performers);\n if (performers && performers.length > 0) {\n data.performer = performers;\n }\n\n // Add organizer if provided\n if (organizer?.name) {\n const org: Record<string, string> = {\n '@type': 'Organization',\n name: organizer.name,\n };\n if (organizer.url) {\n org.url = organizer.url;\n }\n data.organizer = org;\n }\n\n // Add event URL if provided\n if (eventUrl) {\n data.url = eventUrl;\n }\n\n return data;\n}\n","/**\n * Pure function that builds EventSeries JSON-LD structured data.\n *\n * Series use schema.org EventSeries type with subEvent array containing\n * individual Event blocks for each occurrence. Each subEvent has its own\n * offers built from the simplified series ticket DTOs (different shape\n * from the event builder's tickets).\n *\n * Google Rich Results Test does NOT support EventSeries directly, but\n * the individual subEvent Event blocks can qualify for rich results.\n */\n\nimport type { SeriesJsonLdInput } from './types';\nimport {\n formatDateTimeWithOffset,\n buildPostalAddress,\n getEventStatus,\n} from './helpers';\n\nexport interface BuildSeriesJsonLdOptions {\n series: SeriesJsonLdInput;\n baseEventUrl?: string; // e.g., \"https://get-micdrop.com/e\" -- used to build subEvent @id URLs\n}\n\n/**\n * Map a series ticket status string to schema.org ItemAvailability.\n *\n * Series tickets use a simplified { name, price, status } shape from the\n * backend (SeriesTicketDTO), unlike event tickets which have visibility,\n * salesChannel, remainingCapacity, etc.\n */\nfunction mapSeriesTicketAvailability(status: string): string {\n switch (status) {\n case 'available':\n return 'https://schema.org/InStock';\n case 'sold_out':\n return 'https://schema.org/SoldOut';\n case 'coming_soon':\n return 'https://schema.org/PreOrder';\n case 'ended':\n return 'https://schema.org/SoldOut';\n default:\n return 'https://schema.org/InStock';\n }\n}\n\n/**\n * Build offers from series occurrence tickets.\n *\n * Series tickets have a simpler shape than event tickets: { name, price, status }.\n * Uses AggregateOffer for multiple price points, Offer for single price.\n */\nfunction buildSeriesOccurrenceOffers(\n tickets: Array<{ name: string; price: number; status: string }> | undefined,\n occurrenceStatus: string | undefined\n): Record<string, unknown> | null {\n if (!tickets || tickets.length === 0) return null;\n\n // Filter out sold_out and ended tickets for offer building\n const activeTickets = tickets.filter(\n t => t.status !== 'sold_out' && t.status !== 'ended'\n );\n\n // If all tickets are sold out / ended, report SoldOut with the price range\n if (activeTickets.length === 0) {\n const prices = tickets.map(t => t.price).filter(p => !isNaN(p));\n if (prices.length === 0) return null;\n\n const lowPrice = Math.min(...prices);\n const highPrice = Math.max(...prices);\n\n if (lowPrice !== highPrice) {\n return {\n '@type': 'AggregateOffer',\n lowPrice,\n highPrice,\n priceCurrency: 'USD',\n availability: 'https://schema.org/SoldOut',\n };\n }\n\n return {\n '@type': 'Offer',\n price: lowPrice,\n priceCurrency: 'USD',\n availability: 'https://schema.org/SoldOut',\n };\n }\n\n const prices = activeTickets.map(t => t.price).filter(p => !isNaN(p));\n if (prices.length === 0) return null;\n\n const lowPrice = Math.min(...prices);\n const highPrice = Math.max(...prices);\n\n if (lowPrice !== highPrice) {\n return {\n '@type': 'AggregateOffer',\n lowPrice,\n highPrice,\n priceCurrency: 'USD',\n availability: mapSeriesTicketAvailability(activeTickets[0].status),\n };\n }\n\n return {\n '@type': 'Offer',\n price: lowPrice,\n priceCurrency: 'USD',\n availability: mapSeriesTicketAvailability(activeTickets[0].status),\n };\n}\n\nexport function buildSeriesJsonLd(\n options: BuildSeriesJsonLdOptions\n): Record<string, unknown> | null {\n const { series, baseEventUrl } = options;\n\n // Required fields\n if (!series.title) return null;\n if (!series.occurrences || series.occurrences.length === 0) return null;\n if (!series.venue?.name) return null;\n\n // Build location — prefer the venue's discrete address fields, falling back to\n // parsing googleLocationNameCache (see buildEventJsonLd for the rationale).\n const address = buildPostalAddress(\n series.venue,\n series.venue.googleLocationNameCache\n );\n const location: Record<string, unknown> = {\n '@type': 'Place',\n name: series.venue.name,\n };\n if (address) {\n location.address = address;\n }\n\n // Build parent EventSeries\n const data: Record<string, unknown> = {\n '@context': 'https://schema.org',\n '@type': 'EventSeries',\n name: series.title,\n location,\n };\n\n // Description: prefer description, fall back to eventSummary\n const description = series.description || series.eventSummary;\n if (description) {\n data.description = description;\n }\n\n // Image\n if (series.image) {\n data.image = [series.image];\n }\n\n // Performers: flat displayName array (different from event builder's nested shape)\n const performers = series.performers?.filter(p => p.displayName);\n if (performers && performers.length > 0) {\n data.performer = performers.map(p => ({\n '@type': 'PerformingGroup',\n name: p.displayName,\n }));\n }\n\n // SubEvents: build Event blocks for each occurrence\n const subEvents: Record<string, unknown>[] = [];\n\n for (const occ of series.occurrences) {\n const startDate = formatDateTimeWithOffset(\n occ.startDateTime,\n series.timeZone\n );\n if (!startDate) continue; // Skip occurrences without valid startDate\n\n const subEvent: Record<string, unknown> = {\n '@type': 'Event',\n name: occ.title,\n startDate,\n eventStatus: getEventStatus(occ.status),\n location,\n };\n\n // @id from baseEventUrl\n if (baseEventUrl) {\n subEvent['@id'] = `${baseEventUrl}/${occ.id}-${occ.slug}`;\n }\n\n // endDate\n const endDate = formatDateTimeWithOffset(occ.endDateTime, series.timeZone);\n if (endDate) {\n subEvent.endDate = endDate;\n }\n\n // Offers from simplified ticket DTOs\n const offers = buildSeriesOccurrenceOffers(occ.tickets, occ.status);\n if (offers) {\n subEvent.offers = offers;\n }\n\n subEvents.push(subEvent);\n }\n\n // If all occurrences were skipped, return null\n if (subEvents.length === 0) return null;\n\n data.subEvent = subEvents;\n\n return data;\n}\n","/**\r\n * Pure function that builds CollectionPage JSON-LD for an event collection.\r\n *\r\n * A collection is a curated list of shows — NOT a single event, and NOT a\r\n * Festival (the product can't infer festival intent from a collection named\r\n * \"Detroit Comedy Festival\"; it's still just a collection). We emit a\r\n * schema.org `CollectionPage` whose `mainEntity` is a *summary-page* `ItemList`\r\n * — each `ListItem` is just `{ position, name, url }` pointing at the show's\r\n * canonical `/e/{id}-{slug}` leaf. The full `Event` rich-result markup lives on\r\n * each event's own page (one event = one leaf URL); duplicating full Event\r\n * objects here would create competing entities and trigger missing-field\r\n * (offers/address) warnings on the collection page.\r\n *\r\n * (Previously emitted `Festival` + `subEvent`, which Google's Rich Results\r\n * Test does not support.)\r\n */\r\n\r\nimport type { CollectionJsonLdInput } from './types';\r\n\r\nexport interface BuildCollectionJsonLdOptions {\r\n collection: CollectionJsonLdInput;\r\n baseEventUrl?: string; // e.g., \"https://get-micdrop.com/e\"\r\n /** Accepted for API compatibility; the summary ItemList carries no dates. */\r\n defaultTimeZone?: string;\r\n}\r\n\r\nexport function buildCollectionJsonLd(\r\n options: BuildCollectionJsonLdOptions,\r\n): Record<string, unknown> | null {\r\n const { collection, baseEventUrl } = options;\r\n\r\n // Required fields\r\n if (!collection.collectionTitle) return null;\r\n if (!collection.events || collection.events.length === 0) return null;\r\n\r\n // Build a summary ListItem per collection member: position + name + the\r\n // canonical leaf url. Prefer the slugged form, else bare `/e/{id}` (the app\r\n // self-heals bare ids to the slugged canonical).\r\n const itemListElement: Record<string, unknown>[] = [];\r\n\r\n for (const event of collection.events) {\r\n if (!event.title) continue; // Skip members with no name to list\r\n\r\n const listItem: Record<string, unknown> = {\r\n '@type': 'ListItem',\r\n position: itemListElement.length + 1,\r\n name: event.title,\r\n };\r\n\r\n if (baseEventUrl) {\r\n listItem.url = event.slug\r\n ? `${baseEventUrl}/${event.id}-${event.slug}`\r\n : `${baseEventUrl}/${event.id}`;\r\n }\r\n\r\n itemListElement.push(listItem);\r\n }\r\n\r\n // If every event was skipped, there's nothing to mark up.\r\n if (itemListElement.length === 0) return null;\r\n\r\n const data: Record<string, unknown> = {\r\n '@context': 'https://schema.org',\r\n '@type': 'CollectionPage',\r\n name: collection.collectionTitle,\r\n };\r\n\r\n // Description: prefer description, fall back to summary\r\n const description = collection.description || collection.summary;\r\n if (description) {\r\n data.description = description;\r\n }\r\n\r\n // Cover image\r\n if (collection.coverImage) {\r\n data.image = [collection.coverImage];\r\n }\r\n\r\n data.mainEntity = {\r\n '@type': 'ItemList',\r\n numberOfItems: itemListElement.length,\r\n itemListElement,\r\n };\r\n\r\n return data;\r\n}\r\n","/**\r\n * Text utility functions: generateSlug, validateNegativeNumber\r\n *\r\n * classNames and truncateTitle re-exported from @getmicdrop/svelte-components\r\n * via ./utils.js for backward compatibility with existing consumers.\r\n */\r\n\r\n/**\r\n * Generate a URL-friendly slug from a string\r\n */\r\nexport function generateSlug(text: string): string {\r\n if (!text) return '';\r\n\r\n return text\r\n .toLowerCase()\r\n .trim()\r\n .replace(/[^\\w\\s-]/g, '')\r\n .replace(/\\s+/g, '-')\r\n .replace(/--+/g, '-')\r\n .replace(/^-+|-+$/g, '');\r\n}\r\n\r\n/**\r\n * Validate that a value is a positive number\r\n * Returns an error message string, or empty string if valid\r\n */\r\nexport function validateNegativeNumber(\r\n value: string | number | null | undefined\r\n): string {\r\n if (value === null || value === undefined || value === '') {\r\n return 'Must be greater than zero.';\r\n }\r\n\r\n const stringValue = String(value).trim();\r\n\r\n if (\r\n stringValue.includes('--') ||\r\n stringValue.includes('++') ||\r\n stringValue.match(/^-+$/) ||\r\n stringValue.match(/^\\++$/)\r\n ) {\r\n return 'Please enter a valid number';\r\n }\r\n\r\n if (stringValue.match(/^-?\\d+-$/)) {\r\n return 'Please enter a valid number';\r\n }\r\n\r\n if ((stringValue.match(/\\./g) || []).length > 1) {\r\n return 'Please enter a valid number';\r\n }\r\n\r\n const numValue = Number(value);\r\n if (isNaN(numValue)) {\r\n return 'Please enter a valid number';\r\n }\r\n\r\n if (numValue < 0 || numValue === 0) {\r\n return 'Price must be greater than zero.';\r\n }\r\n\r\n return '';\r\n}\r\n","import { getDefaultTimezone } from '@getmicdrop/svelte-components/utils/timezones';\r\n/**\r\n * HostSeoController -- manages JSON-LD structured data, canonical URLs,\r\n * OG tags, meta descriptions, and page title injection into venue pages'\r\n * <head> when the Micdrop calendar widget is embedded.\r\n *\r\n * Follows a save-replace-restore pattern to be a \"good citizen\" on venue pages:\r\n * idle -> listing -> entity -> listing -> ... -> destroyed\r\n *\r\n * Uses textContent (never innerHTML) for JSON-LD script tags to prevent XSS.\r\n */\r\n\r\nimport type { HostSeoControllerOptions } from './types';\r\nimport { buildEventJsonLd } from './buildEventJsonLd';\r\nimport { buildSeriesJsonLd } from './buildSeriesJsonLd';\r\nimport { buildCollectionJsonLd } from './buildCollectionJsonLd';\r\nimport { generateSlug } from '$lib/utils/textUtils';\r\n\r\ntype ControllerState = 'idle' | 'listing' | 'entity' | 'destroyed';\r\n\r\ninterface SavedOriginals {\r\n title: string;\r\n canonicalHref: string | null;\r\n ogTitle: string | null;\r\n ogDescription: string | null;\r\n ogImage: string | null;\r\n ogUrl: string | null;\r\n ogType: string | null;\r\n metaDescription: string | null;\r\n}\r\n\r\n// Minimal event shape for listing mode\r\ninterface ListingEvent {\r\n id: number | string;\r\n name: string;\r\n slug?: string;\r\n startDateTime?: string;\r\n image?: string;\r\n}\r\n\r\n// Venue data passed at runtime\r\ninterface VenueData {\r\n name: string;\r\n address?: string;\r\n googleLocationNameCache?: string;\r\n // Transformed venues carry lowercase `timezone`; raw/host data may use\r\n // PascalCase `timeZone`. Accept both so the venue tz isn't silently dropped.\r\n timeZone?: string;\r\n timezone?: string;\r\n}\r\n\r\nexport class HostSeoController {\r\n /**\r\n * Single active instance. A venue page that mounts >1 calendar embed\r\n * (e.g. \"upcoming shows\" + \"this weekend\") will see the *later*\r\n * controller take over `<head>` — the earlier one is destroyed.\r\n *\r\n * KNOWN LIMITATION: multi-embed pages cannot run two SEO controllers\r\n * in parallel because they would both fight to own `<title>`/OG/etc.\r\n * Pass `data-inject-seo=\"false\"` on all but one embed to opt out.\r\n */\r\n static _activeInstance: HostSeoController | null = null;\r\n\r\n private _state: ControllerState = 'idle';\r\n private _venueName: string;\r\n private _venueAddress: string | undefined;\r\n private _venueTimeZone: string;\r\n private _baseUrl: string;\r\n private _originals: SavedOriginals | null = null;\r\n private _originalsSaved = false;\r\n private _events: ListingEvent[] = [];\r\n\r\n constructor(options: HostSeoControllerOptions) {\r\n // SSR guard\r\n if (typeof document === 'undefined') return;\r\n\r\n // If another instance exists, destroy it first.\r\n if (\r\n HostSeoController._activeInstance &&\r\n HostSeoController._activeInstance !== this\r\n ) {\r\n HostSeoController._activeInstance.destroy();\r\n }\r\n\r\n this._venueName = options.venueName;\r\n this._venueAddress = options.venueAddress;\r\n this._venueTimeZone = options.venueTimeZone || getDefaultTimezone();\r\n this._baseUrl =\r\n options.baseUrl || window.location.origin + window.location.pathname;\r\n\r\n this._saveOriginals();\r\n\r\n HostSeoController._activeInstance = this;\r\n }\r\n\r\n // ---------------------------------------------------------------------------\r\n // Private helpers\r\n // ---------------------------------------------------------------------------\r\n\r\n private _saveOriginals(): void {\r\n if (this._originalsSaved) return;\r\n\r\n const canonical = document.querySelector('link[rel=\"canonical\"]') as HTMLLinkElement | null;\r\n const ogTitle = document.querySelector('meta[property=\"og:title\"]') as HTMLMetaElement | null;\r\n const ogDescription = document.querySelector('meta[property=\"og:description\"]') as HTMLMetaElement | null;\r\n const ogImage = document.querySelector('meta[property=\"og:image\"]') as HTMLMetaElement | null;\r\n const ogUrl = document.querySelector('meta[property=\"og:url\"]') as HTMLMetaElement | null;\r\n const ogType = document.querySelector('meta[property=\"og:type\"]') as HTMLMetaElement | null;\r\n const metaDescription = document.querySelector('meta[name=\"description\"]') as HTMLMetaElement | null;\r\n\r\n this._originals = {\r\n title: document.title,\r\n canonicalHref: canonical?.getAttribute('href') ?? null,\r\n ogTitle: ogTitle?.getAttribute('content') ?? null,\r\n ogDescription: ogDescription?.getAttribute('content') ?? null,\r\n ogImage: ogImage?.getAttribute('content') ?? null,\r\n ogUrl: ogUrl?.getAttribute('content') ?? null,\r\n ogType: ogType?.getAttribute('content') ?? null,\r\n metaDescription: metaDescription?.getAttribute('content') ?? null,\r\n };\r\n\r\n this._originalsSaved = true;\r\n }\r\n\r\n private _injectJsonLd(data: Record<string, unknown>): void {\r\n if (typeof document === 'undefined') return;\r\n\r\n // Remove any existing micdrop JSON-LD\r\n document.querySelectorAll('script[data-micdrop=\"jsonld\"]').forEach((el) => el.remove());\r\n\r\n const script = document.createElement('script');\r\n script.setAttribute('type', 'application/ld+json');\r\n script.setAttribute('data-micdrop', 'jsonld');\r\n // Use textContent (NOT innerHTML) to prevent XSS\r\n script.textContent = JSON.stringify(data);\r\n document.head.appendChild(script);\r\n }\r\n\r\n private _setMeta(property: string, content: string): void {\r\n if (typeof document === 'undefined') return;\r\n\r\n if (property.startsWith('og:')) {\r\n // OG tags use property attribute\r\n let el = document.querySelector(`meta[property=\"${property}\"]`) as HTMLMetaElement | null;\r\n if (el) {\r\n el.setAttribute('content', content);\r\n } else {\r\n el = document.createElement('meta');\r\n el.setAttribute('property', property);\r\n el.setAttribute('content', content);\r\n el.setAttribute('data-micdrop', 'og');\r\n document.head.appendChild(el);\r\n }\r\n } else {\r\n // Standard meta tags use name attribute (e.g., description)\r\n let el = document.querySelector(`meta[name=\"${property}\"]`) as HTMLMetaElement | null;\r\n if (el) {\r\n el.setAttribute('content', content);\r\n } else {\r\n el = document.createElement('meta');\r\n el.setAttribute('name', property);\r\n el.setAttribute('content', content);\r\n el.setAttribute('data-micdrop', 'meta');\r\n document.head.appendChild(el);\r\n }\r\n }\r\n }\r\n\r\n private _setCanonical(url: string): void {\r\n if (typeof document === 'undefined') return;\r\n\r\n let el = document.querySelector('link[rel=\"canonical\"]') as HTMLLinkElement | null;\r\n if (el) {\r\n el.setAttribute('href', url);\r\n } else {\r\n el = document.createElement('link');\r\n el.setAttribute('rel', 'canonical');\r\n el.setAttribute('href', url);\r\n el.setAttribute('data-micdrop', 'canonical');\r\n document.head.appendChild(el);\r\n }\r\n }\r\n\r\n private _removeAllInjected(): void {\r\n if (typeof document === 'undefined') return;\r\n document.querySelectorAll('[data-micdrop]').forEach((el) => el.remove());\r\n }\r\n\r\n private _restoreOriginals(): void {\r\n if (typeof document === 'undefined' || !this._originals) return;\r\n\r\n // Restore title\r\n document.title = this._originals.title;\r\n\r\n // Restore canonical\r\n if (this._originals.canonicalHref !== null) {\r\n const canonical = document.querySelector('link[rel=\"canonical\"]') as HTMLLinkElement | null;\r\n if (canonical) {\r\n canonical.setAttribute('href', this._originals.canonicalHref);\r\n }\r\n }\r\n\r\n // Restore OG tags\r\n this._restoreMetaProperty('og:title', this._originals.ogTitle);\r\n this._restoreMetaProperty('og:description', this._originals.ogDescription);\r\n this._restoreMetaProperty('og:image', this._originals.ogImage);\r\n this._restoreMetaProperty('og:url', this._originals.ogUrl);\r\n this._restoreMetaProperty('og:type', this._originals.ogType);\r\n\r\n // Restore meta description\r\n this._restoreMetaName('description', this._originals.metaDescription);\r\n\r\n this._originalsSaved = false;\r\n }\r\n\r\n private _restoreMetaProperty(property: string, originalContent: string | null): void {\r\n const el = document.querySelector(`meta[property=\"${property}\"]`) as HTMLMetaElement | null;\r\n if (originalContent !== null && el) {\r\n // Had original value -- restore it\r\n el.setAttribute('content', originalContent);\r\n } else if (originalContent === null && el?.hasAttribute('data-micdrop')) {\r\n // We created it -- remove it\r\n el.remove();\r\n }\r\n }\r\n\r\n private _restoreMetaName(name: string, originalContent: string | null): void {\r\n const el = document.querySelector(`meta[name=\"${name}\"]`) as HTMLMetaElement | null;\r\n if (originalContent !== null && el) {\r\n el.setAttribute('content', originalContent);\r\n } else if (originalContent === null && el?.hasAttribute('data-micdrop')) {\r\n el.remove();\r\n }\r\n }\r\n\r\n private _buildItemList(events: ListingEvent[]): Record<string, unknown> {\r\n return {\r\n '@context': 'https://schema.org',\r\n '@type': 'ItemList',\r\n itemListElement: events.map((event, i) => {\r\n const slug = event.slug || generateSlug(event.name);\r\n return {\r\n '@type': 'ListItem',\r\n position: i + 1,\r\n url: `${this._baseUrl}?e=${event.id}-${slug}`,\r\n };\r\n }),\r\n };\r\n }\r\n\r\n private _buildEntityUrl(type: string, id: string | number, slug: string): string {\r\n const prefix = type === 'event' ? 'e' : type === 'series' ? 's' : 'c';\r\n return `${this._baseUrl}?${prefix}=${id}-${slug}`;\r\n }\r\n\r\n // ---------------------------------------------------------------------------\r\n // Public API\r\n // ---------------------------------------------------------------------------\r\n\r\n /**\r\n * Called when events finish loading from the API.\r\n * Injects an ItemList JSON-LD for the listing. Does not modify OG/canonical/title.\r\n */\r\n onEventsLoaded(\r\n events: ListingEvent[],\r\n venue?: VenueData,\r\n ): void {\r\n if (typeof document === 'undefined') return;\r\n if (this._state === 'destroyed') return;\r\n\r\n // Update venue info from parameter if provided\r\n if (venue) {\r\n this._venueName = venue.name || this._venueName;\r\n this._venueAddress = venue.address || venue.googleLocationNameCache || this._venueAddress;\r\n const venueTz = venue.timeZone || venue.timezone;\r\n if (venueTz) {\r\n this._venueTimeZone = venueTz;\r\n }\r\n }\r\n\r\n // Store events for later use (re-injection on back to listing)\r\n this._events = events;\r\n\r\n // Build and inject ItemList JSON-LD\r\n const itemList = this._buildItemList(events);\r\n this._injectJsonLd(itemList);\r\n\r\n this._state = 'listing';\r\n }\r\n\r\n /**\r\n * Called when the user selects an event, series, or collection.\r\n * Replaces ItemList JSON-LD with entity-specific JSON-LD and updates\r\n * all head tags (canonical, OG, meta description, title).\r\n */\r\n onEntitySelected(\r\n type: 'event' | 'series' | 'collection',\r\n data: any,\r\n venue?: VenueData,\r\n ): void {\r\n if (typeof document === 'undefined') return;\r\n if (this._state === 'destroyed') return;\r\n\r\n // Save originals if not already saved\r\n this._saveOriginals();\r\n\r\n // Update venue info from parameter if provided\r\n if (venue) {\r\n this._venueName = venue.name || this._venueName;\r\n this._venueAddress = venue.address || venue.googleLocationNameCache || this._venueAddress;\r\n const venueTz = venue.timeZone || venue.timezone;\r\n if (venueTz) {\r\n this._venueTimeZone = venueTz;\r\n }\r\n }\r\n\r\n let jsonLd: Record<string, unknown> | null = null;\r\n let entityName = '';\r\n let entityDescription = '';\r\n let entityImage = '';\r\n let entityUrl = '';\r\n\r\n if (type === 'event') {\r\n const slug = data.slug || generateSlug(data.name || '');\r\n entityUrl = this._buildEntityUrl('event', data.id || data.eventID || '', slug);\r\n\r\n jsonLd = buildEventJsonLd({\r\n event: data,\r\n venue: {\r\n name: this._venueName,\r\n googleLocationNameCache: this._venueAddress,\r\n },\r\n venueTimeZone: this._venueTimeZone,\r\n eventUrl: entityUrl,\r\n });\r\n\r\n entityName = data.name || '';\r\n entityDescription = data.description || data.eventSummary || '';\r\n entityImage = data.image || data.coverImage || data.ShowImage || '';\r\n } else if (type === 'series') {\r\n const slug = data.slug || generateSlug(data.title || '');\r\n entityUrl = this._buildEntityUrl('series', data.id || '', slug);\r\n\r\n jsonLd = buildSeriesJsonLd({\r\n series: {\r\n ...data,\r\n venue: {\r\n name: this._venueName,\r\n googleLocationNameCache: this._venueAddress,\r\n },\r\n timeZone: this._venueTimeZone,\r\n },\r\n baseEventUrl: entityUrl,\r\n });\r\n\r\n entityName = data.title || '';\r\n entityDescription = data.description || data.eventSummary || '';\r\n entityImage = data.image || '';\r\n } else if (type === 'collection') {\r\n const slug = data.slug || generateSlug(data.collectionTitle || '');\r\n entityUrl = this._buildEntityUrl('collection', data.id || '', slug);\r\n\r\n jsonLd = buildCollectionJsonLd({\r\n collection: data,\r\n baseEventUrl: entityUrl,\r\n defaultTimeZone: this._venueTimeZone,\r\n });\r\n\r\n entityName = data.collectionTitle || '';\r\n entityDescription = data.description || data.summary || '';\r\n entityImage = data.coverImage || '';\r\n }\r\n\r\n // Inject JSON-LD (replaces existing micdrop JSON-LD)\r\n if (jsonLd) {\r\n this._injectJsonLd(jsonLd);\r\n }\r\n\r\n // Update canonical URL\r\n if (entityUrl) {\r\n this._setCanonical(entityUrl);\r\n }\r\n\r\n // Update OG tags\r\n if (entityName) {\r\n this._setMeta('og:title', entityName);\r\n }\r\n if (entityDescription) {\r\n this._setMeta('og:description', entityDescription);\r\n }\r\n if (entityImage) {\r\n this._setMeta('og:image', entityImage);\r\n }\r\n if (entityUrl) {\r\n this._setMeta('og:url', entityUrl);\r\n }\r\n this._setMeta('og:type', 'website');\r\n\r\n // Twitter Card: large image preview\r\n this._setMeta('twitter:card', 'summary_large_image');\r\n\r\n // Update meta description\r\n if (entityDescription) {\r\n this._setMeta('description', entityDescription);\r\n }\r\n\r\n // Update page title\r\n if (entityName) {\r\n document.title = `${entityName} | ${this._venueName}`;\r\n }\r\n\r\n this._state = 'entity';\r\n }\r\n\r\n /**\r\n * Called when the user navigates back to the event listing.\r\n * Restores original head elements and re-injects ItemList JSON-LD.\r\n */\r\n onBackToListing(): void {\r\n if (typeof document === 'undefined') return;\r\n if (this._state === 'destroyed') return;\r\n\r\n // Restore all originals\r\n this._restoreOriginals();\r\n\r\n // Remove any remaining injected elements\r\n this._removeAllInjected();\r\n\r\n // Re-inject ItemList JSON-LD if events are available\r\n if (this._events.length > 0) {\r\n const itemList = this._buildItemList(this._events);\r\n this._injectJsonLd(itemList);\r\n }\r\n\r\n // Re-save originals since we just restored them\r\n this._saveOriginals();\r\n\r\n this._state = 'listing';\r\n }\r\n\r\n /**\r\n * Called when the widget is destroyed. Removes all injected elements\r\n * and restores originals. Clears the static active instance.\r\n */\r\n destroy(): void {\r\n if (typeof document === 'undefined') return;\r\n if (this._state === 'destroyed') return;\r\n\r\n this._removeAllInjected();\r\n this._restoreOriginals();\r\n\r\n if (HostSeoController._activeInstance === this) {\r\n HostSeoController._activeInstance = null;\r\n }\r\n\r\n this._state = 'destroyed';\r\n }\r\n}\r\n"],"names":["getDefaultTimezone","escapeJsonLdForScript","json","formatDateTimeWithOffset","isoString","tz","date","offsetFormatOptions","formatter","parts","get","type","p","year","month","day","hour","minute","second","tzOffset","offset","match","sign","hours","mins","parseAddressToPostal","addressStr","stateZipMatch","buildPostalAddress","fallback","out","getEventStatus","rawStatus","status","getTicketAvailability","ticket","now","salesBegin","salesEnd","buildOffers","tickets","url","publicTickets","t","visibility","isVisible","isPublic","prices","lowPrice","highPrice","buildPerformers","performers","confirmed","isConfirmed","isNotHidden","profile","hasName","buildEventJsonLd","options","event","venue","venueTimeZone","eventUrl","organizer","startDate","postalAddress","location","data","endDate","description","imageUrl","offers","org","mapSeriesTicketAvailability","buildSeriesOccurrenceOffers","occurrenceStatus","activeTickets","buildSeriesJsonLd","series","baseEventUrl","address","subEvents","occ","subEvent","buildCollectionJsonLd","collection","itemListElement","listItem","generateSlug","text","_HostSeoController","canonical","ogTitle","ogDescription","ogImage","ogUrl","ogType","metaDescription","el","script","property","content","originalContent","name","events","i","slug","id","prefix","venueTz","itemList","jsonLd","entityName","entityDescription","entityImage","entityUrl","HostSeoController"],"mappings":"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","x_google_ignoreList":[0]}
|