@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,403 @@
1
+ import { F as parseVofficeFacilityData, I as parseVofficeFeedbackData, L as parseVofficeUnitData, M as VofficeUnitDataPropertyCategoryLabelKeys, N as VofficeUnitDataPropertyCategoryValues, P as VofficeUnitDataPropertyMetadata } from "./client-BbAfk-qa.mjs";
2
+ import "./errors-2cuUGSvi.mjs";
3
+ import { c as localeToLanguage, s as makeTranslate } from "./parser-D6UAf8Ld.mjs";
4
+ import { i as renderLabeledAttributeValue, t as renderCustomAttributeHighlight } from "./custom-attribute-ChCbJKf_.mjs";
5
+ import { n as toAddress, r as toLocalizedString, t as toRentalHighlights } from "./to-rental-highlights-BcRa9Odv.mjs";
6
+ import { Effect } from "effect";
7
+ //#region src/adapters/v9/parser/rentals/to-description.ts
8
+ const toDescription = (data, locale) => {
9
+ const description = {};
10
+ const intro = toLocalizedString(data.known.intro, locale);
11
+ const headline = toLocalizedString(data.known.headline, locale);
12
+ const descriptionText = toLocalizedString(data.known.description, locale);
13
+ const altDescription = toLocalizedString(data.known.altDescription, locale);
14
+ const arrivalInfo = toLocalizedString(data.known.arrivalInfo, locale);
15
+ const departureInfo = toLocalizedString(data.known.departureInfo, locale);
16
+ const locationDescription = toLocalizedString(data.known.locationDescription, locale);
17
+ const directions = toLocalizedString(data.known.directions, locale);
18
+ if (intro != null) description.intro = intro;
19
+ if (headline != null) description.headline = headline;
20
+ if (descriptionText != null) description.description = descriptionText;
21
+ if (altDescription != null) description.altDescription = altDescription;
22
+ if (arrivalInfo != null) description.arrivalInfo = arrivalInfo;
23
+ if (departureInfo != null) description.departureInfo = departureInfo;
24
+ if (locationDescription != null) description.locationDescription = locationDescription;
25
+ if (directions != null) description.directions = directions;
26
+ return description;
27
+ };
28
+ //#endregion
29
+ //#region src/adapters/v9/parser/rentals/to-image.ts
30
+ const IMAGE_ASPECT_RATIO = 3 / 2;
31
+ const IMAGE_SRCSET_WIDTHS = [
32
+ 320,
33
+ 480,
34
+ 640,
35
+ 960,
36
+ 1280,
37
+ 1920,
38
+ 2560
39
+ ];
40
+ const IMAGE_DEFAULT_WIDTH = 960;
41
+ const buildImageUrl = (imageProxyBaseUrl, path, width) => {
42
+ const base = imageProxyBaseUrl.replace(/\/$/, "");
43
+ const normalizedPath = path.replace(/^\/+/, "");
44
+ return `${base}/_/rs:fill:${width}:${Math.round(width / IMAGE_ASPECT_RATIO)}:0/plain/${normalizedPath}`;
45
+ };
46
+ const buildSrcset = (imageProxyBaseUrl, path) => IMAGE_SRCSET_WIDTHS.map((width) => `${buildImageUrl(imageProxyBaseUrl, path, width)} ${width}w`).join(", ");
47
+ const isNonEmptyString = (value) => typeof value === "string" && value.length > 0;
48
+ const toDataImageId = (dataImage) => dataImage?.id?.toString();
49
+ const getImagePath = (entry) => entry.image?.path ?? entry.normalizedPath;
50
+ const getFileName = (path) => path?.split("/").at(-1);
51
+ const getDirectoryName = (path) => {
52
+ const lastSlashIndex = path.lastIndexOf("/");
53
+ return lastSlashIndex === -1 ? void 0 : path.slice(0, lastSlashIndex);
54
+ };
55
+ const stripDataImageFilePrefix = (fileName) => fileName.replace(/^mf(?=\d)/i, "");
56
+ const getDataImageFileName = (dataImage) => {
57
+ const name = dataImage.name != null ? stripDataImageFilePrefix(dataImage.name) : void 0;
58
+ if (name != null) return name;
59
+ return dataImage.id != null ? `${dataImage.id}.jpg` : void 0;
60
+ };
61
+ const normalizeDataImagePath = (dataImage, vofficeImages) => {
62
+ const fileName = getDataImageFileName(dataImage);
63
+ if (fileName == null) return void 0;
64
+ const matchingPath = vofficeImages.find((image) => getFileName(image.path ?? void 0) === fileName)?.path;
65
+ if (matchingPath != null) return matchingPath;
66
+ const directoryName = vofficeImages.map((image) => image.path != null ? getDirectoryName(image.path) : void 0).find((path) => path != null);
67
+ return directoryName != null ? `${directoryName}/${fileName}` : void 0;
68
+ };
69
+ const pathMatchesName = (path, name) => path != null && name != null && (path === name || path.endsWith(`/${name}`));
70
+ const entriesShareId = (left, right) => {
71
+ const leftIds = [left.image?.id, toDataImageId(left.dataImage)].filter(isNonEmptyString);
72
+ const rightIds = [right.image?.id, toDataImageId(right.dataImage)].filter(isNonEmptyString);
73
+ return leftIds.some((leftId) => rightIds.includes(leftId));
74
+ };
75
+ const entriesSharePathOrName = (left, right) => {
76
+ const leftPath = getImagePath(left);
77
+ const rightPath = getImagePath(right);
78
+ const leftName = left.dataImage != null ? getDataImageFileName(left.dataImage) : getFileName(left.image?.path ?? void 0);
79
+ const rightName = right.dataImage != null ? getDataImageFileName(right.dataImage) : getFileName(right.image?.path ?? void 0);
80
+ return leftPath != null && rightPath != null && leftPath === rightPath || pathMatchesName(leftPath, rightName) || pathMatchesName(rightPath, leftName);
81
+ };
82
+ const entriesMatch = (left, right) => entriesShareId(left, right) || entriesSharePathOrName(left, right);
83
+ const mergeEntry = (existing, next) => {
84
+ const image = existing.image ?? next.image;
85
+ const dataImage = existing.dataImage ?? next.dataImage;
86
+ const normalizedPath = existing.normalizedPath ?? next.normalizedPath;
87
+ const entry = { source: existing.source };
88
+ if (image != null) entry.image = image;
89
+ if (dataImage != null) entry.dataImage = dataImage;
90
+ if (normalizedPath != null) entry.normalizedPath = normalizedPath;
91
+ return entry;
92
+ };
93
+ const mergeImageEntries = (entries) => {
94
+ const merged = [];
95
+ for (const entry of entries) {
96
+ const index = merged.findIndex((existing) => entriesMatch(existing, entry));
97
+ if (index === -1) {
98
+ merged.push(entry);
99
+ continue;
100
+ }
101
+ merged[index] = mergeEntry(merged[index], entry);
102
+ }
103
+ return merged;
104
+ };
105
+ const getImageEntries = (rental, data) => {
106
+ const vofficeImages = rental.voffice_images ?? [];
107
+ return [
108
+ ...vofficeImages.map((image) => ({
109
+ source: "voffice_images",
110
+ image
111
+ })),
112
+ ...(rental.voffice_room_images ?? []).map((image) => ({
113
+ source: "voffice_room_images",
114
+ image
115
+ })),
116
+ ...(data.known.images ?? []).flatMap((dataImage) => {
117
+ const normalizedPath = normalizeDataImagePath(dataImage, vofficeImages);
118
+ if (normalizedPath == null) return [];
119
+ return [{
120
+ source: "data_images",
121
+ dataImage,
122
+ normalizedPath
123
+ }];
124
+ })
125
+ ];
126
+ };
127
+ const toImage = (imageProxyBaseUrl, entry, locale) => {
128
+ const path = getImagePath(entry);
129
+ const src = path != null ? buildImageUrl(imageProxyBaseUrl, path, IMAGE_DEFAULT_WIDTH) : "";
130
+ const srcset = path != null ? buildSrcset(imageProxyBaseUrl, path) : "";
131
+ const title = toLocalizedString(entry.dataImage?.titles, locale) ?? entry.image?.title;
132
+ const alt = title ?? entry.image?.description ?? "";
133
+ const metadata = { source: entry.source };
134
+ if (entry.dataImage != null) metadata.dataImage = entry.dataImage;
135
+ const image = {
136
+ idOrPath: path ?? toDataImageId(entry.dataImage) ?? entry.dataImage?.name ?? "",
137
+ src,
138
+ srcset,
139
+ alt,
140
+ metadata
141
+ };
142
+ if (title != null) image.title = title;
143
+ if (entry.dataImage?.tag != null) image.category = entry.dataImage.tag;
144
+ return image;
145
+ };
146
+ const toImages = (imageProxyBaseUrl, rental, locale, data) => {
147
+ if (rental.voffice_images == null || rental.voffice_images.length === 0) return [];
148
+ return mergeImageEntries(getImageEntries(rental, data)).map((entry) => toImage(imageProxyBaseUrl, entry, locale));
149
+ };
150
+ //#endregion
151
+ //#region src/adapters/v9/parser/rentals/to-location.ts
152
+ const toLocation = (location) => {
153
+ if (location == null) return void 0;
154
+ const [longitude, latitude] = location.coordinates;
155
+ return {
156
+ latitude,
157
+ longitude
158
+ };
159
+ };
160
+ //#endregion
161
+ //#region src/adapters/v9/parser/rentals/to-property.ts
162
+ const toProperty = (facility, locale) => {
163
+ const data = parseVofficeFacilityData(facility.data);
164
+ const name = toLocalizedString(data.known.name, locale);
165
+ const description = toLocalizedString(data.known.description, locale);
166
+ const property = {
167
+ id: facility.id,
168
+ nameOrLabel: name ?? facility.name
169
+ };
170
+ if (description != null) property.description = { description };
171
+ return property;
172
+ };
173
+ //#endregion
174
+ //#region src/adapters/v9/parser/rentals/to-rental-attributes.ts
175
+ const GENERAL_CATEGORY$1 = {
176
+ key: "GENERAL",
177
+ labelKey: VofficeUnitDataPropertyCategoryLabelKeys.GENERAL
178
+ };
179
+ const CATEGORY_ORDER = new Map(VofficeUnitDataPropertyCategoryValues.map((category, index) => [category, index]));
180
+ const PROPERTY_METADATA_ENTRIES = Object.entries(VofficeUnitDataPropertyMetadata).map(([key, metadata], index) => ({
181
+ key,
182
+ metadata,
183
+ index
184
+ })).filter(({ metadata }) => metadata.category.key !== "LOCATION").sort((left, right) => {
185
+ return (CATEGORY_ORDER.get(left.metadata.category.key) ?? Number.MAX_SAFE_INTEGER) - (CATEGORY_ORDER.get(right.metadata.category.key) ?? Number.MAX_SAFE_INTEGER) || left.index - right.index;
186
+ });
187
+ const toRentalAttributes = ({ locale, translations, attributes, customAttributeFilterDefinitions }) => Effect.gen(function* () {
188
+ const result = [];
189
+ const categoriesByKey = /* @__PURE__ */ new Map();
190
+ const subCategoriesByKey = /* @__PURE__ */ new Map();
191
+ const knownAttributes = attributes.known;
192
+ const translate = makeTranslate(translations, locale);
193
+ const getGeneralSubCategory = () => Effect.gen(function* () {
194
+ let category = categoriesByKey.get(GENERAL_CATEGORY$1.key);
195
+ if (category == null) {
196
+ category = {
197
+ label: yield* translate(GENERAL_CATEGORY$1.labelKey, GENERAL_CATEGORY$1.key),
198
+ subCategories: []
199
+ };
200
+ categoriesByKey.set(GENERAL_CATEGORY$1.key, category);
201
+ result.push(category);
202
+ }
203
+ let subCategory = subCategoriesByKey.get(GENERAL_CATEGORY$1.key);
204
+ if (subCategory == null) {
205
+ subCategory = {
206
+ label: category.label,
207
+ items: []
208
+ };
209
+ subCategoriesByKey.set(GENERAL_CATEGORY$1.key, subCategory);
210
+ category.subCategories.push(subCategory);
211
+ }
212
+ return subCategory;
213
+ });
214
+ for (const { key, metadata } of PROPERTY_METADATA_ENTRIES) {
215
+ const attribute = renderLabeledAttributeValue({
216
+ key,
217
+ label: yield* translate(metadata.labelKey, key),
218
+ locale,
219
+ value: knownAttributes[key]
220
+ });
221
+ if (attribute == null) continue;
222
+ const categoryMetadata = metadata.category ?? GENERAL_CATEGORY$1;
223
+ let category = categoriesByKey.get(categoryMetadata.key);
224
+ if (category == null) {
225
+ category = {
226
+ label: yield* translate(categoryMetadata.labelKey, categoryMetadata.key),
227
+ subCategories: []
228
+ };
229
+ categoriesByKey.set(categoryMetadata.key, category);
230
+ result.push(category);
231
+ }
232
+ let subCategory = subCategoriesByKey.get(categoryMetadata.key);
233
+ if (subCategory == null) {
234
+ subCategory = {
235
+ label: yield* translate(GENERAL_CATEGORY$1.labelKey, GENERAL_CATEGORY$1.key),
236
+ items: []
237
+ };
238
+ subCategoriesByKey.set(categoryMetadata.key, subCategory);
239
+ category.subCategories.push(subCategory);
240
+ }
241
+ subCategory.items.push(attribute);
242
+ }
243
+ const customAttributeItems = [];
244
+ for (const definition of customAttributeFilterDefinitions) {
245
+ const value = knownAttributes[definition.key] ?? attributes.unknown[definition.key];
246
+ const attribute = renderCustomAttributeHighlight({
247
+ definitions: customAttributeFilterDefinitions,
248
+ key: definition.key,
249
+ locale,
250
+ value
251
+ });
252
+ if (attribute != null) customAttributeItems.push(attribute);
253
+ }
254
+ if (customAttributeItems.length > 0) (yield* getGeneralSubCategory()).items.push(...customAttributeItems);
255
+ return result;
256
+ });
257
+ //#endregion
258
+ //#region src/adapters/v9/parser/rentals/to-rental-type.ts
259
+ const toRentalType = ({ translations, locale, rentalType }) => makeTranslate(translations, locale)(`Rental.rentalType.option.${rentalType}`, rentalType);
260
+ //#endregion
261
+ //#region src/adapters/v9/parser/rentals/to-rental-vicinity.ts
262
+ const GENERAL_CATEGORY = {
263
+ key: "GENERAL",
264
+ labelKey: VofficeUnitDataPropertyCategoryLabelKeys.GENERAL
265
+ };
266
+ const LOCATION_METADATA_ENTRIES = Object.entries(VofficeUnitDataPropertyMetadata).map(([key, metadata], index) => ({
267
+ key,
268
+ metadata,
269
+ index
270
+ })).filter(({ metadata }) => metadata.category.key === "LOCATION").sort((left, right) => left.index - right.index);
271
+ const toRentalVicinity = ({ locale, translations, attributes }) => Effect.gen(function* () {
272
+ const items = [];
273
+ const knownAttributes = attributes.known;
274
+ const translate = makeTranslate(translations, locale);
275
+ for (const { key, metadata } of LOCATION_METADATA_ENTRIES) {
276
+ const item = renderLabeledAttributeValue({
277
+ key,
278
+ label: yield* translate(metadata.labelKey, key),
279
+ locale,
280
+ value: knownAttributes[key]
281
+ });
282
+ if (item != null) items.push(item);
283
+ }
284
+ if (items.length === 0) return [];
285
+ return [{
286
+ label: yield* translate(GENERAL_CATEGORY.labelKey, GENERAL_CATEGORY.key),
287
+ items
288
+ }];
289
+ });
290
+ //#endregion
291
+ //#region src/adapters/v9/parser/rentals/to-reviews.ts
292
+ const toClassification = (rating) => {
293
+ if (rating >= 4.5) return "excellent";
294
+ if (rating >= 4) return "very-good";
295
+ if (rating >= 3) return "good";
296
+ if (rating >= 2) return "fair";
297
+ return "poor";
298
+ };
299
+ const roundRatingUp = (rating) => Math.ceil(Number(rating.toFixed(6)) * 10) / 10;
300
+ const renderTemplate = (template, values) => Object.entries(values).reduce((result, [key, value]) => result.replaceAll(`{${key}}`, value), template);
301
+ const toTravelDate = (locale, value) => {
302
+ if (value == null) return void 0;
303
+ return new Intl.DateTimeFormat(locale, {
304
+ month: "long",
305
+ year: "numeric"
306
+ }).format(new Date(value));
307
+ };
308
+ const toReviews = ({ locale, translations, rental }) => Effect.gen(function* () {
309
+ const parsedItems = (yield* Effect.succeed(rental.voffice_feedbacks))?.flatMap((feedback) => {
310
+ const { forename, surname, avgRating, text, highlighted, arrival, departure, onDate, onTimestamp } = parseVofficeFeedbackData(feedback.data).known;
311
+ const author = [forename, surname].filter((value) => value != null && value !== "").join(" ");
312
+ if (author === "" || avgRating == null || avgRating < 1 || text == null) return [];
313
+ return [{
314
+ rating: roundRatingUp(avgRating),
315
+ author,
316
+ text,
317
+ highlighted: highlighted === true,
318
+ travelDate: toTravelDate(locale, arrival ?? departure ?? onDate ?? onTimestamp)
319
+ }];
320
+ }) ?? [];
321
+ if (parsedItems.length === 0) return void 0;
322
+ const summaryRating = roundRatingUp(parsedItems.reduce((ratingSum, item) => ratingSum + item.rating, 0) / parsedItems.length);
323
+ const summaryRatingTemplate = yield* translations.translate(locale, "reviews.summary.rating");
324
+ const summaryCountTemplate = yield* translations.translate(locale, "reviews.summary.count");
325
+ const classification = toClassification(summaryRating);
326
+ const summary = {
327
+ rating: renderTemplate(summaryRatingTemplate, {
328
+ rating: summaryRating.toString(),
329
+ maxRating: "5"
330
+ }),
331
+ count: renderTemplate(summaryCountTemplate, { count: parsedItems.length.toString() }),
332
+ classification: yield* translations.translate(locale, `reviews.summary.classification.${classification}`)
333
+ };
334
+ const authorTemplate = yield* translations.translate(locale, "reviews.item.author");
335
+ const authorWithDateTemplate = yield* translations.translate(locale, "reviews.item.authorWithDate");
336
+ return {
337
+ items: parsedItems.sort((left, right) => Number(right.highlighted) - Number(left.highlighted)).map(({ highlighted: _highlighted, rating, author, travelDate, ...item }) => ({
338
+ ...item,
339
+ author: travelDate == null ? renderTemplate(authorTemplate, { author }) : renderTemplate(authorWithDateTemplate, {
340
+ author,
341
+ date: travelDate
342
+ }),
343
+ rating: rating.toString()
344
+ })),
345
+ summary
346
+ };
347
+ });
348
+ //#endregion
349
+ //#region src/adapters/v9/parser/rentals/to-rental-list-item.ts
350
+ const toRentalRentalsOutput = ({ rental, locale, translations, imageProxyBaseUrl, rentalHighlightPrioritization, customAttributeFilterDefinitions }) => Effect.gen(function* () {
351
+ const data = parseVofficeUnitData(rental.data);
352
+ const rawRentalType = data.known.type?.trim();
353
+ const translatedRentalType = rawRentalType != null && rawRentalType.length > 0 ? yield* toRentalType({
354
+ locale,
355
+ translations,
356
+ rentalType: rawRentalType
357
+ }) : void 0;
358
+ const address = toAddress(data, locale);
359
+ const description = toDescription(data, locale);
360
+ const images = toImages(imageProxyBaseUrl, rental, locale, data);
361
+ const location = toLocation(data.known.loc);
362
+ const attributes = yield* toRentalAttributes({
363
+ locale,
364
+ translations,
365
+ attributes: data,
366
+ customAttributeFilterDefinitions
367
+ });
368
+ const vicinity = yield* toRentalVicinity({
369
+ locale,
370
+ translations,
371
+ attributes: data
372
+ });
373
+ const highlights = yield* toRentalHighlights({
374
+ locale,
375
+ translations,
376
+ highlightPrioritization: rentalHighlightPrioritization,
377
+ customAttributeFilterDefinitions,
378
+ attributes: data
379
+ });
380
+ const reviews = yield* toReviews({
381
+ locale,
382
+ translations,
383
+ rental
384
+ });
385
+ const item = {
386
+ id: rental.voffice_id.toString(),
387
+ nameOrLabel: typeof data.known.name === "string" ? data.known.name : rental.name,
388
+ timeZone: "Europe/Berlin"
389
+ };
390
+ if (translatedRentalType != null) item.rentalType = translatedRentalType;
391
+ if (location != null) item.location = location;
392
+ if (Object.keys(address).length > 0) item.address = address;
393
+ if (Object.keys(description).length > 0) item.description = description;
394
+ if (images.length > 0) item.images = images;
395
+ if (attributes != null) item.attributes = attributes;
396
+ if (vicinity != null) item.vicinity = vicinity;
397
+ if (highlights != null) item.highlights = highlights;
398
+ if (rental.voffice_facility != null) item.property = toProperty(rental.voffice_facility, locale);
399
+ if (reviews != null) item.reviews = reviews;
400
+ return item;
401
+ });
402
+ //#endregion
403
+ export { localeToLanguage, toRentalRentalsOutput };