@v-office/website-headless 0.1.1 → 1.0.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/README.md +2177 -54
- package/dist/enhance/index.d.mts +501 -180
- package/dist/enhance/index.mjs +1388 -472
- package/dist/index-CPMXjfFH.d.mts +2724 -0
- package/dist/index.d.mts +2602 -1165
- package/dist/index.mjs +8568 -1735
- package/dist/parts-CS1VBrPt.mjs +648 -0
- package/dist/sdk-claim-CsYFzvEc.mjs +72 -0
- package/dist/sdk-create-Bsd9lqj_.mjs +9 -0
- package/instructions/CHANGELOG.md +9 -0
- package/instructions/MIGRATION.md +9 -0
- package/instructions/versions/0.1.1/CHANGELOG.md +7 -0
- package/instructions/versions/0.1.1/MIGRATION.md +4 -0
- package/instructions/versions/0.1.1/release.json +3 -0
- package/instructions/versions/0.2.0/CHANGELOG.md +17 -0
- package/instructions/versions/0.2.0/MIGRATION.md +16 -0
- package/instructions/versions/0.2.0/release.json +3 -0
- package/instructions/versions/1.0.0/CHANGELOG.md +170 -0
- package/instructions/versions/1.0.0/MIGRATION.md +252 -0
- package/instructions/versions/1.0.0/release.json +10 -0
- package/package.json +14 -11
package/dist/index.d.mts
CHANGED
|
@@ -1,338 +1,1143 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
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
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
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
|
-
type
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
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
|
-
|
|
18
|
-
|
|
19
|
-
|
|
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
|
-
|
|
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/
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
/**
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
/**
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
type
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
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
|
-
/**
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
}
|
|
64
|
-
|
|
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
|
-
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
|
-
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
|
-
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/
|
|
103
|
-
/** One
|
|
104
|
-
*
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
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
|
+
price: string | null;
|
|
823
|
+
/** The SDK's localized block reason; only on a non-selectable day outside the chosen range (a
|
|
824
|
+
* departure endpoint re-judged as "no arrival" must not flash its tooltip). */
|
|
825
|
+
reason: string | null;
|
|
826
|
+
/** Position in the chosen range. */
|
|
827
|
+
selection: "start" | "end" | "inside" | null;
|
|
828
|
+
}
|
|
829
|
+
/** The arrival map of one rental over a window. */
|
|
830
|
+
interface InitialAvailability {
|
|
831
|
+
/** Days the SDK flags as a possible check-in. */
|
|
832
|
+
canStart: ReadonlySet<IsoDay>;
|
|
833
|
+
/** Days with a hard status: genuinely not bookable (struck), as opposed to rule-blocked (dimmed). */
|
|
834
|
+
hardBlocked: ReadonlySet<IsoDay>;
|
|
835
|
+
/** The SDK's raw `formattedPrice` by day (already localized, in the rental's booking currency). */
|
|
836
|
+
priceByDay: ReadonlyMap<IsoDay, string>;
|
|
837
|
+
/** The SDK's localized `tooltip` by day (a block reason, or a stay-length hint). */
|
|
838
|
+
tooltipByDay: ReadonlyMap<IsoDay, string>;
|
|
839
|
+
/** Persons cap (adults + children) the SDK states; null when it states none (v9 before SDK 2.28). */
|
|
840
|
+
maxPersons: number | null;
|
|
841
|
+
/** Adults cap the SDK states; null when it states none (v9 before SDK 2.28). */
|
|
842
|
+
maxAdults: number | null;
|
|
843
|
+
/** false when the rental takes no children, babies included; null states no rule (v10, v9 before
|
|
844
|
+
* SDK 2.28). */
|
|
845
|
+
childrenAllowed: boolean | null;
|
|
846
|
+
/** The most dogs the quote accepts, 0 for none; null states no limit (v10, v9 before SDK 2.28). */
|
|
847
|
+
maxPets: number | null;
|
|
848
|
+
}
|
|
849
|
+
/** The departure map for one chosen start. */
|
|
850
|
+
interface EndAvailability {
|
|
851
|
+
/** The start this map answers for. */
|
|
852
|
+
start: IsoDay;
|
|
853
|
+
/** Days the SDK flags as a possible check-out (its minimum stay and check-out rules applied). */
|
|
854
|
+
canEnd: ReadonlySet<IsoDay>;
|
|
855
|
+
/** Days with a hard status for this start (booked, or a range crossing a booked night). */
|
|
856
|
+
hardBlocked: ReadonlySet<IsoDay>;
|
|
857
|
+
/** The SDK's localized `tooltip` by day (minimum stay not met, no check-out, …). */
|
|
858
|
+
tooltipByDay: ReadonlyMap<IsoDay, string>;
|
|
859
|
+
}
|
|
860
|
+
/** What both fetchers take: the rental and the inclusive window the SDK computes over. */
|
|
861
|
+
interface AvailabilityWindowInput {
|
|
862
|
+
/** PMS rental id (numeric unit id on v9). */
|
|
863
|
+
rentalId: string;
|
|
864
|
+
/** First day of the window. */
|
|
865
|
+
from: IsoDay;
|
|
866
|
+
/** Last day of the window. */
|
|
867
|
+
through: IsoDay;
|
|
868
|
+
/** Language of the SDK's tooltips. */
|
|
869
|
+
locale: Locale;
|
|
870
|
+
}
|
|
871
|
+
/** The departure-map input: the window plus the chosen start. */
|
|
872
|
+
interface EndAvailabilityInput extends AvailabilityWindowInput {
|
|
873
|
+
/** The chosen check-in day. */
|
|
874
|
+
start: IsoDay;
|
|
875
|
+
}
|
|
876
|
+
/** How calendar CELLS state the nightly price; never a total.
|
|
877
|
+
* - "rounded": whole units, half up (`formatPriceLabel(·, "round")`) — truncation would understate every
|
|
878
|
+
* night, always in the direction that flatters the price.
|
|
879
|
+
* - "exact": the SDK's figure with only a zero-cents group dropped ("120,00 €" → "120 €").
|
|
880
|
+
* - "none": no figure in the cells (customer rulings on several sites). */
|
|
881
|
+
type DayPrices = "rounded" | "exact" | "none";
|
|
882
|
+
/** Possible check-in days, prices and caps for a rental (`availability.getInitialAvailability`). */
|
|
883
|
+
type FetchInitialAvailability = (sdk: WebsiteSdk, input: AvailabilityWindowInput) => Promise<InitialAvailability>;
|
|
884
|
+
/** Possible check-out days for one start (`availability.getStartDateSelectedAvailability`). */
|
|
885
|
+
type FetchEndAvailability = (sdk: WebsiteSdk, input: EndAvailabilityInput) => Promise<EndAvailability>;
|
|
886
|
+
/** The cell text for one SDK price string, or null for "none". A shape the price reader does not
|
|
887
|
+
* recognise is returned unchanged (a display tidy never turns a price into a wrong one). */
|
|
888
|
+
type DayPriceLabel = (formatted: string, mode: DayPrices) => string | null;
|
|
889
|
+
/** See {@link FetchInitialAvailability}. */
|
|
890
|
+
export declare const fetchInitialAvailability: FetchInitialAvailability;
|
|
891
|
+
/** See {@link FetchEndAvailability}. */
|
|
892
|
+
export declare const fetchEndAvailability: FetchEndAvailability;
|
|
893
|
+
/** See {@link DayPriceLabel}. */
|
|
894
|
+
export declare const dayPriceLabel: DayPriceLabel;
|
|
895
|
+
//#endregion
|
|
896
|
+
//#region src/quote/stay-picker.d.ts
|
|
897
|
+
/** What the picker needs to know about the rental and the site. */
|
|
898
|
+
interface StayPickerOptions {
|
|
899
|
+
/** PMS rental id. */
|
|
900
|
+
rentalId: string;
|
|
901
|
+
/** Language of the SDK's tooltips. */
|
|
902
|
+
locale: Locale;
|
|
903
|
+
/** Navigable months from the current one. Availability is fetched one month further, so the last
|
|
904
|
+
* month's departures exist. Default 12. */
|
|
905
|
+
horizonMonths?: number;
|
|
906
|
+
/** The site's minimum stay in NIGHTS on top of the PMS rules: a departure closer to the start is
|
|
907
|
+
* "restricted". A higher PMS minimum still wins (the SDK's `canEnd` already encodes it). */
|
|
908
|
+
minStayNights?: number;
|
|
909
|
+
/** Clock, for tests. Default `() => new Date()`. */
|
|
910
|
+
now?: () => Date;
|
|
911
|
+
}
|
|
912
|
+
/** The picker's state; serializable apart from nothing — every value is a string, number or null. */
|
|
913
|
+
interface StayPickerState {
|
|
914
|
+
/** The arrival map. "error": the calendar could not load (never rendered as a full calendar). */
|
|
915
|
+
availability: "loading" | "ready" | "error";
|
|
916
|
+
/** First and last navigable days (the fetch reaches one month past `through`). */
|
|
917
|
+
window: {
|
|
918
|
+
from: IsoDay;
|
|
919
|
+
through: IsoDay;
|
|
920
|
+
};
|
|
921
|
+
/** The chosen stay so far. */
|
|
922
|
+
range: {
|
|
923
|
+
start: IsoDay | null;
|
|
924
|
+
end: IsoDay | null;
|
|
925
|
+
};
|
|
926
|
+
/** arrival: no start · departure: a start, no end · complete: both. */
|
|
927
|
+
phase: "arrival" | "departure" | "complete";
|
|
928
|
+
/** The departure map for `range.start`. While "loading" only the start is selectable; a failed load
|
|
929
|
+
* is logged and becomes an empty map (no departure), never a silent free-for-all. */
|
|
930
|
+
departures: "idle" | "loading" | "ready";
|
|
931
|
+
/** Occupancy caps from the arrival map; null until it lands or when the SDK states none. */
|
|
932
|
+
caps: Pick<InitialAvailability, "maxPersons" | "maxAdults" | "childrenAllowed" | "maxPets">;
|
|
933
|
+
/** First arrivable day in the window, or null: a sheet scrolls there, AvailabilityCalendar opens there. */
|
|
934
|
+
firstArrival: IsoDay | null;
|
|
935
|
+
/** Nights of a complete range, else null. */
|
|
936
|
+
nights: number | null;
|
|
937
|
+
/** Bumped on every map change, so a memoized calendar knows to re-read `day()`. */
|
|
938
|
+
revision: number;
|
|
939
|
+
}
|
|
940
|
+
/** The picker: a store plus the day model and the picking actions. */
|
|
941
|
+
interface StayPicker extends Store<StayPickerState> {
|
|
942
|
+
/** The render model of one day in the current phase. Pure over the state and the maps. */
|
|
943
|
+
day(iso: IsoDay): DayState;
|
|
944
|
+
/** A click: sets the start (and fetches its departures), completes the range, deselects the start
|
|
945
|
+
* when clicked again, or — on a complete range — starts a new one (the third click). A
|
|
946
|
+
* non-selectable day is ignored. */
|
|
947
|
+
select(iso: IsoDay): void;
|
|
948
|
+
/** Programmatic range (handover, checkout editor, next arrival). Re-validated against the arrival
|
|
949
|
+
* map once it is in: an arrival the PMS refuses clears the range rather than pricing it; a range
|
|
950
|
+
* shorter than `minStayNights` keeps only its start. */
|
|
951
|
+
setRange(start: IsoDay, end: IsoDay | null): void;
|
|
952
|
+
/** Back to the arrival phase, nothing chosen. */
|
|
953
|
+
reset(): void;
|
|
954
|
+
/** Replace the site's minimum stay (absent = none); the range is re-validated against it. */
|
|
955
|
+
configure(options: Pick<StayPickerOptions, "minStayNights">): void;
|
|
956
|
+
/** Fetch the arrival map. Idempotent; nothing is fetched before it is called. */
|
|
957
|
+
start(): void;
|
|
958
|
+
/** Stop: late answers are dropped. */
|
|
959
|
+
dispose(): void;
|
|
960
|
+
}
|
|
961
|
+
/** A picker over the caller's SDK, resolved once on the first request (again only after a failed
|
|
962
|
+
* resolve). Requests: one arrival map per picker, one departure map per chosen start (the SDK queues
|
|
963
|
+
* and caches them per rental). */
|
|
964
|
+
type CreateStayPicker = (sdk: SdkSource, options: StayPickerOptions) => StayPicker;
|
|
965
|
+
/** See {@link CreateStayPicker}. */
|
|
966
|
+
export declare const createStayPicker: CreateStayPicker;
|
|
967
|
+
//#endregion
|
|
968
|
+
//#region src/quote/adapter.d.ts
|
|
969
|
+
/** One row of the price breakdown. Strings are the SDK's, verbatim. */
|
|
970
|
+
interface PriceLine {
|
|
971
|
+
/** charge: a card line · sub: its sub-item (indented) · discount: a PMS modifier row under the line
|
|
972
|
+
* it adjusts (a deduction, rarely a surcharge). */
|
|
973
|
+
kind: "charge" | "sub" | "discount";
|
|
974
|
+
/** The SDK's `position` (or the modifier's label). */
|
|
975
|
+
label: string;
|
|
976
|
+
/** The figure of the default ("rows") display: the line's `totalBeforeModifiers` when it carries
|
|
977
|
+
* modifiers, so gross − deduction rows add up to the total; else `appliedCharge`. On a discount row
|
|
978
|
+
* the modifier's signed amount. */
|
|
113
979
|
amount: string;
|
|
114
|
-
|
|
115
|
-
discount
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
/** SDK `bookingCardInformation.total`, verbatim (already locale-formatted). */
|
|
980
|
+
/** `appliedCharge` (net of its modifiers); equal to `amount` on a line without modifiers. The
|
|
981
|
+
* "struck-total" display annotates these net lines with the discount rows. */
|
|
982
|
+
netAmount: string;
|
|
983
|
+
}
|
|
984
|
+
/** The price card of one quote. */
|
|
985
|
+
interface PriceView {
|
|
986
|
+
/** `bookingCardInformation.total`, verbatim. */
|
|
122
987
|
total: string;
|
|
988
|
+
/** The PMS's own pre-modifier card total; null without modifiers (v9 always). The only struck price
|
|
989
|
+
* the package ever prints, and only where a site chose to. */
|
|
990
|
+
totalBeforeModifiers: string | null;
|
|
991
|
+
/** The breakdown in SDK order; sub and discount rows follow the charge they belong to. */
|
|
992
|
+
lines: PriceLine[];
|
|
993
|
+
/** Distinct labels of the deductions in first-seen order (the PMS's words, e.g. a campaign name).
|
|
994
|
+
* Surcharges are not offers and are left out. */
|
|
995
|
+
offerLabels: string[];
|
|
123
996
|
}
|
|
124
|
-
/**
|
|
997
|
+
/** A bookable add-on from `quote.additionalServices`, verbatim fields. */
|
|
125
998
|
interface QuoteAddonService {
|
|
999
|
+
/** The service id the selection calls take. */
|
|
126
1000
|
id: string;
|
|
1001
|
+
/** The PMS's name, verbatim. */
|
|
127
1002
|
label: string;
|
|
128
|
-
/** SDK charge string as-is (
|
|
1003
|
+
/** The SDK's charge string as-is ("25,00 €"). */
|
|
129
1004
|
charge: string;
|
|
1005
|
+
/** The PMS's description, verbatim. */
|
|
130
1006
|
description?: string | undefined;
|
|
131
1007
|
/** Max selectable per booking; absent = the SDK states no cap. */
|
|
132
1008
|
maxPerBooking?: number | undefined;
|
|
133
|
-
/**
|
|
134
|
-
*
|
|
135
|
-
*
|
|
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. */
|
|
1009
|
+
/** The quantity the PMS PROPOSES (`0` included) — a proposal, never a selection: a fresh quote
|
|
1010
|
+
* carries it as selected only when the SDK option `shouldAddProposedAdditionalServiceAmount` is on,
|
|
1011
|
+
* and the quote does not say which. Code that needs a known start clears and steps up from zero. */
|
|
154
1012
|
proposedQuantity: number;
|
|
155
|
-
/** False only when the PMS
|
|
156
|
-
* current multi-rate combination (SDK `available: false`). Absent → true.
|
|
157
|
-
* The UI disables adding it rather than letting the click silently no-op. */
|
|
1013
|
+
/** False only when the PMS marks the service unbookable for the current combination. */
|
|
158
1014
|
available: boolean;
|
|
159
|
-
/** PMS's
|
|
160
|
-
* the feed omits it. */
|
|
1015
|
+
/** The PMS's reason it is unavailable, verbatim; absent when the feed states none. */
|
|
161
1016
|
unavailableReason?: string | undefined;
|
|
1017
|
+
/** The service's pictures from the feed (alt verbatim). */
|
|
1018
|
+
images?: readonly CardImage[] | undefined;
|
|
162
1019
|
}
|
|
1020
|
+
/** One refund rule of a cancellation policy. */
|
|
163
1021
|
interface QuotePolicyRule {
|
|
1022
|
+
/** Cancel at least this many days before arrival … */
|
|
164
1023
|
daysBeforeArrival: number;
|
|
165
|
-
/** SDK's formatted percentage
|
|
1024
|
+
/** … and get this back: the SDK's formatted percentage ("100 %"), verbatim. */
|
|
166
1025
|
refundPercentage: string;
|
|
167
1026
|
}
|
|
168
|
-
/**
|
|
1027
|
+
/** A selectable cancellation policy from `quote.cancellationPolicies`. */
|
|
169
1028
|
interface QuoteCancellationPolicy {
|
|
1029
|
+
/** The policy id `selectQuotePolicy` and `?policy=` take. */
|
|
170
1030
|
id: string;
|
|
1031
|
+
/** The host's name, or one of the PMS's built-in names (see `isBuiltInPolicy`). */
|
|
171
1032
|
label: string;
|
|
1033
|
+
/** The rate this quote is priced under. */
|
|
172
1034
|
selected: boolean;
|
|
1035
|
+
/** The PMS's prose, verbatim (often an echo of the label; see `policyTerms`). */
|
|
173
1036
|
description?: string | undefined;
|
|
1037
|
+
/** Present on alternative rates ("-20%"); the base rate carries none. */
|
|
174
1038
|
markUpPercentage?: string | undefined;
|
|
1039
|
+
/** A separate charge for cancelling, formatted by the SDK. */
|
|
175
1040
|
processingFee?: string | undefined;
|
|
1041
|
+
/** The refund ladder; empty on the SDK's minimal policy. */
|
|
176
1042
|
rules: QuotePolicyRule[];
|
|
177
|
-
/** False only when the PMS
|
|
178
|
-
* current multi-rate combination (SDK `available: false`). Absent → true. */
|
|
1043
|
+
/** False only when the PMS marks the policy unselectable for the current combination. */
|
|
179
1044
|
available: boolean;
|
|
180
|
-
/** PMS's
|
|
181
|
-
* the feed omits it. */
|
|
1045
|
+
/** The PMS's reason for `available: false`, verbatim. */
|
|
182
1046
|
unavailableReason?: string | undefined;
|
|
183
1047
|
}
|
|
184
|
-
/**
|
|
185
|
-
* the SDK authoritatively reports whether the code applied a modifier; v9
|
|
186
|
-
* reports `"unknown"`. `message` is the PMS's own note, verbatim. */
|
|
1048
|
+
/** The voucher status echoed on a re-priced quote (v10 authoritative, v9 "unknown"). */
|
|
187
1049
|
interface QuoteVoucher {
|
|
1050
|
+
/** The code as submitted. */
|
|
188
1051
|
code: string;
|
|
1052
|
+
/** v10 says whether a voucher modifier applied; v9 always answers "unknown". */
|
|
189
1053
|
status: "applied" | "not_applied" | "unknown";
|
|
1054
|
+
/** The PMS's own note, verbatim. */
|
|
190
1055
|
message?: string | undefined;
|
|
191
1056
|
}
|
|
192
|
-
/** One selectable travel-insurance product
|
|
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`. */
|
|
1057
|
+
/** One selectable travel-insurance product (`quote.insurance.options`, kind "insurance"). */
|
|
197
1058
|
interface QuoteInsuranceOption {
|
|
1059
|
+
/** The plan id `selectQuoteInsurance` takes. */
|
|
198
1060
|
id: string;
|
|
1061
|
+
/** The product name; the identity the checkout keys documents on. */
|
|
199
1062
|
label: string;
|
|
200
|
-
/** SDK's
|
|
201
|
-
* amount on the public shape — the SDK keeps that internally — so a premium
|
|
202
|
-
* can be DISPLAYED but never added to another price. */
|
|
1063
|
+
/** The SDK's formatted premium; displayable, never added to another price. */
|
|
203
1064
|
formattedPrice: string;
|
|
1065
|
+
/** The insurer's description, verbatim. */
|
|
204
1066
|
description?: string | undefined;
|
|
205
|
-
/**
|
|
206
|
-
* not render it; a site that wants the insurer's mark has it here. */
|
|
1067
|
+
/** The insurer's logo, when the feed has one. */
|
|
207
1068
|
logoUrl?: string | undefined;
|
|
208
|
-
/** Plan feature names, verbatim
|
|
1069
|
+
/** Plan feature names, verbatim. */
|
|
209
1070
|
features?: string[] | undefined;
|
|
210
|
-
/** Product documents (IPID,
|
|
1071
|
+
/** Product documents (IPID, terms), label + URL verbatim. */
|
|
211
1072
|
resources?: {
|
|
212
1073
|
label: string;
|
|
213
1074
|
url: string;
|
|
214
1075
|
}[] | undefined;
|
|
215
|
-
/** True when the product needs per-traveler data
|
|
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. */
|
|
1076
|
+
/** True when the product needs per-traveler data first (not completable by the checkout). */
|
|
221
1077
|
requiresTravelers: boolean;
|
|
222
1078
|
}
|
|
223
|
-
/**
|
|
224
|
-
*
|
|
225
|
-
*
|
|
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. */
|
|
1079
|
+
/** The travel-insurance choice of a live quote. Absent from the view when the feed offers no real
|
|
1080
|
+
* product (v10's none-only placeholder): every insurance surface then self-hides. The premium is NOT
|
|
1081
|
+
* part of the card total. */
|
|
231
1082
|
interface QuoteInsurance {
|
|
232
1083
|
/** The SDK's own "no insurance" label, verbatim. */
|
|
233
1084
|
noneLabel: string;
|
|
1085
|
+
/** The products the feed offers, verbatim. */
|
|
234
1086
|
options: QuoteInsuranceOption[];
|
|
235
1087
|
/** Selected product id, or null = no insurance (the default). */
|
|
236
1088
|
selectedId: string | null;
|
|
237
1089
|
}
|
|
238
|
-
/** The renderable view of a live GuestQuote
|
|
1090
|
+
/** The renderable view of a live GuestQuote. */
|
|
239
1091
|
interface QuoteView {
|
|
240
|
-
card
|
|
1092
|
+
/** The price card. */
|
|
1093
|
+
price: PriceView;
|
|
1094
|
+
/** Bookable add-ons, in SDK order. */
|
|
241
1095
|
services: QuoteAddonService[];
|
|
1096
|
+
/** Cancellation policies, in SDK order, unfiltered (`offeredPolicies` decides what is offered). */
|
|
242
1097
|
policies: QuoteCancellationPolicy[];
|
|
243
|
-
/** Present once a code
|
|
244
|
-
* a fresh quote. */
|
|
1098
|
+
/** Present once a code was submitted and the quote re-priced. */
|
|
245
1099
|
voucher?: QuoteVoucher | undefined;
|
|
246
|
-
/**
|
|
1100
|
+
/** Present only when the feed offers a real product; the quote surface never renders it (the
|
|
1101
|
+
* checkout owns the § 7 VVG gate). */
|
|
247
1102
|
insurance?: QuoteInsurance | undefined;
|
|
248
1103
|
}
|
|
249
|
-
|
|
1104
|
+
/** Why the SDK refused to price a stay — the ONLY thing callers may branch on (`reason` is localized
|
|
1105
|
+
* and carries the same text for different codes). Derived from the SDK's result, never re-listed. */
|
|
1106
|
+
type QuoteUnavailableCode = Extract<GuestQuoteResult, {
|
|
1107
|
+
status: "unavailable";
|
|
1108
|
+
}>["code"];
|
|
1109
|
+
/** The answer for one stay. */
|
|
1110
|
+
type QuoteResult =
|
|
1111
|
+
/** Priced: the live quote (required by `booking.book`, in memory only) and its view. */
|
|
1112
|
+
{
|
|
250
1113
|
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
1114
|
live: GuestQuote;
|
|
256
|
-
/** Add-ons + policies for the checkout's optional sections. */
|
|
257
1115
|
view: QuoteView;
|
|
258
|
-
} |
|
|
1116
|
+
} |
|
|
1117
|
+
/** Refused: the SDK's localized reason and its machine code. */
|
|
1118
|
+
{
|
|
259
1119
|
status: "unavailable";
|
|
260
1120
|
reason: string;
|
|
261
1121
|
code: QuoteUnavailableCode;
|
|
262
1122
|
};
|
|
263
|
-
/**
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
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
|
-
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
|
-
declare function isRateConfigurationFault(code: QuoteUnavailableCode | undefined): boolean;
|
|
298
|
-
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
|
-
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
|
-
declare function fetchQuote(rentalId: string, period: {
|
|
333
|
-
start: string;
|
|
334
|
-
end: string;
|
|
335
|
-
}, occupancy: QuoteOccupancy, sdk: WebsiteSdk, locale: Locale, voucher?: string): Promise<QuoteResult>;
|
|
1123
|
+
/** One stay to price. */
|
|
1124
|
+
interface QuoteRequest {
|
|
1125
|
+
/** PMS rental id (numeric unit id on v9). */
|
|
1126
|
+
rentalId: string;
|
|
1127
|
+
/** Arrival and departure days. */
|
|
1128
|
+
period: {
|
|
1129
|
+
start: IsoDay;
|
|
1130
|
+
end: IsoDay;
|
|
1131
|
+
};
|
|
1132
|
+
/** The party as the SDK takes it (`occupancyOf`). */
|
|
1133
|
+
occupancy: QuoteOccupancy;
|
|
1134
|
+
/** Language of the PMS strings and formats. */
|
|
1135
|
+
locale: Locale;
|
|
1136
|
+
/** Re-price with a code; the PMS decides what it is worth. */
|
|
1137
|
+
voucher?: string | undefined;
|
|
1138
|
+
}
|
|
1139
|
+
/** A selection call's answer: a NEW quote, or a refusal with the PMS's reason when it gave one (only
|
|
1140
|
+
* `UnavailableQuoteCombination` does). Callers keep the previous quote on a refusal. */
|
|
336
1141
|
type QuoteSelectionResult = {
|
|
337
1142
|
ok: true;
|
|
338
1143
|
quote: GuestQuote;
|
|
@@ -340,1018 +1145,1650 @@ type QuoteSelectionResult = {
|
|
|
340
1145
|
ok: false;
|
|
341
1146
|
reason?: string | undefined;
|
|
342
1147
|
};
|
|
343
|
-
|
|
344
|
-
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
|
-
declare function clearQuoteServices(quote: GuestQuote, sdk: WebsiteSdk): Promise<QuoteSelectionResult>;
|
|
355
|
-
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
|
-
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
|
-
declare function fullRefundRule(policy: QuoteCancellationPolicy | undefined): QuotePolicyRule | undefined;
|
|
384
|
-
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
|
-
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. */
|
|
1148
|
+
/** The same stay priced under ONE cancellation policy (the base itself for the selected one). */
|
|
392
1149
|
interface PolicyBranch {
|
|
1150
|
+
/** The policy this branch is priced under. */
|
|
393
1151
|
id: string;
|
|
394
|
-
/** The branch quote
|
|
1152
|
+
/** The branch quote (the base itself for the pre-selected policy); it can be booked as is. */
|
|
395
1153
|
quote: GuestQuote;
|
|
396
|
-
/** That quote flattened
|
|
1154
|
+
/** That quote flattened: `view.price.total` is THIS policy's real price. */
|
|
397
1155
|
view: QuoteView;
|
|
398
1156
|
}
|
|
399
|
-
/**
|
|
400
|
-
*
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
*
|
|
404
|
-
*
|
|
405
|
-
*
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
1157
|
+
/** The two codes that mean "the stay WAS priced, but the rate setup cannot be resolved to the one
|
|
1158
|
+
* multi-rate a booking is written against" — a PMS configuration fault, never "not available". */
|
|
1159
|
+
export declare const MULTI_RATE_CODES: readonly ["AmbiguousMultiRate", "UnresolvableMultiRate"];
|
|
1160
|
+
/** Flatten a GuestQuote; values verbatim. Each modifier becomes its own row and the line above it
|
|
1161
|
+
* switches to `totalBeforeModifiers`, so the column adds up (1.694,00 − 140,00 + 42,70 = 1.596,70).
|
|
1162
|
+
* Consecutive identical SUB lines whose amount states no figure ("Inklusive", multi-season stays)
|
|
1163
|
+
* collapse into one; a line with a figure never collapses. */
|
|
1164
|
+
type AdaptGuestQuote = (quote: GuestQuote) => QuoteView;
|
|
1165
|
+
/** Price a stay (`quote.quote`). The caller owns the SDK; a throw is the caller's to log. */
|
|
1166
|
+
type FetchQuote = (sdk: WebsiteSdk, request: QuoteRequest) => Promise<QuoteResult>;
|
|
1167
|
+
/** A selection call on a live quote (add, remove, select); a throw is logged and becomes `ok: false`. */
|
|
1168
|
+
type SelectOnQuote = (sdk: WebsiteSdk, quote: GuestQuote, id: string) => Promise<QuoteSelectionResult>;
|
|
1169
|
+
/** Drop every service selection — the known starting point (see `proposedQuantity`). */
|
|
1170
|
+
type ClearQuoteServices = (sdk: WebsiteSdk, quote: GuestQuote) => Promise<QuoteSelectionResult>;
|
|
1171
|
+
/** Select an insurance product, or none (`null`). The SDK throws on failure; normalized here. */
|
|
1172
|
+
type SelectQuoteInsurance = (sdk: WebsiteSdk, quote: GuestQuote, id: string | null) => Promise<QuoteSelectionResult>;
|
|
1173
|
+
/** Which rates `pricePolicyBranches` prices. */
|
|
1174
|
+
interface PolicyBranchOptions {
|
|
1175
|
+
/** Only these ids (the rates `offeredPolicies` still offers for the arrival); default every one.
|
|
1176
|
+
* A withdrawn rate is never shown, so pricing it would only queue behind the 1 req/s limit. */
|
|
1177
|
+
only?: readonly string[];
|
|
1178
|
+
}
|
|
1179
|
+
/** Every selectable policy priced as its own quote off one base — sequential (the API allows 1 req/s),
|
|
1180
|
+
* skipping `available: false` and ids outside `only`; the selected one is the base itself; a refused
|
|
1181
|
+
* branch is omitted (a row without a price, never another rate's total). Real branches, because the
|
|
1182
|
+
* markup applies to the rent and not to the total. */
|
|
1183
|
+
type PricePolicyBranches = (sdk: WebsiteSdk, base: GuestQuote, options?: PolicyBranchOptions) => Promise<PolicyBranch[]>;
|
|
1184
|
+
/** True for a rate-configuration fault; the UI branches the MESSAGE on it and keeps booking disabled. */
|
|
1185
|
+
type IsRateConfigurationFault = (code: QuoteUnavailableCode | undefined) => boolean;
|
|
1186
|
+
/** Pin the presentation order of the breakdown across re-quotes: every selection returns a new quote
|
|
1187
|
+
* whose lines may come back reordered, and rows that jump read as flakiness. Each block (a charge with
|
|
1188
|
+
* its sub and discount rows) keeps the position it had when first seen; new blocks append in SDK
|
|
1189
|
+
* order. `orderMap` is the caller's memory across calls and is updated in place. */
|
|
1190
|
+
type StabilizeCardOrder = (view: QuoteView, orderMap: Map<string, number>) => QuoteView;
|
|
1191
|
+
/** See {@link AdaptGuestQuote}. */
|
|
1192
|
+
export declare const adaptGuestQuote: AdaptGuestQuote;
|
|
1193
|
+
/** See {@link FetchQuote}. */
|
|
1194
|
+
export declare const fetchQuote: FetchQuote;
|
|
1195
|
+
/** See {@link SelectOnQuote}: add one unit of a service. */
|
|
1196
|
+
export declare const addQuoteService: SelectOnQuote;
|
|
1197
|
+
/** See {@link SelectOnQuote}: remove one unit of a service. */
|
|
1198
|
+
export declare const removeQuoteService: SelectOnQuote;
|
|
1199
|
+
/** See {@link ClearQuoteServices}. */
|
|
1200
|
+
export declare const clearQuoteServices: ClearQuoteServices;
|
|
1201
|
+
/** See {@link SelectOnQuote}: price the stay under another cancellation policy. */
|
|
1202
|
+
export declare const selectQuotePolicy: SelectOnQuote;
|
|
1203
|
+
/** See {@link SelectQuoteInsurance}. */
|
|
1204
|
+
export declare const selectQuoteInsurance: SelectQuoteInsurance;
|
|
1205
|
+
/** See {@link PricePolicyBranches}. */
|
|
1206
|
+
export declare const pricePolicyBranches: PricePolicyBranches;
|
|
1207
|
+
/** See {@link IsRateConfigurationFault}. */
|
|
1208
|
+
export declare const isRateConfigurationFault: IsRateConfigurationFault;
|
|
1209
|
+
/** See {@link StabilizeCardOrder}. */
|
|
1210
|
+
export declare const stabilizeCardOrder: StabilizeCardOrder;
|
|
1211
|
+
//#endregion
|
|
1212
|
+
//#region src/quote/policies.d.ts
|
|
1213
|
+
/** What a rate is worth FOR THIS STAY.
|
|
1214
|
+
* - "refundable": a full-refund deadline still ahead (or no stay picked yet: nothing has lapsed).
|
|
1215
|
+
* - "expired": it has a full-refund rule and no rule at all can still be met.
|
|
1216
|
+
* - "other": no full-refund rule, or only a partial rule still reachable. */
|
|
1217
|
+
type PolicyStance = "refundable" | "expired" | "other";
|
|
1218
|
+
/** What a row is CALLED. A host-created policy keeps its label; the PMS's three built-ins are titled by
|
|
1219
|
+
* stance, because their names grade the host's strictness and read to a guest as a warning. */
|
|
1220
|
+
type PolicyTitle =
|
|
1221
|
+
/** The host's own name, verbatim — including a host-made default ("a host named theirs Testrichtlinie"). */
|
|
1222
|
+
{
|
|
1223
|
+
kind: "own";
|
|
1224
|
+
label: string;
|
|
1225
|
+
} |
|
|
1226
|
+
/** A built-in that still refunds in full. */
|
|
1227
|
+
{
|
|
1228
|
+
kind: "refundable";
|
|
1229
|
+
} |
|
|
1230
|
+
/** A built-in whose window has closed for this stay. */
|
|
1231
|
+
{
|
|
1232
|
+
kind: "expired";
|
|
1233
|
+
} |
|
|
1234
|
+
/** A built-in that is neither (unreachable with today's three; never falls back to the name). */
|
|
1235
|
+
{
|
|
1236
|
+
kind: "standard";
|
|
1237
|
+
};
|
|
1238
|
+
/** What a policy MEANS, decided by precedence — one variant, never several:
|
|
1239
|
+
* deadline (single rule, still ahead) > prose (not an echo of the title, no smuggled built-in name) >
|
|
1240
|
+
* rules > none; "expired" replaces all of them. */
|
|
1241
|
+
type PolicyTerms =
|
|
1242
|
+
/** "Free cancellation until {date}": arrival − the 100 % rule's days. */
|
|
1243
|
+
{
|
|
1244
|
+
kind: "deadline";
|
|
1245
|
+
date: IsoDay;
|
|
1246
|
+
} |
|
|
1247
|
+
/** The PMS's own description, verbatim. */
|
|
1248
|
+
{
|
|
1249
|
+
kind: "prose";
|
|
1250
|
+
text: string;
|
|
1251
|
+
} |
|
|
1252
|
+
/** The refund ladder, one line per rule. */
|
|
1253
|
+
{
|
|
1254
|
+
kind: "rules";
|
|
1255
|
+
rules: QuotePolicyRule[];
|
|
1256
|
+
} |
|
|
1257
|
+
/** The refundable window has closed; `daysBeforeArrival` names the lapsed deadline. */
|
|
1258
|
+
{
|
|
1259
|
+
kind: "expired";
|
|
1260
|
+
daysBeforeArrival: number;
|
|
1261
|
+
} |
|
|
1262
|
+
/** Nothing to state (the SDK's minimal `{ id, label, selected }`, or the title already said it). */
|
|
1263
|
+
{
|
|
1264
|
+
kind: "none";
|
|
1265
|
+
};
|
|
1266
|
+
/** One offered rate, ready to render. */
|
|
1267
|
+
interface PolicyOption {
|
|
1268
|
+
/** The policy id `choosePolicy` takes. */
|
|
1269
|
+
id: string;
|
|
1270
|
+
/** What the row is called. */
|
|
1271
|
+
title: PolicyTitle;
|
|
1272
|
+
/** The rate the shown price is for. */
|
|
1273
|
+
selected: boolean;
|
|
1274
|
+
/** False: shown, but not selectable for the current combination. */
|
|
1275
|
+
available: boolean;
|
|
1276
|
+
/** The PMS's reason for `available: false`, verbatim, or null. */
|
|
1277
|
+
unavailableReason: string | null;
|
|
1278
|
+
/** This rate's real total from its branch; null while pending, when the branch failed, or when no
|
|
1279
|
+
* branches were priced. */
|
|
1280
|
+
total: string | null;
|
|
1281
|
+
/** What the rate means for this stay. */
|
|
1282
|
+
terms: PolicyTerms;
|
|
1283
|
+
/** Shown under the terms. `markUp` only when `total` is null — a percentage beside a real total
|
|
1284
|
+
* invites a check that finds the site wrong (the markup applies to the rent, not the total). */
|
|
1285
|
+
notes: {
|
|
1286
|
+
markUp: string | null;
|
|
1287
|
+
processingFee: string | null;
|
|
1288
|
+
};
|
|
1289
|
+
}
|
|
1290
|
+
/** Where the guest picks the rate — ONE site decision, the same value on Quote and Booking:
|
|
1291
|
+
* "rental-page" (default): Quote offers the picker, Booking applies the carried ?policy= and states it;
|
|
1292
|
+
* "checkout": Quote states the pre-selected rate's terms only, Booking offers the picker. */
|
|
1293
|
+
type PolicyChoiceMode = "rental-page" | "checkout";
|
|
1294
|
+
/** The policy model of one priced stay. */
|
|
1295
|
+
interface PolicyOffer {
|
|
1296
|
+
/** "choice": ≥ 2 offered rates on the surface that picks → a picker (`options` = every offered rate).
|
|
1297
|
+
* "single": one offered rate there → `freeCancellationUntil` as one sentence, or nothing.
|
|
1298
|
+
* "terms": the other surface → the applied rate's terms block, no picker (`options` = that rate).
|
|
1299
|
+
* "none": the quote carries no policies (every v9 hub) → a site's `cancellationNote` may speak. */
|
|
1300
|
+
mode: "choice" | "single" | "terms" | "none";
|
|
1301
|
+
/** The rows to render (see `mode`). */
|
|
1302
|
+
options: PolicyOption[];
|
|
1303
|
+
/** The guest's explicit pick when it is offered; null = the PMS pre-selection. */
|
|
1304
|
+
chosenId: string | null;
|
|
1305
|
+
/** Branch prices are still being fetched. */
|
|
1306
|
+
pricesPending: boolean;
|
|
1307
|
+
/** For "single" and "terms": the shown rate's live free-cancellation day, or null. */
|
|
1308
|
+
freeCancellationUntil: IsoDay | null;
|
|
1309
|
+
}
|
|
1310
|
+
/** What `describePolicyOffer` takes. */
|
|
1311
|
+
interface PolicyOfferInput {
|
|
1312
|
+
/** The quote's policies (unfiltered; the offer rule is applied here). */
|
|
1313
|
+
policies: readonly QuoteCancellationPolicy[];
|
|
1314
|
+
/** The priced branches, or null when none were priced. */
|
|
1315
|
+
branches: readonly PolicyBranch[] | null;
|
|
1316
|
+
/** Branch prices are still being fetched. */
|
|
1317
|
+
pricesPending: boolean;
|
|
1318
|
+
/** The guest's explicit pick, or null. */
|
|
1319
|
+
chosenId: string | null;
|
|
1320
|
+
/** The stay's arrival. */
|
|
1321
|
+
arrival: IsoDay;
|
|
1322
|
+
/** The site's decision where the rate is chosen. */
|
|
1323
|
+
mode: PolicyChoiceMode;
|
|
1324
|
+
/** Which surface asks: the Quote offers the picker in "rental-page" mode, Booking in "checkout" mode. */
|
|
1325
|
+
surface: "quote" | "checkout";
|
|
1326
|
+
/** Default today in the visitor's timezone. */
|
|
1327
|
+
today?: IsoDay;
|
|
1328
|
+
}
|
|
1329
|
+
/** What `policyTerms` takes. */
|
|
1330
|
+
interface PolicyTermsOptions {
|
|
1331
|
+
/** The stay's arrival; absent before a stay is picked (no deadline line then). */
|
|
1332
|
+
arrival?: IsoDay;
|
|
1333
|
+
/** Default today in the visitor's timezone. */
|
|
1334
|
+
today?: IsoDay;
|
|
1335
|
+
/** The row shows this policy's real total. Terms never depend on it — it decides
|
|
1336
|
+
* `PolicyOption.notes.markUp` — and is accepted so both describe one row with one object. */
|
|
1337
|
+
hasTotal: boolean;
|
|
1338
|
+
/** The row already prints `policyTitle` above the terms: the expired line is then dropped where the
|
|
1339
|
+
* title was substituted (a built-in), never under a host's own name, which says nothing about refunds. */
|
|
1340
|
+
titled: boolean;
|
|
1341
|
+
}
|
|
1342
|
+
/** The backend states deadlines in whole days only (`daysBeforeArrival`; the v1 schema carries no hour,
|
|
1343
|
+
* cut-off or timezone), so no time is ever printed and every comparison is at midnight. */
|
|
1344
|
+
export declare const POLICY_DEADLINE_HAS_TIME: false;
|
|
1345
|
+
/** The type of `BUILT_IN_POLICY_LABELS` (declared below): the PMS's three built-in policy names
|
|
1346
|
+
* (SYSTEM_FIRM_14, SYSTEM_STRICT_30, SYSTEM_STRICT_60) in de + en, lowercased — closed, read out of the
|
|
1347
|
+
* SDK's translation catalogue; a package test re-reads the installed catalogue in both directions. */
|
|
1348
|
+
type BuiltInPolicyLabels = ReadonlySet<string>;
|
|
1349
|
+
/** True for one of the PMS's built-ins; everything else the host created and named. */
|
|
1350
|
+
type IsBuiltInPolicy = (policy: QuoteCancellationPolicy) => boolean;
|
|
1351
|
+
/** The rate's stance for a stay arriving on `arrival` (absent: no stay picked → describe the rate). */
|
|
1352
|
+
type PolicyStanceOf = (policy: QuoteCancellationPolicy, arrival?: IsoDay, today?: IsoDay) => PolicyStance;
|
|
1353
|
+
/** The row title (see `PolicyTitle`). */
|
|
1354
|
+
type PolicyTitleOf = (policy: QuoteCancellationPolicy, arrival?: IsoDay, today?: IsoDay) => PolicyTitle;
|
|
1355
|
+
/** The terms by precedence (see `PolicyTerms`). */
|
|
1356
|
+
type PolicyTermsOf = (policy: QuoteCancellationPolicy, options: PolicyTermsOptions) => PolicyTerms;
|
|
1357
|
+
/** The full set minus rates that stopped being a trade: beside a standard rate that can no longer refund
|
|
1358
|
+
* anything, only rates that still can survive — otherwise the cheaper non-refundable rate is the same
|
|
1359
|
+
* booking for less. The standard (the row without a markup, else the first; never "selected", which
|
|
1360
|
+
* moves) always survives. A rule refunding 0 % refunds nothing, however reachable. */
|
|
1361
|
+
type OfferedPolicies = (policies: readonly QuoteCancellationPolicy[], arrival?: IsoDay, today?: IsoDay) => QuoteCancellationPolicy[];
|
|
1362
|
+
/** Arrival − the 100 % rule's days, if still ahead; else null (never a bygone deadline). */
|
|
1363
|
+
type FreeCancellationUntil = (policy: QuoteCancellationPolicy | undefined, arrival: IsoDay, today?: IsoDay) => IsoDay | null;
|
|
1364
|
+
/** The policy model of a priced stay for one surface (shared by createQuoteFlow and the checkout). */
|
|
1365
|
+
type DescribePolicyOffer = (input: PolicyOfferInput) => PolicyOffer;
|
|
1366
|
+
/** Duplicated from the SDK catalogue (`dist/translations/v10/<locale>/core.json`,
|
|
1367
|
+
* `CancellationPolicy.policyType.option.SYSTEM_*`) because it is not reachable through the public SDK.
|
|
1368
|
+
* A host-created policy is returned untranslated (measured: byte-identical in de-DE and en-US), a
|
|
1369
|
+
* second reason this list can only ever match the backend's own. */
|
|
1370
|
+
export declare const BUILT_IN_POLICY_LABELS: BuiltInPolicyLabels;
|
|
1371
|
+
/** See {@link IsBuiltInPolicy}. */
|
|
1372
|
+
export declare const isBuiltInPolicy: IsBuiltInPolicy;
|
|
1373
|
+
/** See {@link FreeCancellationUntil}. */
|
|
1374
|
+
export declare const freeCancellationUntil: FreeCancellationUntil;
|
|
1375
|
+
/** See {@link PolicyStanceOf}. */
|
|
1376
|
+
export declare const policyStance: PolicyStanceOf;
|
|
1377
|
+
/** See {@link PolicyTitleOf}. */
|
|
1378
|
+
export declare const policyTitle: PolicyTitleOf;
|
|
1379
|
+
/** See {@link PolicyTermsOf}. */
|
|
1380
|
+
export declare const policyTerms: PolicyTermsOf;
|
|
1381
|
+
/** See {@link OfferedPolicies}. */
|
|
1382
|
+
export declare const offeredPolicies: OfferedPolicies;
|
|
1383
|
+
/** See {@link DescribePolicyOffer}. */
|
|
1384
|
+
export declare const describePolicyOffer: DescribePolicyOffer;
|
|
1385
|
+
//#endregion
|
|
1386
|
+
//#region src/quote/events.d.ts
|
|
1387
|
+
/** The window event name (`CustomEvent<QuoteEventDetail>`), dispatched once per stay. */
|
|
1388
|
+
export declare const QUOTE_EVENT: "voffice:quote";
|
|
1389
|
+
/** What happened. "rate-fault": a stay the PMS priced but cannot resolve to one multi-rate
|
|
1390
|
+
* (`AmbiguousMultiRate` / `UnresolvableMultiRate`) — a configuration fault for site operations
|
|
1391
|
+
* (SDK 2.16.0 MIGRATION §2), never shown to the guest as "not available". */
|
|
1392
|
+
type QuoteEventDetail = {
|
|
1393
|
+
type: "rate-fault";
|
|
1394
|
+
/** PMS rental id. */
|
|
1395
|
+
rentalId: string;
|
|
1396
|
+
/** The stay (`stayKeyOf`: dates + party counts, no names). */
|
|
1397
|
+
stayKey: string;
|
|
1398
|
+
/** The SDK's machine code. */
|
|
1399
|
+
code: QuoteUnavailableCode;
|
|
1400
|
+
};
|
|
1401
|
+
//#endregion
|
|
1402
|
+
//#region src/quote/createQuoteFlow.d.ts
|
|
1403
|
+
/** v9 in practice: show the campaign label the search measured for THIS stay.
|
|
1404
|
+
* "none" (default) · "from-search": read `?offer=`, latch it to the first complete stay after the
|
|
1405
|
+
* handover (only when that stay is provably the one the search priced), drop it on any edit, never
|
|
1406
|
+
* re-arm. A label only — never a struck price or a saving (search and quote disagree on discounted
|
|
1407
|
+
* totals). */
|
|
1408
|
+
type CampaignLabelSource = "none" | "from-search";
|
|
1409
|
+
/** What an unavailable stay offers beyond the PMS's reason.
|
|
1410
|
+
* "none" (default) · "next-arrival": the first arrivable day after the chosen start, from the arrival
|
|
1411
|
+
* map already loaded (no extra request). "other-rentals" is reserved (needs a search runner). */
|
|
1412
|
+
type WhenUnavailable = "none" | "next-arrival";
|
|
1413
|
+
/** The options a widget brings to its rental's flow (Quote's props). `configure` applies them to a flow
|
|
1414
|
+
* another island of the rental created first (an occupancy calendar that hydrated earlier). */
|
|
1415
|
+
interface QuoteFlowSettings {
|
|
1416
|
+
/** limits (a 20-person house raises adults), childAge (ages outside it are asked again),
|
|
1417
|
+
* defaultAdults (default 2). */
|
|
1418
|
+
guests?: GuestOptions;
|
|
1419
|
+
/** Default "rental-page". */
|
|
1420
|
+
policyChoice?: PolicyChoiceMode;
|
|
1421
|
+
/** Default "none". */
|
|
1422
|
+
campaignLabels?: CampaignLabelSource;
|
|
1423
|
+
/** Default "none". */
|
|
1424
|
+
whenUnavailable?: WhenUnavailable;
|
|
1425
|
+
/** The site's minimum stay in nights on top of the PMS (see `StayPickerOptions.minStayNights`). */
|
|
1426
|
+
minStayNights?: number;
|
|
1427
|
+
}
|
|
1428
|
+
/** The flow's options: the picker's, plus the widget's settings, the caps and the hooks. */
|
|
1429
|
+
interface QuoteFlowOptions extends StayPickerOptions, QuoteFlowSettings {
|
|
1430
|
+
/** Catalogue capacity (where availability states no caps: v9 before SDK 2.28) and the pets flag. */
|
|
1431
|
+
caps?: Pick<GuestCaps, "maxGuests" | "petsAllowed">;
|
|
1432
|
+
/** Debounce before the auto-quote, coalescing stepper bursts. Default 350. */
|
|
1433
|
+
autoQuoteDelayMs?: number;
|
|
1434
|
+
/** Once per stay on AmbiguousMultiRate / UnresolvableMultiRate. Default: one console.warn. */
|
|
1435
|
+
onEvent?: (detail: QuoteEventDetail) => void;
|
|
1436
|
+
}
|
|
1437
|
+
/** The answer for the current stay. Every non-idle kind carries the `stayKey` it belongs to, so
|
|
1438
|
+
* nothing from a superseded stay is ever shown. */
|
|
1439
|
+
type QuoteOutcome =
|
|
1440
|
+
/** No complete range, no adult, an age missing, or the handover still resolving. */
|
|
1441
|
+
{
|
|
1442
|
+
kind: "idle";
|
|
1443
|
+
} |
|
|
1444
|
+
/** Debounce, the arrival map, the request, or the chosen rate's branch still in flight. */
|
|
1445
|
+
{
|
|
1446
|
+
kind: "pending";
|
|
1447
|
+
stayKey: string;
|
|
1448
|
+
} |
|
|
1449
|
+
/** The price of the shown rate (the guest's pick or the PMS pre-selection). */
|
|
1450
|
+
{
|
|
1451
|
+
kind: "priced";
|
|
1452
|
+
stayKey: string;
|
|
1453
|
+
price: PriceView;
|
|
1454
|
+
policies: PolicyOffer;
|
|
1455
|
+
/** The latched search labels while this IS the stay they were measured for, else []. */
|
|
1456
|
+
campaignLabels: string[];
|
|
1457
|
+
/** Carried for custom UIs; the quote surface never renders insurance. */
|
|
1458
|
+
insurance: QuoteInsurance | null;
|
|
1459
|
+
} |
|
|
1460
|
+
/** NotBookable: the SDK's reason verbatim (possibly multi-line), or null when it states none. */
|
|
1461
|
+
{
|
|
1462
|
+
kind: "unavailable";
|
|
1463
|
+
stayKey: string;
|
|
1464
|
+
reason: string | null;
|
|
1465
|
+
} |
|
|
1466
|
+
/** The days are free but the rate setup is faulty — never say "not available". */
|
|
1467
|
+
{
|
|
1468
|
+
kind: "rate-fault";
|
|
1469
|
+
stayKey: string;
|
|
1470
|
+
code: QuoteUnavailableCode;
|
|
1471
|
+
} |
|
|
1472
|
+
/** Transport failure; only `retry()` refires. */
|
|
1473
|
+
{
|
|
1474
|
+
kind: "error";
|
|
1475
|
+
stayKey: string;
|
|
1476
|
+
};
|
|
1477
|
+
/** What the primary button does now. */
|
|
1478
|
+
type QuoteCta =
|
|
1479
|
+
/** The handover is not read yet → skeleton. */
|
|
1480
|
+
{
|
|
1481
|
+
kind: "resolving";
|
|
1482
|
+
} |
|
|
1483
|
+
/** Opens the calendar. */
|
|
1484
|
+
{
|
|
1485
|
+
kind: "pick-dates";
|
|
1486
|
+
} |
|
|
1487
|
+
/** Opens the guest picker: an age is missing (the age prices the stay). */
|
|
1488
|
+
{
|
|
1489
|
+
kind: "complete-ages";
|
|
1490
|
+
} |
|
|
1491
|
+
/** "Price loading", disabled. */
|
|
1492
|
+
{
|
|
1493
|
+
kind: "pending";
|
|
1494
|
+
} |
|
|
1495
|
+
/** The checkout link (`checkoutHref`). */
|
|
1496
|
+
{
|
|
1497
|
+
kind: "book";
|
|
1498
|
+
} |
|
|
1499
|
+
/** Unavailable or rate fault: opens the calendar. */
|
|
1500
|
+
{
|
|
1501
|
+
kind: "change-dates";
|
|
1502
|
+
} |
|
|
1503
|
+
/** After an error. */
|
|
1504
|
+
{
|
|
1505
|
+
kind: "retry";
|
|
1506
|
+
};
|
|
1507
|
+
/** The flow's state. */
|
|
1508
|
+
interface QuoteFlowState {
|
|
1509
|
+
/** The handover was applied (and a ± window resolved); until then fields show skeletons. */
|
|
1510
|
+
resolved: boolean;
|
|
1511
|
+
/** The calendar's state (`flow.picker.day()` renders the days). */
|
|
1512
|
+
picker: StayPickerState;
|
|
1513
|
+
/** The travelling party (seeded with `defaultAdults`, the handover wins). */
|
|
1514
|
+
party: Party;
|
|
1515
|
+
/** Stepper bounds for this rental (availability caps > catalogue `maxGuests` > limits). */
|
|
1516
|
+
bounds: GuestBounds;
|
|
1517
|
+
/** Every child has an age; no quote runs before. */
|
|
1518
|
+
agesComplete: boolean;
|
|
1519
|
+
/** Dates + party as one string (`stayKeyOf`), or null without a complete range. */
|
|
1520
|
+
stayKey: string | null;
|
|
1521
|
+
/** The answer for the current stay. */
|
|
1522
|
+
outcome: QuoteOutcome;
|
|
1523
|
+
/** What the primary button does now. */
|
|
1524
|
+
cta: QuoteCta;
|
|
1525
|
+
/** The guest's explicit rate; null = the PMS pre-selection. Reset to null when the stay stops
|
|
1526
|
+
* offering it (an unknown id from a link, a rate withdrawn by `offeredPolicies`, a failed branch). */
|
|
1527
|
+
chosenPolicyId: string | null;
|
|
1528
|
+
/** With `whenUnavailable: "next-arrival"` and an unavailable / rate-fault outcome: the day, or null. */
|
|
1529
|
+
nextArrival: IsoDay | null;
|
|
1530
|
+
/** Incremented by `requestFocus` / `pickArrival`; a UI opens or scrolls to the widget on a change. */
|
|
1531
|
+
focusRequest: number;
|
|
1532
|
+
}
|
|
1533
|
+
/** The flow: a store plus the widget's actions. */
|
|
1534
|
+
interface QuoteFlow extends Store<QuoteFlowState> {
|
|
1535
|
+
/** The underlying picker (day models for the calendar). */
|
|
1536
|
+
readonly picker: StayPicker;
|
|
1537
|
+
/** Replace the widget's settings (absent keys fall back to their defaults). For a widget joining a
|
|
1538
|
+
* flow another island created; call it before `hydrate`. Before the handover the party is re-seeded
|
|
1539
|
+
* with the new `defaultAdults`; outside "rental-page" the guest's rate is dropped. */
|
|
1540
|
+
configure(settings: QuoteFlowSettings): void;
|
|
1541
|
+
/** Apply a handover once (`readQuoteHandover(new URLSearchParams(location.search))`); later calls
|
|
1542
|
+
* are ignored. A ± window is parked until the arrival map lands, then resolved in two stages: the
|
|
1543
|
+
* first arrivable day that still fits the stay, then the wanted length or the nearest allowed
|
|
1544
|
+
* departure inside the window. */
|
|
1545
|
+
hydrate(handover: QuoteHandover): void;
|
|
1546
|
+
/** A calendar click (see `StayPicker.select`). */
|
|
1547
|
+
selectDay(iso: IsoDay): void;
|
|
1548
|
+
/** Clear the dates. */
|
|
1549
|
+
resetDates(): void;
|
|
1550
|
+
/** Change the party; clamped to the bounds (a dog cannot be added where pets are not allowed, a
|
|
1551
|
+
* handed-over one can be lowered and is still sent). */
|
|
1552
|
+
setParty(patch: Partial<Party>): void;
|
|
1553
|
+
/** The guest's rate (null = PMS pre-selection); "rental-page" mode only. Switching costs no request:
|
|
1554
|
+
* every branch was priced up front. */
|
|
1555
|
+
choosePolicy(id: string | null): void;
|
|
1556
|
+
/** Re-run a quote that failed in transport. */
|
|
1557
|
+
retry(): void;
|
|
1558
|
+
/** From AvailabilityCalendar: set the arrival only, enter the departure phase, request focus. */
|
|
1559
|
+
pickArrival(iso: IsoDay): void;
|
|
1560
|
+
/** Apply `nextArrival` as the new start (departure phase). */
|
|
1561
|
+
takeNextArrival(): void;
|
|
1562
|
+
/** Ask the UI to bring the widget into view. */
|
|
1563
|
+
requestFocus(): void;
|
|
1564
|
+
/** Fetch availability; nothing is quoted before the arrival map answered (ready or failed). */
|
|
1565
|
+
start(): void;
|
|
1566
|
+
/** Stop: timers cleared, late answers dropped. */
|
|
1567
|
+
dispose(): void;
|
|
1568
|
+
}
|
|
1569
|
+
/** The flow over the caller's SDK, resolved once on the first request (again only after a failed
|
|
1570
|
+
* resolve) and shared with its picker. Requests: the arrival map, one departure map per chosen start,
|
|
1571
|
+
* one quote per settled stay, and in "rental-page" mode one branch per further offered rate. */
|
|
1572
|
+
type CreateQuoteFlow = (sdk: SdkSource, options: QuoteFlowOptions) => QuoteFlow;
|
|
1573
|
+
/** One flow per key across islands (card, bar, an occupancy calendar), ref-counted on `globalThis`. The
|
|
1574
|
+
* first caller's `create` wins (a widget that joins re-applies its settings with `configure`); the last
|
|
1575
|
+
* `release` disposes. Userland can subscribe to a rental's flow (a gallery offer pill) without a window
|
|
1576
|
+
* event. */
|
|
1577
|
+
type AcquireQuoteFlow = (key: string, create: () => QuoteFlow) => {
|
|
1578
|
+
readonly flow: QuoteFlow;
|
|
1579
|
+
release(): void;
|
|
1580
|
+
};
|
|
1581
|
+
/** See {@link CreateQuoteFlow}. */
|
|
1582
|
+
export declare const createQuoteFlow: CreateQuoteFlow;
|
|
1583
|
+
/** See {@link AcquireQuoteFlow}. */
|
|
1584
|
+
export declare const acquireQuoteFlow: AcquireQuoteFlow;
|
|
1585
|
+
//#endregion
|
|
1586
|
+
//#region src/quote/handover.d.ts
|
|
1587
|
+
/** The rental-page keys this surface reads and writes (the funnel contract: search → quote → checkout). */
|
|
1588
|
+
export declare const QUOTE_PARAM_KEYS: readonly ["start", "end", "adults", "children", "childrenAges", "babies", "pets", "policy"];
|
|
1589
|
+
/** Keys a ± search link adds: resolved into a stay, then stripped. */
|
|
1590
|
+
export declare const WINDOW_PARAM_KEYS: readonly ["minNights", "maxNights", "flex"];
|
|
1591
|
+
/** The repeatable campaign label a search card hands over — a label only, never a figure. */
|
|
1592
|
+
export declare const CAMPAIGN_PARAM: "offer";
|
|
1593
|
+
/** What a rental-page URL says. Invalid values degrade to absent, never to an error. */
|
|
1594
|
+
interface QuoteHandover {
|
|
1595
|
+
/** A valid day, or null. */
|
|
1596
|
+
start: IsoDay | null;
|
|
1597
|
+
/** A valid day after `start`, or null. */
|
|
1598
|
+
end: IsoDay | null;
|
|
1599
|
+
/** A ± window: start/end are its bounds and the stay length is pinned. Present only with start + end
|
|
1600
|
+
* and a night pair shorter than the window; nights = `maxNights` (equal to `minNights` on a pinned
|
|
1601
|
+
* pair; a range min < max takes the upper bound, the length the hub prices). */
|
|
1602
|
+
window: {
|
|
1603
|
+
nights: number;
|
|
1604
|
+
} | null;
|
|
1605
|
+
/** Null when the URL carries no party key at all (the flow then seeds `defaultAdults`). Read with
|
|
1606
|
+
* `readParty`: `petsCount` (legacy search links) wins over `pets`. */
|
|
1607
|
+
party: Partial<Party> | null;
|
|
1608
|
+
/** An explicit rate from a shared link; checked against the stay's offered rates later. */
|
|
1609
|
+
policyId: string | null;
|
|
1610
|
+
/** Campaign labels: trimmed, distinct, at most four (a crafted URL cannot paste a list into the box). */
|
|
1611
|
+
campaignLabels: string[];
|
|
1612
|
+
}
|
|
1613
|
+
/** Read a handover. */
|
|
1614
|
+
type ReadQuoteHandover = (params: URLSearchParams) => QuoteHandover;
|
|
1615
|
+
/** The URL after a state change: quote keys set or deleted (start always, end only when complete,
|
|
1616
|
+
* adults even at the default, zero counts omitted, `policy` only for an explicit choice — the absence
|
|
1617
|
+
* IS the deferral to the PMS pre-selection), the legacy `petsCount` and the window keys stripped once
|
|
1618
|
+
* resolved, campaign labels kept only while latched to the priced stay. Every other key keeps its value
|
|
1619
|
+
* and position. Before the handover resolved, `current` is returned unchanged (the first write must not
|
|
1620
|
+
* clobber what is still being read). Returns a new object; equal output = leave the URL alone. */
|
|
1621
|
+
type WriteQuoteParams = (current: URLSearchParams, state: QuoteFlowState) => URLSearchParams;
|
|
1622
|
+
/** `path?<stay>` from STATE (never from location), so the link is the priced stay: start, end, the party
|
|
1623
|
+
* (dogs as `pets`), `policy` only when explicit, `kind=option` for an option booking. Null without a
|
|
1624
|
+
* complete, priced stay. */
|
|
1625
|
+
type CheckoutHref = (path: string, state: QuoteFlowState, kind?: "booking" | "option") => string | null;
|
|
1626
|
+
/** The stay alone as a query string without "?" (dates + party; for an enquiry link). Null without a
|
|
1627
|
+
* complete range. */
|
|
1628
|
+
type StayQuery = (state: QuoteFlowState) => string | null;
|
|
1629
|
+
/** See {@link ReadQuoteHandover}. */
|
|
1630
|
+
export declare const readQuoteHandover: ReadQuoteHandover;
|
|
1631
|
+
/** See {@link WriteQuoteParams}. */
|
|
1632
|
+
export declare const writeQuoteParams: WriteQuoteParams;
|
|
1633
|
+
/** See {@link CheckoutHref}. */
|
|
1634
|
+
export declare const checkoutHref: CheckoutHref;
|
|
1635
|
+
/** See {@link StayQuery}. */
|
|
1636
|
+
export declare const stayQuery: StayQuery;
|
|
1637
|
+
//#endregion
|
|
1638
|
+
//#region src/checkout/stay.d.ts
|
|
1639
|
+
/** A complete stay: arrival and departure (local calendar days, `end` after `start`) and the party.
|
|
1640
|
+
* Serializable on purpose: it travels through the URL handover and into the return snapshot. */
|
|
1641
|
+
interface Stay {
|
|
1642
|
+
/** Arrival. */
|
|
1643
|
+
readonly start: IsoDay;
|
|
1644
|
+
/** Departure (after `start`). */
|
|
1645
|
+
readonly end: IsoDay;
|
|
1646
|
+
/** The travelling party. */
|
|
1647
|
+
readonly party: Party;
|
|
1648
|
+
}
|
|
424
1649
|
//#endregion
|
|
425
|
-
//#region src/
|
|
426
|
-
/**
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
1650
|
+
//#region src/checkout/handover.d.ts
|
|
1651
|
+
/** A confirmed booking, or a non-binding option (SDK `kind: "OPTION"`, v10 and v9). */
|
|
1652
|
+
type BookingKind = "booking" | "option";
|
|
1653
|
+
/** What the checkout URL says. */
|
|
1654
|
+
interface CheckoutHandover {
|
|
1655
|
+
/** From `start`, `end` and the party keys; null when the dates are missing or invalid (the
|
|
1656
|
+
* checkout shows its missing-stay card). */
|
|
1657
|
+
stay: Stay | null;
|
|
1658
|
+
/** `?policy=`: the rate chosen on the rental page. A seed: the flow applies it only while the
|
|
1659
|
+
* policy rule still offers it for this arrival. Null = the PMS pre-selection. */
|
|
1660
|
+
policyId: string | null;
|
|
1661
|
+
/** `?kind=option`, honoured only with `allowOption`; otherwise "booking". */
|
|
1662
|
+
kind: BookingKind;
|
|
1663
|
+
/** `?return=1`: a payment provider sent the guest back to this page (sites that return to the
|
|
1664
|
+
* checkout itself; the package's own return page is a separate route). */
|
|
1665
|
+
paymentReturn: boolean;
|
|
1666
|
+
}
|
|
1667
|
+
/** How the checkout reads its URL. */
|
|
1668
|
+
interface CheckoutHandoverOptions {
|
|
1669
|
+
/** Adults assumed when the URL has dates but no adults. Default 2 (the quote's default). */
|
|
1670
|
+
defaultAdults?: number | undefined;
|
|
1671
|
+
/** Accept `?kind=option`. Default false. */
|
|
1672
|
+
allowOption?: boolean | undefined;
|
|
1673
|
+
}
|
|
1674
|
+
/** Parse the checkout URL (pass `location.search`). Pure. Reads `pets` and the legacy `petsCount`;
|
|
1675
|
+
* `childrenAges` aligns to `children` (a missing age is null, and blocks booking later). */
|
|
1676
|
+
type ReadCheckoutHandover = (search: string, options?: CheckoutHandoverOptions) => CheckoutHandover;
|
|
1677
|
+
/** The query string (no leading "?") for `history.replaceState` after an edit: the stay (pets as
|
|
1678
|
+
* `pets`), an explicit policy only (never today's pre-selection, which would pin it), `kind=option`
|
|
1679
|
+
* for an option. Every other parameter in `current` (tracking, site keys) is preserved after them. */
|
|
1680
|
+
type CheckoutSearch = (input: {
|
|
1681
|
+
stay: Stay | null;
|
|
1682
|
+
policyId: string | null;
|
|
1683
|
+
kind: BookingKind;
|
|
1684
|
+
}, current?: string) => string;
|
|
1685
|
+
/** See {@link ReadCheckoutHandover}. */
|
|
1686
|
+
export declare const readCheckoutHandover: ReadCheckoutHandover;
|
|
1687
|
+
/** See {@link CheckoutSearch}. */
|
|
1688
|
+
export declare const checkoutSearch: CheckoutSearch;
|
|
1689
|
+
//#endregion
|
|
1690
|
+
//#region src/checkout/booking.d.ts
|
|
1691
|
+
/** The guest's contact as the guest form produces it; folded into the SDK's nested contact. */
|
|
1692
|
+
interface BookingContact {
|
|
1693
|
+
/** Given name. */
|
|
432
1694
|
forename: string;
|
|
1695
|
+
/** Family name. */
|
|
433
1696
|
surname: string;
|
|
434
|
-
/**
|
|
435
|
-
title?: string
|
|
1697
|
+
/** A `SALUTATION_WIRE_VALUES` value; omitted when none was chosen. */
|
|
1698
|
+
title?: string;
|
|
1699
|
+
/** Street (with the number unless the PMS splits it into `housenumber`). */
|
|
436
1700
|
street: string;
|
|
437
|
-
|
|
1701
|
+
/** House number, where the form asks it separately. */
|
|
1702
|
+
housenumber?: string;
|
|
1703
|
+
/** Postal code. */
|
|
438
1704
|
postalcode: string;
|
|
1705
|
+
/** Town. */
|
|
439
1706
|
city: string;
|
|
440
|
-
/** ISO
|
|
441
|
-
countryCode:
|
|
1707
|
+
/** ISO country code. */
|
|
1708
|
+
countryCode: CountryCode;
|
|
1709
|
+
/** Email address (the confirmation goes there). */
|
|
442
1710
|
email: string;
|
|
443
|
-
|
|
444
|
-
|
|
1711
|
+
/** Normalized `+CC …` (the booking endpoint accepts nothing else). */
|
|
1712
|
+
phone?: string;
|
|
1713
|
+
/** The guest's message to the host. */
|
|
1714
|
+
note?: string;
|
|
445
1715
|
}
|
|
1716
|
+
/** One line of a payment entry, verbatim. */
|
|
446
1717
|
interface PaymentLine {
|
|
1718
|
+
/** The line's label, verbatim. */
|
|
447
1719
|
label: string;
|
|
1720
|
+
/** The line's formatted amount, verbatim. */
|
|
448
1721
|
amount: string;
|
|
449
1722
|
}
|
|
450
|
-
/** One payment entry, verbatim
|
|
451
|
-
*
|
|
1723
|
+
/** One payment entry, verbatim from the PMS: a contractual step, or a way to settle the whole
|
|
1724
|
+
* balance at once (`full_amount`). */
|
|
452
1725
|
interface PaymentScheduleView {
|
|
453
|
-
/**
|
|
454
|
-
* reads comes from `label`: the SDK translates it, and a v10 installment is
|
|
455
|
-
* not automatically a v9 prepayment. */
|
|
1726
|
+
/** For behaviour only; the guest reads `label` (a v10 installment is not a v9 prepayment). */
|
|
456
1727
|
kind: BookingPaymentScheduleKind | "full_amount";
|
|
1728
|
+
/** The entry's label, verbatim ("Anzahlung", "Restzahlung"). */
|
|
457
1729
|
label: string;
|
|
458
|
-
/**
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
paymentAt?: string | undefined;
|
|
466
|
-
lines: PaymentLine[];
|
|
1730
|
+
/** A complete sentence ("Zahlbar bis zum 6. Februar 2027"): render, never parse or prefix. */
|
|
1731
|
+
dueOn?: string;
|
|
1732
|
+
/** Where the payment happens (v10, when the backend states it). */
|
|
1733
|
+
paymentAt?: string;
|
|
1734
|
+
/** The entry's line items. */
|
|
1735
|
+
lines: readonly PaymentLine[];
|
|
1736
|
+
/** The entry's formatted amount, verbatim. */
|
|
467
1737
|
amount: string;
|
|
468
|
-
/**
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
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. */
|
|
1738
|
+
/** Empty = informational (a future, on-site or external step): no button for it. */
|
|
1739
|
+
options: readonly BookingPaymentOption[];
|
|
1740
|
+
}
|
|
1741
|
+
/** An insurance premium owed to the INSURER (`external_url`), never part of the schedules: a
|
|
1742
|
+
* different contract with a different recipient, paid off the site. */
|
|
479
1743
|
interface InsurancePaymentLink {
|
|
1744
|
+
/** The insurer's label for the payment. */
|
|
480
1745
|
label: string;
|
|
1746
|
+
/** The insurer's payment page. */
|
|
481
1747
|
url: string;
|
|
482
|
-
|
|
1748
|
+
/** The insurer's line about it, when stated. */
|
|
1749
|
+
description?: string;
|
|
483
1750
|
}
|
|
1751
|
+
/** The inline confirmation after `book()`. */
|
|
484
1752
|
interface BookingConfirmation {
|
|
1753
|
+
/** A booking or an option. */
|
|
1754
|
+
kind: BookingKind;
|
|
1755
|
+
/** The PMS booking number, verbatim. */
|
|
485
1756
|
bookingNumber: string;
|
|
486
|
-
/** The contractual steps, in backend order
|
|
487
|
-
schedules: PaymentScheduleView[];
|
|
488
|
-
/** Ways to settle the
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
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[];
|
|
1757
|
+
/** The contractual steps, in backend order. */
|
|
1758
|
+
schedules: readonly PaymentScheduleView[];
|
|
1759
|
+
/** Ways to settle the whole balance at once: an ALTERNATIVE to `schedules`, never added to it. */
|
|
1760
|
+
alternatives: readonly PaymentScheduleView[];
|
|
1761
|
+
/** Empty unless insurance was booked and the insurer returned a payment page. */
|
|
1762
|
+
insurancePayments: readonly InsurancePaymentLink[];
|
|
1763
|
+
/** True when any option in `schedules` or `alternatives` can be performed in the browser. */
|
|
1764
|
+
payable: boolean;
|
|
499
1765
|
}
|
|
500
|
-
/**
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
/**
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
*
|
|
515
|
-
*
|
|
516
|
-
*
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
1766
|
+
/** What `submitBooking` needs besides the quote and the contact. */
|
|
1767
|
+
interface SubmitBookingOptions {
|
|
1768
|
+
/** Where the payment provider returns to, relative to the site origin. The site's route. */
|
|
1769
|
+
returnPath: string;
|
|
1770
|
+
/** Default "booking". */
|
|
1771
|
+
kind?: BookingKind | undefined;
|
|
1772
|
+
/** The transfer account holder when the PMS sends none (v9 hub): the operator's name. The SDK never
|
|
1773
|
+
* substitutes another name (it leaves the row out); this puts the operator back, because the guest
|
|
1774
|
+
* needs the holder for the bank's payee check. */
|
|
1775
|
+
bankHolderFallback?: string | undefined;
|
|
1776
|
+
/** The stay being booked, for the payment-return snapshot. */
|
|
1777
|
+
stay: Stay;
|
|
1778
|
+
}
|
|
1779
|
+
/** ⚠ USER-initiated only: reserves for real. `quote` must be the live instance with every selection
|
|
1780
|
+
* applied. Logs the SDK's cause and rethrows (nothing is necessarily booked when it throws). Writes
|
|
1781
|
+
* the payment-return snapshot the moment the booking number exists (a bank-transfer guest never
|
|
1782
|
+
* leaves the checkout, so this is the only place it can be written). */
|
|
1783
|
+
type SubmitBooking = (sdk: WebsiteSdk, quote: GuestQuote, contact: BookingContact, options: SubmitBookingOptions) => Promise<BookingConfirmation>;
|
|
1784
|
+
/** Every kind but `bank_transfer` (display-only details). */
|
|
1785
|
+
type IsSubmittableOption = (option: BookingPaymentOption) => option is SubmittableBookingPaymentOption;
|
|
1786
|
+
/** ⚠ USER-initiated only: redirects (or form-posts) to the payment provider. By then the reservation
|
|
1787
|
+
* EXISTS, so a failure is an unpaid booking someone has to chase: logged before rethrowing. */
|
|
1788
|
+
type PayWithOption = (sdk: WebsiteSdk, option: SubmittableBookingPaymentOption) => Promise<void>;
|
|
1789
|
+
/** See {@link SubmitBooking}. */
|
|
1790
|
+
export declare const submitBooking: SubmitBooking;
|
|
1791
|
+
/** See {@link IsSubmittableOption}. */
|
|
1792
|
+
export declare const isSubmittableOption: IsSubmittableOption;
|
|
1793
|
+
/** See {@link PayWithOption}. */
|
|
1794
|
+
export declare const payWithOption: PayWithOption;
|
|
520
1795
|
//#endregion
|
|
521
|
-
//#region src/
|
|
522
|
-
/**
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
/**
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
1796
|
+
//#region src/checkout/events.d.ts
|
|
1797
|
+
/** The window CustomEvent name. Renaming it is a major. */
|
|
1798
|
+
export declare const CHECKOUT_EVENT: "voffice:checkout";
|
|
1799
|
+
/** What `CHECKOUT_EVENT` carries; never personal data. */
|
|
1800
|
+
type CheckoutEventDetail =
|
|
1801
|
+
/** A PMS rate-configuration fault (AmbiguousMultiRate / UnresolvableMultiRate): the stay is free,
|
|
1802
|
+
* only the rate cannot be resolved. Route it to monitoring. Once per stay. The same `type` as
|
|
1803
|
+
* `voffice:quote`'s, so one listener serves both surfaces. */
|
|
1804
|
+
{
|
|
1805
|
+
readonly type: "rate-fault";
|
|
1806
|
+
readonly rentalId: string;
|
|
1807
|
+
readonly code: QuoteUnavailableCode;
|
|
1808
|
+
} |
|
|
1809
|
+
/** `book()` succeeded. `payable` is false when nothing can be paid online (bank transfer only): a
|
|
1810
|
+
* site may then send the guest to its return page itself. */
|
|
1811
|
+
{
|
|
1812
|
+
readonly type: "booked";
|
|
1813
|
+
readonly rentalId: string;
|
|
1814
|
+
readonly bookingNumber: string;
|
|
1815
|
+
readonly kind: BookingKind;
|
|
1816
|
+
readonly payable: boolean;
|
|
1817
|
+
} |
|
|
1818
|
+
/** The guest pressed a payment option; the browser is about to leave for the provider. */
|
|
1819
|
+
{
|
|
1820
|
+
readonly type: "payment-started";
|
|
1821
|
+
readonly bookingNumber: string;
|
|
1822
|
+
readonly method: BookingPaymentOption["kind"];
|
|
1823
|
+
};
|
|
1824
|
+
//#endregion
|
|
1825
|
+
//#region src/checkout/insurance.d.ts
|
|
1826
|
+
/** Progress through the document step (held by the flow: the order button, a section away, is gated
|
|
1827
|
+
* on it). */
|
|
1828
|
+
interface InsuranceDocsState {
|
|
1829
|
+
/** URLs opened so far, keyed by URL rather than by plan: plans routinely share every file but the
|
|
1830
|
+
* IPID, a document received does not un-happen when the guest switches tariff, and a URL survives
|
|
1831
|
+
* a re-quote that returns fresh option objects for the same product. */
|
|
1832
|
+
opened: ReadonlySet<string>;
|
|
1833
|
+
/** The product whose documents the guest confirmed receiving (`insuranceProductKey`), or null.
|
|
1834
|
+
* Product-bound, so another tariff demands a fresh acknowledgement while opened files carry over;
|
|
1835
|
+
* by product rather than plan id, because a banded re-price hands back identical documents under
|
|
1836
|
+
* a new id and re-ticking for files the guest already has would be friction with nothing behind it. */
|
|
1837
|
+
confirmedProduct: string | null;
|
|
1838
|
+
}
|
|
1839
|
+
/** Nothing opened, nothing confirmed. */
|
|
1840
|
+
export declare const NO_INSURANCE_DOCS: InsuranceDocsState;
|
|
1841
|
+
/** The documents shown for a product: what the checkout lists, what the guest must open and what
|
|
1842
|
+
* makes a plan sellable read this ONE list, so a hidden document can never be required-but-unlisted
|
|
1843
|
+
* (a gate the guest could not close). Only the GDV consent is excluded, matched by URL (see below). */
|
|
1844
|
+
type ShownInsuranceResources = (option: QuoteInsuranceOption) => NonNullable<QuoteInsuranceOption["resources"]>;
|
|
1845
|
+
/** The products a checkout can complete. Technical: no per-traveler data (forms, pre-contract, SEPA
|
|
1846
|
+
* mandate, a post-booking insurance call is a flow this layer does not run). Legal: at least one
|
|
1847
|
+
* shown pre-contractual document (a product sent without them cannot be concluded lawfully here, so
|
|
1848
|
+
* it self-hides rather than being sold undocumented). */
|
|
1849
|
+
type SelectableInsuranceOptions = (insurance: QuoteInsurance) => QuoteInsuranceOption[];
|
|
1850
|
+
/** The selected plan, but only when the guest could pick it here: no price row can name a product
|
|
1851
|
+
* the section never showed. */
|
|
1852
|
+
type SelectedInsuranceOption = (insurance: QuoteInsurance | undefined) => QuoteInsuranceOption | undefined;
|
|
1853
|
+
/** The identity of a PRODUCT, not of one priced variant of it. An insurer may band its tariffs by
|
|
1854
|
+
* trip value: the same tariff returns under a new id and premium once the total crosses a band (one
|
|
1855
|
+
* insurer: 56,00 € up to about 1.250 €, 65,00 € above, 74,00 € above that). The label survives a
|
|
1856
|
+
* band change, and it is the name the guest chose. */
|
|
1857
|
+
type InsuranceProductKey = (option: QuoteInsuranceOption) => string;
|
|
1858
|
+
/** After a re-quote: the plan id to re-apply, or undefined (nothing chosen, already applied, or a
|
|
1859
|
+
* product the fresh feed no longer offers as selectable). Matched by id, then by product, because
|
|
1860
|
+
* the SDK carries a selection across its refresh only on an exact id: a renamed-but-identical
|
|
1861
|
+
* tariff would otherwise land a guest who only added a towel package back on "no insurance". The
|
|
1862
|
+
* new premium is on screen in the radio, the summary and above the button, so nothing is hidden. */
|
|
1863
|
+
type InsuranceIdToReapply = (fresh: QuoteView, wanted: QuoteInsuranceOption | undefined) => string | undefined;
|
|
1864
|
+
/** True while a chosen plan's document step is unfinished: every shown document opened AND the
|
|
1865
|
+
* product confirmed. No plan chosen is never blocking: the duty attaches to the insurance
|
|
1866
|
+
* contract, not to the stay. */
|
|
1867
|
+
type InsuranceDocsPending = (option: QuoteInsuranceOption | undefined, docs: InsuranceDocsState) => boolean;
|
|
1868
|
+
/** See {@link ShownInsuranceResources}. */
|
|
1869
|
+
export declare const shownInsuranceResources: ShownInsuranceResources;
|
|
1870
|
+
/** See {@link SelectableInsuranceOptions}. */
|
|
1871
|
+
export declare const selectableInsuranceOptions: SelectableInsuranceOptions;
|
|
1872
|
+
/** See {@link SelectedInsuranceOption}. */
|
|
1873
|
+
export declare const selectedInsuranceOption: SelectedInsuranceOption;
|
|
1874
|
+
/** See {@link InsuranceProductKey}. */
|
|
1875
|
+
export declare const insuranceProductKey: InsuranceProductKey;
|
|
1876
|
+
/** See {@link InsuranceIdToReapply}. */
|
|
1877
|
+
export declare const insuranceIdToReapply: InsuranceIdToReapply;
|
|
1878
|
+
/** See {@link InsuranceDocsPending}. */
|
|
1879
|
+
export declare const insuranceDocsPending: InsuranceDocsPending;
|
|
1880
|
+
//#endregion
|
|
1881
|
+
//#region src/checkout/payment-return.d.ts
|
|
1882
|
+
/** Storage keys, unchanged from the kits, so no guest's return breaks on the switch. */
|
|
1883
|
+
export declare const CHECKOUT_CONTEXT_KEY: "voffice:checkout-context";
|
|
1884
|
+
export declare const BOOKING_SNAPSHOT_KEY: "voffice:booking-return";
|
|
1885
|
+
/** One gallery image, in the SDK's own shape minus what the page does not render. */
|
|
531
1886
|
interface SnapshotImage {
|
|
1887
|
+
/** The image URL. */
|
|
532
1888
|
src: string;
|
|
533
|
-
|
|
1889
|
+
/** Responsive candidates. */
|
|
1890
|
+
srcset?: string;
|
|
1891
|
+
/** The alternative text. */
|
|
534
1892
|
alt: string;
|
|
535
1893
|
}
|
|
1894
|
+
/** The rental identity the checkout parks for the return page (it has it from the build). */
|
|
536
1895
|
interface CheckoutContext {
|
|
1896
|
+
/** The booked rental's name. */
|
|
537
1897
|
rentalName: string;
|
|
538
|
-
location
|
|
539
|
-
|
|
540
|
-
/**
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
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"). */
|
|
1898
|
+
/** The rental's location line. */
|
|
1899
|
+
location?: string;
|
|
1900
|
+
/** The rental page. */
|
|
1901
|
+
href?: string;
|
|
1902
|
+
/** At most five are kept (the checkout's own strip). */
|
|
1903
|
+
images?: readonly SnapshotImage[];
|
|
1904
|
+
}
|
|
1905
|
+
/** What the return page renders. Every field but `bookingNumber` and `kind` self-hides when absent. */
|
|
552
1906
|
interface BookingSnapshot {
|
|
1907
|
+
/** The PMS booking number. */
|
|
553
1908
|
bookingNumber: string;
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
/**
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
/** The
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
/**
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
*/
|
|
576
|
-
|
|
577
|
-
//#endregion
|
|
578
|
-
//#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
|
-
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
|
-
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
|
-
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
|
-
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
|
-
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. */
|
|
646
|
-
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. */
|
|
651
|
-
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. */
|
|
660
|
-
confirmedProduct: string | null;
|
|
1909
|
+
/** A booking or an option. */
|
|
1910
|
+
kind: BookingKind;
|
|
1911
|
+
/** The booked rental's name. */
|
|
1912
|
+
rentalName?: string;
|
|
1913
|
+
/** The rental's location line. */
|
|
1914
|
+
location?: string;
|
|
1915
|
+
/** The rental page. */
|
|
1916
|
+
href?: string;
|
|
1917
|
+
/** The rental's photos (at most five). */
|
|
1918
|
+
images?: readonly SnapshotImage[];
|
|
1919
|
+
/** Arrival. */
|
|
1920
|
+
start?: IsoDay;
|
|
1921
|
+
/** Departure. */
|
|
1922
|
+
end?: IsoDay;
|
|
1923
|
+
/** The booked party's counts (no names: no personal data is parked). */
|
|
1924
|
+
guests?: {
|
|
1925
|
+
adults: number;
|
|
1926
|
+
children: number;
|
|
1927
|
+
babies: number;
|
|
1928
|
+
pets: number;
|
|
1929
|
+
};
|
|
1930
|
+
/** The PMS's formatted total, verbatim; never re-derived. */
|
|
1931
|
+
total?: string;
|
|
661
1932
|
}
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
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
|
-
declare function isoDate(date: Date): string;
|
|
683
|
-
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
|
-
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
|
-
declare function stayParams(stay: Stay): string;
|
|
690
|
-
//#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";
|
|
1933
|
+
/** How the return page was reached. */
|
|
1934
|
+
type PaymentReturn =
|
|
1935
|
+
/** This tab booked: the snapshot proves it. */
|
|
1936
|
+
{
|
|
1937
|
+
state: "booked";
|
|
1938
|
+
snapshot: BookingSnapshot;
|
|
1939
|
+
paymentCancelled: boolean;
|
|
1940
|
+
} |
|
|
1941
|
+
/** A provider return without a snapshot (another tab, storage refused). */
|
|
1942
|
+
{
|
|
1943
|
+
state: "returned";
|
|
1944
|
+
paymentCancelled: boolean;
|
|
1945
|
+
} |
|
|
1946
|
+
/** Nobody booked: a bare URL, or tracking parameters only. */
|
|
1947
|
+
{
|
|
1948
|
+
state: "none";
|
|
723
1949
|
};
|
|
724
|
-
/**
|
|
725
|
-
*
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
1950
|
+
/** Park the rental identity for the return page (the flow calls it on `start()`). Never throws.
|
|
1951
|
+
* Default storage `browserStorage("session")`; null = nothing is stored. */
|
|
1952
|
+
type SaveCheckoutContext = (context: CheckoutContext, storage?: StorageLike | null) => void;
|
|
1953
|
+
/** Classify the return page's URL (pass `location.search`). The snapshot comes first; without one,
|
|
1954
|
+
* any parameter that is not tracking (`isTrackingParam`) counts as a provider return; a bare or
|
|
1955
|
+
* tracking-only URL is "none". `paymentCancelled` is Stripe's `payment=stripe&cancel=true`. Fails
|
|
1956
|
+
* soft: an unreadable store reads as no snapshot. */
|
|
1957
|
+
type ReadPaymentReturn = (search: string, storage?: StorageLike | null) => PaymentReturn;
|
|
1958
|
+
/** See {@link SaveCheckoutContext}. */
|
|
1959
|
+
export declare const saveCheckoutContext: SaveCheckoutContext;
|
|
1960
|
+
/** See {@link ReadPaymentReturn}. */
|
|
1961
|
+
export declare const readPaymentReturn: ReadPaymentReturn;
|
|
1962
|
+
//#endregion
|
|
1963
|
+
//#region src/checkout/flow.d.ts
|
|
1964
|
+
/** The sentences the flow itself reports. Copy: always passed in (the components' merged
|
|
1965
|
+
* `CheckoutCopy` satisfies it). */
|
|
1966
|
+
interface CheckoutMessages {
|
|
1967
|
+
/** Replaces the SDK reason on a rate-configuration fault: the stay is free, only its rate cannot
|
|
1968
|
+
* be resolved, so the availability sentence would be false. */
|
|
732
1969
|
rateFault: string;
|
|
733
|
-
/**
|
|
1970
|
+
/** When an unavailable answer carries no reason of its own. */
|
|
734
1971
|
unavailable: string;
|
|
735
|
-
/**
|
|
1972
|
+
/** A re-quote failed on transport (retry offered). */
|
|
736
1973
|
quoteRefreshFailed: string;
|
|
737
|
-
/**
|
|
1974
|
+
/** The voucher could not be applied at all (transport, stay unavailable). */
|
|
738
1975
|
voucherFailed: string;
|
|
739
|
-
/**
|
|
1976
|
+
/** The voucher was processed and rejected. */
|
|
740
1977
|
voucherRejected: string;
|
|
741
1978
|
}
|
|
1979
|
+
/** What a checkout flow is built from. */
|
|
742
1980
|
interface CheckoutFlowOptions {
|
|
1981
|
+
/** PMS id of the rental. */
|
|
743
1982
|
rentalId: string;
|
|
744
|
-
/** The
|
|
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). */
|
|
1983
|
+
/** The PMS answers in this language (quote lines, reasons, rate names). */
|
|
777
1984
|
locale: Locale;
|
|
778
|
-
/** Where the payment provider
|
|
779
|
-
* site's
|
|
780
|
-
|
|
781
|
-
|
|
1985
|
+
/** Where the payment provider returns to, relative to the site origin. Required: routes are the
|
|
1986
|
+
* site's. */
|
|
1987
|
+
returnPath: string;
|
|
1988
|
+
/** `readCheckoutHandover(location.search, …)`. */
|
|
1989
|
+
handover: CheckoutHandover;
|
|
1990
|
+
/** Rental identity for the payment-return page; parked in sessionStorage on `start()`. */
|
|
1991
|
+
context?: CheckoutContext | undefined;
|
|
1992
|
+
/** Where the rate is chosen: "rental-page" (default) applies the carried `?policy=` and states it;
|
|
1993
|
+
* "checkout" makes the offered rates choosable here (`selectPolicy`). */
|
|
1994
|
+
policyChoice?: PolicyChoiceMode | undefined;
|
|
1995
|
+
/** Ids of the consent checkboxes the UI renders ("terms", "privacy", additional-terms ids); each
|
|
1996
|
+
* must be ticked before `book()`. Default none (every consent is a notice). */
|
|
1997
|
+
requiredConsents?: readonly string[] | undefined;
|
|
1998
|
+
/** The transfer account holder when the PMS sends none (v9 hub): the operator's name. */
|
|
1999
|
+
bankHolderFallback?: string | undefined;
|
|
2000
|
+
/** `limits` cap the guest editor; `defaultAdults` seeds a stay picked on a page that arrived
|
|
2001
|
+
* without one; `childAge` is the age band (a handed-over age outside it is asked again). */
|
|
2002
|
+
guests?: GuestOptions | undefined;
|
|
2003
|
+
/** The site's minimum stay in nights on top of the PMS rules (the value Quote takes): `setDates`
|
|
2004
|
+
* refuses a shorter range. Default none (the quote enforces the PMS's own minimum). */
|
|
2005
|
+
minStayNights?: number | undefined;
|
|
2006
|
+
/** Catalogue capacity (caps the party until `loadGuestCaps` brings availability's, and where that
|
|
2007
|
+
* states none) and the pets flag (a dog cannot be added without it; a handed-over one can be
|
|
2008
|
+
* removed and is still sent). */
|
|
2009
|
+
caps?: Pick<GuestCaps, "maxGuests" | "petsAllowed"> | undefined;
|
|
2010
|
+
/** The sentences the flow reports itself (the checkout copy satisfies them). */
|
|
2011
|
+
messages: CheckoutMessages;
|
|
2012
|
+
/** Receives every checkout event: `rate-fault` once per stay, `booked`, `payment-started`.
|
|
2013
|
+
* Without it a rate fault is logged with console.warn. */
|
|
2014
|
+
onEvent?: ((detail: CheckoutEventDetail) => void) | undefined;
|
|
2015
|
+
}
|
|
2016
|
+
/** Where the checkout stands. */
|
|
2017
|
+
type CheckoutPhase = {
|
|
2018
|
+
kind: "loading";
|
|
2019
|
+
} |
|
|
2020
|
+
/** No complete stay arrived (missing or invalid dates). */
|
|
2021
|
+
{
|
|
2022
|
+
kind: "missing-stay";
|
|
2023
|
+
} | {
|
|
2024
|
+
kind: "ready";
|
|
2025
|
+
} |
|
|
2026
|
+
/** The first quote was refused (the SDK reason, or `messages.rateFault` / `unavailable`). */
|
|
2027
|
+
{
|
|
2028
|
+
kind: "unavailable";
|
|
2029
|
+
reason: string;
|
|
2030
|
+
} |
|
|
2031
|
+
/** The first quote failed on transport (`retry()`). */
|
|
2032
|
+
{
|
|
2033
|
+
kind: "error";
|
|
2034
|
+
} |
|
|
2035
|
+
/** `book()` succeeded; `confirmation` is set. */
|
|
2036
|
+
{
|
|
2037
|
+
kind: "booked";
|
|
2038
|
+
};
|
|
2039
|
+
/** One reason the order button is off, in the order the UI should mention them. */
|
|
2040
|
+
type CheckoutBlocker = "no-quote" | "price-pending" | "busy" | "issue" | "child-ages" | "insurance-documents" | "consents";
|
|
2041
|
+
/** One extra as the UI renders it. */
|
|
2042
|
+
interface CheckoutService extends QuoteAddonService {
|
|
2043
|
+
/** Units chosen (0 = not booked). */
|
|
2044
|
+
count: number;
|
|
2045
|
+
/** False at `maxPerBooking` (default 99), while unavailable, or while busy. */
|
|
2046
|
+
canAdd: boolean;
|
|
2047
|
+
/** True while `count > 0`: an unavailable service can still be removed. */
|
|
2048
|
+
canRemove: boolean;
|
|
782
2049
|
}
|
|
2050
|
+
/** The rate the quote carries and the rates the guest may still choose. */
|
|
2051
|
+
interface CheckoutPolicyState {
|
|
2052
|
+
/** The rate the quote carries, or null when the PMS sends none (v9). */
|
|
2053
|
+
applied: QuoteCancellationPolicy | null;
|
|
2054
|
+
/** The rates still offered for this arrival (the offer-withdrawal rule); a picker renders these. */
|
|
2055
|
+
offered: readonly QuoteCancellationPolicy[];
|
|
2056
|
+
/** Every rate on the quote, for the summary's terms (a rate that can no longer be cancelled is what
|
|
2057
|
+
* must be said before paying). */
|
|
2058
|
+
all: readonly QuoteCancellationPolicy[];
|
|
2059
|
+
/** Last day of free cancellation under the applied rate, or null. */
|
|
2060
|
+
freeCancellationUntil: IsoDay | null;
|
|
2061
|
+
/** The rate model for the UI (`describePolicyOffer`, surface "checkout"; no branch prices). */
|
|
2062
|
+
offer: PolicyOffer;
|
|
2063
|
+
}
|
|
2064
|
+
/** Null when there is nothing to choose (no insurance, the v10 placeholder, no selectable product). */
|
|
2065
|
+
interface CheckoutInsuranceState {
|
|
2066
|
+
/** Products this checkout can complete: ride-along, with their pre-contractual documents. */
|
|
2067
|
+
options: readonly QuoteInsuranceOption[];
|
|
2068
|
+
/** The "no insurance" label, verbatim from the SDK. */
|
|
2069
|
+
noneLabel: string;
|
|
2070
|
+
/** The product the quote carries, or null for none. */
|
|
2071
|
+
chosen: QuoteInsuranceOption | null;
|
|
2072
|
+
/** Progress through the § 7 VVG document step. */
|
|
2073
|
+
documents: InsuranceDocsState;
|
|
2074
|
+
/** The documents the guest must open for `chosen` (the shown set, never a label whitelist). */
|
|
2075
|
+
requiredDocuments: readonly {
|
|
2076
|
+
label: string;
|
|
2077
|
+
url: string;
|
|
2078
|
+
}[];
|
|
2079
|
+
/** True while `chosen` blocks booking. */
|
|
2080
|
+
documentsPending: boolean;
|
|
2081
|
+
}
|
|
2082
|
+
/** The voucher field's state. */
|
|
2083
|
+
interface CheckoutVoucherState {
|
|
2084
|
+
/** The code the shown price includes, or null. */
|
|
2085
|
+
applied: string | null;
|
|
2086
|
+
/** Why the last code did not apply, or null. */
|
|
2087
|
+
error: string | null;
|
|
2088
|
+
/** A code is being priced. */
|
|
2089
|
+
checking: boolean;
|
|
2090
|
+
}
|
|
2091
|
+
/** A re-quote that failed or found the stay unavailable. */
|
|
783
2092
|
interface CheckoutIssue {
|
|
784
|
-
/**
|
|
785
|
-
key: string;
|
|
2093
|
+
/** What to tell the guest (the SDK's reason or a messages sentence). */
|
|
786
2094
|
reason: string;
|
|
787
|
-
/** Transport errors offer
|
|
2095
|
+
/** Transport errors offer `retry()`; unavailability does not. */
|
|
788
2096
|
retryable: boolean;
|
|
789
2097
|
}
|
|
790
|
-
/** Everything a UI renders. A new object per change
|
|
791
|
-
|
|
792
|
-
|
|
2098
|
+
/** Everything a UI renders. A new object per change, unchanged otherwise. */
|
|
2099
|
+
interface CheckoutState {
|
|
2100
|
+
/** Where the checkout stands. */
|
|
793
2101
|
phase: CheckoutPhase;
|
|
2102
|
+
/** A booking, or an option (`?kind=option` with `allowOption`). */
|
|
2103
|
+
kind: BookingKind;
|
|
2104
|
+
/** The stay being edited (the source of truth after the handover). */
|
|
2105
|
+
stay: Stay | null;
|
|
2106
|
+
/** What the guest editor may offer for this rental. */
|
|
2107
|
+
bounds: GuestBounds;
|
|
2108
|
+
/** The quote view (of the last priced stay while `pricePending`). */
|
|
794
2109
|
view: QuoteView | null;
|
|
795
|
-
counts
|
|
2110
|
+
/** The extras with their chosen counts. */
|
|
2111
|
+
services: readonly CheckoutService[];
|
|
2112
|
+
/** The applied rate and the offered ones. */
|
|
2113
|
+
policy: CheckoutPolicyState;
|
|
2114
|
+
/** The insurance choice, or null when there is nothing to choose. */
|
|
2115
|
+
insurance: CheckoutInsuranceState | null;
|
|
2116
|
+
/** The voucher field's state. */
|
|
2117
|
+
voucher: CheckoutVoucherState;
|
|
2118
|
+
/** Checkbox state per required consent id. */
|
|
2119
|
+
consents: Readonly<Record<string, boolean>>;
|
|
2120
|
+
/** The shown price does not belong to the shown stay yet. */
|
|
2121
|
+
pricePending: boolean;
|
|
2122
|
+
/** A selection or a re-quote is running. */
|
|
796
2123
|
busy: boolean;
|
|
2124
|
+
/** The last re-quote's failure for this stay, or null. */
|
|
797
2125
|
issue: CheckoutIssue | null;
|
|
2126
|
+
/** The SDK's reason for a refused selection (an extras/rate combination it cannot price). */
|
|
798
2127
|
selectionError: string | null;
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
/**
|
|
802
|
-
|
|
803
|
-
/**
|
|
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";
|
|
2128
|
+
/** Empty when `book()` is allowed. */
|
|
2129
|
+
blockers: readonly CheckoutBlocker[];
|
|
2130
|
+
/** The order button's state: idle, booking, or a failed `book()`. */
|
|
2131
|
+
submit: "idle" | "submitting" | "failed";
|
|
2132
|
+
/** The confirmation after `book()`, or null. */
|
|
811
2133
|
confirmation: BookingConfirmation | null;
|
|
812
|
-
|
|
813
|
-
|
|
2134
|
+
/** The payment buttons' state: the option redirecting now, and a failed start. */
|
|
2135
|
+
payment: {
|
|
2136
|
+
busyOption: SubmittableBookingPaymentOption | null;
|
|
2137
|
+
failed: boolean;
|
|
2138
|
+
};
|
|
814
2139
|
}
|
|
815
|
-
/** The actions a UI calls
|
|
816
|
-
interface
|
|
2140
|
+
/** The actions a UI calls; stable for the flow's lifetime. */
|
|
2141
|
+
interface CheckoutActions {
|
|
2142
|
+
/** A new date range from the stay editor (at least one night, and at least `minStayNights`; a
|
|
2143
|
+
* shorter range is ignored); re-quotes after a short debounce. */
|
|
2144
|
+
setDates(start: IsoDay, end: IsoDay): void;
|
|
2145
|
+
/** A new party from the guest editor; clamped to `bounds` (pets per the caps rule); re-quotes
|
|
2146
|
+
* after a short debounce. */
|
|
2147
|
+
setParty(party: Party): void;
|
|
2148
|
+
/** Load the rental's availability caps once (persons, adults, and on v9 the children and pets
|
|
2149
|
+
* rules), so `bounds` follow them. Call it when the guest editor opens: the page's first request
|
|
2150
|
+
* stays the quote. A failure is logged and the catalogue caps stay. Idempotent. */
|
|
2151
|
+
loadGuestCaps(): void;
|
|
2152
|
+
/** Add (+1) or remove (−1) one unit of an extra. */
|
|
817
2153
|
changeService(id: string, delta: 1 | -1): void;
|
|
2154
|
+
/** Only with `policyChoice: "checkout"`, and only to an offered rate. */
|
|
2155
|
+
selectPolicy(id: string): void;
|
|
2156
|
+
/** A selectable product's id, or null for none. */
|
|
818
2157
|
selectInsurance(id: string | null): void;
|
|
819
|
-
|
|
820
|
-
|
|
2158
|
+
/** The guest opened one of the chosen product's documents (the § 7 VVG step). */
|
|
2159
|
+
openInsuranceDocument(url: string): void;
|
|
2160
|
+
/** The confirmation is keyed by the chosen product inside the flow, so no UI can confirm the
|
|
2161
|
+
* wrong one. */
|
|
2162
|
+
confirmInsuranceDocuments(confirmed: boolean): void;
|
|
2163
|
+
/** Re-quote with a code (null removes it); the previous quote stays when the code is rejected.
|
|
2164
|
+
* Ignored while the shown price is not the shown stay's (`pricePending`, or a failed re-quote). */
|
|
821
2165
|
applyVoucher(code: string | null): void;
|
|
822
|
-
/**
|
|
823
|
-
|
|
824
|
-
/**
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
2166
|
+
/** Tick or untick a required consent checkbox. */
|
|
2167
|
+
setConsent(id: string, value: boolean): void;
|
|
2168
|
+
/** Re-quote after a transport issue. */
|
|
2169
|
+
retry(): void;
|
|
2170
|
+
/** ⚠ USER-initiated only: creates a real booking. Refused while `blockers` is not empty. */
|
|
2171
|
+
book(contact: BookingContact): void;
|
|
2172
|
+
/** ⚠ USER-initiated only: redirects to the real payment provider. Refused for bank transfer and
|
|
2173
|
+
* while another payment is starting. */
|
|
2174
|
+
pay(option: SubmittableBookingPaymentOption): void;
|
|
2175
|
+
}
|
|
2176
|
+
/** The checkout store: its state, its actions and its lifecycle. */
|
|
2177
|
+
interface CheckoutFlow extends Store<CheckoutState>, CheckoutActions {
|
|
2178
|
+
/** Park the context and price the handed-over stay. Idempotent. */
|
|
833
2179
|
start(): void;
|
|
834
2180
|
/** Cancel pending work and drop every in-flight result. */
|
|
835
2181
|
dispose(): void;
|
|
836
2182
|
}
|
|
837
|
-
/**
|
|
838
|
-
type
|
|
839
|
-
|
|
2183
|
+
/** The checkout store. The SDK source is resolved once, on the first request. */
|
|
2184
|
+
type CreateCheckoutFlow = (sdk: SdkSource, options: CheckoutFlowOptions) => CheckoutFlow;
|
|
2185
|
+
/** See {@link CreateCheckoutFlow}. */
|
|
2186
|
+
export declare const createCheckoutFlow: CreateCheckoutFlow;
|
|
840
2187
|
//#endregion
|
|
841
|
-
//#region src/
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
/**
|
|
846
|
-
*
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
*
|
|
850
|
-
*
|
|
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. */
|
|
2188
|
+
//#region src/forms/validation.d.ts
|
|
2189
|
+
/** Salutation choices. The labels are copy; the wire value is fixed, so the PMS sees one vocabulary in
|
|
2190
|
+
* every locale (an agency's staff read "Frau"/"Herr" whatever language the guest booked in). */
|
|
2191
|
+
type Salutation = "female" | "male" | "unspecified";
|
|
2192
|
+
/** What reaches the PMS as `title`. The checkout guest form sends none for "unspecified" (the booking
|
|
2193
|
+
* contact's title is optional); the contact form sends "Keine Angabe" (the contact API requires one). */
|
|
2194
|
+
export declare const SALUTATION_WIRE_VALUES: Readonly<Record<Salutation, string>>;
|
|
2195
|
+
/** Every sentence the field rules can return. A site's merged checkout copy satisfies it
|
|
2196
|
+
* structurally; `phoneRequired`, `housenumberRequired` and `salutationRequired` are read only when
|
|
2197
|
+
* the form makes that field required. */
|
|
857
2198
|
interface ValidationMessages {
|
|
2199
|
+
/** A forename left empty. */
|
|
858
2200
|
forenameRequired: string;
|
|
2201
|
+
/** A surname left empty. */
|
|
859
2202
|
surnameRequired: string;
|
|
2203
|
+
/** A street left empty. */
|
|
860
2204
|
streetRequired: string;
|
|
2205
|
+
/** A town left empty. */
|
|
861
2206
|
cityRequired: string;
|
|
2207
|
+
/** A postal code left empty. */
|
|
862
2208
|
postalcodeRequired: string;
|
|
2209
|
+
/** An email left empty. */
|
|
863
2210
|
emailRequired: string;
|
|
2211
|
+
/** A present email that is no address. */
|
|
864
2212
|
emailInvalid: string;
|
|
2213
|
+
/** A required phone left empty. */
|
|
2214
|
+
phoneRequired: string;
|
|
2215
|
+
/** A number that carries "+CC" but has the wrong length. */
|
|
865
2216
|
phoneInvalid: string;
|
|
2217
|
+
/** A national number the rules cannot prefix (no country code known). */
|
|
866
2218
|
phoneNeedsDialCode: string;
|
|
2219
|
+
/** A required house number left empty. */
|
|
2220
|
+
housenumberRequired: string;
|
|
2221
|
+
/** A required salutation left unchosen. */
|
|
2222
|
+
salutationRequired: string;
|
|
2223
|
+
/** No country chosen. */
|
|
2224
|
+
countryRequired: string;
|
|
2225
|
+
/** A present German postal code that is not five digits. */
|
|
867
2226
|
postalcodeInvalidDE: string;
|
|
2227
|
+
/** A present Austrian postal code that is not four digits. */
|
|
868
2228
|
postalcodeInvalidAT: string;
|
|
2229
|
+
/** A present Swiss postal code that is not four digits. */
|
|
869
2230
|
postalcodeInvalidCH: string;
|
|
2231
|
+
/** Non-blocking: the street seems to carry the house number. */
|
|
870
2232
|
streetHouseNumberWarning: string;
|
|
871
2233
|
}
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
*
|
|
882
|
-
*
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
*
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
2234
|
+
/** Separators → spaces, `00CC` → `+CC`, a trunk `0` → `+<dial>` (the calling code of the selected
|
|
2235
|
+
* country, digits only). Returns the normalized number, or null for a blank input. Without a `dial` a
|
|
2236
|
+
* national number stays as typed, so `phoneError` asks for the code instead of guessing one. */
|
|
2237
|
+
type NormalizePhone = (raw: string, dial: string | undefined) => string | null;
|
|
2238
|
+
/** An optional phone: blank is valid (null). Present, it must normalize to `+<1-9><6–14 digits>`
|
|
2239
|
+
* (E.164 caps the total at 15). A "+CC" number of the wrong length gets `phoneInvalid`; anything
|
|
2240
|
+
* without a country code gets `phoneNeedsDialCode`. Required-ness is the form's own check. */
|
|
2241
|
+
type PhoneError = (raw: string, dial: string | undefined, messages: ValidationMessages) => string | null;
|
|
2242
|
+
/** Required, and shaped like an address with a dotted top-level domain (catches "name@gmail"). The
|
|
2243
|
+
* endpoint's own pattern stays the authority; this is the early, coarse gate. Takes only the two
|
|
2244
|
+
* messages it reads, so the contact form's copy satisfies it too. */
|
|
2245
|
+
type EmailError = (raw: string, messages: Pick<ValidationMessages, "emailRequired" | "emailInvalid">) => string | null;
|
|
2246
|
+
/** Required for every country; a format check only for DE (5 digits), AT (4) and CH (4). Every other
|
|
2247
|
+
* country is accepted as typed: rejecting a valid foreign address is the most expensive mistake a
|
|
2248
|
+
* booking form can make, and no single pattern covers "1234 AB" or an Eircode. */
|
|
2249
|
+
type PostalcodeError = (raw: string, country: CountryCode, messages: ValidationMessages) => string | null;
|
|
2250
|
+
/** A non-blocking hint when the street ends in a number that is probably the house number: the
|
|
2251
|
+
* house-number field is empty, or holds that same number twice. "Bundesstraße 96" with "Nr. 3" is a
|
|
2252
|
+
* real street and gets no hint; digits inside a name ("Straße des 17. Juni") never do. */
|
|
2253
|
+
type StreetWarning = (street: string, housenumber: string, messages: ValidationMessages) => string | null;
|
|
2254
|
+
/** See {@link NormalizePhone}. */
|
|
2255
|
+
export declare const normalizePhone: NormalizePhone;
|
|
2256
|
+
/** See {@link PhoneError}. */
|
|
2257
|
+
export declare const phoneError: PhoneError;
|
|
2258
|
+
/** See {@link EmailError}. */
|
|
2259
|
+
export declare const emailError: EmailError;
|
|
2260
|
+
/** See {@link PostalcodeError}. */
|
|
2261
|
+
export declare const postalcodeError: PostalcodeError;
|
|
2262
|
+
/** See {@link StreetWarning}. */
|
|
2263
|
+
export declare const streetWarning: StreetWarning;
|
|
2264
|
+
//#endregion
|
|
2265
|
+
//#region src/checkout/guest-form.d.ts
|
|
2266
|
+
/** Every field, in visual order (the order `firstInvalid` walks). `salutation` holds a `Salutation`
|
|
2267
|
+
* or "" (none chosen); `country` an ISO code. */
|
|
2268
|
+
type GuestFormField = "salutation" | "forename" | "surname" | "email" | "phone" | "street" | "housenumber" | "postalcode" | "city" | "country" | "note";
|
|
2269
|
+
/** Fields a site may make required on top of the minimum (forename, surname, email, street, postal
|
|
2270
|
+
* code, city, country): an agency that calls back requires the phone, one whose PMS splits the
|
|
2271
|
+
* address requires the house number. */
|
|
2272
|
+
type OptionalGuestField = "salutation" | "phone" | "housenumber";
|
|
2273
|
+
/** What the guest-data form is built from. */
|
|
2274
|
+
interface GuestFormOptions {
|
|
2275
|
+
/** The validators' copy (the checkout copy satisfies it structurally). */
|
|
2276
|
+
messages: ValidationMessages;
|
|
2277
|
+
/** The Land options (`countryOptions` at build). */
|
|
2278
|
+
countries: CountryGroups;
|
|
2279
|
+
/** Default none. */
|
|
2280
|
+
requiredFields?: readonly OptionalGuestField[] | undefined;
|
|
2281
|
+
/** The Land when the browser languages name none of `countries` (with `languages: []` simply the
|
|
2282
|
+
* starting Land), and the dial code of a national phone number while no Land is chosen. Default "DE". */
|
|
2283
|
+
fallbackCountry?: CountryCode | undefined;
|
|
2284
|
+
/** `navigator.languages`, passed in by the UI (headless reads no navigator). Default none. */
|
|
2285
|
+
languages?: readonly string[] | undefined;
|
|
2286
|
+
}
|
|
2287
|
+
/** What a UI renders. */
|
|
2288
|
+
interface GuestFormState {
|
|
2289
|
+
/** Every field's current value. */
|
|
2290
|
+
values: Readonly<Record<GuestFormField, string>>;
|
|
2291
|
+
/** Blocking errors that are SHOWN (judged on blur, on change of a shown one, or on submit). */
|
|
2292
|
+
errors: Readonly<Partial<Record<GuestFormField, string>>>;
|
|
2293
|
+
/** Non-blocking hints (the street/house-number heuristic); never stop `submit()`. */
|
|
2294
|
+
warnings: Readonly<Partial<Record<GuestFormField, string>>>;
|
|
2295
|
+
/** The fields the guest has left once (their present value is judged). */
|
|
2296
|
+
touched: ReadonlySet<GuestFormField>;
|
|
2297
|
+
/** After a failed `submit()`: the topmost invalid field, for focus; null otherwise. */
|
|
2298
|
+
firstInvalid: GuestFormField | null;
|
|
2299
|
+
}
|
|
2300
|
+
/** The guest-data form store: its state and its actions. */
|
|
2301
|
+
interface GuestForm extends Store<GuestFormState> {
|
|
2302
|
+
/** Typing. A shown error is re-judged and clears once valid; changing the country re-checks a
|
|
2303
|
+
* present postal code and phone (a German error on "1234 AB" clears when the guest picks NL). */
|
|
2304
|
+
set(field: GuestFormField, value: string): void;
|
|
2305
|
+
/** Leaving a field: the phone is normalized in place (the guest sees "+49 176 …"), a present value
|
|
2306
|
+
* is judged, an empty one waits for submit. */
|
|
2307
|
+
blur(field: GuestFormField): void;
|
|
2308
|
+
/** Judge everything. Returns the contact for `book()`, or null with every error shown and
|
|
2309
|
+
* `firstInvalid` set. Warnings never block. */
|
|
2310
|
+
submit(): BookingContact | null;
|
|
2311
|
+
/** Stop publishing: later calls change nothing. */
|
|
2312
|
+
dispose(): void;
|
|
2313
|
+
}
|
|
2314
|
+
/** The guest data form store. */
|
|
2315
|
+
type CreateGuestForm = (options: GuestFormOptions) => GuestForm;
|
|
2316
|
+
/** See {@link CreateGuestForm}. */
|
|
2317
|
+
export declare const createGuestForm: CreateGuestForm;
|
|
893
2318
|
//#endregion
|
|
894
2319
|
//#region src/checkout/structure.d.ts
|
|
895
|
-
/**
|
|
896
|
-
*
|
|
897
|
-
|
|
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
|
-
* checked by the platform's publish gate for imported and custom checkouts
|
|
906
|
-
* alike.
|
|
907
|
-
*
|
|
908
|
-
* Two halves:
|
|
909
|
-
* - `CHECKOUT_PARTS`: the `data-part` vocabulary. Imported components carry
|
|
910
|
-
* these attributes; a site's own markup must keep them so the gate (and
|
|
911
|
-
* a site's own CSS) can find the elements.
|
|
912
|
-
* - `CHECKOUT_STRUCTURE` + `assertCheckoutStructure(root)`: the requirements
|
|
913
|
-
* and a pure DOM check the smoke suite runs against the rendered checkout
|
|
914
|
-
* (a browser page or a jsdom document). Every requirement names the legal
|
|
915
|
-
* basis it exists for, so a failure message tells the reader WHY.
|
|
916
|
-
*/
|
|
917
|
-
declare const CHECKOUT_PARTS: {
|
|
918
|
-
/** The one place the guest is told the price they are about to owe. */
|
|
2320
|
+
/** The legal `data-part` vocabulary. Imported components carry it; a custom checkout must keep it.
|
|
2321
|
+
* Style-only hooks are not here (components `BOOKING_PARTS`). Renaming a part is a major. */
|
|
2322
|
+
export declare const CHECKOUT_PARTS: {
|
|
2323
|
+
/** The price the guest is about to owe (the summary, and directly above the order button). */
|
|
919
2324
|
readonly priceTotal: "checkout-price-total";
|
|
920
|
-
/** The
|
|
2325
|
+
/** The insurance premium: a second figure beside each total, never summed into it. */
|
|
2326
|
+
readonly insurancePremium: "checkout-insurance-premium";
|
|
2327
|
+
/** The order button. Carries `data-booking-kind`. */
|
|
921
2328
|
readonly submit: "checkout-submit";
|
|
922
|
-
/** The terms
|
|
2329
|
+
/** The terms notice, or the terms checkbox's label (AGB accepted, privacy information noted). */
|
|
923
2330
|
readonly terms: "checkout-terms";
|
|
924
|
-
/**
|
|
2331
|
+
/** Every consent checkbox (terms, privacy acknowledgement, additional terms). */
|
|
2332
|
+
readonly consent: "checkout-consent";
|
|
2333
|
+
/** One link per pre-contractual insurance document (§ 7 VVG). */
|
|
925
2334
|
readonly insuranceDocument: "checkout-insurance-document";
|
|
926
2335
|
/** The guest's receipt confirmation for those documents. */
|
|
927
2336
|
readonly insuranceConfirm: "checkout-insurance-confirm";
|
|
928
2337
|
};
|
|
2338
|
+
/** One value of `CHECKOUT_PARTS`. */
|
|
929
2339
|
type CheckoutPart = (typeof CHECKOUT_PARTS)[keyof typeof CHECKOUT_PARTS];
|
|
930
|
-
/**
|
|
931
|
-
*
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
readonly description: "when an insurance confirmation is rendered, at least one insurance document precedes it in DOM order";
|
|
954
|
-
};
|
|
955
|
-
};
|
|
956
|
-
type CheckoutRequirement = keyof typeof CHECKOUT_STRUCTURE;
|
|
2340
|
+
/** The attribute on the order button that names its kind ("booking" | "option"); absent reads as a
|
|
2341
|
+
* booking, so a button can only claim the option wording by saying it is one. */
|
|
2342
|
+
export declare const BOOKING_KIND_ATTRIBUTE: "data-booking-kind";
|
|
2343
|
+
/** The label classes the order button's visible text must match, per kind.
|
|
2344
|
+
* booking: "(zahlungspflichtig|kostenpflichtig) (buchen|bestellen|reservieren)", "jetzt kaufen",
|
|
2345
|
+
* "(book|order) with obligation to pay", "book and pay", "pay now", "buy now";
|
|
2346
|
+
* option: "unverbindlich (reservieren|anfragen)", "reserve without obligation",
|
|
2347
|
+
* "non-binding reservation".
|
|
2348
|
+
* A site may re-voice the label, but only within its class. The English and option classes await
|
|
2349
|
+
* a legal review (no case law covers them). */
|
|
2350
|
+
type SubmitWording = Readonly<Record<BookingKind, RegExp>>;
|
|
2351
|
+
/** One requirement: its legal basis and what it checks. */
|
|
2352
|
+
interface StructureRequirement {
|
|
2353
|
+
/** The legal basis ("§ 312j Abs. 3 BGB"). */
|
|
2354
|
+
readonly basis: string;
|
|
2355
|
+
/** What the requirement checks, in one sentence. */
|
|
2356
|
+
readonly description: string;
|
|
2357
|
+
}
|
|
2358
|
+
/** Every requirement `assertCheckoutStructure` checks, by id. */
|
|
2359
|
+
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>>;
|
|
2360
|
+
/** A requirement id. */
|
|
2361
|
+
type CheckoutRequirement = keyof CheckoutStructure;
|
|
2362
|
+
/** One failed requirement, naming its legal basis so a failure says why. */
|
|
957
2363
|
interface StructureViolation {
|
|
2364
|
+
/** The failed requirement's id. */
|
|
958
2365
|
requirement: CheckoutRequirement;
|
|
2366
|
+
/** Its legal basis. */
|
|
959
2367
|
basis: string;
|
|
2368
|
+
/** What was found instead (the offending element or text). */
|
|
960
2369
|
detail: string;
|
|
961
2370
|
}
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
declare const
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
max: number;
|
|
973
|
-
}
|
|
974
|
-
declare const NIGHT_RANGE_KEYS: readonly ["minNights", "maxNights", "nights_min", "nights_max"];
|
|
975
|
-
declare function parseNightRange(params: URLSearchParams): NightRange | null;
|
|
976
|
-
//#endregion
|
|
977
|
-
//#region src/format.d.ts
|
|
978
|
-
/**
|
|
979
|
-
* Display tidy for an SDK pre-formatted German-locale price ("1.234,00 €").
|
|
980
|
-
* Nearly every stay is whole euros, so the trailing ",00" is noise — dropped only
|
|
981
|
-
* when it is the zero-cents group at the end, so real cents ("1.234,50 €") stay.
|
|
982
|
-
* A real value formatted for display, nothing fabricated; other formats pass.
|
|
983
|
-
*/
|
|
984
|
-
declare function tidyPrice(formatted: string): string;
|
|
2371
|
+
/** A pure DOM check over a rendered checkout (`document` in a browser, a jsdom document in a test).
|
|
2372
|
+
* Returns every violation; empty = compliant. Everything is judged relative to the one order
|
|
2373
|
+
* button, in DOM order (a sticky bottom bar after the body is fine while the total, terms and
|
|
2374
|
+
* consents precede it). The insurance rules apply only when a confirmation control is rendered:
|
|
2375
|
+
* the duty attaches to concluding an insurance contract. */
|
|
2376
|
+
type AssertCheckoutStructure = (root: ParentNode) => StructureViolation[];
|
|
2377
|
+
export declare const SUBMIT_WORDING: SubmitWording;
|
|
2378
|
+
export declare const CHECKOUT_STRUCTURE: CheckoutStructure;
|
|
2379
|
+
/** See {@link AssertCheckoutStructure}. */
|
|
2380
|
+
export declare const assertCheckoutStructure: AssertCheckoutStructure;
|
|
985
2381
|
//#endregion
|
|
986
|
-
//#region src/
|
|
987
|
-
/**
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
*
|
|
993
|
-
*
|
|
994
|
-
*
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
* This runs where `priceByDay` is BUILT rather than where a cell renders it, so
|
|
1009
|
-
* every calendar fed from this map agrees: the widget's picker and the
|
|
1010
|
-
* checkout's copy, which reads the same shape.
|
|
1011
|
-
*/
|
|
1012
|
-
declare function nightlyPrice(formatted: string, locale?: Locale): string;
|
|
1013
|
-
type VofficeSdk$1 = WebsiteSdk;
|
|
1014
|
-
interface InitialAvailability {
|
|
1015
|
-
/** ISO dates (`YYYY-MM-DD`) the SDK flags as a valid check-in/arrival day. */
|
|
1016
|
-
canStart: Set<string>;
|
|
1017
|
-
/** ISO dates the rental is genuinely unbookable on (booked / closed / past the
|
|
1018
|
-
* bookable horizon — the SDK's "hard" statuses). The picker STRIKES these
|
|
1019
|
-
* through. A disabled day NOT in this set is only blocked by a rule for the
|
|
1020
|
-
* current phase (e.g. check-in-not-allowed) and is merely dimmed — it could
|
|
1021
|
-
* work in another context. */
|
|
1022
|
-
hardBlocked: Set<string>;
|
|
1023
|
-
/** Per-night price by ISO date — the SDK's localized string, display-tidied
|
|
1024
|
-
* to drop the noise ",00" (e.g. "120 €"). Only days the SDK prices. */
|
|
1025
|
-
priceByDay: Map<string, string>;
|
|
1026
|
-
/** Localized reason a day is blocked, by ISO date (the SDK's own `tooltip`,
|
|
1027
|
-
* e.g. "An diesem Tag ist keine Anreise möglich."). */
|
|
1028
|
-
tooltipByDay: Map<string, string>;
|
|
1029
|
-
/** Occupancy caps the SDK states for this rental (null when omitted). The
|
|
1030
|
-
* picker caps the guest stepper at these so a quote can't fail on too many
|
|
1031
|
-
* guests; the below-minimum case is left to the quote (the SDK is the
|
|
1032
|
-
* arbiter of a bookable party), so `minPersons` isn't surfaced here. */
|
|
1033
|
-
maxPersons: number | null;
|
|
1034
|
-
maxAdults: number | null;
|
|
2382
|
+
//#region src/forms/contact.d.ts
|
|
2383
|
+
/** The built-in fields: the contact API has a slot for each (names follow the SDK's paths). */
|
|
2384
|
+
type ContactFieldName = "salutation" | "forename" | "surname" | "email" | "phone" | "country" | "street" | "postalCode" | "city" | "message";
|
|
2385
|
+
/** Grid width of a field on wide screens (one column below the form's breakpoint). */
|
|
2386
|
+
type FieldWidth = "full" | "half";
|
|
2387
|
+
/** Value rules a spec may declare. Judged only on a present value; required-ness is its own check
|
|
2388
|
+
* with its own message. `pattern` matches the WHOLE value (anchored, `v` flag, as the browser's
|
|
2389
|
+
* `pattern` attribute). `min`/`max` compare numbers for a number field and ISO days for a date field
|
|
2390
|
+
* (`"today"` is today); `step` counts from `min` (default 0). */
|
|
2391
|
+
interface ValueConstraints {
|
|
2392
|
+
/** Lowest value: a number for a number field, an ISO day or "today" for a date field. */
|
|
2393
|
+
readonly min?: number | string;
|
|
2394
|
+
/** Highest value, in the same terms as `min`. */
|
|
2395
|
+
readonly max?: number | string;
|
|
2396
|
+
/** A number field's step, counted from `min` (default 0). */
|
|
2397
|
+
readonly step?: number;
|
|
2398
|
+
/** A regular expression the whole value must match (anchored, `v` flag). */
|
|
2399
|
+
readonly pattern?: string;
|
|
2400
|
+
/** Most characters. */
|
|
2401
|
+
readonly maxLength?: number;
|
|
2402
|
+
/** Shown when a present value fails a constraint; default `messages.fieldInvalid`. */
|
|
2403
|
+
readonly invalidMessage?: string;
|
|
1035
2404
|
}
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
/**
|
|
1041
|
-
*
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
/**
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
/**
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
declare function fetchInitialAvailability(sdk: VofficeSdk$1, rentalId: string, calculateFromDate: string, calculateThroughDate: string, locale: Locale): Promise<InitialAvailability>;
|
|
1053
|
-
/** Possible check-out days for a chosen `selectedStartDate`. */
|
|
1054
|
-
declare function fetchEndAvailability(sdk: VofficeSdk$1, rentalId: string, calculateFromDate: string, calculateThroughDate: string, selectedStartDate: string, locale: Locale): Promise<EndAvailability>;
|
|
1055
|
-
//#endregion
|
|
1056
|
-
//#region src/search.d.ts
|
|
1057
|
-
type VofficeSdk = WebsiteSdk;
|
|
1058
|
-
interface SearchImage {
|
|
1059
|
-
src: string;
|
|
1060
|
-
srcset?: string | undefined;
|
|
1061
|
-
alt: string;
|
|
2405
|
+
/** A built-in field with one or more decisions changed. */
|
|
2406
|
+
interface BuiltInFieldSpec extends ValueConstraints {
|
|
2407
|
+
/** The built-in field this spec changes. */
|
|
2408
|
+
readonly field: ContactFieldName;
|
|
2409
|
+
/** Default: salutation, forename, surname, email, country and message are required; phone, street,
|
|
2410
|
+
* postalCode and city are optional. */
|
|
2411
|
+
readonly required?: boolean;
|
|
2412
|
+
/** Replaces the copy label ("Telefon (tagsüber)", "Name" on a surname-only form). */
|
|
2413
|
+
readonly label?: string;
|
|
2414
|
+
/** Default "full" ("half" pairs two fields on wide screens). */
|
|
2415
|
+
readonly width?: FieldWidth;
|
|
2416
|
+
/** `salutation` only: the choices offered, in order. Default all three. */
|
|
2417
|
+
readonly options?: readonly Salutation[];
|
|
2418
|
+
/** `salutation`: a `Salutation`; `country`: a `CountryCode`; others: text. An answer chosen in
|
|
2419
|
+
* advance, still editable ("unspecified" preselected is an answer, not an absence). */
|
|
2420
|
+
readonly defaultValue?: string;
|
|
1062
2421
|
}
|
|
1063
|
-
/** One
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
* the same period together, the way `mapAlternativeEntry` already resolved them
|
|
1070
|
-
* for the single window it used to render. A window whose dates the feed omits
|
|
1071
|
-
* has no label and never reaches this list; one the feed does not price shows
|
|
1072
|
-
* no price rather than a placeholder. */
|
|
1073
|
-
interface AlternativePeriod {
|
|
1074
|
-
/** „31. Aug - 4. Sep", composed from this window's own start and end. */
|
|
1075
|
-
label: string;
|
|
1076
|
-
/** The detail-page link carrying THIS window's dates, so the rental opens on
|
|
1077
|
-
* the stay the row names rather than on the user's (unmatched) query. */
|
|
1078
|
-
href: string;
|
|
1079
|
-
/** This window's `formattedTotal`, verbatim. null when the feed omits it. */
|
|
1080
|
-
priceLabel: string | null;
|
|
1081
|
-
/** This window's length in nights. null when the feed omits a date. */
|
|
1082
|
-
nights: number | null;
|
|
1083
|
-
/** True when THIS window is discounted — the presence of the SDK's `discount`
|
|
1084
|
-
* block on this period, nothing inferred. Carried so the three discount facts
|
|
1085
|
-
* arrive together from `mapDiscount`, exactly as they do on the card. */
|
|
1086
|
-
isOffer: boolean;
|
|
1087
|
-
/** This window's pre-discount total, for the struck-through "was" price. */
|
|
1088
|
-
offerOriginalPriceLabel: string | null;
|
|
1089
|
-
/** This window's promotions, the PMS's own labels, verbatim. `amount` is
|
|
1090
|
-
* LINE-level and must not be printed beside a total — see the note on
|
|
1091
|
-
* `SearchResultItem.offers`. */
|
|
1092
|
-
offers: {
|
|
1093
|
-
label: string;
|
|
1094
|
-
amount: string | null;
|
|
1095
|
-
}[];
|
|
2422
|
+
/** One option of a select extra; `label` is what the reader sees AND what the message line says. */
|
|
2423
|
+
interface SelectOption {
|
|
2424
|
+
/** What the value is stored as (and sent). */
|
|
2425
|
+
readonly value: string;
|
|
2426
|
+
/** What the reader sees, and what the message line says. */
|
|
2427
|
+
readonly label: string;
|
|
1096
2428
|
}
|
|
1097
|
-
interface
|
|
1098
|
-
/**
|
|
1099
|
-
|
|
1100
|
-
/**
|
|
1101
|
-
|
|
1102
|
-
/**
|
|
1103
|
-
|
|
1104
|
-
/**
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
/**
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
/**
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
/**
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
/**
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
2429
|
+
interface ExtraBase extends ValueConstraints {
|
|
2430
|
+
/** Input name: `[a-z][a-zA-Z0-9]*`, unique, not a `ContactFieldName`. */
|
|
2431
|
+
readonly extra: string;
|
|
2432
|
+
/** The field's label (also the default title of its message line). */
|
|
2433
|
+
readonly label: string;
|
|
2434
|
+
/** Default false. A required checkbox must be ticked. */
|
|
2435
|
+
readonly required?: boolean;
|
|
2436
|
+
/** What the value is titled inside the message; default `label`. */
|
|
2437
|
+
readonly messagePrefix?: string;
|
|
2438
|
+
/** The control's placeholder. Default none. */
|
|
2439
|
+
readonly placeholder?: string;
|
|
2440
|
+
/** Default "full" ("half" pairs two fields on wide screens). */
|
|
2441
|
+
readonly width?: FieldWidth;
|
|
2442
|
+
/** Text fields: the starting text; select: an option value; checkbox: "true" ticks it. */
|
|
2443
|
+
readonly defaultValue?: string;
|
|
2444
|
+
/** Fill from this URL query parameter (`prefillFromParams`). Text, number, date and select extras
|
|
2445
|
+
* only; built-in fields are personal data, and a URL is logged and shared. */
|
|
2446
|
+
readonly prefillParam?: string;
|
|
2447
|
+
}
|
|
2448
|
+
/** A field the contact API has no slot for. Its answer becomes one line of the message:
|
|
2449
|
+
* text/multiline/email/tel/number "<prefix>: <value>" (only when answered)
|
|
2450
|
+
* date "<prefix>: <formatIsoDate(value)>"
|
|
2451
|
+
* select "<prefix>: <option label>"
|
|
2452
|
+
* checkbox "<prefix>: <checkboxYes|checkboxNo>", ALWAYS written (both answers
|
|
2453
|
+
* are worth having to the reader in the PMS)
|
|
2454
|
+
* period "<prefix>: <from> <periodSeparator> <to>" (a missing end is
|
|
2455
|
+
* `periodOpen`), or "<prefix>: <periodFlexible>" */
|
|
2456
|
+
type ExtraFieldSpec = (ExtraBase & {
|
|
2457
|
+
readonly kind?: "text" | "multiline" | "email" | "tel" | "number" | "date";
|
|
2458
|
+
}) | (ExtraBase & {
|
|
2459
|
+
readonly kind: "select";
|
|
2460
|
+
readonly options: readonly SelectOption[];
|
|
2461
|
+
}) | (ExtraBase & {
|
|
2462
|
+
readonly kind: "checkbox";
|
|
2463
|
+
}) | (ExtraBase & {
|
|
2464
|
+
readonly kind: "period";
|
|
2465
|
+
/** Offer "dates are flexible" (the dates then do not count). Default false. */
|
|
2466
|
+
readonly allowFlexible?: boolean;
|
|
2467
|
+
});
|
|
2468
|
+
/** One entry of a form's field list, in render order:
|
|
2469
|
+
* "email" the built-in as it ships
|
|
2470
|
+
* { field: "phone", required } a built-in with one decision changed
|
|
2471
|
+
* { extra: "amount", kind, … } a field the API has no slot for
|
|
2472
|
+
* { group: "Objekt" } an inert heading over the fields that follow */
|
|
2473
|
+
type ContactFieldSpec = ContactFieldName | BuiltInFieldSpec | ExtraFieldSpec | {
|
|
2474
|
+
readonly group: string;
|
|
2475
|
+
};
|
|
2476
|
+
/** A period answer: ISO days or "" each. */
|
|
2477
|
+
interface PeriodValue {
|
|
2478
|
+
/** Arrival, or "" while not chosen. */
|
|
2479
|
+
readonly from: IsoDay | "";
|
|
2480
|
+
/** Departure, or "" while not chosen (an open end). */
|
|
2481
|
+
readonly to: IsoDay | "";
|
|
2482
|
+
/** The visitor ticked "dates are flexible": the dates do not count. */
|
|
2483
|
+
readonly flexible: boolean;
|
|
2484
|
+
}
|
|
2485
|
+
/** Text-like fields hold a string, a checkbox a boolean, a period a `PeriodValue`. */
|
|
2486
|
+
type ContactValue = string | boolean | PeriodValue;
|
|
2487
|
+
/** Every field's value by input name. */
|
|
2488
|
+
type ContactValues = Readonly<Record<string, ContactValue>>;
|
|
2489
|
+
/** Every string the rules read; the merged `ContactFormCopy` of the components satisfies it.
|
|
2490
|
+
* Templates: `{label}` the field label, `{email}` the support inbox, `{max}` the limit. */
|
|
2491
|
+
interface ContactFormMessages {
|
|
2492
|
+
/** The salutation field's label. */
|
|
2493
|
+
salutationLabel: string;
|
|
2494
|
+
/** The forename field's label. */
|
|
2495
|
+
forenameLabel: string;
|
|
2496
|
+
/** The surname field's label. */
|
|
2497
|
+
surnameLabel: string;
|
|
2498
|
+
/** The email field's label. */
|
|
2499
|
+
emailLabel: string;
|
|
2500
|
+
/** The phone field's label. */
|
|
2501
|
+
phoneLabel: string;
|
|
2502
|
+
/** The country field's label. */
|
|
2503
|
+
countryLabel: string;
|
|
2504
|
+
/** The street field's label. */
|
|
2505
|
+
streetLabel: string;
|
|
2506
|
+
/** The postal code field's label. */
|
|
2507
|
+
postalCodeLabel: string;
|
|
2508
|
+
/** The city field's label. */
|
|
2509
|
+
cityLabel: string;
|
|
2510
|
+
/** The message field's label. */
|
|
2511
|
+
messageLabel: string;
|
|
2512
|
+
/** A required salutation left empty. */
|
|
2513
|
+
salutationRequired: string;
|
|
2514
|
+
/** A required forename left empty. */
|
|
2515
|
+
forenameRequired: string;
|
|
2516
|
+
/** A required surname left empty. */
|
|
2517
|
+
surnameRequired: string;
|
|
2518
|
+
/** A required email left empty. */
|
|
2519
|
+
emailRequired: string;
|
|
2520
|
+
/** A required phone left empty. */
|
|
2521
|
+
phoneRequired: string;
|
|
2522
|
+
/** A required country left empty. */
|
|
2523
|
+
countryRequired: string;
|
|
2524
|
+
/** A required street left empty. */
|
|
2525
|
+
streetRequired: string;
|
|
2526
|
+
/** A required postal code left empty. */
|
|
2527
|
+
postalCodeRequired: string;
|
|
2528
|
+
/** A required city left empty. */
|
|
2529
|
+
cityRequired: string;
|
|
2530
|
+
/** A required message left empty. */
|
|
2531
|
+
messageRequired: string;
|
|
2532
|
+
/** An extra left empty ("Bitte {label} ausfüllen."). */
|
|
2533
|
+
extraRequired: Template<"label">;
|
|
2534
|
+
/** A present value that fails a declared constraint ("Bitte {label} prüfen."). */
|
|
2535
|
+
fieldInvalid: Template<"label">;
|
|
2536
|
+
/** A present email that is no address. */
|
|
2537
|
+
emailInvalid: string;
|
|
2538
|
+
/** A period whose end precedes its start, or whose start lies in the past. */
|
|
2539
|
+
periodInvalid: string;
|
|
2540
|
+
/** The composed message exceeds the API limit ({max}). */
|
|
2541
|
+
messageTooLong: Template<"max">;
|
|
2542
|
+
/** The line above the button when the SDK rejected fields that are marked inline. */
|
|
2543
|
+
fixMarkedFields: string;
|
|
2544
|
+
/** The fallback failure line; contains {email}. */
|
|
2545
|
+
sendFailed: Template<"email">;
|
|
2546
|
+
/** The fallback failure line when the site publishes no inbox (`supportEmail: null`). */
|
|
2547
|
+
sendFailedNoEmail: string;
|
|
2548
|
+
/** A ticked checkbox extra in the message line ("ja"). */
|
|
2549
|
+
checkboxYes: string;
|
|
2550
|
+
/** An unticked checkbox extra in the message line ("nein"). */
|
|
2551
|
+
checkboxNo: string;
|
|
2552
|
+
/** A period extra answered with "dates are flexible". */
|
|
2553
|
+
periodFlexible: string;
|
|
2554
|
+
/** A period extra's missing end in the message line. */
|
|
2555
|
+
periodOpen: string;
|
|
2556
|
+
/** Between a period's two days in the message line ("bis"). */
|
|
2557
|
+
periodSeparator: string;
|
|
2558
|
+
}
|
|
2559
|
+
/** The control a field renders. "salutation" and "country" are the two selects whose options the
|
|
2560
|
+
* UI resolves itself (copy labels; the build-time country register). */
|
|
2561
|
+
type ContactControl = "text" | "multiline" | "email" | "tel" | "number" | "date" | "select" | "checkbox" | "period" | "salutation" | "country";
|
|
2562
|
+
/** A spec after defaults: what validation, payload and render all walk, so they cannot disagree
|
|
2563
|
+
* about what the form contains. */
|
|
2564
|
+
type ResolvedContactItem =
|
|
2565
|
+
/** An inert heading over the fields that follow (`{ group }` in the spec list). */
|
|
2566
|
+
{
|
|
2567
|
+
readonly type: "group";
|
|
2568
|
+
readonly label: string;
|
|
2569
|
+
} | {
|
|
2570
|
+
readonly type: "field";
|
|
2571
|
+
/** The input name (a built-in's name or the extra's). */
|
|
2572
|
+
readonly name: string;
|
|
2573
|
+
/** The built-in it fills, or null for an extra. */
|
|
2574
|
+
readonly builtIn: ContactFieldName | null;
|
|
2575
|
+
/** The control to render. */
|
|
2576
|
+
readonly control: ContactControl;
|
|
2577
|
+
/** The label shown (the spec's, else the copy's). */
|
|
2578
|
+
readonly label: string;
|
|
2579
|
+
/** Whether an empty value stops the submit. */
|
|
2580
|
+
readonly required: boolean;
|
|
2581
|
+
/** "full" or "half" on wide screens. */
|
|
2582
|
+
readonly width: FieldWidth;
|
|
2583
|
+
/** The `autocomplete` token of a built-in (personal data the browser may fill), or null. */
|
|
2584
|
+
readonly autoComplete: string | null;
|
|
2585
|
+
/** The control's placeholder, or null. */
|
|
2586
|
+
readonly placeholder: string | null;
|
|
2587
|
+
/** A select extra's options; null for every other control. */
|
|
2588
|
+
readonly options: readonly SelectOption[] | null;
|
|
2589
|
+
/** The salutation choices in order; null for every other control. */
|
|
2590
|
+
readonly salutations: readonly Salutation[] | null;
|
|
2591
|
+
/** A period extra offers "dates are flexible". */
|
|
2592
|
+
readonly allowFlexible: boolean;
|
|
2593
|
+
/** Extras: the title of its message line; null for built-ins. */
|
|
2594
|
+
readonly messagePrefix: string | null;
|
|
2595
|
+
/** The value rules judged on a present value. */
|
|
2596
|
+
readonly constraints: ValueConstraints;
|
|
2597
|
+
/** The URL query parameter that fills it (extras only), or null. */
|
|
2598
|
+
readonly prefillParam: string | null;
|
|
2599
|
+
};
|
|
2600
|
+
/** A resolved field (the non-group member of `ResolvedContactItem`). */
|
|
2601
|
+
type ResolvedContactField = Extract<ResolvedContactItem, {
|
|
2602
|
+
readonly type: "field";
|
|
2603
|
+
}>;
|
|
2604
|
+
/** What a contact form is built from; plain data except the clock. */
|
|
2605
|
+
interface ContactFormOptions {
|
|
2606
|
+
/** The SDK locale of the request and the language of its validation messages. */
|
|
2607
|
+
readonly locale: Locale;
|
|
2608
|
+
/** Recorded on the request in the PMS; names the channel ("Anfrage Eigentümer"). ≤ 200 characters. */
|
|
2609
|
+
readonly subject: string;
|
|
2610
|
+
/** The field list in render order. */
|
|
2611
|
+
readonly fields: readonly ContactFieldSpec[];
|
|
2612
|
+
/** Every string the rules read (the components' merged copy satisfies it). */
|
|
2613
|
+
readonly messages: ContactFormMessages;
|
|
2614
|
+
/** Named in the failure line. Required, no default: a default would print one agency's inbox on
|
|
2615
|
+
* another's site. `null` drops that half of the line (no publishable inbox). */
|
|
2616
|
+
readonly supportEmail: string | null;
|
|
2617
|
+
/** Prefill (`prefillFromParams`) and the preselected country (`detectCountry`); laid over the
|
|
2618
|
+
* fields' own defaults. */
|
|
2619
|
+
readonly initialValues?: ContactValues;
|
|
2620
|
+
/** The clock for "today" constraints and periods, for tests. Default `() => new Date()`. */
|
|
2621
|
+
readonly now?: () => Date;
|
|
2622
|
+
}
|
|
2623
|
+
/** editing → submitting → sent (or back to editing on a failure). */
|
|
2624
|
+
type ContactPhase = "editing" | "submitting" | "sent";
|
|
2625
|
+
/** What a UI renders. */
|
|
2626
|
+
interface ContactFormState {
|
|
2627
|
+
/** The resolved field list (groups and fields), in render order. */
|
|
2628
|
+
readonly items: readonly ResolvedContactItem[];
|
|
2629
|
+
/** Every field's current value by input name. */
|
|
2630
|
+
readonly values: ContactValues;
|
|
2631
|
+
/** Field name → message, in list order; set by submit, by blur (email format) and by SDK issues. */
|
|
2632
|
+
readonly errors: Readonly<Record<string, string>>;
|
|
2633
|
+
/** Where the form stands. */
|
|
2634
|
+
readonly phase: ContactPhase;
|
|
2635
|
+
/** The line above the button after a failed submit (summary, unmapped SDK issues, or the
|
|
2636
|
+
* send-failed fallback). */
|
|
2637
|
+
readonly submitError: string | null;
|
|
2638
|
+
/** The first failed field after a failed submit; the UI focuses it once per `nonce`, after the
|
|
2639
|
+
* render that marks it (so the error is announced with the field). */
|
|
2640
|
+
readonly focusTarget: {
|
|
2641
|
+
readonly name: string;
|
|
2642
|
+
readonly nonce: number;
|
|
1209
2643
|
} | null;
|
|
2644
|
+
/** `contactSpecProblems(fields)`: reported once by the UI (console.warn), never thrown. */
|
|
2645
|
+
readonly specProblems: readonly string[];
|
|
1210
2646
|
}
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
*
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
/**
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
*
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
*
|
|
1244
|
-
* `options.rentalsPath` is the path the site serves its rental detail pages
|
|
1245
|
-
* under — the part of every result `href` before the slug. Routing is the
|
|
1246
|
-
* site's decision (a locale prefix like `/de/unterkuenfte`, another segment),
|
|
1247
|
-
* so it arrives as an option the way `SearchBar.action` and `Quote.checkoutPath`
|
|
1248
|
-
* do; `locale` never implies it. Defaults to `DEFAULT_RENTALS_PATH`.
|
|
1249
|
-
*/
|
|
1250
|
-
declare function runLiveSearch(sdk: VofficeSdk, searchString: string, locale: Locale, cursor?: string | null, options?: LiveSearchOptions): Promise<SearchPayload>;
|
|
2647
|
+
/** What `submit()` resolves to. `rejected` = the SDK refused the input (issues routed); `failed` =
|
|
2648
|
+
* transport, backend or a lazy SDK that did not load. */
|
|
2649
|
+
type ContactSubmitOutcome = {
|
|
2650
|
+
readonly ok: true;
|
|
2651
|
+
} | {
|
|
2652
|
+
readonly ok: false;
|
|
2653
|
+
readonly reason: "busy" | "invalid" | "rejected" | "failed";
|
|
2654
|
+
};
|
|
2655
|
+
/** The actions a UI calls; stable for the form's lifetime. */
|
|
2656
|
+
interface ContactFormActions {
|
|
2657
|
+
/** Typing or choosing: sets a value; a shown error of that field is re-judged. */
|
|
2658
|
+
setValue(name: string, value: ContactValue): void;
|
|
2659
|
+
/** Leaving a field: re-checks the email format; clears that field's shown error once it is fixed. */
|
|
2660
|
+
blur(name: string): void;
|
|
2661
|
+
/** Validate, compose, submit. USER-initiated only. A second call while submitting resolves
|
|
2662
|
+
* `{ ok: false, reason: "busy" }` (double click, second Enter). */
|
|
2663
|
+
submit(): Promise<ContactSubmitOutcome>;
|
|
2664
|
+
/** Back to the initial values and `editing` ("send another message"). */
|
|
2665
|
+
reset(): void;
|
|
2666
|
+
/** Copy, locale, subject or inbox changed (props of the UI); values are kept. The fields and the
|
|
2667
|
+
* SDK source are read from the first options only. */
|
|
2668
|
+
update(options: Partial<Pick<ContactFormOptions, "messages" | "locale" | "subject" | "supportEmail">>): void;
|
|
2669
|
+
/** Stop publishing: later actions and a still pending submit change nothing. */
|
|
2670
|
+
dispose(): void;
|
|
2671
|
+
}
|
|
2672
|
+
/** The contact form store: its state and its actions. */
|
|
2673
|
+
type ContactForm = Store<ContactFormState> & ContactFormActions;
|
|
2674
|
+
/** The contact form store. The SDK source may be lazy (`loadSharedSdk`): it is awaited on the first
|
|
2675
|
+
* submit only, so a contact page ships no SDK until someone sends a message. */
|
|
2676
|
+
type CreateContactForm = (sdk: SdkSource, options: ContactFormOptions) => ContactForm;
|
|
2677
|
+
/** See {@link CreateContactForm}. */
|
|
2678
|
+
export declare const createContactForm: CreateContactForm;
|
|
1251
2679
|
//#endregion
|
|
1252
|
-
//#region src/
|
|
1253
|
-
/**
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
/** The
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
2680
|
+
//#region src/forms/contact-fields.d.ts
|
|
2681
|
+
/** The contact kit's set without its owner-location field (a site that wants one adds an extra). */
|
|
2682
|
+
export declare const DEFAULT_CONTACT_FIELDS: readonly ContactFieldName[];
|
|
2683
|
+
/** The API rejects an empty value for these (`validateContactSubmitInput`); `subject` comes from the
|
|
2684
|
+
* options, and `message` may be composed from extras alone. */
|
|
2685
|
+
export declare const CONTACT_ENDPOINT_REQUIRED: readonly ("salutation" | "surname" | "email" | "country" | "message")[];
|
|
2686
|
+
/** The API's length limits a form can exceed by composition (extras ride in the message). */
|
|
2687
|
+
export declare const CONTACT_LIMITS: {
|
|
2688
|
+
readonly message: 5000;
|
|
2689
|
+
readonly subject: 200;
|
|
2690
|
+
};
|
|
2691
|
+
/** Specs → items, labels from `messages` unless the spec sets one. Validation, the payload and the
|
|
2692
|
+
* render all walk this one list. */
|
|
2693
|
+
type ResolveContactFields = (fields: readonly ContactFieldSpec[], messages: ContactFormMessages) => readonly ResolvedContactItem[];
|
|
2694
|
+
/** Problems that make every submit fail or the form ambiguous, as developer-facing sentences: a
|
|
2695
|
+
* missing or optional endpoint-required built-in (`message` may be absent only when an extra always
|
|
2696
|
+
* writes a line: a required one or a checkbox), a duplicate name, an extra named like a built-in or
|
|
2697
|
+
* not `[a-z][a-zA-Z0-9]*`, a select without options, salutation `options` on another field, a
|
|
2698
|
+
* `prefillParam` on a checkbox or period extra (ignored), an invalid `pattern`. */
|
|
2699
|
+
type ContactSpecProblems = (fields: readonly ContactFieldSpec[]) => readonly string[];
|
|
2700
|
+
/** See {@link ResolveContactFields}. */
|
|
2701
|
+
export declare const resolveContactFields: ResolveContactFields;
|
|
2702
|
+
/** See {@link ContactSpecProblems}. */
|
|
2703
|
+
export declare const contactSpecProblems: ContactSpecProblems;
|
|
2704
|
+
//#endregion
|
|
2705
|
+
//#region src/forms/contact-issues.d.ts
|
|
2706
|
+
/** Issues with a path onto a PRESENT built-in become field errors (`title`→salutation,
|
|
2707
|
+
* `address.countryCode`→country, `address.streetAndHousenumber`→street,
|
|
2708
|
+
* `address.postalCode`→postalCode, `address.city`→city, the rest by name). Issues on fields the form
|
|
2709
|
+
* does not render, or without a path, join `submitError` (an invisible field error helps nobody).
|
|
2710
|
+
* Without issues: `sendFailed` with {email}, or `sendFailedNoEmail` when the site has no inbox.
|
|
2711
|
+
* `reason` is "rejected" when the SDK sent issues, else "failed". */
|
|
2712
|
+
type RouteContactIssues = (error: unknown, items: readonly ResolvedContactItem[], messages: ContactFormMessages, supportEmail: string | null) => RoutedContactIssues;
|
|
2713
|
+
/** What `routeContactIssues` returns. */
|
|
2714
|
+
interface RoutedContactIssues {
|
|
2715
|
+
/** Field name → the SDK's message, in list order. */
|
|
2716
|
+
readonly errors: Readonly<Record<string, string>>;
|
|
2717
|
+
/** The line above the button. */
|
|
2718
|
+
readonly submitError: string;
|
|
2719
|
+
/** "rejected": the SDK refused the input; "failed": transport or backend. */
|
|
2720
|
+
readonly reason: "rejected" | "failed";
|
|
1267
2721
|
}
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
declare function activeSort(search: string): SortValue;
|
|
1271
|
-
/**
|
|
1272
|
-
* Can this query be priced? Mirrors the SDK's own precondition: a fixed period
|
|
1273
|
-
* (`start`+`end`) or a flexible window (`dates`/`month`/`weekend`), AND
|
|
1274
|
-
* occupancy. Without both, the backend returns no totals and the SDK rejects
|
|
1275
|
-
* price ordering before it reaches the wire.
|
|
1276
|
-
*
|
|
1277
|
-
* Period keys count only with a value: the search's `buildQuery` drops empty
|
|
1278
|
-
* params (`?start=&end=`), so a key that is merely present would pass this
|
|
1279
|
-
* gate and still reach the SDK undated. `weekend` is the exception — it is a
|
|
1280
|
-
* presence-only flag on both sides.
|
|
1281
|
-
*/
|
|
1282
|
-
declare function isPricedQuery(search: string): boolean;
|
|
1283
|
-
/** Is this option selectable for the current query? */
|
|
1284
|
-
declare function isSortAvailable(option: SortOption, search: string): boolean;
|
|
1285
|
-
/**
|
|
1286
|
-
* The `sort` to hand the SDK for this URL, or `undefined` for the backend's
|
|
1287
|
-
* own order. Falls back to `undefined` (rather than throwing) when a price
|
|
1288
|
-
* sort survives in the URL after the dates were cleared.
|
|
1289
|
-
*/
|
|
1290
|
-
declare function resolveSdkSort(search: string): SearchSearchSort | undefined;
|
|
2722
|
+
/** See {@link RouteContactIssues}. */
|
|
2723
|
+
export declare const routeContactIssues: RouteContactIssues;
|
|
1291
2724
|
//#endregion
|
|
1292
|
-
//#region src/
|
|
1293
|
-
/**
|
|
1294
|
-
*
|
|
1295
|
-
*
|
|
1296
|
-
*
|
|
1297
|
-
*
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
2725
|
+
//#region src/forms/contact-payload.d.ts
|
|
2726
|
+
/** The `live.contact.submit` input. The message is composed in LIST ORDER around the `message` field:
|
|
2727
|
+
* extra lines listed before it form a block above the free text, lines listed after it a block
|
|
2728
|
+
* below; lines join with "\n", blocks with "\n\n" (whoever reads it in the PMS reads the period and
|
|
2729
|
+
* the headcount first when a form lists them first). The phone is sent as typed: this endpoint does
|
|
2730
|
+
* not constrain it (the booking endpoint does, so the checkout normalizes). */
|
|
2731
|
+
type BuildContactInput = (items: readonly ResolvedContactItem[], values: ContactValues, context: {
|
|
2732
|
+
readonly locale: Locale;
|
|
2733
|
+
readonly subject: string;
|
|
2734
|
+
readonly messages: ContactFormMessages;
|
|
2735
|
+
}) => ContactSubmitInput;
|
|
2736
|
+
/** See {@link BuildContactInput}. */
|
|
2737
|
+
export declare const buildContactInput: BuildContactInput;
|
|
2738
|
+
//#endregion
|
|
2739
|
+
//#region src/forms/contact-prefill.d.ts
|
|
2740
|
+
/** `prefillParam` values from a query string (the UI passes `new URLSearchParams(location.search)`):
|
|
2741
|
+
* trimmed, capped at 200 characters; a number must read as one, a date must be an ISO day, a select
|
|
2742
|
+
* value must be one of its options. The UI applies them to EMPTY fields only (a visitor may have
|
|
2743
|
+
* typed before the island hydrated); the value stays editable. */
|
|
2744
|
+
type PrefillFromParams = (items: readonly ResolvedContactItem[], params: URLSearchParams) => ContactValues;
|
|
2745
|
+
/** See {@link PrefillFromParams}. */
|
|
2746
|
+
export declare const prefillFromParams: PrefillFromParams;
|
|
2747
|
+
//#endregion
|
|
2748
|
+
//#region src/forms/contact-validate.d.ts
|
|
2749
|
+
/** Required, email format, declared constraints, periods, and the composed message length (an error
|
|
2750
|
+
* on `message`, or on the first extra when the form lists no message field). Returns field name →
|
|
2751
|
+
* message, in list order; empty when the values can be sent. */
|
|
2752
|
+
type ValidateContactValues = (items: readonly ResolvedContactItem[], values: ContactValues, messages: ContactFormMessages, context: {
|
|
2753
|
+
readonly locale: Locale;
|
|
2754
|
+
readonly subject: string;
|
|
2755
|
+
readonly today: IsoDay;
|
|
2756
|
+
}) => Readonly<Record<string, string>>;
|
|
2757
|
+
/** See {@link ValidateContactValues}. */
|
|
2758
|
+
export declare const validateContactValues: ValidateContactValues;
|
|
2759
|
+
//#endregion
|
|
2760
|
+
//#region src/forms/events.d.ts
|
|
2761
|
+
/** The window CustomEvent name. Renaming it is a major. */
|
|
2762
|
+
export declare const CONTACT_EVENT: "voffice:contact";
|
|
2763
|
+
/** The event detail. Carries the channel (`subject`) and never a field value: no personal data
|
|
2764
|
+
* reaches a listener (a tracker, a consent-less script). */
|
|
2765
|
+
type ContactEventDetail = {
|
|
2766
|
+
readonly type: "sent";
|
|
2767
|
+
readonly subject: string;
|
|
2768
|
+
};
|
|
2769
|
+
//#endregion
|
|
2770
|
+
//#region src/legal/fetch.d.ts
|
|
2771
|
+
/** One legal document: the PMS's subject (the page's own heading) and its sanitized blocks. */
|
|
2772
|
+
interface LegalDocument {
|
|
2773
|
+
/** The document's subject, as the PMS names it. */
|
|
2774
|
+
readonly title: string;
|
|
2775
|
+
/** The document as sanitized blocks. */
|
|
2776
|
+
readonly blocks: readonly RichBlock[];
|
|
1308
2777
|
}
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
2778
|
+
/** The two documents a checkout names; null = the workspace publishes none (or an empty one). */
|
|
2779
|
+
interface LegalDocuments {
|
|
2780
|
+
/** The terms (AGB), or null. */
|
|
2781
|
+
readonly terms: LegalDocument | null;
|
|
2782
|
+
/** The privacy notice, or null. */
|
|
2783
|
+
readonly privacy: LegalDocument | null;
|
|
1312
2784
|
}
|
|
1313
|
-
/**
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
*
|
|
1317
|
-
*
|
|
1318
|
-
|
|
1319
|
-
*/
|
|
1320
|
-
declare
|
|
1321
|
-
/** The dog-friendly search as the URL carries it: `pets=true` (or a count) and,
|
|
1322
|
-
* from an older vocabulary, `petsCount=N`. Read by the live search. */
|
|
1323
|
-
declare const PETS_PARAM = "pets";
|
|
1324
|
-
declare const PETS_COUNT_PARAM = "petsCount";
|
|
1325
|
-
declare function petsCountInQuery(search: string): number;
|
|
1326
|
-
//#endregion
|
|
1327
|
-
//#region src/regions.d.ts
|
|
1328
|
-
/**
|
|
1329
|
-
* The "Region" scope of the live search.
|
|
1330
|
-
*
|
|
1331
|
-
* "Region" is a v10 custom attribute (MULTI_SELECT, PUBLIC) set per rental in
|
|
1332
|
-
* v-office — e.g. Duhnen, Döse. The live search has no usable server-side key
|
|
1333
|
-
* for it: the SDK filters a custom attribute only on an exact raw match of its
|
|
1334
|
-
* stored JSON-array value, and registering the definition would fold a
|
|
1335
|
-
* `Region: ["…"]` string into every card and detail highlight (verified). So
|
|
1336
|
-
* `region` is dropped from the SDK query (search.ts, DROP_KEYS) and applied
|
|
1337
|
-
* here, client-side, against the per-rental regions the site hydrates from its
|
|
1338
|
-
* static catalogue at build time — the same channel as the other facts the
|
|
1339
|
-
* search projection does not carry.
|
|
1340
|
-
*/
|
|
1341
|
-
/** URL query key carrying the active region scope. Repeatable: several
|
|
1342
|
-
* `region=<name>` values OR together (show rentals in ANY selected region). */
|
|
1343
|
-
declare const REGION_PARAM = "region";
|
|
1344
|
-
/** The regions active in a URL query string: the `region` values, trimmed,
|
|
1345
|
-
* de-duplicated, order preserved. Absent or empty → `[]`. */
|
|
1346
|
-
declare function activeRegions(search: string): string[];
|
|
1347
|
-
/**
|
|
1348
|
-
* The results inside the region scope. A rental may carry several regions
|
|
1349
|
-
* (MULTI_SELECT) and matches when any of them is selected; with no active
|
|
1350
|
-
* region every result stays. A rental the map does not know is outside every
|
|
1351
|
-
* region, so it drops out of a scoped search rather than passing unseen.
|
|
1352
|
-
*/
|
|
1353
|
-
declare function scopeToRegions<T extends {
|
|
1354
|
-
id: string;
|
|
1355
|
-
}>(items: readonly T[], active: readonly string[], regionsByRentalId: (id: string) => readonly string[] | undefined): T[];
|
|
2785
|
+
/** v10: `static.documents.getTermsAndPrivacyPolicy` → `normalizeStructuredDocument` (a leading
|
|
2786
|
+
* headline that repeats the subject is dropped; the page draws its own). A document the workspace
|
|
2787
|
+
* does not publish, or whose data holds no content, is null. Throws on transport, logged with the
|
|
2788
|
+
* SDK's cause: a build never silently ships a checkout whose terms it could not read. v9: both null
|
|
2789
|
+
* without a call, until the SDK serves the hub's documents (CMS-1122). */
|
|
2790
|
+
type FetchLegalDocuments = (sdk: WebsiteSdk, locale: Locale) => Promise<LegalDocuments>;
|
|
2791
|
+
/** See {@link FetchLegalDocuments}. */
|
|
2792
|
+
export declare const fetchLegalDocuments: FetchLegalDocuments;
|
|
1356
2793
|
//#endregion
|
|
1357
|
-
export { ActiveFilter, AlternativePeriod,
|
|
2794
|
+
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 };
|