@curless/hotel-storefront 0.30.3 → 0.30.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/backend.d.ts +7 -1
- package/dist/backend.d.ts.map +1 -1
- package/dist/backend.js +9 -3
- package/dist/backend.js.map +1 -1
- 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/dates.d.ts +11 -0
- package/dist/dates.d.ts.map +1 -1
- package/dist/dates.js +30 -9
- package/dist/dates.js.map +1 -1
- package/dist/demo-hotels.d.ts.map +1 -1
- package/dist/demo-hotels.js +42 -10
- package/dist/demo-hotels.js.map +1 -1
- package/dist/huazhu/backend.d.ts.map +1 -1
- package/dist/huazhu/backend.js +92 -31
- package/dist/huazhu/backend.js.map +1 -1
- package/dist/huazhu/sellable.d.ts +3 -1
- package/dist/huazhu/sellable.d.ts.map +1 -1
- package/dist/huazhu/sellable.js +22 -3
- package/dist/huazhu/sellable.js.map +1 -1
- package/dist/lib.d.ts +1 -1
- package/dist/lib.d.ts.map +1 -1
- package/dist/server.d.ts +2 -2
- package/dist/server.js +2 -2
- package/dist/tools.d.ts.map +1 -1
- package/dist/tools.js +13 -0
- package/dist/tools.js.map +1 -1
- package/dist/types.d.ts +61 -0
- package/dist/types.d.ts.map +1 -1
- package/package.json +1 -1
package/dist/huazhu/backend.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { randomBytes, randomUUID } from 'node:crypto';
|
|
2
|
-
import { DEFAULT_PAGE, MAX_PAGE, REQUEST_KINDS, REQUEST_NOTE_MAX, guestTooLong,
|
|
3
|
-
import { addDays, arrivalInstant, atHotelTime, nights as nightsBetween, stayProblem, todayAt, } from '../dates.js';
|
|
2
|
+
import { DEFAULT_PAGE, MAX_PAGE, REQUEST_KINDS, REQUEST_NOTE_MAX, guestTooLong, partyNeedsOf, } from '../backend.js';
|
|
3
|
+
import { addDays, arrivalInstant, atHotelTime, nights as nightsBetween, stayProblem, stayProblemOf, todayAt, } from '../dates.js';
|
|
4
4
|
import { byWords, splitByStay } from '../demo-hotels.js';
|
|
5
5
|
import { FACILITIES, hasFacility } from '../facilities.js';
|
|
6
6
|
import { endOf, startProblem } from '../hourly.js';
|
|
@@ -583,6 +583,7 @@ export const createHuazhuHotels = (opts) => {
|
|
|
583
583
|
ok: false,
|
|
584
584
|
kind: 'too-far',
|
|
585
585
|
why: `rooms here are on sale at most ${sellDays} days ahead`,
|
|
586
|
+
args: { days: sellDays },
|
|
586
587
|
},
|
|
587
588
|
}))
|
|
588
589
|
.filter((x) => !(x.roomType && x.roomType.status === 0));
|
|
@@ -799,6 +800,12 @@ export const createHuazhuHotels = (opts) => {
|
|
|
799
800
|
...(maxChildren > 0 && maxChildren < 99 ? { maxChildren } : {}),
|
|
800
801
|
};
|
|
801
802
|
};
|
|
803
|
+
/** A hotel's "nothing to book", as HotelDetail carries it. */
|
|
804
|
+
const noRoomsOf = (w) => ({
|
|
805
|
+
noRooms: w.why,
|
|
806
|
+
noRoomsCode: w.code,
|
|
807
|
+
...(w.args ? { noRoomsArgs: w.args } : {}),
|
|
808
|
+
});
|
|
802
809
|
/** The most a room here sleeps, from rates refused for the party ("sleeps 2"). */
|
|
803
810
|
const largestOf = (fails) => Math.max(...fails.map((f) => Number(/\d+/.exec(f.why)?.[0] ?? 0)));
|
|
804
811
|
/**
|
|
@@ -811,49 +818,84 @@ export const createHuazhuHotels = (opts) => {
|
|
|
811
818
|
*/
|
|
812
819
|
const hourlyNoRoomsWhy = (priced, guests) => {
|
|
813
820
|
if (priced.length === 0)
|
|
814
|
-
return 'This hotel sells no rooms by the hour today.';
|
|
821
|
+
return { why: 'This hotel sells no rooms by the hour today.', code: 'hourly-none' };
|
|
815
822
|
const fails = priced.map((p) => p.res).filter((r) => !r.ok);
|
|
816
823
|
if (fails.length > 0 && fails.every((f) => f.kind === 'party'))
|
|
817
|
-
return
|
|
824
|
+
return partyNeedsOf(guests, largestOf(fails));
|
|
818
825
|
if (fails.some((f) => f.kind === 'prepaid'))
|
|
819
|
-
return
|
|
826
|
+
return {
|
|
827
|
+
why: "This hotel's rooms by the hour are paid in advance, and this connector books rates paid at the hotel.",
|
|
828
|
+
code: 'prepaid-only',
|
|
829
|
+
};
|
|
820
830
|
const said = (re) => fails
|
|
821
831
|
.map((f) => (f.kind === 'closed' ? re.exec(f.why)?.[1] : undefined))
|
|
822
832
|
.filter((t) => !!t)
|
|
823
833
|
.sort();
|
|
824
834
|
const opens = said(/on sale today from (\d\d:\d\d)/)[0];
|
|
825
835
|
if (opens)
|
|
826
|
-
return
|
|
836
|
+
return {
|
|
837
|
+
why: `Rooms by the hour here go on sale today from ${opens}.`,
|
|
838
|
+
code: 'hourly-not-yet',
|
|
839
|
+
args: { opensAt: opens },
|
|
840
|
+
};
|
|
827
841
|
const last = said(/the last start today was (\d\d:\d\d)/).pop();
|
|
828
842
|
if (last)
|
|
829
|
-
return
|
|
830
|
-
|
|
843
|
+
return {
|
|
844
|
+
why: `No room by the hour is left here today: the last start today was ${last}.`,
|
|
845
|
+
code: 'hourly-over',
|
|
846
|
+
args: { lastStart: last },
|
|
847
|
+
};
|
|
848
|
+
return { why: 'No room by the hour is left here today.', code: 'sold-out' };
|
|
831
849
|
};
|
|
832
850
|
/** Why nothing here can be sold for these nights - the most useful reason first. */
|
|
833
851
|
const noRoomsWhy = (priced, stay, rooms) => {
|
|
834
852
|
if (priced.length === 0)
|
|
835
|
-
return
|
|
853
|
+
return {
|
|
854
|
+
why: 'This hotel has no rooms on sale through this connector.',
|
|
855
|
+
code: 'none-on-sale',
|
|
856
|
+
};
|
|
836
857
|
const fails = priced.map((p) => p.res).filter((r) => !r.ok);
|
|
837
858
|
const kinds = new Set(fails.map((f) => f.kind));
|
|
838
859
|
const only = (k) => kinds.size === 1 && kinds.has(k);
|
|
839
860
|
if (only('party'))
|
|
840
|
-
return
|
|
861
|
+
return partyNeedsOf(stay.guests, largestOf(fails));
|
|
841
862
|
if (only('prepaid'))
|
|
842
|
-
return
|
|
863
|
+
return {
|
|
864
|
+
why: 'Every rate here for those nights is paid in advance, and this connector books rates paid at the hotel.',
|
|
865
|
+
code: 'prepaid-only',
|
|
866
|
+
};
|
|
843
867
|
if (only('sold-out'))
|
|
844
868
|
return rooms > 1
|
|
845
|
-
?
|
|
846
|
-
|
|
869
|
+
? {
|
|
870
|
+
why: `No room type here has ${rooms} left for those nights.`,
|
|
871
|
+
code: 'sold-out',
|
|
872
|
+
args: { rooms },
|
|
873
|
+
}
|
|
874
|
+
: { why: 'Every room type here is taken for those nights.', code: 'sold-out' };
|
|
847
875
|
if (only('too-many-rooms'))
|
|
848
|
-
return
|
|
876
|
+
return {
|
|
877
|
+
why: `No rate here books ${rooms} rooms at once; ask for fewer.`,
|
|
878
|
+
code: 'too-many-rooms',
|
|
879
|
+
args: { rooms },
|
|
880
|
+
};
|
|
849
881
|
if (only('too-far'))
|
|
850
|
-
return
|
|
882
|
+
return {
|
|
883
|
+
why: `Not on sale yet: ${fails[0]?.why ?? 'rooms here are not sold that far ahead'}.`,
|
|
884
|
+
code: 'too-far',
|
|
885
|
+
...(fails[0]?.args?.days ? { args: { days: fails[0].args.days } } : {}),
|
|
886
|
+
};
|
|
851
887
|
const rule = fails.find((f) => f.kind === 'stay-rule');
|
|
852
888
|
if (rule)
|
|
853
|
-
return
|
|
889
|
+
return {
|
|
890
|
+
why: `Not for those nights: ${rule.why}.`,
|
|
891
|
+
// Booked too far ahead for this rate is "too far", said with its days.
|
|
892
|
+
...(rule.args?.days !== undefined && !rule.args.rule
|
|
893
|
+
? { code: 'too-far', args: { days: rule.args.days } }
|
|
894
|
+
: { code: 'stay-rule', ...(rule.args ? { args: rule.args } : {}) }),
|
|
895
|
+
};
|
|
854
896
|
if (kinds.has('closed'))
|
|
855
|
-
return 'Not on sale for those nights.';
|
|
856
|
-
return 'No room here can be booked for those nights.';
|
|
897
|
+
return { why: 'Not on sale for those nights.', code: 'closed' };
|
|
898
|
+
return { why: 'No room here can be booked for those nights.', code: 'unavailable' };
|
|
857
899
|
};
|
|
858
900
|
// --------------------------------------------------------------- quote ----
|
|
859
901
|
const quoteLines = async (trip, fresh = false) => {
|
|
@@ -2037,11 +2079,14 @@ export const createHuazhuHotels = (opts) => {
|
|
|
2037
2079
|
}
|
|
2038
2080
|
catch {
|
|
2039
2081
|
pricedRooms.set(raw.hotelId, []);
|
|
2040
|
-
why.set(raw.hotelId,
|
|
2082
|
+
why.set(raw.hotelId, {
|
|
2083
|
+
why: 'Rooms by the hour could not be read from Huazhu just now; open the hotel for them.',
|
|
2084
|
+
code: 'unreachable',
|
|
2085
|
+
});
|
|
2041
2086
|
}
|
|
2042
2087
|
return;
|
|
2043
2088
|
}
|
|
2044
|
-
const problem =
|
|
2089
|
+
const problem = stayProblemOf(stay, today());
|
|
2045
2090
|
if (problem) {
|
|
2046
2091
|
pricedRooms.set(raw.hotelId, []);
|
|
2047
2092
|
why.set(raw.hotelId, problem);
|
|
@@ -2055,9 +2100,12 @@ export const createHuazhuHotels = (opts) => {
|
|
|
2055
2100
|
}
|
|
2056
2101
|
catch (err) {
|
|
2057
2102
|
pricedRooms.set(raw.hotelId, []);
|
|
2058
|
-
why.set(raw.hotelId,
|
|
2059
|
-
|
|
2060
|
-
|
|
2103
|
+
why.set(raw.hotelId, {
|
|
2104
|
+
why: err instanceof HuazhuRefusal && err.code === 2009
|
|
2105
|
+
? 'Huazhu is answering slowly just now; open the hotel for its rooms.'
|
|
2106
|
+
: 'Prices could not be read from Huazhu just now; open the hotel for its rooms.',
|
|
2107
|
+
code: 'unreachable',
|
|
2108
|
+
});
|
|
2061
2109
|
}
|
|
2062
2110
|
};
|
|
2063
2111
|
const bestFor = (raw, over) => {
|
|
@@ -2157,11 +2205,18 @@ export const createHuazhuHotels = (opts) => {
|
|
|
2157
2205
|
if (!stay)
|
|
2158
2206
|
return m.hotel;
|
|
2159
2207
|
const b = bestFor(m.raw, f);
|
|
2160
|
-
if (!b)
|
|
2208
|
+
if (!b) {
|
|
2209
|
+
const w = why.get(m.raw.hotelId) ?? {
|
|
2210
|
+
why: 'No room here can be booked for those nights.',
|
|
2211
|
+
code: 'unavailable',
|
|
2212
|
+
};
|
|
2161
2213
|
return {
|
|
2162
2214
|
...m.hotel,
|
|
2163
|
-
noRoomsForStay: why
|
|
2215
|
+
noRoomsForStay: w.why,
|
|
2216
|
+
noRoomsForStayCode: w.code,
|
|
2217
|
+
...(w.args ? { noRoomsForStayArgs: w.args } : {}),
|
|
2164
2218
|
};
|
|
2219
|
+
}
|
|
2165
2220
|
return {
|
|
2166
2221
|
...m.hotel,
|
|
2167
2222
|
forStay: {
|
|
@@ -2250,9 +2305,9 @@ export const createHuazhuHotels = (opts) => {
|
|
|
2250
2305
|
const detail = { hotel: toHotel(raw, view, now()) };
|
|
2251
2306
|
if (!stay)
|
|
2252
2307
|
return detail;
|
|
2253
|
-
const problem =
|
|
2308
|
+
const problem = stayProblemOf(stay, today());
|
|
2254
2309
|
if (problem)
|
|
2255
|
-
return { ...detail,
|
|
2310
|
+
return { ...detail, ...noRoomsOf(problem) };
|
|
2256
2311
|
let priced;
|
|
2257
2312
|
try {
|
|
2258
2313
|
priced = await priceStay(raw, stay, 1);
|
|
@@ -2261,7 +2316,10 @@ export const createHuazhuHotels = (opts) => {
|
|
|
2261
2316
|
return {
|
|
2262
2317
|
...detail,
|
|
2263
2318
|
stay,
|
|
2264
|
-
|
|
2319
|
+
...noRoomsOf({
|
|
2320
|
+
why: `Huazhu could not be asked for this hotel's rooms just now (${err instanceof HuazhuRefusal ? err.huazhuMessage : 'no answer'}). Try again in a moment.`,
|
|
2321
|
+
code: 'unreachable',
|
|
2322
|
+
}),
|
|
2265
2323
|
};
|
|
2266
2324
|
}
|
|
2267
2325
|
const rooms = priced
|
|
@@ -2272,7 +2330,7 @@ export const createHuazhuHotels = (opts) => {
|
|
|
2272
2330
|
...detail,
|
|
2273
2331
|
stay,
|
|
2274
2332
|
rooms,
|
|
2275
|
-
...(rooms.length === 0 ?
|
|
2333
|
+
...(rooms.length === 0 ? noRoomsOf(noRoomsWhy(priced, stay, 1)) : {}),
|
|
2276
2334
|
};
|
|
2277
2335
|
},
|
|
2278
2336
|
async hourlyRooms(id, guests) {
|
|
@@ -2290,7 +2348,10 @@ export const createHuazhuHotels = (opts) => {
|
|
|
2290
2348
|
return {
|
|
2291
2349
|
hotel,
|
|
2292
2350
|
stay,
|
|
2293
|
-
|
|
2351
|
+
...noRoomsOf({
|
|
2352
|
+
why: `Huazhu could not be asked for this hotel's rooms by the hour just now (${err instanceof HuazhuRefusal ? err.huazhuMessage : 'no answer'}). Try again in a moment.`,
|
|
2353
|
+
code: 'unreachable',
|
|
2354
|
+
}),
|
|
2294
2355
|
};
|
|
2295
2356
|
}
|
|
2296
2357
|
const rooms = priced
|
|
@@ -2301,7 +2362,7 @@ export const createHuazhuHotels = (opts) => {
|
|
|
2301
2362
|
hotel,
|
|
2302
2363
|
stay,
|
|
2303
2364
|
rooms,
|
|
2304
|
-
...(rooms.length === 0 ?
|
|
2365
|
+
...(rooms.length === 0 ? noRoomsOf(hourlyNoRoomsWhy(priced, guests)) : {}),
|
|
2305
2366
|
};
|
|
2306
2367
|
},
|
|
2307
2368
|
async datesAround(id, stay, o) {
|