@revolugo/common 6.14.6 → 6.15.0-beta.1

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 (37) hide show
  1. package/package.json +14 -9
  2. package/src/amenities/index.ts +51 -0
  3. package/src/assets/index.ts +3 -0
  4. package/src/assets/placeholder-hotel.png +0 -0
  5. package/src/assets/placeholder-room.png +0 -0
  6. package/src/constants/hotel-room-offer.ts +11 -0
  7. package/src/constants/index.ts +2 -1
  8. package/src/constants/locales.ts +5 -5
  9. package/src/constants/tax.ts +9 -0
  10. package/src/icons/index.ts +2 -1
  11. package/src/schemas/cancellation-policies.ts +18 -0
  12. package/src/schemas/currency.ts +7 -0
  13. package/src/schemas/hotel-offer-request.ts +43 -0
  14. package/src/schemas/hotel-offer.ts +58 -0
  15. package/src/schemas/hotel-room-offer.ts +99 -0
  16. package/src/schemas/hotel-room.ts +106 -0
  17. package/src/schemas/hotel.ts +360 -0
  18. package/src/schemas/index.ts +10 -0
  19. package/src/schemas/list-polling-meta.ts +43 -0
  20. package/src/schemas/tag.ts +13 -0
  21. package/src/schemas/taxes.ts +34 -0
  22. package/src/types/elements/booking-flow.ts +6 -0
  23. package/src/types/elements/contact-person.ts +11 -0
  24. package/src/types/elements/elements-events.ts +84 -0
  25. package/src/types/elements/hotel-offer-request.ts +14 -15
  26. package/src/types/elements/hotel-offers-filters.ts +19 -0
  27. package/src/types/elements/hotel.ts +2 -0
  28. package/src/types/elements/index.ts +3 -0
  29. package/src/types/http-exception/base.exception.ts +1 -1
  30. package/src/types/severities.ts +1 -0
  31. package/src/utils/case-transformers.ts +5 -5
  32. package/src/utils/get-sanitized-room-count.ts +29 -0
  33. package/src/utils/images.ts +3 -3
  34. package/src/utils/index.ts +4 -0
  35. package/src/utils/is-object.ts +2 -0
  36. package/src/utils/keys-case-transformer.ts +118 -0
  37. package/src/utils/transform-schema-keys.ts +143 -0
@@ -7,7 +7,7 @@ import {
7
7
  } from 'change-case'
8
8
  import slugify from 'slugify'
9
9
 
10
- function slugCase(input: string) {
10
+ function slugCase(input: string): string {
11
11
  return slugify(kebabCase(input), {
12
12
  lower: true,
13
13
  strict: true,
@@ -23,7 +23,7 @@ export enum CaseTransformer {
23
23
  Snake = 'snakeCase',
24
24
  }
25
25
 
26
- const CASE_TRANSORMERS_MAPPING = {
26
+ export const CASE_TRANSFORMERS_MAPPING = {
27
27
  [CaseTransformer.Camel]: camelCase,
28
28
  [CaseTransformer.Capital]: capitalCase,
29
29
  [CaseTransformer.Param]: kebabCase,
@@ -38,11 +38,11 @@ export function changeCase<T extends string | string[]>(
38
38
  ): T extends string ? string : string[] {
39
39
  if (Array.isArray(input)) {
40
40
  return input.map(item =>
41
- CASE_TRANSORMERS_MAPPING[toCase](item),
41
+ CASE_TRANSFORMERS_MAPPING[toCase](item),
42
42
  ) as T extends string ? string : string[]
43
43
  }
44
44
 
45
- return CASE_TRANSORMERS_MAPPING[toCase](input) as T extends string
45
+ return CASE_TRANSFORMERS_MAPPING[toCase](input) as T extends string
46
46
  ? string
47
47
  : string[]
48
48
  }
@@ -85,7 +85,7 @@ export function keysChangeCase<T>(
85
85
  const transformedKey =
86
86
  options?.exclude && matches(options.exclude, key)
87
87
  ? key
88
- : CASE_TRANSORMERS_MAPPING[toCase](key)
88
+ : CASE_TRANSFORMERS_MAPPING[toCase](key)
89
89
 
90
90
  result[transformedKey] = options.deep
91
91
  ? keysChangeCase(obj[key], toCase, options)
@@ -0,0 +1,29 @@
1
+ const MAX_ADULTS_PER_ROOM = 2
2
+
3
+ export function getSanitizedRoomCount({
4
+ adultCount,
5
+ maxAdultsPerRoom = MAX_ADULTS_PER_ROOM,
6
+ roomCount,
7
+ }: {
8
+ adultCount: number
9
+ roomCount: number
10
+ maxAdultsPerRoom?: number
11
+ }): number {
12
+ // Ensure at least enough rooms for each adult and at most MAX_ADULTS_PER_ROOM per room
13
+ if (!Number.isFinite(roomCount) || roomCount < 1) {
14
+ return adultCount
15
+ }
16
+
17
+ const minRooms = Math.ceil(adultCount / maxAdultsPerRoom)
18
+ const maxRooms = adultCount
19
+
20
+ if (roomCount <= minRooms) {
21
+ return minRooms
22
+ }
23
+
24
+ if (roomCount >= maxRooms) {
25
+ return maxRooms
26
+ }
27
+
28
+ return roomCount
29
+ }
@@ -26,7 +26,7 @@ export function generateImageUrls(
26
26
  )
27
27
  }
28
28
 
29
- type ImageSize = 'm' | 'l' | 'xl'
29
+ type ImageSize = 'xs' | 's' | 'm' | 'l' | 'xl'
30
30
 
31
31
  function selectBestSize(
32
32
  hotelImage: HotelImage,
@@ -46,10 +46,10 @@ export const mapImagesUrls = (
46
46
  selectBestSize(hotelImage, ['xl', 'l', 'm']),
47
47
  ),
48
48
  lowres: hotelImages.map(hotelImage =>
49
- selectBestSize(hotelImage, ['l', 'xl', 'm']),
49
+ selectBestSize(hotelImage, ['l', 'm', 's']),
50
50
  ),
51
51
  thumbnails: hotelImages.map(hotelImage =>
52
- selectBestSize(hotelImage, ['m', 'l', 'xl']),
52
+ selectBestSize(hotelImage, ['m', 's', 'xs']),
53
53
  ),
54
54
  }
55
55
  }
@@ -1,6 +1,7 @@
1
1
  export * from './add-classes.ts'
2
2
  export * from './amount-from-percentage.ts'
3
3
  export * from './case-transformers.ts'
4
+ export * from './keys-case-transformer.ts'
4
5
  export * from './chunk.ts'
5
6
  export * from './colors.ts'
6
7
  export * from './compact-object.ts'
@@ -20,11 +21,13 @@ export * from './get-guest-count.ts'
20
21
  export * from './get-random-element-from-array.ts'
21
22
  export * from './get-random-hex-color.ts'
22
23
  export * from './get-random-int.ts'
24
+ export * from './get-sanitized-room-count.ts'
23
25
  export * from './group-by.ts'
24
26
  export * from './images.ts'
25
27
  export * from './is-empty.ts'
26
28
  export * from './is-equal.ts'
27
29
  export * from './is-nil.ts'
30
+ export * from './is-object.ts'
28
31
  export * from './key-by.ts'
29
32
  export * from './lang-default-fallbacks.ts'
30
33
  export * from './map-keys.ts'
@@ -45,6 +48,7 @@ export * from './sort-by.ts'
45
48
  export * from './sum-by.ts'
46
49
  export * from './sum.ts'
47
50
  export * from './to-boolean.ts'
51
+ export * from './transform-schema-keys.ts'
48
52
  export * from './uniq.ts'
49
53
  export * from './uniq-by.ts'
50
54
  export * from './uniq-with.ts'
@@ -0,0 +1,2 @@
1
+ export const isObject = (value: unknown): value is object =>
2
+ !!value && value.constructor === Object
@@ -0,0 +1,118 @@
1
+ import { type CaseTransformer, changeCase } from './case-transformers.ts'
2
+ import { isObject } from './is-object.ts'
3
+ import { mapKeys } from './map-keys.ts'
4
+
5
+ import type {
6
+ CamelCasedProperties,
7
+ CamelCasedPropertiesDeep,
8
+ KebabCasedProperties,
9
+ KebabCasedPropertiesDeep,
10
+ PascalCasedProperties,
11
+ PascalCasedPropertiesDeep,
12
+ SnakeCasedProperties,
13
+ SnakeCasedPropertiesDeep,
14
+ } from 'type-fest'
15
+
16
+ /**
17
+ * Maps CaseTransformer enum to the corresponding type-fest property transformation (shallow).
18
+ * Capital and Slug cases don't have type-fest equivalents, so they preserve the original type.
19
+ */
20
+ type TransformProperties<
21
+ T,
22
+ C extends CaseTransformer,
23
+ > = C extends CaseTransformer.Camel
24
+ ? CamelCasedProperties<T>
25
+ : C extends CaseTransformer.Snake
26
+ ? SnakeCasedProperties<T>
27
+ : C extends CaseTransformer.Pascal
28
+ ? PascalCasedProperties<T>
29
+ : C extends CaseTransformer.Param
30
+ ? KebabCasedProperties<T>
31
+ : T
32
+
33
+ /**
34
+ * Maps CaseTransformer enum to the corresponding type-fest deep property transformation.
35
+ * Capital and Slug cases don't have type-fest equivalents, so they preserve the original type.
36
+ */
37
+ type TransformPropertiesDeep<
38
+ T,
39
+ C extends CaseTransformer,
40
+ > = C extends CaseTransformer.Camel
41
+ ? CamelCasedPropertiesDeep<T>
42
+ : C extends CaseTransformer.Snake
43
+ ? SnakeCasedPropertiesDeep<T>
44
+ : C extends CaseTransformer.Pascal
45
+ ? PascalCasedPropertiesDeep<T>
46
+ : C extends CaseTransformer.Param
47
+ ? KebabCasedPropertiesDeep<T>
48
+ : T
49
+
50
+ /**
51
+ * Transforms object keys based on the specified case transformer.
52
+ * - Arrays: recursively applies transformation to each element
53
+ * - Objects: applies property transformation (shallow by default, deep if Deep=true)
54
+ * - Primitives: returns as-is
55
+ */
56
+ export type KeysCaseTransformed<
57
+ T,
58
+ C extends CaseTransformer,
59
+ Deep extends boolean = false,
60
+ > = Deep extends true
61
+ ? T extends readonly (infer U)[]
62
+ ? KeysCaseTransformed<U, C, true>[]
63
+ : T extends object
64
+ ? TransformPropertiesDeep<T, C>
65
+ : T
66
+ : T extends readonly (infer U)[]
67
+ ? KeysCaseTransformed<U, C>[]
68
+ : T extends object
69
+ ? TransformProperties<T, C>
70
+ : T
71
+
72
+ export interface KeysCaseTransformerOptions {
73
+ deep?: boolean
74
+ }
75
+
76
+ // Overload: deep transformation
77
+ export function keysCaseTransformer<T, C extends CaseTransformer>(
78
+ obj: T,
79
+ toCase: C,
80
+ options: { deep: true },
81
+ ): KeysCaseTransformed<T, C, true>
82
+
83
+ // Overload: shallow transformation (default)
84
+ export function keysCaseTransformer<T, C extends CaseTransformer>(
85
+ obj: T,
86
+ toCase: C,
87
+ options?: { deep?: false },
88
+ ): KeysCaseTransformed<T, C>
89
+
90
+ // Implementation
91
+ export function keysCaseTransformer<T, C extends CaseTransformer>(
92
+ obj: T,
93
+ toCase: C,
94
+ options?: KeysCaseTransformerOptions,
95
+ ): KeysCaseTransformed<T, C, boolean> {
96
+ if (Array.isArray(obj)) {
97
+ return obj.map(item =>
98
+ keysCaseTransformer(item, toCase, options as { deep: true }),
99
+ ) as KeysCaseTransformed<T, C, boolean>
100
+ }
101
+
102
+ if (isObject(obj)) {
103
+ const transformed = mapKeys(obj, key => changeCase(key, toCase))
104
+ if (options?.deep) {
105
+ // Recursively transform nested values
106
+ return Object.fromEntries(
107
+ Object.entries(transformed).map(([k, v]) => [
108
+ k,
109
+ keysCaseTransformer(v, toCase, options as { deep: true }),
110
+ ]),
111
+ ) as KeysCaseTransformed<T, C, boolean>
112
+ }
113
+
114
+ return transformed as KeysCaseTransformed<T, C, boolean>
115
+ }
116
+
117
+ return obj as KeysCaseTransformed<T, C, boolean>
118
+ }
@@ -0,0 +1,143 @@
1
+ import { type ZodTypeAny, z } from 'zod'
2
+
3
+ import {
4
+ CASE_TRANSFORMERS_MAPPING,
5
+ type CaseTransformer,
6
+ } from './case-transformers.ts'
7
+
8
+ /* eslint-disable no-underscore-dangle, max-statements */
9
+
10
+ function transformSchemaKeysWithTransformer(
11
+ schema: ZodTypeAny,
12
+ transformer: (str: string) => string,
13
+ ): ZodTypeAny {
14
+ let result: ZodTypeAny = schema
15
+
16
+ // Handle ZodObject
17
+ if (schema instanceof z.ZodObject) {
18
+ const shape = schema._def.shape()
19
+ const transformedShape: Record<string, ZodTypeAny> = {}
20
+
21
+ for (const key in shape) {
22
+ if (Object.hasOwn(shape, key)) {
23
+ const transformedKey = transformer(key)
24
+ transformedShape[transformedKey] = transformSchemaKeysWithTransformer(
25
+ shape[key],
26
+ transformer,
27
+ )
28
+ }
29
+ }
30
+
31
+ result = z.object(transformedShape) as ZodTypeAny
32
+ }
33
+
34
+ // Handle ZodArray
35
+ else if (schema instanceof z.ZodArray) {
36
+ const { type: elementSchema } = schema._def
37
+ result = z.array(
38
+ transformSchemaKeysWithTransformer(elementSchema, transformer),
39
+ )
40
+ }
41
+
42
+ // Handle ZodOptional
43
+ else if (schema instanceof z.ZodOptional) {
44
+ const { innerType: innerSchema } = schema._def
45
+ result = transformSchemaKeysWithTransformer(
46
+ innerSchema,
47
+ transformer,
48
+ ).optional()
49
+ }
50
+
51
+ // Handle ZodNullable
52
+ else if (schema instanceof z.ZodNullable) {
53
+ const { innerType: innerSchema } = schema._def
54
+ result = transformSchemaKeysWithTransformer(
55
+ innerSchema,
56
+ transformer,
57
+ ).nullable()
58
+ }
59
+
60
+ // Handle ZodDefault
61
+ else if (schema instanceof z.ZodDefault) {
62
+ const { defaultValue, innerType: innerSchema } = schema._def
63
+ result = transformSchemaKeysWithTransformer(
64
+ innerSchema,
65
+ transformer,
66
+ ).default(defaultValue())
67
+ }
68
+
69
+ // Handle ZodEffects (includes .refine(), .transform(), etc.)
70
+ else if (schema instanceof z.ZodEffects) {
71
+ const { schema: innerSchema } = schema._def
72
+ const transformedInner = transformSchemaKeysWithTransformer(
73
+ innerSchema,
74
+ transformer,
75
+ )
76
+
77
+ // We need to reconstruct the effects
78
+ // This is a simplified version - effects are not transformed, just passed through
79
+ result = transformedInner
80
+ }
81
+
82
+ // Handle ZodUnion
83
+ else if (schema instanceof z.ZodUnion) {
84
+ const { options } = schema._def
85
+ const transformedOptions = options.map((option: ZodTypeAny) =>
86
+ transformSchemaKeysWithTransformer(option, transformer),
87
+ )
88
+ result = z.union(
89
+ transformedOptions as [ZodTypeAny, ZodTypeAny, ...ZodTypeAny[]],
90
+ )
91
+ }
92
+
93
+ // Handle ZodIntersection
94
+ else if (schema instanceof z.ZodIntersection) {
95
+ const { left, right } = schema._def
96
+ result = z.intersection(
97
+ transformSchemaKeysWithTransformer(left, transformer),
98
+ transformSchemaKeysWithTransformer(right, transformer),
99
+ )
100
+ }
101
+
102
+ // Propagate openapi metadata
103
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
104
+ const { openapi } = schema._def as any
105
+ if (openapi && result !== schema) {
106
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
107
+ ;(result._def as any).openapi = openapi
108
+ }
109
+
110
+ return result
111
+ }
112
+
113
+ /**
114
+ * Transforms all keys in a Zod schema from one case to another (e.g., snake_case to camelCase).
115
+ * This function recursively transforms all nested schemas as well.
116
+ *
117
+ * @param schema - The Zod schema to transform
118
+ * @param toCase - The target case (e.g., CaseTransformer.Camel)
119
+ * @returns A new Zod schema with transformed keys
120
+ *
121
+ * @example
122
+ * ```ts
123
+ * const snakeSchema = z.object({
124
+ * first_name: z.string(),
125
+ * last_name: z.string(),
126
+ * })
127
+ *
128
+ * const camelSchema = transformSchemaKeys(snakeSchema, CaseTransformer.Camel)
129
+ * // Results in: { firstName: z.string(), lastName: z.string() }
130
+ * ```
131
+ */
132
+ export function transformSchemaKeys<T extends ZodTypeAny>(
133
+ schema: T,
134
+ toCase: CaseTransformer,
135
+ ): T {
136
+ const transformer = CASE_TRANSFORMERS_MAPPING[toCase]
137
+
138
+ if (!transformer) {
139
+ throw new Error(`Unsupported case transformer: ${toCase}`)
140
+ }
141
+
142
+ return transformSchemaKeysWithTransformer(schema, transformer) as T
143
+ }