@v-office/website-sdk 2.7.0 → 2.8.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.
Files changed (41) hide show
  1. package/README.md +28 -1
  2. package/dist/cli.mjs +25 -4
  3. package/dist/{client-45_ofP_d.mjs → client-BNbBfJtO.mjs} +385 -187
  4. package/dist/index.d.mts +5 -4
  5. package/dist/index.mjs +3 -3
  6. package/dist/instructions/CHANGELOG.md +3 -1
  7. package/dist/instructions/MIGRATION.md +3 -1
  8. package/dist/instructions/README.md +8 -3
  9. package/dist/instructions/creation.md +65 -64
  10. package/dist/instructions/custom-attributes.md +556 -0
  11. package/dist/instructions/filter.md +51 -23
  12. package/dist/instructions/rentals.md +26 -2
  13. package/dist/instructions/search.md +29 -3
  14. package/dist/instructions/versions/2.5.0/MIGRATION.md +5 -0
  15. package/dist/instructions/versions/2.6.0/CHANGELOG.md +3 -25
  16. package/dist/instructions/versions/2.6.0/MIGRATION.md +4 -72
  17. package/dist/instructions/versions/2.7.0/CHANGELOG.md +44 -0
  18. package/dist/instructions/versions/2.7.0/MIGRATION.md +79 -0
  19. package/dist/instructions/versions/2.8.0/CHANGELOG.md +63 -0
  20. package/dist/instructions/versions/2.8.0/MIGRATION.md +252 -0
  21. package/dist/{rentals-Lw1qUKIj.mjs → rentals-cCejb_2d.mjs} +21 -21
  22. package/dist/{search-wcoZw2rt.mjs → search-Dzzra6IW.mjs} +16 -133
  23. package/dist/{to-rental-highlights-CxWlq76f.mjs → to-rental-highlights-OvNe9ZQS.mjs} +8 -6
  24. package/dist/translations/v10/de-DE/core.json +2 -0
  25. package/dist/translations/v10/en-US/core.json +2 -0
  26. package/instructions/CHANGELOG.md +3 -1
  27. package/instructions/MIGRATION.md +3 -1
  28. package/instructions/README.md +8 -3
  29. package/instructions/creation.md +65 -64
  30. package/instructions/custom-attributes.md +556 -0
  31. package/instructions/filter.md +51 -23
  32. package/instructions/rentals.md +26 -2
  33. package/instructions/search.md +29 -3
  34. package/instructions/versions/2.5.0/MIGRATION.md +5 -0
  35. package/instructions/versions/2.6.0/CHANGELOG.md +3 -25
  36. package/instructions/versions/2.6.0/MIGRATION.md +4 -72
  37. package/instructions/versions/2.7.0/CHANGELOG.md +44 -0
  38. package/instructions/versions/2.7.0/MIGRATION.md +79 -0
  39. package/instructions/versions/2.8.0/CHANGELOG.md +63 -0
  40. package/instructions/versions/2.8.0/MIGRATION.md +252 -0
  41. package/package.json +5 -3
@@ -147,14 +147,38 @@ Optional `WebsiteSDKOptions`:
147
147
 
148
148
  ```json
149
149
  {
150
- "rentalHighlightPrioritization": ["bedrooms", "bathrooms", "maxPersons", "wifi"],
150
+ "customAttributes": {
151
+ "catalog": {
152
+ "privateSauna": {
153
+ "id": "GQYKYgMVh28X7VLGursbkt",
154
+ "type": "BOOLEAN"
155
+ }
156
+ },
157
+ "select": {
158
+ "privateSauna": {
159
+ "label": { "de-DE": "Sauna", "en-US": "Sauna" }
160
+ }
161
+ }
162
+ },
163
+ "rentalHighlightPrioritization": ["bedrooms", "privateSauna", "wifi"],
151
164
  "rentalPropertyHighlightPrioritization": ["wifi", "parking"],
152
165
  "customAttributeFilterDefinitions": [],
153
166
  "translationOverrides": {}
154
167
  }
155
168
  ```
156
169
 
157
- `rentalHighlightPrioritization` controls the order of rental `highlights`. `rentalPropertyHighlightPrioritization` controls property highlights for v10 property data. Custom attribute definitions can add custom values to rental output. Translation overrides affect localized labels.
170
+ `rentalHighlightPrioritization` controls the order of rental `highlights`. `rentalPropertyHighlightPrioritization` controls property highlights for v10 property data. Translation overrides affect localized labels.
171
+
172
+ In CLI JSON, `customAttributes` contains catalog and selection inputs; the CLI builds
173
+ the validated registry. Typed applications normally call `defineCustomAttributes`
174
+ and pass its result instead.
175
+
176
+ Custom attributes configured through `customAttributes` add values to rental output on
177
+ both backends. A value appears in `highlights` only when its key is listed in
178
+ `rentalHighlightPrioritization`, and in `attributes` under the group named by its
179
+ authored `category` — alongside built-in attributes of that category, not in a separate
180
+ bucket. Category and attribute `label`s in the output are localized strings, never
181
+ stable keys. See `custom-attributes.md`.
158
182
 
159
183
  ## CLI Usage
160
184
 
@@ -272,7 +272,12 @@ Sample:
272
272
  "appliedFilters": [
273
273
  {
274
274
  "key": "wifi",
275
- "label": "WiFi"
275
+ "label": "WLAN"
276
+ },
277
+ {
278
+ "key": "region",
279
+ "label": "Region: Norden",
280
+ "value": "north"
276
281
  }
277
282
  ],
278
283
  "unusedFilterKeys": [],
@@ -285,13 +290,34 @@ Sample:
285
290
 
286
291
  Search items share the rental base fields and may include `scope`, `address`, `property`, `highlights`, and `formattedTotal`. Exact-period results are returned in `items`; alternative-period suggestions are returned in root-level `alternatives`.
287
292
 
293
+ `appliedFilters` reports what the backend actually applied, and is what a "remove
294
+ this filter" chip should be built from. `label` is localized presentation and can
295
+ repeat, so it cannot identify a filter; `key` plus `value` can. `value` is the
296
+ canonical applied value and is present only for filters that carry one — on/off
297
+ filters are identified by `key` alone. A key you sent that is **not** listed here
298
+ appears in `unusedFilterKeys` and had no effect on the result set. A scalar key may
299
+ appear in both arrays when its first value was applied but additional values were
300
+ rejected.
301
+
302
+ Composition filters are atomic in both metadata and the backend payload. The SDK
303
+ validates all configured leaves against the active backend before committing any
304
+ generated GraphQL or REST input. If one leaf is unsupported or malformed, no
305
+ composition-generated leaf restricts the result, and only the composition key is
306
+ reported unused. Direct filters are tracked independently and remain applied even
307
+ when a failed composition references the same key.
308
+
309
+ When every leaf succeeds, `appliedFilters` contains both the composition entry and
310
+ the expanded leaf entries. A UI may collapse those leaves under the composition for
311
+ display, but should keep the returned keys and canonical values when removing direct
312
+ filters.
313
+
288
314
  ## v10 Backend
289
315
 
290
316
  v10 search runs against the dedicated REST search backend (`POST` to the configured `searchEndpoint`). Existing calls and the output type remain compatible, while the input additionally supports optional sorting. v10 results reflect the REST projection:
291
317
 
292
318
  - At most five images per item, and no image `category`.
293
319
  - `property` is the minimal `{ id, nameOrLabel }` shape. Hydrate `property.location`, `property.images`, or `property.address` from `sdk.static.rentals.getRentals(...)` when a card needs them.
294
- - Custom-attribute search filters and highlights require a `customAttributeDefinitionManifest`; without it, custom-attribute query keys are left in `unusedFilterKeys` and no custom-attribute highlights are added.
320
+ - Custom-attribute search filters and highlights require the `customAttributes` registry. A definition with no catalog ID cannot be executed, so its query key is left in `unusedFilterKeys`. v9 never executes a custom-attribute filter at all. See `custom-attributes.md`.
295
321
  - Fixed-period and flexible searches are priced by the REST backend and require occupancy.
296
322
  - Pagination uses the backend `from`/`size` window behind the same opaque `cursor`, capped at 1,000 results.
297
323
 
@@ -299,7 +325,7 @@ v10 search runs against the dedicated REST search backend (`POST` to the configu
299
325
 
300
326
  Use flat `WebsiteSDKConfig` files and optional `WebsiteSDKOptions`.
301
327
 
302
- For v10, config requires `searchEndpoint` (the full REST search URL) as of 2.5.0. See `creation.md` for `searchEndpoint` and the `customAttributeDefinitionManifest` option.
328
+ For v10, config requires `searchEndpoint` (the full REST search URL) as of 2.5.0. See `creation.md` for `searchEndpoint` and the `customAttributes` option.
303
329
 
304
330
  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.
305
331
 
@@ -1,5 +1,10 @@
1
1
  # Migration: 2.4.3 to 2.5.0
2
2
 
3
+ > Custom-attribute manifest guidance below is historically correct for 2.5.x but is
4
+ > superseded in 2.8.0. When upgrading current packages, follow
5
+ > `../2.8.0/MIGRATION.md` to replace `customAttributeDefinitionManifest` with
6
+ > `defineCustomAttributes`.
7
+
3
8
  This guide covers upgrading `@v-office/website-sdk` from 2.4.3 to 2.5.0.
4
9
 
5
10
  2.5.0 keeps `createWebsiteSDK({ config, options })`, the flat `WebsiteSDKConfig`, and existing SDK calls. v10 search now runs against the dedicated REST search backend, which requires one new config field and, for custom-attribute search, one new option. Search input also gains optional sorting, and static v10 rental loading uses the full Rental APIs internally. Existing calls and output types remain compatible.
@@ -1,28 +1,6 @@
1
1
  # Changelog: 2.6.0
2
2
 
3
- Release date: 2026-07-27
3
+ `@v-office/website-sdk` 2.6.0 was never published. The Stripe Checkout work
4
+ documented under this version during development shipped as 2.7.0 on 2026-07-27.
4
5
 
5
- This release makes v9 `stripe_checkout` payment options submittable. The vOffice `initStripePayment` action returns only session metadata, so `submitPaymentOption` now opens the Checkout Session with Stripe.js when the backend omits the session URL. Config, service calls, and output types are unchanged.
6
-
7
- ## Added
8
-
9
- - `submitPaymentOption` and `submitPaymentOptionEffect` now complete `stripe_checkout` options that carry only `sessionId` and `accountId`. Previously these options failed with "Stripe Checkout requires a checkout session URL".
10
- - Stripe.js is loaded from `https://js.stripe.com/v3/` on first use and reused for later submissions. A failed load is not kept, so the next submission retries it. An existing `window.Stripe` instance is used when the page already provides one, so no second script tag is added.
11
- - The vOffice Stripe platform publishable keys are embedded in the SDK. Stripe Checkout requires no consumer configuration.
12
-
13
- ## Changed
14
-
15
- - `stripe_checkout` submission prefers the Checkout Session `url` and only falls back to Stripe.js when it is absent. Once vOffice returns the URL, the SDK uses it without a further release.
16
- - The publishable key is selected from the session id, so tenants in Stripe test mode (`cs_test_`) and live mode (`cs_live_`) both work without configuration or environment detection.
17
- - Stripe sessions are created on connected accounts, so the option's `accountId` is passed to Stripe.js as `stripeAccount`.
18
- - The failure raised when a `stripe_checkout` option carries neither a URL nor a session id now names both.
19
- - Browser access helpers used by payment submission moved into an internal module shared by redirect, form-post, and Stripe submission. This is internal only and does not change the public API.
20
-
21
- ## Migration Impact
22
-
23
- - Consumers that hid Stripe payment options because `url` was missing can render them again. Gate the button on the option being present rather than on `option.url`. See `versions/2.6.0/MIGRATION.md`.
24
- - Sites with a Content-Security-Policy must allow `script-src https://js.stripe.com` and `frame-src https://js.stripe.com https://hooks.stripe.com`.
25
- - Payment submission remains browser-only and still fails outside a browser environment.
26
- - Stripe.js is fetched at submission time, so payment submission can now fail from a blocked or unavailable third-party script. Treat that as a payment failure, never as a booking failure: the reservation already exists when payment options are shown.
27
- - No config, option, or output type changes. v10 Adyen `redirect` flows and all `bank_transfer` and `form_post` flows are unaffected.
28
- - Stripe removed `redirectToCheckout` from its versioned Stripe.js release trains (`clover`, 2025-09-30, and later) and from the published `stripe-js` types. The method is still implemented in the unversioned `https://js.stripe.com/v3/` bundle that the SDK loads, which is why this fallback works. It is a bridge until vOffice returns the session URL and is expected to be removed afterwards. Should a future Stripe.js build drop the method entirely, submission fails with a message naming the missing session URL.
6
+ Use `../2.7.0/CHANGELOG.md` as the authoritative release record.
@@ -1,74 +1,6 @@
1
- # Migration: 2.5.x to 2.6.0
1
+ # Migration: 2.6.0
2
2
 
3
- This guide covers upgrading `@v-office/website-sdk` from 2.5.0 or 2.5.1 to 2.6.0.
3
+ `@v-office/website-sdk` 2.6.0 was never published. The planned Stripe Checkout
4
+ change shipped in 2.7.0.
4
5
 
5
- 2.6.0 changes no config, no service call, and no output type. The only change is that `stripe_checkout` payment options can now be submitted on the v9 backend. If your site does not offer Stripe, upgrading requires no work at all.
6
-
7
- ## What Changed
8
-
9
- vOffice's `initStripePayment` action returns only `sessionId` and `accountId`, never the Checkout Session URL. Because `submitPaymentOption` redirected exclusively to a session URL, Stripe payment options could never be completed and failed with "Stripe Checkout requires a checkout session URL".
10
-
11
- 2.6.0 keeps the URL redirect as the preferred path and adds a fallback: when a `stripe_checkout` option has a `sessionId` but no `url`, the SDK loads Stripe.js and opens the Checkout Session with it. This is the same mechanism the classic non-SDK vOffice websites use.
12
-
13
- The fallback is self-retiring. When vOffice starts returning the session URL, the option gains a `url`, the URL redirect wins, and Stripe.js is never loaded. No SDK release is needed for that transition.
14
-
15
- ## Remove Stripe Workarounds
16
-
17
- Sites that hid the Stripe button because no URL was present should now render it. Gate on the option existing rather than on `option.url`:
18
-
19
- ```ts
20
- // Before: Stripe options were unusable, so they had to be hidden
21
- const payable = option.kind !== "stripe_checkout" || option.url !== undefined;
22
-
23
- // After: every submittable option can be submitted
24
- const payable = true;
25
- ```
26
-
27
- Sites that implemented their own Stripe.js bridge with a publishable key can delete it, along with the key configuration. The SDK carries the vOffice platform keys and selects between test and live based on the session id, so a tenant in Stripe test mode and a tenant in live mode both work from the same build.
28
-
29
- ## Content-Security-Policy
30
-
31
- Stripe.js is fetched from Stripe at submission time. If your site sets a CSP, allow:
32
-
33
- ```
34
- script-src https://js.stripe.com
35
- frame-src https://js.stripe.com https://hooks.stripe.com
36
- ```
37
-
38
- Without these directives the script is blocked and Stripe submission fails. Other payment kinds are unaffected.
39
-
40
- ## Error Handling
41
-
42
- Payment submission can now fail for new reasons: Stripe.js could not be loaded, the loaded Stripe.js build does not provide the Checkout redirect, or Stripe rejected the redirect. All surface as a `CoreSDKError` with `operation: "paymentSubmission"`, the same as existing submission failures.
43
-
44
- Offering a retry is worthwhile for these failures. A failed Stripe.js load is not remembered, so the next submission fetches the script again and can succeed without a page reload.
45
-
46
- The booking already exists by the time payment options are rendered, so a submission failure must never be presented as a failed booking:
47
-
48
- ```ts
49
- try {
50
- await sdk.live.booking.submitPaymentOption(option);
51
- } catch (error) {
52
- // The reservation is created. Show the booking number and offer bank transfer.
53
- showPaymentFailure({ bookingNumber: booking.bookingNumber, error });
54
- }
55
- ```
56
-
57
- Submission is still browser-only and fails outside a browser environment, unchanged from earlier versions.
58
-
59
- ## Payment Return
60
-
61
- Unchanged from earlier versions, but worth confirming while testing Stripe end to end. The SDK derives the Stripe success and cancel URLs from the `relativeRedirectUrl` passed to `sdk.live.booking.book(...)` and appends `payment=stripe&success=true` or `payment=stripe&cancel=true`. That path must exist in your site and should read those query parameters.
62
-
63
- Note that `relativeRedirectUrl` is supplied before the reservation exists, so the return URL cannot contain the booking number or guest token. Persist anything the return page needs, for example in `sessionStorage`, before submitting the payment option.
64
-
65
- A `success=true` return means Stripe sent the browser back, not that payment has settled. Payment confirmation reaches vOffice through Stripe's webhook.
66
-
67
- ## Recommended Steps
68
-
69
- 1. Upgrade to `@v-office/website-sdk` 2.6.0.
70
- 2. Remove any conditional that hides Stripe payment options when `option.url` is missing.
71
- 3. Remove a site-level Stripe.js bridge and its publishable key configuration if you added one.
72
- 4. Add the Stripe CSP directives if your site sets a Content-Security-Policy.
73
- 5. Confirm payment submission errors are presented as payment failures with the booking number, not as booking failures.
74
- 6. Smoke-test one booking against a Stripe test tenant and one against a live tenant, checking that Checkout opens and that the configured return path handles both `success=true` and `cancel=true`.
6
+ Consumers upgrading from 2.5.x should follow `../2.7.0/MIGRATION.md`.
@@ -0,0 +1,44 @@
1
+ # Changelog: 2.7.0
2
+
3
+ Release date: 2026-07-27
4
+
5
+ This release makes v9 `stripe_checkout` payment options submittable. The vOffice
6
+ `initStripePayment` action returns only session metadata, so `submitPaymentOption`
7
+ now opens the Checkout Session with Stripe.js when the backend omits the session
8
+ URL. Config, service calls, and output types are unchanged.
9
+
10
+ ## Added
11
+
12
+ - `submitPaymentOption` and `submitPaymentOptionEffect` now complete
13
+ `stripe_checkout` options that carry only `sessionId` and `accountId`.
14
+ - Stripe.js is loaded from `https://js.stripe.com/v3/` on first use and reused for
15
+ later submissions. Failed loads are not cached, and an existing `window.Stripe`
16
+ is reused.
17
+ - The SDK includes the vOffice Stripe platform publishable keys, so Stripe Checkout
18
+ needs no consumer key configuration.
19
+
20
+ ## Changed
21
+
22
+ - `stripe_checkout` prefers a Checkout Session `url` and falls back to Stripe.js
23
+ only when the URL is absent.
24
+ - Test (`cs_test_`) and live (`cs_live_`) sessions select the corresponding
25
+ publishable key automatically.
26
+ - Connected-account sessions pass `accountId` to Stripe.js as `stripeAccount`.
27
+ - Missing-session failures now name both the absent URL and session ID.
28
+
29
+ ## Migration Impact
30
+
31
+ - Sites that hid Stripe options because `url` was absent can render them again.
32
+ Gate the button on the payment option being present rather than on `option.url`.
33
+ - Content-Security-Policy must allow `script-src https://js.stripe.com` and
34
+ `frame-src https://js.stripe.com https://hooks.stripe.com`.
35
+ - Payment submission remains browser-only. A Stripe script or redirect failure is
36
+ a payment failure after the booking exists, not a booking failure.
37
+ - No config, service-call, or output migration is required. See
38
+ `versions/2.7.0/MIGRATION.md`.
39
+
40
+ ## Versioning Note
41
+
42
+ The implementation was documented during development under a planned 2.6.0
43
+ directory, but 2.6.0 was never published. The package was released as 2.7.0; this
44
+ directory is the authoritative release record.
@@ -0,0 +1,79 @@
1
+ # Migration: 2.5.x to 2.7.0
2
+
3
+ This guide covers upgrading `@v-office/website-sdk` from 2.5.0 or 2.5.1 to 2.7.0.
4
+ Version 2.6.0 was not published.
5
+
6
+ 2.7.0 changes no config, service call, or output type. The runtime change is that
7
+ v9 `stripe_checkout` payment options can now be submitted. Sites without Stripe
8
+ need no migration work.
9
+
10
+ ## Remove Stripe Workarounds
11
+
12
+ vOffice's `initStripePayment` action can return `sessionId` and `accountId` without
13
+ the Checkout Session URL. The SDK now keeps URL redirects as the preferred path and
14
+ uses Stripe.js when only session metadata is available.
15
+
16
+ Sites that hid Stripe options without a URL should render them:
17
+
18
+ ```ts
19
+ // Before
20
+ const payable = option.kind !== "stripe_checkout" || option.url !== undefined;
21
+
22
+ // 2.7.0
23
+ const payable = true;
24
+ ```
25
+
26
+ Delete site-level Stripe.js bridges and publishable-key configuration added only to
27
+ work around the missing URL. The SDK selects the vOffice platform test or live key
28
+ from the session ID and passes connected-account context automatically.
29
+
30
+ ## Content-Security-Policy
31
+
32
+ If the site sets a CSP, allow:
33
+
34
+ ```text
35
+ script-src https://js.stripe.com
36
+ frame-src https://js.stripe.com https://hooks.stripe.com
37
+ ```
38
+
39
+ Without these directives, Stripe submission fails. Other payment kinds are
40
+ unaffected.
41
+
42
+ ## Error Handling
43
+
44
+ Stripe.js loading or redirecting can fail. These failures surface as
45
+ `CoreSDKError` with `operation: "paymentSubmission"`. A failed script load is not
46
+ cached, so retrying can succeed.
47
+
48
+ The booking already exists when payment options are rendered. Present a submission
49
+ failure as a payment problem with the booking number, never as a failed booking:
50
+
51
+ ```ts
52
+ try {
53
+ await sdk.live.booking.submitPaymentOption(option);
54
+ } catch (error) {
55
+ showPaymentFailure({ bookingNumber: booking.bookingNumber, error });
56
+ }
57
+ ```
58
+
59
+ Submission remains browser-only.
60
+
61
+ ## Payment Return
62
+
63
+ The SDK derives Stripe success and cancel URLs from the `relativeRedirectUrl`
64
+ passed to `sdk.live.booking.book(...)`, appending
65
+ `payment=stripe&success=true` or `payment=stripe&cancel=true`. Persist anything the
66
+ return page needs before submitting the payment option because the redirect URL is
67
+ configured before the booking number exists.
68
+
69
+ A `success=true` return means the browser returned from Stripe, not that payment
70
+ settled. Stripe's webhook confirms payment to vOffice.
71
+
72
+ ## Recommended Steps
73
+
74
+ 1. Upgrade to `@v-office/website-sdk` 2.7.0.
75
+ 2. Remove checks that hide Stripe options when `option.url` is missing.
76
+ 3. Remove any site-owned Stripe.js bridge and publishable key used for this flow.
77
+ 4. Add the Stripe CSP directives when applicable.
78
+ 5. Present submission failures as post-booking payment failures.
79
+ 6. Smoke-test test-mode and live-mode tenants, including success and cancel returns.
@@ -0,0 +1,63 @@
1
+ # Changelog: 2.8.0
2
+
3
+ This release replaces the loose custom-attribute manifest/configuration split with a
4
+ validated catalog-backed registry shared by rental display, highlights, filter
5
+ discovery, and search.
6
+
7
+ ## Added
8
+
9
+ - `fetchCustomAttributeCatalog({ config })` reads public v10 custom-attribute
10
+ definition IDs, keys, types, and option identities at build time.
11
+ - `defineCustomAttributes(...)` joins that catalog with authored labels, exposure,
12
+ display, category, v9 bindings, and option labels.
13
+ - `defineCustomAttributesFromUnknown(...)`, `parseCustomAttributeCatalog`, and
14
+ `CustomAttributeCatalogSchema` support CLI/JSON and other untyped inputs.
15
+ - The website package exports the catalog, selection, registry, resolved-attribute,
16
+ category, and override types needed to author typed configuration.
17
+ - v10 reads public custom-attribute values for rentals and search output. v9 can
18
+ render the same registry through explicit `p_*` rental-data bindings and `v9Only`
19
+ entries.
20
+ - Applied value-bearing filters include a canonical `value`, allowing consumers to
21
+ remove one exact filter value without relying on a localized label.
22
+
23
+ ## Changed
24
+
25
+ - `WebsiteSDKOptions.customAttributes` now receives the validated registry returned
26
+ by `defineCustomAttributes`.
27
+ - `customAttributeFilterDefinitions` now contains only `backendFilter` and
28
+ `composition` definitions. Catalog-backed custom attributes get their filter
29
+ policy from the registry selection.
30
+ - v10 custom-attribute filters are advertised only when filtering is enabled and a
31
+ backend definition ID exists. v9 does not advertise or execute catalog custom
32
+ attributes.
33
+ - Public/internal filter exposure and display are independent. Internal filters are
34
+ queryable and usable by compositions but absent from `getFilters`.
35
+ - Custom-attribute categories, backend-filter categories, and composition
36
+ categories are returned as localized display labels. Configuration continues to
37
+ store stable category keys.
38
+ - Composition discovery is backend-aware. Every configured leaf and value must be
39
+ executable on the active backend.
40
+ - Composition execution is atomic at the GraphQL/REST payload level. A failed
41
+ composition contributes no generated leaf and reports only its parent key unused.
42
+ - Nested compositions are rejected while recursive expansion remains unsupported.
43
+ - Query-key ownership is validated against period, occupancy, active-backend
44
+ built-ins, overlapping custom definitions, and repeated configured filter keys.
45
+ - Rental highlight prioritization remains an explicit ordered list. Custom
46
+ attributes render only when their resolved key is listed; registry contents do
47
+ not reorder built-in highlights.
48
+
49
+ ## Removed
50
+
51
+ - `customAttributeDefinitionManifest`,
52
+ `CustomAttributeDefinitionManifest`,
53
+ `CustomAttributeDefinitionManifestEntry`, and related manifest helpers.
54
+ - `source: "customAttribute"` entries in `customAttributeFilterDefinitions`.
55
+ - Unsupported composition expression forms `any`, `gte`, and `lte`.
56
+ - `renderCustomAttributeHighlight`; use registry-backed rental/search output, or
57
+ `renderResolvedCustomAttribute` from `@v-office/sdk-core`.
58
+
59
+ ## Migration Impact
60
+
61
+ This is a configuration and public-type migration for consumers using custom
62
+ attributes. Consumers that do not configure custom attributes keep the same SDK
63
+ construction and service calls. Follow `versions/2.8.0/MIGRATION.md`.
@@ -0,0 +1,252 @@
1
+ # Migration: 2.7.0 to 2.8.0
2
+
3
+ 2.8.0 replaces `customAttributeDefinitionManifest` and
4
+ `source: "customAttribute"` filter definitions with one validated custom-attribute
5
+ registry. SDK construction and service method names are unchanged.
6
+
7
+ Consumers without custom attributes can upgrade without configuration changes.
8
+
9
+ ## 1. Replace the Definition Manifest
10
+
11
+ Before:
12
+
13
+ ```ts
14
+ const options = defineWebsiteSDKOptions({
15
+ customAttributeDefinitionManifest: {
16
+ byKey: {
17
+ region: {
18
+ id: "GQYKYgMVh28X7VLGursbkt",
19
+ key: "region",
20
+ type: "OPTION",
21
+ visibility: "PUBLIC",
22
+ },
23
+ },
24
+ byId: {
25
+ GQYKYgMVh28X7VLGursbkt: {
26
+ id: "GQYKYgMVh28X7VLGursbkt",
27
+ key: "region",
28
+ type: "OPTION",
29
+ visibility: "PUBLIC",
30
+ },
31
+ },
32
+ },
33
+ });
34
+ ```
35
+
36
+ After:
37
+
38
+ ```ts
39
+ import {
40
+ defineCustomAttributes,
41
+ defineWebsiteSDKOptions,
42
+ fetchCustomAttributeCatalog,
43
+ } from "@v-office/website-sdk";
44
+
45
+ const catalog = await fetchCustomAttributeCatalog({ config: v10Config });
46
+
47
+ const customAttributes = defineCustomAttributes({
48
+ catalog,
49
+ select: {
50
+ region: {
51
+ label: { "de-DE": "Region", "en-US": "Region" },
52
+ category: "OTHER",
53
+ filter: true,
54
+ optionLabels: {
55
+ north: { "de-DE": "Norden", "en-US": "North" },
56
+ south: { "de-DE": "Süden", "en-US": "South" },
57
+ },
58
+ },
59
+ },
60
+ });
61
+
62
+ const options = defineWebsiteSDKOptions({ customAttributes });
63
+ ```
64
+
65
+ `fetchCustomAttributeCatalog` is v10-only, requires a token authorized for the
66
+ public-definition operation, and returns no localized labels. Fetch it in an
67
+ authorized build environment or pin an equivalent literal catalog.
68
+ `defineCustomAttributes` validates the complete catalog/selection join and reports
69
+ all configuration errors together.
70
+
71
+ ## 2. Move Custom-Attribute Filter Policy
72
+
73
+ Remove every `source: "customAttribute"` entry from
74
+ `customAttributeFilterDefinitions`. Put its behavior in the matching `select`
75
+ entry:
76
+
77
+ ```ts
78
+ select: {
79
+ occupancyScore: {
80
+ label: { "de-DE": "Belegung", "en-US": "Occupancy" },
81
+ category: "ESSENTIALS",
82
+ filter: {
83
+ exposure: "public",
84
+ numericBounds: { min: 1, max: 12 },
85
+ },
86
+ },
87
+ }
88
+ ```
89
+
90
+ Use `filter: "internal"` or `filter: { exposure: "internal" }` for a queryable filter
91
+ that must not appear in `getFilters`. Omit `filter` for display only.
92
+
93
+ `customAttributeFilterDefinitions` remains the home of:
94
+
95
+ - `backendFilter`: v9 backend-native filters;
96
+ - `composition`: virtual Boolean filters composed from executable leaves.
97
+
98
+ ## 3. Configure Display and v9 Bindings
99
+
100
+ Display is independent of filter exposure and defaults to `true`. A selected
101
+ catalog attribute can add a v9 rental-data binding:
102
+
103
+ ```ts
104
+ select: {
105
+ privateSauna: {
106
+ label: { "de-DE": "Sauna", "en-US": "Sauna" },
107
+ display: true,
108
+ filter: true,
109
+ v9: "p_14418",
110
+ },
111
+ }
112
+ ```
113
+
114
+ Use `v9Only` for attributes that have no v10 catalog definition:
115
+
116
+ ```ts
117
+ v9Only: {
118
+ thatchedRoof: {
119
+ v9: "p_14420",
120
+ type: "boolean",
121
+ label: { "de-DE": "Reetdach", "en-US": "Thatched roof" },
122
+ },
123
+ }
124
+ ```
125
+
126
+ v9 renders bound values but does not advertise or execute catalog custom-attribute
127
+ filters. v10 filtering requires an enabled filter and a catalog ID.
128
+
129
+ ## 4. Update Composition Expressions
130
+
131
+ Expressions now support equality leaves joined by `all`:
132
+
133
+ ```ts
134
+ expression: {
135
+ all: [
136
+ { filter: "privateSauna", value: true },
137
+ { filter: "maxPersons", value: 4 },
138
+ ],
139
+ }
140
+ ```
141
+
142
+ Remove `operator`, `gte`, `lte`, and `any`; they never had correct runtime
143
+ semantics. Nested composition references are rejected.
144
+
145
+ A composition is advertised only if every leaf can execute on the active backend.
146
+ Execution is atomic: if one configured leaf is unknown, unsupported, malformed, or
147
+ conflicts with a direct value, no generated composition leaf enters the backend
148
+ payload and only the composition key is unused.
149
+
150
+ Successful compositions return the parent and expanded leaves in `appliedFilters`.
151
+
152
+ ## 5. Rename Colliding Query Keys
153
+
154
+ Every query key must have one owner. Configuration rejects:
155
+
156
+ - period/occupancy keys such as `start`, `adults`, and `pets`;
157
+ - filterable custom attributes or compositions that reuse v10 built-in filter keys;
158
+ - configured v9 filters that reuse v9 stable filter keys;
159
+ - a custom attribute and custom filter definition with the same key.
160
+ - repeated keys within `customAttributeFilterDefinitions`.
161
+
162
+ Use the selection's `key` override when a workspace definition collides:
163
+
164
+ ```ts
165
+ select: {
166
+ sauna: {
167
+ key: "privateSauna",
168
+ label: { "de-DE": "Sauna", "en-US": "Sauna" },
169
+ filter: true,
170
+ },
171
+ }
172
+ ```
173
+
174
+ Update query URLs and `rentalHighlightPrioritization` to the resolved key.
175
+
176
+ ## 6. Treat Categories as Display Labels
177
+
178
+ Configuration keeps stable keys such as `OUTDOORS`. `getFilters` and rental
179
+ attribute groups return localized labels such as `Außenbereich` or `Outdoor Area`.
180
+ Do not compare output categories to stable keys. Customize wording with
181
+ locale-nested overrides:
182
+
183
+ ```ts
184
+ translationOverrides: {
185
+ "de-DE": {
186
+ "RentalAttributesCategory.OUTDOORS.label": "Draußen",
187
+ },
188
+ }
189
+ ```
190
+
191
+ ## 7. Update Highlight Configuration
192
+
193
+ Custom attributes appear in rental/search `highlights` only when their resolved key
194
+ is listed in `rentalHighlightPrioritization`:
195
+
196
+ ```ts
197
+ rentalHighlightPrioritization: ["bedrooms", customAttributes.keys.region, "bathrooms"];
198
+ ```
199
+
200
+ The list remains authoritative for order. Registry contents do not reorder
201
+ built-ins, duplicate keys render once, and a built-in metadata key shadows a
202
+ same-named display-only custom attribute.
203
+
204
+ ## 8. Update Applied-Filter Handling
205
+
206
+ Value-bearing applied filters now include canonical `value`:
207
+
208
+ ```ts
209
+ { key: "region", label: "Region: North", value: "north" }
210
+ ```
211
+
212
+ Use `key` plus `value` to remove an exact filter. Labels remain localized
213
+ presentation and are not identifiers. A scalar key may appear in both
214
+ `appliedFilters` and `unusedFilterKeys` when one value applied and another was
215
+ rejected.
216
+
217
+ ## 9. CLI and Untyped Options
218
+
219
+ CLI JSON supplies catalog and selection **inputs**, not a finished registry:
220
+
221
+ ```json
222
+ {
223
+ "customAttributes": {
224
+ "catalog": {
225
+ "privateSauna": {
226
+ "id": "GQYKYgMVh28X7VLGursbkt",
227
+ "type": "BOOLEAN"
228
+ }
229
+ },
230
+ "select": {
231
+ "privateSauna": {
232
+ "label": { "de-DE": "Sauna", "en-US": "Sauna" },
233
+ "filter": true
234
+ }
235
+ }
236
+ }
237
+ }
238
+ ```
239
+
240
+ The CLI uses `defineCustomAttributesFromUnknown` and applies the same runtime
241
+ validation as typed configuration.
242
+
243
+ ## Recommended Verification
244
+
245
+ 1. Run `getFilters` for v9 and v10 in both locales.
246
+ 2. Confirm internal filters are absent while public compositions using them remain
247
+ executable.
248
+ 3. Inspect v9 GraphQL and v10 REST variables for one successful and one failed
249
+ composition.
250
+ 4. Verify renamed custom attributes render in rentals, search highlights, URLs, and
251
+ applied-filter chips.
252
+ 5. Verify `rentalHighlightPrioritization` order with and without custom attributes.