@revolugo/common 6.10.8 → 6.10.9-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.
- package/package.json +2 -6
- package/src/cancellation-policies.ts +27 -19
- package/src/constants/index.ts +0 -1
- package/src/countries/{constants.ts → countries.ts} +2 -36
- package/src/{constants/countries.ts → countries/country.ts} +0 -1
- package/src/countries/europe.ts +35 -0
- package/src/countries/index.ts +4 -7
- package/src/{types/country.ts → countries/types.ts} +1 -1
- package/src/currencies/index.ts +1 -1
- package/src/types/elements/contact-person.ts +2 -1
- package/src/types/elements/currency.ts +2 -1
- package/src/types/elements/hotel-offer.ts +2 -1
- package/src/types/elements/hotel-room-offer.ts +2 -1
- package/src/types/elements/index.ts +1 -0
- package/src/types/geo-coordinates.ts +4 -0
- package/src/types/index.ts +1 -1
- package/src/utils/{math.ts → amount-from-percentage.ts} +0 -10
- package/src/utils/case-transformers.ts +0 -1
- package/src/utils/chunk.ts +7 -0
- package/src/utils/colors.ts +4 -1
- package/src/utils/compact-object.ts +7 -0
- package/src/utils/compact.ts +28 -0
- package/src/utils/compute-margin-rate.ts +13 -0
- package/src/utils/compute-selling-price.ts +12 -0
- package/src/utils/dates.ts +10 -2
- package/src/utils/dayjs.ts +2 -0
- package/src/utils/defaults-deep.ts +85 -0
- package/src/utils/delay.ts +5 -0
- package/src/utils/generate-numbers-from-str.ts +15 -0
- package/src/utils/generate-pseudo-random-string.ts +3 -0
- package/src/utils/get-guest-count.ts +1 -2
- package/src/utils/get-random-element-from-array.ts +10 -0
- package/src/utils/get-random-hex-color.ts +5 -0
- package/src/utils/{random.ts → get-random-int.ts} +0 -6
- package/src/utils/index.ts +31 -12
- package/src/utils/key-by.ts +25 -0
- package/src/utils/merge.ts +140 -0
- package/src/utils/omit-by.ts +37 -0
- package/src/utils/omit.ts +39 -0
- package/src/utils/pick.ts +12 -0
- package/src/utils/poller.ts +11 -13
- package/src/utils/{strings.ts → prepare-ts-query.ts} +0 -6
- package/src/utils/{promise-tools.ts → promise-timeout.ts} +0 -6
- package/src/utils/random-int.ts +3 -0
- package/src/utils/shuffle-array.ts +13 -0
- package/src/utils/{array-tools.ts → sort-by.ts} +2 -93
- package/src/utils/sum-by.ts +23 -0
- package/src/utils/uniq-with.ts +16 -0
- package/src/utils/validators.ts +0 -1
- package/src/utils/weighted-mean.ts +9 -0
- package/src/utils/numbers.ts +0 -46
- package/src/utils/object-tools.ts +0 -126
- /package/src/utils/{value-tools.ts → is-nil.ts} +0 -0
- /package/src/utils/{children-tools.ts → parse-children.ts} +0 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@revolugo/common",
|
|
3
|
-
"version": "6.10.
|
|
3
|
+
"version": "6.10.9-beta.1",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "Revolugo common",
|
|
6
6
|
"author": "Revolugo",
|
|
@@ -8,9 +8,6 @@
|
|
|
8
8
|
"files": [
|
|
9
9
|
"src"
|
|
10
10
|
],
|
|
11
|
-
"imports": {
|
|
12
|
-
"#constants": "./src/constants/index.ts"
|
|
13
|
-
},
|
|
14
11
|
"exports": {
|
|
15
12
|
"./cancellation-policies": "./src/cancellation-policies.ts",
|
|
16
13
|
"./countries": "./src/countries/index.ts",
|
|
@@ -25,8 +22,7 @@
|
|
|
25
22
|
"dependencies": {
|
|
26
23
|
"change-case": "5.4.4",
|
|
27
24
|
"dayjs": "1.11.18",
|
|
28
|
-
"ky": "1.
|
|
29
|
-
"lodash-es": "4.17.21",
|
|
25
|
+
"ky": "1.11.0",
|
|
30
26
|
"slugify": "1.6.6",
|
|
31
27
|
"uuid": "13.0.0"
|
|
32
28
|
},
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { compact } from './utils/
|
|
1
|
+
import { compact } from './utils/compact.ts'
|
|
2
2
|
import { type Dayjs, dayjs } from './utils/dayjs.ts'
|
|
3
3
|
import { isEmpty } from './utils/is-empty.ts'
|
|
4
4
|
|
|
@@ -169,6 +169,7 @@ export function sanitizeCancellationPolicies({
|
|
|
169
169
|
|
|
170
170
|
if (
|
|
171
171
|
releaseDatetimeUTC &&
|
|
172
|
+
compactedCancellationPolicies[0] &&
|
|
172
173
|
dayjs(releaseDatetimeUTC).isBetween(
|
|
173
174
|
dayjs(bookingDatetimeUTC),
|
|
174
175
|
dayjs(compactedCancellationPolicies[0].dateFrom),
|
|
@@ -176,6 +177,7 @@ export function sanitizeCancellationPolicies({
|
|
|
176
177
|
'[)',
|
|
177
178
|
)
|
|
178
179
|
) {
|
|
180
|
+
const firstPolicyDateFrom = compactedCancellationPolicies[0].dateFrom
|
|
179
181
|
compactedCancellationPolicies.push(
|
|
180
182
|
{
|
|
181
183
|
dateFrom: bookingDatetimeUTC,
|
|
@@ -184,7 +186,7 @@ export function sanitizeCancellationPolicies({
|
|
|
184
186
|
},
|
|
185
187
|
{
|
|
186
188
|
dateFrom: releaseDatetimeUTC,
|
|
187
|
-
dateTo:
|
|
189
|
+
dateTo: firstPolicyDateFrom,
|
|
188
190
|
penaltyPercentage: 100,
|
|
189
191
|
},
|
|
190
192
|
)
|
|
@@ -268,11 +270,14 @@ export function sanitizeCancellationPolicies({
|
|
|
268
270
|
!dayjs(cp.dateFrom).isSameOrAfter(nextDayCheckInDateUTC),
|
|
269
271
|
)
|
|
270
272
|
|
|
271
|
-
sanitizedCancellationPolicies[0]
|
|
273
|
+
if (sanitizedCancellationPolicies[0]) {
|
|
274
|
+
sanitizedCancellationPolicies[0].dateFrom = bookingDatetimeUTC
|
|
275
|
+
}
|
|
272
276
|
|
|
273
|
-
sanitizedCancellationPolicies
|
|
274
|
-
|
|
275
|
-
|
|
277
|
+
const lastIndex = sanitizedCancellationPolicies.length - 1
|
|
278
|
+
if (sanitizedCancellationPolicies[lastIndex]) {
|
|
279
|
+
sanitizedCancellationPolicies[lastIndex].dateTo = nextDayCheckInDateUTC
|
|
280
|
+
}
|
|
276
281
|
|
|
277
282
|
return sanitizedCancellationPolicies
|
|
278
283
|
}
|
|
@@ -340,10 +345,7 @@ export function getPenaltyPercentage({
|
|
|
340
345
|
date: datetime,
|
|
341
346
|
})
|
|
342
347
|
|
|
343
|
-
return
|
|
344
|
-
matchingCancellationPolicies[0] &&
|
|
345
|
-
matchingCancellationPolicies[0].penaltyPercentage
|
|
346
|
-
)
|
|
348
|
+
return matchingCancellationPolicies[0]?.penaltyPercentage ?? 0
|
|
347
349
|
}
|
|
348
350
|
|
|
349
351
|
export function getCurrentPenaltyPercentage({
|
|
@@ -383,7 +385,7 @@ export function isBetterCancellationPolicies(
|
|
|
383
385
|
return false
|
|
384
386
|
}
|
|
385
387
|
|
|
386
|
-
if (!newVal[0]
|
|
388
|
+
if (!newVal[0]?.dateTo) {
|
|
387
389
|
return oldVal.every(oldCancellationPolicy => {
|
|
388
390
|
const relatedNewCancellationPolicy = newVal.find(newCancellationPolicy =>
|
|
389
391
|
dayjs(oldCancellationPolicy.dateTo).isBetween(
|
|
@@ -399,8 +401,8 @@ export function isBetterCancellationPolicies(
|
|
|
399
401
|
}
|
|
400
402
|
|
|
401
403
|
return (
|
|
402
|
-
oldCancellationPolicy.penaltyPercentage
|
|
403
|
-
relatedNewCancellationPolicy.penaltyPercentage
|
|
404
|
+
oldCancellationPolicy.penaltyPercentage >=
|
|
405
|
+
relatedNewCancellationPolicy.penaltyPercentage
|
|
404
406
|
)
|
|
405
407
|
})
|
|
406
408
|
}
|
|
@@ -420,8 +422,8 @@ export function isBetterCancellationPolicies(
|
|
|
420
422
|
}
|
|
421
423
|
|
|
422
424
|
return (
|
|
423
|
-
oldCancellationPolicy.penaltyPercentage
|
|
424
|
-
relatedNewCancellationPolicy.penaltyPercentage
|
|
425
|
+
oldCancellationPolicy.penaltyPercentage >=
|
|
426
|
+
relatedNewCancellationPolicy.penaltyPercentage
|
|
425
427
|
)
|
|
426
428
|
})
|
|
427
429
|
}
|
|
@@ -470,11 +472,17 @@ export function getSanitizedCancellationPolicies({
|
|
|
470
472
|
export function getCurrentCancellationPolicy(
|
|
471
473
|
sanitizedCancellationPolicies: ICancellationPolicy[],
|
|
472
474
|
): ICancellationPolicy {
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
dayjs().isBetween(dayjs(policy.dateFrom), dayjs(policy.dateTo)),
|
|
476
|
-
) || sanitizedCancellationPolicies[0]
|
|
475
|
+
const found = sanitizedCancellationPolicies.find(policy =>
|
|
476
|
+
dayjs().isBetween(dayjs(policy.dateFrom), dayjs(policy.dateTo)),
|
|
477
477
|
)
|
|
478
|
+
if (found) {
|
|
479
|
+
return found
|
|
480
|
+
}
|
|
481
|
+
const first = sanitizedCancellationPolicies[0]
|
|
482
|
+
if (!first) {
|
|
483
|
+
throw new Error('No cancellation policies available')
|
|
484
|
+
}
|
|
485
|
+
return first
|
|
478
486
|
}
|
|
479
487
|
|
|
480
488
|
function adjustIfFullHour(date: string): Dayjs {
|
package/src/constants/index.ts
CHANGED
|
@@ -1,41 +1,8 @@
|
|
|
1
|
-
|
|
2
|
-
import { CountryIso2Code } from '../constants/countries.ts'
|
|
1
|
+
/* eslint-disable max-lines */
|
|
3
2
|
import { Currency } from '../constants/currencies.ts'
|
|
4
3
|
|
|
5
|
-
import type { Country } from '
|
|
4
|
+
import type { Country } from './types.ts'
|
|
6
5
|
|
|
7
|
-
/* @__PURE__ */
|
|
8
|
-
export const EU_COUNTRIES_ISO2_CODE = new Set<string>([
|
|
9
|
-
CountryIso2Code.AT,
|
|
10
|
-
CountryIso2Code.BE,
|
|
11
|
-
CountryIso2Code.BG,
|
|
12
|
-
CountryIso2Code.CY,
|
|
13
|
-
CountryIso2Code.CZ,
|
|
14
|
-
CountryIso2Code.DE,
|
|
15
|
-
CountryIso2Code.DK,
|
|
16
|
-
CountryIso2Code.EE,
|
|
17
|
-
CountryIso2Code.ES,
|
|
18
|
-
CountryIso2Code.FI,
|
|
19
|
-
CountryIso2Code.FR,
|
|
20
|
-
CountryIso2Code.GR,
|
|
21
|
-
CountryIso2Code.HR,
|
|
22
|
-
CountryIso2Code.HU,
|
|
23
|
-
CountryIso2Code.IE,
|
|
24
|
-
CountryIso2Code.IT,
|
|
25
|
-
CountryIso2Code.LT,
|
|
26
|
-
CountryIso2Code.LU,
|
|
27
|
-
CountryIso2Code.LV,
|
|
28
|
-
CountryIso2Code.MT,
|
|
29
|
-
CountryIso2Code.NL,
|
|
30
|
-
CountryIso2Code.PL,
|
|
31
|
-
CountryIso2Code.PT,
|
|
32
|
-
CountryIso2Code.RO,
|
|
33
|
-
CountryIso2Code.SE,
|
|
34
|
-
CountryIso2Code.SI,
|
|
35
|
-
CountryIso2Code.SK,
|
|
36
|
-
])
|
|
37
|
-
|
|
38
|
-
/* @__PURE__ */
|
|
39
6
|
export const COUNTRIES: Record<string, Country> = {
|
|
40
7
|
AD: {
|
|
41
8
|
areaCodes: null,
|
|
@@ -2512,7 +2479,6 @@ export const COUNTRIES: Record<string, Country> = {
|
|
|
2512
2479
|
},
|
|
2513
2480
|
}
|
|
2514
2481
|
|
|
2515
|
-
/* @__PURE__ */
|
|
2516
2482
|
export const ISO_COUNTRIES = Object.values(COUNTRIES).map(
|
|
2517
2483
|
country => country.iso2,
|
|
2518
2484
|
)
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { CountryIso2Code } from './country.ts'
|
|
2
|
+
|
|
3
|
+
export const EU_COUNTRIES_ISO2_CODE = new Set<string>([
|
|
4
|
+
CountryIso2Code.AT,
|
|
5
|
+
CountryIso2Code.BE,
|
|
6
|
+
CountryIso2Code.BG,
|
|
7
|
+
CountryIso2Code.CY,
|
|
8
|
+
CountryIso2Code.CZ,
|
|
9
|
+
CountryIso2Code.DE,
|
|
10
|
+
CountryIso2Code.DK,
|
|
11
|
+
CountryIso2Code.EE,
|
|
12
|
+
CountryIso2Code.ES,
|
|
13
|
+
CountryIso2Code.FI,
|
|
14
|
+
CountryIso2Code.FR,
|
|
15
|
+
CountryIso2Code.GR,
|
|
16
|
+
CountryIso2Code.HR,
|
|
17
|
+
CountryIso2Code.HU,
|
|
18
|
+
CountryIso2Code.IE,
|
|
19
|
+
CountryIso2Code.IT,
|
|
20
|
+
CountryIso2Code.LT,
|
|
21
|
+
CountryIso2Code.LU,
|
|
22
|
+
CountryIso2Code.LV,
|
|
23
|
+
CountryIso2Code.MT,
|
|
24
|
+
CountryIso2Code.NL,
|
|
25
|
+
CountryIso2Code.PL,
|
|
26
|
+
CountryIso2Code.PT,
|
|
27
|
+
CountryIso2Code.RO,
|
|
28
|
+
CountryIso2Code.SE,
|
|
29
|
+
CountryIso2Code.SI,
|
|
30
|
+
CountryIso2Code.SK,
|
|
31
|
+
])
|
|
32
|
+
|
|
33
|
+
export function isInEu(countryCode: string): boolean {
|
|
34
|
+
return EU_COUNTRIES_ISO2_CODE.has(countryCode)
|
|
35
|
+
}
|
package/src/countries/index.ts
CHANGED
|
@@ -1,7 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
export * from './
|
|
4
|
-
|
|
5
|
-
export function isInEu(countryCode: string): boolean {
|
|
6
|
-
return EU_COUNTRIES_ISO2_CODE.has(countryCode)
|
|
7
|
-
}
|
|
1
|
+
export * from './countries.ts'
|
|
2
|
+
export * from './country.ts'
|
|
3
|
+
export * from './europe.ts'
|
|
4
|
+
export type * from './types.ts'
|
package/src/currencies/index.ts
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import type { Currency } from '#constants'
|
|
2
1
|
import type { Amenities } from './amenity.ts'
|
|
3
2
|
import type { HotelImage } from './hotel-image.ts'
|
|
4
3
|
import type { HotelImages } from './hotel-images.ts'
|
|
@@ -6,6 +5,8 @@ import type { HotelReviewRating } from './hotel-review-rating.ts'
|
|
|
6
5
|
import type { HotelRoomOffer } from './hotel-room-offer.ts'
|
|
7
6
|
import type { Tag } from './tag.ts'
|
|
8
7
|
import type { TravelTimeItem } from './travel-times.ts'
|
|
8
|
+
// eslint-disable-next-line no-restricted-imports
|
|
9
|
+
import type { Currency } from '../../currencies/index.ts'
|
|
9
10
|
|
|
10
11
|
export interface HotelOffer {
|
|
11
12
|
/**
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import type { BreakfastOptionType } from '#constants'
|
|
2
1
|
import type { CancellationPolicy } from './cancellation-policy.ts'
|
|
3
2
|
import type { CurrencyType } from './currency.ts'
|
|
4
3
|
import type { HotelRoomOfferPackageType } from './hotel-room-offer-package-type.ts'
|
|
@@ -7,6 +6,8 @@ import type { HotelRoom } from './hotel-room.ts'
|
|
|
7
6
|
import type { SourceMarket } from './source-market.ts'
|
|
8
7
|
import type { Tag } from './tag.ts'
|
|
9
8
|
import type { Tax } from './tax.ts'
|
|
9
|
+
// eslint-disable-next-line no-restricted-imports
|
|
10
|
+
import type { BreakfastOptionType } from '../../constants/index.ts'
|
|
10
11
|
|
|
11
12
|
/**
|
|
12
13
|
* Description of the Hotel Room Offer.
|
package/src/types/index.ts
CHANGED
|
@@ -2,10 +2,10 @@ export type * from './api.ts'
|
|
|
2
2
|
export * from './booking.ts'
|
|
3
3
|
export type * from './calendar.ts'
|
|
4
4
|
export type * from './cancellation-policy.ts'
|
|
5
|
-
export type * from './country.ts'
|
|
6
5
|
export type * from './currency.ts'
|
|
7
6
|
export type * from './date.ts'
|
|
8
7
|
export type * from './event.ts'
|
|
8
|
+
export type * from './geo-coordinates.ts'
|
|
9
9
|
export type * from './hotel-contract.ts'
|
|
10
10
|
export type * from './hotel-room-stock.ts'
|
|
11
11
|
export type * from './money-object.ts'
|
|
@@ -1,13 +1,3 @@
|
|
|
1
|
-
export function weightedMean(values: number[], weights: number[]): number {
|
|
2
|
-
const sum = values.reduce(
|
|
3
|
-
(acc: number, val: number, i: number) => acc + val * weights[i],
|
|
4
|
-
0,
|
|
5
|
-
)
|
|
6
|
-
const sumWeights = weights.reduce((acc: number, val: number) => acc + val, 0)
|
|
7
|
-
|
|
8
|
-
return sum / sumWeights
|
|
9
|
-
}
|
|
10
|
-
|
|
11
1
|
export function amountFromPercentage(
|
|
12
2
|
percentage: number,
|
|
13
3
|
amount: number,
|
package/src/utils/colors.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { generateNumbersFromStr } from './numbers.ts'
|
|
1
|
+
import { generateNumbersFromStr } from './generate-numbers-from-str.ts'
|
|
2
2
|
|
|
3
3
|
export function generateRandomColorFromString(
|
|
4
4
|
str: string,
|
|
@@ -9,6 +9,9 @@ export function generateRandomColorFromString(
|
|
|
9
9
|
const hueRange = 5
|
|
10
10
|
const randomIndex = Math.floor(num1 * existingColors.length)
|
|
11
11
|
const baseColor = existingColors[randomIndex]
|
|
12
|
+
if (!baseColor) {
|
|
13
|
+
throw new Error('Base color not found')
|
|
14
|
+
}
|
|
12
15
|
// extract the hue value from the base color
|
|
13
16
|
const baseHue = Number.parseInt(baseColor.slice(1, 3), 16)
|
|
14
17
|
const minHue = Math.max(0, baseHue - hueRange)
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
export function compact<T>(
|
|
2
|
+
array: (T | null | undefined)[] | null | undefined,
|
|
3
|
+
): Exclude<T, null | undefined>[] {
|
|
4
|
+
const length = array === null || array === undefined ? 0 : array.length
|
|
5
|
+
let index = -1
|
|
6
|
+
let resIndex = 0
|
|
7
|
+
const result: Exclude<T, null | undefined>[] = []
|
|
8
|
+
|
|
9
|
+
while (index < length) {
|
|
10
|
+
index += 1
|
|
11
|
+
if (array !== null && array !== undefined) {
|
|
12
|
+
const value = array[index]
|
|
13
|
+
|
|
14
|
+
if (
|
|
15
|
+
value !== null &&
|
|
16
|
+
value !== undefined &&
|
|
17
|
+
value !== false &&
|
|
18
|
+
value !== 0 &&
|
|
19
|
+
value !== ''
|
|
20
|
+
) {
|
|
21
|
+
result[resIndex] = value as Exclude<T, null | undefined>
|
|
22
|
+
resIndex += 1
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
return result
|
|
28
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export function computeMarginRate(
|
|
2
|
+
buyingPrice: number | string,
|
|
3
|
+
sellingPrice: number | string,
|
|
4
|
+
): number {
|
|
5
|
+
const buyingPriceNum = Number(buyingPrice)
|
|
6
|
+
const sellingPriceNum = Number(sellingPrice)
|
|
7
|
+
|
|
8
|
+
if (Number.isNaN(sellingPriceNum) || Number.isNaN(buyingPriceNum)) {
|
|
9
|
+
throw new TypeError('sellingPrice or buyingPrice is NaN')
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
return Number((1 - buyingPriceNum / sellingPriceNum).toPrecision(10))
|
|
13
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export function computeSellingPrice(
|
|
2
|
+
buyingPrice: number | string | null = 0,
|
|
3
|
+
rate: number | string | null = 0,
|
|
4
|
+
): number {
|
|
5
|
+
if (Number.isNaN(Number(buyingPrice)) || Number.isNaN(Number(rate))) {
|
|
6
|
+
throw new TypeError('price or rate is NaN')
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
const sellingPrice = Number(buyingPrice) / (1 - Number(rate))
|
|
10
|
+
|
|
11
|
+
return Math.ceil(Math.floor(sellingPrice * 100) / 100)
|
|
12
|
+
}
|
package/src/utils/dates.ts
CHANGED
|
@@ -44,14 +44,22 @@ export function sanitizeDateRange(
|
|
|
44
44
|
if (sortedDates.length === 2) {
|
|
45
45
|
return sortedDates as [string, string]
|
|
46
46
|
} else if (sortedDates.length === 1) {
|
|
47
|
-
|
|
47
|
+
const firstDate = sortedDates[0]
|
|
48
|
+
if (!firstDate) {
|
|
48
49
|
return [
|
|
49
50
|
dayjs().format('YYYY-MM-DD'),
|
|
50
51
|
dayjs().add(1, 'day').format('YYYY-MM-DD'),
|
|
51
52
|
]
|
|
52
53
|
}
|
|
53
54
|
|
|
54
|
-
|
|
55
|
+
if (firstDate === dayjs().format('YYYY-MM-DD')) {
|
|
56
|
+
return [
|
|
57
|
+
dayjs().format('YYYY-MM-DD'),
|
|
58
|
+
dayjs().add(1, 'day').format('YYYY-MM-DD'),
|
|
59
|
+
]
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
return [dayjs().format('YYYY-MM-DD'), firstDate]
|
|
55
63
|
}
|
|
56
64
|
|
|
57
65
|
return [
|
package/src/utils/dayjs.ts
CHANGED
|
@@ -4,6 +4,7 @@ import advancedFormat from 'dayjs/plugin/advancedFormat.js'
|
|
|
4
4
|
import isBetween from 'dayjs/plugin/isBetween.js'
|
|
5
5
|
import isSameOrAfter from 'dayjs/plugin/isSameOrAfter.js'
|
|
6
6
|
import isSameOrBefore from 'dayjs/plugin/isSameOrBefore.js'
|
|
7
|
+
import localeData from 'dayjs/plugin/localeData.js'
|
|
7
8
|
import localizedFormat from 'dayjs/plugin/localizedFormat.js'
|
|
8
9
|
import minMax from 'dayjs/plugin/minMax.js'
|
|
9
10
|
import relativeTime from 'dayjs/plugin/relativeTime.js'
|
|
@@ -22,6 +23,7 @@ dayjs.extend(isBetween)
|
|
|
22
23
|
dayjs.extend(isSameOrAfter)
|
|
23
24
|
dayjs.extend(isSameOrBefore)
|
|
24
25
|
dayjs.extend(localizedFormat)
|
|
26
|
+
dayjs.extend(localeData)
|
|
25
27
|
dayjs.extend(minMax)
|
|
26
28
|
dayjs.extend(relativeTime)
|
|
27
29
|
dayjs.extend(timezone)
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Drop-in native TypeScript replacement for `lodash-es` `defaultsDeep`.
|
|
3
|
+
*
|
|
4
|
+
* Behaviour matched (carefully):
|
|
5
|
+
* - Mutates and returns the first argument (target).
|
|
6
|
+
* - Assigns own and *inherited* enumerable **string-keyed** properties from sources
|
|
7
|
+
* to the target only when the target's property is `undefined`.
|
|
8
|
+
* - Recursively assigns (deep) when both target and source values are objects/arrays.
|
|
9
|
+
* - Does not copy symbol-keyed properties (matches the "string keyed" behaviour).
|
|
10
|
+
* - Preserves `null` (i.e. `null` is NOT considered `undefined` so it won't be overwritten).
|
|
11
|
+
* - Avoids infinite recursion for circular source structures.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
15
|
+
type AnyObject = Record<string, any>
|
|
16
|
+
|
|
17
|
+
function isObject(value: any): value is AnyObject {
|
|
18
|
+
return value !== null && typeof value === 'object'
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export function defaultsDeep<T>(
|
|
22
|
+
object: T,
|
|
23
|
+
...sources: any[]
|
|
24
|
+
): T extends AnyObject ? T : any {
|
|
25
|
+
// Ensure we always return an object reference we can mutate (lodash creates/uses {} when target is falsy)
|
|
26
|
+
const target: AnyObject = (
|
|
27
|
+
object === null || object === undefined ? {} : object
|
|
28
|
+
) as AnyObject
|
|
29
|
+
|
|
30
|
+
// WeakMap to track already-cloned source objects so circular refs won't blow the stack
|
|
31
|
+
const seen = new WeakMap<object, object>()
|
|
32
|
+
|
|
33
|
+
function applyDefaults(dst: AnyObject, src: any) {
|
|
34
|
+
// Only iterate string-keyed enumerable properties (own + inherited) => for...in
|
|
35
|
+
for (const key in src) {
|
|
36
|
+
if (Object.hasOwn(src, key)) {
|
|
37
|
+
const srcVal = src[key]
|
|
38
|
+
const dstVal = dst[key]
|
|
39
|
+
|
|
40
|
+
// only apply when destination is strictly `undefined` (lodash semantics)
|
|
41
|
+
if (dstVal === undefined) {
|
|
42
|
+
// eslint-disable-next-line max-depth
|
|
43
|
+
if (isObject(srcVal)) {
|
|
44
|
+
// If we've already cloned this source object (circular), reuse it
|
|
45
|
+
// eslint-disable-next-line max-depth
|
|
46
|
+
if (seen.has(srcVal)) {
|
|
47
|
+
dst[key] = seen.get(srcVal)
|
|
48
|
+
} else {
|
|
49
|
+
// Prepare new container (array vs object)
|
|
50
|
+
const created = Array.isArray(srcVal)
|
|
51
|
+
? ([] as unknown[])
|
|
52
|
+
: ({} as AnyObject)
|
|
53
|
+
// remember mapping to handle circular refs
|
|
54
|
+
seen.set(srcVal, created)
|
|
55
|
+
|
|
56
|
+
// Recursively fill created from srcVal
|
|
57
|
+
applyDefaults(created, srcVal)
|
|
58
|
+
|
|
59
|
+
// assign the created clone as the default
|
|
60
|
+
dst[key] = created
|
|
61
|
+
}
|
|
62
|
+
} else {
|
|
63
|
+
// Primitive or function: assign directly
|
|
64
|
+
dst[key] = srcVal
|
|
65
|
+
}
|
|
66
|
+
} else if (isObject(dstVal) && isObject(srcVal)) {
|
|
67
|
+
// destination already has a value (not undefined) and both are objects => recurse
|
|
68
|
+
// (do not overwrite existing non-undefined values)
|
|
69
|
+
// Note: do not treat arrays specially here; Array.isArray check only used when creating new value
|
|
70
|
+
applyDefaults(dstVal, srcVal)
|
|
71
|
+
}
|
|
72
|
+
// else: destination has a non-undefined primitive or non-object - skip (do not overwrite)
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
for (const src of sources) {
|
|
78
|
+
if (src !== null && src !== undefined) {
|
|
79
|
+
// If source itself has been seen (unlikely across top-level sources), we still traverse it
|
|
80
|
+
applyDefaults(target, src)
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
return target as any
|
|
85
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export function generateNumbersFromStr(str: string): [number, number] {
|
|
2
|
+
let hash = 5381
|
|
3
|
+
for (let i = 0; i < str.length; i++) {
|
|
4
|
+
// eslint-disable-next-line no-bitwise
|
|
5
|
+
hash = (hash << 5) + hash + str.charCodeAt(i)
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
// Generate two numbers using the hash
|
|
9
|
+
// eslint-disable-next-line no-bitwise
|
|
10
|
+
const num1 = (hash & 0xff_ff) / 0xff_ff
|
|
11
|
+
// eslint-disable-next-line no-bitwise
|
|
12
|
+
const num2 = ((hash >> 16) & 0xff_ff) / 0xff_ff
|
|
13
|
+
|
|
14
|
+
return [num1, num2]
|
|
15
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export function getRandomElementFromArray<T>(array: readonly T[]): T {
|
|
2
|
+
if (array.length === 0) {
|
|
3
|
+
throw new Error('Cannot get random element from empty array')
|
|
4
|
+
}
|
|
5
|
+
const element = array[Math.floor(Math.random() * array.length)]
|
|
6
|
+
if (element === undefined) {
|
|
7
|
+
throw new Error('Array element is undefined')
|
|
8
|
+
}
|
|
9
|
+
return element
|
|
10
|
+
}
|
|
@@ -4,9 +4,3 @@ export function getRandomInt(min: number, max: number): number {
|
|
|
4
4
|
|
|
5
5
|
return Math.floor(Math.random() * (roundedMax - roundedMin + 1)) + roundedMin
|
|
6
6
|
}
|
|
7
|
-
|
|
8
|
-
export function getRandomHexColor(): string {
|
|
9
|
-
const hex = Math.floor(Math.random() * 0xffffff).toString(16)
|
|
10
|
-
|
|
11
|
-
return hex.padStart(6, '0').toUpperCase()
|
|
12
|
-
}
|