@v-office/website-headless 0.1.0 → 0.2.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 CHANGED
@@ -64,10 +64,10 @@ parameters — the rules live here, the words live in the site.
64
64
  ## Development
65
65
 
66
66
  ```bash
67
- pnpm install
67
+ pnpm install # at the repository root; website-sdk is a workspace link
68
68
  pnpm build # tsdown → dist/ (index, enhance; .mjs + .d.mts)
69
69
  pnpm check # tsc · oxlint · oxfmt --check
70
- pnpm test # vitest, jsdom environment
70
+ pnpm test # vitest, jsdom environment; then test/dist.node.mjs imports the build by name
71
71
  ```
72
72
 
73
73
  The tests cover the pure rules (validation, insurance gate, stay helpers,
@@ -78,8 +78,9 @@ DOM (jsdom); the SDKs use `node --test` because they do not.
78
78
 
79
79
  ## Publishing
80
80
 
81
- The **Publish** workflow (`.github/workflows/publishSDK.yml`, choice
82
- `website-headless`) publishes the exact version in `package.json` to npm after
83
- build, check and tests pass; it never bumps versions. Set the version by hand,
84
- merge, run the workflow. Consumers pin the exact version and receive updates
85
- through fleet rollouts (docs/website-platform/process/06-package-rollout.md).
81
+ See [docs/internal/releasing/overview.md](../../docs/internal/releasing/overview.md). `node .github/release/cli.mts
82
+ prepare website-headless <bump>` sets the version and scaffolds
83
+ `instructions/versions/<version>/` with the notes and the `release.json`
84
+ release facts; the `Release packages` workflow publishes what is on `main`
85
+ after build, check and tests pass. Consumers pin the exact version and receive
86
+ updates through fleet rollouts (docs/website-platform/process/06-package-rollout.md).
@@ -20,8 +20,8 @@
20
20
  * `{current}` shown) — the component renders the template from its copy, so
21
21
  * the enhancer carries no language of its own.
22
22
  */
23
- declare function initImageCarousels(selector?: string): void;
24
- declare function initRentalCarousel(viewport: HTMLElement): void;
23
+ export declare function initImageCarousels(selector?: string): void;
24
+ export declare function initRentalCarousel(viewport: HTMLElement): void;
25
25
  //#endregion
26
26
  //#region src/enhance/overlay.d.ts
27
27
  /**
@@ -66,11 +66,11 @@ declare function initRentalCarousel(viewport: HTMLElement): void;
66
66
  */
67
67
  /** Run `mutate` inside a view transition where the browser has one and the
68
68
  * visitor has not asked for less motion; run it directly otherwise. */
69
- declare function withViewTransition(mutate: () => void): void;
69
+ export declare function withViewTransition(mutate: () => void): void;
70
70
  /** Install the page's single pair of modality listeners. Safe to call often. */
71
- declare function trackInputModality(): void;
71
+ export declare function trackInputModality(): void;
72
72
  /** Focus `el` only when the last real input was a keyboard one. */
73
- declare function focusForKeyboard(el: HTMLElement | null): void;
73
+ export declare function focusForKeyboard(el: HTMLElement | null): void;
74
74
  /** Every selector the controller needs, stated by the caller. Each is scoped to
75
75
  * `root`, so two overlays on one page cannot reach into each other. */
76
76
  interface HistoryOverlayOptions {
@@ -93,7 +93,7 @@ interface HistoryOverlayOptions {
93
93
  }
94
94
  /** Bind one overlay. Returns silently when the markup is not on this page, so
95
95
  * a component whose overlay self-hides costs nothing. */
96
- declare function initHistoryOverlay(options: HistoryOverlayOptions): void;
96
+ export declare function initHistoryOverlay(options: HistoryOverlayOptions): void;
97
97
  //#endregion
98
98
  //#region src/enhance/gallery.d.ts
99
99
  /**
@@ -179,6 +179,6 @@ interface GalleryOptions {
179
179
  layers: GalleryLayer[];
180
180
  }
181
181
  /** Bind one gallery. Returns silently when its root is not on this page. */
182
- declare function initGallery(options: GalleryOptions): void;
182
+ export declare function initGallery(options: GalleryOptions): void;
183
183
  //#endregion
184
- export { type GalleryLayer, type GalleryOptions, type HistoryOverlayOptions, focusForKeyboard, initGallery, initHistoryOverlay, initImageCarousels, initRentalCarousel, trackInputModality, withViewTransition };
184
+ export type { GalleryLayer, GalleryOptions, HistoryOverlayOptions };
package/dist/index.d.mts CHANGED
@@ -8,31 +8,31 @@ import { BookingPaymentOption, BookingPaymentScheduleKind, GuestQuote, GuestQuot
8
8
  * package never depends on a framework; the adapters live in ./react and
9
9
  * friends.
10
10
  */
11
- type Listener<S> = (state: S) => void;
12
- type Unsubscribe = () => void;
13
- interface Store<S> {
11
+ export type Listener<S> = (state: S) => void;
12
+ export type Unsubscribe = () => void;
13
+ export interface Store<S> {
14
14
  getState(): S;
15
15
  subscribe(listener: Listener<S>): Unsubscribe;
16
16
  }
17
- interface WritableStore<S> extends Store<S> {
17
+ export interface WritableStore<S> extends Store<S> {
18
18
  /** Replaces the state and notifies every listener. Same reference = no-op. */
19
19
  setState(next: S): void;
20
20
  }
21
- declare function createStore<S>(initial: S): WritableStore<S>;
21
+ export declare function createStore<S>(initial: S): WritableStore<S>;
22
22
  //#endregion
23
23
  //#region src/sdk.d.ts
24
- type WebsiteSdk = ReturnType<typeof createWebsiteSDK>;
24
+ export type WebsiteSdk = ReturnType<typeof createWebsiteSDK>;
25
25
  /** Deployment stage: selects the public endpoint set below. */
26
- declare const STAGES: readonly ["development", "production"];
27
- type Stage = (typeof STAGES)[number];
26
+ export declare const STAGES: readonly ["development", "production"];
27
+ export type Stage = (typeof STAGES)[number];
28
28
  /** Public endpoints of the v10 backend per stage. */
29
- declare const VOFFICE_ENDPOINTS: Record<Stage, {
29
+ export declare const VOFFICE_ENDPOINTS: Record<Stage, {
30
30
  apiEndpoint: string;
31
31
  imageBaseUrl: string;
32
32
  searchEndpoint: string;
33
33
  }>;
34
34
  /** Public endpoints of the v9 backend per stage; API URLs derive from `apiBaseUrl`. */
35
- declare const HUB_ENDPOINTS: Record<Stage, {
35
+ export declare const HUB_ENDPOINTS: Record<Stage, {
36
36
  apiBaseUrl: string;
37
37
  imageProxyBaseUrl: string;
38
38
  }>;
@@ -40,7 +40,7 @@ declare const HUB_ENDPOINTS: Record<Stage, {
40
40
  * secret to protect. Serializable on purpose: an Astro island receives it as a
41
41
  * prop, so the site reads its env once (in .astro / a loader) and never inside
42
42
  * a component. */
43
- type SdkConfig = {
43
+ export type SdkConfig = {
44
44
  backend: "v9";
45
45
  imageProxyBaseUrl: string;
46
46
  v0ApiBaseUrl: string;
@@ -56,7 +56,7 @@ type SdkConfig = {
56
56
  };
57
57
  /** The three values a site actually decides — backend, stage, its token — as
58
58
  * a full `SdkConfig`, with the endpoints the platform owns. */
59
- declare function sdkConfigFor({ backend, stage, token }: {
59
+ export declare function sdkConfigFor({ backend, stage, token }: {
60
60
  backend: SdkConfig["backend"];
61
61
  stage: Stage;
62
62
  token: string;
@@ -65,7 +65,7 @@ declare function sdkConfigFor({ backend, stage, token }: {
65
65
  * overrides, custom attributes, highlight prioritization). The caller owns the
66
66
  * lifecycle and calls `await sdk.dispose()` when done. The API is rate-limited
67
67
  * (1/s), so a browser surface keeps ONE instance for its lifetime. */
68
- declare function createSdk(config: SdkConfig, options?: WebsiteSDKOptions): WebsiteSdk;
68
+ export declare function createSdk(config: SdkConfig, options?: WebsiteSDKOptions): WebsiteSdk;
69
69
  /**
70
70
  * Render `error` and its whole cause chain as indented JSON.
71
71
  *
@@ -84,7 +84,7 @@ declare function createSdk(config: SdkConfig, options?: WebsiteSDKOptions): Webs
84
84
  * `CoreSDKError` logged as the five bland fields above with the sentence that
85
85
  * names the fault dropped.
86
86
  */
87
- declare function describeSdkError(error: unknown): string;
87
+ export declare function describeSdkError(error: unknown): string;
88
88
  /**
89
89
  * Log a failed SDK call with the backend's reason, tagged by `scope`.
90
90
  *
@@ -97,7 +97,7 @@ declare function describeSdkError(error: unknown): string;
97
97
  * log it separately for a clickable stack; doubling it here would print every
98
98
  * failure twice on the surfaces that already do.
99
99
  */
100
- declare function logSdkFailure(scope: string, error: unknown): void;
100
+ export declare function logSdkFailure(scope: string, error: unknown): void;
101
101
  //#endregion
102
102
  //#region src/quote.d.ts
103
103
  /** One row of the price breakdown. `label`/`amount` are the SDK's own
@@ -108,13 +108,13 @@ declare function logSdkFailure(scope: string, error: unknown): void;
108
108
  * from a charge line — see `adaptGuestQuote` for why those rows exist. Its
109
109
  * `amount` is the modifier's own signed string ("-140,00 €"), so the row is a
110
110
  * deduction from the running total, not another charge. */
111
- interface QuoteLine {
111
+ export interface QuoteLine {
112
112
  label: string;
113
113
  amount: string;
114
114
  nested: boolean;
115
115
  discount: boolean;
116
116
  }
117
- interface PriceQuote {
117
+ export interface PriceQuote {
118
118
  /** Section/line breakdown flattened in SDK order (sub-items follow their
119
119
  * parent line, flagged `nested`). */
120
120
  lines: QuoteLine[];
@@ -122,7 +122,7 @@ interface PriceQuote {
122
122
  total: string;
123
123
  }
124
124
  /** Bookable add-on from `quote.additionalServices`, verbatim fields. */
125
- interface QuoteAddonService {
125
+ export interface QuoteAddonService {
126
126
  id: string;
127
127
  label: string;
128
128
  /** SDK charge string as-is (e.g. "25,00 €" — the PMS's own formatting). */
@@ -160,13 +160,13 @@ interface QuoteAddonService {
160
160
  * the feed omits it. */
161
161
  unavailableReason?: string | undefined;
162
162
  }
163
- interface QuotePolicyRule {
163
+ export interface QuotePolicyRule {
164
164
  daysBeforeArrival: number;
165
165
  /** SDK's formatted percentage string, verbatim. */
166
166
  refundPercentage: string;
167
167
  }
168
168
  /** Selectable cancellation policy from `quote.cancellationPolicies`. */
169
- interface QuoteCancellationPolicy {
169
+ export interface QuoteCancellationPolicy {
170
170
  id: string;
171
171
  label: string;
172
172
  selected: boolean;
@@ -184,7 +184,7 @@ interface QuoteCancellationPolicy {
184
184
  /** Voucher status echoed back on a re-priced quote (`quote.voucher`). On v10
185
185
  * the SDK authoritatively reports whether the code applied a modifier; v9
186
186
  * reports `"unknown"`. `message` is the PMS's own note, verbatim. */
187
- interface QuoteVoucher {
187
+ export interface QuoteVoucher {
188
188
  code: string;
189
189
  status: "applied" | "not_applied" | "unknown";
190
190
  message?: string | undefined;
@@ -194,7 +194,7 @@ interface QuoteVoucher {
194
194
  * whichever insurer the hub setup configures, and only when that setup enables
195
195
  * insurance at all — otherwise the SDK returns the "none" option alone and the
196
196
  * adapter yields no `QuoteInsurance`. */
197
- interface QuoteInsuranceOption {
197
+ export interface QuoteInsuranceOption {
198
198
  id: string;
199
199
  label: string;
200
200
  /** SDK's own locale-formatted premium (e.g. "14,00 €"). There is no numeric
@@ -228,7 +228,7 @@ interface QuoteInsuranceOption {
228
228
  * policy separately, so a UI that shows both must show them as two figures.
229
229
  * Adding them would mean parsing the PMS's formatted strings, which this file
230
230
  * does not do for any other charge either. */
231
- interface QuoteInsurance {
231
+ export interface QuoteInsurance {
232
232
  /** The SDK's own "no insurance" label, verbatim. */
233
233
  noneLabel: string;
234
234
  options: QuoteInsuranceOption[];
@@ -236,7 +236,7 @@ interface QuoteInsurance {
236
236
  selectedId: string | null;
237
237
  }
238
238
  /** The renderable view of a live GuestQuote — card + selectable options. */
239
- interface QuoteView {
239
+ export interface QuoteView {
240
240
  card: PriceQuote;
241
241
  services: QuoteAddonService[];
242
242
  policies: QuoteCancellationPolicy[];
@@ -246,7 +246,7 @@ interface QuoteView {
246
246
  /** Selectable travel insurance — absent when the feed offers no product. */
247
247
  insurance?: QuoteInsurance | undefined;
248
248
  }
249
- type QuoteResult = {
249
+ export type QuoteResult = {
250
250
  status: "available";
251
251
  /** Flattened price card (back-compat: the widget renders this). */
252
252
  quote: PriceQuote;
@@ -271,7 +271,7 @@ type QuoteResult = {
271
271
  * `@v-office/website-sdk` does not re-export it and sdk-core is a transitive
272
272
  * dependency an adopting project does not declare — so `Extract` off the
273
273
  * result type is the reachable spelling. */
274
- type QuoteUnavailableCode = Extract<GuestQuoteResult, {
274
+ export type QuoteUnavailableCode = Extract<GuestQuoteResult, {
275
275
  status: "unavailable";
276
276
  }>["code"];
277
277
  /** The two codes that mean "the stay WAS priced, but the rental's rate setup
@@ -289,13 +289,13 @@ type QuoteUnavailableCode = Extract<GuestQuoteResult, {
289
289
  * unbookable until the surplus rows are removed in the PMS. What the site owes
290
290
  * the guest is honest copy — never the generic "not available", which asserts
291
291
  * a booking conflict that does not exist. */
292
- declare const MULTI_RATE_CODES: readonly ["AmbiguousMultiRate", "UnresolvableMultiRate"];
292
+ export declare const MULTI_RATE_CODES: readonly ["AmbiguousMultiRate", "UnresolvableMultiRate"];
293
293
  /** True when an unavailable quote is a rate-configuration fault rather than a
294
294
  * genuinely unbookable stay. Callers branch the MESSAGE on this and must keep
295
295
  * booking disabled either way (SDK 2.16.0 MIGRATION, "Required Consumer
296
296
  * Work"). */
297
- declare function isRateConfigurationFault(code: QuoteUnavailableCode | undefined): boolean;
298
- interface QuoteOccupancy {
297
+ export declare function isRateConfigurationFault(code: QuoteUnavailableCode | undefined): boolean;
298
+ export interface QuoteOccupancy {
299
299
  adults: number;
300
300
  children: number;
301
301
  childrenAges?: number[] | undefined;
@@ -325,23 +325,23 @@ interface QuoteOccupancy {
325
325
  * checked by adding, and as a price claim the customer would have to stand
326
326
  * behind. The total stays the single net number.
327
327
  */
328
- declare function adaptGuestQuote(quote: GuestQuote): QuoteView;
328
+ export declare function adaptGuestQuote(quote: GuestQuote): QuoteView;
329
329
  /** Price a stay for `rentalId`. The caller owns the SDK instance and its
330
330
  * lifecycle — pass a shared one to reuse its queue + cache across the caller's
331
331
  * lifetime. `voucher` re-prices with a code (the PMS decides what it's worth). */
332
- declare function fetchQuote(rentalId: string, period: {
332
+ export declare function fetchQuote(rentalId: string, period: {
333
333
  start: string;
334
334
  end: string;
335
335
  }, occupancy: QuoteOccupancy, sdk: WebsiteSdk, locale: Locale, voucher?: string): Promise<QuoteResult>;
336
- type QuoteSelectionResult = {
336
+ export type QuoteSelectionResult = {
337
337
  ok: true;
338
338
  quote: GuestQuote;
339
339
  } | {
340
340
  ok: false;
341
341
  reason?: string | undefined;
342
342
  };
343
- declare function addQuoteService(quote: GuestQuote, id: string, sdk: WebsiteSdk): Promise<QuoteSelectionResult>;
344
- declare function removeQuoteService(quote: GuestQuote, id: string, sdk: WebsiteSdk): Promise<QuoteSelectionResult>;
343
+ export declare function addQuoteService(quote: GuestQuote, id: string, sdk: WebsiteSdk): Promise<QuoteSelectionResult>;
344
+ export declare function removeQuoteService(quote: GuestQuote, id: string, sdk: WebsiteSdk): Promise<QuoteSelectionResult>;
345
345
  /** Drop every additional-service selection from `quote`.
346
346
  *
347
347
  * A fresh quote's selection state is not knowable from the service list. It is
@@ -351,8 +351,8 @@ declare function removeQuoteService(quote: GuestQuote, id: string, sdk: WebsiteS
351
351
  * (`GuestQuote.selections` is behind an unexported symbol). Rather than branch
352
352
  * on something invisible, a caller that needs a known starting point clears
353
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>;
354
+ export declare function clearQuoteServices(quote: GuestQuote, sdk: WebsiteSdk): Promise<QuoteSelectionResult>;
355
+ export declare function selectQuotePolicy(quote: GuestQuote, id: string, sdk: WebsiteSdk): Promise<QuoteSelectionResult>;
356
356
  /** Select a travel-insurance product (`id`) or none (`null`). Unlike the
357
357
  * service/policy calls the SDK returns the new GuestQuote directly and throws
358
358
  * on failure (unknown id) — normalized here to the same QuoteSelectionResult
@@ -364,7 +364,7 @@ declare function selectQuotePolicy(quote: GuestQuote, id: string, sdk: WebsiteSd
364
364
  * off the quote. A product with `requiresTravelers` needs the pre-contract /
365
365
  * payment / `bookInsurance` steps on top, which the checkout does not
366
366
  * implement — it offers only the products it can complete. */
367
- declare function selectQuoteInsurance(quote: GuestQuote, id: string | null, sdk: WebsiteSdk): Promise<QuoteSelectionResult>;
367
+ export declare function selectQuoteInsurance(quote: GuestQuote, id: string | null, sdk: WebsiteSdk): Promise<QuoteSelectionResult>;
368
368
  /**
369
369
  * The concrete "free cancellation until" date for ONE policy — DERIVED, not
370
370
  * fabricated: the PMS states its rules relatively (`daysBeforeArrival` +
@@ -380,16 +380,16 @@ declare function selectQuoteInsurance(quote: GuestQuote, id: string | null, sdk:
380
380
  * own formatted string ("100%"), so this reads it rather than comparing a
381
381
  * number — the one place that pattern is written, because both the derived
382
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;
383
+ export declare function fullRefundRule(policy: QuoteCancellationPolicy | undefined): QuotePolicyRule | undefined;
384
+ export declare function policyFreeCancellationUntil(policy: QuoteCancellationPolicy | undefined, arrival: Date): Date | null;
385
385
  /** The deadline for the stay as it stands — i.e. of the SELECTED policy.
386
386
  * Kept as its own name because that is what a summary line means: not "some
387
387
  * policy allows this" but "what you have chosen allows this". */
388
- declare function freeCancellationUntil(policies: QuoteCancellationPolicy[], arrival: Date): Date | null;
388
+ export declare function freeCancellationUntil(policies: QuoteCancellationPolicy[], arrival: Date): Date | null;
389
389
  /** The same stay priced under ONE cancellation policy. `quote` is a real
390
390
  * GuestQuote branched off the base, so the UI can show its card, and hand the
391
391
  * branch on to a later step, without re-quoting the stay. */
392
- interface PolicyBranch {
392
+ export interface PolicyBranch {
393
393
  id: string;
394
394
  /** The branch quote — the base itself for the already-selected policy. */
395
395
  quote: GuestQuote;
@@ -420,15 +420,15 @@ interface PolicyBranch {
420
420
  * budget delays the ones that can succeed. It simply gets no branch, and the UI
421
421
  * renders it as the unselectable row it already is.
422
422
  */
423
- declare function pricePolicyBranches(base: GuestQuote, sdk: WebsiteSdk): Promise<PolicyBranch[]>;
423
+ export declare function pricePolicyBranches(base: GuestQuote, sdk: WebsiteSdk): Promise<PolicyBranch[]>;
424
424
  //#endregion
425
425
  //#region src/booking.d.ts
426
426
  /** Where the payment provider sends the guest back after an online payment
427
427
  * (relative — the SDK/PSP resolves it against the site origin). */
428
- declare const CHECKOUT_RETURN_PATH = "/buchen/bestaetigung";
428
+ export declare const CHECKOUT_RETURN_PATH = "/buchen/bestaetigung";
429
429
  /** The checkout form's flat contact fields (the UI shape). `toBookingContact`
430
430
  * folds them into the SDK's nested BookingContact. */
431
- interface BookingContactForm {
431
+ export interface BookingContactForm {
432
432
  forename: string;
433
433
  surname: string;
434
434
  /** Anrede ("Frau" / "Herr"); omitted when the guest picks none. */
@@ -443,13 +443,13 @@ interface BookingContactForm {
443
443
  phone?: string | undefined;
444
444
  note?: string | undefined;
445
445
  }
446
- interface PaymentLine {
446
+ export interface PaymentLine {
447
447
  label: string;
448
448
  amount: string;
449
449
  }
450
450
  /** One payment entry, verbatim — a contractual step (Anzahlung / Restzahlung /
451
451
  * Rate / Kaution) or a way to settle the whole outstanding balance. */
452
- interface PaymentScheduleView {
452
+ export interface PaymentScheduleView {
453
453
  /** SDK's stable kind, for programmatic behaviour ONLY. Everything the guest
454
454
  * reads comes from `label`: the SDK translates it, and a v10 installment is
455
455
  * not automatically a v9 prepayment. */
@@ -476,12 +476,12 @@ interface PaymentScheduleView {
476
476
  * a `PaymentScheduleView`: those are the PMS's own steps, owed to the site's
477
477
  * operator and payable through the SDK. This one is a different contract with
478
478
  * a different recipient, and it leaves the site entirely. */
479
- interface InsurancePaymentLink {
479
+ export interface InsurancePaymentLink {
480
480
  label: string;
481
481
  url: string;
482
482
  description?: string | undefined;
483
483
  }
484
- interface BookingConfirmation {
484
+ export interface BookingConfirmation {
485
485
  bookingNumber: string;
486
486
  /** The contractual steps, in backend order — what the guest owes and when. */
487
487
  schedules: PaymentScheduleView[];
@@ -503,10 +503,10 @@ interface BookingConfirmation {
503
503
  * real. Throws on transport/backend errors (the caller renders an honest
504
504
  * retry message; nothing was necessarily booked when it throws).
505
505
  */
506
- declare function submitBooking(quote: GuestQuote, contact: BookingContactForm, sdk: WebsiteSdk, returnPath?: string): Promise<BookingConfirmation>;
506
+ export declare function submitBooking(quote: GuestQuote, contact: BookingContactForm, sdk: WebsiteSdk, returnPath?: string): Promise<BookingConfirmation>;
507
507
  /** True for the option kinds `submitPaymentOption` can perform in-browser
508
508
  * (redirect / form_post / stripe_checkout). Bank transfer is display-only. */
509
- declare function isSubmittableOption(option: BookingPaymentOption): option is SubmittableBookingPaymentOption;
509
+ export declare function isSubmittableOption(option: BookingPaymentOption): option is SubmittableBookingPaymentOption;
510
510
  /** Perform an online payment option (redirects the browser to the PSP).
511
511
  * USER-initiated only.
512
512
  *
@@ -516,31 +516,31 @@ declare function isSubmittableOption(option: BookingPaymentOption): option is Su
516
516
  * booking, not a failed one. The checkout's catch discards the error entirely
517
517
  * — `.catch(() => setPayError(true))` — so without this a guest who cannot
518
518
  * pay leaves no trace at all, on a booking someone now has to chase. */
519
- declare function payWithOption(option: SubmittableBookingPaymentOption, sdk: WebsiteSdk): Promise<void>;
519
+ export declare function payWithOption(option: SubmittableBookingPaymentOption, sdk: WebsiteSdk): Promise<void>;
520
520
  //#endregion
521
521
  //#region src/booking-snapshot.d.ts
522
522
  /** Rental identity, parked by the checkout page (which has it from SSR) and
523
523
  * folded into the snapshot at book time — the SDK seam that writes the
524
524
  * snapshot never sees a rental name or a link. */
525
- declare const CHECKOUT_CONTEXT_KEY = "voffice:checkout-context";
525
+ export declare const CHECKOUT_CONTEXT_KEY = "voffice:checkout-context";
526
526
  /** The finished record `/buchen/bestaetigung` reads. */
527
- declare const BOOKING_SNAPSHOT_KEY = "voffice:booking-return";
527
+ export declare const BOOKING_SNAPSHOT_KEY = "voffice:booking-return";
528
528
  /** One gallery image, in the SDK's own shape (`RentalGalleryImage` minus the
529
529
  * fields this page does not render). Stored so the confirmation can show the
530
530
  * unit rather than describe it — the guest booked a place, not a table. */
531
- interface SnapshotImage {
531
+ export interface SnapshotImage {
532
532
  src: string;
533
533
  srcset?: string | undefined;
534
534
  alt: string;
535
535
  }
536
- interface CheckoutContext {
536
+ export interface CheckoutContext {
537
537
  rentalName: string;
538
538
  location?: string | undefined;
539
539
  href?: string | undefined;
540
540
  /** Capped at the same five the checkout's own summary strip shows. */
541
541
  images?: SnapshotImage[] | undefined;
542
542
  }
543
- interface BookingSnapshotGuests {
543
+ export interface BookingSnapshotGuests {
544
544
  adults: number;
545
545
  children: number;
546
546
  babies: number;
@@ -549,7 +549,7 @@ interface BookingSnapshotGuests {
549
549
  /** Every field but `bookingNumber` self-hides when absent — the return page
550
550
  * renders a row only for what it actually has, never an empty shell
551
551
  * (`DATA_SCHEMA.md`: "Optional field gates optional UI"). */
552
- interface BookingSnapshot {
552
+ export interface BookingSnapshot {
553
553
  bookingNumber: string;
554
554
  rentalName?: string | undefined;
555
555
  location?: string | undefined;
@@ -564,7 +564,7 @@ interface BookingSnapshot {
564
564
  }
565
565
  /** Park the rental's identity for the return page. Called by the checkout page
566
566
  * on load — it is the last place in the flow that knows the rental by name. */
567
- declare function saveCheckoutContext(context: CheckoutContext): void;
567
+ export declare function saveCheckoutContext(context: CheckoutContext): void;
568
568
  /**
569
569
  * Write the return page's record. Called by `submitBooking` the moment the
570
570
  * reservation exists — EARLIER than the SDK's "before submitting a payment
@@ -573,14 +573,14 @@ declare function saveCheckoutContext(context: CheckoutContext): void;
573
573
  *
574
574
  * Never throws. The snapshot is a nicety; the booking is not.
575
575
  */
576
- declare function saveBookingSnapshot(bookingNumber: string, quote: GuestQuote): void;
576
+ export declare function saveBookingSnapshot(bookingNumber: string, quote: GuestQuote): void;
577
577
  //#endregion
578
578
  //#region src/checkout/insurance.d.ts
579
579
  /** The plan's documents as the checkout treats them: what it lists, what the
580
580
  * guest must open, and what makes a plan sellable at all read the SAME list,
581
581
  * so a hidden document can never end up required-but-unlisted (which would be
582
582
  * a gate the guest cannot close). */
583
- declare function shownInsuranceResources(option: QuoteInsuranceOption): {
583
+ export declare function shownInsuranceResources(option: QuoteInsuranceOption): {
584
584
  label: string;
585
585
  url: string;
586
586
  }[];
@@ -599,10 +599,10 @@ declare function shownInsuranceResources(option: QuoteInsuranceOption): {
599
599
  * them cannot be concluded lawfully here, so it self-hides rather than
600
600
  * being sold undocumented — the same rule as (1), applied to a legal
601
601
  * precondition instead of a technical one. */
602
- declare function selectableInsuranceOptions(insurance: QuoteInsurance): QuoteInsuranceOption[];
602
+ export declare function selectableInsuranceOptions(insurance: QuoteInsurance): QuoteInsuranceOption[];
603
603
  /** The selected plan, but only when it is one the guest could actually pick
604
604
  * here — so no price row can name a product the section above never showed. */
605
- declare function selectedInsuranceOption(insurance: QuoteInsurance | undefined): QuoteInsuranceOption | undefined;
605
+ export declare function selectedInsuranceOption(insurance: QuoteInsurance | undefined): QuoteInsuranceOption | undefined;
606
606
  /** The identity of an insurance PRODUCT, as opposed to the id of one priced
607
607
  * variant of it.
608
608
  *
@@ -617,7 +617,7 @@ declare function selectedInsuranceOption(insurance: QuoteInsurance | undefined):
617
617
  *
618
618
  * The label is what survives that: the same string in every band, and the name
619
619
  * the guest actually chose, since it is what the radio shows. */
620
- declare function insuranceProductKey(option: QuoteInsuranceOption): string;
620
+ export declare function insuranceProductKey(option: QuoteInsuranceOption): string;
621
621
  /** The plan id to re-apply onto a re-priced quote, or null when there is
622
622
  * nothing to do — no choice, already applied, or a product this feed no longer
623
623
  * offers as a selectable one. The last case is why the feed is checked rather
@@ -640,7 +640,7 @@ declare function insuranceProductKey(option: QuoteInsuranceOption): string;
640
640
  * the section no longer shows, so re-applying it would leave the quote
641
641
  * carrying an insurance the page neither displays nor can take a document
642
642
  * confirmation for. */
643
- declare function insuranceIdToReapply(fresh: QuoteView, wanted: QuoteInsuranceOption | undefined): string | null;
643
+ export declare function insuranceIdToReapply(fresh: QuoteView, wanted: QuoteInsuranceOption | undefined): string | null;
644
644
  /** Progress through the document step. Held by the orchestration hook because
645
645
  * the submit button, a card away, is gated on it. */
646
646
  interface InsuranceDocsState {
@@ -659,11 +659,11 @@ interface InsuranceDocsState {
659
659
  * total — would be friction with nothing behind it. */
660
660
  confirmedProduct: string | null;
661
661
  }
662
- declare const NO_INSURANCE_DOCS: InsuranceDocsState;
662
+ export declare const NO_INSURANCE_DOCS: InsuranceDocsState;
663
663
  /** True while a chosen plan's document step is unfinished. No plan chosen (or
664
664
  * "keine Versicherung") is never blocking — the duty attaches to the insurance
665
665
  * contract, not to the stay. */
666
- declare function insuranceDocsPending(option: QuoteInsuranceOption | undefined, docs: InsuranceDocsState): boolean;
666
+ export declare function insuranceDocsPending(option: QuoteInsuranceOption | undefined, docs: InsuranceDocsState): boolean;
667
667
  //#endregion
668
668
  //#region src/checkout/stay.d.ts
669
669
  interface Stay {
@@ -679,14 +679,14 @@ interface Stay {
679
679
  * SDK both use. Local on purpose (matches date-fns `format(date,
680
680
  * "yyyy-MM-dd")`, which the sites' own lib/dates.ts uses): a calendar day
681
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;
682
+ export declare function isoDate(date: Date): string;
683
+ export declare function occupancyOf(stay: Stay): QuoteOccupancy;
684
684
  /** The identity of a stay for "is the shown price still the shown stay"
685
685
  * comparisons — every field that changes the quote, in one string. */
686
- declare function stayKeyOf(stay: Stay): string;
686
+ export declare function stayKeyOf(stay: Stay): string;
687
687
  /** The stay as URL search params (`start`/`end`/`adults`/… — the same
688
688
  * vocabulary the rental page reads + writes). */
689
- declare function stayParams(stay: Stay): string;
689
+ export declare function stayParams(stay: Stay): string;
690
690
  //#endregion
691
691
  //#region src/checkout/createCheckoutFlow.d.ts
692
692
  /**
@@ -836,10 +836,10 @@ interface CheckoutFlow extends Store<CheckoutFlowState>, CheckoutFlowActions {
836
836
  }
837
837
  /** What a UI binding hands to the rendering code: the state plus the actions. */
838
838
  type CheckoutFlowApi = CheckoutFlowState & CheckoutFlowActions;
839
- declare function createCheckoutFlow(initialOptions: CheckoutFlowOptions): CheckoutFlow;
839
+ export declare function createCheckoutFlow(initialOptions: CheckoutFlowOptions): CheckoutFlow;
840
840
  //#endregion
841
841
  //#region src/checkout/card-order.d.ts
842
- declare function stabilizeCardOrder(view: QuoteView, order: Map<string, number>): QuoteView;
842
+ export declare function stabilizeCardOrder(view: QuoteView, order: Map<string, number>): QuoteView;
843
843
  //#endregion
844
844
  //#region src/checkout/validation.d.ts
845
845
  /**
@@ -869,19 +869,19 @@ interface ValidationMessages {
869
869
  postalcodeInvalidCH: string;
870
870
  streetHouseNumberWarning: string;
871
871
  }
872
- declare function normalizePhone(raw: string, dial: string | undefined): string;
873
- declare function phoneError(raw: string, dial: string | undefined, c: ValidationMessages): string | undefined;
874
- declare function emailError(raw: string, c: ValidationMessages): string | undefined;
875
- declare function postalcodeError(raw: string, country: string, c: ValidationMessages): string | undefined;
876
- declare function streetWarning(raw: string, c: ValidationMessages): string | undefined;
877
- declare const VALIDATED_FIELDS: readonly ["forename", "surname", "email", "phone", "street", "postalcode", "city"];
872
+ export declare function normalizePhone(raw: string, dial: string | undefined): string;
873
+ export declare function phoneError(raw: string, dial: string | undefined, c: ValidationMessages): string | undefined;
874
+ export declare function emailError(raw: string, c: ValidationMessages): string | undefined;
875
+ export declare function postalcodeError(raw: string, country: string, c: ValidationMessages): string | undefined;
876
+ export declare function streetWarning(raw: string, c: ValidationMessages): string | undefined;
877
+ export declare const VALIDATED_FIELDS: readonly ["forename", "surname", "email", "phone", "street", "postalcode", "city"];
878
878
  /** Validate one named field. Takes the selected COUNTRY plus its calling code
879
879
  * (`dial`, undefined when the register has none for it): two validators depend
880
880
  * on the country — the phone via the dial code, the PLZ via its format rule.
881
881
  * The dial code is passed in rather than looked up because the ISO→dial
882
882
  * mapping lives with the site's country register (its localized "Land"
883
883
  * options), not here. */
884
- declare function fieldError(name: string, value: string, country: string, dial: string | undefined, c: ValidationMessages): string | undefined;
884
+ export declare function fieldError(name: string, value: string, country: string, dial: string | undefined, c: ValidationMessages): string | undefined;
885
885
  /** Country options in the shape the site's country register provides —
886
886
  * `[code, label]` pairs, a featured group plus the full ISO register. Kept
887
887
  * structural so the site's own `CountryGroups` type satisfies it. */
@@ -889,7 +889,7 @@ interface CountryGroupsLike {
889
889
  featured: ReadonlyArray<readonly [string, string]>;
890
890
  all: ReadonlyArray<readonly [string, string]>;
891
891
  }
892
- declare function detectCountry(countries: CountryGroupsLike): string;
892
+ export declare function detectCountry(countries: CountryGroupsLike): string;
893
893
  //#endregion
894
894
  //#region src/checkout/structure.d.ts
895
895
  /**
@@ -901,20 +901,22 @@ declare function detectCountry(countries: CountryGroupsLike): string;
901
901
  * checkout UI can silently drop a legal obligation — the § 312j BGB
902
902
  * Button-Lösung wording, the insurance documents listed before the guest's
903
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.
904
+ * unit test still passes. This module is a contract over the RENDERED result.
905
+ * The imported `Booking` component satisfies it by construction; a site's own
906
+ * checkout is a developer's review item when it changes. The platform does
907
+ * NOT run this assertion at publish time (decided 2026-09-11,
908
+ * docs/website-platform/process/10-dist-checks.md "Custom checkouts").
907
909
  *
908
910
  * Two halves:
909
911
  * - `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
+ * these attributes; a site's own markup must keep them so this check
913
+ * (and a site's own CSS) can find the elements.
912
914
  * - `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
+ * and a pure DOM check over a rendered checkout (a browser page or a
916
+ * jsdom document in a test). Every requirement names the legal
915
917
  * basis it exists for, so a failure message tells the reader WHY.
916
918
  */
917
- declare const CHECKOUT_PARTS: {
919
+ export declare const CHECKOUT_PARTS: {
918
920
  /** The one place the guest is told the price they are about to owe. */
919
921
  readonly priceTotal: "checkout-price-total";
920
922
  /** The § 312j order button. */
@@ -930,8 +932,8 @@ type CheckoutPart = (typeof CHECKOUT_PARTS)[keyof typeof CHECKOUT_PARTS];
930
932
  /** German "kostenpflichtig bestellen"-class labels the courts have accepted
931
933
  * for § 312j Abs. 3 BGB. The check is on the RENDERED button text, so a site
932
934
  * may re-voice the label — but only within this class. */
933
- declare const BUTTON_SOLUTION_PATTERN: RegExp;
934
- declare const CHECKOUT_STRUCTURE: {
935
+ export declare const BUTTON_SOLUTION_PATTERN: RegExp;
936
+ export declare const CHECKOUT_STRUCTURE: {
935
937
  readonly "submit-present": {
936
938
  readonly basis: "§ 312j Abs. 3 BGB (Button-Lösung)";
937
939
  readonly description: "exactly one order button carries data-part=checkout-submit";
@@ -959,20 +961,20 @@ interface StructureViolation {
959
961
  basis: string;
960
962
  detail: string;
961
963
  }
962
- declare function assertCheckoutStructure(root: ParentNode): StructureViolation[];
964
+ export declare function assertCheckoutStructure(root: ParentNode): StructureViolation[];
963
965
  //#endregion
964
966
  //#region src/dates.d.ts
965
- declare function parseParam(value: string | null | undefined): Date | undefined;
966
- declare function parseGermanParam(value: string | null | undefined): Date | undefined;
967
+ export declare function parseParam(value: string | null | undefined): Date | undefined;
968
+ export declare function parseGermanParam(value: string | null | undefined): Date | undefined;
967
969
  /** A flexible-length stay ("3–5 Nächte") as the URL carries it. Off until the
968
970
  * search UI offers it; the keys are reserved so a site can enable it. */
969
- declare const NIGHT_RANGE_ENABLED = false;
970
- interface NightRange {
971
+ export declare const NIGHT_RANGE_ENABLED = false;
972
+ export interface NightRange {
971
973
  min: number;
972
974
  max: number;
973
975
  }
974
- declare const NIGHT_RANGE_KEYS: readonly ["minNights", "maxNights", "nights_min", "nights_max"];
975
- declare function parseNightRange(params: URLSearchParams): NightRange | null;
976
+ export declare const NIGHT_RANGE_KEYS: readonly ["minNights", "maxNights", "nights_min", "nights_max"];
977
+ export declare function parseNightRange(params: URLSearchParams): NightRange | null;
976
978
  //#endregion
977
979
  //#region src/format.d.ts
978
980
  /**
@@ -981,7 +983,7 @@ declare function parseNightRange(params: URLSearchParams): NightRange | null;
981
983
  * when it is the zero-cents group at the end, so real cents ("1.234,50 €") stay.
982
984
  * A real value formatted for display, nothing fabricated; other formats pass.
983
985
  */
984
- declare function tidyPrice(formatted: string): string;
986
+ export declare function tidyPrice(formatted: string): string;
985
987
  //#endregion
986
988
  //#region src/availability.d.ts
987
989
  /**
@@ -1009,9 +1011,9 @@ declare function tidyPrice(formatted: string): string;
1009
1011
  * every calendar fed from this map agrees: the widget's picker and the
1010
1012
  * checkout's copy, which reads the same shape.
1011
1013
  */
1012
- declare function nightlyPrice(formatted: string, locale?: Locale): string;
1014
+ export declare function nightlyPrice(formatted: string, locale?: Locale): string;
1013
1015
  type VofficeSdk$1 = WebsiteSdk;
1014
- interface InitialAvailability {
1016
+ export interface InitialAvailability {
1015
1017
  /** ISO dates (`YYYY-MM-DD`) the SDK flags as a valid check-in/arrival day. */
1016
1018
  canStart: Set<string>;
1017
1019
  /** ISO dates the rental is genuinely unbookable on (booked / closed / past the
@@ -1032,8 +1034,10 @@ interface InitialAvailability {
1032
1034
  * arbiter of a bookable party), so `minPersons` isn't surfaced here. */
1033
1035
  maxPersons: number | null;
1034
1036
  maxAdults: number | null;
1037
+ childrenAllowed: boolean | null;
1038
+ maxPets: number | null;
1035
1039
  }
1036
- interface EndAvailability {
1040
+ export interface EndAvailability {
1037
1041
  /** ISO dates the SDK flags as a valid check-out/departure for the chosen
1038
1042
  * start (min-stay + checkout rules already applied by the SDK). */
1039
1043
  canEnd: Set<string>;
@@ -1049,13 +1053,13 @@ interface EndAvailability {
1049
1053
  /** Possible check-in days + occupancy caps for `rentalId` across the window.
1050
1054
  * The caller's SDK instance is reused so its 30s per-rental cache spans the
1051
1055
  * picker's lifetime. */
1052
- declare function fetchInitialAvailability(sdk: VofficeSdk$1, rentalId: string, calculateFromDate: string, calculateThroughDate: string, locale: Locale): Promise<InitialAvailability>;
1056
+ export declare function fetchInitialAvailability(sdk: VofficeSdk$1, rentalId: string, calculateFromDate: string, calculateThroughDate: string, locale: Locale): Promise<InitialAvailability>;
1053
1057
  /** 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>;
1058
+ export declare function fetchEndAvailability(sdk: VofficeSdk$1, rentalId: string, calculateFromDate: string, calculateThroughDate: string, selectedStartDate: string, locale: Locale): Promise<EndAvailability>;
1055
1059
  //#endregion
1056
1060
  //#region src/search.d.ts
1057
1061
  type VofficeSdk = WebsiteSdk;
1058
- interface SearchImage {
1062
+ export interface SearchImage {
1059
1063
  src: string;
1060
1064
  srcset?: string | undefined;
1061
1065
  alt: string;
@@ -1070,7 +1074,7 @@ interface SearchImage {
1070
1074
  * for the single window it used to render. A window whose dates the feed omits
1071
1075
  * has no label and never reaches this list; one the feed does not price shows
1072
1076
  * no price rather than a placeholder. */
1073
- interface AlternativePeriod {
1077
+ export interface AlternativePeriod {
1074
1078
  /** „31. Aug - 4. Sep", composed from this window's own start and end. */
1075
1079
  label: string;
1076
1080
  /** The detail-page link carrying THIS window's dates, so the rental opens on
@@ -1094,16 +1098,17 @@ interface AlternativePeriod {
1094
1098
  amount: string | null;
1095
1099
  }[];
1096
1100
  }
1097
- interface SearchResultItem {
1101
+ export interface SearchResultItem {
1098
1102
  /** PMS id — shared by the result card and its map marker. */
1099
1103
  id: string;
1100
1104
  /** `nameOrLabel`, verbatim. */
1101
1105
  name: string;
1102
1106
  /** `address.city`, verbatim (empty when the feed omits it). */
1103
1107
  location: string;
1104
- /** Internal detail link, derived from the name (URL generation), with the
1105
- * user's booking context (dates + party size) appended as a query so the
1106
- * search selection carries into the rental page. */
1108
+ /** Internal detail link — `rentalsPath` (see `runLiveSearch`) + the slug
1109
+ * derived from the name (URL generation), with the user's booking context
1110
+ * (dates + party size) appended as a query so the search selection carries
1111
+ * into the rental page. */
1107
1112
  href: string;
1108
1113
  images: SearchImage[];
1109
1114
  /** Parsed out of the SDK highlights; 0 when the highlight is absent. */
@@ -1207,7 +1212,7 @@ interface SearchResultItem {
1207
1212
  lng: number;
1208
1213
  } | null;
1209
1214
  }
1210
- interface SearchPayload {
1215
+ export interface SearchPayload {
1211
1216
  items: SearchResultItem[];
1212
1217
  /** Stay length from the user's start/end query, or null when undated. */
1213
1218
  nights: number | null;
@@ -1219,6 +1224,16 @@ interface SearchPayload {
1219
1224
  /** `pageInfo.hasNextPage`, verbatim. */
1220
1225
  hasNextPage: boolean;
1221
1226
  }
1227
+ export interface LiveSearchOptions {
1228
+ /** Path of the site's rental detail pages, prefixed to every result `href`
1229
+ * before the slug (e.g. `/de/unterkuenfte`). A trailing slash is tolerated.
1230
+ * Default: `DEFAULT_RENTALS_PATH`. */
1231
+ rentalsPath?: string | undefined;
1232
+ }
1233
+ /** The rental detail pages' path when the caller names none — the site
1234
+ * starter's German route. A site whose pages live elsewhere (a locale prefix,
1235
+ * another segment) passes its own `rentalsPath` to `runLiveSearch`. */
1236
+ export declare const DEFAULT_RENTALS_PATH = "/unterkuenfte";
1222
1237
  /**
1223
1238
  * Run a live search for the given URL query string (e.g.
1224
1239
  * `window.location.search`). Builds the SDK query from the picker params,
@@ -1229,28 +1244,34 @@ interface SearchPayload {
1229
1244
  * Pass `cursor` (the previous page's `pageInfo.nextCursor`, treated as
1230
1245
  * opaque) to fetch the next page. The returned `cursor`/`hasNextPage` are
1231
1246
  * the SDK's `pageInfo` straight through, so the caller can keep paging.
1247
+ *
1248
+ * `options.rentalsPath` is the path the site serves its rental detail pages
1249
+ * under — the part of every result `href` before the slug. Routing is the
1250
+ * site's decision (a locale prefix like `/de/unterkuenfte`, another segment),
1251
+ * so it arrives as an option the way `SearchBar.action` and `Quote.checkoutPath`
1252
+ * do; `locale` never implies it. Defaults to `DEFAULT_RENTALS_PATH`.
1232
1253
  */
1233
- declare function runLiveSearch(sdk: VofficeSdk, searchString: string, locale: Locale, cursor?: string | null): Promise<SearchPayload>;
1254
+ export declare function runLiveSearch(sdk: VofficeSdk, searchString: string, locale: Locale, cursor?: string | null, options?: LiveSearchOptions): Promise<SearchPayload>;
1234
1255
  //#endregion
1235
1256
  //#region src/sort.d.ts
1236
1257
  /** URL param. A client-side scope like `property`/`region`, NOT an SDK query
1237
1258
  * key — it is dropped from the SDK query string in search.ts's DROP_KEYS and
1238
1259
  * travels as the separate `sort` input instead. */
1239
- declare const SORT_PARAM = "sort";
1240
- type SortValue = "empfohlen" | "preis-auf" | "preis-ab";
1260
+ export declare const SORT_PARAM = "sort";
1261
+ export type SortValue = "empfohlen" | "preis-auf" | "preis-ab";
1241
1262
  /** The backend's own order. Emits no `sort` at all, so it is exactly the
1242
1263
  * ordering the page has always shown. Typed as the literal (not `SortValue`)
1243
1264
  * so `value === DEFAULT_SORT` narrows the rest away for `SDK_SORT`. */
1244
- declare const DEFAULT_SORT: "empfohlen";
1245
- interface SortOption {
1265
+ export declare const DEFAULT_SORT: "empfohlen";
1266
+ export interface SortOption {
1246
1267
  value: SortValue;
1247
1268
  label: string;
1248
1269
  /** Needs a period + occupancy, or the SDK rejects it before transport. */
1249
1270
  pricedOnly?: boolean;
1250
1271
  }
1251
- declare const SORT_OPTIONS: readonly SortOption[];
1272
+ export declare const SORT_OPTIONS: readonly SortOption[];
1252
1273
  /** The sort in the URL, or the default when absent/unknown. */
1253
- declare function activeSort(search: string): SortValue;
1274
+ export declare function activeSort(search: string): SortValue;
1254
1275
  /**
1255
1276
  * Can this query be priced? Mirrors the SDK's own precondition: a fixed period
1256
1277
  * (`start`+`end`) or a flexible window (`dates`/`month`/`weekend`), AND
@@ -1262,15 +1283,15 @@ declare function activeSort(search: string): SortValue;
1262
1283
  * gate and still reach the SDK undated. `weekend` is the exception — it is a
1263
1284
  * presence-only flag on both sides.
1264
1285
  */
1265
- declare function isPricedQuery(search: string): boolean;
1286
+ export declare function isPricedQuery(search: string): boolean;
1266
1287
  /** Is this option selectable for the current query? */
1267
- declare function isSortAvailable(option: SortOption, search: string): boolean;
1288
+ export declare function isSortAvailable(option: SortOption, search: string): boolean;
1268
1289
  /**
1269
1290
  * The `sort` to hand the SDK for this URL, or `undefined` for the backend's
1270
1291
  * own order. Falls back to `undefined` (rather than throwing) when a price
1271
1292
  * sort survives in the URL after the dates were cleared.
1272
1293
  */
1273
- declare function resolveSdkSort(search: string): SearchSearchSort | undefined;
1294
+ export declare function resolveSdkSort(search: string): SearchSearchSort | undefined;
1274
1295
  //#endregion
1275
1296
  //#region src/filters.d.ts
1276
1297
  /**
@@ -1279,8 +1300,8 @@ declare function resolveSdkSort(search: string): SearchSearchSort | undefined;
1279
1300
  * into `FilterDef[]` (which keys, in which order) and hands the list to the
1280
1301
  * results surface. Boolean filters toggle, integer filters are a lower bound.
1281
1302
  */
1282
- type FilterType = "boolean" | "int";
1283
- interface FilterDef {
1303
+ export type FilterType = "boolean" | "int";
1304
+ export interface FilterDef {
1284
1305
  /** The URL query key, which is also the SDK's search parameter key. */
1285
1306
  key: string;
1286
1307
  label: string;
@@ -1289,23 +1310,23 @@ interface FilterDef {
1289
1310
  min: number;
1290
1311
  max: number;
1291
1312
  }
1292
- interface ActiveFilter {
1313
+ export interface ActiveFilter {
1293
1314
  key: string;
1294
1315
  label: string;
1295
1316
  }
1296
1317
  /** A boolean filter is active when its param is present and truthy. */
1297
- declare const isBooleanFilterActive: (value: string) => boolean;
1318
+ export declare const isBooleanFilterActive: (value: string) => boolean;
1298
1319
  /**
1299
1320
  * The filters currently applied in the query, in the order the defs are
1300
1321
  * declared — the removable chips. An `int` label is prefixed with the localized
1301
1322
  * "minimum" wording, because its value is a lower bound.
1302
1323
  */
1303
- declare function activeFiltersFromQuery(searchParams: URLSearchParams, defs: readonly FilterDef[], minimumPrefix: string): ActiveFilter[];
1324
+ export declare function activeFiltersFromQuery(searchParams: URLSearchParams, defs: readonly FilterDef[], minimumPrefix: string): ActiveFilter[];
1304
1325
  /** The dog-friendly search as the URL carries it: `pets=true` (or a count) and,
1305
1326
  * from an older vocabulary, `petsCount=N`. Read by the live search. */
1306
- declare const PETS_PARAM = "pets";
1307
- declare const PETS_COUNT_PARAM = "petsCount";
1308
- declare function petsCountInQuery(search: string): number;
1327
+ export declare const PETS_PARAM = "pets";
1328
+ export declare const PETS_COUNT_PARAM = "petsCount";
1329
+ export declare function petsCountInQuery(search: string): number;
1309
1330
  //#endregion
1310
1331
  //#region src/regions.d.ts
1311
1332
  /**
@@ -1323,18 +1344,18 @@ declare function petsCountInQuery(search: string): number;
1323
1344
  */
1324
1345
  /** URL query key carrying the active region scope. Repeatable: several
1325
1346
  * `region=<name>` values OR together (show rentals in ANY selected region). */
1326
- declare const REGION_PARAM = "region";
1347
+ export declare const REGION_PARAM = "region";
1327
1348
  /** The regions active in a URL query string: the `region` values, trimmed,
1328
1349
  * de-duplicated, order preserved. Absent or empty → `[]`. */
1329
- declare function activeRegions(search: string): string[];
1350
+ export declare function activeRegions(search: string): string[];
1330
1351
  /**
1331
1352
  * The results inside the region scope. A rental may carry several regions
1332
1353
  * (MULTI_SELECT) and matches when any of them is selected; with no active
1333
1354
  * region every result stays. A rental the map does not know is outside every
1334
1355
  * region, so it drops out of a scoped search rather than passing unseen.
1335
1356
  */
1336
- declare function scopeToRegions<T extends {
1357
+ export declare function scopeToRegions<T extends {
1337
1358
  id: string;
1338
1359
  }>(items: readonly T[], active: readonly string[], regionsByRentalId: (id: string) => readonly string[] | undefined): T[];
1339
1360
  //#endregion
1340
- export { ActiveFilter, AlternativePeriod, BOOKING_SNAPSHOT_KEY, BUTTON_SOLUTION_PATTERN, BookingConfirmation, BookingContactForm, BookingSnapshot, BookingSnapshotGuests, CHECKOUT_CONTEXT_KEY, CHECKOUT_PARTS, CHECKOUT_RETURN_PATH, CHECKOUT_STRUCTURE, CheckoutContext, type CheckoutFlow, type CheckoutFlowActions, type CheckoutFlowApi, type CheckoutFlowMessages, type CheckoutFlowOptions, type CheckoutFlowState, type CheckoutIssue, type CheckoutPart, type CheckoutPhase, type CountryGroupsLike, DEFAULT_SORT, EndAvailability, FilterDef, FilterType, HUB_ENDPOINTS, InitialAvailability, type InsuranceDocsState, InsurancePaymentLink, Listener, type Locale, MULTI_RATE_CODES, NIGHT_RANGE_ENABLED, NIGHT_RANGE_KEYS, NO_INSURANCE_DOCS, NightRange, PETS_COUNT_PARAM, PETS_PARAM, PaymentLine, PaymentScheduleView, PolicyBranch, PriceQuote, QuoteAddonService, QuoteCancellationPolicy, QuoteInsurance, QuoteInsuranceOption, QuoteLine, QuoteOccupancy, QuotePolicyRule, QuoteResult, QuoteSelectionResult, QuoteUnavailableCode, QuoteView, QuoteVoucher, REGION_PARAM, SORT_OPTIONS, SORT_PARAM, STAGES, SdkConfig, SearchImage, SearchPayload, SearchResultItem, SnapshotImage, SortOption, SortValue, Stage, type Stay, Store, type StructureViolation, Unsubscribe, VALIDATED_FIELDS, VOFFICE_ENDPOINTS, type ValidationMessages, WebsiteSdk, WritableStore, activeFiltersFromQuery, activeRegions, activeSort, adaptGuestQuote, addQuoteService, assertCheckoutStructure, clearQuoteServices, createCheckoutFlow, createSdk, createStore, describeSdkError, detectCountry, emailError, fetchEndAvailability, fetchInitialAvailability, fetchQuote, fieldError, freeCancellationUntil, fullRefundRule, insuranceDocsPending, insuranceIdToReapply, insuranceProductKey, isBooleanFilterActive, isPricedQuery, isRateConfigurationFault, isSortAvailable, isSubmittableOption, isoDate, logSdkFailure, nightlyPrice, normalizePhone, occupancyOf, parseGermanParam, parseNightRange, parseParam, payWithOption, petsCountInQuery, phoneError, policyFreeCancellationUntil, postalcodeError, pricePolicyBranches, removeQuoteService, resolveSdkSort, runLiveSearch, saveBookingSnapshot, saveCheckoutContext, scopeToRegions, sdkConfigFor, selectQuoteInsurance, selectQuotePolicy, selectableInsuranceOptions, selectedInsuranceOption, shownInsuranceResources, stabilizeCardOrder, stayKeyOf, stayParams, streetWarning, submitBooking, tidyPrice };
1361
+ export type { CheckoutFlow, CheckoutFlowActions, CheckoutFlowApi, CheckoutFlowMessages, CheckoutFlowOptions, CheckoutFlowState, CheckoutIssue, CheckoutPart, CheckoutPhase, CountryGroupsLike, InsuranceDocsState, Locale, Stay, StructureViolation, ValidationMessages };
package/dist/index.mjs CHANGED
@@ -1384,17 +1384,19 @@ function detectCountry(countries) {
1384
1384
  * checkout UI can silently drop a legal obligation — the § 312j BGB
1385
1385
  * Button-Lösung wording, the insurance documents listed before the guest's
1386
1386
  * confirmation, the price stated directly above the order button — and every
1387
- * unit test still passes. This module is a contract over the RENDERED result,
1388
- * checked by the platform's publish gate for imported and custom checkouts
1389
- * alike.
1387
+ * unit test still passes. This module is a contract over the RENDERED result.
1388
+ * The imported `Booking` component satisfies it by construction; a site's own
1389
+ * checkout is a developer's review item when it changes. The platform does
1390
+ * NOT run this assertion at publish time (decided 2026-09-11,
1391
+ * docs/website-platform/process/10-dist-checks.md "Custom checkouts").
1390
1392
  *
1391
1393
  * Two halves:
1392
1394
  * - `CHECKOUT_PARTS`: the `data-part` vocabulary. Imported components carry
1393
- * these attributes; a site's own markup must keep them so the gate (and
1394
- * a site's own CSS) can find the elements.
1395
+ * these attributes; a site's own markup must keep them so this check
1396
+ * (and a site's own CSS) can find the elements.
1395
1397
  * - `CHECKOUT_STRUCTURE` + `assertCheckoutStructure(root)`: the requirements
1396
- * and a pure DOM check the smoke suite runs against the rendered checkout
1397
- * (a browser page or a jsdom document). Every requirement names the legal
1398
+ * and a pure DOM check over a rendered checkout (a browser page or a
1399
+ * jsdom document in a test). Every requirement names the legal
1398
1400
  * basis it exists for, so a failure message tells the reader WHY.
1399
1401
  */
1400
1402
  const CHECKOUT_PARTS = {
@@ -1442,8 +1444,8 @@ function precedes(a, b) {
1442
1444
  return (a.compareDocumentPosition(b) & 4) === 4;
1443
1445
  }
1444
1446
  /**
1445
- * Check a rendered checkout. `root` is `document` in the smoke suite's browser
1446
- * page, or a jsdom document in tests. Returns every violation found (empty =
1447
+ * Check a rendered checkout. `root` is `document` in a browser page, or a
1448
+ * jsdom document in tests. Returns every violation found (empty =
1447
1449
  * compliant). The insurance rule is conditional on purpose: the duty attaches
1448
1450
  * to concluding an insurance contract, so a checkout that renders no
1449
1451
  * confirmation control has nothing to gate.
@@ -1451,7 +1453,7 @@ function precedes(a, b) {
1451
1453
  /** The text a sighted guest sees: descendants hidden by the `hidden` attribute,
1452
1454
  * `aria-hidden` or an inline display/visibility rule are excluded, so a legal
1453
1455
  * phrase in a hidden span cannot satisfy the wording rule. Stylesheet-based
1454
- * hiding needs layout and is the smoke suite's browser run to catch. */
1456
+ * hiding needs layout and only a browser run can catch it. */
1455
1457
  function visibleText(el) {
1456
1458
  let text = "";
1457
1459
  for (const node of el.childNodes) if (node.nodeType === 3) text += node.textContent ?? "";
@@ -1598,7 +1600,9 @@ async function fetchInitialAvailability(sdk, rentalId, calculateFromDate, calcul
1598
1600
  priceByDay,
1599
1601
  tooltipByDay,
1600
1602
  maxPersons: out.maxPersons ?? null,
1601
- maxAdults: out.maxAdults ?? null
1603
+ maxAdults: out.maxAdults ?? null,
1604
+ childrenAllowed: out.childrenAllowed ?? null,
1605
+ maxPets: out.maxPets ?? null
1602
1606
  };
1603
1607
  }
1604
1608
  }
@@ -1889,14 +1893,18 @@ function buildHandover(params, dates) {
1889
1893
  const suffix = out.toString();
1890
1894
  return suffix ? `?${suffix}` : "";
1891
1895
  }
1892
- function mapBase(item) {
1896
+ /** The rental detail pages' path when the caller names none — the site
1897
+ * starter's German route. A site whose pages live elsewhere (a locale prefix,
1898
+ * another segment) passes its own `rentalsPath` to `runLiveSearch`. */
1899
+ const DEFAULT_RENTALS_PATH = "/unterkuenfte";
1900
+ function mapBase(item, rentalsPath) {
1893
1901
  const name = item.nameOrLabel.trim();
1894
1902
  const highlights = item.highlights ?? [];
1895
1903
  return {
1896
1904
  id: item.id,
1897
1905
  name,
1898
1906
  location: item.address?.city?.trim() ?? "",
1899
- href: `/unterkuenfte/${slugify(name)}`,
1907
+ href: `${rentalsPath.replace(/\/+$/, "")}/${slugify(name)}`,
1900
1908
  images: (item.images ?? []).map((img) => ({
1901
1909
  src: img.src,
1902
1910
  srcset: img.srcset?.trim() || void 0,
@@ -1946,11 +1954,11 @@ function mapDiscount(discount) {
1946
1954
  })
1947
1955
  };
1948
1956
  }
1949
- function mapItem(item, params, locale) {
1957
+ function mapItem(item, params, locale, rentalsPath) {
1950
1958
  const scopeType = typeof item.scope === "string" ? item.scope : item.scope?.type;
1951
1959
  const alternativePeriodLabel = formatPeriodLabel(item.alternativePeriod ?? (typeof item.scope === "object" ? item.scope.alternativePeriod : void 0), locale);
1952
1960
  const isAlternativePeriod = alternativePeriodLabel != null || scopeType === "alternativePeriod" || scopeType === "alternative-period";
1953
- const base = mapBase(item);
1961
+ const base = mapBase(item, rentalsPath);
1954
1962
  return {
1955
1963
  ...base,
1956
1964
  href: `${base.href}${buildHandover(params)}`,
@@ -1967,8 +1975,8 @@ function mapItem(item, params, locale) {
1967
1975
  * label come from the first candidate period (the SDK returns them best-first,
1968
1976
  * `boost`-ordered). Always flagged as an alternative so the card shows the
1969
1977
  * period pill instead of treating it as an exact total. */
1970
- function mapAlternativeEntry(entry, params, locale) {
1971
- const base = mapBase(entry.item);
1978
+ function mapAlternativeEntry(entry, params, locale, rentalsPath) {
1979
+ const base = mapBase(entry.item, rentalsPath);
1972
1980
  const periods = (entry.alternativePeriods ?? []).flatMap((candidate) => {
1973
1981
  const label = formatPeriodLabel({
1974
1982
  start: candidate.start,
@@ -2016,8 +2024,15 @@ function mapAlternativeEntry(entry, params, locale) {
2016
2024
  * Pass `cursor` (the previous page's `pageInfo.nextCursor`, treated as
2017
2025
  * opaque) to fetch the next page. The returned `cursor`/`hasNextPage` are
2018
2026
  * the SDK's `pageInfo` straight through, so the caller can keep paging.
2027
+ *
2028
+ * `options.rentalsPath` is the path the site serves its rental detail pages
2029
+ * under — the part of every result `href` before the slug. Routing is the
2030
+ * site's decision (a locale prefix like `/de/unterkuenfte`, another segment),
2031
+ * so it arrives as an option the way `SearchBar.action` and `Quote.checkoutPath`
2032
+ * do; `locale` never implies it. Defaults to `DEFAULT_RENTALS_PATH`.
2019
2033
  */
2020
- async function runLiveSearch(sdk, searchString, locale, cursor) {
2034
+ async function runLiveSearch(sdk, searchString, locale, cursor, options = {}) {
2035
+ const rentalsPath = options.rentalsPath ?? "/unterkuenfte";
2021
2036
  const params = new URLSearchParams(searchString);
2022
2037
  const nights = computeNights(params);
2023
2038
  const sort = resolveSdkSort(searchString);
@@ -2030,9 +2045,9 @@ async function runLiveSearch(sdk, searchString, locale, cursor) {
2030
2045
  ...sort ? { sort } : {},
2031
2046
  ...cursor ? { cursor } : {}
2032
2047
  });
2033
- const exact = result.items.map((item) => mapItem(item, params, locale));
2048
+ const exact = result.items.map((item) => mapItem(item, params, locale, rentalsPath));
2034
2049
  const exactIds = new Set(exact.map((item) => item.id));
2035
- const alternatives = (result.alternatives ?? []).filter((entry) => entry?.item?.id && !exactIds.has(entry.item.id)).map((entry) => mapAlternativeEntry(entry, params, locale));
2050
+ const alternatives = (result.alternatives ?? []).filter((entry) => entry?.item?.id && !exactIds.has(entry.item.id)).map((entry) => mapAlternativeEntry(entry, params, locale, rentalsPath));
2036
2051
  const items = exact.concat(alternatives);
2037
2052
  return {
2038
2053
  items,
@@ -2082,4 +2097,4 @@ function scopeToRegions(items, active, regionsByRentalId) {
2082
2097
  return items.filter((item) => regionsByRentalId(item.id)?.some((r) => active.includes(r)));
2083
2098
  }
2084
2099
  //#endregion
2085
- export { BOOKING_SNAPSHOT_KEY, BUTTON_SOLUTION_PATTERN, CHECKOUT_CONTEXT_KEY, CHECKOUT_PARTS, CHECKOUT_RETURN_PATH, CHECKOUT_STRUCTURE, DEFAULT_SORT, HUB_ENDPOINTS, MULTI_RATE_CODES, NIGHT_RANGE_ENABLED, NIGHT_RANGE_KEYS, NO_INSURANCE_DOCS, PETS_COUNT_PARAM, PETS_PARAM, REGION_PARAM, SORT_OPTIONS, SORT_PARAM, STAGES, VALIDATED_FIELDS, VOFFICE_ENDPOINTS, activeFiltersFromQuery, activeRegions, activeSort, adaptGuestQuote, addQuoteService, assertCheckoutStructure, clearQuoteServices, createCheckoutFlow, createSdk, createStore, describeSdkError, detectCountry, emailError, fetchEndAvailability, fetchInitialAvailability, fetchQuote, fieldError, freeCancellationUntil, fullRefundRule, insuranceDocsPending, insuranceIdToReapply, insuranceProductKey, isBooleanFilterActive, isPricedQuery, isRateConfigurationFault, isSortAvailable, isSubmittableOption, isoDate, logSdkFailure, nightlyPrice, normalizePhone, occupancyOf, parseGermanParam, parseNightRange, parseParam, payWithOption, petsCountInQuery, phoneError, policyFreeCancellationUntil, postalcodeError, pricePolicyBranches, removeQuoteService, resolveSdkSort, runLiveSearch, saveBookingSnapshot, saveCheckoutContext, scopeToRegions, sdkConfigFor, selectQuoteInsurance, selectQuotePolicy, selectableInsuranceOptions, selectedInsuranceOption, shownInsuranceResources, stabilizeCardOrder, stayKeyOf, stayParams, streetWarning, submitBooking, tidyPrice };
2100
+ export { BOOKING_SNAPSHOT_KEY, BUTTON_SOLUTION_PATTERN, CHECKOUT_CONTEXT_KEY, CHECKOUT_PARTS, CHECKOUT_RETURN_PATH, CHECKOUT_STRUCTURE, DEFAULT_RENTALS_PATH, DEFAULT_SORT, HUB_ENDPOINTS, MULTI_RATE_CODES, NIGHT_RANGE_ENABLED, NIGHT_RANGE_KEYS, NO_INSURANCE_DOCS, PETS_COUNT_PARAM, PETS_PARAM, REGION_PARAM, SORT_OPTIONS, SORT_PARAM, STAGES, VALIDATED_FIELDS, VOFFICE_ENDPOINTS, activeFiltersFromQuery, activeRegions, activeSort, adaptGuestQuote, addQuoteService, assertCheckoutStructure, clearQuoteServices, createCheckoutFlow, createSdk, createStore, describeSdkError, detectCountry, emailError, fetchEndAvailability, fetchInitialAvailability, fetchQuote, fieldError, freeCancellationUntil, fullRefundRule, insuranceDocsPending, insuranceIdToReapply, insuranceProductKey, isBooleanFilterActive, isPricedQuery, isRateConfigurationFault, isSortAvailable, isSubmittableOption, isoDate, logSdkFailure, nightlyPrice, normalizePhone, occupancyOf, parseGermanParam, parseNightRange, parseParam, payWithOption, petsCountInQuery, phoneError, policyFreeCancellationUntil, postalcodeError, pricePolicyBranches, removeQuoteService, resolveSdkSort, runLiveSearch, saveBookingSnapshot, saveCheckoutContext, scopeToRegions, sdkConfigFor, selectQuoteInsurance, selectQuotePolicy, selectableInsuranceOptions, selectedInsuranceOption, shownInsuranceResources, stabilizeCardOrder, stayKeyOf, stayParams, streetWarning, submitBooking, tidyPrice };
@@ -0,0 +1,8 @@
1
+ # Changelog
2
+
3
+ <!-- generated by `node .github/release/cli.mts index`; do not edit by hand -->
4
+
5
+ Release notes of `@v-office/website-headless`, newest first. Each version has its own directory.
6
+
7
+ - `versions/0.2.0/CHANGELOG.md`: release notes for 0.2.0
8
+ - `versions/0.1.1/CHANGELOG.md`: release notes for 0.1.1
@@ -0,0 +1,8 @@
1
+ # Migration
2
+
3
+ <!-- generated by `node .github/release/cli.mts index`; do not edit by hand -->
4
+
5
+ Migration guides of `@v-office/website-headless`, newest first. Apply them in order when skipping versions.
6
+
7
+ - `versions/0.2.0/MIGRATION.md`: Migration: 0.1.1 to 0.2.0
8
+ - `versions/0.1.1/MIGRATION.md`: Migration to 0.1.1
@@ -0,0 +1,7 @@
1
+ # Changelog: 0.1.1
2
+
3
+ Baseline of the shipped release notes for `@v-office/website-headless`. The package is the framework-free behavior layer of the website platform: the checkout flow as a store, validation and insurance rules, the checkout's structural requirements and the progressive-enhancement engines.
4
+
5
+ Changes before 0.1.1 are recorded in this repository's git history only. From
6
+ this version on, every release carries its own `CHANGELOG.md` and
7
+ `MIGRATION.md` in `instructions/versions/<version>/`.
@@ -0,0 +1,4 @@
1
+ # Migration to 0.1.1
2
+
3
+ No changes required. 0.1.1 is the version that was published when the shipped
4
+ release notes were introduced; consumers already on it stay as they are.
@@ -0,0 +1,3 @@
1
+ {
2
+ "componentsAffectedByHeadlessChange": []
3
+ }
@@ -0,0 +1,17 @@
1
+ # Changelog: 0.2.0
2
+
3
+ The initial availability of the booking date picker now carries the v9 rules for children and
4
+ pets, so a guest selector can stop offering a party the quote would refuse.
5
+
6
+ ## Added
7
+
8
+ - `fetchInitialAvailability` returns `childrenAllowed` and `maxPets` in its
9
+ `InitialAvailability`. On v9 with `@v-office/website-sdk` 2.28.0 or later,
10
+ `childrenAllowed` is `false` when the rental takes no children, babies included, and
11
+ `maxPets` is the most pets the quote accepts, `0` for none. Both are `null` on v10, with an
12
+ older SDK and when the rental states no rule; `null` means no limit.
13
+
14
+ ## Unchanged
15
+
16
+ - `maxPersons` and `maxAdults` keep their meaning. On v9 they carry the rental's limits from
17
+ `@v-office/website-sdk` 2.28.0 on, where they were `null` before.
@@ -0,0 +1,16 @@
1
+ # Migration: 0.1.1 to 0.2.0
2
+
3
+ ## 1. Upgrade
4
+
5
+ ```sh
6
+ pnpm add @v-office/website-headless@0.2.0
7
+ ```
8
+
9
+ No changes are required for code that reads `fetchInitialAvailability`. Code that builds an
10
+ `InitialAvailability` itself, such as a test fixture, adds `childrenAllowed: null` and
11
+ `maxPets: null`.
12
+
13
+ A site with its own guest selector can use the two fields: offer neither children nor babies
14
+ when `childrenAllowed` is `false`, and cap pets at `maxPets`, hiding them at `0`. Keep a
15
+ selection the guest arrived with visible until they remove it, so a party the quote refuses
16
+ can still be corrected.
@@ -0,0 +1,3 @@
1
+ {
2
+ "componentsAffectedByHeadlessChange": ["quote", "booking"]
3
+ }
package/package.json CHANGED
@@ -1,10 +1,11 @@
1
1
  {
2
2
  "name": "@v-office/website-headless",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "description": "Framework-free behavior layer for v-office websites, never UI: the checkout flow as a subscribable store (createCheckoutFlow), validation and insurance rules, the checkout's structural requirements, the live search, availability and quote runners over a caller-owned SDK, and progressive-enhancement engines for server-rendered markup",
5
5
  "files": [
6
6
  "dist",
7
- "README.md"
7
+ "README.md",
8
+ "instructions"
8
9
  ],
9
10
  "type": "module",
10
11
  "sideEffects": false,
@@ -18,14 +19,14 @@
18
19
  "access": "public"
19
20
  },
20
21
  "devDependencies": {
21
- "@types/node": "26.1.2",
22
- "@v-office/website-sdk": "2.18.0",
22
+ "@types/node": "26.5.1",
23
+ "@v-office/website-sdk": "2.28.0",
23
24
  "jsdom": "^30.0.1",
24
- "oxfmt": "0.62.0",
25
- "oxlint": "1.77.0",
26
- "tsdown": "0.22.14",
25
+ "oxfmt": "0.67.0",
26
+ "oxlint": "1.82.0",
27
+ "tsdown": "0.23.0",
27
28
  "typescript": "7.0.2",
28
- "vitest": "^4.1.11"
29
+ "vitest": "^5.0.0"
29
30
  },
30
31
  "peerDependencies": {
31
32
  "@v-office/website-sdk": ">=2.13.0 <3"
@@ -42,6 +43,6 @@
42
43
  "fmt": "oxfmt",
43
44
  "fmt:check": "oxfmt --check",
44
45
  "check": "pnpm run typecheck && pnpm run lint && pnpm run fmt:check",
45
- "test": "vitest run"
46
+ "test": "vitest run && node --test test/dist.node.mjs"
46
47
  }
47
48
  }