@v-office/website-sdk 2.21.0 → 2.23.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.
@@ -4,6 +4,8 @@
4
4
 
5
5
  Migration guides of `@v-office/website-sdk`, newest first. Apply them in order when skipping versions.
6
6
 
7
+ - `versions/2.23.0/MIGRATION.md`: Migration: 2.22.0 to 2.23.0
8
+ - `versions/2.22.0/MIGRATION.md`: Migration: 2.21.0 to 2.22.0
7
9
  - `versions/2.21.0/MIGRATION.md`: Migration: 2.20.4 to 2.21.0
8
10
  - `versions/2.20.4/MIGRATION.md`: Migration: 2.20.3 to 2.20.4
9
11
  - `versions/2.20.3/MIGRATION.md`: Migration: 2.20.2 to 2.20.3
@@ -110,9 +110,14 @@ type WebsiteSDKOptions = {
110
110
  rentalPropertyHighlightPrioritization?: readonly RentalPropertyHighlightPrioritizationKey[];
111
111
  rentalScope?: RentalScope;
112
112
  shouldAddProposedAdditionalServiceAmount?: boolean;
113
+ searchAllRentalsAtOnce?: boolean;
113
114
  };
114
115
  ```
115
116
 
117
+ `searchAllRentalsAtOnce` selects how a site searches, on v9 and v10 alike: every
118
+ result with prices in one call (portfolios of up to 100 rentals), or paged results
119
+ plus `mapSearch`. See `search.md`.
120
+
116
121
  `rentalHighlightPrioritization` and `rentalPropertyHighlightPrioritization` select
117
122
  which facts become highlights and set their order. They are ordered allowlists, not
118
123
  sorts over every available fact. A key that is not listed is not emitted even when
@@ -135,6 +140,7 @@ Unset options are normalized by the SDK:
135
140
  - `rentalPropertyHighlightPrioritization` defaults to the SDK default property highlight selection.
136
141
  - `rentalScope` defaults to `{}`.
137
142
  - `shouldAddProposedAdditionalServiceAmount` defaults to `false`.
143
+ - `searchAllRentalsAtOnce` defaults to `false`.
138
144
  - `translationOverrides` is omitted unless provided.
139
145
 
140
146
  ### v9 Property Highlight Sources
@@ -374,6 +374,38 @@ requested locale; backend numeric amounts and modifier type keys are not exposed
374
374
  individual modifier lines. Exact-period results are returned in `items`;
375
375
  alternative-period suggestions are returned in root-level `alternatives`.
376
376
 
377
+ On legacy v9 only, an exact match may include `matchingPeriods` when the backend
378
+ chose the dates itself in a window search (`start`/`end` plus `minNights`/`maxNights`).
379
+ The field exists only on the v9 facade: `sdk.live.search.search` of a
380
+ `WebsiteSDKV9` returns `V9SearchSearchOutput`, whose items are
381
+ `V9SearchSearchOutputItem`. Narrow on `sdk.backend === "v9"` to read it. Each entry has the same shape as an `alternativePeriods` entry: `start`, `end`,
382
+ and the optional `formattedTotal` and `discount` for that period. Entries keep the
383
+ backend order. The item's own `formattedTotal` and `discount` are the price of the
384
+ first entry when that entry has one; otherwise they are the backend's overall price
385
+ for the hit. Show `matchingPeriods[0]` as the dates the card price is for, and open
386
+ the rental page with those dates so card and detail price agree. For a
387
+ fixed-period search without a window, the dates are the requested ones and
388
+ `matchingPeriods` is absent, never an empty array.
389
+
390
+ ```json
391
+ {
392
+ "id": "214950",
393
+ "nameOrLabel": "Ferienhaus Seeblick",
394
+ "timeZone": "Europe/Berlin",
395
+ "formattedTotal": "1.144,10 €",
396
+ "matchingPeriods": [
397
+ {
398
+ "start": "2026-10-01",
399
+ "end": "2026-10-08",
400
+ "formattedTotal": "1.144,10 €"
401
+ }
402
+ ]
403
+ }
404
+ ```
405
+
406
+ `pageInfo.totalCount` is the number of rentals the backend matched for the search
407
+ over all pages.
408
+
377
409
  `appliedFilters` reports what the backend actually applied, and is what a "remove
378
410
  this filter" chip should be built from. `label` is localized presentation and can
379
411
  repeat, so it cannot identify a filter; `key` plus `value` can. `value` is the
@@ -395,6 +427,84 @@ the expanded leaf entries. A UI may collapse those leaves under the composition
395
427
  display, but should keep the returned keys and canonical values when removing direct
396
428
  filters.
397
429
 
430
+ ## All Rentals At Once Or Map Search
431
+
432
+ A search that prices many results is slow the first time the backend sees it, and
433
+ a list next to a map should not wait for every price. An SDK therefore works in one
434
+ of two modes, chosen with the `searchAllRentalsAtOnce` option (default `false`).
435
+ Both modes behave the same on v9 and v10, so a site keeps its code when it moves
436
+ from one backend to the other.
437
+
438
+ ### `searchAllRentalsAtOnce: true`
439
+
440
+ For portfolios of up to `SEARCH_ALL_RENTALS_AT_ONCE_MAX_RESULTS` (100) rentals.
441
+ `sdk.live.search.search` returns every result with its price in one call, so a list
442
+ and a map can show all results with prices at the same time:
443
+
444
+ ```ts
445
+ const sdk = createWebsiteSDK({ config, options: { searchAllRentalsAtOnce: true } });
446
+ const output = await sdk.live.search.search({ locale: "de-DE", query: "adults=2" });
447
+ // output.pageInfo: { hasNextPage: false, totalCount }
448
+ ```
449
+
450
+ - The input has no `cursor` and no `limit`; passing either rejects the call with a
451
+ `CoreSDKError` whose `operation` is `search.allRentalsAtOnce`.
452
+ - When the search matches more than 100 rentals, the call rejects with the same
453
+ `operation`. Use the default mode for such a portfolio.
454
+ - `mapSearch` is not available: with `searchAllRentalsAtOnce: true` written in the
455
+ code, the returned `WebsiteSDKV9AllRentalsAtOnce`, `WebsiteSDKV10AllRentalsAtOnce`
456
+ or `WebsiteSDKAllRentalsAtOnce` type has no `mapSearch` and no `cursor`/`limit`;
457
+ when the value is only known at runtime, `mapSearch` rejects with the `operation`
458
+ `search.mapSearch`.
459
+ - The call counts as one request for the search rate limit.
460
+
461
+ ### `searchAllRentalsAtOnce: false` (default)
462
+
463
+ `sdk.live.search.search` is paged as before. `sdk.live.search.mapSearch` returns
464
+ every rental the search matches, with its location and without prices:
465
+
466
+ ```ts
467
+ const map = await sdk.live.search.mapSearch({ locale: "de-DE", query: "adults=2" });
468
+ // map: { items: { id, location? }[], totalCount, appliedFilters, unusedFilterKeys }
469
+ ```
470
+
471
+ `mapSearch` takes the same `query` as `search`, so map and list use the same
472
+ filters. Because it arrives before any result is priced, its `totalCount` can also
473
+ decide how to load the list: at most `SEARCH_ALL_RENTALS_AT_ONCE_MAX_RESULTS`
474
+ matches can be loaded in one `search` call with that `limit` and shown with prices on
475
+ the map (matched by `id`); more matches are paged, with a map without prices.
476
+
477
+ ```ts
478
+ const map = await sdk.live.search.mapSearch({ locale, query });
479
+ if (map.totalCount <= SEARCH_ALL_RENTALS_AT_ONCE_MAX_RESULTS) {
480
+ const all = await sdk.live.search.search({
481
+ locale,
482
+ query,
483
+ limit: SEARCH_ALL_RENTALS_AT_ONCE_MAX_RESULTS,
484
+ });
485
+ } else {
486
+ const page = await sdk.live.search.search({ locale, query, limit: 24, cursor });
487
+ }
488
+ ```
489
+
490
+ In this mode a `search` call is one backend request, whatever its `limit`.
491
+
492
+ ### How each backend does it
493
+
494
+ - v9 prices the rentals of one request one after another, but separate requests in
495
+ parallel. `searchAllRentalsAtOnce` counts the matches without prices first, then
496
+ fetches the priced results as backend pages of ten, at most eight at a time, and
497
+ merges them in backend order; an oversized search is refused before any price is
498
+ requested. When a booking between the requests moves rentals across page
499
+ boundaries, the pages disagree with the count; the SDK then asks for all results in
500
+ one request, a consistent snapshot, instead of returning a set with a gap. `mapSearch` selects no prices and loads up to 1,000 matches per request,
501
+ as many requests as the matches need.
502
+ - v10 answers the whole result set in one request. `searchAllRentalsAtOnce` asks for
503
+ 101 results and refuses the search when the response total is above 100.
504
+ `mapSearch` is one request without prices that returns only the location, for up
505
+ to 1,000 matches: the backend's result window, so `items` holds at most 1,000
506
+ rentals while `totalCount` can be higher.
507
+
398
508
  ## Backend Behavior
399
509
 
400
510
  ### v10
@@ -421,6 +531,10 @@ projection:
421
531
  and booking flow.
422
532
  - Pagination uses the backend `from`/`size` window behind the same opaque `cursor`,
423
533
  capped at 1,000 results.
534
+ - v10 items have no `matchingPeriods`. A v10 hit counts as exact only when its period
535
+ equals the requested fixed period; a hit the backend priced for other dates,
536
+ including every hit of a `minNights`/`maxNights` window search, is returned in
537
+ `alternatives` with its dates in `alternativePeriods`.
424
538
 
425
539
  ### Legacy v9
426
540
 
@@ -442,7 +556,16 @@ shape:
442
556
  formatted original total.
443
557
  - The same mapping applies to exact-period items and individual alternative
444
558
  periods.
559
+ - For an exact match, every `additional_voffice_data.matchingPeriods` entry with
560
+ both dates becomes a `matchingPeriods` entry (`fromdate`/`tilldate` become
561
+ `start`/`end`, its `calc` becomes the period's price fields); entries without
562
+ dates are dropped. In a window search the guest enters a window and a stay
563
+ length, not the stay itself. The backend returns the bookable stays inside the
564
+ window, earliest first; the SDK keeps that order.
445
565
  - v9 never executes a custom-attribute search filter.
566
+ - A search requests only the rental data the search output reads: `name`,
567
+ `address`, `regionName`, `images`, the attribute behind each configured rental
568
+ highlight, the configured custom attributes and `rentalDataAttributes`.
446
569
 
447
570
  The field is omitted when v9 returns no final total or when its original total is
448
571
  not greater than the final total. v9 exposes at most one aggregated applied entry,
@@ -454,7 +577,7 @@ Use flat `WebsiteSDKConfig` files and optional `WebsiteSDKOptions`.
454
577
 
455
578
  For v10, config requires `searchEndpoint` (the full REST search URL) as of 2.5.0. See `creation.md` for `searchEndpoint` and the `customAttributes` option.
456
579
 
457
- Custom filter definitions can extend accepted search query keys. Search requests are rate-limited to one backend request per second with a queue size of five.
580
+ Custom filter definitions can extend accepted search query keys. Search requests are rate-limited to one backend request per second with a queue size of five. A `searchAllRentalsAtOnce` search and a `mapSearch` each count as one request, including every backend request they make.
458
581
 
459
582
  ## CLI Usage
460
583
 
@@ -0,0 +1,30 @@
1
+ # Changelog: 2.22.0
2
+
3
+ On a v9 hub, a search over a date window (`start`/`end` with `minNights`/`maxNights`)
4
+ lets the hub pick the stay itself: the earliest bookable stay in the window. Such a hit came back as an exact result with a price but without the
5
+ dates that price was for. A site could only link the rental page with the
6
+ window the guest typed, and the detail page then showed a different price than
7
+ the card.
8
+
9
+ ## Added
10
+
11
+ - `v9`: exact search results carry `matchingPeriods` when the hub chose the
12
+ dates. Each entry has `start`, `end` and the optional `formattedTotal` and
13
+ `discount` for that period, in the hub's order. The field is absent for a
14
+ fixed-period search, never an empty array.
15
+ - Types `V9SearchSearchOutput` and `V9SearchSearchOutputItem`.
16
+ `sdk.live.search.search` of a `WebsiteSDKV9` now returns
17
+ `V9SearchSearchOutput`; narrow on `sdk.backend === "v9"` to read
18
+ `matchingPeriods`.
19
+
20
+ ## Changed
21
+
22
+ - `v9`: the `formattedTotal` and `discount` of an exact result are the price of
23
+ its first `matchingPeriods` entry when that entry has a price.
24
+
25
+ ## Unchanged
26
+
27
+ - Fixed-period searches on v9: same items, same prices, no new field.
28
+ - Alternatives and their `alternativePeriods`.
29
+ - The v10 backend. v10 items have no `matchingPeriods`; a v10 hit for other
30
+ dates than the requested ones is still returned in `alternatives`.
@@ -0,0 +1,27 @@
1
+ # Migration: 2.21.0 to 2.22.0
2
+
3
+ ## 1. Upgrade
4
+
5
+ ```sh
6
+ pnpm add @v-office/website-sdk@2.22.0
7
+ ```
8
+
9
+ No changes are required. The v9 search result type is a superset of the
10
+ previous one, so existing code compiles and renders as before.
11
+
12
+ ## 2. Show the dates of a window-search hit (`v9`)
13
+
14
+ A v9 site that offers a date window with a night range should show
15
+ `matchingPeriods[0]` on the result card as the dates the price is for, and open
16
+ the rental page with those dates instead of the typed window. Card and detail
17
+ page then show the same price.
18
+
19
+ ```ts
20
+ if (sdk.backend === "v9") {
21
+ const output = await sdk.live.search.search(input);
22
+ for (const item of output.items) {
23
+ const period = item.matchingPeriods?.[0];
24
+ // period?.start, period?.end: dates for the card and the rental link
25
+ }
26
+ }
27
+ ```
@@ -0,0 +1,62 @@
1
+ # Changelog: 2.23.0
2
+
3
+ A search that the backend has not answered before is slow: the v9 backend prices
4
+ the rentals of one request one after another, about 40–75 ms per rental, and caches
5
+ only whole searches. A site that shows every result with its price on a map had to
6
+ request all of them in one call, which took 3–9 s for a portfolio of about 70
7
+ rentals on v9. Separate requests are priced in parallel, and a search that selects
8
+ no prices costs almost nothing. This release uses both, with the same API on v9 and
9
+ v10 so a site keeps its code when it switches backend: small portfolios get every
10
+ priced result in one call, large portfolios get a map search next to a paged list.
11
+
12
+ Measured against a v9 hub with 74 rentals, first request for a date range: 71
13
+ results in 8.6 s with one request, 2.1 s with `searchAllRentalsAtOnce`. A search
14
+ with few results gains nothing and costs the count request: 18 results in 1.4 s
15
+ before, 1.7 s now.
16
+
17
+ ## Added
18
+
19
+ - The `searchAllRentalsAtOnce` option (default `false`), on v9 and v10. With
20
+ `true`, `sdk.live.search.search` returns every result with its price in one call
21
+ and takes no `cursor` or `limit`. A search that matches more than
22
+ `SEARCH_ALL_RENTALS_AT_ONCE_MAX_RESULTS` (100) rentals, or a passed `cursor` or
23
+ `limit`, rejects with the `CoreSDKError` operation `search.allRentalsAtOnce`. The
24
+ whole call counts as one request for the search rate limit.
25
+ - v9: counts the matches without prices, then fetches the priced results as
26
+ parallel backend pages of ten, at most eight at a time. An oversized search is
27
+ refused before any price is requested. When a booking between the requests
28
+ shifts rentals across page boundaries, it asks for all results in one request
29
+ instead.
30
+ - v10: one request for 101 results; refused when the response total is above 100.
31
+ - `sdk.live.search.mapSearch({ locale, query })` in the default mode, on v9 and
32
+ v10: every rental the search matches, with `id` and `location`, plus
33
+ `totalCount`, `appliedFilters` and `unusedFilterKeys`, without prices. It
34
+ rejects with the operation `search.mapSearch` when the SDK was created with
35
+ `searchAllRentalsAtOnce: true`. v9 loads up to 1,000 matches per backend request;
36
+ v10 answers up to 1,000 matches, its result window.
37
+ - `pageInfo.totalCount` on every search output: the number of rentals the backend
38
+ matched over all pages.
39
+ - The types `WebsiteSDKAllRentalsAtOnce`, `WebsiteSDKV9AllRentalsAtOnce`,
40
+ `WebsiteSDKV10AllRentalsAtOnce` with their `...Live` types and
41
+ `V9SearchAllRentalsAtOnceInput`, and from `@v-office/sdk-core` 1.24.0
42
+ `SEARCH_ALL_RENTALS_AT_ONCE_MAX_RESULTS`, `SearchMapSearchInput`,
43
+ `SearchMapSearchOutput`, `SearchMapSearchOutputItem`,
44
+ `SearchAllRentalsAtOnceInput` and the matching schemas. `createWebsiteSDK`
45
+ returns an `...AllRentalsAtOnce` type when `searchAllRentalsAtOnce: true` is
46
+ written in the options; it has no `mapSearch` and no `cursor`/`limit`.
47
+
48
+ ## Changed
49
+
50
+ - `v9`: a search requests only the rental data the search output reads (`name`,
51
+ `address`, `regionName`, `images`, the attribute behind each configured rental
52
+ highlight, configured custom attributes and `rentalDataAttributes`) instead of
53
+ every known attribute: 56 instead of 450 attributes with the default highlights,
54
+ and a response of 52 KB instead of 164 KB gzipped (624 KB instead of 1,471 KB
55
+ uncompressed) for 71 rentals. The output is unchanged.
56
+ - Requires `@v-office/sdk-core` 1.24.0.
57
+
58
+ ## Unchanged
59
+
60
+ - A paged search in the default mode, on v9 and v10: same request per call, same
61
+ items, alternatives and prices; `pageInfo` gains `totalCount`. On v9 verified
62
+ against the hub with 2.22.0 side by side.
@@ -0,0 +1,33 @@
1
+ # Migration: 2.22.0 to 2.23.0
2
+
3
+ ## 1. Upgrade
4
+
5
+ ```sh
6
+ pnpm add @v-office/website-sdk@2.23.0
7
+ ```
8
+
9
+ No changes are required. The default mode searches as before, and the search
10
+ output only gains `pageInfo.totalCount`.
11
+
12
+ ## 2. Show every result with its price on list and map (up to 100 rentals)
13
+
14
+ Create the SDK with `searchAllRentalsAtOnce: true` and drop the pagination loop:
15
+ one `search` call returns every result with `pageInfo.hasNextPage: false`. Remove
16
+ `cursor` and `limit` from the input; the call rejects them. The same code works on
17
+ v9 and v10.
18
+
19
+ ```ts
20
+ const sdk = createWebsiteSDK({ config, options: { searchAllRentalsAtOnce: true } });
21
+ const { items, alternatives, pageInfo } = await sdk.live.search.search({ locale, query });
22
+ ```
23
+
24
+ ## 3. Map for a larger portfolio (default mode)
25
+
26
+ Load the map with `mapSearch` and page the list with `search`. Use `totalCount`
27
+ from `mapSearch` to load a search with few matches in one call and show its prices
28
+ on the map; see `search.md`, "All Rentals At Once Or Map Search".
29
+
30
+ ```ts
31
+ const map = await sdk.live.search.mapSearch({ locale, query });
32
+ // map.items: { id, location? }[] for every match; map.totalCount
33
+ ```
@@ -1,6 +1,6 @@
1
- import { Ut as V9_FACILITY_ATTRIBUTE_KEYS } from "./client-dWCukmLr.mjs";
1
+ import { Wt as V9_FACILITY_ATTRIBUTE_KEYS } from "./client-CGO5Pmuv.mjs";
2
2
  import { c as parseVofficeUnitData, i as VofficeUnitDataPropertyMetadata, n as VofficeUnitDataPropertyCategoryLabelKeys, o as parseVofficeFacilityData, r as VofficeUnitDataPropertyCategoryValues, s as parseVofficeFeedbackData, t as VofficeUnitDataFieldSchemas } from "./v9-data-BnYxa2Ek.mjs";
3
- import { a as toAddress, c as rentalFacilitiesForObjectGroupRelationQuery, d as rentalsAllFeedbacksQuery, f as rentalsAllWithoutFeedbacksQuery, i as toImages, l as rentalIdsQuery, n as renderVofficePropertyAttribute, o as toLocalizedString, r as toFacilityImages, t as toRentalHighlights, u as rentalsAllByFacilityObjectGroupsWithoutFeedbacksQuery } from "./to-rental-highlights-CN-wpRh3.mjs";
3
+ import { a as toFacilityImages, c as toLocalizedString, d as rentalFacilitiesForObjectGroupRelationQuery, f as rentalIdsQuery, h as rentalsAllWithoutFeedbacksQuery, i as toLocation, m as rentalsAllFeedbacksQuery, o as toImages, p as rentalsAllByFacilityObjectGroupsWithoutFeedbacksQuery, r as renderVofficePropertyAttribute, s as toAddress, t as toRentalHighlights } from "./to-rental-highlights-C1NF2uuE.mjs";
4
4
  import { CoreSDKError, makeTranslate, renderResolvedCustomAttribute, toCustomAttributeList, toResolvedCustomAttributeCategoryTranslationKey } from "@v-office/sdk-core";
5
5
  import { Effect } from "effect";
6
6
  //#region src/legacy-v9/parser/rentals/to-description.ts
@@ -25,16 +25,6 @@ const toDescription = (data, locale) => {
25
25
  return description;
26
26
  };
27
27
  //#endregion
28
- //#region src/legacy-v9/parser/rentals/to-location.ts
29
- const toLocation = (location) => {
30
- if (location == null) return void 0;
31
- const [longitude, latitude] = location.coordinates;
32
- return {
33
- latitude,
34
- longitude
35
- };
36
- };
37
- //#endregion
38
28
  //#region src/legacy-v9/parser/rentals/to-property-attributes.ts
39
29
  const toPropertyAttributes = ({ attributes, locale, translations }) => Effect.gen(function* () {
40
30
  const items = [];
@@ -1,6 +1,6 @@
1
- import { a as parseQueryParameters, i as collectQueryParameters, n as makeV9CustomAttributeFilterCapabilities, o as toQueryString, r as toBasicQueryInputs, s as applyVofficeFilterDerivedBasicQueryInputs } from "./client-dWCukmLr.mjs";
2
- import { c as parseVofficeUnitData, t as VofficeUnitDataFieldSchemas } from "./v9-data-BnYxa2Ek.mjs";
3
- import { a as toAddress, i as toImages, s as searchQuery, t as toRentalHighlights } from "./to-rental-highlights-CN-wpRh3.mjs";
1
+ import { a as parseQueryParameters, i as collectQueryParameters, n as makeV9CustomAttributeFilterCapabilities, o as toQueryString, r as toBasicQueryInputs, s as applyVofficeFilterDerivedBasicQueryInputs } from "./client-CGO5Pmuv.mjs";
2
+ import { c as parseVofficeUnitData } from "./v9-data-BnYxa2Ek.mjs";
3
+ import { i as toLocation, l as mapSearchQuery, n as toV9RentalHighlightKey, o as toImages, s as toAddress, t as toRentalHighlights, u as searchQuery } from "./to-rental-highlights-C1NF2uuE.mjs";
4
4
  import { CoreSDKError, STABLE_SEARCH_INPUTS, daysBetweenLocalDates, expandCustomAttributeFilterCompositions, parseChildrenAges, parseOccupancyCount, resolveExpandedCompositions, toFormattedSearchPrice, toIsoDateFromPeriodQueryDate, toStableSearchInputBackendQueryKeys, toStableSearchInputParameterValues, toStableSearchInputQueryKeys, toUnusedFilterKeys } from "@v-office/sdk-core";
5
5
  import { Effect } from "effect";
6
6
  //#region src/legacy-v9/parser/search/input/search-input-error.ts
@@ -235,12 +235,18 @@ const toQueryInput = ({ query, sort, customAttributeFilterDefinitions, locale, t
235
235
  //#region src/legacy-v9/parser/search/resolve-exact-match.ts
236
236
  const resolveExactMatch = (additionalData) => {
237
237
  const alternatives = additionalData?.alternatives ?? [];
238
- const calc = additionalData?.calc ?? additionalData?.matchingPeriods?.[0]?.calc;
239
- const isExactMatch = additionalData?.foundExactMatch === true || additionalData?.foundExactMatch == null && alternatives.length === 0 && calc != null;
238
+ const matchingPeriods = additionalData?.matchingPeriods ?? [];
239
+ const calcCandidates = [
240
+ matchingPeriods.find((period) => period.fromdate != null && period.tilldate != null)?.calc,
241
+ additionalData?.calc,
242
+ matchingPeriods[0]?.calc
243
+ ];
244
+ const calc = calcCandidates.find((candidate) => candidate?.total != null);
245
+ const isExactMatch = additionalData?.foundExactMatch === true || additionalData?.foundExactMatch == null && alternatives.length === 0 && calcCandidates.some((candidate) => candidate != null);
240
246
  return {
241
247
  isExactMatch,
242
248
  exactMatchCalc: isExactMatch ? calc : void 0,
243
- periods: isExactMatch ? [] : alternatives
249
+ periods: isExactMatch ? matchingPeriods : alternatives
244
250
  };
245
251
  };
246
252
  //#endregion
@@ -302,20 +308,32 @@ const toSearchOutputItem = ({ locale, imageProxyBaseUrl, rentalHighlightPrioriti
302
308
  return item;
303
309
  });
304
310
  //#endregion
305
- //#region src/legacy-v9/parser/search/output/to-alternative-periods.ts
306
- const toAlternativePeriods = ({ locale, alternatives }) => Effect.succeed((alternatives ?? []).flatMap((alternative) => {
307
- if (alternative.fromdate == null || alternative.tilldate == null) return [];
311
+ //#region src/legacy-v9/parser/search/output/to-search-periods.ts
312
+ const toSearchPeriods = ({ locale, periods }) => Effect.succeed((periods ?? []).flatMap((period) => {
313
+ if (period.fromdate == null || period.tilldate == null) return [];
308
314
  return [{
309
- start: alternative.fromdate,
310
- end: alternative.tilldate,
315
+ start: period.fromdate,
316
+ end: period.tilldate,
311
317
  ...toSearchPriceFields({
312
318
  locale,
313
- calc: alternative.calc
319
+ calc: period.calc
314
320
  })
315
321
  }];
316
322
  }));
317
323
  //#endregion
318
324
  //#region src/legacy-v9/runtime/search.ts
319
- const defaultSearchDataAttributes = Object.keys(VofficeUnitDataFieldSchemas);
325
+ /**
326
+ * The rental data `toSearchOutputItem` reads: name, address with the region name,
327
+ * images, and the v9 attribute behind each configured highlight key. Requesting
328
+ * every known attribute instead more than doubles the response for the same output.
329
+ */
330
+ const toSearchDataAttributes = (rentalHighlightPrioritization) => [.../* @__PURE__ */ new Set([
331
+ "name",
332
+ "address",
333
+ "regionName",
334
+ "images",
335
+ ...rentalHighlightPrioritization.flatMap((key) => toV9RentalHighlightKey(key) ?? [])
336
+ ])];
337
+ const mapSearchDataAttributes = ["loc"];
320
338
  //#endregion
321
- export { defaultSearchDataAttributes, resolveExactMatch, searchQuery, toAlternativePeriods, toQueryInput, toSearchOutputItem };
339
+ export { mapSearchDataAttributes, mapSearchQuery, parseVofficeUnitData, resolveExactMatch, searchQuery, toLocation, toQueryInput, toSearchDataAttributes, toSearchOutputItem, toSearchPeriods };