import { EventTicketInput, HostSeoControllerOptions } from './types'; /** * One show, as the listing hands it over. * * It used to be id/name/slug/startDateTime/image, because the ItemList it fed * published nothing but a position and a URL. Everything below the image is * what `buildEventJsonLd` needs to describe a show to a search engine — the * end time, whether it is cancelled, what it costs, where it is. Every field * is optional: an event that cannot be described falls back to a bare * `ListItem` with a URL on it, exactly as before. */ interface ListingEvent { id: number | string; name: string; slug?: string; startDateTime?: string; endDateTime?: string; image?: string; status?: string; description?: string; eventSummary?: string; availableTickets?: EventTicketInput[]; /** The show's own zone, when it differs from the venue's. */ timeZone?: string; /** The show's own venue, on a calendar that spans several. */ showVenue?: ShowVenue; } /** * The venue one show plays at. A multi-venue calendar has no controller-wide * venue, so each show brings its own; it is read per show and never written * back to the controller, which every calendar on the page shares. */ interface ShowVenue { name: string; /** One-line address. Empty publishes the venue name alone. */ address?: string; timeZone?: string; } interface VenueData { name: string; address?: string; googleLocationNameCache?: string; timeZone?: string; timezone?: string; } export declare class HostSeoController { /** * Single active instance: two controllers would fight to own * ``/OG/etc, so constructing one destroys the one before it. * Embeds go through `shared()` instead, so every calendar on a page with * several (e.g. "upcoming shows" + "this weekend") drives the same one. */ static _activeInstance: HostSeoController | null; /** The page's live controller, or a new one when there is none. Each * caller holds it until its own destroy(); the last one out tears it down. */ static shared(options: HostSeoControllerOptions): HostSeoController; private _state; private _holders; private _venueName; private _venueAddress; private _venueTimeZone; private _baseUrl; /** * Origin of the Micdrop site that server-renders `/e/{id}-{slug}`. * * This is the ONLY per-show URL in existence that a search engine can index: * the host page has exactly one URL however many shows the widget draws on * it. Empty means "not configured" — the structured data then points back at * the host page and the shows are, for indexing purposes, invisible. */ private _publicSiteOrigin; private _originals; private _originalsSaved; private _events; constructor(options: HostSeoControllerOptions); private _saveOriginals; private _injectJsonLd; private _setMeta; private _setCanonical; /** * The canonical for the "one show open" state: the host page's own. * * A page that already declares a canonical keeps it untouched — the host * knows its own address better than we do (params it strips, a www variant it * has settled on). One that declares none gets its bare address, never a * query-string variant of it: the `?e={id}-{slug}` shape this replaces was a * distinct URL to a crawler that RENDERED THE LISTING while the canonical * claimed it was a show, which is the worst of the available answers. */ private _setEntityCanonical; private _removeAllInjected; private _restoreOriginals; private _restoreMetaProperty; private _restoreMetaName; /** * The canonical, server-rendered Micdrop page for one show: * `{publicSiteOrigin}/e/{id}-{slug}`. * * This is what "a real link to a show" has to mean for a script embed. A * search engine indexes URLs, and the host page has ONE — a link that only * changes the host page's query string or fragment adds no show to any index, * whatever it renders. `/e/{id}-{slug}` is a micdrop-frontend route that * server-renders the show, so it is both crawlable without JavaScript and the * page a buyer can finish an order on. * * Falls back to the in-place deep link when no public site origin is * configured — a wrong origin would publish 404s, which is worse than * publishing the host page. */ private _showPageUrl; /** * The host-page URL that re-opens this entity in place: `{host page}#{id}-{slug}`. * * THE HASH, NOT A QUERY PARAM, AND THAT IS THE WHOLE OF THIS FUNCTION. Every * URL this controller published used to be `{host page}?e={id}-{slug}` (or * `?s=` / `?c=`), and the embed does not read `?e=`: `parseDeepLink` in * VenueCalendar.js reads `window.location.hash`, and `WidgetUrlManager` * writes it. The only place `?e=` is handled at all is the hosted SvelteKit * app's `routes/+page.svelte`, which is a different origin from any venue's * site. So the canonical URL, the `og:url` and every ItemList entry named a * URL that opened the LISTING — never the show it claimed to be. */ private _entityDeepLink; /** * ItemList for the listing, with each show's own structured data nested in * its ListItem. * * It used to publish a position and a URL per show and nothing else, which * told a search engine that N things exist and not one fact about any of * them. Each item now carries the full `Event` object — name, start, venue, * postal address, offers, image — through the same builder the entity view * and micdrop-frontend's own hosted site pages use, so the listing and the * show page cannot describe the same show differently. * * An event `buildEventJsonLd` refuses (no name, no start, no venue name) * degrades to the bare `ListItem` + `url` shape rather than emitting a * half-built Event: an incomplete Event is a structured-data error, an * unadorned ListItem is merely quiet. */ private _buildItemList; /** * The venue and zone one show is published with: its own when it carries * one, the controller-wide venue otherwise. */ private _venueFor; /** * The URL for the entity the visitor has open: the canonical Micdrop show * page when one exists, so `og:url` and `<link rel="canonical">` name a page * that actually renders this show rather than the host page's listing. * * Series and collections have no `/e/` page of their own, so they keep the * in-place deep link — a link that reopens them where the visitor is, which * is at least true. */ private _buildEntityUrl; /** * Called when events finish loading from the API. * Injects an ItemList JSON-LD for the listing. Does not modify OG/canonical/title. */ onEventsLoaded(events: ListingEvent[], venue?: VenueData): void; /** * Called when the user selects an event, series, or collection. * Replaces ItemList JSON-LD with entity-specific JSON-LD and updates * all head tags (canonical, OG, meta description, title). */ onEntitySelected(type: 'event' | 'series' | 'collection', data: any, venue?: VenueData): void; /** * Called when the user navigates back to the event listing. * Restores original head elements and re-injects ItemList JSON-LD. */ onBackToListing(): void; /** * Called when the widget is destroyed. Removes all injected elements * and restores originals. Clears the static active instance. */ destroy(): void; private _teardown; } export {};