@v-office/website-sdk 2.5.1 → 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 (43) hide show
  1. package/README.md +28 -1
  2. package/dist/cli.mjs +25 -4
  3. package/dist/{client-C5ojrNQA.mjs → client-BNbBfJtO.mjs} +468 -189
  4. package/dist/index.d.mts +5 -4
  5. package/dist/index.mjs +3 -3
  6. package/dist/instructions/CHANGELOG.md +3 -0
  7. package/dist/instructions/MIGRATION.md +3 -0
  8. package/dist/instructions/README.md +8 -2
  9. package/dist/instructions/booking.md +39 -3
  10. package/dist/instructions/creation.md +65 -64
  11. package/dist/instructions/custom-attributes.md +556 -0
  12. package/dist/instructions/filter.md +51 -23
  13. package/dist/instructions/rentals.md +26 -2
  14. package/dist/instructions/search.md +29 -3
  15. package/dist/instructions/versions/2.5.0/MIGRATION.md +5 -0
  16. package/dist/instructions/versions/2.6.0/CHANGELOG.md +6 -0
  17. package/dist/instructions/versions/2.6.0/MIGRATION.md +6 -0
  18. package/dist/instructions/versions/2.7.0/CHANGELOG.md +44 -0
  19. package/dist/instructions/versions/2.7.0/MIGRATION.md +79 -0
  20. package/dist/instructions/versions/2.8.0/CHANGELOG.md +63 -0
  21. package/dist/instructions/versions/2.8.0/MIGRATION.md +252 -0
  22. package/dist/{rentals-Lw1qUKIj.mjs → rentals-cCejb_2d.mjs} +21 -21
  23. package/dist/{search-9yzAqHCg.mjs → search-Dzzra6IW.mjs} +16 -133
  24. package/dist/{to-rental-highlights-CxWlq76f.mjs → to-rental-highlights-OvNe9ZQS.mjs} +8 -6
  25. package/dist/translations/v10/de-DE/core.json +2 -0
  26. package/dist/translations/v10/en-US/core.json +2 -0
  27. package/instructions/CHANGELOG.md +3 -0
  28. package/instructions/MIGRATION.md +3 -0
  29. package/instructions/README.md +8 -2
  30. package/instructions/booking.md +39 -3
  31. package/instructions/creation.md +65 -64
  32. package/instructions/custom-attributes.md +556 -0
  33. package/instructions/filter.md +51 -23
  34. package/instructions/rentals.md +26 -2
  35. package/instructions/search.md +29 -3
  36. package/instructions/versions/2.5.0/MIGRATION.md +5 -0
  37. package/instructions/versions/2.6.0/CHANGELOG.md +6 -0
  38. package/instructions/versions/2.6.0/MIGRATION.md +6 -0
  39. package/instructions/versions/2.7.0/CHANGELOG.md +44 -0
  40. package/instructions/versions/2.7.0/MIGRATION.md +79 -0
  41. package/instructions/versions/2.8.0/CHANGELOG.md +63 -0
  42. package/instructions/versions/2.8.0/MIGRATION.md +252 -0
  43. package/package.json +5 -3
@@ -1,27 +1,200 @@
1
- import { AvailabilityService, BookingService, ContactService, CoreSDKError, DEFAULT_RENTAL_HIGHLIGHT_PRIORITIZATION, DEFAULT_RENTAL_PROPERTY_HIGHLIGHT_PRIORITIZATION, DocumentsService, FilterService, QuoteService, RentalService, SearchService, TranslationService, addGuestQuoteAdditionalServiceSelection, asArray, asRecord, clearGuestQuoteAdditionalServiceSelections, createCoreRuntime, firstNumber, firstString, formatCurrency, formatLocalDate, getGuestQuoteBackendContext, getGuestQuoteInput, isCustomAttributeDefinition, localeToLanguage, makeMinIntervalLimiter, makeTranslationLive, refreshGuestQuoteInsurancePassthrough, removeGuestQuoteAdditionalServiceSelection, selectGuestQuoteCancellationPolicySelection, toCustomAttributeFilterOutputItem, toInitialAvailabilityCalendarDays, toStartDateSelectedCalendarDays, validateContactSubmitInput, withGuestQuoteInsurance, withGuestQuoteInsuranceRefresh } from "@v-office/sdk-core";
1
+ import { AvailabilityService, BookingService, ContactService, CoreSDKError, DEFAULT_RENTAL_HIGHLIGHT_PRIORITIZATION, DEFAULT_RENTAL_PROPERTY_HIGHLIGHT_PRIORITIZATION, DocumentsService, EMPTY_CUSTOM_ATTRIBUTE_REGISTRY, FilterService, QuoteService, RentalService, SearchService, TranslationService, addGuestQuoteAdditionalServiceSelection, asArray, asRecord, assertNoNestedCustomAttributeFilterCompositions, assertNoReservedQueryKeys, assertUniqueCustomFilterQueryKeys, assertUniqueFilterQueryKeys, clearGuestQuoteAdditionalServiceSelections, collectQueryParameters, createCoreRuntime, expandCustomAttributeFilterCompositions, firstNumber, firstString, formatCurrency, formatLocalDate, getGuestQuoteBackendContext, getGuestQuoteInput, isDisplayableCustomAttribute, localeToLanguage, makeMinIntervalLimiter, makeTranslationLive, parseQueryParameters, refreshGuestQuoteInsurancePassthrough, removeGuestQuoteAdditionalServiceSelection, selectGuestQuoteCancellationPolicySelection, toCustomAttributeFilterLabel, toCustomAttributeFilterOutputItem, toCustomAttributeList, toInitialAvailabilityCalendarDays, toQueryString, toResolvedCustomAttributeCategoryTranslationKey, toStartDateSelectedCalendarDays, validateContactSubmitInput, withGuestQuoteInsurance, withGuestQuoteInsuranceRefresh } from "@v-office/sdk-core";
2
2
  import { Context, Data, DateTime, Effect, Layer, Schema } from "effect";
3
3
  import { FetchHttpClient, HttpClient, HttpClientRequest } from "effect/unstable/http";
4
4
  import { print } from "graphql";
5
+ //#region codegen/v9/graphql-codegen/stable-filters.json
6
+ var stable_filters_default = {
7
+ bedrooms: {
8
+ "name": "Bedrooms",
9
+ "type": "InputFilter",
10
+ "group": "rooms",
11
+ "display_type": "number",
12
+ "custom_query": { "query": [{
13
+ "key": "VofficeUnit.raw_data.bedrooms",
14
+ "operator": "GREATER_THAN_OR_EQUAL",
15
+ "value": null
16
+ }] }
17
+ },
18
+ bathrooms: {
19
+ "name": "Bathrooms",
20
+ "type": "InputFilter",
21
+ "group": "rooms",
22
+ "display_type": "number",
23
+ "custom_query": { "query": [{
24
+ "key": "VofficeUnit.raw_data.bathrooms",
25
+ "operator": "GREATER_THAN_OR_EQUAL",
26
+ "value": null
27
+ }] }
28
+ },
29
+ pool: {
30
+ "name": "Pool",
31
+ "type": "BooleanFilter",
32
+ "group": "features",
33
+ "display_type": "checkbox",
34
+ "property": "pool"
35
+ },
36
+ wifi: {
37
+ "name": "WiFi",
38
+ "type": "BooleanFilter",
39
+ "group": "features",
40
+ "display_type": "checkbox",
41
+ "property": "wifi"
42
+ },
43
+ pets: {
44
+ "name": "Pets",
45
+ "type": "BooleanFilter",
46
+ "group": "features",
47
+ "display_type": "checkbox",
48
+ "property": "pets"
49
+ },
50
+ privateparking: {
51
+ "name": "Private parking",
52
+ "type": "BooleanFilter",
53
+ "group": "features",
54
+ "display_type": "checkbox",
55
+ "property": "privateparking"
56
+ },
57
+ ac: {
58
+ "name": "Air conditioning",
59
+ "type": "BooleanFilter",
60
+ "group": "features",
61
+ "display_type": "checkbox",
62
+ "property": "ac"
63
+ },
64
+ washer: {
65
+ "name": "Washer",
66
+ "type": "BooleanFilter",
67
+ "group": "features",
68
+ "display_type": "checkbox",
69
+ "property": "washer"
70
+ },
71
+ dishwasher: {
72
+ "name": "Dishwasher",
73
+ "type": "BooleanFilter",
74
+ "group": "features",
75
+ "display_type": "checkbox",
76
+ "property": "dishwasher"
77
+ },
78
+ beachfront: {
79
+ "name": "Beachfront",
80
+ "type": "BooleanFilter",
81
+ "group": "features",
82
+ "display_type": "checkbox",
83
+ "property": "beachfront"
84
+ },
85
+ seaview: {
86
+ "name": "Sea view",
87
+ "type": "BooleanFilter",
88
+ "group": "features",
89
+ "display_type": "checkbox",
90
+ "property": "seaview"
91
+ },
92
+ balcony: {
93
+ "name": "Balcony",
94
+ "type": "BooleanFilter",
95
+ "group": "features",
96
+ "display_type": "checkbox",
97
+ "property": "balcony"
98
+ },
99
+ terrace: {
100
+ "name": "Terrace",
101
+ "type": "BooleanFilter",
102
+ "group": "features",
103
+ "display_type": "checkbox",
104
+ "property": "terrace"
105
+ },
106
+ garden: {
107
+ "name": "Garden",
108
+ "type": "BooleanFilter",
109
+ "group": "features",
110
+ "display_type": "checkbox",
111
+ "property": "garden"
112
+ },
113
+ bbq: {
114
+ "name": "BBQ",
115
+ "type": "BooleanFilter",
116
+ "group": "features",
117
+ "display_type": "checkbox",
118
+ "property": "bbq"
119
+ },
120
+ sauna: {
121
+ "name": "Sauna",
122
+ "type": "BooleanFilter",
123
+ "group": "features",
124
+ "display_type": "checkbox",
125
+ "property": "sauna"
126
+ },
127
+ fireplace: {
128
+ "name": "Fireplace",
129
+ "type": "BooleanFilter",
130
+ "group": "features",
131
+ "display_type": "checkbox",
132
+ "property": "fireplace"
133
+ },
134
+ tv: {
135
+ "name": "TV",
136
+ "type": "BooleanFilter",
137
+ "group": "features",
138
+ "display_type": "checkbox",
139
+ "property": "tv"
140
+ },
141
+ childrenWelcome: {
142
+ "name": "Children welcome",
143
+ "type": "BooleanFilter",
144
+ "group": "features",
145
+ "display_type": "checkbox",
146
+ "property": "childrenWelcome"
147
+ },
148
+ nonsmoking: {
149
+ "name": "Non-smoking",
150
+ "type": "BooleanFilter",
151
+ "group": "features",
152
+ "display_type": "checkbox",
153
+ "property": "nonsmoking"
154
+ },
155
+ kitchen: {
156
+ "name": "Kitchen",
157
+ "type": "BooleanFilter",
158
+ "group": "features",
159
+ "display_type": "checkbox",
160
+ "property": "kitchen"
161
+ }
162
+ };
163
+ //#endregion
5
164
  //#region src/config.ts
6
- const normalizeWebsiteSDKOptions = (options = {}) => ({
7
- ...options.translationOverrides === void 0 ? {} : { translationOverrides: options.translationOverrides },
8
- customAttributeFilterDefinitions: options.customAttributeFilterDefinitions ?? [],
9
- ...options.customAttributeDefinitionManifest === void 0 ? {} : { customAttributeDefinitionManifest: options.customAttributeDefinitionManifest },
10
- rentalHighlightPrioritization: options.rentalHighlightPrioritization ?? DEFAULT_RENTAL_HIGHLIGHT_PRIORITIZATION,
11
- rentalPropertyHighlightPrioritization: options.rentalPropertyHighlightPrioritization ?? DEFAULT_RENTAL_PROPERTY_HIGHLIGHT_PRIORITIZATION,
12
- rentalScope: options.rentalScope ?? {},
13
- shouldAddProposedAdditionalServiceAmount: options.shouldAddProposedAdditionalServiceAmount ?? false
14
- });
15
- const toV9Options = (options) => ({
16
- ...options.translationOverrides === void 0 ? {} : { translationOverrides: options.translationOverrides },
17
- customAttributeFilterDefinitions: options.customAttributeFilterDefinitions,
18
- rentalHighlightPrioritization: options.rentalHighlightPrioritization,
19
- rentalScope: options.rentalScope,
20
- shouldAddProposedAdditionalServiceAmount: options.shouldAddProposedAdditionalServiceAmount
21
- });
165
+ const normalizeWebsiteSDKOptions = (options = {}) => {
166
+ assertNoReservedQueryKeys(options);
167
+ assertUniqueCustomFilterQueryKeys(options);
168
+ assertNoNestedCustomAttributeFilterCompositions(options.customAttributeFilterDefinitions ?? []);
169
+ return {
170
+ ...options.translationOverrides === void 0 ? {} : { translationOverrides: options.translationOverrides },
171
+ customAttributes: options.customAttributes ?? EMPTY_CUSTOM_ATTRIBUTE_REGISTRY,
172
+ customAttributeFilterDefinitions: options.customAttributeFilterDefinitions ?? [],
173
+ rentalHighlightPrioritization: options.rentalHighlightPrioritization ?? DEFAULT_RENTAL_HIGHLIGHT_PRIORITIZATION,
174
+ rentalPropertyHighlightPrioritization: options.rentalPropertyHighlightPrioritization ?? DEFAULT_RENTAL_PROPERTY_HIGHLIGHT_PRIORITIZATION,
175
+ rentalScope: options.rentalScope ?? {},
176
+ shouldAddProposedAdditionalServiceAmount: options.shouldAddProposedAdditionalServiceAmount ?? false
177
+ };
178
+ };
179
+ const toV9Options = (options) => {
180
+ const collisions = [...new Set(options.customAttributeFilterDefinitions.map((definition) => definition.key).filter((key) => Object.hasOwn(stable_filters_default, key)))];
181
+ if (collisions.length > 0) throw new CoreSDKError({
182
+ source: "v9",
183
+ operation: "options.normalize",
184
+ message: `${collisions.join(", ")} ${collisions.length === 1 ? "is" : "are"} already owned by a v9 built-in search filter. Rename the configured custom filter.`
185
+ });
186
+ return {
187
+ ...options.translationOverrides === void 0 ? {} : { translationOverrides: options.translationOverrides },
188
+ customAttributes: options.customAttributes,
189
+ customAttributeFilterDefinitions: options.customAttributeFilterDefinitions,
190
+ rentalHighlightPrioritization: options.rentalHighlightPrioritization,
191
+ rentalScope: options.rentalScope,
192
+ shouldAddProposedAdditionalServiceAmount: options.shouldAddProposedAdditionalServiceAmount
193
+ };
194
+ };
22
195
  const defineWebsiteSDKOptions = (options) => options;
23
196
  //#endregion
24
- //#region src/payment/submit-payment-option.ts
197
+ //#region src/payment/browser.ts
25
198
  const getBrowser = () => Effect.gen(function* () {
26
199
  const globals = globalThis;
27
200
  if (globals.document === void 0 || globals.window === void 0) return yield* Effect.fail(new CoreSDKError({
@@ -40,6 +213,83 @@ const toPaymentSubmissionError = (cause) => cause instanceof CoreSDKError ? caus
40
213
  message: "Payment submission failed.",
41
214
  cause
42
215
  });
216
+ //#endregion
217
+ //#region src/payment/stripe-checkout.ts
218
+ /**
219
+ * vOffice `initStripePayment` returns only `sessionId` and `accountId`, never the
220
+ * Checkout Session URL, so the session has to be opened with Stripe.js. Sessions are
221
+ * created on connected accounts, which is why the platform publishable key is combined
222
+ * with the reservation's `accountId`. Remove this module once vOffice returns the URL.
223
+ */
224
+ const STRIPE_PUBLISHABLE_KEY_TEST = "pk_test_62sy8HrukNuTd7ko6lZcyJcv00CtNmm5oo";
225
+ const STRIPE_PUBLISHABLE_KEY_LIVE = "pk_live_lHTr8iFxjPqtC0aYmkQYZuzZ00qtUAATYP";
226
+ /**
227
+ * Must stay the unversioned bundle. Stripe gates `redirectToCheckout` behind a per-version
228
+ * feature flag that is on from the `clover` (2025-09-30) release train onwards, so pinning a
229
+ * version such as `https://js.stripe.com/clover/stripe.js` makes the method throw on call.
230
+ */
231
+ const STRIPE_JS_URL = "https://js.stripe.com/v3/";
232
+ let stripeJs;
233
+ const getStripeConstructor = () => globalThis.Stripe;
234
+ const createStripeJsLoader = (document) => new Promise((resolve, reject) => {
235
+ const alreadyLoaded = getStripeConstructor();
236
+ if (alreadyLoaded !== void 0) {
237
+ resolve(alreadyLoaded);
238
+ return;
239
+ }
240
+ const script = document.createElement("script");
241
+ script.src = STRIPE_JS_URL;
242
+ script.async = true;
243
+ script.addEventListener("load", () => {
244
+ const loaded = getStripeConstructor();
245
+ if (loaded === void 0) {
246
+ reject(/* @__PURE__ */ new Error("Stripe.js was loaded but window.Stripe is unavailable."));
247
+ return;
248
+ }
249
+ resolve(loaded);
250
+ });
251
+ script.addEventListener("error", () => {
252
+ reject(/* @__PURE__ */ new Error("Could not load Stripe.js."));
253
+ });
254
+ document.head.appendChild(script);
255
+ });
256
+ const loadStripeJs = () => Effect.gen(function* () {
257
+ const { document } = yield* getBrowser();
258
+ const pending = stripeJs ?? createStripeJsLoader(document);
259
+ stripeJs = pending;
260
+ return yield* Effect.tapError(Effect.tryPromise({
261
+ try: () => pending,
262
+ catch: toPaymentSubmissionError
263
+ }), () => Effect.sync(() => {
264
+ if (stripeJs === pending) stripeJs = void 0;
265
+ }));
266
+ });
267
+ /** A `cs_test_` session can only be opened with the test key, `cs_live_` only with the live key. */
268
+ const toPublishableKey = (sessionId) => sessionId.startsWith("cs_test_") ? STRIPE_PUBLISHABLE_KEY_TEST : STRIPE_PUBLISHABLE_KEY_LIVE;
269
+ const redirectToStripeCheckout = ({ accountId, sessionId }) => Effect.gen(function* () {
270
+ const createStripe = yield* loadStripeJs();
271
+ const stripe = yield* Effect.try({
272
+ try: () => createStripe(toPublishableKey(sessionId), accountId === void 0 ? void 0 : { stripeAccount: accountId }),
273
+ catch: toPaymentSubmissionError
274
+ });
275
+ const redirectToCheckout = stripe.redirectToCheckout;
276
+ if (typeof redirectToCheckout !== "function") return yield* Effect.fail(new CoreSDKError({
277
+ source: "core",
278
+ operation: "paymentSubmission",
279
+ message: "This Stripe.js build no longer provides redirectToCheckout, so a Checkout Session cannot be opened from its session id. This payment option requires a checkout session URL from vOffice."
280
+ }));
281
+ const { error } = yield* Effect.tryPromise({
282
+ try: () => redirectToCheckout.call(stripe, { sessionId }),
283
+ catch: toPaymentSubmissionError
284
+ });
285
+ if (error !== void 0) return yield* Effect.fail(new CoreSDKError({
286
+ source: "core",
287
+ operation: "paymentSubmission",
288
+ message: error.message ?? "Stripe rejected the Checkout redirect."
289
+ }));
290
+ });
291
+ //#endregion
292
+ //#region src/payment/submit-payment-option.ts
43
293
  const submitFormPost = (option) => Effect.gen(function* () {
44
294
  const { document } = yield* getBrowser();
45
295
  yield* Effect.try({
@@ -83,10 +333,14 @@ const submitPaymentOptionEffect = (option) => {
83
333
  case "form_post": return submitFormPost(option);
84
334
  case "stripe_checkout":
85
335
  if (option.url !== void 0) return redirectTo(option.url);
336
+ if (option.sessionId !== void 0) return redirectToStripeCheckout({
337
+ ...option.accountId === void 0 ? {} : { accountId: option.accountId },
338
+ sessionId: option.sessionId
339
+ });
86
340
  return Effect.fail(new CoreSDKError({
87
341
  source: "core",
88
342
  operation: "paymentSubmission",
89
- message: "Stripe Checkout requires a checkout session URL. The current payment option only contains session metadata."
343
+ message: "Stripe Checkout requires a checkout session URL or session id. The current payment option contains neither."
90
344
  }));
91
345
  default: {
92
346
  const runtimeKind = option.kind;
@@ -4624,7 +4878,19 @@ const makeV9GraphQLClient = (config) => ({ execute: (document, variables) => Eff
4624
4878
  })) });
4625
4879
  //#endregion
4626
4880
  //#region src/legacy-v9/parser/to-custom-attribute-rental-data-attributes.ts
4627
- const toCustomAttributeRentalDataAttributes = (definitions) => definitions.flatMap((definition) => definition.source === "customAttribute" && definition.key.startsWith("p_") ? [definition.key] : []);
4881
+ /**
4882
+ * The v9 rental-data fields to request.
4883
+ *
4884
+ * Derived from the registry's `v9` bindings, and only for attributes the display
4885
+ * policy will actually render — requesting a field that display then discards, or
4886
+ * rendering one that was never requested, means request selection and display
4887
+ * selection disagree (CA-V9-010).
4888
+ *
4889
+ * The binding is a separate field from the stable key on purpose: the
4890
+ * workspace-specific `p_*` value must never have to appear in a public URL
4891
+ * (CA-V9-009).
4892
+ */
4893
+ const toCustomAttributeRentalDataAttributes = (customAttributes) => toCustomAttributeList(customAttributes).flatMap((attribute) => attribute.v9 !== void 0 && isDisplayableCustomAttribute(attribute) ? [attribute.v9] : []);
4628
4894
  //#endregion
4629
4895
  //#region src/legacy-v9/rental-scope.ts
4630
4896
  const toV9PropertyScope = (scope) => {
@@ -4686,7 +4952,7 @@ const parseV9SearchCursor = (cursor) => Effect.gen(function* () {
4686
4952
  return page;
4687
4953
  });
4688
4954
  const loadV9SearchRuntime = Effect.tryPromise({
4689
- try: () => import("./search-9yzAqHCg.mjs"),
4955
+ try: () => import("./search-Dzzra6IW.mjs"),
4690
4956
  catch: (cause) => new CoreSDKError({
4691
4957
  source: "v10",
4692
4958
  operation: "v9.search.runtime",
@@ -4697,8 +4963,7 @@ const loadV9SearchRuntime = Effect.tryPromise({
4697
4963
  const makeV9SearchApi = ({ config, options }) => {
4698
4964
  const graphql = makeV9GraphQLClient(config);
4699
4965
  const translations = makeV9TranslationService(options.translationOverrides);
4700
- const customAttributeDataAttributes = toCustomAttributeRentalDataAttributes(options.customAttributeFilterDefinitions);
4701
- const customAttributeDefinitions = options.customAttributeFilterDefinitions.filter(isCustomAttributeDefinition);
4966
+ const customAttributeDataAttributes = toCustomAttributeRentalDataAttributes(options.customAttributes);
4702
4967
  const limitSearch = Effect.runSync(makeMinIntervalLimiter({
4703
4968
  interval: "1 second",
4704
4969
  maxQueueSize: 5,
@@ -4744,7 +5009,7 @@ const makeV9SearchApi = ({ config, options }) => {
4744
5009
  locale: input.locale,
4745
5010
  imageProxyBaseUrl: config.imageProxyBaseUrl,
4746
5011
  rentalHighlightPrioritization: [...options.rentalHighlightPrioritization],
4747
- customAttributeFilterDefinitions: customAttributeDefinitions,
5012
+ customAttributes: options.customAttributes,
4748
5013
  rental,
4749
5014
  includeFormattedTotal: isExactPeriodMatch,
4750
5015
  translations
@@ -4784,165 +5049,6 @@ const makeV9SearchApi = ({ config, options }) => {
4784
5049
  }))) };
4785
5050
  };
4786
5051
  //#endregion
4787
- //#region codegen/v9/graphql-codegen/stable-filters.json
4788
- var stable_filters_default = {
4789
- bedrooms: {
4790
- "name": "Bedrooms",
4791
- "type": "InputFilter",
4792
- "group": "rooms",
4793
- "display_type": "number",
4794
- "custom_query": { "query": [{
4795
- "key": "VofficeUnit.raw_data.bedrooms",
4796
- "operator": "GREATER_THAN_OR_EQUAL",
4797
- "value": null
4798
- }] }
4799
- },
4800
- bathrooms: {
4801
- "name": "Bathrooms",
4802
- "type": "InputFilter",
4803
- "group": "rooms",
4804
- "display_type": "number",
4805
- "custom_query": { "query": [{
4806
- "key": "VofficeUnit.raw_data.bathrooms",
4807
- "operator": "GREATER_THAN_OR_EQUAL",
4808
- "value": null
4809
- }] }
4810
- },
4811
- pool: {
4812
- "name": "Pool",
4813
- "type": "BooleanFilter",
4814
- "group": "features",
4815
- "display_type": "checkbox",
4816
- "property": "pool"
4817
- },
4818
- wifi: {
4819
- "name": "WiFi",
4820
- "type": "BooleanFilter",
4821
- "group": "features",
4822
- "display_type": "checkbox",
4823
- "property": "wifi"
4824
- },
4825
- pets: {
4826
- "name": "Pets",
4827
- "type": "BooleanFilter",
4828
- "group": "features",
4829
- "display_type": "checkbox",
4830
- "property": "pets"
4831
- },
4832
- privateparking: {
4833
- "name": "Private parking",
4834
- "type": "BooleanFilter",
4835
- "group": "features",
4836
- "display_type": "checkbox",
4837
- "property": "privateparking"
4838
- },
4839
- ac: {
4840
- "name": "Air conditioning",
4841
- "type": "BooleanFilter",
4842
- "group": "features",
4843
- "display_type": "checkbox",
4844
- "property": "ac"
4845
- },
4846
- washer: {
4847
- "name": "Washer",
4848
- "type": "BooleanFilter",
4849
- "group": "features",
4850
- "display_type": "checkbox",
4851
- "property": "washer"
4852
- },
4853
- dishwasher: {
4854
- "name": "Dishwasher",
4855
- "type": "BooleanFilter",
4856
- "group": "features",
4857
- "display_type": "checkbox",
4858
- "property": "dishwasher"
4859
- },
4860
- beachfront: {
4861
- "name": "Beachfront",
4862
- "type": "BooleanFilter",
4863
- "group": "features",
4864
- "display_type": "checkbox",
4865
- "property": "beachfront"
4866
- },
4867
- seaview: {
4868
- "name": "Sea view",
4869
- "type": "BooleanFilter",
4870
- "group": "features",
4871
- "display_type": "checkbox",
4872
- "property": "seaview"
4873
- },
4874
- balcony: {
4875
- "name": "Balcony",
4876
- "type": "BooleanFilter",
4877
- "group": "features",
4878
- "display_type": "checkbox",
4879
- "property": "balcony"
4880
- },
4881
- terrace: {
4882
- "name": "Terrace",
4883
- "type": "BooleanFilter",
4884
- "group": "features",
4885
- "display_type": "checkbox",
4886
- "property": "terrace"
4887
- },
4888
- garden: {
4889
- "name": "Garden",
4890
- "type": "BooleanFilter",
4891
- "group": "features",
4892
- "display_type": "checkbox",
4893
- "property": "garden"
4894
- },
4895
- bbq: {
4896
- "name": "BBQ",
4897
- "type": "BooleanFilter",
4898
- "group": "features",
4899
- "display_type": "checkbox",
4900
- "property": "bbq"
4901
- },
4902
- sauna: {
4903
- "name": "Sauna",
4904
- "type": "BooleanFilter",
4905
- "group": "features",
4906
- "display_type": "checkbox",
4907
- "property": "sauna"
4908
- },
4909
- fireplace: {
4910
- "name": "Fireplace",
4911
- "type": "BooleanFilter",
4912
- "group": "features",
4913
- "display_type": "checkbox",
4914
- "property": "fireplace"
4915
- },
4916
- tv: {
4917
- "name": "TV",
4918
- "type": "BooleanFilter",
4919
- "group": "features",
4920
- "display_type": "checkbox",
4921
- "property": "tv"
4922
- },
4923
- childrenWelcome: {
4924
- "name": "Children welcome",
4925
- "type": "BooleanFilter",
4926
- "group": "features",
4927
- "display_type": "checkbox",
4928
- "property": "childrenWelcome"
4929
- },
4930
- nonsmoking: {
4931
- "name": "Non-smoking",
4932
- "type": "BooleanFilter",
4933
- "group": "features",
4934
- "display_type": "checkbox",
4935
- "property": "nonsmoking"
4936
- },
4937
- kitchen: {
4938
- "name": "Kitchen",
4939
- "type": "BooleanFilter",
4940
- "group": "features",
4941
- "display_type": "checkbox",
4942
- "property": "kitchen"
4943
- }
4944
- };
4945
- //#endregion
4946
5052
  //#region src/legacy-v9/parser/filter/to-options.ts
4947
5053
  const toOptions = ({ stableFilter, label }) => {
4948
5054
  if (stableFilter.type === "InputFilter" && stableFilter.group === "rooms" && stableFilter.display_type === "number") return Array.from({ length: 10 }, (_, index) => {
@@ -4987,11 +5093,175 @@ const toGetFiltersOutputItem = ({ locale, stableFilter, translations }) => Effec
4987
5093
  };
4988
5094
  });
4989
5095
  //#endregion
5096
+ //#region src/legacy-v9/parser/search/input/filter/apply-voffice-filter-derived-basic-query-inputs.ts
5097
+ const hasBasicQueryInput = ({ basicQueryInputs, key }) => basicQueryInputs.some((input) => input.key === key);
5098
+ const derivedBooleanFilters = [{
5099
+ key: "pets",
5100
+ isSelected: (filter) => (filter.petsCount ?? 0) > 0
5101
+ }];
5102
+ const applyVofficeFilterDerivedBasicQueryInputs = ({ appliedFilters, basicQueryInputs, filter, locale, translations }) => Effect.gen(function* () {
5103
+ const nextAppliedFilters = [...appliedFilters];
5104
+ const nextBasicQueryInputs = [...basicQueryInputs];
5105
+ for (const derivedFilter of derivedBooleanFilters) {
5106
+ if (!derivedFilter.isSelected(filter) || hasBasicQueryInput({
5107
+ basicQueryInputs: nextBasicQueryInputs,
5108
+ key: derivedFilter.key
5109
+ })) continue;
5110
+ nextBasicQueryInputs.push({
5111
+ key: derivedFilter.key,
5112
+ value: true
5113
+ });
5114
+ nextAppliedFilters.push({
5115
+ key: derivedFilter.key,
5116
+ label: yield* translations.translate(locale, `filter.${derivedFilter.key}.label`, derivedFilter.key)
5117
+ });
5118
+ }
5119
+ return {
5120
+ appliedFilters: nextAppliedFilters,
5121
+ basicQueryInputs: nextBasicQueryInputs
5122
+ };
5123
+ });
5124
+ //#endregion
5125
+ //#region src/legacy-v9/parser/search/input/filter/filter-query-keys.ts
5126
+ const FILTER_QUERY_KEYS = new Set(Object.keys(stable_filters_default));
5127
+ //#endregion
5128
+ //#region src/legacy-v9/parser/search/input/filter/to-basic-query-inputs.ts
5129
+ const isEnabledBooleanFilter = (value) => {
5130
+ const normalizedValue = value.toLowerCase();
5131
+ return normalizedValue === "" || normalizedValue === "true" || normalizedValue === "1";
5132
+ };
5133
+ const toBasicQueryInput = ({ queryKey, value, filter }) => {
5134
+ if (filter.type === "BooleanFilter") return isEnabledBooleanFilter(value) ? {
5135
+ key: queryKey,
5136
+ value: true
5137
+ } : void 0;
5138
+ if (filter.type === "InputFilter") {
5139
+ const numberValue = Number(value);
5140
+ if (!Number.isInteger(numberValue) || numberValue < 0) return;
5141
+ return {
5142
+ key: queryKey,
5143
+ value: numberValue
5144
+ };
5145
+ }
5146
+ };
5147
+ const toCustomBackendFilterInput = ({ queryKey, value, filter }) => {
5148
+ switch (filter.type) {
5149
+ case "boolean": return isEnabledBooleanFilter(value) ? {
5150
+ key: queryKey,
5151
+ value: true
5152
+ } : void 0;
5153
+ case "int": {
5154
+ const numberValue = Number(value);
5155
+ return Number.isInteger(numberValue) && numberValue >= 0 ? {
5156
+ key: queryKey,
5157
+ value: numberValue
5158
+ } : void 0;
5159
+ }
5160
+ case "string": return value.length > 0 ? {
5161
+ key: queryKey,
5162
+ value
5163
+ } : void 0;
5164
+ case "option": return filter.options.some((option) => option.value === value) ? {
5165
+ key: queryKey,
5166
+ value
5167
+ } : void 0;
5168
+ }
5169
+ };
5170
+ const makeV9CustomAttributeFilterCapabilities = (customAttributeFilterDefinitions) => {
5171
+ const capabilities = /* @__PURE__ */ new Map();
5172
+ for (const [queryKey, filter] of Object.entries(stable_filters_default)) {
5173
+ if (!FILTER_QUERY_KEYS.has(queryKey)) continue;
5174
+ capabilities.set(queryKey, { isValidValue: (value) => {
5175
+ if (queryKey === "pets") {
5176
+ const normalizedValue = value.toLowerCase();
5177
+ if ([
5178
+ "",
5179
+ "true",
5180
+ "false"
5181
+ ].includes(normalizedValue)) return true;
5182
+ const count = Number(value);
5183
+ return Number.isInteger(count) && count >= 0;
5184
+ }
5185
+ return toBasicQueryInput({
5186
+ queryKey,
5187
+ value,
5188
+ filter
5189
+ }) != null;
5190
+ } });
5191
+ }
5192
+ for (const definition of customAttributeFilterDefinitions) {
5193
+ if (definition.source !== "backendFilter" || !definition.searchable) continue;
5194
+ capabilities.set(definition.key, { isValidValue: (value) => toCustomBackendFilterInput({
5195
+ queryKey: definition.key,
5196
+ value,
5197
+ filter: definition
5198
+ }) != null });
5199
+ }
5200
+ return capabilities;
5201
+ };
5202
+ const toBasicQueryInputs = ({ query, customAttributeFilterDefinitions, locale, translations }) => {
5203
+ const appliedFilterEffects = [];
5204
+ const basicQueryInputs = [];
5205
+ const remainingParameters = [];
5206
+ const seenScalarFilters = /* @__PURE__ */ new Set();
5207
+ const customBackendFiltersByKey = new Map(customAttributeFilterDefinitions.filter((definition) => definition.source === "backendFilter" && definition.searchable).map((definition) => [definition.key, definition]));
5208
+ for (const [queryKey, value] of parseQueryParameters(query)) {
5209
+ const filter = stable_filters_default[queryKey];
5210
+ const customBackendFilter = customBackendFiltersByKey.get(queryKey);
5211
+ if ((!FILTER_QUERY_KEYS.has(queryKey) || filter == null) && customBackendFilter == null) {
5212
+ remainingParameters.push([queryKey, value]);
5213
+ continue;
5214
+ }
5215
+ if (seenScalarFilters.has(queryKey)) {
5216
+ remainingParameters.push([queryKey, value]);
5217
+ continue;
5218
+ }
5219
+ const basicQueryInput = filter == null ? customBackendFilter != null ? toCustomBackendFilterInput({
5220
+ queryKey,
5221
+ value,
5222
+ filter: customBackendFilter
5223
+ }) : void 0 : toBasicQueryInput({
5224
+ queryKey,
5225
+ value,
5226
+ filter
5227
+ });
5228
+ if (basicQueryInput == null) {
5229
+ remainingParameters.push([queryKey, value]);
5230
+ continue;
5231
+ }
5232
+ seenScalarFilters.add(queryKey);
5233
+ basicQueryInputs.push(basicQueryInput);
5234
+ if (customBackendFilter != null) appliedFilterEffects.push(Effect.succeed({
5235
+ key: queryKey,
5236
+ label: toCustomAttributeFilterLabel({
5237
+ definition: customBackendFilter,
5238
+ locale,
5239
+ value
5240
+ }),
5241
+ ...customBackendFilter.type === "boolean" ? {} : { value: String(basicQueryInput.value) }
5242
+ }));
5243
+ else {
5244
+ const canonicalValue = filter?.type === "InputFilter" ? String(basicQueryInput.value) : void 0;
5245
+ appliedFilterEffects.push(translations.translate(locale, `filter.${queryKey}.label`, queryKey).pipe(Effect.map((label) => ({
5246
+ key: queryKey,
5247
+ label,
5248
+ ...canonicalValue === void 0 ? {} : { value: canonicalValue }
5249
+ }))));
5250
+ }
5251
+ }
5252
+ return {
5253
+ appliedFilters: Effect.all(appliedFilterEffects),
5254
+ basicQueryInputs,
5255
+ remainingQuery: toQueryString(remainingParameters)
5256
+ };
5257
+ };
5258
+ //#endregion
4990
5259
  //#region src/legacy-v9/static/filter.ts
4991
5260
  const makeV9FilterApi = ({ options }) => {
4992
5261
  const translations = makeV9TranslationService(options.translationOverrides);
4993
5262
  return { getFilters: (input) => Effect.runPromise(Effect.gen(function* () {
4994
5263
  const filterItems = [];
5264
+ const capabilities = makeV9CustomAttributeFilterCapabilities(options.customAttributeFilterDefinitions);
4995
5265
  for (const [key, stableFilter] of Object.entries(stable_filters_default)) {
4996
5266
  const filterItem = yield* toGetFiltersOutputItem({
4997
5267
  locale: input.locale,
@@ -5004,13 +5274,23 @@ const makeV9FilterApi = ({ options }) => {
5004
5274
  filterItems.push(filterItem);
5005
5275
  }
5006
5276
  for (const definition of options.customAttributeFilterDefinitions) {
5277
+ if (definition.source === "composition" && !expandCustomAttributeFilterCompositions({
5278
+ capabilities,
5279
+ customAttributeFilterDefinitions: options.customAttributeFilterDefinitions,
5280
+ locale: input.locale,
5281
+ query: `${encodeURIComponent(definition.key)}=true`
5282
+ }).compositions.some((composition) => composition.key === definition.key)) continue;
5007
5283
  const filterItem = toCustomAttributeFilterOutputItem({
5284
+ category: yield* translations.translate(input.locale, toResolvedCustomAttributeCategoryTranslationKey(definition.category), definition.category),
5008
5285
  definition,
5009
5286
  locale: input.locale
5010
5287
  });
5011
5288
  if (filterItem != null) filterItems.push(filterItem);
5012
5289
  }
5013
- return filterItems;
5290
+ return yield* assertUniqueFilterQueryKeys({
5291
+ filterItems,
5292
+ source: "v9"
5293
+ });
5014
5294
  })) };
5015
5295
  };
5016
5296
  //#endregion
@@ -5035,7 +5315,7 @@ const DEFAULT_RENTAL_FEEDBACK_DATA_ATTRIBUTES = [
5035
5315
  "avgRating"
5036
5316
  ];
5037
5317
  const loadV9RentalsRuntime = Effect.tryPromise({
5038
- try: () => import("./rentals-Lw1qUKIj.mjs"),
5318
+ try: () => import("./rentals-cCejb_2d.mjs"),
5039
5319
  catch: (cause) => new CoreSDKError({
5040
5320
  source: "v10",
5041
5321
  operation: "v9.rentals.runtime",
@@ -5046,14 +5326,13 @@ const loadV9RentalsRuntime = Effect.tryPromise({
5046
5326
  const makeV9RentalsApi = ({ config, options }) => {
5047
5327
  const graphql = makeV9GraphQLClient(config);
5048
5328
  const translations = makeV9TranslationService(options.translationOverrides);
5049
- const customAttributeDefinitions = options.customAttributeFilterDefinitions.filter(isCustomAttributeDefinition);
5050
5329
  return { getRentals: (input) => Effect.runPromise(Effect.gen(function* () {
5051
5330
  const { defaultRentalDataAttributes, rentalsAllFeedbacksQuery, rentalsAllWithoutFeedbacksQuery, toRentalRentalsOutput } = yield* loadV9RentalsRuntime;
5052
5331
  const language = localeToLanguage(input.locale);
5053
5332
  const rentalDataAttributes = [
5054
5333
  ...defaultRentalDataAttributes,
5055
5334
  ...config.rentalDataAttributes ?? [],
5056
- ...toCustomAttributeRentalDataAttributes(options.customAttributeFilterDefinitions)
5335
+ ...toCustomAttributeRentalDataAttributes(options.customAttributes)
5057
5336
  ];
5058
5337
  const scopedRentalsWithoutFeedbacks = filterV9RentalsByScope((yield* graphql.execute(rentalsAllWithoutFeedbacksQuery, {
5059
5338
  language,
@@ -5078,7 +5357,7 @@ const makeV9RentalsApi = ({ config, options }) => {
5078
5357
  translations,
5079
5358
  imageProxyBaseUrl: config.imageProxyBaseUrl,
5080
5359
  rentalHighlightPrioritization: [...options.rentalHighlightPrioritization],
5081
- customAttributeFilterDefinitions: customAttributeDefinitions
5360
+ customAttributes: options.customAttributes
5082
5361
  })));
5083
5362
  })) };
5084
5363
  };
@@ -5184,4 +5463,4 @@ function createWebsiteSDK({ config, options }) {
5184
5463
  };
5185
5464
  }
5186
5465
  //#endregion
5187
- export { SetupResponseSchema as $, ErgoPolicyNumberRequestSchema as A, selectInsurancePaymentEffect as At, QuotePricesResponseSchema$1 as B, ErgoAddressSchema as C, getStartDateSelectedAvailabilityEffect as Ct, ErgoCreatePreContractRequestSchema as D, searchEffect as Dt, ErgoCommonFieldsSchema as E, removeAdditionalServiceEffect as Et, OnOfficeUnitSchema as F, RegionListResponseSchema as G, QuoteServiceSchema as H, PaymentScheduleItemSchema as I, ServiceBeonDataSchema as J, RoomImageSchema as K, PaymentScheduleSchema$1 as L, ErgoTripAndCustomerRequestSchema as M, submitPaymentOption as Mt, FacilityListResponseSchema as N, submitPaymentOptionEffect as Nt, ErgoPersonSchema as O, selectCancellationPolicyEffect as Ot, OnOfficeUnitCollectionResponseSchema as P, defineWebsiteSDKOptions as Pt, SetupDataSchema as Q, QuoteLineSchema$1 as R, DocumentTypeSchema as S, getRentalsEffect as St, ErgoBookRequestSchema as T, quoteEffect as Tt, RawObjectResponseSchema as U, QuoteSchema as V, RawVofficeObjectSchema as W, ServiceLimitSchema as X, ServiceImageSchema as Y, ServiceListResponseSchema as Z, DocumentResourceResponseSchema as _, bookInsuranceEffect as _t, makeV0OpenApiClientFetchLive as a, UnitIdsResponseSchema as at, DocumentStatusUpdateRequestSchema as b, getFiltersEffect as bt, V1OpenApiClientError as c, UnitOfferSchema as ct, ApiEnvelopeBaseSchema as d, V1_BASE_URL as dt, TileCollectionResponseSchema as et, CalendarDaySchema as f, V1_OPERATIONS as ft, DocumentCollectionResponseSchema as g, bookEffect as gt, CustomerUnitSummarySchema as h, addAdditionalServiceEffect as ht, V0OpenApiClientError as i, TravelInsuranceBookingStoreRequestSchema as it, ErgoReadPreContractRequestSchema as j, submitContactEffect as jt, ErgoPlanSearchRequestSchema as k, selectInsuranceEffect as kt, makeV1OpenApiClientFetchLive as l, UnitResponseSchema as lt, CurrentMemberResponseSchema as m, decodeV1OperationResponse as mt, stable_filters_default as n, TileTagSchema as nt, makeV0OpenApiClientLive as o, UnitListItemSchema as ot, CalendarResponseSchema$1 as p, VideoResponseSchema as pt, SearchPropertiesResponseSchema as q, V0OpenApiClient as r, TravelInsuranceBookingSchema as rt, V1OpenApiClient as s, UnitListResponseSchema as st, createWebsiteSDK as t, TileSchema as tt, makeV1OpenApiClientLive as u, UnitServicePriceSchema as ut, DocumentResourceSchema as v, clearAdditionalServicesEffect as vt, ErgoBankSchema as w, getTermsAndPrivacyPolicyEffect as wt, DocumentSummarySchema as x, getInitialAvailabilityEffect as xt, DocumentStatusSchema as y, createInsurancePreContractEffect as yt, QuotePricesPayloadSchema as z };
5466
+ export { ServiceBeonDataSchema as $, ErgoBookRequestSchema as A, quoteEffect as At, OnOfficeUnitSchema as B, DocumentResourceSchema as C, clearAdditionalServicesEffect as Ct, DocumentTypeSchema as D, getRentalsEffect as Dt, DocumentSummarySchema as E, getInitialAvailabilityEffect as Et, ErgoPolicyNumberRequestSchema as F, selectInsurancePaymentEffect as Ft, QuotePricesResponseSchema$1 as G, PaymentScheduleSchema$1 as H, ErgoReadPreContractRequestSchema as I, submitContactEffect as It, RawObjectResponseSchema as J, QuoteSchema as K, ErgoTripAndCustomerRequestSchema as L, submitPaymentOption as Lt, ErgoCreatePreContractRequestSchema as M, searchEffect as Mt, ErgoPersonSchema as N, selectCancellationPolicyEffect as Nt, ErgoAddressSchema as O, getStartDateSelectedAvailabilityEffect as Ot, ErgoPlanSearchRequestSchema as P, selectInsuranceEffect as Pt, SearchPropertiesResponseSchema as Q, FacilityListResponseSchema as R, submitPaymentOptionEffect as Rt, DocumentResourceResponseSchema as S, bookInsuranceEffect as St, DocumentStatusUpdateRequestSchema as T, getFiltersEffect as Tt, QuoteLineSchema$1 as U, PaymentScheduleItemSchema as V, QuotePricesPayloadSchema as W, RegionListResponseSchema as X, RawVofficeObjectSchema as Y, RoomImageSchema as Z, CalendarDaySchema as _, V1_OPERATIONS as _t, parseQueryParameters as a, TileCollectionResponseSchema as at, CustomerUnitSummarySchema as b, addAdditionalServiceEffect as bt, V0OpenApiClient as c, TravelInsuranceBookingSchema as ct, makeV0OpenApiClientLive as d, UnitListItemSchema as dt, ServiceImageSchema as et, V1OpenApiClient as f, UnitListResponseSchema as ft, ApiEnvelopeBaseSchema as g, V1_BASE_URL as gt, makeV1OpenApiClientLive as h, UnitServicePriceSchema as ht, collectQueryParameters as i, SetupResponseSchema as it, ErgoCommonFieldsSchema as j, removeAdditionalServiceEffect as jt, ErgoBankSchema as k, getTermsAndPrivacyPolicyEffect as kt, V0OpenApiClientError as l, TravelInsuranceBookingStoreRequestSchema as lt, makeV1OpenApiClientFetchLive as m, UnitResponseSchema as mt, makeV9CustomAttributeFilterCapabilities as n, ServiceListResponseSchema as nt, toQueryString as o, TileSchema as ot, V1OpenApiClientError as p, UnitOfferSchema as pt, QuoteServiceSchema as q, toBasicQueryInputs as r, SetupDataSchema as rt, applyVofficeFilterDerivedBasicQueryInputs as s, TileTagSchema as st, createWebsiteSDK as t, ServiceLimitSchema as tt, makeV0OpenApiClientFetchLive as u, UnitIdsResponseSchema as ut, CalendarResponseSchema$1 as v, VideoResponseSchema as vt, DocumentStatusSchema as w, createInsurancePreContractEffect as wt, DocumentCollectionResponseSchema as x, bookEffect as xt, CurrentMemberResponseSchema as y, decodeV1OperationResponse as yt, OnOfficeUnitCollectionResponseSchema as z, defineWebsiteSDKOptions as zt };