@curless/hotel-storefront 0.50.3 → 0.50.5
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/dist/card.generated.d.ts +1 -1
- package/dist/card.generated.js +2 -2
- package/dist/card.generated.js.map +1 -1
- package/dist/demo-hotels.d.ts.map +1 -1
- package/dist/demo-hotels.js +9 -2
- package/dist/demo-hotels.js.map +1 -1
- package/dist/huazhu/backend.d.ts.map +1 -1
- package/dist/huazhu/backend.js +90 -23
- package/dist/huazhu/backend.js.map +1 -1
- package/dist/huazhu/sandbox.d.ts +7 -0
- package/dist/huazhu/sandbox.d.ts.map +1 -1
- package/dist/huazhu/sandbox.js +2 -2
- package/dist/huazhu/sandbox.js.map +1 -1
- package/dist/legal.d.ts.map +1 -1
- package/dist/legal.js +5 -1
- package/dist/legal.js.map +1 -1
- package/dist/present.d.ts +4 -0
- package/dist/present.d.ts.map +1 -1
- package/dist/present.js +8 -1
- package/dist/present.js.map +1 -1
- package/dist/server.d.ts +2 -2
- package/dist/server.js +2 -2
- package/dist/surface.js +2 -2
- package/dist/surface.js.map +1 -1
- package/dist/tools.d.ts +8 -0
- package/dist/tools.d.ts.map +1 -1
- package/dist/tools.js +131 -8
- package/dist/tools.js.map +1 -1
- package/dist/types.d.ts +21 -3
- package/dist/types.d.ts.map +1 -1
- package/package.json +1 -1
package/dist/huazhu/backend.js
CHANGED
|
@@ -191,6 +191,12 @@ const localIsoAt = (instant, tz) => {
|
|
|
191
191
|
.map((x) => [x.type, x.value]));
|
|
192
192
|
return `${p.year}-${p.month}-${p.day}T${p.hour}:${p.minute}`;
|
|
193
193
|
};
|
|
194
|
+
/**
|
|
195
|
+
* How a rate paid in advance is priced to be SHOWN where none is sold: as the
|
|
196
|
+
* test environment sells it with prepaid open (`OPN`), so the list is the same
|
|
197
|
+
* the day production opens it (robin, 2026-10-30: 「按照生产的标准来」).
|
|
198
|
+
*/
|
|
199
|
+
const SHOWN_AS = 'OPN';
|
|
194
200
|
/** A stay paid for - on the card page, or confirmed on UnionPay (whose one-time card Huazhu then charges). */
|
|
195
201
|
const paidInAdvance = (p) => p.method === 'unionpay' ? p.issuedAt !== undefined : p.paidAt !== undefined;
|
|
196
202
|
const pool = async (items, n, fn) => {
|
|
@@ -499,6 +505,30 @@ export const createHuazhuHotels = (opts) => {
|
|
|
499
505
|
/** Unpaid this long, the page is closed and the booking lapses - inside the card page's own ~31 minutes. */
|
|
500
506
|
const payWithin = opts.prepaid?.payWithinMs ?? 30 * 60_000;
|
|
501
507
|
const prepaidType = checkout ? 'OPN' : undefined;
|
|
508
|
+
/**
|
|
509
|
+
* One rate for one stay: sold here, or refused - and, refused only for being
|
|
510
|
+
* paid in advance where no payment is taken, asked again as `SHOWN_AS`.
|
|
511
|
+
*
|
|
512
|
+
* ⚠️ THE REFUSAL STAYS THE ANSWER (`res`). What a deployment that takes the
|
|
513
|
+
* money would sell goes in `shown`, to be listed; nothing here books it.
|
|
514
|
+
* What even that deployment would refuse (cancelling keeps part of the
|
|
515
|
+
* stay) is not shown, and keeps this refusal: "paid in advance" is what
|
|
516
|
+
* stops it here - "fewer nights may be bookable" would not be true here.
|
|
517
|
+
*/
|
|
518
|
+
const priceOne = (product, w, roomType, context) => {
|
|
519
|
+
const res = sell(product, w, roomType, context);
|
|
520
|
+
if (res.ok || res.kind !== 'prepaid' || context.prepaidType)
|
|
521
|
+
return { res };
|
|
522
|
+
const as = sell(product, w, roomType, { ...context, prepaidType: SHOWN_AS });
|
|
523
|
+
return as.ok ? { res, shown: as } : { res };
|
|
524
|
+
};
|
|
525
|
+
/** A rate found sold out, priced as if it had a room - as a deployment taking prepaid would show it. */
|
|
526
|
+
const openOne = (product, w, roomType, context) => sellIfOpen(product, w, roomType, context) ??
|
|
527
|
+
(context.prepaidType
|
|
528
|
+
? undefined
|
|
529
|
+
: sellIfOpen(product, w, roomType, { ...context, prepaidType: SHOWN_AS }));
|
|
530
|
+
/** What a priced rate is listed as: sold here, or shown (`Priced.shown`) - or nothing. */
|
|
531
|
+
const listed = (p) => p.res.ok ? p : p.shown ? { ...p, res: p.shown } : undefined;
|
|
502
532
|
const now = opts.now ?? (() => Date.now());
|
|
503
533
|
const sleep = opts.sleep ?? ((ms) => new Promise((r) => setTimeout(r, ms)));
|
|
504
534
|
const ariTtl = opts.ariTtlMs ?? 60_000;
|
|
@@ -807,13 +837,22 @@ export const createHuazhuHotels = (opts) => {
|
|
|
807
837
|
.map((product) => {
|
|
808
838
|
const roomType = (h.hotelRoomTypeList ?? []).find((r) => r.roomTypeCode === product.roomTypeCode);
|
|
809
839
|
const w = ari.get(roomIdOf(product));
|
|
810
|
-
const res = w
|
|
811
|
-
?
|
|
812
|
-
: {
|
|
840
|
+
const { res, shown } = w
|
|
841
|
+
? priceOne(product, w, roomType, context)
|
|
842
|
+
: {
|
|
843
|
+
res: { ok: false, kind: 'no-price', why: 'no price for those nights' },
|
|
844
|
+
shown: undefined,
|
|
845
|
+
};
|
|
813
846
|
const ifOpen = w && !res.ok && res.kind === 'sold-out'
|
|
814
|
-
?
|
|
847
|
+
? openOne(product, w, roomType, context)
|
|
815
848
|
: undefined;
|
|
816
|
-
return {
|
|
849
|
+
return {
|
|
850
|
+
product,
|
|
851
|
+
roomType,
|
|
852
|
+
res,
|
|
853
|
+
...(shown ? { shown } : {}),
|
|
854
|
+
...(ifOpen ? { ifOpen } : {}),
|
|
855
|
+
};
|
|
817
856
|
})
|
|
818
857
|
.filter((x) => !(x.roomType && x.roomType.status === 0));
|
|
819
858
|
};
|
|
@@ -898,12 +937,22 @@ export const createHuazhuHotels = (opts) => {
|
|
|
898
937
|
const roomType = roomTypeOf(product);
|
|
899
938
|
const w = ari.get(roomIdOf(product));
|
|
900
939
|
const hourly = hourlyWindow(product, date, clock);
|
|
901
|
-
|
|
902
|
-
?
|
|
903
|
-
: { ok: false, kind: 'no-price', why: 'no price for today' };
|
|
904
|
-
|
|
940
|
+
const priced = w
|
|
941
|
+
? priceOne(product, w, roomType, context)
|
|
942
|
+
: { res: { ok: false, kind: 'no-price', why: 'no price for today' } };
|
|
943
|
+
let { res } = priced;
|
|
944
|
+
let shown = priced.shown;
|
|
945
|
+
if ((res.ok || shown) && !hourly) {
|
|
905
946
|
res = { ok: false, kind: 'closed', why: goneWhy(product, date, clock) };
|
|
906
|
-
|
|
947
|
+
shown = undefined;
|
|
948
|
+
}
|
|
949
|
+
return {
|
|
950
|
+
product,
|
|
951
|
+
roomType,
|
|
952
|
+
res,
|
|
953
|
+
...(shown ? { shown } : {}),
|
|
954
|
+
...(hourly ? { hourly } : {}),
|
|
955
|
+
};
|
|
907
956
|
}),
|
|
908
957
|
...todays.filter((p) => !live.includes(p)).map(gone),
|
|
909
958
|
].filter((x) => !(x.roomType && x.roomType.status === 0));
|
|
@@ -1011,10 +1060,12 @@ export const createHuazhuHotels = (opts) => {
|
|
|
1011
1060
|
...(rateKindOf(p.product) ? { kind: rateKindOf(p.product) } : {}),
|
|
1012
1061
|
},
|
|
1013
1062
|
labels: rateLabels(p.product, { type: s.mealType, count: s.mealCount }),
|
|
1014
|
-
// ⚠️ PREPAID ONLY WHERE THIS DEPLOYMENT TAKES PAYMENT - a sandbox's
|
|
1015
|
-
// mode (`opts.prepaid
|
|
1016
|
-
//
|
|
1063
|
+
// ⚠️ PREPAID IS SOLD ONLY WHERE THIS DEPLOYMENT TAKES PAYMENT - a sandbox's
|
|
1064
|
+
// test mode (`opts.prepaid`, 总线程 2026-10-12). Everywhere else a prepaid
|
|
1065
|
+
// rate is one `priceOne` SHOWED: listed and marked as it is sold, and
|
|
1066
|
+
// not booked here (robin, 2026-10-30).
|
|
1017
1067
|
payment: s.prepaid ? 'prepaid' : 'at-hotel',
|
|
1068
|
+
...(s.prepaid && !prepaidType ? { notSoldHere: 'prepaid' } : {}),
|
|
1018
1069
|
...(hourly ? { hourly } : {}),
|
|
1019
1070
|
...(maxChildren > 0 && maxChildren < 99 ? { maxChildren } : {}),
|
|
1020
1071
|
};
|
|
@@ -1244,8 +1295,14 @@ export const createHuazhuHotels = (opts) => {
|
|
|
1244
1295
|
});
|
|
1245
1296
|
continue;
|
|
1246
1297
|
}
|
|
1298
|
+
// ⚠️ A RATE ONLY SHOWN IS NEVER BOOKED: `res` is its refusal (`priceOne`).
|
|
1247
1299
|
if (!p.res.ok) {
|
|
1248
|
-
dropped.push({
|
|
1300
|
+
dropped.push({
|
|
1301
|
+
title: `${hotelName} - ${roomTypeOf(p.product)}`,
|
|
1302
|
+
why: p.shown
|
|
1303
|
+
? 'This rate is paid in advance, and a stay paid in advance cannot be booked here yet - only rates paid at the hotel can.'
|
|
1304
|
+
: `${p.res.why}.`,
|
|
1305
|
+
});
|
|
1249
1306
|
continue;
|
|
1250
1307
|
}
|
|
1251
1308
|
if (hourly) {
|
|
@@ -3629,7 +3686,7 @@ export const createHuazhuHotels = (opts) => {
|
|
|
3629
3686
|
try {
|
|
3630
3687
|
const priced = await priceHourly(raw, stay.guests, wantRooms);
|
|
3631
3688
|
pricedRooms.set(raw.hotelId, priced);
|
|
3632
|
-
if (!priced.some((p) => p
|
|
3689
|
+
if (!priced.some((p) => listed(p)))
|
|
3633
3690
|
why.set(raw.hotelId, hourlyNoRoomsWhy(priced, stay.guests));
|
|
3634
3691
|
}
|
|
3635
3692
|
catch {
|
|
@@ -3650,7 +3707,7 @@ export const createHuazhuHotels = (opts) => {
|
|
|
3650
3707
|
try {
|
|
3651
3708
|
const priced = await priceStay(raw, stay, wantRooms);
|
|
3652
3709
|
pricedRooms.set(raw.hotelId, priced);
|
|
3653
|
-
if (!priced.some((p) => p
|
|
3710
|
+
if (!priced.some((p) => listed(p)))
|
|
3654
3711
|
why.set(raw.hotelId, noRoomsWhy(priced, stay, wantRooms));
|
|
3655
3712
|
}
|
|
3656
3713
|
catch (err) {
|
|
@@ -3669,8 +3726,9 @@ export const createHuazhuHotels = (opts) => {
|
|
|
3669
3726
|
if (known !== undefined)
|
|
3670
3727
|
return known;
|
|
3671
3728
|
let found = null;
|
|
3672
|
-
for (const
|
|
3673
|
-
|
|
3729
|
+
for (const q of pricedRooms.get(raw.hotelId) ?? []) {
|
|
3730
|
+
const p = listed(q);
|
|
3731
|
+
if (!p || !stay)
|
|
3674
3732
|
continue;
|
|
3675
3733
|
const room = toRoom(raw, p, stay);
|
|
3676
3734
|
if (over.freeCancellation && !room.freeCancellationUntil)
|
|
@@ -3795,6 +3853,8 @@ export const createHuazhuHotels = (opts) => {
|
|
|
3795
3853
|
...(b.room.hourly ? { hours: b.room.hourly.hours } : {}),
|
|
3796
3854
|
...(b.room.labels?.length ? { labels: b.room.labels } : {}),
|
|
3797
3855
|
...(b.room.meal ? { meal: b.room.meal } : {}),
|
|
3856
|
+
...(b.room.payment ? { payment: b.room.payment } : {}),
|
|
3857
|
+
...(b.room.notSoldHere ? { notSoldHere: b.room.notSoldHere } : {}),
|
|
3798
3858
|
},
|
|
3799
3859
|
};
|
|
3800
3860
|
};
|
|
@@ -3904,7 +3964,7 @@ export const createHuazhuHotels = (opts) => {
|
|
|
3904
3964
|
};
|
|
3905
3965
|
}
|
|
3906
3966
|
const rooms = priced
|
|
3907
|
-
.
|
|
3967
|
+
.flatMap((p) => listed(p) ?? [])
|
|
3908
3968
|
.map((p) => toRoom(raw, p, stay))
|
|
3909
3969
|
.sort((a, b) => a.stayTotal.minor - b.stayTotal.minor);
|
|
3910
3970
|
const soldOut = soldOutOf(raw, priced, stay);
|
|
@@ -3938,7 +3998,7 @@ export const createHuazhuHotels = (opts) => {
|
|
|
3938
3998
|
};
|
|
3939
3999
|
}
|
|
3940
4000
|
const rooms = priced
|
|
3941
|
-
.
|
|
4001
|
+
.flatMap((p) => listed(p) ?? [])
|
|
3942
4002
|
.map((p) => toRoom(raw, p, stay))
|
|
3943
4003
|
.sort((a, b) => a.stayTotal.minor - b.stayTotal.minor);
|
|
3944
4004
|
return {
|
|
@@ -3990,6 +4050,7 @@ export const createHuazhuHotels = (opts) => {
|
|
|
3990
4050
|
allowTags,
|
|
3991
4051
|
};
|
|
3992
4052
|
let cheapest;
|
|
4053
|
+
const day = [];
|
|
3993
4054
|
for (const product of products) {
|
|
3994
4055
|
if (!bookableNow(product.bookWindow, checkIn, context.clock))
|
|
3995
4056
|
continue;
|
|
@@ -3997,8 +4058,14 @@ export const createHuazhuHotels = (opts) => {
|
|
|
3997
4058
|
if (!w)
|
|
3998
4059
|
continue;
|
|
3999
4060
|
const roomType = (raw.hotelRoomTypeList ?? []).find((r) => r.roomTypeCode === product.roomTypeCode);
|
|
4000
|
-
|
|
4001
|
-
|
|
4061
|
+
// Priced as the hotel page lists it: a rate shown paid in advance
|
|
4062
|
+
// where none is sold has the price it is listed at (`priceOne`).
|
|
4063
|
+
const one = priceOne(product, w, roomType, context);
|
|
4064
|
+
// Why a day has none is read from its nights' rates, as the hotel page's.
|
|
4065
|
+
if (product.checkinType === 'DAY' && !(roomType && roomType.status === 0))
|
|
4066
|
+
day.push({ product, roomType, ...one });
|
|
4067
|
+
const res = one.res.ok ? one.res : one.shown;
|
|
4068
|
+
if (!res)
|
|
4002
4069
|
continue;
|
|
4003
4070
|
const total = money(res.after.reduce((a, b) => a + toMinor(b, res.currency), 0) * rooms, res.currency);
|
|
4004
4071
|
if (!cheapest || total.minor < cheapest.minor)
|
|
@@ -4007,7 +4074,7 @@ export const createHuazhuHotels = (opts) => {
|
|
|
4007
4074
|
out.push({
|
|
4008
4075
|
checkIn,
|
|
4009
4076
|
checkOut,
|
|
4010
|
-
...(cheapest ? { from: cheapest } : {}),
|
|
4077
|
+
...(cheapest ? { from: cheapest } : { why: noRoomsWhy(day, candidate, rooms).code }),
|
|
4011
4078
|
...(shift === 0 ? { chosen: true } : {}),
|
|
4012
4079
|
});
|
|
4013
4080
|
}
|