@v-office/website-headless 0.2.0 → 1.1.0

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/dist/index.d.mts CHANGED
@@ -1,1361 +1,2800 @@
1
- import { BookingPaymentOption, BookingPaymentScheduleKind, GuestQuote, GuestQuoteResult, Locale, SearchSearchSort, SubmittableBookingPaymentOption, WebsiteSDKOptions, createWebsiteSDK } from "@v-office/website-sdk";
2
- //#region src/store/index.d.ts
1
+ import { $ as describeOutcome, $i as RentalFlag, $n as fetchRentalFlags, $r as CardSource, $t as GuestBound, A as QueryShape, Ai as SdkMode, An as LngLat, Ar as SdkConfigForInput, At as quickFilterDefs, B as toSdkQuery, Bi as RentalDistance, Bn as resultCardSource, Br as RouteParams, Bt as SEARCH_PARAMS, C as SearchState, Ci as isIsoDay, Cn as SameSpotGroup, Cr as EnvRecord, Ct as ResolvedFilters, D as createSearchStore, Di as Locale, Dn as circleOffsets, Dr as HUB_ENDPOINTS, Dt as facetOptions, E as SearchStoreOptions, Ei as Backend, En as SameSpotOptions, Er as sdkConfigFromEnv, Et as activeFilters, F as externalSearchHref, Fi as WebsiteSdk, Fn as mapPoints, Fr as CardSourceExtras, Ft as writeSelection, G as sortFromSearch, Gi as parseCounts, Gn as PreviewStrategy, Gr as CardBadge, Gt as SearchQuery, H as SORT_URL_VALUE, Hi as distanceFact, Hn as shortReviewerName, Hr as routeHref, Ht as SearchParamAliases, I as handoverQuery, Ii as resolveSdk, In as ResultLinkOptions, Ir as cardSource, It as partyPairs, J as EmptyCause, Ji as BedRun, Jn as DEFAULT_FLAG_FILTERS, Jr as CardImage, Jt as searchHref, K as sortResults, Ki as parseDistances, Kn as descriptionPreview, Kr as CardContext, Kt as WriteSearchOptions, L as queryShape, Li as ADJACENT_BELOW_M, Ln as SearchResultItem, Lr as loadRentalFacts, Lt as readParty, M as SanitizedSearch, Mi as SdkSetup, Mn as MapPoint, Mr as VofficeEndpointTable, Mt as resolveFilters, N as SdkQueryOptions, Ni as SdkSource, Nn as PinLabelOptions, Nr as VofficeEndpoints, Nt as withoutFilter, O as ExternalSearchLink, Oi as STAGES, On as distanceMeters, Or as HubEndpointTable, Ot as measureFilters, P as SdkSearchQuery, Pi as Stage, Pn as hasMapPoints, Pr as sdkConfigFor, Pt as withoutFilters, Q as SearchedParts, Qi as RentalDetail, Qn as assignSlugs, Qr as CardRating, Qt as DEFAULT_GUEST_LIMITS, R as resultsQueryFlagScript, Ri as DistanceKind, Rn as catalogueResultItem, Rr as rentalFactsMap, Rt as FlexibleStay, S as SearchScopeKind, Si as formatIsoDate, Sn as PixelOffset, Sr as createSdk, St as ResolveFiltersOptions, T as SearchStore, Ti as todayIso, Tn as SameSpotMode, Tr as SdkEnvError, Tt as WhenEmpty, U as SortKey, Ui as isAdjacent, Un as splitReviewAuthor, Ur as AlternativePeriod, Ut as SearchParamKeys, V as DEFAULT_SORT_OPTIONS, Vi as WATER_KINDS, Vn as newestReviewsFirst, Vr as RouteTemplate, Vt as SearchContract, W as SortOptionState, Wi as isSpecEcho, Wn as DescriptionPreview, Wr as BadgeRule, Wt as SearchPeriod, X as Relaxation, Xi as DescriptionContent, Xn as LoadRentalCatalogueOptions, Xr as CardModelOptions, Xt as ChildAgeRange, Y as OutcomeInput, Yi as CatalogueRental, Yn as FetchRentalFlagsOptions, Yr as CardLiveField, Yt as writeSearch, Z as SearchOutcome, Zi as RentalAddress, Zn as RentalFlagsResult, Zr as CardPill, Zt as DEFAULT_CHILD_AGE, _ as FAVORITE_LEAVE_MS, _a as EnhancePart, _i as Glyph, _n as DEFAULT_FIT, _r as fetchSearchPage, _t as FilterGroupSpec, a as PlaceMarker, aa as ReviewCriterion, ai as RentalFactsMap, an as Party, ar as AmenityPreviewItem, at as OffersWindowSpec, b as SearchActions, ba as GateMarkers, bi as IsoMonth, bn as frameFor, br as SharedSdk, bt as FilterSpec, c as DEFAULT_CLUSTER, ca as PlainTextOptions, ci as toCardModel, cn as clampParty, cr as AdaptEvent, ct as findCampaignStart, d as ResultsMapController, da as StructuredDocumentOptions, di as Range, dn as stayKeyOf, dr as adaptRental, dt as offersWindowQuery, ea as RentalImage, ei as CardStatus, en as GuestBounds, er as loadRentalCatalogue, et as CampaignChip, f as ResultsMapOptions, fa as TextMarkup, fi as matches, fn as Listener, fr as RawSearchPage, ft as ActiveFilter, g as CampaignOf, ga as ENHANCE_PARTS, gi as GLYPHS, gn as createStore, gr as WalkSummary, gt as FilterEntry, h as createResultsMapController, ha as plainText, hi as parsePriceLabel, hn as WritableStore, hr as WalkOptions, ht as FilterDef, i as MapPlace, ia as RentalRoom, ii as RentalFacts, in as GuestOptions, ir as ALWAYS_EXCLUDED_CATEGORIES, it as OfferWindow, j as ResultHandoverOptions, ji as SdkOptions, jn as MapBounds, jr as VOFFICE_ENDPOINTS, jt as readSelection, k as ExternalSearchSource, ki as SdkConfig, kn as layoutSameSpot, kr as HubEndpoints, kt as minimumSteps, l as MapResultsSource, la as RichBlock, li as CardMatch, ln as guestBounds, lr as AdaptRentalOptions, lt as findOfferWindows, m as ResultsMapSurface, ma as parseTextBlocks, mi as formatPriceLabel, mn as Unsubscribe, mr as SearchPageRequest, mt as FacetOptionsOptions, n as RentalAddressModeError, na as RentalReview, ni as PillOptions, nn as GuestKind, nr as slugify, nt as FindCampaignStartOptions, o as mergePlaces, oa as ReviewSummary, oi as StayPrice, on as QuoteOccupancy, or as AmenityPreviewOptions, ot as campaignChips, p as ResultsMapState, pa as normalizeStructuredDocument, pi as PriceCents, pn as Store, pr as SEARCH_PAGE_SIZE, pt as FacetOption, q as toSdkSort, qi as AmenityGroup, qn as descriptionSummary, qr as CardFact, qt as readSearch, r as rentalAddressLine, ra as RentalReviews, ri as RentalCardModel, rn as GuestLimits, rr as HIDDEN_ROOM_ATTRIBUTES, rt as FindOfferWindowsOptions, s as ClusterOptions, sa as RoomKind, si as StayWindow, sn as agesComplete, sr as amenityPreview, st as campaignGroup, t as RentalAddressMode, ta as RentalPropertyInfo, ti as FactKind, tn as GuestCaps, tr as rankHighlights, tt as CampaignChipsInput, u as MapViewReport, ua as RichInline, ui as CardSignal, un as occupancyOf, ur as RentalContentError, ut as offerWindowHref, v as FavoritesPort, va as EnhanceParts, vi as GlyphList, vn as DefaultFit, vr as walkSearchPages, vt as FilterMeasurement, w as SearchStatus, wi as nightsBetween, wn as SameSpotLayout, wr as SDK_ENV, wt as SdkFilterDefinition, x as SearchScope, xi as addDays, xn as idsInView, xr as shareSdk, xt as ImplicitScope, y as HistoryPort, ya as GATE_MARKERS, yi as IsoDay, yn as FitOptions, yr as loadSharedSdk, yt as FilterSelection, z as sanitizeSearch, zi as RentalCounts, zn as compareUndatedSearchOrder, zr as withRentalFacts, zt as QueryPair } from "./index-CPMXjfFH.mjs";
2
+ import { BookingPaymentOption, BookingPaymentScheduleKind, ContactSubmitInput, GuestQuote, GuestQuoteResult, SubmittableBookingPaymentOption } from "@v-office/website-sdk";
3
+ import { BackendKind } from "@v-office/website-sdk/capabilities/search";
4
+ //#region src/map/constants.d.ts
5
+ /** OpenFreeMap "Bright": free for commercial use, no key, no rate limit, OSM data. A third-party
6
+ * request (the visitor's IP reaches the tile host), which is why the map components load tiles
7
+ * on click by default. A brand style is a JSON file the site hosts. */
8
+ export declare const DEFAULT_MAP_STYLE_URL: "https://tiles.openfreemap.org/styles/bright";
9
+ /** Pins closer than this are "the same spot": below it their pills overlap at every usable zoom,
10
+ * and the feeds' distinct pins sit at least 18 m apart, so 12 m splits cleanly (measured). */
11
+ export declare const SAME_SPOT_METERS: 12;
12
+ /** The list's re-settle pulse after a guest map move that changed the in-view set. A perceived-
13
+ * responsiveness transition (the re-filter itself is instant), NOT simulated latency. */
14
+ export declare const RELOAD_FLASH_MS: 220;
15
+ //#endregion
16
+ //#region src/sdk/errors.d.ts
17
+ /** A failed SDK call and its whole `cause` chain as indented JSON (stacks skipped). */
18
+ type DescribeSdkError = (error: unknown) => string;
19
+ /** `console.error` the description, tagged by the SDK operation ("search.search", "contact.submit",
20
+ * "quote.selectInsurance"), never by the component: the console must answer which call the backend
21
+ * refused. Logs the description alone, so a caller that also logs the raw error prints it once. */
22
+ type LogSdkFailure = (scope: string, error: unknown) => void;
23
+ /** See {@link DescribeSdkError}. */
24
+ export declare const describeSdkError: DescribeSdkError;
25
+ /** See {@link LogSdkFailure}. */
26
+ export declare const logSdkFailure: LogSdkFailure;
27
+ //#endregion
28
+ //#region src/rental/images.d.ts
29
+ /** One photo URL, WebP where the host serves it: imgproxy `@webp` (−35 % measured), dyn
30
+ * `.webp` (−17 %). The v10 host already answers WebP; any other host passes unchanged. */
31
+ type WebpUrl = (url: string) => string;
32
+ /** A `srcset` ("url 320w, url 480w"), each candidate through `webpUrl`. */
33
+ type WebpSrcset = (srcset: string) => string;
34
+ /** An uncropped rendition of a v9 imgproxy floor plan (`rs:fill:W:H:x` → `rs:fit:2560:2560:0`:
35
+ * the SDK crops every image to 3:2, which cuts the room labels off a plan), or undefined
36
+ * when the URL has no `rs:fill` segment to rewrite. */
37
+ type FloorplanUrl = (src: string) => string | undefined;
38
+ /** See {@link WebpUrl}. */
39
+ export declare const webpUrl: WebpUrl;
40
+ /** See {@link WebpSrcset}. */
41
+ export declare const webpSrcset: WebpSrcset;
42
+ /** See {@link FloorplanUrl}. */
43
+ export declare const floorplanUrl: FloorplanUrl;
44
+ //#endregion
45
+ //#region src/shared/text.d.ts
46
+ /** A copy string whose `{token}`s are `K`: every one of them is filled, none is required. Only a type
47
+ * (the property never exists), so any string is assignable and a table still serializes. */
48
+ type Template<K extends string> = string & {
49
+ readonly __tokens?: K;
50
+ };
51
+ /** The tokens a string literal carries: every `{…}` in it. A literal brace pair in copy reads as a
52
+ * token, as it does for `fillTemplate`. */
53
+ type TokensOf<S extends string> = S extends `${string}{${infer T}}${infer R}` ? T | TokensOf<R> : never;
54
+ /** The tokens a copy key accepts: `K` of a `Template<K>`, none of a plain string. */
55
+ type TokensAccepted<V> = V extends {
56
+ readonly __tokens?: infer K;
57
+ } ? Exclude<K, undefined> : never;
58
+ /** `O` (a site's literal overrides of `T`) where every string carries only tokens its key accepts. A
59
+ * string with another token becomes `S & { "{x} is not a token of this string": never }`, so the
60
+ * compiler names the token. Recurses into arrays and into nested groups (`SharedCopyOverrides`). */
61
+ type CheckedCopy<T, O> = { readonly [P in keyof O]: P extends keyof T ? CheckedValue<T[P], O[P]> : never; };
62
+ type CheckedValue<V, S> = S extends string ? [Exclude<TokensOf<S>, TokensAccepted<V>>] extends [never] ? S : S & { readonly [M in `{${Exclude<TokensOf<S>, TokensAccepted<V>> & string}} is not a token of this string`]: never; } : S extends readonly unknown[] ? { readonly [I in keyof S]: CheckedValue<V extends readonly (infer E)[] ? E : never, S[I]>; } : S extends object ? CheckedCopy<NonNullable<V>, S> : S;
63
+ /** A site's copy overrides of `T` (a component's `XCopy`, or `SharedCopyOverrides`), checked: an
64
+ * unknown key, a wrong type and a token its key does not accept are compile errors. Curried, so `T`
65
+ * is named and the literal inferred: `defineCopy<ShowcaseCopy>()({ moreCount: "Alle {count}" })`.
66
+ * Returns the overrides unchanged. A prop takes them as they are; the helper only adds the check. */
67
+ type DefineCopy = <T extends object>() => <const O extends Partial<T>>(copy: O & CheckedCopy<T, O>) => O;
68
+ /** Replace `{name}` tokens with `values`; an unknown token stays visible ("{nights}"), so a missing
69
+ * value is seen in review instead of rendering as an empty string. A `Template<K>` needs a value for
70
+ * every token in `K` and takes no other; a plain `string` takes any values. */
71
+ type FillTemplate = <K extends string>(template: Template<K>, values: Readonly<Record<K, string | number>>) => string;
72
+ /** See {@link DefineCopy}. */
73
+ export declare const defineCopy: DefineCopy;
74
+ /** See {@link FillTemplate}. */
75
+ export declare const fillTemplate: FillTemplate;
76
+ //#endregion
77
+ //#region src/shared/tracking.d.ts
78
+ /** Where a page is opened from: the parts of `window.location` a share link is built of. */
79
+ interface ShareLocation {
80
+ /** The page's path. */
81
+ readonly pathname: string;
82
+ /** The page's query, with its "?". */
83
+ readonly search: string;
84
+ /** The page's origin (or the canonical one). */
85
+ readonly origin: string;
86
+ }
87
+ /** Options of `shareUrl`. */
88
+ interface ShareUrlOptions {
89
+ /** Canonical origin (the site's own domain). Default: `location.origin`. */
90
+ readonly origin?: string;
91
+ /** Extra parameters to drop beside the tracking ones. */
92
+ readonly strip?: readonly string[];
93
+ }
94
+ /** utm_*, mtm_*, pk_*, gad_*, gclid, gbraid, wbraid, dclid, fbclid, msclkid, mc_eid, ref, _gl. Not
95
+ * global (no `lastIndex` state); its `source` is safe to inline into a `<head>` script. */
96
+ export declare const TRACKING_PARAM_PATTERN: RegExp;
97
+ /** True for a key of the list. */
98
+ type IsTrackingParam = (key: string) => boolean;
99
+ /** The query (with or without its leading "?") without tracking parameters, returned without "?"
100
+ * ("" when nothing else remains). */
101
+ type WithoutTracking = (search: string) => string;
102
+ /** Canonical origin + path + query without tracking (and `strip`) parameters, no hash: the recipient
103
+ * gets the real domain and the guest's selection (`start`, `end`, `adults`, …). */
104
+ type ShareUrl = (location: ShareLocation, options?: ShareUrlOptions) => string;
105
+ /** See {@link IsTrackingParam}. */
106
+ export declare const isTrackingParam: IsTrackingParam;
107
+ /** See {@link WithoutTracking}. */
108
+ export declare const withoutTracking: WithoutTracking;
109
+ /** See {@link ShareUrl}. */
110
+ export declare const shareUrl: ShareUrl;
111
+ //#endregion
112
+ //#region src/shared/storage.d.ts
113
+ /** The part of Web Storage this package uses. */
114
+ interface StorageLike {
115
+ /** The value under `key`, or null. */
116
+ getItem(key: string): string | null;
117
+ /** Store `value` under `key` (may throw: quota, private mode). */
118
+ setItem(key: string, value: string): void;
119
+ /** Drop `key`. */
120
+ removeItem(key: string): void;
121
+ }
122
+ /** `localStorage`/`sessionStorage` when present and writable, else null (SSR, Safari private mode,
123
+ * storage disabled, a sandboxed frame whose mere access throws). */
124
+ type BrowserStorage = (kind: "local" | "session") => StorageLike | null;
125
+ /** See {@link BrowserStorage}. */
126
+ export declare const browserStorage: BrowserStorage;
127
+ //#endregion
128
+ //#region src/shared/types.d.ts
129
+ /** A link a notice or a fact offers (a phone, a mail, an enquiry page). */
130
+ interface ContactLink {
131
+ /** The visible text ("040 123 456", "Anfrage senden"). */
132
+ readonly label: string;
133
+ /** `tel:`, `mailto:` or a page. */
134
+ readonly href: string;
135
+ }
136
+ /** Not bookable online before `from` (an operator change, a new season). One object for SearchBar
137
+ * (`dates.bookable`) and Quote (`bookable`), so the decision is one site constant (`BOOKABLE`). */
138
+ interface BookableFrom {
139
+ /** First day bookable online. SearchBar floors its calendar, month strip and ± windows here; Quote
140
+ * opens its calendar on this month and does NOT floor (the PMS availability stays the arbiter). */
141
+ readonly from: IsoDay;
142
+ /** The notice shows while today is before this day. Default `from`. A site whose notice ends on a
143
+ * different day than the floor (an operator change on one date, online booking from a later one)
144
+ * sets both. */
145
+ readonly noticeUntil?: IsoDay;
146
+ /** A phone or enquiry link rendered inside the notice (SearchBar date panel, Quote above its calendar). */
147
+ readonly contact?: ContactLink;
148
+ /** How the notice names `from`: the day (default), its month ("Januar 2027") or its year
149
+ * ("2027"), for an opening announced as "ab 2027". Presentation only: the floor stays `from`. */
150
+ readonly precision?: "day" | "month" | "year";
151
+ }
152
+ //#endregion
153
+ //#region src/countries/countries.d.ts
154
+ /** ISO 3166-1 alpha-2 ("DE"). The PMS accepts every assigned code, so the register is never filtered. */
155
+ type CountryCode = string;
156
+ /** A select option: the code and its label in the requested language. */
157
+ type CountryOption = readonly [code: CountryCode, label: string];
158
+ /** A featured group pinned on top plus the rest of the register, collated by locale. A featured code
159
+ * appears ONLY in `featured`: a duplicate made the select's scroll-to-selected land on the alphabetical
160
+ * twin, hiding the pinned group. Serializable: computed at build, passed to islands. */
161
+ interface CountryGroups {
162
+ /** The pinned group, in the site's order. */
163
+ readonly featured: readonly CountryOption[];
164
+ /** Every other country, collated by the locale. */
165
+ readonly all: readonly CountryOption[];
166
+ }
167
+ /** DACH. Sites add their audience (DK, NL, PL …) or reorder (CH first) through `featured`. */
168
+ export declare const DEFAULT_FEATURED_COUNTRIES: readonly CountryCode[];
169
+ /** What `countryOptions` labels. */
170
+ interface CountryOptionsInput {
171
+ /** Label language, e.g. "de-DE" or "en". */
172
+ readonly locale: string;
173
+ /** Codes pinned on top, in this order. Default `DEFAULT_FEATURED_COUNTRIES`; codes outside the
174
+ * register and repeats are skipped. */
175
+ readonly featured?: readonly CountryCode[];
176
+ }
177
+ /** The register as select options. Labels via `Intl.DisplayNames` — call at BUILD time. */
178
+ type CountryOptions = (input: CountryOptionsInput) => CountryGroups;
179
+ /** E.164 calling code (digits, no "+") for Europe and the common overseas origins, else undefined —
180
+ * never a guessed prefix (a wrong one turns a national number into someone else's). */
181
+ type DialCode = (code: CountryCode) => string | undefined;
3
182
  /**
4
- * The smallest external store that every UI framework can subscribe to:
5
- * `getState` + `subscribe` is what React's useSyncExternalStore, Vue's
6
- * watchers, Svelte's store contract and a plain `<script>` all understand.
7
- * Flows in this package (checkout today, more later) build on it so the
8
- * package never depends on a framework; the adapters live in ./react and
9
- * friends.
183
+ * Preselect from the visitor's languages (pass `navigator.languages`): non-English tags first (maximized:
184
+ * "da" → DK), because an added language is a real choice while an English UI is often just the OS
185
+ * default; then English tags with an explicit region ("en-GB"; bare "en" is never maximized — that guesses
186
+ * US for the world's default UI language); else `fallback` (default "DE"). Only codes in `countries`
187
+ * count; a malformed tag is skipped.
10
188
  */
11
- export type Listener<S> = (state: S) => void;
12
- export type Unsubscribe = () => void;
13
- export interface Store<S> {
14
- getState(): S;
15
- subscribe(listener: Listener<S>): Unsubscribe;
189
+ type DetectCountry = (countries: CountryGroups, languages: readonly string[], fallback?: CountryCode) => CountryCode;
190
+ /** See {@link CountryOptions}. */
191
+ export declare const countryOptions: CountryOptions;
192
+ /** See {@link DialCode}. */
193
+ export declare const dialCode: DialCode;
194
+ /** See {@link DetectCountry}. */
195
+ export declare const detectCountry: DetectCountry;
196
+ //#endregion
197
+ //#region src/favorites/favorites.d.ts
198
+ /** The window event every site script listens to (`detail: FavoritesChange`); kept verbatim. The
199
+ * `./enhance` binder dispatches it from `onChange`. */
200
+ export declare const FAVORITES_EVENT: "merkliste:changed";
201
+ /** One rental saved or removed. */
202
+ interface FavoritesChange {
203
+ /** The rental id. */
204
+ readonly id: string;
205
+ /** True when it was saved, false when it was removed. */
206
+ readonly saved: boolean;
207
+ }
208
+ /** `${namespace}-merkliste` — the kits' key shape. `namespace` is required and non-empty (throws
209
+ * otherwise): the package carries no donor default; each site passes the namespace its kit code
210
+ * resolves today (`PUBLIC_STORAGE_NS`, or the kit's fallback when unset). */
211
+ type FavoritesStorageKey = (namespace: string) => string;
212
+ /** The saved ids. */
213
+ interface FavoritesState {
214
+ /** The saved rental ids. */
215
+ readonly ids: ReadonlySet<string>;
16
216
  }
17
- export interface WritableStore<S> extends Store<S> {
18
- /** Replaces the state and notifies every listener. Same reference = no-op. */
19
- setState(next: S): void;
217
+ /** How a Merkliste is stored and reported. */
218
+ interface FavoritesOptions {
219
+ /** `favoritesStorageKey(namespace)`. */
220
+ readonly storageKey: string;
221
+ /** Default `browserStorage("local")`; null = in memory (SSR, tests). Writes fail soft (quota, private
222
+ * mode): the list then lasts for the visit. */
223
+ readonly storage?: StorageLike | null;
224
+ /** Receives every change, once per id (the binder dispatches `FAVORITES_EVENT` on window from it). */
225
+ readonly onChange?: (change: FavoritesChange) => void;
20
226
  }
21
- export declare function createStore<S>(initial: S): WritableStore<S>;
227
+ /** A Merkliste store. Also satisfies the search store's `FavoritesPort` (`ids()` + `subscribe`). */
228
+ interface Favorites extends Store<FavoritesState> {
229
+ /** The saved ids (the current state's set). */
230
+ ids(): ReadonlySet<string>;
231
+ /** Whether `id` is saved. */
232
+ has(id: string): boolean;
233
+ /** Save or remove `id`; returns the new saved state. */
234
+ toggle(id: string): boolean;
235
+ /** Make `id` saved or not; no change, no write, no `onChange`. */
236
+ set(id: string, saved: boolean): void;
237
+ /** Drop ids the site no longer knows (a rental that left the portfolio). */
238
+ prune(known: ReadonlySet<string>): void;
239
+ /** Re-read storage (another tab changed it: the `storage` event); reports each difference. */
240
+ reload(): void;
241
+ /** Stop: later calls change nothing and report nothing. */
242
+ dispose(): void;
243
+ }
244
+ /** The page's Merkliste for `options.storageKey`, shared through the registry so the binder, the
245
+ * favourites page and every heart see one set. The first claim's `storage` wins; every claim's
246
+ * `onChange` receives the changes until it is released. The last release disposes. */
247
+ type AcquireFavorites = (options: FavoritesOptions) => {
248
+ readonly favorites: Favorites;
249
+ release(): void;
250
+ };
251
+ /** A Merkliste the caller owns. */
252
+ type CreateFavorites = (options: FavoritesOptions) => Favorites;
253
+ /** See {@link FavoritesStorageKey}. */
254
+ export declare const favoritesStorageKey: FavoritesStorageKey;
255
+ /** See {@link CreateFavorites}. */
256
+ export declare const createFavorites: CreateFavorites;
257
+ /** See {@link AcquireFavorites}. */
258
+ export declare const acquireFavorites: AcquireFavorites;
22
259
  //#endregion
23
- //#region src/sdk.d.ts
24
- export type WebsiteSdk = ReturnType<typeof createWebsiteSDK>;
25
- /** Deployment stage: selects the public endpoint set below. */
26
- export declare const STAGES: readonly ["development", "production"];
27
- export type Stage = (typeof STAGES)[number];
28
- /** Public endpoints of the v10 backend per stage. */
29
- export declare const VOFFICE_ENDPOINTS: Record<Stage, {
30
- apiEndpoint: string;
31
- imageBaseUrl: string;
32
- searchEndpoint: string;
33
- }>;
34
- /** Public endpoints of the v9 backend per stage; API URLs derive from `apiBaseUrl`. */
35
- export declare const HUB_ENDPOINTS: Record<Stage, {
36
- apiBaseUrl: string;
37
- imageProxyBaseUrl: string;
38
- }>;
39
- /** Everything the SDK needs, as plain public values — a static site has no
40
- * secret to protect. Serializable on purpose: an Astro island receives it as a
41
- * prop, so the site reads its env once (in .astro / a loader) and never inside
42
- * a component. */
43
- export type SdkConfig = {
44
- backend: "v9";
45
- imageProxyBaseUrl: string;
46
- v0ApiBaseUrl: string;
47
- v1ApiBaseUrl: string;
48
- graphqlUrl: string;
49
- apiKey: string;
50
- } | {
51
- backend: "v10";
52
- imageBaseUrl: string;
53
- apiEndpoint: string;
54
- searchEndpoint: string;
55
- accessToken: string;
260
+ //#region src/guests/guest-search.d.ts
261
+ /** Options of a guest search. Everything has a default. */
262
+ interface GuestSearchOptions extends GuestOptions {
263
+ /** Start at defaultAdults when the URL states no party (closed field reads "2 Gäste"). Default false. Same
264
+ * name as SearchBar `guests.showDefault` (one name in both layers). */
265
+ readonly showDefault?: boolean;
266
+ /** A rental's caps (booking contexts); the search bar passes none. */
267
+ readonly caps?: GuestCaps;
268
+ }
269
+ /** The guest search as a UI reads it. */
270
+ interface GuestSearchState {
271
+ /** The chosen party: offered types clamped to their bounds, ages aligned to the children; adults 0 = none
272
+ * chosen. A not-offered type keeps the value it was loaded with. */
273
+ readonly party: Party;
274
+ /** The adults a submit sends: chosen, or defaultAdults. What the adult stepper and the summary show. */
275
+ readonly shownAdults: number;
276
+ /** Any offered type has a count (drives prompt vs summary on the closed field). */
277
+ readonly hasSelection: boolean;
278
+ /** Every child has an age; always true while children are not offered (no one could enter one). */
279
+ readonly agesComplete: boolean;
280
+ /** Per type: whether a control renders, and its stepper bounds. */
281
+ readonly bounds: GuestBounds;
282
+ /** The selectable child ages, `childAge.min`..`childAge.max`. */
283
+ readonly ageOptions: readonly number[];
284
+ /** What a submit sends; a not-offered type keeps the URL's value ("hidden means not offered, never not honoured"). */
285
+ readonly occupancy: Party;
286
+ }
287
+ /** A framework-free guest search: read `getState()`, `subscribe`, call actions. */
288
+ interface GuestSearch extends Store<GuestSearchState> {
289
+ /** Set one count, clamped to its bound; ignored for a type that is not offered. Fewer children drop the
290
+ * trailing ages, more children add unchosen ones. */
291
+ set(kind: GuestKind, count: number): void;
292
+ /** Choose (or clear with null) child `index`'s age; an age outside the band or a missing child is ignored. */
293
+ setAge(index: number, age: number | null): void;
294
+ /** Restore from a read party (the URL); null = nothing stated. Ages outside the band become unchosen, so the
295
+ * guest is asked again instead of priced for an age they did not give. */
296
+ load(party: Party | null): void;
297
+ /** Back to nothing chosen (or the default party with `showDefault`). */
298
+ reset(): void;
299
+ /** Change options; the party is re-clamped. Equal options are a no-op. */
300
+ update(options: Partial<GuestSearchOptions>): void;
301
+ }
302
+ /** A guest search; options are plain data (islands) and default to the kit values. */
303
+ type CreateGuestSearch = (options?: GuestSearchOptions) => GuestSearch;
304
+ /** See {@link CreateGuestSearch}. */
305
+ export declare const createGuestSearch: CreateGuestSearch;
306
+ //#endregion
307
+ //#region src/search-bar/date-search.d.ts
308
+ /** Which half of the picker is active. */
309
+ type DateMode = "dates" | "flexible";
310
+ /** Options of a date search. Everything has a default; only `backend` is required. */
311
+ interface DateSearchOptions {
312
+ /** Which PMS answers (decides the flexible tab and how a ± step is spelled). */
313
+ readonly backend: BackendKind;
314
+ /** See `SearchContract.nightRangeSearch`. Default true. */
315
+ readonly nightRangeSearch?: boolean;
316
+ /** ± chips in days. Default [0, 1, 2, 3, 7, 14]; [] or [0] = no chip row. Shown on v10 always, on v9 only with
317
+ * nightRangeSearch. */
318
+ readonly flexSteps?: readonly number[];
319
+ /** Default ["weekend", "week", "month"]; [] removes the tab; only where supportsFlexiblePeriodSearch. */
320
+ readonly flexibleStays?: readonly FlexibleStay[];
321
+ /** Render the Min./Max. Nächte row (presentation; whether the backend prices the pair is
322
+ * `nightRangeSearch`). Default false. */
323
+ readonly nightRangeRow?: boolean;
324
+ /** Shortest pickable stay in nights; closer departure days are disabled. Default 1. */
325
+ readonly minStayNights?: number;
326
+ /** Not bookable online yet: `from` floors calendar, month strip and ± windows; `deferred` (the notice) is true
327
+ * while today < `noticeUntil ?? from` (`contact` is the component's, ignored here). */
328
+ readonly bookable?: BookableFrom;
329
+ /** The clock, for tests. Default `() => new Date()`; read on every action, so a bar left open past
330
+ * midnight moves its floor with the day. */
331
+ readonly now?: () => Date;
332
+ }
333
+ /** Which controls a UI renders right now. Derived from the options, the backend and the current pick. */
334
+ interface DateControls {
335
+ /** The Datum/Flexibel switch and the flexible half. */
336
+ readonly flexibleTab: boolean;
337
+ /** The ± chips to render, 0 (exact dates) first; empty = no chip row. */
338
+ readonly flexSteps: readonly number[];
339
+ /** The Min./Max. row: enabled, the backend prices the pair, exact dates (± 0) and a window of ≥ 2 nights. */
340
+ readonly nightRangeRow: boolean;
341
+ }
342
+ /** The date search, as a UI reads it. */
343
+ interface DateSearchState {
344
+ /** The active half. */
345
+ readonly mode: DateMode;
346
+ /** Arrival of the guest's own stay (never the widened window). */
347
+ readonly from: IsoDay | null;
348
+ /** Departure of the guest's own stay; null while the second day is being picked. */
349
+ readonly to: IsoDay | null;
350
+ /** Active ± step; 0 = exact dates. */
351
+ readonly flexDays: number;
352
+ /** The night range in force, clamped to the window; null when the stay simply is the window. */
353
+ readonly nightRange: {
354
+ readonly min: number;
355
+ readonly max: number;
356
+ } | null;
357
+ /** The flexible stay length; null until chosen (nothing is preselected). */
358
+ readonly stay: FlexibleStay | null;
359
+ /** The flexible months, sorted. */
360
+ readonly months: readonly IsoMonth[];
361
+ /** Nights between `from` and `to` (0 until both are set). */
362
+ readonly windowNights: number;
363
+ /** What to render. */
364
+ readonly controls: DateControls;
365
+ /** Earliest selectable day: the later of today and `bookable.from`. */
366
+ readonly floor: IsoDay;
367
+ /** The "not bookable online yet" notice shows (today < `noticeUntil ?? from`). */
368
+ readonly deferred: boolean;
369
+ /** The twelve months the flexible strip offers, counted from the floor's month. */
370
+ readonly monthOptions: readonly IsoMonth[];
371
+ /** What a submit sends now; null until a complete period is picked. */
372
+ readonly period: SearchPeriod | null;
373
+ }
374
+ /** A framework-free date search: read `getState()`, `subscribe`, call actions. */
375
+ interface DateSearch extends Store<DateSearchState> {
376
+ /** A calendar click: the first sets `from`; the second sets `to` (a day before `from` moves `from`); a click
377
+ * on a finished stay starts a new one there. Every pick resets the night range to the new window. Disabled
378
+ * days are ignored. */
379
+ pick(day: IsoDay): void;
380
+ /** Before the floor; while `to` is being picked, departure days closer than `minStayNights`. */
381
+ isDisabled(day: IsoDay): boolean;
382
+ /** Ignored for "flexible" while the flexible tab is not offered. */
383
+ setMode(mode: DateMode): void;
384
+ /** Ignored for a step not in `controls.flexSteps`; a step > 0 clears the night range. */
385
+ setFlexDays(days: number): void;
386
+ /** Moving one bound past the other pushes the other along (the SDK refuses min > max). */
387
+ setMinNights(nights: number): void;
388
+ /** See `setMinNights`. */
389
+ setMaxNights(nights: number): void;
390
+ /** Ignored for a stay not in `flexibleStays`. */
391
+ setStay(stay: FlexibleStay): void;
392
+ /** Add or remove a month of `monthOptions`. */
393
+ toggleMonth(month: IsoMonth): void;
394
+ /** Restore from a read period (the URL). A period the controls cannot show (a step not offered, a range
395
+ * without the row, a stay before the floor, a flexible stay not offered) loads as empty. */
396
+ load(period: SearchPeriod | null): void;
397
+ /** Nothing picked, exact-dates mode. */
398
+ reset(): void;
399
+ /** Change options (not the backend); a pick the new options cannot show is cleared. Equal options are a no-op. */
400
+ update(options: Partial<Omit<DateSearchOptions, "backend">>): void;
401
+ }
402
+ /** A date search over `options`. */
403
+ type CreateDateSearch = (options: DateSearchOptions) => DateSearch;
404
+ /** See {@link CreateDateSearch}. */
405
+ export declare const createDateSearch: CreateDateSearch;
406
+ //#endregion
407
+ //#region src/search-bar/period-label.d.ts
408
+ /** A period's summary parts, formatted for the locale. */
409
+ interface PeriodLabel {
410
+ /** "4. – 11. Sept.", "30. Dez. 2026 – 3. Jan. 2027"; null before a day is picked (and on flexible periods). */
411
+ readonly dates: string | null;
412
+ /** The ± step; 0 = exact dates. */
413
+ readonly flexDays: number;
414
+ /** The night range inside the window, or null. */
415
+ readonly nights: {
416
+ readonly min: number;
417
+ readonly max: number;
418
+ } | null;
419
+ /** The flexible stay, or null. */
420
+ readonly stay: FlexibleStay | null;
421
+ /** "Sept. 2026" for one month, "Sept., Okt." for several (sorted; a month outside the current year carries
422
+ * its year); null when not flexible. */
423
+ readonly months: string | null;
424
+ }
425
+ /** A picker's unfinished selection (an arrival, maybe a departure), described while it is being picked. */
426
+ type PartialPeriod = {
427
+ readonly kind: "partial";
428
+ readonly from: IsoDay;
429
+ readonly to: IsoDay | null;
56
430
  };
57
- /** The three values a site actually decides — backend, stage, its token — as
58
- * a full `SdkConfig`, with the endpoints the platform owns. */
59
- export declare function sdkConfigFor({ backend, stage, token }: {
60
- backend: SdkConfig["backend"];
61
- stage: Stage;
62
- token: string;
63
- }): SdkConfig;
64
- /** Build an SDK instance from a config (+ the SDK's options: translation
65
- * overrides, custom attributes, highlight prioritization). The caller owns the
66
- * lifecycle and calls `await sdk.dispose()` when done. The API is rate-limited
67
- * (1/s), so a browser surface keeps ONE instance for its lifetime. */
68
- export declare function createSdk(config: SdkConfig, options?: WebsiteSDKOptions): WebsiteSdk;
69
- /**
70
- * Render `error` and its whole cause chain as indented JSON.
71
- *
72
- * Walks the chain because a cause nests (a `CoreSDKError` wrapping a thrown
73
- * transport object wrapping a response body). Skips `stack` — a backend
74
- * refusal is not diagnosed from the frames that asked, and the raw error is
75
- * one `console.error` away for the cases that are. Reads `name`/`message` by
76
- * hand because `Error` declares both non-enumerable, so `Object.entries`
77
- * alone would return an Error as `{}`.
78
- *
79
- * **`cause` is read by hand for exactly the same reason**, and it was missed
80
- * the first time — so this file shipped without the one property it exists to
81
- * print. Verified against the real class: `Object.keys(new CoreSDKError({…}))`
82
- * is `["source", "operation", "_tag"]`, and `cause`'s own descriptor carries
83
- * `enumerable: false`. `Object.entries` therefore never saw it, and every
84
- * `CoreSDKError` logged as the five bland fields above with the sentence that
85
- * names the fault dropped.
86
- */
87
- export declare function describeSdkError(error: unknown): string;
88
- /**
89
- * Log a failed SDK call with the backend's reason, tagged by `scope`.
90
- *
91
- * `scope` names the SDK operation, not the component — `"quote.selectInsurance"`
92
- * rather than `"the extras panel"`. What the console needs to answer is which
93
- * call the backend refused; which button was on screen at the time is the part
94
- * the reporter already knows.
95
- *
96
- * Logs the description ALONE. Callers that also have the raw error in hand may
97
- * log it separately for a clickable stack; doubling it here would print every
98
- * failure twice on the surfaces that already do.
99
- */
100
- export declare function logSdkFailure(scope: string, error: unknown): void;
431
+ /** Month once within a month, year only when not the current one, months through Intl ("Sept."). */
432
+ type FormatStayRange = (from: IsoDay, to: IsoDay | null, locale: Locale, today?: IsoDay) => string;
433
+ /** The summary parts of a period (or a partial pick, or nothing). `today` decides the current year. */
434
+ type DescribePeriod = (period: SearchPeriod | PartialPeriod | null, locale: Locale, today?: IsoDay) => PeriodLabel;
435
+ /** See {@link FormatStayRange}. */
436
+ export declare const formatStayRange: FormatStayRange;
437
+ /** See {@link DescribePeriod}. */
438
+ export declare const describePeriod: DescribePeriod;
101
439
  //#endregion
102
- //#region src/quote.d.ts
103
- /** One row of the price breakdown. `label`/`amount` are the SDK's own
104
- * `position`/`appliedCharge`, verbatim; `nested` marks a sub-line so the UI
105
- * can indent it under the line above.
106
- *
107
- * `discount` marks a row synthesised from an SDK price MODIFIER rather than
108
- * from a charge line — see `adaptGuestQuote` for why those rows exist. Its
109
- * `amount` is the modifier's own signed string ("-140,00 €"), so the row is a
110
- * deduction from the running total, not another charge. */
111
- export interface QuoteLine {
440
+ //#region src/search-bar/rental-jump.d.ts
441
+ /** One rental a guest can jump to. `href` is the site's own detail URL (never re-derived from the name, which
442
+ * broke umlauts and duplicate names). */
443
+ interface RentalIndexEntry {
444
+ /** PMS rental id. */
445
+ readonly id: string;
446
+ /** The name shown and matched. */
447
+ readonly name: string;
448
+ /** The rental's detail page on this site. */
449
+ readonly href: string;
450
+ /** Object number or another code a guest may type ("10503"); an exact code ranks first. */
451
+ readonly code?: string;
452
+ /** A place shown beside the name (matched too: "name place" finds the rental). */
453
+ readonly place?: string;
454
+ }
455
+ /** How many hits and from which query length. */
456
+ interface RentalMatchOptions {
457
+ /** Hits returned. Default 8. */
458
+ readonly limit?: number;
459
+ /** Shorter queries match nothing (one letter matches the whole catalogue). Default 2. */
460
+ readonly minChars?: number;
461
+ }
462
+ /** The hits for one query. */
463
+ interface RentalMatch {
464
+ /** The best hits, at most `limit`. */
465
+ readonly hits: readonly RentalIndexEntry[];
466
+ /** All matches, for "{count} weitere". */
467
+ readonly total: number;
468
+ }
469
+ /** Lowercase, ä→ae ö→oe ü→ue ß→ss, other diacritics stripped, non-alphanumerics to one space. */
470
+ type FoldSearchText = (raw: string) => string;
471
+ /** Every query word must occur in name, place or code (indexed in both umlaut spellings: "duene" and "dune"
472
+ * find "Düne"). Rank: code exact, name exact, name prefix, word start, contains; ties keep the index order. */
473
+ type MatchRentals = (entries: readonly RentalIndexEntry[], query: string, options?: RentalMatchOptions) => RentalMatch;
474
+ /** Fetched once per URL per page; a failure is not cached (the next call retries). Rows without a string id,
475
+ * name and href are dropped. */
476
+ type LoadRentalIndex = (url: string) => Promise<readonly RentalIndexEntry[]>;
477
+ /** Build-time: index entries from the catalogue (the site's own slugs/hrefs); `place` is the rental's town. */
478
+ type RentalIndexEntries = (rentals: readonly RentalDetail[], href: (rental: RentalDetail) => string, options?: {
479
+ readonly code?: (rental: RentalDetail) => string | undefined;
480
+ }) => RentalIndexEntry[];
481
+ /** See {@link FoldSearchText}. */
482
+ export declare const foldSearchText: FoldSearchText;
483
+ /** See {@link MatchRentals}. */
484
+ export declare const matchRentals: MatchRentals;
485
+ /** See {@link LoadRentalIndex}. */
486
+ export declare const loadRentalIndex: LoadRentalIndex;
487
+ /** See {@link RentalIndexEntries}. */
488
+ export declare const rentalIndexEntries: RentalIndexEntries;
489
+ //#endregion
490
+ //#region src/search-bar/search-form.d.ts
491
+ /** A field of the bar, in render order. "pets" = render the dog control as its own field after guests (instead
492
+ * of inside the guest panel); which control it is follows `guests.limits.pets` (1 a yes/no toggle, more a
493
+ * stepper). "pets" in `fields` while dogs are not offered (`guests.limits.pets` 0) is a spec
494
+ * error: `createSearchForm` throws (build time), never renders an empty field. "region" renders only with at
495
+ * least one region. */
496
+ type SearchField = "region" | "dates" | "guests" | "pets";
497
+ /** Options of a search form: the site's search contract plus what the bar asks. */
498
+ interface SearchFormOptions extends SearchContract {
499
+ /** Default ["region", "dates", "guests"]; an absent field's key is carried, not owned (a `?region=` scope
500
+ * survives a re-search from a bar without a region field). */
501
+ readonly fields?: readonly SearchField[];
502
+ /** The selectable region values (labels are UI); URL values outside the list self-hide. */
503
+ readonly regions?: readonly string[];
504
+ /** Default "multi" (OR). "single": one region or all. */
505
+ readonly regionSelect?: "multi" | "single";
506
+ /** The date picker's options. */
507
+ readonly dates?: Omit<DateSearchOptions, "backend" | "nightRangeSearch">;
508
+ /** The guest picker's options. */
509
+ readonly guests?: GuestSearchOptions;
510
+ /** Default "required": a child without age stops the submit. "optional" submits the ages that exist. */
511
+ readonly childAges?: "required" | "optional";
512
+ /** A landing page's starting search as a query string; its owned keys seed the pickers only when the URL owns
513
+ * none of the bar's keys, its other pairs are submitted with every search from this bar. */
514
+ readonly preset?: string;
515
+ /** The clock, for tests. Default `() => new Date()` (read per URL read and per date action). */
516
+ readonly now?: () => Date;
517
+ }
518
+ /** Why a submit did not go out. */
519
+ type SearchFormIssue = {
520
+ readonly kind: "agesMissing";
521
+ };
522
+ /** The search form as a UI reads it. */
523
+ interface SearchFormState {
524
+ /** The fields that render: `fields` without "region" when there are no regions. */
525
+ readonly fields: readonly SearchField[];
526
+ /** The date picker. */
527
+ readonly dates: DateSearchState;
528
+ /** The guest picker. */
529
+ readonly guests: GuestSearchState;
530
+ /** Selected regions, in option order. */
531
+ readonly regions: readonly string[];
532
+ /** Pairs the bar does not own, carried through a submit (URL + preset). */
533
+ readonly carried: readonly QueryPair[];
534
+ /** false until load() ran (search pages show skeletons, submit disabled). */
535
+ readonly resolved: boolean;
536
+ /** Set by a refused submit, cleared by the next change. */
537
+ readonly issue: SearchFormIssue | null;
538
+ }
539
+ /** A framework-free search form: read `getState()`, `subscribe`, call actions. */
540
+ interface SearchForm extends Store<SearchFormState> {
541
+ /** The composed date search (its actions drive the date panel). */
542
+ readonly dates: DateSearch;
543
+ /** The composed guest search (its actions drive the guest panel and the dog field). */
544
+ readonly guests: GuestSearch;
545
+ /** Multi: add or remove a region; single: choose it, or clear it when chosen. Values outside `regions` are ignored. */
546
+ toggleRegion(value: string): void;
547
+ /** Choose exactly one region; null = all regions. */
548
+ selectRegion(value: string | null): void;
549
+ /** No region chosen. */
550
+ clearRegions(): void;
551
+ /** Read the URL (and the preset) into the pickers; after hydration, the first render is the server's. */
552
+ load(search: string): void;
553
+ /** writeSearch(pickers) + carried — the GET form's hidden inputs. */
554
+ pairs(): QueryPair[];
555
+ /** The pairs to submit, or the issue that stops the submit (a child without an age, while "required").
556
+ * `current` (pass `location.search`): the carried pairs are re-read from it first, so the filters, sort
557
+ * and campaign a results page changed in place since `load` ride along (and a removed one stays
558
+ * removed); the pickers keep the guest's unsubmitted choices. */
559
+ submit(current?: string): {
560
+ readonly ok: true;
561
+ readonly pairs: QueryPair[];
562
+ } | {
563
+ readonly ok: false;
564
+ readonly issue: SearchFormIssue;
565
+ };
566
+ /** Pickers and regions back to nothing chosen; the carried pairs stay. */
567
+ reset(): void;
568
+ /** Change options (not the backend); option objects are merged, equal options are a no-op. */
569
+ update(options: Partial<Omit<SearchFormOptions, "backend">>): void;
570
+ /** Stop listening to the composed stores. */
571
+ dispose(): void;
572
+ }
573
+ /** A search form over `options`; throws on a spec error ("pets" in `fields` while dogs are not offered). */
574
+ type CreateSearchForm = (options: SearchFormOptions) => SearchForm;
575
+ /** See {@link CreateSearchForm}. */
576
+ export declare const createSearchForm: CreateSearchForm;
577
+ //#endregion
578
+ //#region src/search/run.d.ts
579
+ /** One request's options: the search contract, links and facts, filters and paging. */
580
+ interface LiveSearchOptions extends ResultLinkOptions, SearchContract {
581
+ /** The resolved filters: their sdk keys reach the SDK, retired keys never do. */
582
+ readonly filters?: ResolvedFilters | null;
583
+ /** Default "recommended". */
584
+ readonly sort?: SortKey;
585
+ /** The previous page's cursor (opaque). */
586
+ readonly cursor?: string | null;
587
+ /** v10 only: server-side id restriction (favourites, a fixed set); ignored on v9. */
588
+ readonly rentalIdsIn?: readonly string[];
589
+ }
590
+ /** One page of the live search. */
591
+ interface SearchPage {
592
+ /** The rentals of this page, exact matches first. */
593
+ readonly items: readonly SearchResultItem[];
594
+ /** What kind of search the page answers. */
595
+ readonly shape: QueryShape;
596
+ /** The next page's cursor. */
597
+ readonly cursor: string | null;
598
+ /** The backend has another page. */
599
+ readonly hasNextPage: boolean;
600
+ /** `pageInfo.totalCount` when the backend states it. */
601
+ readonly totalCount: number | null;
602
+ /** Keys the backend reports as having had no effect. */
603
+ readonly unusedFilterKeys: readonly string[];
604
+ }
605
+ /** One request with the caller's SDK (never created or disposed here); failures are logged as
606
+ * "search.search" by the SDK module and rethrown. */
607
+ type RunLiveSearch = (sdk: WebsiteSdk, search: string, options: LiveSearchOptions) => Promise<SearchPage>;
608
+ /** See {@link RunLiveSearch}. */
609
+ export declare const runLiveSearch: RunLiveSearch;
610
+ //#endregion
611
+ //#region src/search/registry.d.ts
612
+ /** A claim on the page's store under one key; the last `release` disposes it. */
613
+ interface SearchStoreClaim {
614
+ /** The page's store under the key. */
615
+ readonly store: SearchStore;
616
+ /** Drop this claim (idempotent); the last release disposes the store. */
617
+ release(): void;
618
+ }
619
+ /** The store under `key`, created by `create` when the page has none yet. The same key always
620
+ * yields the same store; the last release disposes it. */
621
+ type AcquireSearchStore = (key: string, create: () => SearchStore) => SearchStoreClaim;
622
+ /** Attach to a store that may appear later (a satellite island hydrating before the results);
623
+ * `onStore(null)` when it is released. Returns the detach. */
624
+ type ConnectSearchStore = (key: string, onStore: (store: SearchStore | null) => void) => () => void;
625
+ /** See {@link AcquireSearchStore}. */
626
+ export declare const acquireSearchStore: AcquireSearchStore;
627
+ /** See {@link ConnectSearchStore}. */
628
+ export declare const connectSearchStore: ConnectSearchStore;
629
+ //#endregion
630
+ //#region src/showcase/select.d.ts
631
+ /** How qualifying rentals are ordered before spreading, pins and the limit.
632
+ * feed: as the candidates arrive (default) · name: by name (numeric-aware) · rating: rating
633
+ * desc, then review count desc · reviews: review count desc, then rating desc · guests:
634
+ * capacity desc, then name · shuffle: deterministic from `seed` (a build date gives "random
635
+ * per build"). Missing values sort last; ties keep the feed order. */
636
+ type ShowcaseOrder = "feed" | "name" | "rating" | "reviews" | "guests" | "shuffle";
637
+ /** Who qualifies for a row and how the row is ordered, spread and gated. */
638
+ interface SelectionSpec {
639
+ /** Who qualifies. Default: every candidate. */
640
+ match?: CardMatch;
641
+ /** These ids lead, in this order, whether or not they match (exclude and minImages still
642
+ * apply). An id the candidates do not know throws: a typo must fail the build. */
643
+ pin?: readonly string[];
644
+ /** Fill after the pins from the matches. Default true; false = the pins only. */
645
+ fill?: boolean;
646
+ /** Ids never shown (the page's own rental). Default none. */
647
+ exclude?: readonly string[];
648
+ /** Default "feed". */
649
+ order?: ShowcaseOrder;
650
+ /** Seed of `order: "shuffle"`. Default "". */
651
+ seed?: string;
652
+ /** Round-robin across towns (or streets) after ordering, keeping the order inside each, so a
653
+ * row opens on the town and not on one building. One town degrades to the order. */
654
+ spreadBy?: "location" | "street" | null;
655
+ /** Images a rental needs to qualify. Default 1. */
656
+ minImages?: number;
657
+ /** Rentals kept. Default: all. */
658
+ limit?: number;
659
+ /** Fewer than this → an empty selection (the section self-hides). Default 1; carousel rows 3
660
+ * (two cards in a scrolling track read as broken). */
661
+ minSize?: number;
662
+ /** A match holding more than this share of the candidates says nothing → empty. Default: no
663
+ * check. */
664
+ maxShare?: number;
665
+ /** Fewer than this throws at build, with the counts — for sections the customer requires.
666
+ * Default: never. */
667
+ failBelow?: number;
668
+ }
669
+ /** A row's rentals and its honest total. */
670
+ interface Selection {
671
+ /** The rentals kept, in row order. */
672
+ rentals: CardSource[];
673
+ /** Before `limit` — the honest "Alle {N}" (0 when a gate emptied the selection). */
674
+ total: number;
675
+ }
676
+ type SelectRentals = (candidates: readonly CardSource[], spec?: SelectionSpec) => Selection;
677
+ /** Deterministic Fisher–Yates over mulberry32: the same seed gives the same order on every
678
+ * visitor and reload. */
679
+ type SeededShuffle = <T>(items: readonly T[], seed: string | number) => T[];
680
+ /** See {@link SelectRentals}. */
681
+ export declare const selectRentals: SelectRentals;
682
+ /** See {@link SeededShuffle}. */
683
+ export declare const seededShuffle: SeededShuffle;
684
+ //#endregion
685
+ //#region src/showcase/groups.d.ts
686
+ /** One row as the site defines it. */
687
+ interface ShowcaseGroupSpec {
688
+ /** The row's id (its tab, its rotation seed). */
689
+ id: string;
690
+ /** The row's heading or tab label. */
112
691
  label: string;
692
+ /** The tab text when the label is long. */
693
+ shortLabel?: string;
694
+ /** A glyph beside the label. Default none. */
695
+ icon?: Glyph;
696
+ /** Where this row's "see all" goes (a scoped search link). */
697
+ href?: string;
698
+ /** One or two sentences of site copy shown with the group (under the active tab, or under a
699
+ * single group's heading). Site data, never derived. */
700
+ blurb?: string;
701
+ /** Who the row shows (`selectRentals`). */
702
+ select: SelectionSpec;
703
+ }
704
+ /** A row ready to render — the Showcase prop. Plain JSON. */
705
+ interface ShowcaseGroup {
706
+ /** The row's id. */
707
+ id: string;
708
+ /** The row's heading or tab label. */
709
+ label: string;
710
+ /** The tab text when the label is long, or null. */
711
+ shortLabel: string | null;
712
+ /** A glyph beside the label, or null. */
713
+ icon: Glyph | null;
714
+ /** Where the row's "see all" goes, or null. */
715
+ href: string | null;
716
+ /** The row's site copy, or null. */
717
+ blurb: string | null;
718
+ /** Qualifying rentals before the row's limit. */
719
+ total: number;
720
+ /** The rentals shown, in row order. */
721
+ rentals: readonly CardSource[];
722
+ }
723
+ /** Rows in spec order; a row whose selection is empty is omitted. `distinct`: a rental shown in
724
+ * an earlier row is not offered to later rows (default false — themed rows overlap by design;
725
+ * "featured, then a dog row without them" sets true). */
726
+ type BuildShowcaseGroups = (candidates: readonly CardSource[], specs: readonly ShowcaseGroupSpec[], options?: {
727
+ distinct?: boolean;
728
+ }) => ShowcaseGroup[];
729
+ /** See {@link BuildShowcaseGroups}. */
730
+ export declare const buildShowcaseGroups: BuildShowcaseGroups;
731
+ //#endregion
732
+ //#region src/showcase/related.d.ts
733
+ /** How "more rentals nearby" is chosen for a rental page. */
734
+ interface RelatedOptions {
735
+ /** What "near" means: the same `location` (default) or a shared region. */
736
+ prefer?: "location" | "region";
737
+ /** Default 8. */
738
+ limit?: number;
739
+ /** Fewer than this → []. Default 2. */
740
+ minSize?: number;
741
+ /** Images a rental needs. Default 1. */
742
+ minImages?: number;
743
+ }
744
+ type SelectRelated = (candidates: readonly CardSource[], currentId: string, options?: RelatedOptions) => CardSource[];
745
+ /** See {@link SelectRelated}. */
746
+ export declare const selectRelated: SelectRelated;
747
+ //#endregion
748
+ //#region src/showcase/rotation.d.ts
749
+ /** A fair rotation: which window of a row shows now. */
750
+ interface RotationOptions {
751
+ /** Tiles shown at once. */
752
+ visible: number;
753
+ /** Minutes a window stands. */
754
+ minutes: number;
755
+ /** Epoch ms; default `Date.now()`. */
756
+ now?: number;
757
+ /** Mixed into every round's shuffle so rows do not rotate in lockstep (the group id). */
758
+ seed?: string;
759
+ }
760
+ /** The indices (0 … count − 1) to show now, in display order. `count <= visible` → all. */
761
+ type RotationWindow = (count: number, options: RotationOptions) => number[];
762
+ /** See {@link RotationWindow}. */
763
+ export declare const rotationWindow: RotationWindow;
764
+ //#endregion
765
+ //#region src/showcase/search-groups.d.ts
766
+ /** At least one of `rentalIds` and `filterQuery` (throws otherwise). */
767
+ interface SearchGroupSpec {
768
+ /** The group's id (the row it fills). */
769
+ id: string;
770
+ /** Curated ids, in curated order. An id the catalogue does not know throws. */
771
+ rentalIds?: readonly string[];
772
+ /** A dateless filter query ("lakeview=true"). */
773
+ filterQuery?: string;
774
+ /** With both: pin every curated id first and fill from the matches (true), or keep only the
775
+ * curated ids that also match, in curated order (false, default). */
776
+ pinCurated?: boolean;
777
+ /** Matches kept (pins always stay). Default 6. */
778
+ limit?: number;
779
+ }
780
+ /** A search-defined group resolved at build: its rentals and what the backend ignored. */
781
+ interface ResolvedSearchGroup {
782
+ /** The spec's id. */
783
+ id: string;
784
+ /** The rentals, curated ones first where pinned. */
785
+ rentalIds: string[];
786
+ /** Keys the backend ignored — the category filters nothing on this tenant (the site decides
787
+ * its fallback, e.g. a SelectionSpec). */
788
+ unusedFilterKeys: string[];
789
+ }
790
+ /** BUILD TIME. Specs in order; an empty category is returned empty, never dropped. A failed
791
+ * or incomplete search rejects (the build fails and the previous build stays live). */
792
+ type ResolveSearchGroups = (sdk: WebsiteSdk, specs: readonly SearchGroupSpec[], options: {
793
+ locale: Locale;
794
+ knownIds: ReadonlySet<string>;
795
+ }) => Promise<ResolvedSearchGroup[]>;
796
+ /** See {@link ResolveSearchGroups}. */
797
+ export declare const resolveSearchGroups: ResolveSearchGroups;
798
+ //#endregion
799
+ //#region src/quote/availability.d.ts
800
+ /** What a day IS for the stay being picked; exactly one per day, the UI maps it to a look.
801
+ * - "past": before today.
802
+ * - "booked": a hard status on the ARRIVAL map (`unavailable`, `after_last_bookable_date`,
803
+ * `range_crosses_unavailable_date_and_is_unavailable`) — genuinely not bookable.
804
+ * - "unreachable": departure phase only — a hard status on the END map for a day that is not booked:
805
+ * the stay would cross a booked night. Kept apart from "booked" so a site can keep the struck set
806
+ * stable while a start is chosen (a customer ruling: no jumps while picking).
807
+ * - "restricted": available, but a rule blocks it in this phase (no check-in, minimum stay, no
808
+ * check-out, the site's `minStayNights`), or the map for this phase is not in yet.
809
+ * - "free": selectable in this phase. */
810
+ type DayKind = "past" | "booked" | "unreachable" | "restricted" | "free";
811
+ /** One calendar day, ready to render. */
812
+ interface DayState {
813
+ /** The day. */
814
+ iso: IsoDay;
815
+ /** What the day is in the current phase. */
816
+ kind: DayKind;
817
+ /** A click does something now: free in this phase, or the chosen start while picking the end. */
818
+ selectable: boolean;
819
+ /** The SDK's formatted nightly price, raw (format it with `dayPriceLabel`), or null: none from the
820
+ * SDK, past, booked, or a chosen endpoint. Shown on restricted days too — the hub's status decides,
821
+ * not the phase (prices on rule-restricted free nights were hidden once; 157 priced days measured).
822
+ * While the end is picked, the departure map's price when the SDK prices end dates (v9 from SDK
823
+ * 2.30), else the arrival price. */
824
+ price: string | null;
825
+ /** The SDK's localized block reason; only on a non-selectable day outside the chosen range (a
826
+ * departure endpoint re-judged as "no arrival" must not flash its tooltip). */
827
+ reason: string | null;
828
+ /** Position in the chosen range. */
829
+ selection: "start" | "end" | "inside" | null;
830
+ }
831
+ /** The arrival map of one rental over a window. */
832
+ interface InitialAvailability {
833
+ /** Days the SDK flags as a possible check-in. */
834
+ canStart: ReadonlySet<IsoDay>;
835
+ /** Days with a hard status: genuinely not bookable (struck), as opposed to rule-blocked (dimmed). */
836
+ hardBlocked: ReadonlySet<IsoDay>;
837
+ /** The SDK's raw `formattedPrice` by day (already localized, in the rental's booking currency). */
838
+ priceByDay: ReadonlyMap<IsoDay, string>;
839
+ /** The SDK's localized `tooltip` by day (a block reason, or a stay-length hint). */
840
+ tooltipByDay: ReadonlyMap<IsoDay, string>;
841
+ /** Persons cap (adults + children) the SDK states; null when it states none (v9 before SDK 2.28). */
842
+ maxPersons: number | null;
843
+ /** Adults cap the SDK states; null when it states none (v9 before SDK 2.28). */
844
+ maxAdults: number | null;
845
+ /** false when the rental takes no children, babies included; null states no rule (v10, v9 before
846
+ * SDK 2.28). */
847
+ childrenAllowed: boolean | null;
848
+ /** The most dogs the quote accepts, 0 for none; null states no limit (v10, v9 before SDK 2.28). */
849
+ maxPets: number | null;
850
+ }
851
+ /** The departure map for one chosen start. */
852
+ interface EndAvailability {
853
+ /** The start this map answers for. */
854
+ start: IsoDay;
855
+ /** Days the SDK flags as a possible check-out (its minimum stay and check-out rules applied). */
856
+ canEnd: ReadonlySet<IsoDay>;
857
+ /** Days with a hard status for this start (booked, or a range crossing a booked night). */
858
+ hardBlocked: ReadonlySet<IsoDay>;
859
+ /** The SDK's raw `formattedPrice` by possible check-out day: the last night of a stay from `start`
860
+ * to that day, at the rate for its length. null when the SDK prices no end dates (v10, v9 before
861
+ * SDK 2.30). */
862
+ priceByDay: ReadonlyMap<IsoDay, string> | null;
863
+ /** The SDK's localized `tooltip` by day (minimum stay not met, no check-out, …). */
864
+ tooltipByDay: ReadonlyMap<IsoDay, string>;
865
+ }
866
+ /** What both fetchers take: the rental and the inclusive window the SDK computes over. */
867
+ interface AvailabilityWindowInput {
868
+ /** PMS rental id (numeric unit id on v9). */
869
+ rentalId: string;
870
+ /** First day of the window. */
871
+ from: IsoDay;
872
+ /** Last day of the window. */
873
+ through: IsoDay;
874
+ /** Language of the SDK's tooltips. */
875
+ locale: Locale;
876
+ }
877
+ /** The departure-map input: the window plus the chosen start. */
878
+ interface EndAvailabilityInput extends AvailabilityWindowInput {
879
+ /** The chosen check-in day. */
880
+ start: IsoDay;
881
+ }
882
+ /** How calendar CELLS state the nightly price; never a total.
883
+ * - "rounded": whole units, half up (`formatPriceLabel(·, "round")`) — truncation would understate every
884
+ * night, always in the direction that flatters the price.
885
+ * - "exact": the SDK's figure with only a zero-cents group dropped ("120,00 €" → "120 €").
886
+ * - "none": no figure in the cells (customer rulings on several sites). */
887
+ type DayPrices = "rounded" | "exact" | "none";
888
+ /** Possible check-in days, prices and caps for a rental (`availability.getInitialAvailability`). */
889
+ type FetchInitialAvailability = (sdk: WebsiteSdk, input: AvailabilityWindowInput) => Promise<InitialAvailability>;
890
+ /** Possible check-out days for one start (`availability.getStartDateSelectedAvailability`). */
891
+ type FetchEndAvailability = (sdk: WebsiteSdk, input: EndAvailabilityInput) => Promise<EndAvailability>;
892
+ /** The cell text for one SDK price string, or null for "none". A shape the price reader does not
893
+ * recognise is returned unchanged (a display tidy never turns a price into a wrong one). */
894
+ type DayPriceLabel = (formatted: string, mode: DayPrices) => string | null;
895
+ /** See {@link FetchInitialAvailability}. */
896
+ export declare const fetchInitialAvailability: FetchInitialAvailability;
897
+ /** See {@link FetchEndAvailability}. */
898
+ export declare const fetchEndAvailability: FetchEndAvailability;
899
+ /** See {@link DayPriceLabel}. */
900
+ export declare const dayPriceLabel: DayPriceLabel;
901
+ //#endregion
902
+ //#region src/quote/stay-picker.d.ts
903
+ /** What the picker needs to know about the rental and the site. */
904
+ interface StayPickerOptions {
905
+ /** PMS rental id. */
906
+ rentalId: string;
907
+ /** Language of the SDK's tooltips. */
908
+ locale: Locale;
909
+ /** Navigable months from the current one. Availability is fetched one month further, so the last
910
+ * month's departures exist. Default 12. */
911
+ horizonMonths?: number;
912
+ /** The site's minimum stay in NIGHTS on top of the PMS rules: a departure closer to the start is
913
+ * "restricted". A higher PMS minimum still wins (the SDK's `canEnd` already encodes it). */
914
+ minStayNights?: number;
915
+ /** Clock, for tests. Default `() => new Date()`. */
916
+ now?: () => Date;
917
+ }
918
+ /** The picker's state; serializable apart from nothing — every value is a string, number or null. */
919
+ interface StayPickerState {
920
+ /** The arrival map. "error": the calendar could not load (never rendered as a full calendar). */
921
+ availability: "loading" | "ready" | "error";
922
+ /** First and last navigable days (the fetch reaches one month past `through`). */
923
+ window: {
924
+ from: IsoDay;
925
+ through: IsoDay;
926
+ };
927
+ /** The chosen stay so far. */
928
+ range: {
929
+ start: IsoDay | null;
930
+ end: IsoDay | null;
931
+ };
932
+ /** arrival: no start · departure: a start, no end · complete: both. */
933
+ phase: "arrival" | "departure" | "complete";
934
+ /** The departure map for `range.start`. While "loading" only the start is selectable; a failed load
935
+ * is logged and becomes an empty map (no departure), never a silent free-for-all. */
936
+ departures: "idle" | "loading" | "ready";
937
+ /** Occupancy caps from the arrival map; null until it lands or when the SDK states none. */
938
+ caps: Pick<InitialAvailability, "maxPersons" | "maxAdults" | "childrenAllowed" | "maxPets">;
939
+ /** First arrivable day in the window, or null: a sheet scrolls there, AvailabilityCalendar opens there. */
940
+ firstArrival: IsoDay | null;
941
+ /** Nights of a complete range, else null. */
942
+ nights: number | null;
943
+ /** Bumped on every map change, so a memoized calendar knows to re-read `day()`. */
944
+ revision: number;
945
+ }
946
+ /** The picker: a store plus the day model and the picking actions. */
947
+ interface StayPicker extends Store<StayPickerState> {
948
+ /** The render model of one day in the current phase. Pure over the state and the maps. */
949
+ day(iso: IsoDay): DayState;
950
+ /** A click: sets the start (and fetches its departures), completes the range, deselects the start
951
+ * when clicked again, or — on a complete range — starts a new one (the third click). A
952
+ * non-selectable day is ignored. */
953
+ select(iso: IsoDay): void;
954
+ /** Programmatic range (handover, checkout editor, next arrival). Re-validated against the arrival
955
+ * map once it is in: an arrival the PMS refuses clears the range rather than pricing it; a range
956
+ * shorter than `minStayNights` keeps only its start. */
957
+ setRange(start: IsoDay, end: IsoDay | null): void;
958
+ /** Back to the arrival phase, nothing chosen. */
959
+ reset(): void;
960
+ /** Replace the site's minimum stay (absent = none); the range is re-validated against it. */
961
+ configure(options: Pick<StayPickerOptions, "minStayNights">): void;
962
+ /** Fetch the arrival map. Idempotent; nothing is fetched before it is called. */
963
+ start(): void;
964
+ /** Stop: late answers are dropped. */
965
+ dispose(): void;
966
+ }
967
+ /** A picker over the caller's SDK, resolved once on the first request (again only after a failed
968
+ * resolve). Requests: one arrival map per picker, one departure map per chosen start (the SDK queues
969
+ * and caches them per rental). */
970
+ type CreateStayPicker = (sdk: SdkSource, options: StayPickerOptions) => StayPicker;
971
+ /** See {@link CreateStayPicker}. */
972
+ export declare const createStayPicker: CreateStayPicker;
973
+ //#endregion
974
+ //#region src/quote/adapter.d.ts
975
+ /** One row of the price breakdown. Strings are the SDK's, verbatim. */
976
+ interface PriceLine {
977
+ /** charge: a card line · sub: its sub-item (indented) · discount: a PMS modifier row under the line
978
+ * it adjusts (a deduction, rarely a surcharge). */
979
+ kind: "charge" | "sub" | "discount";
980
+ /** The SDK's `position` (or the modifier's label). */
981
+ label: string;
982
+ /** The figure of the default ("rows") display: the line's `totalBeforeModifiers` when it carries
983
+ * modifiers, so gross − deduction rows add up to the total; else `appliedCharge`. On a discount row
984
+ * the modifier's signed amount. */
113
985
  amount: string;
114
- nested: boolean;
115
- discount: boolean;
116
- }
117
- export interface PriceQuote {
118
- /** Section/line breakdown flattened in SDK order (sub-items follow their
119
- * parent line, flagged `nested`). */
120
- lines: QuoteLine[];
121
- /** SDK `bookingCardInformation.total`, verbatim (already locale-formatted). */
986
+ /** `appliedCharge` (net of its modifiers); equal to `amount` on a line without modifiers. The
987
+ * "struck-total" display annotates these net lines with the discount rows. */
988
+ netAmount: string;
989
+ }
990
+ /** The price card of one quote. */
991
+ interface PriceView {
992
+ /** `bookingCardInformation.total`, verbatim. */
122
993
  total: string;
994
+ /** The PMS's own pre-modifier card total; null without modifiers (v9 always). The only struck price
995
+ * the package ever prints, and only where a site chose to. */
996
+ totalBeforeModifiers: string | null;
997
+ /** The breakdown in SDK order; sub and discount rows follow the charge they belong to. */
998
+ lines: PriceLine[];
999
+ /** Distinct labels of the deductions in first-seen order (the PMS's words, e.g. a campaign name).
1000
+ * Surcharges are not offers and are left out. */
1001
+ offerLabels: string[];
123
1002
  }
124
- /** Bookable add-on from `quote.additionalServices`, verbatim fields. */
125
- export interface QuoteAddonService {
1003
+ /** A bookable add-on from `quote.additionalServices`, verbatim fields. */
1004
+ interface QuoteAddonService {
1005
+ /** The service id the selection calls take. */
126
1006
  id: string;
1007
+ /** The PMS's name, verbatim. */
127
1008
  label: string;
128
- /** SDK charge string as-is (e.g. "25,00 €" — the PMS's own formatting). */
1009
+ /** The SDK's charge string as-is ("25,00 €"). */
129
1010
  charge: string;
1011
+ /** The PMS's description, verbatim. */
130
1012
  description?: string | undefined;
131
1013
  /** Max selectable per booking; absent = the SDK states no cap. */
132
1014
  maxPerBooking?: number | undefined;
133
- /** Quantity the PMS PROPOSES for this service (`0` included). A proposal,
134
- * NOT a selection — the distinction is the whole reason this field is
135
- * named the way it is.
136
- *
137
- * THE SDK IS EXPLICIT (`instructions/quote.md`): "When the backend provides
138
- * a line amount for an optional service, it is exposed as `proposedQuantity`
139
- * (including `0`). By default this is informational only and is not added to
140
- * the quote." A fresh quote carries these as selections ONLY on a project
141
- * that sets `shouldAddProposedAdditionalServiceAmount: true`, which the SDK
142
- * defaults to `false`.
143
- *
144
- * SO THIS IS NEVER A BASELINE. What a quote actually has selected is not
145
- * readable from the public SDK surface at all — `GuestQuote` keeps its
146
- * `selections` behind an unexported symbol, and there is no quote-side
147
- * counterpart to `getBookingModificationSelectedQuantity`. Code that needs a
148
- * known starting point therefore MAKES one with `clearQuoteServices` and
149
- * steps up from zero. It does not read this number and hope.
150
- *
151
- * RENAMED FROM `preSelectedQuantity` IN booking-kit 1.21.0, because the old
152
- * name stated the one thing the value does not mean, and `checkout-kit`
153
- * believed it — see that kit's 1.21.0 entry. */
1015
+ /** The quantity the PMS PROPOSES (`0` included) — a proposal, never a selection: a fresh quote
1016
+ * carries it as selected only when the SDK option `shouldAddProposedAdditionalServiceAmount` is on,
1017
+ * and the quote does not say which. Code that needs a known start clears and steps up from zero. */
154
1018
  proposedQuantity: number;
155
- /** False only when the PMS explicitly marks this service unbookable for the
156
- * current multi-rate combination (SDK `available: false`). Absent → true.
157
- * The UI disables adding it rather than letting the click silently no-op. */
1019
+ /** False only when the PMS marks the service unbookable for the current combination. */
158
1020
  available: boolean;
159
- /** PMS's own reason the service is unavailable, verbatim; self-hides when
160
- * the feed omits it. */
1021
+ /** The PMS's reason it is unavailable, verbatim; absent when the feed states none. */
161
1022
  unavailableReason?: string | undefined;
1023
+ /** The service's pictures from the feed (alt verbatim). */
1024
+ images?: readonly CardImage[] | undefined;
162
1025
  }
163
- export interface QuotePolicyRule {
1026
+ /** One refund rule of a cancellation policy. */
1027
+ interface QuotePolicyRule {
1028
+ /** Cancel at least this many days before arrival … */
164
1029
  daysBeforeArrival: number;
165
- /** SDK's formatted percentage string, verbatim. */
1030
+ /** … and get this back: the SDK's formatted percentage ("100 %"), verbatim. */
166
1031
  refundPercentage: string;
167
1032
  }
168
- /** Selectable cancellation policy from `quote.cancellationPolicies`. */
169
- export interface QuoteCancellationPolicy {
1033
+ /** A selectable cancellation policy from `quote.cancellationPolicies`. */
1034
+ interface QuoteCancellationPolicy {
1035
+ /** The policy id `selectQuotePolicy` and `?policy=` take. */
170
1036
  id: string;
1037
+ /** The host's name, or one of the PMS's built-in names (see `isBuiltInPolicy`). */
171
1038
  label: string;
1039
+ /** The rate this quote is priced under. */
172
1040
  selected: boolean;
1041
+ /** The PMS's prose, verbatim (often an echo of the label; see `policyTerms`). */
173
1042
  description?: string | undefined;
1043
+ /** Present on alternative rates ("-20%"); the base rate carries none. */
174
1044
  markUpPercentage?: string | undefined;
1045
+ /** A separate charge for cancelling, formatted by the SDK. */
175
1046
  processingFee?: string | undefined;
1047
+ /** The refund ladder; empty on the SDK's minimal policy. */
176
1048
  rules: QuotePolicyRule[];
177
- /** False only when the PMS explicitly marks this policy unselectable for the
178
- * current multi-rate combination (SDK `available: false`). Absent → true. */
1049
+ /** False only when the PMS marks the policy unselectable for the current combination. */
179
1050
  available: boolean;
180
- /** PMS's own reason the policy is unavailable, verbatim; self-hides when
181
- * the feed omits it. */
1051
+ /** The PMS's reason for `available: false`, verbatim. */
182
1052
  unavailableReason?: string | undefined;
183
1053
  }
184
- /** Voucher status echoed back on a re-priced quote (`quote.voucher`). On v10
185
- * the SDK authoritatively reports whether the code applied a modifier; v9
186
- * reports `"unknown"`. `message` is the PMS's own note, verbatim. */
187
- export interface QuoteVoucher {
1054
+ /** The voucher status echoed on a re-priced quote (v10 authoritative, v9 "unknown"). */
1055
+ interface QuoteVoucher {
1056
+ /** The code as submitted. */
188
1057
  code: string;
1058
+ /** v10 says whether a voucher modifier applied; v9 always answers "unknown". */
189
1059
  status: "applied" | "not_applied" | "unknown";
1060
+ /** The PMS's own note, verbatim. */
190
1061
  message?: string | undefined;
191
1062
  }
192
- /** One selectable travel-insurance product from `quote.insurance.options`
193
- * (kind "insurance"), verbatim fields. The backend loads these live from
194
- * whichever insurer the hub setup configures, and only when that setup enables
195
- * insurance at all — otherwise the SDK returns the "none" option alone and the
196
- * adapter yields no `QuoteInsurance`. */
197
- export interface QuoteInsuranceOption {
1063
+ /** One selectable travel-insurance product (`quote.insurance.options`, kind "insurance"). */
1064
+ interface QuoteInsuranceOption {
1065
+ /** The plan id `selectQuoteInsurance` takes. */
198
1066
  id: string;
1067
+ /** The product name; the identity the checkout keys documents on. */
199
1068
  label: string;
200
- /** SDK's own locale-formatted premium (e.g. "14,00 €"). There is no numeric
201
- * amount on the public shape — the SDK keeps that internally — so a premium
202
- * can be DISPLAYED but never added to another price. */
1069
+ /** The SDK's formatted premium; displayable, never added to another price. */
203
1070
  formattedPrice: string;
1071
+ /** The insurer's description, verbatim. */
204
1072
  description?: string | undefined;
205
- /** Insurer logo, when the feed provides one. The kit's insurance card does
206
- * not render it; a site that wants the insurer's mark has it here. */
1073
+ /** The insurer's logo, when the feed has one. */
207
1074
  logoUrl?: string | undefined;
208
- /** Plan feature names, verbatim (e.g. "Reiserücktritt"). */
1075
+ /** Plan feature names, verbatim. */
209
1076
  features?: string[] | undefined;
210
- /** Product documents (IPID, Bedingungen, …) — label + URL, verbatim. */
1077
+ /** Product documents (IPID, terms), label + URL verbatim. */
211
1078
  resources?: {
212
1079
  label: string;
213
1080
  url: string;
214
1081
  }[] | undefined;
215
- /** True when the product needs per-traveler data before selection (the ERGO
216
- * path: traveler forms, pre-contract, SEPA mandate, a post-booking
217
- * `bookInsurance` call). False = the id alone completes the selection and
218
- * the premium books WITH the rental, because `booking.book` injects
219
- * `travelInsurancePlan`/`travelInsuranceAmount` from the quote for exactly
220
- * these products. The checkout only offers the ones it can complete. */
1082
+ /** True when the product needs per-traveler data first (not completable by the checkout). */
221
1083
  requiresTravelers: boolean;
222
1084
  }
223
- /** Travel-insurance choice on a live quote (`quote.insurance`), flattened for
224
- * the checkout's radio section. Absent from the view when the feed offers no
225
- * real product (options empty or "none"-only) — the section self-hides.
226
- *
227
- * The premium is NOT part of `card.total`: the SDK prices the stay and the
228
- * policy separately, so a UI that shows both must show them as two figures.
229
- * Adding them would mean parsing the PMS's formatted strings, which this file
230
- * does not do for any other charge either. */
231
- export interface QuoteInsurance {
1085
+ /** The travel-insurance choice of a live quote. Absent from the view when the feed offers no real
1086
+ * product (v10's none-only placeholder): every insurance surface then self-hides. The premium is NOT
1087
+ * part of the card total. */
1088
+ interface QuoteInsurance {
232
1089
  /** The SDK's own "no insurance" label, verbatim. */
233
1090
  noneLabel: string;
1091
+ /** The products the feed offers, verbatim. */
234
1092
  options: QuoteInsuranceOption[];
235
1093
  /** Selected product id, or null = no insurance (the default). */
236
1094
  selectedId: string | null;
237
1095
  }
238
- /** The renderable view of a live GuestQuote — card + selectable options. */
239
- export interface QuoteView {
240
- card: PriceQuote;
1096
+ /** The renderable view of a live GuestQuote. */
1097
+ interface QuoteView {
1098
+ /** The price card. */
1099
+ price: PriceView;
1100
+ /** Bookable add-ons, in SDK order. */
241
1101
  services: QuoteAddonService[];
1102
+ /** Cancellation policies, in SDK order, unfiltered (`offeredPolicies` decides what is offered). */
242
1103
  policies: QuoteCancellationPolicy[];
243
- /** Present once a code has been submitted and the quote re-priced; absent on
244
- * a fresh quote. */
1104
+ /** Present once a code was submitted and the quote re-priced. */
245
1105
  voucher?: QuoteVoucher | undefined;
246
- /** Selectable travel insurance — absent when the feed offers no product. */
1106
+ /** Present only when the feed offers a real product; the quote surface never renders it (the
1107
+ * checkout owns the § 7 VVG gate). */
247
1108
  insurance?: QuoteInsurance | undefined;
248
1109
  }
249
- export type QuoteResult = {
1110
+ /** Why the SDK refused to price a stay — the ONLY thing callers may branch on (`reason` is localized
1111
+ * and carries the same text for different codes). Derived from the SDK's result, never re-listed. */
1112
+ type QuoteUnavailableCode = Extract<GuestQuoteResult, {
1113
+ status: "unavailable";
1114
+ }>["code"];
1115
+ /** The answer for one stay. */
1116
+ type QuoteResult =
1117
+ /** Priced: the live quote (required by `booking.book`, in memory only) and its view. */
1118
+ {
250
1119
  status: "available";
251
- /** Flattened price card (back-compat: the widget renders this). */
252
- quote: PriceQuote;
253
- /** The live SDK quote instance — required by booking.book. In-memory
254
- * only; not serializable across navigations. */
255
1120
  live: GuestQuote;
256
- /** Add-ons + policies for the checkout's optional sections. */
257
1121
  view: QuoteView;
258
- } | {
1122
+ } |
1123
+ /** Refused: the SDK's localized reason and its machine code. */
1124
+ {
259
1125
  status: "unavailable";
260
1126
  reason: string;
261
1127
  code: QuoteUnavailableCode;
262
1128
  };
263
- /** Why the SDK refused to price the stay — machine-readable since website-sdk
264
- * 2.16.0, and the ONLY thing callers may branch on. `reason` is localized and
265
- * carries the SAME text for different codes, so comparing it is how a UI ends
266
- * up telling a guest a free week is booked out.
267
- *
268
- * DERIVED from `GuestQuoteResult`, not re-listed: the SDK owns this set and a
269
- * copy here would rot silently the day it grows a fourth member.
270
- * `@v-office/sdk-core` exports the union as `GuestQuoteUnavailableCode`, but
271
- * `@v-office/website-sdk` does not re-export it and sdk-core is a transitive
272
- * dependency an adopting project does not declare — so `Extract` off the
273
- * result type is the reachable spelling. */
274
- export type QuoteUnavailableCode = Extract<GuestQuoteResult, {
275
- status: "unavailable";
276
- }>["code"];
277
- /** The two codes that mean "the stay WAS priced, but the rental's rate setup
278
- * cannot be resolved to the single multi-rate a booking must be written
279
- * against" — a v10 backend-configuration fault the guest has no part in.
280
- *
281
- * `resolveV10MultiRate` matches the priced quote's service lines against the
282
- * rental's multi-rate rows for the policy, then tie-breaks on `baseRentRate`,
283
- * which only fires when EXACTLY ONE tied row carries it. A rental whose PMS
284
- * holds several equivalent base-rent rows per cancellation policy leaves the
285
- * tie unbroken, and the SDK reports the quote unavailable rather than guess a
286
- * rate — the price alone does not identify which row the booking belongs to.
287
- *
288
- * There is nothing an adopting site can do about the stay itself: it stays
289
- * unbookable until the surplus rows are removed in the PMS. What the site owes
290
- * the guest is honest copy — never the generic "not available", which asserts
291
- * a booking conflict that does not exist. */
292
- export declare const MULTI_RATE_CODES: readonly ["AmbiguousMultiRate", "UnresolvableMultiRate"];
293
- /** True when an unavailable quote is a rate-configuration fault rather than a
294
- * genuinely unbookable stay. Callers branch the MESSAGE on this and must keep
295
- * booking disabled either way (SDK 2.16.0 MIGRATION, "Required Consumer
296
- * Work"). */
297
- export declare function isRateConfigurationFault(code: QuoteUnavailableCode | undefined): boolean;
298
- export interface QuoteOccupancy {
299
- adults: number;
300
- children: number;
301
- childrenAges?: number[] | undefined;
302
- babies: number;
303
- pets: number;
304
- }
305
- /** Flatten a GuestQuote into the renderable view. Values verbatim.
306
- *
307
- * DISCOUNTS: the SDK does not hand us a discount LINE. When the PMS applies a
308
- * price modifier (Frühbucher/Last-Minute, a voucher, a policy markup) it hangs
309
- * it off the charge line as `modifiers[]` — label plus signed amount — and puts
310
- * the pre-modifier figure in `totalBeforeModifiers`, leaving `appliedCharge`
311
- * already net. Render `appliedCharge` alone (what every adoption did until now)
312
- * and the saving is invisible: the guest sees only the reduced number and is
313
- * never told a discount applied.
314
- *
315
- * So each modifier becomes its own row, and the line above it switches to
316
- * `totalBeforeModifiers`. That pairing is not cosmetic — it is what makes the
317
- * card add up. A deduction row under an `appliedCharge` that has ALREADY been
318
- * reduced double-counts on sight: the column no longer sums to the total, and a
319
- * guest checking the arithmetic finds it wrong. Gross line, then the deduction,
320
- * then the remaining charges: 1.694,00 − 140,00 + 42,70 = 1.596,70.
321
- *
322
- * `bookingCardInformation.totalBeforeModifiers` is deliberately NOT rendered.
323
- * The deduction rows already carry the saving, and a struck-through "was" price
324
- * would state it a second time — as the one figure on the card that cannot be
325
- * checked by adding, and as a price claim the customer would have to stand
326
- * behind. The total stays the single net number.
327
- */
328
- export declare function adaptGuestQuote(quote: GuestQuote): QuoteView;
329
- /** Price a stay for `rentalId`. The caller owns the SDK instance and its
330
- * lifecycle — pass a shared one to reuse its queue + cache across the caller's
331
- * lifetime. `voucher` re-prices with a code (the PMS decides what it's worth). */
332
- export declare function fetchQuote(rentalId: string, period: {
333
- start: string;
334
- end: string;
335
- }, occupancy: QuoteOccupancy, sdk: WebsiteSdk, locale: Locale, voucher?: string): Promise<QuoteResult>;
336
- export type QuoteSelectionResult = {
1129
+ /** One stay to price. */
1130
+ interface QuoteRequest {
1131
+ /** PMS rental id (numeric unit id on v9). */
1132
+ rentalId: string;
1133
+ /** Arrival and departure days. */
1134
+ period: {
1135
+ start: IsoDay;
1136
+ end: IsoDay;
1137
+ };
1138
+ /** The party as the SDK takes it (`occupancyOf`). */
1139
+ occupancy: QuoteOccupancy;
1140
+ /** Language of the PMS strings and formats. */
1141
+ locale: Locale;
1142
+ /** Re-price with a code; the PMS decides what it is worth. */
1143
+ voucher?: string | undefined;
1144
+ }
1145
+ /** A selection call's answer: a NEW quote, or a refusal with the PMS's reason when it gave one (only
1146
+ * `UnavailableQuoteCombination` does). Callers keep the previous quote on a refusal. */
1147
+ type QuoteSelectionResult = {
337
1148
  ok: true;
338
1149
  quote: GuestQuote;
339
1150
  } | {
340
1151
  ok: false;
341
1152
  reason?: string | undefined;
342
1153
  };
343
- export declare function addQuoteService(quote: GuestQuote, id: string, sdk: WebsiteSdk): Promise<QuoteSelectionResult>;
344
- export declare function removeQuoteService(quote: GuestQuote, id: string, sdk: WebsiteSdk): Promise<QuoteSelectionResult>;
345
- /** Drop every additional-service selection from `quote`.
346
- *
347
- * A fresh quote's selection state is not knowable from the service list. It is
348
- * empty by default and carries the PMS's proposed quantities only when the
349
- * project sets `shouldAddProposedAdditionalServiceAmount: true` — an SDK
350
- * option a component cannot see — and the quote will not say which happened
351
- * (`GuestQuote.selections` is behind an unexported symbol). Rather than branch
352
- * on something invisible, a caller that needs a known starting point clears
353
- * first and steps up from zero with `addQuoteService`. */
354
- export declare function clearQuoteServices(quote: GuestQuote, sdk: WebsiteSdk): Promise<QuoteSelectionResult>;
355
- export declare function selectQuotePolicy(quote: GuestQuote, id: string, sdk: WebsiteSdk): Promise<QuoteSelectionResult>;
356
- /** Select a travel-insurance product (`id`) or none (`null`). Unlike the
357
- * service/policy calls the SDK returns the new GuestQuote directly and throws
358
- * on failure (unknown id) — normalized here to the same QuoteSelectionResult
359
- * the other selection helpers use, so a stale id is a silent no-op the caller
360
- * handles by keeping the previous quote.
361
- *
362
- * For a product that needs no traveler data this is the COMPLETE selection:
363
- * the premium books with the rental via `booking.book`, which reads the plan
364
- * off the quote. A product with `requiresTravelers` needs the pre-contract /
365
- * payment / `bookInsurance` steps on top, which the checkout does not
366
- * implement — it offers only the products it can complete. */
367
- export declare function selectQuoteInsurance(quote: GuestQuote, id: string | null, sdk: WebsiteSdk): Promise<QuoteSelectionResult>;
368
- /**
369
- * The concrete "free cancellation until" date for ONE policy — DERIVED, not
370
- * fabricated: the PMS states its rules relatively (`daysBeforeArrival` +
371
- * `refundPercentage`), so with the chosen arrival in hand the deadline is
372
- * plain arithmetic (arrival − daysBeforeArrival), the same category of
373
- * derivation as computing nights from a range.
374
- *
375
- * Uses the policy's full-refund (100%) rule. Returns null when the policy has
376
- * no full-refund rule OR the derived date is already in the past (booking
377
- * inside the window — advertising a bygone deadline would be wrong).
378
- */
379
- /** The policy's full-refund rule, if it has one. The refund figure is the SDK's
380
- * own formatted string ("100%"), so this reads it rather than comparing a
381
- * number — the one place that pattern is written, because both the derived
382
- * deadline and the "is this a refundable rate at all" question depend on it. */
383
- export declare function fullRefundRule(policy: QuoteCancellationPolicy | undefined): QuotePolicyRule | undefined;
384
- export declare function policyFreeCancellationUntil(policy: QuoteCancellationPolicy | undefined, arrival: Date): Date | null;
385
- /** The deadline for the stay as it stands — i.e. of the SELECTED policy.
386
- * Kept as its own name because that is what a summary line means: not "some
387
- * policy allows this" but "what you have chosen allows this". */
388
- export declare function freeCancellationUntil(policies: QuoteCancellationPolicy[], arrival: Date): Date | null;
389
- /** The same stay priced under ONE cancellation policy. `quote` is a real
390
- * GuestQuote branched off the base, so the UI can show its card, and hand the
391
- * branch on to a later step, without re-quoting the stay. */
392
- export interface PolicyBranch {
1154
+ /** The same stay priced under ONE cancellation policy (the base itself for the selected one). */
1155
+ interface PolicyBranch {
1156
+ /** The policy this branch is priced under. */
393
1157
  id: string;
394
- /** The branch quote — the base itself for the already-selected policy. */
1158
+ /** The branch quote (the base itself for the pre-selected policy); it can be booked as is. */
395
1159
  quote: GuestQuote;
396
- /** That quote flattened, so `view.card.total` is THIS policy's real price. */
1160
+ /** That quote flattened: `view.price.total` is THIS policy's real price. */
397
1161
  view: QuoteView;
398
1162
  }
399
- /**
400
- * Price every selectable cancellation policy off ONE base quote, so a guest can
401
- * compare the policies by what each actually costs rather than by a percentage.
402
- *
403
- * WHY REAL BRANCHES AND NOT ARITHMETIC. `markUpPercentage` looks like enough to
404
- * compute the alternative price, and it is not: the modifier applies to the
405
- * RENT, while taxes and fees ride along untouched, so the change it makes to
406
- * the total is always smaller than the percentage says — and a guest who checks
407
- * the number against the total would find the site's own figure wrong. The
408
- * arithmetic is unavailable in any case: every amount the SDK returns is a
409
- * locale-formatted string, so computing on one means parsing it back, which
410
- * this file does for no other charge either. What the PMS prices is what the UI
411
- * shows.
412
- *
413
- * `selectCancellationPolicy` does NOT mutate its input — it returns a new quote
414
- * — so each policy can be branched off the same base and the branches held side
415
- * by side. Costs one SDK call per policy MINUS one: the already-selected policy
416
- * is the base quote itself. Sequential, because the API allows 1 req/s.
417
- *
418
- * A policy the PMS marks `available: false` is skipped rather than attempted:
419
- * selecting it would fail by definition, and a burnt request inside a 1 req/s
420
- * budget delays the ones that can succeed. It simply gets no branch, and the UI
421
- * renders it as the unselectable row it already is.
422
- */
423
- export declare function pricePolicyBranches(base: GuestQuote, sdk: WebsiteSdk): Promise<PolicyBranch[]>;
1163
+ /** The two codes that mean "the stay WAS priced, but the rate setup cannot be resolved to the one
1164
+ * multi-rate a booking is written against" — a PMS configuration fault, never "not available". */
1165
+ export declare const MULTI_RATE_CODES: readonly ["AmbiguousMultiRate", "UnresolvableMultiRate"];
1166
+ /** Flatten a GuestQuote; values verbatim. Each modifier becomes its own row and the line above it
1167
+ * switches to `totalBeforeModifiers`, so the column adds up (1.694,00 − 140,00 + 42,70 = 1.596,70).
1168
+ * Consecutive identical SUB lines whose amount states no figure ("Inklusive", multi-season stays)
1169
+ * collapse into one; a line with a figure never collapses. */
1170
+ type AdaptGuestQuote = (quote: GuestQuote) => QuoteView;
1171
+ /** Price a stay (`quote.quote`). The caller owns the SDK; a throw is the caller's to log. */
1172
+ type FetchQuote = (sdk: WebsiteSdk, request: QuoteRequest) => Promise<QuoteResult>;
1173
+ /** A selection call on a live quote (add, remove, select); a throw is logged and becomes `ok: false`. */
1174
+ type SelectOnQuote = (sdk: WebsiteSdk, quote: GuestQuote, id: string) => Promise<QuoteSelectionResult>;
1175
+ /** Drop every service selection — the known starting point (see `proposedQuantity`). */
1176
+ type ClearQuoteServices = (sdk: WebsiteSdk, quote: GuestQuote) => Promise<QuoteSelectionResult>;
1177
+ /** Select an insurance product, or none (`null`). The SDK throws on failure; normalized here. */
1178
+ type SelectQuoteInsurance = (sdk: WebsiteSdk, quote: GuestQuote, id: string | null) => Promise<QuoteSelectionResult>;
1179
+ /** Which rates `pricePolicyBranches` prices. */
1180
+ interface PolicyBranchOptions {
1181
+ /** Only these ids (the rates `offeredPolicies` still offers for the arrival); default every one.
1182
+ * A withdrawn rate is never shown, so pricing it would only queue behind the 1 req/s limit. */
1183
+ only?: readonly string[];
1184
+ }
1185
+ /** Every selectable policy priced as its own quote off one base — sequential (the API allows 1 req/s),
1186
+ * skipping `available: false` and ids outside `only`; the selected one is the base itself; a refused
1187
+ * branch is omitted (a row without a price, never another rate's total). Real branches, because the
1188
+ * markup applies to the rent and not to the total. */
1189
+ type PricePolicyBranches = (sdk: WebsiteSdk, base: GuestQuote, options?: PolicyBranchOptions) => Promise<PolicyBranch[]>;
1190
+ /** True for a rate-configuration fault; the UI branches the MESSAGE on it and keeps booking disabled. */
1191
+ type IsRateConfigurationFault = (code: QuoteUnavailableCode | undefined) => boolean;
1192
+ /** Pin the presentation order of the breakdown across re-quotes: every selection returns a new quote
1193
+ * whose lines may come back reordered, and rows that jump read as flakiness. Each block (a charge with
1194
+ * its sub and discount rows) keeps the position it had when first seen; new blocks append in SDK
1195
+ * order. `orderMap` is the caller's memory across calls and is updated in place. */
1196
+ type StabilizeCardOrder = (view: QuoteView, orderMap: Map<string, number>) => QuoteView;
1197
+ /** See {@link AdaptGuestQuote}. */
1198
+ export declare const adaptGuestQuote: AdaptGuestQuote;
1199
+ /** See {@link FetchQuote}. */
1200
+ export declare const fetchQuote: FetchQuote;
1201
+ /** See {@link SelectOnQuote}: add one unit of a service. */
1202
+ export declare const addQuoteService: SelectOnQuote;
1203
+ /** See {@link SelectOnQuote}: remove one unit of a service. */
1204
+ export declare const removeQuoteService: SelectOnQuote;
1205
+ /** See {@link ClearQuoteServices}. */
1206
+ export declare const clearQuoteServices: ClearQuoteServices;
1207
+ /** See {@link SelectOnQuote}: price the stay under another cancellation policy. */
1208
+ export declare const selectQuotePolicy: SelectOnQuote;
1209
+ /** See {@link SelectQuoteInsurance}. */
1210
+ export declare const selectQuoteInsurance: SelectQuoteInsurance;
1211
+ /** See {@link PricePolicyBranches}. */
1212
+ export declare const pricePolicyBranches: PricePolicyBranches;
1213
+ /** See {@link IsRateConfigurationFault}. */
1214
+ export declare const isRateConfigurationFault: IsRateConfigurationFault;
1215
+ /** See {@link StabilizeCardOrder}. */
1216
+ export declare const stabilizeCardOrder: StabilizeCardOrder;
1217
+ //#endregion
1218
+ //#region src/quote/policies.d.ts
1219
+ /** What a rate is worth FOR THIS STAY.
1220
+ * - "refundable": a full-refund deadline still ahead (or no stay picked yet: nothing has lapsed).
1221
+ * - "expired": it has a full-refund rule and no rule at all can still be met.
1222
+ * - "other": no full-refund rule, or only a partial rule still reachable. */
1223
+ type PolicyStance = "refundable" | "expired" | "other";
1224
+ /** What a row is CALLED. A host-created policy keeps its label; the PMS's three built-ins are titled by
1225
+ * stance, because their names grade the host's strictness and read to a guest as a warning. */
1226
+ type PolicyTitle =
1227
+ /** The host's own name, verbatim — including a host-made default ("a host named theirs Testrichtlinie"). */
1228
+ {
1229
+ kind: "own";
1230
+ label: string;
1231
+ } |
1232
+ /** A built-in that still refunds in full. */
1233
+ {
1234
+ kind: "refundable";
1235
+ } |
1236
+ /** A built-in whose window has closed for this stay. */
1237
+ {
1238
+ kind: "expired";
1239
+ } |
1240
+ /** A built-in that is neither (unreachable with today's three; never falls back to the name). */
1241
+ {
1242
+ kind: "standard";
1243
+ };
1244
+ /** What a policy MEANS, decided by precedence — one variant, never several:
1245
+ * deadline (single rule, still ahead) > prose (not an echo of the title, no smuggled built-in name) >
1246
+ * rules > none; "expired" replaces all of them. */
1247
+ type PolicyTerms =
1248
+ /** "Free cancellation until {date}": arrival − the 100 % rule's days. */
1249
+ {
1250
+ kind: "deadline";
1251
+ date: IsoDay;
1252
+ } |
1253
+ /** The PMS's own description, verbatim. */
1254
+ {
1255
+ kind: "prose";
1256
+ text: string;
1257
+ } |
1258
+ /** The refund ladder, one line per rule. */
1259
+ {
1260
+ kind: "rules";
1261
+ rules: QuotePolicyRule[];
1262
+ } |
1263
+ /** The refundable window has closed; `daysBeforeArrival` names the lapsed deadline. */
1264
+ {
1265
+ kind: "expired";
1266
+ daysBeforeArrival: number;
1267
+ } |
1268
+ /** Nothing to state (the SDK's minimal `{ id, label, selected }`, or the title already said it). */
1269
+ {
1270
+ kind: "none";
1271
+ };
1272
+ /** One offered rate, ready to render. */
1273
+ interface PolicyOption {
1274
+ /** The policy id `choosePolicy` takes. */
1275
+ id: string;
1276
+ /** What the row is called. */
1277
+ title: PolicyTitle;
1278
+ /** The rate the shown price is for. */
1279
+ selected: boolean;
1280
+ /** False: shown, but not selectable for the current combination. */
1281
+ available: boolean;
1282
+ /** The PMS's reason for `available: false`, verbatim, or null. */
1283
+ unavailableReason: string | null;
1284
+ /** This rate's real total from its branch; null while pending, when the branch failed, or when no
1285
+ * branches were priced. */
1286
+ total: string | null;
1287
+ /** What the rate means for this stay. */
1288
+ terms: PolicyTerms;
1289
+ /** Shown under the terms. `markUp` only when `total` is null — a percentage beside a real total
1290
+ * invites a check that finds the site wrong (the markup applies to the rent, not the total). */
1291
+ notes: {
1292
+ markUp: string | null;
1293
+ processingFee: string | null;
1294
+ };
1295
+ }
1296
+ /** Where the guest picks the rate — ONE site decision, the same value on Quote and Booking:
1297
+ * "rental-page" (default): Quote offers the picker, Booking applies the carried ?policy= and states it;
1298
+ * "checkout": Quote states the pre-selected rate's terms only, Booking offers the picker. */
1299
+ type PolicyChoiceMode = "rental-page" | "checkout";
1300
+ /** The policy model of one priced stay. */
1301
+ interface PolicyOffer {
1302
+ /** "choice": ≥ 2 offered rates on the surface that picks → a picker (`options` = every offered rate).
1303
+ * "single": one offered rate there → `freeCancellationUntil` as one sentence, or nothing.
1304
+ * "terms": the other surface → the applied rate's terms block, no picker (`options` = that rate).
1305
+ * "none": the quote carries no policies (every v9 hub) → a site's `cancellationNote` may speak. */
1306
+ mode: "choice" | "single" | "terms" | "none";
1307
+ /** The rows to render (see `mode`). */
1308
+ options: PolicyOption[];
1309
+ /** The guest's explicit pick when it is offered; null = the PMS pre-selection. */
1310
+ chosenId: string | null;
1311
+ /** Branch prices are still being fetched. */
1312
+ pricesPending: boolean;
1313
+ /** For "single" and "terms": the shown rate's live free-cancellation day, or null. */
1314
+ freeCancellationUntil: IsoDay | null;
1315
+ }
1316
+ /** What `describePolicyOffer` takes. */
1317
+ interface PolicyOfferInput {
1318
+ /** The quote's policies (unfiltered; the offer rule is applied here). */
1319
+ policies: readonly QuoteCancellationPolicy[];
1320
+ /** The priced branches, or null when none were priced. */
1321
+ branches: readonly PolicyBranch[] | null;
1322
+ /** Branch prices are still being fetched. */
1323
+ pricesPending: boolean;
1324
+ /** The guest's explicit pick, or null. */
1325
+ chosenId: string | null;
1326
+ /** The stay's arrival. */
1327
+ arrival: IsoDay;
1328
+ /** The site's decision where the rate is chosen. */
1329
+ mode: PolicyChoiceMode;
1330
+ /** Which surface asks: the Quote offers the picker in "rental-page" mode, Booking in "checkout" mode. */
1331
+ surface: "quote" | "checkout";
1332
+ /** Default today in the visitor's timezone. */
1333
+ today?: IsoDay;
1334
+ }
1335
+ /** What `policyTerms` takes. */
1336
+ interface PolicyTermsOptions {
1337
+ /** The stay's arrival; absent before a stay is picked (no deadline line then). */
1338
+ arrival?: IsoDay;
1339
+ /** Default today in the visitor's timezone. */
1340
+ today?: IsoDay;
1341
+ /** The row shows this policy's real total. Terms never depend on it — it decides
1342
+ * `PolicyOption.notes.markUp` — and is accepted so both describe one row with one object. */
1343
+ hasTotal: boolean;
1344
+ /** The row already prints `policyTitle` above the terms: the expired line is then dropped where the
1345
+ * title was substituted (a built-in), never under a host's own name, which says nothing about refunds. */
1346
+ titled: boolean;
1347
+ }
1348
+ /** The backend states deadlines in whole days only (`daysBeforeArrival`; the v1 schema carries no hour,
1349
+ * cut-off or timezone), so no time is ever printed and every comparison is at midnight. */
1350
+ export declare const POLICY_DEADLINE_HAS_TIME: false;
1351
+ /** The type of `BUILT_IN_POLICY_LABELS` (declared below): the PMS's three built-in policy names
1352
+ * (SYSTEM_FIRM_14, SYSTEM_STRICT_30, SYSTEM_STRICT_60) in de + en, lowercased — closed, read out of the
1353
+ * SDK's translation catalogue; a package test re-reads the installed catalogue in both directions. */
1354
+ type BuiltInPolicyLabels = ReadonlySet<string>;
1355
+ /** True for one of the PMS's built-ins; everything else the host created and named. */
1356
+ type IsBuiltInPolicy = (policy: QuoteCancellationPolicy) => boolean;
1357
+ /** The rate's stance for a stay arriving on `arrival` (absent: no stay picked → describe the rate). */
1358
+ type PolicyStanceOf = (policy: QuoteCancellationPolicy, arrival?: IsoDay, today?: IsoDay) => PolicyStance;
1359
+ /** The row title (see `PolicyTitle`). */
1360
+ type PolicyTitleOf = (policy: QuoteCancellationPolicy, arrival?: IsoDay, today?: IsoDay) => PolicyTitle;
1361
+ /** The terms by precedence (see `PolicyTerms`). */
1362
+ type PolicyTermsOf = (policy: QuoteCancellationPolicy, options: PolicyTermsOptions) => PolicyTerms;
1363
+ /** The full set minus rates that stopped being a trade: beside a standard rate that can no longer refund
1364
+ * anything, only rates that still can survive — otherwise the cheaper non-refundable rate is the same
1365
+ * booking for less. The standard (the row without a markup, else the first; never "selected", which
1366
+ * moves) always survives. A rule refunding 0 % refunds nothing, however reachable. */
1367
+ type OfferedPolicies = (policies: readonly QuoteCancellationPolicy[], arrival?: IsoDay, today?: IsoDay) => QuoteCancellationPolicy[];
1368
+ /** Arrival − the 100 % rule's days, if still ahead; else null (never a bygone deadline). */
1369
+ type FreeCancellationUntil = (policy: QuoteCancellationPolicy | undefined, arrival: IsoDay, today?: IsoDay) => IsoDay | null;
1370
+ /** The policy model of a priced stay for one surface (shared by createQuoteFlow and the checkout). */
1371
+ type DescribePolicyOffer = (input: PolicyOfferInput) => PolicyOffer;
1372
+ /** Duplicated from the SDK catalogue (`dist/translations/v10/<locale>/core.json`,
1373
+ * `CancellationPolicy.policyType.option.SYSTEM_*`) because it is not reachable through the public SDK.
1374
+ * A host-created policy is returned untranslated (measured: byte-identical in de-DE and en-US), a
1375
+ * second reason this list can only ever match the backend's own. */
1376
+ export declare const BUILT_IN_POLICY_LABELS: BuiltInPolicyLabels;
1377
+ /** See {@link IsBuiltInPolicy}. */
1378
+ export declare const isBuiltInPolicy: IsBuiltInPolicy;
1379
+ /** See {@link FreeCancellationUntil}. */
1380
+ export declare const freeCancellationUntil: FreeCancellationUntil;
1381
+ /** See {@link PolicyStanceOf}. */
1382
+ export declare const policyStance: PolicyStanceOf;
1383
+ /** See {@link PolicyTitleOf}. */
1384
+ export declare const policyTitle: PolicyTitleOf;
1385
+ /** See {@link PolicyTermsOf}. */
1386
+ export declare const policyTerms: PolicyTermsOf;
1387
+ /** See {@link OfferedPolicies}. */
1388
+ export declare const offeredPolicies: OfferedPolicies;
1389
+ /** See {@link DescribePolicyOffer}. */
1390
+ export declare const describePolicyOffer: DescribePolicyOffer;
1391
+ //#endregion
1392
+ //#region src/quote/events.d.ts
1393
+ /** The window event name (`CustomEvent<QuoteEventDetail>`), dispatched once per stay. */
1394
+ export declare const QUOTE_EVENT: "voffice:quote";
1395
+ /** What happened. "rate-fault": a stay the PMS priced but cannot resolve to one multi-rate
1396
+ * (`AmbiguousMultiRate` / `UnresolvableMultiRate`) — a configuration fault for site operations
1397
+ * (SDK 2.16.0 MIGRATION §2), never shown to the guest as "not available". */
1398
+ type QuoteEventDetail = {
1399
+ type: "rate-fault";
1400
+ /** PMS rental id. */
1401
+ rentalId: string;
1402
+ /** The stay (`stayKeyOf`: dates + party counts, no names). */
1403
+ stayKey: string;
1404
+ /** The SDK's machine code. */
1405
+ code: QuoteUnavailableCode;
1406
+ };
1407
+ //#endregion
1408
+ //#region src/quote/createQuoteFlow.d.ts
1409
+ /** v9 in practice: show the campaign label the search measured for THIS stay.
1410
+ * "none" (default) · "from-search": read `?offer=`, latch it to the first complete stay after the
1411
+ * handover (only when that stay is provably the one the search priced), drop it on any edit, never
1412
+ * re-arm. A label only — never a struck price or a saving (search and quote disagree on discounted
1413
+ * totals). */
1414
+ type CampaignLabelSource = "none" | "from-search";
1415
+ /** What an unavailable stay offers beyond the PMS's reason.
1416
+ * "none" (default) · "next-arrival": the first arrivable day after the chosen start, from the arrival
1417
+ * map already loaded (no extra request). "other-rentals" is reserved (needs a search runner). */
1418
+ type WhenUnavailable = "none" | "next-arrival";
1419
+ /** The options a widget brings to its rental's flow (Quote's props). `configure` applies them to a flow
1420
+ * another island of the rental created first (an occupancy calendar that hydrated earlier). */
1421
+ interface QuoteFlowSettings {
1422
+ /** limits (a 20-person house raises adults), childAge (ages outside it are asked again),
1423
+ * defaultAdults (default 2). */
1424
+ guests?: GuestOptions;
1425
+ /** Default "rental-page". */
1426
+ policyChoice?: PolicyChoiceMode;
1427
+ /** Default "none". */
1428
+ campaignLabels?: CampaignLabelSource;
1429
+ /** Default "none". */
1430
+ whenUnavailable?: WhenUnavailable;
1431
+ /** The site's minimum stay in nights on top of the PMS (see `StayPickerOptions.minStayNights`). */
1432
+ minStayNights?: number;
1433
+ }
1434
+ /** The flow's options: the picker's, plus the widget's settings, the caps and the hooks. */
1435
+ interface QuoteFlowOptions extends StayPickerOptions, QuoteFlowSettings {
1436
+ /** Catalogue capacity (where availability states no caps: v9 before SDK 2.28) and the pets flag. */
1437
+ caps?: Pick<GuestCaps, "maxGuests" | "petsAllowed">;
1438
+ /** Debounce before the auto-quote, coalescing stepper bursts. Default 350. */
1439
+ autoQuoteDelayMs?: number;
1440
+ /** Once per stay on AmbiguousMultiRate / UnresolvableMultiRate. Default: one console.warn. */
1441
+ onEvent?: (detail: QuoteEventDetail) => void;
1442
+ }
1443
+ /** The answer for the current stay. Every non-idle kind carries the `stayKey` it belongs to, so
1444
+ * nothing from a superseded stay is ever shown. */
1445
+ type QuoteOutcome =
1446
+ /** No complete range, no adult, an age missing, or the handover still resolving. */
1447
+ {
1448
+ kind: "idle";
1449
+ } |
1450
+ /** Debounce, the arrival map, the request, or the chosen rate's branch still in flight. */
1451
+ {
1452
+ kind: "pending";
1453
+ stayKey: string;
1454
+ } |
1455
+ /** The price of the shown rate (the guest's pick or the PMS pre-selection). */
1456
+ {
1457
+ kind: "priced";
1458
+ stayKey: string;
1459
+ price: PriceView;
1460
+ policies: PolicyOffer;
1461
+ /** The latched search labels while this IS the stay they were measured for, else []. */
1462
+ campaignLabels: string[];
1463
+ /** Carried for custom UIs; the quote surface never renders insurance. */
1464
+ insurance: QuoteInsurance | null;
1465
+ } |
1466
+ /** NotBookable: the SDK's reason verbatim (possibly multi-line), or null when it states none. */
1467
+ {
1468
+ kind: "unavailable";
1469
+ stayKey: string;
1470
+ reason: string | null;
1471
+ } |
1472
+ /** The days are free but the rate setup is faulty — never say "not available". */
1473
+ {
1474
+ kind: "rate-fault";
1475
+ stayKey: string;
1476
+ code: QuoteUnavailableCode;
1477
+ } |
1478
+ /** Transport failure; only `retry()` refires. */
1479
+ {
1480
+ kind: "error";
1481
+ stayKey: string;
1482
+ };
1483
+ /** What the primary button does now. */
1484
+ type QuoteCta =
1485
+ /** The handover is not read yet → skeleton. */
1486
+ {
1487
+ kind: "resolving";
1488
+ } |
1489
+ /** Opens the calendar. */
1490
+ {
1491
+ kind: "pick-dates";
1492
+ } |
1493
+ /** Opens the guest picker: an age is missing (the age prices the stay). */
1494
+ {
1495
+ kind: "complete-ages";
1496
+ } |
1497
+ /** "Price loading", disabled. */
1498
+ {
1499
+ kind: "pending";
1500
+ } |
1501
+ /** The checkout link (`checkoutHref`). */
1502
+ {
1503
+ kind: "book";
1504
+ } |
1505
+ /** Unavailable or rate fault: opens the calendar. */
1506
+ {
1507
+ kind: "change-dates";
1508
+ } |
1509
+ /** After an error. */
1510
+ {
1511
+ kind: "retry";
1512
+ };
1513
+ /** The flow's state. */
1514
+ interface QuoteFlowState {
1515
+ /** The handover was applied (and a ± window resolved); until then fields show skeletons. */
1516
+ resolved: boolean;
1517
+ /** The calendar's state (`flow.picker.day()` renders the days). */
1518
+ picker: StayPickerState;
1519
+ /** The travelling party (seeded with `defaultAdults`, the handover wins). */
1520
+ party: Party;
1521
+ /** Stepper bounds for this rental (availability caps > catalogue `maxGuests` > limits). */
1522
+ bounds: GuestBounds;
1523
+ /** Every child has an age; no quote runs before. */
1524
+ agesComplete: boolean;
1525
+ /** Dates + party as one string (`stayKeyOf`), or null without a complete range. */
1526
+ stayKey: string | null;
1527
+ /** The answer for the current stay. */
1528
+ outcome: QuoteOutcome;
1529
+ /** What the primary button does now. */
1530
+ cta: QuoteCta;
1531
+ /** The guest's explicit rate; null = the PMS pre-selection. Reset to null when the stay stops
1532
+ * offering it (an unknown id from a link, a rate withdrawn by `offeredPolicies`, a failed branch). */
1533
+ chosenPolicyId: string | null;
1534
+ /** With `whenUnavailable: "next-arrival"` and an unavailable / rate-fault outcome: the day, or null. */
1535
+ nextArrival: IsoDay | null;
1536
+ /** Incremented by `requestFocus` / `pickArrival`; a UI opens or scrolls to the widget on a change. */
1537
+ focusRequest: number;
1538
+ }
1539
+ /** The flow: a store plus the widget's actions. */
1540
+ interface QuoteFlow extends Store<QuoteFlowState> {
1541
+ /** The underlying picker (day models for the calendar). */
1542
+ readonly picker: StayPicker;
1543
+ /** Replace the widget's settings (absent keys fall back to their defaults). For a widget joining a
1544
+ * flow another island created; call it before `hydrate`. Before the handover the party is re-seeded
1545
+ * with the new `defaultAdults`; outside "rental-page" the guest's rate is dropped. */
1546
+ configure(settings: QuoteFlowSettings): void;
1547
+ /** Apply a handover once (`readQuoteHandover(new URLSearchParams(location.search))`); later calls
1548
+ * are ignored. A ± window is parked until the arrival map lands, then resolved in two stages: the
1549
+ * first arrivable day that still fits the stay, then the wanted length or the nearest allowed
1550
+ * departure inside the window. */
1551
+ hydrate(handover: QuoteHandover): void;
1552
+ /** A calendar click (see `StayPicker.select`). */
1553
+ selectDay(iso: IsoDay): void;
1554
+ /** Clear the dates. */
1555
+ resetDates(): void;
1556
+ /** Change the party; clamped to the bounds (a dog cannot be added where pets are not allowed, a
1557
+ * handed-over one can be lowered and is still sent). */
1558
+ setParty(patch: Partial<Party>): void;
1559
+ /** The guest's rate (null = PMS pre-selection); "rental-page" mode only. Switching costs no request:
1560
+ * every branch was priced up front. */
1561
+ choosePolicy(id: string | null): void;
1562
+ /** Re-run a quote that failed in transport. */
1563
+ retry(): void;
1564
+ /** From AvailabilityCalendar: set the arrival only, enter the departure phase, request focus. */
1565
+ pickArrival(iso: IsoDay): void;
1566
+ /** Apply `nextArrival` as the new start (departure phase). */
1567
+ takeNextArrival(): void;
1568
+ /** Ask the UI to bring the widget into view. */
1569
+ requestFocus(): void;
1570
+ /** Fetch availability; nothing is quoted before the arrival map answered (ready or failed). */
1571
+ start(): void;
1572
+ /** Stop: timers cleared, late answers dropped. */
1573
+ dispose(): void;
1574
+ }
1575
+ /** The flow over the caller's SDK, resolved once on the first request (again only after a failed
1576
+ * resolve) and shared with its picker. Requests: the arrival map, one departure map per chosen start,
1577
+ * one quote per settled stay, and in "rental-page" mode one branch per further offered rate. */
1578
+ type CreateQuoteFlow = (sdk: SdkSource, options: QuoteFlowOptions) => QuoteFlow;
1579
+ /** One flow per key across islands (card, bar, an occupancy calendar), ref-counted on `globalThis`. The
1580
+ * first caller's `create` wins (a widget that joins re-applies its settings with `configure`); the last
1581
+ * `release` disposes. Userland can subscribe to a rental's flow (a gallery offer pill) without a window
1582
+ * event. */
1583
+ type AcquireQuoteFlow = (key: string, create: () => QuoteFlow) => {
1584
+ readonly flow: QuoteFlow;
1585
+ release(): void;
1586
+ };
1587
+ /** See {@link CreateQuoteFlow}. */
1588
+ export declare const createQuoteFlow: CreateQuoteFlow;
1589
+ /** See {@link AcquireQuoteFlow}. */
1590
+ export declare const acquireQuoteFlow: AcquireQuoteFlow;
1591
+ //#endregion
1592
+ //#region src/quote/handover.d.ts
1593
+ /** The rental-page keys this surface reads and writes (the funnel contract: search → quote → checkout). */
1594
+ export declare const QUOTE_PARAM_KEYS: readonly ["start", "end", "adults", "children", "childrenAges", "babies", "pets", "policy"];
1595
+ /** Keys a ± search link adds: resolved into a stay, then stripped. */
1596
+ export declare const WINDOW_PARAM_KEYS: readonly ["minNights", "maxNights", "flex"];
1597
+ /** The repeatable campaign label a search card hands over — a label only, never a figure. */
1598
+ export declare const CAMPAIGN_PARAM: "offer";
1599
+ /** What a rental-page URL says. Invalid values degrade to absent, never to an error. */
1600
+ interface QuoteHandover {
1601
+ /** A valid day, or null. */
1602
+ start: IsoDay | null;
1603
+ /** A valid day after `start`, or null. */
1604
+ end: IsoDay | null;
1605
+ /** A ± window: start/end are its bounds and the stay length is pinned. Present only with start + end
1606
+ * and a night pair shorter than the window; nights = `maxNights` (equal to `minNights` on a pinned
1607
+ * pair; a range min < max takes the upper bound, the length the hub prices). */
1608
+ window: {
1609
+ nights: number;
1610
+ } | null;
1611
+ /** Null when the URL carries no party key at all (the flow then seeds `defaultAdults`). Read with
1612
+ * `readParty`: `petsCount` (legacy search links) wins over `pets`. */
1613
+ party: Partial<Party> | null;
1614
+ /** An explicit rate from a shared link; checked against the stay's offered rates later. */
1615
+ policyId: string | null;
1616
+ /** Campaign labels: trimmed, distinct, at most four (a crafted URL cannot paste a list into the box). */
1617
+ campaignLabels: string[];
1618
+ }
1619
+ /** Read a handover. */
1620
+ type ReadQuoteHandover = (params: URLSearchParams) => QuoteHandover;
1621
+ /** The URL after a state change: quote keys set or deleted (start always, end only when complete,
1622
+ * adults even at the default, zero counts omitted, `policy` only for an explicit choice — the absence
1623
+ * IS the deferral to the PMS pre-selection), the legacy `petsCount` and the window keys stripped once
1624
+ * resolved, campaign labels kept only while latched to the priced stay. Every other key keeps its value
1625
+ * and position. Before the handover resolved, `current` is returned unchanged (the first write must not
1626
+ * clobber what is still being read). Returns a new object; equal output = leave the URL alone. */
1627
+ type WriteQuoteParams = (current: URLSearchParams, state: QuoteFlowState) => URLSearchParams;
1628
+ /** `path?<stay>` from STATE (never from location), so the link is the priced stay: start, end, the party
1629
+ * (dogs as `pets`), `policy` only when explicit, `kind=option` for an option booking. Null without a
1630
+ * complete, priced stay. */
1631
+ type CheckoutHref = (path: string, state: QuoteFlowState, kind?: "booking" | "option") => string | null;
1632
+ /** The stay alone as a query string without "?" (dates + party; for an enquiry link). Null without a
1633
+ * complete range. */
1634
+ type StayQuery = (state: QuoteFlowState) => string | null;
1635
+ /** See {@link ReadQuoteHandover}. */
1636
+ export declare const readQuoteHandover: ReadQuoteHandover;
1637
+ /** See {@link WriteQuoteParams}. */
1638
+ export declare const writeQuoteParams: WriteQuoteParams;
1639
+ /** See {@link CheckoutHref}. */
1640
+ export declare const checkoutHref: CheckoutHref;
1641
+ /** See {@link StayQuery}. */
1642
+ export declare const stayQuery: StayQuery;
1643
+ //#endregion
1644
+ //#region src/checkout/stay.d.ts
1645
+ /** A complete stay: arrival and departure (local calendar days, `end` after `start`) and the party.
1646
+ * Serializable on purpose: it travels through the URL handover and into the return snapshot. */
1647
+ interface Stay {
1648
+ /** Arrival. */
1649
+ readonly start: IsoDay;
1650
+ /** Departure (after `start`). */
1651
+ readonly end: IsoDay;
1652
+ /** The travelling party. */
1653
+ readonly party: Party;
1654
+ }
1655
+ //#endregion
1656
+ //#region src/checkout/handover.d.ts
1657
+ /** A confirmed booking, or a non-binding option (SDK `kind: "OPTION"`, v10 and v9). */
1658
+ type BookingKind = "booking" | "option";
1659
+ /** What the checkout URL says. */
1660
+ interface CheckoutHandover {
1661
+ /** From `start`, `end` and the party keys; null when the dates are missing or invalid (the
1662
+ * checkout shows its missing-stay card). */
1663
+ stay: Stay | null;
1664
+ /** `?policy=`: the rate chosen on the rental page. A seed: the flow applies it only while the
1665
+ * policy rule still offers it for this arrival. Null = the PMS pre-selection. */
1666
+ policyId: string | null;
1667
+ /** `?kind=option`, honoured only with `allowOption`; otherwise "booking". */
1668
+ kind: BookingKind;
1669
+ /** `?return=1`: a payment provider sent the guest back to this page (sites that return to the
1670
+ * checkout itself; the package's own return page is a separate route). */
1671
+ paymentReturn: boolean;
1672
+ }
1673
+ /** How the checkout reads its URL. */
1674
+ interface CheckoutHandoverOptions {
1675
+ /** Adults assumed when the URL has dates but no adults. Default 2 (the quote's default). */
1676
+ defaultAdults?: number | undefined;
1677
+ /** Accept `?kind=option`. Default false. */
1678
+ allowOption?: boolean | undefined;
1679
+ }
1680
+ /** Parse the checkout URL (pass `location.search`). Pure. Reads `pets` and the legacy `petsCount`;
1681
+ * `childrenAges` aligns to `children` (a missing age is null, and blocks booking later). */
1682
+ type ReadCheckoutHandover = (search: string, options?: CheckoutHandoverOptions) => CheckoutHandover;
1683
+ /** The query string (no leading "?") for `history.replaceState` after an edit: the stay (pets as
1684
+ * `pets`), an explicit policy only (never today's pre-selection, which would pin it), `kind=option`
1685
+ * for an option. Every other parameter in `current` (tracking, site keys) is preserved after them. */
1686
+ type CheckoutSearch = (input: {
1687
+ stay: Stay | null;
1688
+ policyId: string | null;
1689
+ kind: BookingKind;
1690
+ }, current?: string) => string;
1691
+ /** See {@link ReadCheckoutHandover}. */
1692
+ export declare const readCheckoutHandover: ReadCheckoutHandover;
1693
+ /** See {@link CheckoutSearch}. */
1694
+ export declare const checkoutSearch: CheckoutSearch;
424
1695
  //#endregion
425
- //#region src/booking.d.ts
426
- /** Where the payment provider sends the guest back after an online payment
427
- * (relative — the SDK/PSP resolves it against the site origin). */
428
- export declare const CHECKOUT_RETURN_PATH = "/buchen/bestaetigung";
429
- /** The checkout form's flat contact fields (the UI shape). `toBookingContact`
430
- * folds them into the SDK's nested BookingContact. */
431
- export interface BookingContactForm {
1696
+ //#region src/checkout/booking.d.ts
1697
+ /** The guest's contact as the guest form produces it; folded into the SDK's nested contact. */
1698
+ interface BookingContact {
1699
+ /** Given name. */
432
1700
  forename: string;
1701
+ /** Family name. */
433
1702
  surname: string;
434
- /** Anrede ("Frau" / "Herr"); omitted when the guest picks none. */
435
- title?: string | undefined;
1703
+ /** A `SALUTATION_WIRE_VALUES` value; omitted when none was chosen. */
1704
+ title?: string;
1705
+ /** Street (with the number unless the PMS splits it into `housenumber`). */
436
1706
  street: string;
437
- housenumber?: string | undefined;
1707
+ /** House number, where the form asks it separately. */
1708
+ housenumber?: string;
1709
+ /** Postal code. */
438
1710
  postalcode: string;
1711
+ /** Town. */
439
1712
  city: string;
440
- /** ISO 3166-1 alpha-2, e.g. "DE". */
441
- countryCode: string;
1713
+ /** ISO country code. */
1714
+ countryCode: CountryCode;
1715
+ /** Email address (the confirmation goes there). */
442
1716
  email: string;
443
- phone?: string | undefined;
444
- note?: string | undefined;
1717
+ /** Normalized `+CC …` (the booking endpoint accepts nothing else). */
1718
+ phone?: string;
1719
+ /** The guest's message to the host. */
1720
+ note?: string;
445
1721
  }
446
- export interface PaymentLine {
1722
+ /** One line of a payment entry, verbatim. */
1723
+ interface PaymentLine {
1724
+ /** The line's label, verbatim. */
447
1725
  label: string;
1726
+ /** The line's formatted amount, verbatim. */
448
1727
  amount: string;
449
1728
  }
450
- /** One payment entry, verbatim — a contractual step (Anzahlung / Restzahlung /
451
- * Rate / Kaution) or a way to settle the whole outstanding balance. */
452
- export interface PaymentScheduleView {
453
- /** SDK's stable kind, for programmatic behaviour ONLY. Everything the guest
454
- * reads comes from `label`: the SDK translates it, and a v10 installment is
455
- * not automatically a v9 prepayment. */
1729
+ /** One payment entry, verbatim from the PMS: a contractual step, or a way to settle the whole
1730
+ * balance at once (`full_amount`). */
1731
+ interface PaymentScheduleView {
1732
+ /** For behaviour only; the guest reads `label` (a v10 installment is not a v9 prepayment). */
456
1733
  kind: BookingPaymentScheduleKind | "full_amount";
1734
+ /** The entry's label, verbatim ("Anzahlung", "Restzahlung"). */
457
1735
  label: string;
458
- /** Complete translated phrase, e.g. "Zahlbar bis zum 6. Februar 2027".
459
- * Render as-is — never parse it, never prefix another due-date label. It
460
- * stopped being a bare date in website-sdk 2.13.0, and it was never reliably
461
- * ISO before that either. */
462
- dueOn?: string | undefined;
463
- /** Where the payment happens, translated (v10 supplies it when the backend
464
- * states a payment location). Same rule: render, don't reformat. */
465
- paymentAt?: string | undefined;
466
- lines: PaymentLine[];
1736
+ /** A complete sentence ("Zahlbar bis zum 6. Februar 2027"): render, never parse or prefix. */
1737
+ dueOn?: string;
1738
+ /** Where the payment happens (v10, when the backend states it). */
1739
+ paymentAt?: string;
1740
+ /** The entry's line items. */
1741
+ lines: readonly PaymentLine[];
1742
+ /** The entry's formatted amount, verbatim. */
467
1743
  amount: string;
468
- /** The PMS's own payment options — the SDK union (bank_transfer / redirect /
469
- * form_post / stripe_checkout), untouched. **EMPTY means informational:**
470
- * the SDK cannot perform this payment right now (a future step, on-site, or
471
- * external), so the card must not offer a button for it. */
472
- options: BookingPaymentOption[];
473
- }
474
- /** A travel-insurance premium still to be settled WITH THE INSURER, from
475
- * `book().insurancePaymentOptions` — label and URL verbatim. Deliberately not
476
- * a `PaymentScheduleView`: those are the PMS's own steps, owed to the site's
477
- * operator and payable through the SDK. This one is a different contract with
478
- * a different recipient, and it leaves the site entirely. */
479
- export interface InsurancePaymentLink {
1744
+ /** Empty = informational (a future, on-site or external step): no button for it. */
1745
+ options: readonly BookingPaymentOption[];
1746
+ }
1747
+ /** An insurance premium owed to the INSURER (`external_url`), never part of the schedules: a
1748
+ * different contract with a different recipient, paid off the site. */
1749
+ interface InsurancePaymentLink {
1750
+ /** The insurer's label for the payment. */
480
1751
  label: string;
1752
+ /** The insurer's payment page. */
481
1753
  url: string;
482
- description?: string | undefined;
1754
+ /** The insurer's line about it, when stated. */
1755
+ description?: string;
483
1756
  }
484
- export interface BookingConfirmation {
1757
+ /** The inline confirmation after `book()`. */
1758
+ interface BookingConfirmation {
1759
+ /** A booking or an option. */
1760
+ kind: BookingKind;
1761
+ /** The PMS booking number, verbatim. */
485
1762
  bookingNumber: string;
486
- /** The contractual steps, in backend order — what the guest owes and when. */
487
- schedules: PaymentScheduleView[];
488
- /** Ways to settle the COMPLETE outstanding balance at once (kind
489
- * `full_amount`), an ALTERNATIVE to the schedule above rather than an
490
- * addition to it. Kept apart because summing the two would double-count, and
491
- * because a synthesized alternative carries no deadline of its own — the
492
- * schedule's steps are where the dates live. */
493
- alternatives: PaymentScheduleView[];
494
- /** Empty unless insurance was actually booked AND the insurer returned a
495
- * payment page for it. Kept out of `schedules` and `alternatives` on
496
- * purpose: the premium is in neither, so a card that listed it among them
497
- * would misstate what is owed to whom (CONTRACT.md, "Travel insurance"). */
498
- insurancePayments: InsurancePaymentLink[];
1763
+ /** The contractual steps, in backend order. */
1764
+ schedules: readonly PaymentScheduleView[];
1765
+ /** Ways to settle the whole balance at once: an ALTERNATIVE to `schedules`, never added to it. */
1766
+ alternatives: readonly PaymentScheduleView[];
1767
+ /** Empty unless insurance was booked and the insurer returned a payment page. */
1768
+ insurancePayments: readonly InsurancePaymentLink[];
1769
+ /** True when any option in `schedules` or `alternatives` can be performed in the browser. */
1770
+ payable: boolean;
499
1771
  }
500
- /**
501
- * Create the booking. `quote` MUST be the live GuestQuote instance (with any
502
- * service/policy selections applied). USER-initiated only — this reserves for
503
- * real. Throws on transport/backend errors (the caller renders an honest
504
- * retry message; nothing was necessarily booked when it throws).
505
- */
506
- export declare function submitBooking(quote: GuestQuote, contact: BookingContactForm, sdk: WebsiteSdk, returnPath?: string): Promise<BookingConfirmation>;
507
- /** True for the option kinds `submitPaymentOption` can perform in-browser
508
- * (redirect / form_post / stripe_checkout). Bank transfer is display-only. */
509
- export declare function isSubmittableOption(option: BookingPaymentOption): option is SubmittableBookingPaymentOption;
510
- /** Perform an online payment option (redirects the browser to the PSP).
511
- * USER-initiated only.
512
- *
513
- * Logged on failure, and this is the worst place in the flow to lose a reason:
514
- * by the time a payment option is on screen THE RESERVATION ALREADY EXISTS
515
- * (the SDK's `booking.md` says as much), so a failure here is an unpaid
516
- * booking, not a failed one. The checkout's catch discards the error entirely
517
- * — `.catch(() => setPayError(true))` — so without this a guest who cannot
518
- * pay leaves no trace at all, on a booking someone now has to chase. */
519
- export declare function payWithOption(option: SubmittableBookingPaymentOption, sdk: WebsiteSdk): Promise<void>;
520
- //#endregion
521
- //#region src/booking-snapshot.d.ts
522
- /** Rental identity, parked by the checkout page (which has it from SSR) and
523
- * folded into the snapshot at book time — the SDK seam that writes the
524
- * snapshot never sees a rental name or a link. */
525
- export declare const CHECKOUT_CONTEXT_KEY = "voffice:checkout-context";
526
- /** The finished record `/buchen/bestaetigung` reads. */
527
- export declare const BOOKING_SNAPSHOT_KEY = "voffice:booking-return";
528
- /** One gallery image, in the SDK's own shape (`RentalGalleryImage` minus the
529
- * fields this page does not render). Stored so the confirmation can show the
530
- * unit rather than describe it — the guest booked a place, not a table. */
531
- export interface SnapshotImage {
532
- src: string;
533
- srcset?: string | undefined;
534
- alt: string;
1772
+ /** What `submitBooking` needs besides the quote and the contact. */
1773
+ interface SubmitBookingOptions {
1774
+ /** Where the payment provider returns to, relative to the site origin. The site's route. */
1775
+ returnPath: string;
1776
+ /** Default "booking". */
1777
+ kind?: BookingKind | undefined;
1778
+ /** The transfer account holder when the PMS sends none (v9 hub): the operator's name. The SDK never
1779
+ * substitutes another name (it leaves the row out); this puts the operator back, because the guest
1780
+ * needs the holder for the bank's payee check. */
1781
+ bankHolderFallback?: string | undefined;
1782
+ /** The stay being booked, for the payment-return snapshot. */
1783
+ stay: Stay;
535
1784
  }
536
- export interface CheckoutContext {
537
- rentalName: string;
538
- location?: string | undefined;
539
- href?: string | undefined;
540
- /** Capped at the same five the checkout's own summary strip shows. */
541
- images?: SnapshotImage[] | undefined;
542
- }
543
- export interface BookingSnapshotGuests {
544
- adults: number;
545
- children: number;
546
- babies: number;
547
- pets: number;
548
- }
549
- /** Every field but `bookingNumber` self-hides when absent — the return page
550
- * renders a row only for what it actually has, never an empty shell
551
- * (`DATA_SCHEMA.md`: "Optional field gates optional UI"). */
552
- export interface BookingSnapshot {
553
- bookingNumber: string;
554
- rentalName?: string | undefined;
555
- location?: string | undefined;
556
- href?: string | undefined;
557
- images?: SnapshotImage[] | undefined;
558
- /** `YYYY-MM-DD` — the vocabulary the whole funnel already carries. */
559
- start?: string | undefined;
560
- end?: string | undefined;
561
- guests?: BookingSnapshotGuests | undefined;
562
- /** The SDK's own formatted total, verbatim. Never re-derived, never summed. */
563
- total?: string | undefined;
564
- }
565
- /** Park the rental's identity for the return page. Called by the checkout page
566
- * on load — it is the last place in the flow that knows the rental by name. */
567
- export declare function saveCheckoutContext(context: CheckoutContext): void;
568
- /**
569
- * Write the return page's record. Called by `submitBooking` the moment the
570
- * reservation exists — EARLIER than the SDK's "before submitting a payment
571
- * option", because that is when the booking number is born, and a guest who
572
- * settles by bank transfer never leaves the checkout at all.
573
- *
574
- * Never throws. The snapshot is a nicety; the booking is not.
575
- */
576
- export declare function saveBookingSnapshot(bookingNumber: string, quote: GuestQuote): void;
1785
+ /** ⚠ USER-initiated only: reserves for real. `quote` must be the live instance with every selection
1786
+ * applied. Logs the SDK's cause and rethrows (nothing is necessarily booked when it throws). Writes
1787
+ * the payment-return snapshot the moment the booking number exists (a bank-transfer guest never
1788
+ * leaves the checkout, so this is the only place it can be written). */
1789
+ type SubmitBooking = (sdk: WebsiteSdk, quote: GuestQuote, contact: BookingContact, options: SubmitBookingOptions) => Promise<BookingConfirmation>;
1790
+ /** Every kind but `bank_transfer` (display-only details). */
1791
+ type IsSubmittableOption = (option: BookingPaymentOption) => option is SubmittableBookingPaymentOption;
1792
+ /** ⚠ USER-initiated only: redirects (or form-posts) to the payment provider. By then the reservation
1793
+ * EXISTS, so a failure is an unpaid booking someone has to chase: logged before rethrowing. */
1794
+ type PayWithOption = (sdk: WebsiteSdk, option: SubmittableBookingPaymentOption) => Promise<void>;
1795
+ /** See {@link SubmitBooking}. */
1796
+ export declare const submitBooking: SubmitBooking;
1797
+ /** See {@link IsSubmittableOption}. */
1798
+ export declare const isSubmittableOption: IsSubmittableOption;
1799
+ /** See {@link PayWithOption}. */
1800
+ export declare const payWithOption: PayWithOption;
1801
+ //#endregion
1802
+ //#region src/checkout/events.d.ts
1803
+ /** The window CustomEvent name. Renaming it is a major. */
1804
+ export declare const CHECKOUT_EVENT: "voffice:checkout";
1805
+ /** What `CHECKOUT_EVENT` carries; never personal data. */
1806
+ type CheckoutEventDetail =
1807
+ /** A PMS rate-configuration fault (AmbiguousMultiRate / UnresolvableMultiRate): the stay is free,
1808
+ * only the rate cannot be resolved. Route it to monitoring. Once per stay. The same `type` as
1809
+ * `voffice:quote`'s, so one listener serves both surfaces. */
1810
+ {
1811
+ readonly type: "rate-fault";
1812
+ readonly rentalId: string;
1813
+ readonly code: QuoteUnavailableCode;
1814
+ } |
1815
+ /** `book()` succeeded. `payable` is false when nothing can be paid online (bank transfer only): a
1816
+ * site may then send the guest to its return page itself. */
1817
+ {
1818
+ readonly type: "booked";
1819
+ readonly rentalId: string;
1820
+ readonly bookingNumber: string;
1821
+ readonly kind: BookingKind;
1822
+ readonly payable: boolean;
1823
+ } |
1824
+ /** The guest pressed a payment option; the browser is about to leave for the provider. */
1825
+ {
1826
+ readonly type: "payment-started";
1827
+ readonly bookingNumber: string;
1828
+ readonly method: BookingPaymentOption["kind"];
1829
+ };
577
1830
  //#endregion
578
1831
  //#region src/checkout/insurance.d.ts
579
- /** The plan's documents as the checkout treats them: what it lists, what the
580
- * guest must open, and what makes a plan sellable at all read the SAME list,
581
- * so a hidden document can never end up required-but-unlisted (which would be
582
- * a gate the guest cannot close). */
583
- export declare function shownInsuranceResources(option: QuoteInsuranceOption): {
584
- label: string;
585
- url: string;
586
- }[];
587
- /** The insurance products a checkout can actually complete. Two gates, and
588
- * this filter is where the layer's capability is declared — a site that adds a
589
- * missing flow adds it HERE, upstream, never in its own copy.
590
- *
591
- * 1. **Technical.** Products that select with the id alone and book WITH the
592
- * rental booking. One that demands per-traveler data first (traveler forms,
593
- * pre-contract, SEPA mandate, a post-booking insurance call) needs a flow
594
- * this layer does not implement — offering it would dead-end the guest.
595
- *
596
- * 2. **Legal.** The product must carry its `resources`, the insurer's
597
- * pre-contractual documents (see "Mandatory pre-contractual documents"
598
- * below for which duties they discharge). A product the feed sends WITHOUT
599
- * them cannot be concluded lawfully here, so it self-hides rather than
600
- * being sold undocumented — the same rule as (1), applied to a legal
601
- * precondition instead of a technical one. */
602
- export declare function selectableInsuranceOptions(insurance: QuoteInsurance): QuoteInsuranceOption[];
603
- /** The selected plan, but only when it is one the guest could actually pick
604
- * here — so no price row can name a product the section above never showed. */
605
- export declare function selectedInsuranceOption(insurance: QuoteInsurance | undefined): QuoteInsuranceOption | undefined;
606
- /** The identity of an insurance PRODUCT, as opposed to the id of one priced
607
- * variant of it.
608
- *
609
- * An insurer may band its tariffs by trip value, and then a plan id is not the
610
- * product — it is the product AT one price. The same tariff comes back under a
611
- * new id and a new premium the moment the total crosses a band. URV is the
612
- * observed case: "RRV ohne SB Deutschland" is `RROR0625` at 56,00 € up to
613
- * roughly 1.250 €, `RROR0725` at 65,00 € above that, `RROR0825` at 74,00 €
614
- * above that again. Every extra, every policy markup and every stay change can
615
- * cross a band, and each of them re-prices the quote — which is what makes the
616
- * SDK re-fetch the insurer's plan list against the new total.
617
- *
618
- * The label is what survives that: the same string in every band, and the name
619
- * the guest actually chose, since it is what the radio shows. */
620
- export declare function insuranceProductKey(option: QuoteInsuranceOption): string;
621
- /** The plan id to re-apply onto a re-priced quote, or null when there is
622
- * nothing to do — no choice, already applied, or a product this feed no longer
623
- * offers as a selectable one. The last case is why the feed is checked rather
624
- * than the call simply attempted: a doomed selection would spend one of the
625
- * rate-limited requests to be refused, and the guest ends up on "keine
626
- * Versicherung" either way.
627
- *
628
- * Matched by id first, then by product (see insuranceProductKey). That
629
- * fallback is what keeps a banded re-price from silently un-selecting
630
- * insurance the feed still sells: the SDK carries a selection across its
631
- * refresh only on an exact id match, so a renamed-but-identical tariff reads
632
- * to it as a plan that vanished, and a guest who only added a towel package
633
- * lands back on "keine Versicherung". Re-applying picks the SAME product at
634
- * its new premium — the one they would in fact be buying — and that premium is
635
- * on screen in the radio, in the summary rail and above the submit button, so
636
- * nothing about the change is hidden.
637
- *
638
- * Checked against the SELECTABLE options, not the raw list: a plan the fresh
639
- * feed carries without its documents (or newly demanding traveler data) is one
640
- * the section no longer shows, so re-applying it would leave the quote
641
- * carrying an insurance the page neither displays nor can take a document
642
- * confirmation for. */
643
- export declare function insuranceIdToReapply(fresh: QuoteView, wanted: QuoteInsuranceOption | undefined): string | null;
644
- /** Progress through the document step. Held by the orchestration hook because
645
- * the submit button, a card away, is gated on it. */
1832
+ /** Progress through the document step (held by the flow: the order button, a section away, is gated
1833
+ * on it). */
646
1834
  interface InsuranceDocsState {
647
- /** URLs opened so far — keyed by URL, not by plan. Plans routinely share
648
- * every file but the IPID, and having received an identical document does
649
- * not un-happen when the guest switches tariff. Keying by URL also survives
650
- * a re-quote, which returns fresh option objects for the same product. */
1835
+ /** URLs opened so far, keyed by URL rather than by plan: plans routinely share every file but the
1836
+ * IPID, a document received does not un-happen when the guest switches tariff, and a URL survives
1837
+ * a re-quote that returns fresh option objects for the same product. */
651
1838
  opened: ReadonlySet<string>;
652
- /** Product whose documents the guest confirmed receiving (keyed by
653
- * insuranceProductKey, not by plan id); null = none yet. Product-bound on
654
- * purpose, so a tariff switch demands a fresh acknowledgement while the
655
- * opened files above carry over. By PRODUCT rather than by plan for the same
656
- * reason the selection is carried by product: a banded re-price hands back
657
- * the identical documents under a new plan id, and making the guest re-tick
658
- * a box for files they already have — because a towel package moved the
659
- * total — would be friction with nothing behind it. */
1839
+ /** The product whose documents the guest confirmed receiving (`insuranceProductKey`), or null.
1840
+ * Product-bound, so another tariff demands a fresh acknowledgement while opened files carry over;
1841
+ * by product rather than plan id, because a banded re-price hands back identical documents under
1842
+ * a new id and re-ticking for files the guest already has would be friction with nothing behind it. */
660
1843
  confirmedProduct: string | null;
661
1844
  }
1845
+ /** Nothing opened, nothing confirmed. */
662
1846
  export declare const NO_INSURANCE_DOCS: InsuranceDocsState;
663
- /** True while a chosen plan's document step is unfinished. No plan chosen (or
664
- * "keine Versicherung") is never blocking — the duty attaches to the insurance
1847
+ /** The documents shown for a product: what the checkout lists, what the guest must open and what
1848
+ * makes a plan sellable read this ONE list, so a hidden document can never be required-but-unlisted
1849
+ * (a gate the guest could not close). Only the GDV consent is excluded, matched by URL (see below). */
1850
+ type ShownInsuranceResources = (option: QuoteInsuranceOption) => NonNullable<QuoteInsuranceOption["resources"]>;
1851
+ /** The products a checkout can complete. Technical: no per-traveler data (forms, pre-contract, SEPA
1852
+ * mandate, a post-booking insurance call is a flow this layer does not run). Legal: at least one
1853
+ * shown pre-contractual document (a product sent without them cannot be concluded lawfully here, so
1854
+ * it self-hides rather than being sold undocumented). */
1855
+ type SelectableInsuranceOptions = (insurance: QuoteInsurance) => QuoteInsuranceOption[];
1856
+ /** The selected plan, but only when the guest could pick it here: no price row can name a product
1857
+ * the section never showed. */
1858
+ type SelectedInsuranceOption = (insurance: QuoteInsurance | undefined) => QuoteInsuranceOption | undefined;
1859
+ /** The identity of a PRODUCT, not of one priced variant of it. An insurer may band its tariffs by
1860
+ * trip value: the same tariff returns under a new id and premium once the total crosses a band (one
1861
+ * insurer: 56,00 € up to about 1.250 €, 65,00 € above, 74,00 € above that). The label survives a
1862
+ * band change, and it is the name the guest chose. */
1863
+ type InsuranceProductKey = (option: QuoteInsuranceOption) => string;
1864
+ /** After a re-quote: the plan id to re-apply, or undefined (nothing chosen, already applied, or a
1865
+ * product the fresh feed no longer offers as selectable). Matched by id, then by product, because
1866
+ * the SDK carries a selection across its refresh only on an exact id: a renamed-but-identical
1867
+ * tariff would otherwise land a guest who only added a towel package back on "no insurance". The
1868
+ * new premium is on screen in the radio, the summary and above the button, so nothing is hidden. */
1869
+ type InsuranceIdToReapply = (fresh: QuoteView, wanted: QuoteInsuranceOption | undefined) => string | undefined;
1870
+ /** True while a chosen plan's document step is unfinished: every shown document opened AND the
1871
+ * product confirmed. No plan chosen is never blocking: the duty attaches to the insurance
665
1872
  * contract, not to the stay. */
666
- export declare function insuranceDocsPending(option: QuoteInsuranceOption | undefined, docs: InsuranceDocsState): boolean;
667
- //#endregion
668
- //#region src/checkout/stay.d.ts
669
- interface Stay {
670
- start: Date;
671
- end: Date;
672
- adults: number;
673
- children: number;
674
- childrenAges: number[];
675
- babies: number;
676
- pets: number;
677
- }
678
- /** Date → `YYYY-MM-DD` in LOCAL time — the vocabulary the URL handover and the
679
- * SDK both use. Local on purpose (matches date-fns `format(date,
680
- * "yyyy-MM-dd")`, which the sites' own lib/dates.ts uses): a calendar day
681
- * picked in the browser must not shift across midnight UTC. */
682
- export declare function isoDate(date: Date): string;
683
- export declare function occupancyOf(stay: Stay): QuoteOccupancy;
684
- /** The identity of a stay for "is the shown price still the shown stay"
685
- * comparisons — every field that changes the quote, in one string. */
686
- export declare function stayKeyOf(stay: Stay): string;
687
- /** The stay as URL search params (`start`/`end`/`adults`/… — the same
688
- * vocabulary the rental page reads + writes). */
689
- export declare function stayParams(stay: Stay): string;
1873
+ type InsuranceDocsPending = (option: QuoteInsuranceOption | undefined, docs: InsuranceDocsState) => boolean;
1874
+ /** See {@link ShownInsuranceResources}. */
1875
+ export declare const shownInsuranceResources: ShownInsuranceResources;
1876
+ /** See {@link SelectableInsuranceOptions}. */
1877
+ export declare const selectableInsuranceOptions: SelectableInsuranceOptions;
1878
+ /** See {@link SelectedInsuranceOption}. */
1879
+ export declare const selectedInsuranceOption: SelectedInsuranceOption;
1880
+ /** See {@link InsuranceProductKey}. */
1881
+ export declare const insuranceProductKey: InsuranceProductKey;
1882
+ /** See {@link InsuranceIdToReapply}. */
1883
+ export declare const insuranceIdToReapply: InsuranceIdToReapply;
1884
+ /** See {@link InsuranceDocsPending}. */
1885
+ export declare const insuranceDocsPending: InsuranceDocsPending;
690
1886
  //#endregion
691
- //#region src/checkout/createCheckoutFlow.d.ts
692
- /**
693
- * The checkout's orchestration — everything between "a complete stay is in
694
- * hand" and "the guest is booked and paying", with no markup and no framework.
695
- *
696
- * The flow owns the quote lifecycle (initial load, re-quote on stay change,
697
- * supersession), the selection mutations (extras / insurance / voucher /
698
- * policy re-apply), the § 7 VVG document gate, booking submit and payment —
699
- * the parts that must be correct on every site and update mechanically. The
700
- * UI owns stay EDITING (calendar, guest panel, URL read/write), copy, and
701
- * every pixel. A UI subscribes to `subscribe`, reads `getState`, calls the
702
- * actions, and feeds stay/policy changes through `update` (React: one
703
- * useSyncExternalStore — see the README).
704
- *
705
- * ONE flow instance owns the whole thing because the live `GuestQuote` that
706
- * `booking.book` requires is not serializable across a navigation: the
707
- * consumer re-quotes from the URL handover on mount and the flow keeps the
708
- * instance in memory through extras / policy / voucher selections until
709
- * book().
710
- *
711
- * ⚠ `book()` creates a REAL booking; `pay()` starts a REAL payment. Both are
712
- * strictly user-initiated — never automate them.
713
- */
714
- type CheckoutPhase = {
715
- kind: "loading";
716
- } | {
717
- kind: "ready";
718
- } | {
719
- kind: "unavailable";
720
- reason: string;
721
- } | {
722
- kind: "error";
1887
+ //#region src/checkout/payment-return.d.ts
1888
+ /** Storage keys, unchanged from the kits, so no guest's return breaks on the switch. */
1889
+ export declare const CHECKOUT_CONTEXT_KEY: "voffice:checkout-context";
1890
+ export declare const BOOKING_SNAPSHOT_KEY: "voffice:booking-return";
1891
+ /** One gallery image, in the SDK's own shape minus what the page does not render. */
1892
+ interface SnapshotImage {
1893
+ /** The image URL. */
1894
+ src: string;
1895
+ /** Responsive candidates. */
1896
+ srcset?: string;
1897
+ /** The alternative text. */
1898
+ alt: string;
1899
+ }
1900
+ /** The rental identity the checkout parks for the return page (it has it from the build). */
1901
+ interface CheckoutContext {
1902
+ /** The booked rental's name. */
1903
+ rentalName: string;
1904
+ /** The rental's location line. */
1905
+ location?: string;
1906
+ /** The rental page. */
1907
+ href?: string;
1908
+ /** At most five are kept (the checkout's own strip). */
1909
+ images?: readonly SnapshotImage[];
1910
+ }
1911
+ /** What the return page renders. Every field but `bookingNumber` and `kind` self-hides when absent. */
1912
+ interface BookingSnapshot {
1913
+ /** The PMS booking number. */
1914
+ bookingNumber: string;
1915
+ /** A booking or an option. */
1916
+ kind: BookingKind;
1917
+ /** The booked rental's name. */
1918
+ rentalName?: string;
1919
+ /** The rental's location line. */
1920
+ location?: string;
1921
+ /** The rental page. */
1922
+ href?: string;
1923
+ /** The rental's photos (at most five). */
1924
+ images?: readonly SnapshotImage[];
1925
+ /** Arrival. */
1926
+ start?: IsoDay;
1927
+ /** Departure. */
1928
+ end?: IsoDay;
1929
+ /** The booked party's counts (no names: no personal data is parked). */
1930
+ guests?: {
1931
+ adults: number;
1932
+ children: number;
1933
+ babies: number;
1934
+ pets: number;
1935
+ };
1936
+ /** The PMS's formatted total, verbatim; never re-derived. */
1937
+ total?: string;
1938
+ }
1939
+ /** How the return page was reached. */
1940
+ type PaymentReturn =
1941
+ /** This tab booked: the snapshot proves it. */
1942
+ {
1943
+ state: "booked";
1944
+ snapshot: BookingSnapshot;
1945
+ paymentCancelled: boolean;
1946
+ } |
1947
+ /** A provider return without a snapshot (another tab, storage refused). */
1948
+ {
1949
+ state: "returned";
1950
+ paymentCancelled: boolean;
1951
+ } |
1952
+ /** Nobody booked: a bare URL, or tracking parameters only. */
1953
+ {
1954
+ state: "none";
723
1955
  };
724
- /** The user-facing strings the flow itself needs — the reasons it reports when
725
- * the backend gives none, or gives one that would be a false statement. Copy,
726
- * so it stays per-site. */
727
- interface CheckoutFlowMessages {
728
- /** Shown INSTEAD of the SDK's localized unavailability reason when the quote
729
- * failed on a rate-configuration fault (AmbiguousMultiRate /
730
- * UnresolvableMultiRate) — the stay is free; only the price cannot be
731
- * resolved, so the availability wording would be a false statement. */
1956
+ /** Park the rental identity for the return page (the flow calls it on `start()`). Never throws.
1957
+ * Default storage `browserStorage("session")`; null = nothing is stored. */
1958
+ type SaveCheckoutContext = (context: CheckoutContext, storage?: StorageLike | null) => void;
1959
+ /** Classify the return page's URL (pass `location.search`). The snapshot comes first; without one,
1960
+ * any parameter that is not tracking (`isTrackingParam`) counts as a provider return; a bare or
1961
+ * tracking-only URL is "none". `paymentCancelled` is Stripe's `payment=stripe&cancel=true`. Fails
1962
+ * soft: an unreadable store reads as no snapshot. */
1963
+ type ReadPaymentReturn = (search: string, storage?: StorageLike | null) => PaymentReturn;
1964
+ /** See {@link SaveCheckoutContext}. */
1965
+ export declare const saveCheckoutContext: SaveCheckoutContext;
1966
+ /** See {@link ReadPaymentReturn}. */
1967
+ export declare const readPaymentReturn: ReadPaymentReturn;
1968
+ //#endregion
1969
+ //#region src/checkout/flow.d.ts
1970
+ /** The sentences the flow itself reports. Copy: always passed in (the components' merged
1971
+ * `CheckoutCopy` satisfies it). */
1972
+ interface CheckoutMessages {
1973
+ /** Replaces the SDK reason on a rate-configuration fault: the stay is free, only its rate cannot
1974
+ * be resolved, so the availability sentence would be false. */
732
1975
  rateFault: string;
733
- /** Fallback when an unavailable result carries no reason of its own. */
1976
+ /** When an unavailable answer carries no reason of its own. */
734
1977
  unavailable: string;
735
- /** Inline issue line when a RE-quote (not the initial load) failed on transport. */
1978
+ /** A re-quote failed on transport (retry offered). */
736
1979
  quoteRefreshFailed: string;
737
- /** Voucher could not be applied at all (transport / stay unavailable). */
1980
+ /** The voucher could not be applied at all (transport, stay unavailable). */
738
1981
  voucherFailed: string;
739
- /** Voucher was processed and rejected / not recognized. */
1982
+ /** The voucher was processed and rejected. */
740
1983
  voucherRejected: string;
741
1984
  }
1985
+ /** What a checkout flow is built from. */
742
1986
  interface CheckoutFlowOptions {
1987
+ /** PMS id of the rental. */
743
1988
  rentalId: string;
744
- /** The site's SDK accessor (one instance for the flow's lifetime — quote +
745
- * selections + book share its queue and 1 req/s rate limit). The flow never
746
- * disposes it; the owner does. */
747
- getSdk: () => WebsiteSdk;
748
- /** The current COMPLETE stay, or null while none is in hand (mid-edit, URL
749
- * handover missing). The flow re-quotes whenever it changes. */
750
- stay: Stay | null;
751
- /** The cancellation rate chosen a step earlier (`?policy=` handover). A SEED
752
- * for the first quote, not the source of truth: once a quote exists, the
753
- * live quote's own `selected` flag is. Null means "whatever the PMS
754
- * pre-selects", which is also what an unknown id degrades to. */
755
- chosenPolicy: string | null;
756
- /**
757
- * Which of a fresh quote's policies may be treated as OFFERED for the
758
- * `chosenPolicy` re-apply — REQUIRED, because this is the enforcement point
759
- * for the whole offer-withdrawal rule: a rate stops being an offer when the
760
- * stay moves inside the standard rate's cancellation window, at which point
761
- * the cheaper non-refundable one is the identical booking for less. Every
762
- * route onto the checkout passes through this check (a stale link, a
763
- * hand-built `?policy=`, an in-page date edit), so all of them lapse to the
764
- * PMS pre-selection together.
765
- */
766
- filterOfferedPolicies: (policies: QuoteCancellationPolicy[], arrival: Date) => QuoteCancellationPolicy[];
767
- messages: CheckoutFlowMessages;
768
- /** Called when a quote returns `AmbiguousMultiRate` / `UnresolvableMultiRate`
769
- * — a PMS rate-configuration fault, not a booked-out stay. Route these to
770
- * monitoring or site operations. */
771
- onRateConfigurationFault?: ((info: {
772
- code: QuoteUnavailableCode;
773
- rentalId: string;
774
- }) => void) | undefined;
775
- /** The SDK locale: the PMS answers in that language (quote lines, reasons,
776
- * policy names). */
1989
+ /** The PMS answers in this language (quote lines, reasons, rate names). */
777
1990
  locale: Locale;
778
- /** Where the payment provider sends the guest back to (relative to the
779
- * site's origin). Default: the platform's `/buchen/bestaetigung`; a site
780
- * with its own return route passes it here. */
781
- returnPath?: string | undefined;
1991
+ /** Where the payment provider returns to, relative to the site origin. Required: routes are the
1992
+ * site's. */
1993
+ returnPath: string;
1994
+ /** `readCheckoutHandover(location.search, …)`. */
1995
+ handover: CheckoutHandover;
1996
+ /** Rental identity for the payment-return page; parked in sessionStorage on `start()`. */
1997
+ context?: CheckoutContext | undefined;
1998
+ /** Where the rate is chosen: "rental-page" (default) applies the carried `?policy=` and states it;
1999
+ * "checkout" makes the offered rates choosable here (`selectPolicy`). */
2000
+ policyChoice?: PolicyChoiceMode | undefined;
2001
+ /** Ids of the consent checkboxes the UI renders ("terms", "privacy", additional-terms ids); each
2002
+ * must be ticked before `book()`. Default none (every consent is a notice). */
2003
+ requiredConsents?: readonly string[] | undefined;
2004
+ /** The transfer account holder when the PMS sends none (v9 hub): the operator's name. */
2005
+ bankHolderFallback?: string | undefined;
2006
+ /** `limits` cap the guest editor; `defaultAdults` seeds a stay picked on a page that arrived
2007
+ * without one; `childAge` is the age band (a handed-over age outside it is asked again). */
2008
+ guests?: GuestOptions | undefined;
2009
+ /** The site's minimum stay in nights on top of the PMS rules (the value Quote takes): `setDates`
2010
+ * refuses a shorter range. Default none (the quote enforces the PMS's own minimum). */
2011
+ minStayNights?: number | undefined;
2012
+ /** Catalogue capacity (caps the party until `loadGuestCaps` brings availability's, and where that
2013
+ * states none) and the pets flag (a dog cannot be added without it; a handed-over one can be
2014
+ * removed and is still sent). */
2015
+ caps?: Pick<GuestCaps, "maxGuests" | "petsAllowed"> | undefined;
2016
+ /** The sentences the flow reports itself (the checkout copy satisfies them). */
2017
+ messages: CheckoutMessages;
2018
+ /** Receives every checkout event: `rate-fault` once per stay, `booked`, `payment-started`.
2019
+ * Without it a rate fault is logged with console.warn. */
2020
+ onEvent?: ((detail: CheckoutEventDetail) => void) | undefined;
2021
+ }
2022
+ /** Where the checkout stands. */
2023
+ type CheckoutPhase = {
2024
+ kind: "loading";
2025
+ } |
2026
+ /** No complete stay arrived (missing or invalid dates). */
2027
+ {
2028
+ kind: "missing-stay";
2029
+ } | {
2030
+ kind: "ready";
2031
+ } |
2032
+ /** The first quote was refused (the SDK reason, or `messages.rateFault` / `unavailable`). */
2033
+ {
2034
+ kind: "unavailable";
2035
+ reason: string;
2036
+ } |
2037
+ /** The first quote failed on transport (`retry()`). */
2038
+ {
2039
+ kind: "error";
2040
+ } |
2041
+ /** `book()` succeeded; `confirmation` is set. */
2042
+ {
2043
+ kind: "booked";
2044
+ };
2045
+ /** One reason the order button is off, in the order the UI should mention them. */
2046
+ type CheckoutBlocker = "no-quote" | "price-pending" | "busy" | "issue" | "child-ages" | "insurance-documents" | "consents";
2047
+ /** One extra as the UI renders it. */
2048
+ interface CheckoutService extends QuoteAddonService {
2049
+ /** Units chosen (0 = not booked). */
2050
+ count: number;
2051
+ /** False at `maxPerBooking` (default 99), while unavailable, or while busy. */
2052
+ canAdd: boolean;
2053
+ /** True while `count > 0`: an unavailable service can still be removed. */
2054
+ canRemove: boolean;
2055
+ }
2056
+ /** The rate the quote carries and the rates the guest may still choose. */
2057
+ interface CheckoutPolicyState {
2058
+ /** The rate the quote carries, or null when the PMS sends none (v9). */
2059
+ applied: QuoteCancellationPolicy | null;
2060
+ /** The rates still offered for this arrival (the offer-withdrawal rule); a picker renders these. */
2061
+ offered: readonly QuoteCancellationPolicy[];
2062
+ /** Every rate on the quote, for the summary's terms (a rate that can no longer be cancelled is what
2063
+ * must be said before paying). */
2064
+ all: readonly QuoteCancellationPolicy[];
2065
+ /** Last day of free cancellation under the applied rate, or null. */
2066
+ freeCancellationUntil: IsoDay | null;
2067
+ /** The rate model for the UI (`describePolicyOffer`, surface "checkout"; no branch prices). */
2068
+ offer: PolicyOffer;
2069
+ }
2070
+ /** Null when there is nothing to choose (no insurance, the v10 placeholder, no selectable product). */
2071
+ interface CheckoutInsuranceState {
2072
+ /** Products this checkout can complete: ride-along, with their pre-contractual documents. */
2073
+ options: readonly QuoteInsuranceOption[];
2074
+ /** The "no insurance" label, verbatim from the SDK. */
2075
+ noneLabel: string;
2076
+ /** The product the quote carries, or null for none. */
2077
+ chosen: QuoteInsuranceOption | null;
2078
+ /** Progress through the § 7 VVG document step. */
2079
+ documents: InsuranceDocsState;
2080
+ /** The documents the guest must open for `chosen` (the shown set, never a label whitelist). */
2081
+ requiredDocuments: readonly {
2082
+ label: string;
2083
+ url: string;
2084
+ }[];
2085
+ /** True while `chosen` blocks booking. */
2086
+ documentsPending: boolean;
2087
+ }
2088
+ /** The voucher field's state. */
2089
+ interface CheckoutVoucherState {
2090
+ /** The code the shown price includes, or null. */
2091
+ applied: string | null;
2092
+ /** Why the last code did not apply, or null. */
2093
+ error: string | null;
2094
+ /** A code is being priced. */
2095
+ checking: boolean;
782
2096
  }
2097
+ /** A re-quote that failed or found the stay unavailable. */
783
2098
  interface CheckoutIssue {
784
- /** The stayKey the issue belongs to — a different stay re-arms the loop. */
785
- key: string;
2099
+ /** What to tell the guest (the SDK's reason or a messages sentence). */
786
2100
  reason: string;
787
- /** Transport errors offer a manual retry; unavailability does not. */
2101
+ /** Transport errors offer `retry()`; unavailability does not. */
788
2102
  retryable: boolean;
789
2103
  }
790
- /** Everything a UI renders. A new object per change; unchanged otherwise, so
791
- * `useSyncExternalStore` and friends can compare by reference. */
792
- interface CheckoutFlowState {
2104
+ /** Everything a UI renders. A new object per change, unchanged otherwise. */
2105
+ interface CheckoutState {
2106
+ /** Where the checkout stands. */
793
2107
  phase: CheckoutPhase;
2108
+ /** A booking, or an option (`?kind=option` with `allowOption`). */
2109
+ kind: BookingKind;
2110
+ /** The stay being edited (the source of truth after the handover). */
2111
+ stay: Stay | null;
2112
+ /** What the guest editor may offer for this rental. */
2113
+ bounds: GuestBounds;
2114
+ /** The quote view (of the last priced stay while `pricePending`). */
794
2115
  view: QuoteView | null;
795
- counts: Record<string, number>;
2116
+ /** The extras with their chosen counts. */
2117
+ services: readonly CheckoutService[];
2118
+ /** The applied rate and the offered ones. */
2119
+ policy: CheckoutPolicyState;
2120
+ /** The insurance choice, or null when there is nothing to choose. */
2121
+ insurance: CheckoutInsuranceState | null;
2122
+ /** The voucher field's state. */
2123
+ voucher: CheckoutVoucherState;
2124
+ /** Checkbox state per required consent id. */
2125
+ consents: Readonly<Record<string, boolean>>;
2126
+ /** The shown price does not belong to the shown stay yet. */
2127
+ pricePending: boolean;
2128
+ /** A selection or a re-quote is running. */
796
2129
  busy: boolean;
2130
+ /** The last re-quote's failure for this stay, or null. */
797
2131
  issue: CheckoutIssue | null;
2132
+ /** The SDK's reason for a refused selection (an extras/rate combination it cannot price). */
798
2133
  selectionError: string | null;
799
- quotedKey: string | null;
800
- stayKey: string | null;
801
- /** A re-quote is pending: the shown price does not match the shown stay. */
802
- pricePending: boolean;
803
- /** Booking is blocked (stale price, in-flight mutation, issue, or documents pending). */
804
- blocked: boolean;
805
- chosenInsurance: QuoteInsuranceOption | undefined;
806
- insuranceDocs: InsuranceDocsState;
807
- documentsPending: boolean;
808
- voucherApplied: string | null;
809
- voucherError: string | null;
810
- bookState: "idle" | "submitting" | "error";
2134
+ /** Empty when `book()` is allowed. */
2135
+ blockers: readonly CheckoutBlocker[];
2136
+ /** The order button's state: idle, booking, or a failed `book()`. */
2137
+ submit: "idle" | "submitting" | "failed";
2138
+ /** The confirmation after `book()`, or null. */
811
2139
  confirmation: BookingConfirmation | null;
812
- payBusyKey: string | null;
813
- payError: boolean;
2140
+ /** The payment buttons' state: the option redirecting now, and a failed start. */
2141
+ payment: {
2142
+ busyOption: SubmittableBookingPaymentOption | null;
2143
+ failed: boolean;
2144
+ };
814
2145
  }
815
- /** The actions a UI calls. Stable functions for the flow's lifetime. */
816
- interface CheckoutFlowActions {
2146
+ /** The actions a UI calls; stable for the flow's lifetime. */
2147
+ interface CheckoutActions {
2148
+ /** A new date range from the stay editor (at least one night, and at least `minStayNights`; a
2149
+ * shorter range is ignored); re-quotes after a short debounce. */
2150
+ setDates(start: IsoDay, end: IsoDay): void;
2151
+ /** A new party from the guest editor; clamped to `bounds` (pets per the caps rule); re-quotes
2152
+ * after a short debounce. */
2153
+ setParty(party: Party): void;
2154
+ /** Load the rental's availability caps once (persons, adults, and on v9 the children and pets
2155
+ * rules), so `bounds` follow them. Call it when the guest editor opens: the page's first request
2156
+ * stays the quote. A failure is logged and the catalogue caps stay. Idempotent. */
2157
+ loadGuestCaps(): void;
2158
+ /** Add (+1) or remove (−1) one unit of an extra. */
817
2159
  changeService(id: string, delta: 1 | -1): void;
2160
+ /** Only with `policyChoice: "checkout"`, and only to an offered rate. */
2161
+ selectPolicy(id: string): void;
2162
+ /** A selectable product's id, or null for none. */
818
2163
  selectInsurance(id: string | null): void;
819
- openInsuranceDoc(url: string): void;
820
- confirmInsuranceDocs(productKey: string, confirmed: boolean): void;
2164
+ /** The guest opened one of the chosen product's documents (the § 7 VVG step). */
2165
+ openInsuranceDocument(url: string): void;
2166
+ /** The confirmation is keyed by the chosen product inside the flow, so no UI can confirm the
2167
+ * wrong one. */
2168
+ confirmInsuranceDocuments(confirmed: boolean): void;
2169
+ /** Re-quote with a code (null removes it); the previous quote stays when the code is rejected.
2170
+ * Ignored while the shown price is not the shown stay's (`pricePending`, or a failed re-quote). */
821
2171
  applyVoucher(code: string | null): void;
822
- /** ⚠ USER-initiated only: creates a real booking. */
823
- book(contact: BookingContactForm): void;
824
- /** ⚠ USER-initiated only: redirects to the real payment provider. */
825
- pay(key: string, option: PaymentScheduleView["options"][number]): void;
826
- clearIssue(): void;
827
- }
828
- interface CheckoutFlow extends Store<CheckoutFlowState>, CheckoutFlowActions {
829
- /** Feed the UI's current inputs. `stay` and `chosenPolicy` drive the
830
- * re-quote; messages and callbacks are read at call time. */
831
- update(options: CheckoutFlowOptions): void;
832
- /** Arm the auto-quote. Idempotent; call once the UI is mounted. */
2172
+ /** Tick or untick a required consent checkbox. */
2173
+ setConsent(id: string, value: boolean): void;
2174
+ /** Re-quote after a transport issue. */
2175
+ retry(): void;
2176
+ /** ⚠ USER-initiated only: creates a real booking. Refused while `blockers` is not empty. */
2177
+ book(contact: BookingContact): void;
2178
+ /** ⚠ USER-initiated only: redirects to the real payment provider. Refused for bank transfer and
2179
+ * while another payment is starting. */
2180
+ pay(option: SubmittableBookingPaymentOption): void;
2181
+ }
2182
+ /** The checkout store: its state, its actions and its lifecycle. */
2183
+ interface CheckoutFlow extends Store<CheckoutState>, CheckoutActions {
2184
+ /** Park the context and price the handed-over stay. Idempotent. */
833
2185
  start(): void;
834
2186
  /** Cancel pending work and drop every in-flight result. */
835
2187
  dispose(): void;
836
2188
  }
837
- /** What a UI binding hands to the rendering code: the state plus the actions. */
838
- type CheckoutFlowApi = CheckoutFlowState & CheckoutFlowActions;
839
- export declare function createCheckoutFlow(initialOptions: CheckoutFlowOptions): CheckoutFlow;
840
- //#endregion
841
- //#region src/checkout/card-order.d.ts
842
- export declare function stabilizeCardOrder(view: QuoteView, order: Map<string, number>): QuoteView;
2189
+ /** The checkout store. The SDK source is resolved once, on the first request. */
2190
+ type CreateCheckoutFlow = (sdk: SdkSource, options: CheckoutFlowOptions) => CheckoutFlow;
2191
+ /** See {@link CreateCheckoutFlow}. */
2192
+ export declare const createCheckoutFlow: CreateCheckoutFlow;
843
2193
  //#endregion
844
- //#region src/checkout/validation.d.ts
845
- /**
846
- * Contact-form field validation — the checkout's data-quality behavior,
847
- * framework-free.
848
- *
849
- * Extracted from the sites' checkout island (`CheckoutFlow.tsx`). The RULES live here so every site validates a booking
850
- * contact the same way; the MESSAGES are copy and stay per-site — every
851
- * function takes them as a parameter (the site's `CheckoutFlowCopy` object
852
- * satisfies `ValidationMessages` structurally, so skins pass their merged copy
853
- * straight through).
854
- */
855
- /** The subset of the checkout copy the validators read. A site's full copy
856
- * object satisfies this structurally. */
2194
+ //#region src/forms/validation.d.ts
2195
+ /** Salutation choices. The labels are copy; the wire value is fixed, so the PMS sees one vocabulary in
2196
+ * every locale (an agency's staff read "Frau"/"Herr" whatever language the guest booked in). */
2197
+ type Salutation = "female" | "male" | "unspecified";
2198
+ /** What reaches the PMS as `title`. The checkout guest form sends none for "unspecified" (the booking
2199
+ * contact's title is optional); the contact form sends "Keine Angabe" (the contact API requires one). */
2200
+ export declare const SALUTATION_WIRE_VALUES: Readonly<Record<Salutation, string>>;
2201
+ /** Every sentence the field rules can return. A site's merged checkout copy satisfies it
2202
+ * structurally; `phoneRequired`, `housenumberRequired` and `salutationRequired` are read only when
2203
+ * the form makes that field required. */
857
2204
  interface ValidationMessages {
2205
+ /** A forename left empty. */
858
2206
  forenameRequired: string;
2207
+ /** A surname left empty. */
859
2208
  surnameRequired: string;
2209
+ /** A street left empty. */
860
2210
  streetRequired: string;
2211
+ /** A town left empty. */
861
2212
  cityRequired: string;
2213
+ /** A postal code left empty. */
862
2214
  postalcodeRequired: string;
2215
+ /** An email left empty. */
863
2216
  emailRequired: string;
2217
+ /** A present email that is no address. */
864
2218
  emailInvalid: string;
2219
+ /** A required phone left empty. */
2220
+ phoneRequired: string;
2221
+ /** A number that carries "+CC" but has the wrong length. */
865
2222
  phoneInvalid: string;
2223
+ /** A national number the rules cannot prefix (no country code known). */
866
2224
  phoneNeedsDialCode: string;
2225
+ /** A required house number left empty. */
2226
+ housenumberRequired: string;
2227
+ /** A required salutation left unchosen. */
2228
+ salutationRequired: string;
2229
+ /** No country chosen. */
2230
+ countryRequired: string;
2231
+ /** A present German postal code that is not five digits. */
867
2232
  postalcodeInvalidDE: string;
2233
+ /** A present Austrian postal code that is not four digits. */
868
2234
  postalcodeInvalidAT: string;
2235
+ /** A present Swiss postal code that is not four digits. */
869
2236
  postalcodeInvalidCH: string;
2237
+ /** Non-blocking: the street seems to carry the house number. */
870
2238
  streetHouseNumberWarning: string;
871
2239
  }
872
- export declare function normalizePhone(raw: string, dial: string | undefined): string;
873
- export declare function phoneError(raw: string, dial: string | undefined, c: ValidationMessages): string | undefined;
874
- export declare function emailError(raw: string, c: ValidationMessages): string | undefined;
875
- export declare function postalcodeError(raw: string, country: string, c: ValidationMessages): string | undefined;
876
- export declare function streetWarning(raw: string, c: ValidationMessages): string | undefined;
877
- export declare const VALIDATED_FIELDS: readonly ["forename", "surname", "email", "phone", "street", "postalcode", "city"];
878
- /** Validate one named field. Takes the selected COUNTRY plus its calling code
879
- * (`dial`, undefined when the register has none for it): two validators depend
880
- * on the country — the phone via the dial code, the PLZ via its format rule.
881
- * The dial code is passed in rather than looked up because the ISO→dial
882
- * mapping lives with the site's country register (its localized "Land"
883
- * options), not here. */
884
- export declare function fieldError(name: string, value: string, country: string, dial: string | undefined, c: ValidationMessages): string | undefined;
885
- /** Country options in the shape the site's country register provides —
886
- * `[code, label]` pairs, a featured group plus the full ISO register. Kept
887
- * structural so the site's own `CountryGroups` type satisfies it. */
888
- interface CountryGroupsLike {
889
- featured: ReadonlyArray<readonly [string, string]>;
890
- all: ReadonlyArray<readonly [string, string]>;
891
- }
892
- export declare function detectCountry(countries: CountryGroupsLike): string;
2240
+ /** Separators → spaces, `00CC` → `+CC`, a trunk `0` → `+<dial>` (the calling code of the selected
2241
+ * country, digits only). Returns the normalized number, or null for a blank input. Without a `dial` a
2242
+ * national number stays as typed, so `phoneError` asks for the code instead of guessing one. */
2243
+ type NormalizePhone = (raw: string, dial: string | undefined) => string | null;
2244
+ /** An optional phone: blank is valid (null). Present, it must normalize to `+<1-9><6–14 digits>`
2245
+ * (E.164 caps the total at 15). A "+CC" number of the wrong length gets `phoneInvalid`; anything
2246
+ * without a country code gets `phoneNeedsDialCode`. Required-ness is the form's own check. */
2247
+ type PhoneError = (raw: string, dial: string | undefined, messages: ValidationMessages) => string | null;
2248
+ /** Required, and shaped like an address with a dotted top-level domain (catches "name@gmail"). The
2249
+ * endpoint's own pattern stays the authority; this is the early, coarse gate. Takes only the two
2250
+ * messages it reads, so the contact form's copy satisfies it too. */
2251
+ type EmailError = (raw: string, messages: Pick<ValidationMessages, "emailRequired" | "emailInvalid">) => string | null;
2252
+ /** Required for every country; a format check only for DE (5 digits), AT (4) and CH (4). Every other
2253
+ * country is accepted as typed: rejecting a valid foreign address is the most expensive mistake a
2254
+ * booking form can make, and no single pattern covers "1234 AB" or an Eircode. */
2255
+ type PostalcodeError = (raw: string, country: CountryCode, messages: ValidationMessages) => string | null;
2256
+ /** A non-blocking hint when the street ends in a number that is probably the house number: the
2257
+ * house-number field is empty, or holds that same number twice. "Bundesstraße 96" with "Nr. 3" is a
2258
+ * real street and gets no hint; digits inside a name ("Straße des 17. Juni") never do. */
2259
+ type StreetWarning = (street: string, housenumber: string, messages: ValidationMessages) => string | null;
2260
+ /** See {@link NormalizePhone}. */
2261
+ export declare const normalizePhone: NormalizePhone;
2262
+ /** See {@link PhoneError}. */
2263
+ export declare const phoneError: PhoneError;
2264
+ /** See {@link EmailError}. */
2265
+ export declare const emailError: EmailError;
2266
+ /** See {@link PostalcodeError}. */
2267
+ export declare const postalcodeError: PostalcodeError;
2268
+ /** See {@link StreetWarning}. */
2269
+ export declare const streetWarning: StreetWarning;
2270
+ //#endregion
2271
+ //#region src/checkout/guest-form.d.ts
2272
+ /** Every field, in visual order (the order `firstInvalid` walks). `salutation` holds a `Salutation`
2273
+ * or "" (none chosen); `country` an ISO code. */
2274
+ type GuestFormField = "salutation" | "forename" | "surname" | "email" | "phone" | "street" | "housenumber" | "postalcode" | "city" | "country" | "note";
2275
+ /** Fields a site may make required on top of the minimum (forename, surname, email, street, postal
2276
+ * code, city, country): an agency that calls back requires the phone, one whose PMS splits the
2277
+ * address requires the house number. */
2278
+ type OptionalGuestField = "salutation" | "phone" | "housenumber";
2279
+ /** What the guest-data form is built from. */
2280
+ interface GuestFormOptions {
2281
+ /** The validators' copy (the checkout copy satisfies it structurally). */
2282
+ messages: ValidationMessages;
2283
+ /** The Land options (`countryOptions` at build). */
2284
+ countries: CountryGroups;
2285
+ /** Default none. */
2286
+ requiredFields?: readonly OptionalGuestField[] | undefined;
2287
+ /** The Land when the browser languages name none of `countries` (with `languages: []` simply the
2288
+ * starting Land), and the dial code of a national phone number while no Land is chosen. Default "DE". */
2289
+ fallbackCountry?: CountryCode | undefined;
2290
+ /** `navigator.languages`, passed in by the UI (headless reads no navigator). Default none. */
2291
+ languages?: readonly string[] | undefined;
2292
+ }
2293
+ /** What a UI renders. */
2294
+ interface GuestFormState {
2295
+ /** Every field's current value. */
2296
+ values: Readonly<Record<GuestFormField, string>>;
2297
+ /** Blocking errors that are SHOWN (judged on blur, on change of a shown one, or on submit). */
2298
+ errors: Readonly<Partial<Record<GuestFormField, string>>>;
2299
+ /** Non-blocking hints (the street/house-number heuristic); never stop `submit()`. */
2300
+ warnings: Readonly<Partial<Record<GuestFormField, string>>>;
2301
+ /** The fields the guest has left once (their present value is judged). */
2302
+ touched: ReadonlySet<GuestFormField>;
2303
+ /** After a failed `submit()`: the topmost invalid field, for focus; null otherwise. */
2304
+ firstInvalid: GuestFormField | null;
2305
+ }
2306
+ /** The guest-data form store: its state and its actions. */
2307
+ interface GuestForm extends Store<GuestFormState> {
2308
+ /** Typing. A shown error is re-judged and clears once valid; changing the country re-checks a
2309
+ * present postal code and phone (a German error on "1234 AB" clears when the guest picks NL). */
2310
+ set(field: GuestFormField, value: string): void;
2311
+ /** Leaving a field: the phone is normalized in place (the guest sees "+49 176 …"), a present value
2312
+ * is judged, an empty one waits for submit. */
2313
+ blur(field: GuestFormField): void;
2314
+ /** Judge everything. Returns the contact for `book()`, or null with every error shown and
2315
+ * `firstInvalid` set. Warnings never block. */
2316
+ submit(): BookingContact | null;
2317
+ /** Stop publishing: later calls change nothing. */
2318
+ dispose(): void;
2319
+ }
2320
+ /** The guest data form store. */
2321
+ type CreateGuestForm = (options: GuestFormOptions) => GuestForm;
2322
+ /** See {@link CreateGuestForm}. */
2323
+ export declare const createGuestForm: CreateGuestForm;
893
2324
  //#endregion
894
2325
  //#region src/checkout/structure.d.ts
895
- /**
896
- * The checkout's STRUCTURAL requirements — what must be on screen, in what
897
- * order, regardless of who produced the markup.
898
- *
899
- * WHY THIS EXISTS. Headless holds the RULES (the § 7 VVG document gate, the
900
- * stale-price booking guard) but no markup, so a site that renders its own
901
- * checkout UI can silently drop a legal obligation — the § 312j BGB
902
- * Button-Lösung wording, the insurance documents listed before the guest's
903
- * confirmation, the price stated directly above the order button — and every
904
- * unit test still passes. This module is a contract over the RENDERED result.
905
- * The imported `Booking` component satisfies it by construction; a site's own
906
- * checkout is a developer's review item when it changes. The platform does
907
- * NOT run this assertion at publish time (decided 2026-09-11,
908
- * docs/website-platform/process/10-dist-checks.md "Custom checkouts").
909
- *
910
- * Two halves:
911
- * - `CHECKOUT_PARTS`: the `data-part` vocabulary. Imported components carry
912
- * these attributes; a site's own markup must keep them so this check
913
- * (and a site's own CSS) can find the elements.
914
- * - `CHECKOUT_STRUCTURE` + `assertCheckoutStructure(root)`: the requirements
915
- * and a pure DOM check over a rendered checkout (a browser page or a
916
- * jsdom document in a test). Every requirement names the legal
917
- * basis it exists for, so a failure message tells the reader WHY.
918
- */
2326
+ /** The legal `data-part` vocabulary. Imported components carry it; a custom checkout must keep it.
2327
+ * Style-only hooks are not here (components `BOOKING_PARTS`). Renaming a part is a major. */
919
2328
  export declare const CHECKOUT_PARTS: {
920
- /** The one place the guest is told the price they are about to owe. */
2329
+ /** The price the guest is about to owe (the summary, and directly above the order button). */
921
2330
  readonly priceTotal: "checkout-price-total";
922
- /** The § 312j order button. */
2331
+ /** The insurance premium: a second figure beside each total, never summed into it. */
2332
+ readonly insurancePremium: "checkout-insurance-premium";
2333
+ /** The order button. Carries `data-booking-kind`. */
923
2334
  readonly submit: "checkout-submit";
924
- /** The terms line (AGB + privacy triggers). */
2335
+ /** The terms notice, or the terms checkbox's label (AGB accepted, privacy information noted). */
925
2336
  readonly terms: "checkout-terms";
926
- /** One link per mandatory insurance document (§ 7 VVG). */
2337
+ /** Every consent checkbox (terms, privacy acknowledgement, additional terms). */
2338
+ readonly consent: "checkout-consent";
2339
+ /** One link per pre-contractual insurance document (§ 7 VVG). */
927
2340
  readonly insuranceDocument: "checkout-insurance-document";
928
2341
  /** The guest's receipt confirmation for those documents. */
929
2342
  readonly insuranceConfirm: "checkout-insurance-confirm";
930
2343
  };
2344
+ /** One value of `CHECKOUT_PARTS`. */
931
2345
  type CheckoutPart = (typeof CHECKOUT_PARTS)[keyof typeof CHECKOUT_PARTS];
932
- /** German "kostenpflichtig bestellen"-class labels the courts have accepted
933
- * for § 312j Abs. 3 BGB. The check is on the RENDERED button text, so a site
934
- * may re-voice the label — but only within this class. */
935
- export declare const BUTTON_SOLUTION_PATTERN: RegExp;
936
- export declare const CHECKOUT_STRUCTURE: {
937
- readonly "submit-present": {
938
- readonly basis: "§ 312j Abs. 3 BGB (Button-Lösung)";
939
- readonly description: "exactly one order button carries data-part=checkout-submit";
940
- };
941
- readonly "submit-wording": {
942
- readonly basis: "§ 312j Abs. 3 BGB (Button-Lösung)";
943
- readonly description: "the order button's visible text is a 'zahlungspflichtig buchen'-class label";
944
- };
945
- readonly "total-before-submit": {
946
- readonly basis: "§ 312j Abs. 2 BGB (price stated immediately before the order)";
947
- readonly description: "a data-part=checkout-price-total precedes the order button in DOM order";
948
- };
949
- readonly "terms-before-submit": {
950
- readonly basis: "§ 312d BGB / Art. 13 DSGVO (terms + privacy notice before contracting)";
951
- readonly description: "a data-part=checkout-terms precedes the order button";
952
- };
953
- readonly "insurance-docs-before-confirm": {
954
- readonly basis: "§ 7 Abs. 1 VVG, § 4 VVG-InfoV (documents before the declaration)";
955
- readonly description: "when an insurance confirmation is rendered, at least one insurance document precedes it in DOM order";
956
- };
957
- };
958
- type CheckoutRequirement = keyof typeof CHECKOUT_STRUCTURE;
2346
+ /** The attribute on the order button that names its kind ("booking" | "option"); absent reads as a
2347
+ * booking, so a button can only claim the option wording by saying it is one. */
2348
+ export declare const BOOKING_KIND_ATTRIBUTE: "data-booking-kind";
2349
+ /** The label classes the order button's visible text must match, per kind.
2350
+ * booking: "(zahlungspflichtig|kostenpflichtig) (buchen|bestellen|reservieren)", "jetzt kaufen",
2351
+ * "(book|order) with obligation to pay", "book and pay", "pay now", "buy now";
2352
+ * option: "unverbindlich (reservieren|anfragen)", "reserve without obligation",
2353
+ * "non-binding reservation".
2354
+ * A site may re-voice the label, but only within its class. The English and option classes await
2355
+ * a legal review (no case law covers them). */
2356
+ type SubmitWording = Readonly<Record<BookingKind, RegExp>>;
2357
+ /** One requirement: its legal basis and what it checks. */
2358
+ interface StructureRequirement {
2359
+ /** The legal basis ("§ 312j Abs. 3 BGB"). */
2360
+ readonly basis: string;
2361
+ /** What the requirement checks, in one sentence. */
2362
+ readonly description: string;
2363
+ }
2364
+ /** Every requirement `assertCheckoutStructure` checks, by id. */
2365
+ type CheckoutStructure = Readonly<Record<"submit-present" | "submit-wording" | "total-before-submit" | "terms-before-submit" | "consents-before-submit" | "insurance-docs-before-confirm" | "premium-before-submit", StructureRequirement>>;
2366
+ /** A requirement id. */
2367
+ type CheckoutRequirement = keyof CheckoutStructure;
2368
+ /** One failed requirement, naming its legal basis so a failure says why. */
959
2369
  interface StructureViolation {
2370
+ /** The failed requirement's id. */
960
2371
  requirement: CheckoutRequirement;
2372
+ /** Its legal basis. */
961
2373
  basis: string;
2374
+ /** What was found instead (the offending element or text). */
962
2375
  detail: string;
963
2376
  }
964
- export declare function assertCheckoutStructure(root: ParentNode): StructureViolation[];
2377
+ /** A pure DOM check over a rendered checkout (`document` in a browser, a jsdom document in a test).
2378
+ * Returns every violation; empty = compliant. Everything is judged relative to the one order
2379
+ * button, in DOM order (a sticky bottom bar after the body is fine while the total, terms and
2380
+ * consents precede it). The insurance rules apply only when a confirmation control is rendered:
2381
+ * the duty attaches to concluding an insurance contract. */
2382
+ type AssertCheckoutStructure = (root: ParentNode) => StructureViolation[];
2383
+ export declare const SUBMIT_WORDING: SubmitWording;
2384
+ export declare const CHECKOUT_STRUCTURE: CheckoutStructure;
2385
+ /** See {@link AssertCheckoutStructure}. */
2386
+ export declare const assertCheckoutStructure: AssertCheckoutStructure;
965
2387
  //#endregion
966
- //#region src/dates.d.ts
967
- export declare function parseParam(value: string | null | undefined): Date | undefined;
968
- export declare function parseGermanParam(value: string | null | undefined): Date | undefined;
969
- /** A flexible-length stay ("3–5 Nächte") as the URL carries it. Off until the
970
- * search UI offers it; the keys are reserved so a site can enable it. */
971
- export declare const NIGHT_RANGE_ENABLED = false;
972
- export interface NightRange {
973
- min: number;
974
- max: number;
975
- }
976
- export declare const NIGHT_RANGE_KEYS: readonly ["minNights", "maxNights", "nights_min", "nights_max"];
977
- export declare function parseNightRange(params: URLSearchParams): NightRange | null;
978
- //#endregion
979
- //#region src/format.d.ts
980
- /**
981
- * Display tidy for an SDK pre-formatted German-locale price ("1.234,00 €").
982
- * Nearly every stay is whole euros, so the trailing ",00" is noise — dropped only
983
- * when it is the zero-cents group at the end, so real cents ("1.234,50 €") stay.
984
- * A real value formatted for display, nothing fabricated; other formats pass.
985
- */
986
- export declare function tidyPrice(formatted: string): string;
987
- //#endregion
988
- //#region src/availability.d.ts
989
- /**
990
- * The per-night price a calendar CELL shows, rounded to whole euros:
991
- * "116,43 €" -> "116 €", "116,50 €" -> "117 €".
992
- *
993
- * A month grid is thirty-odd numbers seen at a glance and acted on by NONE of
994
- * them — the guest picks a range, and the quote under the calendar then states
995
- * the stay to the last cent. Two decimals in every cell buy precision nobody
996
- * reads, at the one size where the digits are already tight.
997
- *
998
- * ROUNDS TO NEAREST, AND THAT IS THE POINT OF THE FUNCTION. Stripping the cents
999
- * with a regex is one line and understates every priced night by up to 99
1000
- * cents, always in the direction that flatters the price. That is the shape of
1001
- * claim a host has to stand behind, so the error goes both ways here and is
1002
- * capped at 50 cents on a figure that is explicitly indicative.
1003
- *
1004
- * Parses the German format rather than doing string surgery on it: "." groups
1005
- * thousands, "," is the decimal. Anything that does not match that shape —
1006
- * another locale, no decimal group, a price the SDK formatted differently — is
1007
- * returned UNCHANGED. A display tidy must never turn a price it did not
1008
- * understand into a wrong one.
1009
- *
1010
- * This runs where `priceByDay` is BUILT rather than where a cell renders it, so
1011
- * every calendar fed from this map agrees: the widget's picker and the
1012
- * checkout's copy, which reads the same shape.
1013
- */
1014
- export declare function nightlyPrice(formatted: string, locale?: Locale): string;
1015
- type VofficeSdk$1 = WebsiteSdk;
1016
- export interface InitialAvailability {
1017
- /** ISO dates (`YYYY-MM-DD`) the SDK flags as a valid check-in/arrival day. */
1018
- canStart: Set<string>;
1019
- /** ISO dates the rental is genuinely unbookable on (booked / closed / past the
1020
- * bookable horizon — the SDK's "hard" statuses). The picker STRIKES these
1021
- * through. A disabled day NOT in this set is only blocked by a rule for the
1022
- * current phase (e.g. check-in-not-allowed) and is merely dimmed — it could
1023
- * work in another context. */
1024
- hardBlocked: Set<string>;
1025
- /** Per-night price by ISO date — the SDK's localized string, display-tidied
1026
- * to drop the noise ",00" (e.g. "120 €"). Only days the SDK prices. */
1027
- priceByDay: Map<string, string>;
1028
- /** Localized reason a day is blocked, by ISO date (the SDK's own `tooltip`,
1029
- * e.g. "An diesem Tag ist keine Anreise möglich."). */
1030
- tooltipByDay: Map<string, string>;
1031
- /** Occupancy caps the SDK states for this rental (null when omitted). The
1032
- * picker caps the guest stepper at these so a quote can't fail on too many
1033
- * guests; the below-minimum case is left to the quote (the SDK is the
1034
- * arbiter of a bookable party), so `minPersons` isn't surfaced here. */
1035
- maxPersons: number | null;
1036
- maxAdults: number | null;
1037
- childrenAllowed: boolean | null;
1038
- maxPets: number | null;
2388
+ //#region src/forms/contact.d.ts
2389
+ /** The built-in fields: the contact API has a slot for each (names follow the SDK's paths). */
2390
+ type ContactFieldName = "salutation" | "forename" | "surname" | "email" | "phone" | "country" | "street" | "postalCode" | "city" | "message";
2391
+ /** Grid width of a field on wide screens (one column below the form's breakpoint). */
2392
+ type FieldWidth = "full" | "half";
2393
+ /** Value rules a spec may declare. Judged only on a present value; required-ness is its own check
2394
+ * with its own message. `pattern` matches the WHOLE value (anchored, `v` flag, as the browser's
2395
+ * `pattern` attribute). `min`/`max` compare numbers for a number field and ISO days for a date field
2396
+ * (`"today"` is today); `step` counts from `min` (default 0). */
2397
+ interface ValueConstraints {
2398
+ /** Lowest value: a number for a number field, an ISO day or "today" for a date field. */
2399
+ readonly min?: number | string;
2400
+ /** Highest value, in the same terms as `min`. */
2401
+ readonly max?: number | string;
2402
+ /** A number field's step, counted from `min` (default 0). */
2403
+ readonly step?: number;
2404
+ /** A regular expression the whole value must match (anchored, `v` flag). */
2405
+ readonly pattern?: string;
2406
+ /** Most characters. */
2407
+ readonly maxLength?: number;
2408
+ /** Shown when a present value fails a constraint; default `messages.fieldInvalid`. */
2409
+ readonly invalidMessage?: string;
1039
2410
  }
1040
- export interface EndAvailability {
1041
- /** ISO dates the SDK flags as a valid check-out/departure for the chosen
1042
- * start (min-stay + checkout rules already applied by the SDK). */
1043
- canEnd: Set<string>;
1044
- /** ISO dates genuinely unbookable (booked / the range would cross an
1045
- * unavailable night that is itself unavailable). STRUCK through. A disabled
1046
- * day not here is only rule-blocked for this start (min-stay, check-out-not-
1047
- * allowed, same-day, would-be a new start) and is merely dimmed. */
1048
- hardBlocked: Set<string>;
1049
- /** Localized reason a departure is blocked, by ISO date (min-stay-not-met,
1050
- * check-out-not-allowed, range-crosses-booked, …) — the SDK's own `tooltip`. */
1051
- tooltipByDay: Map<string, string>;
1052
- }
1053
- /** Possible check-in days + occupancy caps for `rentalId` across the window.
1054
- * The caller's SDK instance is reused so its 30s per-rental cache spans the
1055
- * picker's lifetime. */
1056
- export declare function fetchInitialAvailability(sdk: VofficeSdk$1, rentalId: string, calculateFromDate: string, calculateThroughDate: string, locale: Locale): Promise<InitialAvailability>;
1057
- /** Possible check-out days for a chosen `selectedStartDate`. */
1058
- export declare function fetchEndAvailability(sdk: VofficeSdk$1, rentalId: string, calculateFromDate: string, calculateThroughDate: string, selectedStartDate: string, locale: Locale): Promise<EndAvailability>;
1059
- //#endregion
1060
- //#region src/search.d.ts
1061
- type VofficeSdk = WebsiteSdk;
1062
- export interface SearchImage {
1063
- src: string;
1064
- srcset?: string | undefined;
1065
- alt: string;
2411
+ /** A built-in field with one or more decisions changed. */
2412
+ interface BuiltInFieldSpec extends ValueConstraints {
2413
+ /** The built-in field this spec changes. */
2414
+ readonly field: ContactFieldName;
2415
+ /** Default: salutation, forename, surname, email, country and message are required; phone, street,
2416
+ * postalCode and city are optional. */
2417
+ readonly required?: boolean;
2418
+ /** Replaces the copy label ("Telefon (tagsüber)", "Name" on a surname-only form). */
2419
+ readonly label?: string;
2420
+ /** Default "full" ("half" pairs two fields on wide screens). */
2421
+ readonly width?: FieldWidth;
2422
+ /** `salutation` only: the choices offered, in order. Default all three. */
2423
+ readonly options?: readonly Salutation[];
2424
+ /** `salutation`: a `Salutation`; `country`: a `CountryCode`; others: text. An answer chosen in
2425
+ * advance, still editable ("unspecified" preselected is an answer, not an absence). */
2426
+ readonly defaultValue?: string;
1066
2427
  }
1067
- /** One window the SDK suggests for a rental it could not match exactly — a
1068
- * concrete, priced, bookable stay.
1069
- *
1070
- * EVERY FIELD IS READ OFF THE PERIOD THE SDK SENDS, never off the rental. A
1071
- * promotion belongs to a (rental x window) pair — the same rental prices
1072
- * undiscounted in another window — so the price and the discount are read from
1073
- * the same period together, the way `mapAlternativeEntry` already resolved them
1074
- * for the single window it used to render. A window whose dates the feed omits
1075
- * has no label and never reaches this list; one the feed does not price shows
1076
- * no price rather than a placeholder. */
1077
- export interface AlternativePeriod {
1078
- /** „31. Aug - 4. Sep", composed from this window's own start and end. */
1079
- label: string;
1080
- /** The detail-page link carrying THIS window's dates, so the rental opens on
1081
- * the stay the row names rather than on the user's (unmatched) query. */
1082
- href: string;
1083
- /** This window's `formattedTotal`, verbatim. null when the feed omits it. */
1084
- priceLabel: string | null;
1085
- /** This window's length in nights. null when the feed omits a date. */
1086
- nights: number | null;
1087
- /** True when THIS window is discounted — the presence of the SDK's `discount`
1088
- * block on this period, nothing inferred. Carried so the three discount facts
1089
- * arrive together from `mapDiscount`, exactly as they do on the card. */
1090
- isOffer: boolean;
1091
- /** This window's pre-discount total, for the struck-through "was" price. */
1092
- offerOriginalPriceLabel: string | null;
1093
- /** This window's promotions, the PMS's own labels, verbatim. `amount` is
1094
- * LINE-level and must not be printed beside a total — see the note on
1095
- * `SearchResultItem.offers`. */
1096
- offers: {
1097
- label: string;
1098
- amount: string | null;
1099
- }[];
2428
+ /** One option of a select extra; `label` is what the reader sees AND what the message line says. */
2429
+ interface SelectOption {
2430
+ /** What the value is stored as (and sent). */
2431
+ readonly value: string;
2432
+ /** What the reader sees, and what the message line says. */
2433
+ readonly label: string;
1100
2434
  }
1101
- export interface SearchResultItem {
1102
- /** PMS id — shared by the result card and its map marker. */
1103
- id: string;
1104
- /** `nameOrLabel`, verbatim. */
1105
- name: string;
1106
- /** `address.city`, verbatim (empty when the feed omits it). */
1107
- location: string;
1108
- /** Internal detail link — `rentalsPath` (see `runLiveSearch`) + the slug
1109
- * derived from the name (URL generation), with the user's booking context
1110
- * (dates + party size) appended as a query so the search selection carries
1111
- * into the rental page. */
1112
- href: string;
1113
- images: SearchImage[];
1114
- /** Parsed out of the SDK highlights; 0 when the highlight is absent. */
1115
- guests: number;
1116
- bedrooms: number;
1117
- area: number;
1118
- /** Plain-text amenity highlights (the SDK has no icon concept), CARRIED WHOLE.
1119
- *
1120
- * This used to be `highlights.slice(3, 6)` — the SDK opens `highlights` with
1121
- * the spec stats the line above already prints, and three was a guess at how
1122
- * many. It is not a constant: how many spec lines a feed states depends on
1123
- * which of them the PMS has filled and on `rentalHighlightPrioritization`, so
1124
- * a feed stating four leaked „55 m²" into the pill row, while the same slice
1125
- * ALSO discarded good candidates below position 6.
1126
- *
1127
- * The CARD decides now: `rental-detail-kit`'s `cardHighlights` drops the spec
1128
- * block by SHAPE, then `pillRow` merges and caps. A consumer that renders this
1129
- * array raw must cap it itself — it is no longer bounded here. */
1130
- pills: string[];
1131
- /** `formattedTotal`, verbatim (null when the feed omits a price). */
1132
- priceLabel: string | null;
1133
- /** Label for an SDK-provided alternative period, null for exact matches.
1134
- * The FIRST of `alternativePeriods` — the SDK returns them best-first — kept
1135
- * as its own field because the card's date pill names one window even where
1136
- * the chooser lists several. */
1137
- alternativePeriodLabel: string | null;
1138
- /** EVERY window the SDK suggests for this card, best-first — not just the one
1139
- * `alternativePeriodLabel` names. Empty on exact-match cards.
1140
- *
1141
- * THE FEED STATES MORE THAN ONE AND THIS PROJECT RENDERED ONE. Measured
1142
- * against v10 production on 2026-08-27, query
1143
- * `start=2026-08-31&end=2026-09-02&adults=2`: 19 alternative entries, and 14
1144
- * of them carried TWO priced windows. `mapAlternativeEntry` read
1145
- * `alternativePeriods[0]` and dropped the rest, so on three cards in four a
1146
- * real, priced, bookable window existed that the guest was never shown. The
1147
- * same query's flexible sibling (`month=10-2026&nights=7`) returned exactly
1148
- * one window on all 20 entries — so the count is a property of the QUERY, not
1149
- * a constant, and every surface reading this must handle both. */
1150
- alternativePeriods: AlternativePeriod[];
1151
- /** True only when the SDK flags this item as a non-exact date match. */
1152
- isAlternativePeriod: boolean;
1153
- /** Stay length for THIS card's own window, in nights. Set only on
1154
- * alternative-period cards (whose date pill describes the SDK's suggested
1155
- * window, not the user's search) so the footer's "N Nächte" matches the dates
1156
- * shown. null on exact-match cards — they fall back to the page-level
1157
- * `nights` (their window IS the user's search). */
1158
- nights: number | null;
1159
- /** True when THIS card's window is discounted ("Angebot" badge) — the mere
1160
- * PRESENCE of the SDK's `discount` block on the priced result, nothing
1161
- * inferred. A promotion belongs to a (rental × window) PAIR, not to a rental:
1162
- * the same rental prices undiscounted in another window, so this travels with
1163
- * the price, not with the identity. Never derived from a lead-time threshold —
1164
- * that number is the customer's PMS setting and moves. */
1165
- isOffer: boolean;
1166
- /** `discount.formattedOriginalTotal`, verbatim — the pre-discount total, for
1167
- * the struck-through "was" price. null when the feed states a discount but no
1168
- * original total: presence of a discount and the ability to price it are
1169
- * different facts, so the "was" price gates on its own field. */
1170
- offerOriginalPriceLabel: string | null;
1171
- /** `discount.applied`, verbatim — the PMS's own name for each promotion and
1172
- * its amount. CARRIED, NEVER AUTHORED: the label names the offer ("Last-Minute
1173
- * Angebot" is what this feed states today); writing our own over it would be
1174
- * fabricated data. Empty when the feed states a discount it doesn't itemise —
1175
- * `isOffer` can be true with nothing here, so the label row gates on length.
1176
- *
1177
- * ⚠ `amount` IS LINE-LEVEL, AND MUST NOT BE SHOWN BESIDE A TOTAL. It is the
1178
- * modifier on the rent line, while `offerOriginalPriceLabel` and `priceLabel`
1179
- * are totals — so wherever the PMS puts taxes or fees outside the discounted
1180
- * line, the three do not reconcile. Measured on a live v10 quote of the same
1181
- * stay the search priced: Inklusivpreis 484,35 € − 72,87 € = 411,48 €, plus
1182
- * Steuern & Abgaben 10,77 €, total 422,25 €, against a search
1183
- * `formattedOriginalTotal` of 497,12 €. Both endpoints agree on every figure;
1184
- * the real total saving is 74,87 € because the tax shrank too, while `amount`
1185
- * states the rent's 72,87 €. `search-results-kit` renders the LABEL alone for
1186
- * this reason — it did render the amount, in 1.10.0, and that was the bug.
1187
- * The field STAYS because it is correct DATA: a surface that shows the rent
1188
- * line itself may pair it with that line. Do not "fix" it by computing
1189
- * `original − total` — that number is nowhere in the feed. */
1190
- offers: {
1191
- label: string;
1192
- amount: string | null;
1193
- }[];
1194
- /** True when the rental is newly listed ("Neu" badge). The live search summary
1195
- * feed carries no listing-age signal — the SDK's search result
1196
- * (`toSearchOutputItem`) exposes only id/name/timeZone/address/images/
1197
- * highlights/formattedTotal, with no created/published/firstListed field — so
1198
- * this is always false today and the badge self-hides; wire it to a real SDK
1199
- * source (e.g. a per-rental created/published date) when search exposes one.
1200
- * Never fabricate it — straight-through rule. */
1201
- isNew: boolean;
1202
- /** `property.id`, verbatim (null when the rental belongs to no property). The
1203
- * v-office "Property" is the Anlage/complex a rental sits in; the home page's
1204
- * Anlage cards link to `/suchen?property=<id>`, and the shared search store
1205
- * scopes its snapshot to this id client-side (the live search has no property
1206
- * filter key — verified: `property=` lands in `unusedFilterKeys`). Same
1207
- * scoping model as favorites mode. */
1208
- propertyId: string | null;
1209
- /** `location`, verbatim (null when the feed omits coordinates). */
1210
- coords: {
1211
- lat: number;
1212
- lng: number;
2435
+ interface ExtraBase extends ValueConstraints {
2436
+ /** Input name: `[a-z][a-zA-Z0-9]*`, unique, not a `ContactFieldName`. */
2437
+ readonly extra: string;
2438
+ /** The field's label (also the default title of its message line). */
2439
+ readonly label: string;
2440
+ /** Default false. A required checkbox must be ticked. */
2441
+ readonly required?: boolean;
2442
+ /** What the value is titled inside the message; default `label`. */
2443
+ readonly messagePrefix?: string;
2444
+ /** The control's placeholder. Default none. */
2445
+ readonly placeholder?: string;
2446
+ /** Default "full" ("half" pairs two fields on wide screens). */
2447
+ readonly width?: FieldWidth;
2448
+ /** Text fields: the starting text; select: an option value; checkbox: "true" ticks it. */
2449
+ readonly defaultValue?: string;
2450
+ /** Fill from this URL query parameter (`prefillFromParams`). Text, number, date and select extras
2451
+ * only; built-in fields are personal data, and a URL is logged and shared. */
2452
+ readonly prefillParam?: string;
2453
+ }
2454
+ /** A field the contact API has no slot for. Its answer becomes one line of the message:
2455
+ * text/multiline/email/tel/number "<prefix>: <value>" (only when answered)
2456
+ * date "<prefix>: <formatIsoDate(value)>"
2457
+ * select "<prefix>: <option label>"
2458
+ * checkbox "<prefix>: <checkboxYes|checkboxNo>", ALWAYS written (both answers
2459
+ * are worth having to the reader in the PMS)
2460
+ * period "<prefix>: <from> <periodSeparator> <to>" (a missing end is
2461
+ * `periodOpen`), or "<prefix>: <periodFlexible>" */
2462
+ type ExtraFieldSpec = (ExtraBase & {
2463
+ readonly kind?: "text" | "multiline" | "email" | "tel" | "number" | "date";
2464
+ }) | (ExtraBase & {
2465
+ readonly kind: "select";
2466
+ readonly options: readonly SelectOption[];
2467
+ }) | (ExtraBase & {
2468
+ readonly kind: "checkbox";
2469
+ }) | (ExtraBase & {
2470
+ readonly kind: "period";
2471
+ /** Offer "dates are flexible" (the dates then do not count). Default false. */
2472
+ readonly allowFlexible?: boolean;
2473
+ });
2474
+ /** One entry of a form's field list, in render order:
2475
+ * "email" the built-in as it ships
2476
+ * { field: "phone", required } a built-in with one decision changed
2477
+ * { extra: "amount", kind, … } a field the API has no slot for
2478
+ * { group: "Objekt" } an inert heading over the fields that follow */
2479
+ type ContactFieldSpec = ContactFieldName | BuiltInFieldSpec | ExtraFieldSpec | {
2480
+ readonly group: string;
2481
+ };
2482
+ /** A period answer: ISO days or "" each. */
2483
+ interface PeriodValue {
2484
+ /** Arrival, or "" while not chosen. */
2485
+ readonly from: IsoDay | "";
2486
+ /** Departure, or "" while not chosen (an open end). */
2487
+ readonly to: IsoDay | "";
2488
+ /** The visitor ticked "dates are flexible": the dates do not count. */
2489
+ readonly flexible: boolean;
2490
+ }
2491
+ /** Text-like fields hold a string, a checkbox a boolean, a period a `PeriodValue`. */
2492
+ type ContactValue = string | boolean | PeriodValue;
2493
+ /** Every field's value by input name. */
2494
+ type ContactValues = Readonly<Record<string, ContactValue>>;
2495
+ /** Every string the rules read; the merged `ContactFormCopy` of the components satisfies it.
2496
+ * Templates: `{label}` the field label, `{email}` the support inbox, `{max}` the limit. */
2497
+ interface ContactFormMessages {
2498
+ /** The salutation field's label. */
2499
+ salutationLabel: string;
2500
+ /** The forename field's label. */
2501
+ forenameLabel: string;
2502
+ /** The surname field's label. */
2503
+ surnameLabel: string;
2504
+ /** The email field's label. */
2505
+ emailLabel: string;
2506
+ /** The phone field's label. */
2507
+ phoneLabel: string;
2508
+ /** The country field's label. */
2509
+ countryLabel: string;
2510
+ /** The street field's label. */
2511
+ streetLabel: string;
2512
+ /** The postal code field's label. */
2513
+ postalCodeLabel: string;
2514
+ /** The city field's label. */
2515
+ cityLabel: string;
2516
+ /** The message field's label. */
2517
+ messageLabel: string;
2518
+ /** A required salutation left empty. */
2519
+ salutationRequired: string;
2520
+ /** A required forename left empty. */
2521
+ forenameRequired: string;
2522
+ /** A required surname left empty. */
2523
+ surnameRequired: string;
2524
+ /** A required email left empty. */
2525
+ emailRequired: string;
2526
+ /** A required phone left empty. */
2527
+ phoneRequired: string;
2528
+ /** A required country left empty. */
2529
+ countryRequired: string;
2530
+ /** A required street left empty. */
2531
+ streetRequired: string;
2532
+ /** A required postal code left empty. */
2533
+ postalCodeRequired: string;
2534
+ /** A required city left empty. */
2535
+ cityRequired: string;
2536
+ /** A required message left empty. */
2537
+ messageRequired: string;
2538
+ /** An extra left empty ("Bitte {label} ausfüllen."). */
2539
+ extraRequired: Template<"label">;
2540
+ /** A present value that fails a declared constraint ("Bitte {label} prüfen."). */
2541
+ fieldInvalid: Template<"label">;
2542
+ /** A present email that is no address. */
2543
+ emailInvalid: string;
2544
+ /** A period whose end precedes its start, or whose start lies in the past. */
2545
+ periodInvalid: string;
2546
+ /** The composed message exceeds the API limit ({max}). */
2547
+ messageTooLong: Template<"max">;
2548
+ /** The line above the button when the SDK rejected fields that are marked inline. */
2549
+ fixMarkedFields: string;
2550
+ /** The fallback failure line; contains {email}. */
2551
+ sendFailed: Template<"email">;
2552
+ /** The fallback failure line when the site publishes no inbox (`supportEmail: null`). */
2553
+ sendFailedNoEmail: string;
2554
+ /** A ticked checkbox extra in the message line ("ja"). */
2555
+ checkboxYes: string;
2556
+ /** An unticked checkbox extra in the message line ("nein"). */
2557
+ checkboxNo: string;
2558
+ /** A period extra answered with "dates are flexible". */
2559
+ periodFlexible: string;
2560
+ /** A period extra's missing end in the message line. */
2561
+ periodOpen: string;
2562
+ /** Between a period's two days in the message line ("bis"). */
2563
+ periodSeparator: string;
2564
+ }
2565
+ /** The control a field renders. "salutation" and "country" are the two selects whose options the
2566
+ * UI resolves itself (copy labels; the build-time country register). */
2567
+ type ContactControl = "text" | "multiline" | "email" | "tel" | "number" | "date" | "select" | "checkbox" | "period" | "salutation" | "country";
2568
+ /** A spec after defaults: what validation, payload and render all walk, so they cannot disagree
2569
+ * about what the form contains. */
2570
+ type ResolvedContactItem =
2571
+ /** An inert heading over the fields that follow (`{ group }` in the spec list). */
2572
+ {
2573
+ readonly type: "group";
2574
+ readonly label: string;
2575
+ } | {
2576
+ readonly type: "field";
2577
+ /** The input name (a built-in's name or the extra's). */
2578
+ readonly name: string;
2579
+ /** The built-in it fills, or null for an extra. */
2580
+ readonly builtIn: ContactFieldName | null;
2581
+ /** The control to render. */
2582
+ readonly control: ContactControl;
2583
+ /** The label shown (the spec's, else the copy's). */
2584
+ readonly label: string;
2585
+ /** Whether an empty value stops the submit. */
2586
+ readonly required: boolean;
2587
+ /** "full" or "half" on wide screens. */
2588
+ readonly width: FieldWidth;
2589
+ /** The `autocomplete` token of a built-in (personal data the browser may fill), or null. */
2590
+ readonly autoComplete: string | null;
2591
+ /** The control's placeholder, or null. */
2592
+ readonly placeholder: string | null;
2593
+ /** A select extra's options; null for every other control. */
2594
+ readonly options: readonly SelectOption[] | null;
2595
+ /** The salutation choices in order; null for every other control. */
2596
+ readonly salutations: readonly Salutation[] | null;
2597
+ /** A period extra offers "dates are flexible". */
2598
+ readonly allowFlexible: boolean;
2599
+ /** Extras: the title of its message line; null for built-ins. */
2600
+ readonly messagePrefix: string | null;
2601
+ /** The value rules judged on a present value. */
2602
+ readonly constraints: ValueConstraints;
2603
+ /** The URL query parameter that fills it (extras only), or null. */
2604
+ readonly prefillParam: string | null;
2605
+ };
2606
+ /** A resolved field (the non-group member of `ResolvedContactItem`). */
2607
+ type ResolvedContactField = Extract<ResolvedContactItem, {
2608
+ readonly type: "field";
2609
+ }>;
2610
+ /** What a contact form is built from; plain data except the clock. */
2611
+ interface ContactFormOptions {
2612
+ /** The SDK locale of the request and the language of its validation messages. */
2613
+ readonly locale: Locale;
2614
+ /** Recorded on the request in the PMS; names the channel ("Anfrage Eigentümer"). ≤ 200 characters. */
2615
+ readonly subject: string;
2616
+ /** The field list in render order. */
2617
+ readonly fields: readonly ContactFieldSpec[];
2618
+ /** Every string the rules read (the components' merged copy satisfies it). */
2619
+ readonly messages: ContactFormMessages;
2620
+ /** Named in the failure line. Required, no default: a default would print one agency's inbox on
2621
+ * another's site. `null` drops that half of the line (no publishable inbox). */
2622
+ readonly supportEmail: string | null;
2623
+ /** Prefill (`prefillFromParams`) and the preselected country (`detectCountry`); laid over the
2624
+ * fields' own defaults. */
2625
+ readonly initialValues?: ContactValues;
2626
+ /** The clock for "today" constraints and periods, for tests. Default `() => new Date()`. */
2627
+ readonly now?: () => Date;
2628
+ }
2629
+ /** editing → submitting → sent (or back to editing on a failure). */
2630
+ type ContactPhase = "editing" | "submitting" | "sent";
2631
+ /** What a UI renders. */
2632
+ interface ContactFormState {
2633
+ /** The resolved field list (groups and fields), in render order. */
2634
+ readonly items: readonly ResolvedContactItem[];
2635
+ /** Every field's current value by input name. */
2636
+ readonly values: ContactValues;
2637
+ /** Field name → message, in list order; set by submit, by blur (email format) and by SDK issues. */
2638
+ readonly errors: Readonly<Record<string, string>>;
2639
+ /** Where the form stands. */
2640
+ readonly phase: ContactPhase;
2641
+ /** The line above the button after a failed submit (summary, unmapped SDK issues, or the
2642
+ * send-failed fallback). */
2643
+ readonly submitError: string | null;
2644
+ /** The first failed field after a failed submit; the UI focuses it once per `nonce`, after the
2645
+ * render that marks it (so the error is announced with the field). */
2646
+ readonly focusTarget: {
2647
+ readonly name: string;
2648
+ readonly nonce: number;
1213
2649
  } | null;
2650
+ /** `contactSpecProblems(fields)`: reported once by the UI (console.warn), never thrown. */
2651
+ readonly specProblems: readonly string[];
1214
2652
  }
1215
- export interface SearchPayload {
1216
- items: SearchResultItem[];
1217
- /** Stay length from the user's start/end query, or null when undated. */
1218
- nights: number | null;
1219
- /** Count of SDK-provided alternative-period results in this page. */
1220
- alternativePeriodCount: number;
1221
- /** `pageInfo.nextCursor`, verbatim — opaque, passed straight back to fetch
1222
- * the next page. null when the feed omits it (last page / no more). */
1223
- cursor: string | null;
1224
- /** `pageInfo.hasNextPage`, verbatim. */
1225
- hasNextPage: boolean;
1226
- }
1227
- export interface LiveSearchOptions {
1228
- /** Path of the site's rental detail pages, prefixed to every result `href`
1229
- * before the slug (e.g. `/de/unterkuenfte`). A trailing slash is tolerated.
1230
- * Default: `DEFAULT_RENTALS_PATH`. */
1231
- rentalsPath?: string | undefined;
1232
- }
1233
- /** The rental detail pages' path when the caller names none — the site
1234
- * starter's German route. A site whose pages live elsewhere (a locale prefix,
1235
- * another segment) passes its own `rentalsPath` to `runLiveSearch`. */
1236
- export declare const DEFAULT_RENTALS_PATH = "/unterkuenfte";
1237
- /**
1238
- * Run a live search for the given URL query string (e.g.
1239
- * `window.location.search`). Builds the SDK query from the picker params,
1240
- * calls the rate-limited live API once, and maps the items. Pass an SDK
1241
- * instance to reuse its internal cache across an eager pagination walk;
1242
- * otherwise this function owns a short-lived instance for a single call.
1243
- *
1244
- * Pass `cursor` (the previous page's `pageInfo.nextCursor`, treated as
1245
- * opaque) to fetch the next page. The returned `cursor`/`hasNextPage` are
1246
- * the SDK's `pageInfo` straight through, so the caller can keep paging.
1247
- *
1248
- * `options.rentalsPath` is the path the site serves its rental detail pages
1249
- * under — the part of every result `href` before the slug. Routing is the
1250
- * site's decision (a locale prefix like `/de/unterkuenfte`, another segment),
1251
- * so it arrives as an option the way `SearchBar.action` and `Quote.checkoutPath`
1252
- * do; `locale` never implies it. Defaults to `DEFAULT_RENTALS_PATH`.
1253
- */
1254
- export declare function runLiveSearch(sdk: VofficeSdk, searchString: string, locale: Locale, cursor?: string | null, options?: LiveSearchOptions): Promise<SearchPayload>;
2653
+ /** What `submit()` resolves to. `rejected` = the SDK refused the input (issues routed); `failed` =
2654
+ * transport, backend or a lazy SDK that did not load. */
2655
+ type ContactSubmitOutcome = {
2656
+ readonly ok: true;
2657
+ } | {
2658
+ readonly ok: false;
2659
+ readonly reason: "busy" | "invalid" | "rejected" | "failed";
2660
+ };
2661
+ /** The actions a UI calls; stable for the form's lifetime. */
2662
+ interface ContactFormActions {
2663
+ /** Typing or choosing: sets a value; a shown error of that field is re-judged. */
2664
+ setValue(name: string, value: ContactValue): void;
2665
+ /** Leaving a field: re-checks the email format; clears that field's shown error once it is fixed. */
2666
+ blur(name: string): void;
2667
+ /** Validate, compose, submit. USER-initiated only. A second call while submitting resolves
2668
+ * `{ ok: false, reason: "busy" }` (double click, second Enter). */
2669
+ submit(): Promise<ContactSubmitOutcome>;
2670
+ /** Back to the initial values and `editing` ("send another message"). */
2671
+ reset(): void;
2672
+ /** Copy, locale, subject or inbox changed (props of the UI); values are kept. The fields and the
2673
+ * SDK source are read from the first options only. */
2674
+ update(options: Partial<Pick<ContactFormOptions, "messages" | "locale" | "subject" | "supportEmail">>): void;
2675
+ /** Stop publishing: later actions and a still pending submit change nothing. */
2676
+ dispose(): void;
2677
+ }
2678
+ /** The contact form store: its state and its actions. */
2679
+ type ContactForm = Store<ContactFormState> & ContactFormActions;
2680
+ /** The contact form store. The SDK source may be lazy (`loadSharedSdk`): it is awaited on the first
2681
+ * submit only, so a contact page ships no SDK until someone sends a message. */
2682
+ type CreateContactForm = (sdk: SdkSource, options: ContactFormOptions) => ContactForm;
2683
+ /** See {@link CreateContactForm}. */
2684
+ export declare const createContactForm: CreateContactForm;
1255
2685
  //#endregion
1256
- //#region src/sort.d.ts
1257
- /** URL param. A client-side scope like `property`/`region`, NOT an SDK query
1258
- * key — it is dropped from the SDK query string in search.ts's DROP_KEYS and
1259
- * travels as the separate `sort` input instead. */
1260
- export declare const SORT_PARAM = "sort";
1261
- export type SortValue = "empfohlen" | "preis-auf" | "preis-ab";
1262
- /** The backend's own order. Emits no `sort` at all, so it is exactly the
1263
- * ordering the page has always shown. Typed as the literal (not `SortValue`)
1264
- * so `value === DEFAULT_SORT` narrows the rest away for `SDK_SORT`. */
1265
- export declare const DEFAULT_SORT: "empfohlen";
1266
- export interface SortOption {
1267
- value: SortValue;
1268
- label: string;
1269
- /** Needs a period + occupancy, or the SDK rejects it before transport. */
1270
- pricedOnly?: boolean;
2686
+ //#region src/forms/contact-fields.d.ts
2687
+ /** The contact kit's set without its owner-location field (a site that wants one adds an extra). */
2688
+ export declare const DEFAULT_CONTACT_FIELDS: readonly ContactFieldName[];
2689
+ /** The API rejects an empty value for these (`validateContactSubmitInput`); `subject` comes from the
2690
+ * options, and `message` may be composed from extras alone. */
2691
+ export declare const CONTACT_ENDPOINT_REQUIRED: readonly ("salutation" | "surname" | "email" | "country" | "message")[];
2692
+ /** The API's length limits a form can exceed by composition (extras ride in the message). */
2693
+ export declare const CONTACT_LIMITS: {
2694
+ readonly message: 5000;
2695
+ readonly subject: 200;
2696
+ };
2697
+ /** Specs → items, labels from `messages` unless the spec sets one. Validation, the payload and the
2698
+ * render all walk this one list. */
2699
+ type ResolveContactFields = (fields: readonly ContactFieldSpec[], messages: ContactFormMessages) => readonly ResolvedContactItem[];
2700
+ /** Problems that make every submit fail or the form ambiguous, as developer-facing sentences: a
2701
+ * missing or optional endpoint-required built-in (`message` may be absent only when an extra always
2702
+ * writes a line: a required one or a checkbox), a duplicate name, an extra named like a built-in or
2703
+ * not `[a-z][a-zA-Z0-9]*`, a select without options, salutation `options` on another field, a
2704
+ * `prefillParam` on a checkbox or period extra (ignored), an invalid `pattern`. */
2705
+ type ContactSpecProblems = (fields: readonly ContactFieldSpec[]) => readonly string[];
2706
+ /** See {@link ResolveContactFields}. */
2707
+ export declare const resolveContactFields: ResolveContactFields;
2708
+ /** See {@link ContactSpecProblems}. */
2709
+ export declare const contactSpecProblems: ContactSpecProblems;
2710
+ //#endregion
2711
+ //#region src/forms/contact-issues.d.ts
2712
+ /** Issues with a path onto a PRESENT built-in become field errors (`title`→salutation,
2713
+ * `address.countryCode`→country, `address.streetAndHousenumber`→street,
2714
+ * `address.postalCode`→postalCode, `address.city`→city, the rest by name). Issues on fields the form
2715
+ * does not render, or without a path, join `submitError` (an invisible field error helps nobody).
2716
+ * Without issues: `sendFailed` with {email}, or `sendFailedNoEmail` when the site has no inbox.
2717
+ * `reason` is "rejected" when the SDK sent issues, else "failed". */
2718
+ type RouteContactIssues = (error: unknown, items: readonly ResolvedContactItem[], messages: ContactFormMessages, supportEmail: string | null) => RoutedContactIssues;
2719
+ /** What `routeContactIssues` returns. */
2720
+ interface RoutedContactIssues {
2721
+ /** Field name → the SDK's message, in list order. */
2722
+ readonly errors: Readonly<Record<string, string>>;
2723
+ /** The line above the button. */
2724
+ readonly submitError: string;
2725
+ /** "rejected": the SDK refused the input; "failed": transport or backend. */
2726
+ readonly reason: "rejected" | "failed";
1271
2727
  }
1272
- export declare const SORT_OPTIONS: readonly SortOption[];
1273
- /** The sort in the URL, or the default when absent/unknown. */
1274
- export declare function activeSort(search: string): SortValue;
1275
- /**
1276
- * Can this query be priced? Mirrors the SDK's own precondition: a fixed period
1277
- * (`start`+`end`) or a flexible window (`dates`/`month`/`weekend`), AND
1278
- * occupancy. Without both, the backend returns no totals and the SDK rejects
1279
- * price ordering before it reaches the wire.
1280
- *
1281
- * Period keys count only with a value: the search's `buildQuery` drops empty
1282
- * params (`?start=&end=`), so a key that is merely present would pass this
1283
- * gate and still reach the SDK undated. `weekend` is the exception — it is a
1284
- * presence-only flag on both sides.
1285
- */
1286
- export declare function isPricedQuery(search: string): boolean;
1287
- /** Is this option selectable for the current query? */
1288
- export declare function isSortAvailable(option: SortOption, search: string): boolean;
1289
- /**
1290
- * The `sort` to hand the SDK for this URL, or `undefined` for the backend's
1291
- * own order. Falls back to `undefined` (rather than throwing) when a price
1292
- * sort survives in the URL after the dates were cleared.
1293
- */
1294
- export declare function resolveSdkSort(search: string): SearchSearchSort | undefined;
2728
+ /** See {@link RouteContactIssues}. */
2729
+ export declare const routeContactIssues: RouteContactIssues;
1295
2730
  //#endregion
1296
- //#region src/filters.d.ts
1297
- /**
1298
- * The search filter model the UI consumes — client-safe, no SDK import. The SDK
1299
- * exposes filter definitions via `getFilters`; a site curates them at build time
1300
- * into `FilterDef[]` (which keys, in which order) and hands the list to the
1301
- * results surface. Boolean filters toggle, integer filters are a lower bound.
1302
- */
1303
- export type FilterType = "boolean" | "int";
1304
- export interface FilterDef {
1305
- /** The URL query key, which is also the SDK's search parameter key. */
1306
- key: string;
1307
- label: string;
1308
- type: FilterType;
1309
- /** Only meaningful for `int` filters. */
1310
- min: number;
1311
- max: number;
2731
+ //#region src/forms/contact-payload.d.ts
2732
+ /** The `live.contact.submit` input. The message is composed in LIST ORDER around the `message` field:
2733
+ * extra lines listed before it form a block above the free text, lines listed after it a block
2734
+ * below; lines join with "\n", blocks with "\n\n" (whoever reads it in the PMS reads the period and
2735
+ * the headcount first when a form lists them first). The phone is sent as typed: this endpoint does
2736
+ * not constrain it (the booking endpoint does, so the checkout normalizes). */
2737
+ type BuildContactInput = (items: readonly ResolvedContactItem[], values: ContactValues, context: {
2738
+ readonly locale: Locale;
2739
+ readonly subject: string;
2740
+ readonly messages: ContactFormMessages;
2741
+ }) => ContactSubmitInput;
2742
+ /** See {@link BuildContactInput}. */
2743
+ export declare const buildContactInput: BuildContactInput;
2744
+ //#endregion
2745
+ //#region src/forms/contact-prefill.d.ts
2746
+ /** `prefillParam` values from a query string (the UI passes `new URLSearchParams(location.search)`):
2747
+ * trimmed, capped at 200 characters; a number must read as one, a date must be an ISO day, a select
2748
+ * value must be one of its options. The UI applies them to EMPTY fields only (a visitor may have
2749
+ * typed before the island hydrated); the value stays editable. */
2750
+ type PrefillFromParams = (items: readonly ResolvedContactItem[], params: URLSearchParams) => ContactValues;
2751
+ /** See {@link PrefillFromParams}. */
2752
+ export declare const prefillFromParams: PrefillFromParams;
2753
+ //#endregion
2754
+ //#region src/forms/contact-validate.d.ts
2755
+ /** Required, email format, declared constraints, periods, and the composed message length (an error
2756
+ * on `message`, or on the first extra when the form lists no message field). Returns field name →
2757
+ * message, in list order; empty when the values can be sent. */
2758
+ type ValidateContactValues = (items: readonly ResolvedContactItem[], values: ContactValues, messages: ContactFormMessages, context: {
2759
+ readonly locale: Locale;
2760
+ readonly subject: string;
2761
+ readonly today: IsoDay;
2762
+ }) => Readonly<Record<string, string>>;
2763
+ /** See {@link ValidateContactValues}. */
2764
+ export declare const validateContactValues: ValidateContactValues;
2765
+ //#endregion
2766
+ //#region src/forms/events.d.ts
2767
+ /** The window CustomEvent name. Renaming it is a major. */
2768
+ export declare const CONTACT_EVENT: "voffice:contact";
2769
+ /** The event detail. Carries the channel (`subject`) and never a field value: no personal data
2770
+ * reaches a listener (a tracker, a consent-less script). */
2771
+ type ContactEventDetail = {
2772
+ readonly type: "sent";
2773
+ readonly subject: string;
2774
+ };
2775
+ //#endregion
2776
+ //#region src/legal/fetch.d.ts
2777
+ /** One legal document: the PMS's subject (the page's own heading) and its sanitized blocks. */
2778
+ interface LegalDocument {
2779
+ /** The document's subject, as the PMS names it. */
2780
+ readonly title: string;
2781
+ /** The document as sanitized blocks. */
2782
+ readonly blocks: readonly RichBlock[];
1312
2783
  }
1313
- export interface ActiveFilter {
1314
- key: string;
1315
- label: string;
2784
+ /** The two documents a checkout names; null = the workspace publishes none (or an empty one). */
2785
+ interface LegalDocuments {
2786
+ /** The terms (AGB), or null. */
2787
+ readonly terms: LegalDocument | null;
2788
+ /** The privacy notice, or null. */
2789
+ readonly privacy: LegalDocument | null;
1316
2790
  }
1317
- /** A boolean filter is active when its param is present and truthy. */
1318
- export declare const isBooleanFilterActive: (value: string) => boolean;
1319
- /**
1320
- * The filters currently applied in the query, in the order the defs are
1321
- * declared — the removable chips. An `int` label is prefixed with the localized
1322
- * "minimum" wording, because its value is a lower bound.
1323
- */
1324
- export declare function activeFiltersFromQuery(searchParams: URLSearchParams, defs: readonly FilterDef[], minimumPrefix: string): ActiveFilter[];
1325
- /** The dog-friendly search as the URL carries it: `pets=true` (or a count) and,
1326
- * from an older vocabulary, `petsCount=N`. Read by the live search. */
1327
- export declare const PETS_PARAM = "pets";
1328
- export declare const PETS_COUNT_PARAM = "petsCount";
1329
- export declare function petsCountInQuery(search: string): number;
1330
- //#endregion
1331
- //#region src/regions.d.ts
1332
- /**
1333
- * The "Region" scope of the live search.
1334
- *
1335
- * "Region" is a v10 custom attribute (MULTI_SELECT, PUBLIC) set per rental in
1336
- * v-office — e.g. Duhnen, Döse. The live search has no usable server-side key
1337
- * for it: the SDK filters a custom attribute only on an exact raw match of its
1338
- * stored JSON-array value, and registering the definition would fold a
1339
- * `Region: ["…"]` string into every card and detail highlight (verified). So
1340
- * `region` is dropped from the SDK query (search.ts, DROP_KEYS) and applied
1341
- * here, client-side, against the per-rental regions the site hydrates from its
1342
- * static catalogue at build time — the same channel as the other facts the
1343
- * search projection does not carry.
1344
- */
1345
- /** URL query key carrying the active region scope. Repeatable: several
1346
- * `region=<name>` values OR together (show rentals in ANY selected region). */
1347
- export declare const REGION_PARAM = "region";
1348
- /** The regions active in a URL query string: the `region` values, trimmed,
1349
- * de-duplicated, order preserved. Absent or empty → `[]`. */
1350
- export declare function activeRegions(search: string): string[];
1351
- /**
1352
- * The results inside the region scope. A rental may carry several regions
1353
- * (MULTI_SELECT) and matches when any of them is selected; with no active
1354
- * region every result stays. A rental the map does not know is outside every
1355
- * region, so it drops out of a scoped search rather than passing unseen.
1356
- */
1357
- export declare function scopeToRegions<T extends {
1358
- id: string;
1359
- }>(items: readonly T[], active: readonly string[], regionsByRentalId: (id: string) => readonly string[] | undefined): T[];
2791
+ /** v10: `static.documents.getTermsAndPrivacyPolicy` → `normalizeStructuredDocument` (a leading
2792
+ * headline that repeats the subject is dropped; the page draws its own). A document the workspace
2793
+ * does not publish, or whose data holds no content, is null. Throws on transport, logged with the
2794
+ * SDK's cause: a build never silently ships a checkout whose terms it could not read. v9: both null
2795
+ * without a call, until the SDK serves the hub's documents (CMS-1122). */
2796
+ type FetchLegalDocuments = (sdk: WebsiteSdk, locale: Locale) => Promise<LegalDocuments>;
2797
+ /** See {@link FetchLegalDocuments}. */
2798
+ export declare const fetchLegalDocuments: FetchLegalDocuments;
1360
2799
  //#endregion
1361
- export type { CheckoutFlow, CheckoutFlowActions, CheckoutFlowApi, CheckoutFlowMessages, CheckoutFlowOptions, CheckoutFlowState, CheckoutIssue, CheckoutPart, CheckoutPhase, CountryGroupsLike, InsuranceDocsState, Locale, Stay, StructureViolation, ValidationMessages };
2800
+ export { ADJACENT_BELOW_M, ALWAYS_EXCLUDED_CATEGORIES, type ActiveFilter, type AdaptEvent, type AdaptRentalOptions, type AlternativePeriod, type AmenityGroup, type AmenityPreviewItem, type AmenityPreviewOptions, type AvailabilityWindowInput, type Backend, type BadgeRule, type BedRun, type BookableFrom, type BookingConfirmation, type BookingContact, type BookingKind, type BookingSnapshot, type BuiltInFieldSpec, type BuiltInPolicyLabels, type CampaignChip, type CampaignChipsInput, type CampaignLabelSource, type CampaignOf, type CardBadge, type CardContext, type CardFact, type CardImage, type CardLiveField, type CardMatch, type CardModelOptions, type CardPill, type CardRating, type CardSignal, type CardSource, type CardSourceExtras, type CardStatus, type CatalogueRental, type CheckedCopy, type CheckoutActions, type CheckoutBlocker, type CheckoutContext, type CheckoutEventDetail, type CheckoutFlow, type CheckoutFlowOptions, type CheckoutHandover, type CheckoutHandoverOptions, type CheckoutInsuranceState, type CheckoutIssue, type CheckoutMessages, type CheckoutPart, type CheckoutPhase, type CheckoutPolicyState, type CheckoutRequirement, type CheckoutService, type CheckoutState, type CheckoutStructure, type CheckoutVoucherState, type ChildAgeRange, type ClusterOptions, type ContactControl, type ContactEventDetail, type ContactFieldName, type ContactFieldSpec, type ContactForm, type ContactFormActions, type ContactFormMessages, type ContactFormOptions, type ContactFormState, type ContactLink, type ContactPhase, type ContactSubmitOutcome, type ContactValue, type ContactValues, type CountryCode, type CountryGroups, type CountryOption, type CountryOptionsInput, DEFAULT_CHILD_AGE, DEFAULT_CLUSTER, DEFAULT_FIT, DEFAULT_FLAG_FILTERS, DEFAULT_GUEST_LIMITS, DEFAULT_SORT_OPTIONS, type DateControls, type DateMode, type DateSearch, type DateSearchOptions, type DateSearchState, type DayKind, type DayPrices, type DayState, type DefaultFit, type DescriptionContent, type DescriptionPreview, type DistanceKind, ENHANCE_PARTS, type EmptyCause, type EndAvailability, type EndAvailabilityInput, type EnhancePart, type EnhanceParts, type EnvRecord, type ExternalSearchLink, type ExternalSearchSource, type ExtraFieldSpec, FAVORITE_LEAVE_MS, type FacetOption, type FacetOptionsOptions, type FactKind, type Favorites, type FavoritesChange, type FavoritesOptions, type FavoritesPort, type FavoritesState, type FetchRentalFlagsOptions, type FieldWidth, type FilterDef, type FilterEntry, type FilterGroupSpec, type FilterMeasurement, type FilterSelection, type FilterSpec, type FindCampaignStartOptions, type FindOfferWindowsOptions, type FitOptions, type FlexibleStay, GATE_MARKERS, GLYPHS, type GateMarkers, type Glyph, type GlyphList, type GuestBound, type GuestBounds, type GuestCaps, type GuestForm, type GuestFormField, type GuestFormOptions, type GuestFormState, type GuestKind, type GuestLimits, type GuestOptions, type GuestSearch, type GuestSearchOptions, type GuestSearchState, HIDDEN_ROOM_ATTRIBUTES, HUB_ENDPOINTS, type HistoryPort, type HubEndpointTable, type HubEndpoints, type ImplicitScope, type InitialAvailability, type InsuranceDocsState, type InsurancePaymentLink, type IsoDay, type IsoMonth, type LegalDocument, type LegalDocuments, type Listener, type LiveSearchOptions, type LngLat, type LoadRentalCatalogueOptions, type Locale, type MapBounds, type MapPlace, type MapPoint, type MapResultsSource, type MapViewReport, type OfferWindow, type OffersWindowSpec, type OptionalGuestField, type OutcomeInput, type Party, type PaymentLine, type PaymentReturn, type PaymentScheduleView, type PeriodLabel, type PeriodValue, type PillOptions, type PinLabelOptions, type PixelOffset, type PlaceMarker, type PlainTextOptions, type PolicyBranch, type PolicyBranchOptions, type PolicyChoiceMode, type PolicyOffer, type PolicyOfferInput, type PolicyOption, type PolicyStance, type PolicyTerms, type PolicyTermsOptions, type PolicyTitle, type PreviewStrategy, type PriceCents, type PriceLine, type PriceView, type QueryPair, type QueryShape, type QuoteAddonService, type QuoteCancellationPolicy, type QuoteCta, type QuoteEventDetail, type QuoteFlow, type QuoteFlowOptions, type QuoteFlowSettings, type QuoteFlowState, type QuoteHandover, type QuoteInsurance, type QuoteInsuranceOption, type QuoteOccupancy, type QuoteOutcome, type QuotePolicyRule, type QuoteRequest, type QuoteResult, type QuoteSelectionResult, type QuoteUnavailableCode, type QuoteView, type QuoteVoucher, type Range, type RawSearchPage, type RelatedOptions, type Relaxation, type RentalAddress, type RentalAddressMode, RentalAddressModeError, type RentalCardModel, RentalContentError, type RentalCounts, type RentalDetail, type RentalDistance, type RentalFacts, type RentalFactsMap, type RentalFlag, type RentalFlagsResult, type RentalImage, type RentalIndexEntry, type RentalMatch, type RentalMatchOptions, type RentalPropertyInfo, type RentalReview, type RentalReviews, type RentalRoom, type ResolveFiltersOptions, type ResolvedContactField, type ResolvedContactItem, type ResolvedFilters, type ResolvedSearchGroup, type ResultHandoverOptions, type ResultLinkOptions, type ResultsMapController, type ResultsMapOptions, type ResultsMapState, type ResultsMapSurface, type ReviewCriterion, type ReviewSummary, type RichBlock, type RichInline, type RoomKind, type RotationOptions, type RouteParams, type RouteTemplate, type RoutedContactIssues, SDK_ENV, SEARCH_PAGE_SIZE, SEARCH_PARAMS, SORT_URL_VALUE, STAGES, type Salutation, type SameSpotGroup, type SameSpotLayout, type SameSpotMode, type SameSpotOptions, type SanitizedSearch, type SdkConfig, type SdkConfigForInput, SdkEnvError, type SdkFilterDefinition, type SdkMode, type SdkOptions, type SdkQueryOptions, type SdkSearchQuery, type SdkSetup, type SdkSource, type SearchActions, type SearchContract, type SearchField, type SearchForm, type SearchFormIssue, type SearchFormOptions, type SearchFormState, type SearchGroupSpec, type SearchOutcome, type SearchPage, type SearchPageRequest, type SearchParamAliases, type SearchParamKeys, type SearchPeriod, type SearchQuery, type SearchResultItem, type SearchScope, type SearchScopeKind, type SearchState, type SearchStatus, type SearchStore, type SearchStoreClaim, type SearchStoreOptions, type SearchedParts, type SelectOption, type Selection, type SelectionSpec, type ShareLocation, type ShareUrlOptions, type SharedSdk, type ShowcaseGroup, type ShowcaseGroupSpec, type ShowcaseOrder, type SnapshotImage, type SortKey, type SortOptionState, type Stage, type Stay, type StayPicker, type StayPickerOptions, type StayPickerState, type StayPrice, type StayWindow, type StorageLike, type Store, type StructureRequirement, type StructureViolation, type StructuredDocumentOptions, type SubmitBookingOptions, type SubmitWording, type Template, type TextMarkup, type TokensOf, type Unsubscribe, VOFFICE_ENDPOINTS, type ValidationMessages, type ValueConstraints, type VofficeEndpointTable, type VofficeEndpoints, WATER_KINDS, type WalkOptions, type WalkSummary, type WebsiteSdk, type WhenEmpty, type WhenUnavailable, type WritableStore, type WriteSearchOptions, activeFilters, adaptRental, addDays, agesComplete, amenityPreview, assignSlugs, campaignChips, campaignGroup, cardSource, catalogueResultItem, circleOffsets, clampParty, compareUndatedSearchOrder, createResultsMapController, createSdk, createSearchStore, createStore, describeOutcome, descriptionPreview, descriptionSummary, distanceFact, distanceMeters, externalSearchHref, facetOptions, fetchRentalFlags, fetchSearchPage, findCampaignStart, findOfferWindows, formatIsoDate, formatPriceLabel, frameFor, guestBounds, handoverQuery, hasMapPoints, idsInView, isAdjacent, isIsoDay, isSpecEcho, layoutSameSpot, loadRentalCatalogue, loadRentalFacts, loadSharedSdk, mapPoints, matches, measureFilters, mergePlaces, minimumSteps, newestReviewsFirst, nightsBetween, normalizeStructuredDocument, occupancyOf, offerWindowHref, offersWindowQuery, parseCounts, parseDistances, parsePriceLabel, parseTextBlocks, partyPairs, plainText, queryShape, quickFilterDefs, rankHighlights, readParty, readSearch, readSelection, rentalAddressLine, rentalFactsMap, resolveFilters, resolveSdk, resultCardSource, resultsQueryFlagScript, routeHref, sanitizeSearch, sdkConfigFor, sdkConfigFromEnv, searchHref, shareSdk, shortReviewerName, slugify, sortFromSearch, sortResults, splitReviewAuthor, stayKeyOf, toCardModel, toSdkQuery, toSdkSort, todayIso, walkSearchPages, withRentalFacts, withoutFilter, withoutFilters, writeSearch, writeSelection };