@v-office/website-sdk 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (39) hide show
  1. package/README.md +263 -0
  2. package/dist/cli.d.mts +1 -0
  3. package/dist/cli.mjs +337 -0
  4. package/dist/client-BbAfk-qa.mjs +33672 -0
  5. package/dist/custom-attribute-ChCbJKf_.mjs +54 -0
  6. package/dist/errors-2cuUGSvi.mjs +5 -0
  7. package/dist/index.d.mts +16130 -0
  8. package/dist/index.mjs +3 -0
  9. package/dist/operations-BanW36PK.mjs +12616 -0
  10. package/dist/operations-CHxEQ3jG.mjs +904 -0
  11. package/dist/parser-D6UAf8Ld.mjs +65 -0
  12. package/dist/quote-C3HZsFua.mjs +313 -0
  13. package/dist/quote-Vl6Nutaa.mjs +513 -0
  14. package/dist/rentals-BFmmp26v.mjs +323 -0
  15. package/dist/rentals-cQAg5TTW.mjs +403 -0
  16. package/dist/search-JqA3v_Fx.mjs +342 -0
  17. package/dist/search-filter-metadata-DZP0-Udc.mjs +4294 -0
  18. package/dist/to-rental-highlights-BcRa9Odv.mjs +402 -0
  19. package/dist/translations/shared/de-DE/availability.json +15 -0
  20. package/dist/translations/shared/de-DE/booking.json +15 -0
  21. package/dist/translations/shared/de-DE/insurance.json +18 -0
  22. package/dist/translations/shared/de-DE/quote.json +8 -0
  23. package/dist/translations/shared/de-DE/reviews.json +11 -0
  24. package/dist/translations/shared/en-US/availability.json +15 -0
  25. package/dist/translations/shared/en-US/booking.json +15 -0
  26. package/dist/translations/shared/en-US/insurance.json +18 -0
  27. package/dist/translations/shared/en-US/quote.json +8 -0
  28. package/dist/translations/shared/en-US/reviews.json +11 -0
  29. package/dist/translations/v10/de-DE/core.json +7027 -0
  30. package/dist/translations/v10/en-US/core.json +7027 -0
  31. package/dist/translations/v9/de-DE/core.json +4 -0
  32. package/dist/translations/v9/de-DE/filter.json +25 -0
  33. package/dist/translations/v9/de-DE/rental-attribute-categories.json +19 -0
  34. package/dist/translations/v9/de-DE/rental-attributes.json +451 -0
  35. package/dist/translations/v9/en-US/core.json +4 -0
  36. package/dist/translations/v9/en-US/filter.json +25 -0
  37. package/dist/translations/v9/en-US/rental-attribute-categories.json +19 -0
  38. package/dist/translations/v9/en-US/rental-attributes.json +451 -0
  39. package/package.json +90 -0
@@ -0,0 +1,65 @@
1
+ import { DateTime, Duration, Option } from "effect";
2
+ //#region src/adapters/shared/parser/local-date.ts
3
+ const ISO_LOCAL_DATE_PATTERN = /^\d{4}-\d{2}-\d{2}$/;
4
+ const isLocalDateString = (value) => {
5
+ if (!ISO_LOCAL_DATE_PATTERN.test(value)) return false;
6
+ const date = DateTime.make(value);
7
+ return Option.isSome(date) && DateTime.formatIsoDateUtc(date.value) === value;
8
+ };
9
+ const daysBetweenLocalDates = ({ start, end }) => {
10
+ const startDate = DateTime.removeTime(DateTime.makeUnsafe(start));
11
+ const endDate = DateTime.removeTime(DateTime.makeUnsafe(end));
12
+ const duration = DateTime.toEpochMillis(endDate) - DateTime.toEpochMillis(startDate);
13
+ return Math.trunc(Duration.toDays(Duration.millis(duration)));
14
+ };
15
+ //#endregion
16
+ //#region src/adapters/shared/parser/period-query-date.ts
17
+ const PREFERRED_QUERY_DATE_PATTERN = /^(\d{2})-(\d{2})-(\d{4})$/;
18
+ const ISO_QUERY_DATE_PATTERN = /^\d{4}-\d{2}-\d{2}$/;
19
+ const QUERY_MONTH_PATTERN = /^(\d{2})-(\d{4})$/;
20
+ const toIsoDateFromPeriodQueryDate = (value) => {
21
+ const preferredMatch = PREFERRED_QUERY_DATE_PATTERN.exec(value);
22
+ const isoDate = preferredMatch == null ? ISO_QUERY_DATE_PATTERN.test(value) ? value : void 0 : `${preferredMatch[3]}-${preferredMatch[2]}-${preferredMatch[1]}`;
23
+ if (isoDate === void 0) return;
24
+ return isLocalDateString(isoDate) ? isoDate : void 0;
25
+ };
26
+ const toYearMonthFromPeriodQueryMonth = (value) => {
27
+ const match = QUERY_MONTH_PATTERN.exec(value);
28
+ if (!match) return;
29
+ const [, month, year] = match;
30
+ const monthNumber = Number.parseInt(month, 10);
31
+ return monthNumber >= 1 && monthNumber <= 12 ? {
32
+ year: Number.parseInt(year, 10),
33
+ month: monthNumber
34
+ } : void 0;
35
+ };
36
+ //#endregion
37
+ //#region src/adapters/shared/parser/locale.ts
38
+ const localeToLanguage = (locale) => locale.split("-")[0] ?? locale;
39
+ //#endregion
40
+ //#region src/adapters/shared/parser/translation.ts
41
+ const makeTranslate = (translations, locale) => (key, fallback) => translations.translate(locale, key, fallback);
42
+ //#endregion
43
+ //#region src/adapters/shared/parser/to-country.ts
44
+ const toCountry = (country, locale) => {
45
+ const normalizedCountryCode = country.trim().toUpperCase();
46
+ if (!/^[A-Z]{2}$/.test(normalizedCountryCode)) return country;
47
+ return new Intl.DisplayNames([locale], { type: "region" }).of(normalizedCountryCode) ?? country;
48
+ };
49
+ //#endregion
50
+ //#region src/adapters/shared/parser/to-night-count.ts
51
+ const toNightCount = (input) => {
52
+ const nights = daysBetweenLocalDates({
53
+ start: input.period.start,
54
+ end: input.period.end
55
+ });
56
+ return Math.max(1, nights);
57
+ };
58
+ //#endregion
59
+ //#region src/adapters/shared/parser/unknown.ts
60
+ const asRecord = (value) => typeof value === "object" && value !== null ? value : {};
61
+ const asArray = (value) => Array.isArray(value) ? value : value === void 0 || value === null ? [] : [value];
62
+ const firstString = (...values) => values.find((value) => typeof value === "string" && value.length > 0);
63
+ const firstNumber = (...values) => values.find((value) => typeof value === "number" && Number.isFinite(value));
64
+ //#endregion
65
+ export { toNightCount as a, localeToLanguage as c, daysBetweenLocalDates as d, firstString as i, toIsoDateFromPeriodQueryDate as l, asRecord as n, toCountry as o, firstNumber as r, makeTranslate as s, asArray as t, toYearMonthFromPeriodQueryMonth as u };
@@ -0,0 +1,313 @@
1
+ import { Qt as formatCurrency, Xt as GuestQuote, Zt as addGuestQuoteAdditionalServiceSelection } from "./client-BbAfk-qa.mjs";
2
+ import { a as toNightCount } from "./parser-D6UAf8Ld.mjs";
3
+ import { DateTime, Effect } from "effect";
4
+ //#region src/adapters/v9/parser/quote/to-additional-service-calculation.ts
5
+ const toAdditionalServiceCalculation = (line) => {
6
+ switch (line.calculation) {
7
+ case "NIGHT": return "PER_NIGHT";
8
+ case "DAY": return "PER_DAY";
9
+ case "USAGE": return "PER_USAGE";
10
+ case "FLAT": return "FLAT";
11
+ default: return "FLAT";
12
+ }
13
+ };
14
+ //#endregion
15
+ //#region src/adapters/v9/parser/quote/to-charge.ts
16
+ const toCharge = ({ serviceLine, chargeAmount, currency, locale, translations }) => Effect.gen(function* () {
17
+ switch (serviceLine.calculation) {
18
+ case "NIGHT": return `${formatCurrency(chargeAmount, locale, currency)}${yield* translations.translate(locale, "quote.additionalService.night")}`;
19
+ case "DAY": return `${formatCurrency(chargeAmount, locale, currency)}${yield* translations.translate(locale, "quote.additionalService.day")}`;
20
+ default: return formatCurrency(chargeAmount, locale, currency);
21
+ }
22
+ });
23
+ //#endregion
24
+ //#region src/adapters/v9/parser/quote/to-charge-amount.ts
25
+ const toChargeAmount = ({ input, serviceLine }) => {
26
+ const amount = serviceLine.amount ?? 1;
27
+ if (amount <= 0 || serviceLine.total == null) return void 0;
28
+ switch (serviceLine.calculation) {
29
+ case "NIGHT": return serviceLine.total / toNightCount(input) / amount;
30
+ case "DAY": return serviceLine.total / toNightCount(input) / amount;
31
+ case "USAGE": return serviceLine.total / amount;
32
+ default: return serviceLine.total / amount;
33
+ }
34
+ };
35
+ //#endregion
36
+ //#region src/adapters/v9/parser/quote/to-service-line-label.ts
37
+ const toServiceLineLabel = ({ serviceLine, locale, translations }) => Effect.gen(function* () {
38
+ if (serviceLine.service?.name != null && serviceLine.service.name.trim().length > 0) return serviceLine.service.name;
39
+ if (serviceLine.service?.type != null && serviceLine.service.type.trim().length > 0) return yield* translations.translate(locale, `Service.type.${serviceLine.service.type}`);
40
+ return yield* translations.translate(locale, "Service.type.OTHER");
41
+ });
42
+ //#endregion
43
+ //#region src/adapters/v9/parser/quote/utils.ts
44
+ const hasEnoughLeadTime = ({ bookingStart, leadTimeDays, today }) => {
45
+ if (leadTimeDays == null) return true;
46
+ const start = DateTime.removeTime(DateTime.makeUnsafe(bookingStart));
47
+ const earliestStart = DateTime.add(DateTime.removeTime(today), { days: leadTimeDays });
48
+ return DateTime.toEpochMillis(start) >= DateTime.toEpochMillis(earliestStart);
49
+ };
50
+ const getLineServiceId = (line) => line.service?.id == null ? void 0 : String(line.service.id);
51
+ const isTaxLine = (line) => line.service?.type === "VTAX" || line.service?.type === "TAXES";
52
+ const getTotal = (line) => line.total ?? 0;
53
+ //#endregion
54
+ //#region src/adapters/v9/parser/quote/to-additional-services.ts
55
+ const toDescription = (line) => {
56
+ if (Array.isArray(line.beon_data)) return void 0;
57
+ return line.beon_data?.description;
58
+ };
59
+ const toImages = (line) => {
60
+ if (Array.isArray(line.beon_data)) return void 0;
61
+ const images = (line.beon_data?.images ?? []).flatMap((image) => {
62
+ if (image.url == null) return [];
63
+ return [{
64
+ src: image.url,
65
+ alt: image.title ?? image.description ?? line.service?.name ?? ""
66
+ }];
67
+ });
68
+ return images.length === 0 ? void 0 : images;
69
+ };
70
+ const toPositiveQuantity = (value) => value == null || value <= 0 ? void 0 : value;
71
+ const toAdditionalServices = ({ input, guestQuoteResult, locale, translations }) => Effect.gen(function* () {
72
+ const additionalServices = [];
73
+ const currency = guestQuoteResult?.currency ?? "EUR";
74
+ const today = yield* DateTime.now;
75
+ for (const line of [...guestQuoteResult?.optional ?? [], ...guestQuoteResult?.onsiteOptional ?? []]) {
76
+ const id = getLineServiceId(line);
77
+ if (id === void 0) continue;
78
+ const limit = guestQuoteResult?.serviceLimits?.[id];
79
+ const leadTimeDays = limit?.minDaysBeforeArrival ?? void 0;
80
+ if (!hasEnoughLeadTime({
81
+ bookingStart: input.period.start,
82
+ leadTimeDays,
83
+ today
84
+ })) continue;
85
+ const chargeAmount = toChargeAmount({
86
+ serviceLine: line,
87
+ input
88
+ });
89
+ if (chargeAmount === void 0) continue;
90
+ const label = yield* toServiceLineLabel({
91
+ serviceLine: line,
92
+ locale,
93
+ translations
94
+ });
95
+ const charge = yield* toCharge({
96
+ serviceLine: line,
97
+ chargeAmount,
98
+ currency,
99
+ locale,
100
+ translations
101
+ });
102
+ const maxPerBooking = toPositiveQuantity(limit?.maxPerBooking);
103
+ const lineAmount = toPositiveQuantity(line.amount);
104
+ const preSelectedQuantity = lineAmount === void 0 ? void 0 : Math.min(lineAmount, maxPerBooking ?? lineAmount);
105
+ const description = toDescription(line);
106
+ const images = toImages(line);
107
+ additionalServices.push({
108
+ id,
109
+ label,
110
+ charge,
111
+ chargeAmount,
112
+ calculation: toAdditionalServiceCalculation(line),
113
+ ...preSelectedQuantity === void 0 ? {} : { preSelectedQuantity },
114
+ ...maxPerBooking === void 0 ? {} : { maxPerBooking },
115
+ ...leadTimeDays === void 0 ? {} : { leadTimeDays },
116
+ ...description === void 0 ? {} : { description },
117
+ ...images === void 0 ? {} : { images }
118
+ });
119
+ }
120
+ return additionalServices;
121
+ });
122
+ //#endregion
123
+ //#region src/adapters/v9/parser/quote/to-backend-context.ts
124
+ const toSelectionQuantity = (selections, serviceId) => selections.find((selection) => selection.id === serviceId)?.quantity ?? 0;
125
+ const toSelectedTotal = (line, amount) => {
126
+ if (line.price == null) return void 0;
127
+ switch (line.calculation) {
128
+ case "NIGHT": return line.price * amount * (line.timespan ?? 1);
129
+ case "DAY": return line.price * amount * (line.timespan ?? 1);
130
+ case "PERC": return line.price * amount;
131
+ case "USAGE": return line.price * amount;
132
+ default: return line.price * amount;
133
+ }
134
+ };
135
+ const withSelectedAmount = (line, selections) => {
136
+ const serviceId = getLineServiceId(line);
137
+ if (serviceId === void 0) return line;
138
+ const amount = toSelectionQuantity(selections, serviceId);
139
+ const total = toSelectedTotal(line, amount);
140
+ return {
141
+ ...line,
142
+ amount,
143
+ ...total === void 0 ? {} : { total }
144
+ };
145
+ };
146
+ const withSelectedServiceAmounts = (quote, selections) => {
147
+ if (quote === void 0) return quote;
148
+ return {
149
+ ...quote,
150
+ ...quote.optional === void 0 ? {} : { optional: quote.optional.map((line) => withSelectedAmount(line, selections)) },
151
+ ...quote.onsiteOptional === void 0 ? {} : { onsiteOptional: quote.onsiteOptional.map((line) => withSelectedAmount(line, selections)) }
152
+ };
153
+ };
154
+ const toV9QuoteBackendContext = (input, guestQuoteResult) => ({
155
+ backend: "v9",
156
+ input,
157
+ rawQuote: guestQuoteResult
158
+ });
159
+ const updateV9QuoteBackendContext = (context, selections) => {
160
+ if (context.backend !== "v9") return context;
161
+ return {
162
+ ...context,
163
+ rawQuote: withSelectedServiceAmounts(context.rawQuote, selections)
164
+ };
165
+ };
166
+ //#endregion
167
+ //#region src/adapters/v9/parser/quote/to-sections-included.ts
168
+ const isIncludedSubItem = (line) => line.service?.type === "RENT" || line.service?.summary === "SHOW";
169
+ const toSectionsIncluded = ({ input: _input, guestQuoteResult, locale, translations }) => Effect.gen(function* () {
170
+ const subItems = [];
171
+ let total = 0;
172
+ for (const line of guestQuoteResult?.mandatory ?? []) {
173
+ if (isTaxLine(line)) continue;
174
+ total += getTotal(line);
175
+ if (isIncludedSubItem(line)) {
176
+ const position = yield* toServiceLineLabel({
177
+ serviceLine: line,
178
+ locale,
179
+ translations
180
+ });
181
+ subItems.push({
182
+ position,
183
+ appliedCharge: yield* translations.translate(locale, "quote.sections.included")
184
+ });
185
+ }
186
+ }
187
+ return {
188
+ item: {
189
+ position: yield* translations.translate(locale, "quote.sections.inclusivePrice"),
190
+ appliedCharge: total
191
+ },
192
+ ...subItems.length === 0 ? {} : { subItems }
193
+ };
194
+ });
195
+ //#endregion
196
+ //#region src/adapters/v9/parser/quote/to-sections-other.ts
197
+ const isOther = (line) => line.service?.type !== "RENT" && !isTaxLine(line) && line.service?.summary !== "ADD_NO_SHOW";
198
+ const toSectionsOther = ({ input: _input, guestQuoteResult, locale, translations }) => Effect.gen(function* () {
199
+ const lines = [];
200
+ for (const line of guestQuoteResult?.mandatory ?? []) {
201
+ if (!isOther(line)) continue;
202
+ const position = yield* toServiceLineLabel({
203
+ serviceLine: line,
204
+ locale,
205
+ translations
206
+ });
207
+ lines.push({ item: {
208
+ position,
209
+ appliedCharge: getTotal(line)
210
+ } });
211
+ }
212
+ return lines;
213
+ });
214
+ //#endregion
215
+ //#region src/adapters/v9/parser/quote/to-sections-tax.ts
216
+ const toSectionsTax = ({ input: _input, guestQuoteResult, locale, translations }) => Effect.gen(function* () {
217
+ let total = 0;
218
+ for (const line of guestQuoteResult?.mandatory ?? []) if (isTaxLine(line)) total += getTotal(line);
219
+ return { item: {
220
+ position: yield* translations.translate(locale, "quote.sections.tax"),
221
+ appliedCharge: total
222
+ } };
223
+ });
224
+ //#endregion
225
+ //#region src/adapters/v9/parser/quote/to-sections.ts
226
+ const hasSectionLineContent = (line) => line.item.appliedCharge !== 0 || (line.subItems?.length ?? 0) > 0;
227
+ const toSections = ({ input, guestQuoteResult, locale, translations }) => Effect.gen(function* () {
228
+ const sections = [];
229
+ const sectionsIncluded = yield* toSectionsIncluded({
230
+ input,
231
+ guestQuoteResult,
232
+ locale,
233
+ translations
234
+ });
235
+ const sectionsTax = yield* toSectionsTax({
236
+ input,
237
+ guestQuoteResult,
238
+ locale,
239
+ translations
240
+ });
241
+ const sectionsOther = yield* toSectionsOther({
242
+ input,
243
+ guestQuoteResult,
244
+ locale,
245
+ translations
246
+ });
247
+ if (hasSectionLineContent(sectionsIncluded)) sections.push({ lines: [sectionsIncluded] });
248
+ if (hasSectionLineContent(sectionsTax)) sections.push({ lines: [sectionsTax] });
249
+ if (sectionsOther.length > 0) sections.push({ lines: sectionsOther });
250
+ return sections;
251
+ });
252
+ //#endregion
253
+ //#region src/adapters/v9/parser/quote/to-guest-quote.ts
254
+ const toBaseTotal = (guestQuoteResult) => {
255
+ const paymentScheduleTotal = guestQuoteResult?.paymentSchedule?.total?.amount;
256
+ if (paymentScheduleTotal != null) return paymentScheduleTotal;
257
+ return (guestQuoteResult?.mandatory ?? []).reduce((total, line) => total + (line.total ?? 0), 0);
258
+ };
259
+ const toErrorReason = (guestQuoteResult) => {
260
+ const errors = guestQuoteResult?.errors;
261
+ if (errors === void 0) return void 0;
262
+ const messages = (Array.isArray(errors) ? errors : [errors]).flatMap((error) => Object.values(error).filter((value) => typeof value === "string" && value.trim().length > 0));
263
+ return messages.length === 0 ? void 0 : messages.join(" ");
264
+ };
265
+ const toGuestQuote = (input, guestQuoteResult, locale, translations) => Effect.gen(function* () {
266
+ const errorReason = toErrorReason(guestQuoteResult);
267
+ if (errorReason !== void 0) return {
268
+ status: "unavailable",
269
+ reason: errorReason
270
+ };
271
+ const sections = yield* toSections({
272
+ input,
273
+ guestQuoteResult,
274
+ locale,
275
+ translations
276
+ });
277
+ const additionalServices = yield* toAdditionalServices({
278
+ input,
279
+ guestQuoteResult,
280
+ locale,
281
+ translations
282
+ });
283
+ const baseTotal = toBaseTotal(guestQuoteResult);
284
+ const currency = guestQuoteResult?.currency ?? "EUR";
285
+ let guestQuote = new GuestQuote({
286
+ input,
287
+ bookingCardInformation: {
288
+ sections,
289
+ total: baseTotal
290
+ },
291
+ baseTotal,
292
+ currency,
293
+ additionalServices,
294
+ backendContext: toV9QuoteBackendContext(input, guestQuoteResult),
295
+ updateBackendContext: updateV9QuoteBackendContext
296
+ });
297
+ for (const additionalService of additionalServices) {
298
+ if (additionalService.preSelectedQuantity === void 0) continue;
299
+ for (let quantity = 0; quantity < additionalService.preSelectedQuantity; quantity++) {
300
+ const result = addGuestQuoteAdditionalServiceSelection({
301
+ quote: guestQuote,
302
+ id: additionalService.id
303
+ });
304
+ if (result.ok) guestQuote = result.quote;
305
+ }
306
+ }
307
+ return {
308
+ status: "available",
309
+ quote: guestQuote
310
+ };
311
+ });
312
+ //#endregion
313
+ export { toGuestQuote };